celo-blockchain

+95284
-38579

This is an overview of the changes in celo-blockchain, a fork of go-ethereum.

To minimize changes in this diff, we have replaced github.com/celo-org/celo-blockchain with github.com/ethereum/go-ethereum in imports, removed “ethereum” named imports, and applied gofmt to both codebases. When using individual diffs, double check any changes in the original codebases.

Since the diffs between the two codebases are currently so large, the categories below often reflect different packages as opposed to broader features or logical units.

diff --git go-ethereum/accounts/accounts.go celo/accounts/accounts.go index ea98ab27a4986688ae428e572f9e5f51ce9d3697..6a9fcea0830c162d805b6da74ef72044294a2a5c 100644 --- go-ethereum/accounts/accounts.go +++ celo/accounts/accounts.go @@ -18,9 +18,11 @@ // Package accounts implements high level Ethereum account management. package accounts   import ( + "crypto/ecdsa" "fmt" "math/big"   + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -38,8 +40,9 @@ const ( MimetypeDataWithValidator = "data/validator" MimetypeTypedData = "data/typed" - MimetypeClique = "application/x-clique-header" MimetypeTextPlain = "text/plain" + MimetypeIstanbul = "application/x-istanbul-msg" + MimetypeIstanbulHeader = "application/x-istanbul-header" )   // Wallet represents a software or hardware wallet that might contain one or more @@ -68,6 +71,9 @@ // Please note, if you open a wallet, you must close it to release any allocated // resources (especially important when working with hardware wallets). Open(passphrase string) error   + // ConfirmAddress shows the address of the given path on the wallet's display. + ConfirmAddress(path DerivationPath) (common.Address, error) + // Close releases any resources held by an open wallet instance. Close() error   @@ -78,6 +84,9 @@ Accounts() []Account   // Contains returns whether an account is part of this particular wallet or not. Contains(account Account) bool + + // Decrypt decrypts an ECIES ciphertext. + Decrypt(account Account, c, s1, s2 []byte) ([]byte, error)   // Derive attempts to explicitly derive a hierarchical deterministic account at // the specified derivation path. If requested, the derived account will be added @@ -112,6 +121,14 @@ // the needed details via SignDataWithPassphrase, or by other means (e.g. unlock // the account in a keystore). SignData(account Account, mimeType string, data []byte) ([]byte, error)   + // SignHash is like SignData but doesn't hash the given data + // + // NOTE: DEPRACATED, use SignData for future releases. + // This is needed for backwards compatibility on a network where validators + // started on celo-blockchain 1.8. 1.9 removed the SignHash function, + // replacing it with SignData, which always hashes the input before signing. + SignHash(account Account, hash []byte) ([]byte, error) + // SignDataWithPassphrase is identical to SignData, but also takes a password // NOTE: there's a chance that an erroneous call might mistake the two strings, and // supply password in the mimetype field, or vice versa. Thus, an implementation @@ -135,6 +152,13 @@ SignText(account Account, text []byte) ([]byte, error)   // SignTextWithPassphrase is identical to Signtext, but also takes a password SignTextWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error) + + // SignBLS generates a BLS signature over the provided data with a direct or composite hasher + SignBLS(account Account, msg []byte, extraData []byte, useComposite, cip22 bool) (blscrypto.SerializedSignature, error) + + GenerateProofOfPossession(account Account, address common.Address) ([]byte, []byte, error) + GenerateProofOfPossessionBLS(account Account, address common.Address) ([]byte, []byte, error) + GetPublicKey(account Account) (*ecdsa.PublicKey, error)   // SignTx requests the wallet to sign the given transaction. //
diff --git go-ethereum/accounts/manager.go celo/accounts/manager.go index 7b4b307e58e5d93f4660029008b4ee104842772b..dccb62744463ec47d80048491b7f725606e90294 100644 --- go-ethereum/accounts/manager.go +++ celo/accounts/manager.go @@ -21,6 +21,8 @@ "reflect" "sort" "sync"   + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" ) @@ -232,6 +234,7 @@ if wallet.Contains(account) { return wallet, nil } } + log.Warn("Failed to find the account", "account", account.Address, "account url", account.URL) return nil, ErrUnknownAccount }
diff --git go-ethereum/accounts/external/backend.go celo/accounts/external/backend.go index 7cdd82f8e5c0046df549dcdede91b98f90457d83..aee408e89f665bc6799fc482a6a503c7b506b491 100644 --- go-ethereum/accounts/external/backend.go +++ celo/accounts/external/backend.go @@ -17,10 +17,12 @@ package external   import ( + "crypto/ecdsa" "fmt" "math/big" "sync"   + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" @@ -148,12 +150,12 @@ func (api *ExternalSigner) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { return accounts.Account{}, fmt.Errorf("operation not supported on external signers") }   -func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) { - log.Error("operation SelfDerive not supported on external signers") +func (api *ExternalSigner) ConfirmAddress(path accounts.DerivationPath) (common.Address, error) { + return common.Address{}, fmt.Errorf("operation not supported on external signers") }   -func (api *ExternalSigner) signHash(account accounts.Account, hash []byte) ([]byte, error) { - return []byte{}, fmt.Errorf("operation not supported on external signers") +func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) { + log.Error("operation SelfDerive not supported on external signers") }   // SignData signs keccak256(data). The mimetype parameter describes the type of data being signed @@ -166,11 +168,14 @@ &signAddress, // Need to use the pointer here, because of how MarshalJSON is defined hexutil.Encode(data)); err != nil { return nil, err } - // If V is on 27/28-form, convert to 0/1 for Clique - if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) { - res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use - } return res, nil +} + +// SignHash is not implemented for the external signer +// +// DEPRECATED, use SignData in future releases. +func (api *ExternalSigner) SignHash(account accounts.Account, hash []byte) ([]byte, error) { + panic("SignHash not implemented for the external signer") }   func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) { @@ -254,6 +259,26 @@ return nil, fmt.Errorf("password-operations not supported on external signers") } func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) { return nil, fmt.Errorf("password-operations not supported on external signers") +} + +func (api *ExternalSigner) Decrypt(account accounts.Account, c, s1, s2 []byte) ([]byte, error) { + return nil, accounts.ErrNotSupported +} + +func (api *ExternalSigner) SignBLS(account accounts.Account, msg []byte, extraData []byte, useComposite, cip22 bool) (blscrypto.SerializedSignature, error) { + return blscrypto.SerializedSignature{}, accounts.ErrNotSupported +} + +func (api *ExternalSigner) GenerateProofOfPossession(account accounts.Account, address common.Address) ([]byte, []byte, error) { + return nil, nil, accounts.ErrNotSupported +} + +func (api *ExternalSigner) GenerateProofOfPossessionBLS(account accounts.Account, address common.Address) ([]byte, []byte, error) { + return nil, nil, accounts.ErrNotSupported +} + +func (api *ExternalSigner) GetPublicKey(account accounts.Account) (*ecdsa.PublicKey, error) { + return nil, accounts.ErrNotSupported }   func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
diff --git go-ethereum/accounts/keystore/wallet.go celo/accounts/keystore/wallet.go index ef13d80d9fcd44cea222d9b7435b64b4dbcd9ecf..07fa8b434d2aed940bd49b471701435ebf7b4785 100644 --- go-ethereum/accounts/keystore/wallet.go +++ celo/accounts/keystore/wallet.go @@ -17,12 +17,17 @@ package keystore   import ( + "crypto/ecdsa" "math/big" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls"   "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" )   // keystoreWallet implements the accounts.Wallet interface for the original @@ -69,12 +74,32 @@ func (w *keystoreWallet) Contains(account accounts.Account) bool { return account.Address == w.account.Address && (account.URL == (accounts.URL{}) || account.URL == w.account.URL) }   +// Decrypt decrypts an ECIES ciphertext. +func (w *keystoreWallet) Decrypt(account accounts.Account, c, s1, s2 []byte) ([]byte, error) { + if account.Address != w.account.Address { + log.Debug(accounts.ErrUnknownAccount.Error(), "account", account) + return nil, accounts.ErrUnknownAccount + } + if account.URL != (accounts.URL{}) && account.URL != w.account.URL { + log.Debug(accounts.ErrUnknownAccount.Error(), "account", account) + return nil, accounts.ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.Decrypt(account, c, s1, s2) +} + // Derive implements accounts.Wallet, but is a noop for plain wallets since there // is no notion of hierarchical account derivation for plain keystore accounts. func (w *keystoreWallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { return accounts.Account{}, accounts.ErrNotSupported }   +// ConfirmAddress implements accounts.Wallet, but is a noop for plain wallets since there +// is no notion of address confirmation for plain keystore accounts. +func (w *keystoreWallet) ConfirmAddress(path accounts.DerivationPath) (common.Address, error) { + return common.Address{}, accounts.ErrNotSupported +} + // SelfDerive implements accounts.Wallet, but is a noop for plain wallets since // there is no notion of hierarchical account derivation for plain keystore accounts. func (w *keystoreWallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) { @@ -87,21 +112,73 @@ // able to sign via our shared keystore backend). func (w *keystoreWallet) signHash(account accounts.Account, hash []byte) ([]byte, error) { // Make sure the requested account is contained within if !w.Contains(account) { + log.Debug(accounts.ErrUnknownAccount.Error(), "account", account) return nil, accounts.ErrUnknownAccount } // Account seems valid, request the keystore to sign return w.keystore.SignHash(account, hash) }   +func (w *keystoreWallet) GetPublicKey(account accounts.Account) (*ecdsa.PublicKey, error) { + // Make sure the requested account is contained within + if !w.Contains(account) { + log.Debug(accounts.ErrUnknownAccount.Error(), "account", account) + return nil, accounts.ErrUnknownAccount + } + // Account seems valid, request the public key + return w.keystore.GetPublicKey(account) +} + +func (w *keystoreWallet) SignBLS(account accounts.Account, msg []byte, extraData []byte, useComposite, cip22 bool) (blscrypto.SerializedSignature, error) { + // Make sure the requested account is contained within + if !w.Contains(account) { + log.Debug(accounts.ErrUnknownAccount.Error(), "account", account) + return blscrypto.SerializedSignature{}, accounts.ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.SignBLS(account, msg, extraData, useComposite, cip22) +} + +func (w *keystoreWallet) GenerateProofOfPossession(account accounts.Account, address common.Address) ([]byte, []byte, error) { + // Make sure the requested account is contained within + if !w.Contains(account) { + log.Debug(accounts.ErrUnknownAccount.Error(), "account", account) + return nil, nil, accounts.ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.GenerateProofOfPossession(account, address) +} + +func (w *keystoreWallet) GenerateProofOfPossessionBLS(account accounts.Account, address common.Address) ([]byte, []byte, error) { + // Make sure the requested account is contained within + if !w.Contains(account) { + log.Debug(accounts.ErrUnknownAccount.Error(), "account", account) + return nil, nil, accounts.ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.GenerateProofOfPossessionBLS(account, address) +} + // SignData signs keccak256(data). The mimetype parameter describes the type of data being signed. func (w *keystoreWallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) { return w.signHash(account, crypto.Keccak256(data)) }   +// SignHash implements accounts.Wallet, attempting to sign the given hash with +// the given account. If the wallet does not wrap this particular account, an +// error is returned to avoid account leakage (even though in theory we may be +// able to sign via our shared keystore backend). +// +// DEPRECATED, use SignData in future releases. +func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) { + return w.signHash(account, hash) +} + // SignDataWithPassphrase signs keccak256(data). The mimetype parameter describes the type of data being signed. func (w *keystoreWallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) { // Make sure the requested account is contained within if !w.Contains(account) { + log.Debug(accounts.ErrUnknownAccount.Error(), "account", account) return nil, accounts.ErrUnknownAccount } // Account seems valid, request the keystore to sign @@ -132,6 +209,7 @@ // be able to sign via our shared keystore backend). func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { // Make sure the requested account is contained within if !w.Contains(account) { + log.Debug(accounts.ErrUnknownAccount.Error(), "account", account) return nil, accounts.ErrUnknownAccount } // Account seems valid, request the keystore to sign @@ -143,6 +221,7 @@ // transaction with the given account using passphrase as extra authentication. func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { // Make sure the requested account is contained within if !w.Contains(account) { + log.Debug(accounts.ErrUnknownAccount.Error(), "account", account) return nil, accounts.ErrUnknownAccount } // Account seems valid, request the keystore to sign
diff --git go-ethereum/accounts/keystore/key.go celo/accounts/keystore/key.go index c728765ddfe7fa8bf3f5b00c00c4783385e5aceb..f994af3e09af0a637e7a38d3f554d68c03294ece 100644 --- go-ethereum/accounts/keystore/key.go +++ celo/accounts/keystore/key.go @@ -66,6 +66,7 @@ }   type encryptedKeyJSONV3 struct { Address string `json:"address"` + BLSPublicKey string `json:"blspublickey"` Crypto CryptoJSON `json:"crypto"` Id string `json:"id"` Version int `json:"version"`
diff --git go-ethereum/accounts/keystore/passphrase.go celo/accounts/keystore/passphrase.go index db00ecf61584d629803ae9d5c033ca227cc2a013..77b863aa87f1c211bf03ad6fd12e95d3c5e6bd2b 100644 --- go-ethereum/accounts/keystore/passphrase.go +++ celo/accounts/keystore/passphrase.go @@ -38,6 +38,8 @@ "io/ioutil" "os" "path/filepath"   + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/celo-org/celo-bls-go/bls" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" @@ -185,12 +187,32 @@ // EncryptKey encrypts a key using the specified scrypt parameters into a json // blob that can be decrypted later on. func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) + blsPrivateKeyBytes, err := blscrypto.ECDSAToBLS(key.PrivateKey) + if err != nil { + return nil, err + } + privateKey, err := bls.DeserializePrivateKey(blsPrivateKeyBytes) + if err != nil { + return nil, err + } + defer privateKey.Destroy() + publicKey, err := privateKey.ToPublic() + if err != nil { + return nil, err + } + defer publicKey.Destroy() + publicKeyBytes, err := publicKey.Serialize() + if err != nil { + return nil, err + } + cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP) if err != nil { return nil, err } encryptedKeyJSONV3 := encryptedKeyJSONV3{ hex.EncodeToString(key.Address[:]), + hex.EncodeToString(publicKeyBytes), cryptoStruct, key.Id.String(), version,
diff --git go-ethereum/accounts/keystore/keystore_test.go celo/accounts/keystore/keystore_test.go index e91862b9c4e1a494ce7c7d3f2cb0d931885d7afb..3f4a47b109757f7d1fd7a58ce3210acaea0b02c6 100644 --- go-ethereum/accounts/keystore/keystore_test.go +++ celo/accounts/keystore/keystore_test.go @@ -17,6 +17,7 @@ package keystore   import ( + "encoding/hex" "io/ioutil" "math/rand" "os" @@ -84,6 +85,27 @@ if err := ks.Unlock(a1, ""); err != nil { t.Fatal(err) } if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil { + t.Fatal(err) + } +} + +func TestComputeECDHSharedSecret(t *testing.T) { + keyHex := "044fb0a7e965176de5bbd615c5c9d5ca0927b74d914db881f7742362f2afda8a83b4e8e2cc87a98cdb5e2cf1c87b94a62ca9905de0e80676947166d4ae3d012691" + public := make([]byte, 65) + hex.Decode(public, []byte(keyHex)) + + dir, ks := tmpKeyStore(t, true) + defer os.RemoveAll(dir) + + pass := "" // not used but required by API + a1, err := ks.NewAccount(pass) + if err != nil { + t.Fatal(err) + } + if err := ks.Unlock(a1, ""); err != nil { + t.Fatal(err) + } + if _, err := ks.ComputeECDHSharedSecret(accounts.Account{Address: a1.Address}, public); err != nil { t.Fatal(err) } }
diff --git go-ethereum/accounts/keystore/keystore.go celo/accounts/keystore/keystore.go index 3f4b479583ca878e337028ca196fa97657a793b5..394262fbddf4e4f1618930b966757999009ce2dd 100644 --- go-ethereum/accounts/keystore/keystore.go +++ celo/accounts/keystore/keystore.go @@ -22,8 +22,10 @@ package keystore   import ( "crypto/ecdsa" + "crypto/elliptic" crand "crypto/rand" "errors" + "fmt" "math/big" "os" "path/filepath" @@ -32,11 +34,17 @@ "runtime" "sync" "time"   + //nolint:goimports + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/celo-org/celo-bls-go/bls" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" )   var ( @@ -234,6 +242,21 @@ func (ks *KeyStore) Accounts() []accounts.Account { return ks.cache.accounts() }   +// Decrypt decrypts an ECIES ciphertext. +func (ks *KeyStore) Decrypt(a accounts.Account, c, s1, s2 []byte) ([]byte, error) { + // Look up the key to sign with and abort if it cannot be found + ks.mu.RLock() + defer ks.mu.RUnlock() + + unlockedKey, found := ks.unlocked[a.Address] + if !found { + return nil, ErrLocked + } + // Import the ECDSA key as an ECIES key and decrypt the data. + eciesKey := ecies.ImportECDSA(unlockedKey.PrivateKey) + return eciesKey.Decrypt(c, s1, s2) +} + // Delete deletes the key matched by account if the passphrase is correct. // If the account contains no filename, the address must match a unique key. func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error { @@ -273,6 +296,116 @@ // Sign the hash using plain ECDSA operations return crypto.Sign(hash, unlockedKey.PrivateKey) }   +func (ks *KeyStore) SignBLS(a accounts.Account, msg []byte, extraData []byte, useComposite, cip22 bool) (blscrypto.SerializedSignature, error) { + // Look up the key to sign with and abort if it cannot be found + ks.mu.RLock() + defer ks.mu.RUnlock() + + unlockedKey, found := ks.unlocked[a.Address] + if !found { + return blscrypto.SerializedSignature{}, ErrLocked + } + + privateKeyBytes, err := blscrypto.ECDSAToBLS(unlockedKey.PrivateKey) + if err != nil { + return blscrypto.SerializedSignature{}, err + } + + privateKey, err := bls.DeserializePrivateKey(privateKeyBytes) + if err != nil { + return blscrypto.SerializedSignature{}, err + } + defer privateKey.Destroy() + + signature, err := privateKey.SignMessage(msg, extraData, useComposite, cip22) + if err != nil { + return blscrypto.SerializedSignature{}, err + } + defer signature.Destroy() + signatureBytes, err := signature.Serialize() + if err != nil { + return blscrypto.SerializedSignature{}, err + } + + return blscrypto.SerializedSignatureFromBytes(signatureBytes) +} + +func (ks *KeyStore) GenerateProofOfPossession(a accounts.Account, address common.Address) ([]byte, []byte, error) { + publicKey, err := ks.GetPublicKey(a) + if err != nil { + return nil, nil, err + } + publicKeyBytes := crypto.FromECDSAPub(publicKey) + + hash := crypto.Keccak256(address.Bytes()) + log.Info("msg", "msg", hexutil.Encode(hash)) + msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(hash), hash) + hash = crypto.Keccak256([]byte(msg)) + log.Info("hash", "hash", hexutil.Encode(hash)) + + signature, err := ks.SignHash(a, hash) + if err != nil { + return nil, nil, err + } + return publicKeyBytes, signature, nil +} + +func (ks *KeyStore) GenerateProofOfPossessionBLS(a accounts.Account, address common.Address) ([]byte, []byte, error) { + // Look up the key to sign with and abort if it cannot be found + ks.mu.RLock() + defer ks.mu.RUnlock() + + unlockedKey, found := ks.unlocked[a.Address] + if !found { + return nil, nil, ErrLocked + } + + privateKeyBytes, err := blscrypto.ECDSAToBLS(unlockedKey.PrivateKey) + if err != nil { + return nil, nil, err + } + + privateKey, err := bls.DeserializePrivateKey(privateKeyBytes) + if err != nil { + return nil, nil, err + } + defer privateKey.Destroy() + + signature, err := privateKey.SignPoP(address.Bytes()) + if err != nil { + return nil, nil, err + } + defer signature.Destroy() + signatureBytes, err := signature.Serialize() + if err != nil { + return nil, nil, err + } + + publicKey, err := privateKey.ToPublic() + if err != nil { + return nil, nil, err + } + defer publicKey.Destroy() + publicKeyBytes, err := publicKey.Serialize() + if err != nil { + return nil, nil, err + } + return publicKeyBytes, signatureBytes, nil +} + +// Retrieve the ECDSA public key for a given account. +func (ks *KeyStore) GetPublicKey(a accounts.Account) (*ecdsa.PublicKey, error) { + // Look up the key to sign with and abort if it cannot be found + ks.mu.RLock() + defer ks.mu.RUnlock() + + unlockedKey, found := ks.unlocked[a.Address] + if !found { + return nil, ErrLocked + } + return &unlockedKey.PrivateKey.PublicKey, nil +} + // SignTx signs the given transaction with the requested account. func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { // Look up the key to sign with and abort if it cannot be found @@ -505,3 +638,28 @@ for i := range b { b[i] = 0 } } + +// ComputeECDHSharedSecret computes an ECDH shared secret between the given account's +// private key and the public key provided. The account has to be unlocked first. +// The public key format is a 65 byte array, with byte[0] == 4, 32 bytes for X, +// and 32 bytes for Y (encoded in base256). +func (ks *KeyStore) ComputeECDHSharedSecret(a accounts.Account, public []byte) ([]byte, error) { + curve := crypto.S256() + // Look up the key to sign with and abort if it cannot be found + ks.mu.RLock() + defer ks.mu.RUnlock() + + unlockedKey, found := ks.unlocked[a.Address] + if !found { + return nil, ErrLocked + } + + var x, y *big.Int + x, y = elliptic.Unmarshal(curve, public) + if x == nil || y == nil { + return nil, errors.New("Could not unmarshal public key from bytes") + } + + shared, _ := crypto.S256().ScalarMult(x, y, unlockedKey.PrivateKey.D.Bytes()) + return shared.Bytes(), nil +}
diff --git go-ethereum/accounts/hd.go celo/accounts/hd.go index 23abd67c8dddd391e8e82b975eae706276b112cf..203bea39acbdf164860c7311d03cd47a8762c3e0 100644 --- go-ethereum/accounts/hd.go +++ celo/accounts/hd.go @@ -26,19 +26,19 @@ "strings" )   // DefaultRootDerivationPath is the root path to which custom derivation endpoints -// are appended. As such, the first account will be at m/44'/60'/0'/0, the second -// at m/44'/60'/0'/1, etc. -var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0} +// are appended. As such, the first account will be at m/44'/52752'/0'/0, the second +// at m/44'/52752'/0'/1, etc. +var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0}   // DefaultBaseDerivationPath is the base path from which custom derivation endpoints -// are incremented. As such, the first account will be at m/44'/60'/0'/0/0, the second -// at m/44'/60'/0'/0/1, etc. -var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0} +// are incremented. As such, the first account will be at m/44'/52752'/0'/0/0, the second +// at m/44'/52752'/0'/0/1, etc. +var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0, 0}   // LegacyLedgerBaseDerivationPath is the legacy base path from which custom derivation -// endpoints are incremented. As such, the first account will be at m/44'/60'/0'/0, the -// second at m/44'/60'/0'/1, etc. -var LegacyLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0} +// endpoints are incremented. As such, the first account will be at m/44'/52752'/0'/0, the +// second at m/44'/52752'/0'/1, etc. +var LegacyLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0}   // DerivationPath represents the computer friendly version of a hierarchical // deterministic wallet account derivaion path. @@ -51,9 +51,9 @@ // // The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki // defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and // SLIP-44 https://github.com/satoshilabs/slips/blob/master/slip-0044.md assigns -// the `coin_type` 60' (or 0x8000003C) to Ethereum. +// the `coin_type` 52752' (or 0x8000CE10) to Celo. // -// The root path for Ethereum is m/44'/60'/0'/0 according to the specification +// The root path for Celo is m/44'/52752'/0'/0 according to the specification // from https://github.com/ethereum/EIPs/issues/84, albeit it's not set in stone // yet whether accounts should increment the last component or the children of // that. We will go with the simpler approach of incrementing the last component.
diff --git go-ethereum/accounts/hd_test.go celo/accounts/hd_test.go index 48f67eefb3001bb8a8b58ff29e3aec35e68ff06e..d7928fba3333499dbc7f2ad7ca161554b60c8dcd 100644 --- go-ethereum/accounts/hd_test.go +++ celo/accounts/hd_test.go @@ -30,43 +30,43 @@ input string output DerivationPath }{ // Plain absolute derivation paths - {"m/44'/60'/0'/0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, - {"m/44'/60'/0'/128", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}}, - {"m/44'/60'/0'/0'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, - {"m/44'/60'/0'/128'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}}, - {"m/2147483692/2147483708/2147483648/0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, - {"m/2147483692/2147483708/2147483648/2147483648", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, + {"m/44'/52752'/0'/0", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0}}, + {"m/44'/52752'/0'/128", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 128}}, + {"m/44'/52752'/0'/0'", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0x80000000 + 0}}, + {"m/44'/52752'/0'/128'", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0x80000000 + 128}}, + {"m/2147483692/2147536400/2147483648/0", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0}}, + {"m/2147483692/2147536400/2147483648/2147483648", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0x80000000 + 0}},   // Plain relative derivation paths - {"0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0}}, - {"128", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 128}}, - {"0'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 0}}, - {"128'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 128}}, - {"2147483648", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 0}}, + {"0", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0, 0}}, + {"128", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0, 128}}, + {"0'", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0, 0x80000000 + 0}}, + {"128'", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0, 0x80000000 + 128}}, + {"2147483648", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0, 0x80000000 + 0}},   // Hexadecimal absolute derivation paths - {"m/0x2C'/0x3c'/0x00'/0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, - {"m/0x2C'/0x3c'/0x00'/0x80", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}}, - {"m/0x2C'/0x3c'/0x00'/0x00'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, - {"m/0x2C'/0x3c'/0x00'/0x80'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}}, - {"m/0x8000002C/0x8000003c/0x80000000/0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, - {"m/0x8000002C/0x8000003c/0x80000000/0x80000000", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, + {"m/0x2C'/0xce10'/0x00'/0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0}}, + {"m/0x2C'/0xce10'/0x00'/0x80", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 128}}, + {"m/0x2C'/0xce10'/0x00'/0x00'", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0x80000000 + 0}}, + {"m/0x2C'/0xce10'/0x00'/0x80'", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0x80000000 + 128}}, + {"m/0x8000002C/0x8000ce10/0x80000000/0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0}}, + {"m/0x8000002C/0x8000ce10/0x80000000/0x80000000", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0x80000000 + 0}},   // Hexadecimal relative derivation paths - {"0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0}}, - {"0x80", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 128}}, - {"0x00'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 0}}, - {"0x80'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 128}}, - {"0x80000000", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 0}}, + {"0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0, 0}}, + {"0x80", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0, 128}}, + {"0x00'", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0, 0x80000000 + 0}}, + {"0x80'", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0, 0x80000000 + 128}}, + {"0x80000000", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0, 0x80000000 + 0}},   // Weird inputs just to ensure they work - {" m / 44 '\n/\n 60 \n\n\t' /\n0 ' /\t\t 0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, + {" m / 44 '\n/\n 52752 \n\n\t' /\n0 ' /\t\t 0", DerivationPath{0x80000000 + 44, 0x80000000 + 52752, 0x80000000 + 0, 0}},   // Invalid derivation paths {"", nil}, // Empty relative derivation path {"m", nil}, // Empty absolute derivation path {"m/", nil}, // Missing last derivation component - {"/44'/60'/0'/0", nil}, // Absolute path without m prefix, might be user error + {"/44'/52752'/0'/0", nil}, // Absolute path without m prefix, might be user error {"m/2147483648'", nil}, // Overflows 32 bit integer {"m/-1'", nil}, // Cannot contain negative number } @@ -91,28 +91,28 @@ func TestHdPathIteration(t *testing.T) { testDerive(t, DefaultIterator(DefaultBaseDerivationPath), []string{ - "m/44'/60'/0'/0/0", "m/44'/60'/0'/0/1", - "m/44'/60'/0'/0/2", "m/44'/60'/0'/0/3", - "m/44'/60'/0'/0/4", "m/44'/60'/0'/0/5", - "m/44'/60'/0'/0/6", "m/44'/60'/0'/0/7", - "m/44'/60'/0'/0/8", "m/44'/60'/0'/0/9", + "m/44'/52752'/0'/0/0", "m/44'/52752'/0'/0/1", + "m/44'/52752'/0'/0/2", "m/44'/52752'/0'/0/3", + "m/44'/52752'/0'/0/4", "m/44'/52752'/0'/0/5", + "m/44'/52752'/0'/0/6", "m/44'/52752'/0'/0/7", + "m/44'/52752'/0'/0/8", "m/44'/52752'/0'/0/9", })   testDerive(t, DefaultIterator(LegacyLedgerBaseDerivationPath), []string{ - "m/44'/60'/0'/0", "m/44'/60'/0'/1", - "m/44'/60'/0'/2", "m/44'/60'/0'/3", - "m/44'/60'/0'/4", "m/44'/60'/0'/5", - "m/44'/60'/0'/6", "m/44'/60'/0'/7", - "m/44'/60'/0'/8", "m/44'/60'/0'/9", + "m/44'/52752'/0'/0", "m/44'/52752'/0'/1", + "m/44'/52752'/0'/2", "m/44'/52752'/0'/3", + "m/44'/52752'/0'/4", "m/44'/52752'/0'/5", + "m/44'/52752'/0'/6", "m/44'/52752'/0'/7", + "m/44'/52752'/0'/8", "m/44'/52752'/0'/9", })   testDerive(t, LedgerLiveIterator(DefaultBaseDerivationPath), []string{ - "m/44'/60'/0'/0/0", "m/44'/60'/1'/0/0", - "m/44'/60'/2'/0/0", "m/44'/60'/3'/0/0", - "m/44'/60'/4'/0/0", "m/44'/60'/5'/0/0", - "m/44'/60'/6'/0/0", "m/44'/60'/7'/0/0", - "m/44'/60'/8'/0/0", "m/44'/60'/9'/0/0", + "m/44'/52752'/0'/0/0", "m/44'/52752'/1'/0/0", + "m/44'/52752'/2'/0/0", "m/44'/52752'/3'/0/0", + "m/44'/52752'/4'/0/0", "m/44'/52752'/5'/0/0", + "m/44'/52752'/6'/0/0", "m/44'/52752'/7'/0/0", + "m/44'/52752'/8'/0/0", "m/44'/52752'/9'/0/0", }) }
diff --git go-ethereum/accounts/usbwallet/ledger.go celo/accounts/usbwallet/ledger.go index c2caa39ad6a13cff12e8a65f77cccdb12bc34e6b..31bde56375b1437311a3136d87f294add45b5751 100644 --- go-ethereum/accounts/usbwallet/ledger.go +++ celo/accounts/usbwallet/ledger.go @@ -21,6 +21,7 @@ package usbwallet   import ( + "crypto/sha256" "encoding/binary" "encoding/hex" "errors" @@ -29,6 +30,7 @@ "io" "math/big"   "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/usbwallet/ledger" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" @@ -49,16 +51,26 @@ // specific opcodes. The same parameter values may be reused between opcodes. type ledgerParam2 byte   const ( - ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Ethereum address for a given BIP 32 path - ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs an Ethereum transaction after having the user validate the parameters + ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Celo address for a given BIP 32 path + ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs a Celo transaction after having the user validate the parameters ledgerOpGetConfiguration ledgerOpcode = 0x06 // Returns specific wallet application configuration - ledgerOpSignTypedMessage ledgerOpcode = 0x0c // Signs an Ethereum message following the EIP 712 specification + ledgerOpSignMessage ledgerOpcode = 0x08 // Signs a Celo message after having the user validate the parameters + ledgerOpProvideERC20 ledgerOpcode = 0x0A // Provides ERC20 information for tokens + + /* TODO: add functionality to the Ledger's Celo app + ledgerOpSignTypedMessage ledgerOpcode = 0x0c // Signs a Celo message following the EIP 712 specification + */   ledgerP1DirectlyFetchAddress ledgerParam1 = 0x00 // Return address directly from the wallet + ledgerP1ShowFetchAddress ledgerParam1 = 0x01 // Return address from the wallet after showing it + /* TODO: add functionality to the Ledger's Celo app ledgerP1InitTypedMessageData ledgerParam1 = 0x00 // First chunk of Typed Message data + */ ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address + + statusCodeOK = 0x9000 )   // errLedgerReplyInvalidHeader is the error message returned by a Ledger data exchange @@ -69,6 +81,10 @@ // errLedgerInvalidVersionReply is the error message returned by a Ledger version retrieval // when a response does arrive, but it does not contain the expected data. var errLedgerInvalidVersionReply = errors.New("ledger: invalid version reply") + +// errLedgerBadStatusCode is the error message returned by any Ledger command +// when a response arrives with a bad status code. +var errLedgerBadStatusCode = errors.New("ledger: bad status code")   // ledgerDriver implements the communication with a Ledger hardware wallet. type ledgerDriver struct { @@ -77,6 +93,7 @@ version [3]byte // Current version of the Ledger firmware (zero if app is offline) browser bool // Flag whether the Ledger is in browser mode (reply channel mismatch) failure error // Any failure that would make the device unusable log log.Logger // Contextual logger to tag the ledger with its id + tokens *ledger.Tokens // Tokens list }   // newLedgerDriver creates a new instance of a Ledger USB protocol driver. @@ -93,15 +110,15 @@ if w.failure != nil { return fmt.Sprintf("Failed: %v", w.failure), w.failure } if w.browser { - return "Ethereum app in browser mode", w.failure + return "Celo app in browser mode", w.failure } if w.offline() { - return "Ethereum app offline", w.failure + return "Celo app offline", w.failure } - return fmt.Sprintf("Ethereum app v%d.%d.%d online", w.version[0], w.version[1], w.version[2]), w.failure + return fmt.Sprintf("Celo app v%d.%d.%d online", w.version[0], w.version[1], w.version[2]), w.failure }   -// offline returns whether the wallet and the Ethereum app is offline or not. +// offline returns whether the wallet and the Celo app is offline or not. // // The method assumes that the state lock is held! func (w *ledgerDriver) offline() bool { @@ -114,19 +131,38 @@ // parameter is silently discarded. func (w *ledgerDriver) Open(device io.ReadWriter, passphrase string) error { w.device, w.failure = device, nil   - _, err := w.ledgerDerive(accounts.DefaultBaseDerivationPath) + _, err := w.ledgerDerive(accounts.DefaultBaseDerivationPath, false) if err != nil { - // Ethereum app is not running or in browser mode, nothing more to do, return + // Celo app is not running or in browser mode, nothing more to do, return if err == errLedgerReplyInvalidHeader { w.browser = true } return nil } - // Try to resolve the Ethereum app's version, will fail prior to v1.0.2 + + // Try to resolve the Celo app's version if w.version, err = w.ledgerVersion(); err != nil { - w.version = [3]byte{1, 0, 0} // Assume worst case, can't verify if v1.0.0 or v1.0.1 + w.version = [3]byte{1, 0, 0} // Assume worst case + } + + /* + This is an example of how to enforce version numbers for features + + // Ensure the wallet is capable of signing the given transaction + if chainID != nil && w.version[0] <= 1 && w.version[1] == 0 && w.version[2] <= 2 { + return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2]) + } + */ + if w.tokens, err = ledger.LoadTokens(ledger.TokensBlob); err != nil { + return err } + return nil +} + +// ConfirmAddress implements usbwallet.driver, showing the address on the device. +func (w *ledgerDriver) ConfirmAddress(path accounts.DerivationPath) (common.Address, error) { + return w.ledgerDerive(path, true) }   // Close implements usbwallet.driver, cleaning up and metadata maintained within @@ -147,31 +183,39 @@ return nil }   // Derive implements usbwallet.driver, sending a derivation request to the Ledger -// and returning the Ethereum address located on that derivation path. +// and returning the Celo address located on that derivation path. func (w *ledgerDriver) Derive(path accounts.DerivationPath) (common.Address, error) { - return w.ledgerDerive(path) + return w.ledgerDerive(path, false) }   // SignTx implements usbwallet.driver, sending the transaction to the Ledger and // waiting for the user to confirm or deny the transaction. // -// Note, if the version of the Ethereum application running on the Ledger wallet is +// Note, if the version of the Celo application running on the Ledger wallet is // too old to sign EIP-155 transactions, but such is requested nonetheless, an error // will be returned opposed to silently signing in Homestead mode. func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) { - // If the Ethereum app doesn't run, abort + // If the Celo app doesn't run, abort if w.offline() { return common.Address{}, nil, accounts.ErrWalletClosed } - // Ensure the wallet is capable of signing the given transaction - if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 { - //lint:ignore ST1005 brand name displayed on the console - return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2]) + // All infos gathered and metadata checks out, request signing + return w.ledgerSign(path, tx, chainID) +} + +// SignPersonalMessage implements usbwallet.driver, sending the message to the Ledger and +// waiting for the user to confirm or deny the message. +func (w *ledgerDriver) SignPersonalMessage(path accounts.DerivationPath, message []byte) (common.Address, []byte, []byte, error) { + // If the Celo app doesn't run, abort + if w.offline() { + return common.Address{}, nil, nil, accounts.ErrWalletClosed } + // All infos gathered and metadata checks out, request signing - return w.ledgerSign(path, tx, chainID) + return w.ledgerSignData(path, message) }   +/* TODO: add functionality to the Ledger's Celo app // SignTypedMessage implements usbwallet.driver, sending the message to the Ledger and // waiting for the user to sign or deny the transaction. // @@ -189,6 +233,10 @@ } // All infos gathered and metadata checks out, request signing return w.ledgerSignTypedMessage(path, domainHash, messageHash) } +*/ +func (w *ledgerDriver) SignTypedMessage(path accounts.DerivationPath, domainHash []byte, messageHash []byte) ([]byte, error) { + return nil, accounts.ErrNotSupported +}   // ledgerVersion retrieves the current version of the Ethereum wallet app running // on the Ledger wallet. @@ -222,7 +270,7 @@ copy(version[:], reply[1:]) return version, nil }   -// ledgerDerive retrieves the currently active Ethereum address from a Ledger +// ledgerDerive retrieves the currently active Celo address from a Ledger // wallet at the specified derivation path. // // The address derivation protocol is defined as follows: @@ -250,10 +298,10 @@ // Description | Length // ------------------------+------------------- // Public Key length | 1 byte // Uncompressed Public Key | arbitrary -// Ethereum address length | 1 byte -// Ethereum address | 40 bytes hex ascii +// Celo address length | 1 byte +// Celo address | 40 bytes hex ascii // Chain code if requested | 32 bytes -func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, error) { +func (w *ledgerDriver) ledgerDerive(derivationPath []uint32, showOnWallet bool) (common.Address, error) { // Flatten the derivation path into the Ledger request path := make([]byte, 1+4*len(derivationPath)) path[0] = byte(len(derivationPath)) @@ -261,7 +309,11 @@ for i, component := range derivationPath { binary.BigEndian.PutUint32(path[1+4*i:], component) } // Send the request and wait for the response - reply, err := w.ledgerExchange(ledgerOpRetrieveAddress, ledgerP1DirectlyFetchAddress, ledgerP2DiscardAddressChainCode, path) + p1 := ledgerP1DirectlyFetchAddress + if showOnWallet { + p1 = ledgerP1ShowFetchAddress + } + reply, err := w.ledgerExchange(ledgerOpRetrieveAddress, p1, ledgerP2DiscardAddressChainCode, path) if err != nil { return common.Address{}, err } @@ -271,13 +323,13 @@ return common.Address{}, errors.New("reply lacks public key entry") } reply = reply[1+int(reply[0]):]   - // Extract the Ethereum hex address string + // Extract the Celo hex address string if len(reply) < 1 || len(reply) < 1+int(reply[0]) { return common.Address{}, errors.New("reply lacks address entry") } hexstr := reply[1 : 1+int(reply[0])]   - // Decode the hex sting into an Ethereum address and return + // Decode the hex sting into an Celo address and return var address common.Address if _, err = hex.Decode(address[:], hexstr); err != nil { return common.Address{}, err @@ -326,17 +378,34 @@ path[0] = byte(len(derivationPath)) for i, component := range derivationPath { binary.BigEndian.PutUint32(path[1+4*i:], component) } - // Create the transaction RLP based on whether legacy or EIP155 signing was requested + var ( txrlp []byte err error ) + + if ledger.IsERC20Transfer(tx.Data()) { + err = w.ledgerProvideERC20(*tx.To(), chainID) + if err != nil && err != ledger.ErrCouldNotFindToken { + return common.Address{}, nil, err + } + + if tx.FeeCurrency() != nil { + err = w.ledgerProvideERC20(*tx.FeeCurrency(), chainID) + if err != nil && err != ledger.ErrCouldNotFindToken { + return common.Address{}, nil, err + } + } + } + + // Create the transaction RLP based on whether legacy or EIP155 signing was requested if chainID == nil { - if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data()}); err != nil { + //if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data()}); err != nil { + if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.FeeCurrency(), tx.GatewayFeeRecipient(), tx.GatewayFee(), tx.To(), tx.Value(), tx.Data()}); err != nil { return common.Address{}, nil, err } } else { - if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), chainID, big.NewInt(0), big.NewInt(0)}); err != nil { + if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.FeeCurrency(), tx.GatewayFeeRecipient(), tx.GatewayFee(), tx.To(), tx.Value(), tx.Data(), chainID, big.NewInt(0), big.NewInt(0)}); err != nil { return common.Address{}, nil, err } } @@ -362,7 +431,7 @@ // Shift the payload and ensure subsequent chunks are marked as such payload = payload[chunk:] op = ledgerP1ContTransactionData } - // Extract the Ethereum signature and do a sanity validation + // Extract the Celo signature and do a sanity validation if len(reply) != crypto.SignatureLength { return common.Address{}, nil, errors.New("reply lacks signature") } @@ -387,6 +456,134 @@ } return sender, signed, nil }   +// ledgerProvideERC20 provides ERC20 information for tokens. +// +// The data protocol is defined as follows: +// +// CLA | INS | P1 | P2 | Lc | Le +// ----+-----+----+----+-----+--- +// E0 | 04 | 00: first data block +// 80: subsequent data block +// | 00 | variable | variable +// +// Where the input for the data block is: +// +// Description | Length +// -------------------------------------------------+---------- +// Ticker length | 1 byte +// Ticker | abitrary (< 10 bytes) +// Address | 20 bytes +// Decimals | 4 bytes +// ChainID | 4 bytes +// Signature | arbitrary +// +// And the output data is nothing. +func (w *ledgerDriver) ledgerProvideERC20(contractAddress common.Address, chainID *big.Int) error { + token, err := w.tokens.ByContractAddressAndChainID(contractAddress, chainID) + if err != nil { + return err + } + + _, err = w.ledgerExchange(ledgerOpProvideERC20, 0, 0, token.Data) + if err != nil { + return err + } + + return nil +} + +// ledgerSignData sends the message to the Ledger wallet, and waits for the user +// to confirm or deny the message. +// +// The message signing protocol is defined as follows: +// +// CLA | INS | P1 | P2 | Lc | Le +// ----+-----+----+----+-----+--- +// E0 | 08 | 00: first message data block +// 80: subsequent message data block +// | 00 | variable | variable +// +// Where the input for the first message block (first 255 bytes) is: +// +// Description | Length +// -------------------------------------------------+---------- +// Number of BIP 32 derivations to perform (max 10) | 1 byte +// First derivation index (big endian) | 4 bytes +// ... | 4 bytes +// Last derivation index (big endian) | 4 bytes +// data chunk | arbitrary +// +// And the input for subsequent message blocks (first 255 bytes) are: +// +// Description | Length +// ----------------------+---------- +// data chunk | arbitrary +// +// And the output data is: +// +// Description | Length +// ------------+--------- +// signature V | 1 byte +// signature R | 32 bytes +// signature S | 32 bytes +func (w *ledgerDriver) ledgerSignData(derivationPath []uint32, data []byte) (common.Address, []byte, []byte, error) { + hashMessage := sha256.Sum256(data) + w.log.Info("Signing message on Ledger with hash", "message", hex.EncodeToString(data), "hash", hex.EncodeToString(hashMessage[:])) + + // Flatten the derivation path into the Ledger request + path := make([]byte, 1+4*len(derivationPath)) + path[0] = byte(len(derivationPath)) + for i, component := range derivationPath { + binary.BigEndian.PutUint32(path[1+4*i:], component) + } + + dataLen := make([]byte, 4) + binary.BigEndian.PutUint32(dataLen, uint32(len(data))) + payload := append(path, dataLen...) + payload = append(payload, data...) + + // Send the request and wait for the response + var ( + op = ledgerP1InitTransactionData + reply []byte + err error + ) + for len(payload) > 0 { + // Calculate the size of the next data chunk + chunk := 255 + if chunk > len(payload) { + chunk = len(payload) + } + // Send the chunk over, ensuring it's processed correctly + reply, err = w.ledgerExchange(ledgerOpSignMessage, op, 0, payload[:chunk]) + if err != nil { + return common.Address{}, nil, nil, err + } + // Shift the payload and ensure subsequent chunks are marked as such + payload = payload[chunk:] + op = ledgerP1ContTransactionData + } + // Extract the Celo signature and do a sanity validation + if len(reply) != crypto.SignatureLength { + return common.Address{}, nil, nil, errors.New("reply lacks signature") + } + signature := append(reply[1:], reply[0]) + + msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) + hash := crypto.Keccak256([]byte(msg)) + hashFixedLength := common.Hash{} + copy(hashFixedLength[:], hash) + + signer := new(types.HomesteadSigner) + addr, pubkey, err := signer.SenderData(hashFixedLength, signature) + if err != nil { + return common.Address{}, nil, nil, err + } + + return addr, pubkey, signature, nil +} + +/* TODO: add functionality to the Ledger's Celo app // ledgerSignTypedMessage sends the transaction to the Ledger wallet, and waits for the user // to confirm or deny the transaction. // @@ -448,6 +645,7 @@ } signature := append(reply[1:], reply[0]) return signature, nil } +*/   // ledgerExchange performs a data exchange with the Ledger wallet, sending it a // message and retrieving the response. @@ -543,6 +741,12 @@ } else { reply = append(reply, payload[:left]...) break } + } + + statusCodeBytes := reply[len(reply)-2:] + statusCode := int(binary.BigEndian.Uint16(statusCodeBytes)) + if statusCode != statusCodeOK { + return nil, errLedgerBadStatusCode } return reply[:len(reply)-2], nil }
diff --git go-ethereum/accounts/usbwallet/wallet.go celo/accounts/usbwallet/wallet.go index 86a9390a4694f83866d0272091977b1ec3b4b8f1..edc369eeda7231ac25e5d0e7aa56da12137ada41 100644 --- go-ethereum/accounts/usbwallet/wallet.go +++ celo/accounts/usbwallet/wallet.go @@ -19,11 +19,14 @@ package usbwallet   import ( "context" + "crypto/ecdsa" "fmt" "io" "math/big" "sync" "time" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls"   "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" @@ -53,6 +56,9 @@ // Open initializes access to a wallet instance. The passphrase parameter may // or may not be used by the implementation of a particular wallet instance. Open(device io.ReadWriter, passphrase string) error   + // ConfirmAddress shows the address of the given path on the wallet's display. + ConfirmAddress(path accounts.DerivationPath) (common.Address, error) + // Close releases any resources held by an open wallet instance. Close() error   @@ -67,6 +73,10 @@ // SignTx sends the transaction to the USB device and waits for the user to confirm // or deny the transaction. SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) + + // SignPersonalMessage sends the message to the USB device and waits for the user to confirm + // or deny the message. + SignPersonalMessage(path accounts.DerivationPath, message []byte) (common.Address, []byte, []byte, error)   SignTypedMessage(path accounts.DerivationPath, messageHash []byte, domainHash []byte) ([]byte, error) } @@ -451,6 +461,10 @@ _, exists := w.paths[account.Address] return exists }   +func (w *wallet) Decrypt(a accounts.Account, c, s1, s2 []byte) ([]byte, error) { + return nil, accounts.ErrNotSupported +} + // Derive implements accounts.Wallet, deriving a new account at the specific // derivation path. If pin is set to true, the account will be added to the list // of tracked accounts. @@ -491,6 +505,29 @@ } return account, nil }   +// ConfirmAddress implements accounts.Wallet, showing the address on the device. +func (w *wallet) ConfirmAddress(path accounts.DerivationPath) (common.Address, error) { + // Try to derive the actual account and update its URL if successful + w.stateLock.RLock() // Avoid device disappearing during derivation + + if w.device == nil { + w.stateLock.RUnlock() + return common.Address{}, accounts.ErrWalletClosed + } + <-w.commsLock // Avoid concurrent hardware access + address, err := w.driver.ConfirmAddress(path) + w.commsLock <- struct{}{} + + w.stateLock.RUnlock() + + // If an error occurred or no pinning was requested, return + if err != nil { + return common.Address{}, err + } + + return address, nil +} + // SelfDerive sets a base account derivation path from which the wallet attempts // to discover non zero accounts and automatically add them to list of tracked // accounts. @@ -524,6 +561,62 @@ func (w *wallet) signHash(account accounts.Account, hash []byte) ([]byte, error) { return nil, accounts.ErrNotSupported }   +func (w *wallet) GetPublicKey(account accounts.Account) (*ecdsa.PublicKey, error) { + return nil, accounts.ErrNotSupported +} + +func (w *wallet) SignBLS(account accounts.Account, msg []byte, extraData []byte, useComposite, cip22 bool) (blscrypto.SerializedSignature, error) { + return blscrypto.SerializedSignature{}, accounts.ErrNotSupported +} + +func (w *wallet) GenerateProofOfPossession(account accounts.Account, address common.Address) ([]byte, []byte, error) { + hash := crypto.Keccak256(address.Bytes()) + return w.signText(account, hash) +} + +func (w *wallet) signText(account accounts.Account, text []byte) ([]byte, []byte, error) { + w.stateLock.RLock() // Comms have own mutex, this is for the state fields + defer w.stateLock.RUnlock() + + // If the wallet is closed, abort + if w.device == nil { + return nil, nil, accounts.ErrWalletClosed + } + // Make sure the requested account is contained within + path, ok := w.paths[account.Address] + if !ok { + return nil, nil, accounts.ErrUnknownAccount + } + // All infos gathered and metadata checks out, request signing + <-w.commsLock + defer func() { w.commsLock <- struct{}{} }() + + // Ensure the device isn't screwed with while user confirmation is pending + // TODO(karalabe): remove if hotplug lands on Windows + w.hub.commsLock.Lock() + w.hub.commsPend++ + w.hub.commsLock.Unlock() + + defer func() { + w.hub.commsLock.Lock() + w.hub.commsPend-- + w.hub.commsLock.Unlock() + }() + // Sign the message and verify the sender to avoid hardware fault surprises + sender, pubkey, signed, err := w.driver.SignPersonalMessage(path, text) + if err != nil { + return nil, nil, err + } + if sender != account.Address { + return nil, nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex()) + } + return pubkey, signed, nil +} + +func (w *wallet) GenerateProofOfPossessionBLS(account accounts.Account, address common.Address) ([]byte, []byte, error) { + return nil, nil, accounts.ErrNotSupported +} + // SignData signs keccak256(data). The mimetype parameter describes the type of data being signed func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {   @@ -568,6 +661,16 @@ } return signature, nil }   +// SignHash implements accounts.Wallet, attempting to sign the given hash with +// the given account. If the wallet does not wrap this particular account, an +// error is returned to avoid account leakage (even though in theory we may be +// able to sign via our shared keystore backend). +// +// DEPRECATED, use SignData in future releases. +func (w *wallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) { + return w.signHash(account, hash) +} + // SignDataWithPassphrase implements accounts.Wallet, attempting to sign the given // data with the given account using passphrase as extra authentication. // Since USB wallets don't rely on passphrases, these are silently ignored. @@ -576,7 +679,8 @@ return w.SignData(account, mimeType, data) }   func (w *wallet) SignText(account accounts.Account, text []byte) ([]byte, error) { - return w.signHash(account, accounts.TextHash(text)) + _, signature, err := w.signText(account, text) + return signature, err }   // SignTx implements accounts.Wallet. It sends the transaction over to the Ledger @@ -629,7 +733,7 @@ // SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary // data is not supported for Ledger wallets, so this method will always return // an error. func (w *wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) { - return w.SignText(account, accounts.TextHash(text)) + return w.SignText(account, text) }   // SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
diff --git go-ethereum/accounts/usbwallet/trezor.go celo/accounts/usbwallet/trezor.go index a0f971f875e5256523dc3528fee0733f5e21788a..817df3c904ed7fddbd75993fe1bd9cb85d52547b 100644 --- go-ethereum/accounts/usbwallet/trezor.go +++ celo/accounts/usbwallet/trezor.go @@ -153,6 +153,11 @@ return nil }   +// ConfirmAddress implements usbwallet.driver, showing the address on the device. +func (w *trezorDriver) ConfirmAddress(path accounts.DerivationPath) (common.Address, error) { + return common.Address{}, accounts.ErrNotSupported +} + // Close implements usbwallet.driver, cleaning up and metadata maintained within // the Trezor driver. func (w *trezorDriver) Close() error { @@ -183,6 +188,12 @@ if w.device == nil { return common.Address{}, nil, accounts.ErrWalletClosed } return w.trezorSign(path, tx, chainID) +} + +// SignPersonalMessage implements usbwallet.driver, sending the message to the Trezor and +// waiting for the user to confirm or deny the message. +func (w *trezorDriver) SignPersonalMessage(path accounts.DerivationPath, message []byte) (common.Address, []byte, []byte, error) { + return common.Address{}, nil, nil, accounts.ErrNotSupported }   func (w *trezorDriver) SignTypedMessage(path accounts.DerivationPath, domainHash []byte, messageHash []byte) ([]byte, error) {
diff --git go-ethereum/accounts/usbwallet/ledger/tokens_test.go celo/accounts/usbwallet/ledger/tokens_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c8d9eb2a9863b075faf68b470c566a8cc17c315f --- /dev/null +++ celo/accounts/usbwallet/ledger/tokens_test.go @@ -0,0 +1,29 @@ +package ledger + +import ( + "encoding/hex" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func TestCUSD(t *testing.T) { + tokens, _ := LoadTokens(TokensBlob) + cUSDAddress, _ := hex.DecodeString("765de816845861e75a25fca122bb6898b8b1282a") + var cUSDAddressFixedSize common.Address + copy(cUSDAddressFixedSize[:], cUSDAddress) + cUSDInfo, err := tokens.ByContractAddressAndChainID(cUSDAddressFixedSize, big.NewInt(42220)) + if err != nil { + t.Fatal("Could not find cUSD") + } + if cUSDInfo.Ticker != "cUSD" { + t.Fatalf("Ticker incorrect: %s", cUSDInfo.Ticker) + } + if cUSDInfo.Decimals != 18 { + t.Fatalf("Decimals incorrect: %d", cUSDInfo.Decimals) + } + if cUSDInfo.ChainID != 42220 { + t.Fatalf("ChainID incorrect: %d", cUSDInfo.ChainID) + } +}
diff --git go-ethereum/accounts/usbwallet/ledger/tokens_data.go celo/accounts/usbwallet/ledger/tokens_data.go new file mode 100644 index 0000000000000000000000000000000000000000..0d83eadb63c18d2fc6603278bed97d426d47f9a0 --- /dev/null +++ celo/accounts/usbwallet/ledger/tokens_data.go @@ -0,0 +1,3 @@ +package ledger + +const TokensBlob = "AAAAaARDRUxPRx7ON1DaI3+TuOM5xTaYm4l4pDgAAAASAACk7DBFAiEA5rECRg94+fCoIvoG9/5qWh62zl2C6Y+aFuuZrFe4CtcCIEJbRrkL3gqwT/Jj+7L3neazgpVCCTZZ3HX9JXXg5vleAAAAaARjVVNEdl3oFoRYYedaJfyhIrtomLixKCoAAAASAACk7DBFAiEApwQFHNBKXp+V2jq8BMD2y/5AwC9bhPQ2H4hT/vMl/B4CIFalOVtBFGREUKMU/F5vDlJLeQrTn6GQeDertpB2FpMvAAAAaARjRVVS2HY8uidqNzjm3oW0s79f3tbWynMAAAASAACk7DBFAiEAh2UeP1+SI2Ed5SiAjpJF6MkMrVa94gUwjJztyBlzhWMCIHfaOrEsxdxAGx+P+hxuSNO4zcw6KRLfJkkuic1V/CrHAAAAagZiIENFTE/dyb5X9VP+dXUtYWBrlMvX4CZO+AAAABIAAPNwMEUCIQCi62KsBfuNcfX0MriiRZ7a5DKERhtIz7sZ1SqBT7ruhgIgVrfmavyWzxzDW4AQeHn++A4qPjB1pQKoHvNXo8Hf1SMAAABpBmIgY1VTRGJJKmRKWI/ZBCcL7QatUrmr/qGuAAAAEgAA83AwRAIgGDYx4oB/gkYUqLeXqvEZXx9nOxVHzTe2ajyd2wnehxgCICQBe/rBPcXiaQJj3pdoXxroct/hV6r3G2G7y79EOEAPAAAAaQZiIGNFVVL57OMBJHrSziGJSUGDCiRw9Od0ygAAABIAAPNwMEQCIEdcFWP+HxEUoF1sCGVd34QGS0hL5cVUdrWdqVm3bYTgAiBCMA+Rg3Ubc3xla/35wzZesPlbeSMEPcr4uqL+8PeydwAAAGoGYSBDRUxP8ZSv31CwPmm9fQV8GqnhDJlU5MkAAAASAACu8zBFAiEAk/o0FBus2/QCrunFGEyoneQIRaMRC+y5L6Dvar8MU/kCIByJt2ziRhDG3AAbyXBIuJfZQujSHFcSJL3xF0xIlcPdAAAAaQZhIGNVU0SHQGn6HrFtRNYi8uDKJe6hcjabwQAAABIAAK7zMEQCIClrH2xgE3WMbD+hgQ7t5SiAcVG5WiUZ655voqCszKEoAiA/cO8UVgNY891MNJ5yeDk8w47WO0E1DQecrK71LR8g8gAAAGoGYSBjRVVSEMiSpuxDpT5F0LkWtLfTg7G3jA8AAAASAACu8zBFAiEAgpktbB1ZxyAwMJwKTSbZ30n8zgRuW0twbXoZxlsUAswCIHek4l4CIbjVMG2HVr0Ml9/8kA4F9dr69JBMaoSUkdKl" // #nosec
diff --git go-ethereum/accounts/usbwallet/ledger/tokens.go celo/accounts/usbwallet/ledger/tokens.go new file mode 100644 index 0000000000000000000000000000000000000000..e62d4aedd30d1c4a665209475f5f4e9c4f419f1d --- /dev/null +++ celo/accounts/usbwallet/ledger/tokens.go @@ -0,0 +1,101 @@ +package ledger + +import ( + "bytes" + "encoding/base64" + "encoding/binary" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +var ErrCouldNotFindToken = errors.New("could not find token") +var ErrNotAnERC20Transfer = errors.New("not an ERC20 transfer") + +type Token struct { + Ticker string + Address common.Address + Decimals uint32 + ChainID uint32 + Signature []byte + Data []byte +} + +type Tokens struct { + tokens []Token +} + +func LoadTokens(blob string) (*Tokens, error) { + buf, err := base64.StdEncoding.DecodeString(blob) + if err != nil { + return nil, err + } + + tokens := make([]Token, 0) + + i := 0 + for i < len(buf) { + length := int(binary.BigEndian.Uint32(buf[i : i+4])) + i += 4 + + item := buf[i : i+length] + offset := 0 + + tickerLength := int(item[offset]) + offset += 1 + + ticker := string(item[offset : offset+tickerLength]) + offset += tickerLength + + var address common.Address + copy(address[:], item[offset:offset+20]) + offset += 20 + + decimals := binary.BigEndian.Uint32(item[offset : offset+4]) + offset += 4 + + chainID := binary.BigEndian.Uint32(item[offset : offset+4]) + offset += 4 + + signature := item[offset:length] + + tokens = append(tokens, Token{ + Ticker: ticker, + Address: address, + Decimals: decimals, + ChainID: chainID, + Signature: signature, + Data: item, + }) + + i += length + } + + return &Tokens{ + tokens, + }, nil +} + +func (t *Tokens) ByContractAddressAndChainID(address common.Address, chainID *big.Int) (*Token, error) { + for _, token := range t.tokens { + if token.Address == address && uint64(token.ChainID) == chainID.Uint64() { + return &token, nil + } + } + + return nil, ErrCouldNotFindToken +} + +func IsERC20Transfer(data []byte) bool { + if len(data) < 4 { + return false + } + + // 0xa9059cbb is the ERC20 transfer function signature + if !bytes.Equal(data[:4], []byte{0xa9, 0x05, 0x9c, 0xbb}) { + return false + } + + return true +}
diff --git go-ethereum/accounts/abi/abi.go celo/accounts/abi/abi.go index efcaf3c30c5b445b6159a36213df44d937717dff..7f65daf1ffbb84cb8c73f82779ba9117b9189f16 100644 --- go-ethereum/accounts/abi/abi.go +++ celo/accounts/abi/abi.go @@ -42,6 +42,8 @@ Fallback Method // Note it's also used to represent legacy fallback before v0.6.0 Receive Method }   +var ErrEmptyArguments = errors.New("abi: attempting to unmarshall an empty string while arguments are expected") + // JSON returns a parsed ABI interface and error if it failed. func JSON(reader io.Reader) (ABI, error) { dec := json.NewDecoder(reader)
diff --git go-ethereum/accounts/abi/unpack.go celo/accounts/abi/unpack.go index 53609777d84987c48d71acd438ec1b8bbc5dbe74..86b6b10db30bd603edca1ec41c4b2ac8b0efe21d 100644 --- go-ethereum/accounts/abi/unpack.go +++ celo/accounts/abi/unpack.go @@ -188,6 +188,12 @@ } return retval.Interface(), nil }   +// ToGoType parses the output bytes and recursively assigns the value of these bytes +// into a go type with accordance with the ABI spec. +func ToGoType(index int, t Type, output []byte) (interface{}, error) { + return toGoType(index, t, output) +} + // toGoType parses the output bytes and recursively assigns the value of these bytes // into a go type with accordance with the ABI spec. func toGoType(index int, t Type, output []byte) (interface{}, error) {
diff --git go-ethereum/accounts/abi/argument.go celo/accounts/abi/argument.go index 1c1dd962eef3f4973d25a8a72368e45a188a8c2d..c3145b251a3f942d1bab9045b6fd953fe45505ac 100644 --- go-ethereum/accounts/abi/argument.go +++ celo/accounts/abi/argument.go @@ -79,7 +79,7 @@ // Unpack performs the operation hexdata -> Go format. func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { if len(data) == 0 { if len(arguments) != 0 { - return nil, fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") + return nil, ErrEmptyArguments } // Nothing to unmarshal, return default variables nonIndexedArgs := arguments.NonIndexed() @@ -100,7 +100,7 @@ return fmt.Errorf("abi: cannot unpack into a nil map") } if len(data) == 0 { if len(arguments) != 0 { - return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") + return ErrEmptyArguments } return nil // Nothing to unmarshal, return }
diff --git go-ethereum/accounts/abi/event_test.go celo/accounts/abi/event_test.go index ce9c4aeff983b9534059fe8ed9ae13591c10bd72..33419012195cbec89367bc4c104f82ee18b01db1 100644 --- go-ethereum/accounts/abi/event_test.go +++ celo/accounts/abi/event_test.go @@ -169,7 +169,7 @@ type EventTransferWithTag struct { // this is valid because `value` is not exportable, // so value is only unmarshalled into `Value1`. - value *big.Int //lint:ignore U1000 unused field is part of test + value *big.Int //nolint:unused // field is part of test Value1 *big.Int `abi:"value"` }
diff --git go-ethereum/accounts/abi/bind/base.go celo/accounts/abi/bind/base.go index d6fff9a695dfa54e3ac4d5331c8e88b1eaad7d7a..de7bc725231771855c6315dba25022796bc600ad 100644 --- go-ethereum/accounts/abi/bind/base.go +++ celo/accounts/abi/bind/base.go @@ -53,6 +53,9 @@ Signer SignerFn // Method to use for signing the transaction (mandatory)   Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds) GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) + FeeCurrency *common.Address // Fee currency to be used for transaction (nil = default currency = Celo Gold) + GatewayFeeRecipient *common.Address // Address to which gateway fees should be paid (nil = no gateway fees are paid) + GatewayFee *big.Int // Value of gateway fees to be paid (nil = no gateway fees are paid) GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle) GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle) GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate) @@ -140,6 +143,11 @@ return common.Address{}, nil, nil, err } c.address = crypto.CreateAddress(opts.From, tx.Nonce()) return c.address, tx, c, nil +} + +// ABI returns the bound contract parsed ABI +func (c *BoundContract) ABI() abi.ABI { + return c.abi }   // Call invokes the (constant) contract method with params as input values and @@ -254,40 +262,55 @@ // Figure out reasonable gas price values if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) { return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") } - head, err := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil) - if err != nil { - return nil, err + // head, err := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil) + // if err != nil { + // return nil, err + // } + // TODO: Use GPM here + // if head.BaseFee != nil && opts.GasPrice == nil { + // if opts.GasTipCap == nil { + // tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context)) + // if err != nil { + // return nil, err + // } + // opts.GasTipCap = tip + // } + // if opts.GasFeeCap == nil { + // gasFeeCap := new(big.Int).Add( + // opts.GasTipCap, + // new(big.Int).Mul(head.BaseFee, big.NewInt(2)), + // ) + // opts.GasFeeCap = gasFeeCap + // } + // if opts.GasFeeCap.Cmp(opts.GasTipCap) < 0 { + // return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", opts.GasFeeCap, opts.GasTipCap) + // } + // } else { + if opts.GasFeeCap != nil || opts.GasTipCap != nil { + return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") } - if head.BaseFee != nil && opts.GasPrice == nil { - if opts.GasTipCap == nil { - tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context)) - if err != nil { - return nil, err - } - opts.GasTipCap = tip - } - if opts.GasFeeCap == nil { - gasFeeCap := new(big.Int).Add( - opts.GasTipCap, - new(big.Int).Mul(head.BaseFee, big.NewInt(2)), - ) - opts.GasFeeCap = gasFeeCap - } - if opts.GasFeeCap.Cmp(opts.GasTipCap) < 0 { - return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", opts.GasFeeCap, opts.GasTipCap) - } - } else { - if opts.GasFeeCap != nil || opts.GasTipCap != nil { - return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") + if opts.GasPrice == nil { + price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context)) + if err != nil { + return nil, err } - if opts.GasPrice == nil { - price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context)) - if err != nil { - return nil, err - } - opts.GasPrice = price - } + opts.GasPrice = price } + // } + + feeCurrency := opts.FeeCurrency + // TODO(nategraf): Add SuggestFeeCurrency to Transactor to get fee currency + // Otherwise, the user might not be able to pay in non-native currency for contract + // deployment. Paying for Contract deployment in non-native currency might not work right now. + // Only paying for token transfer in non-native currency is supported. + //if feeCurrency == 0 { + // feeCurrency = c.transactor.SuggestFeeCurrency(opts.Context) + //} + + gatewayFeeRecipient := opts.GatewayFeeRecipient + gatewayFee := opts.GatewayFee + // TODO(nategraf): Add SuggestGatewayFee to Transactor. + gasLimit := opts.GasLimit if gasLimit == 0 { // Gas estimation cannot succeed without code for method invocations @@ -299,7 +322,18 @@ return nil, ErrNoCode } } // If the contract surely has code (or code is not needed), estimate the transaction - msg := ethereum.CallMsg{From: opts.From, To: contract, GasPrice: opts.GasPrice, GasTipCap: opts.GasTipCap, GasFeeCap: opts.GasFeeCap, Value: value, Data: input} + msg := ethereum.CallMsg{ + From: opts.From, + To: contract, + GasPrice: opts.GasPrice, + GasTipCap: opts.GasTipCap, + GasFeeCap: opts.GasFeeCap, + Value: value, + FeeCurrency: feeCurrency, + GatewayFeeRecipient: gatewayFeeRecipient, + GatewayFee: gatewayFee, + Data: input, + } gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) if err != nil { return nil, fmt.Errorf("failed to estimate gas needed: %v", err) @@ -313,6 +347,9 @@ Nonce: nonce, GasPrice: opts.GasPrice, Gas: gasLimit, Value: value, + FeeCurrency: feeCurrency, + GatewayFeeRecipient: gatewayFeeRecipient, + GatewayFee: gatewayFee, Data: input, } if contract != nil { @@ -347,6 +384,19 @@ if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { return nil, err } return signedTx, nil +} + +// LogEventName will verify the log belongs to the contract and return the eventName for it +func (c *BoundContract) LogEventName(log types.Log) (eventName string, ok bool, err error) { + if log.Address != c.address || len(log.Topics) < 1 { + return "", false, err + } + + event, err := c.abi.EventByID(log.Topics[0]) + if err != nil { + return "", false, err + } + return event.Name, true, nil }   // FilterLogs filters contract logs for past blocks, returning the necessary
diff --git go-ethereum/accounts/abi/bind/util_test.go celo/accounts/abi/bind/util_test.go index e97ad65a10642d8a02afa09cf86f23970ebc81e3..cd9edf19ca6dabab52a03846c56f59e7b477f66d 100644 --- go-ethereum/accounts/abi/bind/util_test.go +++ celo/accounts/abi/bind/util_test.go @@ -33,6 +33,8 @@ )   var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")   +var genericBaseFee = big.NewInt(2) // set a constant gas price to be above the GPM. + var waitDeployedTests = map[string]struct { code string gas uint64 @@ -55,18 +57,14 @@ func TestWaitDeployed(t *testing.T) { for name, test := range waitDeployedTests { backend := backends.NewSimulatedBackend( - core.GenesisAlloc{ - crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}, - }, - 10000000, - ) + core.GenesisAlloc{crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}}) defer backend.Close()   // Create the transaction - head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + // head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   - tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code)) + tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, nil, nil, nil, common.FromHex(test.code)) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)   // Wait for it to get mined in the background. @@ -101,19 +99,16 @@ }   func TestWaitDeployedCornerCases(t *testing.T) { backend := backends.NewSimulatedBackend( - core.GenesisAlloc{ - crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}, - }, - 10000000, - ) + core.GenesisAlloc{crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}}) defer backend.Close()   - head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + // head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   // Create a transaction to an account. code := "6060604052600a8060106000396000f360606040526008565b00" - tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code)) + tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, + gasPrice, nil, nil, nil, common.FromHex(code)) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -125,7 +120,7 @@ t.Errorf("error missmatch: want %q, got %q, ", notContentCreation, err) }   // Create a transaction that is not mined. - tx = types.NewContractCreation(1, big.NewInt(0), 3000000, gasPrice, common.FromHex(code)) + tx = types.NewContractCreation(1, big.NewInt(0), 3000000, gasPrice, nil, nil, nil, common.FromHex(code)) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)   go func() {
diff --git go-ethereum/accounts/abi/bind/template.go celo/accounts/abi/bind/template.go index 165c3b2d0e2479758b9dcee28619b5f471aab629..e14b27690f25f713e92869a246062208a757072e 100644 --- go-ethereum/accounts/abi/bind/template.go +++ celo/accounts/abi/bind/template.go @@ -275,6 +275,15 @@ } return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil }   + // Parse{{.Type}}ABI parses the ABI + func Parse{{.Type}}ABI() (*abi.ABI, error) { + parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI)) + if err != nil { + return nil, err + } + return &parsed, nil + } + // Call invokes the (constant) contract method with params as input values and // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named @@ -378,7 +387,25 @@ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}}) } {{end}}   - {{if .Fallback}} + // TryParseLog attempts to parse a log. Returns the parsed log, evenName and whether it was succesfull + func (_{{$contract.Type}} *{{$contract.Type}}Filterer) TryParseLog(log types.Log) (eventName string, event interface{}, ok bool, err error) { + eventName, ok, err = _{{$contract.Type}}.contract.LogEventName(log) + if err != nil || !ok { + return "", nil, false, err + } + + switch eventName { {{range .Events}} + case "{{.Normalized.Name}}": + event, err = _{{$contract.Type}}.Parse{{.Normalized.Name}}(log){{end}} + } + if err != nil { + return "", nil, false, err + } + + return eventName, event, ok, nil + } + + {{if .Fallback}} // Fallback is a paid mutator transaction binding the contract fallback function. // // Solidity: {{.Fallback.Original.String}} @@ -392,7 +419,7 @@ // Solidity: {{.Fallback.Original.String}} func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) { return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata) } - + // Fallback is a paid mutator transaction binding the contract fallback function. // // Solidity: {{.Fallback.Original.String}} @@ -401,7 +428,7 @@ return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata) } {{end}}   - {{if .Receive}} + {{if .Receive}} // Receive is a paid mutator transaction binding the contract receive function. // // Solidity: {{.Receive.Original.String}} @@ -415,7 +442,7 @@ // Solidity: {{.Receive.Original.String}} func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) { return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts) } - + // Receive is a paid mutator transaction binding the contract receive function. // // Solidity: {{.Receive.Original.String}}
diff --git go-ethereum/accounts/abi/bind/bind_test.go celo/accounts/abi/bind/bind_test.go index c3343531e3ffa8dde035134f615501acfca56339..03e4c0760e417b76e6e04ae9401527c3a1cf0b1d 100644 --- go-ethereum/accounts/abi/bind/bind_test.go +++ celo/accounts/abi/bind/bind_test.go @@ -298,7 +298,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // Deploy an interaction tester contract and call a transaction on it @@ -353,7 +353,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // Deploy a tuple tester contract and execute a structured call on it @@ -399,7 +399,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // Deploy a tuple tester contract and execute a structured call on it @@ -457,7 +457,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // Deploy a slice tester contract and execute a n array call on it @@ -505,7 +505,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // Deploy a default method invoker contract and execute its default method @@ -571,7 +571,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // Deploy a structs method invoker contract and execute its default method @@ -614,7 +614,7 @@ `, ` // Create a simulator and wrap a non-deployed contract   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{}, uint64(10000000000)) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{}) defer sim.Close()   nonexistent, err := NewNonExistent(common.Address{}, sim) @@ -653,7 +653,7 @@ `, ` // Create a simulator and wrap a non-deployed contract   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{}, uint64(10000000000)) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{},) defer sim.Close()   nonexistent, err := NewNonExistentStruct(common.Address{}, sim) @@ -703,7 +703,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // Deploy a funky gas pattern contract @@ -753,7 +753,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // Deploy a sender tester contract and execute a structured call on it @@ -828,7 +828,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // Deploy a underscorer tester contract and execute a structured call on it @@ -922,7 +922,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // Deploy an eventer contract @@ -1112,7 +1112,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   //deploy the test contract @@ -1247,7 +1247,7 @@ ` key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   _, _, contract, err := DeployTuple(auth, sim) @@ -1389,7 +1389,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   //deploy the test contract @@ -1454,7 +1454,7 @@ ` // Initialize test accounts key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // deploy the test contract @@ -1544,7 +1544,7 @@ key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey)   // Deploy registrar contract - sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) @@ -1606,7 +1606,7 @@ key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey)   // Deploy registrar contract - sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) @@ -1668,7 +1668,7 @@ // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   // Deploy a tester contract and execute a structured call on it @@ -1728,7 +1728,7 @@ ` key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey)   - sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 1000000) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}) defer sim.Close()   opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) @@ -1811,13 +1811,12 @@ "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/ethconfig" `, ` var ( key, _ = crypto.GenerateKey() user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil) + sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}) ) defer sim.Close()   @@ -1917,7 +1916,7 @@ if out, err := moder.CombinedOutput(); err != nil { t.Fatalf("failed to convert binding test to modules: %v\n%s", err, out) } pwd, _ := os.Getwd() - replacer := exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/ethereum/go-ethereum@v0.0.0", "-replace", "github.com/ethereum/go-ethereum="+filepath.Join(pwd, "..", "..", "..")) // Repo root + replacer := exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/ethereum/go-ethereum@v0.0.0", "-replace", "github.com/celo-org/celo-blockchain="+filepath.Join(pwd, "..", "..", "..")) // Repo root replacer.Dir = pkg if out, err := replacer.CombinedOutput(); err != nil { t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out)
diff --git go-ethereum/accounts/abi/bind/backends/simulated_test.go celo/accounts/abi/bind/backends/simulated_test.go index 1d17c1254db0e2698961174601813a03d98c6a72..6b84336a2edf90b894d48d489b82f4c457bbf9c1 100644 --- go-ethereum/accounts/abi/bind/backends/simulated_test.go +++ celo/accounts/abi/bind/backends/simulated_test.go @@ -37,14 +37,15 @@ "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" )   +var genericBaseFee = big.NewInt(2) // set a constant gas price to be above the GPM. + func TestSimulatedBackend(t *testing.T) { - var gasLimit uint64 = 8000029 key, _ := crypto.GenerateKey() // nolint: gosec auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) genAlloc := make(core.GenesisAlloc) genAlloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(9223372036854775807)}   - sim := NewSimulatedBackend(genAlloc, gasLimit) + sim := NewSimulatedBackend(genAlloc) defer sim.Close()   // should return an error if the tx is not found @@ -59,12 +60,11 @@ t.Fatalf("err should be `ethereum.NotFound` but received %v", err) }   // generate a transaction and confirm you can retrieve it - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   code := `6060604052600a8060106000396000f360606040526008565b00` var gas uint64 = 3000000 - tx := types.NewContractCreation(0, big.NewInt(0), gas, gasPrice, common.FromHex(code)) + tx := types.NewContractCreation(0, big.NewInt(0), gas, gasPrice, nil, nil, nil, common.FromHex(code)) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key)   err = sim.SendTransaction(context.Background(), tx) @@ -115,7 +115,7 @@ func simTestBackend(testAddr common.Address) *SimulatedBackend { return NewSimulatedBackend( core.GenesisAlloc{ testAddr: {Balance: big.NewInt(10000000000000000)}, - }, 10000000, + }, ) }   @@ -125,12 +125,12 @@ expectedBal := big.NewInt(10000000000000000) sim := simTestBackend(testAddr) defer sim.Close()   - if sim.config != params.AllEthashProtocolChanges { - t.Errorf("expected sim config to equal params.AllEthashProtocolChanges, got %v", sim.config) + if sim.config != params.IstanbulTestChainConfig { + t.Errorf("expected sim config to equal params.IstanbulTestChainConfig, got %v", sim.config) }   - if sim.blockchain.Config() != params.AllEthashProtocolChanges { - t.Errorf("expected sim blockchain config to equal params.AllEthashProtocolChanges, got %v", sim.config) + if sim.blockchain.Config() != params.IstanbulTestChainConfig { + t.Errorf("expected sim blockchain config to equal params.IstanbulTestChainConfig, got %v", sim.config) }   stateDB, _ := sim.blockchain.State() @@ -141,9 +141,7 @@ } }   func TestAdjustTime(t *testing.T) { - sim := NewSimulatedBackend( - core.GenesisAlloc{}, 10000000, - ) + sim := NewSimulatedBackend(core.GenesisAlloc{}) defer sim.Close()   prevTime := sim.pendingBlock.Time() @@ -162,10 +160,9 @@ testAddr := crypto.PubkeyToAddress(testKey.PublicKey) sim := simTestBackend(testAddr)   // Create tx and send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   - tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) if err != nil { t.Errorf("could not sign tx: %v", err) @@ -186,7 +183,7 @@ if newTime-prevTime != uint64(time.Minute.Seconds()) { t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime) } // Put a transaction after adjusting time - tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil) signedTx2, err := types.SignTx(tx2, types.HomesteadSigner{}, testKey) if err != nil { t.Errorf("could not sign tx: %v", err) @@ -217,9 +214,7 @@ } }   func TestBlockByHash(t *testing.T) { - sim := NewSimulatedBackend( - core.GenesisAlloc{}, 10000000, - ) + sim := NewSimulatedBackend(core.GenesisAlloc{}) defer sim.Close() bgCtx := context.Background()   @@ -238,9 +233,7 @@ } }   func TestBlockByNumber(t *testing.T) { - sim := NewSimulatedBackend( - core.GenesisAlloc{}, 10000000, - ) + sim := NewSimulatedBackend(core.GenesisAlloc{}) defer sim.Close() bgCtx := context.Background()   @@ -289,10 +282,10 @@ t.Errorf("received incorrect nonce. expected 0, got %v", nonce) }   // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + // head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   - tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) if err != nil { t.Errorf("could not sign tx: %v", err) @@ -333,10 +326,10 @@ defer sim.Close() bgCtx := context.Background()   // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + // head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) if err != nil { t.Errorf("could not sign tx: %v", err) @@ -365,16 +358,16 @@ sim := NewSimulatedBackend( core.GenesisAlloc{ testAddr: {Balance: big.NewInt(10000000000000000)}, - }, 10000000, + }, ) defer sim.Close() bgCtx := context.Background()   // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + // head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) if err != nil { t.Errorf("could not sign tx: %v", err) @@ -430,7 +423,7 @@ key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey) opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))   - sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}, 10000000) + sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}) defer sim.Close()   parsed, _ := abi.JSON(strings.NewReader(contractAbi)) @@ -531,96 +524,6 @@ } } }   -func TestEstimateGasWithPrice(t *testing.T) { - key, _ := crypto.GenerateKey() - addr := crypto.PubkeyToAddress(key.PublicKey) - - sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether*2 + 2e17)}}, 10000000) - defer sim.Close() - - recipient := common.HexToAddress("deadbeef") - var cases = []struct { - name string - message ethereum.CallMsg - expect uint64 - expectError error - }{ - {"EstimateWithoutPrice", ethereum.CallMsg{ - From: addr, - To: &recipient, - Gas: 0, - GasPrice: big.NewInt(0), - Value: big.NewInt(100000000000), - Data: nil, - }, 21000, nil}, - - {"EstimateWithPrice", ethereum.CallMsg{ - From: addr, - To: &recipient, - Gas: 0, - GasPrice: big.NewInt(100000000000), - Value: big.NewInt(100000000000), - Data: nil, - }, 21000, nil}, - - {"EstimateWithVeryHighPrice", ethereum.CallMsg{ - From: addr, - To: &recipient, - Gas: 0, - GasPrice: big.NewInt(1e14), // gascost = 2.1ether - Value: big.NewInt(1e17), // the remaining balance for fee is 2.1ether - Data: nil, - }, 21000, nil}, - - {"EstimateWithSuperhighPrice", ethereum.CallMsg{ - From: addr, - To: &recipient, - Gas: 0, - GasPrice: big.NewInt(2e14), // gascost = 4.2ether - Value: big.NewInt(100000000000), - Data: nil, - }, 21000, errors.New("gas required exceeds allowance (10999)")}, // 10999=(2.2ether-1000wei)/(2e14) - - {"EstimateEIP1559WithHighFees", ethereum.CallMsg{ - From: addr, - To: &addr, - Gas: 0, - GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether - GasTipCap: big.NewInt(1), - Value: big.NewInt(1e17), // the remaining balance for fee is 2.1ether - Data: nil, - }, params.TxGas, nil}, - - {"EstimateEIP1559WithSuperHighFees", ethereum.CallMsg{ - From: addr, - To: &addr, - Gas: 0, - GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether - GasTipCap: big.NewInt(1), - Value: big.NewInt(1e17 + 1), // the remaining balance for fee is 2.1ether - Data: nil, - }, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14) - } - for i, c := range cases { - got, err := sim.EstimateGas(context.Background(), c.message) - if c.expectError != nil { - if err == nil { - t.Fatalf("test %d: expect error, got nil", i) - } - if c.expectError.Error() != err.Error() { - t.Fatalf("test %d: expect error, want %v, got %v", i, c.expectError, err) - } - continue - } - if c.expectError == nil && err != nil { - t.Fatalf("test %d: didn't expect error, got %v", i, err) - } - if got != c.expect { - t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got) - } - } -} - func TestHeaderByHash(t *testing.T) { testAddr := crypto.PubkeyToAddress(testKey.PublicKey)   @@ -709,10 +612,10 @@ if count != 0 { t.Errorf("expected transaction count of %v does not match actual count of %v", 0, count) } // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + // head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) if err != nil { t.Errorf("could not sign tx: %v", err) @@ -766,10 +669,10 @@ if pendingNonce != uint64(0) { t.Errorf("expected pending nonce of 0 got %v", pendingNonce) } // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + // head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) if err != nil { t.Errorf("could not sign tx: %v", err) @@ -824,10 +727,10 @@ t.Errorf("expected pending nonce of 0 got %v", pendingNonce) }   // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + // head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) if err != nil { t.Errorf("could not sign tx: %v", err) @@ -850,7 +753,7 @@ t.Errorf("expected pending nonce of 1 got %v", pendingNonce) }   // make a new transaction with a nonce of 1 - tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil) signedTx, err = types.SignTx(tx, types.HomesteadSigner{}, testKey) if err != nil { t.Errorf("could not sign tx: %v", err) @@ -879,10 +782,10 @@ defer sim.Close() bgCtx := context.Background()   // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + //// head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) if err != nil { t.Errorf("could not sign tx: %v", err) @@ -906,10 +809,7 @@ } }   func TestSuggestGasPrice(t *testing.T) { - sim := NewSimulatedBackend( - core.GenesisAlloc{}, - 10000000, - ) + sim := NewSimulatedBackend(core.GenesisAlloc{}) defer sim.Close() bgCtx := context.Background() gasPrice, err := sim.SuggestGasPrice(bgCtx) @@ -1308,10 +1208,10 @@ defer sim.Close() // 1. parent := sim.blockchain.CurrentBlock() // 2. - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + // head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1))   - _tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + _tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil) tx, _ := types.SignTx(_tx, types.HomesteadSigner{}, testKey) sim.SendTransaction(context.Background(), tx) sim.Commit()
diff --git go-ethereum/accounts/abi/bind/backends/simulated.go celo/accounts/abi/bind/backends/simulated.go index 9af21b95741bb0d67e7576b541d6ee5b17bbfbe0..c717f8fcdbfa214eae7aa5be2651ea25a8247e4c 100644 --- go-ethereum/accounts/abi/bind/backends/simulated.go +++ celo/accounts/abi/bind/backends/simulated.go @@ -30,7 +30,8 @@ "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" @@ -40,7 +41,6 @@ "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -75,10 +75,10 @@ // NewSimulatedBackendWithDatabase creates a new binding backend based on the given database // and uses a simulated blockchain for testing purposes. // A simulated backend always uses chainID 1337. -func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { - genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc} +func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc) *SimulatedBackend { + genesis := core.Genesis{Config: params.IstanbulTestChainConfig, Alloc: alloc} genesis.MustCommit(database) - blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil)   backend := &SimulatedBackend{ database: database, @@ -93,8 +93,8 @@ // NewSimulatedBackend creates a new binding backend using a simulated blockchain // for testing purposes. // A simulated backend always uses chainID 1337. -func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { - return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit) +func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend { + return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc) }   // Close terminates the underlying blockchain's update loop. @@ -126,7 +126,7 @@ b.rollback(b.blockchain.CurrentBlock()) }   func (b *SimulatedBackend) rollback(parent *types.Block) { - blocks, _ := core.GenerateChain(b.config, parent, ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {}) + blocks, _ := core.GenerateChain(b.config, parent, mockEngine.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})   b.pendingBlock = blocks[0] b.pendingState, _ = state.New(b.pendingBlock.Root(), b.blockchain.StateCache(), nil) @@ -471,6 +471,18 @@ func (b *SimulatedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { return big.NewInt(1), nil }   +// CurrentGasPriceMinimum implements ContractTransactor.CurrentGasPriceMinimum. Since the simulated +// chain doesn't have miners, we just return a gas tip of 1 for any call. +func (b *SimulatedBackend) CurrentGasPriceMinimum(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) { + return big.NewInt(1), nil +} + +// GasPriceMinimumForHeader implements ContractTransactor.GasPriceMinimumForHeader. Since the simulated +// chain doesn't have miners, we just return a gas tip of 1 for any call. +func (b *SimulatedBackend) GasPriceMinimumForHeader(ctx context.Context, currencyAddress *common.Address, header *types.Header) (*big.Int, error) { + return big.NewInt(1), nil +} + // EstimateGas executes the requested code against the currently pending block/state and // returns the used amount of gas. func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) { @@ -486,39 +498,8 @@ ) if call.Gas >= params.TxGas { hi = call.Gas } else { - hi = b.pendingBlock.GasLimit() - } - // Normalize the max fee per gas the call is willing to spend. - var feeCap *big.Int - if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) { - return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") - } else if call.GasPrice != nil { - feeCap = call.GasPrice - } else if call.GasFeeCap != nil { - feeCap = call.GasFeeCap - } else { - feeCap = common.Big0 - } - // Recap the highest gas allowance with account's balance. - if feeCap.BitLen() != 0 { - balance := b.pendingState.GetBalance(call.From) // from can't be nil - available := new(big.Int).Set(balance) - if call.Value != nil { - if call.Value.Cmp(available) >= 0 { - return 0, errors.New("insufficient funds for transfer") - } - available.Sub(available, call.Value) - } - allowance := new(big.Int).Div(available, feeCap) - if allowance.IsUint64() && hi > allowance.Uint64() { - transfer := call.Value - if transfer == nil { - transfer = new(big.Int) - } - log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance, - "sent", transfer, "feecap", feeCap, "fundable", allowance) - hi = allowance.Uint64() - } + vmRunner := b.blockchain.NewEVMRunner(b.pendingBlock.Header(), b.pendingState) + hi = blockchain_parameters.GetBlockGasLimitOrDefault(vmRunner) } cap = hi   @@ -583,7 +564,7 @@ if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) { return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") } head := b.blockchain.CurrentHeader() - if !b.blockchain.Config().IsLondon(head.Number) { + if !b.blockchain.Config().IsEspresso(head.Number) { // If there's no basefee, then it must be a non-1559 execution if call.GasPrice == nil { call.GasPrice = new(big.Int) @@ -605,7 +586,11 @@ } // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes call.GasPrice = new(big.Int) if call.GasFeeCap.BitLen() > 0 || call.GasTipCap.BitLen() > 0 { - call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, head.BaseFee), call.GasFeeCap) + gasPriceMinimum, err := b.CurrentGasPriceMinimum(ctx, call.FeeCurrency) + if err != nil { + return nil, err + } + call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, gasPriceMinimum), call.GasFeeCap) } } } @@ -622,14 +607,23 @@ from.SetBalance(math.MaxBig256) // Execute the call. msg := callMsg{call}   - txContext := core.NewEVMTxContext(msg) - evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. + txContext := core.NewEVMTxContext(msg) + evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil) vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true}) gasPool := new(core.GasPool).AddGas(math.MaxUint64) - - return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb() + vmRunner := b.blockchain.NewEVMRunner(block.Header(), stateDB) + var sysCtx *core.SysContractCallCtx + if b.config.IsEspresso(block.Number()) { + parent := b.blockchain.GetBlockByNumber(block.NumberU64() - 1) + sysStateDB, err := b.blockchain.StateAt(parent.Root()) + if err != nil { + return nil, err + } + sysCtx = core.NewSysContractCallCtx(block.Header(), sysStateDB, b.blockchain) + } + return core.NewStateTransition(vmEnv, msg, gasPool, vmRunner, sysCtx).TransitionDb() }   // SendTransaction updates the pending block to include the given transaction. @@ -654,7 +648,7 @@ if tx.Nonce() != nonce { panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)) } // Include tx in chain - blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) { + blocks, _ := core.GenerateChain(b.config, block, mockEngine.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) { for _, tx := range b.pendingBlock.Transactions() { block.AddTxWithChain(b.blockchain, tx) } @@ -769,10 +763,10 @@ b.mu.Lock() defer b.mu.Unlock()   if len(b.pendingBlock.Transactions()) != 0 { - return errors.New("Could not adjust time on non-empty block") + return errors.New("could not adjust time on non-empty block") }   - blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) { + blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), mockEngine.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) { block.OffsetTime(int64(adjustment.Seconds())) }) stateDB, _ := b.blockchain.State() @@ -800,9 +794,13 @@ func (m callMsg) To() *common.Address { return m.CallMsg.To } func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap } func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap } +func (m callMsg) FeeCurrency() *common.Address { return m.CallMsg.FeeCurrency } +func (m callMsg) GatewayFeeRecipient() *common.Address { return m.CallMsg.GatewayFeeRecipient } +func (m callMsg) GatewayFee() *big.Int { return m.CallMsg.GatewayFee } func (m callMsg) Gas() uint64 { return m.CallMsg.Gas } func (m callMsg) Value() *big.Int { return m.CallMsg.Value } func (m callMsg) Data() []byte { return m.CallMsg.Data } +func (m callMsg) EthCompatible() bool { return m.CallMsg.EthCompatible } func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }   // filterBackend implements filters.Backend to support filtering for logs without @@ -874,6 +872,10 @@ func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }   func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) { panic("not supported") +} + +func (fb *filterBackend) RealGasPriceMinimumForHeader(ctx context.Context, currencyAddress *common.Address, header *types.Header) (*big.Int, error) { + return nil, fmt.Errorf("filterBackend does not implement RealGasPriceMinimumForHeader") }   func nullSubscription() event.Subscription {
diff --git go-ethereum/accounts/abi/bind_v2/base_test.go celo/accounts/abi/bind_v2/base_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5960c702a07e7b00b3890be12530a0484e5e0d88 --- /dev/null +++ celo/accounts/abi/bind_v2/base_test.go @@ -0,0 +1,258 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package bind_v2 + +import ( + "context" + "math/big" + "reflect" + "strings" + "testing" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" +) + +type mockCaller struct { + codeAtBlockNumber *big.Int + callContractBlockNumber *big.Int + pendingCodeAtCalled bool + pendingCallContractCalled bool +} + +func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + mc.codeAtBlockNumber = blockNumber + return []byte{1, 2, 3}, nil +} + +func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + mc.callContractBlockNumber = blockNumber + return nil, nil +} + +func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { + mc.pendingCodeAtCalled = true + return nil, nil +} + +func (mc *mockCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { + mc.pendingCallContractCalled = true + return nil, nil +} +func TestPassingBlockNumber(t *testing.T) { + + mc := &mockCaller{} + + bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{ + Methods: map[string]abi.Method{ + "something": { + Name: "something", + Outputs: abi.Arguments{}, + }, + }, + }, mc, nil, nil) + var ret []interface{} + + blockNumber := big.NewInt(42) + + bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, &ret, "something") + + if mc.callContractBlockNumber != blockNumber { + t.Fatalf("CallContract() was not passed the block number") + } + + if mc.codeAtBlockNumber != blockNumber { + t.Fatalf("CodeAt() was not passed the block number") + } + + bc.Call(&bind.CallOpts{}, &ret, "something") + + if mc.callContractBlockNumber != nil { + t.Fatalf("CallContract() was passed a block number when it should not have been") + } + + if mc.codeAtBlockNumber != nil { + t.Fatalf("CodeAt() was passed a block number when it should not have been") + } + + bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, &ret, "something") + + if !mc.pendingCallContractCalled { + t.Fatalf("CallContract() was not passed the block number") + } + + if !mc.pendingCodeAtCalled { + t.Fatalf("CodeAt() was not passed the block number") + } +} + +const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158" + +func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) { + hash := crypto.Keccak256Hash([]byte("testName")) + topics := []common.Hash{ + crypto.Keccak256Hash([]byte("received(string,address,uint256,bytes)")), + hash, + } + mockLog := newMockLog(topics, common.HexToHash("0x0")) + + abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]` + parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) + bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) + + expectedReceivedMap := map[string]interface{}{ + "name": hash, + "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), + "amount": big.NewInt(1), + "memo": []byte{88}, + } + unpackAndCheck(t, bc, expectedReceivedMap, mockLog) +} + +func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) { + sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"}) + if err != nil { + t.Fatal(err) + } + hash := crypto.Keccak256Hash(sliceBytes) + topics := []common.Hash{ + crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")), + hash, + } + mockLog := newMockLog(topics, common.HexToHash("0x0")) + + abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"names","type":"string[]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]` + parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) + bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) + + expectedReceivedMap := map[string]interface{}{ + "names": hash, + "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), + "amount": big.NewInt(1), + "memo": []byte{88}, + } + unpackAndCheck(t, bc, expectedReceivedMap, mockLog) +} + +func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) { + arrBytes, err := rlp.EncodeToBytes([2]common.Address{common.HexToAddress("0x0"), common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")}) + if err != nil { + t.Fatal(err) + } + hash := crypto.Keccak256Hash(arrBytes) + topics := []common.Hash{ + crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")), + hash, + } + mockLog := newMockLog(topics, common.HexToHash("0x0")) + + abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"addresses","type":"address[2]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]` + parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) + bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) + + expectedReceivedMap := map[string]interface{}{ + "addresses": hash, + "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), + "amount": big.NewInt(1), + "memo": []byte{88}, + } + unpackAndCheck(t, bc, expectedReceivedMap, mockLog) +} + +func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) { + mockAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2") + addrBytes := mockAddress.Bytes() + hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)")) + functionSelector := hash[:4] + functionTyBytes := append(addrBytes, functionSelector...) + var functionTy [24]byte + copy(functionTy[:], functionTyBytes[0:24]) + topics := []common.Hash{ + crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")), + common.BytesToHash(functionTyBytes), + } + mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42")) + abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"function","type":"function"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]` + parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) + bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) + + expectedReceivedMap := map[string]interface{}{ + "function": functionTy, + "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), + "amount": big.NewInt(1), + "memo": []byte{88}, + } + unpackAndCheck(t, bc, expectedReceivedMap, mockLog) +} + +func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) { + bytes := []byte{1, 2, 3, 4, 5} + hash := crypto.Keccak256Hash(bytes) + topics := []common.Hash{ + crypto.Keccak256Hash([]byte("received(bytes,address,uint256,bytes)")), + hash, + } + mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42")) + + abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"content","type":"bytes"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]` + parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) + bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) + + expectedReceivedMap := map[string]interface{}{ + "content": hash, + "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), + "amount": big.NewInt(1), + "memo": []byte{88}, + } + unpackAndCheck(t, bc, expectedReceivedMap, mockLog) +} + +func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) { + received := make(map[string]interface{}) + if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil { + t.Error(err) + } + + if len(received) != len(expected) { + t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected)) + } + for name, elem := range expected { + if !reflect.DeepEqual(elem, received[name]) { + t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name]) + } + } +} + +func newMockLog(topics []common.Hash, txHash common.Hash) types.Log { + return types.Log{ + Address: common.HexToAddress("0x0"), + Topics: topics, + Data: hexutil.MustDecode(hexData), + BlockNumber: uint64(26), + TxHash: txHash, + TxIndex: 111, + BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}), + Index: 7, + Removed: false, + } +}
diff --git go-ethereum/accounts/abi/bind_v2/bind.go celo/accounts/abi/bind_v2/bind.go new file mode 100644 index 0000000000000000000000000000000000000000..40ebb0bf4f6e7c0f9c143d38541e1b997209d0aa --- /dev/null +++ celo/accounts/abi/bind_v2/bind.go @@ -0,0 +1,492 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// Package bind generates Ethereum contract Go bindings. +// +// Detailed usage document and tutorial available on the go-ethereum Wiki page: +// https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts +package bind_v2 + +import ( + "bytes" + "fmt" + "go/format" + "regexp" + "strings" + "text/template" + "unicode" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/log" +) + +// Lang is a target programming language selector to generate bindings for. +type Lang int + +const ( + LangGo Lang = iota +) + +// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant +// to be used as is in client code, but rather as an intermediate struct which +// enforces compile time type safety and naming convention opposed to having to +// manually maintain hard coded strings that break on runtime. +func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) { + var ( + // contracts is the map of each individual contract requested binding + contracts = make(map[string]*tmplContract) + + // structs is the map of all reclared structs shared by passed contracts. + structs = make(map[string]*tmplStruct) + + // isLib is the map used to flag each encountered library as such + isLib = make(map[string]struct{}) + ) + for i := 0; i < len(types); i++ { + // Parse the actual ABI to generate the binding for + evmABI, err := abi.JSON(strings.NewReader(abis[i])) + if err != nil { + return "", err + } + // Strip any whitespace from the JSON ABI + strippedABI := strings.Map(func(r rune) rune { + if unicode.IsSpace(r) { + return -1 + } + return r + }, abis[i]) + + // Extract the call and transact methods; events, struct definitions; and sort them alphabetically + var ( + calls = make(map[string]*tmplMethod) + transacts = make(map[string]*tmplMethod) + events = make(map[string]*tmplEvent) + fallback *tmplMethod + receive *tmplMethod + + // identifiers are used to detect duplicated identifier of function + // and event. For all calls, transacts and events, abigen will generate + // corresponding bindings. However we have to ensure there is no + // identifier coliision in the bindings of these categories. + callIdentifiers = make(map[string]bool) + transactIdentifiers = make(map[string]bool) + eventIdentifiers = make(map[string]bool) + ) + for _, original := range evmABI.Methods { + // Normalize the method for capital cases and non-anonymous inputs/outputs + normalized := original + normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) + // Ensure there is no duplicated identifier + var identifiers = callIdentifiers + if !original.IsConstant() { + identifiers = transactIdentifiers + } + if identifiers[normalizedName] { + return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) + } + identifiers[normalizedName] = true + normalized.Name = normalizedName + normalized.Inputs = make([]abi.Argument, len(original.Inputs)) + copy(normalized.Inputs, original.Inputs) + for j, input := range normalized.Inputs { + if input.Name == "" { + normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) + } + if hasStruct(input.Type) { + bindStructType[lang](input.Type, structs) + } + } + normalized.Outputs = make([]abi.Argument, len(original.Outputs)) + copy(normalized.Outputs, original.Outputs) + for j, output := range normalized.Outputs { + if output.Name != "" { + normalized.Outputs[j].Name = capitalise(output.Name) + } + if hasStruct(output.Type) { + bindStructType[lang](output.Type, structs) + } + } + // Append the methods to the call or transact lists + if original.IsConstant() { + calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} + } else { + transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} + } + } + for _, original := range evmABI.Events { + // Skip anonymous events as they don't support explicit filtering + if original.Anonymous { + continue + } + // Normalize the event for capital cases and non-anonymous outputs + normalized := original + + // Ensure there is no duplicated identifier + normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) + if eventIdentifiers[normalizedName] { + return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) + } + eventIdentifiers[normalizedName] = true + normalized.Name = normalizedName + + normalized.Inputs = make([]abi.Argument, len(original.Inputs)) + copy(normalized.Inputs, original.Inputs) + for j, input := range normalized.Inputs { + if input.Name == "" { + normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) + } + if hasStruct(input.Type) { + bindStructType[lang](input.Type, structs) + } + } + // Append the event to the accumulator list + events[original.Name] = &tmplEvent{Original: original, Normalized: normalized} + } + // Add two special fallback functions if they exist + if evmABI.HasFallback() { + fallback = &tmplMethod{Original: evmABI.Fallback} + } + if evmABI.HasReceive() { + receive = &tmplMethod{Original: evmABI.Receive} + } + + contracts[types[i]] = &tmplContract{ + Type: capitalise(types[i]), + InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1), + InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"), + Constructor: evmABI.Constructor, + Calls: calls, + Transacts: transacts, + Fallback: fallback, + Receive: receive, + Events: events, + Libraries: make(map[string]string), + } + // Function 4-byte signatures are stored in the same sequence + // as types, if available. + if len(fsigs) > i { + contracts[types[i]].FuncSigs = fsigs[i] + } + // Parse library references. + for pattern, name := range libs { + matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin)) + if err != nil { + log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err) + } + if matched { + contracts[types[i]].Libraries[pattern] = name + // keep track that this type is a library + if _, ok := isLib[name]; !ok { + isLib[name] = struct{}{} + } + } + } + } + // Check if that type has already been identified as a library + for i := 0; i < len(types); i++ { + _, ok := isLib[types[i]] + contracts[types[i]].Library = ok + } + // Generate the contract template data content and render it + data := &tmplData{ + Package: pkg, + Contracts: contracts, + Libraries: libs, + Structs: structs, + } + buffer := new(bytes.Buffer) + + funcs := map[string]interface{}{ + "bindtype": bindType[lang], + "bindtopictype": bindTopicType[lang], + "namedtype": namedType[lang], + "formatmethod": formatMethod, + "formatevent": formatEvent, + "capitalise": capitalise, + "decapitalise": decapitalise, + } + tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang])) + if err := tmpl.Execute(buffer, data); err != nil { + return "", err + } + // For Go bindings pass the code through gofmt to clean it up + if lang == LangGo { + code, err := format.Source(buffer.Bytes()) + if err != nil { + return "", fmt.Errorf("%v\n%s", err, buffer) + } + return string(code), nil + } + // For all others just return as is for now + return buffer.String(), nil +} + +// bindType is a set of type binders that convert Solidity types to some supported +// programming language types. +var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ + LangGo: bindTypeGo, +} + +// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go one. +func bindBasicTypeGo(kind abi.Type) string { + switch kind.T { + case abi.AddressTy: + return "common.Address" + case abi.IntTy, abi.UintTy: + parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String()) + switch parts[2] { + case "8", "16", "32", "64": + return fmt.Sprintf("%sint%s", parts[1], parts[2]) + } + return "*big.Int" + case abi.FixedBytesTy: + return fmt.Sprintf("[%d]byte", kind.Size) + case abi.BytesTy: + return "[]byte" + case abi.FunctionTy: + return "[24]byte" + default: + // string, bool types + return kind.String() + } +} + +// bindTypeGo converts solidity types to Go ones. Since there is no clear mapping +// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly +// mapped will use an upscaled type (e.g. BigDecimal). +func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { + switch kind.T { + case abi.TupleTy: + return structs[kind.TupleRawName+kind.String()].Name + case abi.ArrayTy: + return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs) + case abi.SliceTy: + return "[]" + bindTypeGo(*kind.Elem, structs) + default: + return bindBasicTypeGo(kind) + } +} + +// bindTopicType is a set of type binders that convert Solidity types to some +// supported programming language topic types. +var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ + LangGo: bindTopicTypeGo, +} + +// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same +// funcionality as for simple types, but dynamic types get converted to hashes. +func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { + bound := bindTypeGo(kind, structs) + + // todo(rjl493456442) according solidity documentation, indexed event + // parameters that are not value types i.e. arrays and structs are not + // stored directly but instead a keccak256-hash of an encoding is stored. + // + // We only convert stringS and bytes to hash, still need to deal with + // array(both fixed-size and dynamic-size) and struct. + if bound == "string" || bound == "[]byte" { + bound = "common.Hash" + } + return bound +} + +// bindStructType is a set of type binders that convert Solidity tuple types to some supported +// programming language struct definition. +var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ + LangGo: bindStructTypeGo, +} + +// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping +// in the given map. +// Notably, this function will resolve and record nested struct recursively. +func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { + switch kind.T { + case abi.TupleTy: + // We compose raw struct name and canonical parameter expression + // together here. The reason is before solidity v0.5.11, kind.TupleRawName + // is empty, so we use canonical parameter expression to distinguish + // different struct definition. From the consideration of backward + // compatibility, we concat these two together so that if kind.TupleRawName + // is not empty, it can have unique id. + id := kind.TupleRawName + kind.String() + if s, exist := structs[id]; exist { + return s.Name + } + var fields []*tmplField + for i, elem := range kind.TupleElems { + field := bindStructTypeGo(*elem, structs) + fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem}) + } + name := kind.TupleRawName + if name == "" { + name = fmt.Sprintf("Struct%d", len(structs)) + } + structs[id] = &tmplStruct{ + Name: name, + Fields: fields, + } + return name + case abi.ArrayTy: + return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs) + case abi.SliceTy: + return "[]" + bindStructTypeGo(*kind.Elem, structs) + default: + return bindBasicTypeGo(kind) + } +} + +// namedType is a set of functions that transform language specific types to +// named versions that my be used inside method names. +var namedType = map[Lang]func(string, abi.Type) string{ + LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, +} + +// alias returns an alias of the given string based on the aliasing rules +// or returns itself if no rule is matched. +func alias(aliases map[string]string, n string) string { + if alias, exist := aliases[n]; exist { + return alias + } + return n +} + +// methodNormalizer is a name transformer that modifies Solidity method names to +// conform to target language naming concentions. +var methodNormalizer = map[Lang]func(string) string{ + LangGo: abi.ToCamelCase, +} + +// capitalise makes a camel-case string which starts with an upper case character. +var capitalise = abi.ToCamelCase + +// decapitalise makes a camel-case string which starts with a lower case character. +func decapitalise(input string) string { + if len(input) == 0 { + return input + } + + goForm := abi.ToCamelCase(input) + return strings.ToLower(goForm[:1]) + goForm[1:] +} + +// structured checks whether a list of ABI data types has enough information to +// operate through a proper Go struct or if flat returns are needed. +func structured(args abi.Arguments) bool { + if len(args) < 2 { + return false + } + exists := make(map[string]bool) + for _, out := range args { + // If the name is anonymous, we can't organize into a struct + if out.Name == "" { + return false + } + // If the field name is empty when normalized or collides (var, Var, _var, _Var), + // we can't organize into a struct + field := capitalise(out.Name) + if field == "" || exists[field] { + return false + } + exists[field] = true + } + return true +} + +// hasStruct returns an indicator whether the given type is struct, struct slice +// or struct array. +func hasStruct(t abi.Type) bool { + switch t.T { + case abi.SliceTy: + return hasStruct(*t.Elem) + case abi.ArrayTy: + return hasStruct(*t.Elem) + case abi.TupleTy: + return true + default: + return false + } +} + +// resolveArgName converts a raw argument representation into a user friendly format. +func resolveArgName(arg abi.Argument, structs map[string]*tmplStruct) string { + var ( + prefix string + embedded string + typ = &arg.Type + ) +loop: + for { + switch typ.T { + case abi.SliceTy: + prefix += "[]" + case abi.ArrayTy: + prefix += fmt.Sprintf("[%d]", typ.Size) + default: + embedded = typ.TupleRawName + typ.String() + break loop + } + typ = typ.Elem + } + if s, exist := structs[embedded]; exist { + return prefix + s.Name + } else { + return arg.Type.String() + } +} + +// formatMethod transforms raw method representation into a user friendly one. +func formatMethod(method abi.Method, structs map[string]*tmplStruct) string { + inputs := make([]string, len(method.Inputs)) + for i, input := range method.Inputs { + inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name) + } + outputs := make([]string, len(method.Outputs)) + for i, output := range method.Outputs { + outputs[i] = resolveArgName(output, structs) + if len(output.Name) > 0 { + outputs[i] += fmt.Sprintf(" %v", output.Name) + } + } + // Extract meaningful state mutability of solidity method. + // If it's default value, never print it. + state := method.StateMutability + if state == "nonpayable" { + state = "" + } + if state != "" { + state = state + " " + } + identity := fmt.Sprintf("function %v", method.RawName) + if method.Type == abi.Fallback { + identity = "fallback" + } else if method.Type == abi.Receive { + identity = "receive" + } + return fmt.Sprintf("%s(%v) %sreturns(%v)", identity, strings.Join(inputs, ", "), state, strings.Join(outputs, ", ")) +} + +// formatEvent transforms raw event representation into a user friendly one. +func formatEvent(event abi.Event, structs map[string]*tmplStruct) string { + inputs := make([]string, len(event.Inputs)) + for i, input := range event.Inputs { + if input.Indexed { + inputs[i] = fmt.Sprintf("%v indexed %v", resolveArgName(input, structs), input.Name) + } else { + inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name) + } + } + return fmt.Sprintf("event %v(%v)", event.RawName, strings.Join(inputs, ", ")) +}
diff --git go-ethereum/accounts/abi/bind_v2/auth.go celo/accounts/abi/bind_v2/auth.go new file mode 100644 index 0000000000000000000000000000000000000000..ec4876d87fc80bed7f0f662aa46b4af327fb0e69 --- /dev/null +++ celo/accounts/abi/bind_v2/auth.go @@ -0,0 +1,182 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package bind_v2 + +import ( + "context" + "crypto/ecdsa" + "errors" + "io" + "io/ioutil" + "math/big" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/external" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" +) + +// ErrNoChainID is returned whenever the user failed to specify a chain id. +var ErrNoChainID = errors.New("no chain id specified") + +// ErrNotAuthorized is returned when an account is not properly unlocked. +var ErrNotAuthorized = errors.New("not authorized to sign this account") + +// NewTransactor is a utility method to easily create a transaction signer from +// an encrypted json key stream and the associated passphrase. +// +// Deprecated: Use NewTransactorWithChainID instead. +func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) { + log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID") + json, err := ioutil.ReadAll(keyin) + if err != nil { + return nil, err + } + key, err := keystore.DecryptKey(json, passphrase) + if err != nil { + return nil, err + } + return NewKeyedTransactor(key.PrivateKey), nil +} + +// NewKeyStoreTransactor is a utility method to easily create a transaction signer from +// an decrypted key from a keystore +// +// Deprecated: Use NewKeyStoreTransactorWithChainID instead. +func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) { + log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID") + signer := types.LatestSignerForChainID(nil) + return &TransactOpts{ + From: account.Address, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if address != account.Address { + return nil, ErrNotAuthorized + } + signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes()) + if err != nil { + return nil, err + } + return tx.WithSignature(signer, signature) + }, + Context: context.Background(), + }, nil +} + +// NewKeyedTransactor is a utility method to easily create a transaction signer +// from a single private key. +// +// Deprecated: Use NewKeyedTransactorWithChainID instead. +func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { + log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID") + keyAddr := crypto.PubkeyToAddress(key.PublicKey) + signer := types.LatestSignerForChainID(nil) + return &TransactOpts{ + From: keyAddr, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if address != keyAddr { + return nil, ErrNotAuthorized + } + signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key) + if err != nil { + return nil, err + } + return tx.WithSignature(signer, signature) + }, + Context: context.Background(), + } +} + +// NewTransactorWithChainID is a utility method to easily create a transaction signer from +// an encrypted json key stream and the associated passphrase. +func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) { + json, err := ioutil.ReadAll(keyin) + if err != nil { + return nil, err + } + key, err := keystore.DecryptKey(json, passphrase) + if err != nil { + return nil, err + } + return NewKeyedTransactorWithChainID(key.PrivateKey, chainID) +} + +// NewKeyStoreTransactorWithChainID is a utility method to easily create a transaction signer from +// an decrypted key from a keystore. +// +// Deprecated: Use NewKeyedTransactorWithChainID instead. +func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accounts.Account, chainID *big.Int) (*TransactOpts, error) { + if chainID == nil { + return nil, ErrNoChainID + } + signer := types.LatestSignerForChainID(chainID) + return &TransactOpts{ + From: account.Address, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if address != account.Address { + return nil, ErrNotAuthorized + } + signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes()) + if err != nil { + return nil, err + } + return tx.WithSignature(signer, signature) + }, + Context: context.Background(), + }, nil +} + +// NewKeyedTransactorWithChainID is a utility method to easily create a transaction signer +// from a single private key. +func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) { + keyAddr := crypto.PubkeyToAddress(key.PublicKey) + if chainID == nil { + return nil, ErrNoChainID + } + signer := types.LatestSignerForChainID(chainID) + return &TransactOpts{ + From: keyAddr, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if address != keyAddr { + return nil, ErrNotAuthorized + } + signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key) + if err != nil { + return nil, err + } + return tx.WithSignature(signer, signature) + }, + Context: context.Background(), + }, nil +} + +// NewClefTransactor is a utility method to easily create a transaction signer +// with a clef backend. +func NewClefTransactor(clef *external.ExternalSigner, account accounts.Account) *TransactOpts { + return &TransactOpts{ + From: account.Address, + Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) { + if address != account.Address { + return nil, errors.New("not authorized to sign this account") + } + return clef.SignTx(account, transaction, nil) // Clef enforces its own chain id + }, + Context: context.Background(), + } +}
diff --git go-ethereum/accounts/abi/bind_v2/backend.go celo/accounts/abi/bind_v2/backend.go new file mode 100644 index 0000000000000000000000000000000000000000..817b3c36e53061dc982c5dcbf1317ef896b2e4d5 --- /dev/null +++ celo/accounts/abi/bind_v2/backend.go @@ -0,0 +1,128 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package bind_v2 + +import ( + "context" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +var ( + // ErrNoCode is returned by call and transact operations for which the requested + // recipient contract to operate on does not exist in the state db or does not + // have any code associated with it (i.e. suicided). + ErrNoCode = errors.New("no contract code at given address") + + // ErrNoPendingState is raised when attempting to perform a pending state action + // on a backend that doesn't implement PendingContractCaller. + ErrNoPendingState = errors.New("backend does not support pending state") + + // ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves + // an empty contract behind. + ErrNoCodeAfterDeploy = errors.New("no contract code after deployment") +) + +// ContractCaller defines the methods needed to allow operating with contract on a read +// only basis. +type ContractCaller interface { + // CodeAt returns the code of the given account. This is needed to differentiate + // between contract internal errors and the local chain being out of sync. + CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) + + // ContractCall executes an Ethereum contract call with the specified data as the + // input. + CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) +} + +// PendingContractCaller defines methods to perform contract calls on the pending state. +// Call will try to discover this interface when access to the pending state is requested. +// If the backend does not support the pending state, Call returns ErrNoPendingState. +type PendingContractCaller interface { + // PendingCodeAt returns the code of the given account in the pending state. + PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) + + // PendingCallContract executes an Ethereum contract call against the pending state. + PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) +} + +// ContractTransactor defines the methods needed to allow operating with contract +// on a write only basis. Beside the transacting method, the remainder are helpers +// used when the user does not provide some needed values, but rather leaves it up +// to the transactor to decide. +type ContractTransactor interface { + // HeaderByNumber returns a block header from the current canonical chain. If + // number is nil, the latest known header is returned. + HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) + + // PendingCodeAt returns the code of the given account in the pending state. + PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) + + // PendingNonceAt retrieves the current pending nonce associated with an account. + PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) + + // SuggestGasPrice retrieves the currently suggested gas price to allow a timely + // execution of a transaction. + SuggestGasPrice(ctx context.Context) (*big.Int, error) + + // SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow + // a timely execution of a transaction. + SuggestGasTipCap(ctx context.Context) (*big.Int, error) + + // EstimateGas tries to estimate the gas needed to execute a specific + // transaction based on the current pending state of the backend blockchain. + // There is no guarantee that this is the true gas limit requirement as other + // transactions may be added or removed by miners, but it should provide a basis + // for setting a reasonable default. + EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) + + // SendTransaction injects the transaction into the pending pool for execution. + SendTransaction(ctx context.Context, tx *types.Transaction) error + + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) +} + +// ContractFilterer defines the methods needed to access log events using one-off +// queries or continuous event subscriptions. +type ContractFilterer interface { + // FilterLogs executes a log filter operation, blocking during execution and + // returning all the results in one batch. + // + // TODO(karalabe): Deprecate when the subscription one can return past data too. + FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) + + // SubscribeFilterLogs creates a background log filtering operation, returning + // a subscription immediately, which can be used to stream the found events. + SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) +} + +// DeployBackend wraps the operations needed by WaitMined and WaitDeployed. +type DeployBackend interface { + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) +} + +// ContractBackend defines the methods needed to work with contracts on a read-write basis. +type ContractBackend interface { + ContractCaller + ContractTransactor + ContractFilterer +}
diff --git go-ethereum/accounts/abi/bind_v2/base.go celo/accounts/abi/bind_v2/base.go new file mode 100644 index 0000000000000000000000000000000000000000..462fe0d90e71931517ae5afb094baad9101defd8 --- /dev/null +++ celo/accounts/abi/bind_v2/base.go @@ -0,0 +1,609 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package bind_v2 + +import ( + "context" + "errors" + "fmt" + "math/big" + "strings" + "sync" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/event" +) + +// SignerFn is a signer function callback when a contract requires a method to +// sign the transaction before submission. +type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error) + +// CallOpts is the collection of options to fine tune a contract call request. +type CallOpts struct { + Pending bool // Whether to operate on the pending state or the last known one + From common.Address // Optional the sender address, otherwise the first account is used + BlockNumber *big.Int // Optional the block number on which the call should be performed + Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) +} + +// TransactOpts is the collection of authorization data required to create a +// valid Ethereum transaction. +type TransactOpts struct { + From common.Address // Ethereum account to send the transaction from + Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state) + Signer SignerFn // Method to use for signing the transaction (mandatory) + ChainID *big.Int // Chain/network id for use for replay protection during signing (nil = no replay protection) + + Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds) + GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) + FeeCurrency *common.Address // Fee currency to be used for transaction (nil = default currency = Celo Gold) + GatewayFeeRecipient *common.Address // Address to which gateway fees should be paid (nil = no gateway fees are paid) + GatewayFee *big.Int // Value of gateway fees to be paid (nil = no gateway fees are paid) + GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle) + GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle) + GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate) + + Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) +} + +// FilterOpts is the collection of options to fine tune filtering for events +// within a bound contract. +type FilterOpts struct { + Start uint64 // Start of the queried range + End *uint64 // End of the range (nil = latest) + + Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) +} + +// WatchOpts is the collection of options to fine tune subscribing for events +// within a bound contract. +type WatchOpts struct { + Start *uint64 // Start of the queried range (nil = latest) + Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) +} + +// MetaData collects all metadata for a bound contract. +type MetaData struct { + mu sync.Mutex + Sigs map[string]string + Bin string + ABI string + ab *abi.ABI +} + +func (m *MetaData) GetAbi() (*abi.ABI, error) { + m.mu.Lock() + defer m.mu.Unlock() + if m.ab != nil { + return m.ab, nil + } + if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil { + return nil, err + } else { + m.ab = &parsed + } + return m.ab, nil +} + +// BoundContract is the base wrapper object that reflects a contract on the +// Ethereum network. It contains a collection of methods that are used by the +// higher level contract bindings to operate. +type BoundContract struct { + address common.Address // Deployment address of the contract on the Ethereum blockchain + abi abi.ABI // Reflect based ABI to access the correct Ethereum methods + backend ContractBackend // Event filtering to interact with the blockchain +} + +// NewBoundContract creates a low level contract interface through which calls +// and transactions may be made through. +func NewBoundContract(address common.Address, abi abi.ABI, backend ContractBackend) *BoundContract { + return &BoundContract{ + address: address, + abi: abi, + backend: backend, + } +} + +// DeployContract deploys a contract onto the Ethereum blockchain and binds the +// deployment address with a Go wrapper. +func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) { + // Otherwise try to deploy the contract + c := NewBoundContract(common.Address{}, abi, backend) + + input, err := c.abi.Pack("", params...) + if err != nil { + return common.Address{}, nil, nil, err + } + tx, err := c.transact(opts, nil, append(bytecode, input...)) + if err != nil { + return common.Address{}, nil, nil, err + } + c.address = crypto.CreateAddress(opts.From, tx.Nonce()) + return c.address, tx, c, nil +} + +// ABI returns the bound contract parsed ABI +func (c *BoundContract) ABI() abi.ABI { + return c.abi +} + +// TxObj returns an obj that can be used to get the transaction, send it, estimate it +func (c *BoundContract) TxObj(opts *TransactOpts, method string, params ...interface{}) *TxObject { + return &TxObject{ + method: method, + params: params, + opts: opts, + c: c, + } +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error { + // Don't crash on a lazy user + if opts == nil { + opts = new(CallOpts) + } + if results == nil { + results = new([]interface{}) + } + // Pack the input, call and unpack the results + input, err := c.abi.Pack(method, params...) + if err != nil { + return err + } + var ( + msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input} + ctx = ensureContext(opts.Context) + code []byte + output []byte + ) + if opts.Pending { + pb, ok := c.backend.(PendingContractCaller) + if !ok { + return ErrNoPendingState + } + output, err = pb.PendingCallContract(ctx, msg) + if err == nil && len(output) == 0 { + // Make sure we have a contract to operate on, and bail out otherwise. + if code, err = pb.PendingCodeAt(ctx, c.address); err != nil { + return err + } else if len(code) == 0 { + return ErrNoCode + } + } + } else { + output, err = c.backend.CallContract(ctx, msg, opts.BlockNumber) + if err != nil { + return err + } + if len(output) == 0 { + // Make sure we have a contract to operate on, and bail out otherwise. + if code, err = c.backend.CodeAt(ctx, c.address, opts.BlockNumber); err != nil { + return err + } else if len(code) == 0 { + return ErrNoCode + } + } + } + + if len(*results) == 0 { + res, err := c.abi.Unpack(method, output) + *results = res + return err + } + res := *results + return c.abi.UnpackIntoInterface(res[0], method, output) +} + +// Transact invokes the (paid) contract method with params as input values. +func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + // Otherwise pack up the parameters and invoke the contract + input, err := c.abi.Pack(method, params...) + if err != nil { + return nil, err + } + // todo(rjl493456442) check the method is payable or not, + // reject invalid transaction at the first place + return c.transact(opts, &c.address, input) +} + +// TransactionFor returns the signed transaction for the contract method with params as input values +func (c *BoundContract) TransactionFor(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + // Otherwise pack up the parameters and invoke the contract + input, err := c.abi.Pack(method, params...) + if err != nil { + return nil, err + } + // todo(rjl493456442) check the method is payable or not, + // reject invalid transaction at the first place + return c.transactionFor(opts, &c.address, input) +} + +// EstimateGas obtains an estimate for calling contract method with params as input values. +func (c *BoundContract) EstimateGas(opts *TransactOpts, method string, params ...interface{}) (uint64, error) { + // Otherwise pack up the parameters and invoke the contract + input, err := c.abi.Pack(method, params...) + if err != nil { + return 0, err + } + + msg := ethereum.CallMsg{From: opts.From, To: &c.address, GasPrice: opts.GasPrice, Value: opts.Value, Data: input} + gasLimit, err := c.backend.EstimateGas(ensureContext(opts.Context), msg) + if err != nil { + return 0, err + } + + // Apply buffer 3/2 => 1.5 => +50% (weird way of doing it, thanks go) + gasLimit = new(big.Int).Div(new(big.Int).Mul(new(big.Int).SetUint64(gasLimit), big.NewInt(3)), big.NewInt(2)).Uint64() + + return gasLimit, nil +} + +// RawTransact initiates a transaction with the given raw calldata as the input. +// It's usually used to initiates transaction for invoking **Fallback** function. +func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) { + // todo(rjl493456442) check the method is payable or not, + // reject invalid transaction at the first place + return c.transact(opts, &c.address, calldata) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) { + // todo(rjl493456442) check the payable fallback or receive is defined + // or not, reject invalid transaction at the first place + return c.transact(opts, &c.address, nil) +} + +func (c *BoundContract) transactionFor(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { + var err error + + // Ensure a valid value field and resolve the account nonce + value := opts.Value + if value == nil { + value = new(big.Int) + } + var nonce uint64 + if opts.Nonce == nil { + nonce, err = c.backend.PendingNonceAt(ensureContext(opts.Context), opts.From) + if err != nil { + return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) + } + } else { + nonce = opts.Nonce.Uint64() + } + // Figure out reasonable gas price values + if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) { + return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + } + // head, err := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil) + // if err != nil { + // return nil, err + // } + // TODO: Use GPM here + // if head.BaseFee != nil && opts.GasPrice == nil { + // if opts.GasTipCap == nil { + // tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context)) + // if err != nil { + // return nil, err + // } + // opts.GasTipCap = tip + // } + // if opts.GasFeeCap == nil { + // gasFeeCap := new(big.Int).Add( + // opts.GasTipCap, + // new(big.Int).Mul(head.BaseFee, big.NewInt(2)), + // ) + // opts.GasFeeCap = gasFeeCap + // } + // if opts.GasFeeCap.Cmp(opts.GasTipCap) < 0 { + // return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", opts.GasFeeCap, opts.GasTipCap) + // } + // } else { + if opts.GasFeeCap != nil || opts.GasTipCap != nil { + return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") + } + if opts.GasPrice == nil { + price, err := c.backend.SuggestGasPrice(ensureContext(opts.Context)) + if err != nil { + return nil, err + } + opts.GasPrice = price + } + // } + + feeCurrency := opts.FeeCurrency + // TODO(nategraf): Add SuggestFeeCurrency to Transactor to get fee currency + // Otherwise, the user might not be able to pay in non-native currency for contract + // deployment. Paying for Contract deployment in non-native currency might not work right now. + // Only paying for token transfer in non-native currency is supported. + //if feeCurrency == 0 { + // feeCurrency = c.backend.SuggestFeeCurrency(opts.Context) + //} + + gatewayFeeRecipient := opts.GatewayFeeRecipient + gatewayFee := opts.GatewayFee + // TODO(nategraf): Add SuggestGatewayFee to Transactor. + + gasLimit := opts.GasLimit + if gasLimit == 0 { + // Gas estimation cannot succeed without code for method invocations + if contract != nil { + if code, err := c.backend.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil { + return nil, err + } else if len(code) == 0 { + return nil, ErrNoCode + } + } + // If the contract surely has code (or code is not needed), estimate the transaction + msg := ethereum.CallMsg{ + From: opts.From, + To: contract, + GasPrice: opts.GasPrice, + GasTipCap: opts.GasTipCap, + GasFeeCap: opts.GasFeeCap, + Value: value, + FeeCurrency: feeCurrency, + GatewayFeeRecipient: gatewayFeeRecipient, + GatewayFee: gatewayFee, + Data: input, + } + gasLimit, err = c.backend.EstimateGas(ensureContext(opts.Context), msg) + if err != nil { + return nil, fmt.Errorf("failed to estimate gas needed: %v", err) + } + + // Apply buffer 3/2 => 1.5 => +50% (weird way of doing it, thanks go) + gasLimit = new(big.Int).Div(new(big.Int).Mul(new(big.Int).SetUint64(gasLimit), big.NewInt(3)), big.NewInt(2)).Uint64() + } + // Create the transaction, sign it and schedule it for execution + var rawTx *types.Transaction + if opts.GasFeeCap == nil { + baseTx := &types.LegacyTx{ + Nonce: nonce, + GasPrice: opts.GasPrice, + Gas: gasLimit, + Value: value, + FeeCurrency: feeCurrency, + GatewayFeeRecipient: gatewayFeeRecipient, + GatewayFee: gatewayFee, + Data: input, + } + if contract != nil { + baseTx.To = &c.address + } + rawTx = types.NewTx(baseTx) + } else { + baseTx := &types.DynamicFeeTx{ + Nonce: nonce, + GasFeeCap: opts.GasFeeCap, + GasTipCap: opts.GasTipCap, + Gas: gasLimit, + Value: value, + Data: input, + } + if contract != nil { + baseTx.To = &c.address + } + rawTx = types.NewTx(baseTx) + } + if opts.Signer == nil { + return nil, errors.New("no signer to authorize the transaction with") + } + signedTx, err := opts.Signer(opts.From, rawTx) + if err != nil { + return nil, err + } + + return signedTx, err +} + +// transact executes an actual transaction invocation, first deriving any missing +// authorization fields, and then scheduling the transaction for execution. +func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { + var err error + + signedTx, err := c.transactionFor(opts, contract, input) + if err != nil { + return nil, err + } + + if err := c.backend.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { + return nil, err + } + return signedTx, nil +} + +// LogEventName will verify the log belongs to the contract and return the eventName for it +func (c *BoundContract) LogEventName(log types.Log) (eventName string, ok bool, err error) { + if log.Address != c.address || len(log.Topics) < 1 { + return "", false, err + } + + event, err := c.abi.EventByID(log.Topics[0]) + if err != nil { + return "", false, err + } + return event.Name, true, nil +} + +// FilterLogs filters contract logs for past blocks, returning the necessary +// channels to construct a strongly typed bound iterator on top of them. +func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { + // Don't crash on a lazy user + if opts == nil { + opts = new(FilterOpts) + } + // Append the event selector to the query parameters and construct the topic set + query = append([][]interface{}{{c.abi.Events[name].ID}}, query...) + + topics, err := abi.MakeTopics(query...) + if err != nil { + return nil, nil, err + } + // Start the background filtering + logs := make(chan types.Log, 128) + + config := ethereum.FilterQuery{ + Addresses: []common.Address{c.address}, + Topics: topics, + FromBlock: new(big.Int).SetUint64(opts.Start), + } + if opts.End != nil { + config.ToBlock = new(big.Int).SetUint64(*opts.End) + } + /* TODO(karalabe): Replace the rest of the method below with this when supported + sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs) + */ + buff, err := c.backend.FilterLogs(ensureContext(opts.Context), config) + if err != nil { + return nil, nil, err + } + sub, err := event.NewSubscription(func(quit <-chan struct{}) error { + for _, log := range buff { + select { + case logs <- log: + case <-quit: + return nil + } + } + return nil + }), nil + + if err != nil { + return nil, nil, err + } + return logs, sub, nil +} + +// WatchLogs filters subscribes to contract logs for future blocks, returning a +// subscription object that can be used to tear down the watcher. +func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { + // Don't crash on a lazy user + if opts == nil { + opts = new(WatchOpts) + } + // Append the event selector to the query parameters and construct the topic set + query = append([][]interface{}{{c.abi.Events[name].ID}}, query...) + + topics, err := abi.MakeTopics(query...) + if err != nil { + return nil, nil, err + } + // Start the background filtering + logs := make(chan types.Log, 128) + + config := ethereum.FilterQuery{ + Addresses: []common.Address{c.address}, + Topics: topics, + } + if opts.Start != nil { + config.FromBlock = new(big.Int).SetUint64(*opts.Start) + } + sub, err := c.backend.SubscribeFilterLogs(ensureContext(opts.Context), config, logs) + if err != nil { + return nil, nil, err + } + return logs, sub, nil +} + +// UnpackLog unpacks a retrieved log into the provided output structure. +func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error { + if log.Topics[0] != c.abi.Events[event].ID { + return fmt.Errorf("event signature mismatch") + } + if len(log.Data) > 0 { + if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return err + } + } + var indexed abi.Arguments + for _, arg := range c.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + return abi.ParseTopics(out, indexed, log.Topics[1:]) +} + +// UnpackLogIntoMap unpacks a retrieved log into the provided map. +func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error { + if log.Topics[0] != c.abi.Events[event].ID { + return fmt.Errorf("event signature mismatch") + } + if len(log.Data) > 0 { + if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil { + return err + } + } + var indexed abi.Arguments + for _, arg := range c.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:]) +} + +// ensureContext is a helper method to ensure a context is not nil, even if the +// user specified it as such. +func ensureContext(ctx context.Context) context.Context { + if ctx == nil { + return context.Background() + } + return ctx +} + +type TxPromise struct { + Transaction *types.Transaction + backend ContractBackend +} + +func (txp *TxPromise) WaitMined(ctx context.Context) (*types.Receipt, error) { + return WaitMined(ctx, txp.backend, txp.Transaction) +} + +type TxObject struct { + method string + params []interface{} + opts *TransactOpts + c *BoundContract +} + +func (txo *TxObject) Send() (*TxPromise, error) { + tx, err := txo.c.Transact(txo.opts, txo.method, txo.params...) + + return &TxPromise{ + Transaction: tx, + backend: txo.c.backend, + }, err +} + +func (txo *TxObject) EstimateGas() (uint64, error) { + return txo.c.EstimateGas(txo.opts, txo.method, txo.params...) +} + +func (txo *TxObject) Transaction() (*types.Transaction, error) { + return txo.c.TransactionFor(txo.opts, txo.method, txo.params...) +}
diff --git go-ethereum/accounts/abi/bind_v2/util_test.go celo/accounts/abi/bind_v2/util_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5b36290e60a8daa7125535c1a9b30648f0242183 --- /dev/null +++ celo/accounts/abi/bind_v2/util_test.go @@ -0,0 +1,135 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package bind_v2 + +import ( + "context" + "errors" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + +var genericBaseFee = big.NewInt(2) // set a constant gas price to be above the GPM. + +var waitDeployedTests = map[string]struct { + code string + gas uint64 + wantAddress common.Address + wantErr error +}{ + "successful deploy": { + code: `6060604052600a8060106000396000f360606040526008565b00`, + gas: 3000000, + wantAddress: common.HexToAddress("0x3a220f351252089d385b29beca14e27f204c296a"), + }, + "empty code": { + code: ``, + gas: 300000, + wantErr: bind.ErrNoCodeAfterDeploy, + wantAddress: common.HexToAddress("0x3a220f351252089d385b29beca14e27f204c296a"), + }, +} + +func TestWaitDeployed(t *testing.T) { + for name, test := range waitDeployedTests { + backend := backends.NewSimulatedBackend( + core.GenesisAlloc{crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}}) + defer backend.Close() + + // Create the transaction + // head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1)) + + tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, nil, nil, nil, common.FromHex(test.code)) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) + + // Wait for it to get mined in the background. + var ( + err error + address common.Address + mined = make(chan struct{}) + ctx = context.Background() + ) + go func() { + address, err = bind.WaitDeployed(ctx, backend, tx) + close(mined) + }() + + // Send and mine the transaction. + backend.SendTransaction(ctx, tx) + backend.Commit() + + select { + case <-mined: + if err != test.wantErr { + t.Errorf("test %q: error mismatch: got %q, want %q", name, err, test.wantErr) + } + if address != test.wantAddress { + t.Errorf("test %q: unexpected contract address %s", name, address.Hex()) + } + case <-time.After(2 * time.Second): + t.Errorf("test %q: timeout", name) + } + } +} + +func TestWaitDeployedCornerCases(t *testing.T) { + backend := backends.NewSimulatedBackend( + core.GenesisAlloc{crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}}) + defer backend.Close() + + // head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(genericBaseFee, big.NewInt(1)) + + // Create a transaction to an account. + code := "6060604052600a8060106000396000f360606040526008565b00" + tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, + gasPrice, nil, nil, nil, common.FromHex(code)) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + backend.SendTransaction(ctx, tx) + backend.Commit() + notContentCreation := errors.New("tx is not contract creation") + if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContentCreation.Error() { + t.Errorf("error missmatch: want %q, got %q, ", notContentCreation, err) + } + + // Create a transaction that is not mined. + tx = types.NewContractCreation(1, big.NewInt(0), 3000000, gasPrice, nil, nil, nil, common.FromHex(code)) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) + + go func() { + contextCanceled := errors.New("context canceled") + if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != contextCanceled.Error() { + t.Errorf("error missmatch: want %q, got %q, ", contextCanceled, err) + } + }() + + backend.SendTransaction(ctx, tx) + cancel() +}
diff --git go-ethereum/accounts/abi/bind_v2/util.go celo/accounts/abi/bind_v2/util.go new file mode 100644 index 0000000000000000000000000000000000000000..c9d60fc101fa6d491c4f84bbbd38fba1cb76d214 --- /dev/null +++ celo/accounts/abi/bind_v2/util.go @@ -0,0 +1,76 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package bind_v2 + +import ( + "context" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" +) + +// WaitMined waits for tx to be mined on the blockchain. +// It stops waiting when the context is canceled. +func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) { + queryTicker := time.NewTicker(time.Second) + defer queryTicker.Stop() + + logger := log.New("hash", tx.Hash()) + for { + receipt, err := b.TransactionReceipt(ctx, tx.Hash()) + if receipt != nil { + return receipt, nil + } + if err != nil { + logger.Trace("Receipt retrieval failed", "err", err) + } else { + logger.Trace("Transaction not yet mined") + } + // Wait for the next round. + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-queryTicker.C: + } + } +} + +// WaitDeployed waits for a contract deployment transaction and returns the on-chain +// contract address when it is mined. It stops waiting when ctx is canceled. +func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) { + if tx.To() != nil { + return common.Address{}, fmt.Errorf("tx is not contract creation") + } + receipt, err := WaitMined(ctx, b, tx) + if err != nil { + return common.Address{}, err + } + if receipt.ContractAddress == (common.Address{}) { + return common.Address{}, fmt.Errorf("zero address") + } + // Check that code has indeed been deployed at the address. + // This matters on pre-Homestead chains: OOG in the constructor + // could leave an empty account behind. + code, err := b.CodeAt(ctx, receipt.ContractAddress, nil) + if err == nil && len(code) == 0 { + err = ErrNoCodeAfterDeploy + } + return receipt.ContractAddress, err +}
diff --git go-ethereum/accounts/abi/bind_v2/template.go celo/accounts/abi/bind_v2/template.go new file mode 100644 index 0000000000000000000000000000000000000000..93a4651d751f9543dfdd9382a71273e574f99ccd --- /dev/null +++ celo/accounts/abi/bind_v2/template.go @@ -0,0 +1,497 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package bind_v2 + +import "github.com/ethereum/go-ethereum/accounts/abi" + +// tmplData is the data structure required to fill the binding template. +type tmplData struct { + Package string // Name of the package to place the generated file in + Contracts map[string]*tmplContract // List of contracts to generate into this file + Libraries map[string]string // Map the bytecode's link pattern to the library name + Structs map[string]*tmplStruct // Contract struct type definitions +} + +// tmplContract contains the data needed to generate an individual contract binding. +type tmplContract struct { + Type string // Type name of the main contract binding + InputABI string // JSON ABI used as the input to generate the binding from + InputBin string // Optional EVM bytecode used to denetare deploy code from + FuncSigs map[string]string // Optional map: string signature -> 4-byte signature + Constructor abi.Method // Contract constructor for deploy parametrization + Calls map[string]*tmplMethod // Contract calls that only read state data + Transacts map[string]*tmplMethod // Contract calls that write state data + Fallback *tmplMethod // Additional special fallback function + Receive *tmplMethod // Additional special receive function + Events map[string]*tmplEvent // Contract events accessors + Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs + Library bool // Indicator whether the contract is a library +} + +// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed +// and cached data fields. +type tmplMethod struct { + Original abi.Method // Original method as parsed by the abi package + Normalized abi.Method // Normalized version of the parsed method (capitalized names, non-anonymous args/returns) + Structured bool // Whether the returns should be accumulated into a struct +} + +// tmplEvent is a wrapper around an abi.Event that contains a few preprocessed +// and cached data fields. +type tmplEvent struct { + Original abi.Event // Original event as parsed by the abi package + Normalized abi.Event // Normalized version of the parsed fields +} + +// tmplField is a wrapper around a struct field with binding language +// struct type definition and relative filed name. +type tmplField struct { + Type string // Field type representation depends on target binding language + Name string // Field name converted from the raw user-defined field name + SolKind abi.Type // Raw abi type information +} + +// tmplStruct is a wrapper around an abi.tuple contains a auto-generated +// struct name. +type tmplStruct struct { + Name string // Auto-generated struct name(before solidity v0.5.11) or raw name. + Fields []*tmplField // Struct fields definition depends on the binding language. +} + +// tmplSource is language to template mapping containing all the supported +// programming languages the package can generate to. +var tmplSource = map[Lang]string{ + LangGo: tmplSourceGo, +} + +// tmplSourceGo is the Go source template use to generate the contract binding +// based on. +const tmplSourceGo = ` +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package {{.Package}} + +import ( + "math/big" + "strings" + "errors" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + bind "github.com/ethereum/go-ethereum/accounts/abi/bind_v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + +{{$structs := .Structs}} +{{range $structs}} + // {{.Name}} is an auto generated low-level Go binding around an user-defined struct. + type {{.Name}} struct { + {{range $field := .Fields}} + {{$field.Name}} {{$field.Type}}{{end}} + } +{{end}} + +{{range $contract := .Contracts}} + // {{.Type}}MetaData contains all meta data concerning the {{.Type}} contract. + var {{.Type}}MetaData = &bind.MetaData{ + ABI: "{{.InputABI}}", + {{if $contract.FuncSigs -}} + Sigs: map[string]string{ + {{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}", + {{end}} + }, + {{end -}} + {{if .InputBin -}} + Bin: "0x{{.InputBin}}", + {{end}} + } + // {{.Type}}ABI is the input ABI used to generate the binding from. + // Deprecated: Use {{.Type}}MetaData.ABI instead. + var {{.Type}}ABI = {{.Type}}MetaData.ABI + + {{if $contract.FuncSigs}} + // Deprecated: Use {{.Type}}MetaData.Sigs instead. + // {{.Type}}FuncSigs maps the 4-byte function signature to its string representation. + var {{.Type}}FuncSigs = {{.Type}}MetaData.Sigs + {{end}} + + {{if .InputBin}} + // {{.Type}}Bin is the compiled bytecode used for deploying new contracts. + // Deprecated: Use {{.Type}}MetaData.Bin instead. + var {{.Type}}Bin = {{.Type}}MetaData.Bin + + // Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it. + func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) { + parsed, err := {{.Type}}MetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + {{range $pattern, $name := .Libraries}} + {{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend) + {{$contract.Type}}Bin = strings.Replace({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:], -1) + {{end}} + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}}) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &{{.Type}}{ contract: contract }, nil + } + {{end}} + + // {{.Type}} is an auto generated Go binding around an Ethereum contract. + type {{.Type}} struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls + } + + // {{.Type}}Session is an auto generated Go binding around an Ethereum contract, + // with pre-set call and transact options. + type {{.Type}}Session struct { + Contract *{{.Type}} // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session + } + + + // {{.Type}}Raw is an auto generated low-level Go binding around an Ethereum contract. + type {{.Type}}Raw struct { + Contract *{{.Type}} // Generic contract binding to access the raw methods on + } + + // New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract. + func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) { + contract, err := bind{{.Type}}(address, backend) + if err != nil { + return nil, err + } + return &{{.Type}}{ contract: contract }, nil + } + + // bind{{.Type}} binds a generic wrapper to an already deployed contract. + func bind{{.Type}}(address common.Address, contract bind.ContractBackend) (*bind.BoundContract, error) { + parsed, err := Parse{{.Type}}ABI() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, contract), nil + } + + // Parse{{.Type}}ABI parses the ABI + func Parse{{.Type}}ABI() (*abi.ABI, error) { + parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI)) + if err != nil { + return nil, err + } + return &parsed, nil + } + + // Call invokes the (constant) contract method with params as input values and + // sets the output to result. The result type might be a single field for simple + // returns, a slice of interfaces for anonymous returns and a struct for named + // returns. + func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...) + } + + // Transfer initiates a plain transaction to move funds to the contract, calling + // its default method if one is available. + func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _{{$contract.Type}}.Contract.contract.Transfer(opts) + } + + // Transact invokes the (paid) contract method with params as input values. + func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...) + } + + // TxObj returns an obj that can be used to get the transaction, send it, estimate it. + func (_{{$contract.Type}} *{{$contract.Type}}Raw) TxObj(opts *bind.TransactOpts, method string, params ...interface{}) *bind.TxObject { + return _{{$contract.Type}}.Contract.contract.TxObj(opts, method, params...) + } + + // EstimateGas obtains an estimate for calling contract method with params as input values. + func (_{{$contract.Type}} *{{$contract.Type}}Raw) EstimateGas(opts *bind.TransactOpts, method string, params ...interface{}) (uint64, error) { + return _{{$contract.Type}}.Contract.contract.EstimateGas(opts, method, params...) + } + + {{range .Calls}} + // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) { + var out []interface{} + err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) + {{if .Structured}} + outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} }) + if err != nil { + return *outstruct, err + } + {{range $i, $t := .Normalized.Outputs}} + outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}} + + return *outstruct, err + {{else}} + if err != nil { + return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err + } + {{range $i, $t := .Normalized.Outputs}} + out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}} + + return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err + {{end}} + } + + // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) { + return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}}) + } + + {{end}} + + {{range .Transacts}} + // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) *bind.TxObject { + return _{{$contract.Type}}.contract.TxObj(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) + } + + // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) *bind.TxObject { + return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}}) + } + + {{end}} + + // TryParseLog attempts to parse a log. Returns the parsed log, evenName and whether it was succesfull + func (_{{$contract.Type}} *{{$contract.Type}}) TryParseLog(log types.Log) (eventName string, event interface{}, ok bool, err error) { + eventName, ok, err = _{{$contract.Type}}.contract.LogEventName(log) + if err != nil || !ok { + return "", nil, false, err + } + + switch eventName { {{range .Events}} + case "{{.Normalized.Name}}": + event, err = _{{$contract.Type}}.Parse{{.Normalized.Name}}(log){{end}} + } + if err != nil { + return "", nil, false, err + } + + return eventName, event, ok, nil + } + + {{if .Fallback}} + // Fallback is a paid mutator transaction binding the contract fallback function. + // + // Solidity: {{.Fallback.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { + return _{{$contract.Type}}.contract.RawTransact(opts, calldata) + } + + // Fallback is a paid mutator transaction binding the contract fallback function. + // + // Solidity: {{.Fallback.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) { + return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata) + } + + {{end}} + + {{if .Receive}} + // Receive is a paid mutator transaction binding the contract receive function. + // + // Solidity: {{.Receive.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}) Receive(opts *bind.TransactOpts) (*types.Transaction, error) { + return _{{$contract.Type}}.contract.RawTransact(opts, nil) // calldata is disallowed for receive function + } + + // Receive is a paid mutator transaction binding the contract receive function. + // + // Solidity: {{.Receive.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) { + return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts) + } + + {{end}} + + {{range .Events}} + // {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract. + type {{$contract.Type}}{{.Normalized.Name}}Iterator struct { + Event *{{$contract.Type}}{{.Normalized.Name}} // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration + } + // Next advances the iterator to the subsequent event, returning whether there + // are any more events found. In case of a retrieval or parsing error, false is + // returned and Error() can be queried for the exact failure. + func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Next() bool { + // If the iterator failed, stop iterating + if (it.fail != nil) { + return false + } + // If the iterator completed, deliver directly whatever's available + if (it.done) { + select { + case log := <-it.logs: + it.Event = new({{$contract.Type}}{{.Normalized.Name}}) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new({{$contract.Type}}{{.Normalized.Name}}) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } + } + // Error returns any retrieval or parsing error occurred during filtering. + func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Error() error { + return it.fail + } + // Close terminates the iteration process, releasing any pending underlying + // resources. + func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Close() error { + it.sub.Unsubscribe() + return nil + } + + // {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract. + type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}} + {{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}} + Raw types.Log // Blockchain specific contextual infos + } + + // Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) { + {{range .Normalized.Inputs}} + {{if .Indexed}}var {{.Name}}Rule []interface{} + for _, {{.Name}}Item := range {{.Name}} { + {{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item) + }{{end}}{{end}} + + logs, sub, err := _{{$contract.Type}}.contract.FilterLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}}) + if err != nil { + return nil, err + } + return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil + } + + // Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) { + {{range .Normalized.Inputs}} + {{if .Indexed}}var {{.Name}}Rule []interface{} + for _, {{.Name}}Item := range {{.Name}} { + {{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item) + }{{end}}{{end}} + + logs, sub, err := _{{$contract.Type}}.contract.WatchLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}}) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new({{$contract.Type}}{{.Normalized.Name}}) + if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil + } + + // Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) { + event := new({{$contract.Type}}{{.Normalized.Name}}) + if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil + } + + {{end}} +{{end}} +`
diff --git go-ethereum/accounts/scwallet/apdu.go celo/accounts/scwallet/apdu.go deleted file mode 100644 index 32b63edeb1fce2d29926739bf3926de440ea8e81..0000000000000000000000000000000000000000 --- go-ethereum/accounts/scwallet/apdu.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package scwallet - -import ( - "bytes" - "encoding/binary" - "fmt" -) - -// commandAPDU represents an application data unit sent to a smartcard. -type commandAPDU struct { - Cla, Ins, P1, P2 uint8 // Class, Instruction, Parameter 1, Parameter 2 - Data []byte // Command data - Le uint8 // Command data length -} - -// serialize serializes a command APDU. -func (ca commandAPDU) serialize() ([]byte, error) { - buf := new(bytes.Buffer) - - if err := binary.Write(buf, binary.BigEndian, ca.Cla); err != nil { - return nil, err - } - if err := binary.Write(buf, binary.BigEndian, ca.Ins); err != nil { - return nil, err - } - if err := binary.Write(buf, binary.BigEndian, ca.P1); err != nil { - return nil, err - } - if err := binary.Write(buf, binary.BigEndian, ca.P2); err != nil { - return nil, err - } - if len(ca.Data) > 0 { - if err := binary.Write(buf, binary.BigEndian, uint8(len(ca.Data))); err != nil { - return nil, err - } - if err := binary.Write(buf, binary.BigEndian, ca.Data); err != nil { - return nil, err - } - } - if err := binary.Write(buf, binary.BigEndian, ca.Le); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// responseAPDU represents an application data unit received from a smart card. -type responseAPDU struct { - Data []byte // response data - Sw1, Sw2 uint8 // status words 1 and 2 -} - -// deserialize deserializes a response APDU. -func (ra *responseAPDU) deserialize(data []byte) error { - if len(data) < 2 { - return fmt.Errorf("can not deserialize data: payload too short (%d < 2)", len(data)) - } - - ra.Data = make([]byte, len(data)-2) - - buf := bytes.NewReader(data) - if err := binary.Read(buf, binary.BigEndian, &ra.Data); err != nil { - return err - } - if err := binary.Read(buf, binary.BigEndian, &ra.Sw1); err != nil { - return err - } - if err := binary.Read(buf, binary.BigEndian, &ra.Sw2); err != nil { - return err - } - return nil -}
diff --git go-ethereum/accounts/scwallet/README.md celo/accounts/scwallet/README.md deleted file mode 100644 index 4313d9c6b2f8ecacf53657409572f53e482bbbe4..0000000000000000000000000000000000000000 --- go-ethereum/accounts/scwallet/README.md +++ /dev/null @@ -1,106 +0,0 @@ -# Using the smartcard wallet - -## Requirements - - * A USB smartcard reader - * A keycard that supports the status app - * PCSCD version 4.3 running on your system **Only version 4.3 is currently supported** - -## Preparing the smartcard - - **WARNING: FOILLOWING THESE INSTRUCTIONS WILL DESTROY THE MASTER KEY ON YOUR CARD. ONLY PROCEED IF NO FUNDS ARE ASSOCIATED WITH THESE ACCOUNTS** - - You can use status' [keycard-cli](https://github.com/status-im/keycard-cli) and you should get _at least_ version 2.1.1 of their [smartcard application](https://github.com/status-im/status-keycard/releases/download/2.2.1/keycard_v2.2.1.cap) - - You also need to make sure that the PCSC daemon is running on your system. - - Then, you can install the application to the card by typing: - - ``` - keycard install -a keycard_v2.2.1.cap && keycard init - ``` - - At the end of this process, you will be provided with a PIN, a PUK and a pairing password. Write them down, you'll need them shortly. - - Start `geth` with the `console` command. You will notice the following warning: - - ``` - WARN [04-09|16:58:38.898] Failed to open wallet url=keycard://044def09 err="smartcard: pairing password needed" - ``` - - Write down the URL (`keycard://044def09` in this example). Then ask `geth` to open the wallet: - - ``` - > personal.openWallet("keycard://044def09", "pairing password") - ``` - - The pairing password has been generated during the card initialization process. - - The process needs to be repeated once more with the PIN: - - ``` - > personal.openWallet("keycard://044def09", "PIN number") - ``` - - If everything goes well, you should see your new account when typing `personal` on the console: - - ``` - > personal - WARN [04-09|17:02:07.330] Smartcard wallet account derivation failed url=keycard://044def09 err="Unexpected response status Cla=0x80, Ins=0xd1, Sw=0x6985" - { - listAccounts: [], - listWallets: [{ - status: "Empty, waiting for initialization", - url: "keycard://044def09" - }], - ... - } - ``` - - So the communication with the card is working, but there is no key associated with this wallet. Let's create it: - - ``` - > personal.initializeWallet("keycard://044def09") - "tilt ... impact" - ``` - - You should get a list of words, this is your seed so write them down. Your wallet should now be initialized: - - ``` - > personal.listWallets - [{ - accounts: [{ - address: "0x678b7cd55c61917defb23546a41803c5bfefbc7a", - url: "keycard://044d/m/44'/60'/0'/0/0" - }], - status: "Online", - url: "keycard://044def09" - }] - ``` - - You're all set! - -## Usage - - 1. Start `geth` with the `console` command - 2. Check the card's URL by checking `personal.listWallets`: - -``` - listWallets: [{ - status: "Online, can derive public keys", - url: "keycard://a4d73015" - }] -``` - - 3. Open the wallet, you will be prompted for your pairing password, then PIN: - -``` -personal.openWallet("keycard://a4d73015") -``` - - 4. Check that creation was successful by typing e.g. `personal`. Then use it like a regular wallet. - -## Known issues - - * Starting geth with a valid card seems to make firefox crash. - * PCSC version 4.4 should work, but is currently untested
diff --git go-ethereum/accounts/scwallet/wallet.go celo/accounts/scwallet/wallet.go deleted file mode 100644 index 0caceabdcb01f1f7ba8264e89b00ffe4397d7a9e..0000000000000000000000000000000000000000 --- go-ethereum/accounts/scwallet/wallet.go +++ /dev/null @@ -1,1085 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package scwallet - -import ( - "bytes" - "context" - "crypto/hmac" - "crypto/sha256" - "crypto/sha512" - "encoding/asn1" - "encoding/binary" - "errors" - "fmt" - "math/big" - "regexp" - "sort" - "strings" - "sync" - "time" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - pcsc "github.com/gballet/go-libpcsclite" - "github.com/status-im/keycard-go/derivationpath" -) - -// ErrPairingPasswordNeeded is returned if opening the smart card requires pairing with a pairing -// password. In this case, the calling application should request user input to enter -// the pairing password and send it back. -var ErrPairingPasswordNeeded = errors.New("smartcard: pairing password needed") - -// ErrPINNeeded is returned if opening the smart card requires a PIN code. In -// this case, the calling application should request user input to enter the PIN -// and send it back. -var ErrPINNeeded = errors.New("smartcard: pin needed") - -// ErrPINUnblockNeeded is returned if opening the smart card requires a PIN code, -// but all PIN attempts have already been exhausted. In this case the calling -// application should request user input for the PUK and a new PIN code to set -// fo the card. -var ErrPINUnblockNeeded = errors.New("smartcard: pin unblock needed") - -// ErrAlreadyOpen is returned if the smart card is attempted to be opened, but -// there is already a paired and unlocked session. -var ErrAlreadyOpen = errors.New("smartcard: already open") - -// ErrPubkeyMismatch is returned if the public key recovered from a signature -// does not match the one expected by the user. -var ErrPubkeyMismatch = errors.New("smartcard: recovered public key mismatch") - -var ( - appletAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01, 0x01, 0x01} - // DerivationSignatureHash is used to derive the public key from the signature of this hash - DerivationSignatureHash = sha256.Sum256(common.Hash{}.Bytes()) -) - -// List of APDU command-related constants -const ( - claISO7816 = 0 - claSCWallet = 0x80 - - insSelect = 0xA4 - insGetResponse = 0xC0 - sw1GetResponse = 0x61 - sw1Ok = 0x90 - - insVerifyPin = 0x20 - insUnblockPin = 0x22 - insExportKey = 0xC2 - insSign = 0xC0 - insLoadKey = 0xD0 - insDeriveKey = 0xD1 - insStatus = 0xF2 -) - -// List of ADPU command parameters -const ( - P1DeriveKeyFromMaster = uint8(0x00) - P1DeriveKeyFromParent = uint8(0x01) - P1DeriveKeyFromCurrent = uint8(0x10) - statusP1WalletStatus = uint8(0x00) - statusP1Path = uint8(0x01) - signP1PrecomputedHash = uint8(0x01) - signP2OnlyBlock = uint8(0x81) - exportP1Any = uint8(0x00) - exportP2Pubkey = uint8(0x01) -) - -// Minimum time to wait between self derivation attempts, even it the user is -// requesting accounts like crazy. -const selfDeriveThrottling = time.Second - -// Wallet represents a smartcard wallet instance. -type Wallet struct { - Hub *Hub // A handle to the Hub that instantiated this wallet. - PublicKey []byte // The wallet's public key (used for communication and identification, not signing!) - - lock sync.Mutex // Lock that gates access to struct fields and communication with the card - card *pcsc.Card // A handle to the smartcard interface for the wallet. - session *Session // The secure communication session with the card - log log.Logger // Contextual logger to tag the base with its id - - deriveNextPaths []accounts.DerivationPath // Next derivation paths for account auto-discovery (multiple bases supported) - deriveNextAddrs []common.Address // Next derived account addresses for auto-discovery (multiple bases supported) - deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with - deriveReq chan chan struct{} // Channel to request a self-derivation on - deriveQuit chan chan error // Channel to terminate the self-deriver with -} - -// NewWallet constructs and returns a new Wallet instance. -func NewWallet(hub *Hub, card *pcsc.Card) *Wallet { - wallet := &Wallet{ - Hub: hub, - card: card, - } - return wallet -} - -// transmit sends an APDU to the smartcard and receives and decodes the response. -// It automatically handles requests by the card to fetch the return data separately, -// and returns an error if the response status code is not success. -func transmit(card *pcsc.Card, command *commandAPDU) (*responseAPDU, error) { - data, err := command.serialize() - if err != nil { - return nil, err - } - - responseData, _, err := card.Transmit(data) - if err != nil { - return nil, err - } - - response := new(responseAPDU) - if err = response.deserialize(responseData); err != nil { - return nil, err - } - - // Are we being asked to fetch the response separately? - if response.Sw1 == sw1GetResponse && (command.Cla != claISO7816 || command.Ins != insGetResponse) { - return transmit(card, &commandAPDU{ - Cla: claISO7816, - Ins: insGetResponse, - P1: 0, - P2: 0, - Data: nil, - Le: response.Sw2, - }) - } - - if response.Sw1 != sw1Ok { - return nil, fmt.Errorf("unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2) - } - - return response, nil -} - -// applicationInfo encodes information about the smartcard application - its -// instance UID and public key. -type applicationInfo struct { - InstanceUID []byte `asn1:"tag:15"` - PublicKey []byte `asn1:"tag:0"` -} - -// connect connects to the wallet application and establishes a secure channel with it. -// must be called before any other interaction with the wallet. -func (w *Wallet) connect() error { - w.lock.Lock() - defer w.lock.Unlock() - - appinfo, err := w.doselect() - if err != nil { - return err - } - - channel, err := NewSecureChannelSession(w.card, appinfo.PublicKey) - if err != nil { - return err - } - - w.PublicKey = appinfo.PublicKey - w.log = log.New("url", w.URL()) - w.session = &Session{ - Wallet: w, - Channel: channel, - } - return nil -} - -// doselect is an internal (unlocked) function to send a SELECT APDU to the card. -func (w *Wallet) doselect() (*applicationInfo, error) { - response, err := transmit(w.card, &commandAPDU{ - Cla: claISO7816, - Ins: insSelect, - P1: 4, - P2: 0, - Data: appletAID, - }) - if err != nil { - return nil, err - } - - appinfo := new(applicationInfo) - if _, err := asn1.UnmarshalWithParams(response.Data, appinfo, "tag:4"); err != nil { - return nil, err - } - return appinfo, nil -} - -// ping checks the card's status and returns an error if unsuccessful. -func (w *Wallet) ping() error { - w.lock.Lock() - defer w.lock.Unlock() - - // We can't ping if not paired - if !w.session.paired() { - return nil - } - if _, err := w.session.walletStatus(); err != nil { - return err - } - return nil -} - -// release releases any resources held by an open wallet instance. -func (w *Wallet) release() error { - if w.session != nil { - return w.session.release() - } - return nil -} - -// pair is an internal (unlocked) function for establishing a new pairing -// with the wallet. -func (w *Wallet) pair(puk []byte) error { - if w.session.paired() { - return fmt.Errorf("wallet already paired") - } - pairing, err := w.session.pair(puk) - if err != nil { - return err - } - if err = w.Hub.setPairing(w, &pairing); err != nil { - return err - } - return w.session.authenticate(pairing) -} - -// Unpair deletes an existing wallet pairing. -func (w *Wallet) Unpair(pin []byte) error { - w.lock.Lock() - defer w.lock.Unlock() - - if !w.session.paired() { - return fmt.Errorf("wallet %x not paired", w.PublicKey) - } - if err := w.session.verifyPin(pin); err != nil { - return fmt.Errorf("failed to verify pin: %s", err) - } - if err := w.session.unpair(); err != nil { - return fmt.Errorf("failed to unpair: %s", err) - } - if err := w.Hub.setPairing(w, nil); err != nil { - return err - } - return nil -} - -// URL retrieves the canonical path under which this wallet is reachable. It is -// user by upper layers to define a sorting order over all wallets from multiple -// backends. -func (w *Wallet) URL() accounts.URL { - return accounts.URL{ - Scheme: w.Hub.scheme, - Path: fmt.Sprintf("%x", w.PublicKey[1:5]), // Byte #0 isn't unique; 1:5 covers << 64K cards, bump to 1:9 for << 4M - } -} - -// Status returns a textual status to aid the user in the current state of the -// wallet. It also returns an error indicating any failure the wallet might have -// encountered. -func (w *Wallet) Status() (string, error) { - w.lock.Lock() - defer w.lock.Unlock() - - // If the card is not paired, we can only wait - if !w.session.paired() { - return "Unpaired, waiting for pairing password", nil - } - // Yay, we have an encrypted session, retrieve the actual status - status, err := w.session.walletStatus() - if err != nil { - return fmt.Sprintf("Failed: %v", err), err - } - switch { - case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0: - return "Bricked, waiting for full wipe", nil - case !w.session.verified && status.PinRetryCount == 0: - return fmt.Sprintf("Blocked, waiting for PUK (%d attempts left) and new PIN", status.PukRetryCount), nil - case !w.session.verified: - return fmt.Sprintf("Locked, waiting for PIN (%d attempts left)", status.PinRetryCount), nil - case !status.Initialized: - return "Empty, waiting for initialization", nil - default: - return "Online", nil - } -} - -// Open initializes access to a wallet instance. It is not meant to unlock or -// decrypt account keys, rather simply to establish a connection to hardware -// wallets and/or to access derivation seeds. -// -// The passphrase parameter may or may not be used by the implementation of a -// particular wallet instance. The reason there is no passwordless open method -// is to strive towards a uniform wallet handling, oblivious to the different -// backend providers. -// -// Please note, if you open a wallet, you must close it to release any allocated -// resources (especially important when working with hardware wallets). -func (w *Wallet) Open(passphrase string) error { - w.lock.Lock() - defer w.lock.Unlock() - - // If the session is already open, bail out - if w.session.verified { - return ErrAlreadyOpen - } - // If the smart card is not yet paired, attempt to do so either from a previous - // pairing key or form the supplied PUK code. - if !w.session.paired() { - // If a previous pairing exists, only ever try to use that - if pairing := w.Hub.pairing(w); pairing != nil { - if err := w.session.authenticate(*pairing); err != nil { - return fmt.Errorf("failed to authenticate card %x: %s", w.PublicKey[:4], err) - } - // Pairing still ok, fall through to PIN checks - } else { - // If no passphrase was supplied, request the PUK from the user - if passphrase == "" { - return ErrPairingPasswordNeeded - } - // Attempt to pair the smart card with the user supplied PUK - if err := w.pair([]byte(passphrase)); err != nil { - return err - } - // Pairing succeeded, fall through to PIN checks. This will of course fail, - // but we can't return ErrPINNeeded directly here because we don't know whether - // a PIN check or a PIN reset is needed. - passphrase = "" - } - } - // The smart card was successfully paired, retrieve its status to check whether - // PIN verification or unblocking is needed. - status, err := w.session.walletStatus() - if err != nil { - return err - } - // Request the appropriate next authentication data, or use the one supplied - switch { - case passphrase == "" && status.PinRetryCount > 0: - return ErrPINNeeded - case passphrase == "": - return ErrPINUnblockNeeded - case status.PinRetryCount > 0: - if !regexp.MustCompile(`^[0-9]{6,}$`).MatchString(passphrase) { - w.log.Error("PIN needs to be at least 6 digits") - return ErrPINNeeded - } - if err := w.session.verifyPin([]byte(passphrase)); err != nil { - return err - } - default: - if !regexp.MustCompile(`^[0-9]{12,}$`).MatchString(passphrase) { - w.log.Error("PUK needs to be at least 12 digits") - return ErrPINUnblockNeeded - } - if err := w.session.unblockPin([]byte(passphrase)); err != nil { - return err - } - } - // Smart card paired and unlocked, initialize and register - w.deriveReq = make(chan chan struct{}) - w.deriveQuit = make(chan chan error) - - go w.selfDerive() - - // Notify anyone listening for wallet events that a new device is accessible - go w.Hub.updateFeed.Send(accounts.WalletEvent{Wallet: w, Kind: accounts.WalletOpened}) - - return nil -} - -// Close stops and closes the wallet, freeing any resources. -func (w *Wallet) Close() error { - // Ensure the wallet was opened - w.lock.Lock() - dQuit := w.deriveQuit - w.lock.Unlock() - - // Terminate the self-derivations - var derr error - if dQuit != nil { - errc := make(chan error) - dQuit <- errc - derr = <-errc // Save for later, we *must* close the USB - } - // Terminate the device connection - w.lock.Lock() - defer w.lock.Unlock() - - w.deriveQuit = nil - w.deriveReq = nil - - if err := w.release(); err != nil { - return err - } - return derr -} - -// selfDerive is an account derivation loop that upon request attempts to find -// new non-zero accounts. -func (w *Wallet) selfDerive() { - w.log.Debug("Smart card wallet self-derivation started") - defer w.log.Debug("Smart card wallet self-derivation stopped") - - // Execute self-derivations until termination or error - var ( - reqc chan struct{} - errc chan error - err error - ) - for errc == nil && err == nil { - // Wait until either derivation or termination is requested - select { - case errc = <-w.deriveQuit: - // Termination requested - continue - case reqc = <-w.deriveReq: - // Account discovery requested - } - // Derivation needs a chain and device access, skip if either unavailable - w.lock.Lock() - if w.session == nil || w.deriveChain == nil { - w.lock.Unlock() - reqc <- struct{}{} - continue - } - pairing := w.Hub.pairing(w) - - // Device lock obtained, derive the next batch of accounts - var ( - paths []accounts.DerivationPath - nextAcc accounts.Account - - nextPaths = append([]accounts.DerivationPath{}, w.deriveNextPaths...) - nextAddrs = append([]common.Address{}, w.deriveNextAddrs...) - - context = context.Background() - ) - for i := 0; i < len(nextAddrs); i++ { - for empty := false; !empty; { - // Retrieve the next derived Ethereum account - if nextAddrs[i] == (common.Address{}) { - if nextAcc, err = w.session.derive(nextPaths[i]); err != nil { - w.log.Warn("Smartcard wallet account derivation failed", "err", err) - break - } - nextAddrs[i] = nextAcc.Address - } - // Check the account's status against the current chain state - var ( - balance *big.Int - nonce uint64 - ) - balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil) - if err != nil { - w.log.Warn("Smartcard wallet balance retrieval failed", "err", err) - break - } - nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil) - if err != nil { - w.log.Warn("Smartcard wallet nonce retrieval failed", "err", err) - break - } - // If the next account is empty, stop self-derivation, but add for the last base path - if balance.Sign() == 0 && nonce == 0 { - empty = true - if i < len(nextAddrs)-1 { - break - } - } - // We've just self-derived a new account, start tracking it locally - path := make(accounts.DerivationPath, len(nextPaths[i])) - copy(path[:], nextPaths[i][:]) - paths = append(paths, path) - - // Display a log message to the user for new (or previously empty accounts) - if _, known := pairing.Accounts[nextAddrs[i]]; !known || !empty || nextAddrs[i] != w.deriveNextAddrs[i] { - w.log.Info("Smartcard wallet discovered new account", "address", nextAddrs[i], "path", path, "balance", balance, "nonce", nonce) - } - pairing.Accounts[nextAddrs[i]] = path - - // Fetch the next potential account - if !empty { - nextAddrs[i] = common.Address{} - nextPaths[i][len(nextPaths[i])-1]++ - } - } - } - // If there are new accounts, write them out - if len(paths) > 0 { - err = w.Hub.setPairing(w, pairing) - } - // Shift the self-derivation forward - w.deriveNextAddrs = nextAddrs - w.deriveNextPaths = nextPaths - - // Self derivation complete, release device lock - w.lock.Unlock() - - // Notify the user of termination and loop after a bit of time (to avoid trashing) - reqc <- struct{}{} - if err == nil { - select { - case errc = <-w.deriveQuit: - // Termination requested, abort - case <-time.After(selfDeriveThrottling): - // Waited enough, willing to self-derive again - } - } - } - // In case of error, wait for termination - if err != nil { - w.log.Debug("Smartcard wallet self-derivation failed", "err", err) - errc = <-w.deriveQuit - } - errc <- err -} - -// Accounts retrieves the list of signing accounts the wallet is currently aware -// of. For hierarchical deterministic wallets, the list will not be exhaustive, -// rather only contain the accounts explicitly pinned during account derivation. -func (w *Wallet) Accounts() []accounts.Account { - // Attempt self-derivation if it's running - reqc := make(chan struct{}, 1) - select { - case w.deriveReq <- reqc: - // Self-derivation request accepted, wait for it - <-reqc - default: - // Self-derivation offline, throttled or busy, skip - } - - w.lock.Lock() - defer w.lock.Unlock() - - if pairing := w.Hub.pairing(w); pairing != nil { - ret := make([]accounts.Account, 0, len(pairing.Accounts)) - for address, path := range pairing.Accounts { - ret = append(ret, w.makeAccount(address, path)) - } - sort.Sort(accounts.AccountsByURL(ret)) - return ret - } - return nil -} - -func (w *Wallet) makeAccount(address common.Address, path accounts.DerivationPath) accounts.Account { - return accounts.Account{ - Address: address, - URL: accounts.URL{ - Scheme: w.Hub.scheme, - Path: fmt.Sprintf("%x/%s", w.PublicKey[1:3], path.String()), - }, - } -} - -// Contains returns whether an account is part of this particular wallet or not. -func (w *Wallet) Contains(account accounts.Account) bool { - if pairing := w.Hub.pairing(w); pairing != nil { - _, ok := pairing.Accounts[account.Address] - return ok - } - return false -} - -// Initialize installs a keypair generated from the provided key into the wallet. -func (w *Wallet) Initialize(seed []byte) error { - go w.selfDerive() - // DO NOT lock at this stage, as the initialize - // function relies on Status() - return w.session.initialize(seed) -} - -// Derive attempts to explicitly derive a hierarchical deterministic account at -// the specified derivation path. If requested, the derived account will be added -// to the wallet's tracked account list. -func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { - w.lock.Lock() - defer w.lock.Unlock() - - account, err := w.session.derive(path) - if err != nil { - return accounts.Account{}, err - } - - if pin { - pairing := w.Hub.pairing(w) - pairing.Accounts[account.Address] = path - if err := w.Hub.setPairing(w, pairing); err != nil { - return accounts.Account{}, err - } - } - - return account, nil -} - -// SelfDerive sets a base account derivation path from which the wallet attempts -// to discover non zero accounts and automatically add them to list of tracked -// accounts. -// -// Note, self derivation will increment the last component of the specified path -// opposed to decending into a child path to allow discovering accounts starting -// from non zero components. -// -// Some hardware wallets switched derivation paths through their evolution, so -// this method supports providing multiple bases to discover old user accounts -// too. Only the last base will be used to derive the next empty account. -// -// You can disable automatic account discovery by calling SelfDerive with a nil -// chain state reader. -func (w *Wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) { - w.lock.Lock() - defer w.lock.Unlock() - - w.deriveNextPaths = make([]accounts.DerivationPath, len(bases)) - for i, base := range bases { - w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base)) - copy(w.deriveNextPaths[i][:], base[:]) - } - w.deriveNextAddrs = make([]common.Address, len(bases)) - w.deriveChain = chain -} - -// SignData requests the wallet to sign the hash of the given data. -// -// It looks up the account specified either solely via its address contained within, -// or optionally with the aid of any location metadata from the embedded URL field. -// -// If the wallet requires additional authentication to sign the request (e.g. -// a password to decrypt the account, or a PIN code o verify the transaction), -// an AuthNeededError instance will be returned, containing infos for the user -// about which fields or actions are needed. The user may retry by providing -// the needed details via SignDataWithPassphrase, or by other means (e.g. unlock -// the account in a keystore). -func (w *Wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) { - return w.signHash(account, crypto.Keccak256(data)) -} - -func (w *Wallet) signHash(account accounts.Account, hash []byte) ([]byte, error) { - w.lock.Lock() - defer w.lock.Unlock() - - path, err := w.findAccountPath(account) - if err != nil { - return nil, err - } - - return w.session.sign(path, hash) -} - -// SignTx requests the wallet to sign the given transaction. -// -// It looks up the account specified either solely via its address contained within, -// or optionally with the aid of any location metadata from the embedded URL field. -// -// If the wallet requires additional authentication to sign the request (e.g. -// a password to decrypt the account, or a PIN code o verify the transaction), -// an AuthNeededError instance will be returned, containing infos for the user -// about which fields or actions are needed. The user may retry by providing -// the needed details via SignTxWithPassphrase, or by other means (e.g. unlock -// the account in a keystore). -func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { - signer := types.LatestSignerForChainID(chainID) - hash := signer.Hash(tx) - sig, err := w.signHash(account, hash[:]) - if err != nil { - return nil, err - } - return tx.WithSignature(signer, sig) -} - -// SignDataWithPassphrase requests the wallet to sign the given hash with the -// given passphrase as extra authentication information. -// -// It looks up the account specified either solely via its address contained within, -// or optionally with the aid of any location metadata from the embedded URL field. -func (w *Wallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) { - return w.signHashWithPassphrase(account, passphrase, crypto.Keccak256(data)) -} - -func (w *Wallet) signHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { - if !w.session.verified { - if err := w.Open(passphrase); err != nil { - return nil, err - } - } - - return w.signHash(account, hash) -} - -// SignText requests the wallet to sign the hash of a given piece of data, prefixed -// by the Ethereum prefix scheme -// It looks up the account specified either solely via its address contained within, -// or optionally with the aid of any location metadata from the embedded URL field. -// -// If the wallet requires additional authentication to sign the request (e.g. -// a password to decrypt the account, or a PIN code o verify the transaction), -// an AuthNeededError instance will be returned, containing infos for the user -// about which fields or actions are needed. The user may retry by providing -// the needed details via SignHashWithPassphrase, or by other means (e.g. unlock -// the account in a keystore). -func (w *Wallet) SignText(account accounts.Account, text []byte) ([]byte, error) { - return w.signHash(account, accounts.TextHash(text)) -} - -// SignTextWithPassphrase implements accounts.Wallet, attempting to sign the -// given hash with the given account using passphrase as extra authentication -func (w *Wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) { - return w.signHashWithPassphrase(account, passphrase, crypto.Keccak256(accounts.TextHash(text))) -} - -// SignTxWithPassphrase requests the wallet to sign the given transaction, with the -// given passphrase as extra authentication information. -// -// It looks up the account specified either solely via its address contained within, -// or optionally with the aid of any location metadata from the embedded URL field. -func (w *Wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { - if !w.session.verified { - if err := w.Open(passphrase); err != nil { - return nil, err - } - } - return w.SignTx(account, tx, chainID) -} - -// findAccountPath returns the derivation path for the provided account. -// It first checks for the address in the list of pinned accounts, and if it is -// not found, attempts to parse the derivation path from the account's URL. -func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationPath, error) { - pairing := w.Hub.pairing(w) - if path, ok := pairing.Accounts[account.Address]; ok { - return path, nil - } - - // Look for the path in the URL - if account.URL.Scheme != w.Hub.scheme { - return nil, fmt.Errorf("scheme %s does not match wallet scheme %s", account.URL.Scheme, w.Hub.scheme) - } - - parts := strings.SplitN(account.URL.Path, "/", 2) - if len(parts) != 2 { - return nil, fmt.Errorf("invalid URL format: %s", account.URL) - } - - if parts[0] != fmt.Sprintf("%x", w.PublicKey[1:3]) { - return nil, fmt.Errorf("URL %s is not for this wallet", account.URL) - } - - return accounts.ParseDerivationPath(parts[1]) -} - -// Session represents a secured communication session with the wallet. -type Session struct { - Wallet *Wallet // A handle to the wallet that opened the session - Channel *SecureChannelSession // A secure channel for encrypted messages - verified bool // Whether the pin has been verified in this session. -} - -// pair establishes a new pairing over this channel, using the provided secret. -func (s *Session) pair(secret []byte) (smartcardPairing, error) { - err := s.Channel.Pair(secret) - if err != nil { - return smartcardPairing{}, err - } - - return smartcardPairing{ - PublicKey: s.Wallet.PublicKey, - PairingIndex: s.Channel.PairingIndex, - PairingKey: s.Channel.PairingKey, - Accounts: make(map[common.Address]accounts.DerivationPath), - }, nil -} - -// unpair deletes an existing pairing. -func (s *Session) unpair() error { - if !s.verified { - return fmt.Errorf("unpair requires that the PIN be verified") - } - return s.Channel.Unpair() -} - -// verifyPin unlocks a wallet with the provided pin. -func (s *Session) verifyPin(pin []byte) error { - if _, err := s.Channel.transmitEncrypted(claSCWallet, insVerifyPin, 0, 0, pin); err != nil { - return err - } - s.verified = true - return nil -} - -// unblockPin unblocks a wallet with the provided puk and resets the pin to the -// new one specified. -func (s *Session) unblockPin(pukpin []byte) error { - if _, err := s.Channel.transmitEncrypted(claSCWallet, insUnblockPin, 0, 0, pukpin); err != nil { - return err - } - s.verified = true - return nil -} - -// release releases resources associated with the channel. -func (s *Session) release() error { - return s.Wallet.card.Disconnect(pcsc.LeaveCard) -} - -// paired returns true if a valid pairing exists. -func (s *Session) paired() bool { - return s.Channel.PairingKey != nil -} - -// authenticate uses an existing pairing to establish a secure channel. -func (s *Session) authenticate(pairing smartcardPairing) error { - if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) { - return fmt.Errorf("cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey) - } - s.Channel.PairingKey = pairing.PairingKey - s.Channel.PairingIndex = pairing.PairingIndex - return s.Channel.Open() -} - -// walletStatus describes a smartcard wallet's status information. -type walletStatus struct { - PinRetryCount int // Number of remaining PIN retries - PukRetryCount int // Number of remaining PUK retries - Initialized bool // Whether the card has been initialized with a private key -} - -// walletStatus fetches the wallet's status from the card. -func (s *Session) walletStatus() (*walletStatus, error) { - response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1WalletStatus, 0, nil) - if err != nil { - return nil, err - } - - status := new(walletStatus) - if _, err := asn1.UnmarshalWithParams(response.Data, status, "tag:3"); err != nil { - return nil, err - } - return status, nil -} - -// derivationPath fetches the wallet's current derivation path from the card. -//lint:ignore U1000 needs to be added to the console interface -func (s *Session) derivationPath() (accounts.DerivationPath, error) { - response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil) - if err != nil { - return nil, err - } - buf := bytes.NewReader(response.Data) - path := make(accounts.DerivationPath, len(response.Data)/4) - return path, binary.Read(buf, binary.BigEndian, &path) -} - -// initializeData contains data needed to initialize the smartcard wallet. -type initializeData struct { - PublicKey []byte `asn1:"tag:0"` - PrivateKey []byte `asn1:"tag:1"` - ChainCode []byte `asn1:"tag:2"` -} - -// initialize initializes the card with new key data. -func (s *Session) initialize(seed []byte) error { - // Check that the wallet isn't currently initialized, - // otherwise the key would be overwritten. - status, err := s.Wallet.Status() - if err != nil { - return err - } - if status == "Online" { - return fmt.Errorf("card is already initialized, cowardly refusing to proceed") - } - - s.Wallet.lock.Lock() - defer s.Wallet.lock.Unlock() - - // HMAC the seed to produce the private key and chain code - mac := hmac.New(sha512.New, []byte("Bitcoin seed")) - mac.Write(seed) - seed = mac.Sum(nil) - - key, err := crypto.ToECDSA(seed[:32]) - if err != nil { - return err - } - - id := initializeData{} - id.PublicKey = crypto.FromECDSAPub(&key.PublicKey) - id.PrivateKey = seed[:32] - id.ChainCode = seed[32:] - data, err := asn1.Marshal(id) - if err != nil { - return err - } - - // Nasty hack to force the top-level struct tag to be context-specific - data[0] = 0xA1 - - _, err = s.Channel.transmitEncrypted(claSCWallet, insLoadKey, 0x02, 0, data) - return err -} - -// derive derives a new HD key path on the card. -func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error) { - startingPoint, path, err := derivationpath.Decode(path.String()) - if err != nil { - return accounts.Account{}, err - } - - var p1 uint8 - switch startingPoint { - case derivationpath.StartingPointMaster: - p1 = P1DeriveKeyFromMaster - case derivationpath.StartingPointParent: - p1 = P1DeriveKeyFromParent - case derivationpath.StartingPointCurrent: - p1 = P1DeriveKeyFromCurrent - default: - return accounts.Account{}, fmt.Errorf("invalid startingPoint %d", startingPoint) - } - - data := new(bytes.Buffer) - for _, segment := range path { - if err := binary.Write(data, binary.BigEndian, segment); err != nil { - return accounts.Account{}, err - } - } - - _, err = s.Channel.transmitEncrypted(claSCWallet, insDeriveKey, p1, 0, data.Bytes()) - if err != nil { - return accounts.Account{}, err - } - - response, err := s.Channel.transmitEncrypted(claSCWallet, insSign, 0, 0, DerivationSignatureHash[:]) - if err != nil { - return accounts.Account{}, err - } - - sigdata := new(signatureData) - if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil { - return accounts.Account{}, err - } - rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes() - sig := make([]byte, 65) - copy(sig[32-len(rbytes):32], rbytes) - copy(sig[64-len(sbytes):64], sbytes) - - if err := confirmPublicKey(sig, sigdata.PublicKey); err != nil { - return accounts.Account{}, err - } - pub, err := crypto.UnmarshalPubkey(sigdata.PublicKey) - if err != nil { - return accounts.Account{}, err - } - return s.Wallet.makeAccount(crypto.PubkeyToAddress(*pub), path), nil -} - -// keyExport contains information on an exported keypair. -//lint:ignore U1000 needs to be added to the console interface -type keyExport struct { - PublicKey []byte `asn1:"tag:0"` - PrivateKey []byte `asn1:"tag:1,optional"` -} - -// publicKey returns the public key for the current derivation path. -//lint:ignore U1000 needs to be added to the console interface -func (s *Session) publicKey() ([]byte, error) { - response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil) - if err != nil { - return nil, err - } - keys := new(keyExport) - if _, err := asn1.UnmarshalWithParams(response.Data, keys, "tag:1"); err != nil { - return nil, err - } - return keys.PublicKey, nil -} - -// signatureData contains information on a signature - the signature itself and -// the corresponding public key. -type signatureData struct { - PublicKey []byte `asn1:"tag:0"` - Signature struct { - R *big.Int - S *big.Int - } -} - -// sign asks the card to sign a message, and returns a valid signature after -// recovering the v value. -func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error) { - startTime := time.Now() - _, err := s.derive(path) - if err != nil { - return nil, err - } - deriveTime := time.Now() - - response, err := s.Channel.transmitEncrypted(claSCWallet, insSign, signP1PrecomputedHash, signP2OnlyBlock, hash) - if err != nil { - return nil, err - } - sigdata := new(signatureData) - if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil { - return nil, err - } - // Serialize the signature - rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes() - sig := make([]byte, 65) - copy(sig[32-len(rbytes):32], rbytes) - copy(sig[64-len(sbytes):64], sbytes) - - // Recover the V value. - sig, err = makeRecoverableSignature(hash, sig, sigdata.PublicKey) - if err != nil { - return nil, err - } - log.Debug("Signed using smartcard", "deriveTime", deriveTime.Sub(startTime), "signingTime", time.Since(deriveTime)) - - return sig, nil -} - -// confirmPublicKey confirms that the given signature belongs to the specified key. -func confirmPublicKey(sig, pubkey []byte) error { - _, err := makeRecoverableSignature(DerivationSignatureHash[:], sig, pubkey) - return err -} - -// makeRecoverableSignature uses a signature and an expected public key to -// recover the v value and produce a recoverable signature. -func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error) { - var libraryError error - for v := 0; v < 2; v++ { - sig[64] = byte(v) - if pubkey, err := crypto.Ecrecover(hash, sig); err == nil { - if bytes.Equal(pubkey, expectedPubkey) { - return sig, nil - } - } else { - libraryError = err - } - } - if libraryError != nil { - return nil, libraryError - } - return nil, ErrPubkeyMismatch -}
diff --git go-ethereum/accounts/scwallet/hub.go celo/accounts/scwallet/hub.go deleted file mode 100644 index fc4f3c5eb7c065a04e414ba150e362214b0a37bf..0000000000000000000000000000000000000000 --- go-ethereum/accounts/scwallet/hub.go +++ /dev/null @@ -1,302 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// This package implements support for smartcard-based hardware wallets such as -// the one written by Status: https://github.com/status-im/hardware-wallet -// -// This implementation of smartcard wallets have a different interaction process -// to other types of hardware wallet. The process works like this: -// -// 1. (First use with a given client) Establish a pairing between hardware -// wallet and client. This requires a secret value called a 'pairing password'. -// You can pair with an unpaired wallet with `personal.openWallet(URI, pairing password)`. -// 2. (First use only) Initialize the wallet, which generates a keypair, stores -// it on the wallet, and returns it so the user can back it up. You can -// initialize a wallet with `personal.initializeWallet(URI)`. -// 3. Connect to the wallet using the pairing information established in step 1. -// You can connect to a paired wallet with `personal.openWallet(URI, PIN)`. -// 4. Interact with the wallet as normal. - -package scwallet - -import ( - "encoding/json" - "io/ioutil" - "os" - "path/filepath" - "sort" - "sync" - "time" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" - pcsc "github.com/gballet/go-libpcsclite" -) - -// Scheme is the URI prefix for smartcard wallets. -const Scheme = "keycard" - -// refreshCycle is the maximum time between wallet refreshes (if USB hotplug -// notifications don't work). -const refreshCycle = time.Second - -// refreshThrottling is the minimum time between wallet refreshes to avoid thrashing. -const refreshThrottling = 500 * time.Millisecond - -// smartcardPairing contains information about a smart card we have paired with -// or might pair with the hub. -type smartcardPairing struct { - PublicKey []byte `json:"publicKey"` - PairingIndex uint8 `json:"pairingIndex"` - PairingKey []byte `json:"pairingKey"` - Accounts map[common.Address]accounts.DerivationPath `json:"accounts"` -} - -// Hub is a accounts.Backend that can find and handle generic PC/SC hardware wallets. -type Hub struct { - scheme string // Protocol scheme prefixing account and wallet URLs. - - context *pcsc.Client - datadir string - pairings map[string]smartcardPairing - - refreshed time.Time // Time instance when the list of wallets was last refreshed - wallets map[string]*Wallet // Mapping from reader names to wallet instances - updateFeed event.Feed // Event feed to notify wallet additions/removals - updateScope event.SubscriptionScope // Subscription scope tracking current live listeners - updating bool // Whether the event notification loop is running - - quit chan chan error - - stateLock sync.RWMutex // Protects the internals of the hub from racey access -} - -func (hub *Hub) readPairings() error { - hub.pairings = make(map[string]smartcardPairing) - pairingFile, err := os.Open(filepath.Join(hub.datadir, "smartcards.json")) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } - - pairingData, err := ioutil.ReadAll(pairingFile) - if err != nil { - return err - } - var pairings []smartcardPairing - if err := json.Unmarshal(pairingData, &pairings); err != nil { - return err - } - - for _, pairing := range pairings { - hub.pairings[string(pairing.PublicKey)] = pairing - } - return nil -} - -func (hub *Hub) writePairings() error { - pairingFile, err := os.OpenFile(filepath.Join(hub.datadir, "smartcards.json"), os.O_RDWR|os.O_CREATE, 0755) - if err != nil { - return err - } - defer pairingFile.Close() - - pairings := make([]smartcardPairing, 0, len(hub.pairings)) - for _, pairing := range hub.pairings { - pairings = append(pairings, pairing) - } - - pairingData, err := json.Marshal(pairings) - if err != nil { - return err - } - - if _, err := pairingFile.Write(pairingData); err != nil { - return err - } - - return nil -} - -func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing { - if pairing, ok := hub.pairings[string(wallet.PublicKey)]; ok { - return &pairing - } - return nil -} - -func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error { - if pairing == nil { - delete(hub.pairings, string(wallet.PublicKey)) - } else { - hub.pairings[string(wallet.PublicKey)] = *pairing - } - return hub.writePairings() -} - -// NewHub creates a new hardware wallet manager for smartcards. -func NewHub(daemonPath string, scheme string, datadir string) (*Hub, error) { - context, err := pcsc.EstablishContext(daemonPath, pcsc.ScopeSystem) - if err != nil { - return nil, err - } - hub := &Hub{ - scheme: scheme, - context: context, - datadir: datadir, - wallets: make(map[string]*Wallet), - quit: make(chan chan error), - } - if err := hub.readPairings(); err != nil { - return nil, err - } - hub.refreshWallets() - return hub, nil -} - -// Wallets implements accounts.Backend, returning all the currently tracked smart -// cards that appear to be hardware wallets. -func (hub *Hub) Wallets() []accounts.Wallet { - // Make sure the list of wallets is up to date - hub.refreshWallets() - - hub.stateLock.RLock() - defer hub.stateLock.RUnlock() - - cpy := make([]accounts.Wallet, 0, len(hub.wallets)) - for _, wallet := range hub.wallets { - cpy = append(cpy, wallet) - } - sort.Sort(accounts.WalletsByURL(cpy)) - return cpy -} - -// refreshWallets scans the devices attached to the machine and updates the -// list of wallets based on the found devices. -func (hub *Hub) refreshWallets() { - // Don't scan the USB like crazy it the user fetches wallets in a loop - hub.stateLock.RLock() - elapsed := time.Since(hub.refreshed) - hub.stateLock.RUnlock() - - if elapsed < refreshThrottling { - return - } - // Retrieve all the smart card reader to check for cards - readers, err := hub.context.ListReaders() - if err != nil { - // This is a perverted hack, the scard library returns an error if no card - // readers are present instead of simply returning an empty list. We don't - // want to fill the user's log with errors, so filter those out. - if err.Error() != "scard: Cannot find a smart card reader." { - log.Error("Failed to enumerate smart card readers", "err", err) - return - } - } - // Transform the current list of wallets into the new one - hub.stateLock.Lock() - - events := []accounts.WalletEvent{} - seen := make(map[string]struct{}) - - for _, reader := range readers { - // Mark the reader as present - seen[reader] = struct{}{} - - // If we already know about this card, skip to the next reader, otherwise clean up - if wallet, ok := hub.wallets[reader]; ok { - if err := wallet.ping(); err == nil { - continue - } - wallet.Close() - events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped}) - delete(hub.wallets, reader) - } - // New card detected, try to connect to it - card, err := hub.context.Connect(reader, pcsc.ShareShared, pcsc.ProtocolAny) - if err != nil { - log.Debug("Failed to open smart card", "reader", reader, "err", err) - continue - } - wallet := NewWallet(hub, card) - if err = wallet.connect(); err != nil { - log.Debug("Failed to connect to smart card", "reader", reader, "err", err) - card.Disconnect(pcsc.LeaveCard) - continue - } - // Card connected, start tracking in amongs the wallets - hub.wallets[reader] = wallet - events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived}) - } - // Remove any wallets no longer present - for reader, wallet := range hub.wallets { - if _, ok := seen[reader]; !ok { - wallet.Close() - events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped}) - delete(hub.wallets, reader) - } - } - hub.refreshed = time.Now() - hub.stateLock.Unlock() - - for _, event := range events { - hub.updateFeed.Send(event) - } -} - -// Subscribe implements accounts.Backend, creating an async subscription to -// receive notifications on the addition or removal of smart card wallets. -func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription { - // We need the mutex to reliably start/stop the update loop - hub.stateLock.Lock() - defer hub.stateLock.Unlock() - - // Subscribe the caller and track the subscriber count - sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink)) - - // Subscribers require an active notification loop, start it - if !hub.updating { - hub.updating = true - go hub.updater() - } - return sub -} - -// updater is responsible for maintaining an up-to-date list of wallets managed -// by the smart card hub, and for firing wallet addition/removal events. -func (hub *Hub) updater() { - for { - // TODO: Wait for a USB hotplug event (not supported yet) or a refresh timeout - // <-hub.changes - time.Sleep(refreshCycle) - - // Run the wallet refresher - hub.refreshWallets() - - // If all our subscribers left, stop the updater - hub.stateLock.Lock() - if hub.updateScope.Count() == 0 { - hub.updating = false - hub.stateLock.Unlock() - return - } - hub.stateLock.Unlock() - } -}
diff --git go-ethereum/accounts/scwallet/securechannel.go celo/accounts/scwallet/securechannel.go deleted file mode 100644 index b3ae6a73d472950bebfd4d6aa2ae89b84ec33c73..0000000000000000000000000000000000000000 --- go-ethereum/accounts/scwallet/securechannel.go +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package scwallet - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/elliptic" - "crypto/rand" - "crypto/sha256" - "crypto/sha512" - "fmt" - - "github.com/ethereum/go-ethereum/crypto" - pcsc "github.com/gballet/go-libpcsclite" - "golang.org/x/crypto/pbkdf2" - "golang.org/x/text/unicode/norm" -) - -const ( - maxPayloadSize = 223 - pairP1FirstStep = 0 - pairP1LastStep = 1 - - scSecretLength = 32 - scBlockSize = 16 - - insOpenSecureChannel = 0x10 - insMutuallyAuthenticate = 0x11 - insPair = 0x12 - insUnpair = 0x13 - - pairingSalt = "Keycard Pairing Password Salt" -) - -// SecureChannelSession enables secure communication with a hardware wallet. -type SecureChannelSession struct { - card *pcsc.Card // A handle to the smartcard for communication - secret []byte // A shared secret generated from our ECDSA keys - publicKey []byte // Our own ephemeral public key - PairingKey []byte // A permanent shared secret for a pairing, if present - sessionEncKey []byte // The current session encryption key - sessionMacKey []byte // The current session MAC key - iv []byte // The current IV - PairingIndex uint8 // The pairing index -} - -// NewSecureChannelSession creates a new secure channel for the given card and public key. -func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSession, error) { - // Generate an ECDSA keypair for ourselves - key, err := crypto.GenerateKey() - if err != nil { - return nil, err - } - cardPublic, err := crypto.UnmarshalPubkey(keyData) - if err != nil { - return nil, fmt.Errorf("could not unmarshal public key from card: %v", err) - } - secret, _ := key.Curve.ScalarMult(cardPublic.X, cardPublic.Y, key.D.Bytes()) - return &SecureChannelSession{ - card: card, - secret: secret.Bytes(), - publicKey: elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y), - }, nil -} - -// Pair establishes a new pairing with the smartcard. -func (s *SecureChannelSession) Pair(pairingPassword []byte) error { - secretHash := pbkdf2.Key(norm.NFKD.Bytes(pairingPassword), norm.NFKD.Bytes([]byte(pairingSalt)), 50000, 32, sha256.New) - - challenge := make([]byte, 32) - if _, err := rand.Read(challenge); err != nil { - return err - } - - response, err := s.pair(pairP1FirstStep, challenge) - if err != nil { - return err - } - - md := sha256.New() - md.Write(secretHash[:]) - md.Write(challenge) - - expectedCryptogram := md.Sum(nil) - cardCryptogram := response.Data[:32] - cardChallenge := response.Data[32:64] - - if !bytes.Equal(expectedCryptogram, cardCryptogram) { - return fmt.Errorf("invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram) - } - - md.Reset() - md.Write(secretHash[:]) - md.Write(cardChallenge) - response, err = s.pair(pairP1LastStep, md.Sum(nil)) - if err != nil { - return err - } - - md.Reset() - md.Write(secretHash[:]) - md.Write(response.Data[1:]) - s.PairingKey = md.Sum(nil) - s.PairingIndex = response.Data[0] - - return nil -} - -// Unpair disestablishes an existing pairing. -func (s *SecureChannelSession) Unpair() error { - if s.PairingKey == nil { - return fmt.Errorf("cannot unpair: not paired") - } - - _, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{}) - if err != nil { - return err - } - s.PairingKey = nil - // Close channel - s.iv = nil - return nil -} - -// Open initializes the secure channel. -func (s *SecureChannelSession) Open() error { - if s.iv != nil { - return fmt.Errorf("session already opened") - } - - response, err := s.open() - if err != nil { - return err - } - - // Generate the encryption/mac key by hashing our shared secret, - // pairing key, and the first bytes returned from the Open APDU. - md := sha512.New() - md.Write(s.secret) - md.Write(s.PairingKey) - md.Write(response.Data[:scSecretLength]) - keyData := md.Sum(nil) - s.sessionEncKey = keyData[:scSecretLength] - s.sessionMacKey = keyData[scSecretLength : scSecretLength*2] - - // The IV is the last bytes returned from the Open APDU. - s.iv = response.Data[scSecretLength:] - - return s.mutuallyAuthenticate() -} - -// mutuallyAuthenticate is an internal method to authenticate both ends of the -// connection. -func (s *SecureChannelSession) mutuallyAuthenticate() error { - data := make([]byte, scSecretLength) - if _, err := rand.Read(data); err != nil { - return err - } - - response, err := s.transmitEncrypted(claSCWallet, insMutuallyAuthenticate, 0, 0, data) - if err != nil { - return err - } - if response.Sw1 != 0x90 || response.Sw2 != 0x00 { - return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2) - } - - if len(response.Data) != scSecretLength { - return fmt.Errorf("response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength) - } - - return nil -} - -// open is an internal method that sends an open APDU. -func (s *SecureChannelSession) open() (*responseAPDU, error) { - return transmit(s.card, &commandAPDU{ - Cla: claSCWallet, - Ins: insOpenSecureChannel, - P1: s.PairingIndex, - P2: 0, - Data: s.publicKey, - Le: 0, - }) -} - -// pair is an internal method that sends a pair APDU. -func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*responseAPDU, error) { - return transmit(s.card, &commandAPDU{ - Cla: claSCWallet, - Ins: insPair, - P1: p1, - P2: 0, - Data: data, - Le: 0, - }) -} - -// transmitEncrypted sends an encrypted message, and decrypts and returns the response. -func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) { - if s.iv == nil { - return nil, fmt.Errorf("channel not open") - } - - data, err := s.encryptAPDU(data) - if err != nil { - return nil, err - } - meta := [16]byte{cla, ins, p1, p2, byte(len(data) + scBlockSize)} - if err = s.updateIV(meta[:], data); err != nil { - return nil, err - } - - fulldata := make([]byte, len(s.iv)+len(data)) - copy(fulldata, s.iv) - copy(fulldata[len(s.iv):], data) - - response, err := transmit(s.card, &commandAPDU{ - Cla: cla, - Ins: ins, - P1: p1, - P2: p2, - Data: fulldata, - }) - if err != nil { - return nil, err - } - - rmeta := [16]byte{byte(len(response.Data))} - rmac := response.Data[:len(s.iv)] - rdata := response.Data[len(s.iv):] - plainData, err := s.decryptAPDU(rdata) - if err != nil { - return nil, err - } - - if err = s.updateIV(rmeta[:], rdata); err != nil { - return nil, err - } - if !bytes.Equal(s.iv, rmac) { - return nil, fmt.Errorf("invalid MAC in response") - } - - rapdu := &responseAPDU{} - rapdu.deserialize(plainData) - - if rapdu.Sw1 != sw1Ok { - return nil, fmt.Errorf("unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2) - } - - return rapdu, nil -} - -// encryptAPDU is an internal method that serializes and encrypts an APDU. -func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) { - if len(data) > maxPayloadSize { - return nil, fmt.Errorf("payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize) - } - data = pad(data, 0x80) - - ret := make([]byte, len(data)) - - a, err := aes.NewCipher(s.sessionEncKey) - if err != nil { - return nil, err - } - crypter := cipher.NewCBCEncrypter(a, s.iv) - crypter.CryptBlocks(ret, data) - return ret, nil -} - -// pad applies message padding to a 16 byte boundary. -func pad(data []byte, terminator byte) []byte { - padded := make([]byte, (len(data)/16+1)*16) - copy(padded, data) - padded[len(data)] = terminator - return padded -} - -// decryptAPDU is an internal method that decrypts and deserializes an APDU. -func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) { - a, err := aes.NewCipher(s.sessionEncKey) - if err != nil { - return nil, err - } - - ret := make([]byte, len(data)) - - crypter := cipher.NewCBCDecrypter(a, s.iv) - crypter.CryptBlocks(ret, data) - return unpad(ret, 0x80) -} - -// unpad strips padding from a message. -func unpad(data []byte, terminator byte) ([]byte, error) { - for i := 1; i <= 16; i++ { - switch data[len(data)-i] { - case 0: - continue - case terminator: - return data[:len(data)-i], nil - default: - return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i]) - } - } - return nil, fmt.Errorf("expected end of padding, got 0") -} - -// updateIV is an internal method that updates the initialization vector after -// each message exchanged. -func (s *SecureChannelSession) updateIV(meta, data []byte) error { - data = pad(data, 0) - a, err := aes.NewCipher(s.sessionMacKey) - if err != nil { - return err - } - crypter := cipher.NewCBCEncrypter(a, make([]byte, 16)) - crypter.CryptBlocks(meta, meta) - crypter.CryptBlocks(data, data) - // The first 16 bytes of the last block is the MAC - s.iv = data[len(data)-32 : len(data)-16] - return nil -}
diff --git go-ethereum/cmd/geth/les_test.go celo/cmd/geth/les_test.go index 75f5f128ddea15df7604d5d2beee18092203794a..7521af48c7bce080203457e6d32b6b76d6c8fa47 100644 --- go-ethereum/cmd/geth/les_test.go +++ celo/cmd/geth/les_test.go @@ -65,41 +65,6 @@ g.callRPC(&g.nodeInfo, "admin_nodeInfo") return g.nodeInfo }   -func (g *gethrpc) waitSynced() { - // Check if it's synced now - var result interface{} - g.callRPC(&result, "eth_syncing") - syncing, ok := result.(bool) - if ok && !syncing { - g.geth.Logf("%v already synced", g.name) - return - } - - // Actually wait, subscribe to the event - ch := make(chan interface{}) - sub, err := g.rpc.Subscribe(context.Background(), "eth", ch, "syncing") - if err != nil { - g.geth.Fatalf("%v syncing: %v", g.name, err) - } - defer sub.Unsubscribe() - timeout := time.After(4 * time.Second) - select { - case ev := <-ch: - g.geth.Log("'syncing' event", ev) - syncing, ok := ev.(bool) - if ok && !syncing { - break - } - g.geth.Log("Other 'syncing' event", ev) - case err := <-sub.Err(): - g.geth.Fatalf("%v notification: %v", g.name, err) - break - case <-timeout: - g.geth.Fatalf("%v timeout syncing", g.name) - break - } -} - // ipcEndpoint resolves an IPC endpoint based on a configured value, taking into // account the set data folders as well as the designated platform we're currently // running on. @@ -165,7 +130,9 @@ datadir := initGeth(t) t.Logf("Importing keys to geth") runGeth(t, "--datadir", datadir, "--password", "./testdata/password.txt", "account", "import", "./testdata/key.prv", "--lightkdf").WaitExit() account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105" - server := startGethWithIpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--mine", "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1", "--verbosity=4") + server := startGethWithIpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", + "--unlock", account, "--mine", "--miner.validator", account, "--tx-fee-recipient", account, + "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1", "--verbosity=4") return server }
diff --git go-ethereum/cmd/geth/chaincmd.go celo/cmd/geth/chaincmd.go index 12616fbbb66fa9bbdee9a06681c891332fcbb2c6..5c67aca3493ca058a79695d9846ea19b91dd2066 100644 --- go-ethereum/cmd/geth/chaincmd.go +++ celo/cmd/geth/chaincmd.go @@ -38,6 +38,7 @@ "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" + "gopkg.in/urfave/cli.v1" )   @@ -65,9 +66,8 @@ Usage: "Dumps genesis block JSON configuration to stdout", ArgsUsage: "", Flags: []cli.Flag{ utils.MainnetFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, }, Category: "BLOCKCHAIN COMMANDS", Description: ` @@ -80,6 +80,8 @@ Usage: "Import a blockchain file", ArgsUsage: "<filename> (<filename 2> ... <filename N>) ", Flags: []cli.Flag{ utils.DataDirFlag, + utils.AlfajoresFlag, + utils.BaklavaFlag, utils.CacheFlag, utils.SyncModeFlag, utils.GCModeFlag, @@ -117,6 +119,8 @@ Usage: "Export blockchain into file", ArgsUsage: "<filename> [<blockNumFirst> <blockNumLast>]", Flags: []cli.Flag{ utils.DataDirFlag, + utils.AlfajoresFlag, + utils.BaklavaFlag, utils.CacheFlag, utils.SyncModeFlag, }, @@ -135,6 +139,8 @@ Usage: "Import the preimage database from an RLP stream", ArgsUsage: "<datafile>", Flags: []cli.Flag{ utils.DataDirFlag, + utils.AlfajoresFlag, + utils.BaklavaFlag, utils.CacheFlag, utils.SyncModeFlag, }, @@ -149,6 +155,8 @@ Usage: "Export the preimage database into an RLP stream", ArgsUsage: "<dumpfile>", Flags: []cli.Flag{ utils.DataDirFlag, + utils.AlfajoresFlag, + utils.BaklavaFlag, utils.CacheFlag, utils.SyncModeFlag, }, @@ -163,6 +171,8 @@ Usage: "Dump a specific block from storage", ArgsUsage: "[? <blockHash> | <blockNum>]", Flags: []cli.Flag{ utils.DataDirFlag, + utils.AlfajoresFlag, + utils.BaklavaFlag, utils.CacheFlag, utils.IterativeOutputFlag, utils.ExcludeCodeFlag, @@ -200,7 +210,7 @@ // Open and initialise both full and light databases stack, _ := makeConfigNode(ctx) defer stack.Close()   - for _, name := range []string{"chaindata", "lightchaindata"} { + for _, name := range []string{"chaindata", "lightchaindata", "lightestchaindata"} { chaindb, err := stack.OpenDatabase(name, 0, 0, "", false) if err != nil { utils.Fatalf("Failed to open database: %v", err) @@ -219,7 +229,7 @@ func dumpGenesis(ctx *cli.Context) error { // TODO(rjl493456442) support loading from the custom datadir genesis := utils.MakeGenesis(ctx) if genesis == nil { - genesis = core.DefaultGenesisBlock() + genesis = core.MainnetGenesisBlock() } if err := json.NewEncoder(os.Stdout).Encode(genesis); err != nil { utils.Fatalf("could not encode genesis") @@ -261,18 +271,10 @@ // Import the chain start := time.Now()   var importErr error - - if len(ctx.Args()) == 1 { - if err := utils.ImportChain(chain, ctx.Args().First()); err != nil { + for _, arg := range ctx.Args() { + if err := utils.ImportChain(chain, arg); err != nil { importErr = err - log.Error("Import error", "err", err) - } - } else { - for _, arg := range ctx.Args() { - if err := utils.ImportChain(chain, arg); err != nil { - importErr = err - log.Error("Import error", "file", arg, "err", err) - } + log.Error("Import error", "file", arg, "err", err) } } chain.Stop()
diff --git go-ethereum/cmd/geth/dbcmd.go celo/cmd/geth/dbcmd.go index cc79c8fcfef4414b4c19f20d57cd31844176a1c2..a2ec506275844980cf013ef33c393c4495dac080 100644 --- go-ethereum/cmd/geth/dbcmd.go +++ celo/cmd/geth/dbcmd.go @@ -72,9 +72,8 @@ Flags: []cli.Flag{ utils.DataDirFlag, utils.SyncModeFlag, utils.MainnetFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, }, Usage: "Inspect the storage size for each type of data in the database", Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`, @@ -87,9 +86,8 @@ Flags: []cli.Flag{ utils.DataDirFlag, utils.SyncModeFlag, utils.MainnetFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, }, } dbCompactCmd = cli.Command{ @@ -100,9 +98,8 @@ Flags: []cli.Flag{ utils.DataDirFlag, utils.SyncModeFlag, utils.MainnetFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, utils.CacheFlag, utils.CacheDatabaseFlag, }, @@ -119,9 +116,8 @@ Flags: []cli.Flag{ utils.DataDirFlag, utils.SyncModeFlag, utils.MainnetFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, }, Description: "This command looks up the specified database key from the database.", } @@ -134,9 +130,8 @@ Flags: []cli.Flag{ utils.DataDirFlag, utils.SyncModeFlag, utils.MainnetFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, }, Description: `This command deletes the specified database key from the database. WARNING: This is a low-level operation which may cause database corruption!`, @@ -150,9 +145,8 @@ Flags: []cli.Flag{ utils.DataDirFlag, utils.SyncModeFlag, utils.MainnetFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, }, Description: `This command sets a given database key to the given value. WARNING: This is a low-level operation which may cause database corruption!`, @@ -166,9 +160,8 @@ Flags: []cli.Flag{ utils.DataDirFlag, utils.SyncModeFlag, utils.MainnetFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, }, Description: "This command looks up the specified database key from the database.", } @@ -181,9 +174,8 @@ Flags: []cli.Flag{ utils.DataDirFlag, utils.SyncModeFlag, utils.MainnetFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, }, Description: "This command displays information about the freezer index.", }
diff --git go-ethereum/cmd/geth/config.go celo/cmd/geth/config.go index caac115e7b9a8d702ec31e34b6c99949602126be..3a299bb2ec2e21de371b65fca46287168bfb73e2 100644 --- go-ethereum/cmd/geth/config.go +++ celo/cmd/geth/config.go @@ -25,21 +25,19 @@ "os" "reflect" "unicode"   - "gopkg.in/urfave/cli.v1" - "github.com/ethereum/go-ethereum/accounts/external" "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/accounts/scwallet" "github.com/ethereum/go-ethereum/accounts/usbwallet" "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" + "github.com/naoina/toml" + "gopkg.in/urfave/cli.v1" )   var ( @@ -135,6 +133,7 @@ }   // Apply flags. utils.SetNodeConfig(ctx, &cfg.Node) + utils.SetProxyConfig(ctx, &cfg.Node, &cfg.Eth) stack, err := node.New(&cfg.Node) if err != nil { utils.Fatalf("Failed to create the protocol stack: %v", err) @@ -145,8 +144,11 @@ utils.Fatalf("Failed to set account manager backends: %v", err) }   utils.SetEthConfig(ctx, stack, &cfg.Eth) - if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) { - cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name) + if ctx.GlobalIsSet(utils.CeloStatsURLFlag.Name) { + cfg.Ethstats.URL = ctx.GlobalString(utils.CeloStatsURLFlag.Name) + } else if ctx.GlobalIsSet(utils.LegacyEthStatsURLFlag.Name) { + cfg.Ethstats.URL = ctx.GlobalString(utils.LegacyEthStatsURLFlag.Name) + log.Warn("The flag --ethstats is deprecated and will be removed in the future, please use --celostats") } applyMetricConfig(ctx, &cfg)   @@ -156,20 +158,10 @@ // makeFullNode loads geth configuration and creates the Ethereum backend. func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { stack, cfg := makeConfigNode(ctx) - if ctx.GlobalIsSet(utils.OverrideLondonFlag.Name) { - cfg.Eth.OverrideLondon = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideLondonFlag.Name)) - } - backend, eth := utils.RegisterEthService(stack, &cfg.Eth) - - // Configure catalyst. - if ctx.GlobalBool(utils.CatalystFlag.Name) { - if eth == nil { - utils.Fatalf("Catalyst does not work in light client mode.") - } - if err := catalyst.Register(stack, eth); err != nil { - utils.Fatalf("%v", err) - } + if ctx.GlobalIsSet(utils.OverrideEHardforkFlag.Name) { + cfg.Eth.OverrideEHardfork = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideEHardforkFlag.Name)) } + backend, _ := utils.RegisterEthService(stack, &cfg.Eth)   // Configure GraphQL if requested if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) { @@ -312,14 +304,6 @@ if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil { log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err)) } else { am.AddBackend(trezorhub) - } - } - if len(conf.SmartCardDaemonPath) > 0 { - // Start a smart card hub - if schub, err := scwallet.NewHub(conf.SmartCardDaemonPath, scwallet.Scheme, keydir); err != nil { - log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err)) - } else { - am.AddBackend(schub) } }
diff --git go-ethereum/cmd/geth/consolecmd_test.go celo/cmd/geth/consolecmd_test.go index 5c032a4bcd48802cf06db63a05f7cb66294d0b38..9c04fa72426b82bd77e17e88ca8c0ee8f2a0e604 100644 --- go-ethereum/cmd/geth/consolecmd_test.go +++ celo/cmd/geth/consolecmd_test.go @@ -31,7 +31,7 @@ "github.com/ethereum/go-ethereum/params" )   const ( - ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0" + ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 istanbul:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0" httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0" )   @@ -39,11 +39,10 @@ // spawns geth with the given command line args, using a set of flags to minimise // memory and disk IO. If the args don't set --datadir, the // child g gets a temporary data directory. func runMinimalGeth(t *testing.T, args ...string) *testgeth { - // --ropsten to make the 'writing genesis to disk' faster (no accounts) // --networkid=1337 to avoid cache bump // --syncmode=full to avoid allocating fast sync bloom - allArgs := []string{"--ropsten", "--networkid", "1337", "--syncmode=full", "--port", "0", - "--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64"} + allArgs := []string{"--networkid", "1337", "--syncmode=full", "--port", "0", + "--nat", "none", "--nodiscover", "--maxpeers", "0", "--light.maxpeers", "0", "--cache", "64"} return runGeth(t, append(allArgs, args...)...) }   @@ -53,7 +52,7 @@ func TestConsoleWelcome(t *testing.T) { coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"   // Start a geth console, make sure it's cleaned up and terminate the console - geth := runMinimalGeth(t, "--miner.etherbase", coinbase, "console") + geth := runMinimalGeth(t, "--etherbase", coinbase, "console")   // Gather all the infos the welcome message needs to contain geth.SetTemplateFunc("goos", func() string { return runtime.GOOS }) @@ -61,15 +60,15 @@ geth.SetTemplateFunc("goarch", func() string { return runtime.GOARCH }) geth.SetTemplateFunc("gover", runtime.Version) geth.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") }) geth.SetTemplateFunc("niltime", func() string { - return time.Unix(0, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)") + return time.Unix(0x5ea06a00, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)") }) geth.SetTemplateFunc("apis", func() string { return ipcAPIs })   // Verify the actual welcome message to the required template geth.Expect(` -Welcome to the Geth JavaScript console! +Welcome to the Celo JavaScript console!   -instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}} +instance: celo/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}} coinbase: {{.Etherbase}} at block: 0 ({{niltime}}) datadir: {{.Datadir}} @@ -100,7 +99,7 @@ // And HTTP + WS attachment p := trulyRandInt(1024, 65533) // Yeah, sometimes this will fail, sorry :P httpPort = strconv.Itoa(p) wsPort = strconv.Itoa(p + 1) - geth := runMinimalGeth(t, "--miner.etherbase", "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182", + geth := runMinimalGeth(t, "--etherbase", "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182", "--ipcpath", ipc, "--http", "--http.port", httpPort, "--ws", "--ws.port", wsPort) @@ -133,7 +132,7 @@ attach.SetTemplateFunc("gover", runtime.Version) attach.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") }) attach.SetTemplateFunc("etherbase", func() string { return geth.Etherbase }) attach.SetTemplateFunc("niltime", func() string { - return time.Unix(0, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)") + return time.Unix(0x5ea06a00, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)") }) attach.SetTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") }) attach.SetTemplateFunc("datadir", func() string { return geth.Datadir }) @@ -141,9 +140,9 @@ attach.SetTemplateFunc("apis", func() string { return apis })   // Verify the actual welcome message to the required template attach.Expect(` -Welcome to the Geth JavaScript console! +Welcome to the Celo JavaScript console!   -instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}} +instance: celo/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}} coinbase: {{etherbase}} at block: 0 ({{niltime}}){{if ipc}} datadir: {{datadir}}{{end}}
diff --git go-ethereum/cmd/geth/main.go celo/cmd/geth/main.go index af3e7722a6559eb62854aa2291c02edeb886d6d8..e7bdf4c2a3d96f756579bd932609dc15db68d031 100644 --- go-ethereum/cmd/geth/main.go +++ celo/cmd/geth/main.go @@ -18,6 +18,7 @@ // geth is the official command-line client for Ethereum. package main   import ( + "context" "fmt" "os" "sort" @@ -30,20 +31,25 @@ "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/console/prompt" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/flags" + lesDownloader "github.com/ethereum/go-ethereum/les/downloader" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rpc" "gopkg.in/urfave/cli.v1" )   const ( - clientIdentifier = "geth" // Client identifier to advertise over the network + clientIdentifier = "celo" // Client identifier to advertise over the network )   var ( @@ -65,16 +71,8 @@ utils.KeyStoreDirFlag, utils.ExternalSignerFlag, utils.NoUSBFlag, utils.USBFlag, - utils.SmartCardDaemonPathFlag, - utils.OverrideLondonFlag, - utils.EthashCacheDirFlag, - utils.EthashCachesInMemoryFlag, - utils.EthashCachesOnDiskFlag, - utils.EthashCachesLockMmapFlag, - utils.EthashDatasetDirFlag, - utils.EthashDatasetsInMemoryFlag, - utils.EthashDatasetsOnDiskFlag, - utils.EthashDatasetsLockMmapFlag, + // utils.SmartCardDaemonPathFlag, + utils.OverrideEHardforkFlag, utils.TxPoolLocalsFlag, utils.TxPoolNoLocalsFlag, utils.TxPoolJournalFlag, @@ -97,11 +95,15 @@ utils.LightEgressFlag, utils.LightMaxPeersFlag, utils.LightNoPruneFlag, utils.LightKDFFlag, + utils.LightGatewayFeeFlag, utils.UltraLightServersFlag, utils.UltraLightFractionFlag, utils.UltraLightOnlyAnnounceFlag, utils.LightNoSyncServeFlag, utils.WhitelistFlag, + utils.EtherbaseFlag, + utils.TxFeeRecipientFlag, + utils.BLSbaseFlag, utils.BloomFilterSizeFlag, utils.CacheFlag, utils.CacheDatabaseFlag, @@ -116,15 +118,10 @@ utils.ListenPortFlag, utils.MaxPeersFlag, utils.MaxPendingPeersFlag, utils.MiningEnabledFlag, - utils.MinerThreadsFlag, - utils.MinerNotifyFlag, - utils.LegacyMinerGasTargetFlag, - utils.MinerGasLimitFlag, - utils.MinerGasPriceFlag, - utils.MinerEtherbaseFlag, + utils.MinerValidatorFlag, + utils.LegacyMinerGasPriceFlag, // switched to gas price flag? utils.MinerExtraDataFlag, - utils.MinerRecommitIntervalFlag, - utils.MinerNoVerifyFlag, + utils.LegacyMinerExtraDataFlag, utils.NATFlag, utils.NoDiscoverFlag, utils.DiscoveryV5Flag, @@ -135,24 +132,38 @@ utils.DNSDiscoveryFlag, utils.MainnetFlag, utils.DeveloperFlag, utils.DeveloperPeriodFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, utils.VMEnableDebugFlag, utils.NetworkIdFlag, - utils.EthStatsURLFlag, - utils.FakePoWFlag, + utils.CeloStatsURLFlag, + utils.LegacyEthStatsURLFlag, utils.NoCompactionFlag, - utils.GpoBlocksFlag, - utils.GpoPercentileFlag, - utils.GpoMaxGasPriceFlag, - utils.GpoIgnoreGasPriceFlag, - utils.MinerNotifyFullFlag, + // utils.GpoBlocksFlag, + // utils.GpoPercentileFlag, + // utils.GpoMaxGasPriceFlag, + // utils.GpoIgnoreGasPriceFlag, configFileFlag, - utils.CatalystFlag, + utils.LegacyIstanbulRequestTimeoutFlag, + utils.LegacyIstanbulBlockPeriodFlag, + utils.LegacyIstanbulProposerPolicyFlag, + utils.IstanbulReplicaFlag, + utils.AnnounceQueryEnodeGossipPeriodFlag, + utils.AnnounceAggressiveQueryEnodeGossipOnEnablementFlag, + utils.PingIPFromPacketFlag, + utils.UseInMemoryDiscoverTableFlag, + utils.VersionCheckFlag, + utils.ProxyFlag, + utils.ProxyInternalFacingEndpointFlag, + utils.ProxiedValidatorAddressFlag, + utils.ProxiedFlag, + utils.ProxyEnodeURLPairsFlag, + utils.LegacyProxyEnodeURLPairsFlag, + utils.ProxyAllowPrivateIPFlag, }   rpcFlags = []cli.Flag{ + utils.DisableRPCETHCompatibility, utils.HTTPEnabledFlag, utils.HTTPListenAddrFlag, utils.HTTPPortFlag, @@ -163,18 +174,25 @@ utils.GraphQLCORSDomainFlag, utils.GraphQLVirtualHostsFlag, utils.HTTPApiFlag, utils.HTTPPathPrefixFlag, + utils.HTTPRequestReadTimeout, + utils.HTTPRequestWriteTimeout, + utils.HTTPRequestIdleTimeout, utils.WSEnabledFlag, utils.WSListenAddrFlag, + utils.LegacyWSListenAddrFlag, utils.WSPortFlag, + utils.LegacyWSPortFlag, utils.WSApiFlag, + utils.LegacyWSApiFlag, utils.WSAllowedOriginsFlag, + utils.LegacyWSAllowedOriginsFlag, utils.WSPathPrefixFlag, utils.IPCDisabledFlag, utils.IPCPathFlag, utils.InsecureUnlockAllowedFlag, + utils.RPCGlobalGasInflationRateFlag, utils.RPCGlobalGasCapFlag, utils.RPCGlobalTxFeeCapFlag, - utils.AllowUnprotectedTxs, }   metricsFlags = []cli.Flag{ @@ -192,6 +210,7 @@ utils.MetricsEnableInfluxDBV2Flag, utils.MetricsInfluxDBTokenFlag, utils.MetricsInfluxDBBucketFlag, utils.MetricsInfluxDBOrganizationFlag, + utils.MetricsLoadTestCSVFlag, } )   @@ -218,8 +237,6 @@ consoleCommand, attachCommand, javascriptCommand, // See misccmd.go: - makecacheCommand, - makedagCommand, versionCommand, versionCheckCommand, licenseCommand, @@ -262,25 +279,21 @@ // This function should be called before launching devp2p stack. func prepare(ctx *cli.Context) { // If we're running a known preset, log it for convenience. switch { - case ctx.GlobalIsSet(utils.RopstenFlag.Name): - log.Info("Starting Geth on Ropsten testnet...") - - case ctx.GlobalIsSet(utils.RinkebyFlag.Name): - log.Info("Starting Geth on Rinkeby testnet...") - - case ctx.GlobalIsSet(utils.GoerliFlag.Name): - log.Info("Starting Geth on Görli testnet...") + case ctx.GlobalIsSet(utils.BaklavaFlag.Name): + log.Info("Starting Geth on Baklava testnet...")   + case ctx.GlobalIsSet(utils.AlfajoresFlag.Name): + log.Info("Starting Geth on Alfajores testnet...") case ctx.GlobalIsSet(utils.DeveloperFlag.Name): log.Info("Starting Geth in ephemeral dev mode...")   case !ctx.GlobalIsSet(utils.NetworkIdFlag.Name): - log.Info("Starting Geth on Ethereum mainnet...") + log.Info("Starting Geth on Celo mainnet...") } // If we're a full node on mainnet without --cache specified, bump default cache allowance if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) { // Make sure we're not on any supported preconfigured testnet either - if !ctx.GlobalIsSet(utils.RopstenFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) { + if !ctx.GlobalIsSet(utils.DeveloperFlag.Name) && !ctx.GlobalIsSet(utils.AlfajoresFlag.Name) && !ctx.GlobalIsSet(utils.BaklavaFlag.Name) { // Nope, we're really on mainnet. Bump that cache up! log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096) ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096)) @@ -376,19 +389,26 @@ // Spawn a standalone goroutine for status synchronization monitoring, // close the node when synchronization is complete if user required. if ctx.GlobalBool(utils.ExitWhenSyncedFlag.Name) { go func() { - sub := stack.EventMux().Subscribe(downloader.DoneEvent{}) + // Subscribing to both events since les has its own downloader + sub := stack.EventMux().Subscribe(downloader.DoneEvent{}, lesDownloader.DoneEvent{}) defer sub.Unsubscribe() for { event := <-sub.Chan() if event == nil { continue } - done, ok := event.Data.(downloader.DoneEvent) - if !ok { + var latest *types.Header + if done, ok := event.Data.(downloader.DoneEvent); ok { + latest = done.Latest + } + if lesDone, ok := event.Data.(lesDownloader.DoneEvent); ok { + latest = lesDone.Latest + } + if latest == nil { continue } - if timestamp := time.Unix(int64(done.Latest.Time), 0); time.Since(timestamp) < 10*time.Minute { - log.Info("Synchronisation completed", "latestnum", done.Latest.Number, "latesthash", done.Latest.Hash(), + if timestamp := time.Unix(int64(latest.Time), 0); time.Since(timestamp) < 10*time.Minute { + log.Info("Synchronisation completed", "latestnum", latest.Number, "latesthash", latest.Hash(), "age", common.PrettyAge(timestamp)) stack.Close() } @@ -396,24 +416,54 @@ } }() }   + isFullNode := ctx.GlobalString(utils.SyncModeFlag.Name) == "full" || ctx.GlobalString(utils.SyncModeFlag.Name) == "fast" + // Miners and proxies only makes sense if a full node is running + if ctx.GlobalBool(utils.ProxyFlag.Name) || ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) { + if !isFullNode { + utils.Fatalf("Miners and Proxies must be run as a full node") + } + } + // Replicas only makes sense if we are mining + if ctx.GlobalBool(utils.IstanbulReplicaFlag.Name) { + if !(ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name)) { + utils.Fatalf("Must run a replica with mining enabled or in dev mode.") + } + } + // Start auxiliary services if enabled if ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) { - // Mining only makes sense if a full Ethereum node is running - if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" { - utils.Fatalf("Light clients do not support mining") + if ctx.GlobalBool(utils.ProxyFlag.Name) { + utils.Fatalf("Proxies can't mine") } ethBackend, ok := backend.(*eth.EthAPIBackend) if !ok { utils.Fatalf("Ethereum service not running: %v", err) } - // Set the gas price to the limits from the CLI and start mining - gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name) - ethBackend.TxPool().SetGasPrice(gasprice) - // start mining - threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name) - if err := ethBackend.StartMining(threads); err != nil { + // // TODO: Handle gas price. + // // Set the gas price to the limits from the CLI and start mining + // // gasprice := utils.GlobalBig(ctx, utils.LegacyMinerGasPriceFlag.Name) // MinerGasPriceFlag + // gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name) + // // Migration from legacy miner gas price to new flag + // if ctx.GlobalIsSet(utils.LegacyMinerGasPriceFlag.Name) && !ctx.GlobalIsSet(utils.MinerGasPriceFlag.Name) { + // gasprice = utils.GlobalBig(ctx, utils.LegacyMinerGasPriceFlag.Name) + // } + // ethBackend.TxPool().SetGasPrice(gasprice) + + if err := ethBackend.StartMining(); err != nil { utils.Fatalf("Failed to start mining: %v", err) } + } + if !ctx.GlobalBool(utils.VersionCheckFlag.Name) { + runnerFactory := func() (vm.EVMRunner, error) { + header := backend.CurrentHeader() + stateDB, _, err := backend.StateAndHeaderByNumberOrHash(context.Background(), rpc.BlockNumberOrHashWithHash(header.Hash(), true)) + if err != nil { + return nil, err + } + return backend.NewEVMRunner(header, stateDB), nil + } + + blockchain_parameters.SpawnCheck(runnerFactory) } }
diff --git go-ethereum/cmd/geth/consolecmd.go celo/cmd/geth/consolecmd.go index f4c25258bd69ee22b4100a1ca1076e461d73cb25..5f2f05c9b21521356d1ef03033f08676768dc139 100644 --- go-ethereum/cmd/geth/consolecmd.go +++ celo/cmd/geth/consolecmd.go @@ -18,7 +18,6 @@ package main   import ( "fmt" - "os" "path/filepath" "strings"   @@ -121,19 +120,10 @@ if ctx.GlobalIsSet(utils.DataDirFlag.Name) { path = ctx.GlobalString(utils.DataDirFlag.Name) } if path != "" { - if ctx.GlobalBool(utils.RopstenFlag.Name) { - // Maintain compatibility with older Geth configurations storing the - // Ropsten database in `testnet` instead of `ropsten`. - legacyPath := filepath.Join(path, "testnet") - if _, err := os.Stat(legacyPath); !os.IsNotExist(err) { - path = legacyPath - } else { - path = filepath.Join(path, "ropsten") - } - } else if ctx.GlobalBool(utils.RinkebyFlag.Name) { - path = filepath.Join(path, "rinkeby") - } else if ctx.GlobalBool(utils.GoerliFlag.Name) { - path = filepath.Join(path, "goerli") + if ctx.GlobalBool(utils.BaklavaFlag.Name) { + path = filepath.Join(path, "baklava") + } else if ctx.GlobalBool(utils.AlfajoresFlag.Name) { + path = filepath.Join(path, "alfajores") } } endpoint = fmt.Sprintf("%s/geth.ipc", path)
diff --git go-ethereum/cmd/geth/usage.go celo/cmd/geth/usage.go index 49036581591fc6277d905a77a0cd77f250512e46..81994d75928c700dd3a9a98e8c7df848d54a1cd6 100644 --- go-ethereum/cmd/geth/usage.go +++ celo/cmd/geth/usage.go @@ -39,20 +39,19 @@ utils.AncientFlag, utils.MinFreeDiskSpaceFlag, utils.KeyStoreDirFlag, utils.USBFlag, - utils.SmartCardDaemonPathFlag, utils.NetworkIdFlag, utils.MainnetFlag, - utils.GoerliFlag, - utils.RinkebyFlag, - utils.RopstenFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, utils.SyncModeFlag, utils.ExitWhenSyncedFlag, utils.GCModeFlag, utils.TxLookupLimitFlag, - utils.EthStatsURLFlag, + utils.CeloStatsURLFlag, utils.IdentityFlag, utils.LightKDFFlag, utils.WhitelistFlag, + utils.TxFeeRecipientFlag, }, }, { @@ -62,6 +61,7 @@ utils.LightServeFlag, utils.LightIngressFlag, utils.LightEgressFlag, utils.LightMaxPeersFlag, + utils.LightGatewayFeeFlag, utils.UltraLightServersFlag, utils.UltraLightFractionFlag, utils.UltraLightOnlyAnnounceFlag, @@ -77,19 +77,6 @@ utils.DeveloperPeriodFlag, }, }, { - Name: "ETHASH", - Flags: []cli.Flag{ - utils.EthashCacheDirFlag, - utils.EthashCachesInMemoryFlag, - utils.EthashCachesOnDiskFlag, - utils.EthashCachesLockMmapFlag, - utils.EthashDatasetDirFlag, - utils.EthashDatasetsInMemoryFlag, - utils.EthashDatasetsOnDiskFlag, - utils.EthashDatasetsLockMmapFlag, - }, - }, - { Name: "TRANSACTION POOL", Flags: []cli.Flag{ utils.TxPoolLocalsFlag, @@ -140,6 +127,9 @@ utils.HTTPApiFlag, utils.HTTPPathPrefixFlag, utils.HTTPCORSDomainFlag, utils.HTTPVirtualHostsFlag, + utils.HTTPRequestReadTimeout, + utils.HTTPRequestWriteTimeout, + utils.HTTPRequestIdleTimeout, utils.WSEnabledFlag, utils.WSListenAddrFlag, utils.WSPortFlag, @@ -149,9 +139,9 @@ utils.WSAllowedOriginsFlag, utils.GraphQLEnabledFlag, utils.GraphQLCORSDomainFlag, utils.GraphQLVirtualHostsFlag, + utils.RPCGlobalGasInflationRateFlag, utils.RPCGlobalGasCapFlag, utils.RPCGlobalTxFeeCapFlag, - utils.AllowUnprotectedTxs, utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag, @@ -171,30 +161,16 @@ utils.DiscoveryV5Flag, utils.NetrestrictFlag, utils.NodeKeyFileFlag, utils.NodeKeyHexFlag, + utils.PingIPFromPacketFlag, + utils.UseInMemoryDiscoverTableFlag, }, }, { Name: "MINER", Flags: []cli.Flag{ utils.MiningEnabledFlag, - utils.MinerThreadsFlag, - utils.MinerNotifyFlag, - utils.MinerNotifyFullFlag, - utils.MinerGasPriceFlag, - utils.MinerGasLimitFlag, - utils.MinerEtherbaseFlag, + utils.MinerValidatorFlag, utils.MinerExtraDataFlag, - utils.MinerRecommitIntervalFlag, - utils.MinerNoVerifyFlag, - }, - }, - { - Name: "GAS PRICE ORACLE", - Flags: []cli.Flag{ - utils.GpoBlocksFlag, - utils.GpoPercentileFlag, - utils.GpoMaxGasPriceFlag, - utils.GpoIgnoreGasPriceFlag, }, }, { @@ -206,7 +182,6 @@ }, { Name: "LOGGING AND DEBUGGING", Flags: append([]cli.Flag{ - utils.FakePoWFlag, utils.NoCompactionFlag, }, debug.Flags...), }, @@ -215,18 +190,55 @@ Name: "METRICS AND STATS", Flags: metricsFlags, }, { + Name: "ISTANBUL", + Flags: []cli.Flag{ + utils.IstanbulReplicaFlag, + }, + }, + { + Name: "ANNOUNCE", + Flags: []cli.Flag{ + utils.AnnounceQueryEnodeGossipPeriodFlag, + utils.AnnounceAggressiveQueryEnodeGossipOnEnablementFlag, + }, + }, + { + Name: "PROXY", + Flags: []cli.Flag{ + utils.ProxyFlag, + utils.ProxyInternalFacingEndpointFlag, + utils.ProxiedValidatorAddressFlag, + utils.ProxiedFlag, + utils.ProxyEnodeURLPairsFlag, + utils.ProxyAllowPrivateIPFlag, + }, + }, + { Name: "ALIASED (deprecated)", Flags: []cli.Flag{ + utils.EtherbaseFlag, + utils.LegacyMinerGasPriceFlag, + utils.LegacyProxyEnodeURLPairsFlag, + utils.LegacyIstanbulRequestTimeoutFlag, + utils.LegacyIstanbulBlockPeriodFlag, + utils.LegacyIstanbulProposerPolicyFlag, + utils.LegacyEthStatsURLFlag, utils.NoUSBFlag, + utils.LegacyWSListenAddrFlag, + utils.LegacyWSPortFlag, + utils.LegacyWSAllowedOriginsFlag, + utils.LegacyWSApiFlag, + utils.LegacyGraphQLListenAddrFlag, + utils.LegacyGraphQLPortFlag, }, }, { Name: "MISC", Flags: []cli.Flag{ + utils.VersionCheckFlag, utils.SnapshotFlag, utils.BloomFilterSizeFlag, cli.HelpFlag, - utils.CatalystFlag, }, }, }
diff --git go-ethereum/cmd/geth/version_check.go celo/cmd/geth/version_check.go index 5c1f724d6f75f121a44243550331001011e11bee..4a4c56f04b67b749eeb80926ca756a9c13a1d6cf 100644 --- go-ethereum/cmd/geth/version_check.go +++ celo/cmd/geth/version_check.go @@ -114,6 +114,8 @@ func fetch(url string) ([]byte, error) { if filep := strings.TrimPrefix(url, "file://"); filep != url { return ioutil.ReadFile(filep) } + // gosec/G107. This function is called with a node/validator flag, no need to run gosec. + //nolint:gosec res, err := http.Get(url) if err != nil { return nil, err
diff --git go-ethereum/cmd/geth/genesis_test.go celo/cmd/geth/genesis_test.go index 823a92d53024bc15ca02b70e81733eff4968c0a8..66bfaa068af2c1dcc06de6be278617624ed454d2 100644 --- go-ethereum/cmd/geth/genesis_test.go +++ celo/cmd/geth/genesis_test.go @@ -17,60 +17,117 @@ package main   import ( + "fmt" "io/ioutil" "os" "path/filepath" "testing" )   -var customGenesisTests = []struct { - genesis string - query string - result string -}{ - // Genesis file with an empty chain configuration (ensure missing fields work) - { - genesis: `{ - "alloc" : {}, +const ( + registryAddress = "'0x000000000000000000000000000000000000ce10'" + registryCode = "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a723058206808dd43e7d765afca53fe439122bc5eac16d708ce7d463451be5042426f101f0029" +) + +// TestCustomGenesis tests that initializing Geth with a custom genesis block and chain definitions +// work properly. +func TestCustomGenesis(t *testing.T) { + customGenesisTests := []struct { + genesis string + query string + result string + }{ + // Genesis file with an empty chain configuration (ensure missing fields work) + // Note: We add Registry to genesis because it's required for initializing a eth node. + { + genesis: fmt.Sprintf(`{ + "alloc" : { + "000000000000000000000000000000000000ce10" : { + "code": + "%s", + "storage":{"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103":"47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95"}, + "balance":"0" + } + }, "coinbase" : "0x0000000000000000000000000000000000000000", - "difficulty" : "0x20000", "extraData" : "", - "gasLimit" : "0x2fefd8", - "nonce" : "0x0000000000001338", - "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", - "timestamp" : "0x00", - "config" : {} - }`, - query: "eth.getBlock(0).nonce", - result: "0x0000000000001338", - }, - // Genesis file with specific chain configurations - { - genesis: `{ - "alloc" : {}, + "timestamp" : "0xabcdef", + "config" : { + "istanbul": {} + } + }`, registryCode), + query: "eth.getBlock(0).timestamp", + result: "11259375", + }, + // Genesis file with specific chain configurations + { + genesis: fmt.Sprintf(`{ + "alloc" : { + "000000000000000000000000000000000000ce10" : { + "code": + "%s", + "storage":{"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103":"47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95"}, + "balance":"0" + } + }, "coinbase" : "0x0000000000000000000000000000000000000000", - "difficulty" : "0x20000", "extraData" : "", - "gasLimit" : "0x2fefd8", - "nonce" : "0x0000000000001339", - "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", - "timestamp" : "0x00", + "timestamp" : "0xabcdf0", "config" : { "homesteadBlock" : 42, "daoForkBlock" : 141, - "daoForkSupport" : true + "daoForkSupport" : true, + "istanbul": {} + } + }`, registryCode), + query: "eth.getBlock(0).timestamp", + result: "11259376", + }, + // Genesis file with an empty chain configuration, and a deployed registry + { + genesis: fmt.Sprintf(`{ + "alloc" : { + "000000000000000000000000000000000000ce10" : { + "code": + "%s", + "storage":{"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103":"47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95"}, + "balance":"0" + } + }, + "coinbase" : "0x0000000000000000000000000000000000000000", + "extraData" : "", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp" : "0xabcdf0", + "config" : { + "istanbul": {} } - }`, - query: "eth.getBlock(0).nonce", - result: "0x0000000000001339", - }, -} + }`, registryCode), + query: "eth.getCode(" + registryAddress + ")", + result: registryCode, + }, + // Genesis file without Registry deployed + { + // nolint:gosimple + genesis: fmt.Sprintf(`{ + "alloc" : {}, + "coinbase" : "0x0000000000000000000000000000000000000000", + "extraData" : "", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp" : "0xabcdf0", + "config" : { + "homesteadBlock" : 42, + "daoForkBlock" : 141, + "daoForkSupport" : true, + "istanbul": {} + } + }`), + query: "", // No need, initializing node is supposed to fail + result: "no Registry Smart Contract deployed in genesis", + }, + }   -// Tests that initializing Geth with a custom genesis block and chain definitions -// work properly. -func TestCustomGenesis(t *testing.T) { for i, tt := range customGenesisTests { // Create a temporary data directory to use and inspect later datadir := tmpdir(t) @@ -84,11 +141,25 @@ } runGeth(t, "--datadir", datadir, "init", json).WaitExit()   // Query the custom genesis block - geth := runGeth(t, "--networkid", "1337", "--syncmode=full", "--cache", "16", - "--datadir", datadir, "--maxpeers", "0", "--port", "0", + geth := runGeth(t, "--nousb", "--networkid", "1337", "--syncmode=full", + "--datadir", datadir, "--maxpeers", "0", "--port", "0", "--light.maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--exec", tt.query, "console") geth.ExpectRegexp(tt.result) geth.ExpectExit() } } + +// TestRegistryInGenesis tests that initializing Geth with a default genesis block(mainnet genesis) +// Expects registry contract is deployed. +func TestRegistryInGenesis(t *testing.T) { + query := fmt.Sprintf("eth.getCode(%s)", registryAddress) + + // Query the custom genesis block + geth := runGeth(t, "--maxpeers", "0", "--port", "0", "--light.maxpeers", "0", + "--nodiscover", "--nat", "none", "--ipcdisable", + "--exec", query, "console") + defer geth.Cleanup() + geth.ExpectRegexp(registryCode) + geth.ExpectExit() +}
diff --git go-ethereum/cmd/geth/accountcmd.go celo/cmd/geth/accountcmd.go index 703a937bfcd32a6888b776d18b09deb17ef51ee6..102a2b1d816338f5bb70e9d73875a6ef73e6d514 100644 --- go-ethereum/cmd/geth/accountcmd.go +++ celo/cmd/geth/accountcmd.go @@ -17,18 +17,25 @@ package main   import ( + "encoding/hex" "fmt" "io/ioutil"   "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/accounts/usbwallet" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" - "gopkg.in/urfave/cli.v1" + cli "gopkg.in/urfave/cli.v1" )   var ( + blsFlag = cli.BoolFlag{ + Name: "bls", + Usage: "Set to specify generation of proof-of-possession of a BLS key.", + } walletCommand = cli.Command{ Name: "wallet", Usage: "Manage Ethereum presale wallets", @@ -101,6 +108,45 @@ Description: ` Print a short summary of all accounts`, }, { + Name: "proof-of-possession", + Usage: "Generate a proof-of-possession for the given account.", + Action: utils.MigrateFlags(accountProofOfPossession), + ArgsUsage: "<signer address> <message>", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.KeyStoreDirFlag, + utils.PasswordFileFlag, + utils.LightKDFFlag, + blsFlag, + }, + Description: ` +Print a proof-of-possession signature for the given account. + +Creates a signature over the given message, for example an account address, +with the private key corresponding to a given signer address. The signature +can then be used to prove possession of the signing key, for example to +authorize a the signer to act as a validator in the Celo protocol. +`, + }, + { + Name: "confirm-address", + Usage: "Confirm the entered address appears on the hardware wallet.", + Action: utils.MigrateFlags(accountConfirmHardwareAddress), + ArgsUsage: "<address>", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.KeyStoreDirFlag, + utils.PasswordFileFlag, + utils.LightKDFFlag, + }, + Description: ` +Confirms the address exists in the hardware wallet. + +The hardware wallet will not display an address that can't be derived from the stored private key. +`, + }, + + { Name: "new", Usage: "Create a new account", Action: utils.MigrateFlags(accountCreate), @@ -203,6 +249,130 @@ } return nil }   +func printProofOfPossession(account accounts.Account, pop []byte, keyType string, key []byte) { + fmt.Printf("Account {%x}:\n Signature: %s\n %s Public Key: %s\n", account.Address, hex.EncodeToString(pop), keyType, hex.EncodeToString(key)) +} + +func accountProofOfPossession(ctx *cli.Context) error { + if len(ctx.Args()) != 2 { + utils.Fatalf("Please specify the address to prove possession of and the address to sign as proof-of-possession.") + } + + stack, _ := makeConfigNode(ctx) + am := stack.AccountManager() + ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + + signer := common.HexToAddress(ctx.Args()[0]) + messageString := ctx.Args()[1] + if !common.IsHexAddress(messageString) { + utils.Fatalf("Address to sign is an invalid address") + } + message := common.HexToAddress(messageString) + + var err error + var wallet accounts.Wallet + account := accounts.Account{Address: signer} + foundAccount := false + + for _, wallet = range am.Wallets() { + if wallet.URL().Scheme == keystore.KeyStoreScheme { + if wallet.Contains(account) { + foundAccount = true + break + } + } else if wallet.URL().Scheme == usbwallet.LedgerScheme { + if err := wallet.Open(""); err != nil { + if err != accounts.ErrWalletAlreadyOpen { + utils.Fatalf("Could not open Ledger wallet: %v", err) + } + } else { + defer wallet.Close() + } + + account, err = wallet.Derive(accounts.DefaultBaseDerivationPath, true) + if err != nil { + return err + } + if account.Address == signer { + foundAccount = true + break + } + } + } + if !foundAccount { + utils.Fatalf("Could not find signer account %x", signer) + } + + if wallet.URL().Scheme == keystore.KeyStoreScheme { + account, _ = unlockAccount(ks, signer.String(), 0, utils.MakePasswordList(ctx)) + } + var key []byte + var pop []byte + keyType := "ECDSA" + if ctx.IsSet(blsFlag.Name) { + keyType = "BLS" + key, pop, err = ks.GenerateProofOfPossessionBLS(account, message) + } else { + key, pop, err = wallet.GenerateProofOfPossession(account, message) + } + if err != nil { + return err + } + + printProofOfPossession(account, pop, keyType, key) + + return nil +} + +func accountConfirmHardwareAddress(ctx *cli.Context) error { + if len(ctx.Args()) != 1 { + utils.Fatalf("Please specify the address to confirm on the hardware wallet.") + } + + stack, _ := makeConfigNode(ctx) + am := stack.AccountManager() + + address := common.HexToAddress(ctx.Args()[0]) + + var err error + var wallet accounts.Wallet + account := accounts.Account{Address: address} + foundAccount := false + + for _, wallet = range am.Wallets() { + if wallet.URL().Scheme == usbwallet.LedgerScheme { + if err := wallet.Open(""); err != nil { + if err != accounts.ErrWalletAlreadyOpen { + utils.Fatalf("Could not open Ledger wallet: %v", err) + } + } else { + defer wallet.Close() + } + + account, err = wallet.Derive(accounts.DefaultBaseDerivationPath, true) + if err != nil { + return err + } + if account.Address == address { + foundAccount = true + receivedAddress, err := wallet.ConfirmAddress(accounts.DefaultBaseDerivationPath) + if err != nil { + return err + } + if receivedAddress != address { + utils.Fatalf("Address %x is different in the ledger %x", address, receivedAddress) + } + break + } + } + } + if !foundAccount { + utils.Fatalf("Could not find account %x", account) + } + + return nil +} + // tries unlocking the specified account a few times. func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) { account, err := utils.MakeAddress(ks, address) @@ -270,7 +440,7 @@ } utils.SetNodeConfig(ctx, &cfg.Node) keydir, err := cfg.Node.KeyDirConfig() if err != nil { - utils.Fatalf("Failed to read configuration: %v", err) + utils.Fatalf("Failed get keystore dir: %v", err) } scryptN := keystore.StandardScryptN scryptP := keystore.StandardScryptP
diff --git go-ethereum/cmd/geth/run_test.go celo/cmd/geth/run_test.go index 8bf09649add1091f2cbe646ddc36bc8c344be20d..5941fde5dab5bb6df9d847f587a5a0f5d70e3687 100644 --- go-ethereum/cmd/geth/run_test.go +++ celo/cmd/geth/run_test.go @@ -75,7 +75,7 @@ case "--datadir": if i < len(args)-1 { tt.Datadir = args[i+1] } - case "--miner.etherbase": + case "--etherbase": if i < len(args)-1 { tt.Etherbase = args[i+1] }
diff --git go-ethereum/cmd/geth/dao_test.go celo/cmd/geth/dao_test.go index 15d498083b5d8549444a9c02bea06fc926aa1ce3..5ba4d9c2b5fad1416fdc4196c4d4e3fcc68abbb5 100644 --- go-ethereum/cmd/geth/dao_test.go +++ celo/cmd/geth/dao_test.go @@ -32,11 +32,8 @@ // Genesis block for nodes which don't care about the DAO fork (i.e. not configured) var daoOldGenesis = `{ "alloc" : {}, "coinbase" : "0x0000000000000000000000000000000000000000", - "difficulty" : "0x20000", "extraData" : "", - "gasLimit" : "0x2fefd8", "nonce" : "0x0000000000000042", - "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00", "config" : { @@ -48,11 +45,8 @@ // Genesis block for nodes which actively oppose the DAO fork var daoNoForkGenesis = `{ "alloc" : {}, "coinbase" : "0x0000000000000000000000000000000000000000", - "difficulty" : "0x20000", "extraData" : "", - "gasLimit" : "0x2fefd8", "nonce" : "0x0000000000000042", - "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00", "config" : { @@ -66,11 +60,8 @@ // Genesis block for nodes which actively support the DAO fork var daoProForkGenesis = `{ "alloc" : {}, "coinbase" : "0x0000000000000000000000000000000000000000", - "difficulty" : "0x20000", "extraData" : "", - "gasLimit" : "0x2fefd8", "nonce" : "0x0000000000000042", - "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00", "config" : { @@ -80,12 +71,14 @@ "daoForkSupport" : true } }`   -var daoGenesisHash = common.HexToHash("5e1fc79cb4ffa4739177b5408045cd5d51c6cf766133f23f7cd72ee1f8d790e0") +var daoGenesisHash = common.HexToHash("0x5e1fc79cb4ffa4739177b5408045cd5d51c6cf766133f23f7cd72ee1f8d790e0") var daoGenesisForkBlock = big.NewInt(314)   // TestDAOForkBlockNewChain tests that the DAO hard-fork number and the nodes support/opposition is correctly // set in the database after various initialization procedures and invocations. func TestDAOForkBlockNewChain(t *testing.T) { + t.Skip() + for i, arg := range []struct { genesis string expectBlock *big.Int @@ -118,7 +111,8 @@ } runGeth(t, "--datadir", datadir, "--networkid", "1337", "init", json).WaitExit() } else { // Force chain initialization - args := []string{"--port", "0", "--networkid", "1337", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir} + args := []string{"--port", "0", "--nousb", "--networkid", "1337", "--maxpeers", "0", "--light.maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir} + // args := []string{"--port", "0", "--networkid", "1337", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir} runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...).WaitExit() } // Retrieve the DAO config flag from the database @@ -129,7 +123,7 @@ t.Fatalf("test %d: failed to open test database: %v", test, err) } defer db.Close()   - genesisHash := common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") + genesisHash := params.MainnetGenesisHash if genesis != "" { genesisHash = daoGenesisHash }
diff --git go-ethereum/cmd/geth/snapshot.go celo/cmd/geth/snapshot.go index f4046255be0f7d38f93c2c85c59de77802466027..0bdac231e6795ed3c2e7ec0905ebf1d85fab085b 100644 --- go-ethereum/cmd/geth/snapshot.go +++ celo/cmd/geth/snapshot.go @@ -61,9 +61,8 @@ Category: "MISCELLANEOUS COMMANDS", Flags: []cli.Flag{ utils.DataDirFlag, utils.AncientFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, utils.CacheTrieJournalFlag, utils.BloomFilterSizeFlag, }, @@ -91,9 +90,8 @@ Category: "MISCELLANEOUS COMMANDS", Flags: []cli.Flag{ utils.DataDirFlag, utils.AncientFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, }, Description: ` geth snapshot verify-state <state-root> @@ -111,9 +109,8 @@ Category: "MISCELLANEOUS COMMANDS", Flags: []cli.Flag{ utils.DataDirFlag, utils.AncientFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, }, Description: ` geth snapshot traverse-state <state-root> @@ -133,9 +130,8 @@ Category: "MISCELLANEOUS COMMANDS", Flags: []cli.Flag{ utils.DataDirFlag, utils.AncientFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, }, Description: ` geth snapshot traverse-rawstate <state-root> @@ -156,9 +152,8 @@ Category: "MISCELLANEOUS COMMANDS", Flags: []cli.Flag{ utils.DataDirFlag, utils.AncientFlag, - utils.RopstenFlag, - utils.RinkebyFlag, - utils.GoerliFlag, + utils.BaklavaFlag, + utils.AlfajoresFlag, utils.ExcludeCodeFlag, utils.ExcludeStorageFlag, utils.StartKeyFlag,
diff --git go-ethereum/cmd/geth/misccmd.go celo/cmd/geth/misccmd.go index 4262b824dc1cadfc4e950396ab628c0e2fcc46e3..3eaef20a6d55820ceed86a1025d6e1cb81b7a991 100644 --- go-ethereum/cmd/geth/misccmd.go +++ celo/cmd/geth/misccmd.go @@ -20,11 +20,9 @@ import ( "fmt" "os" "runtime" - "strconv" "strings"   "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/params" "gopkg.in/urfave/cli.v1" ) @@ -42,32 +40,6 @@ Value: fmt.Sprintf("Geth/v%v/%v-%v/%v", params.VersionWithCommit(gitCommit, gitDate), runtime.GOOS, runtime.GOARCH, runtime.Version()), } - makecacheCommand = cli.Command{ - Action: utils.MigrateFlags(makecache), - Name: "makecache", - Usage: "Generate ethash verification cache (for testing)", - ArgsUsage: "<blockNum> <outputDir>", - Category: "MISCELLANEOUS COMMANDS", - Description: ` -The makecache command generates an ethash cache in <outputDir>. - -This command exists to support the system testing project. -Regular users do not need to execute it. -`, - } - makedagCommand = cli.Command{ - Action: utils.MigrateFlags(makedag), - Name: "makedag", - Usage: "Generate ethash mining DAG (for testing)", - ArgsUsage: "<blockNum> <outputDir>", - Category: "MISCELLANEOUS COMMANDS", - Description: ` -The makedag command generates an ethash DAG in <outputDir>. - -This command exists to support the system testing project. -Regular users do not need to execute it. -`, - } versionCommand = cli.Command{ Action: utils.MigrateFlags(version), Name: "version", @@ -101,36 +73,6 @@ ArgsUsage: " ", Category: "MISCELLANEOUS COMMANDS", } ) - -// makecache generates an ethash verification cache into the provided folder. -func makecache(ctx *cli.Context) error { - args := ctx.Args() - if len(args) != 2 { - utils.Fatalf(`Usage: geth makecache <block number> <outputdir>`) - } - block, err := strconv.ParseUint(args[0], 0, 64) - if err != nil { - utils.Fatalf("Invalid block number: %v", err) - } - ethash.MakeCache(block, args[1]) - - return nil -} - -// makedag generates an ethash mining DAG into the provided folder. -func makedag(ctx *cli.Context) error { - args := ctx.Args() - if len(args) != 2 { - utils.Fatalf(`Usage: geth makedag <block number> <outputdir>`) - } - block, err := strconv.ParseUint(args[0], 0, 64) - if err != nil { - utils.Fatalf("Invalid block number: %v", err) - } - ethash.MakeDataset(block, args[1]) - - return nil -}   func version(ctx *cli.Context) error { fmt.Println(strings.Title(clientIdentifier))
diff --git go-ethereum/cmd/geth/testdata/clique.json celo/cmd/geth/testdata/clique.json index b54b4a7d3b7210f4266d22555b1e0af52d9f19bf..17aced6d05b0e1bbb22b7b7d1fc48281c0aceda7 100644 --- go-ethereum/cmd/geth/testdata/clique.json +++ celo/cmd/geth/testdata/clique.json @@ -8,17 +8,20 @@ "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, - "clique": { - "period": 5, - "epoch": 30000 + "istanbul": { } }, "difficulty": "1", "gasLimit": "8000000", - "extradata": "0x000000000000000000000000000000000000000000000000000000000000000002f0d131f1f97aef08aec6e3291b957d9efe71050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0xecc833a7747eaa8327335e8e0c6b6d8aa3a38d0063591e43ce116ccf5c89753ef90262f869945296a071434eb2943c1bde8a2e1d555d911cd1d594bc236d14fbbe74b4d3bc132309b861dcf1c527c594ab3233a00d1e03b3e0c7c9912421b56f498901ef9421d87e9445d3b355807c91937e70e6f9914f2a1d94f6a964ad845a8a8a4a50b884c140ab8c3ed8252cf901eab86056631e24a6ec7fb89b5d967b6e7169eed00c0d802b9050d5a749b9d69a2c70d047b5add1d78646c4c3d579e7da7691001f0c832e9b26dd02b960b610ebfa63cd3a9dc0d6da8abfd8c16ffc5679debc682216c2934af7dc93d82e4a42564b4d80b86065be0e12d1ac62a4ddf326cb0dde804d8c1aab376dff994c9db706c6a5c0d5ba57319105491658f9ae5d5a4f48752b006137148123d8a526b2237d7284d5f0ab664f186ef0e7649d586a9754f54ca042539b3ba7ea25d0209f639cce30262081b860f1dade6f52a125a236a546a5bf5468ef8c95da980a51ee2ea595919e80bb56b3941bd17315b2681a411b6f7b6a2aaa01815a62fff57a14cae0cbef5a540dbd34f098ae18c07f93137eefc25132ac1971c8e74f2ddf24ceeeece87dcd18f19500b8601e4c01b96874cbc25fe98a4a8300035865d02724ec7b62d1662eba6b49777aaad51b73eb228d136d8d1f436e391e7e01d2c597cfcc17465a3aa1951d610360365bb116ab759887d5a74064c663aeeb3facf55ddc9a212e9f06b9925d27011700b860306ff63a1746b45b891a33d8ad7a02c92bf0ec628499f5308c55b15e7b5a2ab3d97113ef669c5ae446cb69b965c28d0056c780a4f2462b989bcb15380cc71fcf163d7e7be97c8c2a70b72e8273ff87f7d249f3c552fe7ecc28331f4ce90d92808080c3808080c3808080", "alloc": { "02f0d131f1f97aef08aec6e3291b957d9efe7105": { "balance": "300000" + }, + "000000000000000000000000000000000000ce10" : { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a723058206808dd43e7d765afca53fe439122bc5eac16d708ce7d463451be5042426f101f0029", + "storage":{"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103":"47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95"}, + "balance":"0" } } -} \ No newline at end of file +}
diff --git go-ethereum/cmd/bootnode/main.go celo/cmd/bootnode/main.go index 9743498d4e491e782db7fc35a5d96ea878971e4f..90c96d511d3306ecb12d5982bd84d457af7ba501 100644 --- go-ethereum/cmd/bootnode/main.go +++ celo/cmd/bootnode/main.go @@ -27,6 +27,7 @@ "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/nat" @@ -42,16 +43,19 @@ nodeKeyFile = flag.String("nodekey", "", "private key filename") nodeKeyHex = flag.String("nodekeyhex", "", "private key as hex (for testing)") natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:<IP>)") netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)") + runv4 = flag.Bool("v4", true, "run a v4 topic discovery bootnode") runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode") verbosity = flag.Int("verbosity", int(log.LvlInfo), "log verbosity (0-5)") vmodule = flag.String("vmodule", "", "log verbosity pattern") + networkId = flag.Uint64("networkid", 0, "network ID") + pingIPFromPacket = flag.Bool("ping-ip-from-packet", false, "Has the discovery protocol use the IP address given by a ping packet")   nodeKey *ecdsa.PrivateKey err error ) flag.Parse()   - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) + glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.JSONFormat())) glogger.Verbosity(log.Lvl(*verbosity)) glogger.Vmodule(*vmodule) log.Root().SetHandler(glogger) @@ -61,6 +65,8 @@ if err != nil { utils.Fatalf("-nat: %v", err) } switch { + case !*runv4 && !*runv5: + utils.Fatalf("Must specify at least one discovery protocol (v4 or v5)") case *genKey != "": nodeKey, err = crypto.GenerateKey() if err != nil { @@ -84,6 +90,10 @@ case *nodeKeyHex != "": if nodeKey, err = crypto.HexToECDSA(*nodeKeyHex); err != nil { utils.Fatalf("-nodekeyhex: %v", err) } + case *networkId == 0: + utils.Fatalf("--networkid must be set to non zero number") + case *runv5: + utils.Fatalf("Celo networks currently do not support discovery v5") }   if *writeAddr { @@ -120,18 +130,38 @@ }   printNotice(&nodeKey.PublicKey, *realaddr)   + // If v4 and v5 are both enabled, a packet is first tried as v4 + // and then v5 if v4 decoding fails, following the same pattern as full + // nodes that use v4 and v5: + // https://github.com/ethereum/go-ethereum/blob/7fbd6f3574f1c1c1e657c152fc63fb771adab3af/p2p/server.go#L588 + var unhandled chan discover.ReadPacket + var sconn *p2p.SharedUDPConn db, _ := enode.OpenDB("") - ln := enode.NewLocalNode(db, nodeKey) + ln := enode.NewLocalNode(db, nodeKey, *networkId) cfg := discover.Config{ PrivateKey: nodeKey, NetRestrict: restrictList, + PingIPFromPacket: *pingIPFromPacket, } + if *runv4 { + if *runv5 { + unhandled = make(chan discover.ReadPacket, 100) + cfg.Unhandled = unhandled + sconn = &p2p.SharedUDPConn{UDPConn: conn, Unhandled: unhandled} + } + if _, err := discover.ListenUDP(conn, ln, cfg); err != nil { + utils.Fatalf("%v", err) + } + } + if *runv5 { - if _, err := discover.ListenV5(conn, ln, cfg); err != nil { - utils.Fatalf("%v", err) + var err error + if sconn != nil { + _, err = discover.ListenV5(sconn, ln, cfg) + } else { + _, err = discover.ListenV5(conn, ln, cfg) } - } else { - if _, err := discover.ListenUDP(conn, ln, cfg); err != nil { + if err != nil { utils.Fatalf("%v", err) } }
diff --git go-ethereum/cmd/utils/flags_legacy.go celo/cmd/utils/flags_legacy.go index dc8952863872f51c0ca01cc96f56bf1eea24bd62..1c9f01a786b1e46f519bf8c3328bfb6b1a2a9a80 100644 --- go-ethereum/cmd/utils/flags_legacy.go +++ celo/cmd/utils/flags_legacy.go @@ -18,8 +18,10 @@ package utils   import ( "fmt" + "math/big"   "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/node" "gopkg.in/urfave/cli.v1" )   @@ -33,21 +35,120 @@ Description: "Show flags that have been deprecated and will soon be removed", }   var DeprecatedFlags = []cli.Flag{ - LegacyMinerGasTargetFlag, + LegacyTestnetFlag, + LegacyLightServFlag, + LegacyLightPeersFlag, + LegacyMinerExtraDataFlag, + LegacyMinerGasPriceFlag, + EtherbaseFlag, + LegacyProxyEnodeURLPairsFlag, + LegacyIstanbulRequestTimeoutFlag, + LegacyIstanbulBlockPeriodFlag, + LegacyIstanbulProposerPolicyFlag, + LegacyEthStatsURLFlag, NoUSBFlag, }   var ( + // (Deprecated April 2018) + LegacyMinerExtraDataFlag = cli.StringFlag{ + Name: "extradata", + Usage: "Block extra data set by the miner (default = client version, deprecated, use --miner.extradata)", + } + + // (Deprecated June 2019) + LegacyLightServFlag = cli.IntFlag{ + Name: "lightserv", + Usage: "Maximum percentage of time allowed for serving LES requests (deprecated, use --light.serve)", + Value: ethconfig.Defaults.LightServ, + } + LegacyLightPeersFlag = cli.IntFlag{ + Name: "lightpeers", + Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated, use --light.maxpeers)", + Value: ethconfig.Defaults.LightPeers, + } + + // (Deprecated April 2020) + LegacyTestnetFlag = cli.BoolFlag{ // TODO(q9f): Remove after Ropsten is discontinued. + Name: "testnet", + Usage: "Pre-configured test network (Deprecated: Please choose one of --goerli, --rinkeby, or --ropsten.)", + } + // (Deprecated May 2020, shown in aliased flags section) NoUSBFlag = cli.BoolFlag{ Name: "nousb", Usage: "Disables monitoring for and managing USB hardware wallets (deprecated)", } - // (Deprecated July 2021, shown in aliased flags section) - LegacyMinerGasTargetFlag = cli.Uint64Flag{ - Name: "miner.gastarget", - Usage: "Target gas floor for mined blocks (deprecated)", - Value: ethconfig.Defaults.Miner.GasFloor, + LegacyWSListenAddrFlag = cli.StringFlag{ + Name: "wsaddr", + Usage: "WS-RPC server listening interface (deprecated, use --ws.addr)", + Value: node.DefaultWSHost, + } + LegacyWSPortFlag = cli.IntFlag{ + Name: "wsport", + Usage: "WS-RPC server listening port (deprecated, use --ws.port)", + Value: node.DefaultWSPort, + } + LegacyWSApiFlag = cli.StringFlag{ + Name: "wsapi", + Usage: "API's offered over the WS-RPC interface (deprecated, use --ws.api)", + Value: "", + } + LegacyWSAllowedOriginsFlag = cli.StringFlag{ + Name: "wsorigins", + Usage: "Origins from which to accept websockets requests (deprecated, use --ws.origins)", + Value: "", + } + + // Deprecated in celo-blockchain v1.2.0 + LegacyEthStatsURLFlag = cli.StringFlag{ + Name: "ethstats", + Usage: "Reporting URL of a celostats service (nodename:secret@host:port) (deprecated, Use --celostats)", + } + LegacyProxyEnodeURLPairsFlag = cli.StringFlag{ + Name: "proxy.proxyenodeurlpair", + Usage: "Each enode URL in a pair is separated by a semicolon. Enode URL pairs are separated by a space. The format should be \"<proxy 0 internal facing enode URL>;<proxy 0 external facing enode URL>,<proxy 1 internal facing enode URL>;<proxy 1 external facing enode URL>,...\" (deprecated, use --proxy.proxyenodeurlpairs)", + } + + // Deprecated in celo-blockchain v1.3.0 + LegacyMinerEtherbaseFlag = cli.StringFlag{ + Name: "miner.etherbase", + Usage: "Public address for block mining rewards (deprecated, use --etherbase or both --tx-fee-recipient and --miner.validator)", + Value: "0", + } + + LegacyIstanbulRequestTimeoutFlag = cli.Uint64Flag{ + Name: "istanbul.requesttimeout", + Usage: "Timeout for each Istanbul round in milliseconds (deprecated, value obtained from genesis config)", + Value: 0, + } + LegacyIstanbulBlockPeriodFlag = cli.Uint64Flag{ + Name: "istanbul.blockperiod", + Usage: "Default minimum difference between two consecutive block's timestamps in seconds (deprecated, value obtained from genesis config)", + Value: 0, + } + LegacyIstanbulProposerPolicyFlag = cli.Uint64Flag{ + Name: "istanbul.proposerpolicy", + Usage: "Default minimum difference between two consecutive block's timestamps in seconds (deprecated, value obtained from genesis config)", + Value: 0, + } + + // Deprecated in celo-blockchain 1.4.0 + LegacyMinerGasPriceFlag = BigFlag{ + Name: "miner.gasprice", + Usage: "Minimum gas price for mining a transaction", + Value: big.NewInt(1), + } + + // (Deprecated July 2020, shown in aliased flags section) + LegacyGraphQLListenAddrFlag = cli.StringFlag{ + Name: "graphql.addr", + Usage: "GraphQL server listening interface (deprecated, graphql can only be enabled on the HTTP-RPC server endpoint, use --graphql)", + } + LegacyGraphQLPortFlag = cli.IntFlag{ + Name: "graphql.port", + Usage: "GraphQL server listening port (deprecated, graphql can only be enabled on the HTTP-RPC server endpoint, use --graphql)", + Value: node.DefaultHTTPPort, } )
diff --git go-ethereum/cmd/utils/flags.go celo/cmd/utils/flags.go index f3a8aa224ceb0ef248b270835a133acf179776e3..0a367d087ae4f1bcc14a08184eb9036fb718be39 100644 --- go-ethereum/cmd/utils/flags.go +++ celo/cmd/utils/flags.go @@ -23,8 +23,6 @@ "fmt" "io" "io/ioutil" "math" - "math/big" - "os" "path/filepath" godebug "runtime/debug" "strconv" @@ -37,9 +35,8 @@ "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/clique" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/vm" @@ -47,7 +44,6 @@ "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethstats" @@ -66,7 +62,7 @@ "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/params" - pcsclite "github.com/gballet/go-libpcsclite" + "github.com/ethereum/go-ethereum/rpc" gopsutil "github.com/shirou/gopsutil/mem" "gopkg.in/urfave/cli.v1" ) @@ -108,6 +104,7 @@ // are the same for all commands.   var ( // General settings + DataDirFlag = DirectoryFlag{ Name: "datadir", Usage: "Data directory for the databases and keystore", @@ -129,31 +126,22 @@ USBFlag = cli.BoolFlag{ Name: "usb", Usage: "Enable monitoring and management of USB hardware wallets", } - SmartCardDaemonPathFlag = cli.StringFlag{ - Name: "pcscdpath", - Usage: "Path to the smartcard daemon (pcscd) socket file", - Value: pcsclite.PCSCDSockName, - } NetworkIdFlag = cli.Uint64Flag{ Name: "networkid", - Usage: "Explicitly set network id (integer)(For testnets: use --ropsten, --rinkeby, --goerli instead)", - Value: ethconfig.Defaults.NetworkId, + Usage: "Explicitly set network id (integer)(For testnets: use --alfajores or --baklava instead)", + Value: params.MainnetNetworkId, } MainnetFlag = cli.BoolFlag{ Name: "mainnet", - Usage: "Ethereum mainnet", + Usage: "Celo mainnet", } - GoerliFlag = cli.BoolFlag{ - Name: "goerli", - Usage: "Görli network: pre-configured proof-of-authority test network", + AlfajoresFlag = cli.BoolFlag{ + Name: "alfajores", + Usage: "Alfajores network: pre-configured Celo test network", } - RinkebyFlag = cli.BoolFlag{ - Name: "rinkeby", - Usage: "Rinkeby network: pre-configured proof-of-authority test network", - } - RopstenFlag = cli.BoolFlag{ - Name: "ropsten", - Usage: "Ropsten network: pre-configured proof-of-work test network", + BaklavaFlag = cli.BoolFlag{ + Name: "baklava", + Usage: "Baklava network: pre-configured Celo test network", } DeveloperFlag = cli.BoolFlag{ Name: "dev", @@ -162,6 +150,7 @@ } DeveloperPeriodFlag = cli.IntFlag{ Name: "dev.period", Usage: "Block period to use in developer mode (0 = mine only if transaction pending)", + Value: 1, } IdentityFlag = cli.StringFlag{ Name: "identity", @@ -203,9 +192,10 @@ Usage: "Max number of elements (0 = no limit)", Value: 0, } defaultSyncMode = ethconfig.Defaults.SyncMode + // TODO: Check if snap sync is enabled SyncModeFlag = TextMarshalerFlag{ Name: "syncmode", - Usage: `Blockchain sync mode ("fast", "full", "snap" or "light")`, + Usage: `Blockchain sync mode ("fast", "full", "light", or "lightest")`, Value: &defaultSyncMode, } GCModeFlag = cli.StringFlag{ @@ -230,16 +220,35 @@ WhitelistFlag = cli.StringFlag{ Name: "whitelist", Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)", } + EtherbaseFlag = cli.StringFlag{ + Name: "etherbase", + Usage: "Public address for transaction broadcasting and block mining rewards (default = first account)", + Value: "0", + } + TxFeeRecipientFlag = cli.StringFlag{ + Name: "tx-fee-recipient", + Usage: "Public address for block transaction fees and gateway fees", + Value: "0", + } + BLSbaseFlag = cli.StringFlag{ + Name: "blsbase", + Usage: "Public address for block mining BLS signatures (default = first account created)", + Value: "0", + } + + // Hard fork activation overrides + OverrideEHardforkFlag = cli.Uint64Flag{ + Name: "override.espresso", + Usage: "Manually specify the espresso fork block, overriding the bundled setting", + } + BloomFilterSizeFlag = cli.Uint64Flag{ Name: "bloomfilter.size", Usage: "Megabytes of memory allocated to bloom-filter for pruning", Value: 2048, } - OverrideLondonFlag = cli.Uint64Flag{ - Name: "override.london", - Usage: "Manually specify London fork-block, overriding the bundled setting", - } // Light server and client settings + LightServeFlag = cli.IntFlag{ Name: "light.serve", Usage: "Maximum percentage of time allowed for serving LES requests (multi-threaded processing allows values over 100)", @@ -260,6 +269,11 @@ Name: "light.maxpeers", Usage: "Maximum number of light clients to serve, or light servers to attach to", Value: ethconfig.Defaults.LightPeers, } + LightGatewayFeeFlag = BigFlag{ + Name: "light.gatewayfee", + Usage: "Minimum value of gateway fee to serve a light client transaction", + Value: ethconfig.Defaults.GatewayFee, + } UltraLightServersFlag = cli.StringFlag{ Name: "ulc.servers", Usage: "List of trusted ultra-light servers", @@ -278,49 +292,14 @@ LightNoPruneFlag = cli.BoolFlag{ Name: "light.nopruning", Usage: "Disable ancient light chain data pruning", } + LightNoSyncServeFlag = cli.BoolFlag{ Name: "light.nosyncserve", Usage: "Enables serving light clients before syncing", } - // Ethash settings - EthashCacheDirFlag = DirectoryFlag{ - Name: "ethash.cachedir", - Usage: "Directory to store the ethash verification caches (default = inside the datadir)", - } - EthashCachesInMemoryFlag = cli.IntFlag{ - Name: "ethash.cachesinmem", - Usage: "Number of recent ethash caches to keep in memory (16MB each)", - Value: ethconfig.Defaults.Ethash.CachesInMem, - } - EthashCachesOnDiskFlag = cli.IntFlag{ - Name: "ethash.cachesondisk", - Usage: "Number of recent ethash caches to keep on disk (16MB each)", - Value: ethconfig.Defaults.Ethash.CachesOnDisk, - } - EthashCachesLockMmapFlag = cli.BoolFlag{ - Name: "ethash.cacheslockmmap", - Usage: "Lock memory maps of recent ethash caches", - } - EthashDatasetDirFlag = DirectoryFlag{ - Name: "ethash.dagdir", - Usage: "Directory to store the ethash mining DAGs", - Value: DirectoryString(ethconfig.Defaults.Ethash.DatasetDir), - } - EthashDatasetsInMemoryFlag = cli.IntFlag{ - Name: "ethash.dagsinmem", - Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)", - Value: ethconfig.Defaults.Ethash.DatasetsInMem, - } - EthashDatasetsOnDiskFlag = cli.IntFlag{ - Name: "ethash.dagsondisk", - Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)", - Value: ethconfig.Defaults.Ethash.DatasetsOnDisk, - } - EthashDatasetsLockMmapFlag = cli.BoolFlag{ - Name: "ethash.dagslockmmap", - Usage: "Lock memory maps for recent ethash mining DAGs", - } + // Transaction pool settings + TxPoolLocalsFlag = cli.StringFlag{ Name: "txpool.locals", Usage: "Comma separated accounts to treat as locals (no flush, priority inclusion)", @@ -374,7 +353,9 @@ Name: "txpool.lifetime", Usage: "Maximum amount of time non-executable transaction are queued", Value: ethconfig.Defaults.TxPool.Lifetime, } + // Performance tuning settings + CacheFlag = cli.IntFlag{ Name: "cache", Usage: "Megabytes of memory allocated to internal caching (default = 4096 mainnet full node, 128 light mode)", @@ -419,52 +400,23 @@ Name: "cache.preimages", Usage: "Enable recording the SHA3/keccak preimages of trie keys", } // Miner settings + MiningEnabledFlag = cli.BoolFlag{ Name: "mine", Usage: "Enable mining", } - MinerThreadsFlag = cli.IntFlag{ - Name: "miner.threads", - Usage: "Number of CPU threads to use for mining", - Value: 0, - } - MinerNotifyFlag = cli.StringFlag{ - Name: "miner.notify", - Usage: "Comma separated HTTP URL list to notify of new work packages", - } - MinerNotifyFullFlag = cli.BoolFlag{ - Name: "miner.notify.full", - Usage: "Notify with pending block headers instead of work packages", - } - MinerGasLimitFlag = cli.Uint64Flag{ - Name: "miner.gaslimit", - Usage: "Target gas ceiling for mined blocks", - Value: ethconfig.Defaults.Miner.GasCeil, - } - MinerGasPriceFlag = BigFlag{ - Name: "miner.gasprice", - Usage: "Minimum gas price for mining a transaction", - Value: ethconfig.Defaults.Miner.GasPrice, - } - MinerEtherbaseFlag = cli.StringFlag{ - Name: "miner.etherbase", - Usage: "Public address for block mining rewards (default = first account)", + + MinerValidatorFlag = cli.StringFlag{ + Name: "miner.validator", + Usage: "Public address for participation in consensus", Value: "0", } MinerExtraDataFlag = cli.StringFlag{ Name: "miner.extradata", Usage: "Block extra data set by the miner (default = client version)", } - MinerRecommitIntervalFlag = cli.DurationFlag{ - Name: "miner.recommit", - Usage: "Time interval to recreate the block being mined", - Value: ethconfig.Defaults.Miner.Recommit, - } - MinerNoVerifyFlag = cli.BoolFlag{ - Name: "miner.noverify", - Usage: "Disable remote sealing verification", - } // Account settings + UnlockedAccountFlag = cli.StringFlag{ Name: "unlock", Usage: "Comma separated list of accounts to unlock", @@ -488,6 +440,11 @@ InsecureUnlockAllowedFlag = cli.BoolFlag{ Name: "allow-insecure-unlock", Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http", } + RPCGlobalGasInflationRateFlag = cli.Float64Flag{ + Name: "rpc.gasinflationrate", + Usage: "Multiplier applied to the gasEstimation rpc call (1 = gasEstimation, 1.3 = gasEstimation + 30%, etc. Defaults to 1.3)", + Value: ethconfig.Defaults.RPCGasInflationRate, + } RPCGlobalGasCapFlag = cli.Uint64Flag{ Name: "rpc.gascap", Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)", @@ -495,23 +452,25 @@ Value: ethconfig.Defaults.RPCGasCap, } RPCGlobalTxFeeCapFlag = cli.Float64Flag{ Name: "rpc.txfeecap", - Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)", + Usage: "Sets a cap on transaction fee (in celo) that can be sent via the RPC APIs (0 = no cap)", Value: ethconfig.Defaults.RPCTxFeeCap, } // Logging and debug settings - EthStatsURLFlag = cli.StringFlag{ - Name: "ethstats", - Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)", - } - FakePoWFlag = cli.BoolFlag{ - Name: "fakepow", - Usage: "Disables proof-of-work verification", + + CeloStatsURLFlag = cli.StringFlag{ + Name: "celostats", + Usage: "Reporting URL of a celostats service (nodename:secret@host:port)", } NoCompactionFlag = cli.BoolFlag{ Name: "nocompaction", Usage: "Disables db compaction after import", } // RPC settings + DisableRPCETHCompatibility = cli.BoolFlag{ + Name: "disablerpcethcompatibility", + Usage: "If set, blocks returned from the RPC api will not contain the 'gasLimit' and 'baseFeePerGas' fields, which were added to the RPC block representation in order to improve compatibility with ethereum tooling. Note these fields do not currently exist on the internal block representation so they should be disregarded for the purposes of hashing or signing", + } + IPCDisabledFlag = cli.BoolFlag{ Name: "ipcdisable", Usage: "Disable the IPC-RPC server", @@ -554,6 +513,21 @@ Name: "http.rpcprefix", Usage: "HTTP path path prefix on which JSON-RPC is served. Use '/' to serve on all paths.", Value: "", } + HTTPRequestReadTimeout = cli.IntFlag{ + Name: "http.timeout.read", + Usage: "Timeout in seconds for HTTP-RPC read requests", + Value: int(rpc.DefaultHTTPTimeouts.ReadTimeout / time.Second), + } + HTTPRequestWriteTimeout = cli.IntFlag{ + Name: "http.timeout.write", + Usage: "Timeout in seconds for HTTP-RPC write requests", + Value: int(rpc.DefaultHTTPTimeouts.WriteTimeout / time.Second), + } + HTTPRequestIdleTimeout = cli.IntFlag{ + Name: "http.timeout.idle", + Usage: "Timeout in seconds for HTTP-RPC idle connections", + Value: int(rpc.DefaultHTTPTimeouts.IdleTimeout / time.Second), + } GraphQLEnabledFlag = cli.BoolFlag{ Name: "graphql", Usage: "Enable GraphQL on the HTTP-RPC server. Note that GraphQL can only be started if an HTTP server is started as well.", @@ -605,12 +579,8 @@ PreloadJSFlag = cli.StringFlag{ Name: "preload", Usage: "Comma separated list of JavaScript files to preload into the console", } - AllowUnprotectedTxs = cli.BoolFlag{ - Name: "rpc.allow-unprotected-txs", - Usage: "Allow for unprotected (non EIP155 signed) transactions to be submitted via RPC", - } - // Network Settings + MaxPeersFlag = cli.IntFlag{ Name: "maxpeers", Usage: "Maximum number of network peers (network disabled if set to 0)", @@ -656,6 +626,19 @@ NetrestrictFlag = cli.StringFlag{ Name: "netrestrict", Usage: "Restricts network communication to the given IP networks (CIDR masks)", } + PingIPFromPacketFlag = cli.BoolFlag{ + Name: "ping-ip-from-packet", + Usage: "Has the discovery protocol use the IP address given by a ping packet", + } + UseInMemoryDiscoverTableFlag = cli.BoolFlag{ + Name: "use-in-memory-discovery-table", + Usage: "Specifies whether to use an in memory discovery table", + } + + VersionCheckFlag = cli.BoolFlag{ + Name: "disable-version-check", + Usage: "Disable version check. Use if the parameter is set erroneously", + } DNSDiscoveryFlag = cli.StringFlag{ Name: "discovery.dns", Usage: "Sets DNS discovery entry points (use \"\" to disable DNS)", @@ -668,29 +651,30 @@ Usage: "JavaScript root path for `loadScript`", Value: DirectoryString("."), }   - // Gas price oracle settings - GpoBlocksFlag = cli.IntFlag{ - Name: "gpo.blocks", - Usage: "Number of recent blocks to check for gas prices", - Value: ethconfig.Defaults.GPO.Blocks, - } - GpoPercentileFlag = cli.IntFlag{ - Name: "gpo.percentile", - Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", - Value: ethconfig.Defaults.GPO.Percentile, - } - GpoMaxGasPriceFlag = cli.Int64Flag{ - Name: "gpo.maxprice", - Usage: "Maximum gas price will be recommended by gpo", - Value: ethconfig.Defaults.GPO.MaxPrice.Int64(), - } - GpoIgnoreGasPriceFlag = cli.Int64Flag{ - Name: "gpo.ignoreprice", - Usage: "Gas price below which gpo will ignore transactions", - Value: ethconfig.Defaults.GPO.IgnorePrice.Int64(), - } + // // Gas price oracle settings + // GpoBlocksFlag = cli.IntFlag{ + // Name: "gpo.blocks", + // Usage: "Number of recent blocks to check for gas prices", + // // Value: ethconfig.Defaults.GPO.Blocks, + // } + // GpoPercentileFlag = cli.IntFlag{ + // Name: "gpo.percentile", + // Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", + // // Value: ethconfig.Defaults.GPO.Percentile, + // } + // GpoMaxGasPriceFlag = cli.Int64Flag{ + // Name: "gpo.maxprice", + // Usage: "Maximum gas price will be recommended by gpo", + // // Value: ethconfig.Defaults.GPO.MaxPrice.Int64(), + // } + // GpoIgnoreGasPriceFlag = cli.Int64Flag{ + // Name: "gpo.ignoreprice", + // Usage: "Gas price below which gpo will ignore transactions", + // // Value: ethconfig.Defaults.GPO.IgnorePrice.Int64(), + // }   // Metrics flags + MetricsEnabledFlag = cli.BoolFlag{ Name: "metrics", Usage: "Enable metrics collection and reporting", @@ -771,9 +755,60 @@ Usage: "InfluxDB organization name (v2 only)", Value: metrics.DefaultConfig.InfluxDBOrganization, }   - CatalystFlag = cli.BoolFlag{ - Name: "catalyst", - Usage: "Catalyst mode (eth2 integration testing)", + MetricsLoadTestCSVFlag = cli.StringFlag{ + Name: "metrics.loadtestcsvfile", + Usage: "Write a csv with information about the block production cycle to the given file name. If passed an empty string or non-existent, do not output csv metrics.", + Value: "", + } + + // Istanbul settings + + IstanbulReplicaFlag = cli.BoolFlag{ + Name: "istanbul.replica", + Usage: "Run this node as a validator replica. Must be paired with --mine. Use the RPCs to enable participation in consensus.", + } + + // Announce settings + + AnnounceQueryEnodeGossipPeriodFlag = cli.Uint64Flag{ + Name: "announce.queryenodegossipperiod", + Usage: "Time duration (in seconds) between gossiped query enode messages", + Value: ethconfig.Defaults.Istanbul.AnnounceQueryEnodeGossipPeriod, + } + AnnounceAggressiveQueryEnodeGossipOnEnablementFlag = cli.BoolFlag{ + Name: "announce.aggressivequeryenodegossiponenablement", + Usage: "Specifies if this node should aggressively query enodes on announce enablement", + } + + // Proxy node settings + + ProxyFlag = cli.BoolFlag{ + Name: "proxy.proxy", + Usage: "Specifies whether this node is a proxy", + } + ProxyInternalFacingEndpointFlag = cli.StringFlag{ + Name: "proxy.internalendpoint", + Usage: "Specifies the internal facing endpoint for this proxy to listen to. The format should be <ip address>:<port>", + Value: ":30503", + } + ProxiedValidatorAddressFlag = cli.StringFlag{ + Name: "proxy.proxiedvalidatoraddress", + Usage: "Address of the proxied validator", + } + + // Proxied validator settings + + ProxiedFlag = cli.BoolFlag{ + Name: "proxy.proxied", + Usage: "Specifies whether this validator will be proxied by a proxy node", + } + ProxyEnodeURLPairsFlag = cli.StringFlag{ + Name: "proxy.proxyenodeurlpairs", + Usage: "Each enode URL in a pair is separated by a semicolon. Enode URL pairs are separated by a space. The format should be \"<proxy 0 internal facing enode URL>;<proxy 0 external facing enode URL>,<proxy 1 internal facing enode URL>;<proxy 1 external facing enode URL>,...\"", + } + ProxyAllowPrivateIPFlag = cli.BoolFlag{ + Name: "proxy.allowprivateip", + Usage: "Specifies whether private IP is allowed for external facing proxy enodeURL", } )   @@ -782,16 +817,10 @@ // if none (or the empty string) is specified. If the node is starting a testnet, // then a subdirectory of the specified datadir will be used. func MakeDataDir(ctx *cli.Context) string { if path := ctx.GlobalString(DataDirFlag.Name); path != "" { - if ctx.GlobalBool(RopstenFlag.Name) { - // Maintain compatibility with older Geth configurations storing the - // Ropsten database in `testnet` instead of `ropsten`. - return filepath.Join(path, "ropsten") - } - if ctx.GlobalBool(RinkebyFlag.Name) { - return filepath.Join(path, "rinkeby") - } - if ctx.GlobalBool(GoerliFlag.Name) { - return filepath.Join(path, "goerli") + if ctx.GlobalBool(BaklavaFlag.Name) { + return filepath.Join(path, "baklava") + } else if ctx.GlobalBool(AlfajoresFlag.Name) { + return filepath.Join(path, "alfajores") } return path } @@ -832,22 +861,26 @@ cfg.UserIdent = identity } }   -// setBootstrapNodes creates a list of bootstrap nodes from the command line -// flags, reverting to pre-configured ones if none have been specified. -func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { +func GetBootstrapNodes(ctx *cli.Context) []string { urls := params.MainnetBootnodes switch { case ctx.GlobalIsSet(BootnodesFlag.Name): urls = SplitAndTrim(ctx.GlobalString(BootnodesFlag.Name)) - case ctx.GlobalBool(RopstenFlag.Name): - urls = params.RopstenBootnodes - case ctx.GlobalBool(RinkebyFlag.Name): - urls = params.RinkebyBootnodes - case ctx.GlobalBool(GoerliFlag.Name): - urls = params.GoerliBootnodes - case cfg.BootstrapNodes != nil: - return // already set, don't apply defaults. + case ctx.GlobalBool(AlfajoresFlag.Name): + urls = params.AlfajoresBootnodes + case ctx.GlobalBool(BaklavaFlag.Name): + urls = params.BaklavaBootnodes } + return urls +} + +// setBootstrapNodes creates a list of bootstrap nodes from the command line +// flags, reverting to pre-configured ones if none have been specified. +func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { + if cfg.BootstrapNodes != nil { + return + } + urls := GetBootstrapNodes(ctx)   cfg.BootstrapNodes = make([]*enode.Node, 0, len(urls)) for _, url := range urls { @@ -865,8 +898,12 @@ // setBootstrapNodesV5 creates a list of bootstrap nodes from the command line // flags, reverting to pre-configured ones if none have been specified. func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) { - urls := params.V5Bootnodes + urls := params.MainnetBootnodes switch { + case ctx.GlobalBool(AlfajoresFlag.Name): + urls = params.AlfajoresBootnodes + case ctx.GlobalBool(BaklavaFlag.Name): + urls = params.BaklavaBootnodes case ctx.GlobalIsSet(BootnodesFlag.Name): urls = SplitAndTrim(ctx.GlobalString(BootnodesFlag.Name)) case cfg.BootstrapNodesV5 != nil: @@ -946,8 +983,15 @@ if ctx.GlobalIsSet(HTTPPathPrefixFlag.Name) { cfg.HTTPPathPrefix = ctx.GlobalString(HTTPPathPrefixFlag.Name) } - if ctx.GlobalIsSet(AllowUnprotectedTxs.Name) { - cfg.AllowUnprotectedTxs = ctx.GlobalBool(AllowUnprotectedTxs.Name) + + if ctx.GlobalIsSet(HTTPRequestReadTimeout.Name) { + cfg.HTTPTimeouts.ReadTimeout = time.Duration(ctx.GlobalInt(HTTPRequestReadTimeout.Name)) * time.Second + } + if ctx.GlobalIsSet(HTTPRequestWriteTimeout.Name) { + cfg.HTTPTimeouts.WriteTimeout = time.Duration(ctx.GlobalInt(HTTPRequestWriteTimeout.Name)) * time.Second + } + if ctx.GlobalIsSet(HTTPRequestIdleTimeout.Name) { + cfg.HTTPTimeouts.IdleTimeout = time.Duration(ctx.GlobalInt(HTTPRequestIdleTimeout.Name)) * time.Second } }   @@ -1014,6 +1058,9 @@ } if ctx.GlobalIsSet(LightMaxPeersFlag.Name) { cfg.LightPeers = ctx.GlobalInt(LightMaxPeersFlag.Name) } + if ctx.GlobalIsSet(LightGatewayFeeFlag.Name) { + cfg.GatewayFee = GlobalBig(ctx, LightGatewayFeeFlag.Name) + } if ctx.GlobalIsSet(UltraLightServersFlag.Name) { cfg.UltraLightServers = strings.Split(ctx.GlobalString(UltraLightServersFlag.Name), ",") } @@ -1027,6 +1074,10 @@ } if ctx.GlobalIsSet(UltraLightOnlyAnnounceFlag.Name) { cfg.UltraLightOnlyAnnounce = ctx.GlobalBool(UltraLightOnlyAnnounceFlag.Name) } + if ctx.GlobalBool(DeveloperFlag.Name) { + // --dev mode can't use p2p networking. + cfg.LightPeers = 0 + } if ctx.GlobalIsSet(LightNoPruneFlag.Name) { cfg.LightNoPrune = ctx.GlobalBool(LightNoPruneFlag.Name) } @@ -1044,7 +1095,12 @@ Fatalf("Failed to retrieve file descriptor allowance: %v", err) } raised, err := fdlimit.Raise(uint64(limit)) if err != nil { - Fatalf("Failed to raise file descriptor allowance: %v", err) + log.Warn(fmt.Sprintf("Failed to raise file descriptor allowance: %v", err)) + current, err := fdlimit.Current() + if err != nil { + Fatalf("Failed to retrieve current file descriptor limit: %v", err) + } + return current / 2 } return int(raised / 2) // Leave half for networking and other stuff } @@ -1074,25 +1130,76 @@ } return accs[index], nil }   -// setEtherbase retrieves the etherbase either from the directly specified +// setValidator retrieves the validator address either from the directly specified // command line flags or from the keystore if CLI indexed. -func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *ethconfig.Config) { - // Extract the current etherbase - var etherbase string - if ctx.GlobalIsSet(MinerEtherbaseFlag.Name) { - etherbase = ctx.GlobalString(MinerEtherbaseFlag.Name) +// `Validator` is the address used to sign consensus messages. +func setValidator(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) { + // Extract the current validator, new flag overriding legacy etherbase + var validator string + if ctx.GlobalIsSet(EtherbaseFlag.Name) { + validator = ctx.GlobalString(EtherbaseFlag.Name) + } + if ctx.GlobalIsSet(LegacyMinerEtherbaseFlag.Name) { + validator = ctx.GlobalString(LegacyMinerEtherbaseFlag.Name) } - // Convert the etherbase into an address and configure it - if etherbase != "" { - if ks != nil { - account, err := MakeAddress(ks, etherbase) + if ctx.GlobalIsSet(MinerValidatorFlag.Name) { + if validator != "" { + Fatalf("`etherbase` and `miner.validator` flag should not be used together. `miner.validator` and `tx-fee-recipient` constitute both of `etherbase`' functions") + } + validator = ctx.GlobalString(MinerValidatorFlag.Name) + } + + // Convert the validator into an address and configure it + if validator != "" { + account, err := MakeAddress(ks, validator) + if err != nil { + Fatalf("Invalid validator: %v", err) + } + cfg.Miner.Validator = account.Address + } +} + +// setTxFeeRecipient retrieves the txFeeRecipient address either from the directly specified +// command line flags or from the keystore if CLI indexed. +// `TxFeeRecipient` is the address earned block transaction fees are sent to. +func setTxFeeRecipient(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) { + if ctx.GlobalIsSet(TxFeeRecipientFlag.Name) { + if !ctx.GlobalIsSet(MinerValidatorFlag.Name) { + Fatalf("`etherbase` and `tx-fee-recipient` flag should not be used together. `miner.validator` and `tx-fee-recipient` constitute both of `etherbase`' functions") + } + txFeeRecipient := ctx.GlobalString(TxFeeRecipientFlag.Name) + + // Convert the txFeeRecipient into an address and configure it + if txFeeRecipient != "" { + account, err := MakeAddress(ks, txFeeRecipient) if err != nil { - Fatalf("Invalid miner etherbase: %v", err) + Fatalf("Invalid txFeeRecipient: %v", err) } - cfg.Miner.Etherbase = account.Address - } else { - Fatalf("No etherbase configured") + cfg.TxFeeRecipient = account.Address + } + } else { + // Backwards compatibility. If the miner was set by the "etherbase" flag, both should have the same info + cfg.TxFeeRecipient = cfg.Miner.Validator + } +} + +// setBLSbase retrieves the blsbase either from the directly specified +// command line flags or from the keystore if CLI indexed. +// `BLSbase` is the ethereum address which identifies an ECDSA key +// from which the BLS private key used for block finalization in consensus. +func setBLSbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) { + // Extract the current blsbase, new flag overriding legacy one + var blsbase string + if ctx.GlobalIsSet(BLSbaseFlag.Name) { + blsbase = ctx.GlobalString(BLSbaseFlag.Name) + } + // Convert the blsbase into an address and configure it + if blsbase != "" { + account, err := MakeAddress(ks, blsbase) + if err != nil { + Fatalf("Invalid blsbase: %v", err) } + cfg.BLSbase = account.Address } }   @@ -1120,10 +1227,14 @@ setNAT(ctx, cfg) setListenAddress(ctx, cfg) setBootstrapNodes(ctx, cfg) setBootstrapNodesV5(ctx, cfg) + + cfg.NetworkId = getNetworkId(ctx)   lightClient := ctx.GlobalString(SyncModeFlag.Name) == "light" - lightServer := (ctx.GlobalInt(LightServeFlag.Name) != 0) + lightServer := (ctx.GlobalInt(LightServeFlag.Name)) != 0   + // LightPeers is in the eth config, not the p2p config, so we don't have it here, so we + // get it here separately using GlobalInt() lightPeers := ctx.GlobalInt(LightMaxPeersFlag.Name) if lightClient && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) { // dynamic default - for clients we use 1/10th of the default for servers @@ -1150,14 +1261,24 @@ ethPeers := cfg.MaxPeers - lightPeers if lightClient { ethPeers = 0 } + if lightServer { + cfg.MaxLightClients = lightPeers + } log.Info("Maximum peer count", "ETH", ethPeers, "LES", lightPeers, "total", cfg.MaxPeers)   if ctx.GlobalIsSet(MaxPendingPeersFlag.Name) { cfg.MaxPendingPeers = ctx.GlobalInt(MaxPendingPeersFlag.Name) } - if ctx.GlobalIsSet(NoDiscoverFlag.Name) || lightClient { + + if ctx.GlobalBool(NoDiscoverFlag.Name) || lightClient { cfg.NoDiscovery = true } + if ctx.GlobalBool(PingIPFromPacketFlag.Name) { + cfg.PingIPFromPacket = true + } + if ctx.GlobalBool(UseInMemoryDiscoverTableFlag.Name) { + cfg.UseInMemoryNodeDatabase = true + }   // if we're running a light client or server, force enable the v5 peer discovery // unless it is explicitly disabled with --nodiscover note that explicitly specifying @@ -1177,7 +1298,7 @@ } cfg.NetRestrict = list }   - if ctx.GlobalBool(DeveloperFlag.Name) || ctx.GlobalBool(CatalystFlag.Name) { + if ctx.GlobalBool(DeveloperFlag.Name) { // --dev mode can't use p2p networking. cfg.MaxPeers = 0 cfg.ListenAddr = "" @@ -1196,7 +1317,6 @@ setGraphQL(ctx, cfg) setWS(ctx, cfg) setNodeUserIdent(ctx, cfg) setDataDir(ctx, cfg) - setSmartCard(ctx, cfg)   if ctx.GlobalIsSet(ExternalSignerFlag.Name) { cfg.ExternalSigner = ctx.GlobalString(ExternalSignerFlag.Name) @@ -1222,70 +1342,38 @@ cfg.InsecureUnlockAllowed = ctx.GlobalBool(InsecureUnlockAllowedFlag.Name) } }   -func setSmartCard(ctx *cli.Context, cfg *node.Config) { - // Skip enabling smartcards if no path is set - path := ctx.GlobalString(SmartCardDaemonPathFlag.Name) - if path == "" { - return - } - // Sanity check that the smartcard path is valid - fi, err := os.Stat(path) - if err != nil { - log.Info("Smartcard socket not found, disabling", "err", err) - return - } - if fi.Mode()&os.ModeType != os.ModeSocket { - log.Error("Invalid smartcard daemon path", "path", path, "type", fi.Mode().String()) - return - } - // Smartcard daemon path exists and is a socket, enable it - cfg.SmartCardDaemonPath = path -} - func setDataDir(ctx *cli.Context, cfg *node.Config) { switch { case ctx.GlobalIsSet(DataDirFlag.Name): cfg.DataDir = ctx.GlobalString(DataDirFlag.Name) case ctx.GlobalBool(DeveloperFlag.Name): cfg.DataDir = "" // unless explicitly requested, use memory databases - case ctx.GlobalBool(RopstenFlag.Name) && cfg.DataDir == node.DefaultDataDir(): - // Maintain compatibility with older Geth configurations storing the - // Ropsten database in `testnet` instead of `ropsten`. - legacyPath := filepath.Join(node.DefaultDataDir(), "testnet") - if _, err := os.Stat(legacyPath); !os.IsNotExist(err) { - log.Warn("Using the deprecated `testnet` datadir. Future versions will store the Ropsten chain in `ropsten`.") - cfg.DataDir = legacyPath - } else { - cfg.DataDir = filepath.Join(node.DefaultDataDir(), "ropsten") - } - - cfg.DataDir = filepath.Join(node.DefaultDataDir(), "ropsten") - case ctx.GlobalBool(RinkebyFlag.Name) && cfg.DataDir == node.DefaultDataDir(): - cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby") - case ctx.GlobalBool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir(): - cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli") + case ctx.GlobalBool(BaklavaFlag.Name) && cfg.DataDir == node.DefaultDataDir(): + cfg.DataDir = filepath.Join(node.DefaultDataDir(), "baklava") + case ctx.GlobalBool(AlfajoresFlag.Name) && cfg.DataDir == node.DefaultDataDir(): + cfg.DataDir = filepath.Join(node.DefaultDataDir(), "alfajores") } }   -func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) { - // If we are running the light client, apply another group - // settings for gas oracle. - if light { - *cfg = ethconfig.LightClientGPO - } - if ctx.GlobalIsSet(GpoBlocksFlag.Name) { - cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name) - } - if ctx.GlobalIsSet(GpoPercentileFlag.Name) { - cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name) - } - if ctx.GlobalIsSet(GpoMaxGasPriceFlag.Name) { - cfg.MaxPrice = big.NewInt(ctx.GlobalInt64(GpoMaxGasPriceFlag.Name)) - } - if ctx.GlobalIsSet(GpoIgnoreGasPriceFlag.Name) { - cfg.IgnorePrice = big.NewInt(ctx.GlobalInt64(GpoIgnoreGasPriceFlag.Name)) - } -} +// func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) { +// // If we are running the light client, apply another group +// // settings for gas oracle. +// if light { +// *cfg = ethconfig.LightClientGPO +// } +// if ctx.GlobalIsSet(GpoBlocksFlag.Name) { +// cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name) +// } +// if ctx.GlobalIsSet(GpoPercentileFlag.Name) { +// cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name) +// } +// if ctx.GlobalIsSet(GpoMaxGasPriceFlag.Name) { +// cfg.MaxPrice = big.NewInt(ctx.GlobalInt64(GpoMaxGasPriceFlag.Name)) +// } +// if ctx.GlobalIsSet(GpoIgnoreGasPriceFlag.Name) { +// cfg.IgnorePrice = big.NewInt(ctx.GlobalInt64(GpoIgnoreGasPriceFlag.Name)) +// } +// }   func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) { if ctx.GlobalIsSet(TxPoolLocalsFlag.Name) { @@ -1330,55 +1418,13 @@ cfg.Lifetime = ctx.GlobalDuration(TxPoolLifetimeFlag.Name) } }   -func setEthash(ctx *cli.Context, cfg *ethconfig.Config) { - if ctx.GlobalIsSet(EthashCacheDirFlag.Name) { - cfg.Ethash.CacheDir = ctx.GlobalString(EthashCacheDirFlag.Name) - } - if ctx.GlobalIsSet(EthashDatasetDirFlag.Name) { - cfg.Ethash.DatasetDir = ctx.GlobalString(EthashDatasetDirFlag.Name) - } - if ctx.GlobalIsSet(EthashCachesInMemoryFlag.Name) { - cfg.Ethash.CachesInMem = ctx.GlobalInt(EthashCachesInMemoryFlag.Name) - } - if ctx.GlobalIsSet(EthashCachesOnDiskFlag.Name) { - cfg.Ethash.CachesOnDisk = ctx.GlobalInt(EthashCachesOnDiskFlag.Name) - } - if ctx.GlobalIsSet(EthashCachesLockMmapFlag.Name) { - cfg.Ethash.CachesLockMmap = ctx.GlobalBool(EthashCachesLockMmapFlag.Name) - } - if ctx.GlobalIsSet(EthashDatasetsInMemoryFlag.Name) { - cfg.Ethash.DatasetsInMem = ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name) - } - if ctx.GlobalIsSet(EthashDatasetsOnDiskFlag.Name) { - cfg.Ethash.DatasetsOnDisk = ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name) - } - if ctx.GlobalIsSet(EthashDatasetsLockMmapFlag.Name) { - cfg.Ethash.DatasetsLockMmap = ctx.GlobalBool(EthashDatasetsLockMmapFlag.Name) - } -} - func setMiner(ctx *cli.Context, cfg *miner.Config) { - if ctx.GlobalIsSet(MinerNotifyFlag.Name) { - cfg.Notify = strings.Split(ctx.GlobalString(MinerNotifyFlag.Name), ",") + if ctx.GlobalIsSet(LegacyMinerExtraDataFlag.Name) { + cfg.ExtraData = []byte(ctx.GlobalString(LegacyMinerExtraDataFlag.Name)) + log.Warn("The flag --extradata is deprecated and will be removed in the future, please use --miner.extradata") } - cfg.NotifyFull = ctx.GlobalBool(MinerNotifyFullFlag.Name) if ctx.GlobalIsSet(MinerExtraDataFlag.Name) { cfg.ExtraData = []byte(ctx.GlobalString(MinerExtraDataFlag.Name)) - } - if ctx.GlobalIsSet(MinerGasLimitFlag.Name) { - cfg.GasCeil = ctx.GlobalUint64(MinerGasLimitFlag.Name) - } - if ctx.GlobalIsSet(MinerGasPriceFlag.Name) { - cfg.GasPrice = GlobalBig(ctx, MinerGasPriceFlag.Name) - } - if ctx.GlobalIsSet(MinerRecommitIntervalFlag.Name) { - cfg.Recommit = ctx.GlobalDuration(MinerRecommitIntervalFlag.Name) - } - if ctx.GlobalIsSet(MinerNoVerifyFlag.Name) { - cfg.Noverify = ctx.GlobalBool(MinerNoVerifyFlag.Name) - } - if ctx.GlobalIsSet(LegacyMinerGasTargetFlag.Name) { - log.Warn("The generic --miner.gastarget flag is deprecated and will be removed in the future!") } }   @@ -1405,7 +1451,130 @@ cfg.Whitelist[number] = hash } }   -// CheckExclusive verifies that only a single instance of the provided flags was +func setIstanbul(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { + if ctx.GlobalIsSet(LegacyIstanbulRequestTimeoutFlag.Name) { + log.Warn("Flag value is ignored, and obtained from genesis config", "flag", LegacyIstanbulRequestTimeoutFlag.Name) + } + if ctx.GlobalIsSet(LegacyIstanbulBlockPeriodFlag.Name) { + log.Warn("Flag value is ignored, and obtained from genesis config", "flag", LegacyIstanbulBlockPeriodFlag.Name) + } + if ctx.GlobalIsSet(LegacyIstanbulProposerPolicyFlag.Name) { + log.Warn("Flag value is ignored, and obtained from genesis config", "flag", LegacyIstanbulProposerPolicyFlag.Name) + } + cfg.Istanbul.ReplicaStateDBPath = stack.ResolvePath(cfg.Istanbul.ReplicaStateDBPath) + cfg.Istanbul.ValidatorEnodeDBPath = stack.ResolvePath(cfg.Istanbul.ValidatorEnodeDBPath) + cfg.Istanbul.VersionCertificateDBPath = stack.ResolvePath(cfg.Istanbul.VersionCertificateDBPath) + cfg.Istanbul.RoundStateDBPath = stack.ResolvePath(cfg.Istanbul.RoundStateDBPath) + cfg.Istanbul.Validator = ctx.GlobalIsSet(MiningEnabledFlag.Name) || ctx.GlobalIsSet(DeveloperFlag.Name) + cfg.Istanbul.Replica = ctx.GlobalIsSet(IstanbulReplicaFlag.Name) + if ctx.GlobalIsSet(MetricsLoadTestCSVFlag.Name) { + cfg.Istanbul.LoadTestCSVFile = ctx.GlobalString(MetricsLoadTestCSVFlag.Name) + } +} + +func setProxyP2PConfig(ctx *cli.Context, proxyCfg *p2p.Config) { + setNodeKey(ctx, proxyCfg) + setNAT(ctx, proxyCfg) + if ctx.GlobalIsSet(ProxyInternalFacingEndpointFlag.Name) { + proxyCfg.ListenAddr = ctx.GlobalString(ProxyInternalFacingEndpointFlag.Name) + } + + proxyCfg.NetworkId = getNetworkId(ctx) +} + +// Set all of the proxy related configurations. +// These configs span the top level node and istanbul module configuration +func SetProxyConfig(ctx *cli.Context, nodeCfg *node.Config, ethCfg *eth.Config) { + CheckExclusive(ctx, ProxyFlag, ProxiedFlag) + + if ctx.GlobalIsSet(ProxyFlag.Name) { + nodeCfg.Proxy = ctx.GlobalBool(ProxyFlag.Name) + ethCfg.Istanbul.Proxy = ctx.GlobalBool(ProxyFlag.Name) + + // Mining must not be set for proxies + if ctx.GlobalIsSet(MiningEnabledFlag.Name) { + Fatalf("Option --%s must not be used if option --%s is used", MiningEnabledFlag.Name, ProxyFlag.Name) + } + // Replica must not be set for proxies + if ctx.GlobalIsSet(IstanbulReplicaFlag.Name) { + Fatalf("Option --%s must not be used if option --%s is used", IstanbulReplicaFlag.Name, ProxyFlag.Name) + } + + if !ctx.GlobalIsSet(ProxiedValidatorAddressFlag.Name) { + Fatalf("Option --%s must be used if option --%s is used", ProxiedValidatorAddressFlag.Name, ProxyFlag.Name) + } else { + proxiedValidatorAddress := ctx.String(ProxiedValidatorAddressFlag.Name) + if !common.IsHexAddress(proxiedValidatorAddress) { + Fatalf("Invalid address used for option --%s", ProxiedValidatorAddressFlag.Name) + } + ethCfg.Istanbul.ProxiedValidatorAddress = common.HexToAddress(proxiedValidatorAddress) + } + + if !ctx.GlobalIsSet(ProxyInternalFacingEndpointFlag.Name) { + Fatalf("Option --%s must be used if option --%s is used", ProxyInternalFacingEndpointFlag.Name, ProxyFlag.Name) + } else { + setProxyP2PConfig(ctx, &nodeCfg.ProxyP2P) + } + } + + if ctx.GlobalIsSet(ProxiedFlag.Name) { + ethCfg.Istanbul.Proxied = ctx.GlobalBool(ProxiedFlag.Name) + + // Mining must be set for proxied nodes + if !ctx.GlobalIsSet(MiningEnabledFlag.Name) { + Fatalf("Option --%s must be used if option --%s is used", MiningEnabledFlag.Name, ProxiedFlag.Name) + } + + // Extract the proxy enode url pairs, new flag overriding legacy one + var proxyEnodeURLPairs []string + + if ctx.GlobalIsSet(LegacyProxyEnodeURLPairsFlag.Name) { + proxyEnodeURLPairs = strings.Split(ctx.String(LegacyProxyEnodeURLPairsFlag.Name), ",") + log.Warn("The flag --proxy.proxyenodeurlpair is deprecated and will be removed in the future, please use --proxy.proxyenodeurlpairs") + } + + if ctx.GlobalIsSet(ProxyEnodeURLPairsFlag.Name) { + proxyEnodeURLPairs = strings.Split(ctx.String(ProxyEnodeURLPairsFlag.Name), ",") + } + ethCfg.Istanbul.ProxyConfigs = make([]*istanbul.ProxyConfig, len(proxyEnodeURLPairs)) + + for i, proxyEnodeURLPairStr := range proxyEnodeURLPairs { + proxyEnodeURLPair := strings.Split(proxyEnodeURLPairStr, ";") + if len(proxyEnodeURLPair) != 2 { + Fatalf("Invalid format for option --%s", ProxyEnodeURLPairsFlag.Name) + } + + proxyInternalNode, err := enode.ParseV4(proxyEnodeURLPair[0]) + if err != nil { + Fatalf("Proxy internal facing enodeURL (%s) invalid with parse err: %v", proxyEnodeURLPair[0], err) + } + + proxyExternalNode, err := enode.ParseV4(proxyEnodeURLPair[1]) + if err != nil { + Fatalf("Proxy external facing enodeURL (%s) invalid with parse err: %v", proxyEnodeURLPair[1], err) + } + + // Check that external IP is not a private IP address. + if proxyExternalNode.IsPrivateIP() { + if ctx.GlobalBool(ProxyAllowPrivateIPFlag.Name) { + log.Warn("Proxy external facing enodeURL (%s) is private IP.", "proxy external enodeURL", proxyEnodeURLPair[1]) + } else { + Fatalf("Proxy external facing enodeURL (%s) cannot be private IP.", "proxy external enodeURL", proxyEnodeURLPair[1]) + } + } + ethCfg.Istanbul.ProxyConfigs[i] = &istanbul.ProxyConfig{ + InternalNode: proxyInternalNode, + ExternalNode: proxyExternalNode, + } + } + + if !ctx.GlobalBool(NoDiscoverFlag.Name) { + Fatalf("Option --%s must be used if option --%s is used", NoDiscoverFlag.Name, ProxiedFlag.Name) + } + } +} + +// checkExclusive verifies that only a single instance of the provided flags was // set by the user. Each flag might optionally be followed by a string type to // specialize it further. func CheckExclusive(ctx *cli.Context, args ...interface{}) { @@ -1446,10 +1615,25 @@ Fatalf("Flags %v can't be used at the same time", strings.Join(set, ", ")) } }   +func getNetworkId(ctx *cli.Context) uint64 { + if ctx.GlobalIsSet(NetworkIdFlag.Name) { + return ctx.GlobalUint64(NetworkIdFlag.Name) + } + switch { + case ctx.GlobalBool(BaklavaFlag.Name): + return params.BaklavaNetworkId + case ctx.GlobalBool(AlfajoresFlag.Name): + return params.AlfajoresNetworkId + case ctx.GlobalBool(DeveloperFlag.Name): + return 1337 + } + return params.MainnetNetworkId +} + // SetEthConfig applies eth-related command line flags to the config. func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // Avoid conflicting network flags - CheckExclusive(ctx, MainnetFlag, DeveloperFlag, RopstenFlag, RinkebyFlag, GoerliFlag) + CheckExclusive(ctx, MainnetFlag, DeveloperFlag, BaklavaFlag, AlfajoresFlag) CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light") CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer if ctx.GlobalString(GCModeFlag.Name) == "archive" && ctx.GlobalUint64(TxLookupLimitFlag.Name) != 0 { @@ -1463,13 +1647,15 @@ var ks *keystore.KeyStore if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 { ks = keystores[0].(*keystore.KeyStore) } - setEtherbase(ctx, ks, cfg) - setGPO(ctx, &cfg.GPO, ctx.GlobalString(SyncModeFlag.Name) == "light") + setValidator(ctx, ks, cfg) + setTxFeeRecipient(ctx, ks, cfg) + setBLSbase(ctx, ks, cfg) setTxPool(ctx, &cfg.TxPool) - setEthash(ctx, cfg) setMiner(ctx, &cfg.Miner) setWhitelist(ctx, cfg) + setIstanbul(ctx, stack, cfg) setLes(ctx, cfg) + cfg.NetworkId = params.MainnetNetworkId   // Cap the cache allowance and tune the garbage collector mem, err := gopsutil.VirtualMemory() @@ -1539,13 +1725,16 @@ if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheSnapshotFlag.Name) { cfg.SnapshotCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheSnapshotFlag.Name) / 100 } if !ctx.GlobalBool(SnapshotFlag.Name) { - // If snap-sync is requested, this flag is also required - if cfg.SyncMode == downloader.SnapSync { - log.Info("Snap sync requested, enabling --snapshot") - } else { - cfg.TrieCleanCache += cfg.SnapshotCache - cfg.SnapshotCache = 0 // Disabled - } + // Snap Dync Disabled. See https://github.com/ethereum/go-ethereum/issues/1735 + // // If snap-sync is requested, this flag is also required + // if cfg.SyncMode == downloader.SnapSync { + // log.Info("Snap sync requested, enabling --snapshot") + // } else { + // cfg.TrieCleanCache += cfg.SnapshotCache + // cfg.SnapshotCache = 0 // Disabled + // } + cfg.TrieCleanCache += cfg.SnapshotCache + cfg.SnapshotCache = 0 // Disabled } if ctx.GlobalIsSet(DocRootFlag.Name) { cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name) @@ -1555,6 +1744,14 @@ // TODO(fjl): force-enable this in --dev mode cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name) }   + if ctx.GlobalIsSet(RPCGlobalGasInflationRateFlag.Name) { + cfg.RPCGasInflationRate = ctx.GlobalFloat64(RPCGlobalGasInflationRateFlag.Name) + } + if cfg.RPCGasInflationRate < 1 { + Fatalf("The inflation rate shouldn't be less than 1: %f", cfg.RPCGasInflationRate) + } + log.Info("Set global gas inflation rate", "rate", cfg.RPCGasInflationRate) + if ctx.GlobalIsSet(RPCGlobalGasCapFlag.Name) { cfg.RPCGasCap = ctx.GlobalUint64(RPCGlobalGasCapFlag.Name) } @@ -1566,42 +1763,44 @@ } if ctx.GlobalIsSet(RPCGlobalTxFeeCapFlag.Name) { cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCapFlag.Name) } + + cfg.RPCEthCompatibility = true + if ctx.GlobalIsSet(DisableRPCETHCompatibility.Name) { + cfg.RPCEthCompatibility = false + } + + // Disable DNS discovery by default (by using the flag's value even if it hasn't been set and so + // has the default value ""), since we don't have DNS discovery set up for Celo. + // Note that passing --discovery.dns "" is the way the Geth docs specify for disabling DNS discovery, + // so here we just make that be the default. if ctx.GlobalIsSet(NoDiscoverFlag.Name) { cfg.EthDiscoveryURLs, cfg.SnapDiscoveryURLs = []string{}, []string{} - } else if ctx.GlobalIsSet(DNSDiscoveryFlag.Name) { - urls := ctx.GlobalString(DNSDiscoveryFlag.Name) - if urls == "" { - cfg.EthDiscoveryURLs = []string{} - } else { - cfg.EthDiscoveryURLs = SplitAndTrim(urls) - } + } else if urls := ctx.GlobalString(DNSDiscoveryFlag.Name); urls == "" { + cfg.EthDiscoveryURLs = []string{} + } else { + cfg.EthDiscoveryURLs = SplitAndTrim(urls) } // Override any default configs for hard coded networks. switch { case ctx.GlobalBool(MainnetFlag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { - cfg.NetworkId = 1 + cfg.NetworkId = params.MainnetNetworkId } - cfg.Genesis = core.DefaultGenesisBlock() + cfg.Genesis = core.MainnetGenesisBlock() SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) - case ctx.GlobalBool(RopstenFlag.Name): - if !ctx.GlobalIsSet(NetworkIdFlag.Name) { - cfg.NetworkId = 3 - } - cfg.Genesis = core.DefaultRopstenGenesisBlock() - SetDNSDiscoveryDefaults(cfg, params.RopstenGenesisHash) - case ctx.GlobalBool(RinkebyFlag.Name): + case ctx.GlobalBool(BaklavaFlag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { - cfg.NetworkId = 4 + log.Info("Setting baklava id") + cfg.NetworkId = params.BaklavaNetworkId } - cfg.Genesis = core.DefaultRinkebyGenesisBlock() - SetDNSDiscoveryDefaults(cfg, params.RinkebyGenesisHash) - case ctx.GlobalBool(GoerliFlag.Name): + cfg.Genesis = core.DefaultBaklavaGenesisBlock() + SetDNSDiscoveryDefaults(cfg, params.BaklavaGenesisHash) + case ctx.GlobalBool(AlfajoresFlag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { - cfg.NetworkId = 5 + cfg.NetworkId = params.AlfajoresNetworkId } - cfg.Genesis = core.DefaultGoerliGenesisBlock() - SetDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash) + cfg.Genesis = core.DefaultAlfajoresGenesisBlock() + SetDNSDiscoveryDefaults(cfg, params.AlfajoresGenesisHash) case ctx.GlobalBool(DeveloperFlag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { cfg.NetworkId = 1337 @@ -1619,13 +1818,14 @@ // some usages iterate through them as attempts, that doesn't make sense in this setting, // when we're definitely concerned with only one account. passphrase = list[0] } - // setEtherbase has been called above, configuring the miner address from command line flags. - if cfg.Miner.Etherbase != (common.Address{}) { - developer = accounts.Account{Address: cfg.Miner.Etherbase} + // setValidator has been called above, configuring the miner address from command line flags. + if cfg.Miner.Validator != (common.Address{}) { + developer = accounts.Account{Address: cfg.Miner.Validator} } else if accs := ks.Accounts(); len(accs) > 0 { developer = ks.Accounts()[0] } else { - developer, err = ks.NewAccount(passphrase) + key, _ := crypto.HexToECDSA("add67e37fdf5c26743d295b1af6d9b50f2785a6b60bc83a8f05bd1dd4b385c6c") + developer, err = ks.ImportECDSA(key, passphrase) if err != nil { Fatalf("Failed to create developer account: %v", err) } @@ -1633,10 +1833,21 @@ } if err := ks.Unlock(developer, passphrase); err != nil { Fatalf("Failed to unlock developer account: %v", err) } - log.Info("Using developer account", "address", developer.Address) + + // These must be set in order for dev mode to work out of the box with the developer account. + if cfg.Miner.Validator == (common.Address{}) { + log.Info("Setting developer account as validator", "address", developer.Address) + cfg.Miner.Validator = developer.Address + } + if cfg.TxFeeRecipient == (common.Address{}) { + log.Info("Setting developer account as txFeeRecipient", "address", developer.Address) + cfg.TxFeeRecipient = developer.Address + } + + log.Info("Using developer account as validator & txFeeRecipient", "address", developer.Address)   // Create a new developer genesis block or reuse existing one - cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address) + cfg.Genesis = core.DeveloperGenesisBlock(ctx.GlobalUint64(DeveloperPeriodFlag.Name)) if ctx.GlobalIsSet(DataDirFlag.Name) { // Check if we have an already initialized chain and fall back to // that if so. Otherwise we need to generate a new genesis spec. @@ -1646,11 +1857,9 @@ cfg.Genesis = nil // fallback to db content } chaindb.Close() } - if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) { - cfg.Miner.GasPrice = big.NewInt(1) - } + default: - if cfg.NetworkId == 1 { + if cfg.NetworkId == params.MainnetNetworkId { SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) } } @@ -1676,7 +1885,7 @@ // RegisterEthService adds an Ethereum client to the stack. // The second return value is the full node instance, which may be nil if the // node is running as a light client. func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend, *eth.Ethereum) { - if cfg.SyncMode == downloader.LightSync { + if !cfg.SyncMode.SyncFullBlockChain() { backend, err := les.New(stack, cfg) if err != nil { Fatalf("Failed to register the Ethereum service: %v", err) @@ -1801,6 +2010,9 @@ ) if ctx.GlobalString(SyncModeFlag.Name) == "light" { name := "lightchaindata" chainDb, err = stack.OpenDatabase(name, cache, handles, "", readonly) + } else if ctx.GlobalString(SyncModeFlag.Name) == "lightest" { + name := "lightestchaindata" + chainDb, err = stack.OpenDatabaseWithFreezer(name, cache, handles, ctx.GlobalString(AncientFlag.Name), "", readonly) } else { name := "chaindata" chainDb, err = stack.OpenDatabaseWithFreezer(name, cache, handles, ctx.GlobalString(AncientFlag.Name), "", readonly) @@ -1815,13 +2027,11 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis { var genesis *core.Genesis switch { case ctx.GlobalBool(MainnetFlag.Name): - genesis = core.DefaultGenesisBlock() - case ctx.GlobalBool(RopstenFlag.Name): - genesis = core.DefaultRopstenGenesisBlock() - case ctx.GlobalBool(RinkebyFlag.Name): - genesis = core.DefaultRinkebyGenesisBlock() - case ctx.GlobalBool(GoerliFlag.Name): - genesis = core.DefaultGoerliGenesisBlock() + genesis = core.MainnetGenesisBlock() + case ctx.GlobalBool(BaklavaFlag.Name): + genesis = core.DefaultBaklavaGenesisBlock() + case ctx.GlobalBool(AlfajoresFlag.Name): + genesis = core.DefaultAlfajoresGenesisBlock() case ctx.GlobalBool(DeveloperFlag.Name): Fatalf("Developer chains are ephemeral") } @@ -1836,24 +2046,9 @@ config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx)) if err != nil { Fatalf("%v", err) } - var engine consensus.Engine - if config.Clique != nil { - engine = clique.New(config.Clique, chainDb) - } else { - engine = ethash.NewFaker() - if !ctx.GlobalBool(FakePoWFlag.Name) { - engine = ethash.New(ethash.Config{ - CacheDir: stack.ResolvePath(ethconfig.Defaults.Ethash.CacheDir), - CachesInMem: ethconfig.Defaults.Ethash.CachesInMem, - CachesOnDisk: ethconfig.Defaults.Ethash.CachesOnDisk, - CachesLockMmap: ethconfig.Defaults.Ethash.CachesLockMmap, - DatasetDir: stack.ResolvePath(ethconfig.Defaults.Ethash.DatasetDir), - DatasetsInMem: ethconfig.Defaults.Ethash.DatasetsInMem, - DatasetsOnDisk: ethconfig.Defaults.Ethash.DatasetsOnDisk, - DatasetsLockMmap: ethconfig.Defaults.Ethash.DatasetsLockMmap, - }, nil, false) - } - } + config.FullHeaderChainAvailable = ctx.GlobalString(SyncModeFlag.Name) != "lightest" + engine := mockEngine.NewFaker() + if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name) }
diff --git go-ethereum/cmd/utils/customflags.go celo/cmd/utils/customflags.go index a36396d50164bf1f1613a314ee67add705471967..926b58eeb4dcacb4a8c68d330f74099f1248f75d 100644 --- go-ethereum/cmd/utils/customflags.go +++ celo/cmd/utils/customflags.go @@ -45,7 +45,7 @@ return nil }   // Custom cli.Flag type which expand the received string to an absolute path. -// e.g. ~/.ethereum -> /home/username/.ethereum +// e.g. ~/.celo -> /home/username/.celo type DirectoryFlag struct { Name string Value DirectoryString @@ -123,6 +123,15 @@ func (f TextMarshalerFlag) Apply(set *flag.FlagSet) { eachName(f.Name, func(name string) { set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage) }) +} + +// LocalTextMarshaler returns the value of a TextMarshalerFlag from the flag set. +func LocalTextMarshaler(ctx *cli.Context, name string) TextMarshaler { + val := ctx.Generic(name) + if val == nil { + return nil + } + return val.(textMarshalerVal).v }   // GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set.
diff --git go-ethereum/cmd/abigen/main.go celo/cmd/abigen/main.go index bc16bf6a5c3e6c6ba6479fe3fb4000c54aeaaf01..173d7ecc3d6a2a2beb04424bf4979d0a45bbf99d 100644 --- go-ethereum/cmd/abigen/main.go +++ celo/cmd/abigen/main.go @@ -27,6 +27,7 @@ "strings"   "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind_v2" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/crypto" @@ -77,6 +78,10 @@ Name: "vyper", Usage: "Vyper compiler to use if source builds are requested", Value: "vyper", } + truffleFlag = cli.StringFlag{ + Name: "truffle", + Usage: "Path to the json file generated by Truffle", + } excFlag = cli.StringFlag{ Name: "exc", Usage: "Comma separated types to exclude from binding", @@ -98,6 +103,10 @@ aliasFlag = cli.StringFlag{ Name: "alias", Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2", } + version2Flag = cli.BoolFlag{ + Name: "v2", + Usage: "Use version 2 bindings", + } )   func init() { @@ -111,21 +120,28 @@ solFlag, solcFlag, vyFlag, vyperFlag, + truffleFlag, excFlag, pkgFlag, outFlag, langFlag, aliasFlag, + version2Flag, } app.Action = utils.MigrateFlags(abigen) cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate }   func abigen(c *cli.Context) error { - utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag, vyFlag) // Only one source can be selected. + utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag, vyFlag, truffleFlag) // Only one source can be selected. if c.GlobalString(pkgFlag.Name) == "" { utils.Fatalf("No destination package specified (--pkg)") } + + if c.GlobalIsSet(version2Flag.Name) && c.GlobalIsSet(langFlag.Name) { + utils.Fatalf("No --lang when using --v2 flag") + } + var lang bind.Lang switch c.GlobalString(langFlag.Name) { case "go": @@ -147,22 +163,48 @@ sigs []map[string]string libs = make(map[string]string) aliases = make(map[string]string) ) - if c.GlobalString(abiFlag.Name) != "" { + if c.GlobalString(abiFlag.Name) != "" || c.GlobalString(truffleFlag.Name) != "" { // Load up the ABI, optional bytecode and type name from the parameters var ( abi []byte err error ) - input := c.GlobalString(abiFlag.Name) - if input == "-" { - abi, err = ioutil.ReadAll(os.Stdin) + + if c.GlobalString(abiFlag.Name) != "" { + input := c.GlobalString(abiFlag.Name) + if input == "-" { + abi, err = ioutil.ReadAll(os.Stdin) + } else { + abi, err = ioutil.ReadFile(input) + } + if err != nil { + utils.Fatalf("Failed to read input ABI: %v", err) + } + abis = append(abis, string(abi)) } else { - abi, err = ioutil.ReadFile(input) - } - if err != nil { - utils.Fatalf("Failed to read input ABI: %v", err) + // truffle file + input := c.GlobalString(truffleFlag.Name) + var fileData []byte + if input == "-" { + fileData, err = ioutil.ReadAll(os.Stdin) + } else { + fileData, err = ioutil.ReadFile(input) + } + if err != nil { + utils.Fatalf("Failed to read input truffle file: %v", err) + } + + var jsonValue map[string]interface{} + err = json.Unmarshal(fileData, &jsonValue) + if err != nil { + utils.Fatalf("Failed decode json: %v", err) + } + abi, err = json.Marshal(jsonValue["abi"]) + if err != nil { + utils.Fatalf("Can't encode truffle[abi] json: %v", err) + } + abis = append(abis, string(abi)) } - abis = append(abis, string(abi))   var bin []byte if binFile := c.GlobalString(binFlag.Name); binFile != "" { @@ -253,11 +295,21 @@ for _, match := range submatches { aliases[match[1]] = match[2] } } - // Generate the contract binding - code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs, aliases) - if err != nil { - utils.Fatalf("Failed to generate ABI binding: %v", err) + + var code string + var err error + if c.GlobalIsSet(version2Flag.Name) { + code, err = bind_v2.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), bind_v2.LangGo, libs, aliases) + if err != nil { + utils.Fatalf("Failed to generate ABI binding: %v", err) + } + } else { + code, err = bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs, aliases) + if err != nil { + utils.Fatalf("Failed to generate ABI binding: %v", err) + } } + // Either flush it out to a file or display on the standard output if !c.GlobalIsSet(outFlag.Name) { fmt.Printf("%s\n", code)
diff --git go-ethereum/cmd/clef/README.md celo/cmd/clef/README.md deleted file mode 100644 index 27c62817f9252ef4a17d5a0014f95fba424acf45..0000000000000000000000000000000000000000 --- go-ethereum/cmd/clef/README.md +++ /dev/null @@ -1,922 +0,0 @@ -# Clef - -Clef can be used to sign transactions and data and is meant as a(n eventual) replacement for Geth's account management. This allows DApps to not depend on Geth's account management. When a DApp wants to sign data (or a transaction), it can send the content to Clef, which will then provide the user with context and asks for permission to sign the content. If the users grants the signing request, Clef will send the signature back to the DApp. - -This setup allows a DApp to connect to a remote Ethereum node and send transactions that are locally signed. This can help in situations when a DApp is connected to an untrusted remote Ethereum node, because a local one is not available, not synchronised with the chain, or is a node that has no built-in (or limited) account management. - -Clef can run as a daemon on the same machine, off a usb-stick like [USB armory](https://inversepath.com/usbarmory), or even a separate VM in a [QubesOS](https://www.qubes-os.org/) type setup. - -Check out the - -* [CLI tutorial](tutorial.md) for some concrete examples on how Clef works. -* [Setup docs](docs/setup.md) for information on how to configure Clef on QubesOS or USB Armory. -* [Data types](datatypes.md) for details on the communication messages between Clef and an external UI. - -## Command line flags - -Clef accepts the following command line options: - -``` -COMMANDS: - init Initialize the signer, generate secret storage - attest Attest that a js-file is to be used - setpw Store a credential for a keystore file - delpw Remove a credential for a keystore file - gendoc Generate documentation about json-rpc format - help Shows a list of commands or help for one command - -GLOBAL OPTIONS: - --loglevel value log level to emit to the screen (default: 4) - --keystore value Directory for the keystore (default: "$HOME/.ethereum/keystore") - --configdir value Directory for Clef configuration (default: "$HOME/.clef") - --chainid value Chain id to use for signing (1=mainnet, 3=Ropsten, 4=Rinkeby, 5=Goerli) (default: 1) - --lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength - --nousb Disables monitoring for and managing USB hardware wallets - --pcscdpath value Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm") - --http.addr value HTTP-RPC server listening interface (default: "localhost") - --http.vhosts value Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost") - --ipcdisable Disable the IPC-RPC server - --ipcpath Filename for IPC socket/pipe within the datadir (explicit paths escape it) - --http Enable the HTTP-RPC server - --http.port value HTTP-RPC server listening port (default: 8550) - --signersecret value A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash - --4bytedb-custom value File used for writing new 4byte-identifiers submitted via API (default: "./4byte-custom.json") - --auditlog value File used to emit audit logs. Set to "" to disable (default: "audit.log") - --rules value Path to the rule file to auto-authorize requests with - --stdio-ui Use STDIN/STDOUT as a channel for an external UI. This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user interface, and can be used when Clef is started by an external process. - --stdio-ui-test Mechanism to test interface between Clef and UI. Requires 'stdio-ui'. - --advanced If enabled, issues warnings instead of rejections for suspicious requests. Default off - --suppress-bootwarn If set, does not show the warning during boot - --help, -h show help - --version, -v print the version -``` - -Example: - -``` -$ clef -keystore /my/keystore -chainid 4 -``` - -## Security model - -The security model of Clef is as follows: - -* One critical component (the Clef binary / daemon) is responsible for handling cryptographic operations: signing, private keys, encryption/decryption of keystore files. -* Clef has a well-defined 'external' API. -* The 'external' API is considered UNTRUSTED. -* Clef also communicates with whatever process that invoked the binary, via stdin/stdout. - * This channel is considered 'trusted'. Over this channel, approvals and passwords are communicated. - -The general flow for signing a transaction using e.g. Geth is as follows: -![image](sign_flow.png) - -In this case, `geth` would be started with `--signer http://localhost:8550` and would relay requests to `eth.sendTransaction`. - -## TODOs - -Some snags and todos - -* [ ] Clef should take a startup param "--no-change", for UIs that do not contain the capability to perform changes to things, only approve/deny. Such a UI should be able to start the signer in a more secure mode by telling it that it only wants approve/deny capabilities. -* [x] It would be nice if Clef could collect new 4byte-id:s/method selectors, and have a secondary database for those (`4byte_custom.json`). Users could then (optionally) submit their collections for inclusion upstream. -* [ ] It should be possible to configure Clef to check if an account is indeed known to it, before passing on to the UI. The reason it currently does not, is that it would make it possible to enumerate accounts if it immediately returned "unknown account" (side channel attack). -* [x] It should be possible to configure Clef to auto-allow listing (certain) accounts, instead of asking every time. -* [x] Done Upon startup, Clef should spit out some info to the caller (particularly important when executed in `stdio-ui`-mode), invoking methods with the following info: - * [x] Version info about the signer - * [x] Address of API (HTTP/IPC) - * [ ] List of known accounts -* [ ] Have a default timeout on signing operations, so that if the user has not answered within e.g. 60 seconds, the request is rejected. -* [ ] `account_signRawTransaction` -* [ ] `account_bulkSignTransactions([] transactions)` should - * only exist if enabled via config/flag - * only allow non-data-sending transactions - * all txs must use the same `from`-account - * let the user confirm, showing - * the total amount - * the number of unique recipients - -* Geth todos - - The signer should pass the `Origin` header as call-info to the UI. As of right now, the way that info about the request is put together is a bit of a hack into the HTTP server. This could probably be greatly improved. - - Relay: Geth should be started in `geth --signer localhost:8550`. - - Currently, the Geth APIs use `common.Address` in the arguments to transaction submission (e.g `to` field). This type is 20 `bytes`, and is incapable of carrying checksum information. The signer uses `common.MixedcaseAddress`, which retains the original input. - - The Geth API should switch to use the same type, and relay `to`-account verbatim to the external API. -* [x] Storage - * [x] An encrypted key-value storage should be implemented. - * See [rules.md](rules.md) for more info about this. -* Another potential thing to introduce is pairing. - * To prevent spurious requests which users just accept, implement a way to "pair" the caller with the signer (external API). - * Thus Geth/cpp would cryptographically handshake and afterwards the caller would be allowed to make signing requests. - * This feature would make the addition of rules less dangerous. - -* Wallets / accounts. Add API methods for wallets. - -## Communication - -### External API - -Clef listens to HTTP requests on `http.addr`:`http.port` (or to IPC on `ipcpath`), with the same JSON-RPC standard as Geth. The messages are expected to be [JSON-RPC 2.0 standard](https://www.jsonrpc.org/specification). - -Some of these calls can require user interaction. Clients must be aware that responses may be delayed significantly or may never be received if a user decides to ignore the confirmation request. - -The External API is **untrusted**: it does not accept credentials, nor does it expect that requests have any authority. - -### Internal UI API - -Clef has one native console-based UI, for operation without any standalone tools. However, there is also an API to communicate with an external UI. To enable that UI, the signer needs to be executed with the `--stdio-ui` option, which allocates `stdin` / `stdout` for the UI API. - -An example (insecure) proof-of-concept of has been implemented in `pythonsigner.py`. - -The model is as follows: - -* The user starts the UI app (`pythonsigner.py`). -* The UI app starts `clef` with `--stdio-ui`, and listens to the -process output for confirmation-requests. -* `clef` opens the external HTTP API. -* When the `signer` receives requests, it sends a JSON-RPC request via `stdout`. -* The UI app prompts the user accordingly, and responds to `clef`. -* `clef` signs (or not), and responds to the original request. - -## External API - -See the [external API changelog](extapi_changelog.md) for information about changes to this API. - -### Encoding -- number: positive integers that are hex encoded -- data: hex encoded data -- string: ASCII string - -All hex encoded values must be prefixed with `0x`. - -### account_new - -#### Create new password protected account - -The signer will generate a new private key, encrypt it according to [web3 keystore spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) and store it in the keystore directory. -The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving lost accounts. - -#### Arguments - -None - -#### Result - - address [string]: account address that is derived from the generated key - -#### Sample call -```json -{ - "id": 0, - "jsonrpc": "2.0", - "method": "account_new", - "params": [] -} -``` -Response -```json -{ - "id": 0, - "jsonrpc": "2.0", - "result": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133" -} -``` - -### account_list - -#### List available accounts - List all accounts that this signer currently manages - -#### Arguments - -None - -#### Result - - array with account records: - - account.address [string]: account address that is derived from the generated key - -#### Sample call -```json -{ - "id": 1, - "jsonrpc": "2.0", - "method": "account_list" -} -``` -Response -```json -{ - "id": 1, - "jsonrpc": "2.0", - "result": [ - "0xafb2f771f58513609765698f65d3f2f0224a956f", - "0xbea9183f8f4f03d427f6bcea17388bdff1cab133" - ] -} -``` - -### account_signTransaction - -#### Sign transactions - Signs a transaction and responds with the signed transaction in RLP-encoded and JSON forms. - -#### Arguments - 1. transaction object: - - `from` [address]: account to send the transaction from - - `to` [address]: receiver account. If omitted or `0x`, will cause contract creation. - - `gas` [number]: maximum amount of gas to burn - - `gasPrice` [number]: gas price - - `value` [number:optional]: amount of Wei to send with the transaction - - `data` [data:optional]: input data - - `nonce` [number]: account nonce - 1. method signature [string:optional] - - The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected. - - -#### Result - - raw [data]: signed transaction in RLP encoded form - - tx [json]: signed transaction in JSON form - -#### Sample call -```json -{ - "id": 2, - "jsonrpc": "2.0", - "method": "account_signTransaction", - "params": [ - { - "from": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", - "gas": "0x55555", - "gasPrice": "0x1234", - "input": "0xabcd", - "nonce": "0x0", - "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "value": "0x1234" - } - ] -} -``` -Response - -```json -{ - "jsonrpc": "2.0", - "id": 2, - "result": { - "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", - "tx": { - "nonce": "0x0", - "gasPrice": "0x1234", - "gas": "0x55555", - "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "value": "0x1234", - "input": "0xabcd", - "v": "0x26", - "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e", - "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", - "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e" - } - } -} -``` -#### Sample call with ABI-data - - -```json -{ - "id": 67, - "jsonrpc": "2.0", - "method": "account_signTransaction", - "params": [ - { - "from": "0x694267f14675d7e1b9494fd8d72fefe1755710fa", - "gas": "0x333", - "gasPrice": "0x1", - "nonce": "0x0", - "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "value": "0x0", - "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012" - }, - "safeSend(address)" - ] -} -``` -Response - -```json -{ - "jsonrpc": "2.0", - "id": 67, - "result": { - "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", - "tx": { - "nonce": "0x0", - "gasPrice": "0x1", - "gas": "0x333", - "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "value": "0x0", - "input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", - "v": "0x26", - "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e", - "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", - "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e" - } - } -} -``` - -Bash example: -```bash -> curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/ - -{"jsonrpc":"2.0","id":67,"result":{"raw":"0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","tx":{"nonce":"0x0","gasPrice":"0x1","gas":"0x333","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0","value":"0x0","input":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012","v":"0x26","r":"0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e","s":"0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","hash":"0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"}}} -``` - -### account_signData - -#### Sign data - Signs a chunk of data and returns the calculated signature. - -#### Arguments - - content type [string]: type of signed data - - `text/validator`: hex data with custom validator defined in a contract - - `application/clique`: [clique](https://github.com/ethereum/EIPs/issues/225) headers - - `text/plain`: simple hex data validated by `account_ecRecover` - - account [address]: account to sign with - - data [object]: data to sign - -#### Result - - calculated signature [data] - -#### Sample call -```json -{ - "id": 3, - "jsonrpc": "2.0", - "method": "account_signData", - "params": [ - "data/plain", - "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", - "0xaabbccdd" - ] -} -``` -Response - -```json -{ - "id": 3, - "jsonrpc": "2.0", - "result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c" -} -``` - -### account_signTypedData - -#### Sign data - Signs a chunk of structured data conformant to [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) and returns the calculated signature. - -#### Arguments - - account [address]: account to sign with - - data [object]: data to sign - -#### Result - - calculated signature [data] - -#### Sample call -```json -{ - "id": 68, - "jsonrpc": "2.0", - "method": "account_signTypedData", - "params": [ - "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", - { - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "string" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": 1, - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } - } - ] -} -``` -Response - -```json -{ - "id": 1, - "jsonrpc": "2.0", - "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c" -} -``` - -### account_ecRecover - -#### Recover the signing address - -Derive the address from the account that was used to sign data with content type `text/plain` and the signature. - -#### Arguments - - data [data]: data that was signed - - signature [data]: the signature to verify - -#### Result - - derived account [address] - -#### Sample call -```json -{ - "id": 4, - "jsonrpc": "2.0", - "method": "account_ecRecover", - "params": [ - "0xaabbccdd", - "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c" - ] -} -``` -Response - -```json -{ - "id": 4, - "jsonrpc": "2.0", - "result": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db" -} -``` - -### account_version - -#### Get external API version - -Get the version of the external API used by Clef. - -#### Arguments - -None - -#### Result - -* external API version [string] - -#### Sample call -```json -{ - "id": 0, - "jsonrpc": "2.0", - "method": "account_version", - "params": [] -} -``` - -Response -```json -{ - "id": 0, - "jsonrpc": "2.0", - "result": "6.0.0" -} -``` - -## UI API - -These methods needs to be implemented by a UI listener. - -By starting the signer with the switch `--stdio-ui-test`, the signer will invoke all known methods, and expect the UI to respond with -denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented. -See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'. - -All methods in this API use object-based parameters, so that there can be no mixup of parameters: each piece of data is accessed by key. - -See the [ui API changelog](intapi_changelog.md) for information about changes to this API. - -OBS! A slight deviation from `json` standard is in place: every request and response should be confined to a single line. -Whereas the `json` specification allows for linebreaks, linebreaks __should not__ be used in this communication channel, to make -things simpler for both parties. - -### ApproveTx / `ui_approveTx` - -Invoked when there's a transaction for approval. - - -#### Sample call - -Here's a method invocation: -```bash - -curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/ -``` -Results in the following invocation on the UI: -```json - -{ - "jsonrpc": "2.0", - "id": 1, - "method": "ui_approveTx", - "params": [ - { - "transaction": { - "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", - "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "gas": "0x333", - "gasPrice": "0x1", - "value": "0x0", - "nonce": "0x0", - "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", - "input": null - }, - "call_info": [ - { - "type": "WARNING", - "message": "Invalid checksum on to-address" - }, - { - "type": "Info", - "message": "safeSend(address: 0x0000000000000000000000000000000000000012)" - } - ], - "meta": { - "remote": "127.0.0.1:48486", - "local": "localhost:8550", - "scheme": "HTTP/1.1" - } - } - ] -} - -``` - -The same method invocation, but with invalid data: -```bash - -curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000002000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/ -``` - -```json - -{ - "jsonrpc": "2.0", - "id": 1, - "method": "ui_approveTx", - "params": [ - { - "transaction": { - "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", - "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "gas": "0x333", - "gasPrice": "0x1", - "value": "0x0", - "nonce": "0x0", - "data": "0x4401a6e40000000000000002000000000000000000000000000000000000000000000012", - "input": null - }, - "call_info": [ - { - "type": "WARNING", - "message": "Invalid checksum on to-address" - }, - { - "type": "WARNING", - "message": "Transaction data did not match ABI-interface: WARNING: Supplied data is stuffed with extra data. \nWant 0000000000000002000000000000000000000000000000000000000000000012\nHave 0000000000000000000000000000000000000000000000000000000000000012\nfor method safeSend(address)" - } - ], - "meta": { - "remote": "127.0.0.1:48492", - "local": "localhost:8550", - "scheme": "HTTP/1.1" - } - } - ] -} - - -``` - -One which has missing `to`, but with no `data`: - - -```json - -{ - "jsonrpc": "2.0", - "id": 3, - "method": "ui_approveTx", - "params": [ - { - "transaction": { - "from": "", - "to": null, - "gas": "0x0", - "gasPrice": "0x0", - "value": "0x0", - "nonce": "0x0", - "data": null, - "input": null - }, - "call_info": [ - { - "type": "CRITICAL", - "message": "Tx will create contract with empty code!" - } - ], - "meta": { - "remote": "signer binary", - "local": "main", - "scheme": "in-proc" - } - } - ] -} -``` - -### ApproveListing / `ui_approveListing` - -Invoked when a request for account listing has been made. - -#### Sample call - -```json - -{ - "jsonrpc": "2.0", - "id": 5, - "method": "ui_approveListing", - "params": [ - { - "accounts": [ - { - "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42", - "address": "0x123409812340981234098123409812deadbeef42" - }, - { - "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42", - "address": "0xcafebabedeadbeef34098123409812deadbeef42" - } - ], - "meta": { - "remote": "signer binary", - "local": "main", - "scheme": "in-proc" - } - } - ] -} - -``` - - -### ApproveSignData / `ui_approveSignData` - -#### Sample call - -```json -{ - "jsonrpc": "2.0", - "id": 4, - "method": "ui_approveSignData", - "params": [ - { - "address": "0x123409812340981234098123409812deadbeef42", - "raw_data": "0x01020304", - "messages": [ - { - "name": "message", - "value": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004", - "type": "text/plain" - } - ], - "hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310", - "meta": { - "remote": "signer binary", - "local": "main", - "scheme": "in-proc" - } - } - ] -} -``` - -### ApproveNewAccount / `ui_approveNewAccount` - -Invoked when a request for creating a new account has been made. - -#### Sample call - -```json -{ - "jsonrpc": "2.0", - "id": 4, - "method": "ui_approveNewAccount", - "params": [ - { - "meta": { - "remote": "signer binary", - "local": "main", - "scheme": "in-proc" - } - } - ] -} -``` - -### ShowInfo / `ui_showInfo` - -The UI should show the info (a single message) to the user. Does not expect response. - -#### Sample call - -```json -{ - "jsonrpc": "2.0", - "id": 9, - "method": "ui_showInfo", - "params": [ - "Tests completed" - ] -} - -``` - -### ShowError / `ui_showError` - -The UI should show the error (a single message) to the user. Does not expect response. - -```json - -{ - "jsonrpc": "2.0", - "id": 2, - "method": "ui_showError", - "params": [ - "Something bad happened!" - ] -} - -``` - -### OnApprovedTx / `ui_onApprovedTx` - -`OnApprovedTx` is called when a transaction has been approved and signed. The call contains the return value that will be sent to the external caller. The return value from this method is ignored - the reason for having this callback is to allow the ruleset to keep track of approved transactions. - -When implementing rate-limited rules, this callback should be used. - -TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`. - -Example call: -```json - -{ - "jsonrpc": "2.0", - "id": 1, - "method": "ui_onApprovedTx", - "params": [ - { - "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", - "tx": { - "nonce": "0x0", - "gasPrice": "0x1", - "gas": "0x333", - "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "value": "0x0", - "input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", - "v": "0x26", - "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e", - "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", - "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e" - } - } - ] -} -``` - -### OnSignerStartup / `ui_onSignerStartup` - -This method provides the UI with information about what API version the signer uses (both internal and external) as well as build-info and external API, -in k/v-form. - -Example call: -```json - -{ - "jsonrpc": "2.0", - "id": 1, - "method": "ui_onSignerStartup", - "params": [ - { - "info": { - "extapi_http": "http://localhost:8550", - "extapi_ipc": null, - "extapi_version": "2.0.0", - "intapi_version": "1.2.0" - } - } - ] -} - -``` - -### OnInputRequired / `ui_onInputRequired` - -Invoked when Clef requires user input (e.g. a password). - -Example call: -```json - -{ - "jsonrpc": "2.0", - "id": 1, - "method": "ui_onInputRequired", - "params": [ - { - "title": "Account password", - "prompt": "Please enter the password for account 0x694267f14675d7e1b9494fd8d72fefe1755710fa", - "isPassword": true - } - ] -} -``` - - -### Rules for UI apis - -A UI should conform to the following rules. - -* A UI MUST NOT load any external resources that were not embedded/part of the UI package. - * For example, not load icons, stylesheets from the internet - * Not load files from the filesystem, unless they reside in the same local directory (e.g. config files) -* A Graphical UI MUST show the blocky-identicon for ethereum addresses. -* A UI MUST warn display appropriate warning if the destination-account is formatted with invalid checksum. -* A UI MUST NOT open any ports or services - * The signer opens the public port -* A UI SHOULD verify the permissions on the signer binary, and refuse to execute or warn if permissions allow non-user write. -* A UI SHOULD inform the user about the `SHA256` or `MD5` hash of the binary being executed -* A UI SHOULD NOT maintain a secondary storage of data, e.g. list of accounts - * The signer provides accounts -* A UI SHOULD, to the best extent possible, use static linking / bundling, so that required libraries are bundled -along with the UI. - - -### UI Implementations - -There are a couple of implementation for a UI. We'll try to keep this list up to date. - -| Name | Repo | UI type| No external resources| Blocky support| Verifies permissions | Hash information | No secondary storage | Statically linked| Can modify parameters| -| ---- | ---- | -------| ---- | ---- | ---- |---- | ---- | ---- | ---- | -| QtSigner| https://github.com/holiman/qtsigner/| Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: | :+1: (partially)| -| GtkSigner| https://github.com/holiman/gtksigner| Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: | :x: | -| Frame | https://github.com/floating/frame/commits/go-signer| Electron-based| :x:| :x:| :x:| :x:| ?| :x: | :x: | -| Clef UI| https://github.com/ethereum/clef-ui| Golang/QT-based| :+1:| :+1:| :x:| :+1:| :+1:| :x: | :+1: (approve tx only)|
diff --git go-ethereum/cmd/clef/main.go celo/cmd/clef/main.go deleted file mode 100644 index 243080825de7cc501cbcaa817bd4a6afd83878e7..0000000000000000000000000000000000000000 --- go-ethereum/cmd/clef/main.go +++ /dev/null @@ -1,1150 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bufio" - "context" - "crypto/rand" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "math/big" - "os" - "os/signal" - "path/filepath" - "runtime" - "sort" - "strings" - "time" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/internal/flags" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/signer/core" - "github.com/ethereum/go-ethereum/signer/core/apitypes" - "github.com/ethereum/go-ethereum/signer/fourbyte" - "github.com/ethereum/go-ethereum/signer/rules" - "github.com/ethereum/go-ethereum/signer/storage" - "github.com/mattn/go-colorable" - "github.com/mattn/go-isatty" - "gopkg.in/urfave/cli.v1" -) - -const legalWarning = ` -WARNING! - -Clef is an account management tool. It may, like any software, contain bugs. - -Please take care to -- backup your keystore files, -- verify that the keystore(s) can be opened with your password. - -Clef is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. -` - -var ( - logLevelFlag = cli.IntFlag{ - Name: "loglevel", - Value: 4, - Usage: "log level to emit to the screen", - } - advancedMode = cli.BoolFlag{ - Name: "advanced", - Usage: "If enabled, issues warnings instead of rejections for suspicious requests. Default off", - } - acceptFlag = cli.BoolFlag{ - Name: "suppress-bootwarn", - Usage: "If set, does not show the warning during boot", - } - keystoreFlag = cli.StringFlag{ - Name: "keystore", - Value: filepath.Join(node.DefaultDataDir(), "keystore"), - Usage: "Directory for the keystore", - } - configdirFlag = cli.StringFlag{ - Name: "configdir", - Value: DefaultConfigDir(), - Usage: "Directory for Clef configuration", - } - chainIdFlag = cli.Int64Flag{ - Name: "chainid", - Value: params.MainnetChainConfig.ChainID.Int64(), - Usage: "Chain id to use for signing (1=mainnet, 3=Ropsten, 4=Rinkeby, 5=Goerli)", - } - rpcPortFlag = cli.IntFlag{ - Name: "http.port", - Usage: "HTTP-RPC server listening port", - Value: node.DefaultHTTPPort + 5, - } - signerSecretFlag = cli.StringFlag{ - Name: "signersecret", - Usage: "A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash", - } - customDBFlag = cli.StringFlag{ - Name: "4bytedb-custom", - Usage: "File used for writing new 4byte-identifiers submitted via API", - Value: "./4byte-custom.json", - } - auditLogFlag = cli.StringFlag{ - Name: "auditlog", - Usage: "File used to emit audit logs. Set to \"\" to disable", - Value: "audit.log", - } - ruleFlag = cli.StringFlag{ - Name: "rules", - Usage: "Path to the rule file to auto-authorize requests with", - } - stdiouiFlag = cli.BoolFlag{ - Name: "stdio-ui", - Usage: "Use STDIN/STDOUT as a channel for an external UI. " + - "This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user " + - "interface, and can be used when Clef is started by an external process.", - } - testFlag = cli.BoolFlag{ - Name: "stdio-ui-test", - Usage: "Mechanism to test interface between Clef and UI. Requires 'stdio-ui'.", - } - app = cli.NewApp() - initCommand = cli.Command{ - Action: utils.MigrateFlags(initializeSecrets), - Name: "init", - Usage: "Initialize the signer, generate secret storage", - ArgsUsage: "", - Flags: []cli.Flag{ - logLevelFlag, - configdirFlag, - }, - Description: ` -The init command generates a master seed which Clef can use to store credentials and data needed for -the rule-engine to work.`, - } - attestCommand = cli.Command{ - Action: utils.MigrateFlags(attestFile), - Name: "attest", - Usage: "Attest that a js-file is to be used", - ArgsUsage: "<sha256sum>", - Flags: []cli.Flag{ - logLevelFlag, - configdirFlag, - signerSecretFlag, - }, - Description: ` -The attest command stores the sha256 of the rule.js-file that you want to use for automatic processing of -incoming requests. - -Whenever you make an edit to the rule file, you need to use attestation to tell -Clef that the file is 'safe' to execute.`, - } - setCredentialCommand = cli.Command{ - Action: utils.MigrateFlags(setCredential), - Name: "setpw", - Usage: "Store a credential for a keystore file", - ArgsUsage: "<address>", - Flags: []cli.Flag{ - logLevelFlag, - configdirFlag, - signerSecretFlag, - }, - Description: ` -The setpw command stores a password for a given address (keyfile). -`} - delCredentialCommand = cli.Command{ - Action: utils.MigrateFlags(removeCredential), - Name: "delpw", - Usage: "Remove a credential for a keystore file", - ArgsUsage: "<address>", - Flags: []cli.Flag{ - logLevelFlag, - configdirFlag, - signerSecretFlag, - }, - Description: ` -The delpw command removes a password for a given address (keyfile). -`} - newAccountCommand = cli.Command{ - Action: utils.MigrateFlags(newAccount), - Name: "newaccount", - Usage: "Create a new account", - ArgsUsage: "", - Flags: []cli.Flag{ - logLevelFlag, - keystoreFlag, - utils.LightKDFFlag, - acceptFlag, - }, - Description: ` -The newaccount command creates a new keystore-backed account. It is a convenience-method -which can be used in lieu of an external UI.`, - } - - gendocCommand = cli.Command{ - Action: GenDoc, - Name: "gendoc", - Usage: "Generate documentation about json-rpc format", - Description: ` -The gendoc generates example structures of the json-rpc communication types. -`} -) - -// AppHelpFlagGroups is the application flags, grouped by functionality. -var AppHelpFlagGroups = []flags.FlagGroup{ - { - Name: "FLAGS", - Flags: []cli.Flag{ - logLevelFlag, - keystoreFlag, - configdirFlag, - chainIdFlag, - utils.LightKDFFlag, - utils.NoUSBFlag, - utils.SmartCardDaemonPathFlag, - utils.HTTPListenAddrFlag, - utils.HTTPVirtualHostsFlag, - utils.IPCDisabledFlag, - utils.IPCPathFlag, - utils.HTTPEnabledFlag, - rpcPortFlag, - signerSecretFlag, - customDBFlag, - auditLogFlag, - ruleFlag, - stdiouiFlag, - testFlag, - advancedMode, - acceptFlag, - }, - }, -} - -func init() { - app.Name = "Clef" - app.Usage = "Manage Ethereum account operations" - app.Flags = []cli.Flag{ - logLevelFlag, - keystoreFlag, - configdirFlag, - chainIdFlag, - utils.LightKDFFlag, - utils.NoUSBFlag, - utils.SmartCardDaemonPathFlag, - utils.HTTPListenAddrFlag, - utils.HTTPVirtualHostsFlag, - utils.IPCDisabledFlag, - utils.IPCPathFlag, - utils.HTTPEnabledFlag, - rpcPortFlag, - signerSecretFlag, - customDBFlag, - auditLogFlag, - ruleFlag, - stdiouiFlag, - testFlag, - advancedMode, - acceptFlag, - } - app.Action = signer - app.Commands = []cli.Command{initCommand, - attestCommand, - setCredentialCommand, - delCredentialCommand, - newAccountCommand, - gendocCommand} - cli.CommandHelpTemplate = flags.CommandHelpTemplate - // Override the default app help template - cli.AppHelpTemplate = flags.ClefAppHelpTemplate - - // Override the default app help printer, but only for the global app help - originalHelpPrinter := cli.HelpPrinter - cli.HelpPrinter = func(w io.Writer, tmpl string, data interface{}) { - if tmpl == flags.ClefAppHelpTemplate { - // Render out custom usage screen - originalHelpPrinter(w, tmpl, flags.HelpData{App: data, FlagGroups: AppHelpFlagGroups}) - } else if tmpl == flags.CommandHelpTemplate { - // Iterate over all command specific flags and categorize them - categorized := make(map[string][]cli.Flag) - for _, flag := range data.(cli.Command).Flags { - if _, ok := categorized[flag.String()]; !ok { - categorized[flags.FlagCategory(flag, AppHelpFlagGroups)] = append(categorized[flags.FlagCategory(flag, AppHelpFlagGroups)], flag) - } - } - - // sort to get a stable ordering - sorted := make([]flags.FlagGroup, 0, len(categorized)) - for cat, flgs := range categorized { - sorted = append(sorted, flags.FlagGroup{Name: cat, Flags: flgs}) - } - sort.Sort(flags.ByCategory(sorted)) - - // add sorted array to data and render with default printer - originalHelpPrinter(w, tmpl, map[string]interface{}{ - "cmd": data, - "categorizedFlags": sorted, - }) - } else { - originalHelpPrinter(w, tmpl, data) - } - } -} - -func main() { - if err := app.Run(os.Args); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } -} - -func initializeSecrets(c *cli.Context) error { - // Get past the legal message - if err := initialize(c); err != nil { - return err - } - // Ensure the master key does not yet exist, we're not willing to overwrite - configDir := c.GlobalString(configdirFlag.Name) - if err := os.Mkdir(configDir, 0700); err != nil && !os.IsExist(err) { - return err - } - location := filepath.Join(configDir, "masterseed.json") - if _, err := os.Stat(location); err == nil { - return fmt.Errorf("master key %v already exists, will not overwrite", location) - } - // Key file does not exist yet, generate a new one and encrypt it - masterSeed := make([]byte, 256) - num, err := io.ReadFull(rand.Reader, masterSeed) - if err != nil { - return err - } - if num != len(masterSeed) { - return fmt.Errorf("failed to read enough random") - } - n, p := keystore.StandardScryptN, keystore.StandardScryptP - if c.GlobalBool(utils.LightKDFFlag.Name) { - n, p = keystore.LightScryptN, keystore.LightScryptP - } - text := "The master seed of clef will be locked with a password.\nPlease specify a password. Do not forget this password!" - var password string - for { - password = utils.GetPassPhrase(text, true) - if err := core.ValidatePasswordFormat(password); err != nil { - fmt.Printf("invalid password: %v\n", err) - } else { - fmt.Println() - break - } - } - cipherSeed, err := encryptSeed(masterSeed, []byte(password), n, p) - if err != nil { - return fmt.Errorf("failed to encrypt master seed: %v", err) - } - // Double check the master key path to ensure nothing wrote there in between - if err = os.Mkdir(configDir, 0700); err != nil && !os.IsExist(err) { - return err - } - if _, err := os.Stat(location); err == nil { - return fmt.Errorf("master key %v already exists, will not overwrite", location) - } - // Write the file and print the usual warning message - if err = ioutil.WriteFile(location, cipherSeed, 0400); err != nil { - return err - } - fmt.Printf("A master seed has been generated into %s\n", location) - fmt.Printf(` -This is required to be able to store credentials, such as: -* Passwords for keystores (used by rule engine) -* Storage for JavaScript auto-signing rules -* Hash of JavaScript rule-file - -You should treat 'masterseed.json' with utmost secrecy and make a backup of it! -* The password is necessary but not enough, you need to back up the master seed too! -* The master seed does not contain your accounts, those need to be backed up separately! - -`) - return nil -} -func attestFile(ctx *cli.Context) error { - if len(ctx.Args()) < 1 { - utils.Fatalf("This command requires an argument.") - } - if err := initialize(ctx); err != nil { - return err - } - - stretchedKey, err := readMasterKey(ctx, nil) - if err != nil { - utils.Fatalf(err.Error()) - } - configDir := ctx.GlobalString(configdirFlag.Name) - vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) - confKey := crypto.Keccak256([]byte("config"), stretchedKey) - - // Initialize the encrypted storages - configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confKey) - val := ctx.Args().First() - configStorage.Put("ruleset_sha256", val) - log.Info("Ruleset attestation updated", "sha256", val) - return nil -} - -func setCredential(ctx *cli.Context) error { - if len(ctx.Args()) < 1 { - utils.Fatalf("This command requires an address to be passed as an argument") - } - if err := initialize(ctx); err != nil { - return err - } - addr := ctx.Args().First() - if !common.IsHexAddress(addr) { - utils.Fatalf("Invalid address specified: %s", addr) - } - address := common.HexToAddress(addr) - password := utils.GetPassPhrase("Please enter a password to store for this address:", true) - fmt.Println() - - stretchedKey, err := readMasterKey(ctx, nil) - if err != nil { - utils.Fatalf(err.Error()) - } - configDir := ctx.GlobalString(configdirFlag.Name) - vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) - pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) - - pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey) - pwStorage.Put(address.Hex(), password) - - log.Info("Credential store updated", "set", address) - return nil -} - -func removeCredential(ctx *cli.Context) error { - if len(ctx.Args()) < 1 { - utils.Fatalf("This command requires an address to be passed as an argument") - } - if err := initialize(ctx); err != nil { - return err - } - addr := ctx.Args().First() - if !common.IsHexAddress(addr) { - utils.Fatalf("Invalid address specified: %s", addr) - } - address := common.HexToAddress(addr) - - stretchedKey, err := readMasterKey(ctx, nil) - if err != nil { - utils.Fatalf(err.Error()) - } - configDir := ctx.GlobalString(configdirFlag.Name) - vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) - pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) - - pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey) - pwStorage.Del(address.Hex()) - - log.Info("Credential store updated", "unset", address) - return nil -} - -func newAccount(c *cli.Context) error { - if err := initialize(c); err != nil { - return err - } - // The newaccount is meant for users using the CLI, since 'real' external - // UIs can use the UI-api instead. So we'll just use the native CLI UI here. - var ( - ui = core.NewCommandlineUI() - pwStorage storage.Storage = &storage.NoStorage{} - ksLoc = c.GlobalString(keystoreFlag.Name) - lightKdf = c.GlobalBool(utils.LightKDFFlag.Name) - ) - log.Info("Starting clef", "keystore", ksLoc, "light-kdf", lightKdf) - am := core.StartClefAccountManager(ksLoc, true, lightKdf, "") - // This gives is us access to the external API - apiImpl := core.NewSignerAPI(am, 0, true, ui, nil, false, pwStorage) - // This gives us access to the internal API - internalApi := core.NewUIServerAPI(apiImpl) - addr, err := internalApi.New(context.Background()) - if err == nil { - fmt.Printf("Generated account %v\n", addr.String()) - } - return err -} - -func initialize(c *cli.Context) error { - // Set up the logger to print everything - logOutput := os.Stdout - if c.GlobalBool(stdiouiFlag.Name) { - logOutput = os.Stderr - // If using the stdioui, we can't do the 'confirm'-flow - if !c.GlobalBool(acceptFlag.Name) { - fmt.Fprint(logOutput, legalWarning) - } - } else if !c.GlobalBool(acceptFlag.Name) { - if !confirm(legalWarning) { - return fmt.Errorf("aborted by user") - } - fmt.Println() - } - usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" - output := io.Writer(logOutput) - if usecolor { - output = colorable.NewColorable(logOutput) - } - log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(output, log.TerminalFormat(usecolor)))) - - return nil -} - -// ipcEndpoint resolves an IPC endpoint based on a configured value, taking into -// account the set data folders as well as the designated platform we're currently -// running on. -func ipcEndpoint(ipcPath, datadir string) string { - // On windows we can only use plain top-level pipes - if runtime.GOOS == "windows" { - if strings.HasPrefix(ipcPath, `\\.\pipe\`) { - return ipcPath - } - return `\\.\pipe\` + ipcPath - } - // Resolve names into the data directory full paths otherwise - if filepath.Base(ipcPath) == ipcPath { - if datadir == "" { - return filepath.Join(os.TempDir(), ipcPath) - } - return filepath.Join(datadir, ipcPath) - } - return ipcPath -} - -func signer(c *cli.Context) error { - // If we have some unrecognized command, bail out - if args := c.Args(); len(args) > 0 { - return fmt.Errorf("invalid command: %q", args[0]) - } - if err := initialize(c); err != nil { - return err - } - var ( - ui core.UIClientAPI - ) - if c.GlobalBool(stdiouiFlag.Name) { - log.Info("Using stdin/stdout as UI-channel") - ui = core.NewStdIOUI() - } else { - log.Info("Using CLI as UI-channel") - ui = core.NewCommandlineUI() - } - // 4bytedb data - fourByteLocal := c.GlobalString(customDBFlag.Name) - db, err := fourbyte.NewWithFile(fourByteLocal) - if err != nil { - utils.Fatalf(err.Error()) - } - embeds, locals := db.Size() - log.Info("Loaded 4byte database", "embeds", embeds, "locals", locals, "local", fourByteLocal) - - var ( - api core.ExternalAPI - pwStorage storage.Storage = &storage.NoStorage{} - ) - configDir := c.GlobalString(configdirFlag.Name) - if stretchedKey, err := readMasterKey(c, ui); err != nil { - log.Warn("Failed to open master, rules disabled", "err", err) - } else { - vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) - - // Generate domain specific keys - pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) - jskey := crypto.Keccak256([]byte("jsstorage"), stretchedKey) - confkey := crypto.Keccak256([]byte("config"), stretchedKey) - - // Initialize the encrypted storages - pwStorage = storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey) - jsStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "jsstorage.json"), jskey) - configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confkey) - - // Do we have a rule-file? - if ruleFile := c.GlobalString(ruleFlag.Name); ruleFile != "" { - ruleJS, err := ioutil.ReadFile(ruleFile) - if err != nil { - log.Warn("Could not load rules, disabling", "file", ruleFile, "err", err) - } else { - shasum := sha256.Sum256(ruleJS) - foundShaSum := hex.EncodeToString(shasum[:]) - storedShasum, _ := configStorage.Get("ruleset_sha256") - if storedShasum != foundShaSum { - log.Warn("Rule hash not attested, disabling", "hash", foundShaSum, "attested", storedShasum) - } else { - // Initialize rules - ruleEngine, err := rules.NewRuleEvaluator(ui, jsStorage) - if err != nil { - utils.Fatalf(err.Error()) - } - ruleEngine.Init(string(ruleJS)) - ui = ruleEngine - log.Info("Rule engine configured", "file", c.String(ruleFlag.Name)) - } - } - } - } - var ( - chainId = c.GlobalInt64(chainIdFlag.Name) - ksLoc = c.GlobalString(keystoreFlag.Name) - lightKdf = c.GlobalBool(utils.LightKDFFlag.Name) - advanced = c.GlobalBool(advancedMode.Name) - nousb = c.GlobalBool(utils.NoUSBFlag.Name) - scpath = c.GlobalString(utils.SmartCardDaemonPathFlag.Name) - ) - log.Info("Starting signer", "chainid", chainId, "keystore", ksLoc, - "light-kdf", lightKdf, "advanced", advanced) - am := core.StartClefAccountManager(ksLoc, nousb, lightKdf, scpath) - apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced, pwStorage) - - // Establish the bidirectional communication, by creating a new UI backend and registering - // it with the UI. - ui.RegisterUIServer(core.NewUIServerAPI(apiImpl)) - api = apiImpl - // Audit logging - if logfile := c.GlobalString(auditLogFlag.Name); logfile != "" { - api, err = core.NewAuditLogger(logfile, api) - if err != nil { - utils.Fatalf(err.Error()) - } - log.Info("Audit logs configured", "file", logfile) - } - // register signer API with server - var ( - extapiURL = "n/a" - ipcapiURL = "n/a" - ) - rpcAPI := []rpc.API{ - { - Namespace: "account", - Public: true, - Service: api, - Version: "1.0"}, - } - if c.GlobalBool(utils.HTTPEnabledFlag.Name) { - vhosts := utils.SplitAndTrim(c.GlobalString(utils.HTTPVirtualHostsFlag.Name)) - cors := utils.SplitAndTrim(c.GlobalString(utils.HTTPCORSDomainFlag.Name)) - - srv := rpc.NewServer() - err := node.RegisterApis(rpcAPI, []string{"account"}, srv, false) - if err != nil { - utils.Fatalf("Could not register API: %w", err) - } - handler := node.NewHTTPHandlerStack(srv, cors, vhosts) - - // set port - port := c.Int(rpcPortFlag.Name) - - // start http server - httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.HTTPListenAddrFlag.Name), port) - httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler) - if err != nil { - utils.Fatalf("Could not start RPC api: %v", err) - } - extapiURL = fmt.Sprintf("http://%v/", addr) - log.Info("HTTP endpoint opened", "url", extapiURL) - - defer func() { - // Don't bother imposing a timeout here. - httpServer.Shutdown(context.Background()) - log.Info("HTTP endpoint closed", "url", extapiURL) - }() - } - if !c.GlobalBool(utils.IPCDisabledFlag.Name) { - givenPath := c.GlobalString(utils.IPCPathFlag.Name) - ipcapiURL = ipcEndpoint(filepath.Join(givenPath, "clef.ipc"), configDir) - listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI) - if err != nil { - utils.Fatalf("Could not start IPC api: %v", err) - } - log.Info("IPC endpoint opened", "url", ipcapiURL) - defer func() { - listener.Close() - log.Info("IPC endpoint closed", "url", ipcapiURL) - }() - } - - if c.GlobalBool(testFlag.Name) { - log.Info("Performing UI test") - go testExternalUI(apiImpl) - } - ui.OnSignerStartup(core.StartupInfo{ - Info: map[string]interface{}{ - "intapi_version": core.InternalAPIVersion, - "extapi_version": core.ExternalAPIVersion, - "extapi_http": extapiURL, - "extapi_ipc": ipcapiURL, - }, - }) - - abortChan := make(chan os.Signal, 1) - signal.Notify(abortChan, os.Interrupt) - - sig := <-abortChan - log.Info("Exiting...", "signal", sig) - - return nil -} - -// DefaultConfigDir is the default config directory to use for the vaults and other -// persistence requirements. -func DefaultConfigDir() string { - // Try to place the data folder in the user's home dir - home := utils.HomeDir() - if home != "" { - if runtime.GOOS == "darwin" { - return filepath.Join(home, "Library", "Signer") - } else if runtime.GOOS == "windows" { - appdata := os.Getenv("APPDATA") - if appdata != "" { - return filepath.Join(appdata, "Signer") - } - return filepath.Join(home, "AppData", "Roaming", "Signer") - } - return filepath.Join(home, ".clef") - } - // As we cannot guess a stable location, return empty and handle later - return "" -} - -func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) { - var ( - file string - configDir = ctx.GlobalString(configdirFlag.Name) - ) - if ctx.GlobalIsSet(signerSecretFlag.Name) { - file = ctx.GlobalString(signerSecretFlag.Name) - } else { - file = filepath.Join(configDir, "masterseed.json") - } - if err := checkFile(file); err != nil { - return nil, err - } - cipherKey, err := ioutil.ReadFile(file) - if err != nil { - return nil, err - } - var password string - // If ui is not nil, get the password from ui. - if ui != nil { - resp, err := ui.OnInputRequired(core.UserInputRequest{ - Title: "Master Password", - Prompt: "Please enter the password to decrypt the master seed", - IsPassword: true}) - if err != nil { - return nil, err - } - password = resp.Text - } else { - password = utils.GetPassPhrase("Decrypt master seed of clef", false) - } - masterSeed, err := decryptSeed(cipherKey, password) - if err != nil { - return nil, fmt.Errorf("failed to decrypt the master seed of clef") - } - if len(masterSeed) < 256 { - return nil, fmt.Errorf("master seed of insufficient length, expected >255 bytes, got %d", len(masterSeed)) - } - // Create vault location - vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), masterSeed)[:10])) - err = os.Mkdir(vaultLocation, 0700) - if err != nil && !os.IsExist(err) { - return nil, err - } - return masterSeed, nil -} - -// checkFile is a convenience function to check if a file -// * exists -// * is mode 0400 (unix only) -func checkFile(filename string) error { - info, err := os.Stat(filename) - if err != nil { - return fmt.Errorf("failed stat on %s: %v", filename, err) - } - // Check the unix permission bits - // However, on windows, we cannot use the unix perm-bits, see - // https://github.com/ethereum/go-ethereum/issues/20123 - if runtime.GOOS != "windows" && info.Mode().Perm()&0377 != 0 { - return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String()) - } - return nil -} - -// confirm displays a text and asks for user confirmation -func confirm(text string) bool { - fmt.Print(text) - fmt.Printf("\nEnter 'ok' to proceed:\n> ") - - text, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text := strings.TrimSpace(text); text == "ok" { - return true - } - return false -} - -func testExternalUI(api *core.SignerAPI) { - - ctx := context.WithValue(context.Background(), "remote", "clef binary") - ctx = context.WithValue(ctx, "scheme", "in-proc") - ctx = context.WithValue(ctx, "local", "main") - errs := make([]string, 0) - - a := common.HexToAddress("0xdeadbeef000000000000000000000000deadbeef") - addErr := func(errStr string) { - log.Info("Test error", "err", errStr) - errs = append(errs, errStr) - } - - queryUser := func(q string) string { - resp, err := api.UI.OnInputRequired(core.UserInputRequest{ - Title: "Testing", - Prompt: q, - }) - if err != nil { - addErr(err.Error()) - } - return resp.Text - } - expectResponse := func(testcase, question, expect string) { - if got := queryUser(question); got != expect { - addErr(fmt.Sprintf("%s: got %v, expected %v", testcase, got, expect)) - } - } - expectApprove := func(testcase string, err error) { - if err == nil || err == accounts.ErrUnknownAccount { - return - } - addErr(fmt.Sprintf("%v: expected no error, got %v", testcase, err.Error())) - } - expectDeny := func(testcase string, err error) { - if err == nil || err != core.ErrRequestDenied { - addErr(fmt.Sprintf("%v: expected ErrRequestDenied, got %v", testcase, err)) - } - } - var delay = 1 * time.Second - // Test display of info and error - { - api.UI.ShowInfo("If you see this message, enter 'yes' to next question") - time.Sleep(delay) - expectResponse("showinfo", "Did you see the message? [yes/no]", "yes") - api.UI.ShowError("If you see this message, enter 'yes' to the next question") - time.Sleep(delay) - expectResponse("showerror", "Did you see the message? [yes/no]", "yes") - } - { // Sign data test - clique header - api.UI.ShowInfo("Please approve the next request for signing a clique header") - time.Sleep(delay) - cliqueHeader := types.Header{ - ParentHash: common.HexToHash("0000H45H"), - UncleHash: common.HexToHash("0000H45H"), - Coinbase: common.HexToAddress("0000H45H"), - Root: common.HexToHash("0000H00H"), - TxHash: common.HexToHash("0000H45H"), - ReceiptHash: common.HexToHash("0000H45H"), - Difficulty: big.NewInt(1337), - Number: big.NewInt(1337), - GasLimit: 1338, - GasUsed: 1338, - Time: 1338, - Extra: []byte("Extra data Extra data Extra data Extra data Extra data Extra data Extra data Extra data"), - MixDigest: common.HexToHash("0x0000H45H"), - } - cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader) - if err != nil { - utils.Fatalf("Should not error: %v", err) - } - addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") - _, err = api.SignData(ctx, accounts.MimetypeClique, *addr, hexutil.Encode(cliqueRlp)) - expectApprove("signdata - clique header", err) - } - { // Sign data test - typed data - api.UI.ShowInfo("Please approve the next request for signing EIP-712 typed data") - time.Sleep(delay) - addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") - data := `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"test","type":"uint8"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":"1","verifyingContract":"0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","test":"3","wallet":"0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","test":"2"},"contents":"Hello, Bob!"}}` - //_, err := api.SignData(ctx, accounts.MimetypeTypedData, *addr, hexutil.Encode([]byte(data))) - var typedData core.TypedData - json.Unmarshal([]byte(data), &typedData) - _, err := api.SignTypedData(ctx, *addr, typedData) - expectApprove("sign 712 typed data", err) - } - { // Sign data test - plain text - api.UI.ShowInfo("Please approve the next request for signing text") - time.Sleep(delay) - addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") - _, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world"))) - expectApprove("signdata - text", err) - } - { // Sign data test - plain text reject - api.UI.ShowInfo("Please deny the next request for signing text") - time.Sleep(delay) - addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") - _, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world"))) - expectDeny("signdata - text", err) - } - { // Sign transaction - - api.UI.ShowInfo("Please reject next transaction") - time.Sleep(delay) - data := hexutil.Bytes([]byte{}) - to := common.NewMixedcaseAddress(a) - tx := apitypes.SendTxArgs{ - Data: &data, - Nonce: 0x1, - Value: hexutil.Big(*big.NewInt(6)), - From: common.NewMixedcaseAddress(a), - To: &to, - GasPrice: (*hexutil.Big)(big.NewInt(5)), - Gas: 1000, - Input: nil, - } - _, err := api.SignTransaction(ctx, tx, nil) - expectDeny("signtransaction [1]", err) - expectResponse("signtransaction [2]", "Did you see any warnings for the last transaction? (yes/no)", "no") - } - { // Listing - api.UI.ShowInfo("Please reject listing-request") - time.Sleep(delay) - _, err := api.List(ctx) - expectDeny("list", err) - } - { // Import - api.UI.ShowInfo("Please reject new account-request") - time.Sleep(delay) - _, err := api.New(ctx) - expectDeny("newaccount", err) - } - { // Metadata - api.UI.ShowInfo("Please check if you see the Origin in next listing (approve or deny)") - time.Sleep(delay) - api.List(context.WithValue(ctx, "Origin", "origin.com")) - expectResponse("metadata - origin", "Did you see origin (origin.com)? [yes/no] ", "yes") - } - - for _, e := range errs { - log.Error(e) - } - result := fmt.Sprintf("Tests completed. %d errors:\n%s\n", len(errs), strings.Join(errs, "\n")) - api.UI.ShowInfo(result) - -} - -type encryptedSeedStorage struct { - Description string `json:"description"` - Version int `json:"version"` - Params keystore.CryptoJSON `json:"params"` -} - -// encryptSeed uses a similar scheme as the keystore uses, but with a different wrapping, -// to encrypt the master seed -func encryptSeed(seed []byte, auth []byte, scryptN, scryptP int) ([]byte, error) { - cryptoStruct, err := keystore.EncryptDataV3(seed, auth, scryptN, scryptP) - if err != nil { - return nil, err - } - return json.Marshal(&encryptedSeedStorage{"Clef seed", 1, cryptoStruct}) -} - -// decryptSeed decrypts the master seed -func decryptSeed(keyjson []byte, auth string) ([]byte, error) { - var encSeed encryptedSeedStorage - if err := json.Unmarshal(keyjson, &encSeed); err != nil { - return nil, err - } - if encSeed.Version != 1 { - log.Warn(fmt.Sprintf("unsupported encryption format of seed: %d, operation will likely fail", encSeed.Version)) - } - seed, err := keystore.DecryptDataV3(encSeed.Params, auth) - if err != nil { - return nil, err - } - return seed, err -} - -// GenDoc outputs examples of all structures used in json-rpc communication -func GenDoc(ctx *cli.Context) { - - var ( - a = common.HexToAddress("0xdeadbeef000000000000000000000000deadbeef") - b = common.HexToAddress("0x1111111122222222222233333333334444444444") - meta = core.Metadata{ - Scheme: "http", - Local: "localhost:8545", - Origin: "www.malicious.ru", - Remote: "localhost:9999", - UserAgent: "Firefox 3.2", - } - output []string - add = func(name, desc string, v interface{}) { - if data, err := json.MarshalIndent(v, "", " "); err == nil { - output = append(output, fmt.Sprintf("### %s\n\n%s\n\nExample:\n```json\n%s\n```", name, desc, data)) - } else { - log.Error("Error generating output", "err", err) - } - } - ) - - { // Sign plain text request - desc := "SignDataRequest contains information about a pending request to sign some data. " + - "The data to be signed can be of various types, defined by content-type. Clef has done most " + - "of the work in canonicalizing and making sense of the data, and it's up to the UI to present" + - "the user with the contents of the `message`" - sighash, msg := accounts.TextAndHash([]byte("hello world")) - messages := []*core.NameValueType{{Name: "message", Value: msg, Typ: accounts.MimetypeTextPlain}} - - add("SignDataRequest", desc, &core.SignDataRequest{ - Address: common.NewMixedcaseAddress(a), - Meta: meta, - ContentType: accounts.MimetypeTextPlain, - Rawdata: []byte(msg), - Messages: messages, - Hash: sighash}) - } - { // Sign plain text response - add("SignDataResponse - approve", "Response to SignDataRequest", - &core.SignDataResponse{Approved: true}) - add("SignDataResponse - deny", "Response to SignDataRequest", - &core.SignDataResponse{}) - } - { // Sign transaction request - desc := "SignTxRequest contains information about a pending request to sign a transaction. " + - "Aside from the transaction itself, there is also a `call_info`-struct. That struct contains " + - "messages of various types, that the user should be informed of." + - "\n\n" + - "As in any request, it's important to consider that the `meta` info also contains untrusted data." + - "\n\n" + - "The `transaction` (on input into clef) can have either `data` or `input` -- if both are set, " + - "they must be identical, otherwise an error is generated. " + - "However, Clef will always use `data` when passing this struct on (if Clef does otherwise, please file a ticket)" - - data := hexutil.Bytes([]byte{0x01, 0x02, 0x03, 0x04}) - add("SignTxRequest", desc, &core.SignTxRequest{ - Meta: meta, - Callinfo: []apitypes.ValidationInfo{ - {Typ: "Warning", Message: "Something looks odd, show this message as a warning"}, - {Typ: "Info", Message: "User should see this as well"}, - }, - Transaction: apitypes.SendTxArgs{ - Data: &data, - Nonce: 0x1, - Value: hexutil.Big(*big.NewInt(6)), - From: common.NewMixedcaseAddress(a), - To: nil, - GasPrice: (*hexutil.Big)(big.NewInt(5)), - Gas: 1000, - Input: nil, - }}) - } - { // Sign tx response - data := hexutil.Bytes([]byte{0x04, 0x03, 0x02, 0x01}) - add("SignTxResponse - approve", "Response to request to sign a transaction. This response needs to contain the `transaction`"+ - ", because the UI is free to make modifications to the transaction.", - &core.SignTxResponse{Approved: true, - Transaction: apitypes.SendTxArgs{ - Data: &data, - Nonce: 0x4, - Value: hexutil.Big(*big.NewInt(6)), - From: common.NewMixedcaseAddress(a), - To: nil, - GasPrice: (*hexutil.Big)(big.NewInt(5)), - Gas: 1000, - Input: nil, - }}) - add("SignTxResponse - deny", "Response to SignTxRequest. When denying a request, there's no need to "+ - "provide the transaction in return", - &core.SignTxResponse{}) - } - { // WHen a signed tx is ready to go out - desc := "SignTransactionResult is used in the call `clef` -> `OnApprovedTx(result)`" + - "\n\n" + - "This occurs _after_ successful completion of the entire signing procedure, but right before the signed " + - "transaction is passed to the external caller. This method (and data) can be used by the UI to signal " + - "to the user that the transaction was signed, but it is primarily useful for ruleset implementations." + - "\n\n" + - "A ruleset that implements a rate limitation needs to know what transactions are sent out to the external " + - "interface. By hooking into this methods, the ruleset can maintain track of that count." + - "\n\n" + - "**OBS:** Note that if an attacker can restore your `clef` data to a previous point in time" + - " (e.g through a backup), the attacker can reset such windows, even if he/she is unable to decrypt the content. " + - "\n\n" + - "The `OnApproved` method cannot be responded to, it's purely informative" - - rlpdata := common.FromHex("0xf85d640101948a8eafb1cf62bfbeb1741769dae1a9dd47996192018026a0716bd90515acb1e68e5ac5867aa11a1e65399c3349d479f5fb698554ebc6f293a04e8a4ebfff434e971e0ef12c5bf3a881b06fd04fc3f8b8a7291fb67a26a1d4ed") - var tx types.Transaction - tx.UnmarshalBinary(rlpdata) - add("OnApproved - SignTransactionResult", desc, &ethapi.SignTransactionResult{Raw: rlpdata, Tx: &tx}) - - } - { // User input - add("UserInputRequest", "Sent when clef needs the user to provide data. If 'password' is true, the input field should be treated accordingly (echo-free)", - &core.UserInputRequest{IsPassword: true, Title: "The title here", Prompt: "The question to ask the user"}) - add("UserInputResponse", "Response to UserInputRequest", - &core.UserInputResponse{Text: "The textual response from user"}) - } - { // List request - add("ListRequest", "Sent when a request has been made to list addresses. The UI is provided with the "+ - "full `account`s, including local directory names. Note: this information is not passed back to the external caller, "+ - "who only sees the `address`es. ", - &core.ListRequest{ - Meta: meta, - Accounts: []accounts.Account{ - {Address: a, URL: accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/a"}}, - {Address: b, URL: accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}}, - }) - - add("ListResponse", "Response to list request. The response contains a list of all addresses to show to the caller. "+ - "Note: the UI is free to respond with any address the caller, regardless of whether it exists or not", - &core.ListResponse{ - Accounts: []accounts.Account{ - { - Address: common.HexToAddress("0xcowbeef000000cowbeef00000000000000000c0w"), - URL: accounts.URL{Path: ".. ignored .."}, - }, - { - Address: common.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff"), - }, - }}) - } - - fmt.Println(`## UI Client interface - -These data types are defined in the channel between clef and the UI`) - for _, elem := range output { - fmt.Println(elem) - } -}
diff --git go-ethereum/cmd/clef/pythonsigner.py celo/cmd/clef/pythonsigner.py deleted file mode 100644 index 315aabd73fdb0cbd166c6a37187a1c051c23e071..0000000000000000000000000000000000000000 --- go-ethereum/cmd/clef/pythonsigner.py +++ /dev/null @@ -1,177 +0,0 @@ -import os,sys, subprocess -from tinyrpc.transports import ServerTransport -from tinyrpc.protocols.jsonrpc import JSONRPCProtocol -from tinyrpc.dispatch import public,RPCDispatcher -from tinyrpc.server import RPCServer - -""" This is a POC example of how to write a custom UI for Clef. The UI starts the -clef process with the '--stdio-ui' option, and communicates with clef using standard input / output. - -The standard input/output is a relatively secure way to communicate, as it does not require opening any ports -or IPC files. Needless to say, it does not protect against memory inspection mechanisms where an attacker -can access process memory.""" - -try: - import urllib.parse as urlparse -except ImportError: - import urllib as urlparse - -class StdIOTransport(ServerTransport): - """ Uses std input/output for RPC """ - def receive_message(self): - return None, urlparse.unquote(sys.stdin.readline()) - - def send_reply(self, context, reply): - print(reply) - -class PipeTransport(ServerTransport): - """ Uses std a pipe for RPC """ - - def __init__(self,input, output): - self.input = input - self.output = output - - def receive_message(self): - data = self.input.readline() - print(">> {}".format( data)) - return None, urlparse.unquote(data) - - def send_reply(self, context, reply): - print("<< {}".format( reply)) - self.output.write(reply) - self.output.write("\n") - -class StdIOHandler(): - def __init__(self): - pass - - @public - def ApproveTx(self,req): - """ - Example request: - { - "jsonrpc": "2.0", - "method": "ApproveTx", - "params": [{ - "transaction": { - "to": "0xae967917c465db8578ca9024c205720b1a3651A9", - "gas": "0x333", - "gasPrice": "0x123", - "value": "0x10", - "data": "0xd7a5865800000000000000000000000000000000000000000000000000000000000000ff", - "nonce": "0x0" - }, - "from": "0xAe967917c465db8578ca9024c205720b1a3651A9", - "call_info": "Warning! Could not validate ABI-data against calldata\nSupplied ABI spec does not contain method signature in data: 0xd7a58658", - "meta": { - "remote": "127.0.0.1:34572", - "local": "localhost:8550", - "scheme": "HTTP/1.1" - } - }], - "id": 1 - } - - :param transaction: transaction info - :param call_info: info abou the call, e.g. if ABI info could not be - :param meta: metadata about the request, e.g. where the call comes from - :return: - """ - transaction = req.get('transaction') - _from = req.get('from') - call_info = req.get('call_info') - meta = req.get('meta') - - return { - "approved" : False, - #"transaction" : transaction, - # "from" : _from, -# "password" : None, - } - - @public - def ApproveSignData(self, req): - """ Example request - - """ - return {"approved": False, "password" : None} - - @public - def ApproveExport(self, req): - """ Example request - - """ - return {"approved" : False} - - @public - def ApproveImport(self, req): - """ Example request - - """ - return { "approved" : False, "old_password": "", "new_password": ""} - - @public - def ApproveListing(self, req): - """ Example request - - """ - return {'accounts': []} - - @public - def ApproveNewAccount(self, req): - """ - Example request - - :return: - """ - return {"approved": False, - #"password": "" - } - - @public - def ShowError(self,message = {}): - """ - Example request: - - {"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowError'"},"id":1} - - :param message: to show - :return: nothing - """ - if 'text' in message.keys(): - sys.stderr.write("Error: {}\n".format( message['text'])) - return - - @public - def ShowInfo(self,message = {}): - """ - Example request - {"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowInfo'"},"id":0} - - :param message: to display - :return:nothing - """ - - if 'text' in message.keys(): - sys.stdout.write("Error: {}\n".format( message['text'])) - return - -def main(args): - cmd = ["clef", "--stdio-ui"] - if len(args) > 0 and args[0] == "test": - cmd.extend(["--stdio-ui-test"]) - print("cmd: {}".format(" ".join(cmd))) - dispatcher = RPCDispatcher() - dispatcher.register_instance(StdIOHandler(), '') - # line buffered - p = subprocess.Popen(cmd, bufsize=1, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - - rpc_server = RPCServer( - PipeTransport(p.stdout, p.stdin), - JSONRPCProtocol(), - dispatcher - ) - rpc_server.serve_forever() - -if __name__ == '__main__': - main(sys.argv[1:])
diff --git go-ethereum/cmd/clef/intapi_changelog.md celo/cmd/clef/intapi_changelog.md deleted file mode 100644 index eaeb2e68620b6844e0fc535fd99b82d7bec8cba7..0000000000000000000000000000000000000000 --- go-ethereum/cmd/clef/intapi_changelog.md +++ /dev/null @@ -1,191 +0,0 @@ -## Changelog for internal API (ui-api) - -The API uses [semantic versioning](https://semver.org/). - -TL;DR: Given a version number MAJOR.MINOR.PATCH, increment the: - -* MAJOR version when you make incompatible API changes, -* MINOR version when you add functionality in a backwards-compatible manner, and -* PATCH version when you make backwards-compatible bug fixes. - -Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. - -### 7.0.1 - -Added `clef_New` to the internal API callable from a UI. - -> `New` creates a new password protected Account. The private key is protected with -> the given password. Users are responsible to backup the private key that is stored -> in the keystore location that was specified when this API was created. -> This method is the same as New on the external API, the difference being that -> this implementation does not ask for confirmation, since it's initiated by -> the user - -### 7.0.0 - -- The `message` field was renamed to `messages` in all data signing request methods to better reflect that it's a list, not a value. -- The `storage.Put` and `storage.Get` methods in the rule execution engine were lower-cased to `storage.put` and `storage.get` to be consistent with JavaScript call conventions. - -### 6.0.0 - -Removed `password` from responses to operations which require them. This is for two reasons, - -- Consistency between how rulesets operate and how manual processing works. A rule can `Approve` but require the actual password to be stored in the clef storage. -With this change, the same stored password can be used even if rulesets are not enabled, but storage is. -- It also removes the usability-shortcut that a UI might otherwise want to implement; remembering passwords. Since we now will not require the -password on every `Approve`, there's no need for the UI to cache it locally. - - In a future update, we'll likely add `clef_storePassword` to the internal API, so the user can store it via his UI (currently only CLI works). - -Affected datatypes: -- `SignTxResponse` -- `SignDataResponse` -- `NewAccountResponse` - -If `clef` requires a password, the `OnInputRequired` will be used to collect it. - - -### 5.0.0 - -Changed the namespace format to adhere to the legacy ethereum format: `name_methodName`. Changes: - -* `ApproveTx` -> `ui_approveTx` -* `ApproveSignData` -> `ui_approveSignData` -* `ApproveExport` -> `removed` -* `ApproveImport` -> `removed` -* `ApproveListing` -> `ui_approveListing` -* `ApproveNewAccount` -> `ui_approveNewAccount` -* `ShowError` -> `ui_showError` -* `ShowInfo` -> `ui_showInfo` -* `OnApprovedTx` -> `ui_onApprovedTx` -* `OnSignerStartup` -> `ui_onSignerStartup` -* `OnInputRequired` -> `ui_onInputRequired` - - -### 4.0.0 - -* Bidirectional communication implemented, so the UI can query `clef` via the stdin/stdout RPC channel. Methods implemented are: - - `clef_listWallets` - - `clef_listAccounts` - - `clef_listWallets` - - `clef_deriveAccount` - - `clef_importRawKey` - - `clef_openWallet` - - `clef_chainId` - - `clef_setChainId` - - `clef_export` - - `clef_import` - -* The type `Account` was modified (the json-field `type` was removed), to consist of - -```go -type Account struct { - Address common.Address `json:"address"` // Ethereum account address derived from the key - URL URL `json:"url"` // Optional resource locator within a backend -} -``` - - -### 3.2.0 - -* Make `ShowError`, `OnApprovedTx`, `OnSignerStartup` be json-rpc [notifications](https://www.jsonrpc.org/specification#notification): - -> A Notification is a Request object without an "id" member. A Request object that is a Notification signifies the Client's lack of interest in the corresponding Response object, and as such no Response object needs to be returned to the client. The Server MUST NOT reply to a Notification, including those that are within a batch request. -> -> Notifications are not confirmable by definition, since they do not have a Response object to be returned. As such, the Client would not be aware of any errors (like e.g. "Invalid params","Internal error" -### 3.1.0 - -* Add `ContentType` `string` to `SignDataRequest` to accommodate the latest EIP-191 and EIP-712 implementations. - -### 3.0.0 - -* Make use of `OnInputRequired(info UserInputRequest)` for obtaining master password during startup - -### 2.1.0 - -* Add `OnInputRequired(info UserInputRequest)` to internal API. This method is used when Clef needs user input, e.g. passwords. - -The following structures are used: - -```go -UserInputRequest struct { - Prompt string `json:"prompt"` - Title string `json:"title"` - IsPassword bool `json:"isPassword"` -} -UserInputResponse struct { - Text string `json:"text"` -} -``` - -### 2.0.0 - -* Modify how `call_info` on a transaction is conveyed. New format: - -``` -{ - "jsonrpc": "2.0", - "id": 2, - "method": "ApproveTx", - "params": [ - { - "transaction": { - "from": "0x82A2A876D39022B3019932D30Cd9c97ad5616813", - "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "gas": "0x333", - "gasPrice": "0x123", - "value": "0x10", - "nonce": "0x0", - "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", - "input": null - }, - "call_info": [ - { - "type": "WARNING", - "message": "Invalid checksum on to-address" - }, - { - "type": "WARNING", - "message": "Tx contains data, but provided ABI signature could not be matched: Did not match: test (0 matches)" - } - ], - "meta": { - "remote": "127.0.0.1:54286", - "local": "localhost:8550", - "scheme": "HTTP/1.1" - } - } - ] -} -``` - -#### 1.2.0 - -* Add `OnStartup` method, to provide the UI with information about what API version -the signer uses (both internal and external) as well as build-info and external api. - -Example call: -```json -{ - "jsonrpc": "2.0", - "id": 1, - "method": "OnSignerStartup", - "params": [ - { - "info": { - "extapi_http": "http://localhost:8550", - "extapi_ipc": null, - "extapi_version": "2.0.0", - "intapi_version": "1.2.0" - } - } - ] -} -``` - -#### 1.1.0 - -* Add `OnApproved` method - -#### 1.0.0 - -Initial release.
diff --git go-ethereum/cmd/clef/extapi_changelog.md celo/cmd/clef/extapi_changelog.md deleted file mode 100644 index 31554f07902093a93d9ddf72d3f7d9895657986a..0000000000000000000000000000000000000000 --- go-ethereum/cmd/clef/extapi_changelog.md +++ /dev/null @@ -1,104 +0,0 @@ -## Changelog for external API - -The API uses [semantic versioning](https://semver.org/). - -TL;DR: Given a version number MAJOR.MINOR.PATCH, increment the: - -* MAJOR version when you make incompatible API changes, -* MINOR version when you add functionality in a backwards-compatible manner, and -* PATCH version when you make backwards-compatible bug fixes. - -Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. - -### 6.1.0 - -The API-method `account_signGnosisSafeTx` was added. This method takes two parameters, -`[address, safeTx]`. The latter, `safeTx`, can be copy-pasted from the gnosis relay. For example: - -``` -{ - "jsonrpc": "2.0", - "method": "account_signGnosisSafeTx", - "params": ["0xfd1c4226bfD1c436672092F4eCbfC270145b7256", - { - "safe": "0x25a6c4BBd32B2424A9c99aEB0584Ad12045382B3", - "to": "0xB372a646f7F05Cc1785018dBDA7EBc734a2A20E2", - "value": "20000000000000000", - "data": null, - "operation": 0, - "gasToken": "0x0000000000000000000000000000000000000000", - "safeTxGas": 27845, - "baseGas": 0, - "gasPrice": "0", - "refundReceiver": "0x0000000000000000000000000000000000000000", - "nonce": 2, - "executionDate": null, - "submissionDate": "2020-09-15T21:54:49.617634Z", - "modified": "2020-09-15T21:54:49.617634Z", - "blockNumber": null, - "transactionHash": null, - "safeTxHash": "0x2edfbd5bc113ff18c0631595db32eb17182872d88d9bf8ee4d8c2dd5db6d95e2", - "executor": null, - "isExecuted": false, - "isSuccessful": null, - "ethGasPrice": null, - "gasUsed": null, - "fee": null, - "origin": null, - "dataDecoded": null, - "confirmationsRequired": null, - "confirmations": [ - { - "owner": "0xAd2e180019FCa9e55CADe76E4487F126Fd08DA34", - "submissionDate": "2020-09-15T21:54:49.663299Z", - "transactionHash": null, - "confirmationType": "CONFIRMATION", - "signature": "0x95a7250bb645f831c86defc847350e7faff815b2fb586282568e96cc859e39315876db20a2eed5f7a0412906ec5ab57652a6f645ad4833f345bda059b9da2b821c", - "signatureType": "EOA" - } - ], - "signatures": null - } - ], - "id": 67 -} -``` - -Not all fields are required, though. This method is really just a UX helper, which massages the -input to conform to the `EIP-712` [specification](https://docs.gnosis.io/safe/docs/contracts_tx_execution/#transaction-hash) -for the Gnosis Safe, and making the output be directly importable to by a relay service. - - -### 6.0.0 - -* `New` was changed to deliver only an address, not the full `Account` data -* `Export` was moved from External API to the UI Server API - -#### 5.0.0 - -* The external `account_EcRecover`-method was reimplemented. -* The external method `account_sign(address, data)` was replaced with `account_signData(contentType, address, data)`. -The addition of `contentType` makes it possible to use the method for different types of objects, such as: - * signing data with an intended validator (not yet implemented) - * signing clique headers, - * signing plain personal messages, -* The external method `account_signTypedData` implements [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) and makes it possible to sign typed data. - -#### 4.0.0 - -* The external `account_Ecrecover`-method was removed. -* The external `account_Import`-method was removed. - -#### 3.0.0 - -* The external `account_List`-method was changed to not expose `url`, which contained info about the local filesystem. It now returns only a list of addresses. - -#### 2.0.0 - -* Commit `73abaf04b1372fa4c43201fb1b8019fe6b0a6f8d`, move `from` into `transaction` object in `signTransaction`. This -makes the `accounts_signTransaction` identical to the old `eth_signTransaction`. - - -#### 1.0.0 - -Initial release.
(deleted)
(binary file)
diff --git go-ethereum/cmd/clef/sign_flow.png celo/cmd/clef/sign_flow.png deleted file mode 100644 index e7010ab43f3a6f54d69958055b93b611f7853951..0000000000000000000000000000000000000000 Binary files go-ethereum/cmd/clef/sign_flow.png and /dev/null differ
diff --git go-ethereum/cmd/clef/testdata/sign_1559_tx.json celo/cmd/clef/testdata/sign_1559_tx.json deleted file mode 100644 index 29355f6cf5bdb55349c2760736419727202d2c04..0000000000000000000000000000000000000000 --- go-ethereum/cmd/clef/testdata/sign_1559_tx.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "account_signTransaction", - "params": [ - { - "from": "0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192", - "to": "0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192", - "gas": "0x333", - "maxPriorityFeePerGas": "0x123", - "maxFeePerGas": "0x123", - "nonce": "0x0", - "value": "0x10", - "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012" - } - ], - "id": 67 -}
diff --git go-ethereum/cmd/clef/testdata/sign_normal_exp_ok.json celo/cmd/clef/testdata/sign_normal_exp_ok.json deleted file mode 100644 index 7f3a9202a07de70751d51b76dc2d40be9b397ffd..0000000000000000000000000000000000000000 --- go-ethereum/cmd/clef/testdata/sign_normal_exp_ok.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "account_signTransaction", - "params": [ - { - "from":"0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192", - "to":"0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192", - "gas": "0x333", - "gasPrice": "0x123", - "nonce": "0x0", - "value": "0x10", - "data": - "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012" - } - ], - "id": 67 -}
diff --git go-ethereum/cmd/clef/tests/testsigner.js celo/cmd/clef/tests/testsigner.js deleted file mode 100644 index 258679de5073e4c7d6988f60b17ef8d69eb1324d..0000000000000000000000000000000000000000 --- go-ethereum/cmd/clef/tests/testsigner.js +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -// This file is a test-utility for testing clef-functionality -// -// Start clef with -// -// build/bin/clef --4bytedb=./cmd/clef/4byte.json --rpc -// -// Start geth with -// -// build/bin/geth --nodiscover --maxpeers 0 --signer http://localhost:8550 console --preload=cmd/clef/tests/testsigner.js -// -// and in the console simply invoke -// -// > test() -// -// You can reload the file via `reload()` - -function reload(){ - loadScript("./cmd/clef/tests/testsigner.js"); -} - -function init(){ - if (typeof accts == 'undefined' || accts.length == 0){ - accts = eth.accounts - console.log("Got accounts ", accts); - } -} -init() -function testTx(){ - if( accts && accts.length > 0) { - var a = accts[0] - var txdata = eth.signTransaction({from: a, to: a, value: 1, nonce: 1, gas: 1, gasPrice: 1}) - var v = parseInt(txdata.tx.v) - console.log("V value: ", v) - if (v == 37 || v == 38){ - console.log("Mainnet 155-protected chainid was used") - } - if (v == 27 || v == 28){ - throw new Error("Mainnet chainid was used, but without replay protection!") - } - } -} -function testSignText(){ - if( accts && accts.length > 0){ - var a = accts[0] - var r = eth.sign(a, "0x68656c6c6f20776f726c64"); //hello world - console.log("signing response", r) - } -} -function testClique(){ - if( accts && accts.length > 0){ - var a = accts[0] - var r = debug.testSignCliqueBlock(a, 0); // Sign genesis - console.log("signing response", r) - if( a != r){ - throw new Error("Requested signing by "+a+ " but got sealer "+r) - } - } -} - -function test(){ - var tests = [ - testTx, - testSignText, - testClique, - ] - for( i in tests){ - try{ - tests[i]() - }catch(err){ - console.log(err) - } - } - }
diff --git go-ethereum/cmd/clef/testdata/sign_bad_checksum_exp_fail.json celo/cmd/clef/testdata/sign_bad_checksum_exp_fail.json deleted file mode 100644 index 21ba7b3fc090a114d39a3dfdeaae5058a65c594b..0000000000000000000000000000000000000000 --- go-ethereum/cmd/clef/testdata/sign_bad_checksum_exp_fail.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "account_signTransaction", - "params": [ - { - "from":"0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192", - "to":"0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192", - "gas": "0x333", - "gasPrice": "0x123", - "nonce": "0x0", - "value": "0x10", - "data": - "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012" - } - ], - "id": 67 -}
diff --git go-ethereum/cmd/clef/testdata/sign_1559_missing_field_exp_fail.json celo/cmd/clef/testdata/sign_1559_missing_field_exp_fail.json deleted file mode 100644 index c5a133686085f694c99f4993cccc2dac170e3f07..0000000000000000000000000000000000000000 --- go-ethereum/cmd/clef/testdata/sign_1559_missing_field_exp_fail.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "account_signTransaction", - "params": [ - { - "from": "0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192", - "to": "0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192", - "gas": "0x333", - "maxFeePerGas": "0x123", - "nonce": "0x0", - "value": "0x10", - "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012" - } - ], - "id": 67 -}
diff --git go-ethereum/cmd/clef/testdata/sign_1559_missing_maxfeepergas_exp_fail.json celo/cmd/clef/testdata/sign_1559_missing_maxfeepergas_exp_fail.json deleted file mode 100644 index df69231d7efe935744d48b0de45df7b33741764b..0000000000000000000000000000000000000000 --- go-ethereum/cmd/clef/testdata/sign_1559_missing_maxfeepergas_exp_fail.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "account_signTransaction", - "params": [ - { - "from": "0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192", - "to": "0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192", - "gas": "0x333", - "maxPriorityFeePerGas": "0x123", - "nonce": "0x0", - "value": "0x10", - "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012" - } - ], - "id": 67 -}
diff --git go-ethereum/cmd/evm/runner.go celo/cmd/evm/runner.go index 5df8e40021ae3391be922acce69dc2f0cf548575..454370494e0a64cd1d2a620b3d548ad6c0a0d93f 100644 --- go-ethereum/cmd/evm/runner.go +++ celo/cmd/evm/runner.go @@ -197,16 +197,12 @@ } code = common.Hex2Bytes(bin) } initialGas := ctx.GlobalUint64(GasFlag.Name) - if genesisConfig.GasLimit != 0 { - initialGas = genesisConfig.GasLimit - } runtimeConfig := runtime.Config{ Origin: sender, State: statedb, GasLimit: initialGas, GasPrice: utils.GlobalBig(ctx, PriceFlag.Name), Value: utils.GlobalBig(ctx, ValueFlag.Name), - Difficulty: genesisConfig.Difficulty, Time: new(big.Int).SetUint64(genesisConfig.Timestamp), Coinbase: genesisConfig.Coinbase, BlockNumber: new(big.Int).SetUint64(genesisConfig.Number), @@ -232,7 +228,7 @@ if chainConfig != nil { runtimeConfig.ChainConfig = chainConfig } else { - runtimeConfig.ChainConfig = params.AllEthashProtocolChanges + runtimeConfig.ChainConfig = params.MainnetChainConfig }   var hexInput []byte
diff --git go-ethereum/cmd/evm/compiler.go celo/cmd/evm/compiler.go index 18497e493a72b4d7bfcac53e1184f1ddf360f5ce..fb4fbb387ffb1cfe8c08e7765bb4cb52e35c33d3 100644 --- go-ethereum/cmd/evm/compiler.go +++ celo/cmd/evm/compiler.go @@ -22,7 +22,6 @@ "fmt" "io/ioutil"   "github.com/ethereum/go-ethereum/cmd/evm/internal/compiler" - "gopkg.in/urfave/cli.v1" )
diff --git go-ethereum/cmd/evm/t8n_test.go celo/cmd/evm/t8n_test.go index ca5e7c0219086507d0dcd20d1532990077209434..0496d3c6cf4458d2712920ef60966b1ec71ee69e 100644 --- go-ethereum/cmd/evm/t8n_test.go +++ celo/cmd/evm/t8n_test.go @@ -89,6 +89,7 @@ return out }   func TestT8n(t *testing.T) { + t.Skip() // TODO: T8n not yet ready in CELO tt := new(testT8n) tt.TestCmd = cmdtest.NewTestCmd(t, tt) for i, tc := range []struct { @@ -217,6 +218,7 @@ return out }   func TestT9n(t *testing.T) { + t.Skip() // TODO: T8n not yet ready in CELO tt := new(testT8n) tt.TestCmd = cmdtest.NewTestCmd(t, tt) for i, tc := range []struct {
diff --git go-ethereum/cmd/evm/internal/t8ntool/execution.go celo/cmd/evm/internal/t8ntool/execution.go index 62f08da4faa61aa2e215b5d2c5585f68415f9e04..36158a48b7ddd662e75d257975459939645f4533 100644 --- go-ethereum/cmd/evm/internal/t8ntool/execution.go +++ celo/cmd/evm/internal/t8ntool/execution.go @@ -23,8 +23,6 @@ "os"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -125,26 +123,17 @@ txIndex = 0 ) gaspool.AddGas(pre.Env.GasLimit) vmContext := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, + // CanTransfer: core.CanTransfer, + // Transfer: core.Transfer, Coinbase: pre.Env.Coinbase, BlockNumber: new(big.Int).SetUint64(pre.Env.Number), Time: new(big.Int).SetUint64(pre.Env.Timestamp), - Difficulty: pre.Env.Difficulty, - GasLimit: pre.Env.GasLimit, GetHash: getHash, } // If currentBaseFee is defined, add it to the vmContext. if pre.Env.BaseFee != nil { vmContext.BaseFee = new(big.Int).Set(pre.Env.BaseFee) } - // If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's - // done in StateProcessor.Process(block, ...), right before transactions are applied. - if chainConfig.DAOForkSupport && - chainConfig.DAOForkBlock != nil && - chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 { - misc.ApplyDAOHardFork(statedb) - }   for i, tx := range txs { msg, err := tx.AsMessage(signer, pre.Env.BaseFee) @@ -161,11 +150,14 @@ vmConfig.Tracer = tracer vmConfig.Debug = (tracer != nil) statedb.Prepare(tx.Hash(), txIndex) txContext := core.NewEVMTxContext(msg) - snapshot := statedb.Snapshot() + evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)   + snapshot := statedb.Snapshot() + + // FIXME this is broken // (ret []byte, usedGas uint64, failed bool, err error) - msgResult, err := core.ApplyMessage(evm, msg, gaspool) + msgResult, err := core.ApplyMessage(evm, msg, gaspool, nil, &core.SysContractCallCtx{}) if err != nil { statedb.RevertToSnapshot(snapshot) log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err) @@ -254,7 +246,6 @@ Bloom: types.CreateBloom(receipts), LogsHash: rlpHash(statedb.Logs()), Receipts: receipts, Rejected: rejectedTxs, - Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty), } return statedb, execRs, nil } @@ -282,23 +273,3 @@ rlp.Encode(hw, x) hw.Sum(h[:0]) return h } - -// calcDifficulty is based on ethash.CalcDifficulty. This method is used in case -// the caller does not provide an explicit difficulty, but instead provides only -// parent timestamp + difficulty. -// Note: this method only works for ethash engine. -func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime uint64, - parentDifficulty *big.Int, parentUncleHash common.Hash) *big.Int { - uncleHash := parentUncleHash - if uncleHash == (common.Hash{}) { - uncleHash = types.EmptyUncleHash - } - parent := &types.Header{ - ParentHash: common.Hash{}, - UncleHash: uncleHash, - Difficulty: parentDifficulty, - Number: new(big.Int).SetUint64(number - 1), - Time: parentTime, - } - return ethash.CalcDifficulty(config, currentTime, parent) -}
diff --git go-ethereum/cmd/evm/internal/t8ntool/transition.go celo/cmd/evm/internal/t8ntool/transition.go index 08bbc8181ee501eec177ce423266f98c32bc7069..81ed0dc2c4caad1c42473d0a55049a6cea5ec439 100644 --- go-ethereum/cmd/evm/internal/t8ntool/transition.go +++ celo/cmd/evm/internal/t8ntool/transition.go @@ -87,6 +87,9 @@ glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name))) log.Root().SetHandler(glogger)   + // TODO this command is broken: vmContext and vmRunner can not be created as required + log.Crit("Command does not work as expected. Requires Bug Fixing") + var ( err error tracer vm.Tracer @@ -252,25 +255,26 @@ if txs, err = signUnsignedTransactions(txsWithKeys, signer); err != nil { return NewError(ErrorJson, fmt.Errorf("failed signing transactions: %v", err)) } // Sanity check, to not `panic` in state_transition - if chainConfig.IsLondon(big.NewInt(int64(prestate.Env.Number))) { + if chainConfig.IsEspresso(big.NewInt(int64(prestate.Env.Number))) { if prestate.Env.BaseFee == nil { return NewError(ErrorVMConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section")) } } - if env := prestate.Env; env.Difficulty == nil { - // If difficulty was not provided by caller, we need to calculate it. - switch { - case env.ParentDifficulty == nil: - return NewError(ErrorVMConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty")) - case env.Number == 0: - return NewError(ErrorVMConfig, errors.New("currentDifficulty needs to be provided for block number 0")) - case env.Timestamp <= env.ParentTimestamp: - return NewError(ErrorVMConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)", - env.Timestamp, env.ParentTimestamp)) - } - prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp, - env.ParentTimestamp, env.ParentDifficulty, env.ParentUncleHash) - } + // No Difficulty env for CELO + // if env := prestate.Env; env.Difficulty == nil { + // // If difficulty was not provided by caller, we need to calculate it. + // switch { + // case env.ParentDifficulty == nil: + // return NewError(ErrorVMConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty")) + // case env.Number == 0: + // return NewError(ErrorVMConfig, errors.New("currentDifficulty needs to be provided for block number 0")) + // case env.Timestamp <= env.ParentTimestamp: + // return NewError(ErrorVMConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)", + // env.Timestamp, env.ParentTimestamp)) + // } + // prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp, + // env.ParentTimestamp, env.ParentDifficulty, env.ParentUncleHash) + // } // Run the test and aggregate the result s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer) if err != nil { @@ -321,6 +325,7 @@ // // The transactions can have two forms, either // 1. unsigned or // 2. signed +// // For (1), r, s, v, need so be zero, and the `secretKey` needs to be set. // If so, we sign it here and now, with the given `secretKey` // If the condition above is not met, then it's considered a signed transaction.
diff --git go-ethereum/cmd/blspopchecker/main_test.go celo/cmd/blspopchecker/main_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ac4d256a8646deb2644cc360ecc96f0d4ce686f9 --- /dev/null +++ celo/cmd/blspopchecker/main_test.go @@ -0,0 +1,28 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. +package main + +import ( + "io/ioutil" + "testing" +) + +func TestMain(t *testing.T) { + data, err := ioutil.ReadFile("test.json") + check(err) + + run("0x993c4d601ed879b4ad36fc31f0c0214d547113eb", data) +}
diff --git go-ethereum/cmd/blspopchecker/main.go celo/cmd/blspopchecker/main.go new file mode 100644 index 0000000000000000000000000000000000000000..d71e400cf997a177426fae5b18fb879e5cc9227a --- /dev/null +++ celo/cmd/blspopchecker/main.go @@ -0,0 +1,106 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +// blspopchecker checks BLS PoPs in genesis. +package main + +import ( + "encoding/hex" + "encoding/json" + "errors" + "flag" + "fmt" + "io/ioutil" + "os" + + "github.com/celo-org/celo-bls-go/bls" + "github.com/ethereum/go-ethereum/common" +) + +var ( + message = flag.String("message", "", "Common message to verify for the PoPs") + genesis = flag.String("genesis", "", "JSON file containing the genesis PoPs") +) + +func init() { + flag.Usage = func() { + fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-message <message>] [-genesis <path>]") + flag.PrintDefaults() + fmt.Fprintln(os.Stderr, ` +Verifies PoP signatures for the genesis block.`) + } +} + +type genesisItem struct { + Address string `json:"address"` + BLSPublicKey string `json:"blsPublicKey"` + BLSPoP string `json:"blsPop"` +} + +type genesisJson = []genesisItem + +func main() { + flag.Parse() + + if *message == "" { + panic(errors.New("message is not set")) + } + + if *genesis == "" { + panic(errors.New("genesis file is not set")) + } + + data, err := ioutil.ReadFile(*genesis) + check(err) + + run(*message, data) +} + +func run(message string, data []byte) { + var genesisData genesisJson + err := json.Unmarshal(data, &genesisData) + check(err) + + messageBytes := common.HexToAddress(message) + + for _, v := range genesisData { + address, err := hex.DecodeString(v.Address) + check(err) + + blsPublicKey, err := hex.DecodeString(v.BLSPublicKey) + check(err) + + blsPop, err := hex.DecodeString(v.BLSPoP) + check(err) + + publicKey, err := bls.DeserializePublicKey(blsPublicKey) + check(err) + + signature, err := bls.DeserializeSignature(blsPop) + check(err) + + err = publicKey.VerifyPoP(messageBytes.Bytes(), signature) + check(err) + + fmt.Printf("PoP for signer %x is verified\n", address) + } +} + +func check(e error) { + if e != nil { + panic(e) + } +}
diff --git go-ethereum/cmd/blspopchecker/test.json celo/cmd/blspopchecker/test.json new file mode 100644 index 0000000000000000000000000000000000000000..8fb98e534d5d7a65cce6c7e09a4f9cfb8bdb9d62 --- /dev/null +++ celo/cmd/blspopchecker/test.json @@ -0,0 +1,7 @@ +[ + { + "address": "d576b6017c231a8ad6def5c4e2c8369361105b38", + "blsPublicKey": "07460551ec20db520ce5874fd6028dc7a0c53499baa723a4f32008e78fc0d9553d9c845acbe4f71d13ce1f5f7d029301b67e3b4cf2b970611c2b682b174b0b8f0fc7728bb2e73f1df245de975cb2eb22fe7072be691bfce1ea645395ef49b780", + "blsPop": "8a0e5957fbe16b3a41f2d4899761b691e04fef7970b334d79f9c5cac8fe49270ba99a6a40cacf45d5016134542f86c01" + } +]
diff --git go-ethereum/cmd/devp2p/discv4cmd.go celo/cmd/devp2p/discv4cmd.go index c2b9bb9e6af3c0cc5f53579394e30af449c07d2b..d7fb06ce41f2848e3f8086f35baae2d8e4d6e31b 100644 --- go-ethereum/cmd/devp2p/discv4cmd.go +++ celo/cmd/devp2p/discv4cmd.go @@ -23,11 +23,13 @@ "strings" "time"   "github.com/ethereum/go-ethereum/cmd/devp2p/internal/v4test" + "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/params" + "gopkg.in/urfave/cli.v1" )   @@ -35,6 +37,7 @@ var ( discv4Command = cli.Command{ Name: "discv4", Usage: "Node Discovery v4 tools", + Flags: []cli.Flag{networkIdFlag}, Subcommands: []cli.Command{ discv4PingCommand, discv4RequestRecordCommand, @@ -118,6 +121,12 @@ Usage: "Enode of the remote node under test", EnvVar: "REMOTE_ENODE", } ) + +var networkIdFlag = cli.Uint64Flag{ + Name: "networkid", + Usage: "Network identifier", + Value: node.DefaultConfig.P2P.NetworkId, +}   func discv4Ping(ctx *cli.Context) error { n := getNodeArg(ctx) @@ -252,7 +261,8 @@ db, err := enode.OpenDB(dbpath) if err != nil { exit(err) } - ln := enode.NewLocalNode(db, cfg.PrivateKey) + networkId := ctx.GlobalUint64(networkIdFlag.Name) + ln := enode.NewLocalNode(db, cfg.PrivateKey, networkId) return ln, cfg }   @@ -276,7 +286,7 @@ return usocket }   func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) { - s := params.RinkebyBootnodes + s := utils.GetBootstrapNodes(ctx) if ctx.IsSet(bootnodesFlag.Name) { input := ctx.String(bootnodesFlag.Name) if input == "" {
diff --git go-ethereum/cmd/devp2p/rlpxcmd.go celo/cmd/devp2p/rlpxcmd.go index c2234a4aa25ef360e752d538c432da972d81cded..f684ab55960cc36c5e7d98f913f6456a169dfd99 100644 --- go-ethereum/cmd/devp2p/rlpxcmd.go +++ celo/cmd/devp2p/rlpxcmd.go @@ -99,9 +99,9 @@ suite, err := ethtest.NewSuite(getNodeArg(ctx), ctx.Args()[1], ctx.Args()[2]) if err != nil { exit(err) } - // check if given node supports eth66, and if so, run eth66 protocol tests as well - is66Failed, _ := utesting.Run(utesting.Test{Name: "Is_66", Fn: suite.Is_66}) - if is66Failed { + // check if given node supports celo67 (eth66), and if so, run celo67 (eth66) protocol tests as well + is67Failed, _ := utesting.Run(utesting.Test{Name: "Is_67", Fn: suite.Is_67}) + if is67Failed { return runTests(ctx, suite.EthTests()) } return runTests(ctx, suite.AllEthTests())
diff --git go-ethereum/cmd/devp2p/discv5cmd.go celo/cmd/devp2p/discv5cmd.go index 44532b98e350d11ec9ad6cce434bec5406851589..26a43bdc40cfccb5ca8d1639ba600ff6223be5e2 100644 --- go-ethereum/cmd/devp2p/discv5cmd.go +++ celo/cmd/devp2p/discv5cmd.go @@ -30,6 +30,7 @@ var ( discv5Command = cli.Command{ Name: "discv5", Usage: "Node Discovery v5 tools", + Flags: []cli.Flag{networkIdFlag}, Subcommands: []cli.Command{ discv5PingCommand, discv5ResolveCommand,
diff --git go-ethereum/cmd/devp2p/nodesetcmd.go celo/cmd/devp2p/nodesetcmd.go index 9d3cff7cf972a7b72780a8e09eeef4aae1c91e7b..7af760d1fafbe185dfeaae07b2a0cad721db01b6 100644 --- go-ethereum/cmd/devp2p/nodesetcmd.go +++ celo/cmd/devp2p/nodesetcmd.go @@ -229,12 +229,10 @@ var filter forkid.Filter switch args[0] { case "mainnet": filter = forkid.NewStaticFilter(params.MainnetChainConfig, params.MainnetGenesisHash) - case "rinkeby": - filter = forkid.NewStaticFilter(params.RinkebyChainConfig, params.RinkebyGenesisHash) - case "goerli": - filter = forkid.NewStaticFilter(params.GoerliChainConfig, params.GoerliGenesisHash) - case "ropsten": - filter = forkid.NewStaticFilter(params.RopstenChainConfig, params.RopstenGenesisHash) + case "baklava": + filter = forkid.NewStaticFilter(params.BaklavaChainConfig, params.BaklavaGenesisHash) + case "alfajores": + filter = forkid.NewStaticFilter(params.AlfajoresChainConfig, params.AlfajoresGenesisHash) default: return nil, fmt.Errorf("unknown network %q", args[0]) }
diff --git go-ethereum/cmd/devp2p/internal/ethtest/suite.go celo/cmd/devp2p/internal/ethtest/suite.go index 2d97483e725d909150dce008ea9a8e930bfe1c3c..09afed6319c02782915b4b1dee828de1f77cd34a 100644 --- go-ethereum/cmd/devp2p/internal/ethtest/suite.go +++ celo/cmd/devp2p/internal/ethtest/suite.go @@ -53,37 +53,37 @@ func (s *Suite) AllEthTests() []utesting.Test { return []utesting.Test{ // status {Name: "TestStatus", Fn: s.TestStatus}, - {Name: "TestStatus66", Fn: s.TestStatus66}, + {Name: "TestStatus67", Fn: s.TestStatus67}, // get block headers {Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders}, - {Name: "TestGetBlockHeaders66", Fn: s.TestGetBlockHeaders66}, - {Name: "TestSimultaneousRequests66", Fn: s.TestSimultaneousRequests66}, - {Name: "TestSameRequestID66", Fn: s.TestSameRequestID66}, - {Name: "TestZeroRequestID66", Fn: s.TestZeroRequestID66}, + {Name: "TestGetBlockHeaders67", Fn: s.TestGetBlockHeaders67}, + {Name: "TestSimultaneousRequests67", Fn: s.TestSimultaneousRequests67}, + {Name: "TestSameRequestID67", Fn: s.TestSameRequestID67}, + {Name: "TestZeroRequestID67", Fn: s.TestZeroRequestID67}, // get block bodies {Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies}, - {Name: "TestGetBlockBodies66", Fn: s.TestGetBlockBodies66}, + {Name: "TestGetBlockBodies67", Fn: s.TestGetBlockBodies67}, // broadcast {Name: "TestBroadcast", Fn: s.TestBroadcast}, - {Name: "TestBroadcast66", Fn: s.TestBroadcast66}, + {Name: "TestBroadcast67", Fn: s.TestBroadcast67}, {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce}, - {Name: "TestLargeAnnounce66", Fn: s.TestLargeAnnounce66}, + {Name: "TestLargeAnnounce67", Fn: s.TestLargeAnnounce67}, {Name: "TestOldAnnounce", Fn: s.TestOldAnnounce}, - {Name: "TestOldAnnounce66", Fn: s.TestOldAnnounce66}, + {Name: "TestOldAnnounce67", Fn: s.TestOldAnnounce67}, {Name: "TestBlockHashAnnounce", Fn: s.TestBlockHashAnnounce}, - {Name: "TestBlockHashAnnounce66", Fn: s.TestBlockHashAnnounce66}, + {Name: "TestBlockHashAnnounce67", Fn: s.TestBlockHashAnnounce67}, // malicious handshakes + status {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, - {Name: "TestMaliciousHandshake66", Fn: s.TestMaliciousHandshake66}, - {Name: "TestMaliciousStatus66", Fn: s.TestMaliciousStatus66}, + {Name: "TestMaliciousHandshake67", Fn: s.TestMaliciousHandshake67}, + {Name: "TestMaliciousStatus67", Fn: s.TestMaliciousStatus67}, // test transactions {Name: "TestTransaction", Fn: s.TestTransaction}, - {Name: "TestTransaction66", Fn: s.TestTransaction66}, + {Name: "TestTransaction67", Fn: s.TestTransaction67}, {Name: "TestMaliciousTx", Fn: s.TestMaliciousTx}, - {Name: "TestMaliciousTx66", Fn: s.TestMaliciousTx66}, - {Name: "TestLargeTxRequest66", Fn: s.TestLargeTxRequest66}, - {Name: "TestNewPooledTxs66", Fn: s.TestNewPooledTxs66}, + {Name: "TestMaliciousTx67", Fn: s.TestMaliciousTx67}, + {Name: "TestLargeTxRequest67", Fn: s.TestLargeTxRequest67}, + {Name: "TestNewPooledTxs67", Fn: s.TestNewPooledTxs67}, } }   @@ -103,31 +103,31 @@ {Name: "TestMaliciousTx", Fn: s.TestMaliciousTx}, } }   -func (s *Suite) Eth66Tests() []utesting.Test { +func (s *Suite) Eth67Tests() []utesting.Test { return []utesting.Test{ - // only proceed with eth66 test suite if node supports eth 66 protocol - {Name: "TestStatus66", Fn: s.TestStatus66}, - {Name: "TestGetBlockHeaders66", Fn: s.TestGetBlockHeaders66}, - {Name: "TestSimultaneousRequests66", Fn: s.TestSimultaneousRequests66}, - {Name: "TestSameRequestID66", Fn: s.TestSameRequestID66}, - {Name: "TestZeroRequestID66", Fn: s.TestZeroRequestID66}, - {Name: "TestGetBlockBodies66", Fn: s.TestGetBlockBodies66}, - {Name: "TestBroadcast66", Fn: s.TestBroadcast66}, - {Name: "TestLargeAnnounce66", Fn: s.TestLargeAnnounce66}, - {Name: "TestOldAnnounce66", Fn: s.TestOldAnnounce66}, - {Name: "TestBlockHashAnnounce66", Fn: s.TestBlockHashAnnounce66}, - {Name: "TestMaliciousHandshake66", Fn: s.TestMaliciousHandshake66}, - {Name: "TestMaliciousStatus66", Fn: s.TestMaliciousStatus66}, - {Name: "TestTransaction66", Fn: s.TestTransaction66}, - {Name: "TestMaliciousTx66", Fn: s.TestMaliciousTx66}, - {Name: "TestLargeTxRequest66", Fn: s.TestLargeTxRequest66}, - {Name: "TestNewPooledTxs66", Fn: s.TestNewPooledTxs66}, + // only proceed with celo67 (eth66) test suite if node supports celo 67 protocol + {Name: "TestStatus67", Fn: s.TestStatus67}, + {Name: "TestGetBlockHeaders67", Fn: s.TestGetBlockHeaders67}, + {Name: "TestSimultaneousRequests67", Fn: s.TestSimultaneousRequests67}, + {Name: "TestSameRequestID67", Fn: s.TestSameRequestID67}, + {Name: "TestZeroRequestID67", Fn: s.TestZeroRequestID67}, + {Name: "TestGetBlockBodies67", Fn: s.TestGetBlockBodies67}, + {Name: "TestBroadcast67", Fn: s.TestBroadcast67}, + {Name: "TestLargeAnnounce67", Fn: s.TestLargeAnnounce67}, + {Name: "TestOldAnnounce67", Fn: s.TestOldAnnounce67}, + {Name: "TestBlockHashAnnounce67", Fn: s.TestBlockHashAnnounce67}, + {Name: "TestMaliciousHandshake67", Fn: s.TestMaliciousHandshake67}, + {Name: "TestMaliciousStatus67", Fn: s.TestMaliciousStatus67}, + {Name: "TestTransaction67", Fn: s.TestTransaction67}, + {Name: "TestMaliciousTx67", Fn: s.TestMaliciousTx67}, + {Name: "TestLargeTxRequest67", Fn: s.TestLargeTxRequest67}, + {Name: "TestNewPooledTxs67", Fn: s.TestNewPooledTxs67}, } }   var ( - eth66 = true // indicates whether suite should negotiate eth66 connection - eth65 = false // indicates whether suite should negotiate eth65 connection or below. + celo67 = true // indicates whether suite should negotiate celo67 (eth66) connection + celo66 = false // indicates whether suite should negotiate celo66 (eth65) connection or below. )   // TestStatus attempts to connect to the given node and exchange @@ -143,10 +143,10 @@ t.Fatalf("peering failed: %v", err) } }   -// TestStatus66 attempts to connect to the given node and exchange -// a status message with it on the eth66 protocol. -func (s *Suite) TestStatus66(t *utesting.T) { - conn, err := s.dial66() +// TestStatus67 attempts to connect to the given node and exchange +// a status message with it on the celo67 (eth66) protocol. +func (s *Suite) TestStatus67(t *utesting.T) { + conn, err := s.dial67() if err != nil { t.Fatalf("dial failed: %v", err) } @@ -176,7 +176,7 @@ Amount: 2, Skip: 1, Reverse: false, } - headers, err := conn.headersRequest(req, s.chain, eth65, 0) + headers, err := conn.headersRequest(req, s.chain, celo66, 0) if err != nil { t.Fatalf("GetBlockHeaders request failed: %v", err) } @@ -190,10 +190,10 @@ t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers) } }   -// TestGetBlockHeaders66 tests whether the given node can respond to -// an eth66 `GetBlockHeaders` request and that the response is accurate. -func (s *Suite) TestGetBlockHeaders66(t *utesting.T) { - conn, err := s.dial66() +// TestGetBlockHeaders67 tests whether the given node can respond to +// an celo67 (eth66) `GetBlockHeaders` request and that the response is accurate. +func (s *Suite) TestGetBlockHeaders67(t *utesting.T) { + conn, err := s.dial67() if err != nil { t.Fatalf("dial failed: %v", err) } @@ -210,7 +210,7 @@ Amount: 2, Skip: 1, Reverse: false, } - headers, err := conn.headersRequest(req, s.chain, eth66, 33) + headers, err := conn.headersRequest(req, s.chain, celo67, 33) if err != nil { t.Fatalf("could not get block headers: %v", err) } @@ -224,12 +224,12 @@ t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers) } }   -// TestSimultaneousRequests66 sends two simultaneous `GetBlockHeader` requests from +// TestSimultaneousRequests67 sends two simultaneous `GetBlockHeader` requests from // the same connection with different request IDs and checks to make sure the node // responds with the correct headers per request. -func (s *Suite) TestSimultaneousRequests66(t *utesting.T) { +func (s *Suite) TestSimultaneousRequests67(t *utesting.T) { // create a connection - conn, err := s.dial66() + conn, err := s.dial67() if err != nil { t.Fatalf("dial failed: %v", err) } @@ -238,7 +238,7 @@ if err := conn.peer(s.chain, nil); err != nil { t.Fatalf("peering failed: %v", err) } // create two requests - req1 := &eth.GetBlockHeadersPacket66{ + req1 := &eth.GetBlockHeadersPacket67{ RequestId: uint64(111), GetBlockHeadersPacket: &eth.GetBlockHeadersPacket{ Origin: eth.HashOrNumber{ @@ -249,7 +249,7 @@ Skip: 1, Reverse: false, }, } - req2 := &eth.GetBlockHeadersPacket66{ + req2 := &eth.GetBlockHeadersPacket67{ RequestId: uint64(222), GetBlockHeadersPacket: &eth.GetBlockHeadersPacket{ Origin: eth.HashOrNumber{ @@ -261,11 +261,11 @@ Reverse: false, }, } // write the first request - if err := conn.Write66(req1, GetBlockHeaders{}.Code()); err != nil { + if err := conn.Write67(req1, GetBlockHeaders{}.Code()); err != nil { t.Fatalf("failed to write to connection: %v", err) } // write the second request - if err := conn.Write66(req2, GetBlockHeaders{}.Code()); err != nil { + if err := conn.Write67(req2, GetBlockHeaders{}.Code()); err != nil { t.Fatalf("failed to write to connection: %v", err) } // wait for responses @@ -296,10 +296,10 @@ t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected2, headers2) } }   -// TestSameRequestID66 sends two requests with the same request ID to a +// TestSameRequestID_67 sends two requests with the same request ID to a // single node. -func (s *Suite) TestSameRequestID66(t *utesting.T) { - conn, err := s.dial66() +func (s *Suite) TestSameRequestID67(t *utesting.T) { + conn, err := s.dial67() if err != nil { t.Fatalf("dial failed: %v", err) } @@ -309,7 +309,7 @@ t.Fatalf("peering failed: %v", err) } // create requests reqID := uint64(1234) - request1 := &eth.GetBlockHeadersPacket66{ + request1 := &eth.GetBlockHeadersPacket67{ RequestId: reqID, GetBlockHeadersPacket: &eth.GetBlockHeadersPacket{ Origin: eth.HashOrNumber{ @@ -318,7 +318,7 @@ }, Amount: 2, }, } - request2 := &eth.GetBlockHeadersPacket66{ + request2 := &eth.GetBlockHeadersPacket67{ RequestId: reqID, GetBlockHeadersPacket: &eth.GetBlockHeadersPacket{ Origin: eth.HashOrNumber{ @@ -328,10 +328,10 @@ Amount: 2, }, } // write the requests - if err = conn.Write66(request1, GetBlockHeaders{}.Code()); err != nil { + if err = conn.Write67(request1, GetBlockHeaders{}.Code()); err != nil { t.Fatalf("failed to write to connection: %v", err) } - if err = conn.Write66(request2, GetBlockHeaders{}.Code()); err != nil { + if err = conn.Write67(request2, GetBlockHeaders{}.Code()); err != nil { t.Fatalf("failed to write to connection: %v", err) } // wait for responses @@ -362,10 +362,10 @@ t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected2, headers2) } }   -// TestZeroRequestID_66 checks that a message with a request ID of zero is still handled +// TestZeroRequestID_67 checks that a message with a request ID of zero is still handled // by the node. -func (s *Suite) TestZeroRequestID66(t *utesting.T) { - conn, err := s.dial66() +func (s *Suite) TestZeroRequestID67(t *utesting.T) { + conn, err := s.dial67() if err != nil { t.Fatalf("dial failed: %v", err) } @@ -379,7 +379,7 @@ Number: 0, }, Amount: 2, } - headers, err := conn.headersRequest(req, s.chain, eth66, 0) + headers, err := conn.headersRequest(req, s.chain, celo67, 0) if err != nil { t.Fatalf("failed to get block headers: %v", err) } @@ -424,11 +424,11 @@ t.Fatalf("unexpected: %s", pretty.Sdump(msg)) } }   -// TestGetBlockBodies66 tests whether the given node can respond to +// TestGetBlockBodies67 tests whether the given node can respond to // a `GetBlockBodies` request and that the response is accurate over -// the eth66 protocol. -func (s *Suite) TestGetBlockBodies66(t *utesting.T) { - conn, err := s.dial66() +// the celo67 (eth66) protocol. +func (s *Suite) TestGetBlockBodies67(t *utesting.T) { + conn, err := s.dial67() if err != nil { t.Fatalf("dial failed: %v", err) } @@ -437,14 +437,14 @@ if err := conn.peer(s.chain, nil); err != nil { t.Fatalf("peering failed: %v", err) } // create block bodies request - req := &eth.GetBlockBodiesPacket66{ + req := &eth.GetBlockBodiesPacket67{ RequestId: uint64(55), GetBlockBodiesPacket: eth.GetBlockBodiesPacket{ s.chain.blocks[54].Hash(), s.chain.blocks[75].Hash(), }, } - if err := conn.Write66(req, GetBlockBodies{}.Code()); err != nil { + if err := conn.Write67(req, GetBlockBodies{}.Code()); err != nil { t.Fatalf("could not write to connection: %v", err) } // wait for block bodies response @@ -463,15 +463,15 @@ // TestBroadcast tests whether a block announcement is correctly // propagated to the given node's peer(s). func (s *Suite) TestBroadcast(t *utesting.T) { - if err := s.sendNextBlock(eth65); err != nil { + if err := s.sendNextBlock(celo66); err != nil { t.Fatalf("block broadcast failed: %v", err) } }   -// TestBroadcast66 tests whether a block announcement is correctly -// propagated to the given node's peer(s) on the eth66 protocol. -func (s *Suite) TestBroadcast66(t *utesting.T) { - if err := s.sendNextBlock(eth66); err != nil { +// TestBroadcast67 tests whether a block announcement is correctly +// propagated to the given node's peer(s) on the celo67 (eth66) protocol. +func (s *Suite) TestBroadcast67(t *utesting.T) { + if err := s.sendNextBlock(celo67); err != nil { t.Fatalf("block broadcast failed: %v", err) } } @@ -517,14 +517,14 @@ } conn.Close() } // Test the last block as a valid block - if err := s.sendNextBlock(eth65); err != nil { + if err := s.sendNextBlock(celo66); err != nil { t.Fatalf("failed to broadcast next block: %v", err) } }   -// TestLargeAnnounce66 tests the announcement mechanism with a large -// block over the eth66 protocol. -func (s *Suite) TestLargeAnnounce66(t *utesting.T) { +// TestLargeAnnounce67 tests the announcement mechanism with a large +// block over the celo67 (eth66) protocol. +func (s *Suite) TestLargeAnnounce67(t *utesting.T) { nextBlock := len(s.chain.blocks) blocks := []*NewBlock{ { @@ -543,7 +543,7 @@ }   for i, blockAnnouncement := range blocks[0:3] { t.Logf("Testing malicious announcement: %v\n", i) - conn, err := s.dial66() + conn, err := s.dial67() if err != nil { t.Fatalf("dial failed: %v", err) } @@ -564,22 +564,22 @@ } conn.Close() } // Test the last block as a valid block - if err := s.sendNextBlock(eth66); err != nil { + if err := s.sendNextBlock(celo67); err != nil { t.Fatalf("failed to broadcast next block: %v", err) } }   // TestOldAnnounce tests the announcement mechanism with an old block. func (s *Suite) TestOldAnnounce(t *utesting.T) { - if err := s.oldAnnounce(eth65); err != nil { + if err := s.oldAnnounce(celo66); err != nil { t.Fatal(err) } }   -// TestOldAnnounce66 tests the announcement mechanism with an old block, -// over the eth66 protocol. -func (s *Suite) TestOldAnnounce66(t *utesting.T) { - if err := s.oldAnnounce(eth66); err != nil { +// TestOldAnnounce67 tests the announcement mechanism with an old block, +// over the celo67 (eth66) protocol. +func (s *Suite) TestOldAnnounce67(t *utesting.T) { + if err := s.oldAnnounce(celo67); err != nil { t.Fatal(err) } } @@ -587,29 +587,29 @@ // TestBlockHashAnnounce sends a new block hash announcement and expects // the node to perform a `GetBlockHeaders` request. func (s *Suite) TestBlockHashAnnounce(t *utesting.T) { - if err := s.hashAnnounce(eth65); err != nil { + if err := s.hashAnnounce(celo66); err != nil { t.Fatalf("block hash announcement failed: %v", err) } }   -// TestBlockHashAnnounce66 sends a new block hash announcement and expects +// TestBlockHashAnnounce67 sends a new block hash announcement and expects // the node to perform a `GetBlockHeaders` request. -func (s *Suite) TestBlockHashAnnounce66(t *utesting.T) { - if err := s.hashAnnounce(eth66); err != nil { +func (s *Suite) TestBlockHashAnnounce67(t *utesting.T) { + if err := s.hashAnnounce(celo67); err != nil { t.Fatalf("block hash announcement failed: %v", err) } }   // TestMaliciousHandshake tries to send malicious data during the handshake. func (s *Suite) TestMaliciousHandshake(t *utesting.T) { - if err := s.maliciousHandshakes(t, eth65); err != nil { + if err := s.maliciousHandshakes(t, celo66); err != nil { t.Fatal(err) } }   -// TestMaliciousHandshake66 tries to send malicious data during the handshake. -func (s *Suite) TestMaliciousHandshake66(t *utesting.T) { - if err := s.maliciousHandshakes(t, eth66); err != nil { +// TestMaliciousHandshake67 tries to send malicious data during the handshake. +func (s *Suite) TestMaliciousHandshake67(t *utesting.T) { + if err := s.maliciousHandshakes(t, celo67); err != nil { t.Fatal(err) } } @@ -627,10 +627,10 @@ t.Fatal(err) } }   -// TestMaliciousStatus66 sends a status package with a large total -// difficulty over the eth66 protocol. -func (s *Suite) TestMaliciousStatus66(t *utesting.T) { - conn, err := s.dial66() +// TestMaliciousStatus67 sends a status package with a large total +// difficulty over the celo67 (eth66) protocol. +func (s *Suite) TestMaliciousStatus67(t *utesting.T) { + conn, err := s.dial67() if err != nil { t.Fatalf("dial failed: %v", err) } @@ -644,15 +644,15 @@ // TestTransaction sends a valid transaction to the node and // checks if the transaction gets propagated. func (s *Suite) TestTransaction(t *utesting.T) { - if err := s.sendSuccessfulTxs(t, eth65); err != nil { + if err := s.sendSuccessfulTxs(t, celo66); err != nil { t.Fatal(err) } }   -// TestTransaction66 sends a valid transaction to the node and +// TestTransaction67 sends a valid transaction to the node and // checks if the transaction gets propagated. -func (s *Suite) TestTransaction66(t *utesting.T) { - if err := s.sendSuccessfulTxs(t, eth66); err != nil { +func (s *Suite) TestTransaction67(t *utesting.T) { + if err := s.sendSuccessfulTxs(t, celo67); err != nil { t.Fatal(err) } } @@ -660,25 +660,25 @@ // TestMaliciousTx sends several invalid transactions and tests whether // the node will propagate them. func (s *Suite) TestMaliciousTx(t *utesting.T) { - if err := s.sendMaliciousTxs(t, eth65); err != nil { + if err := s.sendMaliciousTxs(t, celo66); err != nil { t.Fatal(err) } }   -// TestMaliciousTx66 sends several invalid transactions and tests whether +// TestMaliciousTx67 sends several invalid transactions and tests whether // the node will propagate them. -func (s *Suite) TestMaliciousTx66(t *utesting.T) { - if err := s.sendMaliciousTxs(t, eth66); err != nil { +func (s *Suite) TestMaliciousTx67(t *utesting.T) { + if err := s.sendMaliciousTxs(t, celo67); err != nil { t.Fatal(err) } }   -// TestLargeTxRequest66 tests whether a node can fulfill a large GetPooledTransactions +// TestLargeTxRequest67 tests whether a node can fulfill a large GetPooledTransactions // request. -func (s *Suite) TestLargeTxRequest66(t *utesting.T) { +func (s *Suite) TestLargeTxRequest67(t *utesting.T) { // send the next block to ensure the node is no longer syncing and // is able to accept txs - if err := s.sendNextBlock(eth66); err != nil { + if err := s.sendNextBlock(celo67); err != nil { t.Fatalf("failed to send next block: %v", err) } // send 2000 transactions to the node @@ -691,7 +691,7 @@ t.Fatalf("failed to send multiple txs: %v", err) } // set up connection to receive to ensure node is peered with the receiving connection // before tx request is sent - conn, err := s.dial66() + conn, err := s.dial67() if err != nil { t.Fatalf("dial failed: %v", err) } @@ -704,11 +704,11 @@ hashes := make([]common.Hash, 0) for _, hash := range hashMap { hashes = append(hashes, hash) } - getTxReq := &eth.GetPooledTransactionsPacket66{ + getTxReq := &eth.GetPooledTransactionsPacket67{ RequestId: 1234, GetPooledTransactionsPacket: hashes, } - if err = conn.Write66(getTxReq, GetPooledTransactions{}.Code()); err != nil { + if err = conn.Write67(getTxReq, GetPooledTransactions{}.Code()); err != nil { t.Fatalf("could not write to conn: %v", err) } // check that all received transactions match those that were sent to node @@ -724,12 +724,12 @@ t.Fatalf("unexpected %s", pretty.Sdump(msg)) } }   -// TestNewPooledTxs_66 tests whether a node will do a GetPooledTransactions +// TestNewPooledTxs67 tests whether a node will do a GetPooledTransactions // request upon receiving a NewPooledTransactionHashes announcement. -func (s *Suite) TestNewPooledTxs66(t *utesting.T) { +func (s *Suite) TestNewPooledTxs67(t *utesting.T) { // send the next block to ensure the node is no longer syncing and // is able to accept txs - if err := s.sendNextBlock(eth66); err != nil { + if err := s.sendNextBlock(celo67); err != nil { t.Fatalf("failed to send next block: %v", err) }   @@ -747,7 +747,7 @@ } announce := NewPooledTransactionHashes(hashes)   // send announcement - conn, err := s.dial66() + conn, err := s.dial67() if err != nil { t.Fatalf("dial failed: %v", err) } @@ -761,7 +761,7 @@ }   // wait for GetPooledTxs request for { - _, msg := conn.readAndServe66(s.chain, timeout) + _, msg := conn.readAndServe67(s.chain, timeout) switch msg := msg.(type) { case GetPooledTransactions: if len(msg) != len(hashes) {
diff --git go-ethereum/cmd/devp2p/internal/v5test/framework.go celo/cmd/devp2p/internal/v5test/framework.go index 8f5e4b548aadd2f099f16fe1e37941d2a61aac79..0cde88888532dbdcf6f2d0f785c38b33be166d92 100644 --- go-ethereum/cmd/devp2p/internal/v5test/framework.go +++ celo/cmd/devp2p/internal/v5test/framework.go @@ -81,7 +81,7 @@ db, err := enode.OpenDB("") if err != nil { panic(err) } - ln := enode.NewLocalNode(db, key) + ln := enode.NewLocalNode(db, key, 1)   return &conn{ localKey: key,
diff --git go-ethereum/cmd/devp2p/internal/ethtest/chain_test.go celo/cmd/devp2p/internal/ethtest/chain_test.go index a67d6ce58983275d604b5744368ddc4f8f0a958c..ccec19b57104ee431b584239866cec94801621cd 100644 --- go-ethereum/cmd/devp2p/internal/ethtest/chain_test.go +++ celo/cmd/devp2p/internal/ethtest/chain_test.go @@ -39,9 +39,9 @@ conn: &Conn{ ourHighestProtoVersion: 65, }, caps: []p2p.Cap{ - {Name: "eth", Version: 63}, - {Name: "eth", Version: 64}, - {Name: "eth", Version: 65}, + {Name: "istanbul", Version: 64}, + {Name: "istanbul", Version: 65}, + {Name: "istanbul", Version: 66}, }, expected: uint32(65), }, @@ -50,53 +50,53 @@ conn: &Conn{ ourHighestProtoVersion: 65, }, caps: []p2p.Cap{ - {Name: "eth", Version: 63}, - {Name: "eth", Version: 64}, - {Name: "eth", Version: 65}, + {Name: "istanbul", Version: 64}, + {Name: "istanbul", Version: 65}, + {Name: "istanbul", Version: 66}, }, expected: uint32(65), }, { conn: &Conn{ - ourHighestProtoVersion: 65, + ourHighestProtoVersion: 66, }, caps: []p2p.Cap{ - {Name: "eth", Version: 63}, - {Name: "eth", Version: 64}, - {Name: "eth", Version: 65}, + {Name: "istanbul", Version: 64}, + {Name: "istanbul", Version: 65}, + {Name: "istanbul", Version: 66}, }, - expected: uint32(65), + expected: uint32(66), }, { conn: &Conn{ ourHighestProtoVersion: 64, }, caps: []p2p.Cap{ - {Name: "eth", Version: 63}, - {Name: "eth", Version: 64}, - {Name: "eth", Version: 65}, + {Name: "istanbul", Version: 64}, + {Name: "istanbul", Version: 65}, + {Name: "istanbul", Version: 66}, }, expected: 64, }, { conn: &Conn{ - ourHighestProtoVersion: 65, + ourHighestProtoVersion: 66, }, caps: []p2p.Cap{ - {Name: "eth", Version: 0}, - {Name: "eth", Version: 89}, - {Name: "eth", Version: 65}, + {Name: "istanbul", Version: 0}, + {Name: "istanbul", Version: 89}, + {Name: "istanbul", Version: 66}, }, - expected: uint32(65), + expected: uint32(66), }, { conn: &Conn{ ourHighestProtoVersion: 64, }, caps: []p2p.Cap{ - {Name: "eth", Version: 63}, - {Name: "eth", Version: 64}, - {Name: "wrongProto", Version: 65}, + {Name: "istanbul", Version: 64}, + {Name: "istanbul", Version: 65}, + {Name: "wrongProto", Version: 66}, }, expected: uint32(64), }, @@ -105,11 +105,11 @@ conn: &Conn{ ourHighestProtoVersion: 65, }, caps: []p2p.Cap{ - {Name: "eth", Version: 63}, - {Name: "eth", Version: 64}, - {Name: "wrongProto", Version: 65}, + {Name: "istanbul", Version: 64}, + {Name: "istanbul", Version: 65}, + {Name: "wrongProto", Version: 66}, }, - expected: uint32(64), + expected: uint32(65), }, }
diff --git go-ethereum/cmd/devp2p/internal/ethtest/types.go celo/cmd/devp2p/internal/ethtest/types.go index c0a4b563e51c441948e0af84f9f96abe6948b847..fefb1447f91b620d0afaaacadd7c4ab04b68041a 100644 --- go-ethereum/cmd/devp2p/internal/ethtest/types.go +++ celo/cmd/devp2p/internal/ethtest/types.go @@ -181,8 +181,8 @@ } return msg }   -// Read66 reads an eth66 packet from the connection. -func (c *Conn) Read66() (uint64, Message) { +// Read67 reads an celo67 (eth66) packet from the connection. +func (c *Conn) Read67() (uint64, Message) { code, rawData, _, err := c.Conn.Read() if err != nil { return 0, errorf("could not read from connection: %v", err) @@ -201,25 +201,25 @@ msg = new(Disconnect) case (Status{}).Code(): msg = new(Status) case (GetBlockHeaders{}).Code(): - ethMsg := new(eth.GetBlockHeadersPacket66) + ethMsg := new(eth.GetBlockHeadersPacket67) if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return 0, errorf("could not rlp decode message: %v", err) } return ethMsg.RequestId, GetBlockHeaders(*ethMsg.GetBlockHeadersPacket) case (BlockHeaders{}).Code(): - ethMsg := new(eth.BlockHeadersPacket66) + ethMsg := new(eth.BlockHeadersPacket67) if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return 0, errorf("could not rlp decode message: %v", err) } return ethMsg.RequestId, BlockHeaders(ethMsg.BlockHeadersPacket) case (GetBlockBodies{}).Code(): - ethMsg := new(eth.GetBlockBodiesPacket66) + ethMsg := new(eth.GetBlockBodiesPacket67) if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return 0, errorf("could not rlp decode message: %v", err) } return ethMsg.RequestId, GetBlockBodies(ethMsg.GetBlockBodiesPacket) case (BlockBodies{}).Code(): - ethMsg := new(eth.BlockBodiesPacket66) + ethMsg := new(eth.BlockBodiesPacket67) if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return 0, errorf("could not rlp decode message: %v", err) } @@ -233,13 +233,13 @@ msg = new(Transactions) case (NewPooledTransactionHashes{}).Code(): msg = new(NewPooledTransactionHashes) case (GetPooledTransactions{}.Code()): - ethMsg := new(eth.GetPooledTransactionsPacket66) + ethMsg := new(eth.GetPooledTransactionsPacket67) if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return 0, errorf("could not rlp decode message: %v", err) } return ethMsg.RequestId, GetPooledTransactions(ethMsg.GetPooledTransactionsPacket) case (PooledTransactions{}.Code()): - ethMsg := new(eth.PooledTransactionsPacket66) + ethMsg := new(eth.PooledTransactionsPacket67) if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return 0, errorf("could not rlp decode message: %v", err) } @@ -272,8 +272,8 @@ _, err = c.Conn.Write(uint64(msg.Code()), payload) return err }   -// Write66 writes an eth66 packet to the connection. -func (c *Conn) Write66(req eth.Packet, code int) error { +// Write67 writes an celo67 (eth66) packet to the connection. +func (c *Conn) Write67(req eth.Packet, code int) error { payload, err := rlp.EncodeToBytes(req) if err != nil { return err
diff --git go-ethereum/cmd/devp2p/internal/ethtest/helpers.go celo/cmd/devp2p/internal/ethtest/helpers.go index 45095494f9c868ac92794182aed31e08d4ea64fb..f3d9033e75dac8eb80975f2b0c97641155e544bf 100644 --- go-ethereum/cmd/devp2p/internal/ethtest/helpers.go +++ celo/cmd/devp2p/internal/ethtest/helpers.go @@ -43,17 +43,17 @@ } timeout = 20 * time.Second )   -// Is_66 checks if the node supports the eth66 protocol version, +// Is_67 checks if the node supports the celo67 (eth66) protocol version, // and if not, exists the test suite -func (s *Suite) Is_66(t *utesting.T) { - conn, err := s.dial66() +func (s *Suite) Is_67(t *utesting.T) { + conn, err := s.dial67() if err != nil { t.Fatalf("dial failed: %v", err) } if err := conn.handshake(); err != nil { t.Fatalf("handshake failed: %v", err) } - if conn.negotiatedProtoVersion < 66 { + if conn.negotiatedProtoVersion < 67 { t.Fail() } } @@ -76,23 +76,23 @@ return nil, err } // set default p2p capabilities conn.caps = []p2p.Cap{ - {Name: "eth", Version: 64}, - {Name: "eth", Version: 65}, + {Name: "istanbul", Version: 65}, + {Name: "istanbul", Version: 66}, } - conn.ourHighestProtoVersion = 65 + conn.ourHighestProtoVersion = 66 return &conn, nil }   -// dial66 attempts to dial the given node and perform a handshake, -// returning the created Conn with additional eth66 capabilities if +// dial67 attempts to dial the given node and perform a handshake, +// returning the created Conn with additional celo67 (eth66) capabilities if // successful -func (s *Suite) dial66() (*Conn, error) { +func (s *Suite) dial67() (*Conn, error) { conn, err := s.dial() if err != nil { return nil, fmt.Errorf("dial failed: %v", err) } - conn.caps = append(conn.caps, p2p.Cap{Name: "eth", Version: 66}) - conn.ourHighestProtoVersion = 66 + conn.caps = append(conn.caps, p2p.Cap{Name: "istanbul", Version: 67}) + conn.ourHighestProtoVersion = 67 return conn, nil }   @@ -144,7 +144,7 @@ // advertised capability from peer. func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) { var highestEthVersion uint for _, capability := range caps { - if capability.Name != "eth" { + if capability.Name != "istanbul" { continue } if capability.Version > highestEthVersion && capability.Version <= c.ourHighestProtoVersion { @@ -169,7 +169,7 @@ if have, want := msg.Head, chain.blocks[chain.Len()-1].Hash(); have != want { return nil, fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x", want, chain.blocks[chain.Len()-1].NumberU64(), have) } - if have, want := msg.TD.Cmp(chain.TD()), 0; have != want { + if have, want := msg.TD.Cmp(chain.TD(int(chain.Head().NumberU64()))), 0; have != want { return nil, fmt.Errorf("wrong TD in status: have %v want %v", have, want) } if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) { @@ -198,7 +198,7 @@ // default status message status = &Status{ ProtocolVersion: uint32(c.negotiatedProtoVersion), NetworkID: chain.chainConfig.ChainID.Uint64(), - TD: chain.TD(), + TD: chain.TD(int(chain.Head().NumberU64())), Head: chain.blocks[chain.Len()-1].Hash(), Genesis: chain.blocks[0].Hash(), ForkID: chain.ForkID(), @@ -212,18 +212,18 @@ }   // createSendAndRecvConns creates two connections, one for sending messages to the // node, and one for receiving messages from the node. -func (s *Suite) createSendAndRecvConns(isEth66 bool) (*Conn, *Conn, error) { +func (s *Suite) createSendAndRecvConns(isCelo67 bool) (*Conn, *Conn, error) { var ( sendConn *Conn recvConn *Conn err error ) - if isEth66 { - sendConn, err = s.dial66() + if isCelo67 { + sendConn, err = s.dial67() if err != nil { return nil, nil, fmt.Errorf("dial failed: %v", err) } - recvConn, err = s.dial66() + recvConn, err = s.dial67() if err != nil { sendConn.Close() return nil, nil, fmt.Errorf("dial failed: %v", err) @@ -243,16 +243,16 @@ return sendConn, recvConn, nil }   func (c *Conn) readAndServe(chain *Chain, timeout time.Duration) Message { - if c.negotiatedProtoVersion == 66 { - _, msg := c.readAndServe66(chain, timeout) + if c.negotiatedProtoVersion == 67 { + _, msg := c.readAndServe67(chain, timeout) return msg } - return c.readAndServe65(chain, timeout) + return c.readAndServe66(chain, timeout) }   // readAndServe serves GetBlockHeaders requests while waiting // on another message from the node. -func (c *Conn) readAndServe65(chain *Chain, timeout time.Duration) Message { +func (c *Conn) readAndServe66(chain *Chain, timeout time.Duration) Message { start := time.Now() for time.Since(start) < timeout { c.SetReadDeadline(time.Now().Add(5 * time.Second)) @@ -275,14 +275,14 @@ } return errorf("no message received within %v", timeout) }   -// readAndServe66 serves eth66 GetBlockHeaders requests while waiting +// readAndServe67 serves celo67 (eth66) GetBlockHeaders requests while waiting // on another message from the node. -func (c *Conn) readAndServe66(chain *Chain, timeout time.Duration) (uint64, Message) { +func (c *Conn) readAndServe67(chain *Chain, timeout time.Duration) (uint64, Message) { start := time.Now() for time.Since(start) < timeout { c.SetReadDeadline(time.Now().Add(10 * time.Second))   - reqID, msg := c.Read66() + reqID, msg := c.Read67()   switch msg := msg.(type) { case *Ping: @@ -292,11 +292,11 @@ headers, err := chain.GetHeaders(msg) if err != nil { return 0, errorf("could not get headers for inbound header request: %v", err) } - resp := &eth.BlockHeadersPacket66{ + resp := &eth.BlockHeadersPacket67{ RequestId: reqID, BlockHeadersPacket: eth.BlockHeadersPacket(headers), } - if err := c.Write66(resp, BlockHeaders{}.Code()); err != nil { + if err := c.Write67(resp, BlockHeaders{}.Code()); err != nil { return 0, errorf("could not write to connection: %v", err) } default: @@ -307,12 +307,12 @@ return 0, errorf("no message received within %v", timeout) }   // headersRequest executes the given `GetBlockHeaders` request. -func (c *Conn) headersRequest(request *GetBlockHeaders, chain *Chain, isEth66 bool, reqID uint64) (BlockHeaders, error) { +func (c *Conn) headersRequest(request *GetBlockHeaders, chain *Chain, isCelo67 bool, reqID uint64) (BlockHeaders, error) { defer c.SetReadDeadline(time.Time{}) c.SetReadDeadline(time.Now().Add(20 * time.Second)) - // if on eth66 connection, perform eth66 GetBlockHeaders request - if isEth66 { - return getBlockHeaders66(chain, c, request, reqID) + // if on celo67 (eth66) connection, perform celo67 GetBlockHeaders request + if isCelo67 { + return getBlockHeaders67(chain, c, request, reqID) } if err := c.Write(request); err != nil { return nil, err @@ -325,15 +325,15 @@ return nil, fmt.Errorf("invalid message: %s", pretty.Sdump(msg)) } }   -// getBlockHeaders66 executes the given `GetBlockHeaders` request over the eth66 protocol. -func getBlockHeaders66(chain *Chain, conn *Conn, request *GetBlockHeaders, id uint64) (BlockHeaders, error) { +// getBlockHeaders67 executes the given `GetBlockHeaders` request over the celo67 (eth66) protocol. +func getBlockHeaders67(chain *Chain, conn *Conn, request *GetBlockHeaders, id uint64) (BlockHeaders, error) { // write request packet := eth.GetBlockHeadersPacket(*request) - req := &eth.GetBlockHeadersPacket66{ + req := &eth.GetBlockHeadersPacket67{ RequestId: id, GetBlockHeadersPacket: &packet, } - if err := conn.Write66(req, GetBlockHeaders{}.Code()); err != nil { + if err := conn.Write67(req, GetBlockHeaders{}.Code()); err != nil { return nil, fmt.Errorf("could not write to connection: %v", err) } // wait for response @@ -354,7 +354,7 @@ // waitForResponse reads from the connection until a response with the expected // request ID is received. func (c *Conn) waitForResponse(chain *Chain, timeout time.Duration, requestID uint64) Message { for { - id, msg := c.readAndServe66(chain, timeout) + id, msg := c.readAndServe67(chain, timeout) if id == requestID { return msg } @@ -363,9 +363,9 @@ }   // sendNextBlock broadcasts the next block in the chain and waits // for the node to propagate the block and import it into its chain. -func (s *Suite) sendNextBlock(isEth66 bool) error { +func (s *Suite) sendNextBlock(isCelo67 bool) error { // set up sending and receiving connections - sendConn, recvConn, err := s.createSendAndRecvConns(isEth66) + sendConn, recvConn, err := s.createSendAndRecvConns(isCelo67) if err != nil { return err } @@ -388,7 +388,7 @@ if err = s.testAnnounce(sendConn, recvConn, blockAnnouncement); err != nil { return fmt.Errorf("failed to announce block: %v", err) } // wait for client to update its chain - if err = s.waitForBlockImport(recvConn, nextBlock, isEth66); err != nil { + if err = s.waitForBlockImport(recvConn, nextBlock, isCelo67); err != nil { return fmt.Errorf("failed to receive confirmation of block import: %v", err) } // update test suite chain @@ -433,7 +433,7 @@ } } }   -func (s *Suite) waitForBlockImport(conn *Conn, block *types.Block, isEth66 bool) error { +func (s *Suite) waitForBlockImport(conn *Conn, block *types.Block, isCelo67 bool) error { defer conn.SetReadDeadline(time.Time{}) conn.SetReadDeadline(time.Now().Add(20 * time.Second)) // create request @@ -450,11 +450,11 @@ var ( headers BlockHeaders err error ) - if isEth66 { + if isCelo67 { requestID := uint64(54) - headers, err = conn.headersRequest(req, s.chain, eth66, requestID) + headers, err = conn.headersRequest(req, s.chain, celo67, requestID) } else { - headers, err = conn.headersRequest(req, s.chain, eth65, 0) + headers, err = conn.headersRequest(req, s.chain, celo66, 0) } if err != nil { return fmt.Errorf("GetBlockHeader request failed: %v", err) @@ -471,8 +471,8 @@ return nil } }   -func (s *Suite) oldAnnounce(isEth66 bool) error { - sendConn, receiveConn, err := s.createSendAndRecvConns(isEth66) +func (s *Suite) oldAnnounce(isCelo67 bool) error { + sendConn, receiveConn, err := s.createSendAndRecvConns(isCelo67) if err != nil { return err } @@ -487,7 +487,7 @@ } // create old block announcement oldBlockAnnounce := &NewBlock{ Block: s.chain.blocks[len(s.chain.blocks)/2], - TD: s.chain.blocks[len(s.chain.blocks)/2].Difficulty(), + TD: s.chain.blocks[len(s.chain.blocks)/2].Number(), } if err := sendConn.Write(oldBlockAnnounce); err != nil { return fmt.Errorf("could not write to connection: %v", err) @@ -518,13 +518,13 @@ } return nil }   -func (s *Suite) maliciousHandshakes(t *utesting.T, isEth66 bool) error { +func (s *Suite) maliciousHandshakes(t *utesting.T, isCelo67 bool) error { var ( conn *Conn err error ) - if isEth66 { - conn, err = s.dial66() + if isCelo67 { + conn, err = s.dial67() if err != nil { return fmt.Errorf("dial failed: %v", err) } @@ -541,38 +541,38 @@ handshakes := []*Hello{ { Version: 5, Caps: []p2p.Cap{ - {Name: largeString(2), Version: 64}, + {Name: largeString(2), Version: 66}, }, ID: pub0, }, { Version: 5, Caps: []p2p.Cap{ - {Name: "eth", Version: 64}, - {Name: "eth", Version: 65}, + {Name: "istanbul", Version: 66}, + {Name: "istanbul", Version: 67}, }, ID: append(pub0, byte(0)), }, { Version: 5, Caps: []p2p.Cap{ - {Name: "eth", Version: 64}, - {Name: "eth", Version: 65}, + {Name: "istanbul", Version: 66}, + {Name: "istanbul", Version: 67}, }, ID: append(pub0, pub0...), }, { Version: 5, Caps: []p2p.Cap{ - {Name: "eth", Version: 64}, - {Name: "eth", Version: 65}, + {Name: "istanbul", Version: 66}, + {Name: "istanbul", Version: 67}, }, ID: largeBuffer(2), }, { Version: 5, Caps: []p2p.Cap{ - {Name: largeString(2), Version: 64}, + {Name: largeString(2), Version: 66}, }, ID: largeBuffer(2), }, @@ -595,8 +595,8 @@ return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) } } // dial for the next round - if isEth66 { - conn, err = s.dial66() + if isCelo67 { + conn, err = s.dial67() if err != nil { return fmt.Errorf("dial failed: %v", err) } @@ -643,9 +643,9 @@ return fmt.Errorf("expected disconnect, got: %s", pretty.Sdump(msg)) } }   -func (s *Suite) hashAnnounce(isEth66 bool) error { +func (s *Suite) hashAnnounce(isCelo67 bool) error { // create connections - sendConn, recvConn, err := s.createSendAndRecvConns(isEth66) + sendConn, recvConn, err := s.createSendAndRecvConns(isCelo67) if err != nil { return fmt.Errorf("failed to create connections: %v", err) } @@ -674,8 +674,8 @@ id uint64 msg Message blockHeaderReq GetBlockHeaders ) - if isEth66 { - id, msg = sendConn.Read66() + if isCelo67 { + id, msg = sendConn.Read67() switch msg := msg.(type) { case GetBlockHeaders: blockHeaderReq = msg @@ -690,7 +690,7 @@ return fmt.Errorf("unexpected block header requested. Announced:\n %v\n Remote request:\n%v", pretty.Sdump(announcement), pretty.Sdump(blockHeaderReq)) } - if err := sendConn.Write66(&eth.BlockHeadersPacket66{ + if err := sendConn.Write67(&eth.BlockHeadersPacket67{ RequestId: id, BlockHeadersPacket: eth.BlockHeadersPacket{ nextBlock.Header(), @@ -733,7 +733,7 @@ } case *NewBlock: // node should only propagate NewBlock without having requested the body if the body is empty nextBlockBody := nextBlock.Body() - if len(nextBlockBody.Transactions) != 0 || len(nextBlockBody.Uncles) != 0 { + if len(nextBlockBody.Transactions) != 0 { return fmt.Errorf("unexpected non-empty new block propagated: %s", pretty.Sdump(msg)) } if msg.Block.Hash() != nextBlock.Hash() { @@ -748,7 +748,7 @@ default: return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) } // confirm node imported block - if err := s.waitForBlockImport(recvConn, nextBlock, isEth66); err != nil { + if err := s.waitForBlockImport(recvConn, nextBlock, isCelo67); err != nil { return fmt.Errorf("error waiting for node to import new block: %v", err) } // update the chain
diff --git go-ethereum/cmd/devp2p/internal/ethtest/suite_test.go celo/cmd/devp2p/internal/ethtest/suite_test.go index aae59e2d4fa10a8a018b47d9c1e0f37a50f844d1..0782856dca5997981894c896c5eb984a14f24a19 100644 --- go-ethereum/cmd/devp2p/internal/ethtest/suite_test.go +++ celo/cmd/devp2p/internal/ethtest/suite_test.go @@ -35,6 +35,7 @@ fullchainFile = "./testdata/chain.rlp" )   func TestEthSuite(t *testing.T) { + t.Skip("Incompatible chain file.") geth, err := runGeth() if err != nil { t.Fatalf("could not run geth: %v", err) @@ -45,7 +46,7 @@ suite, err := NewSuite(geth.Server().Self(), fullchainFile, genesisFile) if err != nil { t.Fatalf("could not create new test suite: %v", err) } - for _, test := range suite.Eth66Tests() { + for _, test := range suite.Eth67Tests() { t.Run(test.Name, func(t *testing.T) { result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout) if result[0].Failed {
diff --git go-ethereum/cmd/devp2p/internal/ethtest/large.go celo/cmd/devp2p/internal/ethtest/large.go index 2af47a937c62136df6d4522b740c009bfa44ac87..e7562214e973270e617b08aac8fe03f16aadf022 100644 --- go-ethereum/cmd/devp2p/internal/ethtest/large.go +++ celo/cmd/devp2p/internal/ethtest/large.go @@ -61,20 +61,15 @@ }   func largeHeader() *types.Header { return &types.Header{ - MixDigest: randHash(), ReceiptHash: randHash(), TxHash: randHash(), - Nonce: types.BlockNonce{}, Extra: []byte{}, Bloom: types.Bloom{}, GasUsed: 0, Coinbase: common.Address{}, - GasLimit: 0, - UncleHash: types.EmptyUncleHash, Time: 1337, ParentHash: randHash(), Root: randHash(), Number: largeNumber(2), - Difficulty: largeNumber(2), } }
diff --git go-ethereum/cmd/devp2p/internal/ethtest/transaction.go celo/cmd/devp2p/internal/ethtest/transaction.go index 546d3692b2625b14c3b80ef0c79556d68f5a3a55..585a612a9b751812ffdbddb04ae0d911b262bc3c 100644 --- go-ethereum/cmd/devp2p/internal/ethtest/transaction.go +++ celo/cmd/devp2p/internal/ethtest/transaction.go @@ -32,7 +32,7 @@ //var faucetAddr = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") var faucetKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")   -func (s *Suite) sendSuccessfulTxs(t *utesting.T, isEth66 bool) error { +func (s *Suite) sendSuccessfulTxs(t *utesting.T, isCelo67 bool) error { tests := []*types.Transaction{ getNextTxFromChain(s), unknownTx(s), @@ -48,15 +48,15 @@ if i != 0 { prevTx = tests[i-1] } // write tx to connection - if err := sendSuccessfulTx(s, tx, prevTx, isEth66); err != nil { + if err := sendSuccessfulTx(s, tx, prevTx, isCelo67); err != nil { return fmt.Errorf("send successful tx test failed: %v", err) } } return nil }   -func sendSuccessfulTx(s *Suite, tx *types.Transaction, prevTx *types.Transaction, isEth66 bool) error { - sendConn, recvConn, err := s.createSendAndRecvConns(isEth66) +func sendSuccessfulTx(s *Suite, tx *types.Transaction, prevTx *types.Transaction, isCelo67 bool) error { + sendConn, recvConn, err := s.createSendAndRecvConns(isCelo67) if err != nil { return err } @@ -114,7 +114,7 @@ } } }   -func (s *Suite) sendMaliciousTxs(t *utesting.T, isEth66 bool) error { +func (s *Suite) sendMaliciousTxs(t *utesting.T, isCelo67 bool) error { badTxs := []*types.Transaction{ getOldTxFromChain(s), invalidNonceTx(s), @@ -127,8 +127,8 @@ var ( recvConn *Conn err error ) - if isEth66 { - recvConn, err = s.dial66() + if isCelo67 { + recvConn, err = s.dial67() } else { recvConn, err = s.dial() } @@ -141,7 +141,7 @@ return fmt.Errorf("peering failed: %v", err) } for i, tx := range badTxs { t.Logf("Testing malicious tx propagation: %v\n", i) - if err = sendMaliciousTx(s, tx, isEth66); err != nil { + if err = sendMaliciousTx(s, tx, isCelo67); err != nil { return fmt.Errorf("malicious tx test failed:\ntx: %v\nerror: %v", tx, err) } } @@ -149,14 +149,14 @@ // check to make sure bad txs aren't propagated return checkMaliciousTxPropagation(s, badTxs, recvConn) }   -func sendMaliciousTx(s *Suite, tx *types.Transaction, isEth66 bool) error { +func sendMaliciousTx(s *Suite, tx *types.Transaction, isCelo67 bool) error { // setup connection var ( conn *Conn err error ) - if isEth66 { - conn, err = s.dial66() + if isCelo67 { + conn, err = s.dial67() } else { conn, err = s.dial() } @@ -300,7 +300,7 @@ var to common.Address if tx.To() != nil { to = *tx.To() } - txNew := types.NewTransaction(tx.Nonce()+1, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data()) + txNew := types.NewTransaction(tx.Nonce()+1, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.FeeCurrency(), tx.GatewayFeeRecipient(), tx.GatewayFee(), tx.Data()) return signWithFaucet(s.chain.chainConfig, txNew) }   @@ -341,7 +341,7 @@ }   func generateTx(chainConfig *params.ChainConfig, nonce uint64, gas uint64) *types.Transaction { var to common.Address - tx := types.NewTransaction(nonce, to, big.NewInt(1), gas, big.NewInt(1), []byte{}) + tx := types.NewTransaction(nonce, to, big.NewInt(1), gas, big.NewInt(1), nil, nil, nil, []byte{}) return signWithFaucet(chainConfig, tx) }   @@ -364,7 +364,7 @@ var to common.Address if tx.To() != nil { to = *tx.To() } - txNew := types.NewTransaction(tx.Nonce()-2, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data()) + txNew := types.NewTransaction(tx.Nonce()-2, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.FeeCurrency(), tx.GatewayFeeRecipient(), tx.GatewayFee(), tx.Data()) return signWithFaucet(s.chain.chainConfig, txNew) }   @@ -378,7 +378,7 @@ var to common.Address if tx.To() != nil { to = *tx.To() } - txNew := types.NewTransaction(tx.Nonce(), to, amount, tx.Gas(), tx.GasPrice(), tx.Data()) + txNew := types.NewTransaction(tx.Nonce(), to, amount, tx.Gas(), tx.GasPrice(), tx.FeeCurrency(), tx.GatewayFeeRecipient(), tx.GatewayFee(), tx.Data()) return signWithFaucet(s.chain.chainConfig, txNew) }   @@ -392,7 +392,7 @@ var to common.Address if tx.To() != nil { to = *tx.To() } - txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), gasPrice, tx.Data()) + txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), gasPrice, tx.FeeCurrency(), tx.GatewayFeeRecipient(), tx.GatewayFee(), tx.Data()) return signWithFaucet(s.chain.chainConfig, txNew) }   @@ -405,7 +405,7 @@ var to common.Address if tx.To() != nil { to = *tx.To() } - txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), tx.GasPrice(), largeBuffer(2)) + txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.FeeCurrency(), tx.GatewayFeeRecipient(), tx.GatewayFee(), largeBuffer(2)) return signWithFaucet(s.chain.chainConfig, txNew) }
diff --git go-ethereum/cmd/devp2p/internal/ethtest/chain.go celo/cmd/devp2p/internal/ethtest/chain.go index 78a31c5421c722aedaa6425bd6404e5a937ecf07..9ad1f30b4b2c6230083b09003a581ed71bc2e5be 100644 --- go-ethereum/cmd/devp2p/internal/ethtest/chain.go +++ celo/cmd/devp2p/internal/ethtest/chain.go @@ -44,27 +44,16 @@ func (c *Chain) Len() int { return len(c.blocks) }   -// TD calculates the total difficulty of the chain at the -// chain head. -func (c *Chain) TD() *big.Int { - sum := big.NewInt(0) - for _, block := range c.blocks[:c.Len()] { - sum.Add(sum, block.Difficulty()) - } - return sum +// TD calculates the total difficulty of the chain. +func (c *Chain) TD(height int) *big.Int { // TODO later on channge scheme so that the height is included in range + diff := height + 1 + return big.NewInt(int64(diff)) }   // TotalDifficultyAt calculates the total difficulty of the chain // at the given block height. func (c *Chain) TotalDifficultyAt(height int) *big.Int { - sum := big.NewInt(0) - if height >= c.Len() { - return sum - } - for _, block := range c.blocks[:height+1] { - sum.Add(sum, block.Difficulty()) - } - return sum + return c.TD(height) }   // ForkID gets the fork id of the chain.
diff --git go-ethereum/cmd/devp2p/internal/ethtest/testdata/chain.rlp celo/cmd/devp2p/internal/ethtest/testdata/chain.rlp old mode 100644 new mode 100755 index 5ebc2f3bb788825e2c5fc48ecfb85a697f016506..7f26f8cf0097919083a0eb02272cae39ea8d0537 Binary files go-ethereum/cmd/devp2p/internal/ethtest/testdata/chain.rlp and celo/cmd/devp2p/internal/ethtest/testdata/chain.rlp differ
diff --git go-ethereum/cmd/devp2p/internal/ethtest/testdata/genesis.json celo/cmd/devp2p/internal/ethtest/testdata/genesis.json index d8b5d225024ea25f1e4699fb6c5d3f8e41efb461..28c572603dad345867ee5958acccada877ff5b9c 100644 --- go-ethereum/cmd/devp2p/internal/ethtest/testdata/genesis.json +++ celo/cmd/devp2p/internal/ethtest/testdata/genesis.json @@ -1,26 +1,728 @@ { - "config": { - "chainId": 19763, - "homesteadBlock": 0, - "eip150Block": 0, - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, - "ethash": {} + "config": { + "chainId": 19763, + "homesteadBlock": 0, + "eip150Block": 0, + "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "churritoBlock": 0, + "donutBlock": 0, + "istanbul": { + "epoch": 10, + "policy": 2, + "lookbackwindow": 3, + "blockperiod": 1, + "requesttimeout": 3000 + }, + "FullHeaderChainAvailable": false + }, + "timestamp": "0x615dfa21", + "extraData": "0xecc833a7747eaa8327335e8e0c6b6d8aa3a38d0063591e43ce116ccf5c89753ef884d594039eaa4f6c1ff4586873852ef3c1b81a44d2729ef862b86040d2ebd74fafb063047a458d983eb642cb7d97bf73d042e29c5ae217374f0b69c73d67fe5116a88a3e2a2fd22893a600fa79ddde9107cb516184094aa1789840f86658f42ad8960f7d76ac31f47ad794454e5c8dbbc1c5ff5838538bb03868008080c3808080c3808080", + "coinbase": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8", + "alloc": { + "71562b71999873db5b286df957af199ec94617f7": { + "balance": "0xffffffffffffffffffffffffff" + }, + "000000000000000000000000000000000000a001": { + "code": "0x73000000000000000000000000000000000000000030146080604052600080fdfea265627a7a72315820b97b1764741b830d9c30e84fd2a6e99b8fdd1434da0a34ea19eec430999f922964736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000a002": { + "code": "0x73000000000000000000000000000000000000000030146080604052600436106100565760003560e01c80633053123f1461005b578063b05cd27f14610313578063c67e7b4b1461037c578063e6a5192f146103b7575b600080fd5b81801561006757600080fd5b50610311600480360360e081101561007e57600080fd5b8101908080359060200190929190803590602001906401000000008111156100a557600080fd5b8201836020820111156100b757600080fd5b803590602001918460208302840111640100000000831117156100d957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561013957600080fd5b82018360208201111561014b57600080fd5b8035906020019184602083028401116401000000008311171561016d57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156101cd57600080fd5b8201836020820111156101df57600080fd5b8035906020019184600183028401116401000000008311171561020157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561026457600080fd5b82018360208201111561027657600080fd5b8035906020019184602083028401116401000000008311171561029857600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506104a2565b005b81801561031f57600080fd5b5061037a600480360360a081101561033657600080fd5b81019080803590602001909291908035906020019092919080359060200190929190803560ff169060200190929190803560ff169060200190929190505050610711565b005b81801561038857600080fd5b506103b56004803603602081101561039f57600080fd5b81019080803590602001909291905050506108c8565b005b6103ed600480360360408110156103cd57600080fd5b810190808035906020019092919080359060200190929190505050610a2f565b604051808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561046557808201518184015260208101905061044a565b50505050905090810190601f1680156104925780820380516001836020036101000a031916815260200191505b5094505050505060405180910390f35b845186511480156104b4575082518551145b610526576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4172726179206c656e677468206d69736d61746368000000000000000000000081525060200191505060405180910390fd5b600086519050828860000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081886001018190555042886002018190555060008090508860060160006105969190610fc7565b60008090505b82811015610705578960060160405180606001604052808b84815181106105bf57fe5b602002602001015181526020018a84815181106105d857fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff168152602001610623858a868151811061060b57fe5b60200260200101518c610ba99092919063ffffffff16565b815250908060018154018082558091505090600182039060005260206000209060030201600090919290919091506000820151816000015560208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160020190805190602001906106be929190610feb565b505050506106e88682815181106106d157fe5b602002602001015183610c3590919063ffffffff16565b91506106fe600182610c3590919063ffffffff16565b905061059c565b50505050505050505050565b6001600381111561071e57fe5b82600381111561072a57fe5b141561075a5761074a848660030160020154610cbd90919063ffffffff16565b85600301600201819055506107e9565b60038081111561076657fe5b82600381111561077257fe5b14156107a257610792848660030160000154610cbd90919063ffffffff16565b85600301600001819055506107e8565b600260038111156107af57fe5b8260038111156107bb57fe5b14156107e7576107db848660030160010154610cbd90919063ffffffff16565b85600301600101819055505b5b5b600160038111156107f657fe5b81600381111561080257fe5b141561083257610822838660030160020154610c3590919063ffffffff16565b85600301600201819055506108c1565b60038081111561083e57fe5b81600381111561084a57fe5b141561087a5761086a838660030160000154610c3590919063ffffffff16565b85600301600001819055506108c0565b6002600381111561088757fe5b81600381111561089357fe5b14156108bf576108b3838660030160010154610c3590919063ffffffff16565b85600301600101819055505b5b5b5050505050565b610a2c81600601805480602002602001604051908101604052809291908181526020016000905b82821015610a235783829060005260206000209060030201604051806060016040529081600082015481526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600282018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a0b5780601f106109e057610100808354040283529160200191610a0b565b820191906000526020600020905b8154815290600101906020018083116109ee57829003601f168201915b505050505081525050815260200190600101906108ef565b50505050610d07565b50565b600080606084600601805490508410610ab0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f6765745472616e73616374696f6e3a2062616420696e6465780000000000000081525060200191505060405180910390fd5b6000856006018581548110610ac157fe5b9060005260206000209060030201905080600001548160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1682600201808054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b945780601f10610b6957610100808354040283529160200191610b94565b820191906000526020600020905b815481529060010190602001808311610b7757829003601f168201915b50505050509050935093509350509250925092565b606081830184511015610bbb57600080fd5b6060821560008114610bd857604051915060208201604052610c29565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015610c165780518352602083019250602081019050610bf9565b50868552601f19601f8301166040525050505b50809150509392505050565b600080828401905083811015610cb3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b6000610cff83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250610e10565b905092915050565b60008090505b8151811015610e0c57610d7f828281518110610d2557fe5b602002602001015160200151838381518110610d3d57fe5b602002602001015160000151848481518110610d5557fe5b60200260200101516040015151858581518110610d6e57fe5b602002602001015160400151610ed0565b610df1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f50726f706f73616c20657865637574696f6e206661696c65640000000000000081525060200191505060405180910390fd5b610e05600182610c3590919063ffffffff16565b9050610d0d565b5050565b6000838311158290610ebd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610e82578082015181840152602081019050610e67565b50505050905090810190601f168015610eaf5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b6000806000841115610f5857610ee586610f7c565b610f57576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b5b6040516020840160008287838a8c6187965a03f19250505080915050949350505050565b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f9150808214158015610fbe57506000801b8214155b92505050919050565b5080546000825560030290600052602060002090810190610fe8919061106b565b50565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061102c57805160ff191683800117855561105a565b8280016001018555821561105a579182015b8281111561105957825182559160200191906001019061103e565b5b50905061106791906110c9565b5090565b6110c691905b808211156110c2576000808201600090556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556002820160006110b991906110ee565b50600301611071565b5090565b90565b6110eb91905b808211156110e75760008160009055506001016110cf565b5090565b90565b50805460018160011615610100020316600290046000825580601f106111145750611133565b601f01602090049060005260206000209081019061113291906110c9565b5b5056fea265627a7a72315820709a59e5de67644688c0a5a980f28229852b229e78f02b90dac7713a6836116a64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000a003": { + "code": "0x73000000000000000000000000000000000000000030146080604052600080fdfea265627a7a723158207a48fecd912aab914413fd617c04e83ce5b119a8d037ff3b4de8b3e7455d2b3a64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000a004": { + "code": "0x73000000000000000000000000000000000000000030146080604052600080fdfea265627a7a72315820b7a05c38050aa48a1913d906139b6702116b9d860757e6a6160bf7920fea14a664736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000a006": { + "code": "0x730000000000000000000000000000000000000000301460806040526004361061009d5760003560e01c8063593b79fe11610070578063593b79fe1461026c578063b1cfea43146102c4578063b2f8fe9614610351578063e2c0c56a146103ec578063fe3c7a8e146104475761009d565b806307debf7c146100a257806326afac491461013d578063341f662314610198578063542424fb14610206575b600080fd5b8180156100ae57600080fd5b5061013b600480360360808110156100c557600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506104ca565b005b81801561014957600080fd5b506101966004803603604081101561016057600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506104ff565b005b6101c4600480360360208110156101ae57600080fd5b8101908080359060200190929190505050610529565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102526004803603604081101561021c57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061053a565b604051808215151515815260200191505060405180910390f35b6102ae6004803603602081101561028257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610572565b6040518082815260200191505060405180910390f35b6102fa600480360360408110156102da57600080fd5b810190808035906020019092919080359060200190929190505050610599565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561033d578082015181840152602081019050610322565b505050509050019250505060405180910390f35b81801561035d57600080fd5b506103ea6004803603608081101561037457600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061067c565b005b8180156103f857600080fd5b506104456004803603604081101561040f57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506106b1565b005b6104736004803603602081101561045d57600080fd5b81019080803590602001909291905050506106d0565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156104b657808201518184015260208101905061049b565b505050509050019250505060405180910390f35b6104f96104d684610572565b6104df84610572565b6104e884610572565b876106e7909392919063ffffffff16565b50505050565b61052561050b82610572565b6000801b8460010154856106e7909392919063ffffffff16565b5050565b600060608260001c901c9050919050565b600082600301600061054b84610572565b815260200190815260200160002060020160009054906101000a900460ff16905092915050565b600060608273ffffffffffffffffffffffffffffffffffffffff16901b60001b9050919050565b6060806105af8385610b2e90919063ffffffff16565b90506060836040519080825280602002602001820160405280156105e25781602001602082028038833980820191505090505b50905060008090505b848110156106705761060f83828151811061060257fe5b6020026020010151610529565b82828151811061061b57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050610669600182610c5090919063ffffffff16565b90506105eb565b50809250505092915050565b6106ab61068884610572565b61069184610572565b61069a84610572565b87610cd8909392919063ffffffff16565b50505050565b6106cc6106bd82610572565b83610d9790919063ffffffff16565b5050565b60606106e0828360020154610599565b9050919050565b6000801b831415610760576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f4b6579206d75737420626520646566696e65640000000000000000000000000081525060200191505060405180910390fd5b61076a8484610f34565b156107dd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e277420696e7365727420616e206578697374696e6720656c656d656e7481525060200191505060405180910390fd5b8282141580156107ed5750828114155b610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603081526020018061106f6030913960400191505060405180910390fd5b6000846003016000858152602001908152602001600020905060018160020160006101000a81548160ff02191690831515021790555060008560020154141561089c57838560010181905550838560000181905550610b07565b6000801b831415806108b157506000801b8214155b610906576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d81526020018061112a602d913960400191505060405180910390fd5b8281600001819055508181600101819055506000801b8314610a055761092c8584610f34565b610981576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260348152602001806110c66034913960400191505060405180910390fd5b60008560030160008581526020019081526020016000209050828160010154146109f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061109f6027913960400191505060405180910390fd5b84816001018190555050610a0f565b8385600101819055505b6000801b8214610afc57610a238583610f34565b610a78576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806110fa6030913960400191505060405180910390fd5b6000856003016000848152602001908152602001600020905083816000015414610aed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061109f6027913960400191505060405180910390fd5b84816000018190555050610b06565b8385600001819055505b5b610b1f60018660020154610c5090919063ffffffff16565b85600201819055505050505050565b60608260020154821115610baa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6e6f7420656e6f75676820656c656d656e74730000000000000000000000000081525060200191505060405180910390fd5b606082604051908082528060200260200182016040528015610bdb5781602001602082028038833980820191505090505b50905060008460000154905060008090505b84811015610c445781838281518110610c0257fe5b602002602001018181525050856003016000838152602001908152602001600020600001549150610c3d600182610c5090919063ffffffff16565b9050610bed565b50819250505092915050565b600080828401905083811015610cce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b6000801b8314158015610ceb5750818314155b8015610cf75750808314155b8015610d095750610d088484610f34565b5b610d7b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f6b6579206f6e20696e206c69737400000000000000000000000000000000000081525060200191505060405180910390fd5b610d858484610d97565b610d91848484846106e7565b50505050565b600082600301600083815260200190815260200160002090506000801b8214158015610dc95750610dc88383610f34565b5b610e3b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f6b6579206e6f7420696e206c697374000000000000000000000000000000000081525060200191505060405180910390fd5b6000801b816000015414610e795760008360030160008360000154815260200190815260200160002090508160010154816001018190555050610e87565b806001015483600101819055505b6000801b816001015414610ec55760008360030160008360010154815260200190815260200160002090508160000154816000018190555050610ed3565b806000015483600001819055505b82600301600083815260200190815260200160002060008082016000905560018201600090556002820160006101000a81549060ff02191690555050610f2760018460020154610f6490919063ffffffff16565b8360020181905550505050565b600082600301600083815260200190815260200160002060020160009054906101000a900460ff16905092915050565b6000610fa683836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250610fae565b905092915050565b600083831115829061105b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015611020578082015181840152602081019050611005565b50505050905090810190601f16801561104d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838503905080915050939250505056fe4b65792063616e6e6f74206265207468652073616d652061732070726576696f75734b6579206f72206e6578744b657970726576696f75734b6579206d7573742062652061646a6163656e7420746f206e6578744b657949662070726576696f75734b657920697320646566696e65642c206974206d75737420657869737420696e20746865206c6973744966206e6578744b657920697320646566696e65642c206974206d75737420657869737420696e20746865206c6973744569746865722070726576696f75734b6579206f72206e6578744b6579206d75737420626520646566696e6564a265627a7a723158206680f27fc60f4df3cadfb6f8cb9143fc0222492dba64c7804eee10a83771abfb64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000a007": { + "code": "0x73000000000000000000000000000000000000000030146080604052600436106100b35760003560e01c806342b6351a1161007b57806342b6351a1461030f578063593b79fe1461036557806369b317e3146103bd578063cab455ae14610488578063dcb2a4dd1461052d578063e0fe44b3146105ba576100b3565b806302f13028146100b8578063281359291461011e5780632dedbbf014610179578063341f66231461021e5780633a72e8021461028c575b600080fd5b610104600480360360408110156100ce57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061061c565b604051808215151515815260200191505060405180910390f35b81801561012a57600080fd5b506101776004803603604081101561014157600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610641565b005b81801561018557600080fd5b5061021c600480360360a081101561019c57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610660565b005b61024a6004803603602081101561023457600080fd5b8101908080359060200190929190505050610698565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102b8600480360360208110156102a257600080fd5b81019080803590602001909291905050506106a9565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156102fb5780820151818401526020810190506102e0565b505050509050019250505060405180910390f35b61034f6004803603606081101561032557600080fd5b810190808035906020019092919080359060200190929190803590602001909291905050506106c3565b6040518082815260200191505060405180910390f35b6103a76004803603602081101561037b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061075f565b6040518082815260200191505060405180910390f35b6103e9600480360360208110156103d357600080fd5b8101908080359060200190929190505050610786565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015610430578082015181840152602081019050610415565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015610472578082015181840152602081019050610457565b5050505090500194505050505060405180910390f35b81801561049457600080fd5b5061052b600480360360a08110156104ab57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108dc565b005b6105636004803603604081101561054357600080fd5b810190808035906020019092919080359060200190929190505050610914565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105a657808201518184015260208101905061058b565b505050509050019250505060405180910390f35b610606600480360360408110156105d057600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109f7565b6040518082815260200191505060405180910390f35b600061063961062a8361075f565b84610a1c90919063ffffffff16565b905092915050565b61065c61064d8261075f565b83610a3c90919063ffffffff16565b5050565b61069161066c8561075f565b846106768561075f565b61067f8561075f565b89610a7190949392919063ffffffff16565b5050505050565b600060608260001c901c9050919050565b60606106bc828360000160020154610914565b9050919050565b6000806106d7838660000160020154610d0f565b905060008560000160000154905060008090505b8281101561075157856107078389610d2890919063ffffffff16565b101561071857809350505050610758565b86600001600301600083815260200190815260200160002060000154915061074a600182610d4890919063ffffffff16565b90506106eb565b5081925050505b9392505050565b600060608273ffffffffffffffffffffffffffffffffffffffff16901b60001b9050919050565b606080606061079484610dd0565b9050606081516040519080825280602002602001820160405280156107c85781602001602082028038833980820191505090505b509050606082516040519080825280602002602001820160405280156107fd5781602001602082028038833980820191505090505b50905060008090505b83518110156108cd5761082b84828151811061081e57fe5b6020026020010151610698565b83828151811061083757fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505086600401600085838151811061088357fe5b60200260200101518152602001908152602001600020548282815181106108a657fe5b6020026020010181815250506108c6600182610d4890919063ffffffff16565b9050610806565b50818194509450505050915091565b61090d6108e88561075f565b846108f28561075f565b6108fb8561075f565b89610de590949392919063ffffffff16565b5050505050565b60608061092a8385610e0390919063ffffffff16565b905060608360405190808252806020026020018201604052801561095d5781602001602082028038833980820191505090505b50905060008090505b848110156109eb5761098a83828151811061097d57fe5b6020026020010151610698565b82828151811061099657fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506109e4600182610d4890919063ffffffff16565b9050610966565b50809250505092915050565b6000610a14610a058361075f565b84610d2890919063ffffffff16565b905092915050565b6000610a348284600001610e2390919063ffffffff16565b905092915050565b610a528183600001610e5390919063ffffffff16565b6000826004016000838152602001908152602001600020819055505050565b6000801b8414158015610a845750818414155b8015610a905750808414155b8015610aa35750610aa18585610a1c565b155b610b15576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600b8152602001807f696e76616c6964206b657900000000000000000000000000000000000000000081525060200191505060405180910390fd5b6000801b82141580610b2a57506000801b8114155b80610b3c575060008560000160020154145b610bae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f6772656174657220616e64206c6573736572206b6579207a65726f000000000081525060200191505060405180910390fd5b610bb88583610a1c565b80610bc557506000801b82145b610c37576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f696e76616c6964206c6573736572206b6579000000000000000000000000000081525060200191505060405180910390fd5b610c418582610a1c565b80610c4e57506000801b81145b610cc0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c69642067726561746572206b65790000000000000000000000000081525060200191505060405180910390fd5b610ccc85848484610ff0565b8092508193505050610cee848383886000016111a3909392919063ffffffff16565b82856004016000868152602001908152602001600020819055505050505050565b6000818310610d1e5781610d20565b825b905092915050565b600082600401600083815260200190815260200160002054905092915050565b600080828401905083811015610dc6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b6060610dde826000016115ea565b9050919050565b610def8585610a3c565b610dfc8585858585610a71565b5050505050565b6060610e1b828460000161160190919063ffffffff16565b905092915050565b600082600301600083815260200190815260200160002060020160009054906101000a900460ff16905092915050565b600082600301600083815260200190815260200160002090506000801b8214158015610e855750610e848383610e23565b5b610ef7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f6b6579206e6f7420696e206c697374000000000000000000000000000000000081525060200191505060405180910390fd5b6000801b816000015414610f355760008360030160008360000154815260200190815260200160002090508160010154816001018190555050610f43565b806001015483600101819055505b6000801b816001015414610f815760008360030160008360010154815260200190815260200160002090508160000154816000018190555050610f8f565b806000015483600001819055505b82600301600083815260200190815260200160002060008082016000905560018201600090556002820160006101000a81549060ff02191690555050610fe36001846002015461172390919063ffffffff16565b8360020181905550505050565b6000806000801b841480156110145750611013868686896000016001015461176d565b5b1561102b578386600001600101549150915061119a565b6000801b8314801561104c575061104b868688600001600001548661176d565b5b15611063578560000160000154839150915061119a565b6000801b841415801561109957506110988686868960000160030160008981526020019081526020016000206001015461176d565b5b156110c45783866000016003016000868152602001908152602001600020600101549150915061119a565b6000801b83141580156110fa57506110f98686886000016003016000878152602001908152602001600020600001548661176d565b5b156111255785600001600301600084815260200190815260200160002060000154839150915061119a565b6000611199576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f676574206c657373657220616e642067726561746572206661696c757265000081525060200191505060405180910390fd5b5b94509492505050565b6000801b83141561121c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f4b6579206d75737420626520646566696e65640000000000000000000000000081525060200191505060405180910390fd5b6112268484610e23565b15611299576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e277420696e7365727420616e206578697374696e6720656c656d656e7481525060200191505060405180910390fd5b8282141580156112a95750828114155b6112fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806118996030913960400191505060405180910390fd5b6000846003016000858152602001908152602001600020905060018160020160006101000a81548160ff021916908315150217905550600085600201541415611358578385600101819055508385600001819055506115c3565b6000801b8314158061136d57506000801b8214155b6113c2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d815260200180611954602d913960400191505060405180910390fd5b8281600001819055508181600101819055506000801b83146114c1576113e88584610e23565b61143d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260348152602001806118f06034913960400191505060405180910390fd5b60008560030160008581526020019081526020016000209050828160010154146114b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806118c96027913960400191505060405180910390fd5b848160010181905550506114cb565b8385600101819055505b6000801b82146115b8576114df8583610e23565b611534576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806119246030913960400191505060405180910390fd5b60008560030160008481526020019081526020016000209050838160000154146115a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806118c96027913960400191505060405180910390fd5b848160000181905550506115c2565b8385600001819055505b5b6115db60018660020154610d4890919063ffffffff16565b85600201819055505050505050565b60606115fa828360020154611601565b9050919050565b6060826002015482111561167d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6e6f7420656e6f75676820656c656d656e74730000000000000000000000000081525060200191505060405180910390fd5b6060826040519080825280602002602001820160405280156116ae5781602001602082028038833980820191505090505b50905060008460000154905060008090505b8481101561171757818382815181106116d557fe5b602002602001018181525050856003016000838152602001908152602001600020600001549150611710600182610d4890919063ffffffff16565b90506116c0565b50819250505092915050565b600061176583836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506117d8565b905092915050565b6000806000801b8414806117965750848660040160008681526020019081526020016000205411155b905060008060001b8414806117c05750858760040160008681526020019081526020016000205410155b90508180156117cc5750805b92505050949350505050565b6000838311158290611885576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561184a57808201518184015260208101905061182f565b50505050905090810190601f1680156118775780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838503905080915050939250505056fe4b65792063616e6e6f74206265207468652073616d652061732070726576696f75734b6579206f72206e6578744b657970726576696f75734b6579206d7573742062652061646a6163656e7420746f206e6578744b657949662070726576696f75734b657920697320646566696e65642c206974206d75737420657869737420696e20746865206c6973744966206e6578744b657920697320646566696e65642c206974206d75737420657869737420696e20746865206c6973744569746865722070726576696f75734b6579206f72206e6578744b6579206d75737420626520646566696e6564a265627a7a72315820e3b70453032197d718fd30ee0381dbff75265bffb9c56bfa94579f90069993f564736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000a008": { + "code": "0x73000000000000000000000000000000000000000030146080604052600436106100925760003560e01c8063b4bd30b511610065578063b4bd30b5146102ab578063bfc516381461030e578063d7a8acc11461035e578063eed5f7be146103a357610092565b8063239491ba1461009757806369b317e3146100fa57806375777599146101c557806377b0247914610211575b600080fd5b8180156100a357600080fd5b506100f8600480360360a08110156100ba57600080fd5b8101908080359060200190929190803590602001909291908035906020019092919080359060200190929190803590602001909291905050506103e8565b005b6101266004803603602081101561011057600080fd5b8101908080359060200190929190505050610411565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561016d578082015181840152602081019050610152565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156101af578082015181840152602081019050610194565b5050505090500194505050505060405180910390f35b6101fb600480360360408110156101db57600080fd5b810190808035906020019092919080359060200190929190505050610534565b6040518082815260200191505060405180910390f35b81801561021d57600080fd5b506102546004803603604081101561023457600080fd5b810190808035906020019092919080359060200190929190505050610554565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561029757808201518184015260208101905061027c565b505050509050019250505060405180910390f35b8180156102b757600080fd5b5061030c600480360360a08110156102ce57600080fd5b810190808035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050610606565b005b6103446004803603604081101561032457600080fd5b81019080803590602001909291908035906020019092919050505061062f565b604051808215151515815260200191505060405180910390f35b81801561036a57600080fd5b506103a16004803603604081101561038157600080fd5b81019080803590602001909291908035906020019092919050505061064f565b005b8180156103af57600080fd5b506103e6600480360360408110156103c657600080fd5b810190808035906020019092919080359060200190929190505050610669565b005b61040a8460001b848460001b8460001b8961068390949392919063ffffffff16565b5050505050565b606080606061041f846106a1565b9050606081516040519080825280602002602001820160405280156104535781602001602082028038833980820191505090505b509050606082516040519080825280602002602001820160405280156104885781602001602082028038833980820191505090505b50905060008090505b8351811015610525578381815181106104a657fe5b602002602001015160001c8382815181106104bd57fe5b6020026020010181815250508660040160008583815181106104db57fe5b60200260200101518152602001908152602001600020548282815181106104fe57fe5b60200260200101818152505061051e6001826106b690919063ffffffff16565b9050610491565b50818194509450505050915091565b600061054c8260001b8461073e90919063ffffffff16565b905092915050565b60608061056a838561075e90919063ffffffff16565b90506060815160405190808252806020026020018201604052801561059e5781602001602082028038833980820191505090505b50905060008090505b82518110156105fa578281815181106105bc57fe5b602002602001015160001c8282815181106105d357fe5b6020026020010181815250506105f36001826106b690919063ffffffff16565b90506105a7565b50809250505092915050565b6106288460001b848460001b8460001b8961087590949392919063ffffffff16565b5050505050565b60006106478260001b84610b1390919063ffffffff16565b905092915050565b6106658160001b83610b3390919063ffffffff16565b5050565b61067f8160001b83610b4f90919063ffffffff16565b5050565b61068d8585610b4f565b61069a8585858585610875565b5050505050565b60606106af82600001610b84565b9050919050565b600080828401905083811015610734576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600082600401600083815260200190815260200160002054905092915050565b606082600001600201548211156107dd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6e6f7420656e6f75676820656c656d656e74730000000000000000000000000081525060200191505060405180910390fd5b60608260405190808252806020026020018201604052801561080e5781602001602082028038833980820191505090505b50905060008090505b8381101561086a576000856000016000015490508083838151811061083857fe5b60200260200101818152505061084e8682610b4f565b506108636001826106b690919063ffffffff16565b9050610817565b508091505092915050565b6000801b84141580156108885750818414155b80156108945750808414155b80156108a757506108a58585610b13565b155b610919576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600b8152602001807f696e76616c6964206b657900000000000000000000000000000000000000000081525060200191505060405180910390fd5b6000801b8214158061092e57506000801b8114155b80610940575060008560000160020154145b6109b2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f6772656174657220616e64206c6573736572206b6579207a65726f000000000081525060200191505060405180910390fd5b6109bc8583610b13565b806109c957506000801b82145b610a3b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f696e76616c6964206c6573736572206b6579000000000000000000000000000081525060200191505060405180910390fd5b610a458582610b13565b80610a5257506000801b81145b610ac4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c69642067726561746572206b65790000000000000000000000000081525060200191505060405180910390fd5b610ad085848484610b9b565b8092508193505050610af284838388600001610d4e909392919063ffffffff16565b82856004016000868152602001908152602001600020819055505050505050565b6000610b2b828460000161119590919063ffffffff16565b905092915050565b610b4b828260008060001b8660000160010154610875565b5050565b610b6581836000016111c590919063ffffffff16565b6000826004016000838152602001908152602001600020819055505050565b6060610b94828360020154611362565b9050919050565b6000806000801b84148015610bbf5750610bbe8686868960000160010154611484565b5b15610bd65783866000016001015491509150610d45565b6000801b83148015610bf75750610bf68686886000016000015486611484565b5b15610c0e5785600001600001548391509150610d45565b6000801b8414158015610c445750610c4386868689600001600301600089815260200190815260200160002060010154611484565b5b15610c6f57838660000160030160008681526020019081526020016000206001015491509150610d45565b6000801b8314158015610ca55750610ca486868860000160030160008781526020019081526020016000206000015486611484565b5b15610cd057856000016003016000848152602001908152602001600020600001548391509150610d45565b6000610d44576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f676574206c657373657220616e642067726561746572206661696c757265000081525060200191505060405180910390fd5b5b94509492505050565b6000801b831415610dc7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f4b6579206d75737420626520646566696e65640000000000000000000000000081525060200191505060405180910390fd5b610dd18484611195565b15610e44576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e277420696e7365727420616e206578697374696e6720656c656d656e7481525060200191505060405180910390fd5b828214158015610e545750828114155b610ea9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806115fa6030913960400191505060405180910390fd5b6000846003016000858152602001908152602001600020905060018160020160006101000a81548160ff021916908315150217905550600085600201541415610f035783856001018190555083856000018190555061116e565b6000801b83141580610f1857506000801b8214155b610f6d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d8152602001806116b5602d913960400191505060405180910390fd5b8281600001819055508181600101819055506000801b831461106c57610f938584611195565b610fe8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260348152602001806116516034913960400191505060405180910390fd5b600085600301600085815260200190815260200160002090508281600101541461105d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061162a6027913960400191505060405180910390fd5b84816001018190555050611076565b8385600101819055505b6000801b82146111635761108a8583611195565b6110df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806116856030913960400191505060405180910390fd5b6000856003016000848152602001908152602001600020905083816000015414611154576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061162a6027913960400191505060405180910390fd5b8481600001819055505061116d565b8385600001819055505b5b611186600186600201546106b690919063ffffffff16565b85600201819055505050505050565b600082600301600083815260200190815260200160002060020160009054906101000a900460ff16905092915050565b600082600301600083815260200190815260200160002090506000801b82141580156111f757506111f68383611195565b5b611269576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f6b6579206e6f7420696e206c697374000000000000000000000000000000000081525060200191505060405180910390fd5b6000801b8160000154146112a757600083600301600083600001548152602001908152602001600020905081600101548160010181905550506112b5565b806001015483600101819055505b6000801b8160010154146112f35760008360030160008360010154815260200190815260200160002090508160000154816000018190555050611301565b806000015483600001819055505b82600301600083815260200190815260200160002060008082016000905560018201600090556002820160006101000a81549060ff02191690555050611355600184600201546114ef90919063ffffffff16565b8360020181905550505050565b606082600201548211156113de576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6e6f7420656e6f75676820656c656d656e74730000000000000000000000000081525060200191505060405180910390fd5b60608260405190808252806020026020018201604052801561140f5781602001602082028038833980820191505090505b50905060008460000154905060008090505b84811015611478578183828151811061143657fe5b6020026020010181815250508560030160008381526020019081526020016000206000015491506114716001826106b690919063ffffffff16565b9050611421565b50819250505092915050565b6000806000801b8414806114ad5750848660040160008681526020019081526020016000205411155b905060008060001b8414806114d75750858760040160008681526020019081526020016000205410155b90508180156114e35750805b92505050949350505050565b600061153183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250611539565b905092915050565b60008383111582906115e6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156115ab578082015181840152602081019050611590565b50505050905090810190601f1680156115d85780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838503905080915050939250505056fe4b65792063616e6e6f74206265207468652073616d652061732070726576696f75734b6579206f72206e6578744b657970726576696f75734b6579206d7573742062652061646a6163656e7420746f206e6578744b657949662070726576696f75734b657920697320646566696e65642c206974206d75737420657869737420696e20746865206c6973744966206e6578744b657920697320646566696e65642c206974206d75737420657869737420696e20746865206c6973744569746865722070726576696f75734b6579206f72206e6578744b6579206d75737420626520646566696e6564a265627a7a72315820b8070e5d541c1cc9e9b9eef9f347daceefbcff846c817195fb8248b3de6b2abe64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000a009": { + "code": "0x73000000000000000000000000000000000000000030146080604052600436106100d95760003560e01c80636eafa6c31161009657806395073a791161007057806395073a791461051e578063c1e728e914610584578063d4a09272146105df578063d938ec7b14610684576100d9565b80636eafa6c3146103d55780637c6bb86214610417578063832a214714610479576100d9565b80630944c594146100de5780633118159e1461014c578063341f6623146101ba578063593b79fe1461022857806359d556a8146102805780636cfa3873146102c2575b600080fd5b61010a600480360360208110156100f457600080fd5b81019080803590602001909291905050506106f2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101786004803603602081101561016257600080fd5b810190808035906020019092919050505061070c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101e6600480360360208110156101d057600080fd5b8101908080359060200190929190505050610726565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61026a6004803603602081101561023e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610737565b6040518082815260200191505060405180910390f35b6102ac6004803603602081101561029657600080fd5b810190808035906020019092919050505061075e565b6040518082815260200191505060405180910390f35b6102ee600480360360208110156102d857600080fd5b810190808035906020019092919050505061077e565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b8381101561033957808201518184015260208101905061031e565b50505050905001848103835286818151815260200191508051906020019060200280838360005b8381101561037b578082015181840152602081019050610360565b50505050905001848103825285818151815260200191508051906020019060200280838360005b838110156103bd5780820151818401526020810190506103a2565b50505050905001965050505050505060405180910390f35b610401600480360360208110156103eb57600080fd5b8101908080359060200190929190505050610973565b6040518082815260200191505060405180910390f35b6104636004803603604081101561042d57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610985565b6040518082815260200191505060405180910390f35b81801561048557600080fd5b5061051c600480360360a081101561049c57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109aa565b005b61056a6004803603604081101561053457600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109e2565b604051808215151515815260200191505060405180910390f35b81801561059057600080fd5b506105dd600480360360408110156105a757600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610a07565b005b8180156105eb57600080fd5b50610682600480360360a081101561060257600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610a26565b005b6106b06004803603602081101561069a57600080fd5b8101908080359060200190929190505050610a5e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600061070561070083610a78565b610726565b9050919050565b600061071f61071a83610a8c565b610726565b9050919050565b600060608260001c901c9050919050565b600060608273ffffffffffffffffffffffffffffffffffffffff16901b60001b9050919050565b6000610777826005015483610a9a90919063ffffffff16565b9050919050565b60608060608061078d85610abd565b9050606081516040519080825280602002602001820160405280156107c15781602001602082028038833980820191505090505b509050606082516040519080825280602002602001820160405280156107f65781602001602082028038833980820191505090505b5090506060825160405190808252806020026020018201604052801561082b5781602001602082028038833980820191505090505b50905060008090505b845181101561095e5761085985828151811061084c57fe5b6020026020010151610726565b84828151811061086557fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506108c58582815181106108ae57fe5b60200260200101518a610a9a90919063ffffffff16565b8382815181106108d157fe5b6020026020010181815250508860060160008683815181106108ef57fe5b6020026020010151815260200190815260200160002060009054906101000a900460ff1682828151811061091f57fe5b6020026020010190600381111561093257fe5b9081600381111561093f57fe5b81525050610957600182610ad290919063ffffffff16565b9050610834565b50828282965096509650505050509193909250565b600061097e82610b5a565b9050919050565b60006109a261099383610737565b84610a9a90919063ffffffff16565b905092915050565b6109db6109b685610737565b846109c085610737565b6109c985610737565b89610b6e90949392919063ffffffff16565b5050505050565b60006109ff6109f083610737565b84610b8c90919063ffffffff16565b905092915050565b610a22610a1382610737565b83610bac90919063ffffffff16565b5050565b610a57610a3285610737565b84610a3c85610737565b610a4585610737565b89610d2e90949392919063ffffffff16565b5050505050565b6000610a71610a6c83610f99565b610726565b9050919050565b600081600001600001600001549050919050565b600081600501549050919050565b600082600001600401600083815260200190815260200160002054905092915050565b6060610acb82600001610fad565b9050919050565b600080828401905083811015610b50576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600081600001600001600201549050919050565b610b788585610bac565b610b858585858585610d2e565b5050505050565b6000610ba48284600001610fc290919063ffffffff16565b905092915050565b6000809050600083600001600001600201541415610bd5576000801b8360050181905550610d09565b60006002846000016000016002015481610beb57fe5b061415610c7f5760026003811115610bff57fe5b83600601600084815260200190815260200160002060009054906101000a900460ff166003811115610c2d57fe5b1480610c705750600380811115610c4057fe5b83600601600084815260200190815260200160002060009054906101000a900460ff166003811115610c6e57fe5b145b15610c7a57600190505b610d08565b60016003811115610c8c57fe5b83600601600084815260200190815260200160002060009054906101000a900460ff166003811115610cba57fe5b1480610cfd5750600380811115610ccd57fe5b83600601600084815260200190815260200160002060009054906101000a900460ff166003811115610cfb57fe5b145b15610d0757600290505b5b5b610d138382610fe2565b610d29828460000161111990919063ffffffff16565b505050565b610d4a848484848960000161114e90949392919063ffffffff16565b600085600001600001600301600086815260200190815260200160002090506000809050600187600001600001600201541415610dc657858760050181905550600387600601600088815260200190815260200160002060006101000a81548160ff02191690836003811115610dbc57fe5b0217905550610f86565b60016002886000016000016002015481610ddc57fe5b061415610eb6576000801b82600001541480610e34575060016003811115610e0057fe5b8760060160008460000154815260200190815260200160002060009054906101000a900460ff166003811115610e3257fe5b145b15610e795760019050600187600601600088815260200190815260200160002060006101000a81548160ff02191690836003811115610e6f57fe5b0217905550610eb1565b600287600601600088815260200190815260200160002060006101000a81548160ff02191690836003811115610eab57fe5b02179055505b610f85565b6000801b82600101541480610f07575060026003811115610ed357fe5b8760060160008460010154815260200190815260200160002060009054906101000a900460ff166003811115610f0557fe5b145b15610f4c5760029050600287600601600088815260200190815260200160002060006101000a81548160ff02191690836003811115610f4257fe5b0217905550610f84565b600187600601600088815260200190815260200160002060006101000a81548160ff02191690836003811115610f7e57fe5b02179055505b5b5b610f908782610fe2565b50505050505050565b600081600001600001600101549050919050565b6060610fbb826000016113ec565b9050919050565b6000610fda828460000161140390919063ffffffff16565b905092915050565b60008260000160000160030160008460050154815260200190815260200160002090506001600281111561101257fe5b82600281111561101e57fe5b14156110715760028360060160008560050154815260200190815260200160002060006101000a81548160ff0219169083600381111561105a57fe5b0217905550806000015483600501819055506110d9565b60028081111561107d57fe5b82600281111561108957fe5b14156110d85760018360060160008560050154815260200190815260200160002060006101000a81548160ff021916908360038111156110c557fe5b0217905550806001015483600501819055505b5b60038360060160008560050154815260200190815260200160002060006101000a81548160ff0219169083600381111561110f57fe5b0217905550505050565b61112f818360000161143390919063ffffffff16565b6000826004016000838152602001908152602001600020819055505050565b6000801b84141580156111615750818414155b801561116d5750808414155b8015611180575061117e8585610fc2565b155b6111f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600b8152602001807f696e76616c6964206b657900000000000000000000000000000000000000000081525060200191505060405180910390fd5b6000801b8214158061120757506000801b8114155b80611219575060008560000160020154145b61128b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f6772656174657220616e64206c6573736572206b6579207a65726f000000000081525060200191505060405180910390fd5b6112958583610fc2565b806112a257506000801b82145b611314576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f696e76616c6964206c6573736572206b6579000000000000000000000000000081525060200191505060405180910390fd5b61131e8582610fc2565b8061132b57506000801b81145b61139d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c69642067726561746572206b65790000000000000000000000000081525060200191505060405180910390fd5b6113a9858484846115d0565b80925081935050506113cb84838388600001611783909392919063ffffffff16565b82856004016000868152602001908152602001600020819055505050505050565b60606113fc828360020154611bca565b9050919050565b600082600301600083815260200190815260200160002060020160009054906101000a900460ff16905092915050565b600082600301600083815260200190815260200160002090506000801b821415801561146557506114648383611403565b5b6114d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f6b6579206e6f7420696e206c697374000000000000000000000000000000000081525060200191505060405180910390fd5b6000801b8160000154146115155760008360030160008360000154815260200190815260200160002090508160010154816001018190555050611523565b806001015483600101819055505b6000801b816001015414611561576000836003016000836001015481526020019081526020016000209050816000015481600001819055505061156f565b806000015483600001819055505b82600301600083815260200190815260200160002060008082016000905560018201600090556002820160006101000a81549060ff021916905550506115c360018460020154611cec90919063ffffffff16565b8360020181905550505050565b6000806000801b841480156115f457506115f38686868960000160010154611d36565b5b1561160b578386600001600101549150915061177a565b6000801b8314801561162c575061162b8686886000016000015486611d36565b5b15611643578560000160000154839150915061177a565b6000801b8414158015611679575061167886868689600001600301600089815260200190815260200160002060010154611d36565b5b156116a45783866000016003016000868152602001908152602001600020600101549150915061177a565b6000801b83141580156116da57506116d986868860000160030160008781526020019081526020016000206000015486611d36565b5b156117055785600001600301600084815260200190815260200160002060000154839150915061177a565b6000611779576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f676574206c657373657220616e642067726561746572206661696c757265000081525060200191505060405180910390fd5b5b94509492505050565b6000801b8314156117fc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f4b6579206d75737420626520646566696e65640000000000000000000000000081525060200191505060405180910390fd5b6118068484611403565b15611879576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e277420696e7365727420616e206578697374696e6720656c656d656e7481525060200191505060405180910390fd5b8282141580156118895750828114155b6118de576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180611e626030913960400191505060405180910390fd5b6000846003016000858152602001908152602001600020905060018160020160006101000a81548160ff02191690831515021790555060008560020154141561193857838560010181905550838560000181905550611ba3565b6000801b8314158061194d57506000801b8214155b6119a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d815260200180611f1d602d913960400191505060405180910390fd5b8281600001819055508181600101819055506000801b8314611aa1576119c88584611403565b611a1d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526034815260200180611eb96034913960400191505060405180910390fd5b6000856003016000858152602001908152602001600020905082816001015414611a92576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180611e926027913960400191505060405180910390fd5b84816001018190555050611aab565b8385600101819055505b6000801b8214611b9857611abf8583611403565b611b14576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180611eed6030913960400191505060405180910390fd5b6000856003016000848152602001908152602001600020905083816000015414611b89576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180611e926027913960400191505060405180910390fd5b84816000018190555050611ba2565b8385600001819055505b5b611bbb60018660020154610ad290919063ffffffff16565b85600201819055505050505050565b60608260020154821115611c46576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6e6f7420656e6f75676820656c656d656e74730000000000000000000000000081525060200191505060405180910390fd5b606082604051908082528060200260200182016040528015611c775781602001602082028038833980820191505090505b50905060008460000154905060008090505b84811015611ce05781838281518110611c9e57fe5b602002602001018181525050856003016000838152602001908152602001600020600001549150611cd9600182610ad290919063ffffffff16565b9050611c89565b50819250505092915050565b6000611d2e83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250611da1565b905092915050565b6000806000801b841480611d5f5750848660040160008681526020019081526020016000205411155b905060008060001b841480611d895750858760040160008681526020019081526020016000205410155b9050818015611d955750805b92505050949350505050565b6000838311158290611e4e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015611e13578082015181840152602081019050611df8565b50505050905090810190601f168015611e405780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838503905080915050939250505056fe4b65792063616e6e6f74206265207468652073616d652061732070726576696f75734b6579206f72206e6578744b657970726576696f75734b6579206d7573742062652061646a6163656e7420746f206e6578744b657949662070726576696f75734b657920697320646566696e65642c206974206d75737420657869737420696e20746865206c6973744966206e6578744b657920697320646566696e65642c206974206d75737420657869737420696e20746865206c6973744569746865722070726576696f75734b6579206f72206e6578744b6579206d75737420626520646566696e6564a265627a7a723158209775f075d4f7bc6136adc1d928f6667e203ef72332cbe0010b088d4587fb0ae264736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000a010": { + "code": "0x73000000000000000000000000000000000000000030146080604052600436106100565760003560e01c806334d1a2331461005b57806368331709146100f457806396ef41a114610140578063b3abdb0c146101e5575b600080fd5b6100b2600480360360a081101561007157600080fd5b810190808035906020019092919080359060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050610274565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61012a6004803603604081101561010a57600080fd5b8101908080359060200190929190803590602001909291905050506102e6565b6040518082815260200191505060405180910390f35b6101a36004803603608081101561015657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050610347565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610232600480360360808110156101fb57600080fd5b8101908080359060200190929190803560ff16906020019092919080359060200190929190803590602001909291905050506103b9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000606060416040519080825280601f01601f1916602001820160405280156102ac5781602001600182028038833980820191505090505b50905083602082015282604082015284606082015360006102cd88886102e6565b90506102d98183610429565b9250505095945050505050565b6000828260405160200180807f19010000000000000000000000000000000000000000000000000000000000008152506002018381526020018281526020019250505060405160208183030381529060405280519060200120905092915050565b60008085604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014019150506040516020818303038152906040528051906020012090506103ae818686866103b9565b915050949350505050565b6000606060416040519080825280601f01601f1916602001820160405280156103f15781602001600182028038833980820191505090505b50905083602082015282604082015284606082015360006104118761052d565b905061041d8183610429565b92505050949350505050565b6000604182511461043d5760009050610527565b60008060006020850151925060408501519150606085015160001a90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08260001c11156104915760009350505050610527565b601b8160ff16141580156104a95750601c8160ff1614155b156104ba5760009350505050610527565b60018682858560405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610517573d6000803e3d6000fd5b5050506020604051035193505050505b92915050565b60008160405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c0182815260200191505060405160208183030381529060405280519060200120905091905056fea265627a7a72315820d3015cba406766ac0652a531d851eaf712bdd44135498f5a603ea546323f814a64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000a051": { + "code": "0x73000000000000000000000000000000000000000030146080604052600080fdfea265627a7a72315820cb60a651ffaa7c5355cfb1f4bdbf9a52ab8d8d0b01cf6d5d455eb9c6455e109964736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000ce10": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x007996a2955dfb0e6cc1f05f5df3579d5831b46aaaa0fe130da794ddfdda18ad": "0x000000000000000000000000000000000000000000000000000000000000d020", + "0x1e25a176e2a29be4895c82c1fea3a7bb055502dbeea521433ecfc88437374d29": "0x000000000000000000000000000000000000000000000000000000000000d018", + "0x20bba94ff21426d24a63b6beab6173053751a645a7d15588358171dd6e96ee0b": "0x000000000000000000000000000000000000000000000000000000000000d011", + "0x2131a4338f6fb8d4507e234a7c72af8efefbbf2f1817ed570bce33eb6667feb9": "0x000000000000000000000000000000000000000000000000000000000000d007", + "0x29629c343b5c9fff49cd3a964bb74da872f3e0e9073cf3ea89a87d628c2d2ca7": "0x000000000000000000000000000000000000000000000000000000000000d015", + "0x2f4e7b858e1ed178207a10238ebf5b1358e6a4a41e765a3edf3260730760b38c": "0x000000000000000000000000000000000000000000000000000000000000d008", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000ce11", + "0x75183b8aabdad5fbffc715bbe70535afe50ff8ed453a4223c47a5d1ab0048387": "0x0000000000000000000000000f9602b6756b63dfb94ec11b7a3c2619ed4246ef", + "0x7710c8bd31e5abcf8e2338ad2569e2a8461b242c3be6fae253ffa1f963ddd0df": "0x000000000000000000000000000000000000000000000000000000000000d009", + "0x773cc8652456781771d48fb3d560d7ba3d6d1882654492b68beec583d2c590aa": "0x000000000000000000000000000000000000000000000000000000000000d003", + "0x78e70a8344b05ed06597d2ea30a73432dda3d18e13fe04cdf5f151b6125a4a80": "0x000000000000000000000000000000000000000000000000000000000000d016", + "0x793b1416a9371863f87b090796dcb7b72023635eca4ce3d9e5fa1d1e0fa079f1": "0x000000000000000000000000000000000000000000000000000000000000d001", + "0x91646b8507bf2e54d7c3de9155442ba111546b81af1cbdd1f68eeb6926b98d58": "0x000000000000000000000000000000000000000000000000000000000000d023", + "0x92ddd299fce8b8859b13f0abb8acd4201708ef817ce47d1e5c73bfcf022d4219": "0x000000000000000000000000000000000000000000000000000000000000d025", + "0x99f4d1d2143fa272a89811a6763d70f10f8ea59f7bed6c8d26575068d6c25b36": "0x000000000000000000000000000000000000000000000000000000000000d014", + "0xa183d5e9218bd0cfb583447ccc3cef80b84785f7bdc0238ffd8eabb05d65d1ea": "0x000000000000000000000000000000000000000000000000000000000000d017", + "0xa9245761f51d6db8b57b57f6a4e786d36bae68f0b6e37574f144134797f83226": "0x000000000000000000000000000000000000000000000000000000000000d019", + "0xb52eb58cb1dd3553b2be57a59d9c31b6c860a30720b166c0bbc7357126eb2cab": "0x000000000000000000000000000000000000000000000000000000000000d010", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xc175c49c4a7dbc28c923d41d18b2e5bfc6ac7519c367d7fddecfb6166035d8e2": "0x000000000000000000000000000000000000000000000000000000000000d021", + "0xc2afdc46de17996f5f21e9c9578a94e0fd98d1b2dfd1606177b845f447daa8c5": "0x000000000000000000000000000000000000000000000000000000000000d024", + "0xc8df4f680dc540ec9b0c613a7976e29988d07a5b20c6b972785f8d9cac08f4f5": "0x000000000000000000000000000000000000000000000000000000000000d005", + "0xcff58fbbf19e274834dbbb0d6c1c84666c663932b89291bf061ab49d7e3f639a": "0x000000000000000000000000000000000000000000000000000000000000d004", + "0xd367e4a67661f77f499d4555e9708608d5c2a630ce781de18076a8937a468d1e": "0x000000000000000000000000000000000000000000000000000000000000d013", + "0xd830678365b5dbc6b4dde9665b5546573fb66956cd1a3e9c931e98cd38f26f60": "0x000000000000000000000000000000000000000000000000000000000000d002", + "0xdb86e256b54dc557d351c1e75149bf37b30262c1488d7a96a5b42562c7344c93": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0xe79a7848066cc9bfbdadfb6b63d8a5f3431bf53fec40f4cdce6d5fc90cc4ad78": "0x000000000000000000000000000000000000000000000000000000000000d012" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000ce11": { + "code": "0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c80638932cbf41161008c578063c586579311610066578063c586579314610407578063dcf0aaed146104a0578063dd9272331461050e578063f2fde38b1461057c576100cf565b80638932cbf4146102e25780638da5cb5b1461039b5780638f32d59b146103e5576100cf565b8063158ef93e146100d457806317c50818146100f6578063715018a6146101a75780637ef50298146101b15780638129fc1c1461021f578063853db32314610229575b600080fd5b6100dc6105c0565b604051808215151515815260200191505060405180910390f35b61018d6004803603604081101561010c57600080fd5b810190808035906020019064010000000081111561012957600080fd5b82018360208201111561013b57600080fd5b8035906020019184602083028401116401000000008311171561015d57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506105d3565b604051808215151515815260200191505060405180910390f35b6101af610691565b005b6101dd600480360360208110156101c757600080fd5b81019080803590602001909291905050506107ca565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102276107fd565b005b6102a06004803603602081101561023f57600080fd5b810190808035906020019064010000000081111561025c57600080fd5b82018360208201111561026e57600080fd5b8035906020019184600183028401116401000000008311171561029057600080fd5b90919293919293905050506108a6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610359600480360360208110156102f857600080fd5b810190808035906020019064010000000081111561031557600080fd5b82018360208201111561032757600080fd5b8035906020019184600183028401116401000000008311171561034957600080fd5b9091929391929390505050610918565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103a3610a60565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103ed610a89565b604051808215151515815260200191505060405180910390f35b61049e6004803603604081101561041d57600080fd5b810190808035906020019064010000000081111561043a57600080fd5b82018360208201111561044c57600080fd5b8035906020019184600183028401116401000000008311171561046e57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ae7565b005b6104cc600480360360208110156104b657600080fd5b8101908080359060200190929190505050610c68565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61053a6004803603602081101561052457600080fd5b8101908080359060200190929190505050610d7a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105be6004803603602081101561059257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610db7565b005b600060149054906101000a900460ff1681565b600080600090505b84849050811015610684578273ffffffffffffffffffffffffffffffffffffffff166001600087878581811061060d57fe5b90506020020135815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561066957600191505061068a565b61067d600182610e3d90919063ffffffff16565b90506105db565b50600090505b9392505050565b610699610a89565b61070b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60016020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1615610880576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff0219169083151502179055506108a433610ec5565b565b60008083836040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090506001600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691505092915050565b6000808383604051602001808383808284378083019250505092505050604051602081830303815290604052805190602001209050600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a23576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6964656e74696669657220686173206e6f20726567697374727920656e74727981525060200191505060405180910390fd5b6001600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16610acb611009565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b610aef610a89565b610b61576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008383604051602001808383808284378083019250505092505050604051602081830303815290604052805190602001209050816001600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff16817f4166d073a7a5e704ce0db7113320f88da2457f872d46dc020c805c562c1582a0868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a350505050565b60008073ffffffffffffffffffffffffffffffffffffffff166001600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610d3f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6964656e74696669657220686173206e6f20726567697374727920656e74727981525060200191505060405180910390fd5b6001600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60006001600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b610dbf610a89565b610e31576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b610e3a81610ec5565b50565b600080828401905083811015610ebb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610f4b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806110126026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60003390509056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373a265627a7a7231582014bf4b544961f87b7ea2d17ba279c780eee7a908a2f78a1ad2bf2875ce45763164736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000d001": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f001", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d002": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f002", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": "0x000000000000000000000000000000000000000000000000000000000000d008", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf7": "0x000000000000000000000000000000000000000000000000000000000000d024", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" }, - "nonce": "0xdeadbeefdeadbeef", - "timestamp": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x80000000", - "difficulty": "0x20000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "coinbase": "0x0000000000000000000000000000000000000000", - "alloc": { - "71562b71999873db5b286df957af199ec94617f7": { - "balance": "0xffffffffffffffffffffffffff" - } + "balance": "0x0" + }, + "000000000000000000000000000000000000d003": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b801", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f003", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" }, - "number": "0x0", - "gasUsed": "0x0", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" -} + "balance": "0x0" + }, + "000000000000000000000000000000000000d004": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000000000000000000000000000000000000000012c", + "0x001e4c0503fbf2e78ed29e91878976820db477f49c7cee92132ee4be1d44a2c0": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x07a0b62bfd6cfc8172938de86a2002d7b5ee749982a3bb8ee6f2a6c92fd40e6b": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000", + "0x07a0b62bfd6cfc8172938de86a2002d7b5ee749982a3bb8ee6f2a6c92fd40e6c": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000", + "0x07a0b62bfd6cfc8172938de86a2002d7b5ee749982a3bb8ee6f2a6c92fd40e6d": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x07a0b62bfd6cfc8172938de86a2002d7b5ee749982a3bb8ee6f2a6c92fd40e70": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000", + "0x0978e7c453bd1b10febf4123eeedc5f616587ae8672e58b0849c47ffe1eef2ca": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0beb9968b5ced0222bac3a1115a9434c9f6a978020c6c12fa8a14f4ed8bd240d": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0d3b7d0839c558877cd4527b8e8005c8fae31e528b1f17c00c5f6a4738b1ff7c": "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x23704e2490fbae8a38af5fd0bfa3ac31ffd676abce71112668d090cbb48b14f5": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x25a7cd3222b924e910e8adc63d7ade07f052b3a8b991ab6802f34717cabb5d30": "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x2e7dd1fab4af71d56398ec765a742e5bc4cd5cdac0e8c39ef2cd13df8f936c86": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f004", + "0x437460e452dce9439f8a9ec3dbf3bc18a77bfcbfd7d923f0dbf2194514b293a2": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x4d6b8703a52deaaad795caf23e8a1b3f255291bf95b246b2f64bf3f669d0d958": "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000", + "0x5c0318101ad9d39f2a6ef70ad6e0f12e68bb0a200e70185bc8c22cb452147df1": "0x00000000000000000000000000000000000000000000000000000000615dfa21", + "0x7a99d521a86fc12ab824fb762af869390f3a29b01b81ce2b5f49b2a49006b6c3": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x8042dd3cf2aad6c7f6be872c851f50834f7d918245ce976dc287fa2d0d98457b": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000", + "0x8042dd3cf2aad6c7f6be872c851f50834f7d918245ce976dc287fa2d0d98457c": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000", + "0x8042dd3cf2aad6c7f6be872c851f50834f7d918245ce976dc287fa2d0d98457d": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x8042dd3cf2aad6c7f6be872c851f50834f7d918245ce976dc287fa2d0d984580": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000", + "0x80caaeeace4d8f5bd27ec10957e6a2d690c1d7b931e4fcbc8bf9090b7c4836a2": "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000", + "0x90686d1420fd133619d7edd8ebd2797496cb285a7dd5e19c0e0db704da8bf352": "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x930131e9862a130287fdbc94fa765cf1b85883503df05fab140f74ff01d5bb60": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000", + "0x930131e9862a130287fdbc94fa765cf1b85883503df05fab140f74ff01d5bb61": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000", + "0x930131e9862a130287fdbc94fa765cf1b85883503df05fab140f74ff01d5bb62": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x930131e9862a130287fdbc94fa765cf1b85883503df05fab140f74ff01d5bb65": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xb9ecb924452c59641b63dead705975f72ead7ba7d8b0fa4ab52777e7b7def309": "0x0000000000000000000000000000000000000000000000000000000000000003", + "0xba4938ab2de8a9e2a5520e9a13180b574bf2b69fbb73507b3b6b9d7c336d2509": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xe9ab6d9af889d274bf853a4111020c8700f606d423e5ef7845613647e3210c08": "0x00000000000000000000000000000000000000000000000000000000615dfa21", + "0xf68f31b0a34908329e090bbc11708f0907bdf5a86c81d765f9e61941a73f425b": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xfb02e7800380c3761f3b7ed4fca25055fefb17e8cd22b9c62a183cd9454ccc5d": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xff91e89845a878c184c13bf4b2b5a9dd7ffcbfc86208a7863c18551144859076": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000", + "0xff91e89845a878c184c13bf4b2b5a9dd7ffcbfc86208a7863c18551144859077": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000", + "0xff91e89845a878c184c13bf4b2b5a9dd7ffcbfc86208a7863c18551144859078": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xff91e89845a878c184c13bf4b2b5a9dd7ffcbfc86208a7863c1855114485907b": "0xf08cf7b0bbf851a89b0044a31241b722774bb8b8000000000000000000000000" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d005": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000005f5e100", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000005f5e100", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000069e10de76676d0800000", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000069e10de76676d0800000", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f005", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d006": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f006", + "0x711b55d2f08093e0906894ff3e727f3fff429b866243e3575b1b6d54b00f53b9": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d007": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x00000000000000000000000000000000000000000000000000000000bbf81e00", + "0x000000000000000000000000000000000000000000000000000000000000000c": "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x0000000000000000000000000000000000000000000000000000000000000010": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x0000000000000000000000000000000000000000000000000000000000000012": "0x00000000000000000000000000000000000000000000000000000000000049da", + "0x0b2c95dbe217d38133b333fe4633824d5c413792c4234bceeb674bee68767322": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x35beac6488ba161f51907ab4a4fdca29e33df771f8ec0f2a4add06bfe653f266": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f007", + "0x486533e5ef5711c6fceba0b8e8d907d58b0d418a02599d00d65a64e01c112d77": "0x0000000000000000000000000000000000000000000069e10de76676d0800000", + "0x57cdb9ef7213bbbf7c5fc0fb4e7e34c6b2f85ede46c59664e6628544e8e0fc7a": "0x000000000000000000000000000000000000000000003f870857a3e0e3800000", + "0x7e473b39842f5492dcf17de24abfb7efcf57498a6bfddd15fa3c4b3eb85e66e0": "0x000000000000000000000000000000000000000000001fc3842bd1f071c00000", + "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": "0x000000000000000000000000000000000000000000000000000000000000d008", + "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19c": "0x000000000000000000000000000000000000000000000000000000000000d024", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xc803fc9067f1a5bdfaa8afc48959a846c6644578db912825f2e6d7ae68d3c8a5": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xd8569f4bacb6d2b673d2c7ec8f0a845093274b42772c4f079bc9f4fab8ada8b7": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7": "0x63474c4400000000000000000000000000000000000000000000000000000000", + "0xdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8": "0x4254430000000000000000000000000000000000000000000000000000000000", + "0xdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c9": "0x4554480000000000000000000000000000000000000000000000000000000000", + "0xdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8ca": "0x4441490000000000000000000000000000000000000000000000000000000000" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d008": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x43656c6f20446f6c6c6172000000000000000000000000000000000000000016", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x6355534400000000000000000000000000000000000000000000000000000008", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000012", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000069e10de76676d0800000", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000", + "0x0000000000000000000000000000000000000000000000000000000000000009": "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000", + "0x000000000000000000000000000000000000000000000000000000000000000a": "0x0000000000000000000000000000000000000000000000000000000003c26700", + "0x000000000000000000000000000000000000000000000000000000000000000b": "0x00000000000000000000000000000000000000000000000000000000615dfa21", + "0x000000000000000000000000000000000000000000000000000000000000000c": "0xddd112a261429abc594f5771eb08d7fa47bff456b2e5f1a47907b78573e33d96", + "0x261e732f7947f3a57ec95fb794b549fbf243e0407155f45e76092c6dcd3edb5a": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x2d34a4ab7728e93a96971d0e81f3b8493e58489fd3665370d9a4972235453de7": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x317d75fbbf6f68327d2b7402218ced2e491b558f3a9efe54919f8070f15cc1bf": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f008", + "0x832d485c2af9b080939eb699d8ba2f0702d80a38f66d40bb18f154d24fc6a2ce": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x97823d62bda2748f4a1b145234fc8b383c25fc1f97e1ff2b86c3bcf8ae6d7add": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xb5c50026c41f6e17650a93e5148e603165f591a34b734c897cf4a364a1dda002": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xb7047d9ea1104d24bae6eb9fcc06e03fa4ab28e16153c452122409408242d591": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xc7fe9684d7a9625e7f90c3a904a17d5f868c68a6b05254670fc2387ba4218957": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xeb10ebddaaef4aae8f0e58a54838b516b4889b73f9b30df57433d7608a569ea6": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xf6b718911c51d885cae307119ddeb3b6e944d7382cf9b0ce637f0c4ebd62329e": "0x000000000000000000000000000000000000000000000a968163f0a57b400000" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d009": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b801", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x00000000000000000000000000000000000000000000010f0cf064dd59200000", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000000000000000000000000000000000000000d008", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x00000000000000000000000000000000000000000000000000000000615dfa21", + "0x0000000000000000000000000000000000000000000000000000000000000009": "0x000000000000000000000000000000000000000000000000000000000000012c", + "0x000000000000000000000000000000000000000000000000000000000000000a": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f009", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d010": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000ce1001", + "0x0000000000000000000000000000000000000000000000000000000000000007": "0xd9389a73daee90803ab4402bacf4b04514acc6c72072e8045fe3bab63533689e", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f010", + "0x66065e70d9ee923d8742517c8f75983faaae11a1789ae2fca099f7ef675d228d": "0x0466ccc9b9cbe31b9772a48c454dfe6f97cd4e118d6a9cb76ac114418053f12a", + "0x66065e70d9ee923d8742517c8f75983faaae11a1789ae2fca099f7ef675d228e": "0x333082c2c2e84a3c219b1ec647d5e3c331963634f7fd077703406a7da65e08b4", + "0x66065e70d9ee923d8742517c8f75983faaae11a1789ae2fca099f7ef675d228f": "0xff00000000000000000000000000000000000000000000000000000000000000", + "0x6b866babff3247a7b71da30853feaf1c930ddd4eddce6ce12402c07b1840257d": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x6b866babff3247a7b71da30853feaf1c930ddd4eddce6ce12402c07b18402582": "0x67726f7570203030300000000000000000000000000000000000000000000012", + "0x6b866babff3247a7b71da30853feaf1c930ddd4eddce6ce12402c07b18402583": "0x0000000000000000000000000000000000000000000000000000000000000083", + "0xb0f825f69d418c84de5db08d8548b897cca9dd4e1810cfc833f14bf91724aacf": "0x0467bd61e1495f6a1efb959e898c0f7fcf3238e33c57916141269a21eac152d5", + "0xb0f825f69d418c84de5db08d8548b897cca9dd4e1810cfc833f14bf91724aad0": "0xf47a6b01c8842d190287dcd39d71ee27f9578e054c88a543a573d10ad6a37f0e", + "0xb0f825f69d418c84de5db08d8548b897cca9dd4e1810cfc833f14bf91724aad1": "0x1100000000000000000000000000000000000000000000000000000000000000", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xd19ddcd9e3b67493ad09f69fc8ba2e482881f6d5eb339eb03eef21c51b1df86b": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xd19ddcd9e3b67493ad09f69fc8ba2e482881f6d5eb339eb03eef21c51b1df870": "0x76616c696461746f72203030300000000000000000000000000000000000001a", + "0xd19ddcd9e3b67493ad09f69fc8ba2e482881f6d5eb339eb03eef21c51b1df871": "0x0000000000000000000000000000000000000000000000000000000000000083" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d011": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b801", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", + "0x0000000000000000000000000000000000000000000000000000000000000007": "0x000000000000000000000000000000000000000000000000000000000003f480", + "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0": "0x49399c1f1c39c8fa6ef22452af2bd0f965b694cb662347489bd8de38891eacd2", + "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db1": "0xd4c6fa7b845999fadc6b2d61b758ebce59acf3c4975a3ba44c3863a6ca385b21", + "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db2": "0xf85a8831772b00f60d09cf91c6167852af544ccf577454fa9a7f1277b735491d", + "0x09e2b1ec7636dd982e5b5a6181ae716388c1cb8201b69fc2ca077d3f606154b3": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x1fb6721ecb9d1e8ab201429fc24ac401c175148dd7658aada7db47699444bfc0": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f011", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xd19ddcd9e3b67493ad09f69fc8ba2e482881f6d5eb339eb03eef21c51b1df86b": "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", + "0xeecf7c67a6662c150a8b082baccdcdaba780bd5e156d8b03d7501e34fa8fc941": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "balance": "0x43c33c1937564800000" + }, + "000000000000000000000000000000000000d012": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000006", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000ce1001", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000007": "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x00000000000000000000000000000000000000000000000000000000004f1a00", + "0x0000000000000000000000000000000000000000000000000000000000000009": "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", + "0x000000000000000000000000000000000000000000000000000000000000000a": "0x0000000000000000000000000000000000000000000000000000000000ed4e00", + "0x000000000000000000000000000000000000000000000000000000000000000b": "0x000000000000000000000000000000000000000000000000000000000000000a", + "0x000000000000000000000000000000000000000000000000000000000000000c": "0x00000000000000000000000000000000000000000000152d02c7e14af6800000", + "0x000000000000000000000000000000000000000000000000000000000000000d": "0x000000000000000000000000000000000000000000000000000000000000003c", + "0x000000000000000000000000000000000000000000000000000000000000000e": "0x0000000000000000000000000000000000000000000000000000000000000005", + "0x000000000000000000000000000000000000000000000000000000000000000f": "0x000000000000000000000000000000000000000000000000000000000000ca80", + "0x0000000000000000000000000000000000000000000000000000000000000010": "0x0000000000000000000000000000000000000000000000000000000000278d00", + "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0": "0x0000000000000000000000000443514737b4e2efa0309c250cf9d3d72572c9b6", + "0x2bca50c95146df3244e02b8bc92d04811cb689e9864e0695fa505f58cd8dd99b": "0x67bd61e1495f6a1efb959e898c0f7fcf3238e33c57916141269a21eac152d5f4", + "0x2bca50c95146df3244e02b8bc92d04811cb689e9864e0695fa505f58cd8dd99c": "0x7a6b01c8842d190287dcd39d71ee27f9578e054c88a543a573d10ad6a37f0e11", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f012", + "0x5fc1cab307f3bc70ff2db748692f73eb43fa06e0d170cdef311730f8301b0b0a": "0x40d2ebd74fafb063047a458d983eb642cb7d97bf73d042e29c5ae217374f0b69", + "0x5fc1cab307f3bc70ff2db748692f73eb43fa06e0d170cdef311730f8301b0b0b": "0xc73d67fe5116a88a3e2a2fd22893a600fa79ddde9107cb516184094aa1789840", + "0x5fc1cab307f3bc70ff2db748692f73eb43fa06e0d170cdef311730f8301b0b0c": "0xf86658f42ad8960f7d76ac31f47ad794454e5c8dbbc1c5ff5838538bb0386800", + "0x6b866babff3247a7b71da30853feaf1c930ddd4eddce6ce12402c07b1840257d": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x6b866babff3247a7b71da30853feaf1c930ddd4eddce6ce12402c07b1840257e": "0x039eaa4f6c1ff4586873852ef3c1b81a44d2729e000000000000000000000000", + "0x6b866babff3247a7b71da30853feaf1c930ddd4eddce6ce12402c07b1840257f": "0x039eaa4f6c1ff4586873852ef3c1b81a44d2729e000000000000000000000000", + "0x6b866babff3247a7b71da30853feaf1c930ddd4eddce6ce12402c07b18402580": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x6b866babff3247a7b71da30853feaf1c930ddd4eddce6ce12402c07b18402582": "0x00000000000000000000000000000000000000000000152d02c7e14af6800000", + "0x6b866babff3247a7b71da30853feaf1c930ddd4eddce6ce12402c07b18402585": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x6b866babff3247a7b71da30853feaf1c930ddd4eddce6ce12402c07b18402586": "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000", + "0x9dbb2ad1bb29fe39a63cac988edbe4df0c30673361c63c525e28760f2f67acc7": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xbaf30c4fcc05fde501f6b6e3017e8243c6450f6fe02271df0cf05987e7858d6a": "0x0000000000000000000000000000000000000000000000000000000000000081", + "0xbaf30c4fcc05fde501f6b6e3017e8243c6450f6fe02271df0cf05987e7858d6b": "0x00000000000000000000000000000000000000000000000000000000000000c1", + "0xbaf30c4fcc05fde501f6b6e3017e8243c6450f6fe02271df0cf05987e7858d6c": "0x0000000000000000000000000443514737b4e2efa0309c250cf9d3d72572c9b6", + "0xbaf30c4fcc05fde501f6b6e3017e8243c6450f6fe02271df0cf05987e7858d6f": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xbeeef002bb22d374e58b66ac37154ac9eba42d1417fe4c60c7151a6f28dd40b5": "0x0000000000000000000000000443514737b4e2efa0309c250cf9d3d72572c9b6", + "0xed4118490bb3739e094b4fe659ded789107faafb9bb1b7f79b877d5079c58bf8": "0x00000000000000000000000000000000000000000000000000000000615dfa21", + "0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f": "0x000000000000000000000000039eaa4f6c1ff4586873852ef3c1b81a44d2729e" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d013": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000ce1001", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", + "0x0000000000000000000000000000000000000000000000000000000000000007": "0x0443514737b4e2efa0309c250cf9d3d72572c9b6000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x0443514737b4e2efa0309c250cf9d3d72572c9b6000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000009": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x000000000000000000000000000000000000000000000000000000000000000d": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x000000000000000000000000000000000000000000000000000000000000000e": "0x0000000000000000000000000000000000000000000000000000000000000064", + "0x000000000000000000000000000000000000000000000000000000000000000f": "0x000000000000000000000000000000000000000000000000000000000000000a", + "0x0000000000000000000000000000000000000000000000000000000000000010": "0x00000000000000000000000000000000000000000000003635c9adc5dea00000", + "0x179cad8364fb7c26e46c0f3205ef17d7b0a73cc6ca96018c3eaf751a8f034336": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f013", + "0x6ba905204bd83f8afb15673231b2404cbb27c9726120de4e529678fa377ed936": "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", + "0x8ce0180647891c3a7dbb9c6755c8e49eb19332451227528978871348d0fd8ab6": "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xb82f26e0f4265c2cb73185d185c777d57721e11eb24792114faa094f01e46030": "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", + "0xc7f8d4754d2079af5b8f81e1a8611f52f70baef2d8dfc4a0a0971a9033a70234": "0x0000000000000000000000000443514737b4e2efa0309c250cf9d3d72572c9b6", + "0xf856cbe07e3b927f2e3a0580df05c7fa359b74cc9279e688fd2bf50d7a6a08c7": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d014": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x00000000000000000000000000000000000000000000000000000000615dfa21", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000069e10de76676d0800000", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000422ca8b0a00a425000000", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000000000000000000000001a784379d99db42000000", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x00000000000000000000000000000000000000000000001b1ae4d6e2ef500000", + "0x0000000000000000000000000000000000000000000000000000000000000009": "0x0000000000000000000000000000000000000000000069e10de76676d0800000", + "0x000000000000000000000000000000000000000000000000000000000000000a": "0x0000000000000000000000000000000000000000000034f086f3b33b68400000", + "0x000000000000000000000000000000000000000000000000000000000000000b": "0x00000000000000000000000000000000000000000000003635c9adc5dea00000", + "0x000000000000000000000000000000000000000000000000000000000000000d": "0x00000000000000000000000000000000000000000000000b2399a8f78fefc7e3", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f014", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d015": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x00000000000000000000000000000000000000000000000000000000000002d0", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f015", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d016": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x00000000000000000000000000000000000000000000000000000000000002d0", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000000000064", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f016", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xb8f8251e709071e143cba5fbf46dd864fb760f2c6513325fc286c82a76e020bc": "0x00000000000000000000000000000000000000000000000000b1a2bc2ec50000" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d017": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f017", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d018": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000c65d40", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000000000000000000000000000000000000000c350", + "0x0000000000000000000000000000000000000000000000000000000000000007": "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f018", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d019": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f019", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d020": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000001e7e4171bf4d3a00000", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x00000000000000000000000000000000000000000000003635c9adc5dea00000", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f020", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d021": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f021", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d022": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f022", + "0x711b55d2f08093e0906894ff3e727f3fff429b866243e3575b1b6d54b00f53b9": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d023": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000708", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000e10", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000015180", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x000000000000000000000000000000000000000000000000000000000024ea00", + "0x0000000000000000000000000000000000000000000000000000000000000007": "0x0000000000000000000000000000000000000000000000000000000000000708", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x000000000000000000000000000000000000000000000000000000000000d022", + "0x0000000000000000000000000000000000000000000000000000000000000009": "0x00000000000000000000000000000000000000000000000000000000615dfa21", + "0x000000000000000000000000000000000000000000000000000000000000000a": "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x000000000000000000000000000000000000000000000000000000000000000c": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000", + "0x0000000000000000000000000000000000000000000000000000000000000019": "0x00000000000000000000000000000000000000000000010f0cf064dd59200000", + "0x000000000000000000000000000000000000000000000000000000000000001a": "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", + "0x000000000000000000000000000000000000000000000000000000000000001b": "0x000000000000000000000000000000000000000000002a5a058fc295ed000000", + "0x000000000000000000000000000000000000000000000000000000000000001c": "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f023", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d024": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x43656c6f204575726f0000000000000000000000000000000000000000000012", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x6345555200000000000000000000000000000000000000000000000000000008", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000012", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000069e10de76676d0800000", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000", + "0x0000000000000000000000000000000000000000000000000000000000000009": "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000", + "0x000000000000000000000000000000000000000000000000000000000000000a": "0x0000000000000000000000000000000000000000000000000000000003c26700", + "0x000000000000000000000000000000000000000000000000000000000000000b": "0x00000000000000000000000000000000000000000000000000000000615dfa21", + "0x000000000000000000000000000000000000000000000000000000000000000c": "0xec9430f559dcce86affd707bcc9fd6dfd7935b4cb934e078882caa95ebf975d6", + "0x261e732f7947f3a57ec95fb794b549fbf243e0407155f45e76092c6dcd3edb5a": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x2d34a4ab7728e93a96971d0e81f3b8493e58489fd3665370d9a4972235453de7": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x317d75fbbf6f68327d2b7402218ced2e491b558f3a9efe54919f8070f15cc1bf": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f024", + "0x832d485c2af9b080939eb699d8ba2f0702d80a38f66d40bb18f154d24fc6a2ce": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x97823d62bda2748f4a1b145234fc8b383c25fc1f97e1ff2b86c3bcf8ae6d7add": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0xb5c50026c41f6e17650a93e5148e603165f591a34b734c897cf4a364a1dda002": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xb7047d9ea1104d24bae6eb9fcc06e03fa4ab28e16153c452122409408242d591": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xc7fe9684d7a9625e7f90c3a904a17d5f868c68a6b05254670fc2387ba4218957": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xeb10ebddaaef4aae8f0e58a54838b516b4889b73f9b30df57433d7608a569ea6": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xf6b718911c51d885cae307119ddeb3b6e944d7382cf9b0ce637f0c4ebd62329e": "0x000000000000000000000000000000000000000000000a968163f0a57b400000" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000d025": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b801", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x00000000000000000000000000000000000000000000010f0cf064dd59200000", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000000000000000000000000000000000000000d024", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x00000000000000000000000000000000000000000000000000000000615dfa21", + "0x0000000000000000000000000000000000000000000000000000000000000009": "0x000000000000000000000000000000000000000000000000000000000000012c", + "0x000000000000000000000000000000000000000000000000000000000000000a": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000000000000000000000000000000000000000f025", + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8" + }, + "balance": "0x0" + }, + "000000000000000000000000000000000000f001": { + "code": "0x608060405234801561001057600080fd5b50600436106100935760003560e01c80638d1fdf2f116100665780638d1fdf2f146101125780638da5cb5b146101565780638f32d59b146101a0578063e5839836146101c2578063f2fde38b1461021e57610093565b8063158ef93e1461009857806345c8b1a6146100ba578063715018a6146100fe5780638129fc1c14610108575b600080fd5b6100a0610262565b604051808215151515815260200191505060405180910390f35b6100fc600480360360208110156100d057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610275565b005b61010661034a565b005b610110610483565b005b6101546004803603602081101561012857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061052c565b005b61015e610600565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101a8610629565b604051808215151515815260200191505060405180910390f35b610204600480360360208110156101d857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610687565b604051808215151515815260200191505060405180910390f35b6102606004803603602081101561023457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506106a7565b005b600060149054906101000a900460ff1681565b61027d610629565b6102ef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b610352610629565b6103c4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600060149054906101000a900460ff1615610506576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff02191690831515021790555061052a3361072d565b565b610534610629565b6105a6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60018060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661066b610871565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b60016020528060005260406000206000915054906101000a900460ff1681565b6106af610629565b610721576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61072a8161072d565b50565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156107b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061087a6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60003390509056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373a265627a7a7231582074ce3308f04c2aa3c7237b26578a0a11f6e8dbe2cd6cc2812dce676c6bd6a39464736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f002": { + "code": "0x608060405234801561001057600080fd5b50600436106100935760003560e01c80638da5cb5b116100665780638da5cb5b1461013c5780638f32d59b14610186578063d01f63f5146101a8578063d48bfca714610207578063f2fde38b1461024b57610093565b8063158ef93e14610098578063715018a6146100ba5780637ebd1b30146100c45780638129fc1c14610132575b600080fd5b6100a061028f565b604051808215151515815260200191505060405180910390f35b6100c26102a2565b005b6100f0600480360360208110156100da57600080fd5b81019080803590602001909291905050506103db565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61013a610417565b005b6101446104c0565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61018e6104e9565b604051808215151515815260200191505060405180910390f35b6101b0610547565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156101f35780820151818401526020810190506101d8565b505050509050019250505060405180910390f35b6102496004803603602081101561021d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506105d5565b005b61028d6004803603602081101561026157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506106b8565b005b600060149054906101000a900460ff1681565b6102aa6104e9565b61031c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600181815481106103e857fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff161561049a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff0219169083151502179055506104be3361073e565b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661052b610882565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b606060018054806020026020016040519081016040528092919081815260200182805480156105cb57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610581575b5050505050905090565b6105dd6104e9565b61064f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60018190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6106c06104e9565b610732576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61073b8161073e565b50565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156107c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061088b6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60003390509056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373a265627a7a7231582046c6127ccc1fbb4091dea2f0746beffd9a630578d15b377f382cd56efec62a8964736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f003": { + "code": "0x608060405234801561001057600080fd5b506004361061014d5760003560e01c80637b103999116100c3578063a91ee0dc1161007c578063a91ee0dc1461068b578063b921e163146106cf578063c4d66de8146106fd578063dd62ed3e14610741578063e1d6aceb146107b9578063f2fde38b146108745761014d565b80637b103999146104865780638da5cb5b146104d05780638f32d59b1461051a57806395d89b411461053c578063a457c2d7146105bf578063a9059cbb146106255761014d565b8063313ce56711610115578063313ce56714610301578063395093511461032557806340c10f191461038b57806354255be0146103f157806370a0823114610424578063715018a61461047c5761014d565b806306fdde0314610152578063095ea7b3146101d5578063158ef93e1461023b57806318160ddd1461025d57806323b872dd1461027b575b600080fd5b61015a6108b8565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561019a57808201518184015260208101905061017f565b50505050905090810190601f1680156101c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610221600480360360408110156101eb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506108f5565b604051808215151515815260200191505060405180910390f35b610243610a89565b604051808215151515815260200191505060405180910390f35b610265610a9b565b6040518082815260200191505060405180910390f35b6102e76004803603606081101561029157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610aa5565b604051808215151515815260200191505060405180910390f35b610309610f9c565b604051808260ff1660ff16815260200191505060405180910390f35b6103716004803603604081101561033b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610fa5565b604051808215151515815260200191505060405180910390f35b6103d7600480360360408110156103a157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506111d3565b604051808215151515815260200191505060405180910390f35b6103f9611556565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b6104666004803603602081101561043a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061157c565b6040518082815260200191505060405180910390f35b61048461159d565b005b61048e6116d7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6104d86116fd565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610522611726565b604051808215151515815260200191505060405180910390f35b610544611784565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610584578082015181840152602081019050610569565b50505050905090810190601f1680156105b15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61060b600480360360408110156105d557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506117c1565b604051808215151515815260200191505060405180910390f35b6106716004803603604081101561063b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061194c565b604051808215151515815260200191505060405180910390f35b6106cd600480360360208110156106a157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611960565b005b6106fb600480360360208110156106e557600080fd5b8101908080359060200190929190505050611b04565b005b61073f6004803603602081101561071357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611bc4565b005b6107a36004803603604081101561075757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c7d565b6040518082815260200191505060405180910390f35b61085a600480360360608110156107cf57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561081657600080fd5b82018360208201111561082857600080fd5b8035906020019184600183028401116401000000008311171561084a57600080fd5b9091929391929390505050611d04565b604051808215151515815260200191505060405180910390f35b6108b66004803603602081101561088a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611d82565b005b60606040518060400160405280601181526020017f43656c6f206e6174697665206173736574000000000000000000000000000000815250905090565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610999576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f63616e6e6f742073657420616c6c6f77616e636520666f72203000000000000081525060200191505060405180910390fd5b81600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b6000809054906101000a900460ff1681565b6000600254905090565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610b2c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a8152602001806124a8602a913960400191505060405180910390fd5b610b358461157c565b821115610b8d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806124476029913960400191505060405180910390fd5b600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054821115610c62576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260388152602001806124706038913960400191505060405180910390fd5b600060fd73ffffffffffffffffffffffffffffffffffffffff1660005a90878787604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d3b5780518252602082019150602081019050602083039250610d18565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381858888f193505050503d8060008114610d9e576040519150601f19603f3d011682016040523d82523d6000602084013e610da3565b606091505b50508091505080610e1c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f43454c4f207472616e73666572206661696c656400000000000000000000000081525060200191505060405180910390fd5b610eab83600360008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054611e0890919063ffffffff16565b600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60006012905090565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611049576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f63616e6e6f742073657420616c6c6f77616e636520666f72203000000000000081525060200191505060405180910390fd5b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905060006110df8483611e5290919063ffffffff16565b905080600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a360019250505092915050565b60008073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611276576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b60008214156112885760019050611550565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561130e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806123fb6026913960400191505060405180910390fd5b61132382600254611e5290919063ffffffff16565b600281905550600060fd73ffffffffffffffffffffffffffffffffffffffff1660005a9060008787604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200193505050506040516020818303038152906040526040518082805190602001908083835b6020831061140357805182526020820191506020810190506020830392506113e0565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381858888f193505050503d8060008114611466576040519150601f19603f3d011682016040523d82523d6000602084013e61146b565b606091505b505080915050806114e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f43454c4f207472616e73666572206661696c656400000000000000000000000081525060200191505060405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150505b92915050565b600080600080600180600180839350829250819150809050935093509350935090919293565b60008173ffffffffffffffffffffffffffffffffffffffff16319050919050565b6115a5611726565b611617576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16611768611eda565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b60606040518060400160405280600481526020017f43454c4f00000000000000000000000000000000000000000000000000000000815250905090565b600080600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905060006118588483611e0890919063ffffffff16565b905080600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a360019250505092915050565b60006119588383611ee2565b905092915050565b611968611726565b6119da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611a7d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611ba6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b611bbb81600254611e5290919063ffffffff16565b60028190555050565b6000809054906101000a900460ff1615611c46576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b60016000806101000a81548160ff0219169083151502179055506000600281905550611c71336121f4565b611c7a81611960565b50565b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600080611d118686611ee2565b90507fe5d4e30fb8364e57bc4d662a07d0cf36f4c34552004c4c3624620a2c1d1c03dc848460405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a180915050949350505050565b611d8a611726565b611dfc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b611e05816121f4565b50565b6000611e4a83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061233a565b905092915050565b600080828401905083811015611ed0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600033905090565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611f69576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a8152602001806124a8602a913960400191505060405180910390fd5b611f723361157c565b821115611fca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806124476029913960400191505060405180910390fd5b600060fd73ffffffffffffffffffffffffffffffffffffffff1660005a90338787604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200193505050506040516020818303038152906040526040518082805190602001908083835b602083106120a35780518252602082019150602081019050602083039250612080565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381858888f193505050503d8060008114612106576040519150601f19603f3d011682016040523d82523d6000602084013e61210b565b606091505b50508091505080612184576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f43454c4f207472616e73666572206661696c656400000000000000000000000081525060200191505060405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561227a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806124216026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600060016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008383111582906123e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156123ac578082015181840152602081019050612391565b50505050905090810190601f1680156123d95780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838503905080915050939250505056fe6d696e7420617474656d7074656420746f2072657365727665642061646472657373203078304f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573737472616e736665722076616c75652065786365656465642062616c616e6365206f662073656e6465727472616e736665722076616c75652065786365656465642073656e646572277320616c6c6f77616e636520666f7220726563697069656e747472616e7366657220617474656d7074656420746f207265736572766564206164647265737320307830a265627a7a723158202a28f294255bc558be8943e3fd61974af8c78ccef033cf1571b498b95e2e8bfb64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f004": { + "code": "0x608060405234801561001057600080fd5b506004361061018e5760003560e01c80638e749281116100de578063ebc1d6bb11610097578063f2fde38b11610071578063f2fde38b14610a64578063fc20935d14610aa8578063fe4b84df14610af6578063ffe736bf14610b245761018e565b8063ebc1d6bb14610973578063ef90e1b0146109a1578063f0ca4adb14610a005761018e565b80638e7492811461065b5780638f32d59b146106f4578063a00a8b2c14610716578063b9292158146107a4578063bbc66a94146108cd578063dd34ca3b146109255761018e565b806353a572971161014b5780636deb6799116101255780636deb679914610521578063715018a61461057957806380e50744146105835780638da5cb5b146106115761018e565b806353a572971461042857806354255be0146104965780636dd6ef0c146104c95761018e565b806302f55b6114610193578063071b48fc146102bc578063158ef93e146103145780632e86bc0114610336578063370c998e1461038e578063493a353c1461040a575b600080fd5b6101d5600480360360208110156101a957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bb3565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015610220578082015181840152602081019050610205565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015610262578082015181840152602081019050610247565b50505050905001848103825285818151815260200191508051906020019060200280838360005b838110156102a4578082015181840152602081019050610289565b50505050905001965050505050505060405180910390f35b6102fe600480360360208110156102d257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e4f565b6040518082815260200191505060405180910390f35b61031c610f22565b604051808215151515815260200191505060405180910390f35b6103786004803603602081101561034c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610f35565b6040518082815260200191505060405180910390f35b6103f0600480360360408110156103a457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610f4d565b604051808215151515815260200191505060405180910390f35b610412610f7c565b6040518082815260200191505060405180910390f35b6104946004803603606081101561043e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610f82565b005b61049e6114be565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b61050b600480360360208110156104df57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506114e5565b6040518082815260200191505060405180910390f35b6105636004803603602081101561053757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506115b8565b6040518082815260200191505060405180910390f35b610581611653565b005b61060f6004803603608081101561059957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061178c565b005b610619612163565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61069d6004803603602081101561067157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061218c565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106e05780820151818401526020810190506106c5565b505050509050019250505060405180910390f35b6106fc612259565b604051808215151515815260200191505060405180910390f35b6107626004803603604081101561072c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506122b7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6107e6600480360360208110156107ba57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612302565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015610831578082015181840152602081019050610816565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015610873578082015181840152602081019050610858565b50505050905001848103825285818151815260200191508051906020019060200280838360005b838110156108b557808201518184015260208101905061089a565b50505050905001965050505050505060405180910390f35b61090f600480360360208110156108e357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061259e565b6040518082815260200191505060405180910390f35b6109716004803603604081101561093b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612671565b005b61099f6004803603602081101561098957600080fd5b8101908080359060200190929190505050612827565b005b6109e3600480360360208110156109b757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612996565b604051808381526020018281526020019250505060405180910390f35b610a6260048036036040811015610a1657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612a8f565b005b610aa660048036036020811015610a7a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612df1565b005b610af460048036036040811015610abe57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612e77565b005b610b2260048036036020811015610b0c57600080fd5b8101908080359060200190929190505050613095565b005b610b6660048036036020811015610b3a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613148565b60405180831515151581526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390f35b6060806060600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a009636cfa387390916040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b158015610c4757600080fd5b505af4158015610c5b573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052506060811015610c8557600080fd5b8101908080516040519392919084640100000000821115610ca557600080fd5b83820191506020820185811115610cbb57600080fd5b8251866020820283011164010000000082111715610cd857600080fd5b8083526020830192505050908051906020019060200280838360005b83811015610d0f578082015181840152602081019050610cf4565b5050505090500160405260200180516040519392919084640100000000821115610d3857600080fd5b83820191506020820185811115610d4e57600080fd5b8251866020820283011164010000000082111715610d6b57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015610da2578082015181840152602081019050610d87565b5050505090500160405260200180516040519392919084640100000000821115610dcb57600080fd5b83820191506020820185811115610de157600080fd5b8251866020820283011164010000000082111715610dfe57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015610e35578082015181840152602081019050610e1a565b505050509050016040525050509250925092509193909250565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a0096359d556a890916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015610ee057600080fd5b505af4158015610ef4573d6000803e3d6000fd5b505050506040513d6020811015610f0a57600080fd5b81019080805190602001909291905050509050919050565b600060149054906101000a900460ff1681565b60066020528060005260406000206000915090505481565b60036020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b60055481565b610f8a612259565b610ffc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141580156110665750600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b80156110b3575080600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080549050115b801561115e57508173ffffffffffffffffffffffffffffffffffffffff16600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020828154811061111b57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b6111b3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526056815260200180613d5c6056913960600191505060405180910390fd5b6000600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206112dd6001600460008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905061339390919063ffffffff16565b815481106112e757fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020828154811061135c57fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506113fa6001600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905061339390919063ffffffff16565b600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020816114449190613cb0565b5061144f83836133dd565b1561145f5761145e83836135eb565b5b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f6dc84b66cc948d847632b9d829f7cb1cb904fbf2c084554a9bc22ad9d845334060405160405180910390a3505050565b60008060008060018060026001839350829250819150809050935093509350935090919293565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a009636eafa6c390916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561157657600080fd5b505af415801561158a573d6000803e3d6000fd5b505050506040513d60208110156115a057600080fd5b81019080805190602001909291905050509050919050565b600080600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054141561160b57600554905061164e565b600660008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b61165b612259565b6116cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b83600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1661186c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180613e0c6027913960400191505060405180910390fd5b6000600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a0096359d556a890916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156118fd57600080fd5b505af4158015611911573d6000803e3d6000fd5b505050506040513d602081101561192757600080fd5b81019080805190602001909291905050509050600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a0096395073a799091336040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b1580156119fd57600080fd5b505af4158015611a11573d6000803e3d6000fd5b505050506040513d6020811015611a2757600080fd5b810190808051906020019092919050505015611c6857600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a00963832a21479091338888886040518663ffffffff1660e01b8152600401808681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019550505050505060006040518083038186803b158015611b7057600080fd5b505af4158015611b84573d6000803e3d6000fd5b50505050600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a00963c1e728e99091336040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060006040518083038186803b158015611c4b57600080fd5b505af4158015611c5f573d6000803e3d6000fd5b50505050611db4565b600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a00963d4a092729091338888886040518663ffffffff1660e01b8152600401808681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019550505050505060006040518083038186803b158015611d9b57600080fd5b505af4158015611daf573d6000803e3d6000fd5b505050505b600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a00963d4a0927290913342600260008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a009630944c59490916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015611ea057600080fd5b505af4158015611eb4573d6000803e3d6000fd5b505050506040513d6020811015611eca57600080fd5b810190808051906020019092919050505060006040518663ffffffff1660e01b8152600401808681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019550505050505060006040518083038186803b158015611fb157600080fd5b505af4158015611fc5573d6000803e3d6000fd5b505050503373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167f7cebb17173a9ed273d2b7538f64395c0ebf352ff743f1cf8ce66b437a61442134288604051808381526020018281526020019250505060405180910390a36000600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a0096359d556a890916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156120c757600080fd5b505af41580156120db573d6000803e3d6000fd5b505050506040513d60208110156120f157600080fd5b8101908080519060200190929190505050905081811461215a578673ffffffffffffffffffffffffffffffffffffffff167fa9981ebfc3b766a742486e898f54959b050a66006dbce1a4155c1f84a08bcf41826040518082815260200191505060405180910390a25b50505050505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561224d57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311612203575b50505050509050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661229b613a1c565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b600460205281600052604060002081815481106122d057fe5b906000526020600020016000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060806060600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a009636cfa387390916040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b15801561239657600080fd5b505af41580156123aa573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060608110156123d457600080fd5b81019080805160405193929190846401000000008211156123f457600080fd5b8382019150602082018581111561240a57600080fd5b825186602082028301116401000000008211171561242757600080fd5b8083526020830192505050908051906020019060200280838360005b8381101561245e578082015181840152602081019050612443565b505050509050016040526020018051604051939291908464010000000082111561248757600080fd5b8382019150602082018581111561249d57600080fd5b82518660208202830111640100000000821117156124ba57600080fd5b8083526020830192505050908051906020019060200280838360005b838110156124f15780820151818401526020810190506124d6565b505050509050016040526020018051604051939291908464010000000082111561251a57600080fd5b8382019150602082018581111561253057600080fd5b825186602082028301116401000000008211171561254d57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015612584578082015181840152602081019050612569565b505050509050016040525050509250925092509193909250565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a009636eafa6c390916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561262f57600080fd5b505af4158015612643573d6000803e3d6000fd5b505050506040513d602081101561265957600080fd5b81019080805190602001909291905050509050919050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156127765750600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a009636eafa6c390916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561273857600080fd5b505af415801561274c573d6000803e3d6000fd5b505050506040513d602081101561276257600080fd5b810190808051906020019092919050505081105b6127cb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526034815260200180613d286034913960400191505060405180910390fd5b60008090505b81811015612822576000806127e585613148565b9150915081156127fe576127f985826135eb565b612805565b5050612822565b505061281b600182613a2490919063ffffffff16565b90506127d1565b505050565b61282f612259565b6128a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600081116128fa576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180613e7d6021913960400191505060405180910390fd5b600554811415612955576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180613e5b6022913960400191505060405180910390fd5b806005819055507fc68a9b88effd8a11611ff410efbc83569f0031b7bc70dd455b61344c7f0a042f816040518082815260200191505060405180910390a150565b600080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a0096359d556a890916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612a2857600080fd5b505af4158015612a3c573d6000803e3d6000fd5b505050506040513d6020811015612a5257600080fd5b81019080805190602001909291905050506000612a6e8561259e565b14612a835769d3c21bcecceda1000000612a86565b60005b91509150915091565b612a97612259565b612b09576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614158015612b735750600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b8015612c065750600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16155b612c5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252605a815260200180613db2605a913960600191505060405180910390fd5b6001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f828d2be040dede7698182e08dfa8bfbd663c879aee772509c4a2bd961d0ed43f60405160405180910390a35050565b612df9612259565b612e6b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b612e7481613aac565b50565b612e7f612259565b612ef1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008111612f4a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180613e7d6021913960400191505060405180910390fd5b600660008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054811415612fe2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180613e336028913960400191505060405180910390fd5b80600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507ff8324c8592dfd9991ee3e717351afe0a964605257959e3d99b0eb3d45bff94228282604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a15050565b600060149054906101000a900460ff1615613118576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff02191690831515021790555061313c33613aac565b61314581612827565b50565b600080600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561318557600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a00963d938ec7b90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561321657600080fd5b505af415801561322a573d6000803e3d6000fd5b505050506040513d602081101561324057600080fd5b810190808051906020019092919050505090506000600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a009637c6bb8629091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b15801561331857600080fd5b505af415801561332c573d6000803e3d6000fd5b505050506040513d602081101561334257600080fd5b8101908080519060200190929190505050905061335e856115b8565b613371824261339390919063ffffffff16565b106133845760018293509350505061338e565b6000829350935050505b915091565b60006133d583836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613bf0565b905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a0096395073a799091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b1580156134a257600080fd5b505af41580156134b6573d6000803e3d6000fd5b505050506040513d60208110156134cc57600080fd5b810190808051906020019092919050505080156135e35750600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a0096395073a799091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b1580156135a757600080fd5b505af41580156135bb573d6000803e3d6000fd5b505050506040513d60208110156135d157600080fd5b81019080805190602001909291905050505b905092915050565b60016135f6836114e5565b148015613609575061360882826133dd565b5b1561361357613a18565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a0096359d556a890916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156136a457600080fd5b505af41580156136b8573d6000803e3d6000fd5b505050506040513d60208110156136ce57600080fd5b81019080805190602001909291905050509050600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a00963c1e728e99091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060006040518083038186803b1580156137a457600080fd5b505af41580156137b8573d6000803e3d6000fd5b50505050600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a00963c1e728e99091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060006040518083038186803b15801561387f57600080fd5b505af4158015613893573d6000803e3d6000fd5b505050508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fe21a44017b6fa1658d84e937d56ff408501facdb4ff7427c479ac460d76f789360405160405180910390a36000600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002073000000000000000000000000000000000000a0096359d556a890916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561398257600080fd5b505af4158015613996573d6000803e3d6000fd5b505050506040513d60208110156139ac57600080fd5b81019080805190602001909291905050509050818114613a15578373ffffffffffffffffffffffffffffffffffffffff167fa9981ebfc3b766a742486e898f54959b050a66006dbce1a4155c1f84a08bcf41826040518082815260200191505060405180910390a25b50505b5050565b600033905090565b600080828401905083811015613aa2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613b32576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180613d026026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000838311158290613c9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613c62578082015181840152602081019050613c47565b50505050905090810190601f168015613c8f5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b815481835581811115613cd757818360005260206000209182019101613cd69190613cdc565b5b505050565b613cfe91905b80821115613cfa576000816000905550600101613ce2565b5090565b9056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373746f6b656e2061646472206e756c6c206f7220747279696e6720746f2072656d6f766520746f6f206d616e79207265706f727473746f6b656e2061646472206e756c6c206f72206f7261636c652061646472206e756c6c206f7220696e646578206f6620746f6b656e206f7261636c65206e6f74206d617070656420746f206f7261636c652061646472746f6b656e206164647220776173206e756c6c206f72206f7261636c65206164647220776173206e756c6c206f72206f7261636c652061646472206973206e6f7420616e206f7261636c6520666f7220746f6b656e206164647273656e64657220776173206e6f7420616e206f7261636c6520666f7220746f6b656e2061646472746f6b656e207265706f72744578706972795365636f6e6473206861736e2774206368616e6765647265706f72744578706972795365636f6e6473206861736e2774206368616e6765647265706f727420657870697279207365636f6e6473206d757374206265203e2030a265627a7a72315820e7749c172a905785864331dac7084ce1e6d4496156e629b244e3dba60d16a95b64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f005": { + "code": "0x608060405234801561001057600080fd5b50600436106101215760003560e01c80638f32d59b116100ad578063b830f4a411610071578063b830f4a4146103ef578063c12398b41461041d578063ceff0bd614610469578063ef712c5b14610487578063f2fde38b146104d357610121565b80638f32d59b146102e557806393ca6fc414610307578063a54b7fc014610335578063a68f548e1461038d578063a91ee0dc146103ab57610121565b80634ec81af1116100f45780634ec81af1146101b257806354255be014610214578063715018a6146102475780637b103999146102515780638da5cb5b1461029b57610121565b8063158ef93e1461012657806330f726b91461014857806336945c2d146101765780634a3d5fe214610194575b600080fd5b61012e610517565b604051808215151515815260200191505060405180910390f35b6101746004803603602081101561015e57600080fd5b810190808035906020019092919050505061052a565b005b61017e61067b565b6040518082815260200191505060405180910390f35b61019c610681565b6040518082815260200191505060405180910390f35b610212600480360360808110156101c857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019092919050505061068d565b005b61021c610765565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b61024f61078c565b005b6102596108c5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102a36108eb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102ed610914565b604051808215151515815260200191505060405180910390f35b6103336004803603602081101561031d57600080fd5b8101908080359060200190929190505050610972565b005b6103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ac3565b6040518082815260200191505060405180910390f35b610395610e22565b6040518082815260200191505060405180910390f35b6103ed600480360360208110156103c157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e2e565b005b61041b6004803603602081101561040557600080fd5b8101908080359060200190929190505050610fd2565b005b6104536004803603604081101561043357600080fd5b8101908080359060200190929190803590602001909291905050506110e6565b6040518082815260200191505060405180910390f35b6104716111dd565b6040518082815260200191505060405180910390f35b6104bd6004803603604081101561049d57600080fd5b8101908080359060200190929190803590602001909291905050506111e3565b6040518082815260200191505060405180910390f35b610515600480360360208110156104e957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611389565b005b600060149054906101000a900460ff1681565b610532610914565b6105a4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6105ad8161140f565b6005600082015181600001559050506105ec6105c761142d565b600560405180602001604052908160008201548152505061145390919063ffffffff16565b610641576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180611ff86027913960400191505060405180910390fd5b7fd2e71cd7012df1df07d4908ff75ae4b2bfbb6c49d39144404661f1fd47253283816040518082815260200191505060405180910390a150565b60025481565b60048060000154905081565b600060149054906101000a900460ff1615610710576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff02191690831515021790555061073433611468565b61073d84610e2e565b8260028190555061074d83610fd2565b61075682610972565b61075f8161052a565b50505050565b60008060008060018060016000839350829250819150809050935093509350935090919293565b610794610914565b610806576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166109566115ac565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b61097a610914565b6109ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6109f58161140f565b600460008201518160000155905050610a34610a0f61142d565b600460405180602001604052908160008201548152505061145390919063ffffffff16565b610a89576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260258152602001806120cd6025913960400191505060405180910390fd5b7f2a109bad06121312708ed2a3e9b3556ea85ef8eadd4d10d8181f50d114eb4fab816040518082815260200191505060405180910390a150565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161480610c1d5750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f476f6c64546f6b656e00000000000000000000000000000000000000000000008152506009019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015610bb357600080fd5b505afa158015610bc7573d6000803e3d6000fd5b505050506040513d6020811015610bdd57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b15610c2c576002549050610e1d565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f536f727465644f7261636c657300000000000000000000000000000000000000815250600d019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015610ce757600080fd5b505afa158015610cfb573d6000803e3d6000fd5b505050506040513d6020811015610d1157600080fd5b810190808051906020019092919050505090506000808273ffffffffffffffffffffffffffffffffffffffff1663ef90e1b0866040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050604080518083038186803b158015610da357600080fd5b505afa158015610db7573d6000803e3d6000fd5b505050506040513d6040811015610dcd57600080fd5b8101908080519060200190929190805190602001909291905050508092508193505050610e1781610e09846002546115b490919063ffffffff16565b61163a90919063ffffffff16565b93505050505b919050565b60058060000154905081565b610e36610914565b610ea8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610f4b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b610fda610914565b61104c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600081116110a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603181526020018061207b6031913960400191505060405180910390fd5b806003819055507f5548a13ccc1d9e4e2860461edda5ad49ba8a4fda485f67d954f9d7da8d2aff27816040518082815260200191505060405180910390a150565b60008073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611189576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b61119383836111e3565b6002819055507f6e53b2f8b69496c2a175588ad1326dbabe2f66df4d82f817aeca52e3474807fb6002546040518082815260200191505060405180910390a1600254905092915050565b60035481565b60006111ed611fe4565b6111f78484611684565b905060006112246004604051806020016040529081600082015481525050836116c690919063ffffffff16565b905061122e611fe4565b816112615761125c8360046040518060200160405290816000820154815250506116db90919063ffffffff16565b61128b565b61128a6004604051806020016040529081600082015481525050846116db90919063ffffffff16565b5b9050611295611fe4565b826112e1576112dc6112c683600560405180602001604052908160008201548152505061178290919063ffffffff16565b6112ce61142d565b6116db90919063ffffffff16565b611324565b61132361130d83600560405180602001604052908160008201548152505061178290919063ffffffff16565b61131561142d565b611be190919063ffffffff16565b5b9050600061136661136161133661142d565b611353611344600254611c8a565b8661178290919063ffffffff16565b611be190919063ffffffff16565b611d14565b905060035481101561137a5760035461137c565b805b9550505050505092915050565b611391610914565b611403576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61140c81611468565b50565b611417611fe4565b6040518060200160405280838152509050919050565b611435611fe4565b604051806020016040528069d3c21bcecceda1000000815250905090565b60008160000151836000015110905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156114ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061201f6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600033905090565b6000808314156115c75760009050611634565b60008284029050828482816115d857fe5b041461162f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806120ac6021913960400191505060405180910390fd5b809150505b92915050565b600061167c83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250611d35565b905092915050565b61168c611fe4565b611694611fe4565b61169d84611c8a565b90506116a7611fe4565b6116b084611c8a565b90506116bc8282611dfb565b9250505092915050565b60008160000151836000015111905092915050565b6116e3611fe4565b816000015183600001511015611761576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f737562737472616374696f6e20756e646572666c6f772064657465637465640081525060200191505060405180910390fd5b60405180602001604052808360000151856000015103815250905092915050565b61178a611fe4565b6000836000015114806117a1575060008260000151145b156117bd57604051806020016040528060008152509050611bdb565b69d3c21bcecceda1000000826000015114156117db57829050611bdb565b69d3c21bcecceda1000000836000015114156117f957819050611bdb565b600069d3c21bcecceda100000061180f85611f44565b600001518161181a57fe5b049050600061182885611f7b565b600001519050600069d3c21bcecceda100000061184486611f44565b600001518161184f57fe5b049050600061185d86611f7b565b60000151905060008285029050600085146118f1578285828161187c57fe5b04146118f0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600069d3c21bcecceda100000082029050600082146119935769d3c21bcecceda100000082828161191e57fe5b0414611992576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f6f766572666c6f772078317931202a206669786564312064657465637465640081525060200191505060405180910390fd5b5b8091506000848602905060008614611a2457848682816119af57fe5b0414611a23576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b6000848802905060008814611ab25784888281611a3d57fe5b0414611ab1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b611aba611fb8565b8781611ac257fe5b049650611acd611fb8565b8581611ad557fe5b0494506000858802905060008814611b665785888281611af157fe5b0414611b65576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b611b6e611fe4565b6040518060200160405280878152509050611b9781604051806020016040528087815250611be1565b9050611bb181604051806020016040528086815250611be1565b9050611bcb81604051806020016040528085815250611be1565b9050809a50505050505050505050505b92915050565b611be9611fe4565b6000826000015184600001510190508360000151811015611c72576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f616464206f766572666c6f77206465746563746564000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808281525091505092915050565b611c92611fe4565b611c9a611fc5565b821115611cf2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260368152602001806120456036913960400191505060405180910390fd5b604051806020016040528069d3c21bcecceda100000084028152509050919050565b600069d3c21bcecceda1000000826000015181611d2d57fe5b049050919050565b60008083118290611de1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015611da6578082015181840152602081019050611d8b565b50505050905090810190601f168015611dd35780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581611ded57fe5b049050809150509392505050565b611e03611fe4565b600082600001511415611e7e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f63616e277420646976696465206279203000000000000000000000000000000081525060200191505060405180910390fd5b600069d3c21bcecceda10000008460000151029050836000015169d3c21bcecceda10000008281611eab57fe5b0414611f1f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6f766572666c6f7720617420646976696465000000000000000000000000000081525060200191505060405180910390fd5b604051806020016040528084600001518381611f3757fe5b0481525091505092915050565b611f4c611fe4565b604051806020016040528069d3c21bcecceda100000080856000015181611f6f57fe5b04028152509050919050565b611f83611fe4565b604051806020016040528069d3c21bcecceda100000080856000015181611fa657fe5b04028460000151038152509050919050565b600064e8d4a51000905090565b60007601357c299a88ea76a58924d52ce4f26a85af186c2b9e74905090565b604051806020016040528060008152509056fe61646a7573746d656e74207370656564206d75737420626520736d616c6c6572207468616e20314f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737363616e277420637265617465206669786964697479206e756d626572206c6172676572207468616e206d61784e657746697865642829676173207072696365206d696e696d756d20666c6f6f72206d7573742062652067726561746572207468616e207a65726f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f777461726765742064656e73697479206d75737420626520736d616c6c6572207468616e2031a265627a7a72315820d2ba16b0fbe5be19a76fcf30881e75c515a4c31f03f79992da3013af2e06d2e564736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f006": { + "code": "0x6080604052600436106101665760003560e01c80639ace38c2116100d1578063ba51a6df1161008a578063d74f8edd11610064578063d74f8edd14610a2e578063dc8452cd14610a59578063e20056e614610a84578063ee22610b14610af557610166565b8063ba51a6df146108f4578063c01a8c841461092f578063c64274741461096a57610166565b80639ace38c2146105f7578063a0e67e2b146106f0578063a24efcdf1461075c578063a8abe69a14610787578063b5dc40c314610839578063b77bf600146108c957610166565b80633411c81c116101235780633411c81c1461039a578063547415251461040d5780635eae79591461046a5780637065cb4814610504578063784547a7146105555780638b51d13f146105a857610166565b8063025e7c27146101c0578063158ef93e1461023b578063173825d91461026a57806320ea8d86146102bb5780632e6c3721146102f65780632f54bf6e14610331575b60003411156101be573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b005b3480156101cc57600080fd5b506101f9600480360360208110156101e357600080fd5b8101908080359060200190929190505050610b30565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561024757600080fd5b50610250610b6c565b604051808215151515815260200191505060405180910390f35b34801561027657600080fd5b506102b96004803603602081101561028d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b7e565b005b3480156102c757600080fd5b506102f4600480360360208110156102de57600080fd5b8101908080359060200190929190505050610f1b565b005b34801561030257600080fd5b5061032f6004803603602081101561031957600080fd5b81019080803590602001909291905050506111dc565b005b34801561033d57600080fd5b506103806004803603602081101561035457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611347565b604051808215151515815260200191505060405180910390f35b3480156103a657600080fd5b506103f3600480360360408110156103bd57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611367565b604051808215151515815260200191505060405180910390f35b34801561041957600080fd5b506104546004803603604081101561043057600080fd5b81019080803515159060200190929190803515159060200190929190505050611396565b6040518082815260200191505060405180910390f35b34801561047657600080fd5b506105026004803603606081101561048d57600080fd5b81019080803590602001906401000000008111156104aa57600080fd5b8201836020820111156104bc57600080fd5b803590602001918460208302840111640100000000831117156104de57600080fd5b90919293919293908035906020019092919080359060200190929190505050611448565b005b34801561051057600080fd5b506105536004803603602081101561052757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061182b565b005b34801561056157600080fd5b5061058e6004803603602081101561057857600080fd5b8101908080359060200190929190505050611bd5565b604051808215151515815260200191505060405180910390f35b3480156105b457600080fd5b506105e1600480360360208110156105cb57600080fd5b8101908080359060200190929190505050611d66565b6040518082815260200191505060405180910390f35b34801561060357600080fd5b506106306004803603602081101561061a57600080fd5b8101908080359060200190929190505050611e4d565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b838110156106b2578082015181840152602081019050610697565b50505050905090810190601f1680156106df5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b3480156106fc57600080fd5b50610705611f42565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561074857808201518184015260208101905061072d565b505050509050019250505060405180910390f35b34801561076857600080fd5b50610771611fd0565b6040518082815260200191505060405180910390f35b34801561079357600080fd5b506107e2600480360360808110156107aa57600080fd5b810190808035906020019092919080359060200190929190803515159060200190929190803515159060200190929190505050611fd6565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561082557808201518184015260208101905061080a565b505050509050019250505060405180910390f35b34801561084557600080fd5b506108726004803603602081101561085c57600080fd5b8101908080359060200190929190505050612188565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156108b557808201518184015260208101905061089a565b505050509050019250505060405180910390f35b3480156108d557600080fd5b506108de6123e0565b6040518082815260200191505060405180910390f35b34801561090057600080fd5b5061092d6004803603602081101561091757600080fd5b81019080803590602001909291905050506123e6565b005b34801561093b57600080fd5b506109686004803603602081101561095257600080fd5b8101908080359060200190929190505050612551565b005b34801561097657600080fd5b50610a186004803603606081101561098d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156109d457600080fd5b8201836020820111156109e657600080fd5b80359060200191846001830284011164010000000083111715610a0857600080fd5b909192939192939050505061286d565b6040518082815260200191505060405180910390f35b348015610a3a57600080fd5b50610a436128d1565b6040518082815260200191505060405180910390f35b348015610a6557600080fd5b50610a6e6128d6565b6040518082815260200191505060405180910390f35b348015610a9057600080fd5b50610af360048036036040811015610aa757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506128dc565b005b348015610b0157600080fd5b50610b2e60048036036020811015610b1857600080fd5b8101908080359060200190929190505050612db7565b005b60048181548110610b3d57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900460ff1681565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610c02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806139716022913960400191505060405180910390fd5b80600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16610cc2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f6f776e657220646f6573206e6f7420657869737400000000000000000000000081525060200191505060405180910390fd5b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060008090505b610d3960016004805490506131ec90919063ffffffff16565b811015610e70578273ffffffffffffffffffffffffffffffffffffffff1660048281548110610d6457fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610e55576004610dc660016004805490506131ec90919063ffffffff16565b81548110610dd057fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660048281548110610e0857fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610e70565b610e6960018261323690919063ffffffff16565b9050610d20565b50610e8a60016004805490506131ec90919063ffffffff16565b600481610e97919061376a565b506004805490506005541115610eb657610eb56004805490506123e6565b5b6004805490506006541115610ed457610ed36004805490506111dc565b5b8173ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a25050565b33600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16610fdb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f6f776e657220646f6573206e6f7420657869737400000000000000000000000081525060200191505060405180910390fd5b81336002600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16611090576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061394a6027913960400191505060405180910390fd5b836001600082815260200190815260200160002060030160009054906101000a900460ff1615611128576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f7472616e73616374696f6e2077617320657865637574656420616c726561647981525060200191505060405180910390fd5b60006002600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35050505050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611260576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806139716022913960400191505060405180910390fd5b60048054905081603282111580156112785750818111155b8015611285575060008114155b8015611292575060008214155b611304576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c696420726571756972656d656e740000000000000000000000000081525060200191505060405180910390fd5b826006819055507fa07eff79ea50418b0e96ff7c01d65eb6c3a5a240ee91cd81c70c89503dd41239836040518082815260200191505060405180910390a1505050565b60036020528060005260406000206000915054906101000a900460ff1681565b60026020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600754811015611441578380156113d657506001600082815260200190815260200160002060030160009054906101000a900460ff16155b8061140a575082801561140957506001600082815260200190815260200160002060030160009054906101000a900460ff165b5b156114265761142360018361323690919063ffffffff16565b91505b61143a60018261323690919063ffffffff16565b905061139e565b5092915050565b6000809054906101000a900460ff16156114ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b60016000806101000a81548160ff0219169083151502179055508383905082603282111580156114fa5750818111155b8015611507575060008114155b8015611514575060008214155b611586576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c696420726571756972656d656e740000000000000000000000000081525060200191505060405180910390fd5b85859050836032821115801561159c5750818111155b80156115a9575060008114155b80156115b6575060008214155b611628576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c696420726571756972656d656e740000000000000000000000000081525060200191505060405180910390fd5b60008090505b8888905081101561180057600360008a8a8481811061164957fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161580156117105750600073ffffffffffffffffffffffffffffffffffffffff168989838181106116da57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b611765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180613993602c913960400191505060405180910390fd5b6001600360008b8b8581811061177757fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506117f960018261323690919063ffffffff16565b905061162e565b50878760049190611812929190613796565b5085600581905550846006819055505050505050505050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146118af576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806139716022913960400191505060405180910390fd5b80600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611970576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f6f776e657220616c72656164792065786973746564000000000000000000000081525060200191505060405180910390fd5b81600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611a14576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6164647265737320776173206e756c6c0000000000000000000000000000000081525060200191505060405180910390fd5b611a2d600160048054905061323690919063ffffffff16565b60065460328211158015611a415750818111155b8015611a4e575060008114155b8015611a5b575060008214155b611acd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c696420726571756972656d656e740000000000000000000000000081525060200191505060405180910390fd5b6001600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060048590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b6000806000905060008090505b600480549050811015611d5a5760026000858152602001908152602001600020600060048381548110611c1157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611ca057611c9d60018361323690919063ffffffff16565b91505b60003073ffffffffffffffffffffffffffffffffffffffff166001600087815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16149050808015611d17575060065483145b80611d2d575080158015611d2c575060055483145b5b15611d3e5760019350505050611d61565b50611d5360018261323690919063ffffffff16565b9050611be2565b5060009150505b919050565b600080600090505b600480549050811015611e475760026000848152602001908152602001600020600060048381548110611d9d57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611e2c57611e2960018361323690919063ffffffff16565b91505b611e4060018261323690919063ffffffff16565b9050611d6e565b50919050565b60016020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015490806002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611f255780601f10611efa57610100808354040283529160200191611f25565b820191906000526020600020905b815481529060010190602001808311611f0857829003601f168201915b5050505050908060030160009054906101000a900460ff16905084565b60606004805480602002602001604051908101604052809291908181526020018280548015611fc657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611f7c575b5050505050905090565b60065481565b60608060075460405190808252806020026020018201604052801561200a5781602001602082028038833980820191505090505b509050600080905060008090505b6007548110156120d45785801561205057506001600082815260200190815260200160002060030160009054906101000a900460ff16155b80612084575084801561208357506001600082815260200190815260200160002060030160009054906101000a900460ff165b5b156120b9578083838151811061209657fe5b6020026020010181815250506120b660018361323690919063ffffffff16565b91505b6120cd60018261323690919063ffffffff16565b9050612018565b6120e788886131ec90919063ffffffff16565b6040519080825280602002602001820160405280156121155781602001602082028038833980820191505090505b5093508790505b8681101561217d5782818151811061213057fe5b60200260200101518461214c8a846131ec90919063ffffffff16565b8151811061215657fe5b60200260200101818152505061217660018261323690919063ffffffff16565b905061211c565b505050949350505050565b6060806004805490506040519080825280602002602001820160405280156121bf5781602001602082028038833980820191505090505b509050600080905060008090505b60048054905081101561232457600260008681526020019081526020016000206000600483815481106121fc57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615612309576004818154811061228157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168383815181106122b857fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505061230660018361323690919063ffffffff16565b91505b61231d60018261323690919063ffffffff16565b90506121cd565b816040519080825280602002602001820160405280156123535781602001602082028038833980820191505090505b509350600090505b818110156123d85782818151811061236f57fe5b602002602001015184828151811061238357fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506123d160018261323690919063ffffffff16565b905061235b565b505050919050565b60075481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461246a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806139716022913960400191505060405180910390fd5b60048054905081603282111580156124825750818111155b801561248f575060008114155b801561249c575060008214155b61250e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c696420726571756972656d656e740000000000000000000000000081525060200191505060405180910390fd5b826005819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a1505050565b33600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16612611576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f6f776e657220646f6573206e6f7420657869737400000000000000000000000081525060200191505060405180910390fd5b81600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156126eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f7472616e73616374696f6e20646f6573206e6f7420657869737400000000000081525060200191505060405180910390fd5b82336002600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156127a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b81526020018061391f602b913960400191505060405180910390fd5b60016002600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361285785611bd5565b156128665761286585612db7565b5b5050505050565b60006128be858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506132be565b90506128c981612551565b949350505050565b603281565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806139716022913960400191505060405180910390fd5b81600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16612a20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f6f776e657220646f6573206e6f7420657869737400000000000000000000000081525060200191505060405180910390fd5b81600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612ac4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6164647265737320776173206e756c6c0000000000000000000000000000000081525060200191505060405180910390fd5b82600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615612b85576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f6f776e657220616c72656164792065786973746564000000000000000000000081525060200191505060405180910390fd5b60008090505b600480549050811015612c79578573ffffffffffffffffffffffffffffffffffffffff1660048281548110612bbc57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612c5e578460048281548110612c1157fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550612c79565b612c7260018261323690919063ffffffff16565b9050612b8b565b506000600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b33600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16612e77576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f6f776e657220646f6573206e6f7420657869737400000000000000000000000081525060200191505060405180910390fd5b81336002600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16612f2c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061394a6027913960400191505060405180910390fd5b836001600082815260200190815260200160002060030160009054906101000a900460ff1615612fc4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f7472616e73616374696f6e2077617320657865637574656420616c726561647981525060200191505060405180910390fd5b612fcd85611bd5565b61303f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f5472616e73616374696f6e206e6f7420636f6e6669726d65642e00000000000081525060200191505060405180910390fd5b600060016000878152602001908152602001600020905060018160030160006101000a81548160ff02191690831515021790555060606131448260000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360010154846002018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561313a5780601f1061310f5761010080835404028352916020019161313a565b820191906000526020600020905b81548152906001019060200180831161311d57829003601f168201915b5050505050613498565b9050867f0c18aae526accb31b01cf9a15bdf435e70632ee31efc4c5c0752c4262ea45d2f826040518080602001828103825283818151815260200191508051906020019080838360005b838110156131a957808201518184015260208101905061318e565b50505050905090810190601f1680156131d65780820380516001836020036101000a031916815260200191505b509250505060405180910390a250505050505050565b600061322e83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061365f565b905092915050565b6000808284019050838110156132b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600083600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613364576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6164647265737320776173206e756c6c0000000000000000000000000000000081525060200191505060405180910390fd5b600754915060405180608001604052808673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001848152602001600015158152506001600084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550602082015181600101556040820151816002019080519060200190613423929190613836565b5060608201518160030160006101000a81548160ff02191690831515021790555090505061345d600160075461323690919063ffffffff16565b600781905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a2509392505050565b6060600082511115613520576134ad8461371f565b61351f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b5b600060608573ffffffffffffffffffffffffffffffffffffffff1685856040518082805190602001908083835b60208310613570578051825260208201915060208101905060208303925061354d565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146135d2576040519150601f19603f3d011682016040523d82523d6000602084013e6135d7565b606091505b50809250819350505081613653576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f5472616e73616374696f6e20657865637574696f6e206661696c65642e00000081525060200191505060405180910390fd5b80925050509392505050565b600083831115829061370c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156136d15780820151818401526020810190506136b6565b50505050905090810190601f1680156136fe5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561376157506000801b8214155b92505050919050565b8154818355818111156137915781836000526020600020918201910161379091906138b6565b5b505050565b828054828255906000526020600020908101928215613825579160200282015b8281111561382457823573ffffffffffffffffffffffffffffffffffffffff168260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906137b6565b5b50905061383291906138db565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061387757805160ff19168380011785556138a5565b828001600101855582156138a5579182015b828111156138a4578251825591602001919060010190613889565b5b5090506138b291906138b6565b5090565b6138d891905b808211156138d45760008160009055506001016138bc565b5090565b90565b61391b91905b8082111561391757600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016138e1565b5090565b9056fe7472616e73616374696f6e2077617320616c726561647920636f6e6669726d656420666f72206f776e65727472616e73616374696f6e20776173206e6f7420636f6e6669726d656420666f72206f776e65726d73672e73656e64657220776173206e6f74206d756c74697369672077616c6c65746f776e657220776173206e756c6c206f7220616c726561647920676976656e206f776e657220737461747573a265627a7a72315820dd709c9d3ccc3a6db73a5e9d5ce158e89da8a32d01056ba1ec25b13d621016fc64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f007": { + "code": "0x6080604052600436106103505760003560e01c806381b861a6116101c6578063ad62ad10116100f7578063e6b76e9c11610095578063ec4f797b1161006f578063ec4f797b1461146e578063f0b7182b146114bd578063f2fde38b1461150e578063fa9ed95a1461155f57610350565b8063e6b76e9c1461139d578063e7e31e7a146113d8578063e83b373b1461142957610350565b8063d48bfca7116100d1578063d48bfca714611272578063e30f579d146112db578063e33a88e714611306578063e50a6c1e1461133157610350565b8063ad62ad1014611083578063b003dcf1146110be578063ca56d33b1461111957610350565b80638f32d59b11610164578063a1ab55b31161013e578063a1ab55b314610e53578063a3e1f00d14610e8e578063a91ee0dc14610fc6578063aa6ca8081461101757610350565b80638f32d59b14610d4f5780639a206ece14610d7e5780639c3e2f0f14610de757610350565b80638b7df8d4116101a05780638b7df8d414610c515780638ce5877c14610c7c5780638d9a5e6f14610ccd5780638da5cb5b14610cf857610350565b806381b861a614610b8f5780638438796a14610bba578063894098d614610c2657610350565b806339d7f76e116102a05780637090db4e1161023e57806376769a601161021857806376769a6014610a795780637897a78e14610aa45780637b10399914610acf5780637b52207514610b2657610350565b80637090db4e14610a0c578063715018a614610a37578063765c1fe914610a4e57610350565b806354255be01161027a57806354255be0146108b357806356b6d0d5146108f35780635a18b08b1461091e5780635c4a31451461099957610350565b806339d7f76e146107a457806340899365146107cf5780634cea8ded1461084a57610350565b8063158ef93e1161030d5780631c39c7d5116102e75780631c39c7d514610623578063220159681461069657806322796e83146106ff5780632aa1c16d1461077957610350565b8063158ef93e1461055957806317f9a6f71461058857806319f37361146105ba57610350565b806301da32bd1461035257806303a0fea31461038d57806303d835f3146104005780630db279be1461042b5780631218f9821461047a57806313baf1e6146104e6575b005b34801561035e57600080fd5b5061038b6004803603602081101561037557600080fd5b810190808035906020019092919050505061158a565b005b34801561039957600080fd5b506103e6600480360360408110156103b057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506116db565b604051808215151515815260200191505060405180910390f35b34801561040c57600080fd5b506104156118d9565b6040518082815260200191505060405180910390f35b34801561043757600080fd5b506104646004803603602081101561044e57600080fd5b81019080803590602001909291905050506118df565b6040518082815260200191505060405180910390f35b34801561048657600080fd5b5061048f611900565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156104d25780820151818401526020810190506104b7565b505050509050019250505060405180910390f35b3480156104f257600080fd5b5061053f6004803603604081101561050957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061198e565b604051808215151515815260200191505060405180910390f35b34801561056557600080fd5b5061056e611d18565b604051808215151515815260200191505060405180910390f35b34801561059457600080fd5b5061059d611d2b565b604051808381526020018281526020019250505060405180910390f35b3480156105c657600080fd5b50610609600480360360208110156105dd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611ee1565b604051808215151515815260200191505060405180910390f35b34801561062f57600080fd5b5061067c6004803603604081101561064657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611f01565b604051808215151515815260200191505060405180910390f35b3480156106a257600080fd5b506106e5600480360360208110156106b957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061215c565b604051808215151515815260200191505060405180910390f35b34801561070b57600080fd5b50610714612432565b60405180836fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168152602001826fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff1681526020019250505060405180910390f35b34801561078557600080fd5b5061078e61247c565b6040518082815260200191505060405180910390f35b3480156107b057600080fd5b506107b9612500565b6040518082815260200191505060405180910390f35b3480156107db57600080fd5b50610808600480360360208110156107f257600080fd5b8101908080359060200190929190505050612506565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561085657600080fd5b506108996004803603602081101561086d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612542565b604051808215151515815260200191505060405180910390f35b3480156108bf57600080fd5b506108c8612562565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b3480156108ff57600080fd5b50610908612589565b6040518082815260200191505060405180910390f35b34801561092a57600080fd5b506109576004803603602081101561094157600080fd5b8101908080359060200190929190505050612955565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156109a557600080fd5b506109f2600480360360408110156109bc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612991565b604051808215151515815260200191505060405180910390f35b348015610a1857600080fd5b50610a21612d19565b6040518082815260200191505060405180910390f35b348015610a4357600080fd5b50610a4c612d1f565b005b348015610a5a57600080fd5b50610a63612e58565b6040518082815260200191505060405180910390f35b348015610a8557600080fd5b50610a8e612ef8565b6040518082815260200191505060405180910390f35b348015610ab057600080fd5b50610ab9612efe565b6040518082815260200191505060405180910390f35b348015610adb57600080fd5b50610ae4612f24565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b348015610b3257600080fd5b50610b7560048036036020811015610b4957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612f4a565b604051808215151515815260200191505060405180910390f35b348015610b9b57600080fd5b50610ba4612f6a565b6040518082815260200191505060405180910390f35b348015610bc657600080fd5b50610bcf612f70565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610c12578082015181840152602081019050610bf7565b505050509050019250505060405180910390f35b348015610c3257600080fd5b50610c3b612fc8565b6040518082815260200191505060405180910390f35b348015610c5d57600080fd5b50610c66612fce565b6040518082815260200191505060405180910390f35b348015610c8857600080fd5b50610ccb60048036036020811015610c9f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612ff6565b005b348015610cd957600080fd5b50610ce261310e565b6040518082815260200191505060405180910390f35b348015610d0457600080fd5b50610d0d61312f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b348015610d5b57600080fd5b50610d64613158565b604051808215151515815260200191505060405180910390f35b348015610d8a57600080fd5b50610dcd60048036036020811015610da157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506131b6565b604051808215151515815260200191505060405180910390f35b348015610df357600080fd5b50610dfc6131d6565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610e3f578082015181840152602081019050610e24565b505050509050019250505060405180910390f35b348015610e5f57600080fd5b50610e8c60048036036020811015610e7657600080fd5b8101908080359060200190929190505050613264565b005b348015610e9a57600080fd5b50610fc46004803603610120811015610eb257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190640100000000811115610f1757600080fd5b820183602082011115610f2957600080fd5b80359060200191846020830284011164010000000083111715610f4b57600080fd5b909192939192939080359060200190640100000000811115610f6c57600080fd5b820183602082011115610f7e57600080fd5b80359060200191846020830284011164010000000083111715610fa057600080fd5b90919293919293908035906020019092919080359060200190929190505050613395565b005b348015610fd257600080fd5b5061101560048036036020811015610fe957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061350c565b005b34801561102357600080fd5b5061102c6136b0565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561106f578082015181840152602081019050611054565b505050509050019250505060405180910390f35b34801561108f57600080fd5b506110bc600480360360208110156110a657600080fd5b810190808035906020019092919050505061373e565b005b3480156110ca57600080fd5b50611117600480360360408110156110e157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506137f9565b005b34801561112557600080fd5b506112706004803603604081101561113c57600080fd5b810190808035906020019064010000000081111561115957600080fd5b82018360208201111561116b57600080fd5b8035906020019184602083028401116401000000008311171561118d57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156111ed57600080fd5b8201836020820111156111ff57600080fd5b8035906020019184602083028401116401000000008311171561122157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050613b7e565b005b34801561127e57600080fd5b506112c16004803603602081101561129557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614036565b604051808215151515815260200191505060405180910390f35b3480156112e757600080fd5b506112f061454f565b6040518082815260200191505060405180910390f35b34801561131257600080fd5b5061131b61458a565b6040518082815260200191505060405180910390f35b34801561133d57600080fd5b50611346614590565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561138957808201518184015260208101905061136e565b505050509050019250505060405180910390f35b3480156113a957600080fd5b506113d6600480360360208110156113c057600080fd5b8101908080359060200190929190505050614643565b005b3480156113e457600080fd5b50611427600480360360208110156113fb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614775565b005b34801561143557600080fd5b5061146c6004803603604081101561144c57600080fd5b810190808035906020019092919080359060200190929190505050614930565b005b34801561147a57600080fd5b506114a76004803603602081101561149157600080fd5b8101908080359060200190929190505050614a45565b6040518082815260200191505060405180910390f35b3480156114c957600080fd5b5061150c600480360360208110156114e057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614a5d565b005b34801561151a57600080fd5b5061155d6004803603602081101561153157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614d21565b005b34801561156b57600080fd5b50611574614da7565b6040518082815260200191505060405180910390f35b611592613158565b611604576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61160d81614dad565b60106000820151816000015590505061164c611627614dcb565b6010604051806020016040529081600082015481525050614df190919063ffffffff16565b6116a1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180615f296026913960400191505060405180910390fd5b7fb08f0607338ad77f5b08ccf831e533cefcc2d373c173e87a8f61144f1d82be1e816040518082815260200191505060405180910390a150565b600033601460008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff168061185457508073ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f45786368616e67650000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561180157600080fd5b505afa158015611815573d6000803e3d6000fd5b505050506040513d602081101561182b57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff16145b6118c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f41646472657373206e6f7420616c6c6f77656420746f207370656e640000000081525060200191505060405180910390fd5b6118d08484614e07565b91505092915050565b60115481565b600c81815481106118ec57fe5b906000526020600020016000915090505481565b6060601580548060200260200160405190810160405280929190818152602001828054801561198457602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161193a575b5050505050905090565b6000611998613158565b611a0a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b82600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16611aca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f746f6b656e206164647220776173206e6576657220726567697374657265640081525060200191505060405180910390fd5b60048054905083108015611b4057508373ffffffffffffffffffffffffffffffffffffffff1660048481548110611afd57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b611b95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180615f4f602a913960400191505060405180910390fd5b6000600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060006004611c0a6001600480549050614f1e90919063ffffffff16565b81548110611c1457fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508060048581548110611c4f57fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611cb06001600480549050614f1e90919063ffffffff16565b600481611cbd9190615d09565b508473ffffffffffffffffffffffffffffffffffffffff167fbe9bb4bdca0a094babd75e3a98b1d2e2390633430d0a2f6e2b9970e2ee03fb2e856040518082815260200191505060405180910390a260019250505092915050565b600060149054906101000a900460ff1681565b600080600160026000828254019250508190555060006002549050600654611d8f600560000160109054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff1642614f1e90919063ffffffff16565b1115611e1d57611da5611da0614f68565b614fd0565b600560000160006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555042600560000160106101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b600560000160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16611e61611e5c614dcb565b614fd0565b925092506002548114611edc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b509091565b60036020528060005260406000206000915054906101000a900460ff1681565b6000600960003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16611fa5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180615fa3602c913960400191505060405180910390fd5b600a60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16612047576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180615f79602a913960400191505060405180910390fd5b600062015180428161205557fe5b049050600e548111156120b657600061206c612fce565b905081600e819055506120ae6120a961208483614fde565b601060405180602001604052908160008201548152505061506890919063ffffffff16565b6154c7565b600f81905550505b82600f54101561212e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f457863656564696e67207370656e64696e67206c696d6974000000000000000081525060200191505060405180910390fd5b61214383600f54614f1e90919063ffffffff16565b600f819055506121538484614e07565b91505092915050565b6000612166613158565b6121d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600160026000828254019250508190555060006002549050600a60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156122b0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f72657365727665206164647220616c726561647920616464656400000000000081525060200191505060405180910390fd5b6001600a60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600b8390806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508273ffffffffffffffffffffffffffffffffffffffff167fd78793225285ecf9cf5f0f84b1cdc335c2cb4d6810ff0b9fd156ad6026c89cea60405160405180910390a260019150600254811461242c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50919050565b60058060000160009054906101000a90046fffffffffffffffffffffffffffffffff16908060000160109054906101000a90046fffffffffffffffffffffffffffffffff16905082565b60008062015180428161248b57fe5b04905060006124a560125483614f1e90919063ffffffff16565b905060135481106124bb576000925050506124fd565b6124f86124e76013546124d9846011546154e890919063ffffffff16565b61556e90919063ffffffff16565b601154614f1e90919063ffffffff16565b925050505b90565b600f5481565b600b818154811061251357fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60146020528060005260406000206000915054906101000a900460ff1681565b60008060008060018060026001839350829250819150809050935093509350935090919293565b600080600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f536f727465644f7261636c657300000000000000000000000000000000000000815250600d019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561264557600080fd5b505afa158015612659573d6000803e3d6000fd5b505050506040513d602081101561266f57600080fd5b8101908080519060200190929190505050905060008190506000612691612fce565b905060008090506126a0615d35565b6126dc600d60007f63474c4400000000000000000000000000000000000000000000000000000000815260200190815260200160002054614dad565b905060008090505b60048054905081101561290d576000808673ffffffffffffffffffffffffffffffffffffffff1663ef90e1b06004858154811061271d57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050604080518083038186803b1580156127a757600080fd5b505afa1580156127bb573d6000803e3d6000fd5b505050506040513d60408110156127d157600080fd5b810190808051906020019092919080519060200190929190505050809250819350505060006004848154811061280357fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561287357600080fd5b505afa158015612887573d6000803e3d6000fd5b505050506040513d602081101561289d57600080fd5b8101908080519060200190929190505050905060006128d7846128c985856154e890919063ffffffff16565b61556e90919063ffffffff16565b90506128ec81886155b890919063ffffffff16565b9650505050506129066001826155b890919063ffffffff16565b90506126e4565b5061294b61294661291d84614fde565b6129388461292a88614fde565b61564090919063ffffffff16565b61564090919063ffffffff16565b614fd0565b9550505050505090565b6015818154811061296257fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061299b613158565b612a0d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600a60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16612acc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f72657365727665206164647220776173206e657665722061646465640000000081525060200191505060405180910390fd5b600b8054905082108015612b4257508273ffffffffffffffffffffffffffffffffffffffff16600b8381548110612aff57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b612b97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d815260200180615efc602d913960400191505060405180910390fd5b6000600a60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506000600b612c0c6001600b80549050614f1e90919063ffffffff16565b81548110612c1657fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905080600b8481548110612c5157fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550612cb26001600b80549050614f1e90919063ffffffff16565b600b81612cbf9190615d09565b508373ffffffffffffffffffffffffffffffffffffffff167f89b4ee5cecfdfb246ede373c10283b5038afe56a531fc1d2f3ed8c5507a52fcb846040518082815260200191505060405180910390a2600191505092915050565b60135481565b612d27613158565b612d99576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000806000905060008090505b600b80549050811015612ef057612ed3600b8281548110612e8257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1631836155b890919063ffffffff16565b9150612ee96001826155b890919063ffffffff16565b9050612e65565b508091505090565b60085481565b6000612f1f6010604051806020016040529081600082015481525050614fd0565b905090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600a6020528060005260406000206000915054906101000a900460ff1681565b60125481565b6060600c805480602002602001604051908101604052809291908181526020018280548015612fbe57602002820191906000526020600020905b815481526020019060010190808311612faa575b5050505050905090565b60075481565b6000612ff1612fdb612e58565b612fe361454f565b6155b890919063ffffffff16565b905090565b612ffe613158565b613070576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000600960008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508073ffffffffffffffffffffffffffffffffffffffff167fab8cff50266d80b9c9d9703af934ca455b9218286bf4fcaa05653a564c499e4b60405160405180910390a250565b600061312a61311b612e58565b476155b890919063ffffffff16565b905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661319a615789565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b60096020528060005260406000206000915054906101000a900460ff1681565b6060600b80548060200260200160405190810160405280929190818152602001828054801561325a57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311613210575b5050505050905090565b61326c613158565b6132de576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008111613354576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f76616c756520776173207a65726f00000000000000000000000000000000000081525060200191505060405180910390fd5b806006819055507f7bfe94ca3147f135fcd6d94ebf61d33fa34fbe904f933ccae66911b9548544f2816040518082815260200191505060405180910390a150565b600060149054906101000a900460ff1615613418576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff02191690831515021790555061343c33615791565b6134458b61350c565b61344e8a613264565b6134578961158a565b6134618888614930565b6134ed868680806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050858580806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050613b7e565b6134f682614643565b6134ff8161373e565b5050505050505050505050565b613514613158565b613586576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613629576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b6060600480548060200260200160405190810160405280929190818152602001828054801561373457602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116136ea575b5050505050905090565b613746613158565b6137b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b806008819055507f4da8e8b2223fbbb897200fb9dfb6b986c1b4188621114d407ee8ec363569fc37816040518082815260200191505060405180910390a150565b613801613158565b613873576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000601460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506000601580549050905080821061394a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f496e64657820697320696e76616c69640000000000000000000000000000000081525060200191505060405180910390fd5b6015828154811061395757fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614613a22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f496e64657820646f6573206e6f74206d61746368207370656e6465720000000081525060200191505060405180910390fd5b6000613a38600183614f1e90919063ffffffff16565b9050808314613acf5760158181548110613a4e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660158481548110613a8657fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b600060158281548110613ade57fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080601581613b349190615d09565b508373ffffffffffffffffffffffffffffffffffffffff167f20aaa18caa668680a42b328a15fd50d580bac65d8bd346e104355473c6373ff360405160405180910390a250505050565b613b86613158565b613bf8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b8051825114613c6f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4172726179206c656e677468206d69736d61746368000000000000000000000081525060200191505060405180910390fd5b613c77615d35565b613c816000614dad565b905060008090505b8251811015613cdd57613cc0613cb1848381518110613ca457fe5b6020026020010151614dad565b836158d590919063ffffffff16565b9150613cd66001826155b890919063ffffffff16565b9050613c89565b50613cf8613ce9614dcb565b8261597e90919063ffffffff16565b613d4d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180615eba6021913960400191505060405180910390fd5b60008090505b600c80549050811015613da957600d6000600c8381548110613d7157fe5b9060005260206000200154815260200190815260200160002060009055613da26001826155b890919063ffffffff16565b9050613d53565b5082600c9080519060200190613dc0929190615d48565b5060008090505b8351811015613ec5576000600d6000868481518110613de257fe5b602002602001015181526020019081526020016000205414613e6c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f43616e6e6f74207365742077656967687420747769636500000000000000000081525060200191505060405180910390fd5b828181518110613e7857fe5b6020026020010151600d6000868481518110613e9057fe5b6020026020010151815260200190815260200160002081905550613ebe6001826155b890919063ffffffff16565b9050613dc7565b506000600d60007f63474c44000000000000000000000000000000000000000000000000000000008152602001908152602001600020541415613f70576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4d757374207365742063474c442061737365742077656967687400000000000081525060200191505060405180910390fd5b7f55b488abd19ae7621712324d3d42c2ef7a9575f64f5503103286a1161fb408558383604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015613fda578082015181840152602081019050613fbf565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561401c578082015181840152602081019050614001565b5050505090500194505050505060405180910390a1505050565b6000614040613158565b6140b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600160026000828254019250508190555060006002549050600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561418a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f746f6b656e206164647220616c7265616479207265676973746572656400000081525060200191505060405180910390fd5b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f536f727465644f7261636c657300000000000000000000000000000000000000815250600d019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561424557600080fd5b505afa158015614259573d6000803e3d6000fd5b505050506040513d602081101561426f57600080fd5b8101908080519060200190929190505050905060008190506000808273ffffffffffffffffffffffffffffffffffffffff1663ef90e1b0886040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050604080518083038186803b15801561430657600080fd5b505afa15801561431a573d6000803e3d6000fd5b505050506040513d604081101561433057600080fd5b8101908080519060200190929190805190602001909291905050508092508193505050600081116143c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f6d656469616e20726174652072657475726e6564203020676f6c64000000000081525060200191505060405180910390fd5b6001600360008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060048790806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508673ffffffffffffffffffffffffffffffffffffffff167f784c8f4dbf0ffedd6e72c76501c545a70f8b203b30a26ce542bf92ba87c248a460405160405180910390a260019550505050506002548114614549576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50919050565b600080479050600061455f61247c565b905080821161456f576000614583565b6145828183614f1e90919063ffffffff16565b5b9250505090565b60065481565b606080600c805490506040519080825280602002602001820160405280156145c75781602001602082028038833980820191505090505b50905060008090505b600c8054905081101561463b57600d6000600c83815481106145ee57fe5b906000526020600020015481526020019081526020016000205482828151811061461457fe5b6020026020010181815250506146346001826155b890919063ffffffff16565b90506145d0565b508091505090565b61464b613158565b6146bd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6146df6146c8614dcb565b6146d183614dad565b614df190919063ffffffff16565b614734576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180615e066021913960400191505060405180910390fd5b806007819055507ffe69856ffb1b1d6cb00c1d8151726e6e95032b1666282eeb293ecadd58b29a6e816040518082815260200191505060405180910390a150565b61477d613158565b6147ef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff161415614892576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f5370656e6465722063616e2774206265206e756c6c000000000000000000000081525060200191505060405180910390fd5b6001600960008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508073ffffffffffffffffffffffffffffffffffffffff167f3139419c41cdd7abca84fa19dd21118cd285d3e2ce1a9444e8161ce9fa62fdcd60405160405180910390a250565b614938613158565b6149aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b47821115614a20576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f43616e6e6f7420667265657a65206d6f7265207468616e2062616c616e63650081525060200191505060405180910390fd5b81601181905550620151804281614a3357fe5b04601281905550806013819055505050565b600d6020528060005260406000206000915090505481565b614a65613158565b614ad7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff161415614b7a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f5370656e6465722063616e2774206265206e756c6c000000000000000000000081525060200191505060405180910390fd5b601460008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615614c1d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180615e616023913960400191505060405180910390fd5b6001601460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060158190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508073ffffffffffffffffffffffffffffffffffffffff167f71bccdb89fff4d914e3d2e472b327e3debaf4c4d6f1dfe528f430447e4cbcf5f60405160405180910390a250565b614d29613158565b614d9b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b614da481615791565b50565b600e5481565b614db5615d35565b6040518060200160405280838152509050919050565b614dd3615d35565b604051806020016040528069d3c21bcecceda1000000815250905090565b6000816000015183600001511115905092915050565b6000614e1161454f565b821115614e86576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f457863656564696e6720756e66726f7a656e207265736572766573000000000081525060200191505060405180910390fd5b614eaf828473ffffffffffffffffffffffffffffffffffffffff1661599390919063ffffffff16565b8273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f4dd1abe16ad3d4f829372dc77766ca2cce34e205af9b10f8cc1fab370425864f846040518082815260200191505060405180910390a36001905092915050565b6000614f6083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250615acd565b905092915050565b614f70615d35565b614f78615d35565b614f88614f83612589565b614dad565b9050614fa7614f98600854614dad565b82615b8d90919063ffffffff16565b15614fbe57614fb66000614dad565b915050614fcd565b614fc9600754614dad565b9150505b90565b600081600001519050919050565b614fe6615d35565b614fee615ba3565b821115615046576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180615e846036913960400191505060405180910390fd5b604051806020016040528069d3c21bcecceda100000084028152509050919050565b615070615d35565b600083600001511480615087575060008260000151145b156150a3576040518060200160405280600081525090506154c1565b69d3c21bcecceda1000000826000015114156150c1578290506154c1565b69d3c21bcecceda1000000836000015114156150df578190506154c1565b600069d3c21bcecceda10000006150f585615bc2565b600001518161510057fe5b049050600061510e85615bf9565b600001519050600069d3c21bcecceda100000061512a86615bc2565b600001518161513557fe5b049050600061514386615bf9565b60000151905060008285029050600085146151d7578285828161516257fe5b04146151d6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600069d3c21bcecceda100000082029050600082146152795769d3c21bcecceda100000082828161520457fe5b0414615278576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f6f766572666c6f772078317931202a206669786564312064657465637465640081525060200191505060405180910390fd5b5b809150600084860290506000861461530a578486828161529557fe5b0414615309576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b6000848802905060008814615398578488828161532357fe5b0414615397576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b6153a0615c36565b87816153a857fe5b0496506153b3615c36565b85816153bb57fe5b049450600085880290506000881461544c57858882816153d757fe5b041461544b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b615454615d35565b604051806020016040528087815250905061547d816040518060200160405280878152506158d5565b9050615497816040518060200160405280868152506158d5565b90506154b1816040518060200160405280858152506158d5565b9050809a50505050505050505050505b92915050565b600069d3c21bcecceda10000008260000151816154e057fe5b049050919050565b6000808314156154fb5760009050615568565b600082840290508284828161550c57fe5b0414615563576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180615edb6021913960400191505060405180910390fd5b809150505b92915050565b60006155b083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250615c43565b905092915050565b600080828401905083811015615636576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b615648615d35565b6000826000015114156156c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f63616e277420646976696465206279203000000000000000000000000000000081525060200191505060405180910390fd5b600069d3c21bcecceda10000008460000151029050836000015169d3c21bcecceda100000082816156f057fe5b0414615764576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6f766572666c6f7720617420646976696465000000000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808460000151838161577c57fe5b0481525091505092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415615817576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180615de06026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6158dd615d35565b6000826000015184600001510190508360000151811015615966576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f616464206f766572666c6f77206465746563746564000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808281525091505092915050565b60008160000151836000015114905092915050565b80471015615a09576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f416464726573733a20696e73756666696369656e742062616c616e636500000081525060200191505060405180910390fd5b60008273ffffffffffffffffffffffffffffffffffffffff168260405180600001905060006040518083038185875af1925050503d8060008114615a69576040519150601f19603f3d011682016040523d82523d6000602084013e615a6e565b606091505b5050905080615ac8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a815260200180615e27603a913960400191505060405180910390fd5b505050565b6000838311158290615b7a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015615b3f578082015181840152602081019050615b24565b50505050905090810190601f168015615b6c5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b6000816000015183600001511015905092915050565b60007601357c299a88ea76a58924d52ce4f26a85af186c2b9e74905090565b615bca615d35565b604051806020016040528069d3c21bcecceda100000080856000015181615bed57fe5b04028152509050919050565b615c01615d35565b604051806020016040528069d3c21bcecceda100000080856000015181615c2457fe5b04028460000151038152509050919050565b600064e8d4a51000905090565b60008083118290615cef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015615cb4578082015181840152602081019050615c99565b50505050905090810190601f168015615ce15780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581615cfb57fe5b049050809150509392505050565b815481835581811115615d3057818360005260206000209182019101615d2f9190615d95565b5b505050565b6040518060200160405280600081525090565b828054828255906000526020600020908101928215615d84579160200282015b82811115615d83578251825591602001919060010190615d68565b5b509050615d919190615dba565b5090565b615db791905b80821115615db3576000816000905550600101615d9b565b5090565b90565b615ddc91905b80821115615dd8576000816000905550600101615dc0565b5090565b9056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373746f62696e207461782063616e6e6f74206265206c6172676572207468616e2031416464726573733a20756e61626c6520746f2073656e642076616c75652c20726563697069656e74206d617920686176652072657665727465644164647265737320697320616c72656164792045786368616e6765205370656e64657263616e277420637265617465206669786964697479206e756d626572206c6172676572207468616e206d61784e65774669786564282953756d206f6620617373657420616c6c6f636174696f6e206d7573742062652031536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77696e64657820696e746f2072657365727665206c697374206e6f74206d617070656420746f20616464726573737370656e64696e6720726174696f2063616e6e6f74206265206c6172676572207468616e2031696e64657820696e746f20746f6b656e73206c697374206e6f74206d617070656420746f20746f6b656e63616e206f6e6c79207472616e7366657220746f206f746865722072657365727665206164647265737373656e646572206e6f7420616c6c6f77656420746f207472616e7366657220526573657276652066756e6473a265627a7a723158203ee2bb5897047b5f77030cee494af35e85630a33a096c7e9f2599c38ee6917fb64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f008": { + "code": "0x608060405234801561001057600080fd5b506004361061027f5760003560e01c806370a082311161015c578063a457c2d7116100ce578063df4da46111610087578063df4da461146112b1578063e1d6aceb146112cf578063e50e652d1461138a578063ec683072146113cc578063f2fde38b14611447578063fae8db0a1461148b5761027f565b8063a457c2d7146110b4578063a67f87471461111a578063a9059cbb1461114d578063a91ee0dc146111b3578063af31f587146111f7578063dd62ed3e146112395761027f565b80638a883626116101205780638a88362614610e965780638da5cb5b14610f655780638f32d59b14610faf57806395d89b4114610fd15780639a7b3be7146110545780639b2b592f146110725761027f565b806370a0823114610dae578063715018a614610e065780637385e5da14610e105780637b10399914610e2e57806387ee8a0f14610e785761027f565b806339509351116101f55780634b2c2f44116101b95780634b2c2f4414610a4a57806354255be014610b1957806358cf967214610b4c5780635d180adb14610b9a57806367960e9114610c125780636a30b25314610ce15761027f565b806339509351146108d85780633b1eb4bf1461093e57806340a12f641461098057806340c10f191461099e57806342966c6814610a045761027f565b806318160ddd1161024757806318160ddd1461043f5780631e4f0e031461045d578063222836ad1461066c57806323b872dd146106a457806323f0ab651461072a578063313ce567146108b45761027f565b806306fdde0314610284578063095ea7b314610307578063123633ea1461036d57806312c6c099146103db578063158ef93e1461041d575b600080fd5b61028c6114cd565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156102cc5780820151818401526020810190506102b1565b50505050905090810190601f1680156102f95780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6103536004803603604081101561031d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061156f565b604051808215151515815260200191505060405180910390f35b6103996004803603602081101561038357600080fd5b8101908080359060200190929190505050611792565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610407600480360360208110156103f157600080fd5b81019080803590602001909291905050506118e3565b6040518082815260200191505060405180910390f35b61042561190c565b604051808215151515815260200191505060405180910390f35b61044761191f565b6040518082815260200191505060405180910390f35b61066a600480360361012081101561047457600080fd5b810190808035906020019064010000000081111561049157600080fd5b8201836020820111156104a357600080fd5b803590602001918460018302840111640100000000831117156104c557600080fd5b9091929391929390803590602001906401000000008111156104e657600080fd5b8201836020820111156104f857600080fd5b8035906020019184600183028401116401000000008311171561051a57600080fd5b9091929391929390803560ff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019064010000000081111561057c57600080fd5b82018360208201111561058e57600080fd5b803590602001918460208302840111640100000000831117156105b057600080fd5b9091929391929390803590602001906401000000008111156105d157600080fd5b8201836020820111156105e357600080fd5b8035906020019184602083028401116401000000008311171561060557600080fd5b90919293919293908035906020019064010000000081111561062657600080fd5b82018360208201111561063857600080fd5b8035906020019184600183028401116401000000008311171561065a57600080fd5b9091929391929390505050611931565b005b6106a26004803603604081101561068257600080fd5b810190808035906020019092919080359060200190929190505050611c57565b005b610710600480360360608110156106ba57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611ebc565b604051808215151515815260200191505060405180910390f35b61089a6004803603606081101561074057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561077d57600080fd5b82018360208201111561078f57600080fd5b803590602001918460018302840111640100000000831117156107b157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561081457600080fd5b82018360208201111561082657600080fd5b8035906020019184600183028401116401000000008311171561084857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612542565b604051808215151515815260200191505060405180910390f35b6108bc6126fb565b604051808260ff1660ff16815260200191505060405180910390f35b610924600480360360408110156108ee57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612712565b604051808215151515815260200191505060405180910390f35b61096a6004803603602081101561095457600080fd5b81019080803590602001909291905050506129cf565b6040518082815260200191505060405180910390f35b6109886129e9565b6040518082815260200191505060405180910390f35b6109ea600480360360408110156109b457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612a4f565b604051808215151515815260200191505060405180910390f35b610a3060048036036020811015610a1a57600080fd5b8101908080359060200190929190505050612eb5565b604051808215151515815260200191505060405180910390f35b610b0360048036036020811015610a6057600080fd5b8101908080359060200190640100000000811115610a7d57600080fd5b820183602082011115610a8f57600080fd5b80359060200191846001830284011164010000000083111715610ab157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506133dc565b6040518082815260200191505060405180910390f35b610b21613570565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b610b9860048036036040811015610b6257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050613598565b005b610bd060048036036040811015610bb057600080fd5b8101908080359060200190929190803590602001909291905050506138d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610ccb60048036036020811015610c2857600080fd5b8101908080359060200190640100000000811115610c4557600080fd5b820183602082011115610c5757600080fd5b80359060200191846001830284011164010000000083111715610c7957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050613a28565b6040518082815260200191505060405180910390f35b610dac6004803603610100811015610cf857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050613bbc565b005b610df060048036036020811015610dc457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613eb2565b6040518082815260200191505060405180910390f35b610e0e613f03565b005b610e1861403c565b6040518082815260200191505060405180910390f35b610e3661404c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610e80614072565b6040518082815260200191505060405180910390f35b610f4f60048036036020811015610eac57600080fd5b8101908080359060200190640100000000811115610ec957600080fd5b820183602082011115610edb57600080fd5b80359060200191846001830284011164010000000083111715610efd57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506141b9565b6040518082815260200191505060405180910390f35b610f6d61434d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610fb7614376565b604051808215151515815260200191505060405180910390f35b610fd96143d4565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015611019578082015181840152602081019050610ffe565b50505050905090810190601f1680156110465780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61105c614476565b6040518082815260200191505060405180910390f35b61109e6004803603602081101561108857600080fd5b8101908080359060200190929190505050614486565b6040518082815260200191505060405180910390f35b611100600480360360408110156110ca57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506145cf565b604051808215151515815260200191505060405180910390f35b611122614806565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b6111996004803603604081101561116357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061486a565b604051808215151515815260200191505060405180910390f35b6111f5600480360360208110156111c957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614a3e565b005b6112236004803603602081101561120d57600080fd5b8101908080359060200190929190505050614be2565b6040518082815260200191505060405180910390f35b61129b6004803603604081101561124f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614c24565b6040518082815260200191505060405180910390f35b6112b9614cab565b6040518082815260200191505060405180910390f35b611370600480360360608110156112e557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561132c57600080fd5b82018360208201111561133e57600080fd5b8035906020019184600183028401116401000000008311171561136057600080fd5b9091929391929390505050614de7565b604051808215151515815260200191505060405180910390f35b6113b6600480360360208110156113a057600080fd5b8101908080359060200190929190505050615026565b6040518082815260200191505060405180910390f35b61142a600480360360c08110156113e257600080fd5b81019080803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050615071565b604051808381526020018281526020019250505060405180910390f35b6114896004803603602081101561145d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050615285565b005b6114b7600480360360208110156114a157600080fd5b810190808035906020019092919050505061530b565b6040518082815260200191505060405180910390f35b606060028054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156115655780601f1061153a57610100808354040283529160200191611565565b820191906000526020600020905b81548152906001019060200180831161154857829003601f168201915b5050505050905090565b6000611579616af4565b6000611583615454565b8092508193505050600860030154811461161a5781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a976115f7600860010160405180602001604052908160008201548152505061561f565b600860030154604051808381526020018281526020019250505060405180910390a15b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614156116a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180616dad602a913960400191505060405180910390fd5b83600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925866040518082815260200191505060405180910390a360019250505092915050565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16844360405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061180b57805182526020820191506020810190506020830392506117e8565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461186b576040519150601f19603f3d011682016040523d82523d6000602084013e611870565b606091505b508093508192505050806118cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180616d05603d913960400191505060405180910390fd5b6118da82600061562d565b92505050919050565b60006118ed616af4565b6118f5615454565b50809150506119048184615644565b915050919050565b600060149054906101000a900460ff1681565b600061192c600654614be2565b905090565b600060149054906101000a900460ff16156119b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff0219169083151502179055506000881415611a29576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180616c286026913960400191505060405180910390fd5b60008711611a82576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180616bdb6027913960400191505060405180910390fd5b611a8b33615671565b60006006819055508d8d60029190611aa4929190616b07565b508b8b60039190611ab6929190616b07565b5089600460006101000a81548160ff021916908360ff160217905550611adb886157b5565b600860000160008201518160000155905050611af56157d3565b6008600101600082015181600001559050508660086002018190555042600860030181905550838390508686905014611b96576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4172726179206c656e677468206d69736d61746368000000000000000000000081525060200191505060405180910390fd5b60008090505b86869050811015611c0757611beb878783818110611bb657fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff16868684818110611bdf57fe5b905060200201356157f9565b50611c006001826159f890919063ffffffff16565b9050611b9c565b50611c1189614a3e565b818160405160200180838380828437808301925050509250505060405160208183030381529060405280519060200120600c819055505050505050505050505050505050565b611c5f614376565b611cd1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b611cd9616af4565b6000611ce3615454565b80925081935050506008600301548114611d7a5781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97611d57600860010160405180602001604052908160008201548152505061561f565b600860030154604051808381526020018281526020019250505060405180910390a15b6000841415611dd4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180616c4e6027913960400191505060405180910390fd5b60008311611e4a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f757064617465506572696f64206d757374206265203e2030000000000000000081525060200191505060405180910390fd5b611e53846157b5565b600860000160008201518160000155905050826008600201819055507fa0035d6667ffb7d387c86c7228141c4a877e8ed831b267ac928a2f5b651c155d84844260405180848152602001838152602001828152602001935050505060405180910390a150505050565b6000611ec6616af4565b6000611ed0615454565b80925081935050506008600301548114611f675781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97611f44600860010160405180602001604052908160008201548152505061561f565b600860030154604051808381526020018281526020019250505060405180910390a15b611f6f615a80565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611feb57600080fd5b505afa158015611fff573d6000803e3d6000fd5b505050506040513d602081101561201557600080fd5b81019080805190602001909291905050501561207c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180616cad6022913960400191505060405180910390fd5b60006120a1600860010160405180602001604052908160008201548152505086615644565b9050600073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415612129576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180616f02602a913960400191505060405180910390fd5b600560008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548111156121c1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526029815260200180616df86029913960400191505060405180910390fd5b600760008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054851115612296576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526038815260200180616e216038913960400191505060405180910390fd5b6122e881600560008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546159f890919063ffffffff16565b600560008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061237d81600560008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054615b7b90919063ffffffff16565b600560008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061244f85600760008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054615b7b90919063ffffffff16565b600760008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040518082815260200191505060405180910390a3600193505050509392505050565b60008060fb73ffffffffffffffffffffffffffffffffffffffff16858585604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140183805190602001908083835b602083106125cb57805182526020820191506020810190506020830392506125a8565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b6020831061261c57805182526020820191506020810190506020830392506125f9565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506040516020818303038152906040526040518082805190602001908083835b602083106126855780518252602082019150602081019050602083039250612662565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146126e5576040519150601f19603f3d011682016040523d82523d6000602084013e6126ea565b606091505b505080915050809150509392505050565b6000600460009054906101000a900460ff16905090565b600061271c616af4565b6000612726615454565b809250819350505060086003015481146127bd5781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a9761279a600860010160405180602001604052908160008201548152505061561f565b600860030154604051808381526020018281526020019250505060405180910390a15b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161415612843576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180616dad602a913960400191505060405180910390fd5b6000600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905060006128d986836159f890919063ffffffff16565b905080600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3600194505050505092915050565b60006129e2826129dd614cab565b615bc5565b9050919050565b60008060001b600c541415612a465760405160200180807f45786368616e67650000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001209050612a4c565b600c5490505b90565b6000612a59616af4565b6000612a63615454565b80925081935050506008600301548114612afa5781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97612ad7600860010160405180602001604052908160008201548152505061561f565b600860030154604051808381526020018281526020019250505060405180910390a15b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed612b406129e9565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612b7457600080fd5b505afa158015612b88573d6000803e3d6000fd5b505050506040513d6020811015612b9e57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480612d065750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd92723360405160200180807f56616c696461746f727300000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612c9c57600080fd5b505afa158015612cb0573d6000803e3d6000fd5b505050506040513d6020811015612cc657600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b80612e2f5750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd92723360405160200180807f4772616e64614d656e746f000000000000000000000000000000000000000000815250600b019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612dc557600080fd5b505afa158015612dd9573d6000803e3d6000fd5b505050506040513d6020811015612def57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b612ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f53656e646572206e6f7420617574686f72697a656420746f206d696e7400000081525060200191505060405180910390fd5b612eab85856157f9565b9250505092915050565b6000612ebf616af4565b6000612ec9615454565b80925081935050506008600301548114612f605781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97612f3d600860010160405180602001604052908160008201548152505061561f565b600860030154604051808381526020018281526020019250505060405180910390a15b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed612fa66129e9565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612fda57600080fd5b505afa158015612fee573d6000803e3d6000fd5b505050506040513d602081101561300457600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061316c5750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd92723360405160200180807f4772616e64614d656e746f000000000000000000000000000000000000000000815250600b019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561310257600080fd5b505afa158015613116573d6000803e3d6000fd5b505050506040513d602081101561312c57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b6131de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f53656e646572206e6f7420617574686f72697a656420746f206275726e00000081525060200191505060405180910390fd5b6000613203600860010160405180602001604052908160008201548152505086615644565b9050600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548111156132ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f76616c75652065786365656465642062616c616e6365206f662073656e64657281525060200191505060405180910390fd5b6132cf81600654615b7b90919063ffffffff16565b60068190555061332781600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054615b7b90919063ffffffff16565b600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a360019350505050919050565b60006060600060f473ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310613431578051825260208201915060208101905060208303925061340e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106134985780518252602082019150602081019050602083039250613475565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146134f8576040519150601f19603f3d011682016040523d82523d6000602084013e6134fd565b606091505b5080935081925050508061355c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526038815260200180616c756038913960400191505060405180910390fd5b613567826000615c0d565b92505050919050565b6000806000806001600260006001839350829250819150809050935093509350935090919293565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461363a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b613642615a80565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156136be57600080fd5b505afa1580156136d2573d6000803e3d6000fd5b505050506040513d60208110156136e857600080fd5b81019080805190602001909291905050501561374f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180616cad6022913960400191505060405180910390fd5b613757616af4565b6000613761615454565b809250819350505060086003015481146137f85781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a976137d5600860010160405180602001604052908160008201548152505061561f565b600860030154604051808381526020018281526020019250505060405180910390a15b600061381d600860010160405180602001604052908160008201548152505085615644565b905061387181600560008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054615b7b90919063ffffffff16565b600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506138c981600654615b7b90919063ffffffff16565b6006819055505050505050565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16858560405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061394f578051825260208201915060208101905060208303925061392c565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146139af576040519150601f19603f3d011682016040523d82523d6000602084013e6139b4565b606091505b50809350819250505080613a13576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180616d776036913960400191505060405180910390fd5b613a1e82600061562d565b9250505092915050565b60006060600060f673ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310613a7d5780518252602082019150602081019050602083039250613a5a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310613ae45780518252602082019150602081019050602083039250613ac1565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613b44576040519150601f19603f3d011682016040523d82523d6000602084013e613b49565b606091505b50809350819250505080613ba8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180616f2c6023913960400191505060405180910390fd5b613bb3826000615c0d565b92505050919050565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614613c5e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b613c66615a80565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015613ce257600080fd5b505afa158015613cf6573d6000803e3d6000fd5b505050506040513d6020811015613d0c57600080fd5b810190808051906020019092919050505015613d73576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180616cad6022913960400191505060405180910390fd5b6000613d98600860010160405180602001604052908160008201548152505086615644565b9050613dec81600560008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546159f890919063ffffffff16565b600560008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550613e4c613e3d8a8885615cae565b826159f890919063ffffffff16565b9050613e6b613e5c8a8a87615cae565b826159f890919063ffffffff16565b9050613e8a613e7b8a8986615cae565b826159f890919063ffffffff16565b9050613ea1816006546159f890919063ffffffff16565b600681905550505050505050505050565b6000613efc600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054614be2565b9050919050565b613f0b614376565b613f7d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600061404743615026565b905090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1643604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106140e357805182526020820191506020810190506020830392506140c0565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114614143576040519150601f19603f3d011682016040523d82523d6000602084013e614148565b606091505b508093508192505050806141a7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526035815260200180616d426035913960400191505060405180910390fd5b6141b282600061562d565b9250505090565b60006060600060f773ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b6020831061420e57805182526020820191506020810190506020830392506141eb565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106142755780518252602082019150602081019050602083039250614252565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146142d5576040519150601f19603f3d011682016040523d82523d6000602084013e6142da565b606091505b50809350819250505080614339576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180616ed16031913960400191505060405180910390fd5b61434482600061562d565b92505050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166143b8615e1a565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b606060038054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561446c5780601f106144415761010080835404028352916020019161446c565b820191906000526020600020905b81548152906001019060200180831161444f57829003601f168201915b5050505050905090565b6000614481436129cf565b905090565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106144f757805182526020820191506020810190506020830392506144d4565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114614557576040519150601f19603f3d011682016040523d82523d6000602084013e61455c565b606091505b508093508192505050806145bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180616bad602e913960400191505060405180910390fd5b6145c682600061562d565b92505050919050565b60006145d9616af4565b60006145e3615454565b8092508193505050600860030154811461467a5781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97614657600860010160405180602001604052908160008201548152505061561f565b600860030154604051808381526020018281526020019250505060405180910390a15b6000600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905060006147108683615b7b90919063ffffffff16565b905080600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3600194505050505092915050565b60008060008061482e600860000160405180602001604052908160008201548152505061561f565b614850600860010160405180602001604052908160008201548152505061561f565b600860020154600860030154935093509350935090919293565b6000614874616af4565b600061487e615454565b809250819350505060086003015481146149155781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a976148f2600860010160405180602001604052908160008201548152505061561f565b600860030154604051808381526020018281526020019250505060405180910390a15b61491d615a80565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561499957600080fd5b505afa1580156149ad573d6000803e3d6000fd5b505050506040513d60208110156149c357600080fd5b810190808051906020019092919050505015614a2a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180616cad6022913960400191505060405180910390fd5b614a348585615e22565b9250505092915050565b614a46614376565b614ab8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415614b5b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b6000614bec616af4565b614bf4615454565b5080915050614c1c614c1782614c0986616102565b61618c90919063ffffffff16565b6162d5565b915050919050565b6000600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b60006060600060f873ffffffffffffffffffffffffffffffffffffffff166040516020016040516020818303038152906040526040518082805190602001908083835b60208310614d115780518252602082019150602081019050602083039250614cee565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114614d71576040519150601f19603f3d011682016040523d82523d6000602084013e614d76565b606091505b50809350819250505080614dd5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180616e806025913960400191505060405180910390fd5b614de082600061562d565b9250505090565b6000614df1616af4565b6000614dfb615454565b80925081935050506008600301548114614e925781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97614e6f600860010160405180602001604052908160008201548152505061561f565b600860030154604051808381526020018281526020019250505060405180910390a15b614e9a615a80565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015614f1657600080fd5b505afa158015614f2a573d6000803e3d6000fd5b505050506040513d6020811015614f4057600080fd5b810190808051906020019092919050505015614fa7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180616cad6022913960400191505060405180910390fd5b6000614fb3888861486a565b90507fe5d4e30fb8364e57bc4d662a07d0cf36f4c34552004c4c3624620a2c1d1c03dc868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a1809350505050949350505050565b600061506a600361505c600261504e600261504088614486565b6162f690919063ffffffff16565b6159f890919063ffffffff16565b61637c90919063ffffffff16565b9050919050565b60008060008714158015615086575060008514155b6150f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f612064656e6f6d696e61746f72206973207a65726f000000000000000000000081525060200191505060405180910390fd5b6000806000606060fc73ffffffffffffffffffffffffffffffffffffffff168c8c8c8c8c8c6040516020018087815260200186815260200185815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040526040518082805190602001908083835b60208310615192578051825260208201915060208101905060208303925061516f565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146151f2576040519150601f19603f3d011682016040523d82523d6000602084013e6151f7565b606091505b50809250819350505081615256576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180616e596027913960400191505060405180910390fd5b61526181600061562d565b935061526e81602061562d565b925083839550955050505050965096945050505050565b61528d614376565b6152ff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61530881615671565b50565b60006060600060f573ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b6020831061537c5780518252602082019150602081019050602083039250615359565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146153dc576040519150601f19603f3d011682016040523d82523d6000602084013e6153e1565b606091505b50809350819250505080615440576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180616ea5602c913960400191505060405180910390fd5b61544b826000615c0d565b92505050919050565b61545c616af4565b600061547b6008600201546008600301546159f890919063ffffffff16565b4210156154ae5760086001016008600301548160405180602001604052908160008201548152505091509150915061561b565b60008060006154e26008600201546154d460086003015442615b7b90919063ffffffff16565b61637c90919063ffffffff16565b9050615564615509600860010160405180602001604052908160008201548152505061561f565b6155196155146157d3565b61561f565b61553b600860000160405180602001604052908160008201548152505061561f565b61554b6155466157d3565b61561f565b85600460009054906101000a900460ff1660ff16615071565b8093508194505050600083148061557b5750600082145b156155af5760086001016008600301548160405180602001604052908160008201548152505091509450945050505061561b565b6155b7616af4565b6155da6155c3846157b5565b6155cc866157b5565b61618c90919063ffffffff16565b9050600061560d6155f9846008600201546162f690919063ffffffff16565b6008600301546159f890919063ffffffff16565b905081819650965050505050505b9091565b600081600001519050919050565b60006156398383615c0d565b60001c905092915050565b600061566961566461565584616102565b856163c690919063ffffffff16565b6162d5565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156156f7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180616c026026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6157bd616af4565b6040518060200160405280838152509050919050565b6157db616af4565b604051806020016040528069d3c21bcecceda1000000815250905090565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561589d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f302069732061207265736572766564206164647265737300000000000000000081525060200191505060405180910390fd5b60008214156158af57600190506159f2565b60006158d4600860010160405180602001604052908160008201548152505084615644565b90506158eb816006546159f890919063ffffffff16565b60068190555061594381600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546159f890919063ffffffff16565b600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150505b92915050565b600080828401905083811015615a76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f467265657a6572000000000000000000000000000000000000000000000000008152506007019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015615b3b57600080fd5b505afa158015615b4f573d6000803e3d6000fd5b505050506040513d6020811015615b6557600080fd5b8101908080519060200190929190505050905090565b6000615bbd83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250616825565b905092915050565b600080828481615bd157fe5b0490506000838581615bdf57fe5b061415615bef5780915050615c07565b615c036001826159f890919063ffffffff16565b9150505b92915050565b6000615c236020836159f890919063ffffffff16565b83511015615c99576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f736c6963696e67206f7574206f662072616e676500000000000000000000000081525060200191505060405180910390fd5b60006020830184015190508091505092915050565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415615ced5760009050615e13565b6000615d12600860010160405180602001604052908160008201548152505084615644565b9050615d6681600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546159f890919063ffffffff16565b600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3809150505b9392505050565b600033905090565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415615ea9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180616f02602a913960400191505060405180910390fd5b6000615ece600860010160405180602001604052908160008201548152505084615644565b905080600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015615f68576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526029815260200180616df86029913960400191505060405180910390fd5b615fba81600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054615b7b90919063ffffffff16565b600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061604f81600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546159f890919063ffffffff16565b600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191505092915050565b61610a616af4565b6161126168e5565b82111561616a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180616ccf6036913960400191505060405180910390fd5b604051806020016040528069d3c21bcecceda100000084028152509050919050565b616194616af4565b60008260000151141561620f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f63616e277420646976696465206279203000000000000000000000000000000081525060200191505060405180910390fd5b600069d3c21bcecceda10000008460000151029050836000015169d3c21bcecceda1000000828161623c57fe5b04146162b0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6f766572666c6f7720617420646976696465000000000000000000000000000081525060200191505060405180910390fd5b6040518060200160405280846000015183816162c857fe5b0481525091505092915050565b600069d3c21bcecceda10000008260000151816162ee57fe5b049050919050565b6000808314156163095760009050616376565b600082840290508284828161631a57fe5b0414616371576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180616dd76021913960400191505060405180910390fd5b809150505b92915050565b60006163be83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250616904565b905092915050565b6163ce616af4565b6000836000015114806163e5575060008260000151145b156164015760405180602001604052806000815250905061681f565b69d3c21bcecceda10000008260000151141561641f5782905061681f565b69d3c21bcecceda10000008360000151141561643d5781905061681f565b600069d3c21bcecceda1000000616453856169ca565b600001518161645e57fe5b049050600061646c85616a01565b600001519050600069d3c21bcecceda1000000616488866169ca565b600001518161649357fe5b04905060006164a186616a01565b600001519050600082850290506000851461653557828582816164c057fe5b0414616534576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600069d3c21bcecceda100000082029050600082146165d75769d3c21bcecceda100000082828161656257fe5b04146165d6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f6f766572666c6f772078317931202a206669786564312064657465637465640081525060200191505060405180910390fd5b5b809150600084860290506000861461666857848682816165f357fe5b0414616667576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b60008488029050600088146166f6578488828161668157fe5b04146166f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b6166fe616a3e565b878161670657fe5b049650616711616a3e565b858161671957fe5b04945060008588029050600088146167aa578588828161673557fe5b04146167a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b6167b2616af4565b60405180602001604052808781525090506167db81604051806020016040528087815250616a4b565b90506167f581604051806020016040528086815250616a4b565b905061680f81604051806020016040528085815250616a4b565b9050809a50505050505050505050505b92915050565b60008383111582906168d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561689757808201518184015260208101905061687c565b50505050905090810190601f1680156168c45780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b60007601357c299a88ea76a58924d52ce4f26a85af186c2b9e74905090565b600080831182906169b0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561697557808201518184015260208101905061695a565b50505050905090810190601f1680156169a25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816169bc57fe5b049050809150509392505050565b6169d2616af4565b604051806020016040528069d3c21bcecceda1000000808560000151816169f557fe5b04028152509050919050565b616a09616af4565b604051806020016040528069d3c21bcecceda100000080856000015181616a2c57fe5b04028460000151038152509050919050565b600064e8d4a51000905090565b616a53616af4565b6000826000015184600001510190508360000151811015616adc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f616464206f766572666c6f77206465746563746564000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808281525091505092915050565b6040518060200160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10616b4857803560ff1916838001178555616b76565b82800160010185558215616b76579182015b82811115616b75578235825591602001919060010190616b5a565b5b509050616b839190616b87565b5090565b616ba991905b80821115616ba5576000816000905550600101616b8d565b5090565b9056fe6572726f722063616c6c696e67206e756d62657256616c696461746f7273496e53657420707265636f6d70696c65696e666c6174696f6e466163746f72557064617465506572696f64206d757374206265203e20304f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734d7573742070726f766964652061206e6f6e2d7a65726f20696e666c6174696f6e20726174654d7573742070726f766964652061206e6f6e2d7a65726f20696e666c6174696f6e20726174652e6572726f722063616c6c696e672067657456657269666965645365616c4269746d617046726f6d48656164657220707265636f6d70696c6563616e27742063616c6c207768656e20636f6e74726163742069732066726f7a656e63616e277420637265617465206669786964697479206e756d626572206c6172676572207468616e206d61784e6577466978656428296572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e67206e756d62657256616c696461746f7273496e43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d53657420707265636f6d70696c6572657365727665642061646472657373203078302063616e6e6f74206861766520616c6c6f77616e6365536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f777472616e736665722076616c75652065786365656465642062616c616e6365206f662073656e6465727472616e736665722076616c75652065786365656465642073656e646572277320616c6c6f77616e636520666f7220726563697069656e746572726f722063616c6c696e67206672616374696f6e4d756c45787020707265636f6d70696c656572726f722063616c6c696e672067657445706f636853697a6520707265636f6d70696c656572726f722063616c6c696e6720676574506172656e745365616c4269746d617020707265636f6d70696c656572726f722063616c6c696e6720676574426c6f636b4e756d62657246726f6d48656164657220707265636f6d70696c657472616e7366657220617474656d7074656420746f2072657365727665642061646472657373203078306572726f722063616c6c696e67206861736848656164657220707265636f6d70696c65a265627a7a72315820980595f3c0ea09d14bc3dab4cdd338b43808a39f35efc017ab8c609273a7e0bf64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f009": { + "code": "0x608060405234801561001057600080fd5b50600436106101c45760003560e01c80637b103999116100f9578063b66a261c11610097578063db1bc87b11610071578063db1bc87b14610735578063dda57b9314610779578063e0c8b50a14610797578063f2fde38b146107b5576101c4565b8063b66a261c14610681578063c3434883146106af578063d404f7f814610707576101c4565b80638da5cb5b116100d35780638da5cb5b146105835780638f32d59b146105cd5780639ed02b58146105ef578063a91ee0dc1461063d576101c4565b80637b1039991461045557806386489ba91461049f5780638ab1a5d41461052b576101c4565b806354255be011610166578063673ea08611610140578063673ea086146103b45780636a5eaf47146103d2578063715018a61461040057806378ba9cfd1461040a576101c4565b806354255be0146103455780635c25c76c1461037857806362f0908414610396576101c4565b806325ac2de6116101a257806325ac2de6146102535780632bc7d67a146102715780634a1be6cb146102c95780634c0226a2146102f7576101c4565b8063158ef93e146101c957806322503ce5146101eb57806322be3de114610209575b600080fd5b6101d16107f9565b604051808215151515815260200191505060405180910390f35b6101f361080b565b6040518082815260200191505060405180910390f35b610211610811565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61025b610837565b6040518082815260200191505060405180910390f35b6102b36004803603606081101561028757600080fd5b81019080803590602001909291908035906020019092919080351515906020019092919050505061083d565b6040518082815260200191505060405180910390f35b6102f5600480360360208110156102df57600080fd5b8101908080359060200190929190505050610853565b005b61032f6004803603604081101561030d57600080fd5b810190808035906020019092919080351515906020019092919050505061090e565b6040518082815260200191505060405180910390f35b61034d610935565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b61038061095c565b6040518082815260200191505060405180910390f35b61039e610968565b6040518082815260200191505060405180910390f35b6103bc61096e565b6040518082815260200191505060405180910390f35b6103fe600480360360208110156103e857600080fd5b8101908080359060200190929190505050610974565b005b610408610ac5565b005b6104386004803603602081101561042057600080fd5b81019080803515159060200190929190505050610bff565b604051808381526020018281526020019250505060405180910390f35b61045d610c4f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610529600480360360c08110156104b557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050610c75565b005b61056d6004803603606081101561054157600080fd5b810190808035906020019092919080359060200190929190803515159060200190929190505050610d60565b6040518082815260200191505060405180910390f35b61058b610f9f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105d5610fc8565b604051808215151515815260200191505060405180910390f35b6106276004803603604081101561060557600080fd5b8101908080359060200190929190803515159060200190929190505050611026565b6040518082815260200191505060405180910390f35b61067f6004803603602081101561065357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061104d565b005b6106ad6004803603602081101561069757600080fd5b81019080803590602001909291905050506111f1565b005b6106f1600480360360608110156106c557600080fd5b8101908080359060200190929190803590602001909291908035151590602001909291905050506112bd565b6040518082815260200191505060405180910390f35b6107336004803603602081101561071d57600080fd5b8101908080359060200190929190505050611503565b005b6107776004803603602081101561074b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506115be565b005b6107816116bf565b6040518082815260200191505060405180910390f35b61079f6116cb565b6040518082815260200191505060405180910390f35b6107f7600480360360208110156107cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506116d1565b005b6000809054906101000a900460ff1681565b600a5481565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60075481565b600061084a848484610d60565b90509392505050565b61085b610fc8565b6108cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b806009819055507f90c0a4a142fbfbc2ae8c21f50729a2f4bc56e85a66c1a1b6654f1e85092a54a6816040518082815260200191505060405180910390a150565b600080600061091c84610bff565b9150915061092b828287611757565b9250505092915050565b60008060008060018060016000839350829250819150809050935093509350935090919293565b60038060000154905081565b60065481565b60095481565b61097c610fc8565b6109ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6109f781611824565b600460008201518160000155905050610a36610a11611842565b600460405180602001604052908160008201548152505061186890919063ffffffff16565b610a8b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806139546027913960400191505060405180910390fd5b7fb690f84efb1d9039c2834effb7bebc792a85bfec7ef84f4b269528454f363ccf816040518082815260200191505060405180910390a150565b610acd610fc8565b610b3f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000806000600654905060006007549050610c1861187d565b15610c2e57610c25611c7a565b80925081935050505b8415610c41578082935093505050610c4a565b81819350935050505b915091565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900460ff1615610cf7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b60016000806101000a81548160ff021916908315150217905550610d1a33611cd3565b610d238661104d565b610d2c856115be565b610d35846111f1565b610d3e83610974565b610d4782610853565b610d5081611503565b610d58611e19565b505050505050565b6000610d6a611e8f565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610de657600080fd5b505afa158015610dfa573d6000803e3d6000fd5b505050506040513d6020811015610e1057600080fd5b810190808051906020019092919050505015610e77576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061386d6022913960400191505060405180910390fd5b610e7f611e19565b600160026000828254019250508190555060006002549050600080610ea385611f8a565b915091506000610eb483838a611fb2565b905086811015610f0f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260398152602001806138346039913960400191505060405180910390fd5b610f1a888288612052565b8094505050506002548114610f97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b509392505050565b60008060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661100a61285e565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b600080600061103484610bff565b91509150611043828287611fb2565b9250505092915050565b611055610fc8565b6110c7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561116a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b6111f9610fc8565b61126b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61127481611824565b6003600082015181600001559050507f8946f328efcc515b5cc3282f6cd95e87a6c0d3508421af0b52d4d3620b3e2db3816040518082815260200191505060405180910390a150565b60006112c7611e8f565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561134357600080fd5b505afa158015611357573d6000803e3d6000fd5b505050506040513d602081101561136d57600080fd5b8101908080519060200190929190505050156113d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061386d6022913960400191505060405180910390fd5b6113dc611e19565b60016002600082825401925050819055506000600254905060008315905060008061140683611f8a565b91509150600061141783838b611757565b905087811115611472576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603e8152602001806138c5603e913960400191505060405180910390fd5b61147d818a86612052565b8095505050505060025481146114fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b509392505050565b61150b610fc8565b61157d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b80600a819055507f08523596abc266fb46d9c40ddf78fdfd3c08142252833ddce1a2b46f76521035816040518082815260200191505060405180910390a150565b6115c6610fc8565b611638576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f119a23392e161a0bc5f9d5f3e2a6040c45b40d43a36973e10ea1de916f3d8a8a60405160405180910390a250565b60048060000154905081565b60085481565b6116d9610fc8565b61174b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61175481611cd3565b50565b60008082141561176a576000905061181d565b6117726137fa565b61178d611788858561286690919063ffffffff16565b6128ec565b90506117976137fa565b6117f36117ca60036040518060200160405290816000820154815250506117bc611842565b61297690919063ffffffff16565b6117e56117e0878a612a1d90919063ffffffff16565b6128ec565b612a6790919063ffffffff16565b905061181861180182612ec6565b61180a84612ec6565b612ed490919063ffffffff16565b925050505b9392505050565b61182c6137fa565b6040518060200160405280838152509050919050565b61184a6137fa565b604051806020016040528069d3c21bcecceda1000000815250905090565b60008160000151836000015110905092915050565b600080600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f536f727465644f7261636c657300000000000000000000000000000000000000815250600d019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561193957600080fd5b505afa15801561194d573d6000803e3d6000fd5b505050506040513d602081101561196357600080fd5b8101908080519060200190929190505050905060008173ffffffffffffffffffffffffffffffffffffffff1663ffe736bf600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050604080518083038186803b158015611a1657600080fd5b505afa158015611a2a573d6000803e3d6000fd5b505050506040513d6040811015611a4057600080fd5b8101908080519060200190929190805190602001909291905050505090506000611a77600954600854612f1e90919063ffffffff16565b42101590506000600a548473ffffffffffffffffffffffffffffffffffffffff1663bbc66a94600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611b2057600080fd5b505afa158015611b34573d6000803e3d6000fd5b505050506040513d6020811015611b4a57600080fd5b8101908080519060200190929190505050101590506000611b7660095442612a1d90919063ffffffff16565b8573ffffffffffffffffffffffffffffffffffffffff1663071b48fc600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611c1557600080fd5b505afa158015611c29573d6000803e3d6000fd5b505050506040513d6020811015611c3f57600080fd5b8101908080519060200190929190505050119050828015611c5d5750815b8015611c665750805b8015611c70575083155b9550505050505090565b6000806000611c87612fa6565b9050600080611c94613072565b80925081935050506000611cc382611cb5868661286690919063ffffffff16565b612ed490919063ffffffff16565b9050838195509550505050509091565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611d59576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061380e6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600060016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b611e2161187d565b15611e8d5742600881905550611e35611c7a565b60066000600760008491905055839190505550507fa18ec663cb684011386aa866c4dacb32d2d2ad859a35d3440b6ce7200a76bad8600654600754604051808381526020018281526020019250505060405180910390a15b565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f467265657a6572000000000000000000000000000000000000000000000000008152506007019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015611f4a57600080fd5b505afa158015611f5e573d6000803e3d6000fd5b505050506040513d6020811015611f7457600080fd5b8101908080519060200190929190505050905090565b6000808215611fa25760075460065491509150611fad565b600654600754915091505b915091565b600080821415611fc5576000905061204b565b611fcd6137fa565b611fd6836132bb565b9050611fe06137fa565b611ffb611fec876128ec565b83612a6790919063ffffffff16565b90506120056137fa565b61202083612012886128ec565b61331490919063ffffffff16565b905061204561202e82612ec6565b61203784612ec6565b612ed490919063ffffffff16565b93505050505b9392505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f52657365727665000000000000000000000000000000000000000000000000008152506007019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561210d57600080fd5b505afa158015612121573d6000803e3d6000fd5b505050506040513d602081101561213757600080fd5b8101908080519060200190929190505050905081156124505761216584600654612f1e90919063ffffffff16565b60068190555061218083600754612a1d90919063ffffffff16565b60078190555061218e6133bd565b73ffffffffffffffffffffffffffffffffffffffff166323b872dd3383876040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050602060405180830381600087803b15801561224857600080fd5b505af115801561225c573d6000803e3d6000fd5b505050506040513d602081101561227257600080fd5b81019080805190602001909291905050506122f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f5472616e73666572206f662073656c6c20746f6b656e206661696c656400000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166340c10f1933856040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561239e57600080fd5b505af11580156123b2573d6000803e3d6000fd5b505050506040513d60208110156123c857600080fd5b810190808051906020019092919050505061244b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f4d696e74206f6620737461626c6520746f6b656e206661696c6564000000000081525060200191505060405180910390fd5b6127f6565b61246584600754612f1e90919063ffffffff16565b60078190555061248083600654612a1d90919063ffffffff16565b600681905550600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd3330876040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050602060405180830381600087803b15801561256357600080fd5b505af1158015612577573d6000803e3d6000fd5b505050506040513d602081101561258d57600080fd5b8101908080519060200190929190505050612610576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f5472616e73666572206f662073656c6c20746f6b656e206661696c656400000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342966c68856040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561268557600080fd5b505af1158015612699573d6000803e3d6000fd5b505050506040513d60208110156126af57600080fd5b8101908080519060200190929190505050508073ffffffffffffffffffffffffffffffffffffffff166303a0fea333856040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561274857600080fd5b505af115801561275c573d6000803e3d6000fd5b505050506040513d602081101561277257600080fd5b81019080805190602001909291905050506127f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f5472616e73666572206f6620627579546f6b656e206661696c6564000000000081525060200191505060405180910390fd5b5b3373ffffffffffffffffffffffffffffffffffffffff167f402ac9185b4616422c2794bf5b118bfcc68ed496d52c0d9841dfa114fdeb05ba8585856040518084815260200183815260200182151515158152602001935050505060405180910390a250505050565b600033905090565b60008083141561287957600090506128e6565b600082840290508284828161288a57fe5b04146128e1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806139336021913960400191505060405180910390fd5b809150505b92915050565b6128f46137fa565b6128fc6134b8565b821115612954576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061388f6036913960400191505060405180910390fd5b604051806020016040528069d3c21bcecceda100000084028152509050919050565b61297e6137fa565b8160000151836000015110156129fc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f737562737472616374696f6e20756e646572666c6f772064657465637465640081525060200191505060405180910390fd5b60405180602001604052808360000151856000015103815250905092915050565b6000612a5f83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506134d7565b905092915050565b612a6f6137fa565b600083600001511480612a86575060008260000151145b15612aa257604051806020016040528060008152509050612ec0565b69d3c21bcecceda100000082600001511415612ac057829050612ec0565b69d3c21bcecceda100000083600001511415612ade57819050612ec0565b600069d3c21bcecceda1000000612af485613597565b6000015181612aff57fe5b0490506000612b0d856135ce565b600001519050600069d3c21bcecceda1000000612b2986613597565b6000015181612b3457fe5b0490506000612b42866135ce565b6000015190506000828502905060008514612bd65782858281612b6157fe5b0414612bd5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600069d3c21bcecceda10000008202905060008214612c785769d3c21bcecceda1000000828281612c0357fe5b0414612c77576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f6f766572666c6f772078317931202a206669786564312064657465637465640081525060200191505060405180910390fd5b5b8091506000848602905060008614612d095784868281612c9457fe5b0414612d08576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b6000848802905060008814612d975784888281612d2257fe5b0414612d96576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b612d9f61360b565b8781612da757fe5b049650612db261360b565b8581612dba57fe5b0494506000858802905060008814612e4b5785888281612dd657fe5b0414612e4a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b612e536137fa565b6040518060200160405280878152509050612e7c81604051806020016040528087815250613314565b9050612e9681604051806020016040528086815250613314565b9050612eb081604051806020016040528085815250613314565b9050809a50505050505050505050505b92915050565b600081600001519050919050565b6000612f1683836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613618565b905092915050565b600080828401905083811015612f9c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600080612fb16136de565b73ffffffffffffffffffffffffffffffffffffffff16638b7df8d46040518163ffffffff1660e01b815260040160206040518083038186803b158015612ff657600080fd5b505afa15801561300a573d6000803e3d6000fd5b505050506040513d602081101561302057600080fd5b8101908080519060200190929190505050905061306c613067613042836128ec565b6004604051806020016040529081600082015481525050612a6790919063ffffffff16565b6137d9565b91505090565b600080600080600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f536f727465644f7261636c657300000000000000000000000000000000000000815250600d019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561313157600080fd5b505afa158015613145573d6000803e3d6000fd5b505050506040513d602081101561315b57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1663ef90e1b0600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050604080518083038186803b15801561320957600080fd5b505afa15801561321d573d6000803e3d6000fd5b505050506040513d604081101561323357600080fd5b8101908080519060200190929190805190602001909291905050508092508193505050600081116132af576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806139036030913960400191505060405180910390fd5b81819350935050509091565b6132c36137fa565b61330d6132cf836128ec565b6132ff60036040518060200160405290816000820154815250506132f1611842565b61297690919063ffffffff16565b612a6790919063ffffffff16565b9050919050565b61331c6137fa565b60008260000151846000015101905083600001518110156133a5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f616464206f766572666c6f77206465746563746564000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808281525091505092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f476f6c64546f6b656e00000000000000000000000000000000000000000000008152506009019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561347857600080fd5b505afa15801561348c573d6000803e3d6000fd5b505050506040513d60208110156134a257600080fd5b8101908080519060200190929190505050905090565b60007601357c299a88ea76a58924d52ce4f26a85af186c2b9e74905090565b6000838311158290613584576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561354957808201518184015260208101905061352e565b50505050905090810190601f1680156135765780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b61359f6137fa565b604051806020016040528069d3c21bcecceda1000000808560000151816135c257fe5b04028152509050919050565b6135d66137fa565b604051806020016040528069d3c21bcecceda1000000808560000151816135f957fe5b04028460000151038152509050919050565b600064e8d4a51000905090565b600080831182906136c4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561368957808201518184015260208101905061366e565b50505050905090810190601f1680156136b65780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816136d057fe5b049050809150509392505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f52657365727665000000000000000000000000000000000000000000000000008152506007019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561379957600080fd5b505afa1580156137ad573d6000803e3d6000fd5b505050506040513d60208110156137c357600080fd5b8101908080519060200190929190505050905090565b600069d3c21bcecceda10000008260000151816137f257fe5b049050919050565b604051806020016040528060008152509056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737343616c63756c6174656420627579416d6f756e7420776173206c657373207468616e20737065636966696564206d696e427579416d6f756e7463616e27742063616c6c207768656e20636f6e74726163742069732066726f7a656e63616e277420637265617465206669786964697479206e756d626572206c6172676572207468616e206d61784e65774669786564282943616c63756c617465642073656c6c416d6f756e74207761732067726561746572207468616e20737065636966696564206d617853656c6c416d6f756e7465786368616e676520726174652064656e6f6d696e61746f72206d7573742062652067726561746572207468616e2030536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7772657365727665206672616374696f6e206d75737420626520736d616c6c6572207468616e2031a265627a7a72315820761f3070f713d7a5909130c457c71c27609c2dce73019257807d78f978ed978664736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f010": { + "code": "0x608060405234801561001057600080fd5b50600436106103d05760003560e01c80637b103999116101ff578063a91ee0dc1161011a578063c48830d9116100ad578063f2fde38b1161007c578063f2fde38b14611e4c578063f333d83614611e90578063fbe3c37314611ed6578063ff836d9314611f24576103d0565b8063c48830d914611d36578063c4d66de814611dbc578063e7a16e6b14611e00578063f0c5658414611e2e576103d0565b8063ba40e4f6116100e9578063ba40e4f614611b09578063baf7ef0f14611bba578063c2e0ee2014611c1f578063c47f002714611c7b576103d0565b8063a91ee0dc146118b5578063ae32fa0e146118f9578063b5a664c2146119b6578063b6c6662514611a3a576103d0565b806391cd074b116101925780639dca362f116101615780639dca362f1461177e5780639f682976146117a0578063a5ec94f9146117ee578063a8ae1a3d146117f8576103d0565b806391cd074b146115a057806392f90fbf1461162657806393c5c487146116955780639cafb2a114611719576103d0565b80638bceca58116101ce5780638bceca58146113975780638da5cb5b146114255780638f32d59b1461146f57806390b12b4714611491576103d0565b80637b103999146111015780637b2434cb1461114b57806387affe68146111cf5780638adaf96f1461125d576103d0565b806349045e16116102ef57806361bab1ae11610282578063727d079c11610251578063727d079c14610fcb578063747daec514611019578063760fbbb21461109257806376afa04c1461109c576103d0565b806361bab1ae14610e3557806364439b4314610eb95780636642d59414610f3d578063715018a614610fc1576103d0565b80635b07fdd8116102be5780635b07fdd814610c705780635b6d900414610c8e5780635fd4b08a14610d1c578063614ed49314610dd9576103d0565b806349045e1614610b0f5780634ce38b5f14610b6b57806354255be014610bef57806358b81ea814610c22576103d0565b80631465b923116103675780633184b3c5116103365780633184b3c51461099657806341ddd880146109a05780634282ee6d14610a24578063485a46d114610a89576103d0565b80631465b92314610730578063158ef93e146108945780631fd9afa5146108b657806325ca4c9c1461093a576103d0565b80630fa750d2116103a35780630fa750d21461054b5780630fe7abab1461060557806310c504b5146106c05780631441ece7146106ca576103d0565b80630127dbed146103d55780630185a2321461043157806305be62291461045f5780630b8e0562146104c5575b600080fd5b610417600480360360208110156103eb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611f8a565b604051808215151515815260200191505060405180910390f35b61045d6004803603602081101561044757600080fd5b8101908080359060200190929190505050611fe3565b005b6104ab6004803603604081101561047557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061209c565b604051808215151515815260200191505060405180910390f35b610531600480360360608110156104db57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506120df565b604051808215151515815260200191505060405180910390f35b610603600480360360a081101561056157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190803590602001906401000000008111156105bf57600080fd5b8201836020820111156105d157600080fd5b803590602001918460018302840111640100000000831117156105f357600080fd5b9091929391929390505050612189565b005b6106be6004803603602081101561061b57600080fd5b810190808035906020019064010000000081111561063857600080fd5b82018360208201111561064a57600080fd5b8035906020019184600183028401116401000000008311171561066c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506124bb565b005b6106c8612646565b005b610716600480360360408110156106e057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612767565b604051808215151515815260200191505060405180910390f35b610892600480360360e081101561074657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190803590602001906401000000008111156107a457600080fd5b8201836020820111156107b657600080fd5b803590602001918460018302840111640100000000831117156107d857600080fd5b9091929391929390803590602001906401000000008111156107f957600080fd5b82018360208201111561080b57600080fd5b8035906020019184600183028401116401000000008311171561082d57600080fd5b90919293919293908035906020019064010000000081111561084e57600080fd5b82018360208201111561086057600080fd5b8035906020019184600183028401116401000000008311171561088257600080fd5b90919293919293905050506127aa565b005b61089c612b65565b604051808215151515815260200191505060405180910390f35b6108f8600480360360208110156108cc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612b78565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61097c6004803603602081101561095057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612be4565b604051808215151515815260200191505060405180910390f35b61099e612c3d565b005b6109e2600480360360208110156109b657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612d47565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610a8760048036036080811015610a3a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050612da0565b005b610af560048036036060811015610a9f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612f52565b604051808215151515815260200191505060405180910390f35b610b5160048036036020811015610b2557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506130a3565b604051808215151515815260200191505060405180910390f35b610bad60048036036020811015610b8157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061313b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610bf7613194565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b610c6e60048036036040811015610c3857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506131bb565b005b610c78613410565b6040518082815260200191505060405180910390f35b610cda60048036036040811015610ca457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050613416565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610d5e60048036036020811015610d3257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506134d1565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610d9e578082015181840152602081019050610d83565b50505050905090810190601f168015610dcb5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610e1b60048036036020811015610def57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506135b5565b604051808215151515815260200191505060405180910390f35b610e7760048036036020811015610e4b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061360e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610efb60048036036020811015610ecf57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613667565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610f7f60048036036020811015610f5357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506136c0565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610fc9613719565b005b61101760048036036040811015610fe157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050613852565b005b6110906004803603602081101561102f57600080fd5b810190808035906020019064010000000081111561104c57600080fd5b82018360208201111561105e57600080fd5b8035906020019184600183028401116401000000008311171561108057600080fd5b9091929391929390505050613e41565b005b61109a613f92565b005b6110ff600480360360808110156110b257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff16906020019092919080359060200190929190803590602001909291905050506140b3565b005b6111096141d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61118d6004803603602081101561116157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506141fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61121b600480360360408110156111e557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050614255565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6112d46004803603602081101561127357600080fd5b810190808035906020019064010000000081111561129057600080fd5b8201836020820111156112a257600080fd5b803590602001918460208302840111640100000000831117156112c457600080fd5b9091929391929390505050614286565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561131b578082015181840152602081019050611300565b50505050905001838103825284818151815260200191508051906020019080838360005b8381101561135a57808201518184015260208101905061133f565b50505050905090810190601f1680156113875780820380516001836020036101000a031916815260200191505b5094505050505060405180910390f35b6113e3600480360360408110156113ad57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050614585565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61142d614802565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61147761482b565b604051808215151515815260200191505060405180910390f35b61159e600480360360c08110156114a757600080fd5b81019080803590602001906401000000008111156114c457600080fd5b8201836020820111156114d657600080fd5b803590602001918460018302840111640100000000831117156114f857600080fd5b90919293919293908035906020019064010000000081111561151957600080fd5b82018360208201111561152b57600080fd5b8035906020019184600183028401116401000000008311171561154d57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050614889565b005b61160c600480360360608110156115b657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050614950565b604051808215151515815260200191505060405180910390f35b611693600480360360a081101561163c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050614bc3565b005b6116d7600480360360208110156116ab57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614d3d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61177c6004803603608081101561172f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050614e64565b005b6117866151cb565b604051808215151515815260200191505060405180910390f35b6117ec600480360360408110156117b657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050615304565b005b6117f66156ac565b005b61183a6004803603602081101561180e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506157cd565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561187a57808201518184015260208101905061185f565b50505050905090810190601f1680156118a75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6118f7600480360360208110156118cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506158b1565b005b61193b6004803603602081101561190f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050615a55565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561197b578082015181840152602081019050611960565b50505050905090810190601f1680156119a85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6119f8600480360360208110156119cc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050615b39565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b611ac7600480360360c0811015611a5057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050615b6c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b611ba060048036036040811015611b1f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115611b5c57600080fd5b820183602082011115611b6e57600080fd5b80359060200191846001830284011164010000000083111715611b9057600080fd5b9091929391929390505050615cdf565b604051808215151515815260200191505060405180910390f35b611c1d60048036036080811015611bd057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050615d23565b005b611c6160048036036020811015611c3557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050615fea565b604051808215151515815260200191505060405180910390f35b611d3460048036036020811015611c9157600080fd5b8101908080359060200190640100000000811115611cae57600080fd5b820183602082011115611cc057600080fd5b80359060200191846001830284011164010000000083111715611ce257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050616043565b005b611da260048036036060811015611d4c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506161d1565b604051808215151515815260200191505060405180910390f35b611dfe60048036036020811015611dd257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050616205565b005b611e2c60048036036020811015611e1657600080fd5b81019080803590602001909291905050506162c0565b005b611e3661644b565b6040518082815260200191505060405180910390f35b611e8e60048036036020811015611e6257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050616467565b005b611ebc60048036036020811015611ea657600080fd5b81019080803590602001909291905050506164ed565b604051808215151515815260200191505060405180910390f35b611f2260048036036040811015611eec57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506165df565b005b611f7060048036036040811015611f3a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050616730565b604051808215151515815260200191505060405180910390f35b6000611fdc8260405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f72000000000000000000815250601701905060405160208183030381529060405280519060200120612767565b9050919050565b6000611fef3383614255565b9050611ffa826164ed565b61200c57612007826162c0565b612016565b61201582616761565b5b3373ffffffffffffffffffffffffffffffffffffffff167fccc97b55d227538f487c521e1218ba74768b73d088310238027c2ae3b43e9c918284604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a25050565b60008273ffffffffffffffffffffffffffffffffffffffff166120bf8484613416565b73ffffffffffffffffffffffffffffffffffffffff161415905092915050565b60008273ffffffffffffffffffffffffffffffffffffffff16600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161490509392505050565b60018060008282540192505081905550600060015490506121f38760405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f72000000000000000000815250601701905060405160208183030381529060405280519060200120888888616a77565b6122438760405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f72000000000000000000815250601701905060405160208183030381529060405280519060200120613852565b61224b616bf0565b73ffffffffffffffffffffffffffffffffffffffff16634e06fd8a338986866040518563ffffffff1660e01b8152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050602060405180830381600087803b15801561233157600080fd5b505af1158015612345573d6000803e3d6000fd5b505050506040513d602081101561235b57600080fd5b81019080805190602001909291905050506123c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806179776021913960400191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f16e382723fb40543364faf68863212ba253a099607bf6d3a5b47e50a8bf9494388604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a260015481146124b2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50505050505050565b602181511015612533576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6461746120656e6372797074696f6e206b6579206c656e677468203c3d20333281525060200191505060405180910390fd5b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508181600601908051906020019061258e92919061761e565b503373ffffffffffffffffffffffffffffffffffffffff167f43fdefe0a824cb0e3bbaf9c4bc97669187996136fe9282382baf10787f0d808d836040518080602001828103825283818151815260200191508051906020019080838360005b838110156126085780820151818401526020810190506125ed565b50505050905090810190601f1680156126355780820380516001836020036101000a031916815260200191505b509250505060405180910390a25050565b60006126983360405160200180807f63656c6f2e6f72672f636f72652f766f74650000000000000000000000000000815250601201905060405160208183030381529060405280519060200120614585565b90506126ea8160405160200180807f63656c6f2e6f72672f636f72652f766f746500000000000000000000000000008152506012019050604051602081830303815290604052805190602001206165df565b3373ffffffffffffffffffffffffffffffffffffffff167fa197481f404d8a8082368ad7445380f01e75f27dea6b7aef234a4ce071127fae82604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a250565b60008273ffffffffffffffffffffffffffffffffffffffff1661278a8484614585565b73ffffffffffffffffffffffffffffffffffffffff161415905092915050565b60018060008282540192505081905550600060015490506128148b60405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f720000000000000000008152506017019050604051602081830303815290604052805190602001208c8c8c616a77565b6128648b60405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f72000000000000000000815250601701905060405160208183030381529060405280519060200120613852565b61286c616bf0565b73ffffffffffffffffffffffffffffffffffffffff1663713ea0f3338d8a8a8a8a8a8a6040518963ffffffff1660e01b8152600401808973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001806020018060200184810384528a8a82818152602001925080828437600081840152601f19601f8201169050808301925050508481038352888882818152602001925080828437600081840152601f19601f8201169050808301925050508481038252868682818152602001925080828437600081840152601f19601f8201169050808301925050509b505050505050505050505050602060405180830381600087803b1580156129ba57600080fd5b505af11580156129ce573d6000803e3d6000fd5b505050506040513d60208110156129e457600080fd5b8101908080519060200190929190505050612a67576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4661696c656420746f207570646174652076616c696461746f72206b6579730081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f16e382723fb40543364faf68863212ba253a099607bf6d3a5b47e50a8bf949438c604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a26001548114612b58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5050505050505050505050565b600260009054906101000a900460ff1681565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff169050919050565b600046905060405180806178af60529139605201905060405180910390206040518060400160405280601381526020017f43656c6f20436f726520436f6e747261637473000000000000000000000000008152508051906020012060405180807f312e300000000000000000000000000000000000000000000000000000000000815250600301905060405180910390208330604051602001808681526020018581526020018481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001955050505050506040516020818303038152906040528051906020012060078190555050565b6000612d998260405160200180807f63656c6f2e6f72672f636f72652f766f74650000000000000000000000000000815250601201905060405160208183030381529060405280519060200120614585565b9050919050565b6001806000828254019250508190555060006001549050612e0a8560405160200180807f63656c6f2e6f72672f636f72652f766f74650000000000000000000000000000815250601201905060405160208183030381529060405280519060200120868686616a77565b612e5a8560405160200180807f63656c6f2e6f72672f636f72652f766f74650000000000000000000000000000815250601201905060405160208183030381529060405280519060200120613852565b3373ffffffffffffffffffffffffffffffffffffffff167faab5f8a189373aaa290f42ae65ea5d7971b732366ca5bf66556e76263944af2886604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a26001548114612f4b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5050505050565b6000612f5f848484614950565b8061309a5750600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160019054906101000a900460ff16801561309957508373ffffffffffffffffffffffffffffffffffffffff16600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b5b90509392505050565b60008073ffffffffffffffffffffffffffffffffffffffff16600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614159050919050565b600061318d8260405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f72000000000000000000815250601701905060405160208183030381529060405280519060200120614585565b9050919050565b60008060008060018060026001839350829250819150809050935093509350935090919293565b6131c433612be4565b613236576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f556e6b6e6f776e206163636f756e74000000000000000000000000000000000081525060200191505060405180910390fd5b61323f82616ceb565b801561325157506132503383616d45565b5b6132a6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806179306022913960400191505060405180910390fd5b604051806040016040528060011515815260200160001515815250600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548160ff021916908315150217905550905050803373ffffffffffffffffffffffffffffffffffffffff167f7a162218a1b7bec7fb15b4bb95220fbf423fa3a49718133f5c50361ff1c8537684604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a35050565b60075481565b600080600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146134c657806134c8565b835b91505092915050565b6060600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206005018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156135a95780601f1061357e576101008083540402835291602001916135a9565b820191906000526020600020905b81548152906001019060200180831161358c57829003601f168201915b50505050509050919050565b60006136078260405160200180807f63656c6f2e6f72672f636f72652f766f74650000000000000000000000000000815250601201905060405160208183030381529060405280519060200120612767565b9050919050565b60006136608260405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e00000000000000815250601901905060405160208183030381529060405280519060200120614585565b9050919050565b60006136b98260405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f72000000000000000000815250601701905060405160208183030381529060405280519060200120616e72565b9050919050565b60006137128260405160200180807f63656c6f2e6f72672f636f72652f766f74650000000000000000000000000000815250601201905060405160208183030381529060405280519060200120616e72565b9050919050565b61372161482b565b613793576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b61385b33612be4565b6138cd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f4e6f7420616e206163636f756e7400000000000000000000000000000000000081525060200191505060405180910390fd5b6138d682616ceb565b61392b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806178266022913960400191505060405180910390fd5b6139353383616d45565b6139a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f4e6f742061207369676e657220666f722074686973206163636f756e7400000081525060200191505060405180910390fd5b6139b2338383612f52565b613a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f815260200180617901602f913960400191505060405180910390fd5b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050613a53826164ed565b15613ca85760405160200180807f63656c6f2e6f72672f636f72652f766f74650000000000000000000000000000815250601201905060405160208183030381529060405280519060200120821415613af157828160010160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550613c21565b60405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e00000000000000815250601901905060405160208183030381529060405280519060200120821415613b8a57828160010160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550613c20565b60405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f72000000000000000000815250601701905060405160208183030381529060405280519060200120821415613c1f57828160010160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b5b3373ffffffffffffffffffffffffffffffffffffffff167fc5cd67202a8095484f559b338b2b6fee0dd81af9f70c4814c6517fcf9a09564c8484604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a2613dba565b82600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff167f2613ed414d18d8152e86c896c04ccce344b75a2f06141f04d39ad069a38725238484604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a25b3373ffffffffffffffffffffffffffffffffffffffff167f8a00ae3e0722558391733230bfc96d425df2dd7b44f7ce506580785adcf171a28484604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a2505050565b613e4a33612be4565b613ebc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f556e6b6e6f776e206163636f756e74000000000000000000000000000000000081525060200191505060405180910390fd5b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508282826007019190613f1292919061769e565b503373ffffffffffffffffffffffffffffffffffffffff167f0b5629fec5b6b5a1c2cfe0de7495111627a8cf297dced72e0669527425d3f01b848460405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a2505050565b6000613fe43360405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e00000000000000815250601901905060405160208183030381529060405280519060200120614585565b90506140368160405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e000000000000008152506019019050604051602081830303815290604052805190602001206165df565b3373ffffffffffffffffffffffffffffffffffffffff167f14670729407debb6ed03d885f8ba57155de89ce39bf17127ae4900ec7c2ad10382604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a250565b6141068460405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e00000000000000815250601901905060405160208183030381529060405280519060200120858585616a77565b6141568460405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e00000000000000815250601901905060405160208183030381529060405280519060200120613852565b3373ffffffffffffffffffffffffffffffffffffffff167f9dfbc5a621c3e2d0d83beee687a17dfc796bbce2118793e5e254409bb265ca0b85604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a250505050565b600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061424e8260405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e00000000000000815250601901905060405160208183030381529060405280519060200120616e72565b9050919050565b6000614260826164ed565b6142735761426e8383613416565b61427e565b61427d8383614585565b5b905092915050565b60608060008090506060858590506040519080825280602002602001820160405280156142c25781602001602082028038833980820191505090505b50905060008090505b868690508110156143b057600360008888848181106142e657fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060070180546001816001161561010002031660029004905082828151811061436157fe5b60200260200101818152505061439382828151811061437c57fe5b602002602001015184616ffa90919063ffffffff16565b92506143a9600182616ffa90919063ffffffff16565b90506142cb565b506060826040519080825280601f01601f1916602001820160405280156143e65781602001600182028038833980820191505090505b509050600080905060008090505b888890508110156145735760008090505b84828151811061441157fe5b602002602001015181101561455757600360008b8b8581811061443057fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600701818154600181600116156101000203166002900481106144a657fe5b8154600116156144c55790600052602060002090602091828204019190065b9054901a7f0100000000000000000000000000000000000000000000000000000000000000028484815181106144f757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535061453a600184616ffa90919063ffffffff16565b9250614550600182616ffa90919063ffffffff16565b9050614405565b5061456c600182616ffa90919063ffffffff16565b90506143f4565b50828295509550505050509250929050565b6000614590826164ed565b614602576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f526f6c65206973206e6f742061206c6567616379207369676e6572000000000081525060200191505060405180910390fd5b6000600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600060405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f720000000000000000008152506017019050604051602081830303815290604052805190602001208414156146c4578160010160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506147bc565b60405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e00000000000000815250601901905060405160208183030381529060405280519060200120841415614741578160010160020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506147bb565b60405160200180807f63656c6f2e6f72672f636f72652f766f746500000000000000000000000000008152506012019050604051602081830303815290604052805190602001208414156147ba578160010160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b5b5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146147f657806147f8565b845b9250505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661486d617082565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b61489233612be4565b6148a05761489e6151cb565b505b6148ed88888080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050616043565b61493a86868080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506124bb565b61494684848484614e64565b5050505050505050565b600080600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f7200000000000000000081525060170190506040516020818303038152906040528051906020012083148015614a3b57508373ffffffffffffffffffffffffffffffffffffffff168160010160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b15614a4a576001915050614bbc565b60405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e0000000000000081525060190190506040516020818303038152906040528051906020012083148015614af157508373ffffffffffffffffffffffffffffffffffffffff168160010160020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b15614b00576001915050614bbc565b60405160200180807f63656c6f2e6f72672f636f72652f766f7465000000000000000000000000000081525060120190506040516020818303038152906040528051906020012083148015614ba757508373ffffffffffffffffffffffffffffffffffffffff168160010160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b15614bb6576001915050614bbc565b60009150505b9392505050565b614bd0858585858561708a565b604051806040016040528060011515815260200160011515815250600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600086815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548160ff021916908315150217905550905050833373ffffffffffffffffffffffffffffffffffffffff167f6cc56bd06daacce5b10fdf5ad1dc781941e14d7a71d29d33e7001e2956d14e0787604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a35050505050565b600080600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614614ddf5780915050614e5f565b614de883612be4565b614e5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f4e6f7420616e206163636f756e7400000000000000000000000000000000000081525060200191505060405180910390fd5b829150505b919050565b614e6d33612be4565b614edf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f556e6b6e6f776e206163636f756e74000000000000000000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480614f455750600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16145b6150c457600073000000000000000000000000000000000000a0106396ef41a1338686866040518563ffffffff1660e01b8152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018460ff1660ff16815260200183815260200182815260200194505050505060206040518083038186803b158015614fe457600080fd5b505af4158015614ff8573d6000803e3d6000fd5b505050506040513d602081101561500e57600080fd5b810190808051906020019092919050505090508473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146150c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f496e76616c6964207369676e617475726500000000000000000000000000000081525060200191505060405180910390fd5b505b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050848160040160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff167ff81d74398fd47e35c36b714019df15f200f623dde569b5b531d6a0b4da5c5f2686604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a25050505050565b60006151d633616ceb565b80156151e757506151e63361714e565b5b615259576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f4163636f756e742065786973747300000000000000000000000000000000000081525060200191505060405180910390fd5b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060018160000160006101000a81548160ff0219169083151502179055503373ffffffffffffffffffffffffffffffffffffffff167f805996f252884581e2f74cf3d2b03564d5ec26ccc90850ae12653dc1b72d1fa260405160405180910390a2600191505090565b61530d82612be4565b61537f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f556e6b6e6f776e206163636f756e74000000000000000000000000000000000081525060200191505060405180910390fd5b61538833616ceb565b801561539a57506153998233616d45565b5b6153ef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806179306022913960400191505060405180910390fd5b60011515600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16151514615506576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f5369676e657220617574686f72697a6174696f6e206e6f74207374617274656481525060200191505060405180910390fd5b81600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160016101000a81548160ff021916908315150217905550808273ffffffffffffffffffffffffffffffffffffffff167f9eeca140dda0bdb74fc9acfda0f1c0324e188a732bd48e078a96b16d97eb54e533604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a35050565b60006156fe3360405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f72000000000000000000815250601701905060405160208183030381529060405280519060200120614585565b90506157508160405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f720000000000000000008152506017019050604051602081830303815290604052805190602001206165df565b3373ffffffffffffffffffffffffffffffffffffffff167fa54764c62865ff0cd3f271fb1d4635662bff10f0878694f1654fb7fbdecb830d82604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a250565b6060600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206007018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156158a55780601f1061587a576101008083540402835291602001916158a5565b820191906000526020600020905b81548152906001019060200180831161588857829003601f168201915b50505050509050919050565b6158b961482b565b61592b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156159ce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600260016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b6060600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206006018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015615b2d5780601f10615b0257610100808354040283529160200191615b2d565b820191906000526020600020905b815481529060010190602001808311615b1057829003601f168201915b50505050509050919050565b60046020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060405180806177ea603c9139603c0190506040518091039020888888604051602001808581526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200194505050505060405160208183030381529060405280519060200120905073000000000000000000000000000000000000a0106334d1a233600754838888886040518663ffffffff1660e01b8152600401808681526020018581526020018460ff1660ff1681526020018381526020018281526020019550505050505060206040518083038186803b158015615c9757600080fd5b505af4158015615cab573d6000803e3d6000fd5b505050506040513d6020811015615cc157600080fd5b81019080805190602001909291905050509150509695505050505050565b6000615d1a84848460405160200180838380828437808301925050509250505060405160208183030381529060405280519060200120616730565b90509392505050565b6001806000828254019250508190555060006001549050615d8d8560405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f72000000000000000000815250601701905060405160208183030381529060405280519060200120868686616a77565b615ddd8560405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f72000000000000000000815250601701905060405160208183030381529060405280519060200120613852565b615de5616bf0565b73ffffffffffffffffffffffffffffffffffffffff1663facd743b336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015615e6157600080fd5b505afa158015615e75573d6000803e3d6000fd5b505050506040513d6020811015615e8b57600080fd5b810190808051906020019092919050505015615ef2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061788e6021913960400191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f16e382723fb40543364faf68863212ba253a099607bf6d3a5b47e50a8bf9494386604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a26001548114615fe3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5050505050565b600061603c8260405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e00000000000000815250601901905060405160208183030381529060405280519060200120612767565b9050919050565b61604c33612be4565b6160be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f556e6b6e6f776e206163636f756e74000000000000000000000000000000000081525060200191505060405180910390fd5b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508181600501908051906020019061611992919061771e565b503373ffffffffffffffffffffffffffffffffffffffff167fa6e2c5a23bb917ba0a584c4b250257ddad698685829b66a8813c004b39934fe4836040518080602001828103825283818151815260200191508051906020019080838360005b83811015616193578082015181840152602081019050616178565b50505050905090810190601f1680156161c05780820380516001836020036101000a031916815260200191505b509250505060405180910390a25050565b60006161dc826164ed565b6161f0576161eb8484846120df565b6161fc565b6161fb848484614950565b5b90509392505050565b600260009054906101000a900460ff1615616288576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600260006101000a81548160ff0219169083151502179055506162ac336171e5565b6162b5816158b1565b6162bd612c3d565b50565b6000600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506000600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff167fe553a3065d5a77d4ec2a0e0c31d49be4bf4d9f4c45883b2d67f61ba9c1b89c5d8284604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a25050565b60405180806177ea603c9139603c019050604051809103902081565b61646f61482b565b6164e1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6164ea816171e5565b50565b600060405160200180807f63656c6f2e6f72672f636f72652f766f74650000000000000000000000000000815250601201905060405160208183030381529060405280519060200120821480616588575060405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f7200000000000000000081525060170190506040516020818303038152906040528051906020012082145b806165d8575060405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e0000000000000081525060190190506040516020818303038152906040528051906020012082145b9050919050565b6165ea3383836161d1565b156165f9576165f881611fe3565b5b600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600080820160006101000a81549060ff02191690556000820160016101000a81549060ff02191690555050803373ffffffffffffffffffffffffffffffffffffffff167fde9ce22cf1f8631ae2b668300f0493971114f40edd305173bd099ce7100fbe0b84604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a35050565b600061673b826164ed565b61674e57616749838361209c565b616759565b6167588383612767565b5b905092915050565b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600060405160200180807f63656c6f2e6f72672f636f72652f76616c696461746f7200000000000000000081525060170190506040516020818303038152906040528051906020012083141561686a578160010160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905060008260010160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506169f0565b60405160200180807f63656c6f2e6f72672f636f72652f6174746573746174696f6e0000000000000081525060190190506040516020818303038152906040528051906020012083141561692e578160010160020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905060008260010160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506169ef565b60405160200180807f63656c6f2e6f72672f636f72652f766f746500000000000000000000000000008152506012019050604051602081830303815290604052805190602001208314156169ee578160010160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905060008260010160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b5b3373ffffffffffffffffffffffffffffffffffffffff167fdd0b0d959c29750e7bfabbb7543a56957699d07edc512d2523ffe7502901ac198285604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a2505050565b616a8385848484617329565b604051806040016040528060011515815260200160011515815250600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600086815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548160ff021916908315150217905550905050833373ffffffffffffffffffffffffffffffffffffffff167f6cc56bd06daacce5b10fdf5ad1dc781941e14d7a71d29d33e7001e2956d14e0787604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a35050505050565b6000600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f56616c696461746f727300000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015616cab57600080fd5b505afa158015616cbf573d6000803e3d6000fd5b505050506040513d6020811015616cd557600080fd5b8101908080519060200190929190505050905090565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16159050919050565b60008073ffffffffffffffffffffffffffffffffffffffff16600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161480616e6a57508273ffffffffffffffffffffffffffffffffffffffff16600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b905092915050565b600080600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614616f7457616f16818585612f52565b616f6b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260258152602001806179526025913960400191505060405180910390fd5b80915050616ff4565b616f7d84612be4565b616fef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f6e6f7420616e206163636f756e7400000000000000000000000000000000000081525060200191505060405180910390fd5b839150505b92915050565b600080828401905083811015617078576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600033905090565b600061709a338787878787615b6c565b90508573ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461713d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f496e76616c6964207369676e617475726500000000000000000000000000000081525060200191505060405180910390fd5b617146866174b2565b505050505050565b60008073ffffffffffffffffffffffffffffffffffffffff16600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16149050919050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561726b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806177c46026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073000000000000000000000000000000000000a0106396ef41a1338686866040518563ffffffff1660e01b8152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018460ff1660ff16815260200183815260200182815260200194505050505060206040518083038186803b1580156173c457600080fd5b505af41580156173d8573d6000803e3d6000fd5b505050506040513d60208110156173ee57600080fd5b810190808051906020019092919050505090508473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146174a2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f496e76616c6964207369676e617475726500000000000000000000000000000081525060200191505060405180910390fd5b6174ab856174b2565b5050505050565b6174bb33612be4565b61752d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f556e6b6e6f776e206163636f756e74000000000000000000000000000000000081525060200191505060405180910390fd5b61753681616ceb565b801561754857506175473382616d45565b5b61759d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260468152602001806178486046913960600191505060405180910390fd5b33600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061765f57805160ff191683800117855561768d565b8280016001018555821561768d579182015b8281111561768c578251825591602001919060010190617671565b5b50905061769a919061779e565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106176df57803560ff191683800117855561770d565b8280016001018555821561770d579182015b8281111561770c5782358255916020019190600101906176f1565b5b50905061771a919061779e565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061775f57805160ff191683800117855561778d565b8280016001018555821561778d579182015b8281111561778c578251825591602001919060010190617771565b5b50905061779a919061779e565b5090565b6177c091905b808211156177bc5760008160009055506001016177a4565b5090565b9056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373417574686f72697a655369676e65722861646472657373206163636f756e742c61646472657373207369676e65722c6279746573333220726f6c652943616e6e6f7420617574686f72697a65206163636f756e74206173207369676e657243616e6e6f742072652d617574686f72697a652061646472657373206f72206c6f636b656420676f6c64206163636f756e7420666f7220616e6f74686572206163636f756e7443616e6e6f7420617574686f72697a652076616c696461746f72207369676e6572454950373132446f6d61696e28737472696e67206e616d652c737472696e672076657273696f6e2c75696e7432353620636861696e49642c6164647265737320766572696679696e67436f6e7472616374294d75737420617574686f72697a65207369676e6572206265666f72652073657474696e672061732064656661756c7443616e6e6f742072652d617574686f72697a652061646472657373207369676e65726e6f742061637469766520617574686f72697a6564207369676e657220666f7220726f6c654661696c656420746f20757064617465204543445341207075626c6963206b6579a265627a7a723158208f2181e5a5c5d0e41ca111a1b9bccefd9e3fb8784c5f6f9f6b5a203d5b3ca2bc64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f011": { + "code": "0x6080604052600436106101cd5760003560e01c80636adcc938116100f7578063a91ee0dc11610095578063cd6dc68711610064578063cd6dc68714610acb578063f2fde38b14610b26578063f340c0d014610b77578063f83d08ba14610c65576101cd565b8063a91ee0dc146109a5578063b2fb30cb146109f6578063b6e1e49d14610a3b578063c1867f6d14610aa0576101cd565b80637b103999116100d15780637b1039991461089d578063807876b7146108f45780638da5cb5b1461091f5780638f32d59b14610976576101cd565b80636adcc938146107dc5780636edf77a51461082b578063715018a614610886576101cd565b806330ec70f51161016f57806357601c5d1161013e57806357601c5d146106775780636198e339146106e0578063648911981461071b57806366f0633b146107a1576101cd565b806330ec70f5146103e957806331993fc91461044e5780633f199b40146105d257806354255be014610637576101cd565b80631fe2dfda116101ab5780631fe2dfda146102c857806320637d8e146103585780632e1a7d4d1461038357806330a61d59146103be576101cd565b806308764ee2146101d2578063158ef93e1461023e57806318a4ff8c1461026d575b600080fd5b3480156101de57600080fd5b506101e7610c6f565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561022a57808201518184015260208101905061020f565b505050509050019250505060405180910390f35b34801561024a57600080fd5b50610253610cc7565b604051808215151515815260200191505060405180910390f35b34801561027957600080fd5b506102c66004803603604081101561029057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610cda565b005b3480156102d457600080fd5b50610356600480360360408110156102eb57600080fd5b810190808035906020019064010000000081111561030857600080fd5b82018360208201111561031a57600080fd5b8035906020019184600183028401116401000000008311171561033c57600080fd5b909192939192939080359060200190929190505050610e7e565b005b34801561036457600080fd5b5061036d611164565b6040518082815260200191505060405180910390f35b34801561038f57600080fd5b506103bc600480360360208110156103a657600080fd5b810190808035906020019092919050505061116a565b005b3480156103ca57600080fd5b506103d361158c565b6040518082815260200191505060405180910390f35b3480156103f557600080fd5b506104386004803603602081101561040c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061162f565b6040518082815260200191505060405180910390f35b34801561045a57600080fd5b506105d0600480360360e081101561047157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156104e257600080fd5b8201836020820111156104f457600080fd5b8035906020019184602083028401116401000000008311171561051657600080fd5b90919293919293908035906020019064010000000081111561053757600080fd5b82018360208201111561054957600080fd5b8035906020019184602083028401116401000000008311171561056b57600080fd5b90919293919293908035906020019064010000000081111561058c57600080fd5b82018360208201111561059e57600080fd5b803590602001918460208302840111640100000000831117156105c057600080fd5b9091929391929390505050611750565b005b3480156105de57600080fd5b50610621600480360360208110156105f557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611de5565b6040518082815260200191505060405180910390f35b34801561064357600080fd5b5061064c611e31565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b34801561068357600080fd5b506106c66004803603602081101561069a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611e58565b604051808215151515815260200191505060405180910390f35b3480156106ec57600080fd5b506107196004803603602081101561070357600080fd5b8101908080359060200190929190505050611f82565b005b34801561072757600080fd5b5061079f6004803603602081101561073e57600080fd5b810190808035906020019064010000000081111561075b57600080fd5b82018360208201111561076d57600080fd5b8035906020019184600183028401116401000000008311171561078f57600080fd5b90919293919293905050506124ee565b005b3480156107ad57600080fd5b506107da600480360360208110156107c457600080fd5b8101908080359060200190929190505050612828565b005b3480156107e857600080fd5b50610815600480360360208110156107ff57600080fd5b810190808035906020019092919050505061295b565b6040518082815260200191505060405180910390f35b34801561083757600080fd5b506108846004803603604081101561084e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061297c565b005b34801561089257600080fd5b5061089b612b20565b005b3480156108a957600080fd5b506108b2612c59565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561090057600080fd5b50610909612c7f565b6040518082815260200191505060405180910390f35b34801561092b57600080fd5b50610934612c89565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561098257600080fd5b5061098b612cb2565b604051808215151515815260200191505060405180910390f35b3480156109b157600080fd5b506109f4600480360360208110156109c857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612d10565b005b348015610a0257600080fd5b50610a3960048036036040811015610a1957600080fd5b810190808035906020019092919080359060200190929190505050612eb4565b005b348015610a4757600080fd5b50610a8a60048036036020811015610a5e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061324c565b6040518082815260200191505060405180910390f35b348015610aac57600080fd5b50610ab5613364565b6040518082815260200191505060405180910390f35b348015610ad757600080fd5b50610b2460048036036040811015610aee57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061336a565b005b348015610b3257600080fd5b50610b7560048036036020811015610b4957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613426565b005b348015610b8357600080fd5b50610bc660048036036020811015610b9a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506134ac565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015610c0d578082015181840152602081019050610bf2565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015610c4f578082015181840152602081019050610c34565b5050505090500194505050505060405180910390f35b610c6d613789565b005b60606005805480602002602001604051908101604052809291908181526020018280548015610cbd57602002820191906000526020600020905b815481526020019060010190808311610ca9575b5050505050905090565b600160009054906101000a900460ff1681565b60405160200180807f456c656374696f6e0000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001203373ffffffffffffffffffffffffffffffffffffffff16600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015610dab57600080fd5b505afa158015610dbf573d6000803e3d6000fd5b505050506040513d6020811015610dd557600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614610e6f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f6f6e6c79207265676973746572656420636f6e7472616374000000000000000081525060200191505060405180910390fd5b610e7983836139a2565b505050565b610e86612cb2565b610ef8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600083836040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090506004600082815260200190815260200160002060009054906101000a900460ff16610fa2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806144fe6027913960400191505060405180910390fd5b6005805490508210610fff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260288152602001806144d66028913960400191505060405180910390fd5b806005838154811061100d57fe5b90600052602060002001541461108b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f496e64657820646f65736e2774206d61746368206964656e746966696572000081525060200191505060405180910390fd5b6005600160058054905003815481106110a057fe5b9060005260206000200154600583815481106110b857fe5b906000526020600020018190555060058054806110d157fe5b6001900381819060005260206000200160009055905560006004600083815260200190815260200160002060006101000a81548160ff021916908315150217905550838360405180838380828437808301925050509250505060405180910390207faee8df56d95b5766042c2ff4dcb39a120f0a09dd21bb9c143f86a314eff4b71460405160405180910390a250505050565b60075481565b60016000808282540192505081905550600080549050611188613a5c565b73ffffffffffffffffffffffffffffffffffffffff166325ca4c9c336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561120457600080fd5b505afa158015611218573d6000803e3d6000fd5b505050506040513d602081101561122e57600080fd5b81019080805190602001909291905050506112b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f556e6b6e6f776e206163636f756e74000000000000000000000000000000000081525060200191505060405180910390fd5b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905080600101805490508310611370576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f4261642070656e64696e67207769746864726177616c20696e6465780000000081525060200191505060405180910390fd5b600081600101848154811061138157fe5b90600052602060002090600202019050806001015442101561140b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f50656e64696e67207769746864726177616c206e6f7420617661696c61626c6581525060200191505060405180910390fd5b6000816000015490506114218360010186613b57565b47811115611497576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f496e636f6e73697374656e742062616c616e636500000000000000000000000081525060200191505060405180910390fd5b6114c0813373ffffffffffffffffffffffffffffffffffffffff16613bd190919063ffffffff16565b3373ffffffffffffffffffffffffffffffffffffffff167f292d39ba701489b7f640c83806d3eeabe0a32c9f0a61b49e95612ebad42211cd826040518082815260200191505060405180910390a25050506000548114611588576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5050565b600061162a611599613d0b565b73ffffffffffffffffffffffffffffffffffffffff16639a0e7d666040518163ffffffff1660e01b815260040160206040518083038186803b1580156115de57600080fd5b505afa1580156115f2573d6000803e3d6000fd5b505050506040513d602081101561160857600080fd5b8101908080519060200190929190505050600654613e0690919063ffffffff16565b905090565b600080600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001549050611748611682613d0b565b73ffffffffffffffffffffffffffffffffffffffff16636c781a2c856040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156116fe57600080fd5b505afa158015611712573d6000803e3d6000fd5b505050506040513d602081101561172857600080fd5b810190808051906020019092919050505082613e0690919063ffffffff16565b915050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166317c508186005336040518363ffffffff1660e01b815260040180806020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828103825284818154815260200191508054801561181757602002820191906000526020600020905b815481526020019060010190808311611803575b5050935050505060206040518083038186803b15801561183657600080fd5b505afa15801561184a573d6000803e3d6000fd5b505050506040513d602081101561186057600080fd5b81019080805190602001909291905050506118c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001806144296024913960400191505060405180910390fd5b60006118da8a6118d58d61162f565b613e8e565b905087811015611952576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f7265776172642063616e6e6f74206578636565642070656e616c74792e00000081525060200191505060405180910390fd5b6000600360008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001549050600080905082821015611b81576119b98284613ea790919063ffffffff16565b9050806119c4613d0b565b73ffffffffffffffffffffffffffffffffffffffff16638ef01def8f848d8d8d8d8d8d6040518963ffffffff1660e01b8152600401808973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200188815260200180602001806020018060200184810384528a8a82818152602001925060200280828437600081840152601f19601f8201169050808301925050508481038352888882818152602001925060200280828437600081840152601f19601f8201169050808301925050508481038252868682818152602001925060200280828437600081840152601f19601f8201169050808301925050509b505050505050505050505050602060405180830381600087803b158015611aef57600080fd5b505af1158015611b03573d6000803e3d6000fd5b505050506040513d6020811015611b1957600080fd5b810190808051906020019092919050505014611b80576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806145256021913960400191505060405180910390fd5b5b611b9d8d611b988386613ea790919063ffffffff16565b6139a2565b611ba78b8b613ef1565b50506000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f476f7665726e616e636500000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015611c6457600080fd5b505afa158015611c78573d6000803e3d6000fd5b505050506040513d6020811015611c8e57600080fd5b81019080805190602001909291905050509050600081905047611cba8b85613ea790919063ffffffff16565b1115611d2e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f496e636f6e73697374656e742062616c616e636500000000000000000000000081525060200191505060405180910390fd5b611d69611d448b85613ea790919063ffffffff16565b8273ffffffffffffffffffffffffffffffffffffffff16613bd190919063ffffffff16565b8a73ffffffffffffffffffffffffffffffffffffffff168d73ffffffffffffffffffffffffffffffffffffffff167f7abcb995a115c34a67528d58d5fc5ce02c22cb835ce1685046163f7d366d7111858d604051808381526020018281526020019250505060405180910390a350505050505050505050505050565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001549050919050565b60008060008060018060016002839350829250819150809050935093509350935090919293565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166317c508186005846040518363ffffffff1660e01b815260040180806020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281038252848181548152602001915080548015611f2157602002820191906000526020600020905b815481526020019060010190808311611f0d575b5050935050505060206040518083038186803b158015611f4057600080fd5b505afa158015611f54573d6000803e3d6000fd5b505050506040513d6020811015611f6a57600080fd5b81019080805190602001909291905050509050919050565b60016000808282540192505081905550600080549050611fa0613a5c565b73ffffffffffffffffffffffffffffffffffffffff166325ca4c9c336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561201c57600080fd5b505afa158015612030573d6000803e3d6000fd5b505050506040513d602081101561204657600080fd5b81019080805190602001909291905050506120c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f556e6b6e6f776e206163636f756e74000000000000000000000000000000000081525060200191505060405180910390fd5b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050612114613fab565b73ffffffffffffffffffffffffffffffffffffffff16635f8dd649336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561219057600080fd5b505afa1580156121a4573d6000803e3d6000fd5b505050506040513d60208110156121ba57600080fd5b81019080805190602001909291905050501561223e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f4163636f756e74206c6f636b656400000000000000000000000000000000000081525060200191505060405180910390fd5b60006122486140a6565b73ffffffffffffffffffffffffffffffffffffffff1663dcff4cf6336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156122c457600080fd5b505afa1580156122d8573d6000803e3d6000fd5b505050506040513d60208110156122ee57600080fd5b81019080805190602001909291905050509050600081148061232a5750612326846123183361162f565b613ea790919063ffffffff16565b8111155b61239c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f547279696e6720746f20756e6c6f636b20746f6f206d75636820676f6c64000081525060200191505060405180910390fd5b6123a633856139a2565b60006123bd60075442613e0690919063ffffffff16565b9050826001016040518060400160405280878152602001838152509080600181540180825580915050906001820390600052602060002090600202016000909192909190915060008201518160000155602082015181600101555050503373ffffffffffffffffffffffffffffffffffffffff167fb1a3aef2a332070da206ad1868a5e327f5aa5144e00e9a7b40717c153158a5888683604051808381526020018281526020019250505060405180910390a250505060005481146124ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5050565b6124f6612cb2565b612568576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008282604051602001808383808284378083019250505092505050604051602081830303815290604052805190602001209050600073ffffffffffffffffffffffffffffffffffffffff16600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd927233836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561262757600080fd5b505afa15801561263b573d6000803e3d6000fd5b505050506040513d602081101561265157600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614156126ec576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f4964656e746966696572206973206e6f7420726567697374657265640000000081525060200191505060405180910390fd5b6004600082815260200190815260200160002060009054906101000a900460ff1615612780576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f43616e6e6f742061646420736c61736865722049442074776963652e0000000081525060200191505060405180910390fd5b600581908060018154018082558091505090600182039060005260206000200160009091929091909150555060016004600083815260200190815260200160002060006101000a81548160ff021916908315150217905550828260405180838380828437808301925050509250505060405180910390207f92a16cb9e1846d175c3007fc61953d186452c9ea1aa34183eb4b7f88cd3f07bb60405160405180910390a2505050565b612830612cb2565b6128a2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60075481141561291a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f556e6c6f636b696e6720706572696f64206e6f74206368616e6765640000000081525060200191505060405180910390fd5b806007819055507fd9274a7c98edc7c66931fc71872764091e7023fe3867358f8504d4c21b161fc5816040518082815260200191505060405180910390a150565b6005818154811061296857fe5b906000526020600020016000915090505481565b60405160200180807f456c656374696f6e0000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001203373ffffffffffffffffffffffffffffffffffffffff16600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612a4d57600080fd5b505afa158015612a61573d6000803e3d6000fd5b505050506040513d6020811015612a7757600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614612b11576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f6f6e6c79207265676973746572656420636f6e7472616374000000000000000081525060200191505060405180910390fd5b612b1b8383613ef1565b505050565b612b28612cb2565b612b9a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360006001806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600654905090565b60006001809054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006001809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16612cf46141a1565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b612d18612cb2565b612d8a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612e2d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b60016000808282540192505081905550600080549050612ed2613a5c565b73ffffffffffffffffffffffffffffffffffffffff166325ca4c9c336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015612f4e57600080fd5b505afa158015612f62573d6000803e3d6000fd5b505050506040513d6020811015612f7857600080fd5b8101908080519060200190929190505050612ffb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f556e6b6e6f776e206163636f756e74000000000000000000000000000000000081525060200191505060405180910390fd5b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050806001018054905084106130ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f4261642070656e64696e67207769746864726177616c20696e6465780000000081525060200191505060405180910390fd5b60008160010185815481106130cb57fe5b906000526020600020906002020190508060000154841115613138576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806144ad6029913960400191505060405180910390fd5b8060000154841415613156576131518260010186613b57565b613176565b61316d848260000154613ea790919063ffffffff16565b81600001819055505b6131803385613ef1565b3373ffffffffffffffffffffffffffffffffffffffff167fa823fc38a01c2f76d7057a79bb5c317710f26f7dbdea78634598d5519d0f7cb0856040518082815260200191505060405180910390a250506000548114613247576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b505050565b600080600090506060600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101805480602002602001604051908101604052809291908181526020016000905b828210156132fd578382906000526020600020906002020160405180604001604052908160008201548152602001600182015481525050815260200190600101906132b7565b50505050905060008090505b81518110156133595761333c82828151811061332157fe5b60200260200101516000015184613e0690919063ffffffff16565b9250613352600182613e0690919063ffffffff16565b9050613309565b508192505050919050565b60065481565b600160009054906101000a900460ff16156133ed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b60018060006101000a81548160ff021916908315150217905550613410336141a9565b61341982612d10565b61342281612828565b5050565b61342e612cb2565b6134a0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6134a9816141a9565b50565b6060806134b7613a5c565b73ffffffffffffffffffffffffffffffffffffffff166325ca4c9c846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561353357600080fd5b505afa158015613547573d6000803e3d6000fd5b505050506040513d602081101561355d57600080fd5b81019080805190602001909291905050506135e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f556e6b6e6f776e206163636f756e74000000000000000000000000000000000081525060200191505060405180910390fd5b6000600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010180549050905060608160405190808252806020026020018201604052801561365b5781602001602082028038833980820191505090505b50905060608260405190808252806020026020018201604052801561368f5781602001602082028038833980820191505090505b50905060008090505b8381101561377a576136a86143ad565b600360008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010182815481106136f557fe5b9060005260206000209060020201604051806040016040529081600082015481526020016001820154815250509050806000015184838151811061373557fe5b602002602001018181525050806020015183838151811061375257fe5b60200260200101818152505050613773600182613e0690919063ffffffff16565b9050613698565b50818194509450505050915091565b600160008082825401925050819055506000805490506137a7613a5c565b73ffffffffffffffffffffffffffffffffffffffff166325ca4c9c336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561382357600080fd5b505afa158015613837573d6000803e3d6000fd5b505050506040513d602081101561384d57600080fd5b81019080805190602001909291905050506138d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600b8152602001807f6e6f74206163636f756e7400000000000000000000000000000000000000000081525060200191505060405180910390fd5b6138da3334613ef1565b3373ffffffffffffffffffffffffffffffffffffffff167f0f0f2fc5b4c987a49e1663ce2c2d65de12f3b701ff02b4d09461421e63e609e7346040518082815260200191505060405180910390a2600054811461399f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50565b6139f781600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000154613ea790919063ffffffff16565b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000181905550613a5281600654613ea790919063ffffffff16565b6006819055505050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4163636f756e74730000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015613b1757600080fd5b505afa158015613b2b573d6000803e3d6000fd5b505050506040513d6020811015613b4157600080fd5b8101908080519060200190929190505050905090565b6000613b7160018480549050613ea790919063ffffffff16565b9050828181548110613b7f57fe5b9060005260206000209060020201838381548110613b9957fe5b90600052602060002090600202016000820154816000015560018201548160010155905050808381613bcb91906143c7565b50505050565b80471015613c47576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f416464726573733a20696e73756666696369656e742062616c616e636500000081525060200191505060405180910390fd5b60008273ffffffffffffffffffffffffffffffffffffffff168260405180600001905060006040518083038185875af1925050503d8060008114613ca7576040519150601f19603f3d011682016040523d82523d6000602084013e613cac565b606091505b5050905080613d06576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a815260200180614473603a913960400191505060405180910390fd5b505050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f456c656374696f6e0000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015613dc657600080fd5b505afa158015613dda573d6000803e3d6000fd5b505050506040513d6020811015613df057600080fd5b8101908080519060200190929190505050905090565b600080828401905083811015613e84576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b6000818310613e9d5781613e9f565b825b905092915050565b6000613ee983836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506142ed565b905092915050565b613f4681600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000154613e0690919063ffffffff16565b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000181905550613fa181600654613e0690919063ffffffff16565b6006819055505050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f476f7665726e616e636500000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561406657600080fd5b505afa15801561407a573d6000803e3d6000fd5b505050506040513d602081101561409057600080fd5b8101908080519060200190929190505050905090565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f56616c696461746f727300000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561416157600080fd5b505afa158015614175573d6000803e3d6000fd5b505050506040513d602081101561418b57600080fd5b8101908080519060200190929190505050905090565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561422f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061444d6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166001809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806001806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600083831115829061439a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561435f578082015181840152602081019050614344565b50505050905090810190601f16801561438c5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b604051806040016040528060008152602001600081525090565b8154818355818111156143f4576002028160020283600052602060002091820191016143f391906143f9565b5b505050565b61442591905b80821115614421576000808201600090556001820160009055506002016143ff565b5090565b9056fe43616c6c6572206973206e6f7420612077686974656c697374656420736c61736865722e4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373416464726573733a20756e61626c6520746f2073656e642076616c75652c20726563697069656e74206d617920686176652072657665727465645265717565737465642076616c7565206c6172676572207468616e2070656e64696e672076616c756550726f766964656420696e64657820657863656564732077686974656c69737420626f756e64732e43616e6e6f742072656d6f766520736c6173686572204944206e6f74207965742061646465642e43616e6e6f74207265766f6b6520656e6f75676820766f74696e6720676f6c642ea265627a7a723158206ea491703d5e30d016da8bb9e9d7281d6e7fce7924191e01db98f0447703f7a064736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f012": { + "code": "0x608060405234801561001057600080fd5b50600436106104b75760003560e01c80638da5cb5b11610278578063ca6d56dc1161015c578063e50e652d116100ce578063ee09831011610092578063ee09831014612489578063eff2ea3f146124cf578063f2fde38b14612515578063facd743b14612559578063fae8db0a146125b5578063fffdfccb146125f7576104b7565b8063e50e652d146121ef578063e7f0376614612231578063ea684f771461223b578063eb1d0b4214612376578063ec6830721461240e576104b7565b8063dba94fcd11610120578063dba94fcd14612079578063dcff4cf6146120d1578063df4da46114612129578063e0e3ffe614612147578063e1497ff714612165578063e33301aa146121ab576104b7565b8063ca6d56dc14611ead578063cb8f98e014611f09578063d55dcbcf14611f59578063d69ef6cf14611fb8578063d93ab5ad1461201a576104b7565b8063b591d3a5116101f5578063bfdb7417116101b9578063bfdb741714611c8f578063c0c6ad6f14611d75578063c10c96ef14611dc3578063c22d3bba14611de8578063c54c1cd414611e2c578063c580514014611e88576104b7565b8063b591d3a514611b29578063b730a29914611b85578063b8f9394314611c42578063b915f53014611c4c578063bd9e9d9414611c6a576104b7565b80639a7b3be71161023c5780639a7b3be7146119535780639b2b592f146119715780639b9d5161146119b3578063a57bff9014611ab7578063a91ee0dc14611ae5576104b7565b80638da5cb5b146117665780638dd31e39146117b05780638f32d59b1461185357806394903a9714611875578063988dcd1f146118b7576104b7565b806354255be01161039f578063713ea0f31161031c57806376f7425d116102e057806376f7425d1461152e5780637b103999146115bb57806386d81a5a1461160557806387ee8a0f146116335780638a883626146116515780638b16b1c614611720576104b7565b8063713ea0f314611288578063715018a6146114035780637385e5da1461140d578063757d03801461142b57806376c0a9ed146114de576104b7565b806367960e911161036357806367960e911461106a5780636ab951a0146111395780636c620d90146111675780636fa476471461119557806370447754146111ba576104b7565b806354255be014610f0b5780635779e93d14610f3e5780635a61d15b14610f5c5780635d180adb14610fac57806360fb822c14611024576104b7565b806336407b70116104385780634b2c2f44116103fc5780634b2c2f4414610c4f5780634cd76db414610d1e5780634e06fd8a14610d3c578063517f6d3314610e0d57806351b5222514610e2b57806352f13a4e14610eaf576104b7565b806336407b7014610b1a57806339e618e814610b385780633b1eb4bf14610b905780633f27089814610bd257806343d9669914610c31576104b7565b80631904bb2e1161047f5780631904bb2e1461064a57806319113e3b146107e057806323f0ab65146108055780633173b8db1461098f57806335244f5114610a2b576104b7565b80630352a592146104bc5780630b1ca49a146104da5780630d1312b814610536578063123633ea146105ba578063158ef93e14610628575b600080fd5b6104c4612619565b6040518082815260200191505060405180910390f35b61051c600480360360208110156104f057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061261f565b604051808215151515815260200191505060405180910390f35b6105786004803603602081101561054c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612811565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105e6600480360360208110156105d057600080fd5b8101908080359060200190929190505050612930565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610630612a81565b604051808215151515815260200191505060405180910390f35b61068c6004803603602081101561066057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612a94565b6040518080602001806020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838103835288818151815260200191508051906020019080838360005b8381101561073a57808201518184015260208101905061071f565b50505050905090810190601f1680156107675780820380516001836020036101000a031916815260200191505b50838103825287818151815260200191508051906020019080838360005b838110156107a0578082015181840152602081019050610785565b50505050905090810190601f1680156107cd5780820380516001836020036101000a031916815260200191505b5097505050505050505060405180910390f35b6107e8612db9565b604051808381526020018281526020019250505060405180910390f35b6109756004803603606081101561081b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561085857600080fd5b82018360208201111561086a57600080fd5b8035906020019184600183028401116401000000008311171561088c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156108ef57600080fd5b82018360208201111561090157600080fd5b8035906020019184600183028401116401000000008311171561092357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612dec565b604051808215151515815260200191505060405180910390f35b610a11600480360360608110156109a557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612fa5565b604051808215151515815260200191505060405180910390f35b610a6d60048036036020811015610a4157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506131ca565b604051808060200180602001858152602001848152602001838103835287818151815260200191508051906020019060200280838360005b83811015610ac0578082015181840152602081019050610aa5565b50505050905001838103825286818151815260200191508051906020019060200280838360005b83811015610b02578082015181840152602081019050610ae7565b50505050905001965050505050505060405180910390f35b610b2261339e565b6040518082815260200191505060405180910390f35b610b7a60048036036020811015610b4e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506133a4565b6040518082815260200191505060405180910390f35b610bbc60048036036020811015610ba657600080fd5b810190808035906020019092919050505061346e565b6040518082815260200191505060405180910390f35b610bda613488565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610c1d578082015181840152602081019050610c02565b505050509050019250505060405180910390f35b610c39613516565b6040518082815260200191505060405180910390f35b610d0860048036036020811015610c6557600080fd5b8101908080359060200190640100000000811115610c8257600080fd5b820183602082011115610c9457600080fd5b80359060200191846001830284011164010000000083111715610cb657600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050613520565b6040518082815260200191505060405180910390f35b610d266136b4565b6040518082815260200191505060405180910390f35b610df360048036036060811015610d5257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610daf57600080fd5b820183602082011115610dc157600080fd5b80359060200191846001830284011164010000000083111715610de357600080fd5b90919293919293905050506136ba565b604051808215151515815260200191505060405180910390f35b610e156139df565b6040518082815260200191505060405180910390f35b610e6d60048036036020811015610e4157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506139ec565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610ef160048036036020811015610ec557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613b3c565b604051808215151515815260200191505060405180910390f35b610f13613b95565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b610f46613bbd565b6040518082815260200191505060405180910390f35b610f9260048036036040811015610f7257600080fd5b810190808035906020019092919080359060200190929190505050613bc3565b604051808215151515815260200191505060405180910390f35b610fe260048036036040811015610fc257600080fd5b810190808035906020019092919080359060200190929190505050613d46565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6110506004803603602081101561103a57600080fd5b8101908080359060200190929190505050613e98565b604051808215151515815260200191505060405180910390f35b6111236004803603602081101561108057600080fd5b810190808035906020019064010000000081111561109d57600080fd5b8201836020820111156110af57600080fd5b803590602001918460018302840111640100000000831117156110d157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061431f565b6040518082815260200191505060405180910390f35b6111656004803603602081101561114f57600080fd5b81019080803590602001909291905050506144b3565b005b6111936004803603602081101561117d57600080fd5b81019080803590602001909291905050506145c6565b005b61119d6146dc565b604051808381526020018281526020019250505060405180910390f35b611231600480360360208110156111d057600080fd5b81019080803590602001906401000000008111156111ed57600080fd5b8201836020820111156111ff57600080fd5b8035906020019184602083028401116401000000008311171561122157600080fd5b90919293919293905050506146f3565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015611274578082015181840152602081019050611259565b505050509050019250505060405180910390f35b6113e9600480360360a081101561129e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001906401000000008111156112fb57600080fd5b82018360208201111561130d57600080fd5b8035906020019184600183028401116401000000008311171561132f57600080fd5b90919293919293908035906020019064010000000081111561135057600080fd5b82018360208201111561136257600080fd5b8035906020019184600183028401116401000000008311171561138457600080fd5b9091929391929390803590602001906401000000008111156113a557600080fd5b8201836020820111156113b757600080fd5b803590602001918460018302840111640100000000831117156113d957600080fd5b90919293919293905050506147ab565b604051808215151515815260200191505060405180910390f35b61140b614bda565b005b611415614d13565b6040518082815260200191505060405180910390f35b6114dc600480360361018081101561144257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050614d23565b005b611514600480360360408110156114f457600080fd5b810190808035906020019092919080359060200190929190505050614e31565b604051808215151515815260200191505060405180910390f35b6115a56004803603602081101561154457600080fd5b810190808035906020019064010000000081111561156157600080fd5b82018360208201111561157357600080fd5b8035906020019184602083028401116401000000008311171561159557600080fd5b9091929391929390505050614f97565b6040518082815260200191505060405180910390f35b6115c361510a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6116316004803603602081101561161b57600080fd5b8101908080359060200190929190505050615130565b005b61163b615445565b6040518082815260200191505060405180910390f35b61170a6004803603602081101561166757600080fd5b810190808035906020019064010000000081111561168457600080fd5b82018360208201111561169657600080fd5b803590602001918460018302840111640100000000831117156116b857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061558c565b6040518082815260200191505060405180910390f35b61174c6004803603602081101561173657600080fd5b8101908080359060200190929190505050615720565b604051808215151515815260200191505060405180910390f35b61176e615cd4565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6117fc600480360360408110156117c657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050615cfd565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561183f578082015181840152602081019050611824565b505050509050019250505060405180910390f35b61185b615ff2565b604051808215151515815260200191505060405180910390f35b6118a16004803603602081101561188b57600080fd5b8101908080359060200190929190505050616050565b6040518082815260200191505060405180910390f35b611939600480360360608110156118cd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061616d565b604051808215151515815260200191505060405180910390f35b61195b61669e565b6040518082815260200191505060405180910390f35b61199d6004803603602081101561198757600080fd5b81019080803590602001909291905050506166ae565b6040518082815260200191505060405180910390f35b6119f5600480360360208110156119c957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506167f7565b60405180806020018881526020018781526020018681526020018060200185815260200184815260200183810383528a818151815260200191508051906020019060200280838360005b83811015611a5a578082015181840152602081019050611a3f565b50505050905001838103825286818151815260200191508051906020019060200280838360005b83811015611a9c578082015181840152602081019050611a81565b50505050905001995050505050505050505060405180910390f35b611ae360048036036020811015611acd57600080fd5b8101908080359060200190929190505050616acd565b005b611b2760048036036020811015611afb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050616be0565b005b611b6b60048036036020811015611b3f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050616d84565b604051808215151515815260200191505060405180910390f35b611bc760048036036020811015611b9b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506171f6565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015611c07578082015181840152602081019050611bec565b50505050905090810190601f168015611c345780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b611c4a61741c565b005b611c546176c0565b6040518082815260200191505060405180910390f35b611c726176ca565b604051808381526020018281526020019250505060405180910390f35b611d5b60048036036040811015611ca557600080fd5b8101908080359060200190640100000000811115611cc257600080fd5b820183602082011115611cd457600080fd5b80359060200191846001830284011164010000000083111715611cf657600080fd5b909192939192939080359060200190640100000000811115611d1757600080fd5b820183602082011115611d2957600080fd5b80359060200191846001830284011164010000000083111715611d4b57600080fd5b90919293919293905050506176dc565b604051808215151515815260200191505060405180910390f35b611dc160048036036040811015611d8b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050617972565b005b611dcb617a22565b604051808381526020018281526020019250505060405180910390f35b611e2a60048036036020811015611dfe57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050617a39565b005b611e6e60048036036020811015611e4257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050617d1a565b604051808215151515815260200191505060405180910390f35b611e90617e05565b604051808381526020018281526020019250505060405180910390f35b611eef60048036036020811015611ec357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050617e17565b604051808215151515815260200191505060405180910390f35b611f3f60048036036040811015611f1f57600080fd5b81019080803590602001909291908035906020019092919050505061803b565b604051808215151515815260200191505060405180910390f35b611f61618243565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015611fa4578082015181840152602081019050611f89565b505050509050019250505060405180910390f35b61200460048036036040811015611fce57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506183f2565b6040518082815260200191505060405180910390f35b6120226184a7565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561206557808201518184015260208101905061204a565b505050509050019250505060405180910390f35b6120bb6004803603602081101561208f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050618535565b6040518082815260200191505060405180910390f35b612113600480360360208110156120e757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050618621565b6040518082815260200191505060405180910390f35b6121316187a6565b6040518082815260200191505060405180910390f35b61214f6188e2565b6040518082815260200191505060405180910390f35b6121916004803603602081101561217b57600080fd5b81019080803590602001909291905050506188e8565b604051808215151515815260200191505060405180910390f35b6121ed600480360360208110156121c157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050618a99565b005b61221b6004803603602081101561220557600080fd5b8101908080359060200190929190505050618d13565b6040518082815260200191505060405180910390f35b612239618d5e565b005b61235c6004803603606081101561225157600080fd5b810190808035906020019064010000000081111561226e57600080fd5b82018360208201111561228057600080fd5b803590602001918460018302840111640100000000831117156122a257600080fd5b9091929391929390803590602001906401000000008111156122c357600080fd5b8201836020820111156122d557600080fd5b803590602001918460018302840111640100000000831117156122f757600080fd5b90919293919293908035906020019064010000000081111561231857600080fd5b82018360208201111561232a57600080fd5b8035906020019184600183028401116401000000008311171561234c57600080fd5b9091929391929390505050619055565b604051808215151515815260200191505060405180910390f35b6123cc6004803603606081101561238c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019092919050505061970a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61246c600480360360c081101561242457600080fd5b81019080803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050619a9a565b604051808381526020018281526020019250505060405180910390f35b6124b56004803603602081101561249f57600080fd5b8101908080359060200190929190505050619cae565b604051808215151515815260200191505060405180910390f35b6124fb600480360360208110156124e557600080fd5b810190808035906020019092919050505061a21d565b604051808215151515815260200191505060405180910390f35b6125576004803603602081101561252b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061a394565b005b61259b6004803603602081101561256f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061a41a565b604051808215151515815260200191505060405180910390f35b6125e1600480360360208110156125cb57600080fd5b810190808035906020019092919050505061a47f565b6040518082815260200191505060405180910390f35b6125ff61a5c8565b604051808215151515815260200191505060405180910390f35b60115481565b60006001806000828254019250508190555060006001549050600061264261a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156126be57600080fd5b505afa1580156126d2573d6000803e3d6000fd5b505050506040513d60208110156126e857600080fd5b8101908080519060200190929190505050905061270481613b3c565b801561271557506127148461a41a565b5b612787576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f6973206e6f742067726f757020616e642076616c696461746f7200000000000081525060200191505060405180910390fd5b612791818561a9b0565b925050600154811461280b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50919050565b60008061281c61669e565b90506000600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040190506000808260010154146128a45761289f61288c6001846001015461ae0f90919063ffffffff16565b836000015461ae5990919063ffffffff16565b6128a7565b60005b9050828260020160008381526020019081526020016000206000015414156128ed5781600001548111156128ec576128e960018261ae0f90919063ffffffff16565b90505b5b81600201600082815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169350505050919050565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16844360405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b602083106129a95780518252602082019150602081019050602083039250612986565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612a09576040519150601f19603f3d011682016040523d82523d6000602084013e612a0e565b606091505b50809350819250505080612a6d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d81526020018061e0de603d913960400191505060405180910390fd5b612a7882600061aee1565b92505050919050565b600260009054906101000a900460ff1681565b6060806000806000612aa58661a41a565b612b17576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b6000600460008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905080600001600001816000016001018260020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16612bae8460030160405180602001604052908160008201548152505061aef8565b612bb661a8b5565b73ffffffffffffffffffffffffffffffffffffffff16634ce38b5f8c6040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015612c3257600080fd5b505afa158015612c46573d6000803e3d6000fd5b505050506040513d6020811015612c5c57600080fd5b8101908080519060200190929190505050848054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612d025780601f10612cd757610100808354040283529160200191612d02565b820191906000526020600020905b815481529060010190602001808311612ce557829003601f168201915b50505050509450838054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612d9e5780601f10612d7357610100808354040283529160200191612d9e565b820191906000526020600020905b815481529060010190602001808311612d8157829003601f168201915b50505050509350955095509550955095505091939590929450565b600080600b60000154612de4600b60010160405180602001604052908160008201548152505061aef8565b915091509091565b60008060fb73ffffffffffffffffffffffffffffffffffffffff16858585604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140183805190602001908083835b60208310612e755780518252602082019150602081019050602083039250612e52565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b60208310612ec65780518252602082019150602081019050602083039250612ea3565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506040516020818303038152906040526040518082805190602001908083835b60208310612f2f5780518252602082019150602081019050602083039250612f0c565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612f8f576040519150601f19603f3d011682016040523d82523d6000602084013e612f94565b606091505b505080915050809150509392505050565b600060018060008282540192505081905550600060015490506000612fc861a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561304457600080fd5b505afa158015613058573d6000803e3d6000fd5b505050506040513d602081101561306e57600080fd5b810190808051906020019092919050505090506000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101600201541461313c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f56616c696461746f722067726f7570206e6f7420656d7074790000000000000081525060200191505060405180910390fd5b6131488187878761af06565b92505060015481146131c2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b509392505050565b6060806000806000600460008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040190506060816001015460405190808252806020026020018201604052801561324b5781602001602082028038833980820191505090505b509050606082600101546040519080825280602002602001820160405280156132835781602001602082028038833980820191505090505b50905060008090505b836001015481101561337f5760006132b182866000015461ae5990919063ffffffff16565b9050846002016000828152602001908152602001600020600001548483815181106132d857fe5b60200260200101818152505084600201600082815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811061332957fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505061337860018261ae5990919063ffffffff16565b905061328c565b5081818460030154856000015496509650965096505050509193509193565b60105481565b60006133af82613b3c565b613421576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f4e6f742076616c696461746f722067726f75700000000000000000000000000081525060200191505060405180910390fd5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101600201549050919050565b60006134818261347c6187a6565b61b5e3565b9050919050565b6060600580548060200260200160405190810160405280929190818152602001828054801561350c57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116134c2575b5050505050905090565b6000600e54905090565b60006060600060f473ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b602083106135755780518252602082019150602081019050602083039250613552565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106135dc57805182526020820191506020810190506020830392506135b9565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461363c576040519150601f19603f3d011682016040523d82523d6000602084013e613641565b606091505b508093508192505050806136a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603881526020018061e0046038913960400191505060405180910390fd5b6136ab82600061b62b565b92505050919050565b600d5481565b600060405160200180807f4163636f756e74730000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001203373ffffffffffffffffffffffffffffffffffffffff16600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561378d57600080fd5b505afa1580156137a1573d6000803e3d6000fd5b505050506040513d60208110156137b757600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614613851576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f6f6e6c79207265676973746572656420636f6e7472616374000000000000000081525060200191505060405180910390fd5b61385a8661a41a565b6138cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b6000600460008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905061395f81888888888080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505061b6cc565b6139d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4572726f72207570646174696e67204543445341207075626c6963206b65790081525060200191505060405180910390fd5b600192505050949350505050565b6000600680549050905090565b6000806139f761a8b5565b73ffffffffffffffffffffffffffffffffffffffff166393c5c487846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015613a7357600080fd5b505afa158015613a87573d6000803e3d6000fd5b505050506040513d6020811015613a9d57600080fd5b81019080805190602001909291905050509050613ab98161a41a565b613b2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b613b3481612811565b915050919050565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff169050919050565b6000806000806001600260006002839350829250819150809050935093509350935090919293565b600e5481565b6000613bcd615ff2565b613c3f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600060099050806000015484141580613c5c575080600101548314155b613cce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f47726f757020726571756972656d656e7473206e6f74206368616e676564000081525060200191505060405180910390fd5b604051806040016040528085815260200184815250600960008201518160000155602082015181600101559050507f999f7ee1917e6d7ea08360edfe9250cda3eda859c38dcb71a92623665de64dd48484604051808381526020018281526020019250505060405180910390a1600191505092915050565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16858560405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310613dbf5780518252602082019150602081019050602083039250613d9c565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613e1f576040519150601f19603f3d011682016040523d82523d6000602084013e613e24565b606091505b50809350819250505080613e83576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061e1506036913960400191505060405180910390fd5b613e8e82600061aee1565b9250505092915050565b600060018060008282540192505081905550600060015490506000613ebb61a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015613f3757600080fd5b505afa158015613f4b573d6000803e3d6000fd5b505050506040513d6020811015613f6157600080fd5b81019080805190602001909291905050509050613f7d81613b3c565b613fef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f7420612076616c696461746f722067726f7570000000000000000000000081525060200191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160020154146140aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f56616c696461746f722067726f7570206e6f7420656d7074790000000000000081525060200191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206008019050600181805490501115614184574261412d6009600101548360018154811061411457fe5b906000526020600020015461ae5990919063ffffffff16565b10614183576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061e3046021913960400191505060405180910390fd5b5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600080820160006101000a81549060ff021916905560018201600080820160009055600182016000905560028201600090555050600582016000808201600090555050600682016000808201600090555050600782016000905560088201600061422d919061dd96565b60098201600080820160008082016000905550506001820160009055505050506142596005838761b8cb565b8173ffffffffffffffffffffffffffffffffffffffff167fae7e034b0748a10a219b46074b20977a9170bf4027b156c797093773619a866960405160405180910390a26001935050506001548114614319576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50919050565b60006060600060f673ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b602083106143745780518252602082019150602081019050602083039250614351565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106143db57805182526020820191506020810190506020830392506143b8565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461443b576040519150601f19603f3d011682016040523d82523d6000602084013e614440565b606091505b5080935081925050508061449f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061e3726023913960400191505060405180910390fd5b6144aa82600061b62b565b92505050919050565b60018060008282540192505081905550600060015490506144d2615ff2565b614544576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b8160108190555060015481146145c2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5050565b6145ce615ff2565b614640576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600f5481141561469b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061e2b06023913960400191505060405180910390fd5b80600f819055507ff2da07d08fd8dc9c5dcf87ad6f540e306f884a47dd8de14b718a4d5395f1ca9b816040518082815260200191505060405180910390a150565b600080600960000154600960010154915091509091565b606080838390506040519080825280602002602001820160405280156147285781602001602082028038833980820191505090505b50905060008090505b848490508110156147a05761476d85858381811061474b57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff166133a4565b82828151811061477957fe5b60200260200101818152505061479960018261ae5990919063ffffffff16565b9050614731565b508091505092915050565b600060405160200180807f4163636f756e74730000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001203373ffffffffffffffffffffffffffffffffffffffff16600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561487e57600080fd5b505afa158015614892573d6000803e3d6000fd5b505050506040513d60208110156148a857600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614614942576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f6f6e6c79207265676973746572656420636f6e7472616374000000000000000081525060200191505060405180910390fd5b61494b8a61a41a565b6149bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b6000600460008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050614a50818c8c8c8c8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505061b6cc565b614ac2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4572726f72207570646174696e67204543445341207075626c6963206b65790081525060200191505060405180910390fd5b614b56818c89898080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505088888080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505061ba88565b614bc8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f4572726f72207570646174696e6720424c53207075626c6963206b657900000081525060200191505060405180910390fd5b60019250505098975050505050505050565b614be2615ff2565b614c54576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000614d1e43618d13565b905090565b600260009054906101000a900460ff1615614da6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600260006101000a81548160ff021916908315150217905550614dca3361bcd0565b614dd38c616be0565b614ddd8b8b613bc3565b50614de88989614e31565b50614df3878761803b565b50614dfd836188e8565b50614e07826145c6565b614e108561a21d565b50614e1a846144b3565b614e2381616acd565b505050505050505050505050565b6000614e3b615ff2565b614ead576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600060079050806000015484141580614eca575080600101548314155b614f1f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061dfe26022913960400191505060405180910390fd5b604051806040016040528085815260200184815250600760008201518160000155602082015181600101559050507f62d947118dd4c1f5ece7f787a9cad4e1127d14d403b71133e95792b473bf83898484604051808381526020018281526020019250505060405180910390a1600191505092915050565b6000808383905011615011576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f557074696d6520617272617920656d707479000000000000000000000000000081525060200191505060405180910390fd5b600e5483839050111561506f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b81526020018061dfb7602b913960400191505060405180910390fd5b61507761ddb7565b60008090505b848490508110156150da576150bd6150ae6150a987878581811061509d57fe5b90506020020135616050565b61be14565b8361be3290919063ffffffff16565b91506150d360018261ae5990919063ffffffff16565b905061507d565b506151016150fc6150ed8686905061bedb565b8361bf6590919063ffffffff16565b61aef8565b91505092915050565b600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061513a61a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156151b657600080fd5b505afa1580156151ca573d6000803e3d6000fd5b505050506040513d60208110156151e057600080fd5b810190808051906020019092919050505090506151fc81613b3c565b61526e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f7420612076616c696461746f722067726f7570000000000000000000000081525060200191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506152c16152bc61c0ae565b61aef8565b831115615319576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061e3256025913960400191505060405180910390fd5b61533a8160050160405180602001604052908160008201548152505061aef8565b8314156153af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f436f6d6d697373696f6e206d75737420626520646966666572656e740000000081525060200191505060405180910390fd5b6153b88361be14565b81600601600082015181600001559050506153de600f544361ae5990919063ffffffff16565b81600701819055508173ffffffffffffffffffffffffffffffffffffffff167f557d39a57520d9835859d4b7eda805a7f4115a59c3a374eeed488436fc62a152848360070154604051808381526020018281526020019250505060405180910390a2505050565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1643604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106154b65780518252602082019150602081019050602083039250615493565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114615516576040519150601f19603f3d011682016040523d82523d6000602084013e61551b565b606091505b5080935081925050508061557a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603581526020018061e11b6035913960400191505060405180910390fd5b61558582600061aee1565b9250505090565b60006060600060f773ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b602083106155e157805182526020820191506020810190506020830392506155be565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106156485780518252602082019150602081019050602083039250615625565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146156a8576040519150601f19603f3d011682016040523d82523d6000602084013e6156ad565b606091505b5080935081925050508061570c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603181526020018061e2d36031913960400191505060405180910390fd5b61571782600061aee1565b92505050919050565b60006001806000828254019250508190555060006001549050600061574361a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156157bf57600080fd5b505afa1580156157d3573d6000803e3d6000fd5b505050506040513d60208110156157e957600080fd5b810190808051906020019092919050505090506158058161a41a565b615877576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b6000600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600073ffffffffffffffffffffffffffffffffffffffff168160020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614615aab57600360008260020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010173000000000000000000000000000000000000a00663542424fb9091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b1580156159fc57600080fd5b505af4158015615a10573d6000803e3d6000fd5b505050506040513d6020811015615a2657600080fd5b810190808051906020019092919050505015615aaa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f486173206265656e2067726f7570206d656d62657220726563656e746c79000081525060200191505060405180910390fd5b5b6000615acc600760010154836004016003015461ae5990919063ffffffff16565b9050428110615b43576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f4e6f742079657420726571756972656d656e7420656e642074696d650000000081525060200191505060405180910390fd5b615b4f6006848861b8cb565b600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600080820160008082016000615ba4919061ddca565b600182016000615bb4919061ddca565b50506002820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556003820160008082016000905550506004820160008082016000905560018201600090556003820160009055505050508273ffffffffffffffffffffffffffffffffffffffff167f51407fafe7ef9bec39c65a12a4885a274190991bf1e9057fcc384fc77ff1a7f060405160405180910390a2600194505050506001548114615cce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606080600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010173000000000000000000000000000000000000a00663b1cfea439091856040518363ffffffff1660e01b8152600401808381526020018281526020019250505060006040518083038186803b158015615d9a57600080fd5b505af4158015615dae573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052506020811015615dd857600080fd5b8101908080516040519392919084640100000000821115615df857600080fd5b83820191506020820185811115615e0e57600080fd5b8251866020820283011164010000000082111715615e2b57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015615e62578082015181840152602081019050615e47565b505050509050016040525050509050606083604051908082528060200260200182016040528015615ea25781602001602082028038833980820191505090505b50905060008090505b84811015615fe657615ebb61a8b5565b73ffffffffffffffffffffffffffffffffffffffff16634ce38b5f848381518110615ee257fe5b60200260200101516040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015615f4a57600080fd5b505afa158015615f5e573d6000803e3d6000fd5b505050506040513d6020811015615f7457600080fd5b8101908080519060200190929190505050828281518110615f9157fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050615fdf60018261ae5990919063ffffffff16565b9050615eab565b50809250505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661603461c0d4565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b600061606261605d61c0ae565b61aef8565b8211156160d7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f557074696d652063616e6e6f74206265206c6172676572207468616e206f6e6581525060200191505060405180910390fd5b6000806161076160f26011548661ae5990919063ffffffff16565b6161026160fd61c0ae565b61aef8565b61c0dc565b935061614a61611c61611761c0ae565b61aef8565b61612c61612761c0ae565b61aef8565b8661613d61613861c0ae565b61aef8565b600b600001546012619a9a565b809250819350505061616461615f838361c0f5565b61aef8565b92505050919050565b60006001806000828254019250508190555060006001549050600061619061a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561620c57600080fd5b505afa158015616220573d6000803e3d6000fd5b505050506040513d602081101561623657600080fd5b8101908080519060200190929190505050905061625281613b3c565b6162c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600b8152602001807f4e6f7420612067726f757000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6162cd8661a41a565b61633f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010173000000000000000000000000000000000000a00663542424fb9091896040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b15801561640a57600080fd5b505af415801561641e573d6000803e3d6000fd5b505050506040513d602081101561643457600080fd5b81019080805190602001909291905050506164b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4e6f742061206d656d626572206f66207468652067726f75700000000000000081525060200191505060405180910390fd5b8060010173000000000000000000000000000000000000a00663b2f8fe9690918989896040518563ffffffff1660e01b8152600401808581526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200194505050505060006040518083038186803b1580156165a757600080fd5b505af41580156165bb573d6000803e3d6000fd5b505050508673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f38819cc49a343985b478d72f531a35b15384c398dd80fd191a14662170f895c660405160405180910390a36001935050506001548114616696576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b509392505050565b60006166a94361346e565b905090565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b6020831061671f57805182526020820191506020810190506020830392506166fc565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461677f576040519150601f19603f3d011682016040523d82523d6000602084013e616784565b606091505b508093508192505050806167e3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e81526020018061df07602e913960400191505060405180910390fd5b6167ee82600061aee1565b92505050919050565b60606000806000606060008061680c88613b3c565b61687e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f7420612076616c696461746f722067726f7570000000000000000000000081525060200191505060405180910390fd5b6000600360008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010173000000000000000000000000000000000000a00663fe3c7a8e90916040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b15801561691557600080fd5b505af4158015616929573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250602081101561695357600080fd5b810190808051604051939291908464010000000082111561697357600080fd5b8382019150602082018581111561698957600080fd5b82518660208202830111640100000000821117156169a657600080fd5b8083526020830192505050908051906020019060200280838360005b838110156169dd5780820151818401526020810190506169c2565b50505050905001604052505050616a0b8260050160405180602001604052908160008201548152505061aef8565b616a2c8360060160405180602001604052908160008201548152505061aef8565b836007015484600801616a598660090160000160405180602001604052908160008201548152505061aef8565b866009016001015482805480602002602001604051908101604052809291908181526020018280548015616aac57602002820191906000526020600020905b815481526020019060010190808311616a98575b50505050509250975097509750975097509750975050919395979092949650565b6001806000828254019250508190555060006001549050616aec615ff2565b616b5e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b816011819055506001548114616bdc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5050565b616be8615ff2565b616c5a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415616cfd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600260016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b600060018060008282540192505081905550600060015490506000616da761a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015616e2357600080fd5b505afa158015616e37573d6000803e3d6000fd5b505050506040513d6020811015616e4d57600080fd5b81019080805190602001909291905050509050616e698161a41a565b616edb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b616ee484613b3c565b616f56576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f7420612076616c696461746f722067726f7570000000000000000000000081525060200191505060405180910390fd5b616f5f81617d1a565b616fb4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061dee46023913960400191505060405180910390fd5b616fbd84617d1a565b61702f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f47726f757020646f65736e2774206d65657420726571756972656d656e74730081525060200191505060405180910390fd5b6000600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600073ffffffffffffffffffffffffffffffffffffffff168160020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146170d6576170d4818361c137565b505b848160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f91ef92227057e201e406c3451698dd780fe7672ad74328591c88d281af31581d60405160405180910390a360019350505060015481146171f0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50919050565b6060600061720261a8b5565b73ffffffffffffffffffffffffffffffffffffffff166393c5c487846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561727e57600080fd5b505afa158015617292573d6000803e3d6000fd5b505050506040513d60208110156172a857600080fd5b810190808051906020019092919050505090506172c48161a41a565b617336576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b600460008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016001018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561740f5780601f106173e45761010080835404028352916020019161740f565b820191906000526020600020905b8154815290600101906020018083116173f257829003601f168201915b5050505050915050919050565b6001806000828254019250508190555060006001549050600061743d61a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156174b957600080fd5b505afa1580156174cd573d6000803e3d6000fd5b505050506040513d60208110156174e357600080fd5b810190808051906020019092919050505090506174ff81613b3c565b617571576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f7420612076616c696461746f722067726f7570000000000000000000000081525060200191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506175d0601054826009016001015461ae5990919063ffffffff16565b421015617628576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603b81526020018061df5b603b913960400191505060405180910390fd5b61763061c0ae565b8160090160000160008201518160000155905050505060015481146176bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50565b6000600f54905090565b60078060000154908060010154905082565b6000806176e761a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561776357600080fd5b505afa158015617777573d6000803e3d6000fd5b505050506040513d602081101561778d57600080fd5b810190808051906020019092919050505090506177a98161a41a565b61781b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b6000600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506178f2818389898080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505088888080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505061ba88565b617964576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f4572726f72207570646174696e6720424c53207075626c6963206b657900000081525060200191505060405180910390fd5b600192505050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614617a14576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b617a1e828261c322565b5050565b600080600760000154600760010154915091509091565b6001806000828254019250508190555060006001549050617a5861c6a0565b73ffffffffffffffffffffffffffffffffffffffff166357601c5d336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015617ad457600080fd5b505afa158015617ae8573d6000803e3d6000fd5b505050506040513d6020811015617afe57600080fd5b8101908080519060200190929190505050617b81576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f6e6c79207265676973746572656420736c61736865722063616e2063616c6c81525060200191505060405180910390fd5b617b8a82613b3c565b617bfc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f7420612076616c696461746f722067726f7570000000000000000000000081525060200191505060405180910390fd5b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050617c7e617c796002617c6b8460090160000160405180602001604052908160008201548152505061aef8565b61c79b90919063ffffffff16565b61be14565b8160090160000160008201518160000155905050428160090160010181905550506001548114617d16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5050565b600080617d2561c6a0565b73ffffffffffffffffffffffffffffffffffffffff166330ec70f5846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015617da157600080fd5b505afa158015617db5573d6000803e3d6000fd5b505050506040513d6020811015617dcb57600080fd5b81019080805190602001909291905050509050617de783618621565b617dfb600a8361ae5990919063ffffffff16565b1015915050919050565b60098060000154908060010154905082565b600060018060008282540192505081905550600060015490506000617e3a61a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015617eb657600080fd5b505afa158015617eca573d6000803e3d6000fd5b505050506040513d6020811015617ee057600080fd5b810190808051906020019092919050505090506000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001016002015411617fae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f56616c696461746f722067726f757020656d707479000000000000000000000081525060200191505060405180910390fd5b617fbb818560008061af06565b9250506001548114618035576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50919050565b6000618045615ff2565b6180b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6180c76180c261c0ae565b61aef8565b82111561811f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602881526020018061e34a6028913960400191505060405180910390fd5b600b60000154831415806181645750618162600b6001016040518060200160405290816000820154815250506181548461be14565b61c7e590919063ffffffff16565b155b6181b9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602981526020018061e3956029913960400191505060405180910390fd5b60405180604001604052808481526020016181d38461be14565b815250600b600082015181600001556020820151816001016000820151816000015550509050507f4b48724280029c2ea7a445c9cea30838525342e7a9ea9468f630b52e75d6c5368383604051808381526020018281526020019250505060405180910390a16001905092915050565b6060600061824f61a8b5565b905060606006805490506040519080825280602002602001820160405280156182875781602001602082028038833980820191505090505b50905060008090505b81518110156183e9578273ffffffffffffffffffffffffffffffffffffffff16634ce38b5f600683815481106182c257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561834d57600080fd5b505afa158015618361573d6000803e3d6000fd5b505050506040513d602081101561837757600080fd5b810190808051906020019092919050505082828151811061839457fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506183e260018261ae5990919063ffffffff16565b9050618290565b50809250505090565b60008073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614618495576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b61849f838361c7fa565b905092915050565b6060600680548060200260200160405190810160405280929190818152602001828054801561852b57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116184e1575b5050505050905090565b600061854082613b3c565b6185b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f7420612076616c696461746f722067726f7570000000000000000000000081525060200191505060405180910390fd5b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506186198160090160000160405180602001604052908160008201548152505061aef8565b915050919050565b600061862c8261a41a565b1561863e5760076000015490506187a1565b61864782613b3c565b1561879c57600061869e6001600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001016002015461ce69565b90506000600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600801905060008180549050111561877b57600061870d6001838054905061ae0f90919063ffffffff16565b90505b6000811115618779574261874860096001015484848154811061872f57fe5b906000526020600020015461ae5990919063ffffffff16565b1061875e57618757818461ce69565b9250618779565b61877260018261ae0f90919063ffffffff16565b9050618710565b505b6187938260096000015461ce8390919063ffffffff16565b925050506187a1565b600090505b919050565b60006060600060f873ffffffffffffffffffffffffffffffffffffffff166040516020016040516020818303038152906040526040518082805190602001908083835b6020831061880c57805182526020820191506020810190506020830392506187e9565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461886c576040519150601f19603f3d011682016040523d82523d6000602084013e618871565b606091505b508093508192505050806188d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061e25f6025913960400191505060405180910390fd5b6188db82600061aee1565b9250505090565b600f5481565b60006188f2615ff2565b618964576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b816000106189da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f4d61782067726f75702073697a652063616e6e6f74206265207a65726f00000081525060200191505060405180910390fd5b600e54821415618a52576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4d61782067726f75702073697a65206e6f74206368616e67656400000000000081525060200191505060405180910390fd5b81600e819055507f603fe12c33c253a23da1680aa453dc70c3a0ee07763569bd5f602406ebd4e5d5826040518082815260200191505060405180910390a160019050919050565b6001806000828254019250508190555060006001549050618ab861c6a0565b73ffffffffffffffffffffffffffffffffffffffff166357601c5d336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015618b3457600080fd5b505afa158015618b48573d6000803e3d6000fd5b505050506040513d6020811015618b5e57600080fd5b8101908080519060200190929190505050618be1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f6e6c79207265676973746572656420736c61736865722063616e2063616c6c81525060200191505060405180910390fd5b618bea8261a41a565b15618c98576000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600073ffffffffffffffffffffffffffffffffffffffff168160020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614618c9657618c94818461c137565b505b505b6001548114618d0f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5050565b6000618d576003618d496002618d3b6002618d2d886166ae565b61ce8390919063ffffffff16565b61ae5990919063ffffffff16565b61c79b90919063ffffffff16565b9050919050565b6000618d6861a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015618de457600080fd5b505afa158015618df8573d6000803e3d6000fd5b505050506040513d6020811015618e0e57600080fd5b81019080805190602001909291905050509050618e2a81613b3c565b618e9c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f7420612076616c696461746f722067726f7570000000000000000000000081525060200191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600081600701541415618f5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f4e6f20636f6d6d697373696f6e2075706461746520717565756564000000000081525060200191505060405180910390fd5b4381600701541115618fb7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061df966021913960400191505060405180910390fd5b80600601816005016000820154816000015590505080600601600080820160009055505080600701600090558173ffffffffffffffffffffffffffffffffffffffff167f815d292dbc1a08dfb3103aabb6611233dd2393903e57bdf4c5b3db91198a826c61903c8360050160405180602001604052908160008201548152505061aef8565b6040518082815260200191505060405180910390a25050565b60006001806000828254019250508190555060006001549050600061907861a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156190f457600080fd5b505afa158015619108573d6000803e3d6000fd5b505050506040513d602081101561911e57600080fd5b8101908080519060200190929190505050905061913a8161a41a565b15801561914d575061914b81613b3c565b155b6191bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f416c72656164792072656769737465726564000000000000000000000000000081525060200191505060405180910390fd5b60006191c961c6a0565b73ffffffffffffffffffffffffffffffffffffffff166330ec70f5836040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561924557600080fd5b505afa158015619259573d6000803e3d6000fd5b505050506040513d602081101561926f57600080fd5b810190808051906020019092919050505090506007600001548110156192fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f4465706f73697420746f6f20736d616c6c00000000000000000000000000000081525060200191505060405180910390fd5b6000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600061934a61a8b5565b73ffffffffffffffffffffffffffffffffffffffff16634ce38b5f856040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156193c657600080fd5b505afa1580156193da573d6000803e3d6000fd5b505050506040513d60208110156193f057600080fd5b810190808051906020019092919050505090506194538285838f8f8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505061b6cc565b6194c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4572726f72207570646174696e67204543445341207075626c6963206b65790081525060200191505060405180910390fd5b61955982858c8c8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508b8b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505061ba88565b6195cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f4572726f72207570646174696e6720424c53207075626c6963206b657900000081525060200191505060405180910390fd5b60068490806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061963c84600061cf09565b508373ffffffffffffffffffffffffffffffffffffffff167fd09501348473474a20c772c79c653e1fd7e8b437e418fe235d277d2c8885325160405160405180910390a2600195505050505060015481146196ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b509695505050505050565b60006197158461a41a565b619787576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b61978f61669e565b8311156197e7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061e1ae6023913960400191505060405180910390fd5b6000600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040190506198488160010154826000015461ae5990919063ffffffff16565b83106198bc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e646578206f7574206f6620626f756e64730000000000000000000000000081525060200191505060405180910390fd5b806000015483101580156198d4575060008160010154115b619946576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e646578206f7574206f6620626f756e64730000000000000000000000000081525060200191505060405180910390fd5b60008482600201600086815260200190815260200160002060000154149050600061997f6001846001015461ae0f90919063ffffffff16565b61999684600001548761ae0f90919063ffffffff16565b149050600086846002016000888152602001908152602001600020600001541080156199f45750868460020160006199d860018a61ae5990919063ffffffff16565b81526020019081526020016000206000015411806199f35750815b5b905082806199ff5750805b619a54576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252604781526020018061e0976047913960600191505060405180910390fd5b83600201600087815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169450505050509392505050565b60008060008714158015619aaf575060008514155b619b21576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f612064656e6f6d696e61746f72206973207a65726f000000000000000000000081525060200191505060405180910390fd5b6000806000606060fc73ffffffffffffffffffffffffffffffffffffffff168c8c8c8c8c8c6040516020018087815260200186815260200185815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040526040518082805190602001908083835b60208310619bbb5780518252602082019150602081019050602083039250619b98565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114619c1b576040519150601f19603f3d011682016040523d82523d6000602084013e619c20565b606091505b50809250819350505081619c7f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061e2176027913960400191505060405180910390fd5b619c8a81600061aee1565b9350619c9781602061aee1565b925083839550955050505050965096945050505050565b60006001806000828254019250508190555060006001549050619cd7619cd261c0ae565b61aef8565b831115619d2f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061e3256025913960400191505060405180910390fd5b6000619d3961a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015619db557600080fd5b505afa158015619dc9573d6000803e3d6000fd5b505050506040513d6020811015619ddf57600080fd5b81019080805190602001909291905050509050619dfb8161a41a565b15619e6e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f416c726561647920726567697374657265642061732076616c696461746f720081525060200191505060405180910390fd5b619e7781613b3c565b15619eea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f416c726561647920726567697374657265642061732067726f7570000000000081525060200191505060405180910390fd5b6000619ef461c6a0565b73ffffffffffffffffffffffffffffffffffffffff166330ec70f5836040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015619f7057600080fd5b505afa158015619f84573d6000803e3d6000fd5b505050506040513d6020811015619f9a57600080fd5b8101908080519060200190929190505050905060096000015481101561a028576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4e6f7420656e6f756768206c6f636b656420676f6c640000000000000000000081525060200191505060405180910390fd5b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060018160000160006101000a81548160ff02191690831515021790555061a0918661be14565b8160050160008201518160000155905050604051806040016040528061a0b561c0ae565b81526020016000815250816009016000820151816000016000820151816000015550506020820151816001015590505060058390806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508273ffffffffffffffffffffffffffffffffffffffff167fbf4b45570f1907a94775f8449817051a492a676918e38108bb762e991e6b58dc876040518082815260200191505060405180910390a260019450505050600154811461a217576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50919050565b600061a227615ff2565b61a299576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b8160001061a2f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602881526020018061e1866028913960400191505060405180910390fd5b600d5482141561a34d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061e1f26025913960400191505060405180910390fd5b81600d819055507f1c75c7fb3ee9d13d8394372d8c7cdf1702fa947faa03f6ccfa500f787b09b48a826040518082815260200191505060405180910390a160019050919050565b61a39c615ff2565b61a40e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61a4178161bcd0565b50565b600080600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600101805460018160011615610100020316600290049050119050919050565b60006060600060f573ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b6020831061a4f0578051825260208201915060208101905060208303925061a4cd565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461a550576040519150601f19603f3d011682016040523d82523d6000602084013e61a555565b606091505b5080935081925050508061a5b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018061e284602c913960400191505060405180910390fd5b61a5bf82600061b62b565b92505050919050565b60006001806000828254019250508190555060006001549050600061a5eb61a8b5565b73ffffffffffffffffffffffffffffffffffffffff166364439b43336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561a66757600080fd5b505afa15801561a67b573d6000803e3d6000fd5b505050506040513d602081101561a69157600080fd5b8101908080519060200190929190505050905061a6ad8161a41a565b61a71f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b6000600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600073ffffffffffffffffffffffffffffffffffffffff168160020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561a829576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f6465616666696c696174653a206e6f7420616666696c6961746564000000000081525060200191505060405180910390fd5b61a833818361c137565b50600193505050600154811461a8b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5090565b6000600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4163636f756e74730000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561a97057600080fd5b505afa15801561a984573d6000803e3d6000fd5b505050506040513d602081101561a99a57600080fd5b8101908080519060200190929190505050905090565b600080600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508373ffffffffffffffffffffffffffffffffffffffff16600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461aaf7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f4e6f7420616666696c696174656420746f2067726f757000000000000000000081525060200191505060405180910390fd5b8060010173000000000000000000000000000000000000a00663542424fb9091856040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b15801561ab7f57600080fd5b505af415801561ab93573d6000803e3d6000fd5b505050506040513d602081101561aba957600080fd5b810190808051906020019092919050505061ac2c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4e6f742061206d656d626572206f66207468652067726f75700000000000000081525060200191505060405180910390fd5b8060010173000000000000000000000000000000000000a00663e2c0c56a9091856040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060006040518083038186803b15801561acb457600080fd5b505af415801561acc8573d6000803e3d6000fd5b50505050600081600101600201549050600081141561ad805761ace961d32a565b73ffffffffffffffffffffffffffffffffffffffff1663a8e45871866040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050600060405180830381600087803b15801561ad6757600080fd5b505af115801561ad7b573d6000803e3d6000fd5b505050505b61ad8b84600061cf09565b5061ada98561ada460018461ae5990919063ffffffff16565b61d425565b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fc7666a52a66ff601ff7c0d4d6efddc9ac20a34792f6aa003d1804c9d4d5baa5760405160405180910390a360019250505092915050565b600061ae5183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061d54e565b905092915050565b60008082840190508381101561aed7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600061aeed838361b62b565b60001c905092915050565b600081600001519050919050565b600061af1185613b3c565b801561af22575061af218461a41a565b5b61af94576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f4e6f742076616c696461746f7220616e642067726f757000000000000000000081525060200191505060405180910390fd5b6000600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600e5481600101600201541061b055576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f67726f757020776f756c6420657863656564206d6178696d756d2073697a650081525060200191505060405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff16600460008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461b158576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f4e6f7420616666696c696174656420746f2067726f757000000000000000000081525060200191505060405180910390fd5b8060010173000000000000000000000000000000000000a00663542424fb9091876040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b15801561b1e057600080fd5b505af415801561b1f4573d6000803e3d6000fd5b505050506040513d602081101561b20a57600080fd5b81019080805190602001909291905050501561b28e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f416c726561647920696e2067726f75700000000000000000000000000000000081525060200191505060405180910390fd5b600061b2ab6001836001016002015461ae5990919063ffffffff16565b90508160010173000000000000000000000000000000000000a0066326afac499091886040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060006040518083038186803b15801561b33557600080fd5b505af415801561b349573d6000803e3d6000fd5b5050505061b35687617d1a565b61b3c8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f47726f757020726571756972656d656e7473206e6f74206d657400000000000081525060200191505060405180910390fd5b61b3d186617d1a565b61b443576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f56616c696461746f7220726571756972656d656e7473206e6f74206d6574000081525060200191505060405180910390fd5b600181141561b5535761b45461d32a565b73ffffffffffffffffffffffffffffffffffffffff1663a18fb2db8887876040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019350505050600060405180830381600087803b15801561b53a57600080fd5b505af115801561b54e573d6000803e3d6000fd5b505050505b61b55d868861cf09565b5061b57b8761b57660018461ae0f90919063ffffffff16565b61d425565b8573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fbdf7e616a6943f81e07a7984c9d4c00197dc2f481486ce4ffa6af52a113974ad60405160405180910390a3600192505050949350505050565b60008082848161b5ef57fe5b049050600083858161b5fd57fe5b06141561b60d578091505061b625565b61b62160018261ae5990919063ffffffff16565b9150505b92915050565b600061b64160208361ae5990919063ffffffff16565b8351101561b6b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f736c6963696e67206f7574206f662072616e676500000000000000000000000081525060200191505060405180910390fd5b60006020830184015190508091505092915050565b6000604082511461b745576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f57726f6e67204543445341207075626c6963206b6579206c656e67746800000081525060200191505060405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff16828051906020012060001c73ffffffffffffffffffffffffffffffffffffffff161461b7f0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4543445341206b657920646f6573206e6f74206d61746368207369676e65720081525060200191505060405180910390fd5b8185600001600001908051906020019061b80b92919061de12565b508373ffffffffffffffffffffffffffffffffffffffff167f213377eec2c15b21fa7abcbb0cb87a67e893cdb94a2564aa4bb4d380869473c8836040518080602001828103825283818151815260200191508051906020019080838360005b8381101561b88557808201518184015260208101905061b86a565b50505050905090810190601f16801561b8b25780820380516001836020036101000a031916815260200191505b509250505060405180910390a260019050949350505050565b82805490508110801561b93f57508173ffffffffffffffffffffffffffffffffffffffff1683828154811061b8fc57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b61b994576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061e23e6021913960400191505060405180910390fd5b600061b9ae6001858054905061ae0f90919063ffffffff16565b905083818154811061b9bc57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1684838154811061b9f357fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555083818154811061ba4757fe5b9060005260206000200160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905580848161ba81919061de92565b5050505050565b6000606083511461bb01576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f57726f6e6720424c53207075626c6963206b6579206c656e677468000000000081525060200191505060405180910390fd5b603082511461bb78576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f57726f6e6720424c5320506f50206c656e67746800000000000000000000000081525060200191505060405180910390fd5b61bb83848484612dec565b61bbf5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f496e76616c696420424c5320506f50000000000000000000000000000000000081525060200191505060405180910390fd5b8285600001600101908051906020019061bc1092919061de12565b508373ffffffffffffffffffffffffffffffffffffffff167f36a1aabe506bbe8802233cbb9aad628e91269e77077c953f9db3e02d7092ee33846040518080602001828103825283818151815260200191508051906020019080838360005b8381101561bc8a57808201518184015260208101905061bc6f565b50505050905090810190601f16801561bcb75780820380516001836020036101000a031916815260200191505b509250505060405180910390a260019050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561bd56576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061df356026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61be1c61ddb7565b6040518060200160405280838152509050919050565b61be3a61ddb7565b600082600001518460000151019050836000015181101561bec3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f616464206f766572666c6f77206465746563746564000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808281525091505092915050565b61bee361ddb7565b61beeb61d60e565b82111561bf43576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061e03c6036913960400191505060405180910390fd5b604051806020016040528069d3c21bcecceda100000084028152509050919050565b61bf6d61ddb7565b60008260000151141561bfe8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f63616e277420646976696465206279203000000000000000000000000000000081525060200191505060405180910390fd5b600069d3c21bcecceda10000008460000151029050836000015169d3c21bcecceda1000000828161c01557fe5b041461c089576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6f766572666c6f7720617420646976696465000000000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808460000151838161c0a157fe5b0481525091505092915050565b61c0b661ddb7565b604051806020016040528069d3c21bcecceda1000000815250905090565b600033905090565b600081831061c0eb578161c0ed565b825b905092915050565b61c0fd61ddb7565b61c10561ddb7565b61c10e8461bedb565b905061c11861ddb7565b61c1218461bedb565b905061c12d828261bf65565b9250505092915050565b6000808360020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010173000000000000000000000000000000000000a00663542424fb9091866040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b15801561c22c57600080fd5b505af415801561c240573d6000803e3d6000fd5b505050506040513d602081101561c25657600080fd5b81019080805190602001909291905050501561c2785761c276828561a9b0565b505b60008560020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f71815121f0622b31a3e7270eb28acb9fd10825ff418c9a18591f617bb8a31a6c60405160405180910390a360019250505092915050565b600061c32c61a8b5565b73ffffffffffffffffffffffffffffffffffffffff166393c5c487846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561c3a857600080fd5b505afa15801561c3bc573d6000803e3d6000fd5b505050506040513d602081101561c3d257600080fd5b8101908080519060200190929190505050905061c3ee8161a41a565b61c460576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b61c46861ddb7565b61c47961c47484616050565b61be14565b905061c48361ddb7565b61c4af82600b60010160405180602001604052908160008201548152505061d62d90919063ffffffff16565b905061c4b961ddb7565b61c4ec600b60010160405180602001604052908160008201548152505061c4de61c0ae565b61da8c90919063ffffffff16565b905061c557600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003016040518060200160405290816000820154815250508261d62d90919063ffffffff16565b905061c58d61c58861c5688561aef8565b61c58361c57e858761be3290919063ffffffff16565b61aef8565b61c0dc565b61be14565b600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600301600082015181600001559050508373ffffffffffffffffffffffffffffffffffffffff167fedf9f87e50e10c533bf3ae7f5a7894ae66c23e6cbbe8773d7765d20ad6f995e961c673600460008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030160405180602001604052908160008201548152505061aef8565b61c67c8661aef8565b604051808381526020018281526020019250505060405180910390a2505050505050565b6000600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4c6f636b6564476f6c6400000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561c75b57600080fd5b505afa15801561c76f573d6000803e3d6000fd5b505050506040513d602081101561c78557600080fd5b8101908080519060200190929190505050905090565b600061c7dd83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061db33565b905092915050565b60008160000151836000015114905092915050565b60008061c80561a8b5565b73ffffffffffffffffffffffffffffffffffffffff166393c5c487856040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561c88157600080fd5b505afa15801561c895573d6000803e3d6000fd5b505050506040513d602081101561c8ab57600080fd5b8101908080519060200190929190505050905061c8c78161a41a565b61c939576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f7420612076616c696461746f72000000000000000000000000000000000081525060200191505060405180910390fd5b600061c94482612811565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561c9cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061e0726025913960400191505060405180910390fd5b61c9d582617d1a565b801561c9e6575061c9e581617d1a565b5b1561ce5c5761c9f361ddb7565b61cacf600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060090160000160405180602001604052908160008201548152505061cac1600460008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030160405180602001604052908160008201548152505061cab38961bedb565b61d62d90919063ffffffff16565b61d62d90919063ffffffff16565b9050600061cb4461cb3f600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206005016040518060200160405290816000820154815250508461d62d90919063ffffffff16565b61dbf9565b9050600061cb638261cb558561dbf9565b61ae0f90919063ffffffff16565b9050600061cb6f61dc1a565b90508073ffffffffffffffffffffffffffffffffffffffff166340c10f1986856040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561cbf857600080fd5b505af115801561cc0c573d6000803e3d6000fd5b505050506040513d602081101561cc2257600080fd5b810190808051906020019092919050505061cca5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f6d696e74206661696c656420746f2076616c696461746f722067726f7570000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166340c10f1987846040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561cd2c57600080fd5b505af115801561cd40573d6000803e3d6000fd5b505050506040513d602081101561cd5657600080fd5b810190808051906020019092919050505061cdd9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6d696e74206661696c656420746f2076616c696461746f72206163636f756e7481525060200191505060405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167f6f5937add2ec38a0fa4959bccd86e3fcc2aafb706cd3e6c0565f87a7b36b99758486604051808381526020018281526020019250505060405180910390a361ce4f8461dbf9565b965050505050505061ce63565b6000925050505b92915050565b60008183101561ce79578161ce7b565b825b905092915050565b60008083141561ce96576000905061cf03565b600082840290508284828161cea757fe5b041461cefe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061e1d16021913960400191505060405180910390fd5b809150505b92915050565b600080600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206004019050600061cf5a61669e565b905060008083600101541461cf9c5761cf9761cf846001856001015461ae0f90919063ffffffff16565b846000015461ae5990919063ffffffff16565b61cf9f565b60005b90506000836001015411801561cfe15750600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16145b1561cff0574283600301819055505b6000836001015411801561d01b57508183600201600083815260200190815260200160002060000154145b1561d0c05760405180604001604052808381526020018673ffffffffffffffffffffffffffffffffffffffff168152508360020160008381526020019081526020016000206000820151816000015560208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509050506001935050505061d324565b60008084600101541461d0e65761d0e160018361ae5990919063ffffffff16565b61d0e9565b60005b905060405180604001604052808481526020018773ffffffffffffffffffffffffffffffffffffffff168152508460020160008381526020019081526020016000206000820151816000015560208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550905050600d548460010154101561d1b25761d1a56001856001015461ae5990919063ffffffff16565b846001018190555061d31b565b600d548460010154141561d23057836002016000856000015481526020019081526020016000206000808201600090556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055505061d2236001856000015461ae5990919063ffffffff16565b846000018190555061d31a565b836002016000856000015481526020019081526020016000206000808201600090556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055505083600201600061d2996001876000015461ae5990919063ffffffff16565b81526020019081526020016000206000808201600090556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055505061d2f16001856001015461ae0f90919063ffffffff16565b846001018190555061d3116002856000015461ae5990919063ffffffff16565b84600001819055505b5b60019450505050505b92915050565b6000600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f456c656374696f6e0000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561d3e557600080fd5b505afa15801561d3f9573d6000803e3d6000fd5b505050506040513d602081101561d40f57600080fd5b8101908080519060200190929190505050905090565b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206008019050808054905082141561d4a7578042908060018154018082558091505090600182039060005260206000200160009091929091909150555061d549565b808054905082101561d4d3574281838154811061d4c057fe5b906000526020600020018190555061d548565b600061d547576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f556e61626c6520746f207570646174652073697a6520686973746f727900000081525060200191505060405180910390fd5b5b5b505050565b600083831115829061d5fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561d5c057808201518184015260208101905061d5a5565b50505050905090810190601f16801561d5ed5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b60007601357c299a88ea76a58924d52ce4f26a85af186c2b9e74905090565b61d63561ddb7565b60008360000151148061d64c575060008260000151145b1561d6685760405180602001604052806000815250905061da86565b69d3c21bcecceda10000008260000151141561d6865782905061da86565b69d3c21bcecceda10000008360000151141561d6a45781905061da86565b600069d3c21bcecceda100000061d6ba8561dd15565b600001518161d6c557fe5b049050600061d6d38561dd4c565b600001519050600069d3c21bcecceda100000061d6ef8661dd15565b600001518161d6fa57fe5b049050600061d7088661dd4c565b600001519050600082850290506000851461d79c578285828161d72757fe5b041461d79b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600069d3c21bcecceda1000000820290506000821461d83e5769d3c21bcecceda100000082828161d7c957fe5b041461d83d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f6f766572666c6f772078317931202a206669786564312064657465637465640081525060200191505060405180910390fd5b5b809150600084860290506000861461d8cf578486828161d85a57fe5b041461d8ce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600084880290506000881461d95d578488828161d8e857fe5b041461d95c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b61d96561dd89565b878161d96d57fe5b04965061d97861dd89565b858161d98057fe5b049450600085880290506000881461da11578588828161d99c57fe5b041461da10576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b61da1961ddb7565b604051806020016040528087815250905061da428160405180602001604052808781525061be32565b905061da5c8160405180602001604052808681525061be32565b905061da768160405180602001604052808581525061be32565b9050809a50505050505050505050505b92915050565b61da9461ddb7565b81600001518360000151101561db12576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f737562737472616374696f6e20756e646572666c6f772064657465637465640081525060200191505060405180910390fd5b60405180602001604052808360000151856000015103815250905092915050565b6000808311829061dbdf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561dba457808201518184015260208101905061db89565b50505050905090810190601f16801561dbd15780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161dbeb57fe5b049050809150509392505050565b600069d3c21bcecceda100000082600001518161dc1257fe5b049050919050565b6000600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f537461626c65546f6b656e000000000000000000000000000000000000000000815250600b019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561dcd557600080fd5b505afa15801561dce9573d6000803e3d6000fd5b505050506040513d602081101561dcff57600080fd5b8101908080519060200190929190505050905090565b61dd1d61ddb7565b604051806020016040528069d3c21bcecceda10000008085600001518161dd4057fe5b04028152509050919050565b61dd5461ddb7565b604051806020016040528069d3c21bcecceda10000008085600001518161dd7757fe5b04028460000151038152509050919050565b600064e8d4a51000905090565b508054600082559060005260206000209081019061ddb4919061debe565b50565b6040518060200160405280600081525090565b50805460018160011615610100020316600290046000825580601f1061ddf0575061de0f565b601f01602090049060005260206000209081019061de0e919061debe565b5b50565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061de5357805160ff191683800117855561de81565b8280016001018555821561de81579182015b8281111561de8057825182559160200191906001019061de65565b5b50905061de8e919061debe565b5090565b81548183558181111561deb95781836000526020600020918201910161deb8919061debe565b5b505050565b61dee091905b8082111561dedc57600081600090555060010161dec4565b5090565b9056fe56616c696461746f7220646f65736e2774206d65657420726571756972656d656e74736572726f722063616c6c696e67206e756d62657256616c696461746f7273496e53657420707265636f6d70696c654f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373607265736574536c617368696e674d756c7469706c696572602063616c6c6564206265666f7265207265736574506572696f64206578706972656443616e2774206170706c7920636f6d6d697373696f6e2075706461746520796574557074696d65206172726179206c6172676572207468616e206d6178696d756d2067726f75702073697a6556616c696461746f7220726571756972656d656e7473206e6f74206368616e6765646572726f722063616c6c696e672067657456657269666965645365616c4269746d617046726f6d48656164657220707265636f6d70696c6563616e277420637265617465206669786964697479206e756d626572206c6172676572207468616e206d61784e65774669786564282956616c696461746f72206e6f742072656769737465726564207769746820612067726f757070726f766964656420696e64657820646f6573206e6f74206d617463682070726f76696465642065706f63684e756d62657220617420696e64657820696e20686973746f72792e6572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e67206e756d62657256616c696461746f7273496e43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d53657420707265636f6d70696c654d656d6265727368697020686973746f7279206c656e6774682063616e6e6f74206265207a65726f45706f63682063616e6e6f74206265206c6172676572207468616e2063757272656e74536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f774d656d6265727368697020686973746f7279206c656e677468206e6f74206368616e6765646572726f722063616c6c696e67206672616374696f6e4d756c45787020707265636f6d70696c6564656c657465456c656d656e743a20696e646578206f7574206f662072616e67656572726f722063616c6c696e672067657445706f636853697a6520707265636f6d70696c656572726f722063616c6c696e6720676574506172656e745365616c4269746d617020707265636f6d70696c65636f6d6d697373696f6e207570646174652064656c6179206e6f74206368616e6765646572726f722063616c6c696e6720676574426c6f636b4e756d62657246726f6d48656164657220707265636f6d70696c654861736e2774206265656e20656d70747920666f72206c6f6e6720656e6f756768436f6d6d697373696f6e2063616e27742062652067726561746572207468616e203130302541646a7573746d656e742073706565642063616e6e6f74206265206c6172676572207468616e20316572726f722063616c6c696e67206861736848656164657220707265636f6d70696c6541646a7573746d656e7420737065656420616e64206578706f6e656e74206e6f74206368616e676564a265627a7a72315820fa5dfad1a8d4e3cdd928d20ecdf2b88ab85149b2ad1f7dc0a67d51236509ff8464736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f013": { + "code": "0x608060405234801561001057600080fd5b50600436106103d05760003560e01c80638ef01def116101ff578063bdd143181161011a578063ec683072116100ad578063f92ad2191161007c578063f92ad21914611c84578063f9d7daae14611cf0578063f9f41a7a14611d15578063fae8db0a14611d3a576103d0565b8063ec68307214611abe578063f23263f914611b39578063f2fde38b14611bf0578063f911f0b714611c34576103d0565b8063df4da461116100e9578063df4da46114611952578063e0a2ab5214611970578063e50e652d14611a16578063e59ea3e814611a58576103d0565b8063bdd14318146117e8578063c14470c414611806578063d3e242a414611882578063dedafeae146118fa576103d0565b80639b95975f11610192578063a5826ab211610161578063a5826ab2146116e3578063a8e4587114611742578063a91ee0dc14611786578063ac839d69146117ca576103d0565b80639b95975f146114bf5780639dfb608114611537578063a18fb2db146115e7578063a2fb4ddf1461166b576103d0565b806395128ce3116101ce57806395128ce3146113e95780639a0e7d66146114415780639a7b3be71461145f5780639b2b592f1461147d576103d0565b80638ef01def146111815780638f32d59b146112e257806390a4dd5c14611304578063926d00ca14611391576103d0565b806354255be0116102ef5780637046c96b1161028257806387ee8a0f1161025157806387ee8a0f14610fee5780638a8836261461100c5780638c666775146110db5780638da5cb5b14611137576103d0565b80637046c96b14610ed5578063715018a614610f7c5780637385e5da14610f865780637b10399914610fa4576103d0565b8063631db7e7116102be578063631db7e714610cb857806367960e9114610cfe5780636c781a2c14610dcd5780636e19847514610e25576103d0565b806354255be014610b0f578063580d747a14610b425780635bb5acfb14610be85780635d180adb14610c40576103d0565b80632c3b791611610367578063448144c811610336578063448144c81461092a578063457578a3146109895780634b2c2f4414610a225780634be8843b14610af1576103d0565b80632c3b7916146107d2578063386172721461082a5780633b1eb4bf146108a25780633c55a73c146108e4576103d0565b80631f604243116103a35780631f6042431461054f57806323f0ab651461056d578063263ecf74146106f75780632ba38e6914610773576103d0565b8063123633ea146103d557806312541a6b14610443578063158ef93e146104d15780631c5a9d9c146104f3575b600080fd5b610401600480360360208110156103eb57600080fd5b8101908080359060200190929190505050611d7c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6104cf6004803603608081101561045957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611ecd565b005b6104d9611f81565b604051808215151515815260200191505060405180910390f35b6105356004803603602081101561050957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611f94565b604051808215151515815260200191505060405180910390f35b6105576120fa565b6040518082815260200191505060405180910390f35b6106dd6004803603606081101561058357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001906401000000008111156105c057600080fd5b8201836020820111156105d257600080fd5b803590602001918460018302840111640100000000831117156105f457600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561065757600080fd5b82018360208201111561066957600080fd5b8035906020019184600183028401116401000000008311171561068b57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061210a565b604051808215151515815260200191505060405180910390f35b6107596004803603604081101561070d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506122c3565b604051808215151515815260200191505060405180910390f35b61077b612374565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156107be5780820151818401526020810190506107a3565b505050509050019250505060405180910390f35b610814600480360360208110156107e857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061238f565b6040518082815260200191505060405180910390f35b61088c6004803603604081101561084057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506125b5565b6040518082815260200191505060405180910390f35b6108ce600480360360208110156108b857600080fd5b81019080803590602001909291905050506125ef565b6040518082815260200191505060405180910390f35b610910600480360360208110156108fa57600080fd5b8101908080359060200190929190505050612609565b604051808215151515815260200191505060405180910390f35b610932612744565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561097557808201518184015260208101905061095a565b505050509050019250505060405180910390f35b6109cb6004803603602081101561099f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612807565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610a0e5780820151818401526020810190506109f3565b505050509050019250505060405180910390f35b610adb60048036036020811015610a3857600080fd5b8101908080359060200190640100000000811115610a5557600080fd5b820183602082011115610a6757600080fd5b80359060200191846001830284011164010000000083111715610a8957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506128d7565b6040518082815260200191505060405180910390f35b610af9612a6b565b6040518082815260200191505060405180910390f35b610b17612a77565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b610bce60048036036080811015610b5857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612a9e565b604051808215151515815260200191505060405180910390f35b610c2a60048036036020811015610bfe57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061311c565b6040518082815260200191505060405180910390f35b610c7660048036036040811015610c5657600080fd5b81019080803590602001909291908035906020019092919050505061316e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610ce460048036036020811015610cce57600080fd5b81019080803590602001909291905050506132c0565b604051808215151515815260200191505060405180910390f35b610db760048036036020811015610d1457600080fd5b8101908080359060200190640100000000811115610d3157600080fd5b820183602082011115610d4357600080fd5b80359060200191846001830284011164010000000083111715610d6557600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050613419565b6040518082815260200191505060405180910390f35b610e0f60048036036020811015610de357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506135ad565b6040518082815260200191505060405180910390f35b610ebb600480360360a0811015610e3b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506136e5565b604051808215151515815260200191505060405180910390f35b610edd61378e565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015610f24578082015181840152602081019050610f09565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015610f66578082015181840152602081019050610f4b565b5050505090500194505050505060405180910390f35b610f84613959565b005b610f8e613a92565b6040518082815260200191505060405180910390f35b610fac613aa2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610ff6613ac8565b6040518082815260200191505060405180910390f35b6110c56004803603602081101561102257600080fd5b810190808035906020019064010000000081111561103f57600080fd5b82018360208201111561105157600080fd5b8035906020019184600183028401116401000000008311171561107357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050613c0f565b6040518082815260200191505060405180910390f35b61111d600480360360208110156110f157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613da3565b604051808215151515815260200191505060405180910390f35b61113f613e73565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6112cc600480360360a081101561119757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156111de57600080fd5b8201836020820111156111f057600080fd5b8035906020019184602083028401116401000000008311171561121257600080fd5b90919293919293908035906020019064010000000081111561123357600080fd5b82018360208201111561124557600080fd5b8035906020019184602083028401116401000000008311171561126757600080fd5b90919293919293908035906020019064010000000081111561128857600080fd5b82018360208201111561129a57600080fd5b803590602001918460208302840111640100000000831117156112bc57600080fd5b9091929391929390505050613e9c565b6040518082815260200191505060405180910390f35b6112ea614446565b604051808215151515815260200191505060405180910390f35b61133a6004803603604081101561131a57600080fd5b8101908080359060200190929190803590602001909291905050506144a4565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561137d578082015181840152602081019050611362565b505050509050019250505060405180910390f35b6113d3600480360360208110156113a757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614f85565b6040518082815260200191505060405180910390f35b61142b600480360360208110156113ff57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614fd7565b6040518082815260200191505060405180910390f35b611449615029565b6040518082815260200191505060405180910390f35b611467615053565b6040518082815260200191505060405180910390f35b6114a96004803603602081101561149357600080fd5b8101908080359060200190929190505050615063565b6040518082815260200191505060405180910390f35b611521600480360360408110156114d557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506151ac565b6040518082815260200191505060405180910390f35b6115cd600480360360a081101561154d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061523f565b604051808215151515815260200191505060405180910390f35b611669600480360360608110156115fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061569d565b005b6116cd6004803603604081101561168157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061599d565b6040518082815260200191505060405180910390f35b6116eb615a2d565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561172e578082015181840152602081019050611713565b505050509050019250505060405180910390f35b6117846004803603602081101561175857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050615b61565b005b6117c86004803603602081101561179c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050615de1565b005b6117d2615f85565b6040518082815260200191505060405180910390f35b6117f0615f8b565b6040518082815260200191505060405180910390f35b6118686004803603604081101561181c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050615fb1565b604051808215151515815260200191505060405180910390f35b6118e46004803603604081101561189857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050616054565b6040518082815260200191505060405180910390f35b61193c6004803603602081101561191057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506160ed565b6040518082815260200191505060405180910390f35b61195a616199565b6040518082815260200191505060405180910390f35b6119fc6004803603608081101561198657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506162d5565b604051808215151515815260200191505060405180910390f35b611a4260048036036020811015611a2c57600080fd5b8101908080359060200190929190505050616450565b6040518082815260200191505060405180910390f35b611aa460048036036040811015611a6e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061649b565b604051808215151515815260200191505060405180910390f35b611b1c600480360360c0811015611ad457600080fd5b810190808035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190803590602001909291905050506166e5565b604051808381526020018281526020019250505060405180910390f35b611bda60048036036060811015611b4f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190640100000000811115611b9657600080fd5b820183602082011115611ba857600080fd5b80359060200191846020830284011164010000000083111715611bca57600080fd5b90919293919293905050506168f9565b6040518082815260200191505060405180910390f35b611c3260048036036020811015611c0657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050616c33565b005b611c6a60048036036040811015611c4a57600080fd5b810190808035906020019092919080359060200190929190505050616cb9565b604051808215151515815260200191505060405180910390f35b611cee600480360360a0811015611c9a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050616ee9565b005b611cf8616fbf565b604051808381526020018281526020019250505060405180910390f35b611d1d616fd1565b604051808381526020018281526020019250505060405180910390f35b611d6660048036036020811015611d5057600080fd5b8101908080359060200190929190505050616fe8565b6040518082815260200191505060405180910390f35b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16844360405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310611df55780518252602082019150602081019050602083039250611dd2565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611e55576040519150601f19603f3d011682016040523d82523d6000602084013e611e5a565b606091505b50809350819250505080611eb9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d81526020018061a104603d913960400191505060405180910390fd5b611ec4826000617131565b92505050919050565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611f6f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b611f7b84848484617148565b50505050565b600260009054906101000a900460ff1681565b600060018060008282540192505081905550600060015490506000611fb7617529565b73ffffffffffffffffffffffffffffffffffffffff16636642d594336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561203357600080fd5b505afa158015612047573d6000803e3d6000fd5b505050506040513d602081101561205d57600080fd5b8101908080519060200190929190505050905061207a8482617624565b92505060015481146120f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50919050565b6000600360020160000154905090565b60008060fb73ffffffffffffffffffffffffffffffffffffffff16858585604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140183805190602001908083835b602083106121935780518252602082019150602081019050602083039250612170565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b602083106121e457805182526020820191506020810190506020830392506121c1565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506040516020818303038152906040526040518082805190602001908083835b6020831061224d578051825260208201915060208101905060208303925061222a565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146122ad576040519150601f19603f3d011682016040523d82523d6000602084013e6122b2565b606091505b505080915050809150509392505050565b600080600360000160010160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050612355615053565b816001015410801561236b575060008160000154115b91505092915050565b606061238a600d60000154600d600101546144a4565b905090565b6000806124fd61239d617841565b73ffffffffffffffffffffffffffffffffffffffff166330a61d596040518163ffffffff1660e01b815260040160206040518083038186803b1580156123e257600080fd5b505afa1580156123f6573d6000803e3d6000fd5b505050506040513d602081101561240c57600080fd5b81019080805190602001909291905050506124ef600161242a61793c565b73ffffffffffffffffffffffffffffffffffffffff166339e618e8886040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156124a657600080fd5b505afa1580156124ba573d6000803e3d6000fd5b505050506040513d60208110156124d057600080fd5b8101908080519060200190929190505050617a3790919063ffffffff16565b617abf90919063ffffffff16565b90506000612597600d6001015461251261793c565b73ffffffffffffffffffffffffffffffffffffffff1663517f6d336040518163ffffffff1660e01b815260040160206040518083038186803b15801561255757600080fd5b505afa15801561256b573d6000803e3d6000fd5b505050506040513d602081101561258157600080fd5b8101908080519060200190929190505050617b45565b90506125ac8183617b5e90919063ffffffff16565b92505050919050565b6000806125c284846151ac565b905060006125d08585616054565b90506125e58183617a3790919063ffffffff16565b9250505092915050565b6000612602826125fd616199565b617ba8565b9050919050565b6000612613614446565b612685576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600f548214156126fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4d61782067726f75707320766f74656420666f72206e6f74206368616e67656481525060200191505060405180910390fd5b81600f819055507f1993a3864c31265ef86eec51d147eff697dee0466c92ac9abddcc4c4c6829348826040518082815260200191505060405180910390a160019050919050565b60606000612750613ac8565b90506060816040519080825280602002602001820160405280156127835781602001602082028038833980820191505090505b50905060008090505b828110156127fe5761279d81611d7c565b8282815181106127a957fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506127f7600182617a3790919063ffffffff16565b905061278c565b50809250505090565b6060600360090160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156128cb57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311612881575b50505050509050919050565b60006060600060f473ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b6020831061292c5780518252602082019150602081019050602083039250612909565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106129935780518252602082019150602081019050602083039250612970565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146129f3576040519150601f19603f3d011682016040523d82523d6000602084013e6129f8565b606091505b50809350819250505080612a57576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603881526020018061a0736038913960400191505060405180910390fd5b612a62826000617bf0565b92505050919050565b60108060000154905081565b60008060008060018060026001839350829250819150809050935093509350935090919293565b60006001806000828254019250508190555060006001549050600360040160000173000000000000000000000000000000000000a0076302f130289091886040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b158015612b4357600080fd5b505af4158015612b57573d6000803e3d6000fd5b505050506040513d6020811015612b6d57600080fd5b8101908080519060200190929190505050612bf0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f47726f7570206e6f7420656c696769626c65000000000000000000000000000081525060200191505060405180910390fd5b84600010612c66576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f566f74652076616c75652063616e6e6f74206265207a65726f0000000000000081525060200191505060405180910390fd5b612c70868661649b565b612ce2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f47726f75702063616e6e6f74207265636569766520766f74657300000000000081525060200191505060405180910390fd5b6000612cec617529565b73ffffffffffffffffffffffffffffffffffffffff16636642d594336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015612d6857600080fd5b505afa158015612d7c573d6000803e3d6000fd5b505050506040513d6020811015612d9257600080fd5b8101908080519060200190929190505050905060008090506000600360090160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008090505b8180549050811015612e8c578280612e6f57508973ffffffffffffffffffffffffffffffffffffffff16828281548110612e2c57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b9250612e85600182617a3790919063ffffffff16565b9050612df6565b5081612f7357600f54818054905010612f0d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f566f74656420666f7220746f6f206d616e792067726f7570730000000000000081525060200191505060405180910390fd5b808990806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505b612f7e89848a617c91565b612f8a89898989617d99565b612f92617841565b73ffffffffffffffffffffffffffffffffffffffff166318a4ff8c848a6040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050600060405180830381600087803b15801561301857600080fd5b505af115801561302c573d6000803e3d6000fd5b505050508873ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fd3532f70444893db82221041edb4dc26c94593aeb364b0b14dfc77d5ee9051528a6040518082815260200191505060405180910390a3600194505050506001548114613113576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50949350505050565b6000600360020160010160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101549050919050565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16858560405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b602083106131e757805182526020820191506020810190506020830392506131c4565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613247576040519150601f19603f3d011682016040523d82523d6000602084013e61324c565b606091505b508093508192505050806132ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061a1766036913960400191505060405180910390fd5b6132b6826000617131565b9250505092915050565b60006132ca614446565b61333c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61334582617f91565b60106000820151816000015590505061338461335f617faf565b6010604051806020016040529081600082015481525050617fd590919063ffffffff16565b6133d9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e81526020018061a045602e913960400191505060405180910390fd5b7f9854be03126e38f9c318d8aabe1b150d09cb3a57059b21855b1e11d44e082c1a826040518082815260200191505060405180910390a160019050919050565b60006060600060f673ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b6020831061346e578051825260208201915060208101905060208303925061344b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106134d557805182526020820191506020810190506020830392506134b2565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613535576040519150601f19603f3d011682016040523d82523d6000602084013e61353a565b606091505b50809350819250505080613599576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061a30f6023913960400191505060405180910390fd5b6135a4826000617bf0565b92505050919050565b600080600090506060600360090160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561367857602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161362e575b5050505050905060008090505b81518110156136da576136bd6136ae8383815181106136a057fe5b6020026020010151876125b5565b84617a3790919063ffffffff16565b92506136d3600182617a3790919063ffffffff16565b9050613685565b508192505050919050565b6000600180600082825401925050819055506000600154905061370b8787878787617fea565b91506001548114613784576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5095945050505050565b606080600360040160000173000000000000000000000000000000000000a0076369b317e390916040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b1580156137e957600080fd5b505af41580156137fd573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250604081101561382757600080fd5b810190808051604051939291908464010000000082111561384757600080fd5b8382019150602082018581111561385d57600080fd5b825186602082028301116401000000008211171561387a57600080fd5b8083526020830192505050908051906020019060200280838360005b838110156138b1578082015181840152602081019050613896565b50505050905001604052602001805160405193929190846401000000008211156138da57600080fd5b838201915060208201858111156138f057600080fd5b825186602082028301116401000000008211171561390d57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015613944578082015181840152602081019050613929565b50505050905001604052505050915091509091565b613961614446565b6139d3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000613a9d43616450565b905090565b600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1643604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310613b395780518252602082019150602081019050602083039250613b16565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613b99576040519150601f19603f3d011682016040523d82523d6000602084013e613b9e565b606091505b50809350819250505080613bfd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603581526020018061a1416035913960400191505060405180910390fd5b613c08826000617131565b9250505090565b60006060600060f773ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310613c645780518252602082019150602081019050602083039250613c41565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310613ccb5780518252602082019150602081019050602083039250613ca8565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613d2b576040519150601f19603f3d011682016040523d82523d6000602084013e613d30565b606091505b50809350819250505080613d8f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603181526020018061a28c6031913960400191505060405180910390fd5b613d9a826000617131565b92505050919050565b6000600360040160000173000000000000000000000000000000000000a0076302f130289091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b158015613e3157600080fd5b505af4158015613e45573d6000803e3d6000fd5b505050506040513d6020811015613e5b57600080fd5b81019080805190602001909291905050509050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000600180600082825401925050819055506000600154905060405160200180807f4c6f636b6564476f6c6400000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001203373ffffffffffffffffffffffffffffffffffffffff16600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015613f8657600080fd5b505afa158015613f9a573d6000803e3d6000fd5b505050506040513d6020811015613fb057600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff161461404a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f6f6e6c79207265676973746572656420636f6e7472616374000000000000000081525060200191505060405180910390fd5b60008a116140a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061a2e86027913960400191505060405180910390fd5b6140ab619f03565b6040518060400160405280600360090160008f73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561417857602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161412e575b505050505081526020018c81525090508060000151518a8a9050111580156141a55750878790508a8a9050145b80156141b657508585905088889050145b61420b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061a0246021913960400191505060405180910390fd5b600081600001515190505b60008111156143425761430c6142f98e846000015161423f6001866183c590919063ffffffff16565b8151811061424957fe5b602002602001015185602001518f8f61426c6001896183c590919063ffffffff16565b81811061427557fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff168e8e6142a860018a6183c590919063ffffffff16565b8181106142b157fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff168d8d6142e460018b6183c590919063ffffffff16565b8181106142ed57fe5b9050602002013561840f565b83602001516183c590919063ffffffff16565b82602001818152505060008260200151141561432757614342565b61433b6001826183c590919063ffffffff16565b9050614216565b5060008160200151146143bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4661696c75726520746f2064656372656d656e7420616c6c20766f7465732e0081525060200191505060405180910390fd5b8a935050506001548114614439576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5098975050505050505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16614488618623565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b606060006144e86144e36144be6144b9615029565b61862b565b60106040518060200160405290816000820154815250506186b590919063ffffffff16565b618b14565b90506000600360040160000173000000000000000000000000000000000000a0076342b6351a909184876040518463ffffffff1660e01b815260040180848152602001838152602001828152602001935050505060206040518083038186803b15801561455457600080fd5b505af4158015614568573d6000803e3d6000fd5b505050506040513d602081101561457e57600080fd5b810190808051906020019092919050505090506060600360040160000173000000000000000000000000000000000000a00763dcb2a4dd9091846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060006040518083038186803b1580156145f357600080fd5b505af4158015614607573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250602081101561463157600080fd5b810190808051604051939291908464010000000082111561465157600080fd5b8382019150602082018581111561466757600080fd5b825186602082028301116401000000008211171561468457600080fd5b8083526020830192505050908051906020019060200280838360005b838110156146bb5780820151818401526020810190506146a0565b50505050905001604052505050905060606146d461793c565b73ffffffffffffffffffffffffffffffffffffffff166370447754836040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019060200280838360005b83811015614742578082015181840152602081019050614727565b505050509050019250505060006040518083038186803b15801561476557600080fd5b505afa158015614779573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060208110156147a357600080fd5b81019080805160405193929190846401000000008211156147c357600080fd5b838201915060208201858111156147d957600080fd5b82518660208202830111640100000000821117156147f657600080fd5b8083526020830192505050908051906020019060200280838360005b8381101561482d578082015181840152602081019050614812565b5050505090500160405250505090506060825160405190808252806020026020018201604052801561486e5781602001602082028038833980820191505090505b5090506000809050606084516040519080825280602002602001820160405280156148a85781602001602082028038833980820191505090505b509050606085516040519080825280602002602001820160405280156148e857816020015b6148d5619f1d565b8152602001906001900390816148cd5790505b50905060008090505b8651811015614a27578083828151811061490757fe5b6020026020010181815250506149f5600360040160000173000000000000000000000000000000000000a00763e0fe44b390918a858151811061494657fe5b60200260200101516040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b1580156149b557600080fd5b505af41580156149c9573d6000803e3d6000fd5b505050506040513d60208110156149df57600080fd5b810190808051906020019092919050505061862b565b828281518110614a0157fe5b6020026020010181905250614a20600182617a3790919063ffffffff16565b90506148f1565b505b8983108015614a39575060008651115b15614c7357600082600081518110614a4d57fe5b602002602001015190506000614a75838381518110614a6857fe5b6020026020010151618b35565b1415614a815750614c73565b848181518110614a8d57fe5b6020026020010151868281518110614aa157fe5b602002602001015111614ad457614ab86000617f91565b828281518110614ac457fe5b6020026020010181905250614c63565b614afb6001868381518110614ae557fe5b6020026020010151617a3790919063ffffffff16565b858281518110614b0757fe5b602002602001018181525050614b27600185617a3790919063ffffffff16565b9350614c4b614b5b614b566001888581518110614b4057fe5b6020026020010151617a3790919063ffffffff16565b61862b565b614c3d600360040160000173000000000000000000000000000000000000a00763e0fe44b390918c8781518110614b8e57fe5b60200260200101516040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b158015614bfd57600080fd5b505af4158015614c11573d6000803e3d6000fd5b505050506040513d6020811015614c2757600080fd5b810190808051906020019092919050505061862b565b618b4390919063ffffffff16565b828281518110614c5757fe5b60200260200101819052505b614c6d8383618c8c565b50614a29565b8a831015614ce9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f4e6f7420656e6f75676820656c65637465642076616c696461746f727300000081525060200191505060405180910390fd5b606083604051908082528060200260200182016040528015614d1a5781602001602082028038833980820191505090505b5090506000935060008090505b8751811015614f72576060614d3a61793c565b73ffffffffffffffffffffffffffffffffffffffff16638dd31e398a8481518110614d6157fe5b6020026020010151898581518110614d7557fe5b60200260200101516040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060006040518083038186803b158015614de457600080fd5b505afa158015614df8573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052506020811015614e2257600080fd5b8101908080516040519392919084640100000000821115614e4257600080fd5b83820191506020820185811115614e5857600080fd5b8251866020820283011164010000000082111715614e7557600080fd5b8083526020830192505050908051906020019060200280838360005b83811015614eac578082015181840152602081019050614e91565b50505050905001604052505050905060008090505b8151811015614f5557818181518110614ed657fe5b6020026020010151848881518110614eea57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050614f38600188617a3790919063ffffffff16565b9650614f4e600182617a3790919063ffffffff16565b9050614ec1565b5050614f6b600182617a3790919063ffffffff16565b9050614d27565b5080995050505050505050505092915050565b6000600360020160010160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001549050919050565b6000600360000160010160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001549050919050565b600061504e600360000160000154600360020160000154617a3790919063ffffffff16565b905090565b600061505e436125ef565b905090565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106150d457805182526020820191506020810190506020830392506150b1565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114615134576040519150601f19603f3d011682016040523d82523d6000602084013e615139565b606091505b50809350819250505080615198576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180619fd0602e913960400191505060405180910390fd5b6151a3826000617131565b92505050919050565b6000600360000160010160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000154905092915050565b60006001806000828254019250508190555060006001549050600073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614156152fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f47726f75702061646472657373207a65726f000000000000000000000000000081525060200191505060405180910390fd5b6000615305617529565b73ffffffffffffffffffffffffffffffffffffffff16636642d594336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561538157600080fd5b505afa158015615395573d6000803e3d6000fd5b505050506040513d60208110156153ab57600080fd5b8101908080519060200190929190505050905086600010615434576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f566f74652076616c75652063616e6e6f74206265207a65726f0000000000000081525060200191505060405180910390fd5b61543e88826151ac565b871115615496576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602481526020018061a2196024913960400191505060405180910390fd5b6154a1888289618c9e565b6154ad88888888618dae565b6154b5617841565b73ffffffffffffffffffffffffffffffffffffffff16636edf77a582896040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050600060405180830381600087803b15801561553b57600080fd5b505af115801561554f573d6000803e3d6000fd5b50505050600061555f89836125b5565b14156155b2576155b1600360090160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208986619073565b5b8773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f148075455e24d5cf538793db3e917a157cbadac69dd6a304186daf11b23f76fe896040518082815260200191505060405180910390a360019250506001548114615693576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5095945050505050565b60405160200180807f56616c696461746f727300000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001203373ffffffffffffffffffffffffffffffffffffffff16600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561576e57600080fd5b505afa158015615782573d6000803e3d6000fd5b505050506040513d602081101561579857600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614615832576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f6f6e6c79207265676973746572656420636f6e7472616374000000000000000081525060200191505060405180910390fd5b600061583d856160ed565b9050600360040160000173000000000000000000000000000000000000a007632dedbbf09091878488886040518663ffffffff1660e01b8152600401808681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019550505050505060006040518083038186803b15801561593b57600080fd5b505af415801561594f573d6000803e3d6000fd5b505050508473ffffffffffffffffffffffffffffffffffffffff167f8f21dc7ff6f55d73e4fca52a4ef4fcc14fbda43ac338d24922519d51455d39c160405160405180910390a25050505050565b6000600360020160010160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6060600360040160000173000000000000000000000000000000000000a007633a72e80290916040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b158015615a8757600080fd5b505af4158015615a9b573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052506020811015615ac557600080fd5b8101908080516040519392919084640100000000821115615ae557600080fd5b83820191506020820185811115615afb57600080fd5b8251866020820283011164010000000082111715615b1857600080fd5b8083526020830192505050908051906020019060200280838360005b83811015615b4f578082015181840152602081019050615b34565b50505050905001604052505050905090565b60405160200180807f56616c696461746f727300000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001203373ffffffffffffffffffffffffffffffffffffffff16600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015615c3257600080fd5b505afa158015615c46573d6000803e3d6000fd5b505050506040513d6020811015615c5c57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614615cf6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f6f6e6c79207265676973746572656420636f6e7472616374000000000000000081525060200191505060405180910390fd5b600360040160000173000000000000000000000000000000000000a00763281359299091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060006040518083038186803b158015615d8257600080fd5b505af4158015615d96573d6000803e3d6000fd5b505050508173ffffffffffffffffffffffffffffffffffffffff167f5c8cd4e832f3a7d79f9208c2acf25a412143aa3f751cfd3728c42a0fea4921a860405160405180910390a25050565b615de9614446565b615e5b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415615efe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600260016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b600f5481565b6000615fac6010604051806020016040529081600082015481525050618b35565b905090565b60006001806000828254019250508190555060006001549050615fd48484617624565b9150600154811461604d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5092915050565b60006160e583600360020160010160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054619214565b905092915050565b6000616192600360020160010160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000154600360000160010160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000154617a3790919063ffffffff16565b9050919050565b60006060600060f873ffffffffffffffffffffffffffffffffffffffff166040516020016040516020818303038152906040526040518082805190602001908083835b602083106161ff57805182526020820191506020810190506020830392506161dc565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461625f576040519150601f19603f3d011682016040523d82523d6000602084013e616264565b606091505b508093508192505050806162c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061a1f46025913960400191505060405180910390fd5b6162ce826000617131565b9250505090565b6000600180600082825401925050819055506000600154905060006162f8617529565b73ffffffffffffffffffffffffffffffffffffffff16636642d594336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561637457600080fd5b505afa158015616388573d6000803e3d6000fd5b505050506040513d602081101561639e57600080fd5b8101908080519060200190929190505050905060006163bd8883616054565b90506163cc8882898989617fea565b935050506001548114616447576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50949350505050565b600061649460036164866002616478600261646a88615063565b617abf90919063ffffffff16565b617a3790919063ffffffff16565b617b5e90919063ffffffff16565b9050919050565b6000806164b9836164ab866160ed565b617a3790919063ffffffff16565b90506000616565616556600d600101546164d161793c565b73ffffffffffffffffffffffffffffffffffffffff1663517f6d336040518163ffffffff1660e01b815260040160206040518083038186803b15801561651657600080fd5b505afa15801561652a573d6000803e3d6000fd5b505050506040513d602081101561654057600080fd5b8101908080519060200190929190505050617b45565b83617abf90919063ffffffff16565b905060006166d4616574617841565b73ffffffffffffffffffffffffffffffffffffffff166330a61d596040518163ffffffff1660e01b815260040160206040518083038186803b1580156165b957600080fd5b505afa1580156165cd573d6000803e3d6000fd5b505050506040513d60208110156165e357600080fd5b81019080805190602001909291905050506166c6600161660161793c565b73ffffffffffffffffffffffffffffffffffffffff166339e618e88b6040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561667d57600080fd5b505afa158015616691573d6000803e3d6000fd5b505050506040513d60208110156166a757600080fd5b8101908080519060200190929190505050617a3790919063ffffffff16565b617abf90919063ffffffff16565b905080821115935050505092915050565b600080600087141580156166fa575060008514155b61676c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f612064656e6f6d696e61746f72206973207a65726f000000000000000000000081525060200191505060405180910390fd5b6000806000606060fc73ffffffffffffffffffffffffffffffffffffffff168c8c8c8c8c8c6040516020018087815260200186815260200185815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040526040518082805190602001908083835b6020831061680657805182526020820191506020810190506020830392506167e3565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114616866576040519150601f19603f3d011682016040523d82523d6000602084013e61686b565b606091505b508092508193505050816168ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061a1cd6027913960400191505060405180910390fd5b6168d5816000617131565b93506168e2816020617131565b925083839550955050505050965096945050505050565b60008061690461793c565b90508073ffffffffffffffffffffffffffffffffffffffff1663c54c1cd4876040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561698357600080fd5b505afa158015616997573d6000803e3d6000fd5b505050506040513d60208110156169ad57600080fd5b810190808051906020019092919050505015806169d35750600060036002016000015411155b156169e2576000915050616c2b565b6169ea619f30565b616a44600360020160010160008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000015460036002016000015461932d565b9050616a4e619f30565b616b118373ffffffffffffffffffffffffffffffffffffffff166376f7425d88886040518363ffffffff1660e01b815260040180806020018281038252848482818152602001925060200280828437600081840152601f19601f820116905080830192505050935050505060206040518083038186803b158015616ad157600080fd5b505afa158015616ae5573d6000803e3d6000fd5b505050506040513d6020811015616afb57600080fd5b8101908080519060200190929190505050617f91565b9050616b1b619f30565b616bdb8473ffffffffffffffffffffffffffffffffffffffff1663dba94fcd8b6040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015616b9b57600080fd5b505afa158015616baf573d6000803e3d6000fd5b505050506040513d6020811015616bc557600080fd5b8101908080519060200190929190505050617f91565b9050616c24616c1f82616c1185616c0388616bf58f61862b565b6186b590919063ffffffff16565b6186b590919063ffffffff16565b6186b590919063ffffffff16565b618b14565b9450505050505b949350505050565b616c3b614446565b616cad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b616cb68161936f565b50565b6000616cc3614446565b616d35576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b82600010616d8e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b81526020018061a2bd602b913960400191505060405180910390fd5b81831115616de7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603b815260200180619f95603b913960400191505060405180910390fd5b600d6000015483141580616e005750600d600101548214155b616e72576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f456c65637461626c652076616c696461746f7273206e6f74206368616e67656481525060200191505060405180910390fd5b604051806040016040528084815260200183815250600d60008201518160000155602082015181600101559050507fb3ae64819ff89f6136eb58b8563cb32c6550f17eaf97f9ecc32f23783229f6de8383604051808381526020018281526020019250505060405180910390a16001905092915050565b600260009054906101000a900460ff1615616f6c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600260006101000a81548160ff021916908315150217905550616f903361936f565b616f9985615de1565b616fa38484616cb9565b50616fad82612609565b50616fb7816132c0565b505050505050565b600d8060000154908060010154905082565b600080600d60000154600d60010154915091509091565b60006060600060f573ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106170595780518252602082019150602081019050602083039250617036565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146170b9576040519150601f19603f3d011682016040523d82523d6000602084013e6170be565b606091505b5080935081925050508061711d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018061a260602c913960400191505060405180910390fd5b617128826000617bf0565b92505050919050565b600061713d8383617bf0565b60001c905092915050565b600360040160000173000000000000000000000000000000000000a0076302f130289091866040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b1580156171d457600080fd5b505af41580156171e8573d6000803e3d6000fd5b505050506040513d60208110156171fe57600080fd5b8101908080519060200190929190505050156174075760006172ef84600360040160000173000000000000000000000000000000000000a00763e0fe44b39091896040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b1580156172a657600080fd5b505af41580156172ba573d6000803e3d6000fd5b505050506040513d60208110156172d057600080fd5b8101908080519060200190929190505050617a3790919063ffffffff16565b9050600360040160000173000000000000000000000000000000000000a00763cab455ae9091878487876040518663ffffffff1660e01b8152600401808681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019550505050505060006040518083038186803b1580156173ed57600080fd5b505af4158015617401573d6000803e3d6000fd5b50505050505b61746283600360020160010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000154617a3790919063ffffffff16565b600360020160010160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001819055506174c983600360020160000154617a3790919063ffffffff16565b6003600201600001819055508373ffffffffffffffffffffffffffffffffffffffff167f91ba34d62474c14d6c623cd322f4256666c7a45b7fdaa3378e009d39dfcec2a7846040518082815260200191505060405180910390a250505050565b6000600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4163636f756e74730000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156175e457600080fd5b505afa1580156175f8573d6000803e3d6000fd5b505050506040513d602081101561760e57600080fd5b8101908080519060200190929190505050905090565b600080600360000160010160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506176b6615053565b81600101541061772e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f50656e64696e6720766f74652065706f6368206e6f742070617373656400000081525060200191505060405180910390fd5b600081600001549050600081116177ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f566f74652076616c75652063616e6e6f74206265207a65726f0000000000000081525060200191505060405180910390fd5b6177b8858583618c9e565b60006177c58686846194b3565b90508573ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f45aac85f38083b18efe2d441a65b9c1ae177c78307cb5a5d4aec8f7dbcaeabfe8484604051808381526020018281526020019250505060405180910390a36001935050505092915050565b6000600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4c6f636b6564476f6c6400000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156178fc57600080fd5b505afa158015617910573d6000803e3d6000fd5b505050506040513d602081101561792657600080fd5b8101908080519060200190929190505050905090565b6000600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f56616c696461746f727300000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156179f757600080fd5b505afa158015617a0b573d6000803e3d6000fd5b505050506040513d6020811015617a2157600080fd5b8101908080519060200190929190505050905090565b600080828401905083811015617ab5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600080831415617ad25760009050617b3f565b6000828402905082848281617ae357fe5b0414617b3a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061a1ac6021913960400191505060405180910390fd5b809150505b92915050565b6000818310617b545781617b56565b825b905092915050565b6000617ba083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250619613565b905092915050565b600080828481617bb457fe5b0490506000838581617bc257fe5b061415617bd25780915050617bea565b617be6600182617a3790919063ffffffff16565b9150505b92915050565b6000617c06602083617a3790919063ffffffff16565b83511015617c7c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f736c6963696e67206f7574206f662072616e676500000000000000000000000081525060200191505060405180910390fd5b60006020830184015190508091505092915050565b600060036000019050617cb1828260000154617a3790919063ffffffff16565b816000018190555060008160010160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050617d15838260000154617a3790919063ffffffff16565b816000018190555060008160010160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050617d79848260000154617a3790919063ffffffff16565b8160000181905550617d89615053565b8160010181905550505050505050565b6000617e7484600360040160000173000000000000000000000000000000000000a00763e0fe44b39091896040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b158015617e2b57600080fd5b505af4158015617e3f573d6000803e3d6000fd5b505050506040513d6020811015617e5557600080fd5b8101908080519060200190929190505050617a3790919063ffffffff16565b9050600360040160000173000000000000000000000000000000000000a00763cab455ae9091878487876040518663ffffffff1660e01b8152600401808681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019550505050505060006040518083038186803b158015617f7257600080fd5b505af4158015617f86573d6000803e3d6000fd5b505050505050505050565b617f99619f30565b6040518060200160405280838152509050919050565b617fb7619f30565b604051806020016040528069d3c21bcecceda1000000815250905090565b60008160000151836000015110905092915050565b60008073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16141561808e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f47726f75702061646472657373207a65726f000000000000000000000000000081525060200191505060405180910390fd5b6000618098617529565b73ffffffffffffffffffffffffffffffffffffffff16636642d594336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561811457600080fd5b505afa158015618128573d6000803e3d6000fd5b505050506040513d602081101561813e57600080fd5b81019080805190602001909291905050509050856000106181c7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f566f74652076616c75652063616e6e6f74206265207a65726f0000000000000081525060200191505060405180910390fd5b6181d18782616054565b861115618229576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061a0e16023913960400191505060405180910390fd5b60006182368883896196d9565b905061824488888888618dae565b61824c617841565b73ffffffffffffffffffffffffffffffffffffffff16636edf77a583896040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050600060405180830381600087803b1580156182d257600080fd5b505af11580156182e6573d6000803e3d6000fd5b5050505060006182f689846125b5565b141561834957618348600360090160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208986619073565b5b8773ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fae7458f8697a680da6be36406ea0b8f40164915ac9cc40c0dad05a2ff6e8c6a88984604051808381526020018281526020019250505060405180910390a360019250505095945050505050565b600061840783836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061989d565b905092915050565b6000808590506000618421888a6151ac565b905060008111156184c15760006184388383617b45565b9050618445898b83618c9e565b8873ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff167f148075455e24d5cf538793db3e917a157cbadac69dd6a304186daf11b23f76fe836040518082815260200191505060405180910390a36184bd81846183c590919063ffffffff16565b9250505b60006184cd898b616054565b90506000811180156184df5750600083115b156185865760006184f08483617b45565b905060006184ff8b8d846196d9565b90508a73ffffffffffffffffffffffffffffffffffffffff168c73ffffffffffffffffffffffffffffffffffffffff167fae7458f8697a680da6be36406ea0b8f40164915ac9cc40c0dad05a2ff6e8c6a88484604051808381526020018281526020019250505060405180910390a361858182866183c590919063ffffffff16565b945050505b600061859b848a6183c590919063ffffffff16565b90506000811115618612576185b28a828a8a618dae565b60006185be8b8d6125b5565b141561861157618610600360090160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208b88619073565b5b5b809450505050509695505050505050565b600033905090565b618633619f30565b61863b61995d565b821115618693576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061a0ab6036913960400191505060405180910390fd5b604051806020016040528069d3c21bcecceda100000084028152509050919050565b6186bd619f30565b6000836000015114806186d4575060008260000151145b156186f057604051806020016040528060008152509050618b0e565b69d3c21bcecceda10000008260000151141561870e57829050618b0e565b69d3c21bcecceda10000008360000151141561872c57819050618b0e565b600069d3c21bcecceda10000006187428561997c565b600001518161874d57fe5b049050600061875b856199b3565b600001519050600069d3c21bcecceda10000006187778661997c565b600001518161878257fe5b0490506000618790866199b3565b600001519050600082850290506000851461882457828582816187af57fe5b0414618823576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600069d3c21bcecceda100000082029050600082146188c65769d3c21bcecceda100000082828161885157fe5b04146188c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f6f766572666c6f772078317931202a206669786564312064657465637465640081525060200191505060405180910390fd5b5b809150600084860290506000861461895757848682816188e257fe5b0414618956576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b60008488029050600088146189e5578488828161897057fe5b04146189e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b6189ed6199f0565b87816189f557fe5b049650618a006199f0565b8581618a0857fe5b0494506000858802905060008814618a995785888281618a2457fe5b0414618a98576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b618aa1619f30565b6040518060200160405280878152509050618aca816040518060200160405280878152506199fd565b9050618ae4816040518060200160405280868152506199fd565b9050618afe816040518060200160405280858152506199fd565b9050809a50505050505050505050505b92915050565b600069d3c21bcecceda1000000826000015181618b2d57fe5b049050919050565b600081600001519050919050565b618b4b619f30565b600082600001511415618bc6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f63616e277420646976696465206279203000000000000000000000000000000081525060200191505060405180910390fd5b600069d3c21bcecceda10000008460000151029050836000015169d3c21bcecceda10000008281618bf357fe5b0414618c67576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6f766572666c6f7720617420646976696465000000000000000000000000000081525060200191505060405180910390fd5b604051806020016040528084600001518381618c7f57fe5b0481525091505092915050565b618c9a828260008551619aa6565b5050565b600060036000019050618cbe8282600001546183c590919063ffffffff16565b816000018190555060008160010160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050618d228382600001546183c590919063ffffffff16565b816000018190555060008160010160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050618d868482600001546183c590919063ffffffff16565b8160000181905550600081600001541415618da657600081600101819055505b505050505050565b600360040160000173000000000000000000000000000000000000a0076302f130289091866040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b158015618e3a57600080fd5b505af4158015618e4e573d6000803e3d6000fd5b505050506040513d6020811015618e6457600080fd5b81019080805190602001909291905050501561906d576000618f5584600360040160000173000000000000000000000000000000000000a00763e0fe44b39091896040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b158015618f0c57600080fd5b505af4158015618f20573d6000803e3d6000fd5b505050506040513d6020811015618f3657600080fd5b81019080805190602001909291905050506183c590919063ffffffff16565b9050600360040160000173000000000000000000000000000000000000a00763cab455ae9091878487876040518663ffffffff1660e01b8152600401808681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019550505050505060006040518083038186803b15801561905357600080fd5b505af4158015619067573d6000803e3d6000fd5b50505050505b50505050565b8280549050811080156190e757508173ffffffffffffffffffffffffffffffffffffffff168382815481106190a457fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b619159576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f42616420696e646578000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6000619173600185805490506183c590919063ffffffff16565b905083818154811061918157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168483815481106191b857fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080848161920d9190619f43565b5050505050565b600080600360020160010160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154141561926f5760009050619327565b619324600360020160010160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154619316600360020160010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000015485617abf90919063ffffffff16565b617b5e90919063ffffffff16565b90505b92915050565b619335619f30565b61933d619f30565b6193468461862b565b9050619350619f30565b6193598461862b565b90506193658282618b43565b9250505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156193f5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180619ffe6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600080600360020190506194d4838260000154617a3790919063ffffffff16565b816000018190555060006194e88685619dbb565b905060008260010160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050619546858260000154617a3790919063ffffffff16565b8160000181905550619565828260010154617a3790919063ffffffff16565b81600101819055506195c1828260020160008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054617a3790919063ffffffff16565b8160020160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508193505050509392505050565b600080831182906196bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015619684578082015181840152602081019050619669565b50505050905090810190601f1680156196b15780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816196cb57fe5b049050809150509392505050565b600080600360020190506196fa8382600001546183c590919063ffffffff16565b8160000181905550600080905060006197138787616054565b905060008360010160008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050858214156197ab578060020160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205492506197b8565b6197b58887619dbb565b92505b6197cf8682600001546183c590919063ffffffff16565b81600001819055506197ee8382600101546183c590919063ffffffff16565b816001018190555061984a838260020160008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546183c590919063ffffffff16565b8160020160008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550829450505050509392505050565b600083831115829061994a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561990f5780820151818401526020810190506198f4565b50505050905090810190601f16801561993c5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b60007601357c299a88ea76a58924d52ce4f26a85af186c2b9e74905090565b619984619f30565b604051806020016040528069d3c21bcecceda1000000808560000151816199a757fe5b04028152509050919050565b6199bb619f30565b604051806020016040528069d3c21bcecceda1000000808560000151816199de57fe5b04028460000151038152509050919050565b600064e8d4a51000905090565b619a05619f30565b6000826000015184600001510190508360000151811015619a8e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f616464206f766572666c6f77206465746563746564000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808281525091505092915050565b8251845114619b00576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061a23d6023913960400191505060405180910390fd5b83518210619b76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f6865617020737461727420696e646578206f7574206f662072616e676500000081525060200191505060405180910390fd5b8351811115619bed576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f68656170206c656e677468206f7574206f662072616e6765000000000000000081525060200191505060405180910390fd5b60008290505b600115619db4576000619c236001619c15600285617abf90919063ffffffff16565b617a3790919063ffffffff16565b90506000619c4e6002619c40600286617abf90919063ffffffff16565b617a3790919063ffffffff16565b905060008390508483108015619cbf5750619cbe87898381518110619c6f57fe5b602002602001015181518110619c8157fe5b6020026020010151888a8681518110619c9657fe5b602002602001015181518110619ca857fe5b6020026020010151619eee90919063ffffffff16565b5b15619cc8578290505b8482108015619d325750619d3187898381518110619ce257fe5b602002602001015181518110619cf457fe5b6020026020010151888a8581518110619d0957fe5b602002602001015181518110619d1b57fe5b6020026020010151619eee90919063ffffffff16565b5b15619d3b578190505b83811415619d4b57505050619db4565b6000888581518110619d5957fe5b60200260200101519050888281518110619d6f57fe5b6020026020010151898681518110619d8357fe5b60200260200101818152505080898381518110619d9c57fe5b60200260200101818152505081945050505050619bf3565b5050505050565b600080600360020160010160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101541415619e3057619e2968056bc75e2d6310000083617abf90919063ffffffff16565b9050619ee8565b619ee5600360020160010160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000154619ed7600360020160010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001015485617abf90919063ffffffff16565b617b5e90919063ffffffff16565b90505b92915050565b60008160000151836000015111905092915050565b604051806040016040528060608152602001600081525090565b6040518060200160405280600081525090565b6040518060200160405280600081525090565b815481835581811115619f6a57818360005260206000209182019101619f699190619f6f565b5b505050565b619f9191905b80821115619f8d576000816000905550600101619f75565b5090565b9056fe4d6178696d756d20656c65637461626c652076616c696461746f72732063616e6e6f7420626520736d616c6c6572207468616e206d696e696d756d6572726f722063616c6c696e67206e756d62657256616c696461746f7273496e53657420707265636f6d70696c654f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373496e707574206c656e67746873206d75737420626520636f72726573706f6e642e456c6563746162696c697479207468726573686f6c64206d757374206265206c6f776572207468616e20313030256572726f722063616c6c696e672067657456657269666965645365616c4269746d617046726f6d48656164657220707265636f6d70696c6563616e277420637265617465206669786964697479206e756d626572206c6172676572207468616e206d61784e657746697865642829566f74652076616c7565206c6172676572207468616e2061637469766520766f7465736572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e67206e756d62657256616c696461746f7273496e43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d53657420707265636f6d70696c65536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f776572726f722063616c6c696e67206672616374696f6e4d756c45787020707265636f6d70696c656572726f722063616c6c696e672067657445706f636853697a6520707265636f6d70696c65566f74652076616c7565206c6172676572207468616e2070656e64696e6720766f7465736b657920616e642076616c7565206172726179206c656e677468206d69736d617463686572726f722063616c6c696e6720676574506172656e745365616c4269746d617020707265636f6d70696c656572726f722063616c6c696e6720676574426c6f636b4e756d62657246726f6d48656164657220707265636f6d70696c654d696e696d756d20656c65637461626c652076616c696461746f72732063616e6e6f74206265207a65726f44656372656d656e742076616c7565206d7573742062652067726561746572207468616e20302e6572726f722063616c6c696e67206861736848656164657220707265636f6d70696c65a265627a7a72315820b41c16c49fd0e33cab72157c7f919ce09d7f54c7e7f433c7f3f6147bddbc1d4f64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f014": { + "code": "0x608060405234801561001057600080fd5b50600436106102955760003560e01c80637d164125116101675780639b2b592f116100ce578063df4da46111610087578063df4da46114610eae578063e185aaa814610ecc578063e50e652d14610eea578063ec68307214610f2c578063f2fde38b14610fa7578063fae8db0a14610feb57610295565b80639b2b592f14610d60578063a1b9596214610da2578063a91ee0dc14610dc0578063ae098de214610e04578063b63b4a2314610e22578063cd52782e14610e6857610295565b806392ecd7451161012057806392ecd74514610c6c5780639402838414610c7657806396c3d2fd14610cbc5780639917907f14610d025780639a7b3be714610d205780639ad0cce714610d3e57610295565b80637d16412514610a9b5780638331c1d714610ab957806387ee8a0f14610b135780638a88362614610b315780638da5cb5b14610c005780638f32d59b14610c4a57610295565b80635049890f1161020b57806367960e91116101c457806367960e9114610873578063715018a6146109425780637385e5da1461094c57806378e979251461096a5780637b103999146109885780637cca2a3c146109d257610295565b80635049890f146106fb57806354255be0146107195780635918bb581461074c5780635d180adb1461079c5780635f396e4814610814578063643470431461084057610295565b806323f0ab651161025d57806323f0ab65146103be5780632848f9e3146105485780633b1eb4bf14610566578063434c99c4146105a85780634901c7251461060e5780634b2c2f441461062c57610295565b80630203ab241461029a578063123633ea146102b8578063158ef93e14610326578063171af90f1461034857806322dae21f14610374575b600080fd5b6102a261102d565b6040518082815260200191505060405180910390f35b6102e4600480360360208110156102ce57600080fd5b810190808035906020019092919050505061104c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61032e61119d565b604051808215151515815260200191505060405180910390f35b6103506111b0565b60405180848152602001838152602001828152602001935050505060405180910390f35b61037c611229565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61052e600480360360608110156103d457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561041157600080fd5b82018360208201111561042357600080fd5b8035906020019184600183028401116401000000008311171561044557600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156104a857600080fd5b8201836020820111156104ba57600080fd5b803590602001918460018302840111640100000000831117156104dc57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061124f565b604051808215151515815260200191505060405180910390f35b610550611408565b6040518082815260200191505060405180910390f35b6105926004803603602081101561057c57600080fd5b81019080803590602001909291905050506114d2565b6040518082815260200191505060405180910390f35b6105f4600480360360408110156105be57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506114ec565b604051808215151515815260200191505060405180910390f35b61061661176d565b6040518082815260200191505060405180910390f35b6106e56004803603602081101561064257600080fd5b810190808035906020019064010000000081111561065f57600080fd5b82018360208201111561067157600080fd5b8035906020019184600183028401116401000000008311171561069357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061197d565b6040518082815260200191505060405180910390f35b610703611b11565b6040518082815260200191505060405180910390f35b610721611c3b565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b6107826004803603604081101561076257600080fd5b810190808035906020019092919080359060200190929190505050611c62565b604051808215151515815260200191505060405180910390f35b6107d2600480360360408110156107b257600080fd5b810190808035906020019092919080359060200190929190505050611e8b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61081c611fdd565b60405180848152602001838152602001828152602001935050505060405180910390f35b61084861205c565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b61092c6004803603602081101561088957600080fd5b81019080803590602001906401000000008111156108a657600080fd5b8201836020820111156108b857600080fd5b803590602001918460018302840111640100000000831117156108da57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061217c565b6040518082815260200191505060405180910390f35b61094a612310565b005b610954612449565b6040518082815260200191505060405180910390f35b610972612459565b6040518082815260200191505060405180910390f35b61099061245f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610a9960048036036101808110156109e957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612485565b005b610aa3612594565b6040518082815260200191505060405180910390f35b610af960048036036060811015610acf57600080fd5b810190808035906020019092919080359060200190929190803590602001909291905050506125ba565b604051808215151515815260200191505060405180910390f35b610b1b6127f0565b6040518082815260200191505060405180910390f35b610bea60048036036020811015610b4757600080fd5b8101908080359060200190640100000000811115610b6457600080fd5b820183602082011115610b7657600080fd5b80359060200191846001830284011164010000000083111715610b9857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612937565b6040518082815260200191505060405180910390f35b610c08612acb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610c52612af4565b604051808215151515815260200191505060405180910390f35b610c74612b52565b005b610ca260048036036020811015610c8c57600080fd5b8101908080359060200190929190505050612d13565b604051808215151515815260200191505060405180910390f35b610ce860048036036020811015610cd257600080fd5b8101908080359060200190929190505050612ec5565b604051808215151515815260200191505060405180910390f35b610d0a612fe3565b6040518082815260200191505060405180910390f35b610d28613009565b6040518082815260200191505060405180910390f35b610d46613019565b604051808215151515815260200191505060405180910390f35b610d8c60048036036020811015610d7657600080fd5b810190808035906020019092919050505061317b565b6040518082815260200191505060405180910390f35b610daa6132c4565b6040518082815260200191505060405180910390f35b610e0260048036036020811015610dd657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506134a8565b005b610e0c61364c565b6040518082815260200191505060405180910390f35b610e4e60048036036020811015610e3857600080fd5b8101908080359060200190929190505050613672565b604051808215151515815260200191505060405180910390f35b610e9460048036036020811015610e7e57600080fd5b81019080803590602001909291905050506137d7565b604051808215151515815260200191505060405180910390f35b610eb661393b565b6040518082815260200191505060405180910390f35b610ed4613a77565b6040518082815260200191505060405180910390f35b610f1660048036036020811015610f0057600080fd5b8101908080359060200190929190505050613a7d565b6040518082815260200191505060405180910390f35b610f8a600480360360c0811015610f4257600080fd5b81019080803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050613ac8565b604051808381526020018281526020019250505060405180910390f35b610fe960048036036020811015610fbd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613cdc565b005b6110176004803603602081101561100157600080fd5b8101908080359060200190929190505050613d62565b6040518082815260200191505060405180910390f35b600061104761104261103d613eab565b613f62565b614267565b905090565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16844360405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b602083106110c557805182526020820191506020810190506020830392506110a2565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611125576040519150601f19603f3d011682016040523d82523d6000602084013e61112a565b606091505b50809350819250505080611189576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180615be1603d913960400191505060405180910390fd5b611194826000614275565b92505050919050565b600060149054906101000a900460ff1681565b600080600080600690506111db81600001604051806020016040529081600082015481525050614267565b6111fc82600201604051806020016040529081600082015481525050614267565b61121d83600101604051806020016040529081600082015481525050614267565b93509350935050909192565b600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060fb73ffffffffffffffffffffffffffffffffffffffff16858585604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140183805190602001908083835b602083106112d857805182526020820191506020810190506020830392506112b5565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b602083106113295780518252602082019150602081019050602083039250611306565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506040516020818303038152906040526040518082805190602001908083835b60208310611392578051825260208201915060208101905060208303925061136f565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146113f2576040519150601f19603f3d011682016040523d82523d6000602084013e6113f7565b606091505b505080915050809150509392505050565b60006114cd6114c860066000016040518060200160405290816000820154815250506114ba61143561428c565b73ffffffffffffffffffffffffffffffffffffffff16631f6042436040518163ffffffff1660e01b815260040160206040518083038186803b15801561147a57600080fd5b505afa15801561148e573d6000803e3d6000fd5b505050506040513d60208110156114a457600080fd5b8101908080519060200190929190505050614387565b61441190919063ffffffff16565b614870565b905090565b60006114e5826114e061393b565b614891565b9050919050565b60006114f6612af4565b611568576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415806115e357506115df600b604051806020016040529081600082015481525050614267565b8214155b611638576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526048815260200180615db76048913960600191505060405180910390fd5b6116486116436148d9565b614267565b82106116bc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f56616c7565206d757374206265206c657373207468616e20310000000000000081525060200191505060405180910390fd5b82600c60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611706826148ff565b600b600082015181600001559050508273ffffffffffffffffffffffffffffffffffffffff167fe296227209b47bb8f4a76768ebd564dcde1c44be325a5d262f27c1fd4fd4538b836040518082815260200191505060405180910390a26001905092915050565b600080600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f537461626c65546f6b656e000000000000000000000000000000000000000000815250600b019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561182957600080fd5b505afa15801561183d573d6000803e3d6000fd5b505050506040513d602081101561185357600080fd5b8101908080519060200190929190505050905060008061187161491d565b73ffffffffffffffffffffffffffffffffffffffff1663ef90e1b0846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050604080518083038186803b1580156118ec57600080fd5b505afa158015611900573d6000803e3d6000fd5b505050506040513d604081101561191657600080fd5b810190808051906020019092919080519060200190929190505050915091506119758261196783611959600d5461194b6127f0565b614a1890919063ffffffff16565b614a1890919063ffffffff16565b614a9e90919063ffffffff16565b935050505090565b60006060600060f473ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b602083106119d257805182526020820191506020810190506020830392506119af565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310611a395780518252602082019150602081019050602083039250611a16565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611a99576040519150601f19603f3d011682016040523d82523d6000602084013e611a9e565b606091505b50809350819250505080611afd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526038815260200180615b2f6038913960400191505060405180910390fd5b611b08826000614ae8565b92505050919050565b600080611b2960025442614b8990919063ffffffff16565b90506201518061016d600f0202811015611bdb576000611b7d6002611b6f6b01f04ef12cb04cf1580000006b033b2e3c9fd0803ce8000000614b8990919063ffffffff16565b614a9e90919063ffffffff16565b90506000611bb06201518061016d600f0202611ba28585614a1890919063ffffffff16565b614a9e90919063ffffffff16565b9050611bd16b01f04ef12cb04cf15800000082614bd390919063ffffffff16565b9350505050611c38565b6000611c32576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180615af96036913960400191505060405180910390fd5b60009150505b90565b60008060008060018060016000839350829250819150809050935093509350935090919293565b6000611c6c612af4565b611cde576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b611d006006600201604051806020016040529081600082015481525050614267565b83141580611d2f5750611d2b6006600101604051806020016040529081600082015481525050614267565b8214155b611d84576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180615bbf6022913960400191505060405180910390fd5b611d8d836148ff565b600660020160008201518160000155905050611da8826148ff565b600660010160008201518160000155905050611ded611dc56148d9565b6006600201604051806020016040529081600082015481525050614c5b90919063ffffffff16565b611e42576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f815260200180615d88602f913960400191505060405180910390fd5b7f1b76e38f3fdd1f284ed4d47c9d50ff407748c516ff9761616ff638c2331076258383604051808381526020018281526020019250505060405180910390a16001905092915050565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16858560405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310611f045780518252602082019150602081019050602083039250611ee1565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611f64576040519150601f19603f3d011682016040523d82523d6000602084013e611f69565b606091505b50809350819250505080611fc8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180615c886036913960400191505060405180910390fd5b611fd3826000614275565b9250505092915050565b6000806000806003905061200881600201604051806020016040529081600082015481525050614267565b61202c82600001600001604051806020016040529081600082015481525050614267565b61205083600001600101604051806020016040529081600082015481525050614267565b93509350935050909192565b600080600080600061206c611408565b90506000612078613eab565b90506120826159a1565b61208b82613f62565b90506120b26120ad8261209f600d54614387565b61441190919063ffffffff16565b614870565b6120d56120d0836120c287614387565b61441190919063ffffffff16565b614870565b61212061211b8461210d600a6040518060200160405290816000820154815250506120ff89614387565b61441190919063ffffffff16565b61441190919063ffffffff16565b614870565b61216b61216685612158600b60405180602001604052908160008201548152505061214a8a614387565b61441190919063ffffffff16565b61441190919063ffffffff16565b614870565b965096509650965050505090919293565b60006060600060f673ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b602083106121d157805182526020820191506020810190506020830392506121ae565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106122385780518252602082019150602081019050602083039250612215565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612298576040519150601f19603f3d011682016040523d82523d6000602084013e61229d565b606091505b508093508192505050806122fc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180615dff6023913960400191505060405180910390fd5b612307826000614ae8565b92505050919050565b612318612af4565b61238a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600061245443613a7d565b905090565b60025481565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1615612508576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff02191690831515021790555061252c33614c70565b6125358c6134a8565b61253f8a8a611c62565b5061254b8888886125ba565b5061255585612d13565b5061255f84612ec5565b5061256983613672565b5061257482826114ec565b5061257e8b6137d7565b5042600281905550505050505050505050505050565b60006125b5600b604051806020016040529081600082015481525050614267565b905090565b60006125c4612af4565b612636576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6126586003600201604051806020016040529081600082015481525050614267565b8414158061268a57506126866003600001600101604051806020016040529081600082015481525050614267565b8214155b806126b957506126b56003600001600001604051806020016040529081600082015481525050614267565b8314155b61270e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180615a5a6021913960400191505060405180910390fd5b6040518060400160405280604051806040016040528061272d876148ff565b815260200161273b866148ff565b815250815260200161274c866148ff565b815250600360008201518160000160008201518160000160008201518160000155505060208201518160010160008201518160000155505050506020820151816002016000820151816000015550509050507f191445ee0115396c9725b9c642b985d63820ca57d54e42e5eb38faec4022f05d84848460405180848152602001838152602001828152602001935050505060405180910390a1600190509392505050565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1643604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310612861578051825260208201915060208101905060208303925061283e565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146128c1576040519150601f19603f3d011682016040523d82523d6000602084013e6128c6565b606091505b50809350819250505080612925576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526035815260200180615c1e6035913960400191505060405180910390fd5b612930826000614275565b9250505090565b60006060600060f773ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b6020831061298c5780518252602082019150602081019050602083039250612969565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106129f357805182526020820191506020810190506020830392506129d0565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612a53576040519150601f19603f3d011682016040523d82523d6000602084013e612a58565b606091505b50809350819250505080612ab7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180615d576031913960400191505060405180910390fd5b612ac2826000614275565b92505050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16612b36614db4565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612bf4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b612bfc614dbc565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015612c7857600080fd5b505afa158015612c8c573d6000803e3d6000fd5b505050506040513d6020811015612ca257600080fd5b810190808051906020019092919050505015612d09576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180615b676022913960400191505060405180910390fd5b612d11614eb7565b565b6000612d1d612af4565b612d8f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b612dae6009604051806020016040529081600082015481525050614267565b821415612e06576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180615a7b6025913960400191505060405180910390fd5b612e16612e116148d9565b614267565b8210612e6d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526033815260200180615ac66033913960400191505060405180910390fd5b612e76826148ff565b6009600082015181600001559050507fbae2f33c70949fbc7325c98655f3039e5e1c7f774874c99fd4f31ec5f432b159826040518082815260200191505060405180910390a160019050919050565b6000612ecf612af4565b612f41576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600d54821415612f9c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180615a046028913960400191505060405180910390fd5b81600d819055507fa21d141963bb2c1064b5376f762d22d3e9c4c51617edcf105bcec0f14e36800c826040518082815260200191505060405180910390a160019050919050565b6000613004600a604051806020016040529081600082015481525050614267565b905090565b6000613014436114d2565b905090565b60006130236159a1565b61304061303b60025442614b8990919063ffffffff16565b614387565b905061304a6159a1565b613057632efe0780614387565b90506130616159a1565b61306b6002614387565b90506130756159a1565b61308883856152ab90919063ffffffff16565b1561309c576130956148d9565b90506130c4565b6130c16130b284866152c190919063ffffffff16565b8361540a90919063ffffffff16565b90505b6130cc6159a1565b61315c6130d76154b1565b73ffffffffffffffffffffffffffffffffffffffff166356b6d0d56040518163ffffffff1660e01b815260040160206040518083038186803b15801561311c57600080fd5b505afa158015613130573d6000803e3d6000fd5b505050506040513d602081101561314657600080fd5b81019080805190602001909291905050506148ff565b905061317182826155ac90919063ffffffff16565b9550505050505090565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106131ec57805182526020820191506020810190506020830392506131c9565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461324c576040519150601f19603f3d011682016040523d82523d6000602084013e613251565b606091505b508093508192505050806132b0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180615a2c602e913960400191505060405180910390fd5b6132bb826000614275565b92505050919050565b6000806133e86132d26154b1565b73ffffffffffffffffffffffffffffffffffffffff16638d9a5e6f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561331757600080fd5b505afa15801561332b573d6000803e3d6000fd5b505050506040513d602081101561334157600080fd5b810190808051906020019092919050505061335a6155c2565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561339f57600080fd5b505afa1580156133b3573d6000803e3d6000fd5b505050506040513d60208110156133c957600080fd5b8101908080519060200190929190505050614b8990919063ffffffff16565b905060006133f461428c565b73ffffffffffffffffffffffffffffffffffffffff16639a0e7d666040518163ffffffff1660e01b815260040160206040518083038186803b15801561343957600080fd5b505afa15801561344d573d6000803e3d6000fd5b505050506040513d602081101561346357600080fd5b810190808051906020019092919050505090506134a161349c61348584614387565b61348e84614387565b6152c190919063ffffffff16565b614267565b9250505090565b6134b0612af4565b613522576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156135c5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b600061366d6009604051806020016040529081600082015481525050614267565b905090565b600061367c612af4565b6136ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61370d600a604051806020016040529081600082015481525050614267565b821415801561372a57506137276137226148d9565b614267565b82105b61377f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252604f8152602001806159b5604f913960600191505060405180910390fd5b613788826148ff565b600a600082015181600001559050507fe6c1b64ad7e601924731051286b7b408b1a6f3ae96dcd6d2d9cd82578372ef9e826040518082815260200191505060405180910390a160019050919050565b60006137e1612af4565b613853576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61385b6159a1565b613864836148ff565b90506138926006600201604051806020016040529081600082015481525050826155ac90919063ffffffff16565b6138e7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526035815260200180615c536035913960400191505060405180910390fd5b806006600001600082015181600001559050507f152c3fc1e1cd415804bc9ae15876b37e62d8909358b940e6f4847ca927f46637836040518082815260200191505060405180910390a16001915050919050565b60006060600060f873ffffffffffffffffffffffffffffffffffffffff166040516020016040516020818303038152906040526040518082805190602001908083835b602083106139a1578051825260208201915060208101905060208303925061397e565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613a01576040519150601f19603f3d011682016040523d82523d6000602084013e613a06565b606091505b50809350819250505080613a65576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180615d066025913960400191505060405180910390fd5b613a70826000614275565b9250505090565b600d5481565b6000613ac16003613ab36002613aa56002613a978861317b565b614a1890919063ffffffff16565b614bd390919063ffffffff16565b614a9e90919063ffffffff16565b9050919050565b60008060008714158015613add575060008514155b613b4f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f612064656e6f6d696e61746f72206973207a65726f000000000000000000000081525060200191505060405180910390fd5b6000806000606060fc73ffffffffffffffffffffffffffffffffffffffff168c8c8c8c8c8c6040516020018087815260200186815260200185815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040526040518082805190602001908083835b60208310613be95780518252602082019150602081019050602083039250613bc6565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613c49576040519150601f19603f3d011682016040523d82523d6000602084013e613c4e565b606091505b50809250819350505081613cad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180615cdf6027913960400191505060405180910390fd5b613cb8816000614275565b9350613cc5816020614275565b925083839550955050505050965096945050505050565b613ce4612af4565b613d56576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b613d5f81614c70565b50565b60006060600060f573ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310613dd35780518252602082019150602081019050602083039250613db0565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613e33576040519150601f19603f3d011682016040523d82523d6000602084013e613e38565b606091505b50809350819250505080613e97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180615d2b602c913960400191505060405180910390fd5b613ea2826000614ae8565b92505050919050565b600080613eb6611408565b90506000613ec261176d565b90506000613ed98284614bd390919063ffffffff16565b9050613f57613f52613f3b600b604051806020016040529081600082015481525050613f2d600a604051806020016040529081600082015481525050613f1f6001614387565b61540a90919063ffffffff16565b61540a90919063ffffffff16565b613f4484614387565b6152c190919063ffffffff16565b614870565b905080935050505090565b613f6a6159a1565b6000613f74611b11565b90506000613f806155c2565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015613fc557600080fd5b505afa158015613fd9573d6000803e3d6000fd5b505050506040513d6020811015613fef57600080fd5b81019080805190602001909291905050509050600061403561401a8684614bd390919063ffffffff16565b6b033b2e3c9fd0803ce8000000614b8990919063ffffffff16565b90506000614058846b033b2e3c9fd0803ce8000000614b8990919063ffffffff16565b90506140626159a1565b61408561406e83614387565b61407785614387565b6152c190919063ffffffff16565b90506140a16140926148d9565b826156bd90919063ffffffff16565b15614184576140ae6159a1565b6140f660036000016000016040518060200160405290816000820154815250506140e86140d96148d9565b8561540a90919063ffffffff16565b61441190919063ffffffff16565b90506141006159a1565b61411a8261410c6148d9565b6156d290919063ffffffff16565b9050614148600360020160405180602001604052908160008201548152505082614c5b90919063ffffffff16565b1561415c5780975050505050505050614262565b6003600201604051806020016040529081600082015481525050975050505050505050614262565b61419e61418f6148d9565b82614c5b90919063ffffffff16565b15614252576141ab6159a1565b6141f360036000016001016040518060200160405290816000820154815250506141e5846141d76148d9565b61540a90919063ffffffff16565b61441190919063ffffffff16565b905061420f6142006148d9565b82614c5b90919063ffffffff16565b1561423b5761422e816142206148d9565b61540a90919063ffffffff16565b9650505050505050614262565b61424560006148ff565b9650505050505050614262565b61425a6148d9565b955050505050505b919050565b600081600001519050919050565b60006142818383614ae8565b60001c905092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f456c656374696f6e0000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561434757600080fd5b505afa15801561435b573d6000803e3d6000fd5b505050506040513d602081101561437157600080fd5b8101908080519060200190929190505050905090565b61438f6159a1565b61439761577b565b8211156143ef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180615b896036913960400191505060405180910390fd5b604051806020016040528069d3c21bcecceda100000084028152509050919050565b6144196159a1565b600083600001511480614430575060008260000151145b1561444c5760405180602001604052806000815250905061486a565b69d3c21bcecceda10000008260000151141561446a5782905061486a565b69d3c21bcecceda1000000836000015114156144885781905061486a565b600069d3c21bcecceda100000061449e8561579a565b60000151816144a957fe5b04905060006144b7856157d1565b600001519050600069d3c21bcecceda10000006144d38661579a565b60000151816144de57fe5b04905060006144ec866157d1565b6000015190506000828502905060008514614580578285828161450b57fe5b041461457f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600069d3c21bcecceda100000082029050600082146146225769d3c21bcecceda10000008282816145ad57fe5b0414614621576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f6f766572666c6f772078317931202a206669786564312064657465637465640081525060200191505060405180910390fd5b5b80915060008486029050600086146146b3578486828161463e57fe5b04146146b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600084880290506000881461474157848882816146cc57fe5b0414614740576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b61474961580e565b878161475157fe5b04965061475c61580e565b858161476457fe5b04945060008588029050600088146147f5578588828161478057fe5b04146147f4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b6147fd6159a1565b6040518060200160405280878152509050614826816040518060200160405280878152506156d2565b9050614840816040518060200160405280868152506156d2565b905061485a816040518060200160405280858152506156d2565b9050809a50505050505050505050505b92915050565b600069d3c21bcecceda100000082600001518161488957fe5b049050919050565b60008082848161489d57fe5b04905060008385816148ab57fe5b0614156148bb57809150506148d3565b6148cf600182614bd390919063ffffffff16565b9150505b92915050565b6148e16159a1565b604051806020016040528069d3c21bcecceda1000000815250905090565b6149076159a1565b6040518060200160405280838152509050919050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f536f727465644f7261636c657300000000000000000000000000000000000000815250600d019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156149d857600080fd5b505afa1580156149ec573d6000803e3d6000fd5b505050506040513d6020811015614a0257600080fd5b8101908080519060200190929190505050905090565b600080831415614a2b5760009050614a98565b6000828402905082848281614a3c57fe5b0414614a93576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180615cbe6021913960400191505060405180910390fd5b809150505b92915050565b6000614ae083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061581b565b905092915050565b6000614afe602083614bd390919063ffffffff16565b83511015614b74576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f736c6963696e67206f7574206f662072616e676500000000000000000000000081525060200191505060405180910390fd5b60006020830184015190508091505092915050565b6000614bcb83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506158e1565b905092915050565b600080828401905083811015614c51576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b60008160000151836000015110905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415614cf6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180615aa06026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600033905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f467265657a6572000000000000000000000000000000000000000000000000008152506007019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015614e7757600080fd5b505afa158015614e8b573d6000803e3d6000fd5b505050506040513d6020811015614ea157600080fd5b8101908080519060200190929190505050905090565b614ebf614dbc565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015614f3b57600080fd5b505afa158015614f4f573d6000803e3d6000fd5b505050506040513d6020811015614f6557600080fd5b810190808051906020019092919050505015614fcc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180615b676022913960400191505060405180910390fd5b614fd46159a1565b614fe4614fdf6132c4565b6148ff565b905061500f6009604051806020016040529081600082015481525050826156bd90919063ffffffff16565b156151155761501c6159a1565b61504560096040518060200160405290816000820154815250508361540a90919063ffffffff16565b905061504f6159a1565b61507b60066001016040518060200160405290816000820154815250508361441190919063ffffffff16565b90506150a96006600001604051806020016040529081600082015481525050826152ab90919063ffffffff16565b156150cf576150b86000614387565b60066000016000820151816000015590505061510e565b6150fb81600660000160405180602001604052908160008201548152505061540a90919063ffffffff16565b6006600001600082015181600001559050505b5050615250565b61513e600960405180602001604052908160008201548152505082614c5b90919063ffffffff16565b1561524f5761514b6159a1565b61517482600960405180602001604052908160008201548152505061540a90919063ffffffff16565b905061517e6159a1565b6151aa60066001016040518060200160405290816000820154815250508361441190919063ffffffff16565b90506151d88160066000016040518060200160405290816000820154815250506156d290919063ffffffff16565b60066000016000820151816000015590505061522f600660020160405180602001604052908160008201548152505060066000016040518060200160405290816000820154815250506156bd90919063ffffffff16565b1561524c5760066002016006600001600082015481600001559050505b50505b5b7f49d8cdfe05bae61517c234f65f4088454013bafe561115126a8fe0074dc7700e6152936006600001604051806020016040529081600082015481525050614267565b6040518082815260200191505060405180910390a150565b6000816000015183600001511015905092915050565b6152c96159a1565b600082600001511415615344576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f63616e277420646976696465206279203000000000000000000000000000000081525060200191505060405180910390fd5b600069d3c21bcecceda10000008460000151029050836000015169d3c21bcecceda1000000828161537157fe5b04146153e5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6f766572666c6f7720617420646976696465000000000000000000000000000081525060200191505060405180910390fd5b6040518060200160405280846000015183816153fd57fe5b0481525091505092915050565b6154126159a1565b816000015183600001511015615490576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f737562737472616374696f6e20756e646572666c6f772064657465637465640081525060200191505060405180910390fd5b60405180602001604052808360000151856000015103815250905092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f52657365727665000000000000000000000000000000000000000000000000008152506007019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561556c57600080fd5b505afa158015615580573d6000803e3d6000fd5b505050506040513d602081101561559657600080fd5b8101908080519060200190929190505050905090565b6000816000015183600001511115905092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f476f6c64546f6b656e00000000000000000000000000000000000000000000008152506009019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561567d57600080fd5b505afa158015615691573d6000803e3d6000fd5b505050506040513d60208110156156a757600080fd5b8101908080519060200190929190505050905090565b60008160000151836000015111905092915050565b6156da6159a1565b6000826000015184600001510190508360000151811015615763576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f616464206f766572666c6f77206465746563746564000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808281525091505092915050565b60007601357c299a88ea76a58924d52ce4f26a85af186c2b9e74905090565b6157a26159a1565b604051806020016040528069d3c21bcecceda1000000808560000151816157c557fe5b04028152509050919050565b6157d96159a1565b604051806020016040528069d3c21bcecceda1000000808560000151816157fc57fe5b04028460000151038152509050919050565b600064e8d4a51000905090565b600080831182906158c7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561588c578082015181840152602081019050615871565b50505050905090810190601f1680156158b95780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816158d357fe5b049050809150509392505050565b600083831115829061598e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015615953578082015181840152602081019050615938565b50505050905090810190601f1680156159805780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b604051806020016040528060008152509056fe56616c7565206d75737420626520646966666572656e742066726f6d206578697374696e6720636f6d6d756e69747920726577617264206672616374696f6e20616e64206c657373207468616e20315461726765742076616c696461746f722065706f6368207061796d656e7420756e6368616e6765646572726f722063616c6c696e67206e756d62657256616c696461746f7273496e53657420707265636f6d70696c654261642072657761726473206d756c7469706c69657220706172616d657465727354617267657420766f74696e6720676f6c64206672616374696f6e20756e6368616e6765644f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737354617267657420766f74696e6720676f6c64206672616374696f6e2063616e6e6f74206265206c6172676572207468616e2031426c6f636b207265776172642063616c63756c6174696f6e20666f722079656172732031352d333020756e696d706c656d656e7465646572726f722063616c6c696e672067657456657269666965645365616c4269746d617046726f6d48656164657220707265636f6d70696c6563616e27742063616c6c207768656e20636f6e74726163742069732066726f7a656e63616e277420637265617465206669786964697479206e756d626572206c6172676572207468616e206d61784e6577466978656428294261642074617267657420766f74696e67207969656c6420706172616d65746572736572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e67206e756d62657256616c696461746f7273496e43757272656e7453657420707265636f6d70696c6554617267657420766f74696e67207969656c64206d757374206265206c657373207468616e206f7220657175616c20746f206d61786572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d53657420707265636f6d70696c65536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f776572726f722063616c6c696e67206672616374696f6e4d756c45787020707265636f6d70696c656572726f722063616c6c696e672067657445706f636853697a6520707265636f6d70696c656572726f722063616c6c696e6720676574506172656e745365616c4269746d617020707265636f6d70696c656572726f722063616c6c696e6720676574426c6f636b4e756d62657246726f6d48656164657220707265636f6d70696c654d61782074617267657420766f74696e67207969656c64206d757374206265206c6f776572207468616e2031303025506172746e657220616e642076616c7565206d75737420626520646966666572656e742066726f6d206578697374696e6720636172626f6e206f666673657474696e672066756e646572726f722063616c6c696e67206861736848656164657220707265636f6d70696c65a265627a7a7231582020d9a0ab9994c757607d58482a9f9888486977cde1a4e4fb7807f854fc5540f864736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f015": { + "code": "0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c80638da5cb5b11610104578063e45def95116100a2578063f2fde38b11610071578063f2fde38b14610a91578063fae8db0a14610ad5578063fc48472614610b17578063fe4b84df14610b59576101cf565b8063e45def951461095e578063e50e652d1461097c578063e8fcf723146109be578063ec68307214610a16576101cf565b80639a7b3be7116100de5780639a7b3be71461089e5780639b2b592f146108bc578063c387742b146108fe578063df4da46114610940576101cf565b80638da5cb5b146108045780638f32d59b1461084e57806392e5d98f14610870576101cf565b80635ec01e4d116101715780637385e5da1161014b5780637385e5da146106a157806375832efc146106bf57806387ee8a0f146107175780638a88362614610735576101cf565b80635ec01e4d146105aa57806367960e91146105c8578063715018a614610697576101cf565b80633b1eb4bf116101ad5780633b1eb4bf146103ee5780634b2c2f441461043057806354255be0146104ff5780635d180adb14610532576101cf565b8063123633ea146101d4578063158ef93e1461024257806323f0ab6514610264575b600080fd5b610200600480360360208110156101ea57600080fd5b8101908080359060200190929190505050610b87565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61024a610cd8565b604051808215151515815260200191505060405180910390f35b6103d46004803603606081101561027a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001906401000000008111156102b757600080fd5b8201836020820111156102c957600080fd5b803590602001918460018302840111640100000000831117156102eb57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561034e57600080fd5b82018360208201111561036057600080fd5b8035906020019184600183028401116401000000008311171561038257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610ceb565b604051808215151515815260200191505060405180910390f35b61041a6004803603602081101561040457600080fd5b8101908080359060200190929190505050610ea4565b6040518082815260200191505060405180910390f35b6104e96004803603602081101561044657600080fd5b810190808035906020019064010000000081111561046357600080fd5b82018360208201111561047557600080fd5b8035906020019184600183028401116401000000008311171561049757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610ebe565b6040518082815260200191505060405180910390f35b610507611052565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b6105686004803603604081101561054857600080fd5b810190808035906020019092919080359060200190929190505050611079565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105b26111cb565b6040518082815260200191505060405180910390f35b610681600480360360208110156105de57600080fd5b81019080803590602001906401000000008111156105fb57600080fd5b82018360208201111561060d57600080fd5b8035906020019184600183028401116401000000008311171561062f57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506111dc565b6040518082815260200191505060405180910390f35b61069f611370565b005b6106a96114a9565b6040518082815260200191505060405180910390f35b610715600480360360608110156106d557600080fd5b810190808035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506114b9565b005b61071f61156b565b6040518082815260200191505060405180910390f35b6107ee6004803603602081101561074b57600080fd5b810190808035906020019064010000000081111561076857600080fd5b82018360208201111561077a57600080fd5b8035906020019184600183028401116401000000008311171561079c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506116b2565b6040518082815260200191505060405180910390f35b61080c611846565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61085661186f565b604051808215151515815260200191505060405180910390f35b61089c6004803603602081101561088657600080fd5b81019080803590602001909291905050506118cd565b005b6108a66119e1565b6040518082815260200191505060405180910390f35b6108e8600480360360208110156108d257600080fd5b81019080803590602001909291905050506119f1565b6040518082815260200191505060405180910390f35b61092a6004803603602081101561091457600080fd5b8101908080359060200190929190505050611b3a565b6040518082815260200191505060405180910390f35b610948611b6a565b6040518082815260200191505060405180910390f35b610966611ca6565b6040518082815260200191505060405180910390f35b6109a86004803603602081101561099257600080fd5b8101908080359060200190929190505050611cac565b6040518082815260200191505060405180910390f35b610a00600480360360208110156109d457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611cf7565b6040518082815260200191505060405180910390f35b610a74600480360360c0811015610a2c57600080fd5b81019080803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050611d0f565b604051808381526020018281526020019250505060405180910390f35b610ad360048036036020811015610aa757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611f23565b005b610b0160048036036020811015610aeb57600080fd5b8101908080359060200190929190505050611fa9565b6040518082815260200191505060405180910390f35b610b4360048036036020811015610b2d57600080fd5b81019080803590602001909291905050506120f2565b6040518082815260200191505060405180910390f35b610b8560048036036020811015610b6f57600080fd5b8101908080359060200190929190505050612105565b005b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16844360405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610c005780518252602082019150602081019050602083039250610bdd565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114610c60576040519150601f19603f3d011682016040523d82523d6000602084013e610c65565b606091505b50809350819250505080610cc4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612dc5603d913960400191505060405180910390fd5b610ccf8260006121b8565b92505050919050565b600060149054906101000a900460ff1681565b60008060fb73ffffffffffffffffffffffffffffffffffffffff16858585604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140183805190602001908083835b60208310610d745780518252602082019150602081019050602083039250610d51565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b60208310610dc55780518252602082019150602081019050602083039250610da2565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506040516020818303038152906040526040518082805190602001908083835b60208310610e2e5780518252602082019150602081019050602083039250610e0b565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114610e8e576040519150601f19603f3d011682016040523d82523d6000602084013e610e93565b606091505b505080915050809150509392505050565b6000610eb782610eb2611b6a565b6121cf565b9050919050565b60006060600060f473ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310610f135780518252602082019150602081019050602083039250610ef0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310610f7a5780518252602082019150602081019050602083039250610f57565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114610fda576040519150601f19603f3d011682016040523d82523d6000602084013e610fdf565b606091505b5080935081925050508061103e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526038815260200180612d8d6038913960400191505060405180910390fd5b611049826000612217565b92505050919050565b60008060008060018060016000839350829250819150809050935093509350935090919293565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16858560405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b602083106110f257805182526020820191506020810190506020830392506110cf565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611152576040519150601f19603f3d011682016040523d82523d6000602084013e611157565b606091505b508093508192505050806111b6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180612e376036913960400191505060405180910390fd5b6111c18260006121b8565b9250505092915050565b60006111d743436122b8565b905090565b60006060600060f673ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310611231578051825260208201915060208101905060208303925061120e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106112985780518252602082019150602081019050602083039250611275565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146112f8576040519150601f19603f3d011682016040523d82523d6000602084013e6112fd565b606091505b5080935081925050508061135c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180612f9a6023913960400191505060405180910390fd5b611367826000612217565b92505050919050565b61137861186f565b6113ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60006114b443611cac565b905090565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461155b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b6115668383836123d3565b505050565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1643604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106115dc57805182526020820191506020810190506020830392506115b9565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461163c576040519150601f19603f3d011682016040523d82523d6000602084013e611641565b606091505b508093508192505050806116a0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526035815260200180612e026035913960400191505060405180910390fd5b6116ab8260006121b8565b9250505090565b60006060600060f773ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b6020831061170757805182526020820191506020810190506020830392506116e4565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b6020831061176e578051825260208201915060208101905060208303925061174b565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146117ce576040519150601f19603f3d011682016040523d82523d6000602084013e6117d3565b606091505b50809350819250505080611832576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180612f696031913960400191505060405180910390fd5b61183d8260006121b8565b92505050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166118b16126bb565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b6118d561186f565b611947576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600081116119a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180612d34602c913960400191505060405180910390fd5b806002819055507f337b24e614d34558109f3dee80fbcb3c5a4b08a6611bee45581772f64d1681e5816040518082815260200191505060405180910390a150565b60006119ec43610ea4565b905090565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310611a625780518252602082019150602081019050602083039250611a3f565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611ac2576040519150601f19603f3d011682016040523d82523d6000602084013e611ac7565b606091505b50809350819250505080611b26576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180612ce0602e913960400191505060405180910390fd5b611b318260006121b8565b92505050919050565b60008160405160200180828152602001915050604051602081830303815290604052805190602001209050919050565b60006060600060f873ffffffffffffffffffffffffffffffffffffffff166040516020016040516020818303038152906040526040518082805190602001908083835b60208310611bd05780518252602082019150602081019050602083039250611bad565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611c30576040519150601f19603f3d011682016040523d82523d6000602084013e611c35565b606091505b50809350819250505080611c94576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180612edd6025913960400191505060405180910390fd5b611c9f8260006121b8565b9250505090565b60025481565b6000611cf06003611ce26002611cd46002611cc6886119f1565b6126c390919063ffffffff16565b61274990919063ffffffff16565b6127d190919063ffffffff16565b9050919050565b60016020528060005260406000206000915090505481565b60008060008714158015611d24575060008514155b611d96576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f612064656e6f6d696e61746f72206973207a65726f000000000000000000000081525060200191505060405180910390fd5b6000806000606060fc73ffffffffffffffffffffffffffffffffffffffff168c8c8c8c8c8c6040516020018087815260200186815260200185815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040526040518082805190602001908083835b60208310611e305780518252602082019150602081019050602083039250611e0d565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611e90576040519150601f19603f3d011682016040523d82523d6000602084013e611e95565b606091505b50809250819350505081611ef4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612eb66027913960400191505060405180910390fd5b611eff8160006121b8565b9350611f0c8160206121b8565b925083839550955050505050965096945050505050565b611f2b61186f565b611f9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b611fa68161281b565b50565b60006060600060f573ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b6020831061201a5780518252602082019150602081019050602083039250611ff7565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461207a576040519150601f19603f3d011682016040523d82523d6000602084013e61207f565b606091505b508093508192505050806120de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180612f3d602c913960400191505060405180910390fd5b6120e9826000612217565b92505050919050565b60006120fe82436122b8565b9050919050565b600060149054906101000a900460ff1615612188576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff0219169083151502179055506121ac3361281b565b6121b5816118cd565b50565b60006121c48383612217565b60001c905092915050565b6000808284816121db57fe5b04905060008385816121e957fe5b0614156121f95780915050612211565b61220d60018261274990919063ffffffff16565b9150505b92915050565b600061222d60208361274990919063ffffffff16565b835110156122a3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f736c6963696e67206f7574206f662072616e676500000000000000000000000081525060200191505060405180910390fd5b60006020830184015190508091505092915050565b600081831115612313576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612e8e6028913960400191505060405180910390fd5b60065483148061236257506123336005548361295f90919063ffffffff16565b83118015612361575081600254101580612360575061235d6002548361295f90919063ffffffff16565b83115b5b5b6123b7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526035815260200180612fbd6035913960400191505060405180910390fd5b6003600084815260200190815260200160002054905092915050565b6123df6000801b611b3a565b821415612454576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f63616e6e6f7420636f6d6d6974207a65726f2072616e646f6d6e65737300000081525060200191505060405180910390fd5b6000801b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054146125a3576000801b8314156124f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603b815260200180612f02603b913960400191505060405180910390fd5b600061250484611b3a565b9050600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054811461259d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d815260200180612d60602d913960400191505060405180910390fd5b506125ff565b6000801b83146125fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612ca4603c913960400191505060405180910390fd5b5b60008043146126215761261c60014361295f90919063ffffffff16565b612624565b60005b9050612671436003600084815260200190815260200160002054866040516020018083815260200182815260200192505050604051602081830303815290604052805190602001206129a9565b82600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050505050565b600033905090565b6000808314156126d65760009050612743565b60008284029050828482816126e757fe5b041461273e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612e6d6021913960400191505060405180910390fd5b809150505b92915050565b6000808284019050838110156127c7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600061281383836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612af9565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156128a1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180612d0e6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006129a183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612bbf565b905092915050565b80600360008481526020019081526020016000208190555060006129cb611b6a565b83816129d357fe5b061415612a0c576004546006541015612a0057600360006006548152602001908152602001600020600090555b81600681905550612af5565b60006005541415612a2b57816004819055506001600581905550612af4565b6002546005541115612a9d57612a42600454612c7f565b612a60612a5b600160045461274990919063ffffffff16565b612c7f565b612a76600260045461274990919063ffffffff16565b600481905550612a92600160055461295f90919063ffffffff16565b600581905550612af3565b6002546005541415612ad557612ab4600454612c7f565b612aca600160045461274990919063ffffffff16565b600481905550612af2565b612aeb600160055461274990919063ffffffff16565b6005819055505b5b5b5b5050565b60008083118290612ba5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612b6a578082015181840152602081019050612b4f565b50505050905090810190601f168015612b975780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581612bb157fe5b049050809150509392505050565b6000838311158290612c6c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612c31578082015181840152602081019050612c16565b50505050905090810190601f168015612c5e5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b6006548114612ca05760036000828152602001908152602001600020600090555b5056fe72616e646f6d6e6573732073686f756c64206265207a65726f206966207468657265206973206e6f2070726576696f757320636f6d6d69746d656e746572726f722063616c6c696e67206e756d62657256616c696461746f7273496e53657420707265636f6d70696c654f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737372616e646f6d6e657373426c6f636b5265746574696f6e57696e646f772063616e6e6f74206265207a65726f636f6d6d69746d656e74206469646e2774206d617463682074686520706f737465642072616e646f6d6e6573736572726f722063616c6c696e672067657456657269666965645365616c4269746d617046726f6d48656164657220707265636f6d70696c656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e67206e756d62657256616c696461746f7273496e43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d53657420707265636f6d70696c65536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7743616e6e6f742071756572792072616e646f6d6e657373206f662066757475726520626c6f636b736572726f722063616c6c696e67206672616374696f6e4d756c45787020707265636f6d70696c656572726f722063616c6c696e672067657445706f636853697a6520707265636f6d70696c6572616e646f6d6e6573732063616e6e6f74206265207a65726f20696620746865726520697320612070726576696f757320636f6d6d69746d656e746572726f722063616c6c696e6720676574506172656e745365616c4269746d617020707265636f6d70696c656572726f722063616c6c696e6720676574426c6f636b4e756d62657246726f6d48656164657220707265636f6d70696c656572726f722063616c6c696e67206861736848656164657220707265636f6d70696c6543616e6e6f742071756572792072616e646f6d6e657373206f6c646572207468616e207468652073746f72656420686973746f7279a265627a7a7231582016a80e9f8452473a1f96416822a1e8c77de4ca39eeef6492069172281b26cd8b64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f016": { + "code": "0x608060405234801561001057600080fd5b50600436106102f15760003560e01c80638f32d59b1161019d578063d02e0f0c116100e9578063e831be58116100a2578063f3ff26c61161007c578063f3ff26c6146116ba578063fae8db0a14611708578063fb6a2e531461174a578063fd536f5d14611799576102f1565b8063e831be5814611583578063ec683072146115fb578063f2fde38b14611676576102f1565b8063d02e0f0c146113bb578063df4da46114611413578063e02659ce14611431578063e221932e1461145f578063e3d0f66f1461148d578063e50e652d14611541576102f1565b8063a91ee0dc11610156578063bb46942f11610130578063bb46942f14611279578063bd93f998146112b1578063be2c47a614611309578063c8e74d7314611337576102f1565b8063a91ee0dc14611149578063b45eb7da1461118d578063b5599cc6146111ab576102f1565b80638f32d59b14610e9557806396357c0a14610eb75780639a7b3be71461105d5780639b2b592f1461107b578063a6437e73146110bd578063a762825a146110eb576102f1565b80635fc5c9161161025c5780637b1039991161021557806387ee8a0f116101ef57806387ee8a0f14610d4057806389d3528614610d5e5780638a88362614610d7c5780638da5cb5b14610e4b576102f1565b80637b10399914610c725780638218c6fe14610cbc57806384a1a4fc14610cda576102f1565b80635fc5c91614610a62578063623d593114610b0557806367960e9114610b5d578063715018a614610c2c5780637385e5da14610c365780637796a68414610c54576102f1565b80634eef7e85116102ae5780634eef7e85146106a457806351cff8d91461084357806354255be014610887578063596abea5146108ba5780635ce9bc071461093b5780635d180adb146109ea576102f1565b806303cc1aff146102f6578063123633ea14610379578063158ef93e146103e757806323f0ab65146104095780633b1eb4bf146105935780634b2c2f44146105d5575b600080fd5b6103226004803603602081101561030c57600080fd5b81019080803590602001909291905050506118a5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561036557808201518184015260208101905061034a565b505050509050019250505060405180910390f35b6103a56004803603602081101561038f57600080fd5b8101908080359060200190929190505050611949565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103ef611a9a565b604051808215151515815260200191505060405180910390f35b6105796004803603606081101561041f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561045c57600080fd5b82018360208201111561046e57600080fd5b8035906020019184600183028401116401000000008311171561049057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156104f357600080fd5b82018360208201111561050557600080fd5b8035906020019184600183028401116401000000008311171561052757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611aad565b604051808215151515815260200191505060405180910390f35b6105bf600480360360208110156105a957600080fd5b8101908080359060200190929190505050611c66565b6040518082815260200191505060405180910390f35b61068e600480360360208110156105eb57600080fd5b810190808035906020019064010000000081111561060857600080fd5b82018360208201111561061a57600080fd5b8035906020019184600183028401116401000000008311171561063c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611c80565b6040518082815260200191505060405180910390f35b6106f0600480360360408110156106ba57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611e14565b6040518080602001806020018060200180602001858103855289818151815260200191508051906020019060200280838360005b8381101561073f578082015181840152602081019050610724565b50505050905001858103845288818151815260200191508051906020019060200280838360005b83811015610781578082015181840152602081019050610766565b50505050905001858103835287818151815260200191508051906020019060200280838360005b838110156107c35780820151818401526020810190506107a8565b50505050905001858103825286818151815260200191508051906020019080838360005b838110156108025780820151818401526020810190506107e7565b50505050905090810190601f16801561082f5780820380516001836020036101000a031916815260200191505b509850505050505050505060405180910390f35b6108856004803603602081101561085957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506123f8565b005b61088f6127d2565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b610906600480360360408110156108d057600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506127f9565b604051808363ffffffff1663ffffffff1681526020018263ffffffff1663ffffffff1681526020019250505060405180910390f35b6109a8600480360360a081101561095157600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff16906020019092919080359060200190929190803590602001909291905050506128d5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610a2060048036036040811015610a0057600080fd5b810190808035906020019092919080359060200190929190505050612c5e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610aae60048036036040811015610a7857600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612db0565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610af1578082015181840152602081019050610ad6565b505050509050019250505060405180910390f35b610b4760048036036020811015610b1b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612e95565b6040518082815260200191505060405180910390f35b610c1660048036036020811015610b7357600080fd5b8101908080359060200190640100000000811115610b9057600080fd5b820183602082011115610ba257600080fd5b80359060200191846001830284011164010000000083111715610bc457600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612ede565b6040518082815260200191505060405180910390f35b610c34613072565b005b610c3e6131ab565b6040518082815260200191505060405180910390f35b610c5c6131bb565b6040518082815260200191505060405180910390f35b610c7a6131c5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610cc46131eb565b6040518082815260200191505060405180910390f35b610d2660048036036040811015610cf057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506131f1565b604051808215151515815260200191505060405180910390f35b610d48613220565b6040518082815260200191505060405180910390f35b610d66613367565b6040518082815260200191505060405180910390f35b610e3560048036036020811015610d9257600080fd5b8101908080359060200190640100000000811115610daf57600080fd5b820183602082011115610dc157600080fd5b80359060200191846001830284011164010000000083111715610de357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061336d565b6040518082815260200191505060405180910390f35b610e53613501565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610e9d61352a565b604051808215151515815260200191505060405180910390f35b610f2e60048036036020811015610ecd57600080fd5b8101908080359060200190640100000000811115610eea57600080fd5b820183602082011115610efc57600080fd5b80359060200191846020830284011164010000000083111715610f1e57600080fd5b9091929391929390505050613588565b6040518080602001806020018060200180602001858103855289818151815260200191508051906020019060200280838360005b83811015610f7d578082015181840152602081019050610f62565b50505050905001858103845288818151815260200191508051906020019060200280838360005b83811015610fbf578082015181840152602081019050610fa4565b50505050905001858103835287818151815260200191508051906020019060200280838360005b83811015611001578082015181840152602081019050610fe6565b50505050905001858103825286818151815260200191508051906020019060200280838360005b83811015611043578082015181840152602081019050611028565b505050509050019850505050505050505060405180910390f35b611065613a92565b6040518082815260200191505060405180910390f35b6110a76004803603602081101561109157600080fd5b8101908080359060200190929190505050613aa2565b6040518082815260200191505060405180910390f35b6110e9600480360360208110156110d357600080fd5b8101908080359060200190929190505050613beb565b005b6111476004803603606081101561110157600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803563ffffffff169060200190929190505050613cff565b005b61118b6004803603602081101561115f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613dd4565b005b611195613f78565b6040518082815260200191505060405180910390f35b611217600480360360608110156111c157600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613f7e565b604051808460ff1660ff1681526020018363ffffffff1663ffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001935050505060405180910390f35b6112af6004803603604081101561128f57600080fd5b810190808035906020019092919080359060200190929190505050614080565b005b6112f3600480360360208110156112c757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614394565b6040518082815260200191505060405180910390f35b6113356004803603602081101561131f57600080fd5b81019080803590602001909291905050506143ac565b005b6113b9600480360360a081101561134d57600080fd5b810190808035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035151590602001909291905050506144c0565b005b611411600480360360608110156113d157600080fd5b810190808035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614865565b005b61141b61504c565b6040518082815260200191505060405180910390f35b61145d6004803603602081101561144757600080fd5b8101908080359060200190929190505050615188565b005b61148b6004803603602081101561147557600080fd5b810190808035906020019092919050505061529c565b005b6114d9600480360360408110156114a357600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506154bf565b604051808463ffffffff1663ffffffff1681526020018363ffffffff1663ffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001935050505060405180910390f35b61156d6004803603602081101561155757600080fd5b8101908080359060200190929190505050615616565b6040518082815260200191505060405180910390f35b6115e56004803603604081101561159957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050615661565b6040518082815260200191505060405180910390f35b611659600480360360c081101561161157600080fd5b81019080803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050615686565b604051808381526020018281526020019250505060405180910390f35b6116b86004803603602081101561168c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061589a565b005b611706600480360360408110156116d057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050615920565b005b6117346004803603602081101561171e57600080fd5b8101908080359060200190929190505050615a89565b6040518082815260200191505060405180910390f35b6117976004803603608081101561176057600080fd5b8101908080359060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050615bd2565b005b6118a3600480360360c08110156117af57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190803590602001909291908035906020019064010000000081111561180a57600080fd5b82018360208201111561181c57600080fd5b8035906020019184602083028401116401000000008311171561183e57600080fd5b90919293919293908035906020019064010000000081111561185f57600080fd5b82018360208201111561187157600080fd5b8035906020019184602083028401116401000000008311171561189357600080fd5b9091929391929390505050616101565b005b60606003600083815260200190815260200160002060000180548060200260200160405190810160405280929190818152602001828054801561193d57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116118f3575b50505050509050919050565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16844360405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b602083106119c2578051825260208201915060208101905060208303925061199f565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611a22576040519150601f19603f3d011682016040523d82523d6000602084013e611a27565b606091505b50809350819250505080611a86576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180617f34603d913960400191505060405180910390fd5b611a918260006162b4565b92505050919050565b600060149054906101000a900460ff1681565b60008060fb73ffffffffffffffffffffffffffffffffffffffff16858585604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140183805190602001908083835b60208310611b365780518252602082019150602081019050602083039250611b13565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b60208310611b875780518252602082019150602081019050602083039250611b64565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506040516020818303038152906040526040518082805190602001908083835b60208310611bf05780518252602082019150602081019050602083039250611bcd565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611c50576040519150601f19603f3d011682016040523d82523d6000602084013e611c55565b606091505b505080915050809150509392505050565b6000611c7982611c7461504c565b6162cb565b9050919050565b60006060600060f473ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310611cd55780518252602082019150602081019050602083039250611cb2565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310611d3c5780518252602082019150602081019050602083039250611d19565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611d9c576040519150601f19603f3d011682016040523d82523d6000602084013e611da1565b606091505b50809350819250505080611e00576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526038815260200180617e996038913960400191505060405180910390fd5b611e0b826000616313565b92505050919050565b60608060608060006003600088815260200190815260200160002060010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000816001019050600080905060008090505b8280549050811015611f4657611f0f846002016000858481548110611ea557fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206163b4565b15611f2b57611f2860018361640e90919063ffffffff16565b91505b611f3f60018261640e90919063ffffffff16565b9050611e84565b50606081604051908082528060200260200182016040528015611f785781602001602082028038833980820191505090505b509050606082604051908082528060200260200182016040528015611fac5781602001602082028038833980820191505090505b509050600080905060008090505b85805490508110156121aa57612045876002016000888481548110611fdb57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206163b4565b1561218f5786600201600087838154811061205c57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160019054906101000a900463ffffffff168483815181106120e157fe5b602002602001019063ffffffff16908163ffffffff168152505085818154811061210757fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811061213e57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505061218c60018361640e90919063ffffffff16565b91505b6121a360018261640e90919063ffffffff16565b9050611fba565b506060806121b6616496565b73ffffffffffffffffffffffffffffffffffffffff16638adaf96f856040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019060200280838360005b83811015612224578082015181840152602081019050612209565b505050509050019250505060006040518083038186803b15801561224757600080fd5b505afa15801561225b573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250604081101561228557600080fd5b81019080805160405193929190846401000000008211156122a557600080fd5b838201915060208201858111156122bb57600080fd5b82518660208202830111640100000000821117156122d857600080fd5b8083526020830192505050908051906020019060200280838360005b8381101561230f5780820151818401526020810190506122f4565b505050509050016040526020018051604051939291908464010000000082111561233857600080fd5b8382019150602082018581111561234e57600080fd5b825186600182028301116401000000008211171561236b57600080fd5b8083526020830192505050908051906020019080838360005b8381101561239f578082015181840152602081019050612384565b50505050905090810190601f1680156123cc5780820380516001836020036101000a031916815260200191505b506040525050508092508193505050848483839b509b509b509b50505050505050505092959194509250565b6000612402616496565b73ffffffffffffffffffffffffffffffffffffffff16637b2434cb336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561247e57600080fd5b505afa158015612492573d6000803e3d6000fd5b505050506040513d60208110156124a857600080fd5b810190808051906020019092919050505090506000600860008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050600081116125b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f76616c756520776173206e656761746976652f7a65726f00000000000000000081525060200191505060405180910390fd5b6000600860008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b1580156126bb57600080fd5b505af11580156126cf573d6000803e3d6000fd5b505050506040513d60208110156126e557600080fd5b8101908080519060200190929190505050612768576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f746f6b656e207472616e73666572206661696c6564000000000000000000000081525060200191505060405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b6398836040518082815260200191505060405180910390a3505050565b60008060008060018060016002839350829250819150809050935093509350935090919293565b6000806003600085815260200190815260200160002060010160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160049054906101000a900463ffffffff166003600086815260200190815260200160002060010160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff16915091509250929050565b6000808686604051602001808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140192505050604051602081830303815290604052805190602001209050600073000000000000000000000000000000000000a01063b3abdb0c838888886040518563ffffffff1660e01b8152600401808581526020018460ff1660ff16815260200183815260200182815260200194505050505060206040518083038186803b1580156129a757600080fd5b505af41580156129bb573d6000803e3d6000fd5b505050506040513d60208110156129d157600080fd5b8101908080519060200190929190505050905060006129ee616496565b73ffffffffffffffffffffffffffffffffffffffff16637b2434cb836040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015612a6a57600080fd5b505afa158015612a7e573d6000803e3d6000fd5b505050506040513d6020811015612a9457600080fd5b810190808051906020019092919050505090506000600360008b815260200190815260200160002060010160008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060016002811115612b4b57fe5b8160000160009054906101000a900460ff166002811115612b6857fe5b14612bbe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603b815260200180617fa6603b913960400191505060405180910390fd5b612bdb8160000160019054906101000a900463ffffffff16616591565b15612c4e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4174746573746174696f6e2074696d6564206f7574000000000000000000000081525060200191505060405180910390fd5b8194505050505095945050505050565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16858560405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310612cd75780518252602082019150602081019050602083039250612cb4565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612d37576040519150601f19603f3d011682016040523d82523d6000602084013e612d3c565b606091505b50809350819250505080612d9b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180617fe16036913960400191505060405180910390fd5b612da68260006162b4565b9250505092915050565b60606003600084815260200190815260200160002060010160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101805480602002602001604051908101604052809291908181526020018280548015612e8857602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311612e3e575b5050505050905092915050565b6000600760008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60006060600060f673ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310612f335780518252602082019150602081019050602083039250612f10565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310612f9a5780518252602082019150602081019050602083039250612f77565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612ffa576040519150601f19603f3d011682016040523d82523d6000602084013e612fff565b606091505b5080935081925050508061305e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806181e96023913960400191505060405180910390fd5b613069826000616313565b92505050919050565b61307a61352a565b6130ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60006131b643615616565b905090565b6000600654905090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60065481565b60096020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1643604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310613291578051825260208201915060208101905060208303925061326e565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146132f1576040519150601f19603f3d011682016040523d82523d6000602084013e6132f6565b606091505b50809350819250505080613355576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526035815260200180617f716035913960400191505060405180910390fd5b6133608260006162b4565b9250505090565b60055481565b60006060600060f773ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b602083106133c2578051825260208201915060208101905060208303925061339f565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106134295780518252602082019150602081019050602083039250613406565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613489576040519150601f19603f3d011682016040523d82523d6000602084013e61348e565b606091505b508093508192505050806134ed576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260318152602001806181926031913960400191505060405180910390fd5b6134f88260006162b4565b92505050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661356c6165b8565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b606080606080600086869050116135ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260288152602001806180636028913960400191505060405180910390fd5b606080613637888880806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050506165c0565b8092508193505050606081516040519080825280602002602001820160405280156136715781602001602082028038833980820191505090505b509050606082516040519080825280602002602001820160405280156136a65781602001602082028038833980820191505090505b509050600080905060008090505b8b8b9050811015613a77576060600360008e8e858181106136d157fe5b90506020020135815260200190815260200160002060000180548060200260200160405190810160405280929190818152602001828054801561376957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161371f575b5050505050905060008090505b87838151811061378257fe5b6020026020010151811015613a5a57613799616496565b73ffffffffffffffffffffffffffffffffffffffff16631fd9afa58383815181106137c057fe5b60200260200101516040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561382857600080fd5b505afa15801561383c573d6000803e3d6000fd5b505050506040513d602081101561385257600080fd5b810190808051906020019092919050505087858151811061386f57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600360008f8f868181106138b957fe5b90506020020135815260200190815260200160002060010160008383815181106138df57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160049054906101000a900463ffffffff1663ffffffff1686858151811061394757fe5b602002602001019067ffffffffffffffff16908167ffffffffffffffff1681525050600360008f8f8681811061397957fe5b905060200201358152602001908152602001600020600101600083838151811061399f57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff1663ffffffff16858581518110613a0757fe5b602002602001019067ffffffffffffffff16908167ffffffffffffffff1681525050613a3d60018561640e90919063ffffffff16565b9350613a5360018261640e90919063ffffffff16565b9050613776565b5050613a7060018261640e90919063ffffffff16565b90506136b4565b50848484849850985098509850505050505092959194509250565b6000613a9d43611c66565b905090565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310613b135780518252602082019150602081019050602083039250613af0565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613b73576040519150601f19603f3d011682016040523d82523d6000602084013e613b78565b606091505b50809350819250505080613bd7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180617d33602e913960400191505060405180910390fd5b613be28260006162b4565b92505050919050565b613bf361352a565b613c65576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008111613cbe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180617e186030913960400191505060405180910390fd5b806004819055507f4fbe976a07a9260091c2d347f8780c4bc636392e34d5b249b367baf8a5c7ca69816040518082815260200191505060405180910390a150565b60006003600085815260200190815260200160002060010160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff1663ffffffff1690508163ffffffff168114613dce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180617cbb602e913960400191505060405180910390fd5b50505050565b613ddc61352a565b613e4e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613ef1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b60045481565b6000806000806003600088815260200190815260200160002060010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060000160009054906101000a900460ff16600281111561403657fe5b8160000160019054906101000a900463ffffffff168260000160059054906101000a900473ffffffffffffffffffffffffffffffffffffffff169350935093505093509350939050565b600060036000848152602001908152602001600020600001805490509050808210614113576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f496e64657820697320696e76616c69640000000000000000000000000000000081525060200191505060405180910390fd5b60036000848152602001908152602001600020600001828154811061413457fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146141ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f496e64657820646f6573206e6f74206d61746368206d73672e73656e6465720081525060200191505060405180910390fd5b600061421560018361672190919063ffffffff16565b90508083146142d45760036000858152602001908152602001600020600001818154811061423f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660036000868152602001908152602001600020600001848154811061428b57fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b60006003600086815260200190815260200160002060000182815481106142f757fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061436c6001600360008781526020019081526020016000206000018054905061672190919063ffffffff16565b600360008681526020019081526020016000206000018161438d9190617bb3565b5050505050565b60076020528060005260406000206000915090505481565b6143b461352a565b614426576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000811161447f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180617ce96028913960400191505060405180910390fd5b806006819055507fc1f217a1246a98ce04e938768309107630ed86c1e0e9f9995af28e23a9c06178816040518082815260200191505060405180910390a150565b8273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061452557508173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b61457a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180617e696030913960400191505060405180910390fd5b6000858484604051602001808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401935050505060405160208183030381529060405280519060200120905060008473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461464e5784614650565b835b90508280156146b95750600960008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060009054906101000a900460ff165b15614738576146ca8787878761676b565b6000600960008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002060006101000a81548160ff02191690831515021790555061485c565b82600960003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002060006101000a81548160ff021916908315150217905550863373ffffffffffffffffffffffffffffffffffffffff167f14d7ffb83f4265cb6fb62188eb603269555bf46efbc2923909ed7ac313d57af7878787604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182151515158152602001935050505060405180910390a35b50505050505050565b6001600260008282540192505081905550600060025490506000600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205411614915576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180617d116022913960400191505060405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff166323b872dd333061498587600760008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054616e4990919063ffffffff16565b6040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050602060405180830381600087803b158015614a2157600080fd5b505af1158015614a35573d6000803e3d6000fd5b505050506040513d6020811015614a4b57600080fd5b8101908080519060200190929190505050614ab1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b815260200180618017602b913960400191505060405180910390fd5b60008311614b0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180617f0a602a913960400191505060405180910390fd5b600654831115614b82576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f546f6f206d616e79206174746573746174696f6e73207265717565737465640081525060200191505060405180910390fd5b600060036000868152602001908152602001600020905060008160020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff1663ffffffff161480614c5b5750614c5a8160020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff16616591565b5b80614cc65750614cc48160020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff1663ffffffff16616ecf565b155b614d1b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526039815260200180617db76039913960400191505060405180910390fd5b614d2443616f74565b8160020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548163ffffffff021916908363ffffffff160217905550614d8f84616f74565b8160020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160046101000a81548163ffffffff021916908363ffffffff160217905550828160020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160086101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550614ee9614ee4858360010160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff1663ffffffff1661640e90919063ffffffff16565b616f74565b8160010160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548163ffffffff021916908363ffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff16857f381545d9b1fffcb94ffbbd0bccfff9f1fb3acd474d34f7d59112a5c9973fee498686604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a3506002548114615046576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50505050565b60006060600060f873ffffffffffffffffffffffffffffffffffffffff166040516020016040516020818303038152906040526040518082805190602001908083835b602083106150b2578051825260208201915060208101905060208303925061508f565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114615112576040519150601f19603f3d011682016040523d82523d6000602084013e615117565b606091505b50809350819250505080615176576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260258152602001806180b26025913960400191505060405180910390fd5b6151818260006162b4565b9250505090565b61519061352a565b615202576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000811161525b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180617d616030913960400191505060405180910390fd5b806005819055507f954fa47fa6f4e8017b99f93c73f4fbe599d786f9f5da73fe9086ab473fb455d8816040518082815260200191505060405180910390a150565b600060036000838152602001908152602001600020905060008160020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff1663ffffffff1611615366576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603781526020018061812f6037913960400191505060405180910390fd5b6153c38160020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff16616591565b15615419576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061810c6023913960400191505060405180910390fd5b61542282616fdb565b8060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600080820160006101000a81549063ffffffff02191690556000820160046101000a81549063ffffffff02191690556000820160086101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550505050565b60008060006003600086815260200190815260200160002060020160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff166003600087815260200190815260200160002060020160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160049054906101000a900463ffffffff166003600088815260200190815260200160002060020160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160089054906101000a900473ffffffffffffffffffffffffffffffffffffffff169250925092509250925092565b600061565a600361564c600261563e600261563088613aa2565b616e4990919063ffffffff16565b61640e90919063ffffffff16565b6177a490919063ffffffff16565b9050919050565b6008602052816000526040600020602052806000526040600020600091509150505481565b6000806000871415801561569b575060008514155b61570d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f612064656e6f6d696e61746f72206973207a65726f000000000000000000000081525060200191505060405180910390fd5b6000806000606060fc73ffffffffffffffffffffffffffffffffffffffff168c8c8c8c8c8c6040516020018087815260200186815260200185815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040526040518082805190602001908083835b602083106157a75780518252602082019150602081019050602083039250615784565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114615807576040519150601f19603f3d011682016040523d82523d6000602084013e61580c565b606091505b5080925081935050508161586b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061808b6027913960400191505060405180910390fd5b6158768160006162b4565b93506158838160206162b4565b925083839550955050505050965096945050505050565b6158a261352a565b615914576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61591d816177ee565b50565b61592861352a565b61599a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600081116159f3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180617df06028913960400191505060405180910390fd5b80600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff167f7cf8b633f218e9f9bc2c06107bcaddcfee6b90580863768acdcfd4f05d7af394826040518082815260200191505060405180910390a25050565b60006060600060f573ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310615afa5780518252602082019150602081019050602083039250615ad7565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114615b5a576040519150601f19603f3d011682016040523d82523d6000602084013e615b5f565b606091505b50809350819250505080615bbe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180618166602c913960400191505060405180910390fd5b615bc9826000616313565b92505050919050565b6000615be185338686866128d5565b905060006003600087815260200190815260200160002060010160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160000160059054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050615cac43616f74565b8260000160016101000a81548163ffffffff021916908363ffffffff16021790555060028260000160006101000a81548160ff02191690836002811115615cef57fe5b02179055508160000160056101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905560006003600089815260200190815260200160002060010160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060018160000160049054906101000a900463ffffffff160163ffffffff168160000160049054906101000a900463ffffffff1663ffffffff1610615e1e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d617468333220696e7465676572206f766572666c6f77000000000081525060200191505060405180910390fd5b60018160000160049054906101000a900463ffffffff16018160000160046101000a81548163ffffffff021916908363ffffffff160217905550615f26600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054600860008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461640e90919063ffffffff16565b600860008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506000600360008a815260200190815260200160002090506001600360008b815260200190815260200160002060010160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160049054906101000a900463ffffffff1663ffffffff16141561609b57806000013390806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505b8473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff168a7f414ff2c18c092697c4b8de49f515ac44f8bebc19b24553cf58ace913a6ac639d60405160405180910390a4505050505050505050565b600060149054906101000a900460ff1615616184576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff0219169083151502179055506161a8336177ee565b6161b188613dd4565b6161ba87613beb565b6161c386615188565b6161cc856143ac565b6000848490501180156161e457508181905084849050145b616239576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260358152602001806180d76035913960400191505060405180910390fd5b60008090505b848490508110156162a95761628e85858381811061625957fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1684848481811061628257fe5b90506020020135615920565b6162a260018261640e90919063ffffffff16565b905061623f565b505050505050505050565b60006162c08383616313565b60001c905092915050565b6000808284816162d757fe5b04905060008385816162e557fe5b0614156162f5578091505061630d565b61630960018261640e90919063ffffffff16565b9150505b92915050565b600061632960208361640e90919063ffffffff16565b8351101561639f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f736c6963696e67206f7574206f662072616e676500000000000000000000000081525060200191505060405180910390fd5b60006020830184015190508091505092915050565b6000600160028111156163c357fe5b8260000160009054906101000a900460ff1660028111156163e057fe5b14801561640757506164058260000160019054906101000a900463ffffffff16616591565b155b9050919050565b60008082840190508381101561648c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4163636f756e74730000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561655157600080fd5b505afa158015616565573d6000803e3d6000fd5b505050506040513d602081101561657b57600080fd5b8101908080519060200190929190505050905090565b60006165ae6004548363ffffffff1661640e90919063ffffffff16565b4310159050919050565b600033905090565b606080600083511161661d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260288152602001806180636028913960400191505060405180910390fd5b6000809050606084516040519080825280602002602001820160405280156166545781602001602082028038833980820191505090505b50905060008090505b85518110156166e15760006003600088848151811061667857fe5b602002602001015181526020019081526020016000206000018054905090506166aa818561640e90919063ffffffff16565b9350808383815181106166b957fe5b602002602001018181525050506166da60018261640e90919063ffffffff16565b905061665d565b5080826040519080825280602002602001820160405280156167125781602001602082028038833980820191505090505b50809050935093505050915091565b600061676383836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250617932565b905092915050565b6000600360008681526020019081526020016000206000018054905090508084106167fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f496e64657820697320696e76616c69640000000000000000000000000000000081525060200191505060405180910390fd5b60036000868152602001908152602001600020600001848154811061681f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146168cd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180617e486021913960400191505060405180910390fd5b60006003600087815260200190815260200160002060010160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff1663ffffffff1614616992576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526039815260200180617ed16039913960400191505060405180910390fd5b816003600087815260200190815260200160002060000185815481106169b457fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506003600086815260200190815260200160002060010160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003600087815260200190815260200160002060010160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000820160009054906101000a900463ffffffff168160000160006101000a81548163ffffffff021916908363ffffffff1602179055506000820160049054906101000a900463ffffffff168160000160046101000a81548163ffffffff021916908363ffffffff1602179055506001820181600101908054616b26929190617bdf565b509050506003600086815260200190815260200160002060020160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003600087815260200190815260200160002060020160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000820160009054906101000a900463ffffffff168160000160006101000a81548163ffffffff021916908363ffffffff1602179055506000820160049054906101000a900463ffffffff168160000160046101000a81548163ffffffff021916908363ffffffff1602179055506000820160089054906101000a900473ffffffffffffffffffffffffffffffffffffffff168160000160086101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509050506003600086815260200190815260200160002060010160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600080820160006101000a81549063ffffffff02191690556000820160046101000a81549063ffffffff0219169055600182016000616d3a9190617c31565b50506003600086815260200190815260200160002060020160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600080820160006101000a81549063ffffffff02191690556000820160046101000a81549063ffffffff02191690556000820160086101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16867f35bc19e2c74829d0a96c765bb41b09ce24a9d0757486ced0d075e7908932363860405160405180910390a45050505050565b600080831415616e5c5760009050616ec9565b6000828402905082848281616e6d57fe5b0414616ec4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806180426021913960400191505060405180910390fd5b809150505b92915050565b6000616f6b616edc6179f2565b73ffffffffffffffffffffffffffffffffffffffff1663e45def956040518163ffffffff1660e01b815260040160206040518083038186803b158015616f2157600080fd5b505afa158015616f35573d6000803e3d6000fd5b505050506040513d6020811015616f4b57600080fd5b81019080805190602001909291905050508361640e90919063ffffffff16565b43109050919050565b60006401000000008210616fd3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806181c36026913960400191505060405180910390fd5b819050919050565b60006003600083815260200190815260200160002060010160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060006003600084815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060006170936179f2565b73ffffffffffffffffffffffffffffffffffffffff1663fc4847266170dd6005548560000160009054906101000a900463ffffffff1663ffffffff1661640e90919063ffffffff16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561711157600080fd5b505afa158015617125573d6000803e3d6000fd5b505050506040513d602081101561713b57600080fd5b810190808051906020019092919050505090506000617158616496565b90506000617164613220565b90506060816040519080825280602002602001820160405280156171975781602001602082028038833980820191505090505b50905060008090505b828110156171dc57808282815181106171b557fe5b6020026020010181815250506171d560018261640e90919063ffffffff16565b90506171a0565b50818560000160049054906101000a900463ffffffff1663ffffffff16111561726d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6e6f7420656e6f7567682069737375657273000000000000000000000000000081525060200191505060405180910390fd5b60008090505b8560000160049054906101000a900463ffffffff1663ffffffff1681101561779a576000831161730b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6e6f7420656e6f7567682069737375657273000000000000000000000000000081525060200191505060405180910390fd5b84604051602001808281526020019150506040516020818303038152906040528051906020012094506000838660001c8161734257fe5b069050600061736384838151811061735657fe5b6020026020010151611949565b905060008673ffffffffffffffffffffffffffffffffffffffff166393c5c487836040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156173e457600080fd5b505afa1580156173f8573d6000803e3d6000fd5b505050506040513d602081101561740e57600080fd5b8101908080519060200190929190505050905060008a60020160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000600281111561747357fe5b8160000160009054906101000a900460ff16600281111561749057fe5b14801561755157508773ffffffffffffffffffffffffffffffffffffffff1663c2e0ee20836040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561751557600080fd5b505afa158015617529573d6000803e3d6000fd5b505050506040513d602081101561753f57600080fd5b81019080805190602001909291905050505b1561774f5761756a60018661640e90919063ffffffff16565b945060018160000160006101000a81548160ff0219169083600281111561758d57fe5b02179055508960000160009054906101000a900463ffffffff168160000160016101000a81548163ffffffff021916908363ffffffff1602179055508960000160089054906101000a900473ffffffffffffffffffffffffffffffffffffffff168160000160056101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508a6001018290806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff168d7faf7f470b643316cf44c1f2898328a075e7602945b4f8584f48ba4ad2d8a2ea9d8d60000160089054906101000a900473ffffffffffffffffffffffffffffffffffffffff16604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a45b61776360018861672190919063ffffffff16565b965085878151811061777157fe5b602002602001015186858151811061778557fe5b60200260200101818152505050505050617273565b5050505050505050565b60006177e683836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250617aed565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415617874576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180617d916026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008383111582906179df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156179a4578082015181840152602081019050617989565b50505050905090810190601f1680156179d15780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f52616e646f6d00000000000000000000000000000000000000000000000000008152506006019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015617aad57600080fd5b505afa158015617ac1573d6000803e3d6000fd5b505050506040513d6020811015617ad757600080fd5b8101908080519060200190929190505050905090565b60008083118290617b99576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015617b5e578082015181840152602081019050617b43565b50505050905090810190601f168015617b8b5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581617ba557fe5b049050809150509392505050565b815481835581811115617bda57818360005260206000209182019101617bd99190617c52565b5b505050565b828054828255906000526020600020908101928215617c205760005260206000209182015b82811115617c1f578254825591600101919060010190617c04565b5b509050617c2d9190617c77565b5090565b5080546000825590600052602060002090810190617c4f9190617c52565b50565b617c7491905b80821115617c70576000816000905550600101617c58565b5090565b90565b617cb791905b80821115617cb357600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550600101617c7d565b5090565b9056fe726571756573746564206174746573746174696f6e7320646f6573206e6f74206d617463682065787065637465646d61784174746573746174696f6e732068617320746f2062652067726561746572207468616e2030496e76616c6964206174746573746174696f6e52657175657374466565546f6b656e6572726f722063616c6c696e67206e756d62657256616c696461746f7273496e53657420707265636f6d70696c6573656c6563744973737565727357616974426c6f636b732068617320746f2062652067726561746572207468616e20304f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737354686572652065786973747320616e20756e657870697265642c20756e73656c6563746564206174746573746174696f6e2072657175657374596f75206861766520746f20737065636966792061206665652067726561746572207468616e20306174746573746174696f6e457870697279426c6f636b732068617320746f2062652067726561746572207468616e2030496e64657820646f6573206e6f74206d617463682066726f6d2061646472657373417070726f766572206d7573742062652073656e646572206f7220726563697069656e74206f66207472616e736665726572726f722063616c6c696e672067657456657269666965645365616c4269746d617046726f6d48656164657220707265636f6d70696c6541646472657373207472616e66657272696e6720746f2068617320616c726561647920726571756573746564206174746573746174696f6e73596f75206861766520746f2072657175657374206174206c656173742031206174746573746174696f6e6572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e67206e756d62657256616c696461746f7273496e43757272656e7453657420707265636f6d70696c654174746573746174696f6e20636f646520646f6573206e6f74206d6174636820616e79206f75747374616e64696e67206174746573746174696f6e6572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d53657420707265636f6d70696c655472616e73666572206f66206174746573746174696f6e20726571756573742066656573206661696c6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77596f75206861766520746f2070617373206174206c65617374206f6e65206964656e7469666965726572726f722063616c6c696e67206672616374696f6e4d756c45787020707265636f6d70696c656572726f722063616c6c696e672067657445706f636853697a6520707265636f6d70696c656174746573746174696f6e52657175657374466565546f6b656e732073706563696669636174696f6e2077617320696e76616c6964546865206174746573746174696f6e20726571756573742068617320657870697265644e6f20756e73656c6563746564206174746573746174696f6e207265717565737420746f2073656c656374206973737565727320666f726572726f722063616c6c696e6720676574506172656e745365616c4269746d617020707265636f6d70696c656572726f722063616c6c696e6720676574426c6f636b4e756d62657246726f6d48656164657220707265636f6d70696c6553616665436173743a2076616c756520646f65736e27742066697420696e20333220626974736572726f722063616c6c696e67206861736848656164657220707265636f6d70696c65a265627a7a72315820a4e47edec5981430279920022f76481c4bdcf5fd30fb25e61427ebf9e4134bee64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f017": { + "code": "0x608060405234801561001057600080fd5b506004361061010b5760003560e01c806374a8f103116100a25780638f80c33e116100715780638f80c33e146105a6578063a91ee0dc1461061e578063c4d66de814610662578063e1d9a080146106a6578063f2fde38b146107345761010b565b806374a8f103146104945780637b103999146104f05780638da5cb5b1461053a5780638f32d59b146105845761010b565b80635b57b65b116100de5780635b57b65b1461027b578063680d782c146102fe578063702cb75d146103e6578063715018a61461048a5761010b565b8063158ef93e1461011057806318d46532146101325780633e68d5d7146101cb57806354255be014610248575b600080fd5b610118610778565b604051808215151515815260200191505060405180910390f35b6101746004803603602081101561014857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061078b565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156101b757808201518184015260208101905061019c565b505050509050019250505060405180910390f35b61022e600480360360808110156101e157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050610858565b604051808215151515815260200191505060405180910390f35b61025061108d565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b6102a76004803603602081101561029157600080fd5b81019080803590602001909291905050506110b4565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156102ea5780820151818401526020810190506102cf565b505050509050019250505060405180910390f35b6103406004803603602081101561031457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611155565b604051808a81526020018973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001878152602001868152602001858152602001848152602001838152602001828152602001995050505050505050505060405180910390f35b610470600480360360c08110156103fc57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506111e3565b604051808215151515815260200191505060405180910390f35b610492611a4f565b005b6104d6600480360360208110156104aa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611b8a565b604051808215151515815260200191505060405180910390f35b6104f8612055565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61054261207b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61058c6120a5565b604051808215151515815260200191505060405180910390f35b6105dc600480360360408110156105bc57600080fd5b810190808035906020019092919080359060200190929190505050612104565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6106606004803603602081101561063457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061214f565b005b6106a46004803603602081101561067857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506122f3565b005b6106f2600480360360408110156106bc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506123a5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6107766004803603602081101561074a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506123f0565b005b600160149054906101000a900460ff1681565b6060600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561084c57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610802575b50505050509050919050565b600060016000808282540192505081905550600080549050600073000000000000000000000000000000000000a0106396ef41a1338888886040518563ffffffff1660e01b8152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018460ff1660ff16815260200183815260200182815260200194505050505060206040518083038186803b15801561090b57600080fd5b505af415801561091f573d6000803e3d6000fd5b505050506040513d602081101561093557600080fd5b810190808051906020019092919050505090508673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146109cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d815260200180612d17602d913960400191505060405180910390fd5b6109d4612b6f565b600360008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180610120016040529081600082015481526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200160038201548152602001600482015481526020016005820154815260200160068201548152602001600782015481526020016008820154815250509050600073ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1614158015610b58575060008160600151115b610bca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f496e76616c69642077697468647261772076616c75652e00000000000000000081525060200191505060405180910390fd5b6000816000015150602060ff161115610e18576000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd92723360405160200180807f4174746573746174696f6e730000000000000000000000000000000000000000815250600c019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015610c9857600080fd5b505afa158015610cac573d6000803e3d6000fd5b505050506040513d6020811015610cc257600080fd5b8101908080519060200190929190505050905060008173ffffffffffffffffffffffffffffffffffffffff1663596abea58460000151336040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050604080518083038186803b158015610d5f57600080fd5b505afa158015610d73573d6000803e3d6000fd5b505050506040513d6040811015610d8957600080fd5b8101908080519060200190929190805190602001909291905050505063ffffffff1690508261010001518167ffffffffffffffff161015610e15576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526048815260200180612ccf6048913960600191505060405180910390fd5b50505b610e2188612476565b806040015173ffffffffffffffffffffffffffffffffffffffff1663a9059cbb3383606001516040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b158015610eb057600080fd5b505af1158015610ec4573d6000803e3d6000fd5b505050506040513d6020811015610eda57600080fd5b8101908080519060200190929190505050610f5d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f5472616e73666572206e6f74207375636365737366756c2e000000000000000081525060200191505060405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff1682600001517fab4f92d461fdbd1af5db2375223d65edb43bcb99129b19ab4954004883e5202584606001518c604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a46001935050506000548114611084576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50949350505050565b60008060008060018060016002839350829250819150809050935093509350935090919293565b60606004600083815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561114957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116110ff575b50505050509050919050565b60036020528060005260406000206000915090508060000154908060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060030154908060040154908060050154908060060154908060070154908060080154905089565b600060016000808282540192505081905550600080549050600073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16141580156112385750600086115b80156112445750600085115b6112b6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c6964207472616e7366657220696e707574732e000000000000000081525060200191505060405180910390fd5b6000602060ff16111580156112cc575060008314155b15611322576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526043815260200180612c8c6043913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd92723360405160200180807f4174746573746174696f6e730000000000000000000000000000000000000000815250600c019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156113dd57600080fd5b505afa1580156113f1573d6000803e3d6000fd5b505050506040513d602081101561140757600080fd5b810190808051906020019092919050505090508073ffffffffffffffffffffffffffffffffffffffff16637796a6846040518163ffffffff1660e01b815260040160206040518083038186803b15801561146057600080fd5b505afa158015611474573d6000803e3d6000fd5b505050506040513d602081101561148a57600080fd5b81019080805190602001909291905050508411156114f3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612d446021913960400191505060405180910390fd5b60006115aa6001600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208890806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061288f90919063ffffffff16565b905060006116376001600460008e81526020019081526020016000208990806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061288f90919063ffffffff16565b90506000600360008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160060154146116f6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f7061796d656e74496420616c726561647920757365640000000000000000000081525060200191505060405180910390fd5b8b8160000181905550338160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508a8160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508981600301819055508281600401819055508181600501819055504281600601819055508881600701819055508681600801819055508a73ffffffffffffffffffffffffffffffffffffffff166323b872dd33308d6040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050602060405180830381600087803b15801561187657600080fd5b505af115801561188a573d6000803e3d6000fd5b505050506040513d60208110156118a057600080fd5b8101908080519060200190929190505050611923576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f5472616e7366657220756e7375636365737366756c2e0000000000000000000081525060200191505060405180910390fd5b8a73ffffffffffffffffffffffffffffffffffffffff168c3373ffffffffffffffffffffffffffffffffffffffff167f0fc2463e82c3b8a7868e75b68a76a144816d772687e5b09f45c02db37eedf4f68d8c8c604051808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a460019550505050506000548114611a44576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b509695505050505050565b611a576120a5565b611ac9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a36000600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600060016000808282540192505081905550600080549050611baa612b6f565b600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180610120016040529081600082015481526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600382015481526020016004820154815260200160058201548152602001600682015481526020016007820154815260200160088201548152505090503373ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff1614611d71576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526035815260200180612d656035913960400191505060405180910390fd5b611d8c8160e001518260c001516128d990919063ffffffff16565b421015611de4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180612c62602a913960400191505060405180910390fd5b611ded84612476565b806040015173ffffffffffffffffffffffffffffffffffffffff1663a9059cbb3383606001516040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b158015611e7c57600080fd5b505af1158015611e90573d6000803e3d6000fd5b505050506040513d6020811015611ea657600080fd5b8101908080519060200190929190505050611f29576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f5472616e73666572206e6f74207375636365737366756c2e000000000000000081525060200191505060405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff1682600001517f6c464fad8039e6f09ec3a57a29f132cf2573d166833256960e2407eefff8f592846060015188604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a46001925050600054811461204f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b50919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166120e8612961565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b6004602052816000526040600020818154811061211d57fe5b906000526020600020016000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6121576120a5565b6121c9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561226c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b600160149054906101000a900460ff1615612376576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b60018060146101000a81548160ff02191690831515021790555061239933612969565b6123a28161214f565b50565b600560205281600052604060002081815481106123be57fe5b906000526020600020016000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6123f86120a5565b61246a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61247381612969565b50565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000600460008360000154815260200190815260200160002090506000600560008460010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508260050154600360008460018680549050038154811061255757fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060050181905550816001838054905003815481106125d657fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168284600501548154811061261157fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506126716001838054905061288f90919063ffffffff16565b828161267d9190612bea565b508260040154600360008360018580549050038154811061269a57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600401819055508060018280549050038154811061271957fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168184600401548154811061275457fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506127b46001828054905061288f90919063ffffffff16565b81816127c09190612bea565b50600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000808201600090556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556002820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600382016000905560048201600090556005820160009055600682016000905560078201600090556008820160009055505050505050565b60006128d183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612aaf565b905092915050565b600080828401905083811015612957576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156129ef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180612c3c6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000838311158290612b5c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612b21578082015181840152602081019050612b06565b50505050905090810190601f168015612b4e5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b60405180610120016040528060008019168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff1681526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b815481835581811115612c1157818360005260206000209182019101612c109190612c16565b5b505050565b612c3891905b80821115612c34576000816000905550600101612c1c565b5090565b9056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573735472616e73616374696f6e206e6f742072656465656d61626c6520666f722073656e646572207965742e496e76616c6964207072697661637920696e707574733a2043616e27742072657175697265206174746573746174696f6e73206966206e6f206964656e74696669657254686973206163636f756e7420646f6573206e6f74206861766520656e6f756768206174746573746174696f6e7320746f2077697468647261772074686973207061796d656e742e4661696c656420746f2070726f7665206f776e657273686970206f6620746865207769746864726177206b65796d696e4174746573746174696f6e73206c6172676572207468616e206c696d69744f6e6c792073656e646572206f66207061796d656e742063616e20617474656d707420746f207265766f6b65207061796d656e742ea265627a7a7231582014bea8adfa7c9641f4ee5d75bb244b834907615a206273301612d8107cac6fec64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f018": { + "code": "0x608060405234801561001057600080fd5b50600436106101e55760003560e01c8063808474f11161010f578063bb3ff745116100a2578063e94fd10911610071578063e94fd10914610a80578063ec68307214610aae578063f2fde38b14610b29578063fae8db0a14610b6d576101e5565b8063bb3ff745146109b0578063cb0ec628146109f2578063df4da46114610a20578063e50e652d14610a3e576101e5565b80638f32d59b116100de5780638f32d59b146109005780639a7b3be7146109225780639b2b592f14610940578063a69257f314610982576101e5565b8063808474f1146107ab57806387ee8a0f146107c95780638a883626146107e75780638da5cb5b146108b6576101e5565b806352bed4d71161018757806367960e911161015657806367960e9114610696578063715018a6146107655780637385e5da1461076f5780637877a7971461078d576101e5565b806352bed4d7146105a157806354255be0146105bf5780635d180adb146105f2578063615688281461066a576101e5565b806325eb315d116101c357806325eb315d146104045780632d7aa82b146104305780633b1eb4bf146104905780634b2c2f44146104d2576101e5565b8063123633ea146101ea578063158ef93e1461025857806323f0ab651461027a575b600080fd5b6102166004803603602081101561020057600080fd5b8101908080359060200190929190505050610baf565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610260610d00565b604051808215151515815260200191505060405180910390f35b6103ea6004803603606081101561029057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001906401000000008111156102cd57600080fd5b8201836020820111156102df57600080fd5b8035906020019184600183028401116401000000008311171561030157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561036457600080fd5b82018360208201111561037657600080fd5b8035906020019184600183028401116401000000008311171561039857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610d13565b604051808215151515815260200191505060405180910390f35b61040c610ecc565b60405180848152602001838152602001828152602001935050505060405180910390f35b61048e600480360360c081101561044657600080fd5b81019080803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050610eed565b005b6104bc600480360360208110156104a657600080fd5b8101908080359060200190929190505050610fc2565b6040518082815260200191505060405180910390f35b61058b600480360360208110156104e857600080fd5b810190808035906020019064010000000081111561050557600080fd5b82018360208201111561051757600080fd5b8035906020019184600183028401116401000000008311171561053957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610fdc565b6040518082815260200191505060405180910390f35b6105a9611170565b6040518082815260200191505060405180910390f35b6105c76111d9565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b6106286004803603604081101561060857600080fd5b810190808035906020019092919080359060200190929190505050611200565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610672611352565b60405180848152602001838152602001828152602001935050505060405180910390f35b61074f600480360360208110156106ac57600080fd5b81019080803590602001906401000000008111156106c957600080fd5b8201836020820111156106db57600080fd5b803590602001918460018302840111640100000000831117156106fd57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061136a565b6040518082815260200191505060405180910390f35b61076d6114fe565b005b610777611637565b6040518082815260200191505060405180910390f35b610795611647565b6040518082815260200191505060405180910390f35b6107b361164d565b6040518082815260200191505060405180910390f35b6107d1611653565b6040518082815260200191505060405180910390f35b6108a0600480360360208110156107fd57600080fd5b810190808035906020019064010000000081111561081a57600080fd5b82018360208201111561082c57600080fd5b8035906020019184600183028401116401000000008311171561084e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061179a565b6040518082815260200191505060405180910390f35b6108be61192e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610908611957565b604051808215151515815260200191505060405180910390f35b61092a6119b5565b6040518082815260200191505060405180910390f35b61096c6004803603602081101561095657600080fd5b81019080803590602001909291905050506119c5565b6040518082815260200191505060405180910390f35b6109ae6004803603602081101561099857600080fd5b8101908080359060200190929190505050611b0e565b005b6109f0600480360360608110156109c657600080fd5b81019080803590602001909291908035906020019092919080359060200190929190505050611bc9565b005b610a1e60048036036020811015610a0857600080fd5b8101908080359060200190929190505050611cac565b005b610a28611d67565b6040518082815260200191505060405180910390f35b610a6a60048036036020811015610a5457600080fd5b8101908080359060200190929190505050611ea3565b6040518082815260200191505060405180910390f35b610aac60048036036020811015610a9657600080fd5b8101908080359060200190929190505050611eee565b005b610b0c600480360360c0811015610ac457600080fd5b810190808035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190803590602001909291905050506120c9565b604051808381526020018281526020019250505060405180910390f35b610b6b60048036036020811015610b3f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506122dd565b005b610b9960048036036020811015610b8357600080fd5b8101908080359060200190929190505050612363565b6040518082815260200191505060405180910390f35b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16844360405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610c285780518252602082019150602081019050602083039250610c05565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114610c88576040519150601f19603f3d011682016040523d82523d6000602084013e610c8d565b606091505b50809350819250505080610cec576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612adb603d913960400191505060405180910390fd5b610cf78260006124ac565b92505050919050565b600060149054906101000a900460ff1681565b60008060fb73ffffffffffffffffffffffffffffffffffffffff16858585604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140183805190602001908083835b60208310610d9c5780518252602082019150602081019050602083039250610d79565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b60208310610ded5780518252602082019150602081019050602083039250610dca565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506040516020818303038152906040526040518082805190602001908083835b60208310610e565780518252602082019150602081019050602083039250610e33565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114610eb6576040519150601f19603f3d011682016040523d82523d6000602084013e610ebb565b606091505b505080915050809150509392505050565b60008060006001600001546001800154600160020154925092509250909192565b600060149054906101000a900460ff1615610f70576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff021916908315150217905550610f94336124c3565b610f9f868686611bc9565b610fa882611b0e565b610fb183611cac565b610fba81611eee565b505050505050565b6000610fd582610fd0611d67565b612607565b9050919050565b60006060600060f473ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310611031578051825260208201915060208101905060208303925061100e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106110985780518252602082019150602081019050602083039250611075565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146110f8576040519150601f19603f3d011682016040523d82523d6000602084013e6110fd565b606091505b5080935081925050508061115c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526038815260200180612aa36038913960400191505060405180910390fd5b61116782600061264f565b92505050919050565b600061117a6126f0565b905060008114156111d6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c2e6027913960400191505060405180910390fd5b90565b60008060008060016002600080839350829250819150809050935093509350935090919293565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16858560405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b602083106112795780518252602082019150602081019050602083039250611256565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146112d9576040519150601f19603f3d011682016040523d82523d6000602084013e6112de565b606091505b5080935081925050508061133d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180612b4d6036913960400191505060405180910390fd5b6113488260006124ac565b9250505092915050565b60068060000154908060010154908060020154905083565b60006060600060f673ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b602083106113bf578051825260208201915060208101905060208303925061139c565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106114265780518252602082019150602081019050602083039250611403565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611486576040519150601f19603f3d011682016040523d82523d6000602084013e61148b565b606091505b508093508192505050806114ea576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180612ce06023913960400191505060405180910390fd5b6114f582600061264f565b92505050919050565b611506611957565b611578576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600061164243611ea3565b905090565b60045481565b60055481565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1643604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106116c457805182526020820191506020810190506020830392506116a1565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611724576040519150601f19603f3d011682016040523d82523d6000602084013e611729565b606091505b50809350819250505080611788576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526035815260200180612b186035913960400191505060405180910390fd5b6117938260006124ac565b9250505090565b60006060600060f773ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b602083106117ef57805182526020820191506020810190506020830392506117cc565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106118565780518252602082019150602081019050602083039250611833565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146118b6576040519150601f19603f3d011682016040523d82523d6000602084013e6118bb565b606091505b5080935081925050508061191a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180612c816031913960400191505060405180910390fd5b6119258260006124ac565b92505050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661199961271e565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b60006119c043610fc2565b905090565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310611a365780518252602082019150602081019050602083039250611a13565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611a96576040519150601f19603f3d011682016040523d82523d6000602084013e611a9b565b606091505b50809350819250505080611afa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180612a4f602e913960400191505060405180910390fd5b611b058260006124ac565b92505050919050565b611b16611957565b611b88576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b806004819055507f55311ae9c14427b0863f38ed97a2a5944c50d824bbf692836246512e6822c3cf816040518082815260200191505060405180910390a150565b611bd1611957565b611c43576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b82600160000181905550816001800181905550806001600201819055507f809db05bd174a70ede53d18fc046c5ceb86ebffbb7746a0c8605772c97ef0d5283838360405180848152602001838152602001828152602001935050505060405180910390a1505050565b611cb4611957565b611d26576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b806005819055507fba9c6f28c7d9990745a5b5282dbee04706c28cae24a44736c3ba99b57c021f3e816040518082815260200191505060405180910390a150565b60006060600060f873ffffffffffffffffffffffffffffffffffffffff166040516020016040516020818303038152906040526040518082805190602001908083835b60208310611dcd5780518252602082019150602081019050602083039250611daa565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611e2d576040519150601f19603f3d011682016040523d82523d6000602084013e611e32565b606091505b50809350819250505080611e91576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180612c096025913960400191505060405180910390fd5b611e9c8260006124ac565b9250505090565b6000611ee76003611ed96002611ecb6002611ebd886119c5565b61272690919063ffffffff16565b6127ac90919063ffffffff16565b61283490919063ffffffff16565b9050919050565b611ef6611957565b611f68576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60038110158015611f7b57506102d08111155b611fd0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180612cb2602e913960400191505060405180910390fd5b611feb6002611fdd611d67565b61287e90919063ffffffff16565b811115612043576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603e815260200180612b83603e913960400191505060405180910390fd5b61204b6126f0565b60066000018190555061206f60016120616119b5565b6127ac90919063ffffffff16565b600660020181905550806006600101819055507f484a24d7faca8c4330aaf9ba5f131e6bd474ed6877a555511f39d16a1d71d15a81600660020154604051808381526020018281526020019250505060405180910390a150565b600080600087141580156120de575060008514155b612150576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f612064656e6f6d696e61746f72206973207a65726f000000000000000000000081525060200191505060405180910390fd5b6000806000606060fc73ffffffffffffffffffffffffffffffffffffffff168c8c8c8c8c8c6040516020018087815260200186815260200185815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040526040518082805190602001908083835b602083106121ea57805182526020820191506020810190506020830392506121c7565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461224a576040519150601f19603f3d011682016040523d82523d6000602084013e61224f565b606091505b508092508193505050816122ae576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612be26027913960400191505060405180910390fd5b6122b98160006124ac565b93506122c68160206124ac565b925083839550955050505050965096945050505050565b6122e5611957565b612357576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b612360816124c3565b50565b60006060600060f573ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106123d457805182526020820191506020810190506020830392506123b1565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612434576040519150601f19603f3d011682016040523d82523d6000602084013e612439565b606091505b50809350819250505080612498576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180612c55602c913960400191505060405180910390fd5b6124a382600061264f565b92505050919050565b60006124b8838361264f565b60001c905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612549576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180612a7d6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008082848161261357fe5b049050600083858161262157fe5b0614156126315780915050612649565b6126456001826127ac90919063ffffffff16565b9150505b92915050565b60006126656020836127ac90919063ffffffff16565b835110156126db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f736c6963696e67206f7574206f662072616e676500000000000000000000000081525060200191505060405180910390fd5b60006020830184015190508091505092915050565b60006006600201546127006119b5565b1061271257600660010154905061271b565b60066000015490505b90565b600033905090565b60008083141561273957600090506127a6565b600082840290508284828161274a57fe5b04146127a1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612bc16021913960400191505060405180910390fd5b809150505b92915050565b60008082840190508381101561282a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600061287683836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506128c8565b905092915050565b60006128c083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061298e565b905092915050565b60008083118290612974576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561293957808201518184015260208101905061291e565b50505050905090810190601f1680156129665780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161298057fe5b049050809150509392505050565b6000838311158290612a3b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612a005780820151818401526020810190506129e5565b50505050905090810190601f168015612a2d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838503905080915050939250505056fe6572726f722063616c6c696e67206e756d62657256616c696461746f7273496e53657420707265636f6d70696c654f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573736572726f722063616c6c696e672067657456657269666965645365616c4269746d617046726f6d48656164657220707265636f6d70696c656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e67206e756d62657256616c696461746f7273496e43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d53657420707265636f6d70696c65557074696d654c6f6f6b6261636b57696e646f77206d75737420626520736d616c6c6572206f7220657175616c20746f2065706f636853697a65202d2032536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f776572726f722063616c6c696e67206672616374696f6e4d756c45787020707265636f6d70696c656572726f722063616c6c696e672067657445706f636853697a6520707265636f6d70696c65557074696d654c6f6f6b6261636b57696e646f77206973206e6f7420696e697469616c697a65646572726f722063616c6c696e6720676574506172656e745365616c4269746d617020707265636f6d70696c656572726f722063616c6c696e6720676574426c6f636b4e756d62657246726f6d48656164657220707265636f6d70696c65557074696d654c6f6f6b6261636b57696e646f77206d7573742062652077697468696e20736166652072616e67656572726f722063616c6c696e67206861736848656164657220707265636f6d70696c65a265627a7a72315820d528087b4b46909f6228c0f51bddfa015c5983f842766d3ffe6b0100d67100b864736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f019": { + "code": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80638f32d59b116100715780638f32d59b146101bc578063a91ee0dc146101de578063c4d66de814610222578063d6cbae0214610266578063da853aed146103c1578063f2fde38b14610419576100a9565b8063158ef93e146100ae5780631c75f5c2146100d0578063715018a61461011e5780637b103999146101285780638da5cb5b14610172575b600080fd5b6100b661045d565b604051808215151515815260200191505060405180910390f35b61011c600480360360408110156100e657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610470565b005b6101266105d1565b005b61013061070a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61017a610730565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101c4610759565b604051808215151515815260200191505060405180910390f35b610220600480360360208110156101f457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107b7565b005b6102646004803603602081101561023857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061095b565b005b6103a76004803603608081101561027c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001906401000000008111156102b957600080fd5b8201836020820111156102cb57600080fd5b803590602001918460208302840111640100000000831117156102ed57600080fd5b90919293919293908035906020019064010000000081111561030e57600080fd5b82018360208201111561032057600080fd5b8035906020019184602083028401116401000000008311171561034257600080fd5b90919293919293908035906020019064010000000081111561036357600080fd5b82018360208201111561037557600080fd5b8035906020019184602083028401116401000000008311171561039757600080fd5b9091929391929390505050610a0e565b604051808215151515815260200191505060405180910390f35b610403600480360360208110156103d757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cf4565b6040518082815260200191505060405180910390f35b61045b6004803603602081101561042f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d3d565b005b600060149054906101000a900460ff1681565b610478610759565b6104ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61053c81600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610dc390919063ffffffff16565b600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff167f031b4696d0f74746e384670f56dacc22f2565b5637b797d8cf861dd5dfe73ed5826040518082815260200191505060405180910390a25050565b6105d9610759565b61064b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661079b610e4b565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b6107bf610759565b610831576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156108d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b600060149054906101000a900460ff16156109de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff021916908315150217905550610a0233610e53565b610a0b816107b7565b50565b600080600260008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905060008111610ac9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f4e6f2070656e616c747920676976656e20627920676f7665726e616e6365000081525060200191505060405180910390fd5b6000600260008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610b16610f97565b73ffffffffffffffffffffffffffffffffffffffff166331993fc98a836000808d8d8d8d8d8d6040518b63ffffffff1660e01b8152600401808b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018a81526020018973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200188815260200180602001806020018060200184810384528a8a82818152602001925060200280828437600081840152601f19601f8201169050808301925050508481038352888882818152602001925060200280828437600081840152601f19601f8201169050808301925050508481038252868682818152602001925060200280828437600081840152601f19601f8201169050808301925050509d5050505050505050505050505050600060405180830381600087803b158015610c7e57600080fd5b505af1158015610c92573d6000803e3d6000fd5b505050508873ffffffffffffffffffffffffffffffffffffffff167fd2b041bb62d3ac9e704aadbea1d3a21b6f5b4677d0766e204c2d30dfc1a022f9826040518082815260200191505060405180910390a26001915050979650505050505050565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b610d45610759565b610db7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b610dc081610e53565b50565b600080828401905083811015610e41576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610ed9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806110936026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4c6f636b6564476f6c6400000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561105257600080fd5b505afa158015611066573d6000803e3d6000fd5b505050506040513d602081101561107c57600080fd5b810190808051906020019092919050505090509056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373a265627a7a72315820eff0251a6457f78d3ea5680f8542740129a5f51ef92601859227facaa135612564736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f020": { + "code": "0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c806387ee8a0f116101045780639b2b592f116100a2578063e50e652d11610071578063e50e652d146110e2578063ec68307214611124578063f2fde38b1461119f578063fae8db0a146111e3576101cf565b80639b2b592f14611006578063a91ee0dc14611048578063bd0d99791461108c578063df4da461146110c4576101cf565b80638cc26910116100de5780638cc2691014610a7d5780638da5cb5b14610f7c5780638f32d59b14610fc65780639a7b3be714610fe8576101cf565b806387ee8a0f146108f857806388498aaf146109165780638a883626146109ae576101cf565b806354255be011610171578063715018a61161014b578063715018a61461082e5780637385e5da146108385780637a1ac61e146108565780637b103999146108ae576101cf565b806354255be0146106b45780635d180adb146106e757806367960e911461075f576101cf565b8063158ef93e116101ad578063158ef93e146103f757806323f0ab65146104195780633b1eb4bf146105a35780634b2c2f44146105e5576101cf565b806309f99447146101d45780630a05cd8414610364578063123633ea14610389575b600080fd5b61034e600480360360808110156101ea57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561023157600080fd5b82018360208201111561024357600080fd5b8035906020019184600183028401116401000000008311171561026557600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156102c857600080fd5b8201836020820111156102da57600080fd5b803590602001918460018302840111640100000000831117156102fc57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611225565b6040518082815260200191505060405180910390f35b61036c61161e565b604051808381526020018281526020019250505060405180910390f35b6103b56004803603602081101561039f57600080fd5b8101908080359060200190929190505050611630565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103ff611781565b604051808215151515815260200191505060405180910390f35b6105896004803603606081101561042f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561046c57600080fd5b82018360208201111561047e57600080fd5b803590602001918460018302840111640100000000831117156104a057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561050357600080fd5b82018360208201111561051557600080fd5b8035906020019184600183028401116401000000008311171561053757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611794565b604051808215151515815260200191505060405180910390f35b6105cf600480360360208110156105b957600080fd5b810190808035906020019092919050505061194d565b6040518082815260200191505060405180910390f35b61069e600480360360208110156105fb57600080fd5b810190808035906020019064010000000081111561061857600080fd5b82018360208201111561062a57600080fd5b8035906020019184600183028401116401000000008311171561064c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611967565b6040518082815260200191505060405180910390f35b6106bc611afb565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b61071d600480360360408110156106fd57600080fd5b810190808035906020019092919080359060200190929190505050611b22565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6108186004803603602081101561077557600080fd5b810190808035906020019064010000000081111561079257600080fd5b8201836020820111156107a457600080fd5b803590602001918460018302840111640100000000831117156107c657600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611c74565b6040518082815260200191505060405180910390f35b610836611e08565b005b610840611f41565b6040518082815260200191505060405180910390f35b6108ac6004803603606081101561086c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190505050611f51565b005b6108b6612010565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610900612036565b6040518082815260200191505060405180910390f35b61096c6004803603606081101561092c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019092919050505061217d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610a67600480360360208110156109c457600080fd5b81019080803590602001906401000000008111156109e157600080fd5b8201836020820111156109f357600080fd5b80359060200191846001830284011164010000000083111715610a1557600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506122ee565b6040518082815260200191505060405180910390f35b610f7a6004803603610160811015610a9457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190640100000000811115610adb57600080fd5b820183602082011115610aed57600080fd5b80359060200191846001830284011164010000000083111715610b0f57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610b7257600080fd5b820183602082011115610b8457600080fd5b80359060200191846001830284011164010000000083111715610ba657600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019092919080359060200190640100000000811115610c1357600080fd5b820183602082011115610c2557600080fd5b80359060200191846020830284011164010000000083111715610c4757600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610ca757600080fd5b820183602082011115610cb957600080fd5b80359060200191846020830284011164010000000083111715610cdb57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610d3b57600080fd5b820183602082011115610d4d57600080fd5b80359060200191846020830284011164010000000083111715610d6f57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610dcf57600080fd5b820183602082011115610de157600080fd5b80359060200191846020830284011164010000000083111715610e0357600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610e6357600080fd5b820183602082011115610e7557600080fd5b80359060200191846020830284011164010000000083111715610e9757600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610ef757600080fd5b820183602082011115610f0957600080fd5b80359060200191846020830284011164010000000083111715610f2b57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050612482565b005b610f846125ce565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610fce6125f7565b604051808215151515815260200191505060405180910390f35b610ff0612655565b6040518082815260200191505060405180910390f35b6110326004803603602081101561101c57600080fd5b8101908080359060200190929190505050612665565b6040518082815260200191505060405180910390f35b61108a6004803603602081101561105e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506127ae565b005b6110c2600480360360408110156110a257600080fd5b810190808035906020019092919080359060200190929190505050612952565b005b6110cc612a7b565b6040518082815260200191505060405180910390f35b61110e600480360360208110156110f857600080fd5b8101908080359060200190929190505050612bb7565b6040518082815260200191505060405180910390f35b611182600480360360c081101561113a57600080fd5b81019080803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050612c02565b604051808381526020018281526020019250505060405180910390f35b6111e1600480360360208110156111b557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612e16565b005b61120f600480360360208110156111f957600080fd5b8101908080359060200190929190505050612e9c565b6040518082815260200191505060405180910390f35b600061123082611c74565b61123984611c74565b1415611290576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061409a6021913960400191505060405180910390fd5b600061129b846122ee565b90506112a6836122ee565b81146112fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806140dc6027913960400191505060405180910390fd5b61130681612665565b851061137a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f4261642076616c696461746f7220696e6465780000000000000000000000000081525060200191505060405180910390fd5b6113848582611b22565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614611424576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f5761736e27742061207369676e6572207769746820676976656e20696e64657881525060200191505060405180910390fd5b600061142f85611967565b60001c9050600061143f85611967565b60001c90506000876001901b831614156114c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f4469646e2774207369676e20666972737420626c6f636b00000000000000000081525060200191505060405180910390fd5b6000876001901b8216141561153e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f4469646e2774207369676e207365636f6e6420626c6f636b000000000000000081525060200191505060405180910390fd5b61154783612bb7565b61155083612fe5565b10156115a7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180613f1d6025913960400191505060405180910390fd5b6115b083612bb7565b6115b982612fe5565b1015611610576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061417b6026913960400191505060405180910390fd5b829350505050949350505050565b60028060000154908060010154905082565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16844360405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b602083106116a95780518252602082019150602081019050602083039250611686565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611709576040519150601f19603f3d011682016040523d82523d6000602084013e61170e565b606091505b5080935081925050508061176d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180613ff2603d913960400191505060405180910390fd5b611778826000613050565b92505050919050565b600060149054906101000a900460ff1681565b60008060fb73ffffffffffffffffffffffffffffffffffffffff16858585604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140183805190602001908083835b6020831061181d57805182526020820191506020810190506020830392506117fa565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b6020831061186e578051825260208201915060208101905060208303925061184b565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506040516020818303038152906040526040518082805190602001908083835b602083106118d757805182526020820191506020810190506020830392506118b4565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611937576040519150601f19603f3d011682016040523d82523d6000602084013e61193c565b606091505b505080915050809150509392505050565b60006119608261195b612a7b565b613067565b9050919050565b60006060600060f473ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b602083106119bc5780518252602082019150602081019050602083039250611999565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310611a235780518252602082019150602081019050602083039250611a00565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611a83576040519150601f19603f3d011682016040523d82523d6000602084013e611a88565b606091505b50809350819250505080611ae7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526038815260200180613fba6038913960400191505060405180910390fd5b611af28260006130af565b92505050919050565b60008060008060018060016000839350829250819150809050935093509350935090919293565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16858560405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310611b9b5780518252602082019150602081019050602083039250611b78565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611bfb576040519150601f19603f3d011682016040523d82523d6000602084013e611c00565b606091505b50809350819250505080611c5f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260368152602001806140646036913960400191505060405180910390fd5b611c6a826000613050565b9250505092915050565b60006060600060f673ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310611cc95780518252602082019150602081019050602083039250611ca6565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310611d305780518252602082019150602081019050602083039250611d0d565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611d90576040519150601f19603f3d011682016040523d82523d6000602084013e611d95565b606091505b50809350819250505080611df4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806141d26023913960400191505060405180910390fd5b611dff8260006130af565b92505050919050565b611e106125f7565b611e82576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000611f4c43612bb7565b905090565b600060149054906101000a900460ff1615611fd4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff021916908315150217905550611ff833613150565b612001836127ae565b61200b8282612952565b505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1643604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106120a75780518252602082019150602081019050602083039250612084565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612107576040519150601f19603f3d011682016040523d82523d6000602084013e61210c565b606091505b5080935081925050508061216b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603581526020018061402f6035913960400191505060405180910390fd5b612176826000613050565b9250505090565b6000806121898461194d565b90506000811415612202576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f43616e6e6f7420736c617368206f6e2065706f6368203000000000000000000081525060200191505060405180910390fd5b61220a613294565b73ffffffffffffffffffffffffffffffffffffffff1663eb1d0b428661223a60018561338f90919063ffffffff16565b866040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838152602001828152602001935050505060206040518083038186803b1580156122a957600080fd5b505afa1580156122bd573d6000803e3d6000fd5b505050506040513d60208110156122d357600080fd5b81019080805190602001909291905050509150509392505050565b60006060600060f773ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b602083106123435780518252602082019150602081019050602083039250612320565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106123aa5780518252602082019150602081019050602083039250612387565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461240a576040519150601f19603f3d011682016040523d82523d6000602084013e61240f565b606091505b5080935081925050508061246e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260318152602001806141a16031913960400191505060405180910390fd5b612479826000613050565b92505050919050565b61248c8b8a6133d9565b6124968b896133d9565b60006124a48c8c8c8c611225565b905060006124b0613525565b73ffffffffffffffffffffffffffffffffffffffff166393c5c4878e6040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561252c57600080fd5b505afa158015612540573d6000803e3d6000fd5b505050506040513d602081101561255657600080fd5b8101908080519060200190929190505050905061257b8133848c8c8c8c8c8c8c613620565b818173ffffffffffffffffffffffffffffffffffffffff167fca7992de940988854714f90c0236621d5b6b850313f03eeea47f7028aaecea4060405160405180910390a350505050505050505050505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16612639613b3b565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b60006126604361194d565b905090565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106126d657805182526020820191506020810190506020830392506126b3565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612736576040519150601f19603f3d011682016040523d82523d6000602084013e61273b565b606091505b5080935081925050508061279a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180613f42602e913960400191505060405180910390fd5b6127a5826000613050565b92505050919050565b6127b66125f7565b612828576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156128cb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b61295a6125f7565b6129cc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b808211612a24576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180613f706024913960400191505060405180910390fd5b81600260000181905550806002600101819055507f716dc7c34384df36c6ccc5a2949f2ce9b019f5d4075ef39139a80038a4fdd1c38282604051808381526020018281526020019250505060405180910390a15050565b60006060600060f873ffffffffffffffffffffffffffffffffffffffff166040516020016040516020818303038152906040526040518082805190602001908083835b60208310612ae15780518252602082019150602081019050602083039250612abe565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612b41576040519150601f19603f3d011682016040523d82523d6000602084013e612b46565b606091505b50809350819250505080612ba5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061412a6025913960400191505060405180910390fd5b612bb0826000613050565b9250505090565b6000612bfb6003612bed6002612bdf6002612bd188612665565b613b4390919063ffffffff16565b613bc990919063ffffffff16565b613c5190919063ffffffff16565b9050919050565b60008060008714158015612c17575060008514155b612c89576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f612064656e6f6d696e61746f72206973207a65726f000000000000000000000081525060200191505060405180910390fd5b6000806000606060fc73ffffffffffffffffffffffffffffffffffffffff168c8c8c8c8c8c6040516020018087815260200186815260200185815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040526040518082805190602001908083835b60208310612d235780518252602082019150602081019050602083039250612d00565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612d83576040519150601f19603f3d011682016040523d82523d6000602084013e612d88565b606091505b50809250819350505081612de7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806141036027913960400191505060405180910390fd5b612df2816000613050565b9350612dff816020613050565b925083839550955050505050965096945050505050565b612e1e6125f7565b612e90576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b612e9981613150565b50565b60006060600060f573ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310612f0d5780518252602082019150602081019050602083039250612eea565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612f6d576040519150601f19603f3d011682016040523d82523d6000602084013e612f72565b606091505b50809350819250505080612fd1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018061414f602c913960400191505060405180910390fd5b612fdc8260006130af565b92505050919050565b60008060009050600083905060008090505b610100811015613045576001808316141561302357613020600184613bc990919063ffffffff16565b92505b600182901c915061303e600182613bc990919063ffffffff16565b9050612ff7565b508192505050919050565b600061305c83836130af565b60001c905092915050565b60008082848161307357fe5b049050600083858161308157fe5b06141561309157809150506130a9565b6130a5600182613bc990919063ffffffff16565b9150505b92915050565b60006130c5602083613bc990919063ffffffff16565b8351101561313b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f736c6963696e67206f7574206f662072616e676500000000000000000000000081525060200191505060405180910390fd5b60006020830184015190508091505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156131d6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180613f946026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f56616c696461746f727300000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561334f57600080fd5b505afa158015613363573d6000803e3d6000fd5b505050506040513d602081101561337957600080fd5b8101908080519060200190929190505050905090565b60006133d183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613c9b565b905092915050565b60006133e482611c74565b9050600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082815260200190815260200160002060009054906101000a900460ff16156134b7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f416c726561647920736c6173686564000000000000000000000000000000000081525060200191505060405180910390fd5b6001600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060006101000a81548160ff021916908315150217905550505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4163636f756e74730000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156135e057600080fd5b505afa1580156135f4573d6000803e3d6000fd5b505050506040513d602081101561360a57600080fd5b8101908080519060200190929190505050905090565b600061362a613d5b565b90508073ffffffffffffffffffffffffffffffffffffffff166331993fc98c6002600001548d6002600101548c8c8c6040518863ffffffff1660e01b8152600401808873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018781526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015613723578082015181840152602081019050613708565b50505050905001848103835286818151815260200191508051906020019060200280838360005b8381101561376557808201518184015260208101905061374a565b50505050905001848103825285818151815260200191508051906020019060200280838360005b838110156137a757808201518184015260208101905061378c565b505050509050019a5050505050505050505050600060405180830381600087803b1580156137d457600080fd5b505af11580156137e8573d6000803e3d6000fd5b5050505060006137f98c8b8b61217d565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561383257fe5b8173ffffffffffffffffffffffffffffffffffffffff166331993fc9826002600001548e6002600101548a8a8a6040518863ffffffff1660e01b8152600401808873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018781526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b8381101561392957808201518184015260208101905061390e565b50505050905001848103835286818151815260200191508051906020019060200280838360005b8381101561396b578082015181840152602081019050613950565b50505050905001848103825285818151815260200191508051906020019060200280838360005b838110156139ad578082015181840152602081019050613992565b505050509050019a5050505050505050505050600060405180830381600087803b1580156139da57600080fd5b505af11580156139ee573d6000803e3d6000fd5b5050505060006139fc613294565b90508073ffffffffffffffffffffffffffffffffffffffff1663e33301aa8e6040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050600060405180830381600087803b158015613a7d57600080fd5b505af1158015613a91573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff1663c22d3bba836040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050600060405180830381600087803b158015613b1457600080fd5b505af1158015613b28573d6000803e3d6000fd5b5050505050505050505050505050505050565b600033905090565b600080831415613b565760009050613bc3565b6000828402905082848281613b6757fe5b0414613bbe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806140bb6021913960400191505060405180910390fd5b809150505b92915050565b600080828401905083811015613c47576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b6000613c9383836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613e56565b905092915050565b6000838311158290613d48576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613d0d578082015181840152602081019050613cf2565b50505050905090810190601f168015613d3a5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4c6f636b6564476f6c6400000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015613e1657600080fd5b505afa158015613e2a573d6000803e3d6000fd5b505050506040513d6020811015613e4057600080fd5b8101908080519060200190929190505050905090565b60008083118290613f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613ec7578082015181840152602081019050613eac565b50505050905090810190601f168015613ef45780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581613f0e57fe5b04905080915050939250505056fe4e6f7420656e6f756768207369676e65727320696e2074686520666972737420626c6f636b6572726f722063616c6c696e67206e756d62657256616c696461746f7273496e53657420707265636f6d70696c6550656e616c74792068617320746f206265206c6172676572207468616e207265776172644f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573736572726f722063616c6c696e672067657456657269666965645365616c4269746d617046726f6d48656164657220707265636f6d70696c656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e67206e756d62657256616c696461746f7273496e43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d53657420707265636f6d70696c65426c6f636b20686173686573206861766520746f20626520646966666572656e74536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77426c6f636b2068656164657273206172652066726f6d20646966666572656e74206865696768746572726f722063616c6c696e67206672616374696f6e4d756c45787020707265636f6d70696c656572726f722063616c6c696e672067657445706f636853697a6520707265636f6d70696c656572726f722063616c6c696e6720676574506172656e745365616c4269746d617020707265636f6d70696c654e6f7420656e6f756768207369676e65727320696e20746865207365636f6e6420626c6f636b6572726f722063616c6c696e6720676574426c6f636b4e756d62657246726f6d48656164657220707265636f6d70696c656572726f722063616c6c696e67206861736848656164657220707265636f6d70696c65a265627a7a723158206e1fb68c9ffb55dbe49e846ba8a96d5d206f1d02e3a24009ca89b3d56b62429364736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f021": { + "code": "0x608060405234801561001057600080fd5b50600436106102275760003560e01c806387ee8a0f11610130578063a91ee0dc116100b8578063ec611ffc1161007c578063ec611ffc146113fe578063ec68307214611458578063f2fde38b146114d3578063fae8db0a14611517578063fafec0f61461155957610227565b8063a91ee0dc1461112a578063bd0d99791461116e578063df4da461146111a6578063e252e904146111c4578063e50e652d146113bc57610227565b80638f32d59b116100ff5780638f32d59b14610ff057806391275b4f146110125780639a7b3be71461107e5780639b2b592f1461109c578063a654a494146110de57610227565b806387ee8a0f14610e2157806388498aaf14610e3f5780638a88362614610ed75780638da5cb5b14610fa657610227565b80634b2c2f44116101b35780635d180adb116101825780635d180adb14610c6857806367960e9114610ce0578063715018a614610daf5780637385e5da14610db95780637b10399914610dd757610227565b80634b2c2f4414610ad65780634d643e1714610ba55780634ec81af114610bd357806354255be014610c3557610227565b80631bf0925b116101fa5780631bf0925b14610844578063222d6b9f1461089457806323f0ab65146108ec5780633b1eb4bf14610a765780634227d97114610ab857610227565b80630a05cd841461022c578063123633ea14610251578063158ef93e146102bf578063190ad68b146102e1575b600080fd5b6102346115a5565b604051808381526020018281526020019250505060405180910390f35b61027d6004803603602081101561026757600080fd5b81019080803590602001909291905050506115b7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102c7611708565b604051808215151515815260200191505060405180910390f35b61084260048036036101408110156102f857600080fd5b810190808035906020019064010000000081111561031557600080fd5b82018360208201111561032757600080fd5b8035906020019184602083028401116401000000008311171561034957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156103a957600080fd5b8201836020820111156103bb57600080fd5b803590602001918460208302840111640100000000831117156103dd57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561043d57600080fd5b82018360208201111561044f57600080fd5b8035906020019184602083028401116401000000008311171561047157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190929190803590602001906401000000008111156104db57600080fd5b8201836020820111156104ed57600080fd5b8035906020019184602083028401116401000000008311171561050f57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561056f57600080fd5b82018360208201111561058157600080fd5b803590602001918460208302840111640100000000831117156105a357600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561060357600080fd5b82018360208201111561061557600080fd5b8035906020019184602083028401116401000000008311171561063757600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561069757600080fd5b8201836020820111156106a957600080fd5b803590602001918460208302840111640100000000831117156106cb57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561072b57600080fd5b82018360208201111561073d57600080fd5b8035906020019184602083028401116401000000008311171561075f57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156107bf57600080fd5b8201836020820111156107d157600080fd5b803590602001918460208302840111640100000000831117156107f357600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929050505061171b565b005b61087a6004803603604081101561085a57600080fd5b8101908080359060200190929190803590602001909291905050506119c0565b604051808215151515815260200191505060405180910390f35b6108d6600480360360208110156108aa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611a32565b6040518082815260200191505060405180910390f35b610a5c6004803603606081101561090257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561093f57600080fd5b82018360208201111561095157600080fd5b8035906020019184600183028401116401000000008311171561097357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156109d657600080fd5b8201836020820111156109e857600080fd5b80359060200191846001830284011164010000000083111715610a0a57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611a4a565b604051808215151515815260200191505060405180910390f35b610aa260048036036020811015610a8c57600080fd5b8101908080359060200190929190505050611c03565b6040518082815260200191505060405180910390f35b610ac0611c1d565b6040518082815260200191505060405180910390f35b610b8f60048036036020811015610aec57600080fd5b8101908080359060200190640100000000811115610b0957600080fd5b820183602082011115610b1b57600080fd5b80359060200191846001830284011164010000000083111715610b3d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611c23565b6040518082815260200191505060405180910390f35b610bd160048036036020811015610bbb57600080fd5b8101908080359060200190929190505050611db7565b005b610c3360048036036080811015610be957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019092919080359060200190929190505050611ecc565b005b610c3d611f95565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b610c9e60048036036040811015610c7e57600080fd5b810190808035906020019092919080359060200190929190505050611fbc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610d9960048036036020811015610cf657600080fd5b8101908080359060200190640100000000811115610d1357600080fd5b820183602082011115610d2557600080fd5b80359060200191846001830284011164010000000083111715610d4757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061210e565b6040518082815260200191505060405180910390f35b610db76122a2565b005b610dc16123db565b6040518082815260200191505060405180910390f35b610ddf6123eb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610e29612411565b6040518082815260200191505060405180910390f35b610e9560048036036060811015610e5557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190505050612558565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610f9060048036036020811015610eed57600080fd5b8101908080359060200190640100000000811115610f0a57600080fd5b820183602082011115610f1c57600080fd5b80359060200191846001830284011164010000000083111715610f3e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506126c9565b6040518082815260200191505060405180910390f35b610fae61285d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610ff8612886565b604051808215151515815260200191505060405180910390f35b6110686004803603606081101561102857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291905050506128e4565b6040518082815260200191505060405180910390f35b611086612916565b6040518082815260200191505060405180910390f35b6110c8600480360360208110156110b257600080fd5b8101908080359060200190929190505050612926565b6040518082815260200191505060405180910390f35b611114600480360360408110156110f457600080fd5b810190808035906020019092919080359060200190929190505050612a6f565b6040518082815260200191505060405180910390f35b61116c6004803603602081101561114057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612c84565b005b6111a46004803603604081101561118457600080fd5b810190808035906020019092919080359060200190929190505050612e28565b005b6111ae612f51565b6040518082815260200191505060405180910390f35b6113a2600480360360608110156111da57600080fd5b81019080803590602001906401000000008111156111f757600080fd5b82018360208201111561120957600080fd5b8035906020019184602083028401116401000000008311171561122b57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561128b57600080fd5b82018360208201111561129d57600080fd5b803590602001918460208302840111640100000000831117156112bf57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561131f57600080fd5b82018360208201111561133157600080fd5b8035906020019184602083028401116401000000008311171561135357600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929050505061308d565b604051808215151515815260200191505060405180910390f35b6113e8600480360360208110156113d257600080fd5b8101908080359060200190929190505050613575565b6040518082815260200191505060405180910390f35b61143e6004803603606081101561141457600080fd5b810190808035906020019092919080359060200190929190803590602001909291905050506135c0565b604051808215151515815260200191505060405180910390f35b6114b6600480360360c081101561146e57600080fd5b810190808035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190803590602001909291905050506136fa565b604051808381526020018281526020019250505060405180910390f35b611515600480360360208110156114e957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061390e565b005b6115436004803603602081101561152d57600080fd5b8101908080359060200190929190505050613994565b6040518082815260200191505060405180910390f35b61158f6004803603604081101561156f57600080fd5b810190808035906020019092919080359060200190929190505050613add565b6040518082815260200191505060405180910390f35b60028060000154908060010154905082565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16844360405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310611630578051825260208201915060208101905060208303925061160d565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611690576040519150601f19603f3d011682016040523d82523d6000602084013e611695565b606091505b508093508192505050806116f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180614da0603d913960400191505060405180910390fd5b6116ff826000613c2a565b92505050919050565b600060149054906101000a900460ff1681565b60008a60008151811061172a57fe5b6020026020010151905060008a61174c60018d51613c4190919063ffffffff16565b8151811061175657fe5b60200260200101519050600654611789600161177b8585613c4190919063ffffffff16565b613c8b90919063ffffffff16565b10156117e0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526039815260200180614cbe6039913960400191505060405180910390fd5b60006118008b6000815181106117f257fe5b602002602001015184613d13565b9050600460008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548311611899576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526050815260200180614cf76050913960600191505060405180910390fd5b6118a48d8d8d61308d565b611916576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260088152602001807f6e6f7420646f776e00000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b81600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061196c8133858d8d8d8d8d8d8d613de5565b81838273ffffffffffffffffffffffffffffffffffffffff167f229d63d990a0f1068a86ee5bdce0b23fe156ff5d5174cc634d5da8ed3618e0c960405160405180910390a450505050505050505050505050565b60008060001b600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008581526020019081526020016000206000848152602001908152602001600020541415905092915050565b60046020528060005260406000206000915090505481565b60008060fb73ffffffffffffffffffffffffffffffffffffffff16858585604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140183805190602001908083835b60208310611ad35780518252602082019150602081019050602083039250611ab0565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b60208310611b245780518252602082019150602081019050602083039250611b01565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506040516020818303038152906040526040518082805190602001908083835b60208310611b8d5780518252602082019150602081019050602083039250611b6a565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611bed576040519150601f19603f3d011682016040523d82523d6000602084013e611bf2565b606091505b505080915050809150509392505050565b6000611c1682611c11612f51565b614300565b9050919050565b60065481565b60006060600060f473ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310611c785780518252602082019150602081019050602083039250611c55565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310611cdf5780518252602082019150602081019050602083039250611cbc565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114611d3f576040519150601f19603f3d011682016040523d82523d6000602084013e611d44565b606091505b50809350819250505080611da3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526038815260200180614d476038913960400191505060405180910390fd5b611dae826000614348565b92505050919050565b611dbf612886565b611e31576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000811415611e8b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180614b886021913960400191505060405180910390fd5b806006819055507fc3293b70d45615822039f6f13747ece88efbbb4e645c42070413a6c3fd21d771816040518082815260200191505060405180910390a150565b600060149054906101000a900460ff1615611f4f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff021916908315150217905550611f73336143e9565b611f7c84612c84565b611f868383612e28565b611f8f81611db7565b50505050565b60008060008060026000806000839350829250819150809050935093509350935090919293565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16858560405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b602083106120355780518252602082019150602081019050602083039250612012565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612095576040519150601f19603f3d011682016040523d82523d6000602084013e61209a565b606091505b508093508192505050806120f9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180614e8d6036913960400191505060405180910390fd5b612104826000613c2a565b9250505092915050565b60006060600060f673ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b602083106121635780518252602082019150602081019050602083039250612140565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106121ca57805182526020820191506020810190506020830392506121a7565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461222a576040519150601f19603f3d011682016040523d82523d6000602084013e61222f565b606091505b5080935081925050508061228e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806150ba6023913960400191505060405180910390fd5b612299826000614348565b92505050919050565b6122aa612886565b61231c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60006123e643613575565b905090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1643604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310612482578051825260208201915060208101905060208303925061245f565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146124e2576040519150601f19603f3d011682016040523d82523d6000602084013e6124e7565b606091505b50809350819250505080612546576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526035815260200180614ddd6035913960400191505060405180910390fd5b612551826000613c2a565b9250505090565b60008061256484611c03565b905060008114156125dd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f43616e6e6f7420736c617368206f6e2065706f6368203000000000000000000081525060200191505060405180910390fd5b6125e561452d565b73ffffffffffffffffffffffffffffffffffffffff1663eb1d0b4286612615600185613c4190919063ffffffff16565b866040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838152602001828152602001935050505060206040518083038186803b15801561268457600080fd5b505afa158015612698573d6000803e3d6000fd5b505050506040513d60208110156126ae57600080fd5b81019080805190602001909291905050509150509392505050565b60006060600060f773ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b6020831061271e57805182526020820191506020810190506020830392506126fb565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106127855780518252602082019150602081019050602083039250612762565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146127e5576040519150601f19603f3d011682016040523d82523d6000602084013e6127ea565b606091505b50809350819250505080612849576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180614fec6031913960400191505060405180910390fd5b612854826000613c2a565b92505050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166128c8614628565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b600560205282600052604060002060205281600052604060002060205280600052604060002060009250925050505481565b600061292143611c03565b905090565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106129975780518252602082019150602081019050602083039250612974565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146129f7576040519150601f19603f3d011682016040523d82523d6000602084013e6129fc565b606091505b50809350819250505080612a5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180614ba9602e913960400191505060405180910390fd5b612a66826000613c2a565b92505050919050565b600082821015612aca576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180614f306031913960400191505060405180910390fd5b6000612ae0600243613c4190919063ffffffff16565b905080831115612b3b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260368152602001806150846036913960400191505060405180910390fd5b6000612b45612f51565b9050612b5b60048261463090919063ffffffff16565b612b6e8643613c4190919063ffffffff16565b10612bc4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061501d6036913960400191505060405180910390fd5b612bce8482614300565b612bd88683614300565b14612c2e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260318152602001806150536031913960400191505060405180910390fd5b6000808690505b858111612c7757612c58612c53600183613c8b90919063ffffffff16565b613994565b82179150612c70600182613c8b90919063ffffffff16565b9050612c35565b5080935050505092915050565b612c8c612886565b612cfe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612da1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b612e30612886565b612ea2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b808211612efa576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180614bd76024913960400191505060405180910390fd5b81600260000181905550806002600101819055507f716dc7c34384df36c6ccc5a2949f2ce9b019f5d4075ef39139a80038a4fdd1c38282604051808381526020018281526020019250505060405180910390a15050565b60006060600060f873ffffffffffffffffffffffffffffffffffffffff166040516020016040516020818303038152906040526040518082805190602001908083835b60208310612fb75780518252602082019150602081019050602083039250612f94565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613017576040519150601f19603f3d011682016040523d82523d6000602084013e61301c565b606091505b5080935081925050508061307b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180614f0b6025913960400191505060405180910390fd5b613086826000613c2a565b9250505090565b600080845111613105576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f7265717569726573206174206c65617374206f6e6520696e74657276616c000081525060200191505060405180910390fd5b825184511461315f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526033815260200180614c4a6033913960400191505060405180910390fd5b60008251116131b9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180614d7f6021913960400191505060405180910390fd5b60006131c3612f51565b9050600080905060008090505b86518110156135665760008111156134f7578681815181106131ee57fe5b60200260200101518761320b600184613c4190919063ffffffff16565b8151811061321557fe5b602002602001015110613273576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526041815260200180614c7d6041913960600191505060405180910390fd5b6132ad60018761328d600185613c4190919063ffffffff16565b8151811061329757fe5b6020026020010151613c8b90919063ffffffff16565b8782815181106132b957fe5b60200260200101511115613318576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526051815260200180614e126051913960600191505060405180910390fd5b85818151811061332457fe5b602002602001015186613341600184613c4190919063ffffffff16565b8151811061334b57fe5b6020026020010151106133a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180614f83603d913960400191505060405180910390fd5b60016133d1848984815181106133bb57fe5b60200260200101516146b690919063ffffffff16565b14156134f65761341a856133ef600185613c8b90919063ffffffff16565b815181106133f957fe5b602002602001015188838151811061340d57fe5b6020026020010151613d13565b73ffffffffffffffffffffffffffffffffffffffff1661347386848151811061343f57fe5b602002602001015161346e60018b868151811061345857fe5b6020026020010151613c4190919063ffffffff16565b613d13565b73ffffffffffffffffffffffffffffffffffffffff16146134df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180614e63602a913960400191505060405180910390fd5b6134f3600183613c8b90919063ffffffff16565b91505b5b61353b87828151811061350657fe5b602002602001015187838151811061351a57fe5b602002602001015187858151811061352e57fe5b60200260200101516135c0565b61354b576000935050505061356e565b61355f600182613c8b90919063ffffffff16565b90506131d0565b506001925050505b9392505050565b60006135b960036135ab600261359d600261358f88612926565b61463090919063ffffffff16565b613c8b90919063ffffffff16565b61470090919063ffffffff16565b9050919050565b60006135cb84612926565b8210613622576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180614f616022913960400191505060405180910390fd5b61362c84846119c0565b613681576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526029815260200180614bfb6029913960400191505060405180910390fd5b6000801b826001901b60001b600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000878152602001908152602001600020600086815260200190815260200160002054161490509392505050565b6000806000871415801561370f575060008514155b613781576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f612064656e6f6d696e61746f72206973207a65726f000000000000000000000081525060200191505060405180910390fd5b6000806000606060fc73ffffffffffffffffffffffffffffffffffffffff168c8c8c8c8c8c6040516020018087815260200186815260200185815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040526040518082805190602001908083835b6020831061381b57805182526020820191506020810190506020830392506137f8565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461387b576040519150601f19603f3d011682016040523d82523d6000602084013e613880565b606091505b508092508193505050816138df576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180614ee46027913960400191505060405180910390fd5b6138ea816000613c2a565b93506138f7816020613c2a565b925083839550955050505050965096945050505050565b613916612886565b613988576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b613991816143e9565b50565b60006060600060f573ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310613a0557805182526020820191506020810190506020830392506139e2565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613a65576040519150601f19603f3d011682016040523d82523d6000602084013e613a6a565b606091505b50809350819250505080613ac9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180614fc0602c913960400191505060405180910390fd5b613ad4826000614348565b92505050919050565b6000613ae983836119c0565b15613b5c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6269746d617020616c726561647920736574000000000000000000000000000081525060200191505060405180910390fd5b6000613b688484612a6f565b905080600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600086815260200190815260200160002060008581526020019081526020016000208190555082843373ffffffffffffffffffffffffffffffffffffffff167f0aa96aa275a5f936eed2a6a01f082594744dcc2510f575101366f8f479f03235846040518082815260200191505060405180910390a48091505092915050565b6000613c368383614348565b60001c905092915050565b6000613c8383836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061474a565b905092915050565b600080828401905083811015613d09576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b6000613d1d61480a565b73ffffffffffffffffffffffffffffffffffffffff166393c5c487613d428585611fbc565b6040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015613da257600080fd5b505afa158015613db6573d6000803e3d6000fd5b505050506040513d6020811015613dcc57600080fd5b8101908080519060200190929190505050905092915050565b6000613def614905565b90508073ffffffffffffffffffffffffffffffffffffffff166331993fc98c6002600001548d6002600101548c8c8c6040518863ffffffff1660e01b8152600401808873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018781526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015613ee8578082015181840152602081019050613ecd565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015613f2a578082015181840152602081019050613f0f565b50505050905001848103825285818151815260200191508051906020019060200280838360005b83811015613f6c578082015181840152602081019050613f51565b505050509050019a5050505050505050505050600060405180830381600087803b158015613f9957600080fd5b505af1158015613fad573d6000803e3d6000fd5b505050506000613fbe8c8b8b612558565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613ff757fe5b8173ffffffffffffffffffffffffffffffffffffffff166331993fc9826002600001548e6002600101548a8a8a6040518863ffffffff1660e01b8152600401808873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018781526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b838110156140ee5780820151818401526020810190506140d3565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015614130578082015181840152602081019050614115565b50505050905001848103825285818151815260200191508051906020019060200280838360005b83811015614172578082015181840152602081019050614157565b505050509050019a5050505050505050505050600060405180830381600087803b15801561419f57600080fd5b505af11580156141b3573d6000803e3d6000fd5b5050505060006141c161452d565b90508073ffffffffffffffffffffffffffffffffffffffff1663e33301aa8e6040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050600060405180830381600087803b15801561424257600080fd5b505af1158015614256573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff1663c22d3bba836040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050600060405180830381600087803b1580156142d957600080fd5b505af11580156142ed573d6000803e3d6000fd5b5050505050505050505050505050505050565b60008082848161430c57fe5b049050600083858161431a57fe5b06141561432a5780915050614342565b61433e600182613c8b90919063ffffffff16565b9150505b92915050565b600061435e602083613c8b90919063ffffffff16565b835110156143d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f736c6963696e67206f7574206f662072616e676500000000000000000000000081525060200191505060405180910390fd5b60006020830184015190508091505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561446f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180614c246026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f56616c696461746f727300000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156145e857600080fd5b505afa1580156145fc573d6000803e3d6000fd5b505050506040513d602081101561461257600080fd5b8101908080519060200190929190505050905090565b600033905090565b60008083141561464357600090506146b0565b600082840290508284828161465457fe5b04146146ab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180614ec36021913960400191505060405180910390fd5b809150505b92915050565b60006146f883836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f0000000000000000815250614a00565b905092915050565b600061474283836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614ac1565b905092915050565b60008383111582906147f7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156147bc5780820151818401526020810190506147a1565b50505050905090810190601f1680156147e95780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4163636f756e74730000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156148c557600080fd5b505afa1580156148d9573d6000803e3d6000fd5b505050506040513d60208110156148ef57600080fd5b8101908080519060200190929190505050905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4c6f636b6564476f6c6400000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156149c057600080fd5b505afa1580156149d4573d6000803e3d6000fd5b505050506040513d60208110156149ea57600080fd5b8101908080519060200190929190505050905090565b6000808314158290614aad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614a72578082015181840152602081019050614a57565b50505050905090810190601f168015614a9f5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50828481614ab757fe5b0690509392505050565b60008083118290614b6d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614b32578082015181840152602081019050614b17565b50505050905090810190601f168015614b5f5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581614b7957fe5b04905080915050939250505056fe736c61736861626c6520646f776e74696d652063616e6e6f74206265207a65726f6572726f722063616c6c696e67206e756d62657256616c696461746f7273496e53657420707265636f6d70696c6550656e616c74792068617320746f206265206c6172676572207468616e207265776172646269746d617020666f722073706563696669656420696e74657276616c206e6f7420796574207365744f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573737374617274426c6f636b7320616e6420656e64426c6f636b73206d7573742068617665207468652073616d65206c656e6774686561636820696e74657276616c206d75737420737461727420616674657220746865207374617274206f66207468652070726576696f757320696e74657276616c7468652070726f766964656420696e74657276616c73206d757374207370616e20736c61736861626c65446f776e74696d6520626c6f636b7363616e6e6f7420736c6173682076616c696461746f7220666f7220646f776e74696d6520666f722077686963682074686579206d617920616c72656164792068617665206265656e20736c61736865646572726f722063616c6c696e672067657456657269666965645365616c4269746d617046726f6d48656164657220707265636f6d70696c657265717569726573206174206c65617374206f6e65207369676e6572496e6465786572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e67206e756d62657256616c696461746f7273496e43757272656e7453657420707265636f6d70696c656561636820696e74657276616c206d757374207374617274206174206d6f7374206f6e6520626c6f636b2061667465722074686520656e64206f66207468652070726576696f757320696e74657276616c696e646963657320646f206e6f7420706f696e7420746f207468652073616d652076616c696461746f726572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d53657420707265636f6d70696c65536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f776572726f722063616c6c696e67206672616374696f6e4d756c45787020707265636f6d70696c656572726f722063616c6c696e672067657445706f636853697a6520707265636f6d70696c65656e64426c6f636b206d7573742062652067726561746572206f7220657175616c207468616e207374617274426c6f636b6261642076616c696461746f7220696e64657820617420737461727420626c6f636b6561636820696e74657276616c206d75737420656e642061667465722074686520656e64206f66207468652070726576696f757320696e74657276616c6572726f722063616c6c696e6720676574506172656e745365616c4269746d617020707265636f6d70696c656572726f722063616c6c696e6720676574426c6f636b4e756d62657246726f6d48656164657220707265636f6d70696c657374617274426c6f636b206d7573742062652077697468696e20342065706f636873206f66207468652063757272656e7420686561647374617274426c6f636b20616e6420656e64426c6f636b206d75737420626520696e207468652073616d652065706f6368746865207369676e6174757265206269746d617020666f7220656e64426c6f636b206973206e6f742079657420617661696c61626c656572726f722063616c6c696e67206861736848656164657220707265636f6d70696c65a265627a7a723158201f837e1a359a0fa0c4d6d6c7d906160a346902cfe73da57fe3b880e2b57bb89b64736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f022": { + "code": "0x6080604052600436106101665760003560e01c80639ace38c2116100d1578063ba51a6df1161008a578063d74f8edd11610064578063d74f8edd14610a2e578063dc8452cd14610a59578063e20056e614610a84578063ee22610b14610af557610166565b8063ba51a6df146108f4578063c01a8c841461092f578063c64274741461096a57610166565b80639ace38c2146105f7578063a0e67e2b146106f0578063a24efcdf1461075c578063a8abe69a14610787578063b5dc40c314610839578063b77bf600146108c957610166565b80633411c81c116101235780633411c81c1461039a578063547415251461040d5780635eae79591461046a5780637065cb4814610504578063784547a7146105555780638b51d13f146105a857610166565b8063025e7c27146101c0578063158ef93e1461023b578063173825d91461026a57806320ea8d86146102bb5780632e6c3721146102f65780632f54bf6e14610331575b60003411156101be573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b005b3480156101cc57600080fd5b506101f9600480360360208110156101e357600080fd5b8101908080359060200190929190505050610b30565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561024757600080fd5b50610250610b6c565b604051808215151515815260200191505060405180910390f35b34801561027657600080fd5b506102b96004803603602081101561028d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b7e565b005b3480156102c757600080fd5b506102f4600480360360208110156102de57600080fd5b8101908080359060200190929190505050610f1b565b005b34801561030257600080fd5b5061032f6004803603602081101561031957600080fd5b81019080803590602001909291905050506111dc565b005b34801561033d57600080fd5b506103806004803603602081101561035457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611347565b604051808215151515815260200191505060405180910390f35b3480156103a657600080fd5b506103f3600480360360408110156103bd57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611367565b604051808215151515815260200191505060405180910390f35b34801561041957600080fd5b506104546004803603604081101561043057600080fd5b81019080803515159060200190929190803515159060200190929190505050611396565b6040518082815260200191505060405180910390f35b34801561047657600080fd5b506105026004803603606081101561048d57600080fd5b81019080803590602001906401000000008111156104aa57600080fd5b8201836020820111156104bc57600080fd5b803590602001918460208302840111640100000000831117156104de57600080fd5b90919293919293908035906020019092919080359060200190929190505050611448565b005b34801561051057600080fd5b506105536004803603602081101561052757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061182b565b005b34801561056157600080fd5b5061058e6004803603602081101561057857600080fd5b8101908080359060200190929190505050611bd5565b604051808215151515815260200191505060405180910390f35b3480156105b457600080fd5b506105e1600480360360208110156105cb57600080fd5b8101908080359060200190929190505050611d66565b6040518082815260200191505060405180910390f35b34801561060357600080fd5b506106306004803603602081101561061a57600080fd5b8101908080359060200190929190505050611e4d565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b838110156106b2578082015181840152602081019050610697565b50505050905090810190601f1680156106df5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b3480156106fc57600080fd5b50610705611f42565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561074857808201518184015260208101905061072d565b505050509050019250505060405180910390f35b34801561076857600080fd5b50610771611fd0565b6040518082815260200191505060405180910390f35b34801561079357600080fd5b506107e2600480360360808110156107aa57600080fd5b810190808035906020019092919080359060200190929190803515159060200190929190803515159060200190929190505050611fd6565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561082557808201518184015260208101905061080a565b505050509050019250505060405180910390f35b34801561084557600080fd5b506108726004803603602081101561085c57600080fd5b8101908080359060200190929190505050612188565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156108b557808201518184015260208101905061089a565b505050509050019250505060405180910390f35b3480156108d557600080fd5b506108de6123e0565b6040518082815260200191505060405180910390f35b34801561090057600080fd5b5061092d6004803603602081101561091757600080fd5b81019080803590602001909291905050506123e6565b005b34801561093b57600080fd5b506109686004803603602081101561095257600080fd5b8101908080359060200190929190505050612551565b005b34801561097657600080fd5b50610a186004803603606081101561098d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156109d457600080fd5b8201836020820111156109e657600080fd5b80359060200191846001830284011164010000000083111715610a0857600080fd5b909192939192939050505061286d565b6040518082815260200191505060405180910390f35b348015610a3a57600080fd5b50610a436128d1565b6040518082815260200191505060405180910390f35b348015610a6557600080fd5b50610a6e6128d6565b6040518082815260200191505060405180910390f35b348015610a9057600080fd5b50610af360048036036040811015610aa757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506128dc565b005b348015610b0157600080fd5b50610b2e60048036036020811015610b1857600080fd5b8101908080359060200190929190505050612db7565b005b60048181548110610b3d57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900460ff1681565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610c02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806139716022913960400191505060405180910390fd5b80600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16610cc2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f6f776e657220646f6573206e6f7420657869737400000000000000000000000081525060200191505060405180910390fd5b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060008090505b610d3960016004805490506131ec90919063ffffffff16565b811015610e70578273ffffffffffffffffffffffffffffffffffffffff1660048281548110610d6457fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610e55576004610dc660016004805490506131ec90919063ffffffff16565b81548110610dd057fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660048281548110610e0857fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610e70565b610e6960018261323690919063ffffffff16565b9050610d20565b50610e8a60016004805490506131ec90919063ffffffff16565b600481610e97919061376a565b506004805490506005541115610eb657610eb56004805490506123e6565b5b6004805490506006541115610ed457610ed36004805490506111dc565b5b8173ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a25050565b33600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16610fdb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f6f776e657220646f6573206e6f7420657869737400000000000000000000000081525060200191505060405180910390fd5b81336002600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16611090576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061394a6027913960400191505060405180910390fd5b836001600082815260200190815260200160002060030160009054906101000a900460ff1615611128576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f7472616e73616374696f6e2077617320657865637574656420616c726561647981525060200191505060405180910390fd5b60006002600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35050505050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611260576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806139716022913960400191505060405180910390fd5b60048054905081603282111580156112785750818111155b8015611285575060008114155b8015611292575060008214155b611304576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c696420726571756972656d656e740000000000000000000000000081525060200191505060405180910390fd5b826006819055507fa07eff79ea50418b0e96ff7c01d65eb6c3a5a240ee91cd81c70c89503dd41239836040518082815260200191505060405180910390a1505050565b60036020528060005260406000206000915054906101000a900460ff1681565b60026020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600754811015611441578380156113d657506001600082815260200190815260200160002060030160009054906101000a900460ff16155b8061140a575082801561140957506001600082815260200190815260200160002060030160009054906101000a900460ff165b5b156114265761142360018361323690919063ffffffff16565b91505b61143a60018261323690919063ffffffff16565b905061139e565b5092915050565b6000809054906101000a900460ff16156114ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b60016000806101000a81548160ff0219169083151502179055508383905082603282111580156114fa5750818111155b8015611507575060008114155b8015611514575060008214155b611586576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c696420726571756972656d656e740000000000000000000000000081525060200191505060405180910390fd5b85859050836032821115801561159c5750818111155b80156115a9575060008114155b80156115b6575060008214155b611628576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c696420726571756972656d656e740000000000000000000000000081525060200191505060405180910390fd5b60008090505b8888905081101561180057600360008a8a8481811061164957fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161580156117105750600073ffffffffffffffffffffffffffffffffffffffff168989838181106116da57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b611765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180613993602c913960400191505060405180910390fd5b6001600360008b8b8581811061177757fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506117f960018261323690919063ffffffff16565b905061162e565b50878760049190611812929190613796565b5085600581905550846006819055505050505050505050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146118af576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806139716022913960400191505060405180910390fd5b80600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611970576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f6f776e657220616c72656164792065786973746564000000000000000000000081525060200191505060405180910390fd5b81600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611a14576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6164647265737320776173206e756c6c0000000000000000000000000000000081525060200191505060405180910390fd5b611a2d600160048054905061323690919063ffffffff16565b60065460328211158015611a415750818111155b8015611a4e575060008114155b8015611a5b575060008214155b611acd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c696420726571756972656d656e740000000000000000000000000081525060200191505060405180910390fd5b6001600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060048590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b6000806000905060008090505b600480549050811015611d5a5760026000858152602001908152602001600020600060048381548110611c1157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611ca057611c9d60018361323690919063ffffffff16565b91505b60003073ffffffffffffffffffffffffffffffffffffffff166001600087815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16149050808015611d17575060065483145b80611d2d575080158015611d2c575060055483145b5b15611d3e5760019350505050611d61565b50611d5360018261323690919063ffffffff16565b9050611be2565b5060009150505b919050565b600080600090505b600480549050811015611e475760026000848152602001908152602001600020600060048381548110611d9d57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611e2c57611e2960018361323690919063ffffffff16565b91505b611e4060018261323690919063ffffffff16565b9050611d6e565b50919050565b60016020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015490806002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611f255780601f10611efa57610100808354040283529160200191611f25565b820191906000526020600020905b815481529060010190602001808311611f0857829003601f168201915b5050505050908060030160009054906101000a900460ff16905084565b60606004805480602002602001604051908101604052809291908181526020018280548015611fc657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611f7c575b5050505050905090565b60065481565b60608060075460405190808252806020026020018201604052801561200a5781602001602082028038833980820191505090505b509050600080905060008090505b6007548110156120d45785801561205057506001600082815260200190815260200160002060030160009054906101000a900460ff16155b80612084575084801561208357506001600082815260200190815260200160002060030160009054906101000a900460ff165b5b156120b9578083838151811061209657fe5b6020026020010181815250506120b660018361323690919063ffffffff16565b91505b6120cd60018261323690919063ffffffff16565b9050612018565b6120e788886131ec90919063ffffffff16565b6040519080825280602002602001820160405280156121155781602001602082028038833980820191505090505b5093508790505b8681101561217d5782818151811061213057fe5b60200260200101518461214c8a846131ec90919063ffffffff16565b8151811061215657fe5b60200260200101818152505061217660018261323690919063ffffffff16565b905061211c565b505050949350505050565b6060806004805490506040519080825280602002602001820160405280156121bf5781602001602082028038833980820191505090505b509050600080905060008090505b60048054905081101561232457600260008681526020019081526020016000206000600483815481106121fc57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615612309576004818154811061228157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168383815181106122b857fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505061230660018361323690919063ffffffff16565b91505b61231d60018261323690919063ffffffff16565b90506121cd565b816040519080825280602002602001820160405280156123535781602001602082028038833980820191505090505b509350600090505b818110156123d85782818151811061236f57fe5b602002602001015184828151811061238357fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506123d160018261323690919063ffffffff16565b905061235b565b505050919050565b60075481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461246a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806139716022913960400191505060405180910390fd5b60048054905081603282111580156124825750818111155b801561248f575060008114155b801561249c575060008214155b61250e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f696e76616c696420726571756972656d656e740000000000000000000000000081525060200191505060405180910390fd5b826005819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a1505050565b33600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16612611576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f6f776e657220646f6573206e6f7420657869737400000000000000000000000081525060200191505060405180910390fd5b81600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156126eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f7472616e73616374696f6e20646f6573206e6f7420657869737400000000000081525060200191505060405180910390fd5b82336002600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156127a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b81526020018061391f602b913960400191505060405180910390fd5b60016002600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361285785611bd5565b156128665761286585612db7565b5b5050505050565b60006128be858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506132be565b90506128c981612551565b949350505050565b603281565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806139716022913960400191505060405180910390fd5b81600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16612a20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f6f776e657220646f6573206e6f7420657869737400000000000000000000000081525060200191505060405180910390fd5b81600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612ac4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6164647265737320776173206e756c6c0000000000000000000000000000000081525060200191505060405180910390fd5b82600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615612b85576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f6f776e657220616c72656164792065786973746564000000000000000000000081525060200191505060405180910390fd5b60008090505b600480549050811015612c79578573ffffffffffffffffffffffffffffffffffffffff1660048281548110612bbc57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612c5e578460048281548110612c1157fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550612c79565b612c7260018261323690919063ffffffff16565b9050612b8b565b506000600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b33600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16612e77576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f6f776e657220646f6573206e6f7420657869737400000000000000000000000081525060200191505060405180910390fd5b81336002600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16612f2c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061394a6027913960400191505060405180910390fd5b836001600082815260200190815260200160002060030160009054906101000a900460ff1615612fc4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f7472616e73616374696f6e2077617320657865637574656420616c726561647981525060200191505060405180910390fd5b612fcd85611bd5565b61303f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f5472616e73616374696f6e206e6f7420636f6e6669726d65642e00000000000081525060200191505060405180910390fd5b600060016000878152602001908152602001600020905060018160030160006101000a81548160ff02191690831515021790555060606131448260000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360010154846002018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561313a5780601f1061310f5761010080835404028352916020019161313a565b820191906000526020600020905b81548152906001019060200180831161311d57829003601f168201915b5050505050613498565b9050867f0c18aae526accb31b01cf9a15bdf435e70632ee31efc4c5c0752c4262ea45d2f826040518080602001828103825283818151815260200191508051906020019080838360005b838110156131a957808201518184015260208101905061318e565b50505050905090810190601f1680156131d65780820380516001836020036101000a031916815260200191505b509250505060405180910390a250505050505050565b600061322e83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061365f565b905092915050565b6000808284019050838110156132b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600083600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613364576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6164647265737320776173206e756c6c0000000000000000000000000000000081525060200191505060405180910390fd5b600754915060405180608001604052808673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001848152602001600015158152506001600084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550602082015181600101556040820151816002019080519060200190613423929190613836565b5060608201518160030160006101000a81548160ff02191690831515021790555090505061345d600160075461323690919063ffffffff16565b600781905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a2509392505050565b6060600082511115613520576134ad8461371f565b61351f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b5b600060608573ffffffffffffffffffffffffffffffffffffffff1685856040518082805190602001908083835b60208310613570578051825260208201915060208101905060208303925061354d565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146135d2576040519150601f19603f3d011682016040523d82523d6000602084013e6135d7565b606091505b50809250819350505081613653576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f5472616e73616374696f6e20657865637574696f6e206661696c65642e00000081525060200191505060405180910390fd5b80925050509392505050565b600083831115829061370c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156136d15780820151818401526020810190506136b6565b50505050905090810190601f1680156136fe5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561376157506000801b8214155b92505050919050565b8154818355818111156137915781836000526020600020918201910161379091906138b6565b5b505050565b828054828255906000526020600020908101928215613825579160200282015b8281111561382457823573ffffffffffffffffffffffffffffffffffffffff168260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906137b6565b5b50905061383291906138db565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061387757805160ff19168380011785556138a5565b828001600101855582156138a5579182015b828111156138a4578251825591602001919060010190613889565b5b5090506138b291906138b6565b5090565b6138d891905b808211156138d45760008160009055506001016138bc565b5090565b90565b61391b91905b8082111561391757600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016138e1565b5090565b9056fe7472616e73616374696f6e2077617320616c726561647920636f6e6669726d656420666f72206f776e65727472616e73616374696f6e20776173206e6f7420636f6e6669726d656420666f72206f776e65726d73672e73656e64657220776173206e6f74206d756c74697369672077616c6c65746f776e657220776173206e756c6c206f7220616c726561647920676976656e206f776e657220737461747573a265627a7a72315820e4ee801454d3ad10dfd0cced0d242c239e5f778e8309baf2b3f27ec918745be764736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f023": { + "code": "0x6080604052600436106104e15760003560e01c80637b10399911610281578063b15f0f581161015a578063cea69e74116100cc578063e50e652d11610085578063e50e652d146125a3578063ec683072146125f2578063ed3852741461267a578063f2fde38b146126fe578063fae8db0a1461274f578063ffea74c01461279e576104e1565b8063cea69e741461222e578063cf48eb9414612269578063d704f0c5146123f8578063da35c664146124f0578063df4da4611461251b578063e41db45514612546576104e1565b8063c1939b201161011e578063c1939b2014611f18578063c73a6d7814611ff8578063c7f758a81461204b578063c805956d14612147578063c8d8d2b514612187578063cd845a76146121c2576104e1565b8063b15f0f5814611dca578063b8f7700514611e05578063bbb2eab914611e30578063c0aee5f414611e9a578063c134b2fc14611ec5576104e1565b806398f42702116101f3578063a91ee0dc116101b7578063a91ee0dc14611c18578063aa2feb8314611c69578063ad78c10914611cb8578063add004df14611ce3578063af108a0e14611d32578063b0f9984214611d8f576104e1565b806398f4270214611ad95780639a6c3d8314611b285780639a7b3be714611b635780639b2b592f14611b8e5780639cb02dfc14611bdd576104e1565b80638da5cb5b116102455780638da5cb5b146119305780638e905ed6146119875780638f32d59b146119b25780638fcc9cfb146119e15780639381ab2514611a1c57806397b9eba614611a4b576104e1565b80637b103999146117485780638018556e1461179f57806381d4728d146117da57806387ee8a0f146118295780638a88362614611854576104e1565b80633fa5fed0116103be5780635f115a85116103305780636de8a63b116102e95780636de8a63b146115c95780636f2ab69314611635578063715018a6146116885780637385e5da1461169f57806377d26a2a146116ca5780637910867b146116f5576104e1565b80635f115a85146111865780635f8dd6491461120357806360b4d34d1461126c57806365bbdaa0146112d15780636643ac58146114b257806367960e91146114ed576104e1565b80635601eaea116103825780635601eaea14610f485780635733397814610fa5578063582ae53b1461100c5780635c759394146110695780635d180adb146110a45780635d35a3d914611129576104e1565b80633fa5fed014610d2957806341b3d18514610d9c57806345a7849914610dc75780634b2c2f4414610e2c57806354255be014610f08576104e1565b806323f0ab65116104575780633156560e1161041b5780633156560e14610bb5578063344944cf14610c065780633b1eb4bf14610c595780633bb0ed2b14610ca85780633ccfd60b14610cbf5780633db9dd9a14610cee576104e1565b806323f0ab65146109005780632762132114610a97578063283aaefb14610aea5780632c05235514610b4f57806330a095d014610b8a576104e1565b8063123633ea116104a9578063123633ea146107145780631374b22d1461078f578063141a8dd8146107e2578063152b483414610839578063158ef93e146108965780631c65bc61146108c5576104e1565b806301fce27e1461055c57806304acaec9146106105780630e0b78ae1461064b5780630f717e42146106b05780631201a0fb146106e9575b6000803690501461055a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f756e6b6e6f776e206d6574686f6400000000000000000000000000000000000081525060200191505060405180910390fd5b005b34801561056857600080fd5b506105716127c9565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156105b857808201518184015260208101905061059d565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156105fa5780820151818401526020810190506105df565b5050505090500194505050505060405180910390f35b34801561061c57600080fd5b506106496004803603602081101561063357600080fd5b810190808035906020019092919050505061298e565b005b34801561065757600080fd5b506106846004803603602081101561066e57600080fd5b8101908080359060200190929190505050612b66565b604051808415151515815260200183151515158152602001828152602001935050505060405180910390f35b3480156106bc57600080fd5b506106c5612bd7565b60405180848152602001838152602001828152602001935050505060405180910390f35b3480156106f557600080fd5b506106fe612bef565b6040518082815260200191505060405180910390f35b34801561072057600080fd5b5061074d6004803603602081101561073757600080fd5b8101908080359060200190929190505050612bf5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561079b57600080fd5b506107c8600480360360208110156107b257600080fd5b8101908080359060200190929190505050612d46565b604051808215151515815260200191505060405180910390f35b3480156107ee57600080fd5b506107f7612d6a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561084557600080fd5b5061087c6004803603604081101561085c57600080fd5b810190808035906020019092919080359060200190929190505050612d90565b604051808215151515815260200191505060405180910390f35b3480156108a257600080fd5b506108ab612db7565b604051808215151515815260200191505060405180910390f35b3480156108d157600080fd5b506108fe600480360360208110156108e857600080fd5b8101908080359060200190929190505050612dca565b005b34801561090c57600080fd5b50610a7d6004803603606081101561092357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561096057600080fd5b82018360208201111561097257600080fd5b8035906020019184600183028401116401000000008311171561099457600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156109f757600080fd5b820183602082011115610a0957600080fd5b80359060200191846001830284011164010000000083111715610a2b57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612f85565b604051808215151515815260200191505060405180910390f35b348015610aa357600080fd5b50610ad060048036036020811015610aba57600080fd5b810190808035906020019092919050505061313e565b604051808215151515815260200191505060405180910390f35b348015610af657600080fd5b50610b3960048036036020811015610b0d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613162565b6040518082815260200191505060405180910390f35b348015610b5b57600080fd5b50610b8860048036036020811015610b7257600080fd5b81019080803590602001909291905050506131ae565b005b348015610b9657600080fd5b50610b9f61333a565b6040518082815260200191505060405180910390f35b348015610bc157600080fd5b50610c0460048036036020811015610bd857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613347565b005b348015610c1257600080fd5b50610c3f60048036036020811015610c2957600080fd5b81019080803590602001909291905050506135af565b604051808215151515815260200191505060405180910390f35b348015610c6557600080fd5b50610c9260048036036020811015610c7c57600080fd5b81019080803590602001909291905050506135cb565b6040518082815260200191505060405180910390f35b348015610cb457600080fd5b50610cbd6135e5565b005b348015610ccb57600080fd5b50610cd46139d4565b604051808215151515815260200191505060405180910390f35b348015610cfa57600080fd5b50610d2760048036036020811015610d1157600080fd5b8101908080359060200190929190505050613c0b565b005b348015610d3557600080fd5b50610d8260048036036040811015610d4c57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613de3565b604051808215151515815260200191505060405180910390f35b348015610da857600080fd5b50610db1613e4e565b6040518082815260200191505060405180910390f35b348015610dd357600080fd5b50610e0060048036036020811015610dea57600080fd5b8101908080359060200190929190505050613e54565b604051808415151515815260200183151515158152602001828152602001935050505060405180910390f35b348015610e3857600080fd5b50610ef260048036036020811015610e4f57600080fd5b8101908080359060200190640100000000811115610e6c57600080fd5b820183602082011115610e7e57600080fd5b80359060200191846001830284011164010000000083111715610ea057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050613e98565b6040518082815260200191505060405180910390f35b348015610f1457600080fd5b50610f1d61402c565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b348015610f5457600080fd5b50610f8b60048036036040811015610f6b57600080fd5b810190808035906020019092919080359060200190929190505050614053565b604051808215151515815260200191505060405180910390f35b348015610fb157600080fd5b50610ff260048036036060811015610fc857600080fd5b8101908080359060200190929190803590602001909291908035906020019092919050505061423e565b604051808215151515815260200191505060405180910390f35b34801561101857600080fd5b506110456004803603602081101561102f57600080fd5b81019080803590602001909291905050506148d3565b6040518082600581111561105557fe5b60ff16815260200191505060405180910390f35b34801561107557600080fd5b506110a26004803603602081101561108c57600080fd5b810190808035906020019092919050505061496f565b005b3480156110b057600080fd5b506110e7600480360360408110156110c757600080fd5b810190808035906020019092919080359060200190929190505050614b47565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561113557600080fd5b5061116c6004803603604081101561114c57600080fd5b810190808035906020019092919080359060200190929190505050614c99565b604051808215151515815260200191505060405180910390f35b34801561119257600080fd5b506111df600480360360408110156111a957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050614f7e565b60405180848152602001838152602001828152602001935050505060405180910390f35b34801561120f57600080fd5b506112526004803603602081101561122657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061500e565b604051808215151515815260200191505060405180910390f35b34801561127857600080fd5b506112bb6004803603602081101561128f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506150ea565b6040518082815260200191505060405180910390f35b61149c600480360360a08110156112e757600080fd5b810190808035906020019064010000000081111561130457600080fd5b82018360208201111561131657600080fd5b8035906020019184602083028401116401000000008311171561133857600080fd5b90919293919293908035906020019064010000000081111561135957600080fd5b82018360208201111561136b57600080fd5b8035906020019184602083028401116401000000008311171561138d57600080fd5b9091929391929390803590602001906401000000008111156113ae57600080fd5b8201836020820111156113c057600080fd5b803590602001918460018302840111640100000000831117156113e257600080fd5b90919293919293908035906020019064010000000081111561140357600080fd5b82018360208201111561141557600080fd5b8035906020019184602083028401116401000000008311171561143757600080fd5b90919293919293908035906020019064010000000081111561145857600080fd5b82018360208201111561146a57600080fd5b8035906020019184600183028401116401000000008311171561148c57600080fd5b9091929391929390505050615102565b6040518082815260200191505060405180910390f35b3480156114be57600080fd5b506114eb600480360360208110156114d557600080fd5b810190808035906020019092919050505061547e565b005b3480156114f957600080fd5b506115b36004803603602081101561151057600080fd5b810190808035906020019064010000000081111561152d57600080fd5b82018360208201111561153f57600080fd5b8035906020019184600183028401116401000000008311171561156157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061562d565b6040518082815260200191505060405180910390f35b3480156115d557600080fd5b506115de6157c1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015611621578082015181840152602081019050611606565b505050509050019250505060405180910390f35b34801561164157600080fd5b5061166e6004803603602081101561165857600080fd5b8101908080359060200190929190505050615819565b604051808215151515815260200191505060405180910390f35b34801561169457600080fd5b5061169d615856565b005b3480156116ab57600080fd5b506116b461598f565b6040518082815260200191505060405180910390f35b3480156116d657600080fd5b506116df61599f565b6040518082815260200191505060405180910390f35b34801561170157600080fd5b5061172e6004803603602081101561171857600080fd5b81019080803590602001909291905050506159a5565b604051808215151515815260200191505060405180910390f35b34801561175457600080fd5b5061175d6159c9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156117ab57600080fd5b506117d8600480360360208110156117c257600080fd5b81019080803590602001909291905050506159ef565b005b3480156117e657600080fd5b50611813600480360360208110156117fd57600080fd5b8101908080359060200190929190505050615b7b565b6040518082815260200191505060405180910390f35b34801561183557600080fd5b5061183e615cd1565b6040518082815260200191505060405180910390f35b34801561186057600080fd5b5061191a6004803603602081101561187757600080fd5b810190808035906020019064010000000081111561189457600080fd5b8201836020820111156118a657600080fd5b803590602001918460018302840111640100000000831117156118c857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050615e18565b6040518082815260200191505060405180910390f35b34801561193c57600080fd5b50611945615fac565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561199357600080fd5b5061199c615fd5565b6040518082815260200191505060405180910390f35b3480156119be57600080fd5b506119c7615fdb565b604051808215151515815260200191505060405180910390f35b3480156119ed57600080fd5b50611a1a60048036036020811015611a0457600080fd5b8101908080359060200190929190505050616039565b005b348015611a2857600080fd5b50611a316161e2565b604051808215151515815260200191505060405180910390f35b348015611a5757600080fd5b50611ac360048036036040811015611a6e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050616667565b6040518082815260200191505060405180910390f35b348015611ae557600080fd5b50611b1260048036036020811015611afc57600080fd5b8101908080359060200190929190505050616683565b6040518082815260200191505060405180910390f35b348015611b3457600080fd5b50611b6160048036036020811015611b4b57600080fd5b810190808035906020019092919050505061679c565b005b348015611b6f57600080fd5b50611b7861694b565b6040518082815260200191505060405180910390f35b348015611b9a57600080fd5b50611bc760048036036020811015611bb157600080fd5b810190808035906020019092919050505061695b565b6040518082815260200191505060405180910390f35b348015611be957600080fd5b50611c1660048036036020811015611c0057600080fd5b8101908080359060200190929190505050616aa4565b005b348015611c2457600080fd5b50611c6760048036036020811015611c3b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050616c62565b005b348015611c7557600080fd5b50611ca260048036036020811015611c8c57600080fd5b8101908080359060200190929190505050616e06565b6040518082815260200191505060405180910390f35b348015611cc457600080fd5b50611ccd616e27565b6040518082815260200191505060405180910390f35b348015611cef57600080fd5b50611d1c60048036036020811015611d0657600080fd5b8101908080359060200190929190505050616e34565b6040518082815260200191505060405180910390f35b348015611d3e57600080fd5b50611d7560048036036040811015611d5557600080fd5b810190808035906020019092919080359060200190929190505050616e55565b604051808215151515815260200191505060405180910390f35b348015611d9b57600080fd5b50611dc860048036036020811015611db257600080fd5b81019080803590602001909291905050506172e8565b005b348015611dd657600080fd5b50611e0360048036036020811015611ded57600080fd5b81019080803590602001909291905050506174a3565b005b348015611e1157600080fd5b50611e1a61760f565b6040518082815260200191505060405180910390f35b348015611e3c57600080fd5b50611e8060048036036060811015611e5357600080fd5b810190808035906020019092919080359060200190929190803560ff16906020019092919050505061761f565b604051808215151515815260200191505060405180910390f35b348015611ea657600080fd5b50611eaf617d49565b6040518082815260200191505060405180910390f35b348015611ed157600080fd5b50611efe60048036036020811015611ee857600080fd5b8101908080359060200190929190505050617d4f565b604051808215151515815260200191505060405180910390f35b348015611f2457600080fd5b50611ff660048036036101a0811015611f3c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050617d73565b005b34801561200457600080fd5b506120316004803603602081101561201b57600080fd5b8101908080359060200190929190505050617ea5565b604051808215151515815260200191505060405180910390f35b34801561205757600080fd5b506120846004803603602081101561206e57600080fd5b8101908080359060200190929190505050617f43565b604051808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200185815260200184815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b838110156121085780820151818401526020810190506120ed565b50505050905090810190601f1680156121355780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b34801561215357600080fd5b5061215c618015565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b34801561219357600080fd5b506121c0600480360360208110156121aa57600080fd5b81019080803590602001909291905050506180b1565b005b3480156121ce57600080fd5b50612211600480360360208110156121e557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061823d565b604051808381526020018281526020019250505060405180910390f35b34801561223a57600080fd5b506122676004803603602081101561225157600080fd5b81019080803590602001909291905050506182bf565b005b34801561227557600080fd5b506123f6600480360360a081101561228c57600080fd5b81019080803590602001906401000000008111156122a957600080fd5b8201836020820111156122bb57600080fd5b803590602001918460208302840111640100000000831117156122dd57600080fd5b9091929391929390803590602001906401000000008111156122fe57600080fd5b82018360208201111561231057600080fd5b8035906020019184602083028401116401000000008311171561233257600080fd5b90919293919293908035906020019064010000000081111561235357600080fd5b82018360208201111561236557600080fd5b8035906020019184600183028401116401000000008311171561238757600080fd5b9091929391929390803590602001906401000000008111156123a857600080fd5b8201836020820111156123ba57600080fd5b803590602001918460208302840111640100000000831117156123dc57600080fd5b90919293919293908035906020019092919050505061846e565b005b34801561240457600080fd5b5061243b6004803603604081101561241b57600080fd5b810190808035906020019092919080359060200190929190505050618855565b604051808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b838110156124b3578082015181840152602081019050612498565b50505050905090810190601f1680156124e05780820380516001836020036101000a031916815260200191505b5094505050505060405180910390f35b3480156124fc57600080fd5b506125056189df565b6040518082815260200191505060405180910390f35b34801561252757600080fd5b506125306189e5565b6040518082815260200191505060405180910390f35b34801561255257600080fd5b5061257f6004803603602081101561256957600080fd5b8101908080359060200190929190505050618b21565b60405180848152602001838152602001828152602001935050505060405180910390f35b3480156125af57600080fd5b506125dc600480360360208110156125c657600080fd5b8101908080359060200190929190505050618b4e565b6040518082815260200191505060405180910390f35b3480156125fe57600080fd5b5061265d600480360360c081101561261557600080fd5b81019080803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050618b99565b604051808381526020018281526020019250505060405180910390f35b34801561268657600080fd5b506126fc6004803603606081101561269d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080359060200190929190505050618dad565b005b34801561270a57600080fd5b5061274d6004803603602081101561272157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506190ea565b005b34801561275b57600080fd5b506127886004803603602081101561277257600080fd5b8101908080359060200190929190505050619170565b6040518082815260200191505060405180910390f35b3480156127aa57600080fd5b506127b36192b9565b6040518082815260200191505060405180910390f35b606080601273000000000000000000000000000000000000a0086369b317e390916040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b15801561281e57600080fd5b505af4158015612832573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250604081101561285c57600080fd5b810190808051604051939291908464010000000082111561287c57600080fd5b8382019150602082018581111561289257600080fd5b82518660208202830111640100000000821117156128af57600080fd5b8083526020830192505050908051906020019060200280838360005b838110156128e65780820151818401526020810190506128cb565b505050509050016040526020018051604051939291908464010000000082111561290f57600080fd5b8382019150602082018581111561292557600080fd5b825186602082028301116401000000008211171561294257600080fd5b8083526020830192505050908051906020019060200280838360005b8381101561297957808201518184015260208101905061295e565b50505050905001604052505050915091509091565b612996615fdb565b612a08576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b612a1061b7e5565b612a19826192c6565b9050612a24816192e4565b612a79576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061bbc86027913960400191505060405180910390fd5b612aa56019600301604051806020016040529081600082015481525050826192fe90919063ffffffff16565b15612b18576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f426173656c696e652071756f72756d20666163746f7220756e6368616e67656481525060200191505060405180910390fd5b806019600301600082015181600001559050507fddfdbe55eaaa70fe2b8bc82a9b0734c25cabe7cb6f1457f9644019f0b5ff91fc826040518082815260200191505060405180910390a15050565b60008060006011600085815260200190815260200160002060000160019054906101000a900460ff166011600086815260200190815260200160002060000160009054906101000a900460ff1660116000878152602001908152602001600020600101549250925092509193909250565b60038060000154908060010154908060020154905083565b600a5481565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16844360405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310612c6e5780518252602082019150602081019050602083039250612c4b565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612cce576040519150601f19603f3d011682016040523d82523d6000602084013e612cd3565b606091505b50809350819250505080612d32576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d81526020018061bd55603d913960400191505060405180910390fd5b612d3d826000619313565b92505050919050565b6000612d63600f600084815260200190815260200160002061932a565b9050919050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000612daf600f6000858152602001908152602001600020848461933a565b905092915050565b600060149054906101000a900460ff1681565b612dd2615fdb565b612e44576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b612e4c61b7e5565b612e55826192c6565b9050612e60816192e4565b612eb5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602481526020018061bf2f6024913960400191505060405180910390fd5b612ee16019600101604051806020016040529081600082015481525050826192fe90919063ffffffff16565b15612f37576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061bc376026913960400191505060405180910390fd5b806019600101600082015181600001559050507f122a37b609e0f758b6a23c43506cb567017a4d18b21fa91312fb42b44975a5b5826040518082815260200191505060405180910390a15050565b60008060fb73ffffffffffffffffffffffffffffffffffffffff16858585604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140183805190602001908083835b6020831061300e5780518252602082019150602081019050602083039250612feb565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b6020831061305f578051825260208201915060208101905060208303925061303c565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506040516020818303038152906040526040518082805190602001908083835b602083106130c857805182526020820191506020810190506020830392506130a5565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613128576040519150601f19603f3d011682016040523d82523d6000602084013e61312d565b606091505b505080915050809150509392505050565b600061315b600f60008481526020019081526020016000206193cd565b9050919050565b6000601060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600201549050919050565b6131b6615fdb565b613228576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008111613281576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061bad76021913960400191505060405180910390fd5b6006548114156132f9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f517565756545787069727920756e6368616e676564000000000000000000000081525060200191505060405180910390fd5b806006819055507f4ecbf0bb0701615e2d6f9b0a0996056c959fe359ce76aa7ce06c5f1d57dae4d7816040518082815260200191505060405180910390a150565b6000600360020154905090565b61334f615fdb565b6133c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613464576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f417070726f7665722063616e6e6f74206265203000000000000000000000000081525060200191505060405180910390fd5b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613528576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f417070726f76657220756e6368616e676564000000000000000000000000000081525060200191505060405180910390fd5b80600860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fa03757d836cb0b61c0fbba2147f5d51d6071ff3dd5bf6963beb55563d64878e160405160405180910390a250565b60006135b961598f565b6135c283615b7b565b10159050919050565b60006135de826135d96189e5565b6195ac565b9050919050565b6135fc6007546009546195f490919063ffffffff16565b42106139d2576000613618600a5460126000016002015461967c565b90506060601273000000000000000000000000000000000000a0086377b024799091846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060006040518083038186803b15801561367657600080fd5b505af415801561368a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060208110156136b457600080fd5b81019080805160405193929190846401000000008211156136d457600080fd5b838201915060208201858111156136ea57600080fd5b825186602082028301116401000000008211171561370757600080fd5b8083526020830192505050908051906020019060200280838360005b8381101561373e578082015181840152602081019050613723565b50505050905001604052505050905060008090505b828110156139c757600082828151811061376957fe5b602002602001015190506000600f6000838152602001908152602001600020905061379381619695565b156137cc57817f88e53c486703527139dfc8d97a1e559d9bd93d3f9d52cda4e06564111e7a264360405160405180910390a250506139ac565b6138468160010154600d60008460000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546195f490919063ffffffff16565b600d60008360000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550428160020181905550600060188054905011156139445760006138df60016018805490506196ba90919063ffffffff16565b9050826017601883815481106138f157fe5b90600052602060002001548154811061390657fe5b90600052602060002001819055506018818154811061392157fe5b90600052602060002001600090558060188161393d919061b7f8565b5050613971565b60178290806001815401808255809150509060018203906000526020600020016000909192909190915055505b817f3e069fb74dcf5fbc07740b0d40d7f7fc48e9c0ca5dc3d19eb34d2e05d74c5543426040518082815260200191505060405180910390a250505b6139c06001826195f490919063ffffffff16565b9050613753565b504260098190555050505b565b600060018060008282540192505081905550600060015490506000600d60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905060008111613aa7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f4e6f7468696e6720746f2077697468647261770000000000000000000000000081525060200191505060405180910390fd5b47811115613b1d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f496e636f6e73697374656e742062616c616e636500000000000000000000000081525060200191505060405180910390fd5b6000600d60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550613b8b813373ffffffffffffffffffffffffffffffffffffffff1661970490919063ffffffff16565b60019250506001548114613c07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5090565b613c13615fdb565b613c85576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b613c8d61b7e5565b613c96826192c6565b9050613ca1816192e4565b613cf6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061bb266027913960400191505060405180910390fd5b613d226019600001604051806020016040529081600082015481525050826192fe90919063ffffffff16565b15613d95576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f50617274696369706174696f6e20626173656c696e6520756e6368616e67656481525060200191505060405180910390fd5b806019600001600082015181600001559050507f51131d2820f04a6b6edd20e22a07d5bf847e265a3906e85256fca7d6043417c5826040518082815260200191505060405180910390a15050565b60006011600084815260200190815260200160002060020160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b600c5481565b60116020528060005260406000206000915090508060000160009054906101000a900460ff16908060000160019054906101000a900460ff16908060010154905083565b60006060600060f473ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310613eed5780518252602082019150602081019050602083039250613eca565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310613f545780518252602082019150602081019050602083039250613f31565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613fb4576040519150601f19603f3d011682016040523d82523d6000602084013e613fb9565b606091505b50809350819250505080614018576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603881526020018061bcbe6038913960400191505060405180910390fd5b61402382600061983e565b92505050919050565b60008060008060016002600180839350829250819150809050935093509350935090919293565b600060018060008282540192505081905550600060015490506140746135e5565b60008061408186866198df565b9150915060006140908361932a565b905080156141ba57600460058111156140a557fe5b8260058111156140b157fe5b1480156140c357506140c2836193cd565b5b614118576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e81526020018061bdfd602e913960400191505060405180910390fd5b8273000000000000000000000000000000000000a00263c67e7b4b90916040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b15801561416957600080fd5b505af415801561417d573d6000803e3d6000fd5b50505050867f712ae1383f79ac853f8d882153778e0260ef8f03b504e2866e0593e04d2b291f60405160405180910390a26141b98388886199c6565b5b8094505050506001548114614237576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5092915050565b6000600180600082825401925050819055506000600154905061425f6135e5565b61426885619ae4565b156142765760009150614854565b6000614280619bb7565b73ffffffffffffffffffffffffffffffffffffffff16636642d594336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156142fc57600080fd5b505afa158015614310573d6000803e3d6000fd5b505050506040513d602081101561432657600080fd5b810190808051906020019092919050505090506000601060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905061438c8160000160000154619ae4565b506000614397619cb2565b73ffffffffffffffffffffffffffffffffffffffff166330ec70f5846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561441357600080fd5b505afa158015614427573d6000803e3d6000fd5b505050506040513d602081101561443d57600080fd5b81019080805190602001909291905050509050600081116144a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061be9b6022913960400191505060405180910390fd5b601273000000000000000000000000000000000000a00863bfc5163890918a6040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561450357600080fd5b505af4158015614517573d6000803e3d6000fd5b505050506040513d602081101561452d57600080fd5b8101908080519060200190929190505050614593576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602981526020018061bd2c6029913960400191505060405180910390fd5b6000826000016000015414806146425750601273000000000000000000000000000000000000a00863bfc51638909184600001600001546040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561460557600080fd5b505af4158015614619573d6000803e3d6000fd5b505050506040513d602081101561462f57600080fd5b8101908080519060200190929190505050155b614697576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b81526020018061bfb0602b913960400191505060405180910390fd5b600061474082601273000000000000000000000000000000000000a008637577759990918d6040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b1580156146f757600080fd5b505af415801561470b573d6000803e3d6000fd5b505050506040513d602081101561472157600080fd5b81019080805190602001909291905050506195f490919063ffffffff16565b9050601273000000000000000000000000000000000000a00863239491ba90918b848c8c6040518663ffffffff1660e01b8152600401808681526020018581526020018481526020018381526020018281526020019550505050505060006040518083038186803b1580156147b457600080fd5b505af41580156147c8573d6000803e3d6000fd5b5050505060405180604001604052808a8152602001838152508360000160008201518160000155602082015181600101559050508373ffffffffffffffffffffffffffffffffffffffff16897fd19965d25ef670a1e322fbf05475924b7b12d81fd6b96ab718b261782efb3d62846040518082815260200191505060405180910390a360019550505050505b60015481146148cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b509392505050565b6000808214806148e45750600b5482115b156148f2576000905061496a565b6000600f6000848152602001908152602001600020905061491283617ea5565b156149365761492081619695565b61492b57600161492e565b60055b91505061496a565b600061494c600383619dad90919063ffffffff16565b90506149588282619e6a565b6149625780614965565b60055b925050505b919050565b614977615fdb565b6149e9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6149f161b7e5565b6149fa826192c6565b9050614a05816192e4565b614a5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061bc976027913960400191505060405180910390fd5b614a866019600201604051806020016040529081600082015481525050826192fe90919063ffffffff16565b15614af9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f426173656c696e652075706461746520666163746f7220756e6368616e67656481525060200191505060405180910390fd5b806019600201600082015181600001559050507f8dedb4d995dd500718c7c5f6a077fba6153a7ee063da961d9fcab90ff528ac1f826040518082815260200191505060405180910390a15050565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16858560405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310614bc05780518252602082019150602081019050602083039250614b9d565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114614c20576040519150601f19603f3d011682016040523d82523d6000602084013e614c25565b606091505b50809350819250505080614c84576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061bdc76036913960400191505060405180910390fd5b614c8f826000619313565b9250505092915050565b6000600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614614d5e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f6d73672e73656e646572206e6f7420617070726f76657200000000000000000081525060200191505060405180910390fd5b614d666135e5565b600080614d7385856198df565b91509150614d808261932a565b614d8f57600092505050614f78565b614d9882619ef4565b15614e0b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f50726f706f73616c20616c726561647920617070726f7665640000000000000081525060200191505060405180910390fd5b60026005811115614e1857fe5b816005811115614e2457fe5b14614e97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f50726f706f73616c206e6f7420696e20617070726f76616c207374616765000081525060200191505060405180910390fd5b60018260070160006101000a81548160ff021916908315150217905550614ebc619cb2565b73ffffffffffffffffffffffffffffffffffffffff166330a61d596040518163ffffffff1660e01b815260040160206040518083038186803b158015614f0157600080fd5b505afa158015614f15573d6000803e3d6000fd5b505050506040513d6020811015614f2b57600080fd5b81019080805190602001909291905050508260080181905550847f28ec9e38ba73636ceb2f6c1574136f83bd46284a3c74734b711bf45e12f8d92960405160405180910390a26001925050505b92915050565b600080600080601060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003016000868152602001908152602001600020905080600101548160000160009054906101000a900460ff166003811115614ffb57fe5b8260020154935093509350509250925092565b600080601060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000816000016000015490506000808214158015615075575061507482617ea5565b5b8015615087575061508582617d4f565b155b90506000600f60008560020154815260200190815260200160002090506000600360058111156150b357fe5b6150c7600384619dad90919063ffffffff16565b60058111156150d257fe5b14905082806150de5750805b95505050505050919050565b600d6020528060005260406000206000915090505481565b600061510c6135e5565b600c54341015615184576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f546f6f20736d616c6c206465706f73697400000000000000000000000000000081525060200191505060405180910390fd5b61519a6001600b546195f490919063ffffffff16565b600b819055506000600f6000600b54815260200190815260200160002090508073000000000000000000000000000000000000a002633053123f90918e8e8e8e8e8e8e8e33346040518c63ffffffff1660e01b8152600401808c8152602001806020018060200180602001806020018773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200186815260200185810385528f8f82818152602001925060200280828437600081840152601f19601f82011690508083019250505085810384528d8d82818152602001925060200280828437600081840152601f19601f82011690508083019250505085810383528b8b82818152602001925080828437600081840152601f19601f8201169050808301925050508581038252898982818152602001925060200280828437600081840152601f19601f8201169050808301925050509f5050505050505050505050505050505060006040518083038186803b15801561531f57600080fd5b505af4158015615333573d6000803e3d6000fd5b5050505061538e84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505082619f0f90919063ffffffff16565b601273000000000000000000000000000000000000a00863d7a8acc19091600b546040518363ffffffff1660e01b8152600401808381526020018281526020019250505060006040518083038186803b1580156153ea57600080fd5b505af41580156153fe573d6000803e3d6000fd5b505050503373ffffffffffffffffffffffffffffffffffffffff16600b547f1bfe527f3548d9258c2512b6689f0acfccdd0557d80a53845db25fc57e93d8fe8360060180549050344260405180848152602001838152602001828152602001935050505060405180910390a3600b549150509a9950505050505050505050565b615486615fdb565b6154f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000811161556e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f4475726174696f6e206d757374206265206c6172676572207468616e2030000081525060200191505060405180910390fd5b6003600201548114156155e9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f4475726174696f6e20756e6368616e676564000000000000000000000000000081525060200191505060405180910390fd5b806003600201819055507f7819c8059302d1d66abc7fe228ecc02214e0bc5c529956c13717aabefce937d8816040518082815260200191505060405180910390a150565b60006060600060f673ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310615682578051825260208201915060208101905060208303925061565f565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106156e957805182526020820191506020810190506020830392506156c6565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114615749576040519150601f19603f3d011682016040523d82523d6000602084013e61574e565b606091505b508093508192505050806157ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061bfdb6023913960400191505060405180910390fd5b6157b882600061983e565b92505050919050565b6060601780548060200260200160405190810160405280929190818152602001828054801561580f57602002820191906000526020600020905b8154815260200190600101908083116157fb575b5050505050905090565b600080600f6000848152602001908152602001600020905061584e81615849600384619dad90919063ffffffff16565b619e6a565b915050919050565b61585e615fdb565b6158d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600061599a43618b4e565b905090565b60075481565b60006159c2600f6000848152602001908152602001600020619ef4565b9050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6159f7615fdb565b615a69576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008111615ac2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061be2b6026913960400191505060405180910390fd5b600754811415615b3a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f646571756575654672657175656e637920756e6368616e67656400000000000081525060200191505060405180910390fd5b806007819055507f391e82aae76c653cd640ad1b6028e2ee39ca4f29b30152e3d0a9ddd7e1169c34816040518082815260200191505060405180910390a150565b600080600090506000615b8c615cd1565b90506000615b98619bb7565b905060008090505b82811015615cc5576000615bb382612bf5565b905060008373ffffffffffffffffffffffffffffffffffffffff166393c5c487836040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015615c3457600080fd5b505afa158015615c48573d6000803e3d6000fd5b505050506040513d6020811015615c5e57600080fd5b81019080805190602001909291905050509050615c7b8883613de3565b80615c8c5750615c8b8882613de3565b5b15615ca857615ca56001876195f490919063ffffffff16565b95505b5050615cbe6001826195f490919063ffffffff16565b9050615ba0565b50829350505050919050565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1643604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b60208310615d425780518252602082019150602081019050602083039250615d1f565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114615da2576040519150601f19603f3d011682016040523d82523d6000602084013e615da7565b606091505b50809350819250505080615e06576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603581526020018061bd926035913960400191505060405180910390fd5b615e11826000619313565b9250505090565b60006060600060f773ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310615e6d5780518252602082019150602081019050602083039250615e4a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310615ed45780518252602082019150602081019050602083039250615eb1565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114615f34576040519150601f19603f3d011682016040523d82523d6000602084013e615f39565b606091505b50809350819250505080615f98576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603181526020018061bf7f6031913960400191505060405180910390fd5b615fa3826000619313565b92505050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60065481565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661601d619f87565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b616041615fdb565b6160b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008111616129576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6d696e4465706f736974206d757374206265206c6172676572207468616e203081525060200191505060405180910390fd5b600c548114156161a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4d696e696d756d206465706f73697420756e6368616e6765640000000000000081525060200191505060405180910390fd5b80600c819055507fc50a7f0bdf88c216b2541b0bdea26f22305460e39ffc672ec1a7501732c5ba81816040518082815260200191505060405180910390a150565b600060018060008282540192505081905550600060015490506000616205619bb7565b73ffffffffffffffffffffffffffffffffffffffff16636642d594336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561628157600080fd5b505afa158015616295573d6000803e3d6000fd5b505050506040513d60208110156162ab57600080fd5b810190808051906020019092919050505090506000601060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008090505b6017805490508110156165db57600082600301600083815260200190815260200160002090506000600381111561633a57fe5b8160000160009054906101000a900460ff16600381111561635757fe5b1415801561637f57506017828154811061636d57fe5b90600052602060002001548160010154145b156165bf576000806163958360010154856198df565b91509150600360058111156163a657fe5b8160058111156163b257fe5b1415616580578173000000000000000000000000000000000000a00263b05cd27f9091856002015460008760000160009054906101000a900460ff1660006040518663ffffffff1660e01b81526004018086815260200185815260200184815260200183600381111561642157fe5b60ff16815260200182600381111561643557fe5b60ff1681526020019550505050505060006040518083038186803b15801561645c57600080fd5b505af4158015616470573d6000803e3d6000fd5b5050505061647c619cb2565b73ffffffffffffffffffffffffffffffffffffffff166330a61d596040518163ffffffff1660e01b815260040160206040518083038186803b1580156164c157600080fd5b505afa1580156164d5573d6000803e3d6000fd5b505050506040513d60208110156164eb57600080fd5b810190808051906020019092919050505082600801819055508573ffffffffffffffffffffffffffffffffffffffff1683600101547fb59283e3d5436f05576bddef72ddbfb6344c216ed6ea6d7ced2e9bbb94c661ab8560000160009054906101000a900460ff16600381111561655e57fe5b8660020154604051808381526020018281526020019250505060405180910390a35b846003016000858152602001908152602001600020600080820160006101000a81549060ff021916905560018201600090556002820160009055505050505b506165d46001826195f490919063ffffffff16565b9050616307565b50600081600201819055506001935050506001548114616663576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5090565b600061667b6166768484619f8f565b61a1e1565b905092915050565b600061668e82617ea5565b616700576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f50726f706f73616c206e6f74207175657565640000000000000000000000000081525060200191505060405180910390fd5b601273000000000000000000000000000000000000a00863757775999091846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561675a57600080fd5b505af415801561676e573d6000803e3d6000fd5b505050506040513d602081101561678457600080fd5b81019080805190602001909291905050509050919050565b6167a4615fdb565b616816576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000811161688c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f4475726174696f6e206d757374206265206c6172676572207468616e2030000081525060200191505060405180910390fd5b600360000154811415616907576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f4475726174696f6e20756e6368616e676564000000000000000000000000000081525060200191505060405180910390fd5b806003600001819055507fbc44c003a66b841b48c220869efb738b265af305649ac8bafe8efe0c8130e313816040518082815260200191505060405180910390a150565b6000616956436135cb565b905090565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106169cc57805182526020820191506020810190506020830392506169a9565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114616a2c576040519150601f19603f3d011682016040523d82523d6000602084013e616a31565b606091505b50809350819250505080616a90576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e81526020018061baf8602e913960400191505060405180910390fd5b616a9b826000619313565b92505050919050565b806011600082815260200190815260200160002060000160009054906101000a900460ff1615616b3c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f686f7466697820616c726561647920657865637574656400000000000000000081525060200191505060405180910390fd5b616b45826135af565b616b9a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602981526020018061be726029913960400191505060405180910390fd5b6000616ba461694b565b905080601160008581526020019081526020016000206001015410616c14576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061bee46026913960400191505060405180910390fd5b80601160008581526020019081526020016000206001018190555080837f6f184ec313435b3307a4fe59e2293381f08419a87214464c875a2a247e8af5e060405160405180910390a3505050565b616c6a615fdb565b616cdc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415616d7f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b60188181548110616e1357fe5b906000526020600020016000915090505481565b6000600360010154905090565b60178181548110616e4157fe5b906000526020600020016000915090505481565b60006001806000828254019250508190555060006001549050616e766135e5565b6000616e80619bb7565b73ffffffffffffffffffffffffffffffffffffffff16636642d594336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015616efc57600080fd5b505afa158015616f10573d6000803e3d6000fd5b505050506040513d6020811015616f2657600080fd5b810190808051906020019092919050505090506000601060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000816000016000015490506000811415616fff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4163636f756e7420686173206e6f20686973746f726963616c207570766f746581525060200191505060405180910390fd5b61700881619ae4565b50601273000000000000000000000000000000000000a00863bfc516389091836040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561706357600080fd5b505af4158015617077573d6000803e3d6000fd5b505050506040513d602081101561708d57600080fd5b81019080805190602001909291905050501561723157601273000000000000000000000000000000000000a00863239491ba9091836171708660000160010154601273000000000000000000000000000000000000a00863757775999091896040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561712757600080fd5b505af415801561713b573d6000803e3d6000fd5b505050506040513d602081101561715157600080fd5b81019080805190602001909291905050506196ba90919063ffffffff16565b8b8b6040518663ffffffff1660e01b8152600401808681526020018581526020018481526020018381526020018281526020019550505050505060006040518083038186803b1580156171c257600080fd5b505af41580156171d6573d6000803e3d6000fd5b505050508273ffffffffffffffffffffffffffffffffffffffff16817f7dc46237a819c9171a9c037ec98928e563892905c4d23373ca0f3f500f4ed11484600001600101546040518082815260200191505060405180910390a35b60405180604001604052806000815260200160008152508260000160008201518160000155602082015181600101559050506001945050505060015481146172e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b5092915050565b806011600082815260200190815260200160002060000160009054906101000a900460ff1615617380576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f686f7466697820616c726561647920657865637574656400000000000000000081525060200191505060405180910390fd5b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614617443576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f6d73672e73656e646572206e6f7420617070726f76657200000000000000000081525060200191505060405180910390fd5b60016011600084815260200190815260200160002060000160016101000a81548160ff021916908315150217905550817f36bc158cba244a94dc9b8c08d327e8f7e3c2ab5f1925454c577527466f04851f60405160405180910390a25050565b806011600082815260200190815260200160002060000160009054906101000a900460ff161561753b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f686f7466697820616c726561647920657865637574656400000000000000000081525060200191505060405180910390fd5b60016011600084815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550817ff6d22d0b43a6753880b8f9511b82b86cd0fe349cd580bbe6a25b6dc063ef496f33604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a25050565b6000601260000160020154905090565b600060018060008282540192505081905550600060015490506176406135e5565b60008061764d87876198df565b9150915061765a8261932a565b61766957600093505050617cca565b6000617673619bb7565b73ffffffffffffffffffffffffffffffffffffffff16636642d594336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156176ef57600080fd5b505afa158015617703573d6000803e3d6000fd5b505050506040513d602081101561771957600080fd5b810190808051906020019092919050505090506000601060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000617779619cb2565b73ffffffffffffffffffffffffffffffffffffffff166330ec70f5846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156177f557600080fd5b505afa158015617809573d6000803e3d6000fd5b505050506040513d602081101561781f57600080fd5b8101908080519060200190929190505050905061783b85619ef4565b6178ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f50726f706f73616c206e6f7420617070726f766564000000000000000000000081525060200191505060405180910390fd5b600360058111156178ba57fe5b8460058111156178c657fe5b14617939576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e636f72726563742070726f706f73616c207374617465000000000000000081525060200191505060405180910390fd5b6000600381111561794657fe5b88600381111561795257fe5b14156179c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f566f74652076616c756520756e7365740000000000000000000000000000000081525060200191505060405180910390fd5b60008111617a3c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f566f74657220776569676874207a65726f00000000000000000000000000000081525060200191505060405180910390fd5b60008260030160008b815260200190815260200160002090508573000000000000000000000000000000000000a00263b05cd27f90918360020154858f866001015414617a8a576000617a9d565b8560000160009054906101000a900460ff165b8e6040518663ffffffff1660e01b815260040180868152602001858152602001848152602001836003811115617acf57fe5b60ff168152602001826003811115617ae357fe5b60ff1681526020019550505050505060006040518083038186803b158015617b0a57600080fd5b505af4158015617b1e573d6000803e3d6000fd5b50505050617b2a619cb2565b73ffffffffffffffffffffffffffffffffffffffff166330a61d596040518163ffffffff1660e01b815260040160206040518083038186803b158015617b6f57600080fd5b505afa158015617b83573d6000803e3d6000fd5b505050506040513d6020811015617b9957600080fd5b8101908080519060200190929190505050866008018190555060405180606001604052808a6003811115617bc957fe5b81526020018c8152602001838152508360030160008c815260200190815260200160002060008201518160000160006101000a81548160ff02191690836003811115617c1157fe5b02179055506020820151816001015560408201518160020155905050600f6000846002015481526020019081526020016000206002015486600201541115617c5d578a83600201819055505b8373ffffffffffffffffffffffffffffffffffffffff168b7ff3709dc32cf1356da6b8a12a5be1401aeb00989556be7b16ae566e65fef7a9df8b6003811115617ca257fe5b85604051808381526020018281526020019250505060405180910390a3600197505050505050505b6001548114617d41576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b509392505050565b60095481565b6000617d6c600f6000848152602001908152602001600020619695565b9050919050565b600060149054906101000a900460ff1615617df6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff021916908315150217905550617e1a3361a1ef565b617e238d616c62565b617e2c8c613347565b617e358b6180b1565b617e3e8a616039565b617e47896131ae565b617e50886159ef565b617e598761679c565b617e62866182bf565b617e6b8561547e565b617e7484613c0b565b617e7d83612dca565b617e868261496f565b617e8f8161298e565b4260098190555050505050505050505050505050565b6000601273000000000000000000000000000000000000a00863bfc516389091846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b158015617f0157600080fd5b505af4158015617f15573d6000803e3d6000fd5b505050506040513d6020811015617f2b57600080fd5b81019080805190602001909291905050509050919050565b6000806000806060617f66600f600088815260200190815260200160002061a333565b808054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015617ffb5780601f10617fd057610100808354040283529160200191617ffb565b820191906000526020600020905b815481529060010190602001808311617fde57829003601f168201915b505050505090509450945094509450945091939590929450565b60008060008061803d601960000160405180602001604052908160008201548152505061a1e1565b61805f601960010160405180602001604052908160008201548152505061a1e1565b618081601960020160405180602001604052908160008201548152505061a1e1565b6180a3601960030160405180602001604052908160008201548152505061a1e1565b935093509350935090919293565b6180b9615fdb565b61812b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008111618184576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018061bb4d602c913960400191505060405180910390fd5b600a548114156181fc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f4e756d626572206f662070726f706f73616c7320756e6368616e67656400000081525060200191505060405180910390fd5b80600a819055507f85399b9b2595eb13c392e1638ca77730156cd8d48d4733df4db068e4aa6b93a6816040518082815260200191505060405180910390a150565b60008061824861b824565b601060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001604051806040016040529081600082015481526020016001820154815250509050806000015181602001519250925050915091565b6182c7615fdb565b618339576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600081116183af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f4475726174696f6e206d757374206265206c6172676572207468616e2030000081525060200191505060405180910390fd5b60036001015481141561842a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f4475726174696f6e20756e6368616e676564000000000000000000000000000081525060200191505060405180910390fd5b806003600101819055507f90290eb9b27055e686a69fb810bada5381e544d07b8270021da2d355a6c96ed6816040518082815260200191505060405180910390a150565b6000898989898989898989604051602001808060200180602001806020018060200186815260200185810385528e8e82818152602001925060200280828437600081840152601f19601f82011690508083019250505085810384528c8c82818152602001925060200280828437600081840152601f19601f82011690508083019250505085810383528a8a82818152602001925080828437600081840152601f19601f8201169050808301925050508581038252888882818152602001925060200280828437600081840152601f19601f8201169050808301925050509d5050505050505050505050505050604051602081830303815290604052805190602001209050600080600061858084612b66565b92509250925081156185fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f686f7466697820616c726561647920657865637574656400000000000000000081525060200191505060405180910390fd5b8261866d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f686f74666978206e6f7420617070726f7665640000000000000000000000000081525060200191505060405180910390fd5b61867561694b565b81146186cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061bab16026913960400191505060405180910390fd5b6187ea6187e58e8e80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508d8d80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508c8c8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508b8b80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505033600061a38c565b61a5b5565b60016011600086815260200190815260200160002060000160006101000a81548160ff021916908315150217905550837f708a7934acb657a77a617b1fcd5f6d7d9ad592b72934841bff01acefd10f9b6360405160405180910390a250505050505050505050505050565b6000806060600f600086815260200190815260200160002073000000000000000000000000000000000000a00263e6a5192f9091866040518363ffffffff1660e01b8152600401808381526020018281526020019250505060006040518083038186803b1580156188c557600080fd5b505af41580156188d9573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250606081101561890357600080fd5b8101908080519060200190929190805190602001909291908051604051939291908464010000000082111561893757600080fd5b8382019150602082018581111561894d57600080fd5b825186600182028301116401000000008211171561896a57600080fd5b8083526020830192505050908051906020019080838360005b8381101561899e578082015181840152602081019050618983565b50505050905090810190601f1680156189cb5780820380516001836020036101000a031916815260200191505b506040525050509250925092509250925092565b600b5481565b60006060600060f873ffffffffffffffffffffffffffffffffffffffff166040516020016040516020818303038152906040526040518082805190602001908083835b60208310618a4b5780518252602082019150602081019050602083039250618a28565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114618aab576040519150601f19603f3d011682016040523d82523d6000602084013e618ab0565b606091505b50809350819250505080618b0f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061bf0a6025913960400191505060405180910390fd5b618b1a826000619313565b9250505090565b6000806000618b41600f600086815260200190815260200160002061a5c5565b9250925092509193909250565b6000618b926003618b846002618b766002618b688861695b565b61a5ef90919063ffffffff16565b6195f490919063ffffffff16565b61a67590919063ffffffff16565b9050919050565b60008060008714158015618bae575060008514155b618c20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f612064656e6f6d696e61746f72206973207a65726f000000000000000000000081525060200191505060405180910390fd5b6000806000606060fc73ffffffffffffffffffffffffffffffffffffffff168c8c8c8c8c8c6040516020018087815260200186815260200185815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040526040518082805190602001908083835b60208310618cba5780518252602082019150602081019050602083039250618c97565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114618d1a576040519150601f19603f3d011682016040523d82523d6000602084013e618d1f565b606091505b50809250819350505081618d7e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061bebd6027913960400191505060405180910390fd5b618d89816000619313565b9350618d96816020619313565b925083839550955050505050965096945050505050565b618db5615fdb565b618e27576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415618eca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f44657374696e6174696f6e2063616e6e6f74206265207a65726f00000000000081525060200191505060405180910390fd5b6969e10de76676d080000081118015618ef25750618eee618ee961a6bf565b61a1e1565b8111155b618f47576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252604881526020018061bbef6048913960600191505060405180910390fd5b600060e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415618fcf57618f7b816192c6565b600e60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008201518160000155905050619077565b618fd8816192c6565b600e60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001016000847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001908152602001600020600082015181600001559050505b817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168373ffffffffffffffffffffffffffffffffffffffff167f60c5b4756af49d7b071b00dbf0f87af605cce11896ecd3b760d19f0f9d3fbcef836040518082815260200191505060405180910390a3505050565b6190f2615fdb565b619164576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61916d8161a1ef565b50565b60006060600060f573ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106191e157805182526020820191506020810190506020830392506191be565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114619241576040519150601f19603f3d011682016040523d82523d6000602084013e619246565b606091505b508093508192505050806192a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018061bf53602c913960400191505060405180910390fd5b6192b082600061983e565b92505050919050565b6000600360000154905090565b6192ce61b7e5565b6040518060200160405280838152509050919050565b60006192f7826192f261a6bf565b61a6e5565b9050919050565b60008160000151836000015114905092915050565b600061931f838361983e565b60001c905092915050565b6000808260020154119050919050565b60006017805490508210619399576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b81526020018061bffe602b913960400191505060405180910390fd5b6193a28461932a565b80156193c4575082601783815481106193b757fe5b9060005260206000200154145b90509392505050565b60006193d761b7e5565b61942e61941f6019600301604051806020016040529081600082015481525050601960000160405180602001604052908160008201548152505061a6fb90919063ffffffff16565b8461ab5a90919063ffffffff16565b905060008090505b83600601805490508110156195a057600061950885600601838154811061945957fe5b90600052602060002090600302016002018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156194fe5780601f106194d3576101008083540402835291602001916194fe565b820191906000526020600020905b8154815290600101906020018083116194e157829003601f168201915b505050505061ac49565b905061951261b7e5565b61955c86600601848154811061952457fe5b906000526020600020906003020160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683619f8f565b9050619571818561a6e590919063ffffffff16565b156195835760009450505050506195a7565b50506195996001826195f490919063ffffffff16565b9050619436565b5060019150505b919050565b6000808284816195b857fe5b04905060008385816195c657fe5b0614156195d657809150506195ee565b6195ea6001826195f490919063ffffffff16565b9150505b92915050565b600080828401905083811015619672576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600081831061968b578161968d565b825b905092915050565b60006196b060065483600201546195f490919063ffffffff16565b4210159050919050565b60006196fc83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061ada6565b905092915050565b8047101561977a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f416464726573733a20696e73756666696369656e742062616c616e636500000081525060200191505060405180910390fd5b60008273ffffffffffffffffffffffffffffffffffffffff168260405180600001905060006040518083038185875af1925050503d80600081146197da576040519150601f19603f3d011682016040523d82523d6000602084013e6197df565b606091505b5050905080619839576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a81526020018061bc5d603a913960400191505060405180910390fd5b505050565b60006198546020836195f490919063ffffffff16565b835110156198ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f736c6963696e67206f7574206f662072616e676500000000000000000000000081525060200191505060405180910390fd5b60006020830184015190508091505092915050565b6000806000600f6000868152602001908152602001600020905061990481868661933a565b619976576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f50726f706f73616c206e6f74206465717565756564000000000000000000000081525060200191505060405180910390fd5b600061998c600383619dad90919063ffffffff16565b90506199988282619e6a565b156199b6576199a88287876199c6565b8160059350935050506199bf565b81819350935050505b9250929050565b6199cf83619ef4565b80156199df575060008360080154115b156199ee576199ed8361ae66565b5b6000601782815481106199fd57fe5b90600052602060002001819055506018819080600181540180825580915050906001820390600052602060002001600090919290919091505550600f6000838152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160009055600282016000905560038201600080820160009055600182016000905560028201600090555050600682016000619ab1919061b83e565b6007820160006101000a81549060ff02191690556008820160009055600982016000619add919061b862565b5050505050565b6000619aef82617ea5565b8015619b005750619aff82617d4f565b5b15619bad57601273000000000000000000000000000000000000a00863eed5f7be9091846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060006040518083038186803b158015619b5f57600080fd5b505af4158015619b73573d6000803e3d6000fd5b50505050817f88e53c486703527139dfc8d97a1e559d9bd93d3f9d52cda4e06564111e7a264360405160405180910390a260019050619bb2565b600090505b919050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4163636f756e74730000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015619c7257600080fd5b505afa158015619c86573d6000803e3d6000fd5b505050506040513d6020811015619c9c57600080fd5b8101908080519060200190929190505050905090565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4c6f636b6564476f6c6400000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015619d6d57600080fd5b505afa158015619d81573d6000803e3d6000fd5b505050506040513d6020811015619d9757600080fd5b8101908080519060200190929190505050905090565b600080619df78360020154619de98560010154619ddb876000015489600201546195f490919063ffffffff16565b6195f490919063ffffffff16565b6195f490919063ffffffff16565b9050804210619e0a576005915050619e64565b619e218360020154826196ba90919063ffffffff16565b9050804210619e34576004915050619e64565b619e4b8360010154826196ba90919063ffffffff16565b9050804210619e5e576003915050619e64565b60029150505b92915050565b600060046005811115619e7957fe5b826005811115619e8557fe5b1180619eb9575060036005811115619e9957fe5b826005811115619ea557fe5b118015619eb85750619eb6836193cd565b155b5b80619eec575060026005811115619ecc57fe5b826005811115619ed857fe5b118015619eeb5750619ee983619ef4565b155b5b905092915050565b60008160070160009054906101000a900460ff169050919050565b600081511415619f6a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602981526020018061bb9f6029913960400191505060405180910390fd5b80826009019080519060200190619f8292919061b8aa565b505050565b600033905090565b619f9761b7e5565b619f9f61b7e5565b619fb26969e10de76676d08000006192c6565b9050600061a064600e60008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001016000867bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060405180602001604052908160008201548152505061a1e1565b1461a11657600e60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001016000847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001908152602001600020604051806020016040529081600082015481525050905061a1d7565b600061a177600e60008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160405180602001604052908160008201548152505061a1e1565b1461a1d657600e60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160405180602001604052908160008201548152505090505b5b8091505092915050565b600081600001519050919050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561a275576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061bb796026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008060008060008560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201548860060180549050896009018090509450945094509450945091939590929450565b61a39461b92a565b8551875114801561a3a6575083518651145b61a418576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4172726179206c656e677468206d69736d61746368000000000000000000000081525060200191505060405180910390fd5b60008751905061a42661b92a565b84816000019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050838160200181815250504281604001818152505060008090508260405190808252806020026020018201604052801561a4b157816020015b61a49e61b98d565b81526020019060019003908161a4965790505b50826080018190525060008090505b8381101561a5a45760405180606001604052808c838151811061a4df57fe5b602002602001015181526020018b838151811061a4f857fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16815260200161a543848b858151811061a52b57fe5b60200260200101518d61affc9092919063ffffffff16565b8152508360800151828151811061a55657fe5b602002602001018190525061a58788828151811061a57057fe5b6020026020010151836195f490919063ffffffff16565b915061a59d6001826195f490919063ffffffff16565b905061a4c0565b508193505050509695505050505050565b61a5c2816080015161b088565b50565b60008060008360030160000154846003016001015485600301600201549250925092509193909250565b60008083141561a602576000905061a66f565b600082840290508284828161a61357fe5b041461a66a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061be516021913960400191505060405180910390fd5b809150505b92915050565b600061a6b783836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061b191565b905092915050565b61a6c761b7e5565b604051806020016040528069d3c21bcecceda1000000815250905090565b6000816000015183600001511115905092915050565b61a70361b7e5565b60008360000151148061a71a575060008260000151145b1561a7365760405180602001604052806000815250905061ab54565b69d3c21bcecceda10000008260000151141561a7545782905061ab54565b69d3c21bcecceda10000008360000151141561a7725781905061ab54565b600069d3c21bcecceda100000061a7888561b257565b600001518161a79357fe5b049050600061a7a18561b28e565b600001519050600069d3c21bcecceda100000061a7bd8661b257565b600001518161a7c857fe5b049050600061a7d68661b28e565b600001519050600082850290506000851461a86a578285828161a7f557fe5b041461a869576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600069d3c21bcecceda1000000820290506000821461a90c5769d3c21bcecceda100000082828161a89757fe5b041461a90b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f6f766572666c6f772078317931202a206669786564312064657465637465640081525060200191505060405180910390fd5b5b809150600084860290506000861461a99d578486828161a92857fe5b041461a99c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600084880290506000881461aa2b578488828161a9b657fe5b041461aa2a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b61aa3361b2cb565b878161aa3b57fe5b04965061aa4661b2cb565b858161aa4e57fe5b049450600085880290506000881461aadf578588828161aa6a57fe5b041461aade576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b61aae761b7e5565b604051806020016040528087815250905061ab108160405180602001604052808781525061b2d8565b905061ab2a8160405180602001604052808681525061b2d8565b905061ab448160405180602001604052808581525061b2d8565b9050809a50505050505050505050505b92915050565b61ab6261b7e5565b600083600301600001549050600081141561ab895761ab81600061b381565b91505061ac43565b600084600301600101549050600061abc3866003016002015461abb584866195f490919063ffffffff16565b6195f490919063ffffffff16565b9050600061abee61abe961abda896008015461b381565b8861a6fb90919063ffffffff16565b61b40b565b90508181111561ac205761ac1d61ac0e83836196ba90919063ffffffff16565b846195f490919063ffffffff16565b92505b61ac3c8461ac3785876195f490919063ffffffff16565b61b42c565b9450505050505b92915050565b600060188260038151811061ac5a57fe5b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901c60108360028151811061acb757fe5b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901c60088460018151811061ad1457fe5b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901c8460008151811061ad6f57fe5b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161717179050919050565b600083831115829061ae53576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561ae1857808201518184015260208101905061adfd565b50505050905090810190601f16801561ae455780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b61ae6e61b7e5565b61ae778261b46e565b905061ae8161b7e5565b61aead60196002016040518060200160405290816000820154815250508361a6fb90919063ffffffff16565b905061aeb761b7e5565b61af1561aeed601960020160405180602001604052908160008201548152505061aedf61a6bf565b61b4ca90919063ffffffff16565b601960000160405180602001604052908160008201548152505061a6fb90919063ffffffff16565b905061af2a818361b2d890919063ffffffff16565b60196000016000820151816000015590505061af816019600101604051806020016040529081600082015481525050601960000160405180602001604052908160008201548152505061b57190919063ffffffff16565b1561af9e5760196001016019600001600082015481600001559050505b7f51131d2820f04a6b6edd20e22a07d5bf847e265a3906e85256fca7d6043417c561afe1601960000160405180602001604052908160008201548152505061a1e1565b6040518082815260200191505060405180910390a150505050565b60608183018451101561b00e57600080fd5b606082156000811461b02b5760405191506020820160405261b07c565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561b069578051835260208301925060208101905061b04c565b50868552601f19601f8301166040525050505b50809150509392505050565b60008090505b815181101561b18d5761b10082828151811061b0a657fe5b60200260200101516020015183838151811061b0be57fe5b60200260200101516000015184848151811061b0d657fe5b6020026020010151604001515185858151811061b0ef57fe5b60200260200101516040015161b586565b61b172576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f50726f706f73616c20657865637574696f6e206661696c65640000000000000081525060200191505060405180910390fd5b61b1866001826195f490919063ffffffff16565b905061b08e565b5050565b6000808311829061b23d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561b20257808201518184015260208101905061b1e7565b50505050905090810190601f16801561b22f5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161b24957fe5b049050809150509392505050565b61b25f61b7e5565b604051806020016040528069d3c21bcecceda10000008085600001518161b28257fe5b04028152509050919050565b61b29661b7e5565b604051806020016040528069d3c21bcecceda10000008085600001518161b2b957fe5b04028460000151038152509050919050565b600064e8d4a51000905090565b61b2e061b7e5565b600082600001518460000151019050836000015181101561b369576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f616464206f766572666c6f77206465746563746564000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808281525091505092915050565b61b38961b7e5565b61b39161b632565b82111561b3e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061bcf66036913960400191505060405180910390fd5b604051806020016040528069d3c21bcecceda100000084028152509050919050565b600069d3c21bcecceda100000082600001518161b42457fe5b049050919050565b61b43461b7e5565b61b43c61b7e5565b61b4458461b381565b905061b44f61b7e5565b61b4588461b381565b905061b464828261b651565b9250505092915050565b61b47661b7e5565b600061b4b2836003016002015461b4a4856003016001015486600301600001546195f490919063ffffffff16565b6195f490919063ffffffff16565b905061b4c281846008015461b42c565b915050919050565b61b4d261b7e5565b81600001518360000151101561b550576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f737562737472616374696f6e20756e646572666c6f772064657465637465640081525060200191505060405180910390fd5b60405180602001604052808360000151856000015103815250905092915050565b60008160000151836000015110905092915050565b600080600084111561b60e5761b59b8661b79a565b61b60d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b5b6040516020840160008287838a8c6187965a03f19250505080915050949350505050565b60007601357c299a88ea76a58924d52ce4f26a85af186c2b9e74905090565b61b65961b7e5565b60008260000151141561b6d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f63616e277420646976696465206279203000000000000000000000000000000081525060200191505060405180910390fd5b600069d3c21bcecceda10000008460000151029050836000015169d3c21bcecceda1000000828161b70157fe5b041461b775576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6f766572666c6f7720617420646976696465000000000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808460000151838161b78d57fe5b0481525091505092915050565b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561b7dc57506000801b8214155b92505050919050565b6040518060200160405280600081525090565b81548183558181111561b81f5781836000526020600020918201910161b81e919061b9c4565b5b505050565b604051806040016040528060008152602001600081525090565b508054600082556003029060005260206000209081019061b85f919061b9e9565b50565b50805460018160011615610100020316600290046000825580601f1061b888575061b8a7565b601f01602090049060005260206000209081019061b8a6919061b9c4565b5b50565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061b8eb57805160ff191683800117855561b919565b8280016001018555821561b919579182015b8281111561b91857825182559160200191906001019061b8fd565b5b50905061b926919061b9c4565b5090565b604051806101000160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000815260200161b96961ba47565b81526020016060815260200160001515815260200160008152602001606081525090565b604051806060016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001606081525090565b61b9e691905b8082111561b9e257600081600090555060010161b9ca565b5090565b90565b61ba4491905b8082111561ba40576000808201600090556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905560028201600061ba37919061ba68565b5060030161b9ef565b5090565b90565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f1061ba8e575061baad565b601f01602090049060005260206000209081019061baac919061b9c4565b5b5056fe686f74666978206d75737420626520707265706172656420666f7220746869732065706f63685175657565457870697279206d757374206265206c6172676572207468616e20306572726f722063616c6c696e67206e756d62657256616c696461746f7273496e53657420707265636f6d70696c6550617274696369706174696f6e20626173656c696e652067726561746572207468616e206f6e654e756d626572206f662070726f706f73616c73206d757374206265206c6172676572207468616e207a65726f4f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734465736372697074696f6e2075726c206d7573742068617665206e6f6e2d7a65726f206c656e677468426173656c696e652071756f72756d20666163746f722067726561746572207468616e206f6e655468726573686f6c642068617320746f2062652067726561746572207468616e206d616a6f7269747920616e64206e6f742067726561746572207468616e20756e616e696d69747950617274696369706174696f6e20626173656c696e6520666c6f6f7220756e6368616e676564416464726573733a20756e61626c6520746f2073656e642076616c75652c20726563697069656e74206d61792068617665207265766572746564426173656c696e652075706461746520666163746f722067726561746572207468616e206f6e656572726f722063616c6c696e672067657456657269666965645365616c4269746d617046726f6d48656164657220707265636f6d70696c6563616e277420637265617465206669786964697479206e756d626572206c6172676572207468616e206d61784e65774669786564282963616e6e6f74207570766f746520612070726f706f73616c206e6f7420696e207468652071756575656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e67206e756d62657256616c696461746f7273496e43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d53657420707265636f6d70696c6550726f706f73616c206e6f7420696e20657865637574696f6e207374616765206f72206e6f742070617373696e67646571756575654672657175656e6379206d757374206265206c6172676572207468616e2030536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77686f74666978206e6f742077686974656c69737465642062792032662b312076616c696461746f727363616e6e6f74207570766f746520776974686f7574206c6f636b696e6720676f6c646572726f722063616c6c696e67206672616374696f6e4d756c45787020707265636f6d70696c65686f7466697820616c726561647920707265706172656420666f7220746869732065706f63686572726f722063616c6c696e672067657445706f636853697a6520707265636f6d70696c6550617274696369706174696f6e20666c6f6f722067726561746572207468616e206f6e656572726f722063616c6c696e6720676574506172656e745365616c4269746d617020707265636f6d70696c656572726f722063616c6c696e6720676574426c6f636b4e756d62657246726f6d48656164657220707265636f6d70696c6563616e6e6f74207570766f7465206d6f7265207468616e206f6e65207175657565642070726f706f73616c6572726f722063616c6c696e67206861736848656164657220707265636f6d70696c6550726f766964656420696e6465782067726561746572207468616e2064657175657565206c656e6774682ea265627a7a723158201022d469e86c2d3bd1840c944a8d1b3d51f07ad379185878fd8117447bdaf36164736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f024": { + "code": "0x608060405234801561001057600080fd5b506004361061027f5760003560e01c806370a082311161015c578063a457c2d7116100ce578063df4da46111610087578063df4da461146112b1578063e1d6aceb146112cf578063e50e652d1461138a578063ec683072146113cc578063f2fde38b14611447578063fae8db0a1461148b5761027f565b8063a457c2d7146110b4578063a67f87471461111a578063a9059cbb1461114d578063a91ee0dc146111b3578063af31f587146111f7578063dd62ed3e146112395761027f565b80638a883626116101205780638a88362614610e965780638da5cb5b14610f655780638f32d59b14610faf57806395d89b4114610fd15780639a7b3be7146110545780639b2b592f146110725761027f565b806370a0823114610dae578063715018a614610e065780637385e5da14610e105780637b10399914610e2e57806387ee8a0f14610e785761027f565b806339509351116101f55780634b2c2f44116101b95780634b2c2f4414610a4a57806354255be014610b1957806358cf967214610b4c5780635d180adb14610b9a57806367960e9114610c125780636a30b25314610ce15761027f565b806339509351146108d85780633b1eb4bf1461093e57806340a12f641461098057806340c10f191461099e57806342966c6814610a045761027f565b806318160ddd1161024757806318160ddd1461043f5780631e4f0e031461045d578063222836ad1461066c57806323b872dd146106a457806323f0ab651461072a578063313ce567146108b45761027f565b806306fdde0314610284578063095ea7b314610307578063123633ea1461036d57806312c6c099146103db578063158ef93e1461041d575b600080fd5b61028c6114cd565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156102cc5780820151818401526020810190506102b1565b50505050905090810190601f1680156102f95780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6103536004803603604081101561031d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061156f565b604051808215151515815260200191505060405180910390f35b6103996004803603602081101561038357600080fd5b8101908080359060200190929190505050611792565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610407600480360360208110156103f157600080fd5b81019080803590602001909291905050506118e3565b6040518082815260200191505060405180910390f35b61042561190c565b604051808215151515815260200191505060405180910390f35b61044761191f565b6040518082815260200191505060405180910390f35b61066a600480360361012081101561047457600080fd5b810190808035906020019064010000000081111561049157600080fd5b8201836020820111156104a357600080fd5b803590602001918460018302840111640100000000831117156104c557600080fd5b9091929391929390803590602001906401000000008111156104e657600080fd5b8201836020820111156104f857600080fd5b8035906020019184600183028401116401000000008311171561051a57600080fd5b9091929391929390803560ff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019064010000000081111561057c57600080fd5b82018360208201111561058e57600080fd5b803590602001918460208302840111640100000000831117156105b057600080fd5b9091929391929390803590602001906401000000008111156105d157600080fd5b8201836020820111156105e357600080fd5b8035906020019184602083028401116401000000008311171561060557600080fd5b90919293919293908035906020019064010000000081111561062657600080fd5b82018360208201111561063857600080fd5b8035906020019184600183028401116401000000008311171561065a57600080fd5b9091929391929390505050611931565b005b6106a26004803603604081101561068257600080fd5b810190808035906020019092919080359060200190929190505050611c57565b005b610710600480360360608110156106ba57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611ebc565b604051808215151515815260200191505060405180910390f35b61089a6004803603606081101561074057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561077d57600080fd5b82018360208201111561078f57600080fd5b803590602001918460018302840111640100000000831117156107b157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561081457600080fd5b82018360208201111561082657600080fd5b8035906020019184600183028401116401000000008311171561084857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612542565b604051808215151515815260200191505060405180910390f35b6108bc6126fb565b604051808260ff1660ff16815260200191505060405180910390f35b610924600480360360408110156108ee57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612712565b604051808215151515815260200191505060405180910390f35b61096a6004803603602081101561095457600080fd5b81019080803590602001909291905050506129cf565b6040518082815260200191505060405180910390f35b6109886129e9565b6040518082815260200191505060405180910390f35b6109ea600480360360408110156109b457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612a4f565b604051808215151515815260200191505060405180910390f35b610a3060048036036020811015610a1a57600080fd5b8101908080359060200190929190505050612eb5565b604051808215151515815260200191505060405180910390f35b610b0360048036036020811015610a6057600080fd5b8101908080359060200190640100000000811115610a7d57600080fd5b820183602082011115610a8f57600080fd5b80359060200191846001830284011164010000000083111715610ab157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506133dc565b6040518082815260200191505060405180910390f35b610b21613570565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b610b9860048036036040811015610b6257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050613597565b005b610bd060048036036040811015610bb057600080fd5b8101908080359060200190929190803590602001909291905050506138d5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610ccb60048036036020811015610c2857600080fd5b8101908080359060200190640100000000811115610c4557600080fd5b820183602082011115610c5757600080fd5b80359060200191846001830284011164010000000083111715610c7957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050613a27565b6040518082815260200191505060405180910390f35b610dac6004803603610100811015610cf857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050613bbb565b005b610df060048036036020811015610dc457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613eb1565b6040518082815260200191505060405180910390f35b610e0e613f02565b005b610e1861403b565b6040518082815260200191505060405180910390f35b610e3661404b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610e80614071565b6040518082815260200191505060405180910390f35b610f4f60048036036020811015610eac57600080fd5b8101908080359060200190640100000000811115610ec957600080fd5b820183602082011115610edb57600080fd5b80359060200191846001830284011164010000000083111715610efd57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506141b8565b6040518082815260200191505060405180910390f35b610f6d61434c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610fb7614375565b604051808215151515815260200191505060405180910390f35b610fd96143d3565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015611019578082015181840152602081019050610ffe565b50505050905090810190601f1680156110465780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61105c614475565b6040518082815260200191505060405180910390f35b61109e6004803603602081101561108857600080fd5b8101908080359060200190929190505050614485565b6040518082815260200191505060405180910390f35b611100600480360360408110156110ca57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506145ce565b604051808215151515815260200191505060405180910390f35b611122614805565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b6111996004803603604081101561116357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050614869565b604051808215151515815260200191505060405180910390f35b6111f5600480360360208110156111c957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614a3d565b005b6112236004803603602081101561120d57600080fd5b8101908080359060200190929190505050614be1565b6040518082815260200191505060405180910390f35b61129b6004803603604081101561124f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614c23565b6040518082815260200191505060405180910390f35b6112b9614caa565b6040518082815260200191505060405180910390f35b611370600480360360608110156112e557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561132c57600080fd5b82018360208201111561133e57600080fd5b8035906020019184600183028401116401000000008311171561136057600080fd5b9091929391929390505050614de6565b604051808215151515815260200191505060405180910390f35b6113b6600480360360208110156113a057600080fd5b8101908080359060200190929190505050615025565b6040518082815260200191505060405180910390f35b61142a600480360360c08110156113e257600080fd5b81019080803590602001909291908035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050615070565b604051808381526020018281526020019250505060405180910390f35b6114896004803603602081101561145d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050615284565b005b6114b7600480360360208110156114a157600080fd5b810190808035906020019092919050505061530a565b6040518082815260200191505060405180910390f35b606060028054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156115655780601f1061153a57610100808354040283529160200191611565565b820191906000526020600020905b81548152906001019060200180831161154857829003601f168201915b5050505050905090565b6000611579616af3565b6000611583615453565b8092508193505050600860030154811461161a5781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a976115f7600860010160405180602001604052908160008201548152505061561e565b600860030154604051808381526020018281526020019250505060405180910390a15b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614156116a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180616dac602a913960400191505060405180910390fd5b83600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925866040518082815260200191505060405180910390a360019250505092915050565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16844360405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061180b57805182526020820191506020810190506020830392506117e8565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811461186b576040519150601f19603f3d011682016040523d82523d6000602084013e611870565b606091505b508093508192505050806118cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180616d04603d913960400191505060405180910390fd5b6118da82600061562c565b92505050919050565b60006118ed616af3565b6118f5615453565b50809150506119048184615643565b915050919050565b600060149054906101000a900460ff1681565b600061192c600654614be1565b905090565b600060149054906101000a900460ff16156119b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff0219169083151502179055506000881415611a29576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180616c276026913960400191505060405180910390fd5b60008711611a82576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180616bda6027913960400191505060405180910390fd5b611a8b33615670565b60006006819055508d8d60029190611aa4929190616b06565b508b8b60039190611ab6929190616b06565b5089600460006101000a81548160ff021916908360ff160217905550611adb886157b4565b600860000160008201518160000155905050611af56157d2565b6008600101600082015181600001559050508660086002018190555042600860030181905550838390508686905014611b96576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4172726179206c656e677468206d69736d61746368000000000000000000000081525060200191505060405180910390fd5b60008090505b86869050811015611c0757611beb878783818110611bb657fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff16868684818110611bdf57fe5b905060200201356157f8565b50611c006001826159f790919063ffffffff16565b9050611b9c565b50611c1189614a3d565b818160405160200180838380828437808301925050509250505060405160208183030381529060405280519060200120600c819055505050505050505050505050505050565b611c5f614375565b611cd1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b611cd9616af3565b6000611ce3615453565b80925081935050506008600301548114611d7a5781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97611d57600860010160405180602001604052908160008201548152505061561e565b600860030154604051808381526020018281526020019250505060405180910390a15b6000841415611dd4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180616c4d6027913960400191505060405180910390fd5b60008311611e4a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f757064617465506572696f64206d757374206265203e2030000000000000000081525060200191505060405180910390fd5b611e53846157b4565b600860000160008201518160000155905050826008600201819055507fa0035d6667ffb7d387c86c7228141c4a877e8ed831b267ac928a2f5b651c155d84844260405180848152602001838152602001828152602001935050505060405180910390a150505050565b6000611ec6616af3565b6000611ed0615453565b80925081935050506008600301548114611f675781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97611f44600860010160405180602001604052908160008201548152505061561e565b600860030154604051808381526020018281526020019250505060405180910390a15b611f6f615a7f565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611feb57600080fd5b505afa158015611fff573d6000803e3d6000fd5b505050506040513d602081101561201557600080fd5b81019080805190602001909291905050501561207c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180616cac6022913960400191505060405180910390fd5b60006120a1600860010160405180602001604052908160008201548152505086615643565b9050600073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415612129576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180616f01602a913960400191505060405180910390fd5b600560008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548111156121c1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526029815260200180616df76029913960400191505060405180910390fd5b600760008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054851115612296576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526038815260200180616e206038913960400191505060405180910390fd5b6122e881600560008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546159f790919063ffffffff16565b600560008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061237d81600560008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054615b7a90919063ffffffff16565b600560008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061244f85600760008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054615b7a90919063ffffffff16565b600760008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040518082815260200191505060405180910390a3600193505050509392505050565b60008060fb73ffffffffffffffffffffffffffffffffffffffff16858585604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140183805190602001908083835b602083106125cb57805182526020820191506020810190506020830392506125a8565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b6020831061261c57805182526020820191506020810190506020830392506125f9565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506040516020818303038152906040526040518082805190602001908083835b602083106126855780518252602082019150602081019050602083039250612662565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146126e5576040519150601f19603f3d011682016040523d82523d6000602084013e6126ea565b606091505b505080915050809150509392505050565b6000600460009054906101000a900460ff16905090565b600061271c616af3565b6000612726615453565b809250819350505060086003015481146127bd5781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a9761279a600860010160405180602001604052908160008201548152505061561e565b600860030154604051808381526020018281526020019250505060405180910390a15b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161415612843576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180616dac602a913960400191505060405180910390fd5b6000600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905060006128d986836159f790919063ffffffff16565b905080600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3600194505050505092915050565b60006129e2826129dd614caa565b615bc4565b9050919050565b60008060001b600c541415612a465760405160200180807f45786368616e67650000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001209050612a4c565b600c5490505b90565b6000612a59616af3565b6000612a63615453565b80925081935050506008600301548114612afa5781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97612ad7600860010160405180602001604052908160008201548152505061561e565b600860030154604051808381526020018281526020019250505060405180910390a15b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed612b406129e9565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612b7457600080fd5b505afa158015612b88573d6000803e3d6000fd5b505050506040513d6020811015612b9e57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480612d065750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd92723360405160200180807f56616c696461746f727300000000000000000000000000000000000000000000815250600a019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612c9c57600080fd5b505afa158015612cb0573d6000803e3d6000fd5b505050506040513d6020811015612cc657600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b80612e2f5750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd92723360405160200180807f4772616e64614d656e746f000000000000000000000000000000000000000000815250600b019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612dc557600080fd5b505afa158015612dd9573d6000803e3d6000fd5b505050506040513d6020811015612def57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b612ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f53656e646572206e6f7420617574686f72697a656420746f206d696e7400000081525060200191505060405180910390fd5b612eab85856157f8565b9250505092915050565b6000612ebf616af3565b6000612ec9615453565b80925081935050506008600301548114612f605781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97612f3d600860010160405180602001604052908160008201548152505061561e565b600860030154604051808381526020018281526020019250505060405180910390a15b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed612fa66129e9565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612fda57600080fd5b505afa158015612fee573d6000803e3d6000fd5b505050506040513d602081101561300457600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061316c5750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd92723360405160200180807f4772616e64614d656e746f000000000000000000000000000000000000000000815250600b019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561310257600080fd5b505afa158015613116573d6000803e3d6000fd5b505050506040513d602081101561312c57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b6131de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f53656e646572206e6f7420617574686f72697a656420746f206275726e00000081525060200191505060405180910390fd5b6000613203600860010160405180602001604052908160008201548152505086615643565b9050600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548111156132ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f76616c75652065786365656465642062616c616e6365206f662073656e64657281525060200191505060405180910390fd5b6132cf81600654615b7a90919063ffffffff16565b60068190555061332781600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054615b7a90919063ffffffff16565b600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a360019350505050919050565b60006060600060f473ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310613431578051825260208201915060208101905060208303925061340e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106134985780518252602082019150602081019050602083039250613475565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146134f8576040519150601f19603f3d011682016040523d82523d6000602084013e6134fd565b606091505b5080935081925050508061355c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526038815260200180616c746038913960400191505060405180910390fd5b613567826000615c0c565b92505050919050565b60008060008060018060006001839350829250819150809050935093509350935090919293565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614613639576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b613641615a7f565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156136bd57600080fd5b505afa1580156136d1573d6000803e3d6000fd5b505050506040513d60208110156136e757600080fd5b81019080805190602001909291905050501561374e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180616cac6022913960400191505060405180910390fd5b613756616af3565b6000613760615453565b809250819350505060086003015481146137f75781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a976137d4600860010160405180602001604052908160008201548152505061561e565b600860030154604051808381526020018281526020019250505060405180910390a15b600061381c600860010160405180602001604052908160008201548152505085615643565b905061387081600560008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054615b7a90919063ffffffff16565b600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506138c881600654615b7a90919063ffffffff16565b6006819055505050505050565b60006060600060fa73ffffffffffffffffffffffffffffffffffffffff16858560405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061394e578051825260208201915060208101905060208303925061392b565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146139ae576040519150601f19603f3d011682016040523d82523d6000602084013e6139b3565b606091505b50809350819250505080613a12576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180616d766036913960400191505060405180910390fd5b613a1d82600061562c565b9250505092915050565b60006060600060f673ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b60208310613a7c5780518252602082019150602081019050602083039250613a59565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b60208310613ae35780518252602082019150602081019050602083039250613ac0565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613b43576040519150601f19603f3d011682016040523d82523d6000602084013e613b48565b606091505b50809350819250505080613ba7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180616f2b6023913960400191505060405180910390fd5b613bb2826000615c0c565b92505050919050565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614613c5d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f4f6e6c7920564d2063616e2063616c6c0000000000000000000000000000000081525060200191505060405180910390fd5b613c65615a7f565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015613ce157600080fd5b505afa158015613cf5573d6000803e3d6000fd5b505050506040513d6020811015613d0b57600080fd5b810190808051906020019092919050505015613d72576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180616cac6022913960400191505060405180910390fd5b6000613d97600860010160405180602001604052908160008201548152505086615643565b9050613deb81600560008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546159f790919063ffffffff16565b600560008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550613e4b613e3c8a8885615cad565b826159f790919063ffffffff16565b9050613e6a613e5b8a8a87615cad565b826159f790919063ffffffff16565b9050613e89613e7a8a8986615cad565b826159f790919063ffffffff16565b9050613ea0816006546159f790919063ffffffff16565b600681905550505050505050505050565b6000613efb600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054614be1565b9050919050565b613f0a614375565b613f7c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600061404643615025565b905090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1643604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106140e257805182526020820191506020810190506020830392506140bf565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114614142576040519150601f19603f3d011682016040523d82523d6000602084013e614147565b606091505b508093508192505050806141a6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526035815260200180616d416035913960400191505060405180910390fd5b6141b182600061562c565b9250505090565b60006060600060f773ffffffffffffffffffffffffffffffffffffffff16846040516020018082805190602001908083835b6020831061420d57805182526020820191506020810190506020830392506141ea565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106142745780518252602082019150602081019050602083039250614251565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146142d4576040519150601f19603f3d011682016040523d82523d6000602084013e6142d9565b606091505b50809350819250505080614338576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180616ed06031913960400191505060405180910390fd5b61434382600061562c565b92505050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166143b7615e19565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b606060038054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561446b5780601f106144405761010080835404028352916020019161446b565b820191906000526020600020905b81548152906001019060200180831161444e57829003601f168201915b5050505050905090565b6000614480436129cf565b905090565b60006060600060f973ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b602083106144f657805182526020820191506020810190506020830392506144d3565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114614556576040519150601f19603f3d011682016040523d82523d6000602084013e61455b565b606091505b508093508192505050806145ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180616bac602e913960400191505060405180910390fd5b6145c582600061562c565b92505050919050565b60006145d8616af3565b60006145e2615453565b809250819350505060086003015481146146795781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97614656600860010160405180602001604052908160008201548152505061561e565b600860030154604051808381526020018281526020019250505060405180910390a15b6000600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050600061470f8683615b7a90919063ffffffff16565b905080600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3600194505050505092915050565b60008060008061482d600860000160405180602001604052908160008201548152505061561e565b61484f600860010160405180602001604052908160008201548152505061561e565b600860020154600860030154935093509350935090919293565b6000614873616af3565b600061487d615453565b809250819350505060086003015481146149145781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a976148f1600860010160405180602001604052908160008201548152505061561e565b600860030154604051808381526020018281526020019250505060405180910390a15b61491c615a7f565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561499857600080fd5b505afa1580156149ac573d6000803e3d6000fd5b505050506040513d60208110156149c257600080fd5b810190808051906020019092919050505015614a29576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180616cac6022913960400191505060405180910390fd5b614a338585615e21565b9250505092915050565b614a45614375565b614ab7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415614b5a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b6000614beb616af3565b614bf3615453565b5080915050614c1b614c1682614c0886616101565b61618b90919063ffffffff16565b6162d4565b915050919050565b6000600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b60006060600060f873ffffffffffffffffffffffffffffffffffffffff166040516020016040516020818303038152906040526040518082805190602001908083835b60208310614d105780518252602082019150602081019050602083039250614ced565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114614d70576040519150601f19603f3d011682016040523d82523d6000602084013e614d75565b606091505b50809350819250505080614dd4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180616e7f6025913960400191505060405180910390fd5b614ddf82600061562c565b9250505090565b6000614df0616af3565b6000614dfa615453565b80925081935050506008600301548114614e915781600860010160008201518160000155905050806008600301819055507f08f3ed03ec9e579d1f6ab2f9e0d3dc661704696deabe37a6b6df7014f1b30a97614e6e600860010160405180602001604052908160008201548152505061561e565b600860030154604051808381526020018281526020019250505060405180910390a15b614e99615a7f565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015614f1557600080fd5b505afa158015614f29573d6000803e3d6000fd5b505050506040513d6020811015614f3f57600080fd5b810190808051906020019092919050505015614fa6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180616cac6022913960400191505060405180910390fd5b6000614fb28888614869565b90507fe5d4e30fb8364e57bc4d662a07d0cf36f4c34552004c4c3624620a2c1d1c03dc868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a1809350505050949350505050565b6000615069600361505b600261504d600261503f88614485565b6162f590919063ffffffff16565b6159f790919063ffffffff16565b61637b90919063ffffffff16565b9050919050565b60008060008714158015615085575060008514155b6150f7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f612064656e6f6d696e61746f72206973207a65726f000000000000000000000081525060200191505060405180910390fd5b6000806000606060fc73ffffffffffffffffffffffffffffffffffffffff168c8c8c8c8c8c6040516020018087815260200186815260200185815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040526040518082805190602001908083835b60208310615191578051825260208201915060208101905060208303925061516e565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146151f1576040519150601f19603f3d011682016040523d82523d6000602084013e6151f6565b606091505b50809250819350505081615255576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180616e586027913960400191505060405180910390fd5b61526081600061562c565b935061526d81602061562c565b925083839550955050505050965096945050505050565b61528c614375565b6152fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61530781615670565b50565b60006060600060f573ffffffffffffffffffffffffffffffffffffffff1684604051602001808281526020019150506040516020818303038152906040526040518082805190602001908083835b6020831061537b5780518252602082019150602081019050602083039250615358565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d80600081146153db576040519150601f19603f3d011682016040523d82523d6000602084013e6153e0565b606091505b5080935081925050508061543f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180616ea4602c913960400191505060405180910390fd5b61544a826000615c0c565b92505050919050565b61545b616af3565b600061547a6008600201546008600301546159f790919063ffffffff16565b4210156154ad5760086001016008600301548160405180602001604052908160008201548152505091509150915061561a565b60008060006154e16008600201546154d360086003015442615b7a90919063ffffffff16565b61637b90919063ffffffff16565b9050615563615508600860010160405180602001604052908160008201548152505061561e565b6155186155136157d2565b61561e565b61553a600860000160405180602001604052908160008201548152505061561e565b61554a6155456157d2565b61561e565b85600460009054906101000a900460ff1660ff16615070565b8093508194505050600083148061557a5750600082145b156155ae5760086001016008600301548160405180602001604052908160008201548152505091509450945050505061561a565b6155b6616af3565b6155d96155c2846157b4565b6155cb866157b4565b61618b90919063ffffffff16565b9050600061560c6155f8846008600201546162f590919063ffffffff16565b6008600301546159f790919063ffffffff16565b905081819650965050505050505b9091565b600081600001519050919050565b60006156388383615c0c565b60001c905092915050565b600061566861566361565484616101565b856163c590919063ffffffff16565b6162d4565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156156f6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180616c016026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6157bc616af3565b6040518060200160405280838152509050919050565b6157da616af3565b604051806020016040528069d3c21bcecceda1000000815250905090565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561589c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f302069732061207265736572766564206164647265737300000000000000000081525060200191505060405180910390fd5b60008214156158ae57600190506159f1565b60006158d3600860010160405180602001604052908160008201548152505084615643565b90506158ea816006546159f790919063ffffffff16565b60068190555061594281600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546159f790919063ffffffff16565b600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150505b92915050565b600080828401905083811015615a75576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f467265657a6572000000000000000000000000000000000000000000000000008152506007019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015615b3a57600080fd5b505afa158015615b4e573d6000803e3d6000fd5b505050506040513d6020811015615b6457600080fd5b8101908080519060200190929190505050905090565b6000615bbc83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250616824565b905092915050565b600080828481615bd057fe5b0490506000838581615bde57fe5b061415615bee5780915050615c06565b615c026001826159f790919063ffffffff16565b9150505b92915050565b6000615c226020836159f790919063ffffffff16565b83511015615c98576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f736c6963696e67206f7574206f662072616e676500000000000000000000000081525060200191505060405180910390fd5b60006020830184015190508091505092915050565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415615cec5760009050615e12565b6000615d11600860010160405180602001604052908160008201548152505084615643565b9050615d6581600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546159f790919063ffffffff16565b600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3809150505b9392505050565b600033905090565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415615ea8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180616f01602a913960400191505060405180910390fd5b6000615ecd600860010160405180602001604052908160008201548152505084615643565b905080600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015615f67576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526029815260200180616df76029913960400191505060405180910390fd5b615fb981600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054615b7a90919063ffffffff16565b600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061604e81600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546159f790919063ffffffff16565b600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191505092915050565b616109616af3565b6161116168e4565b821115616169576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526036815260200180616cce6036913960400191505060405180910390fd5b604051806020016040528069d3c21bcecceda100000084028152509050919050565b616193616af3565b60008260000151141561620e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f63616e277420646976696465206279203000000000000000000000000000000081525060200191505060405180910390fd5b600069d3c21bcecceda10000008460000151029050836000015169d3c21bcecceda1000000828161623b57fe5b04146162af576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6f766572666c6f7720617420646976696465000000000000000000000000000081525060200191505060405180910390fd5b6040518060200160405280846000015183816162c757fe5b0481525091505092915050565b600069d3c21bcecceda10000008260000151816162ed57fe5b049050919050565b6000808314156163085760009050616375565b600082840290508284828161631957fe5b0414616370576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180616dd66021913960400191505060405180910390fd5b809150505b92915050565b60006163bd83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250616903565b905092915050565b6163cd616af3565b6000836000015114806163e4575060008260000151145b156164005760405180602001604052806000815250905061681e565b69d3c21bcecceda10000008260000151141561641e5782905061681e565b69d3c21bcecceda10000008360000151141561643c5781905061681e565b600069d3c21bcecceda1000000616452856169c9565b600001518161645d57fe5b049050600061646b85616a00565b600001519050600069d3c21bcecceda1000000616487866169c9565b600001518161649257fe5b04905060006164a086616a00565b600001519050600082850290506000851461653457828582816164bf57fe5b0414616533576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600069d3c21bcecceda100000082029050600082146165d65769d3c21bcecceda100000082828161656157fe5b04146165d5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f6f766572666c6f772078317931202a206669786564312064657465637465640081525060200191505060405180910390fd5b5b809150600084860290506000861461666757848682816165f257fe5b0414616666576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b60008488029050600088146166f5578488828161668057fe5b04146166f4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b6166fd616a3d565b878161670557fe5b049650616710616a3d565b858161671857fe5b04945060008588029050600088146167a9578588828161673457fe5b04146167a8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b6167b1616af3565b60405180602001604052808781525090506167da81604051806020016040528087815250616a4a565b90506167f481604051806020016040528086815250616a4a565b905061680e81604051806020016040528085815250616a4a565b9050809a50505050505050505050505b92915050565b60008383111582906168d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561689657808201518184015260208101905061687b565b50505050905090810190601f1680156168c35780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b60007601357c299a88ea76a58924d52ce4f26a85af186c2b9e74905090565b600080831182906169af576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015616974578082015181840152602081019050616959565b50505050905090810190601f1680156169a15780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816169bb57fe5b049050809150509392505050565b6169d1616af3565b604051806020016040528069d3c21bcecceda1000000808560000151816169f457fe5b04028152509050919050565b616a08616af3565b604051806020016040528069d3c21bcecceda100000080856000015181616a2b57fe5b04028460000151038152509050919050565b600064e8d4a51000905090565b616a52616af3565b6000826000015184600001510190508360000151811015616adb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f616464206f766572666c6f77206465746563746564000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808281525091505092915050565b6040518060200160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10616b4757803560ff1916838001178555616b75565b82800160010185558215616b75579182015b82811115616b74578235825591602001919060010190616b59565b5b509050616b829190616b86565b5090565b616ba891905b80821115616ba4576000816000905550600101616b8c565b5090565b9056fe6572726f722063616c6c696e67206e756d62657256616c696461746f7273496e53657420707265636f6d70696c65696e666c6174696f6e466163746f72557064617465506572696f64206d757374206265203e20304f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734d7573742070726f766964652061206e6f6e2d7a65726f20696e666c6174696f6e20726174654d7573742070726f766964652061206e6f6e2d7a65726f20696e666c6174696f6e20726174652e6572726f722063616c6c696e672067657456657269666965645365616c4269746d617046726f6d48656164657220707265636f6d70696c6563616e27742063616c6c207768656e20636f6e74726163742069732066726f7a656e63616e277420637265617465206669786964697479206e756d626572206c6172676572207468616e206d61784e6577466978656428296572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e67206e756d62657256616c696461746f7273496e43757272656e7453657420707265636f6d70696c656572726f722063616c6c696e672076616c696461746f725369676e65724164647265737346726f6d53657420707265636f6d70696c6572657365727665642061646472657373203078302063616e6e6f74206861766520616c6c6f77616e6365536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f777472616e736665722076616c75652065786365656465642062616c616e6365206f662073656e6465727472616e736665722076616c75652065786365656465642073656e646572277320616c6c6f77616e636520666f7220726563697069656e746572726f722063616c6c696e67206672616374696f6e4d756c45787020707265636f6d70696c656572726f722063616c6c696e672067657445706f636853697a6520707265636f6d70696c656572726f722063616c6c696e6720676574506172656e745365616c4269746d617020707265636f6d70696c656572726f722063616c6c696e6720676574426c6f636b4e756d62657246726f6d48656164657220707265636f6d70696c657472616e7366657220617474656d7074656420746f2072657365727665642061646472657373203078306572726f722063616c6c696e67206861736848656164657220707265636f6d70696c65a265627a7a723158202d85c2c5fbe4327ba89e466b4fca7f1795bbe7c9056d8b230ebc092d37119c5164736f6c634300050d0032", + "balance": "0x0" + }, + "000000000000000000000000000000000000f025": { + "code": "0x608060405234801561001057600080fd5b50600436106101c45760003560e01c80637b103999116100f9578063b66a261c11610097578063db1bc87b11610071578063db1bc87b14610735578063dda57b9314610779578063e0c8b50a14610797578063f2fde38b146107b5576101c4565b8063b66a261c14610681578063c3434883146106af578063d404f7f814610707576101c4565b80638da5cb5b116100d35780638da5cb5b146105835780638f32d59b146105cd5780639ed02b58146105ef578063a91ee0dc1461063d576101c4565b80637b1039991461045557806386489ba91461049f5780638ab1a5d41461052b576101c4565b806354255be011610166578063673ea08611610140578063673ea086146103b45780636a5eaf47146103d2578063715018a61461040057806378ba9cfd1461040a576101c4565b806354255be0146103455780635c25c76c1461037857806362f0908414610396576101c4565b806325ac2de6116101a257806325ac2de6146102535780632bc7d67a146102715780634a1be6cb146102c95780634c0226a2146102f7576101c4565b8063158ef93e146101c957806322503ce5146101eb57806322be3de114610209575b600080fd5b6101d16107f9565b604051808215151515815260200191505060405180910390f35b6101f361080b565b6040518082815260200191505060405180910390f35b610211610811565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61025b610837565b6040518082815260200191505060405180910390f35b6102b36004803603606081101561028757600080fd5b81019080803590602001909291908035906020019092919080351515906020019092919050505061083d565b6040518082815260200191505060405180910390f35b6102f5600480360360208110156102df57600080fd5b8101908080359060200190929190505050610853565b005b61032f6004803603604081101561030d57600080fd5b810190808035906020019092919080351515906020019092919050505061090e565b6040518082815260200191505060405180910390f35b61034d610935565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b61038061095b565b6040518082815260200191505060405180910390f35b61039e610967565b6040518082815260200191505060405180910390f35b6103bc61096d565b6040518082815260200191505060405180910390f35b6103fe600480360360208110156103e857600080fd5b8101908080359060200190929190505050610973565b005b610408610ac4565b005b6104386004803603602081101561042057600080fd5b81019080803515159060200190929190505050610bfe565b604051808381526020018281526020019250505060405180910390f35b61045d610c4e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610529600480360360c08110156104b557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050610c74565b005b61056d6004803603606081101561054157600080fd5b810190808035906020019092919080359060200190929190803515159060200190929190505050610d5f565b6040518082815260200191505060405180910390f35b61058b610f9e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105d5610fc7565b604051808215151515815260200191505060405180910390f35b6106276004803603604081101561060557600080fd5b8101908080359060200190929190803515159060200190929190505050611025565b6040518082815260200191505060405180910390f35b61067f6004803603602081101561065357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061104c565b005b6106ad6004803603602081101561069757600080fd5b81019080803590602001909291905050506111f0565b005b6106f1600480360360608110156106c557600080fd5b8101908080359060200190929190803590602001909291908035151590602001909291905050506112bc565b6040518082815260200191505060405180910390f35b6107336004803603602081101561071d57600080fd5b8101908080359060200190929190505050611502565b005b6107776004803603602081101561074b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506115bd565b005b6107816116be565b6040518082815260200191505060405180910390f35b61079f6116ca565b6040518082815260200191505060405180910390f35b6107f7600480360360208110156107cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506116d0565b005b6000809054906101000a900460ff1681565b600a5481565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60075481565b600061084a848484610d5f565b90509392505050565b61085b610fc7565b6108cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b806009819055507f90c0a4a142fbfbc2ae8c21f50729a2f4bc56e85a66c1a1b6654f1e85092a54a6816040518082815260200191505060405180910390a150565b600080600061091c84610bfe565b9150915061092b828287611756565b9250505092915050565b600080600080600180600080839350829250819150809050935093509350935090919293565b60038060000154905081565b60065481565b60095481565b61097b610fc7565b6109ed576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6109f681611823565b600460008201518160000155905050610a35610a10611841565b600460405180602001604052908160008201548152505061186790919063ffffffff16565b610a8a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806139536027913960400191505060405180910390fd5b7fb690f84efb1d9039c2834effb7bebc792a85bfec7ef84f4b269528454f363ccf816040518082815260200191505060405180910390a150565b610acc610fc7565b610b3e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000806000600654905060006007549050610c1761187c565b15610c2d57610c24611c79565b80925081935050505b8415610c40578082935093505050610c49565b81819350935050505b915091565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900460ff1615610cf6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b60016000806101000a81548160ff021916908315150217905550610d1933611cd2565b610d228661104c565b610d2b856115bd565b610d34846111f0565b610d3d83610973565b610d4682610853565b610d4f81611502565b610d57611e18565b505050505050565b6000610d69611e8e565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610de557600080fd5b505afa158015610df9573d6000803e3d6000fd5b505050506040513d6020811015610e0f57600080fd5b810190808051906020019092919050505015610e76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061386c6022913960400191505060405180910390fd5b610e7e611e18565b600160026000828254019250508190555060006002549050600080610ea285611f89565b915091506000610eb383838a611fb1565b905086811015610f0e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260398152602001806138336039913960400191505060405180910390fd5b610f19888288612051565b8094505050506002548114610f96576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b509392505050565b60008060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661100961285d565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b600080600061103384610bfe565b91509150611042828287611fb1565b9250505092915050565b611054610fc7565b6110c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611169576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b6111f8610fc7565b61126a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61127381611823565b6003600082015181600001559050507f8946f328efcc515b5cc3282f6cd95e87a6c0d3508421af0b52d4d3620b3e2db3816040518082815260200191505060405180910390a150565b60006112c6611e8e565b73ffffffffffffffffffffffffffffffffffffffff1663e5839836306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561134257600080fd5b505afa158015611356573d6000803e3d6000fd5b505050506040513d602081101561136c57600080fd5b8101908080519060200190929190505050156113d3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061386c6022913960400191505060405180910390fd5b6113db611e18565b60016002600082825401925050819055506000600254905060008315905060008061140583611f89565b91509150600061141683838b611756565b905087811115611471576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603e8152602001806138c4603e913960400191505060405180910390fd5b61147c818a86612051565b8095505050505060025481146114fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f7265656e7472616e742063616c6c00000000000000000000000000000000000081525060200191505060405180910390fd5b509392505050565b61150a610fc7565b61157c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b80600a819055507f08523596abc266fb46d9c40ddf78fdfd3c08142252833ddce1a2b46f76521035816040518082815260200191505060405180910390a150565b6115c5610fc7565b611637576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f119a23392e161a0bc5f9d5f3e2a6040c45b40d43a36973e10ea1de916f3d8a8a60405160405180910390a250565b60048060000154905081565b60085481565b6116d8610fc7565b61174a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61175381611cd2565b50565b600080821415611769576000905061181c565b6117716137f9565b61178c611787858561286590919063ffffffff16565b6128eb565b90506117966137f9565b6117f26117c960036040518060200160405290816000820154815250506117bb611841565b61297590919063ffffffff16565b6117e46117df878a612a1c90919063ffffffff16565b6128eb565b612a6690919063ffffffff16565b905061181761180082612ec5565b61180984612ec5565b612ed390919063ffffffff16565b925050505b9392505050565b61182b6137f9565b6040518060200160405280838152509050919050565b6118496137f9565b604051806020016040528069d3c21bcecceda1000000815250905090565b60008160000151836000015110905092915050565b600080600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f536f727465644f7261636c657300000000000000000000000000000000000000815250600d019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561193857600080fd5b505afa15801561194c573d6000803e3d6000fd5b505050506040513d602081101561196257600080fd5b8101908080519060200190929190505050905060008173ffffffffffffffffffffffffffffffffffffffff1663ffe736bf600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050604080518083038186803b158015611a1557600080fd5b505afa158015611a29573d6000803e3d6000fd5b505050506040513d6040811015611a3f57600080fd5b8101908080519060200190929190805190602001909291905050505090506000611a76600954600854612f1d90919063ffffffff16565b42101590506000600a548473ffffffffffffffffffffffffffffffffffffffff1663bbc66a94600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611b1f57600080fd5b505afa158015611b33573d6000803e3d6000fd5b505050506040513d6020811015611b4957600080fd5b8101908080519060200190929190505050101590506000611b7560095442612a1c90919063ffffffff16565b8573ffffffffffffffffffffffffffffffffffffffff1663071b48fc600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611c1457600080fd5b505afa158015611c28573d6000803e3d6000fd5b505050506040513d6020811015611c3e57600080fd5b8101908080519060200190929190505050119050828015611c5c5750815b8015611c655750805b8015611c6f575083155b9550505050505090565b6000806000611c86612fa5565b9050600080611c93613071565b80925081935050506000611cc282611cb4868661286590919063ffffffff16565b612ed390919063ffffffff16565b9050838195509550505050509091565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611d58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061380d6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600060016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b611e2061187c565b15611e8c5742600881905550611e34611c79565b60066000600760008491905055839190505550507fa18ec663cb684011386aa866c4dacb32d2d2ad859a35d3440b6ce7200a76bad8600654600754604051808381526020018281526020019250505060405180910390a15b565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f467265657a6572000000000000000000000000000000000000000000000000008152506007019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015611f4957600080fd5b505afa158015611f5d573d6000803e3d6000fd5b505050506040513d6020811015611f7357600080fd5b8101908080519060200190929190505050905090565b6000808215611fa15760075460065491509150611fac565b600654600754915091505b915091565b600080821415611fc4576000905061204a565b611fcc6137f9565b611fd5836132ba565b9050611fdf6137f9565b611ffa611feb876128eb565b83612a6690919063ffffffff16565b90506120046137f9565b61201f83612011886128eb565b61331390919063ffffffff16565b905061204461202d82612ec5565b61203684612ec5565b612ed390919063ffffffff16565b93505050505b9392505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f52657365727665000000000000000000000000000000000000000000000000008152506007019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561210c57600080fd5b505afa158015612120573d6000803e3d6000fd5b505050506040513d602081101561213657600080fd5b81019080805190602001909291905050509050811561244f5761216484600654612f1d90919063ffffffff16565b60068190555061217f83600754612a1c90919063ffffffff16565b60078190555061218d6133bc565b73ffffffffffffffffffffffffffffffffffffffff166323b872dd3383876040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050602060405180830381600087803b15801561224757600080fd5b505af115801561225b573d6000803e3d6000fd5b505050506040513d602081101561227157600080fd5b81019080805190602001909291905050506122f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f5472616e73666572206f662073656c6c20746f6b656e206661696c656400000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166340c10f1933856040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561239d57600080fd5b505af11580156123b1573d6000803e3d6000fd5b505050506040513d60208110156123c757600080fd5b810190808051906020019092919050505061244a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f4d696e74206f6620737461626c6520746f6b656e206661696c6564000000000081525060200191505060405180910390fd5b6127f5565b61246484600754612f1d90919063ffffffff16565b60078190555061247f83600654612a1c90919063ffffffff16565b600681905550600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd3330876040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050602060405180830381600087803b15801561256257600080fd5b505af1158015612576573d6000803e3d6000fd5b505050506040513d602081101561258c57600080fd5b810190808051906020019092919050505061260f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f5472616e73666572206f662073656c6c20746f6b656e206661696c656400000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342966c68856040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561268457600080fd5b505af1158015612698573d6000803e3d6000fd5b505050506040513d60208110156126ae57600080fd5b8101908080519060200190929190505050508073ffffffffffffffffffffffffffffffffffffffff166303a0fea333856040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561274757600080fd5b505af115801561275b573d6000803e3d6000fd5b505050506040513d602081101561277157600080fd5b81019080805190602001909291905050506127f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f5472616e73666572206f6620627579546f6b656e206661696c6564000000000081525060200191505060405180910390fd5b5b3373ffffffffffffffffffffffffffffffffffffffff167f402ac9185b4616422c2794bf5b118bfcc68ed496d52c0d9841dfa114fdeb05ba8585856040518084815260200183815260200182151515158152602001935050505060405180910390a250505050565b600033905090565b60008083141561287857600090506128e5565b600082840290508284828161288957fe5b04146128e0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806139326021913960400191505060405180910390fd5b809150505b92915050565b6128f36137f9565b6128fb6134b7565b821115612953576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061388e6036913960400191505060405180910390fd5b604051806020016040528069d3c21bcecceda100000084028152509050919050565b61297d6137f9565b8160000151836000015110156129fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f737562737472616374696f6e20756e646572666c6f772064657465637465640081525060200191505060405180910390fd5b60405180602001604052808360000151856000015103815250905092915050565b6000612a5e83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506134d6565b905092915050565b612a6e6137f9565b600083600001511480612a85575060008260000151145b15612aa157604051806020016040528060008152509050612ebf565b69d3c21bcecceda100000082600001511415612abf57829050612ebf565b69d3c21bcecceda100000083600001511415612add57819050612ebf565b600069d3c21bcecceda1000000612af385613596565b6000015181612afe57fe5b0490506000612b0c856135cd565b600001519050600069d3c21bcecceda1000000612b2886613596565b6000015181612b3357fe5b0490506000612b41866135cd565b6000015190506000828502905060008514612bd55782858281612b6057fe5b0414612bd4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b600069d3c21bcecceda10000008202905060008214612c775769d3c21bcecceda1000000828281612c0257fe5b0414612c76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f6f766572666c6f772078317931202a206669786564312064657465637465640081525060200191505060405180910390fd5b5b8091506000848602905060008614612d085784868281612c9357fe5b0414612d07576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279312064657465637465640000000000000000000081525060200191505060405180910390fd5b5b6000848802905060008814612d965784888281612d2157fe5b0414612d95576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783179322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b612d9e61360a565b8781612da657fe5b049650612db161360a565b8581612db957fe5b0494506000858802905060008814612e4a5785888281612dd557fe5b0414612e49576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6f766572666c6f7720783279322064657465637465640000000000000000000081525060200191505060405180910390fd5b5b612e526137f9565b6040518060200160405280878152509050612e7b81604051806020016040528087815250613313565b9050612e9581604051806020016040528086815250613313565b9050612eaf81604051806020016040528085815250613313565b9050809a50505050505050505050505b92915050565b600081600001519050919050565b6000612f1583836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613617565b905092915050565b600080828401905083811015612f9b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600080612fb06136dd565b73ffffffffffffffffffffffffffffffffffffffff16638b7df8d46040518163ffffffff1660e01b815260040160206040518083038186803b158015612ff557600080fd5b505afa158015613009573d6000803e3d6000fd5b505050506040513d602081101561301f57600080fd5b8101908080519060200190929190505050905061306b613066613041836128eb565b6004604051806020016040529081600082015481525050612a6690919063ffffffff16565b6137d8565b91505090565b600080600080600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f536f727465644f7261636c657300000000000000000000000000000000000000815250600d019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561313057600080fd5b505afa158015613144573d6000803e3d6000fd5b505050506040513d602081101561315a57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1663ef90e1b0600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050604080518083038186803b15801561320857600080fd5b505afa15801561321c573d6000803e3d6000fd5b505050506040513d604081101561323257600080fd5b8101908080519060200190929190805190602001909291905050508092508193505050600081116132ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806139026030913960400191505060405180910390fd5b81819350935050509091565b6132c26137f9565b61330c6132ce836128eb565b6132fe60036040518060200160405290816000820154815250506132f0611841565b61297590919063ffffffff16565b612a6690919063ffffffff16565b9050919050565b61331b6137f9565b60008260000151846000015101905083600001518110156133a4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f616464206f766572666c6f77206465746563746564000000000000000000000081525060200191505060405180910390fd5b60405180602001604052808281525091505092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f476f6c64546f6b656e00000000000000000000000000000000000000000000008152506009019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561347757600080fd5b505afa15801561348b573d6000803e3d6000fd5b505050506040513d60208110156134a157600080fd5b8101908080519060200190929190505050905090565b60007601357c299a88ea76a58924d52ce4f26a85af186c2b9e74905090565b6000838311158290613583576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561354857808201518184015260208101905061352d565b50505050905090810190601f1680156135755780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b61359e6137f9565b604051806020016040528069d3c21bcecceda1000000808560000151816135c157fe5b04028152509050919050565b6135d56137f9565b604051806020016040528069d3c21bcecceda1000000808560000151816135f857fe5b04028460000151038152509050919050565b600064e8d4a51000905090565b600080831182906136c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561368857808201518184015260208101905061366d565b50505050905090810190601f1680156136b55780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816136cf57fe5b049050809150509392505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f52657365727665000000000000000000000000000000000000000000000000008152506007019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561379857600080fd5b505afa1580156137ac573d6000803e3d6000fd5b505050506040513d60208110156137c257600080fd5b8101908080519060200190929190505050905090565b600069d3c21bcecceda10000008260000151816137f157fe5b049050919050565b604051806020016040528060008152509056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737343616c63756c6174656420627579416d6f756e7420776173206c657373207468616e20737065636966696564206d696e427579416d6f756e7463616e27742063616c6c207768656e20636f6e74726163742069732066726f7a656e63616e277420637265617465206669786964697479206e756d626572206c6172676572207468616e206d61784e65774669786564282943616c63756c617465642073656c6c416d6f756e74207761732067726561746572207468616e20737065636966696564206d617853656c6c416d6f756e7465786368616e676520726174652064656e6f6d696e61746f72206d7573742062652067726561746572207468616e2030536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7772657365727665206672616374696f6e206d75737420626520736d616c6c6572207468616e2031a265627a7a7231582007d21984b2c0bbe9518735751eb892b9494e432a63c98253e00a7e367dbda95364736f6c634300050d0032", + "balance": "0x0" + }, + "0f9602b6756b63dfb94ec11b7a3c2619ed4246ef": { + "code": "0x608060405234801561001057600080fd5b50600436106100f55760003560e01c8063879337f111610097578063a91ee0dc11610066578063a91ee0dc1461035d578063c0d72486146103a1578063d01f63f51461041a578063f2fde38b14610479576100f5565b8063879337f11461026e5780638da5cb5b146102e75780638f32d59b146103315780639cb8a26a14610353576100f5565b80632ed1ce72116100d35780632ed1ce721461019457806341566585146101d6578063715018a61461021a5780637b10399914610224576100f5565b80630662f008146100fa5780631dc34b891461014857806323a39b5414610166575b600080fd5b6101466004803603604081101561011057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506104bd565b005b610150610792565b6040518082815260200191505060405180910390f35b6101926004803603602081101561017c57600080fd5b810190808035906020019092919050505061079f565b005b6101c0600480360360208110156101aa57600080fd5b81019080803590602001909291905050506109a8565b6040518082815260200191505060405180910390f35b610218600480360360208110156101ec57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109c9565b005b610222610aef565b005b61022c610c28565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102e56004803603602081101561028457600080fd5b81019080803590602001906401000000008111156102a157600080fd5b8201836020820111156102b357600080fd5b803590602001918460208302840111640100000000831117156102d557600080fd5b9091929391929390505050610c4e565b005b6102ef610de3565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610339610e0c565b604051808215151515815260200191505060405180910390f35b61035b610e6a565b005b61039f6004803603602081101561037357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610efd565b005b610418600480360360208110156103b757600080fd5b81019080803590602001906401000000008111156103d457600080fd5b8201836020820111156103e657600080fd5b8035906020019184602083028401116401000000008311171561040857600080fd5b90919293919293905050506110a1565b005b610422611131565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561046557808201518184015260208101905061044a565b505050509050019250505060405180910390f35b6104bb6004803603602081101561048f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061138d565b005b6104c5610e0c565b610537576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60028054905081106105b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f57686974656c69737420696e646578206f7574206f662072616e67650000000081525060200191505060405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600282815481106105d557fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610689576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f4261642077686974656c69737420696e6465780000000000000000000000000081525060200191505060405180910390fd5b60006106a4600160028054905061141390919063ffffffff16565b905080821461073b57600281815481106106ba57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600283815481106106f257fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b8060028161074991906116f1565b508273ffffffffffffffffffffffffffffffffffffffff167ff1abf01a1043b7c244d128e8595cf0c1d10743b022b03a02dffd8ca3bf729f5a60405160405180910390a2505050565b6000600380549050905090565b6107a7610e0c565b610819576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd927233836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156108a457600080fd5b505afa1580156108b8573d6000803e3d6000fd5b505050506040513d60208110156108ce57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff16141561094c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603e8152602001806117db603e913960400191505060405180910390fd5b6003819080600181540180825580915050906001820390600052602060002001600090919290919091505550807f4084a390e8b98bd7a32fac9847f0147f7f6773834e5738af4fe38598f80bd61960405160405180910390a250565b600381815481106109b557fe5b906000526020600020016000915090505481565b6109d1610e0c565b610a43576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60028190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508073ffffffffffffffffffffffffffffffffffffffff167f9659de77bedae44137acf94b0b2a83c1a7e51fec986c5d6098bbde7e4f0d02e860405160405180910390a250565b610af7610e0c565b610b69576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610c56610e0c565b610cc8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008090505b600280549050811015610d705760028181548110610ce857fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167ff1abf01a1043b7c244d128e8595cf0c1d10743b022b03a02dffd8ca3bf729f5a60405160405180910390a2610d6960018261145d90919063ffffffff16565b9050610cce565b506000600281610d8091906116f1565b5060008090505b82829050811015610dde57610dc3838383818110610da157fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff166109c9565b610dd760018261145d90919063ffffffff16565b9050610d87565b505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16610e4e6114e5565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b610e72610e0c565b610ee4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff16ff5b610f05610e0c565b610f77576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561101a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616e6e6f7420726567697374657220746865206e756c6c206164647265737381525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b60405160405180910390a250565b6110a9610e0c565b61111b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b81816003919061112c92919061171d565b505050565b6060600061115260038054905060028054905061145d90919063ffffffff16565b90506060816040519080825280602002602001820160405280156111855781602001602082028038833980820191505090505b50905060008090505b60028054905081101561123457600281815481106111a857fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168282815181106111df57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505061122d60018261145d90919063ffffffff16565b905061118e565b60008090505b60038054905081101561138357600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd9272336003838154811061129257fe5b90600052602060002001546040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156112d157600080fd5b505afa1580156112e5573d6000803e3d6000fd5b505050506040513d60208110156112fb57600080fd5b810190808051906020019092919050505083838151811061131857fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505061136660018361145d90919063ffffffff16565b915061137c60018261145d90919063ffffffff16565b905061123a565b5081935050505090565b611395610e0c565b611407576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b611410816114ed565b50565b600061145583836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250611631565b905092915050565b6000808284019050838110156114db576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611573576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806117b56026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008383111582906116de576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156116a3578082015181840152602081019050611688565b50505050905090810190601f1680156116d05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b81548183558181111561171857818360005260206000209182019101611717919061176a565b5b505050565b828054828255906000526020600020908101928215611759579160200282015b8281111561175857823582559160200191906001019061173d565b5b509050611766919061178f565b5090565b61178c91905b80821115611788576000816000905550600101611770565b5090565b90565b6117b191905b808211156117ad576000816000905550600101611795565b5090565b9056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373636f6e74726163744964656e74696669657220646f6573206e6f7420636f72726573706f6e6420746f206120726567697374657265642061646472657373a265627a7a7231582023cb39ebfdce172479d0e66436f38be42af6347f92459ada513ae82cdf35d44064736f6c634300050d0032", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000f08cf7b0bbf851a89b0044a31241b722774bb8b8", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000000000000000000000000000000000000000ce10" + }, + "balance": "0x0" + }, + "2fcaea6b66143fac148b899059b70ba7d1615e88": { + "balance": "0xa968163f0a57b400000" + }, + "3664f51c5a7f9a0706773c7d9a6fecc2a84d73e8": { + "balance": "0xa968163f0a57b400000" + }, + "45229b4f2b9bdad04042a1fbd0fcdc94aef0f36a": { + "balance": "0xa968163f0a57b400000" + }, + "70de4b1b5182233fe5ba4397d9f8806381a573e5": { + "balance": "0xa968163f0a57b400000" + }, + "762a9894c09fd23344b5e741c0516a7d8fc9dca2": { + "balance": "0xa968163f0a57b400000" + }, + "9accb78f41caddadf6e41bbbd50077cf3f7253d6": { + "balance": "0xa968163f0a57b400000" + }, + "a03c986eb7448e33ebc62cd76bff19963ac2d160": { + "balance": "0xa968163f0a57b400000" + }, + "af3113e79b8552ee2dfdc8137f7a1cb2192f489f": { + "balance": "0xa968163f0a57b400000" + }, + "bd5d615613a530072be80f3369ba9414f2bd62ae": { + "balance": "0xa968163f0a57b400000" + }, + "cfade27db9fe673b48385eaff83b852f789aeb49": { + "balance": "0xa968163f0a57b400000" + }, + "f08cf7b0bbf851a89b0044a31241b722774bb8b8": { + "balance": "0x152d02c7e14af6800000" + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } \ No newline at end of file
diff --git go-ethereum/cmd/devp2p/internal/ethtest/testdata/halfchain.rlp celo/cmd/devp2p/internal/ethtest/testdata/halfchain.rlp old mode 100644 new mode 100755 index 1a820734e10c39ab39cad27671c4fcb6b9305943..5527886b5da5c5c44bf0dc28d1fc8c9795f6412c Binary files go-ethereum/cmd/devp2p/internal/ethtest/testdata/halfchain.rlp and celo/cmd/devp2p/internal/ethtest/testdata/halfchain.rlp differ
diff --git go-ethereum/cmd/faucet/faucet.go celo/cmd/faucet/faucet.go deleted file mode 100644 index f1ce5f6b1e9603233a633f6c4c41f6c6cf0ad1b3..0000000000000000000000000000000000000000 --- go-ethereum/cmd/faucet/faucet.go +++ /dev/null @@ -1,902 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -// faucet is an Ether faucet backed by a light client. -package main - -//go:generate go-bindata -nometadata -o website.go faucet.html -//go:generate gofmt -w -s website.go - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "flag" - "fmt" - "html/template" - "io/ioutil" - "math" - "math/big" - "net/http" - "net/url" - "os" - "path/filepath" - "regexp" - "strconv" - "strings" - "sync" - "time" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/ethstats" - "github.com/ethereum/go-ethereum/les" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/nat" - "github.com/ethereum/go-ethereum/params" - "github.com/gorilla/websocket" -) - -var ( - genesisFlag = flag.String("genesis", "", "Genesis json file to seed the chain with") - apiPortFlag = flag.Int("apiport", 8080, "Listener port for the HTTP API connection") - ethPortFlag = flag.Int("ethport", 30303, "Listener port for the devp2p connection") - bootFlag = flag.String("bootnodes", "", "Comma separated bootnode enode URLs to seed with") - netFlag = flag.Uint64("network", 0, "Network ID to use for the Ethereum protocol") - statsFlag = flag.String("ethstats", "", "Ethstats network monitoring auth string") - - netnameFlag = flag.String("faucet.name", "", "Network name to assign to the faucet") - payoutFlag = flag.Int("faucet.amount", 1, "Number of Ethers to pay out per user request") - minutesFlag = flag.Int("faucet.minutes", 1440, "Number of minutes to wait between funding rounds") - tiersFlag = flag.Int("faucet.tiers", 3, "Number of funding tiers to enable (x3 time, x2.5 funds)") - - accJSONFlag = flag.String("account.json", "", "Key json file to fund user requests with") - accPassFlag = flag.String("account.pass", "", "Decryption password to access faucet funds") - - captchaToken = flag.String("captcha.token", "", "Recaptcha site key to authenticate client side") - captchaSecret = flag.String("captcha.secret", "", "Recaptcha secret key to authenticate server side") - - noauthFlag = flag.Bool("noauth", false, "Enables funding requests without authentication") - logFlag = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet") - - twitterTokenFlag = flag.String("twitter.token", "", "Bearer token to authenticate with the v2 Twitter API") - twitterTokenV1Flag = flag.String("twitter.token.v1", "", "Bearer token to authenticate with the v1.1 Twitter API") - - goerliFlag = flag.Bool("goerli", false, "Initializes the faucet with Görli network config") - rinkebyFlag = flag.Bool("rinkeby", false, "Initializes the faucet with Rinkeby network config") -) - -var ( - ether = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) -) - -var ( - gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags) - gitDate = "" // Git commit date YYYYMMDD of the release (set via linker flags) -) - -func main() { - // Parse the flags and set up the logger to print everything requested - flag.Parse() - log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*logFlag), log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) - - // Construct the payout tiers - amounts := make([]string, *tiersFlag) - periods := make([]string, *tiersFlag) - for i := 0; i < *tiersFlag; i++ { - // Calculate the amount for the next tier and format it - amount := float64(*payoutFlag) * math.Pow(2.5, float64(i)) - amounts[i] = fmt.Sprintf("%s Ethers", strconv.FormatFloat(amount, 'f', -1, 64)) - if amount == 1 { - amounts[i] = strings.TrimSuffix(amounts[i], "s") - } - // Calculate the period for the next tier and format it - period := *minutesFlag * int(math.Pow(3, float64(i))) - periods[i] = fmt.Sprintf("%d mins", period) - if period%60 == 0 { - period /= 60 - periods[i] = fmt.Sprintf("%d hours", period) - - if period%24 == 0 { - period /= 24 - periods[i] = fmt.Sprintf("%d days", period) - } - } - if period == 1 { - periods[i] = strings.TrimSuffix(periods[i], "s") - } - } - // Load up and render the faucet website - tmpl, err := Asset("faucet.html") - if err != nil { - log.Crit("Failed to load the faucet template", "err", err) - } - website := new(bytes.Buffer) - err = template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{ - "Network": *netnameFlag, - "Amounts": amounts, - "Periods": periods, - "Recaptcha": *captchaToken, - "NoAuth": *noauthFlag, - }) - if err != nil { - log.Crit("Failed to render the faucet template", "err", err) - } - // Load and parse the genesis block requested by the user - genesis, err := getGenesis(genesisFlag, *goerliFlag, *rinkebyFlag) - if err != nil { - log.Crit("Failed to parse genesis config", "err", err) - } - // Convert the bootnodes to internal enode representations - var enodes []*enode.Node - for _, boot := range strings.Split(*bootFlag, ",") { - if url, err := enode.Parse(enode.ValidSchemes, boot); err == nil { - enodes = append(enodes, url) - } else { - log.Error("Failed to parse bootnode URL", "url", boot, "err", err) - } - } - // Load up the account key and decrypt its password - blob, err := ioutil.ReadFile(*accPassFlag) - if err != nil { - log.Crit("Failed to read account password contents", "file", *accPassFlag, "err", err) - } - pass := strings.TrimSuffix(string(blob), "\n") - - ks := keystore.NewKeyStore(filepath.Join(os.Getenv("HOME"), ".faucet", "keys"), keystore.StandardScryptN, keystore.StandardScryptP) - if blob, err = ioutil.ReadFile(*accJSONFlag); err != nil { - log.Crit("Failed to read account key contents", "file", *accJSONFlag, "err", err) - } - acc, err := ks.Import(blob, pass, pass) - if err != nil && err != keystore.ErrAccountAlreadyExists { - log.Crit("Failed to import faucet signer account", "err", err) - } - if err := ks.Unlock(acc, pass); err != nil { - log.Crit("Failed to unlock faucet signer account", "err", err) - } - // Assemble and start the faucet light service - faucet, err := newFaucet(genesis, *ethPortFlag, enodes, *netFlag, *statsFlag, ks, website.Bytes()) - if err != nil { - log.Crit("Failed to start faucet", "err", err) - } - defer faucet.close() - - if err := faucet.listenAndServe(*apiPortFlag); err != nil { - log.Crit("Failed to launch faucet API", "err", err) - } -} - -// request represents an accepted funding request. -type request struct { - Avatar string `json:"avatar"` // Avatar URL to make the UI nicer - Account common.Address `json:"account"` // Ethereum address being funded - Time time.Time `json:"time"` // Timestamp when the request was accepted - Tx *types.Transaction `json:"tx"` // Transaction funding the account -} - -// faucet represents a crypto faucet backed by an Ethereum light client. -type faucet struct { - config *params.ChainConfig // Chain configurations for signing - stack *node.Node // Ethereum protocol stack - client *ethclient.Client // Client connection to the Ethereum chain - index []byte // Index page to serve up on the web - - keystore *keystore.KeyStore // Keystore containing the single signer - account accounts.Account // Account funding user faucet requests - head *types.Header // Current head header of the faucet - balance *big.Int // Current balance of the faucet - nonce uint64 // Current pending nonce of the faucet - price *big.Int // Current gas price to issue funds with - - conns []*wsConn // Currently live websocket connections - timeouts map[string]time.Time // History of users and their funding timeouts - reqs []*request // Currently pending funding requests - update chan struct{} // Channel to signal request updates - - lock sync.RWMutex // Lock protecting the faucet's internals -} - -// wsConn wraps a websocket connection with a write mutex as the underlying -// websocket library does not synchronize access to the stream. -type wsConn struct { - conn *websocket.Conn - wlock sync.Mutex -} - -func newFaucet(genesis *core.Genesis, port int, enodes []*enode.Node, network uint64, stats string, ks *keystore.KeyStore, index []byte) (*faucet, error) { - // Assemble the raw devp2p protocol stack - stack, err := node.New(&node.Config{ - Name: "geth", - Version: params.VersionWithCommit(gitCommit, gitDate), - DataDir: filepath.Join(os.Getenv("HOME"), ".faucet"), - P2P: p2p.Config{ - NAT: nat.Any(), - NoDiscovery: true, - DiscoveryV5: true, - ListenAddr: fmt.Sprintf(":%d", port), - MaxPeers: 25, - BootstrapNodesV5: enodes, - }, - }) - if err != nil { - return nil, err - } - - // Assemble the Ethereum light client protocol - cfg := ethconfig.Defaults - cfg.SyncMode = downloader.LightSync - cfg.NetworkId = network - cfg.Genesis = genesis - utils.SetDNSDiscoveryDefaults(&cfg, genesis.ToBlock(nil).Hash()) - - lesBackend, err := les.New(stack, &cfg) - if err != nil { - return nil, fmt.Errorf("Failed to register the Ethereum service: %w", err) - } - - // Assemble the ethstats monitoring and reporting service' - if stats != "" { - if err := ethstats.New(stack, lesBackend.ApiBackend, lesBackend.Engine(), stats); err != nil { - return nil, err - } - } - // Boot up the client and ensure it connects to bootnodes - if err := stack.Start(); err != nil { - return nil, err - } - for _, boot := range enodes { - old, err := enode.Parse(enode.ValidSchemes, boot.String()) - if err == nil { - stack.Server().AddPeer(old) - } - } - // Attach to the client and retrieve and interesting metadatas - api, err := stack.Attach() - if err != nil { - stack.Close() - return nil, err - } - client := ethclient.NewClient(api) - - return &faucet{ - config: genesis.Config, - stack: stack, - client: client, - index: index, - keystore: ks, - account: ks.Accounts()[0], - timeouts: make(map[string]time.Time), - update: make(chan struct{}, 1), - }, nil -} - -// close terminates the Ethereum connection and tears down the faucet. -func (f *faucet) close() error { - return f.stack.Close() -} - -// listenAndServe registers the HTTP handlers for the faucet and boots it up -// for service user funding requests. -func (f *faucet) listenAndServe(port int) error { - go f.loop() - - http.HandleFunc("/", f.webHandler) - http.HandleFunc("/api", f.apiHandler) - return http.ListenAndServe(fmt.Sprintf(":%d", port), nil) -} - -// webHandler handles all non-api requests, simply flattening and returning the -// faucet website. -func (f *faucet) webHandler(w http.ResponseWriter, r *http.Request) { - w.Write(f.index) -} - -// apiHandler handles requests for Ether grants and transaction statuses. -func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { - upgrader := websocket.Upgrader{} - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - return - } - - // Start tracking the connection and drop at the end - defer conn.Close() - - f.lock.Lock() - wsconn := &wsConn{conn: conn} - f.conns = append(f.conns, wsconn) - f.lock.Unlock() - - defer func() { - f.lock.Lock() - for i, c := range f.conns { - if c.conn == conn { - f.conns = append(f.conns[:i], f.conns[i+1:]...) - break - } - } - f.lock.Unlock() - }() - // Gather the initial stats from the network to report - var ( - head *types.Header - balance *big.Int - nonce uint64 - ) - for head == nil || balance == nil { - // Retrieve the current stats cached by the faucet - f.lock.RLock() - if f.head != nil { - head = types.CopyHeader(f.head) - } - if f.balance != nil { - balance = new(big.Int).Set(f.balance) - } - nonce = f.nonce - f.lock.RUnlock() - - if head == nil || balance == nil { - // Report the faucet offline until initial stats are ready - //lint:ignore ST1005 This error is to be displayed in the browser - if err = sendError(wsconn, errors.New("Faucet offline")); err != nil { - log.Warn("Failed to send faucet error to client", "err", err) - return - } - time.Sleep(3 * time.Second) - } - } - // Send over the initial stats and the latest header - f.lock.RLock() - reqs := f.reqs - f.lock.RUnlock() - if err = send(wsconn, map[string]interface{}{ - "funds": new(big.Int).Div(balance, ether), - "funded": nonce, - "peers": f.stack.Server().PeerCount(), - "requests": reqs, - }, 3*time.Second); err != nil { - log.Warn("Failed to send initial stats to client", "err", err) - return - } - if err = send(wsconn, head, 3*time.Second); err != nil { - log.Warn("Failed to send initial header to client", "err", err) - return - } - // Keep reading requests from the websocket until the connection breaks - for { - // Fetch the next funding request and validate against github - var msg struct { - URL string `json:"url"` - Tier uint `json:"tier"` - Captcha string `json:"captcha"` - } - if err = conn.ReadJSON(&msg); err != nil { - return - } - if !*noauthFlag && !strings.HasPrefix(msg.URL, "https://twitter.com/") && !strings.HasPrefix(msg.URL, "https://www.facebook.com/") { - if err = sendError(wsconn, errors.New("URL doesn't link to supported services")); err != nil { - log.Warn("Failed to send URL error to client", "err", err) - return - } - continue - } - if msg.Tier >= uint(*tiersFlag) { - //lint:ignore ST1005 This error is to be displayed in the browser - if err = sendError(wsconn, errors.New("Invalid funding tier requested")); err != nil { - log.Warn("Failed to send tier error to client", "err", err) - return - } - continue - } - log.Info("Faucet funds requested", "url", msg.URL, "tier", msg.Tier) - - // If captcha verifications are enabled, make sure we're not dealing with a robot - if *captchaToken != "" { - form := url.Values{} - form.Add("secret", *captchaSecret) - form.Add("response", msg.Captcha) - - res, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", form) - if err != nil { - if err = sendError(wsconn, err); err != nil { - log.Warn("Failed to send captcha post error to client", "err", err) - return - } - continue - } - var result struct { - Success bool `json:"success"` - Errors json.RawMessage `json:"error-codes"` - } - err = json.NewDecoder(res.Body).Decode(&result) - res.Body.Close() - if err != nil { - if err = sendError(wsconn, err); err != nil { - log.Warn("Failed to send captcha decode error to client", "err", err) - return - } - continue - } - if !result.Success { - log.Warn("Captcha verification failed", "err", string(result.Errors)) - //lint:ignore ST1005 it's funny and the robot won't mind - if err = sendError(wsconn, errors.New("Beep-bop, you're a robot!")); err != nil { - log.Warn("Failed to send captcha failure to client", "err", err) - return - } - continue - } - } - // Retrieve the Ethereum address to fund, the requesting user and a profile picture - var ( - id string - username string - avatar string - address common.Address - ) - switch { - case strings.HasPrefix(msg.URL, "https://twitter.com/"): - id, username, avatar, address, err = authTwitter(msg.URL, *twitterTokenV1Flag, *twitterTokenFlag) - case strings.HasPrefix(msg.URL, "https://www.facebook.com/"): - username, avatar, address, err = authFacebook(msg.URL) - id = username - case *noauthFlag: - username, avatar, address, err = authNoAuth(msg.URL) - id = username - default: - //lint:ignore ST1005 This error is to be displayed in the browser - err = errors.New("Something funky happened, please open an issue at https://github.com/ethereum/go-ethereum/issues") - } - if err != nil { - if err = sendError(wsconn, err); err != nil { - log.Warn("Failed to send prefix error to client", "err", err) - return - } - continue - } - log.Info("Faucet request valid", "url", msg.URL, "tier", msg.Tier, "user", username, "address", address) - - // Ensure the user didn't request funds too recently - f.lock.Lock() - var ( - fund bool - timeout time.Time - ) - if timeout = f.timeouts[id]; time.Now().After(timeout) { - // User wasn't funded recently, create the funding transaction - amount := new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether) - amount = new(big.Int).Mul(amount, new(big.Int).Exp(big.NewInt(5), big.NewInt(int64(msg.Tier)), nil)) - amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil)) - - tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, 21000, f.price, nil) - signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainID) - if err != nil { - f.lock.Unlock() - if err = sendError(wsconn, err); err != nil { - log.Warn("Failed to send transaction creation error to client", "err", err) - return - } - continue - } - // Submit the transaction and mark as funded if successful - if err := f.client.SendTransaction(context.Background(), signed); err != nil { - f.lock.Unlock() - if err = sendError(wsconn, err); err != nil { - log.Warn("Failed to send transaction transmission error to client", "err", err) - return - } - continue - } - f.reqs = append(f.reqs, &request{ - Avatar: avatar, - Account: address, - Time: time.Now(), - Tx: signed, - }) - timeout := time.Duration(*minutesFlag*int(math.Pow(3, float64(msg.Tier)))) * time.Minute - grace := timeout / 288 // 24h timeout => 5m grace - - f.timeouts[id] = time.Now().Add(timeout - grace) - fund = true - } - f.lock.Unlock() - - // Send an error if too frequent funding, othewise a success - if !fund { - if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(timeout)))); err != nil { // nolint: gosimple - log.Warn("Failed to send funding error to client", "err", err) - return - } - continue - } - if err = sendSuccess(wsconn, fmt.Sprintf("Funding request accepted for %s into %s", username, address.Hex())); err != nil { - log.Warn("Failed to send funding success to client", "err", err) - return - } - select { - case f.update <- struct{}{}: - default: - } - } -} - -// refresh attempts to retrieve the latest header from the chain and extract the -// associated faucet balance and nonce for connectivity caching. -func (f *faucet) refresh(head *types.Header) error { - // Ensure a state update does not run for too long - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - // If no header was specified, use the current chain head - var err error - if head == nil { - if head, err = f.client.HeaderByNumber(ctx, nil); err != nil { - return err - } - } - // Retrieve the balance, nonce and gas price from the current head - var ( - balance *big.Int - nonce uint64 - price *big.Int - ) - if balance, err = f.client.BalanceAt(ctx, f.account.Address, head.Number); err != nil { - return err - } - if nonce, err = f.client.NonceAt(ctx, f.account.Address, head.Number); err != nil { - return err - } - if price, err = f.client.SuggestGasPrice(ctx); err != nil { - return err - } - // Everything succeeded, update the cached stats and eject old requests - f.lock.Lock() - f.head, f.balance = head, balance - f.price, f.nonce = price, nonce - for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce { - f.reqs = f.reqs[1:] - } - f.lock.Unlock() - - return nil -} - -// loop keeps waiting for interesting events and pushes them out to connected -// websockets. -func (f *faucet) loop() { - // Wait for chain events and push them to clients - heads := make(chan *types.Header, 16) - sub, err := f.client.SubscribeNewHead(context.Background(), heads) - if err != nil { - log.Crit("Failed to subscribe to head events", "err", err) - } - defer sub.Unsubscribe() - - // Start a goroutine to update the state from head notifications in the background - update := make(chan *types.Header) - - go func() { - for head := range update { - // New chain head arrived, query the current stats and stream to clients - timestamp := time.Unix(int64(head.Time), 0) - if time.Since(timestamp) > time.Hour { - log.Warn("Skipping faucet refresh, head too old", "number", head.Number, "hash", head.Hash(), "age", common.PrettyAge(timestamp)) - continue - } - if err := f.refresh(head); err != nil { - log.Warn("Failed to update faucet state", "block", head.Number, "hash", head.Hash(), "err", err) - continue - } - // Faucet state retrieved, update locally and send to clients - f.lock.RLock() - log.Info("Updated faucet state", "number", head.Number, "hash", head.Hash(), "age", common.PrettyAge(timestamp), "balance", f.balance, "nonce", f.nonce, "price", f.price) - - balance := new(big.Int).Div(f.balance, ether) - peers := f.stack.Server().PeerCount() - - for _, conn := range f.conns { - if err := send(conn, map[string]interface{}{ - "funds": balance, - "funded": f.nonce, - "peers": peers, - "requests": f.reqs, - }, time.Second); err != nil { - log.Warn("Failed to send stats to client", "err", err) - conn.conn.Close() - continue - } - if err := send(conn, head, time.Second); err != nil { - log.Warn("Failed to send header to client", "err", err) - conn.conn.Close() - } - } - f.lock.RUnlock() - } - }() - // Wait for various events and assing to the appropriate background threads - for { - select { - case head := <-heads: - // New head arrived, send if for state update if there's none running - select { - case update <- head: - default: - } - - case <-f.update: - // Pending requests updated, stream to clients - f.lock.RLock() - for _, conn := range f.conns { - if err := send(conn, map[string]interface{}{"requests": f.reqs}, time.Second); err != nil { - log.Warn("Failed to send requests to client", "err", err) - conn.conn.Close() - } - } - f.lock.RUnlock() - } - } -} - -// sends transmits a data packet to the remote end of the websocket, but also -// setting a write deadline to prevent waiting forever on the node. -func send(conn *wsConn, value interface{}, timeout time.Duration) error { - if timeout == 0 { - timeout = 60 * time.Second - } - conn.wlock.Lock() - defer conn.wlock.Unlock() - conn.conn.SetWriteDeadline(time.Now().Add(timeout)) - return conn.conn.WriteJSON(value) -} - -// sendError transmits an error to the remote end of the websocket, also setting -// the write deadline to 1 second to prevent waiting forever. -func sendError(conn *wsConn, err error) error { - return send(conn, map[string]string{"error": err.Error()}, time.Second) -} - -// sendSuccess transmits a success message to the remote end of the websocket, also -// setting the write deadline to 1 second to prevent waiting forever. -func sendSuccess(conn *wsConn, msg string) error { - return send(conn, map[string]string{"success": msg}, time.Second) -} - -// authTwitter tries to authenticate a faucet request using Twitter posts, returning -// the uniqueness identifier (user id/username), username, avatar URL and Ethereum address to fund on success. -func authTwitter(url string, tokenV1, tokenV2 string) (string, string, string, common.Address, error) { - // Ensure the user specified a meaningful URL, no fancy nonsense - parts := strings.Split(url, "/") - if len(parts) < 4 || parts[len(parts)-2] != "status" { - //lint:ignore ST1005 This error is to be displayed in the browser - return "", "", "", common.Address{}, errors.New("Invalid Twitter status URL") - } - // Strip any query parameters from the tweet id and ensure it's numeric - tweetID := strings.Split(parts[len(parts)-1], "?")[0] - if !regexp.MustCompile("^[0-9]+$").MatchString(tweetID) { - return "", "", "", common.Address{}, errors.New("Invalid Tweet URL") - } - // Twitter's API isn't really friendly with direct links. - // It is restricted to 300 queries / 15 minute with an app api key. - // Anything more will require read only authorization from the users and that we want to avoid. - - // If Twitter bearer token is provided, use the API, selecting the version - // the user would prefer (currently there's a limit of 1 v2 app / developer - // but unlimited v1.1 apps). - switch { - case tokenV1 != "": - return authTwitterWithTokenV1(tweetID, tokenV1) - case tokenV2 != "": - return authTwitterWithTokenV2(tweetID, tokenV2) - } - // Twiter API token isn't provided so we just load the public posts - // and scrape it for the Ethereum address and profile URL. We need to load - // the mobile page though since the main page loads tweet contents via JS. - url = strings.Replace(url, "https://twitter.com/", "https://mobile.twitter.com/", 1) - - res, err := http.Get(url) - if err != nil { - return "", "", "", common.Address{}, err - } - defer res.Body.Close() - - // Resolve the username from the final redirect, no intermediate junk - parts = strings.Split(res.Request.URL.String(), "/") - if len(parts) < 4 || parts[len(parts)-2] != "status" { - //lint:ignore ST1005 This error is to be displayed in the browser - return "", "", "", common.Address{}, errors.New("Invalid Twitter status URL") - } - username := parts[len(parts)-3] - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return "", "", "", common.Address{}, err - } - address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body))) - if address == (common.Address{}) { - //lint:ignore ST1005 This error is to be displayed in the browser - return "", "", "", common.Address{}, errors.New("No Ethereum address found to fund") - } - var avatar string - if parts = regexp.MustCompile("src=\"([^\"]+twimg.com/profile_images[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 { - avatar = parts[1] - } - return username + "@twitter", username, avatar, address, nil -} - -// authTwitterWithTokenV1 tries to authenticate a faucet request using Twitter's v1 -// API, returning the user id, username, avatar URL and Ethereum address to fund on -// success. -func authTwitterWithTokenV1(tweetID string, token string) (string, string, string, common.Address, error) { - // Query the tweet details from Twitter - url := fmt.Sprintf("https://api.twitter.com/1.1/statuses/show.json?id=%s", tweetID) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return "", "", "", common.Address{}, err - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - res, err := http.DefaultClient.Do(req) - if err != nil { - return "", "", "", common.Address{}, err - } - defer res.Body.Close() - - var result struct { - Text string `json:"text"` - User struct { - ID string `json:"id_str"` - Username string `json:"screen_name"` - Avatar string `json:"profile_image_url"` - } `json:"user"` - } - err = json.NewDecoder(res.Body).Decode(&result) - if err != nil { - return "", "", "", common.Address{}, err - } - address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(result.Text)) - if address == (common.Address{}) { - //lint:ignore ST1005 This error is to be displayed in the browser - return "", "", "", common.Address{}, errors.New("No Ethereum address found to fund") - } - return result.User.ID + "@twitter", result.User.Username, result.User.Avatar, address, nil -} - -// authTwitterWithTokenV2 tries to authenticate a faucet request using Twitter's v2 -// API, returning the user id, username, avatar URL and Ethereum address to fund on -// success. -func authTwitterWithTokenV2(tweetID string, token string) (string, string, string, common.Address, error) { - // Query the tweet details from Twitter - url := fmt.Sprintf("https://api.twitter.com/2/tweets/%s?expansions=author_id&user.fields=profile_image_url", tweetID) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return "", "", "", common.Address{}, err - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - res, err := http.DefaultClient.Do(req) - if err != nil { - return "", "", "", common.Address{}, err - } - defer res.Body.Close() - - var result struct { - Data struct { - AuthorID string `json:"author_id"` - Text string `json:"text"` - } `json:"data"` - Includes struct { - Users []struct { - ID string `json:"id"` - Username string `json:"username"` - Avatar string `json:"profile_image_url"` - } `json:"users"` - } `json:"includes"` - } - - err = json.NewDecoder(res.Body).Decode(&result) - if err != nil { - return "", "", "", common.Address{}, err - } - - address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(result.Data.Text)) - if address == (common.Address{}) { - //lint:ignore ST1005 This error is to be displayed in the browser - return "", "", "", common.Address{}, errors.New("No Ethereum address found to fund") - } - return result.Data.AuthorID + "@twitter", result.Includes.Users[0].Username, result.Includes.Users[0].Avatar, address, nil -} - -// authFacebook tries to authenticate a faucet request using Facebook posts, -// returning the username, avatar URL and Ethereum address to fund on success. -func authFacebook(url string) (string, string, common.Address, error) { - // Ensure the user specified a meaningful URL, no fancy nonsense - parts := strings.Split(strings.Split(url, "?")[0], "/") - if parts[len(parts)-1] == "" { - parts = parts[0 : len(parts)-1] - } - if len(parts) < 4 || parts[len(parts)-2] != "posts" { - //lint:ignore ST1005 This error is to be displayed in the browser - return "", "", common.Address{}, errors.New("Invalid Facebook post URL") - } - username := parts[len(parts)-3] - - // Facebook's Graph API isn't really friendly with direct links. Still, we don't - // want to do ask read permissions from users, so just load the public posts and - // scrape it for the Ethereum address and profile URL. - // - // Facebook recently changed their desktop webpage to use AJAX for loading post - // content, so switch over to the mobile site for now. Will probably end up having - // to use the API eventually. - crawl := strings.Replace(url, "www.facebook.com", "m.facebook.com", 1) - - res, err := http.Get(crawl) - if err != nil { - return "", "", common.Address{}, err - } - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return "", "", common.Address{}, err - } - address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body))) - if address == (common.Address{}) { - //lint:ignore ST1005 This error is to be displayed in the browser - return "", "", common.Address{}, errors.New("No Ethereum address found to fund") - } - var avatar string - if parts = regexp.MustCompile("src=\"([^\"]+fbcdn.net[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 { - avatar = parts[1] - } - return username + "@facebook", avatar, address, nil -} - -// authNoAuth tries to interpret a faucet request as a plain Ethereum address, -// without actually performing any remote authentication. This mode is prone to -// Byzantine attack, so only ever use for truly private networks. -func authNoAuth(url string) (string, string, common.Address, error) { - address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(url)) - if address == (common.Address{}) { - //lint:ignore ST1005 This error is to be displayed in the browser - return "", "", common.Address{}, errors.New("No Ethereum address found to fund") - } - return address.Hex() + "@noauth", "", address, nil -} - -// getGenesis returns a genesis based on input args -func getGenesis(genesisFlag *string, goerliFlag bool, rinkebyFlag bool) (*core.Genesis, error) { - switch { - case genesisFlag != nil: - var genesis core.Genesis - err := common.LoadJSON(*genesisFlag, &genesis) - return &genesis, err - case goerliFlag: - return core.DefaultGoerliGenesisBlock(), nil - case rinkebyFlag: - return core.DefaultRinkebyGenesisBlock(), nil - default: - return nil, fmt.Errorf("no genesis flag provided") - } -}
diff --git go-ethereum/cmd/faucet/README.md celo/cmd/faucet/README.md deleted file mode 100644 index 364689a7827704cf094f26d3210781f58b769351..0000000000000000000000000000000000000000 --- go-ethereum/cmd/faucet/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# Faucet - -The `faucet` is a simplistic web application with the goal of distributing small amounts of Ether in private and test networks. - -Users need to post their Ethereum addresses to fund in a Twitter status update or public Facebook post and share the link to the faucet. The faucet will in turn deduplicate user requests and send the Ether. After a funding round, the faucet prevents the same user requesting again for a pre-configured amount of time, proportional to the amount of Ether requested. - -## Operation - -The `faucet` is a single binary app (everything included) with all configurations set via command line flags and a few files. - -First thing's first, the `faucet` needs to connect to an Ethereum network, for which it needs the necessary genesis and network infos. Each of the following flags must be set: - -- `--genesis` is a path to a file containin the network `genesis.json` -- `--network` is the devp2p network id used during connection -- `--bootnodes` is a list of `enode://` ids to join the network through - -The `faucet` will use the `les` protocol to join the configured Ethereum network and will store its data in `$HOME/.faucet` (currently not configurable). - -## Funding - -To be able to distribute funds, the `faucet` needs access to an already funded Ethereum account. This can be configured via: - -- `--account.json` is a path to the Ethereum account's JSON key file -- `--account.pass` is a path to a text file with the decryption passphrase - -The faucet is able to distribute various amounts of Ether in exchange for various timeouts. These can be configured via: - -- `--faucet.amount` is the number of Ethers to send by default -- `--faucet.minutes` is the time to wait before allowing a rerequest -- `--faucet.tiers` is the funding tiers to support (x3 time, x2.5 funds) - -## Sybil protection - -To prevent the same user from exhausting funds in a loop, the `faucet` ties requests to social networks and captcha resolvers. - -Captcha protection uses Google's invisible ReCaptcha, thus the `faucet` needs to run on a live domain. The domain needs to be registered in Google's systems to retrieve the captcha API token and secrets. After doing so, captcha protection may be enabled via: - -- `--captcha.token` is the API token for ReCaptcha -- `--captcha.secret` is the API secret for ReCaptcha - -Sybil protection via Twitter requires an API key as of 15th December, 2020. To obtain it, a Twitter user must be upgraded to developer status and a new Twitter App deployed with it. The app's `Bearer` token is required by the faucet to retrieve tweet data: - -- `--twitter.token` is the Bearer token for `v2` API access -- `--twitter.token.v1` is the Bearer token for `v1` API access - -Sybil protection via Facebook uses the website to directly download post data thus does not currently require an API configuration. - -## Miscellaneous - -Beside the above - mostly essential - CLI flags, there are a number that can be used to fine tune the `faucet`'s operation. Please see `faucet --help` for a full list. \ No newline at end of file
diff --git go-ethereum/cmd/faucet/faucet_test.go celo/cmd/faucet/faucet_test.go deleted file mode 100644 index 0cc77e36b20b54ac33dce62a3c3b67590a7d36fe..0000000000000000000000000000000000000000 --- go-ethereum/cmd/faucet/faucet_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "testing" - - "github.com/ethereum/go-ethereum/common" -) - -func TestFacebook(t *testing.T) { - // TODO: Remove facebook auth or implement facebook api, which seems to require an API key - t.Skipf("The facebook access is flaky, needs to be reimplemented or removed") - for _, tt := range []struct { - url string - want common.Address - }{ - { - "https://www.facebook.com/fooz.gazonk/posts/2837228539847129", - common.HexToAddress("0xDeadDeaDDeaDbEefbEeFbEEfBeeFBeefBeeFbEEF"), - }, - } { - _, _, gotAddress, err := authFacebook(tt.url) - if err != nil { - t.Fatal(err) - } - if gotAddress != tt.want { - t.Fatalf("address wrong, have %v want %v", gotAddress, tt.want) - } - } -}
diff --git go-ethereum/cmd/faucet/faucet.html celo/cmd/faucet/faucet.html deleted file mode 100644 index dad5ad84f21013749c8622644c69b63756c2203c..0000000000000000000000000000000000000000 --- go-ethereum/cmd/faucet/faucet.html +++ /dev/null @@ -1,233 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="utf-8"> - <meta http-equiv="X-UA-Compatible" content="IE=edge"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <title>{{.Network}}: Authenticated Faucet</title> - - <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" /> - <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" /> - - <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-noty/2.4.1/packaged/jquery.noty.packaged.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.0/moment.min.js"></script> - - <style> - .vertical-center { - min-height: 100%; - min-height: 100vh; - display: flex; - align-items: center; - } - .progress { - position: relative; - } - .progress span { - position: absolute; - display: block; - width: 100%; - color: white; - } - pre { - padding: 6px; - margin: 0; - } - </style> - </head> - - <body> - <div class="vertical-center"> - <div class="container"> - <div class="row" style="margin-bottom: 16px;"> - <div class="col-lg-12"> - <h1 style="text-align: center;"><i class="fa fa-bath" aria-hidden="true"></i> {{.Network}} Authenticated Faucet</h1> - </div> - </div> - <div class="row"> - <div class="col-lg-8 col-lg-offset-2"> - <div class="input-group"> - <input id="url" name="url" type="text" class="form-control" placeholder="Social network URL containing your Ethereum address..."/> - <span class="input-group-btn"> - <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Give me Ether <i class="fa fa-caret-down" aria-hidden="true"></i></button> - <ul class="dropdown-menu dropdown-menu-right">{{range $idx, $amount := .Amounts}} - <li><a style="text-align: center;" onclick="tier={{$idx}}; {{if $.Recaptcha}}grecaptcha.execute(){{else}}submit({{$idx}}){{end}}">{{$amount}} / {{index $.Periods $idx}}</a></li>{{end}} - </ul> - </span> - </div>{{if .Recaptcha}} - <div class="g-recaptcha" data-sitekey="{{.Recaptcha}}" data-callback="submit" data-size="invisible"></div>{{end}} - </div> - </div> - <div class="row" style="margin-top: 32px;"> - <div class="col-lg-6 col-lg-offset-3"> - <div class="panel panel-small panel-default"> - <div class="panel-body" style="padding: 0; overflow: auto; max-height: 300px;"> - <table id="requests" class="table table-condensed" style="margin: 0;"></table> - </div> - <div class="panel-footer"> - <table style="width: 100%"><tr> - <td style="text-align: center;"><i class="fa fa-rss" aria-hidden="true"></i> <span id="peers"></span> peers</td> - <td style="text-align: center;"><i class="fa fa-database" aria-hidden="true"></i> <span id="block"></span> blocks</td> - <td style="text-align: center;"><i class="fa fa-heartbeat" aria-hidden="true"></i> <span id="funds"></span> Ethers</td> - <td style="text-align: center;"><i class="fa fa-university" aria-hidden="true"></i> <span id="funded"></span> funded</td> - </tr></table> - </div> - </div> - </div> - </div> - <div class="row" style="margin-top: 32px;"> - <div class="col-lg-12"> - <h3>How does this work?</h3> - <p>This Ether faucet is running on the {{.Network}} network. To prevent malicious actors from exhausting all available funds or accumulating enough Ether to mount long running spam attacks, requests are tied to common 3rd party social network accounts. Anyone having a Twitter or Facebook account may request funds within the permitted limits.</p> - <dl class="dl-horizontal"> - <dt style="width: auto; margin-left: 40px;"><i class="fa fa-twitter" aria-hidden="true" style="font-size: 36px;"></i></dt> - <dd style="margin-left: 88px; margin-bottom: 10px;"></i> To request funds via Twitter, make a <a href="https://twitter.com/intent/tweet?text=Requesting%20faucet%20funds%20into%200x0000000000000000000000000000000000000000%20on%20the%20%23{{.Network}}%20%23Ethereum%20test%20network." target="_about:blank">tweet</a> with your Ethereum address pasted into the contents (surrounding text doesn't matter).<br/>Copy-paste the <a href="https://support.twitter.com/articles/80586" target="_about:blank">tweets URL</a> into the above input box and fire away!</dd> - - <dt style="width: auto; margin-left: 40px;"><i class="fa fa-facebook" aria-hidden="true" style="font-size: 36px;"></i></dt> - <dd style="margin-left: 88px; margin-bottom: 10px;"></i> To request funds via Facebook, publish a new <strong>public</strong> post with your Ethereum address embedded into the content (surrounding text doesn't matter).<br/>Copy-paste the <a href="https://www.facebook.com/help/community/question/?id=282662498552845" target="_about:blank">posts URL</a> into the above input box and fire away!</dd> - - {{if .NoAuth}} - <dt class="text-danger" style="width: auto; margin-left: 40px;"><i class="fa fa-unlock-alt" aria-hidden="true" style="font-size: 36px;"></i></dt> - <dd class="text-danger" style="margin-left: 88px; margin-bottom: 10px;"></i> To request funds <strong>without authentication</strong>, simply copy-paste your Ethereum address into the above input box (surrounding text doesn't matter) and fire away.<br/>This mode is susceptible to Byzantine attacks. Only use for debugging or private networks!</dd> - {{end}} - </dl> - <p>You can track the current pending requests below the input field to see how much you have to wait until your turn comes.</p> - {{if .Recaptcha}}<em>The faucet is running invisible reCaptcha protection against bots.</em>{{end}} - </div> - </div> - </div> - </div> - - <script> - // Global variables to hold the current status of the faucet - var attempt = 0; - var server; - var tier = 0; - var requests = []; - - // Define a function that creates closures to drop old requests - var dropper = function(hash) { - return function() { - for (var i=0; i<requests.length; i++) { - if (requests[i].tx.hash == hash) { - requests.splice(i, 1); - break; - } - } - } - }; - // Define the function that submits a gist url to the server - var submit = function({{if .Recaptcha}}captcha{{end}}) { - server.send(JSON.stringify({url: $("#url")[0].value, tier: tier{{if .Recaptcha}}, captcha: captcha{{end}}}));{{if .Recaptcha}} - grecaptcha.reset();{{end}} - }; - // Define a method to reconnect upon server loss - var reconnect = function() { - server = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/api"); - - server.onmessage = function(event) { - var msg = JSON.parse(event.data); - if (msg === null) { - return; - } - - if (msg.funds !== undefined) { - $("#funds").text(msg.funds); - } - if (msg.funded !== undefined) { - $("#funded").text(msg.funded); - } - if (msg.peers !== undefined) { - $("#peers").text(msg.peers); - } - if (msg.number !== undefined) { - $("#block").text(parseInt(msg.number, 16)); - } - if (msg.error !== undefined) { - noty({layout: 'topCenter', text: msg.error, type: 'error', timeout: 5000, progressBar: true}); - } - if (msg.success !== undefined) { - noty({layout: 'topCenter', text: msg.success, type: 'success', timeout: 5000, progressBar: true}); - } - if (msg.requests !== undefined && msg.requests !== null) { - // Mark all previous requests missing as done - for (var i=0; i<requests.length; i++) { - if (msg.requests.length > 0 && msg.requests[0].tx.hash == requests[i].tx.hash) { - break; - } - if (requests[i].time != "") { - requests[i].time = ""; - setTimeout(dropper(requests[i].tx.hash), 3000); - } - } - // Append any new requests into our local collection - var common = -1; - if (requests.length > 0) { - for (var i=0; i<msg.requests.length; i++) { - if (requests[requests.length-1].tx.hash == msg.requests[i].tx.hash) { - common = i; - break; - } - } - } - for (var i=common+1; i<msg.requests.length; i++) { - requests.push(msg.requests[i]); - } - // Iterate over our entire local collection and re-render the funding table - var content = ""; - for (var i=requests.length-1; i >= 0; i--) { - var done = requests[i].time == ""; - var elapsed = moment().unix()-moment(requests[i].time).unix(); - - content += "<tr id='" + requests[i].tx.hash + "'>"; - content += " <td><div style=\"background: url('" + requests[i].avatar + "'); background-size: cover; width:32px; height: 32px; border-radius: 4px;\"></div></td>"; - content += " <td><pre>" + requests[i].account + "</pre></td>"; - content += " <td style=\"width: 100%; text-align: center; vertical-align: middle;\">"; - if (done) { - content += " funded"; - } else { - content += " <span id='time-" + i + "' class='timer'>" + moment.duration(-elapsed, 'seconds').humanize(true) + "</span>"; - } - content += " <div class='progress' style='height: 4px; margin: 0;'>"; - if (done) { - content += " <div class='progress-bar progress-bar-success' role='progressbar' aria-valuenow='30' style='width:100%;'></div>"; - } else if (elapsed > 30) { - content += " <div class='progress-bar progress-bar-danger progress-bar-striped active' role='progressbar' aria-valuenow='30' style='width:100%;'></div>"; - } else { - content += " <div class='progress-bar progress-bar-striped active' role='progressbar' aria-valuenow='" + elapsed + "' style='width:" + (elapsed * 100 / 30) + "%;'></div>"; - } - content += " </div>"; - content += " </td>"; - content += "</tr>"; - } - $("#requests").html("<tbody>" + content + "</tbody>"); - } - } - server.onclose = function() { setTimeout(reconnect, 3000); }; - } - // Start a UI updater to push the progress bars forward until they are done - setInterval(function() { - $('.progress-bar').each(function() { - var progress = Number($(this).attr('aria-valuenow')) + 1; - if (progress < 30) { - $(this).attr('aria-valuenow', progress); - $(this).css('width', (progress * 100 / 30) + '%'); - } else if (progress == 30) { - $(this).css('width', '100%'); - $(this).addClass("progress-bar-danger"); - } - }) - $('.timer').each(function() { - var index = Number($(this).attr('id').substring(5)); - $(this).html(moment.duration(moment(requests[index].time).unix()-moment().unix(), 'seconds').humanize(true)); - }) - }, 1000); - - // Establish a websocket connection to the API server - reconnect(); - </script>{{if .Recaptcha}} - <script src="https://www.google.com/recaptcha/api.js" async defer></script>{{end}} - </body> -</html>
diff --git go-ethereum/cmd/faucet/website.go celo/cmd/faucet/website.go deleted file mode 100644 index 8e55a113be8d2a0d72a97537e0881f7f7d914a95..0000000000000000000000000000000000000000 --- go-ethereum/cmd/faucet/website.go +++ /dev/null @@ -1,271 +0,0 @@ -// Code generated by go-bindata. DO NOT EDIT. -// sources: -// faucet.html (11.276kB) - -package main - -import ( - "bytes" - "compress/gzip" - "crypto/sha256" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo - digest [sha256.Size]byte -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -func (fi bindataFileInfo) Name() string { - return fi.name -} -func (fi bindataFileInfo) Size() int64 { - return fi.size -} -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} -func (fi bindataFileInfo) IsDir() bool { - return false -} -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -var _faucetHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x5a\x7b\x93\xdb\x36\x92\xff\x7b\xfc\x29\x3a\x3c\x7b\x25\x9d\x87\xa4\x66\xc6\xf6\xfa\x24\x52\x29\xaf\x37\xbb\xe7\xab\xbb\x24\x95\x38\x75\xb7\x95\x4d\x5d\x81\x64\x4b\x84\x07\x04\x18\x00\x94\x46\x99\xd2\x77\xbf\x6a\x80\xa4\xa8\xc7\x4c\xec\xb5\xaf\x6a\xfd\xc7\x98\xc4\xa3\xd1\x8f\x5f\xa3\x1f\x54\xf2\xd5\x9f\xbf\x7b\xfb\xfe\x6f\xdf\x7f\x03\xa5\xad\xc4\xe2\x49\x42\xff\x81\x60\x72\x95\x06\x28\x83\xc5\x93\x8b\xa4\x44\x56\x2c\x9e\x5c\x5c\x24\x15\x5a\x06\x79\xc9\xb4\x41\x9b\x06\x8d\x5d\x86\xaf\x83\xfd\x44\x69\x6d\x1d\xe2\xaf\x0d\x5f\xa7\xc1\xff\x84\x3f\xbd\x09\xdf\xaa\xaa\x66\x96\x67\x02\x03\xc8\x95\xb4\x28\x6d\x1a\xbc\xfb\x26\xc5\x62\x85\x83\x7d\x92\x55\x98\x06\x6b\x8e\x9b\x5a\x69\x3b\x58\xba\xe1\x85\x2d\xd3\x02\xd7\x3c\xc7\xd0\xbd\x5c\x02\x97\xdc\x72\x26\x42\x93\x33\x81\xe9\x55\xb0\x78\x42\x74\x2c\xb7\x02\x17\xf7\xf7\xd1\xb7\x68\x37\x4a\xdf\xee\x76\x33\x78\xd3\xd8\x12\xa5\xe5\x39\xb3\x58\xc0\x5f\x58\x93\xa3\x4d\x62\xbf\xd2\x6d\x12\x5c\xde\x42\xa9\x71\x99\x06\xc4\xba\x99\xc5\x71\x5e\xc8\x0f\x26\xca\x85\x6a\x8a\xa5\x60\x1a\xa3\x5c\x55\x31\xfb\xc0\xee\x62\xc1\x33\x13\xdb\x0d\xb7\x16\x75\x98\x29\x65\x8d\xd5\xac\x8e\x6f\xa2\x9b\xe8\x8f\x71\x6e\x4c\xdc\x8f\x45\x15\x97\x51\x6e\x4c\x00\x1a\x45\x1a\x18\xbb\x15\x68\x4a\x44\x1b\x40\xbc\xf8\xc7\xce\x5d\x2a\x69\x43\xb6\x41\xa3\x2a\x8c\x5f\x44\x7f\x8c\xa6\xee\xc8\xe1\xf0\xe3\xa7\xd2\xb1\x26\xd7\xbc\xb6\x60\x74\xfe\xd1\xe7\x7e\xf8\xb5\x41\xbd\x8d\x6f\xa2\xab\xe8\xaa\x7d\x71\xe7\x7c\x30\xc1\x22\x89\x3d\xc1\xc5\x67\xd1\x0e\xa5\xb2\xdb\xf8\x3a\x7a\x11\x5d\xc5\x35\xcb\x6f\xd9\x0a\x8b\xee\x24\x9a\x8a\xba\xc1\x2f\x76\xee\x43\x36\xfc\x70\x6c\xc2\x2f\x71\x58\xa5\x2a\x94\x36\xfa\x60\xe2\xeb\xe8\xea\x75\x34\xed\x06\x4e\xe9\xbb\x03\xc8\x68\x74\xd4\x45\xb4\x46\x4d\xc8\x15\x61\x8e\xd2\xa2\x86\x7b\x1a\xbd\xa8\xb8\x0c\x4b\xe4\xab\xd2\xce\xe0\x6a\x3a\x7d\x36\x3f\x37\xba\x2e\xfd\x70\xc1\x4d\x2d\xd8\x76\x06\x4b\x81\x77\x7e\x88\x09\xbe\x92\x21\xb7\x58\x99\x19\x78\xca\x6e\x62\xe7\xce\xac\xb5\x5a\x69\x34\xa6\x3d\xac\x56\x86\x5b\xae\xe4\x8c\x10\xc5\x2c\x5f\xe3\xb9\xb5\xa6\x66\xf2\x64\x03\xcb\x8c\x12\x8d\xc5\x23\x46\x32\xa1\xf2\x5b\x3f\xe6\xbc\x79\x28\x44\xae\x84\xd2\x33\xd8\x94\xbc\xdd\x06\xee\x20\xa8\x35\xb6\xe4\xa1\x66\x45\xc1\xe5\x6a\x06\xaf\xea\x56\x1e\xa8\x98\x5e\x71\x39\x83\xe9\x7e\x4b\x12\x77\x6a\x4c\x62\x7f\x71\x3d\xb9\x48\x32\x55\x6c\x9d\x0d\x0b\xbe\x86\x5c\x30\x63\xd2\xe0\x48\xc5\xee\x42\x3a\x58\x40\xf7\x10\xe3\xb2\x9b\x3a\x98\xd3\x6a\x13\x80\x3b\x28\x0d\x3c\x13\x61\xa6\xac\x55\xd5\x0c\xae\x88\xbd\x76\xcb\x11\x3d\x11\x8a\x55\x78\x75\xdd\x4d\x5e\x24\xe5\x55\x47\xc4\xe2\x9d\x0d\x9d\x7d\x7a\xcb\x04\x8b\x84\x77\x7b\x97\x0c\x96\x2c\xcc\x98\x2d\x03\x60\x9a\xb3\xb0\xe4\x45\x81\x32\x0d\xac\x6e\x90\x70\xc4\x17\x30\xbc\xfe\x1e\xb8\xfd\xca\xab\x8e\xaf\xb8\xe0\xeb\x56\xac\xc1\xe3\x91\x84\x0f\x0b\xf1\x1a\xda\x07\xb5\x5c\x1a\xb4\xe1\x40\xa6\xc1\x62\x2e\xeb\xc6\x86\x2b\xad\x9a\xba\x9f\xbf\x48\xdc\x28\xf0\x22\x0d\x1a\x2d\x82\xf6\xfa\x77\x8f\x76\x5b\xb7\xaa\x08\x7a\xc1\x95\xae\x42\xb2\x84\x56\x22\x80\x5a\xb0\x1c\x4b\x25\x0a\xd4\x69\xf0\xa3\xca\x39\x13\x20\xbd\xcc\xf0\xd3\x0f\xff\x09\xad\xc9\xb8\x5c\xc1\x56\x35\x1a\xbe\xb1\x25\x6a\x6c\x2a\x60\x45\x41\x70\x8d\xa2\x28\x88\xf7\x9c\x38\xf0\x9e\xf2\x1a\x66\x56\xee\xf9\xbd\x48\xb2\xc6\x5a\xd5\x2f\xcc\xac\x84\xcc\xca\xb0\xc0\x25\x6b\x84\x85\x42\xab\xba\x50\x1b\x19\x5a\xb5\x5a\x51\xa8\xf3\x52\xf8\x4d\x01\x14\xcc\xb2\x76\x2a\x0d\xba\xb5\x9d\x11\x99\xa9\x55\xdd\xd4\xad\x19\xfd\x20\xde\xd5\x4c\x16\x58\x90\xd1\x85\xc1\x60\xf1\x57\xbe\x46\xa8\xd0\x0b\x73\x71\x8c\x89\x9c\x69\xb4\xe1\x90\xe8\x09\x32\x92\xd8\x33\xe3\x45\x82\xf6\x5f\xd2\x88\x8e\x52\x2f\x42\x85\xb2\x81\x83\xb7\x50\xd3\xc5\x12\x2c\xee\xef\x35\x93\x2b\x84\xa7\xbc\xb8\xbb\x84\xa7\xac\x52\x8d\xb4\x30\x4b\x21\x7a\xe3\x1e\xcd\x6e\x77\x40\x1d\x20\x11\x7c\x91\xb0\xc7\xf0\x0d\x4a\xe6\x82\xe7\xb7\x69\x60\x39\xea\xf4\xfe\x9e\x88\xef\x76\x73\xb8\xbf\xe7\x4b\x78\x1a\xfd\x80\x39\xab\x6d\x5e\xb2\xdd\x6e\xa5\xbb\xe7\x08\xef\x30\x6f\x2c\x8e\x27\xf7\xf7\x28\x0c\xee\x76\xa6\xc9\x2a\x6e\xc7\xdd\x76\x1a\x97\xc5\x6e\x47\x3c\xb7\x7c\xee\x76\x10\x13\x51\x59\xe0\x1d\x3c\x8d\xbe\x47\xcd\x55\x61\xc0\xaf\x4f\x62\xb6\x48\x62\xc1\x17\xed\xbe\x43\x25\xc5\x8d\xd8\xe3\x25\x26\xc0\xf4\x40\x77\x7e\xe3\x58\x1d\x72\x7a\xc6\x0d\x56\x61\xcf\x7d\x8b\x07\xc3\x2d\xde\xe2\x36\x0d\xee\xef\x87\x7b\xdb\xd9\x9c\x09\x91\x31\xd2\x8b\x17\xad\xdf\xf4\x1b\x12\x4e\xd7\xdc\xb8\x9c\x6a\xd1\x71\xb0\x67\xfb\x23\xfd\xfa\xe8\xe6\xb2\xaa\x9e\xc1\xcd\xf5\xe0\xda\x3a\xe7\xf2\xaf\x8e\x5c\xfe\xe6\xec\xe2\x9a\x49\x14\xe0\xfe\x86\xa6\x62\xa2\x7b\x6e\xbd\x65\x70\x0d\x1c\x6f\x0a\xe9\x92\xee\x59\xeb\x2f\xfb\xe9\x1c\xd4\x1a\xf5\x52\xa8\xcd\x0c\x58\x63\xd5\x1c\x2a\x76\xd7\x07\xbc\x9b\xe9\x74\xc8\x37\xe5\x82\x2c\x13\xe8\xae\x17\x8d\xbf\x36\x68\xac\xe9\x2f\x13\x3f\xe5\xfe\xd2\x9d\x52\xa0\x34\x58\x1c\x69\x83\x4e\x24\xd5\xba\x55\x03\xd3\xf7\xca\x3c\xcb\xfb\x52\xa9\x3e\x86\x0c\xd9\x68\x49\x0f\xc2\x5d\xb0\x48\xac\xde\xaf\xbb\x48\x6c\xf1\x49\x31\x40\x53\x8e\xf7\x50\x08\xf0\x37\x1a\xc9\x5e\x23\x6a\x9f\x60\x10\x64\xc1\xbd\x26\xb1\x2d\x3e\xe3\x64\x02\x61\xc6\x0c\x7e\xcc\xf1\x2e\xd4\xef\x8f\x77\xaf\x9f\x7b\x7e\x89\x4c\xdb\x0c\x99\xfd\x18\x06\x96\x8d\x2c\x06\xf2\xbb\xbb\xf3\x73\x19\x68\x24\x5f\xa3\x36\xdc\x6e\x3f\x96\x03\x2c\xf6\x2c\xf8\xf7\x43\x16\x92\xd8\xea\xc7\xb1\x36\x7c\xf9\x42\xce\xfd\x7b\x39\xc9\xcd\xe2\xdf\xd5\x06\x0a\x85\x06\x6c\xc9\x0d\x50\x74\xfd\x3a\x89\xcb\x9b\x7e\x49\xbd\x78\x4f\x13\x4e\xa9\xb0\x74\xb9\x05\x70\x03\xba\x91\x2e\xf4\x2a\x09\xb6\xc4\xc3\x7c\xa4\x8d\xd2\x11\xbc\x57\x94\xd3\xad\x51\x5a\xa8\x98\xe0\x39\x57\x8d\x01\x96\x5b\xa5\x0d\x2c\xb5\xaa\x00\xef\x4a\xd6\x18\x4b\x84\xe8\xfa\x60\x6b\xc6\x85\xf3\x25\x67\x52\x50\x1a\x58\x9e\x37\x55\x43\x39\xa9\x5c\x01\x4a\xd5\xac\xca\x96\x17\xab\xc0\x07\x26\xa1\xe4\xaa\xe7\xc7\xd4\xac\x02\x66\x2d\xcb\x6f\xcd\x25\x74\xb7\x02\x30\x8d\x60\x39\x16\xb4\x2b\x57\x55\xa5\x24\xdc\xe8\x02\x6a\xa6\xed\x16\xcc\x61\x72\xc1\xf2\xdc\x45\xb9\x08\xde\xc8\xad\x92\x08\x25\x5b\x3b\x0e\xe1\xbd\xaf\x27\x88\xaf\xbf\xb0\x1c\x33\xa5\xfa\xd5\x50\xb1\x6d\x77\x5c\xcb\xfd\x86\xdb\x92\x7b\xf5\xd4\xa8\x2b\xda\x5a\x80\xe0\x15\xb7\x26\x4a\xe2\x7a\x7f\xa3\xee\x63\xb3\x08\x4b\xa5\xf9\x6f\x94\xd9\x88\xe1\xf5\x69\x8f\x2e\x97\xee\x6e\x74\x56\x17\xb8\xb4\x33\x78\xe1\xef\xc6\x63\x1c\xb7\x25\xd0\x39\x10\x77\x34\x5d\x69\x49\x01\x67\x06\x37\x3e\x9f\xf5\x89\x44\x61\x07\x1c\x14\x47\x50\xf3\x87\xbe\x7e\x5d\xdf\xf5\x7c\xf4\x49\xf1\xb4\x27\x42\x08\x38\x54\xca\x9a\xf7\x6a\xbc\x84\x8a\xdd\x22\x30\x48\xd8\x51\x89\xdc\x32\xed\x0a\x2c\xee\x1a\x04\xb1\xdd\x20\xda\xaf\xc9\x75\xd3\x1f\x3c\x41\x2e\x57\xcf\xae\xa7\x1e\x91\xf4\x40\xe4\x9f\x5d\x4f\xb9\xb4\xea\xd9\xf5\x74\x7a\x37\xfd\xc8\x7f\xcf\xae\xa7\x4a\x3e\xbb\x9e\xda\x12\x9f\x5d\x4f\x9f\x5d\xdf\x0c\xb1\xec\x47\xba\xd4\x92\x56\xa1\xa1\xd3\x3a\x88\x07\x60\x99\x5e\xa1\x4d\x83\xff\x65\x99\x6a\xec\x2c\x13\x4c\xde\x06\x0b\xc7\x2e\x65\x1b\x0e\x05\xe7\x13\x54\xa8\x99\x21\x48\x10\xc7\x0e\x25\x6d\x33\xc4\xc0\xd8\x34\x5a\xab\x46\x52\x54\x04\x92\xd9\x79\xa8\x1c\x11\xca\x48\x31\x93\x28\xc9\x74\xbc\x78\xab\xea\x6d\xe8\x88\xb8\xed\x27\x6a\x34\x4d\x5d\x2b\x6d\xa3\xa1\x3a\x19\x15\x42\x02\x4d\xfc\x7a\xfa\xf2\xf5\xab\x47\xd9\x37\x94\x66\x3b\x19\x7a\x0e\x59\xa6\xd6\x08\x3e\xa9\xcf\xd4\x1d\x30\x59\xc0\x92\x6b\x04\xb6\x61\xdb\xaf\x92\xb8\x70\x25\xd8\xe7\xa3\x76\xd9\x7a\xd7\x3f\x15\x6c\x3b\x97\xbf\x84\xba\xc9\x04\x37\x25\x30\x90\xb8\x81\xc4\x58\xad\xe4\x6a\xe1\x46\x73\xaa\x49\xdd\x2b\xd4\xca\xd8\xc7\xcc\x8f\x55\x86\x45\x71\x06\x00\x5f\xca\xfe\x9b\xcd\x26\xea\x34\xe9\x8c\x5f\xa2\xa8\x63\xba\xfe\x1a\xc9\xed\x36\xf6\x6e\xa4\x64\xfc\x35\x2f\xd2\xeb\xd7\xd7\xaf\x5e\x5d\xbf\xf8\xb7\xd7\x2f\x5f\x5e\xbf\x7e\xf1\xf2\x21\x64\x90\x50\x9f\x09\x0c\x9f\x46\x7f\xab\xa8\x6c\xed\x73\x68\x8f\x97\x2e\x77\xa3\x08\x5d\x50\x0d\xa2\x83\x7f\x18\x43\x8d\xa4\x44\x24\x64\xe2\x6c\x0e\xf1\x09\x28\x72\x30\x7a\x84\xb3\xcf\x84\x56\x07\x1f\x42\x8a\x6a\x2c\x49\xd8\x55\xf3\x5c\xc9\x1e\x4e\x97\x60\x78\x55\x8b\x2d\xe4\x7b\xab\x9f\xc7\xd5\x83\x46\xf9\x5d\x58\x1d\x9a\xcd\x83\xcc\x45\xff\x4a\x15\x48\x51\xdf\x34\x26\xc7\xda\xb5\x79\x29\x92\xfe\x69\xfb\x1b\x93\x96\x4b\xec\x22\x6e\x04\xdf\x49\xb1\x85\xc6\x20\x2c\x95\x86\x02\xb3\x66\xb5\x72\x69\x82\x86\x5a\xf3\x35\xb3\xd8\x85\x59\xd3\xa2\xa2\x07\xc5\xa0\xb2\xa1\x94\x47\x0c\x32\x90\xbf\xa9\x06\x72\x26\xc1\x6a\x96\xdf\x7a\x4f\x69\xb4\x26\x4f\xa9\xd1\x4b\xd3\x07\xfa\x0c\x85\xda\xb8\x25\x5e\xee\x25\x47\xe1\xa2\xbe\x41\x84\x52\x6d\xa0\x6a\x72\xe7\x90\x14\xd5\x9d\x10\x1b\xc6\x2d\x34\xd2\x72\xe1\xf5\x69\x1b\x2d\x29\x47\xc0\x83\x28\x7d\x52\xfb\x25\x58\x2d\xde\x97\x78\x26\x25\xea\xab\x36\xd0\xf8\xd6\x2f\x87\x5a\x2b\x8b\x39\x19\x14\xd8\x8a\x71\x69\xc8\x22\x2e\x0f\xc0\xea\x23\xaa\xba\xfe\xa9\x7d\xd8\xb7\x28\xdd\x74\x1c\xc3\x5f\x85\xca\x98\x80\x35\x21\x3d\x13\x94\xce\x29\x28\x15\x89\x3e\xd0\x96\xb1\xcc\x36\x06\xd4\xd2\x8d\x7a\xce\x69\xff\x9a\x69\xb2\x20\x56\xb5\x85\xb4\x6d\xb0\xd1\x98\x41\xbd\x6e\xdb\x86\xf4\x4a\x95\xfb\xc1\x7c\xaf\xf5\x14\x7e\xfe\x65\xfe\xa4\x65\xe5\xcf\xb8\x74\x90\x20\x7c\x7b\x91\x6d\xc9\x2c\xe4\x1a\x99\x45\x03\xb9\x50\xa6\xd1\x9e\xc3\x42\xab\x1a\x88\xcb\x8e\x52\x47\x99\x26\x6a\x77\x5a\x47\x64\x5c\x32\x53\x4e\xda\xfe\xa0\x46\x67\xa5\x7e\xae\x1b\xbf\x20\xd4\x8d\x89\x00\x4f\xa7\x73\xe0\x49\x47\x37\x12\x28\x57\xb6\x9c\x03\x7f\xfe\xbc\x5f\x7c\xc1\x97\x30\xee\x56\xfc\xcc\x7f\x89\xec\x5d\x44\xa7\x40\x9a\xc2\xf0\x34\x77\x60\x4b\xc7\xd4\x82\xe7\x38\xe6\x97\x70\x35\x99\x77\xb3\x99\x46\x76\xdb\xbd\xb5\x76\xf4\xff\xb9\xbf\xbb\xf9\xa1\x66\x9c\xf2\x0f\x74\xe3\x6b\x7f\x03\x0c\x56\xdc\x58\x68\xb4\x80\xd6\x87\xbd\x09\x7a\x83\xb8\x75\x43\xad\x9c\xe0\xb2\x7d\x68\x31\xd5\x89\xe0\xc9\x44\x06\x65\x31\xfe\x8f\x1f\xbf\xfb\x36\x32\x56\x73\xb9\xe2\xcb\xed\xf8\xbe\xd1\x62\x06\x4f\xc7\xc1\xbf\x34\x5a\x04\x93\x9f\xa7\xbf\x44\x6b\x26\x1a\xbc\x74\xf6\x9e\xb9\xbf\x27\xa7\x5c\x42\xfb\x38\x83\xc3\x03\x77\x93\xc9\xfc\x7c\x9f\x64\xd0\xd6\xd1\x68\xd0\x8e\x69\x61\x0f\xfc\x63\x1d\x31\xa8\xd0\x96\xca\xb9\xae\xc6\x5c\x49\x89\xb9\x85\xa6\x56\xb2\x55\x09\x08\x65\xcc\x1e\x88\xdd\x8a\xf4\x14\x14\xed\xfa\xd4\x05\xeb\xff\xc6\xec\x47\x95\xdf\xa2\x1d\x8f\xc7\x1b\x2e\x0b\xb5\x89\x84\xf2\x57\x6d\x44\x4e\xaa\x72\x25\x20\x4d\x53\x68\xa3\x68\x30\x81\xaf\x21\xd8\x18\x8a\xa7\x01\xcc\xe8\x91\x9e\x26\xf0\x1c\x8e\xb7\x97\x14\xef\x9f\x43\x10\xb3\x9a\x07\x13\xef\x0e\x9d\xe2\x95\xac\xd0\x18\xb6\xc2\x21\x83\xae\x32\xea\x41\x46\x72\x54\x66\x05\x29\x38\x03\xd5\x4c\x1b\xf4\x4b\x22\xaa\xc6\x3b\xb4\x11\x66\xdd\xb2\x34\x05\xd9\x08\xb1\x07\xa9\x77\x8a\x79\x07\xbf\x83\xe5\x91\x8f\x35\x5f\xa5\x29\x50\x69\x4a\x2a\x2e\xf6\x3b\xc9\xf8\xbe\x88\x9e\x44\x14\x17\xf6\x3b\x26\xf3\x21\x9a\x0f\xa8\x61\xf1\x7b\xe4\xb0\x38\xa6\x87\xc5\x03\x04\x5d\xcf\xe2\x31\x7a\xbe\xc7\x31\x20\xe7\x06\x1e\xa0\x26\x9b\x2a\x43\xfd\x18\x39\xdf\xb3\x68\xc9\x39\x55\xbf\x93\x76\xb0\xf7\x12\xae\x5e\x4d\x1e\xa0\x8e\x5a\xab\x07\x89\x4b\x65\xb7\xe3\x7b\xc1\xb6\x94\x33\xc1\xc8\xaa\xfa\xad\x6b\x31\x8c\x2e\x5d\xc4\x9d\x41\x4f\xe1\xd2\x35\x8f\x67\x30\x72\x6f\x34\xcf\x2b\x74\xbb\x5e\x4e\xa7\xd3\x4b\xe8\x3e\xbb\xfc\x89\x91\x13\xea\x06\x77\x0f\xf0\x63\x9a\x3c\xa7\xb8\xff\x39\x1c\xb5\x34\x7a\x9e\xda\xf7\xcf\xe0\xaa\x8f\x0d\x07\x6c\xc1\x1f\xfe\x00\x27\xb3\x87\x30\x8e\x63\xf8\x2f\x46\x65\xb8\x10\xae\x7b\xe0\x9a\x06\xfd\xfa\x8a\x1b\xe3\x8a\x71\x03\x85\x92\xd8\xee\xf9\xb4\x6b\xff\x84\xc7\x76\x19\x2c\x60\x7a\xcc\x20\x5d\x87\x83\xb0\x70\x26\x5a\x0c\xe8\x1e\x06\x82\x8b\xdd\xf0\xbc\x83\x9d\xbc\x42\xf8\x2a\x85\x20\x18\x6e\x3e\x59\x41\x0b\x7a\x62\x17\x06\xed\x7b\x6f\x8b\x71\x1b\x1d\xcf\xc5\xae\xc9\x25\xdc\x4c\xa7\xd3\xc9\x09\x13\xbb\xbd\x7a\xdf\xd4\x94\x36\x01\x93\x5b\x77\x25\xf6\xba\x75\x89\x23\xa5\x40\x74\xa5\x09\xc8\x95\x10\x3e\x67\x69\xb7\x92\x82\xdb\xe6\x49\x0a\xe1\xd5\xfc\x4c\x14\x1d\x68\x72\x20\xda\xb1\x79\xce\xe8\xfe\xd8\x44\x87\x3a\x3b\x5a\x1c\x5e\x1d\x18\xe5\xc0\x5e\xe7\x0d\x73\xd1\xf3\xcd\xf7\x1a\x3d\x32\xd7\xde\x5e\xc7\x3a\x1b\xf0\xef\xe9\x3c\xbf\xfa\x48\x31\xfa\xe9\xba\x31\xe5\xf8\x88\xd1\xc9\xfc\xd4\x36\xef\x2c\x6a\xca\x92\x15\x85\x2c\xb2\x05\x95\x02\x1a\x4f\x4c\xe2\x52\x75\x8d\xa1\x46\x59\xa0\xee\x52\x0a\x9f\xd9\x53\x02\x78\x60\x32\x5f\x55\x0e\xe1\x34\x90\xe8\x44\xb7\x73\xe0\xb0\xa0\x34\x0f\x78\x18\x0e\x64\x71\x79\x99\x92\x08\x00\x70\xe4\x09\x0e\xad\x07\x70\xa5\xc5\x28\x58\x6d\xb0\x80\x14\xfc\xa7\xf0\xf1\x24\x6a\x24\xbf\x1b\x4f\xc2\xf6\xfd\x98\x46\x37\x3f\xef\x6b\xc5\x8e\xf7\xe7\x29\x04\x89\xd5\xc0\x8b\x74\x14\xc0\xf3\x73\x7e\x48\xa1\x77\xb4\xd8\x73\x30\xdc\x0a\x90\xd8\x62\xe1\x9a\xa1\xbe\x68\xfb\x7b\x90\xb1\xfc\x76\xe5\xaa\xa1\x19\xe5\x5b\xe3\x13\xb2\x6c\xcd\x2c\xd3\x8e\xea\x64\x0e\xfb\xe5\x6d\xb5\x98\x93\x85\xe6\xe0\xcb\x52\xd7\x73\x85\xfe\x3b\x85\x7b\xcb\x94\x2e\x50\x87\x9a\x15\xbc\x31\x33\x78\x51\xdf\xcd\xff\xde\x7d\xc7\x71\x9d\xe1\x47\x59\xad\x35\x2e\x4e\x38\x6a\x5b\x8d\xcf\x21\x48\x62\x5a\xf0\x7b\x64\x7a\x61\x87\x9f\xe0\xe1\x4c\xff\x1b\xfa\x0f\xe4\xed\x78\xc5\x8b\x42\x20\x31\xbc\x27\x4f\x1e\x49\xf6\x1f\xfa\xd5\xe1\x91\xd0\x36\xbe\xf7\x7b\x76\x80\xc2\xe0\x23\x1b\xfa\x1e\xfa\x88\x00\x10\x92\xc8\xdc\xe9\xbc\xad\xb8\xdd\xb0\x1e\x39\x5d\xb4\x3f\xa8\x28\x1a\xed\x12\xae\x71\xd8\x02\xec\x12\x46\x86\x12\xc0\xc2\x8c\x26\x51\xd9\x54\x4c\xf2\xdf\x70\x4c\xc1\x69\xe2\x75\xe5\x9a\xf2\xc1\xe9\xbd\x7c\xc2\xcc\xbe\x5b\x3e\xea\x02\xdd\xa8\x55\xe2\xa8\xb3\xee\x8b\x7d\x81\x3f\x83\xe9\x7c\xf4\x89\x1a\x3a\x7f\x4a\x98\x31\x0d\xc3\x97\xb0\x8b\xc0\xa0\x15\x9d\xde\xcd\x65\x4c\x8f\x7c\x3b\xc3\x25\xe9\x52\x6d\xd2\xd1\xcd\xb4\x67\xd2\x1b\xda\xd9\x79\xd4\x62\xed\xc4\x18\xc4\x65\xe7\x9a\x0b\xb8\x99\x7e\x09\x6e\x7d\x4b\xe4\x48\x02\xab\x79\x8d\x05\xb0\xdc\xf2\x35\xfe\x3f\x08\xf2\x05\x94\xfc\xc9\x2c\x12\x0e\x3b\xe5\x39\x98\x1e\xf0\x4b\xb3\xbd\x6e\xff\x95\xfc\x0d\x62\xa7\xe1\xe7\x10\x9c\x15\xe4\x41\x24\x1e\x2d\x3c\x72\xed\x87\xfd\xde\x7d\x65\x0a\x8e\x03\x0b\xa5\xbc\xfd\x17\xd2\x49\x54\xda\x4a\x8c\x83\xc4\xba\x9f\xca\x10\xcf\x3d\x05\x47\xc0\x0f\x1f\xe6\x75\xbb\xc3\x6a\x86\x8a\x78\x3c\x2a\xb6\x60\x90\xa1\xf4\x05\x59\x97\x8e\xc0\x6e\xff\x8b\xa2\x38\x86\x1f\x2d\xd3\x16\x18\xfc\xf4\x0e\x9a\xba\x60\xd6\x7f\xcf\xa1\x20\xe9\xbf\x97\x74\x3f\x39\xca\x98\x36\xb0\x54\x7a\xc3\x74\xd1\x36\x69\x6c\x89\x5b\xf7\x3d\xa7\xcb\xff\x0c\xda\x77\x74\x8b\xad\x99\x18\x9f\x14\x7f\x4f\xc7\xa3\x68\x68\xf2\xd1\x24\x42\x96\x97\xa7\x0b\x5d\xc4\xea\xcf\x4d\xe1\x5b\x57\x07\x8c\x9f\x8e\x6d\xc9\xcd\x24\x62\xd6\xea\xf1\xe8\x00\x0c\xa3\x09\xd9\xf5\x6a\x50\x97\xf5\xdb\x93\x03\xb7\x7a\x8c\xc6\x3e\xa3\xee\xb3\x81\x6e\x79\x6e\xcc\xd8\xe3\x6a\x74\x39\xa0\x7d\x08\xab\xd1\xb3\x51\x6f\xa8\xbd\x7b\xef\xe5\x48\xcf\x72\x72\x40\x7a\x44\x5e\x36\x3a\x39\x9e\x15\xc5\x5b\xf2\x9f\x71\x70\xc6\xd3\x8f\xd1\x31\xe9\x95\xed\xef\xeb\x47\xb5\xec\x7f\x9b\xf1\x80\x8a\x79\x31\x9a\x44\xa6\xc9\x7c\x83\x62\xfc\xb2\xaf\xc2\xba\x65\x0e\xbc\xc7\xa1\xe0\x24\xa1\xa0\x23\x0e\x93\x8a\xf0\x28\x09\x79\x24\x6a\xb4\x47\x7a\xa9\x76\x97\xa4\xf0\xe9\xa4\xef\x6f\x7d\x63\x28\xc3\xf2\xfd\xff\x0d\x66\xc6\xb5\x13\xa0\xc5\xbb\x6b\xe9\xf8\xd6\xcd\x9b\xef\xdf\x0d\xda\x37\xbd\x47\x8c\x1d\xf5\xfe\xd7\x80\xe7\x9a\x25\x67\x7f\x7e\xb8\xd9\x6c\xa2\x95\x52\x2b\xe1\x7f\x78\xd8\x77\x53\x62\x56\xf3\xe8\x83\x09\x80\x99\xad\xcc\xa1\xc0\x25\xea\xc5\x80\x7c\xdb\x62\x49\x62\xff\xc3\xb8\x24\xf6\xbf\xfd\xfd\xbf\x00\x00\x00\xff\xff\xb2\x1e\x6f\x68\x0c\x2c\x00\x00") - -func faucetHtmlBytes() ([]byte, error) { - return bindataRead( - _faucetHtml, - "faucet.html", - ) -} - -func faucetHtml() (*asset, error) { - bytes, err := faucetHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "faucet.html", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc5, 0x8d, 0xb, 0x7a, 0xfd, 0x70, 0x68, 0x68, 0xd2, 0xd8, 0xf3, 0xf6, 0xac, 0x72, 0xed, 0xc2, 0x76, 0x18, 0x2d, 0x1, 0xe5, 0x3b, 0x55, 0xb, 0xce, 0xfc, 0xb6, 0xd5, 0x59, 0xc3, 0x94, 0x5b}} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// AssetString returns the asset contents as a string (instead of a []byte). -func AssetString(name string) (string, error) { - data, err := Asset(name) - return string(data), err -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// MustAssetString is like AssetString but panics when Asset would return an -// error. It simplifies safe initialization of global variables. -func MustAssetString(name string) string { - return string(MustAsset(name)) -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetDigest returns the digest of the file with the given name. It returns an -// error if the asset could not be found or the digest could not be loaded. -func AssetDigest(name string) ([sha256.Size]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) - } - return a.digest, nil - } - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) -} - -// Digests returns a map of all known files and their checksums. -func Digests() (map[string][sha256.Size]byte, error) { - mp := make(map[string][sha256.Size]byte, len(_bindata)) - for name := range _bindata { - a, err := _bindata[name]() - if err != nil { - return nil, err - } - mp[name] = a.digest - } - return mp, nil -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "faucet.html": faucetHtml, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"}, -// AssetDir("data/img") would return []string{"a.png", "b.png"}, -// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - canonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(canonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "faucet.html": {faucetHtml, map[string]*bintree{}}, -}} - -// RestoreAsset restores an asset under the given directory. -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) -} - -// RestoreAssets restores an asset under the given directory recursively. -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - canonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) -}
diff --git go-ethereum/cmd/puppeth/wizard_faucet.go celo/cmd/puppeth/wizard_faucet.go deleted file mode 100644 index 32fb24f74cf00bbb66eadb85bade80159b791bc0..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/wizard_faucet.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "encoding/json" - "fmt" - - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/log" -) - -// deployFaucet queries the user for various input on deploying a faucet, after -// which it executes it. -func (w *wizard) deployFaucet() { - // Select the server to interact with - server := w.selectServer() - if server == "" { - return - } - client := w.servers[server] - - // Retrieve any active faucet configurations from the server - infos, err := checkFaucet(client, w.network) - if err != nil { - infos = &faucetInfos{ - node: &nodeInfos{port: 30303, peersTotal: 25}, - port: 80, - host: client.server, - amount: 1, - minutes: 1440, - tiers: 3, - } - } - existed := err == nil - - infos.node.genesis, _ = json.MarshalIndent(w.conf.Genesis, "", " ") - infos.node.network = w.conf.Genesis.Config.ChainID.Int64() - - // Figure out which port to listen on - fmt.Println() - fmt.Printf("Which port should the faucet listen on? (default = %d)\n", infos.port) - infos.port = w.readDefaultInt(infos.port) - - // Figure which virtual-host to deploy ethstats on - if infos.host, err = w.ensureVirtualHost(client, infos.port, infos.host); err != nil { - log.Error("Failed to decide on faucet host", "err", err) - return - } - // Port and proxy settings retrieved, figure out the funding amount per period configurations - fmt.Println() - fmt.Printf("How many Ethers to release per request? (default = %d)\n", infos.amount) - infos.amount = w.readDefaultInt(infos.amount) - - fmt.Println() - fmt.Printf("How many minutes to enforce between requests? (default = %d)\n", infos.minutes) - infos.minutes = w.readDefaultInt(infos.minutes) - - fmt.Println() - fmt.Printf("How many funding tiers to feature (x2.5 amounts, x3 timeout)? (default = %d)\n", infos.tiers) - infos.tiers = w.readDefaultInt(infos.tiers) - if infos.tiers == 0 { - log.Error("At least one funding tier must be set") - return - } - // Accessing the reCaptcha service requires API authorizations, request it - if infos.captchaToken != "" { - fmt.Println() - fmt.Println("Reuse previous reCaptcha API authorization (y/n)? (default = yes)") - if !w.readDefaultYesNo(true) { - infos.captchaToken, infos.captchaSecret = "", "" - } - } - if infos.captchaToken == "" { - // No previous authorization (or old one discarded) - fmt.Println() - fmt.Println("Enable reCaptcha protection against robots (y/n)? (default = no)") - if !w.readDefaultYesNo(false) { - log.Warn("Users will be able to requests funds via automated scripts") - } else { - // Captcha protection explicitly requested, read the site and secret keys - fmt.Println() - fmt.Printf("What is the reCaptcha site key to authenticate human users?\n") - infos.captchaToken = w.readString() - - fmt.Println() - fmt.Printf("What is the reCaptcha secret key to verify authentications? (won't be echoed)\n") - infos.captchaSecret = w.readPassword() - } - } - // Accessing the Twitter API requires a bearer token, request it - if infos.twitterToken != "" { - fmt.Println() - fmt.Println("Reuse previous Twitter API token (y/n)? (default = yes)") - if !w.readDefaultYesNo(true) { - infos.twitterToken = "" - } - } - if infos.twitterToken == "" { - // No previous twitter token (or old one discarded) - fmt.Println() - fmt.Println() - fmt.Printf("What is the Twitter API app Bearer token?\n") - infos.twitterToken = w.readString() - } - // Figure out where the user wants to store the persistent data - fmt.Println() - if infos.node.datadir == "" { - fmt.Printf("Where should data be stored on the remote machine?\n") - infos.node.datadir = w.readString() - } else { - fmt.Printf("Where should data be stored on the remote machine? (default = %s)\n", infos.node.datadir) - infos.node.datadir = w.readDefaultString(infos.node.datadir) - } - // Figure out which port to listen on - fmt.Println() - fmt.Printf("Which TCP/UDP port should the light client listen on? (default = %d)\n", infos.node.port) - infos.node.port = w.readDefaultInt(infos.node.port) - - // Set a proper name to report on the stats page - fmt.Println() - if infos.node.ethstats == "" { - fmt.Printf("What should the node be called on the stats page?\n") - infos.node.ethstats = w.readString() + ":" + w.conf.ethstats - } else { - fmt.Printf("What should the node be called on the stats page? (default = %s)\n", infos.node.ethstats) - infos.node.ethstats = w.readDefaultString(infos.node.ethstats) + ":" + w.conf.ethstats - } - // Load up the credential needed to release funds - if infos.node.keyJSON != "" { - if key, err := keystore.DecryptKey([]byte(infos.node.keyJSON), infos.node.keyPass); err != nil { - infos.node.keyJSON, infos.node.keyPass = "", "" - } else { - fmt.Println() - fmt.Printf("Reuse previous (%s) funding account (y/n)? (default = yes)\n", key.Address.Hex()) - if !w.readDefaultYesNo(true) { - infos.node.keyJSON, infos.node.keyPass = "", "" - } - } - } - for i := 0; i < 3 && infos.node.keyJSON == ""; i++ { - fmt.Println() - fmt.Println("Please paste the faucet's funding account key JSON:") - infos.node.keyJSON = w.readJSON() - - fmt.Println() - fmt.Println("What's the unlock password for the account? (won't be echoed)") - infos.node.keyPass = w.readPassword() - - if _, err := keystore.DecryptKey([]byte(infos.node.keyJSON), infos.node.keyPass); err != nil { - log.Error("Failed to decrypt key with given password") - infos.node.keyJSON = "" - infos.node.keyPass = "" - } - } - // Check if the user wants to run the faucet in debug mode (noauth) - noauth := "n" - if infos.noauth { - noauth = "y" - } - fmt.Println() - fmt.Printf("Permit non-authenticated funding requests (y/n)? (default = %v)\n", infos.noauth) - infos.noauth = w.readDefaultString(noauth) != "n" - - // Try to deploy the faucet server on the host - nocache := false - if existed { - fmt.Println() - fmt.Printf("Should the faucet be built from scratch (y/n)? (default = no)\n") - nocache = w.readDefaultYesNo(false) - } - if out, err := deployFaucet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil { - log.Error("Failed to deploy faucet container", "err", err) - if len(out) > 0 { - fmt.Printf("%s\n", out) - } - return - } - // All ok, run a network scan to pick any changes up - w.networkStats() -}
diff --git go-ethereum/cmd/puppeth/wizard_dashboard.go celo/cmd/puppeth/wizard_dashboard.go deleted file mode 100644 index b64bdca0b98b855cb0282b696aab27390511990c..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/wizard_dashboard.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/log" -) - -// deployDashboard queries the user for various input on deploying a web-service -// dashboard, after which is pushes the container. -func (w *wizard) deployDashboard() { - // Select the server to interact with - server := w.selectServer() - if server == "" { - return - } - client := w.servers[server] - - // Retrieve any active dashboard configurations from the server - infos, err := checkDashboard(client, w.network) - if err != nil { - infos = &dashboardInfos{ - port: 80, - host: client.server, - } - } - existed := err == nil - - // Figure out which port to listen on - fmt.Println() - fmt.Printf("Which port should the dashboard listen on? (default = %d)\n", infos.port) - infos.port = w.readDefaultInt(infos.port) - - // Figure which virtual-host to deploy the dashboard on - infos.host, err = w.ensureVirtualHost(client, infos.port, infos.host) - if err != nil { - log.Error("Failed to decide on dashboard host", "err", err) - return - } - // Port and proxy settings retrieved, figure out which services are available - available := make(map[string][]string) - for server, services := range w.services { - for _, service := range services { - available[service] = append(available[service], server) - } - } - for _, service := range []string{"ethstats", "explorer", "faucet"} { - // Gather all the locally hosted pages of this type - var pages []string - for _, server := range available[service] { - client := w.servers[server] - if client == nil { - continue - } - // If there's a service running on the machine, retrieve it's port number - var port int - switch service { - case "ethstats": - if infos, err := checkEthstats(client, w.network); err == nil { - port = infos.port - } - case "explorer": - if infos, err := checkExplorer(client, w.network); err == nil { - port = infos.port - } - case "faucet": - if infos, err := checkFaucet(client, w.network); err == nil { - port = infos.port - } - } - if page, err := resolve(client, w.network, service, port); err == nil && page != "" { - pages = append(pages, page) - } - } - // Prompt the user to chose one, enter manually or simply not list this service - defLabel, defChoice := "don't list", len(pages)+2 - if len(pages) > 0 { - defLabel, defChoice = pages[0], 1 - } - fmt.Println() - fmt.Printf("Which %s service to list? (default = %s)\n", service, defLabel) - for i, page := range pages { - fmt.Printf(" %d. %s\n", i+1, page) - } - fmt.Printf(" %d. List external %s service\n", len(pages)+1, service) - fmt.Printf(" %d. Don't list any %s service\n", len(pages)+2, service) - - choice := w.readDefaultInt(defChoice) - if choice < 0 || choice > len(pages)+2 { - log.Error("Invalid listing choice, aborting") - return - } - var page string - switch { - case choice <= len(pages): - page = pages[choice-1] - case choice == len(pages)+1: - fmt.Println() - fmt.Printf("Which address is the external %s service at?\n", service) - page = w.readString() - default: - // No service hosting for this - } - // Save the users choice - switch service { - case "ethstats": - infos.ethstats = page - case "explorer": - infos.explorer = page - case "faucet": - infos.faucet = page - } - } - // If we have ethstats running, ask whether to make the secret public or not - if w.conf.ethstats != "" { - fmt.Println() - fmt.Println("Include ethstats secret on dashboard (y/n)? (default = yes)") - infos.trusted = w.readDefaultYesNo(true) - } - // Try to deploy the dashboard container on the host - nocache := false - if existed { - fmt.Println() - fmt.Printf("Should the dashboard be built from scratch (y/n)? (default = no)\n") - nocache = w.readDefaultYesNo(false) - } - if out, err := deployDashboard(client, w.network, &w.conf, infos, nocache); err != nil { - log.Error("Failed to deploy dashboard container", "err", err) - if len(out) > 0 { - fmt.Printf("%s\n", out) - } - return - } - // All ok, run a network scan to pick any changes up - w.networkStats() -}
diff --git go-ethereum/cmd/puppeth/wizard_ethstats.go celo/cmd/puppeth/wizard_ethstats.go deleted file mode 100644 index 5af50c256c7e69f6b691c439179fc0c4939d1fff..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/wizard_ethstats.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "fmt" - "sort" - - "github.com/ethereum/go-ethereum/log" -) - -// deployEthstats queries the user for various input on deploying an ethstats -// monitoring server, after which it executes it. -func (w *wizard) deployEthstats() { - // Select the server to interact with - server := w.selectServer() - if server == "" { - return - } - client := w.servers[server] - - // Retrieve any active ethstats configurations from the server - infos, err := checkEthstats(client, w.network) - if err != nil { - infos = &ethstatsInfos{ - port: 80, - host: client.server, - secret: "", - } - } - existed := err == nil - - // Figure out which port to listen on - fmt.Println() - fmt.Printf("Which port should ethstats listen on? (default = %d)\n", infos.port) - infos.port = w.readDefaultInt(infos.port) - - // Figure which virtual-host to deploy ethstats on - if infos.host, err = w.ensureVirtualHost(client, infos.port, infos.host); err != nil { - log.Error("Failed to decide on ethstats host", "err", err) - return - } - // Port and proxy settings retrieved, figure out the secret and boot ethstats - fmt.Println() - if infos.secret == "" { - fmt.Printf("What should be the secret password for the API? (must not be empty)\n") - infos.secret = w.readString() - } else { - fmt.Printf("What should be the secret password for the API? (default = %s)\n", infos.secret) - infos.secret = w.readDefaultString(infos.secret) - } - // Gather any banned lists to ban from reporting - if existed { - fmt.Println() - fmt.Printf("Keep existing IP %v in the banned list (y/n)? (default = yes)\n", infos.banned) - if !w.readDefaultYesNo(true) { - // The user might want to clear the entire list, although generally probably not - fmt.Println() - fmt.Printf("Clear out the banned list and start over (y/n)? (default = no)\n") - if w.readDefaultYesNo(false) { - infos.banned = nil - } - // Offer the user to explicitly add/remove certain IP addresses - fmt.Println() - fmt.Println("Which additional IP addresses should be in the banned list?") - for { - if ip := w.readIPAddress(); ip != "" { - infos.banned = append(infos.banned, ip) - continue - } - break - } - fmt.Println() - fmt.Println("Which IP addresses should not be in the banned list?") - for { - if ip := w.readIPAddress(); ip != "" { - for i, addr := range infos.banned { - if ip == addr { - infos.banned = append(infos.banned[:i], infos.banned[i+1:]...) - break - } - } - continue - } - break - } - sort.Strings(infos.banned) - } - } - // Try to deploy the ethstats server on the host - nocache := false - if existed { - fmt.Println() - fmt.Printf("Should the ethstats be built from scratch (y/n)? (default = no)\n") - nocache = w.readDefaultYesNo(false) - } - trusted := make([]string, 0, len(w.servers)) - for _, client := range w.servers { - if client != nil { - trusted = append(trusted, client.address) - } - } - if out, err := deployEthstats(client, w.network, infos.port, infos.secret, infos.host, trusted, infos.banned, nocache); err != nil { - log.Error("Failed to deploy ethstats container", "err", err) - if len(out) > 0 { - fmt.Printf("%s\n", out) - } - return - } - // All ok, run a network scan to pick any changes up - w.networkStats() -}
diff --git go-ethereum/cmd/puppeth/module_dashboard.go celo/cmd/puppeth/module_dashboard.go deleted file mode 100644 index 98e28a6aabcd3f21be25065633e089fa34e4e688..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/module_dashboard.go +++ /dev/null @@ -1,692 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "html/template" - "math/rand" - "path/filepath" - "strconv" - "strings" - - "github.com/ethereum/go-ethereum/log" -) - -// dashboardContent is the actual dashboard HTML content to serve up when users -// load the dashboard website. -var dashboardContent = ` -<!DOCTYPE html> -<html lang="en"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> - <!-- Meta, title, CSS, favicons, etc. --> - <meta charset="utf-8"> - <meta http-equiv="X-UA-Compatible" content="IE=edge"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <title>{{.NetworkTitle}}: Network Dashboard</title> - - <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> - <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> - <link href="https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.3.0/css/custom.min.css" rel="stylesheet"> - <style> - .vertical-center { - min-height: 100%; - min-height: 95vh; - display: flex; - align-items: center; - } - .nav.side-menu li a { - font-size: 18px; - } - .nav-sm .nav.side-menu li a { - font-size: 10px; - } - pre{ - white-space: pre-wrap; - } - </style> - </head> - - <body class="nav-sm" style="overflow-x: hidden"> - <div class="container body"> - <div class="main_container"> - <div class="col-md-3 left_col"> - <div class="left_col scroll-view"> - <div class="navbar nav_title" style="border: 0; margin-top: 8px;"> - <a class="site_title"><i class="fa fa-globe" style="margin-left: 6px"></i> <span>{{.NetworkTitle}} Testnet</span></a> - </div> - <div class="clearfix"></div> - <br /> - <div id="sidebar-menu" class="main_menu_side hidden-print main_menu"> - <div class="menu_section"> - <ul class="nav side-menu"> - {{if .EthstatsPage}}<li id="stats_menu"><a onclick="load('#stats')"><i class="fa fa-tachometer"></i> Network Stats</a></li>{{end}} - {{if .ExplorerPage}}<li id="explorer_menu"><a onclick="load('#explorer')"><i class="fa fa-database"></i> Block Explorer</a></li>{{end}} - {{if .FaucetPage}}<li id="faucet_menu"><a onclick="load('#faucet')"><i class="fa fa-bath"></i> Crypto Faucet</a></li>{{end}} - <li id="connect_menu"><a><i class="fa fa-plug"></i> Connect Yourself</a> - <ul id="connect_list" class="nav child_menu"> - <li><a onclick="$('#connect_menu').removeClass('active'); $('#connect_list').toggle(); load('#geth')">Go Ethereum: Geth</a></li> - <li><a onclick="$('#connect_menu').removeClass('active'); $('#connect_list').toggle(); load('#mobile')">Go Ethereum: Android & iOS</a></li>{{if .Ethash}} - <li><a onclick="$('#connect_menu').removeClass('active'); $('#connect_list').toggle(); load('#other')">Other Ethereum Clients</a></li>{{end}} - </ul> - </li> - <li id="about_menu"><a onclick="load('#about')"><i class="fa fa-heartbeat"></i> About Puppeth</a></li> - </ul> - </div> - </div> - </div> - </div> - <div class="right_col" role="main" style="padding: 0 !important"> - <div id="geth" hidden style="padding: 16px;"> - <div class="page-title"> - <div class="title_left"> - <h3>Connect Yourself &ndash; Go Ethereum: Geth</h3> - </div> - </div> - <div class="clearfix"></div> - <div class="row"> - <div class="col-md-6"> - <div class="x_panel"> - <div class="x_title"> - <h2><i class="fa fa-archive" aria-hidden="true"></i> Archive node <small>Retains all historical data</small></h2> - <div class="clearfix"></div> - </div> - <div class="x_content"> - <p>An archive node synchronizes the blockchain by downloading the full chain from the genesis block to the current head block, executing all the transactions contained within. As the node crunches through the transactions, all past historical state is stored on disk, and can be queried for each and every block.</p> - <p>Initial processing required to execute all transactions may require non-negligible time and disk capacity required to store all past state may be non-insignificant. High end machines with SSD storage, modern CPUs and 8GB+ RAM are recommended.</p> - <br/> - <p>To run an archive node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: - <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> - <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=1024 --syncmode=full{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre> - </p> - <br/> - <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> - </div> - </div> - </div> - <div class="col-md-6"> - <div class="x_panel"> - <div class="x_title"> - <h2><i class="fa fa-laptop" aria-hidden="true"></i> Full node <small>Retains recent data only</small></h2> - <div class="clearfix"></div> - </div> - <div class="x_content"> - <p>A full node synchronizes the blockchain by downloading the full chain from the genesis block to the current head block, but does not execute the transactions. Instead, it downloads all the transactions receipts along with the entire recent state. As the node downloads the recent state directly, historical data can only be queried from that block onward.</p> - <p>Initial processing required to synchronize is more bandwidth intensive, but is light on the CPU and has significantly reduced disk requirements. Mid range machines with HDD storage, decent CPUs and 4GB+ RAM should be enough.</p> - <br/> - <p>To run a full node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: - <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> - <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=512{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre> - </p> - <br/> - <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> - </div> - </div> - </div> - </div> - <div class="clearfix"></div> - <div class="row"> - <div class="col-md-6"> - <div class="x_panel"> - <div class="x_title"> - <h2><i class="fa fa-mobile" aria-hidden="true"></i> Light node <small>Retrieves data on demand</small></h2> - <div class="clearfix"></div> - </div> - <div class="x_content"> - <p>A light node synchronizes the blockchain by downloading and verifying only the chain of headers from the genesis block to the current head, without executing any transactions or retrieving any associated state. As no state is available locally, any interaction with the blockchain relies on on-demand data retrievals from remote nodes.</p> - <p>Initial processing required to synchronize is light, as it only verifies the validity of the headers; similarly required disk capacity is small, tallying around 500 bytes per header. Low end machines with arbitrary storage, weak CPUs and 512MB+ RAM should cope well.</p> - <br/> - <p>To run a light node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: - <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> - <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre> - </p> - <br/> - <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> - </div> - </div> - </div> - <div class="col-md-6"> - <div class="x_panel"> - <div class="x_title"> - <h2><i class="fa fa-microchip" aria-hidden="true"></i> Embedded node <small>Conserves memory vs. speed</small></h2> - <div class="clearfix"></div> - </div> - <div class="x_content"> - <p>An embedded node is a variation of the light node with configuration parameters tuned towards low memory footprint. As such, it may sacrifice processing and disk IO performance to conserve memory. It should be considered an <strong>experimental</strong> direction for now without hard guarantees or bounds on the resources used.</p> - <p>Initial processing required to synchronize is light, as it only verifies the validity of the headers; similarly required disk capacity is small, tallying around 500 bytes per header. Embedded machines with arbitrary storage, low power CPUs and 128MB+ RAM may work.</p> - <br/> - <p>To run an embedded node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: - <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> - <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=16 --ethash.cachesinmem=1 --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre> - </p> - <br/> - <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> - </div> - </div> - </div> - </div> - </div> - <div id="mobile" hidden style="padding: 16px;"> - <div class="page-title"> - <div class="title_left"> - <h3>Connect Yourself &ndash; Go Ethereum: Android &amp; iOS</h3> - </div> - </div> - <div class="clearfix"></div> - <div class="row"> - <div class="col-md-6"> - <div class="x_panel"> - <div class="x_title"> - <h2><i class="fa fa-android" aria-hidden="true"></i> Android devices <small>Accesses Ethereum via Java</small></h2> - <div class="clearfix"></div> - </div> - <div class="x_content"> - <p>Starting with the 1.5 release of go-ethereum, we've transitioned away from shipping only full blown Ethereum clients and started focusing on releasing the code as reusable packages initially for Go projects, then later for Java based Android projects too. Mobile support is still evolving, hence is bound to change often and hard, but the Ethereum network can nonetheless be accessed from Android too.</p> - <p>Under the hood the Android library is backed by a go-ethereum light node, meaning that given a not-too-old Android device, you should be able to join the network without significant issues. Certain functionality is not yet available and rough edges are bound to appear here and there, please report issues if you find any.</p> - <br/> - <p>The stable Android archives are distributed via Maven Central, and the develop snapshots via the Sonatype repositories. Before proceeding, please ensure you have a recent version configured in your Android project. You can find details in <a href="https://github.com/ethereum/go-ethereum/wiki/Mobile:-Introduction#android-archive" target="about:blank">Mobile: Introduction &ndash; Android archive</a>. - <p>Before connecting to the Ethereum network, download the <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> genesis json file and either store it in your Android project as a resource file you can access, or save it as a string in a variable. You're going to need to initialize your client.</p> - <p>Inside your Java code you can now import the geth archive and connect to Ethereum: - <pre>import org.ethereum.geth.*;</pre> -<pre> -Enodes bootnodes = new Enodes();{{range .Bootnodes}} -bootnodes.append(new Enode("{{.}}"));{{end}} - -NodeConfig config = new NodeConfig(); -config.setBootstrapNodes(bootnodes); -config.setEthereumNetworkID({{.NetworkID}}); -config.setEthereumGenesis(genesis);{{if .Ethstats}} -config.setEthereumNetStats("{{.Ethstats}}");{{end}} - -Node node = new Node(getFilesDir() + "/.{{.Network}}", config); -node.start(); -</pre> - <p> - </div> - </div> - </div> - <div class="col-md-6"> - <div class="x_panel"> - <div class="x_title"> - <h2><i class="fa fa-apple" aria-hidden="true"></i> iOS devices <small>Accesses Ethereum via ObjC/Swift</small></h2> - <div class="clearfix"></div> - </div> - <div class="x_content"> - <p>Starting with the 1.5 release of go-ethereum, we've transitioned away from shipping only full blown Ethereum clients and started focusing on releasing the code as reusable packages initially for Go projects, then later for ObjC/Swift based iOS projects too. Mobile support is still evolving, hence is bound to change often and hard, but the Ethereum network can nonetheless be accessed from iOS too.</p> - <p>Under the hood the iOS library is backed by a go-ethereum light node, meaning that given a not-too-old Apple device, you should be able to join the network without significant issues. Certain functionality is not yet available and rough edges are bound to appear here and there, please report issues if you find any.</p> - <br/> - <p>Both stable and develop builds of the iOS framework are available via CocoaPods. Before proceeding, please ensure you have a recent version configured in your iOS project. You can find details in <a href="https://github.com/ethereum/go-ethereum/wiki/Mobile:-Introduction#ios-framework" target="about:blank">Mobile: Introduction &ndash; iOS framework</a>. - <p>Before connecting to the Ethereum network, download the <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> genesis json file and either store it in your iOS project as a resource file you can access, or save it as a string in a variable. You're going to need to initialize your client.</p> - <p>Inside your Swift code you can now import the geth framework and connect to Ethereum (ObjC should be analogous): - <pre>import Geth</pre> -<pre> -var error: NSError? - -let bootnodes = GethNewEnodesEmpty(){{range .Bootnodes}} -bootnodes?.append(GethNewEnode("{{.}}", &error)){{end}} - -let config = GethNewNodeConfig() -config?.setBootstrapNodes(bootnodes) -config?.setEthereumNetworkID({{.NetworkID}}) -config?.setEthereumGenesis(genesis){{if .Ethstats}} -config?.setEthereumNetStats("{{.Ethstats}}"){{end}} - -let datadir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] -let node = GethNewNode(datadir + "/.{{.Network}}", config, &error); -try! node?.start(); -</pre> - <p> - </div> - </div> - </div> - </div> - </div>{{if .Ethash}} - <div id="other" hidden style="padding: 16px;"> - <div class="page-title"> - <div class="title_left"> - <h3>Connect Yourself &ndash; Other Ethereum Clients</h3> - </div> - </div> - <div class="clearfix"></div> - <div class="row"> - <div class="col-md-6"> - <div class="x_panel"> - <div class="x_title"> - <h2> - <svg height="14px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 115 115"><path fill="#5C8DBC" d="M9.7 83.3V35.5s0-3.4 3.3-5.2c3.3-1.8 39.6-23.5 39.6-23.5s4.6-3.1 9.4 0c0 0 43.1 23.9 42.4 25.3L85.3 43.3s-3.6-8.4-13.1-13c-11.3-5.5-29.7-6.2-42.9 13.3 0 0-8.6 13.5.3 31.6l-19 10.7s-.9-.6-.9-2.6z"/><path fill="#5C8DBC" d="M71 51.3c-2.8-4.7-7.9-7.9-13.8-7.9-8.8 0-16 7.2-16 16 0 2.8.7 5.4 2 7.7L71 51.3z"/><path fill="#194674" d="M43.1 67c2.8 4.7 7.9 7.9 13.8 7.9 8.8 0 16-7.2 16-16 0-2.8-.7-5.4-2-7.7L43.1 67z"/><path fill="#1B598E" d="M104.4 32.1s1.3 52.6-.3 53.6L58 58.6l46.4-26.5z"/><path fill="#FFF" d="M90 57h-3.9v-4.1h-4.2V57h-4v4.1h4V65h4.2v-3.9H90zm13.6 0h-3.9v-4.1h-4.2V57h-4v4.1h4V65h4.2v-3.9h3.9z"/><path fill="#194674" d="M29.5 75.1s9.2 17 28.5 16.1 27.3-16.6 27.3-16.6L104 85.4s4.1.8-41.6 25.7c0 0-4.9 3.3-10.2 0 0 0-41.3-23.1-41.6-25.3l18.9-10.7z"/></svg> - C++ Ethereum <small>Official C++ client from the Ethereum Foundation</small> - </h2> - <div class="clearfix"></div> - </div> - <div class="x_content"> - <p>C++ Ethereum is the third most popular of the Ethereum clients, focusing on code portability to a broad range of operating systems and hardware. The client is currently a full node with transaction processing based synchronization.</p> - <br/> - <p>To run a cpp-ethereum node, download <a href="/{{.CppGenesis}}"><code>{{.CppGenesis}}</code></a> and start the node with: - <pre>eth --config {{.CppGenesis}} --datadir $HOME/.{{.Network}} --peerset "{{.CppBootnodes}}"</pre> - </p> - <br/> - <p>You can find cpp-ethereum at <a href="https://github.com/ethereum/cpp-ethereum/" target="about:blank">https://github.com/ethereum/cpp-ethereum/</a>.</p> - </div> - </div> - </div> - <div class="col-md-6"> - <div class="x_panel"> - <div class="x_title"> - <h2> - <svg height="14px" version="1.1" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M46.42,13.07S24.51,18.54,35,30.6c3.09,3.55-.81,6.75-0.81,6.75s7.84-4,4.24-9.11C35,23.51,32.46,21.17,46.42,13.07ZM32.1,16.88C45.05,6.65,38.4,0,38.4,0c2.68,10.57-9.46,13.76-13.84,20.34-3,4.48,1.46,9.3,7.53,14.77C29.73,29.77,21.71,25.09,32.1,16.88Z" transform="translate(-8.4)" fill="#e57125"/><path d="M23.6,49.49c-9.84,2.75,6,8.43,18.51,3.06a23.06,23.06,0,0,1-3.52-1.72,36.62,36.62,0,0,1-13.25.56C21.16,50.92,23.6,49.49,23.6,49.49Zm17-5.36a51.7,51.7,0,0,1-17.1.82c-4.19-.43-1.45-2.46-1.45-2.46-10.84,3.6,6,7.68,21.18,3.25A7.59,7.59,0,0,1,40.62,44.13ZM51.55,54.68s1.81,1.49-2,2.64c-7.23,2.19-30.1,2.85-36.45.09-2.28-1,2-2.37,3.35-2.66a8.69,8.69,0,0,1,2.21-.25c-2.54-1.79-16.41,3.51-7,5C37.15,63.67,58.17,57.67,51.55,54.68ZM42.77,39.12a20.42,20.42,0,0,1,2.93-1.57s-4.83.86-9.65,1.27A87.37,87.37,0,0,1,20.66,39c-7.51-1,4.12-3.77,4.12-3.77A22,22,0,0,0,14.7,37.61C8.14,40.79,31,42.23,42.77,39.12Zm2.88,7.77a1,1,0,0,1-.24.31C61.44,43,55.54,32.35,47.88,35a2.19,2.19,0,0,0-1,.79,9,9,0,0,1,1.37-.37C52.1,34.66,57.65,40.65,45.64,46.89Zm0.43,14.75a94.76,94.76,0,0,1-29.17.45s1.47,1.22,9,1.7c11.53,0.74,29.22-.41,29.64-5.86C55.6,57.94,54.79,60,46.08,61.65Z" transform="translate(-8.4)" fill="#5482a2"/></svg> - Ethereum Harmony<small>Third party Java client from EtherCamp</small> - </h2> - <div class="clearfix"></div> - </div> - <div class="x_content"> - <p>Ethereum Harmony is a web user-interface based graphical Ethereum client built on top of the EthereumJ Java implementation of the Ethereum protocol. The client currently is a full node with state download based synchronization.</p> - <br/> - <p>To run an Ethereum Harmony node, download <a href="/{{.HarmonyGenesis}}"><code>{{.HarmonyGenesis}}</code></a> and start the node with: - <pre>./gradlew runCustom -DgenesisFile={{.HarmonyGenesis}} -Dpeer.networkId={{.NetworkID}} -Ddatabase.dir=$HOME/.harmony/{{.Network}} {{.HarmonyBootnodes}} </pre> - </p> - <br/> - <p>You can find Ethereum Harmony at <a href="https://github.com/ether-camp/ethereum-harmony/" target="about:blank">https://github.com/ether-camp/ethereum-harmony/</a>.</p> - </div> - </div> - </div> - </div> - <div class="clearfix"></div> - <div class="row"> - <div class="col-md-6"> - <div class="x_panel"> - <div class="x_title"> - <h2> - <svg height="14px" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 104.56749 104.56675" version="1.1" viewbox="0 0 144 144" y="0px" x="0px"><metadata id="metadata10"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs id="defs8" /><path style="fill:#676767;" id="path2" d="m 49.0125,12.3195 a 3.108,3.108 0 0 1 6.216,0 3.108,3.108 0 0 1 -6.216,0 m -37.077,28.14 a 3.108,3.108 0 0 1 6.216,0 3.108,3.108 0 0 1 -6.216,0 m 74.153,0.145 a 3.108,3.108 0 0 1 6.216,0 3.108,3.108 0 0 1 -6.216,0 m -65.156,4.258 c 1.43,-0.635 2.076,-2.311 1.441,-3.744 l -1.379,-3.118 h 5.423 v 24.444 h -10.941 a 38.265,38.265 0 0 1 -1.239,-14.607 z m 22.685,0.601 v -7.205 h 12.914 c 0.667,0 4.71,0.771 4.71,3.794 0,2.51 -3.101,3.41 -5.651,3.41 z m -17.631,38.793 a 3.108,3.108 0 0 1 6.216,0 3.108,3.108 0 0 1 -6.216,0 m 46.051,0.145 a 3.108,3.108 0 0 1 6.216,0 3.108,3.108 0 0 1 -6.216,0 m 0.961,-7.048 c -1.531,-0.328 -3.037,0.646 -3.365,2.18 l -1.56,7.28 a 38.265,38.265 0 0 1 -31.911,-0.153 l -1.559,-7.28 c -0.328,-1.532 -1.834,-2.508 -3.364,-2.179 l -6.427,1.38 a 38.265,38.265 0 0 1 -3.323,-3.917 h 31.272 c 0.354,0 0.59,-0.064 0.59,-0.386 v -11.062 c 0,-0.322 -0.236,-0.386 -0.59,-0.386 h -9.146 v -7.012 h 9.892 c 0.903,0 4.828,0.258 6.083,5.275 0.393,1.543 1.256,6.562 1.846,8.169 0.588,1.802 2.982,5.402 5.533,5.402 h 16.146 a 38.265,38.265 0 0 1 -3.544,4.102 z m 17.365,-29.207 a 38.265,38.265 0 0 1 0.081,6.643 h -3.926 c -0.393,0 -0.551,0.258 -0.551,0.643 v 1.803 c 0,4.244 -2.393,5.167 -4.49,5.402 -1.997,0.225 -4.211,-0.836 -4.484,-2.058 -1.178,-6.626 -3.141,-8.041 -6.241,-10.486 3.847,-2.443 7.85,-6.047 7.85,-10.871 0,-5.209 -3.571,-8.49 -6.005,-10.099 -3.415,-2.251 -7.196,-2.702 -8.216,-2.702 h -40.603 a 38.265,38.265 0 0 1 21.408,-12.082 l 4.786,5.021 c 1.082,1.133 2.874,1.175 4.006,0.092 l 5.355,-5.122 a 38.265,38.265 0 0 1 26.196,18.657 l -3.666,8.28 c -0.633,1.433 0.013,3.109 1.442,3.744 z m 9.143,0.134 -0.125,-1.28 3.776,-3.522 c 0.768,-0.716 0.481,-2.157 -0.501,-2.523 l -4.827,-1.805 -0.378,-1.246 3.011,-4.182 c 0.614,-0.85 0.05,-2.207 -0.984,-2.377 l -5.09,-0.828 -0.612,-1.143 2.139,-4.695 c 0.438,-0.956 -0.376,-2.179 -1.428,-2.139 l -5.166,0.18 -0.816,-0.99 1.187,-5.032 c 0.24,-1.022 -0.797,-2.06 -1.819,-1.82 l -5.031,1.186 -0.992,-0.816 0.181,-5.166 c 0.04,-1.046 -1.184,-1.863 -2.138,-1.429 l -4.694,2.14 -1.143,-0.613 -0.83,-5.091 c -0.168,-1.032 -1.526,-1.596 -2.376,-0.984 l -4.185,3.011 -1.244,-0.377 -1.805,-4.828 c -0.366,-0.984 -1.808,-1.267 -2.522,-0.503 l -3.522,3.779 -1.28,-0.125 -2.72,-4.395 c -0.55,-0.89 -2.023,-0.89 -2.571,0 l -2.72,4.395 -1.281,0.125 -3.523,-3.779 c -0.714,-0.764 -2.156,-0.481 -2.522,0.503 l -1.805,4.828 -1.245,0.377 -4.184,-3.011 c -0.85,-0.614 -2.209,-0.048 -2.377,0.984 l -0.83,5.091 -1.143,0.613 -4.694,-2.14 c -0.954,-0.436 -2.178,0.383 -2.138,1.429 l 0.18,5.166 -0.992,0.816 -5.031,-1.186 c -1.022,-0.238 -2.06,0.798 -1.82,1.82 l 1.185,5.032 -0.814,0.99 -5.166,-0.18 c -1.042,-0.03 -1.863,1.183 -1.429,2.139 l 2.14,4.695 -0.613,1.143 -5.09,0.828 c -1.034,0.168 -1.594,1.527 -0.984,2.377 l 3.011,4.182 -0.378,1.246 -4.828,1.805 c -0.98,0.366 -1.267,1.807 -0.501,2.523 l 3.777,3.522 -0.125,1.28 -4.394,2.72 c -0.89,0.55 -0.89,2.023 0,2.571 l 4.394,2.72 0.125,1.28 -3.777,3.523 c -0.766,0.714 -0.479,2.154 0.501,2.522 l 4.828,1.805 0.378,1.246 -3.011,4.183 c -0.612,0.852 -0.049,2.21 0.985,2.376 l 5.089,0.828 0.613,1.145 -2.14,4.693 c -0.436,0.954 0.387,2.181 1.429,2.139 l 5.164,-0.181 0.816,0.992 -1.185,5.033 c -0.24,1.02 0.798,2.056 1.82,1.816 l 5.031,-1.185 0.992,0.814 -0.18,5.167 c -0.04,1.046 1.184,1.864 2.138,1.428 l 4.694,-2.139 1.143,0.613 0.83,5.088 c 0.168,1.036 1.527,1.596 2.377,0.986 l 4.182,-3.013 1.246,0.379 1.805,4.826 c 0.366,0.98 1.808,1.269 2.522,0.501 l 3.523,-3.777 1.281,0.128 2.72,4.394 c 0.548,0.886 2.021,0.888 2.571,0 l 2.72,-4.394 1.28,-0.128 3.522,3.777 c 0.714,0.768 2.156,0.479 2.522,-0.501 l 1.805,-4.826 1.246,-0.379 4.183,3.013 c 0.85,0.61 2.208,0.048 2.376,-0.986 l 0.83,-5.088 1.143,-0.613 4.694,2.139 c 0.954,0.436 2.176,-0.38 2.138,-1.428 l -0.18,-5.167 0.991,-0.814 5.031,1.185 c 1.022,0.24 2.059,-0.796 1.819,-1.816 l -1.185,-5.033 0.814,-0.992 5.166,0.181 c 1.042,0.042 1.866,-1.185 1.428,-2.139 l -2.139,-4.693 0.612,-1.145 5.09,-0.828 c 1.036,-0.166 1.598,-1.524 0.984,-2.376 l -3.011,-4.183 0.378,-1.246 4.827,-1.805 c 0.982,-0.368 1.269,-1.808 0.501,-2.522 l -3.776,-3.523 0.125,-1.28 4.394,-2.72 c 0.89,-0.548 0.891,-2.021 10e-4,-2.571 z" /></svg> - Parity<small>Third party Rust client from Parity Technologies</small> - </h2> - <div class="clearfix"></div> - </div> - <div class="x_content"> - <p>Parity is a fast, light and secure Ethereum client, supporting both headless mode of operation as well as a web user interface for direct manual interaction. The client is currently a full node with transaction processing based synchronization and state pruning enabled.</p> - <br/> - <p>To run a Parity node, download <a href="/{{.ParityGenesis}}"><code>{{.ParityGenesis}}</code></a> and start the node with: - <pre>parity --chain={{.ParityGenesis}}</pre> - </p> - <br/> - <p>You can find Parity at <a href="https://parity.io/" target="about:blank">https://parity.io/</a>.</p> - </div> - </div> - </div> - <div class="col-md-6"> - <div class="x_panel"> - <div class="x_title"> - <h2> - <svg height="14px" version="1.1" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><linearGradient id="a" x1="13.79" y1="38.21" x2="75.87" y2="-15.2" gradientTransform="matrix(0.56, 0, 0, -0.57, -8.96, 23.53)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#5c9fd3"/><stop offset="1" stop-color="#316a99"/></linearGradient><linearGradient id="b" x1="99.87" y1="-47.53" x2="77.7" y2="-16.16" gradientTransform="matrix(0.56, 0, 0, -0.57, -8.96, 23.53)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ffd43d"/><stop offset="1" stop-color="#fee875"/></linearGradient></defs><g><path d="M31.62,0a43.6,43.6,0,0,0-7.3.62c-6.46,1.14-7.63,3.53-7.63,7.94v5.82H32v1.94H11a9.53,9.53,0,0,0-9.54,7.74,28.54,28.54,0,0,0,0,15.52c1.09,4.52,3.68,7.74,8.11,7.74h5.25v-7a9.7,9.7,0,0,1,9.54-9.48H39.58a7.69,7.69,0,0,0,7.63-7.76V8.56c0-4.14-3.49-7.25-7.63-7.94A47.62,47.62,0,0,0,31.62,0ZM23.37,4.68A2.91,2.91,0,1,1,20.5,7.6,2.9,2.9,0,0,1,23.37,4.68Z" transform="translate(-0.35)" fill="url(#a)"/><path d="M49.12,16.32V23.1a9.79,9.79,0,0,1-9.54,9.68H24.33a7.79,7.79,0,0,0-7.63,7.76V55.08c0,4.14,3.6,6.57,7.63,7.76a25.55,25.55,0,0,0,15.25,0c3.84-1.11,7.63-3.35,7.63-7.76V49.26H32V47.32H54.85c4.44,0,6.09-3.1,7.63-7.74s1.53-9.38,0-15.52c-1.1-4.42-3.19-7.74-7.63-7.74H49.12ZM40.54,53.14A2.91,2.91,0,1,1,37.67,56,2.88,2.88,0,0,1,40.54,53.14Z" transform="translate(-0.35)" fill="url(#b)"/></g></svg> - PyEthApp<small>Official Python client from the Ethereum Foundation</small> - </h2> - <div class="clearfix"></div> - </div> - <div class="x_content"> - <p>Pyethapp is the Ethereum Foundation's research client, aiming to provide an easily hackable and extendable codebase. The client is currently a full node with transaction processing based synchronization and state pruning enabled.</p> - <br/> - <p>To run a pyethapp node, download <a href="/{{.PythonGenesis}}"><code>{{.PythonGenesis}}</code></a> and start the node with: - <pre>mkdir -p $HOME/.config/pyethapp/{{.Network}}</pre> - <pre>pyethapp -c eth.genesis="$(cat {{.PythonGenesis}})" -c eth.network_id={{.NetworkID}} -c data_dir=$HOME/.config/pyethapp/{{.Network}} -c discovery.bootstrap_nodes="[{{.PythonBootnodes}}]" -c eth.block.HOMESTEAD_FORK_BLKNUM={{.Homestead}} -c eth.block.ANTI_DOS_FORK_BLKNUM={{.Tangerine}} -c eth.block.SPURIOUS_DRAGON_FORK_BLKNUM={{.Spurious}} -c eth.block.METROPOLIS_FORK_BLKNUM={{.Byzantium}} -c eth.block.DAO_FORK_BLKNUM=18446744073709551615 run --console</pre> - </p> - <br/> - <p>You can find pyethapp at <a href="https://github.com/ethereum/pyethapp/" target="about:blank">https://github.com/ethereum/pyethapp/</a>.</p> - </div> - </div> - </div> - </div> - </div>{{end}} - <div id="about" hidden> - <div class="row vertical-center"> - <div style="margin: 0 auto;"> - <div class="x_panel"> - <div class="x_title"> - <h3>Puppeth &ndash; Your Ethereum private network manager</h3> - <div class="clearfix"></div> - </div> - <div style="display: inline-block; vertical-align: bottom; width: 623px; margin-top: 16px;"> - <p>Puppeth is a tool to aid you in creating a new Ethereum network down to the genesis block, bootnodes, signers, ethstats server, crypto faucet, block explorer, dashboard and more; without the hassle that it would normally entail to manually configure all these services one by one.</p> - <p>Puppeth uses ssh to dial in to remote servers, and builds its network components out of docker containers using docker-compose. The user is guided through the process via a command line wizard that does the heavy lifting and topology configuration automatically behind the scenes.</p> - <br/> - <p>Puppeth is distributed as part of the <a href="https://geth.ethereum.org/downloads/" target="about:blank">Geth &amp; Tools</a> bundles, but can also be installed separately via:<pre>go get github.com/ethereum/go-ethereum/cmd/puppeth</pre></p> - <br/> - <p><em>Copyright 2017. The go-ethereum Authors.</em></p> - </div> - <div style="display: inline-block; vertical-align: bottom; width: 217px;"> - <img src="puppeth.png" style="height: 256px; margin: 16px 16px 16px 16px"></img> - </div> - </div> - </div> - </div> - </div> - <div id="frame-wrapper" hidden style="position: absolute; height: 100%;"> - <iframe id="frame" style="position: absolute; width: 1920px; height: 100%; border: none;" onload="if ($(this).attr('src') != '') { resize(); $('#frame-wrapper').fadeIn(300); }"></iframe> - </div> - </div> - </div> - </div> - - <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.3.0/js/custom.min.js"></script> - <script> - var load = function(hash) { - window.location.hash = hash; - - // Fade out all possible pages (yes, ugly, no, don't care) - $("#geth").fadeOut(300) - $("#mist").fadeOut(300) - $("#mobile").fadeOut(300) - $("#other").fadeOut(300) - $("#about").fadeOut(300) - $("#frame-wrapper").fadeOut(300); - - // Depending on the hash, resolve it into a local or remote URL - var url = hash; - switch (hash) { - case "#stats": - url = "//{{.EthstatsPage}}"; - break; - case "#explorer": - url = "//{{.ExplorerPage}}"; - break; - case "#faucet": - url = "//{{.FaucetPage}}"; - break; - } - setTimeout(function() { - if (url.substring(0, 1) == "#") { - $('.body').css({overflowY: 'auto'}); - $("#frame").attr("src", ""); - $(url).fadeIn(300); - } else { - $('.body').css({overflowY: 'hidden'}); - $("#frame").attr("src", url); - } - }, 300); - } - var resize = function() { - var sidebar = $($(".navbar")[0]).width(); - var limit = document.body.clientWidth - sidebar; - var scale = limit / 1920; - - $("#frame-wrapper").width(limit); - $("#frame-wrapper").height(document.body.clientHeight / scale); - $("#frame-wrapper").css({ - transform: 'scale(' + (scale) + ')', - transformOrigin: "0 0" - }); - }; - $(window).resize(resize); - - if (window.location.hash == "") { - var item = $(".side-menu").children()[0]; - $(item).children()[0].click(); - $(item).addClass("active"); - } else { - load(window.location.hash); - var menu = $(window.location.hash + "_menu"); - if (menu !== undefined) { - $(menu).addClass("active"); - } - } - </script> - </body> -</html> -` - -// dashboardMascot is the png dump of the mascot to display on the dashboard about page. -// nolint: misspell -var dashboardMascot = []byte("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01s\x00\x00\x02\x00\b\x06\x00\x00\x00p\xe4\x8c`\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\tpHYs\x00\x00\v\x13\x00\x00\v\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\atIME\a\xe1\x03\x1d\x0e0&\xf3\xca\t\x11\x00\x00\x00\x1diTXtComment\x00\x00\x00\x00\x00Created with GIMPd.e\a\x00\x00 \x00IDATx\xda\xec\xbdw|\x15U\xfe\xff\xff<3s[\x12BB\x12H!\x90\u0411&H\a\x01i\"V\xb0!b\xc1^>\xbb\xee\xae\xdd\xd5u\xed}-k/X\u058e\xa8\xa0\u049bH\x87\bH\xef\x04BI\x81\xf4[\xa6\x9c\xf3\xfbcnB`\xdd\xef\xcf\xdd\xd5\xddU\xe7\xf9x\\\u023d\u027dw\xee\u0339\xafy\xcd\xfb\xbc\xcf\xfb\r\x1e\x1e\xff\";wn\x17\xb7\xdf~\xabQw_)\xa5+\xa5\x1a)\xa5\u0494R\xc9J\xa9D\xa5T\xf0\xb8\xa7\xe9\x1f|\xf0\x9eQYyXx{\xd0\xc3\xe3\xc7\xc3\xfbBy\xfcK\xbc\xf3\xce[\xe2\xd2K/Wq\x11o>y\xf2G'\xaf[\xb7\xae\xff\u05ad\xdb2\xa5\x94\xa9\xd1h\xb4:---\u06b9s\xe7HJJ\xf2\xb2f\xcd27\x9e}\xf6\x98\"!\xc4\u07ba\xd7x\xf6\xd9g\xb4\xcb.\xbbD\xa6\xa4\xa4\xb1d\xc97\f\x18p\xb2\xb7c=<<1\xf7\xf8O\xf0\x97\xbf<EII\x89x\xf4\xd1\xc7\x14\xc0\x8c\xe9_\x0e\xffv\u035a\xa7\u05ec]\u05f9`\xf5jJK\xcb0M\x13M\xd3\b\x85B4i\x92J\xabV\xf9\xe4\xe5\u54d9\x99Y\x90\x9a\x9a2\xa5_\xbf\xfeS\xfb\xf5\ubfe9\xee5o\xba\xe9V\x9e}\xf6\t\x94R\b\xe1\rI\x0f\x0f\x0f\x8f\x9f\x9c\xf7\xde{\xb7\x81;\u007fs\xe4\xc4+.\xafi\u04e6\x8d\x02\xfe\xe1M\xd75\x95\x9c\x9c\xacZ\xb7n\xadF\x8d\x1a\xa5\u018d\xbbp\xd7=\xf7\xfc\xf1\xf5\xaf\x17.\x18q\xfc\xeb\u007f\xfa\xe9'\xdeN\xf6\xf0\xf0\xf0\xf8\xa9\xc9i\x9e\v\xc0\x97_N\x1bq\u0265\x97\x1c\xceh\u06b4^\xb4\x85\xa6)\xc3\xe7S>\xbf_\x89\xef\x15u]\x86B!\x99\x93\x93\xa3\xfa\xf6\xed\xab.\xb8\xe0\x82\x8a\a\x1e\xb8\xef\x83}\xfbv\xf7k\xf8\x1e\u007f\xf8\xc3\x1f\xc4\u0739\xb3\xbc\x9d\xed\xe1\xe1\xe1\xf1S\xb0r\xe5r\r@)\xd5\xee\xf6\xdbo\u06d2\u07fa\xb5\x02$\xa04]W\x86\xe1S\t\u024dUjV\xaeJ\xcdn\xa9\x12S\u04d5\x10\xe2\x18A\x17B(\xbf\xdfo'5jde\xe74W\xfd\xfa\xf5WW^yE\xe5k\xaf\xbf\xfa\xa0R*\xb1\xee\xbdz\xf7\xee#\xdex\xe35o\xa7{x\xfc@\fo\x17x\xfc\x10\x94R\x9cz\xeaH\x00\xfe\xf6\xb7\xb7\xaf\u0634yK\xfb\xdd;w\u0680\xa1\xe9:\x86\xcfO\x93\xdc\xd6t\x18r\x06\x19m\xbb\xa0\xe9\x06f\xd5\x11\xcavmd\xff\xa65\x1c\u06be\x91pU\x05J)L\xd3\xd4\x1d)\xb1\x1d\u01e9\xae\xae\xe1\u0421\x83\u027bw\xef\xfe\xe3\x96\xcd[\u03999s\xfam\xa3F\x8d\x9e\xber\xe5\n\xb5r\xe5\n>\xfa\xe8\x03q\xe1\x85\x17)\xef\bxxxx\xfc\b\u0318\xf1U\x9d+O\xbe\u66ab\xe7\xb6\xcc\xcbW\x80\x85\x10J7|\xaay\x97^\ua497\xa7\xab{\xd7Ku\xcfw\x8e\xbao\x83\xa3\x9e\xd8\"\xd53[\x1d\xf5\xe0\xd2\xfd\xea\xca\x17>Q\xfd/\xbcZ\xa5\xe5\xe6\xff]X&!1Q\xa6\xa5\xa5\xa9\xf6\xed\u06ebs\xce9[=\xfa\xe8#\x1f\u06b6\x99Y\xf7\u0793&\xbd\xe1\x99\x0e\x0f\x0f\u03d9{\xfc\x18l\u0630A\x03\xe4\xf4\xe9\xd3z\x95\x1d>\xd2\xfdPq1\x80.4\x8d`R2\x9dF\x9eG\xab\xc1\xa7\x11\x8dJ|\xd2B\xf3\t4\f4M#%3\x9b\x93\xce<\x97\x13O=\x97\xc2u+Y\xf1\xc9$VM{\x8fhm\r\xb6\x94H\xc7\x11JJu\xe8\xd0!QYYIY\xd9\xe1\v\v\v\v{\xbf\xfa\xea\xcb\u007f\xbc\xfa\xeak?\x12B\xd8c\u019c\xa3=\xf0\xc0\xfd\xaas\u7b9eK\xf7\xf0\xf0\xc4\xdc\xe3\xdf\b\xb3\u803dq\u00e6v\xa6i6\x89E\xc2\x12\xd0p\x1c2\xf2\xdb\u04a6\xcf`t\xc0pb\x04|\x02\x03\xb0-\vi\x83\xa6\t\x10\x02C\xf7\u0476ooZu\xefM\x87\x81#\x98?\xe9iv\x16,AJI$\x12\x11J)4Mc\u02d6-\x1c<x0\xff\u0421C\xefWTT\x9c\xa7\x94\xfa\x83\x10\xa2\xf0\xb3\xcf>\xe7\xc9'\x9f\x10II\x89\xea\xba\xebn\xf0\x0e\x8a\x87G\x03to\x17x\xfc\x10\xe6\u0319\xeb(\xa5\xf4\x85\v\x17\\\xb5x\xf1\u0493JJJ\x00\x84\xa6\xe9\xe4\x9f4\x90^\xe7\\\x8a\xdf\x1f\xc4'\x1c4\x01 @\x80B\xe1(\x85t\x14\xb6\xed`\xc5\x1c\x84\xd0\xc9\xed|\x02\xed\xfb\x8d\xc4\x17\brp\xebz,3\x86m\xdbH)\xd14\r\u02f2\u063f\u007f?\xd5\xd5\xd5\x1d\xb7n\xdd:\xfc\xfd\xf7\u07dd\xf3\xf4\xd3\xcf\x1c\x99={\x0eYYYb\xfc\xf8q\u031e=\xc7;0\x1e\x1eq4o\x17x\xfc\x13\xe4X\x96\u0569\xa2\xa2<\xae\xd6`\x04\x83\xa4d\u5498\x92\x8a\x81D\x17u+\xd1\x14(\x85T \x158\xb87KB,\x16\xa5\xb6\"L\x93\x9c\xe6\x9cu\xcb\xc3\\\xf9\u0707\xb4\xee\xd1\x1f\x00\xd34\xa9\xad\xad\xc54M\xa4t\xd4\u06b5\xeb\x983gn\xe7G\x1ey\xec\u04d2\x92\x03\xb9\x00\xaf\xbd\xf6\xba*--\xf5V\x17yxxb\xee\xf1\xafP[Se\x16\x15\xed\x8fX\x8e\x13Wk\b$$\xd18\xb39\x9a\x06\x02\x85&@\x8b\vz\xc3\u015c\ueb27B\x01\xb6\u0490B'\\S\x8be\xd9t\x191\x8a\u02df\xf9\x80\xbec/\x03\xc0\xb6mjjj\x88FcB\b\xa1v\xed\xda\u0152%K\xbb\xfc\xe1\x0f\xb7~\xban\u0777-\x00\x1e{\xec\t5c\u0197\x9e\xa0{xxb\xee\xf1\xcfR\xb4\xbfH\x84\xc3a\u0371\xed\xfa\xc7|\x81 \xa1\u4538\x11W8J \x95\xa83\ueba0\xbb&\x1d\xea\\\xbar\x1d\xba\x12:\u04b1\xa9\xad\xb6i\u05a6\x05\xe3\x1fy\x83\xb3n~\x88P\xa3d\xa4\x94\xd4\u0506\tG\"BJ\xa9\n\v\vY\xb7n]\xcfG\x1ey\xec\x8b\x193\xbe\xec\fp\xdaig\xa8\r\x1b\xbe\xf3B\x85\x1e\x1e\x9e\x98{\xfc3\xa4\xa4\xa4\xa0i\x9aRR\x1e}P\b\x94Tq\xe7-\u0730J\u0703\u02fa$\u0138So\x98\x86\"\x15\xd8R\xa0\x14(\xe9P]a\xe2O\xd09\xf5\x86\xbb\xb8\xe0\xde\x17H\xcdj\x8e\x92\x0e\xe1p\x98H4*\x14\xa8\x83\a\x0f\xb1a\u00c6\xae\xef\xbf\xff\xc1\x97\v\x16\xcc;\x1f\xa0s\xe7\xae\xce\u0319\u04fdq\xec\u1279\xb7\v<~(\u035ae\xeb)\xa9\xa9\xbe:\xd3\r`\x9b&\xe1\x8a#q]\x17\xf5\x95\xdb\xd4q\xff\x1f\r\xb7\xa8\xfa\u01dc\xb8\x93\a\xd7\xd5\xd7VE\xb1\xa5\xa2\xf7y\x13\x18\xf7\xd0\xeb4ks\x02(E4\x12!\x12\x8d\n\u02d1\xeaPq\t\u02d7\xafh\xf9\xe1\a\x1f~<s\xe6\xf4\xdb\x00F\x8d\x1a-\xdfy\xe7m\xf1\xd9gS\xbc\x83\xe4\u1279\x87\xc7\x0f\xa0*))\xb1\xd8\xe7\xf3\xd7\xeb\xb4\x15\x8dPY\xb2\x1f%\xeb*\x1e\xaa\xb88\xbb\xee[\xd5\u074e\x13\xf6\xba\xfb\x96t\x1d\xbc\x16\u007f\x8e\x15\x8b\x11\x8b\xdat\x1av*\xe3\x1ez\x9d\xdc\u03bd\x000\xa3Qj\xc3a\x11\xb5lJ\x0f\x973c\xe6,\xde\u007f\xff\x83\u01fe\xf8b\xeam\x00\x97^z\x99Z\xbf~\x83\x98<\xf9#\xef(yxb\xee\xe1\xf1\x8f0\f\x9f.\x84\xa8\xb2L\xab --\r\xdc:+8\xb6EU\xc9Aj+\xcaA\xd3P\xc7LI\n\xb7p\xcbqB~\u053d\v$\x02[\xb9~]\x13\x02M\b\xa4m\x13\xad1i\u0777\x1f\x17>\xfc:\xad{\x0f\x06\xc0\x8eE\x89\x86\u00d8\xb6Cyu\r\xf3\xe7/\xe0\x93O\xa6<\xb6h\xd1\u009b\x00\xfe\xf4\xa7{\u055a5k\u0164I\xaf{\a\xcc\xc3\x13s\x0f\x8f\xef\xe3\xfe\a\xfe\xac\x03\xb4i\u04fa\xbcY\xb3f\xf5\x9a,\x1d\x87\xaa\u0483T\x1c\xda\x17\x9f\xect\xeb%\xba\x99,\xea8\xf9v\u007f\xaf\xe2.\xbc\xee/l)p\x94h\x10W\x17\xeeB\xa2\xea(\xb9\u077ar\xc1C\xaf\xd3a\xd0i\xae\xa0\x9b1\xccH\x18\u01d1T\xd4\u052a\xb9\xf3\x17\xf0\u059bo=\xb3`\xfe\u071b\x00\x1e~\xf8\x11\x15\x8b\u017cq\xed\u1279\x87\xc7?\xc0\x01h\u07fe\xfd\x16M\xd3\x0e\x01B)\xa5P\x8a\xea\xb2b\x8e\xec\u0749\xe1sE\xbc\xae\xc1\x84\x10 \x8e\x9f\xfa\x14\xaa~R\xb4>$\x03\xd8Ra\xab\x86a\x18\xe1\xc6\u02ebbd\xb5o\xc3y\xf7\xbfD\x87\x93G\xb9\x82n\x99\u0122a\x1c\xa9DyU\x8d\x9c5g\x1e\x93&\xbd\xf9\xccg\x9f~\xf2;\x80\ubbffQ~\xfa\xe9'\xde\xd8\xf6\xf0\xc4\xdc\xc3\xe3x\xfa\xf4\xee\xad\x00\x86\x0e\x1d\xbe==#cKBR\x12\xae\xc1\x96\xd4\x1c.\xe6\xd0\xf6\x8d\xf1\xa5\xfb\xc7\x0f\xa9\xe3\x03,\xc2\x15\xf4\x06\x8f\v\x14J\x81\x1d\x8f\xb3\x83B\x13\n\x81\xe6\nz\xb5Ef\xbb\x96\x9c\xff\xd0kt\x19y\xae{f1M\xccH\x18\xe98Zyu\xad\x9c\xbb\xe0k>\xfc\u88e7\xdf{\xf7\x9d\xdb\x01\u018e=O\xae^\xbd\xd2K[\xf4\xf0\xc4\xdc\u00e3!C\x87\x0e\x97\x80\x10B\x1c\xc8i\xde|k\x8b\xdc\\\x00[I\x89\x15\rS\xbag;U%\xc5\xe8\x86\xcfm\xff\u01b1\xee\\\x1c\u04e1P\x1c\xa3\xf3*\x1e\x9d\xc15\xfa\rD\x1f4\xcdM]\xac=b\x91\u05a29c\xef}\x81\ue9cfs\x05\xdd2\xb1\xcc(J)\xad\xaa6\xa2\xbeY\xb2\x8c\xaf\xa6O\u007ft\xea\xd4Oo\a\xe8\u0673\xb73c\xc6W\xde\xc2\"\x0fO\xcc=<\xea\xe5W\b\x9ey\xe6\x19\r\xa0u~\xfe\xdaF\x8d\x1b\x03\xf8\xa5m#\x1d\x87\xe2]\x9b9\xb4s\x13\xbe@\xbd<\xd7GR\xc41.\xbdAz\v\r&G\xd5\u047fQ\xf5r\xee\xfe\xa4\t\x01\xca!V\x15%-\xa7\x19c\xee~\x8e\xeeg\\T\xef\u042dh\x18)\xa5\xa8\xac\x8d\xb0h\xf1R\xbe\xf8\xe2\xabG\xe7\u039d5\x01\xe0\xb4\xd3NW\x0f<p\x9fX\xbdz\xa5w\x10=<1\xf7\xf0\x00\xc8\xcfo)\x01.\xba\xf0\xbc\x95\xe9\xe9\x19\xbb}\xfe\x80+\xbfJq\xa4h\x0fE\x1b\v\xb0M\xd0u\xbd\x81\xcbv\xe5\xf9\xe8\x9a\xd0:\xbb\x1e\x97\xeacD\xfd\xa8\x84\xcb\xe3\x025Z<\x16\x1f\xae\x8a\x92\x9c\x95\xc1\xd9w=\u0349\xa7]\x00\xb8\x93\xa2V4\x8cr$\xe5\u0575\xcc_\xf05\x1f~\xf8\xf1\x13\u02d6~\xd3\x15\xe0\x9e{\xeeU_\u007f\xbdH\x1c<\xb8\xdf;\x88\x1e\x9e\x98{x\x9cr\xca)\n \xb5I\u01b7\x1d\u06b7\u06d9\x95\x95\x05\xa0\xa4ccFj)\\\xb3\x8c#\xfbv\x11\b\xf9Q\xaa.\xa5\xa5\ue98e\xae5\xaa_\x19ZW\x95+.\xe0\xf1\xa5\xfeu\x81\x96\xfa\xfctE\xbcD\x80[N7Z\x15%5\xbb\x19g\xde\xf54\x9d\x86\x9fsT\xd0\xcd(JA\u0251\n\x96._\x99\xf9\xde\xfb\x1f\xbe\xab\x94\xca\x03\xb8\xf9\xe6[\u051bo\xbe\xe5\x1dD\x0fO\xcc=<\x92\x93S\xea\u007f\xee|B\x87\u03dbff\x02\b\xe56\x98`\xef\x86\x02\xf6\xac]\x1e\x17]\xcd\xf5\xe4B\x1d\xe3\xb0\x05G\x8bn\x1d\x1bEw\x05\xdd]\x15\x1a\x1f\x9c\xc2\x1d\xa0\r\x9f\x03\nM\x83hu\x94\xf4\xdcl\u03be\xebiZ\xf5r\xf3\u042dh\x04;\x16E*\xa5\xf6\xec\xdb\xcf\xeao\xd7ty\xe4\xe1\x87\xea\x15\u0732L/~\xee\xf1\x8b\u015b\xed\xf7\xf8\x97\x98:u\u06bey\xf3\xe7\x8f(\xda\u007f 3\x16\x8d\x80t\xb0bQ\x1c\u01e1y\xe7\xde$\xa7\xa5#m\xbbA\xce9GeY\x88\xfa\xf4E\x94:\xb6\xbcb\xbd+\x17\xf5\xa1\x97\xba\x12\x00uN]w\xb3\x16\xb1mh\u04bc\t\xcd\u069e\xc4\xfe\xf5\xab\xa9*9\x80\xe3\xd8h\x9a&\x10B\x95\x1d>,L3\x9a\xf7\u007f7\u07980{\xf6\xec\xb9\v\x17~\r\xc0k\xaf\xbd\xca\x17_|\xe1\x1dD\x0f\u03d9{\xfcz\xb9\xe1\x86\xeb]\xc1\x15\xe2\u0420\x81\x03\x16d\xba\xee\xdc\xcd\x15\xb7,\xf6|\xbb\x94\xdd\x05\x8bQJa\xf8|GE\xbb>\x98\u00b1\xf1\xf3\xfag\x1f]`TW\xac\xcbQ\x02[\x1d\x9f\xc4\xe8.0B\b\x94c\x13\xaeth\u0465\vg\xdc\xf9\x14i-\u06c0RX\xb1\bJ\xda\xc2r\xa4Z\xb7~\x13\x05\x05\x05\xb7\u035e=}\f\xc0\x94)\x93\xf5\xab\xaf\xbe\xc6;\x90\x1e\x9e\x98{\xfc\xba\xb9\xf6\u06ab\xeb\u007f>}\xf4i\x9ft<\xa1\x83\n%$\xba\x8a\xac$\xe1\x8a26\xcd\xff\x9c\u0292\x03\xf8\x03:\xba\x00\xedh\xbb\x8a\x06\x01\x95\x06%\x15\x1b8\xf2\xba`\x8a#AJ\x15\xaf\xaa\x18\xbf\xc5\v\xbe\xd4M\x90\n\x01\xca6\x89\xd5Z\xb4\xedw2\xa7\xfd\xfea\x12S\u04d0\x8e\x83m\xc6\x10(Q\x13\x8eX\x9b6oe\xc6\xf4YW\x01\x9c{\xee\xf9\xce\u0295\u02fdq\xef\u1279\u01ef\x9bn\xddz0y\xf2\xc7\x00\xe4\xe7\xb7^\u04a3[\xb79\xd99\xd9\xf5Z,m\x8b\u00b5+(\\\xbb\x1c!\xc00\f4\xa1\x8e6\xach \xe0\xa2\xc1?nhE\"m\a\u01f4pL\x13\u01f6\xdd>\xa2\xf1a\xea\x98Ql3\x86m\xd9HG\xba\xb1u!P\x8e\x85\x15u8q\xf4\xf9\f\x9ax\v\bp,\v\xdb4\xd1\f\xdd\xd8Y\xb8\x97\xad\xdbw\x8c\xfe\xf8\xe3\x0fo\x04\xe8\u077b\xaf\xfa\xee\xbb5\xde\xc1\xf4\xf0\xc4\xdc\xe3\xd7\xcd\xf9\xe7_P\xffs\xcb\x16-\x1e\xec\u043e\x1dqw\x0e@e\xf1~\xd6N\xff\x98\u02b2rt\xbf\x8e@\xb8\x0e\xbd~y\u007f\u0709\xd7?\xa6PR\"-\xcb\x15k3\x86c\x9b\x14\x16,a\xf6Sw1\xf7\xaf\xf7rp\xdb\x06\x84\xeesE:\x1a\x8d\xf7\fu\x90\n\x84\xa6\xe3X&\n\xe87\xfez\xba\x8d\x1eW/\xfe\x8ei\nG*\xb9u\xfbN\xbe-\xf8\xf67J\xa9\x96\x80\x9a9s\xb67\x19\xea\xf1\x8b\u009b\x00\xf5\xf8\x97\x989s\x86x\xf7\xdd\xf7\xf8\xec\xb3\xcf\x0f.[\xb6\xb4WQQQ\x9b\xca\xca\xca\xfa\xdf\xd7\x1c)\xa1i\xab\x0e4?\xa1\x13\x8eS\x97\x9a(\xe2\xf9\u3abe\xad\x9c\x06(i#-\vi\xdbH\xdbB)\x89c[\xac\xf9\xfcmV|\xf0\x02\xfb\xd6-\xa7t\xf762\xdbu%9#\x13;\x16E9\x0e2\xde$C\x17\x02M\u05f0-\x8bPr\x12\xa9-:\xb0k\xd5\"j\x8f\x94 \x1d\x1b\xc3\xf0\x8bp4\x8a\xa6\x91n\x9b\x91\xa2i\u04fe\\6g\xce\\f\u039c\u03bb\xef\xbe\xe7\x1dL\x0f\u03d9{\xfcz\x195\xea4w\xe1\xbd\x10V\x97\u039d\xa6\xb6i\u075a@0T\xff\xfb\xda#\xa5\xac\xfd\xea#*\x8b\xcb\xf0\x05\xf4z'\xae\x8b\xa3\v\x88\x04\xa0\xa4\x04\xdbF\xd9\x16\u04b6\x90\x8e\x83\xb4,t\xdd 5;\x8fPr*\x00\xbbW.\xe4\x9bIOR]V\x8c\xee\xf3!-\x13\u01ccaG\xa3\u0626+\xee\x9a\xd00\xc3&M;t\xa6\xcfe7\xa3\xf9\xfc()1c\x11\xa4\x92l\u0676\x83\xd5\x05k\xae\t\xd7V\xe7\x02L\x9c8\xd1s\xe7\x1e\x9e3\xf7\xf0\xa8\xe3\x8b/\xbe(.(X5\xbch\xff\xfef\xb5\xe1\bR:\x00T\x1c\xdaKJ\xb3\x1cZ\x9e\xd8\vi+\x94\x92\xf5\x8b\x81\xa4\x02\xa9\x14\u02b6Q\xb6\x8dt,\x1c\xc7A92\xbezT\x91\x9e\xd7\x16\xcd0(\u0779\x19\xc74)?\xb0\a#\x98@\xf3N'!m\x1b%%B\xc9x-\x18\x88w\x95F\t\x1f)\xf9\x9d)\u07fb\x9d\xd2\xed\xebQn\x03j,[\xe2\xd8vZ\xf9\x91\xb2\xb2\xf9\xf3\x17|SSS\x8bR\x92\xfb\xee\xbb\xcf;\x88\x1e\x9e3\xf7\xf8\xf5\xb2c\xc7\u05b89\xd7\xf6\xf5\xec\xd9\xf3\xf5.\x9d:\x11\b\x06\xd0\r\x03\x003\\\u02ea\xcf\xdf\xe1\u040e\x1d\x18A\xb7\x00\x97T\x10\xb3%Q\xcb\xc1q$B)7\\\"\x15\x9a\xaa\x13g\x89c\x99\x18\xfe }\xc6]\xcf\xc0\x897\u04e8i6V4\xc2\xe6yS)Z\xbf\x1a\xdd\x1f@96R\xd6M\x9a\x9a(+\x86\x94\x92X$\x8a\x11\xf4\xd1k\xfcoIHM\a\xeaV\x88\xc6\xd8[T\xc4\xe6-[\xafUJ\xba+\x9e\x84\xf7\x15\xf0\xf0\x9c\xb9\u01ef\x9c\xe7\x9e{\x9e/\xbf\x9cf\xbc\xff\xfe\a\xf2\xb3\xcf>\xdf\xf5\xdd\xfa\xef\x86\x1e<x(\xbb\xaa\xaa\xcaQRjJ)*\x0f\x15\xe1\x0f%\u043a\xcf0\x14\x02\u06f6\xb1\xe2\xe9\x85~\rt%\x91\xb6\x8d\xe3\xd8 \x9dz\xc1\x17\n\xa4c\xa3\x19\x06\xd9't\xc7\x17J\xa0d\xc7F\xaa\x0e\x15\xa1\x80\xecN=1\x82!pl4\r\x10\xae\x9b\a\x81\xe9\x80\x14\x06\x8d\x9b\xe6Pyp/\a7\xaer\xdd~<\x97\xc6\xd0\xf5\u0186&J\xa7N\x9d\xb6\x04`\xf1\xe2EL\x9a\xf4\xa6w@=<g\xee\xf1\xeb\xe5\xf4\xd3\u03f4;th\xaf\v!JO<\xb1\xdbcy-[\u0621PH7|>Y\xe7zWN\x99\u0136of\x12Lr\x1d\xbbO\x17\x04}\x1aZ\xbc\u035c\xc2\x15b'\xde4T(U\xbfX\u050aF\x90\xb6M\x97Q\xe7\xd3\xed\x8c\xf1\x18\xc1\x10;\x97\xcfg\xc7\u04b9h\x9a\x8e\x00\xa4\x94\u060eD:\xd2\x15w\xe5\x80\x19!\x10\xd4\xe8|\xda8B)\xae;\x97\xb6I,\x16\xa5\xb8\xb8\x84\u056b\v\xaePJ5\x05\x188p\x90w =<1\xf7\xf8al\u07bc\xe1\u07dal[\xb3\xa6\xe0\u007f\xb2\xea\x9f\x10\x82\u0673g\xba\xd5\x14/\xbaxr\xabV\xf9S\xf3Z\xb6@\xd34G\xb8\xa5\x0e\xa99\\\u02827\x9e\xe4\xf0\xde\x03$$'\xa0\xa1\u0705D\x02\x948Zd\xcb\xed\x17\x1a_\xbc_W\x17W)\xcch\x18!4:\x8f<\x8f\xfc\x9e\x83\x88V\x1da\xc3\xec)\x1c.\u070e\x11\f\xe1H\x85\x94\xf1\xd5E\xd2A96\xb6\xe3\x10\x8bJ2\xdau\xa5\xc3\u0433\u074dU\n\u01f69|\xa4\x9cM[\xb6\xb4\xff\xec\xd3O\xeas,\xa7O\xff\xd2\x1b\xa4\x1e\x9e\x98{\x1c/\xdc\x1b\xffN\xb8;v\xec\xac\xfe\x9d\xd7\xec\xde\xfd$\x95\x95\x95\xf3w\x8f\xef\u06f7\xe7\xbf\xfaY\x8b\x8a\xf6\u04a2E\xbe\x9a>\xfdK\r\u0b33\u039a\x94\x97\x97\x17m\x94\x94\xe4\xf3\x19>U\x97W\xbec\xf9|\xbe\x9e\xf4$2f\x13LH<\xdaRN\xd3\x11\xba\x01B\xe0H\x85-\xdd0\x8b\x8aOl\xd6aE#4JoF\xd7\xd1\xe3Hm\u078aC\u06fec\u04c2/\x88\x86k1|~\x90\x12\u02d1\x98\x96\x83m\xdb\b\xe9\xe0\xc4\xc2\x04\x1b5\xa6\u07503\xf1\x05\x13\\w\xee\xd8D\xa2QJJ\xcbX\xb0p\u163a\xd7\x1f=\xfa\fo\xe0z\xfc\xac\xf1R\xb3\xfe\r\x94R\u031f?W[\xbbv\x9d\xb6k\xd7N\xfd\x85\x17^\x8a\x1d\xf7\xfb\x04)\xed\xc0\u0739s\xd2g\u03de\xa3L\xd3N\xea\u07ff\uf165\xa5\xa5I\u06f7\uf215\x96\x96\x12\x0eG\xb0m7\x93C\xd7u\f\xc3 11\x91\xa6M\x9b\x92\x97\x97\xe7\xcf\xc8H\x0fo\u077au\xea\x91#\x87\xcbN9\xe5\x14\u0577o\u07f2\x9c\x9c\x16\xa6\x10\"|\xdc\xe6\x18\x8f=\xf6\xa8\xc8\xc9\xc9v.\xbe\xf8\x12)\xc4\u007f\xef\xd0>\xf8\xe0\xfd\x0f|\xf5\xd5\xf4\xbb7n\xdeJ$\x1cV\x96e\n\xe2\x19'g\xdd\xf9\x17\x06^z\x13\b\x81m\u0190\nl\xd3$V[K4\x1aA\xd96B\x88x\xfdrw\x88\xaa\xb8\xab\xd6t\x1dG\x18,\xfb\xf8u\xd6M~\x89\x84\x944\x86\xff\xdf}\xb4\xee=\x18+\x16\xc1Q\x02\x89@7\f\x94\xe1G\xe9>\xfc\t\x89\u012a\x8e0\xe3\xc1\xeb\xd9<\xf73\x00t\u007f@\xa56N\x16}\xfb\xf4\xae\xbe\xf7O\xf7\\\u052bw\u07ef\x00\xbe\xfbn\r]\xbbv\xf7\x06\xb6\x87'\xe6\xbf&\xbe\xfa\xea\v\xed\x0f\u007f\xb8\x85\xad[\xb7\xca\x06\xe2ml\u0630\xb6\xcd\xe4\u025f\xa4WUU\xb7\xf6\xfb\x03\xc3\xf7\xed+\xca\b\x04|\x03\x0e\x1e<\x94\xb4\u007f\xff\x01\xc2\xe10\xb1X\x94X\xcc\xc4i\xb0\xf0E)U_\x94J\xd34\fC\xc7\xe7\xf3\xe1\xf7\a\b\x85\x82dee\u046cY\xb3\x9ah4\xba8''\xa7\xaci\u04cc-J\xa9UC\x86\f\xd9;h\u0410\"!DM\xddv\xa4\xa5\xa5j/\xbe\xf8\"\x17\\0\xee\xbf\"\xeaJ\xa9\x8c\x1b\u007f\xfb\xdbY\u04e7\xcf\xe8^\\\\\x8c\x15\x8bb[\x16\x00\xa1F)\x8c\xfd\xd3_\xe9u\xee\x04\xa2\x11\a\xe9\xd8\u0636\x85Y[K,\x12\xc1\xb6,\x84R\b];Z\x90K\xc5\xc3/Rb\x1a\t\x1c\u06b7\x97o'=\xc8\xfe\xb5\x8bi?\xf8L\x86]\u007f7\xc1\xe4\x14b\xd1\x18a\a\xfc>\x03\u007f\xc0\x87\xa3\xfb\xd14\x8dPJ\n\x05\x1f\xbd\xca\xf4\a\xae\a%\xd1t\x03\u007f {\x9d\xd4C\x1b}\u06a8G\xee\xbc\xeb\x8fwy#\xda\xc3\x13\xf3_\t\xf3\xe6\xcdf\u0630\x91L\x9a\xf4\x12\x13'^W/\xbcJ\xa9\xd0\xec\xd93\xfb\u035a5+\xaf\xa4\xa4\xb4\xb7eY\xa7TTT\xb4\u06f3\xa7\x90\x92\x92\x12,\xcbB)E0\x18\xa4q\xe3\xc6$''\x93\x9c\u0708\x84\x84D\x12\x12B\x04\x02\x01t]G\xd7uw\"\u03f6\x89FcD\"a\xc2\xe1\b\xd5\xd5\xd5TUUQQQA4\x1a\x05\xc0\xe73h\u04a4\t\xf9\xf9y4i\x92V\u0628Q\xa3e\u035b7_\u06f6m\x9b\xe5\x13&\\\xbaR\b\x119\xea\x92\x1f\xc4q\x14\xfd\xfa\xf5d\xe4\xc8\xd3~\xd2}t\xf7\xddw\xf1\xe0\x83\x0f\x030k\xd1\xe2S\x9e{\xfa\x99\x99\u02d7.\xf6\xd7VWc\u01a2\xc8x\xbewjV.\x17<2\x89\x0e\x83\x87\x13\xad\xb5\xb1\xad\x18v$\x82\x15\tc\x99\x16\x8eTG\xfbV4\xa8\xb4(\x95\xa2*\xea@0\x89\x03K\xbed\u065b\x8f#\x1d\x87\xc1W\xdfA\xe7\x91c\x89DbT\xc7,\x82>\x83P\u0407\xd2\xfdh\x86N\xa0q\x13\x0emY\xcb\u053b.\xa3x\xdbw\bM\xc3\x1f\bZ\xad\xf3\xf3|C\x86\f\xfa\xfa\x85\x17^\x1a%\x84\x88\x1e<\xb8Ode\xe5*o\xb4{xb\xfe\v\xa4\xa4\xe4 M\x9bf\xfd\xdd\xe3{\xf7\xee\xea2y\xf2\xa7\x97-_\xbe\xbc]MM\xcd\xe0\x8a\x8a\x8a\xe4\xad[\xb7Q[[\x8b\xae\x1bdee\u04be};\xf2\xf2\xf2\xc8\xc9\xc9&++\x8b\x8c\x8c\f22\xd2INN&!!\x81P(\x84\xdf\xef\xbaGW\xcc\x15\x8ec\x13\x8b\u0148D\"D\xa3Q\xaa\xaa\xaa)++\xa3\xa4\xa4\x84\xe2\xe2b\x8a\x8a\xf6SX\xb8\x97\xed\u06f7\xb3\u007f\xff~b\xb1\x18\xc1`\x90\xb6m\u06d2\x9c\x9c\\\x15\f\x06\xbe\xee\xd9\xf3\xa4\xcdc\u01ce\xfd\xb8{\xf7\x93\n\x8e\xdf\xee\u077b\xb7\x93\x9f\xdf\xf6'\xd9W\x05\x05\xab\x985\u007f\xa1\xb8\xeb\xd6[\x15\xc0\v\x93\xdez\xf1\x93\x0f?\xbc~\xf5\u0295\u0122\x11e\u0162\xa2.\x0e\u07b2[\x1f\xce}\xe0Ur\xbbv%R\x15\u00caF\xb0\"\x11wE\xa7\xe3\xb8\xcd+\x94jP[\xd1Mo\x89\xda\x12\xcd\xf0#\xec\x18\x8b_\u007f\x94Ms>#\xf7\u013e\x8c\xb8\xe9A\x82\xe99T\xd7F0\f\x9d\u0120\x81\xe1\xf3#u\x1f\xba?\x00\x02f>\xfc\x1b\xd6|\xf6\xa6+\xe6\xfe\x80\u04f8qc}\xc8\xe0\x93\xf7>\xf5\xd4\xe3\x17\xe5\xe6\xe6/\xfd\xfc\xf3O\xc59\xe7\x8c\xf5\xc4\xdc\xe3g\x89\xe1\xed\x82\xefg\u0294\u027c\xf3\xce;\xa2i\xd3,u\xdc\xe3\xe7}\xfe\xf9\u0511\x97]v\xc5\u041a\x9a\xda\xd6;w\ue92a\xaa\x8aF\x8d\x1a\xa9.]:\x8b\x9e=O\xa2{\xf7\x1e\xe4\xe7\xbb\"\x9e\x91\x91Abb\xf2\xf1\x81\b@\x1d3\xc9W\xf7\xbf\x887np\xd3\xfa\xfe\xbe\xf2w8\xec\x8a\xfb\xc1\x83\a),\xdc\u02da5k((\xf8\x965k\u05a8\x8a\x8a\xaa\u4924\x843\xf7\xef\xdf\u007f\xe6\u0085\x8b.\xbc\xfc\xf2\xcbV\x8c\x181b\xf2\xc5\x17O\xf8\xa4\xee\xf9uB>s\xe6tF\x8d\x1a\xfd\xa3\uecd3N\xeau\xd4J\x037L\xbc\xec\xee\u28bd9\xd5\x15G\xce\u06bcy\xb3@\xf91c&\xa0(\\\xb7\x82\x99O\xdf\xcd\xd8\xfb^\xa2I\xf3\x1cl\xdbF7,\x94\xae\xa1\x94\x83-\x05R\xe0\x86\\\xe2\xcbF\x95\x02\xbf\xae\xa1\t\a\u007fr\n\x1dO9\x93\x03\x1b\v8\xb4e-[\xbe\x9eA\x971Wa\xe8\x1aR),[\xa1\xeb\x0eB\xd3Q\x8eE(5\x85\x8cV\x1d\xdd}-%RJ-\x12\x8b\x12\xae\xad\xcd]\xf4\xf57\ud065;v\xec\x10\r\xb7\xdf\xc3\xc3\x13\xf3\x9f1_}\xf5\x05\x8b\x16-\x12\xe7\x9e{\xfe\xd1\xc6\xf1J\xa5\xbd\xf9\xe6\xeb\x17O\x9d\xfa\xc5\x19\x0f>\xf8\xf0\xc9ee\xa5\xc1\xe2\xe2\x12\xfc~\xbf\u0763G\x0fN=u\x84\u07bbw/\x91\x9f\u07caf\u035a\x92\x94\u0538^\xb4m\xdb$\x1a\xad=&3\u37c9c\x1f\x15{\xb7\xff\xa5\xdf\xef\xa7E\x8b\x96\xb4h\x91O\x9f>\xfd9\xf3\xcc3(--e\xf7\xee=b\xe5\xcaUj\xee\u0739\xce\xea\u056b\u067auk\xcb\xed\u06f7\xb7\\\xbd\xba`\xf4\x84\t\x13\xee9\xf5\xd4\x113&L\xb8\xf4q!\xc4\x11\x80Q\xa3Fs\ubb77\x88!C\x06\xab\xd3O?\xf3G\u0747u\xf1\u007f!\xc4\x11\xa5\u0504CE\xfb>\x8aE\"\xa7\xed\u06bd\xcbM54-@\xb1i\xc1\x17$7\xcba\xf4\x1dO\xe3OL$b\x9a\b\xc3@H\x89\xa6\x1c\xa4\x12G\xd3\x14\x1b4\xa9PRa\x9bQ\xb2;\x9dD\xab\u07a7\xb0\xf6\u02ff\xb1c\xd9\x1c\x9au?\x99\xf4\xbc\xf6\u0122\x11L\xdb\x01M#\xa89 u\x90\x90\x9c\x99K09\x95hU9JI\x11\x8b\xc6,\xdbq|\xc5%\xa5\x1d\x00\xaa\xaa\xaa\xbd+U\x0fO\xcc\u007f\tTU\x95\x8b\xe4\xe4\u0506mox\xf6\xd9g\xae\xba\xf2\xca+\xee[\xb6ly\xf6\u07bd\x85\x84\xc3\x11\xb2\xb3\xb3\xcdq\xe3.\xd0/\xb8\xe0|\xa3{\xf7\x1edd\xa4\xe3\xf3\x05\x01\xb0\xed\x18\xe1p5J)4M\x8b\v\xb7@\xd3\xfe5\x9d8^\xf8m\xdb\u01b2,\xa4\x94\b!\xf0\xf9|\xb4h\x91O\x8b\x16\xf9\f\x18\xd0_\\~\xf9\xa5\xc6\xfa\xf5\x1b\x98:u\xaa3g\xce\\\xb9}\xfb\x8e\xa4\xfd\xfb\xf7w]\xb3fM\xd7\xf9\xf3\x17\\\xfe\xfa\ubbfd|\xe5\x95W\xdd/\x84\x90O<\xf1\xa4z\xe2\x89'9r\xa4T4i\x92\xf1\xa39R!\x04J)\xee{\xf0\x01\x9f\x10\xa2z\u03de\x1d\xf7\xff\xe5/\xcf\f\xa8\xae\xaeJ.--SJ)a\xdb6J)\x96\u007f\xf82\xfe\xe44\x06\xdf\xf0gT\xa0\x11\xb1\xa8\x89\xc2F)\a\x15oN\xd1\xf0\xe2\xa4.\xd5Q:\x0e\xbeP\x02\xed\x06\x8f\xa6p\xcd\x12\xcavof\xd7\xf2\xb9\xa4\u4dad\x0f\xc98\x8e\xc4r\x14>]\"\x1dE\xa3\xf4L\x12\x1a7\x89\x8b\xb9['\xecHy9\u06f7os\xdc\x10\xd4n\xefK\xe0\xe1\x89\xf9\u03dd\x993\xbf\u0493\x93S\x9d\xb8\xb3\f=\xf3\xcc3#\x97.]r\xe3k\xaf\xbd>\xa2\xa8\xa8\x88H$B\u01ce\x1d\x195j\xa4\x1a3f\x8c\xbf[\xb7\xae\x04\x02\t\xf1g;XV\xf4hIV\xfd\xa7\xab\x92P\x17\x86q\x1b&\xbb\xab\x1fc\xb10B\xb8\xae=##\x93\xa1C3\x192d\xb0\xbe{\xf7n\xfd\xf3\u03e72m\xda\x17j\xed\xdau\xe2\xc0\x81\x03\xcd\n\n\n\xee\x9d5kV\xffg\x9e\xf9\xcb\xf37\xdd\xf4\xfb9B\x88H\x93&\x19j\xe6\xcc\xe9\xfa\xa8Q\xa3\x9d\x1fs;\xcb\u028a\xed?\xdf\xf3'\xf2\xf2\xda,_\xb4h\xc1\x05EE\a\xfe\xb6d\u0252\x8cp8\xecD\xa3\x11\u0772\x1dP\x8aE\xaf>\x04\xbaA\x9f\x89w \x83\xc9D\xa2&8\x02\xa4[tK\b04\x81\xae\xc5\xc3N\xc2]d\xe4\xd8\x0e\xe9m:\u04e2\xd7\x10\x8e|6\x89}\xab\x16\xd0\xf2\xa4!4m\xd3\t\u06cc`\xa1\x10\x8e\u0110\x12\u01f2\t5iJ0\u0794Z\u0157\xf7G\xa31\x8e\x1c)\a\xa0\xbc\xbc\x9c\x993\xa72j\xd4\xd9\xde\x17\xc2\xc3\x13\xf3\x9f\x1b;vlc\xc0\x80\x93\u0168Q\xa7;\x00K\x96|3\xec\xe6\x9b\u007f\u007f\xc7\xe2\xc5K\x86o\u0672\x95H$\xc2\t'td\xec\u0631j\u0738\vD\xbbv\x1d\xc5\xd1\x10J\f\xc7q\xfe'>\x87R\x8aX,V\u007f21\f\x1f\xad[\xb7\xe3\xe6\x9bo\xe5\xe2\x8b\u01cb\xa9S\xa71y\xf2dV\xacX\xc9\xee\xdd{F\x14\x16\xee\x1dQX\xb8w\xee\u0085\xf3\x1e\x1d2d\u063cQ\xa3F;-Z\u42af\xbf^\xa8\xf2\xf3[\xff(\u06d4\x9e\xdeL-[\xb6X\xf4\xeb7P\r\x1at\u02ac7\xdex\xedV\u04cc\xbd\xb5r\xe5J\xddql\th\x96e\x03\xb0\xe8\xa5\xfb@)\xfa_u\x17\xaaq\x1a\x91#%8\xb6\u0114\uef02O\x13\x04\r\x1d]S\xf5\u92a6i\xa2\x05\x13\xc9:i\b;\x96\u03a6\xa2p\x1b%\x9bV\u04fcCg\x94\xa90\xa5\xc4q$\xd2qP\xd2\u0097\x90T\xbfx(\xfe\x12H\xa5\x88F#\x0e@ff3O\xc8=~\xb6\xfc*c\x84EE{\x99:u\x1a7\xde\xf8\u007f\r\xc50\xf9\xa6\x9b~{\xe5\xb6m\xdb\xfe\xbca\u00c6\u48a2\xfd\xe4\xe66g\xfc\xf8\xf1L\x980\x9e\u039d\xbb\u015d\xb0uL~\xf8\xff\xf4\xc1\x15\"\x9e\xf6\xe8\a\xe0\xc0\x81}|\xfc\xf1d\xde~\xfb\x1d\u05ad\xfb\x8e\xec\xec,\xbat\xe9R\u0561C\x87??\xfd\xf43o\b!\xaa\xea\x9e\xfb\xf6\u06d38\xe5\x94Sh\xd1\"\xff\xdf\u078e\xab\xae\xba\x92\xd7_\u007f\x03\x80G\x1ey\u8669S\xa7\u0774e\xcb\x16,\xcbR\xa6i\t+\x9e\x83\x0e\xd0\u007f\xe2\xad\xf4\x9dx;\x18A\xc2e\xc5\u0636\x8d\xe9Hl\x05\xba&\xf0k\xe0\xd3\xdc@\x8a\xa3\x14\xd2\x1f\xa2\xb6\xb2\x825o?\xce\ue15f\x93?`\x14C\xae\xb9\x93\xa4\xd4tjL\x1bt?\xc1\x80ABR\x12\xb6\x19e\xca\xed\x17\xb3s\xe9\x1c4]\xc7\b\x84\x9c\u071cl}\xf4\xa8\x11\x9f>\xf7\xdc\xf3\x13\x84\x10\x91\xc2\xc2]\xa2e\xcbV\xde$\xa8\xc7\u03ce_\xe5r\xfeo\xbeY|\x8c\x90/^\xbc\xa8\xef\x84\t\x17\u007f5s\xe6\u033f\u031a5;\xf9\u0421b\u018c9\x87\xd7^{\x95G\x1f}\x84\u039d\xbba\xdb1b\xb1p}\xbc\xfa\xe7\x80R\n\u06f6\xe3\xdb\x1d%;;\x97\xdf\xfd\xee\x0f\xbc\xf1\xc6k\xdcp\xc3\xf5\xc4b&s\xe6\xccM\x9e1c\xc6_.\xbf\xfc\xb2\xaf\u05ac)\xe8[\xf7\xdc\xcb.\xbb\x82\xa5K\x97\xfd(\xdb1n\xdc\xd16sw\xde\xf9\xc7\xdf\xf5\xee\xddkR\u02d6y\x18\x86!\x02\x01\xbf\xf2\xfb\xfd\xf5s\x03K\xdf|\x82yO\xddL\xf8\xf0A\x92\u049a\x12\xf0\x1b\x18\x9a\xc0\xd0\x04R*\xa2\x0e\x84-E\xad%\x89\xd8\xeed\xaa/)\x85f\x9d\xfbb\x84\x12(\u0771\x9e\x8a};\t\x04\x02\b\xe9\xa0\xe2\xee\u0736\x1d@\xa05\x98\x83\xd04!\"\x91(MR\x9bt\x06\xda\x03\u0636\ud578\xf0\xf0\xc4\xfc\xe7\xc0\xa2E\v\xc5E\x17\x8d\xaf\xbf\xff\xf8\xe3\x8f]\u007f\xd7]w\u007f2k\u05ac\x81\u06f6m'??O>\xf2\xc8\u00fc\xfc\xf2\x8b\x9cz\xeai8\x8eM$R\xf3?\x13N\xf9W\x91R\x12\x8d\xd6b\x9a\x11z\xf4\xe8\xc5\x13O<\u018b/\xbe@\x9f>}\xe4\xb6m\u06d81c\xc6\xc0[n\xb9e\xf2s\xcf=\xf3\x1b\xa5T\xc0\x15\xe1\xf1\u03181\xfd\xdf~\xef\xe1\xc3Oe\u0294\xc9\xf5\xf7\x9f}\xf6\xafw\x9e}\xf6Y\x85\xb9\xb9-\x10B\b\xbf\u07e7|>_\xfd<\xc0\xba\xa9o3\xeb\xa1\xeb)\u07bc\x8a`\xe3&\x84BA\fA\xbc@W}).\xb7\xe2\xa2\x02#\x10\"\xa5U'\x92\x9a\xe5R{\xb8\x98\x8a};\xb0L\xab\xfe\xaf\xed\xf8D\xaa\xb2-l\xdb>\xbaO\x14*\x10\nRU]\xb5\x1d\xd8\x1d\x0fQy\xae\xdc\xe3g\u026f*f>o\xdelm\u0420!2\xeeZ\x1b\xdd~\xfb\xad/\u007f\xf4\xd1\xc7\xe3\v\n\n\x00\x9cQ\xa3N\x15w\xdcq\x9b6x\xf0\x10\xc0\xcd\xe9\xd64\xad^d~\xf615!\x90R\x12\x0eW\x13\f\x069\xff\xfc\v\xe8\u05ad\x8b\xf6\xdcs\xcf\u02f7\xdf~G\u035b\xb7\xa0yuu\xcds\a\x0e\x1c\x1c\xa0\x94\xbaB\b\x11>\xed\xb4\xd1,]\xbaX\xeb\xdf\u007f\xe0\xbfu9r\xee\xb9\xe7S^^Fjj:B\x88\x92}\xfb\xf6^\\YY\xf5\xf1\xb4i\u04f2\x8b\x8b\x0f\t\xbf\u07e7\x84\x10\u00b2m\xa4\xe3\xb0{\xd6vD[\x00\x00 \x00IDAT\xf9\\j\x8a\x8b\xe8?\xf16\xf2\xfa\x8e !1\x89\xda\xdaZ\x1c\xe9\x80\x10\bM\u00f6lJ6\xad\xc0\xb1LR\xf3;\x92\u07be\a\x15\x85\xdb(\u07f7\x93hM9\x9a?\x19G)lGaK\x90\x910\x8e\x19\xab\xdf\x17\x80\n\x85B\x1c8pp\xa3\x10\xa223;K\xcb\xcd\u0353\x9e,xx\xce\xfc\u007f\x98\xd7_\u007fM\x1b6l\xa4\x04X\xb1bY\x9f\xf1\xe3/\x9a\xf5\u99df\x8d/(( 11Q\xddv\xdb-\xda\xdf\xfe\xf6\xb66x\xf0P\x00\xa2\xd1\b\xba\xae\xf3\xdf,X\xf5S\t\xba\xae\ub626\x89\u3634k\u05d1\xe7\x9e{Z{\xfa\u99f4\xbc\xbc\x96j\xe5\xcaUL\x992\xe5\xc2\xf3\xce;\xf7\xf3\x82\x82\xd5]\x00\xfa\xf7\x1f(?\xf8\xe0\xbd\u007f{\xac\xa4\xa6\xa63c\xc6W\x1a@nn\x8b%c\u01desf\x8f\x1e=\xf64i\x92\x86\x10B\x05\x02~\xfc>_}6P\xe9\xee-\xcc|\xf4&\x96\xbf\xf98\xe1\xb2\xfd$%7\xc6\xef\xf3#\xa4D\xd7t\x90\x0e\aV\xcfc\xcd[\x0f\xb3o\xe9t\x1ae\xb6@\xf7\xfb).\xdcAuy9\x9a\u03c7\x94\n)\x1d,\x05U\xe5e\xc4j\xea\x9aN\v\xea\u02a6\u06cek\xd7'\x8c\x1f\x1f\xb8\xf3\xce;|g\x9ey\x86~\xe3\x8d\xd7\ubbfe\xfa\xb2>g\xce,}\u04e6\rZ\xc3u\x02\x1e\x1e\x9e3\xff/\xf1\xc8#\x0f\x8b\xab\xae\xbaZ\x02|\xfc\xf1G\xe7>\xf1\u0113o-X\xb00\xe9\xf0\xe1\xc34o\x9e\xa3\xee\xb8\xe3vq\xe3\x8d7\x00:\xb6\x1dsK\xa8\x8a_\xf6\u0730\x10n\xd7\x1f\xc7q\xf0\xfbC\\u\xd55\"77\x97\xbb\xef\xbeG\xae^]\xa0UTT\x8e\b\x85\x9e^2}\xfa\x97\x13G\x8f>c\xcaE\x17],\x9f}\xf6\x19q\xd3M\xbf\xfb\xb7T\xed\xb4\xd3N\x97\xef\xbf\xff\x9e\x18?\xfeb5x\xf0)\u07fe\xfa\xea+cm\u06da\xf2\xed\xb7k\xf2\xcb\u02cfH\xc3\u0405\x10BX\x96\x1b\x12\x89\x85\xabY\xf5\xc1\xf3\x14o]G\xb7\xb1\u05d0\u0575?\xbe\xe4T\xccH-\t\t\x89d\xb4\xeeD\xe1\u0499\xec\x98\xfd1M\xf2;\xe0\v%RUz\x90\x8a\xcaJ\x82\xb9\x06J\xc4\xdcu\xb4B\x10\xa9\xac \x16\xae\x89\u007f~\x90\xd2\xc1\xef\xf7\xd3(\xa9Q-\xc0\x93O>\x15\xf9G\xdb=p\xe0@\xfdO\u007f\xba\x1b\u01d1j\xf8\xf0a\xea\xe4\x93\a(\xc3\b\xb2g\xcfN\xf2\xf2Z{J\xe2\xf1\xdf\xffN\xff\x92?\xdc\xef\u007f\u007f\x13\x8e\xe3\x88\xe7\x9e{^\x01<\xff\xfc_\xc7N\x9b6\xed\u0765K\x97\x85jjjh\u06f6\xadz\xfc\xf1G\xc59\xe7\x8c\x05\xc04#\xfcZ\x1dX \x10\x044\u05ae\xfd\x96;\xee\xb8S\u035a5[\xa4\xa44f\u0420A\x911c\xc6L\x988\xf1\x8aO\x01n\xbd\xf5\x16\xe1\xf7\xfb\xd5C\x0f=\xfcO\xbf\u01de=;\xf9\xeb__\u2a67\x9e\xe4\uaaef\xe6\xb5\xd7^\x03\xe0\xbd\xf7\xde\xed>u\xea\xd4OV\xacX\u046a\xa4\xa4\x04!4%\xa5\x8c/,r\x87\xa9t,\x92\x9ad\xd2\xfe\x941\xb4\x1dq.\xa9y\x1d\x11\x02\xecX\x94\xed\xf3\xa6\xb0i\xc6\xfbD*\x8f\xa0l\x1b4A\xff\xdf<F\x8b>\u00d0\xb1(J@Bj\x06{\x17\u007f\xc1\xdc\xc7~Cmy\x19\x86\u03cf\x12\x9a\u04f6Mk}\xe4\xf0\xa1\u04c7\x0f\x1b\xf6F\xd1\xfe\xfdM5M\v\xef\u077bw\xe5\xc9'\x0f\x8c\x0e\x1f>\xd40\x8c\xa0\x01\xec\xfd\x9e\x92\xc3\xc7T\xba\\\xb6l\t\xfd\xfa\r\xf0\x14\xc5\xc3\x13\xf3\x9f\x82\x1bn\xb8^\xbc\xf8\xe2K\n\xe0\xc5\x17\xffz\xfaG\x1fM\x9e\xbcr\xe5\xaaP$\x12\xa1[\xb7\xae<\xf9\xe4\x13\f\x1f>\x12)M\xea\xf2\x9d\u007f\u0543A\b\xfc\xfe\x10\x85\x85\xbb\xb8\xf3\xce?\xf2\xe1\x87\x1f\x92\x90\x90\xc0\xc0\x81\x03#\xe3\u018d;\u007f\xe2\xc4+\xbe\x02\xb8\xed\xb6[\xc5\xe3\x8f?\xf1\x0f\xcfz\xdf~\xbb\x9a\x1e=z\xfeS\xef]SS\xde\xea\xce;\xef\x992s\xe6\xac\x13\x8b\x8a\x8a\\\xb1\x94\x12\xa9\x14\b\x1d!t\x94t\x90\xb6EZ\xab\x8et\x18u\x11m\x06\x9fIJ\xb3\xe68\xb10;W-b\xeb\xbcO)\u07b8\x12'\x16\xa5\xf7\xb5\xf7\x91?\xe8\f\x84\xb4\xd1\x04$\xa5g\xb1\xe1\xb3W\x99\xf7\xd4\x1fp,\v\xe16\x0e\xc5\xd05\u0661C\a-'';~\xa5\"),,\\\u05e8QR$33\u04d7\x96\x96\xee\x03\xb9\xc3\xe7\xf3\xefh\u07fe}E\xf3\xe6\xcd\x17]r\u0265\x87\x85\x10[\xbe\xefs\xfc\xf1\x8fw1x\xf0 F\x8e\x1c\u5a4b\x87'\xe6?\x06o\xbe9IL\x9cxE\x9d\x90\x8f\xfe\xf0\u00cf\xdf[\xb5jUJ$\x12\xa5W\xaf\x9e<\xfb\xec\xd3\xf4\xeb7\x10\u02ca\xe28\xce/>\xac\xf2Cq\xcb\xf5&RZZ\xcc\x1f\xffx7\x93&M\xc2\xef\xf73`\xc0\x80\x8aK/\xbd\xf4\xe2K/\xbdl:\xc0G\x1f} .\xbc\xf0\xa2c\x04\xfd\xb3\u03e60f\u0339?\xe4=\xda\xee\u07bd\xa3\xe3\xea\xd5\x05r\xf7\xee\u0772\xb2\xb2Jj\x9aV2l\xd8\xd0\xd3\u05ee]w\xff\xfb\xef\xbf\u03d6-[\xb0m\xc7\xcd\"\x12\x1aB\xe8\b\xcd\x00\x01v4\x8c\x11\b\u0462\xe7`N\x18y.-N:\x05\xbdq\x06\xbbW-`\xc5\xcb\u007f\xa6r\xdf\x0ez]s/\xad\x86\x8dE\x03\f\rR\xd23Y\xfc\u04bd,\x99\xf4\x88\x1bc\xf9\xfe+0\xe5\x9e\xd3D})\x06M\xd3HJJ\"++\x93\x94\x94\x14,\xcb\u079b\x99\x99Y\x99\x9f\x9fw 77w\xe6%\x97L\xf8\xb2Y\xb3\xac\x1d\xff\xaf\xfd\xe9\x8d-\x0fO\xcc\xffEV\xae\\&z\xf7\xee\xa7\\\xd1\xf9p\xf4\xcb/\xbf\xf2\xde\xe2\u014bS,\u02e2w\xef\u07bc\xf0\xc2s\xf4\xec\xd9\a\u04cc\xd4\xd78\xf18\x8a\x94\x92P(\x89\x8a\x8a\xc3\xdcq\u01dd\xbc\xfa\xeak\xe8\xba\xce\u0211\xa7V\xfc\xfe\xf7\xbf\xbbx\u0108\x91\xd3\x01V\xaf^!z\xf6\xec\xf3\x0f\x1d\xfa\xaaU+\xfa}\xf6\xd9\xe7\xadJKK\xba$$$\x0e\u07bd{O\xb8\xac\xacL$$$\xe4\x80\u02a8\xaa\xaaR\xb1XL\u0555\xfe5\f\xa3\xa6}\xfb\xf6\xd9EEE\xbe-[\xb6P[S\x8b\xed\xb8\x95\x0fu\u0747\xa6\x1b\bMC\xd3t\x1c\xdb\u010eFH\xca\xc8&\xb7\xfb@:\x9c~\t\xca\b\xb0\xf4\x85\xbb(\u07fd\x99>\xd7\xddG\x9ba\xe7!P\x18~\x1f2Z\xcb\xfc\xc7\u007f\xcf\xf6E\xd3\x10\x9a\x86j\xb0V\xa0\xaeDB\x03\x01\x96\xc7=\xae\x1c\u01d1\x80\x1e\n\x055\x9f\xcfOVV&\t\t\x894j\x94\xb4\xbbC\x87\x0e\xbb\xf3\xf2\xf2>\x1f>|\xd8w\xbd{\xf7\xfd\xfa\xfb\xf6\xc5\xff\xfd\xdf\r\\{\xed5t\xe9r\xa27\xc0<<1\xff!\xbc\xf3\xce[\xe2\xd2K/\aP\xdf~[0\xfc\xfe\xfb\xef\x9f2}\xfa\x8cd\xd34\x9d\xee\u077b\ubbfc\xf2\"\xbdz\xf5\xf5\x84\xfc\a\nzyy\x197\xdf|+o\xbe\xf9\x96\x13\b\x04\xf41c\xc6T\xdd{\xef=c:v\xec4?\x18\fj\xd1h\xb4auI\u07c2\x05\xf3:~\xf8\xe1G\x83\x0e\x1e<x\xd9\xee\xdd{:\x16\x17\x17k\x9a&B\xc1`\x90h4\x86e\xd9\xc4bQ\x1c\xc7F\b\xed8!u\x17\x01%&&*P\"\x1c\x8e\xb8\xcd=\x00\xc3\xf0c\x18>4]\a\xb4x\x98\x04\xacH\x18P\xa4\xb4hKbz6\xe5{\xb7a\x9b\x11\x86\xde\xfa,\xad\x87\x9c\x8dm\x9a\x04\x92\x1aQ\xba\xa5\x80\x19\u007f\xbe\x92\x92m\xeb\xfeN\u033f/\xdcTGC\x87\x1e/ \xe6(\xa5\xb0,\xcb\xd14\xcd\u07e8Q\x12\xa9\xa9\xa9\x04\x83A'9\xb9QU\xabV\xad\xcb:v\uce22}\xfb\xb6S\x87\r\x1b\xb6*##\xb3\xf0\xf8\xd7_\xb6l\x89\xe8\xd7o\x80\x97\x1e\xe3\xe1\x89\xf9\x0f\xb9\xa4-**l\xf5\xdb\xdf\xfe~\xfa\xfc\xf9\xf3\xdbWTT\u0431c\a^\u007f\xfdU\xfa\xf7?\x19\xa5\xdc\x06\x10\x9e\x90\xff\xff\f\x8ex\f\xfd\u02112\xae\xbd\xf6:>\xf9d\niiM\x18:t\xe8\xc6\x17^x\xee\xf4\xa6M\xb3\v\xe3\xfb=\xf5\x81\a\xee;s\u04e6\u037f\u0674is\xcf\x1d;v \x84 \x1c\x0e7\x9cP\xae\xaf\u007fX\x97\x1e\xf9}\xf9\xfbu\xc7\xd00\x8c\xfa\x15\xac\xb6m\xbb\xee\xdc\xf0\xa3\xeb\x06\x9a\xa6\xe3\u0583\xaf;\xf1\xd8H\xcbD\xd3\f4\x9f\x8f\xa4\xa6\xd9\xf4\xbb\xf2.\xd2\xdbt&RSE \x94\xc8\xc1\x8d\xabX\xfa\xda\x03T\x97\x1e\xa8\xaf\xeaX\xf7\x15h8\f\xea\xab4\xba\xa7\xa7\xef\x1d#\rJ\xfc\"\xa5T\x80\xd0u\xb7\xcd_rr2\x81@\x80\xb4\xb4&t\xea\xd4\xc9NII\xf9\xa8m\u06f6_\xe6\xe7\xb7Z}\xd6Yg\x954,\x99\u0423G\x0f\xe3\xba\xeb\xaea\xe4\xc8\x11N^^kO\xdc=<1\xff\x9e/[\xfa\x84\t\xe3\u07de7o\xfe\xe8C\x87\x8a\xc9\xc9\xc9\xe1\xf9\xe7\x9f\xc3\xcdZq\x88F\xa3\x9e\x90\xff@4M\xc3\xe7\v\xb2c\xc7V\xae\xb9\xe6:\x16,XHNN6#G\x8e\xfcj\u04a47\xcf\x02|\xbf\xfb\xddM\x9f~\xf3\u0362\xd1\x1b7n\"\x163\x8f9\x14\xc4c\xd0J\xb9\xa1\xea\xba\xfd\xfe}9\xfc\r\ufeee\xdd}\t\xdbvk\xe1h\xba\x0fM3\x10\x9a~\x8c\x007\x8c\x81\vM#\u0538\t\x89M\x9ab[&V,\x82t\x1cb5\x95\x84+\u0290\x8e\xddP\xad\u007f2|>\x1f\xc1`\x90`0H\xeb\u05ad\xc9\xc9\xc9.MKK[\x92\x96\x96V\x90\x92\xd2x\xfem\xb7\xddQ$\x84\xd8[\xf7\xf7o\xbd\xf5\x866r\xe4\xa92;\xbb9\xf3\xe7\xcfe\xe8\xd0\xe1\xde\xe0\xf3\xf8\xf5\x89\xf9\x8c\x19_\xb1p\xe1\xd7\xe2\xb1\xc7\x1eW\x00\xf7\xdcs\xf7\vS\xa6|z\u00e6M\x9bHHHP\x8f?\xfe\x98pk\xb1(\xa2\u0470'\xe4\xff\xe4\u054ea\x18\x18F\x80\xe5\u02d7r\xf5\xd5\u05e8\r\x1b6\x8aN\x9dN\xe0\x8a+&>\x9e\x94\x94\xd4\xf4\xd9g\x9f\xbb|\xf3\xe6-\xff0\xad\xb3n\u007f7lX}\xb4\xd6\xfb?h\u05a1\x14R)\xb7T\xadRnHL\xd3\u30ae\xd7\u05c9o(\xca\xf1P7B)\xecX\fG\xd9?H\xb2u@\xd3\x05\xb6\xa3~\x12\x897\f\x03\xbf\xdfG(\x14\"33\x93\x9c\x9c\x1c\u0574i\xd3\xe5\x19\x19\xe9S\u018e\x1d\xb3\xec\u44c7,=\xfe93g~\u0168Q\xa7{\x03\xd0\xe3\xd7%\xe67\xdex\x03/\xbc\xf0\"\x00\xaf\xbe\xfa\u0288\x17_|q\u06bau\xdf\x05\x95R\xfc\xe1\x0f\xbf\xe7\xd1G\x1f\xc6\xe7\xf3y\x8e\xfc\xdf\x10tM\xd3\xf0\xfbC|\xf8\xe1\xfb\xfc\xf6\xb77QZZF\x9f>\xbdcyy-\x03\v\x16,\xa4\xa4\xa4L\x89x\xe7\x88\xe3E\xfd\x18\xc7]\xe7\xa4\x1b\x8a|\x03\x1b\x0f\n%]!\xff\u01c36^b\xa1>\xde\xee\xde\xfc\t\x894JoF()\x19_(\x01_ \x88\xe1\xf3\xe1\xf3\x19\x84\xfc\x06\x86\xc0\x9dh\xd5\x04\x89\x8d\x92\xc9h\u0782\xe4\x8cl\x84\xe1\aM#\x1251-\x13G\xbaW\x03V\xa4\x96HM5\xb5U\x15T\x94\x1e\xa2d\xefN\x8e\xec\xdbE\xac\xb6\xa6~\xfb\xea?O\x83\xab\x83\xff\xd7IM\b\xd04\x9dF\x8d\x1a\u047auk\xd2\xd3\u04cb\xf3\xf3\xf3\xbe>\xf7\u0731\x8b\x86\r\x1b\xf1\x8a\x10\xa2>G\xf6\x92K&0b\xc4p\xe2s@\x1e\x1e\xbfl1\u007f\xe2\x89\u01f8\xf5\xd6\xdb\xeb\xbeDY\xa7\x9f~\xfa\xeco\xbe\xf9\xa6suu5g\x9cq:o\xbc\xf1\x1aM\x9bf\x11\x0eW\xff\xa4M#~\xe9H\xa9\xf0\xfb}\b!\xb8\xff\xfe\ax\xf8\xe1G\xd1u\x9d\xe4\xe4d\x94\x92\xaa\xb2\xb2\xaa\xbe{\xd0O\xb1\xf0J\xd7\r\x8c@\x00_ \xc1m\xd4l\u06d8\xd1\bB\bB\u0269d\xb4\xea@\xeb\x01\xa7\u04b4M\x17t\xbf\x1f_ \x80?\x10\xc4\xef\xf7\x91\x14\xf4\x91\x92\xe0\xc3\xf0\xb9\xd5\x195\x01\x9a/@(9\x15\x15\x00G\x82\xe5\x80%\x8f\x9eT\x1c\a\xa4\x05\x96\x19\xc32\xa3\x985U\u0516\x1f\xa6\xa4p'\xfb\xb7\xac\xa1\xe8\xbb\x15\x14\xef\xdcL\xb4\xaa\x1c+v\xb41\x89\xe6\xaau\xbc\u035d\x02)\xff\xce\xed\xc7E]*\x05~\xbfO\xcb\xcf\xcf'//\x8f\x13N8a\u0168Q#\xdf\x1e1b\xd4K\rO\x80\u007f\xfd\xeb\xb3\xe27\xbf\xb9\u024b\xa9{\xfc\xb2\xc5|\xc1\x82\xb9\xe2\x94S\x86+\x80K/\xbd\xe4\x91y\xf3\xe6\u0771\u007f\xff\x01\u06b5k\xab\xde~\xfbM\u0477\xef\x00\"\x91\x9a_L\xb1\xac\xff\xae\xa0KB\xa1D\xca\xcaJ\xb9\xfa\xeak\xf8\xfc\xf3\xa9\xca\xef\xf7\x8b\xc4\xc4$\x1c\xc7&\x1c\x0e#\xe3\xcd$\xfe\x91\xa0\a\x02A\xfc\xc1 >\u007f\x00M\xd7\x11\x9a\xee\xe6\x91\xeb\x1a\x9an\xe0\v\x04\t&5&)-\x83\x94f9$7\xcb!9\xad\x19IM\xd2\t5N\xc5\x1f\b\xa1\xa4\xc24c\u0516\x97Qqp?\xc2\xf0\x91\x9a\u05d1\x94\x96\xed\xd0\x02!l\xc7F\x17`h\x1a\xba\xa6\xe1\xd3\x05~C\x03MG\x02\xb6\x84\x98\xa3\x88\xc6Lbu\xd50\x95@\x89\xba/\x85\xdb\xcdH\b\xf7\n\xc0gh\x04|\x06\x01\xbf\x0f\xa5 RSM\xb8\xe20\x87\xf7\xed\xe2\xc0\x96\xef\u063f~\x05%\xdb\xd6Q[v\x88XMe}\xa6\x8c\xd0\u2379\x1b\x84\x8b\x8e\x17uM\x13\xd2q\xa4\r\xf8[\xb5jEnns:w\xee\xfc\xdd\xc9'\x0f|l\u0738\xf1\xef\x1f\xbf\xff\x16/^$\x06\x0e\x1c\xe4\t\xbb\xc7/G\xcc\xdf\u007f\xff]\u018f\x9fP\u007f\xff\x85\x17\xfez\xfeK/\xbd\xfc\u0386\r\x1b\x83III\xea\xe9\xa7\xff\"\xae\xba\xea\xea\xff\xa9n@\xbf\x14\x02\x81\x04V\xadZ\xc1\xd5W_\u00fau\xdf\u0468Q#\xfc~?\xd55n\xa9`\xa5\x14\xb2\xc1>Oi\x9aI\u04fc\xb6\xb4\xec\u0503\xdc\xf6]H\xcbjNb\xe3\x14\x94\xe1\a\xcd@\t\x1d\xdd\xef\xc7\x1f\bb\xf8\xfdh\x86\x0f\xdd\xe7G\xf3\xf9\x11\x86\x0f\xc3\aq\xcdwO*\x8e\xeb\x9c\x1d\x1b\xccH\x14\u04f2\x89Y\n\xd32\xe3B.\xd04\x1d]\xd7PB\xc3A\xb8\xff+0\xa5\xdbrNA}\x8c\\*7n.\x04\b\x14*^[W\x13\x02\x05\xe8\xc2-\xb7+P\u8e86\xe6\xf7\xe3\xf3\xfb\xdd\x05L\xa6\x83\x19\rS]\xba\x9f\xd2-k8\xb0v\te\xdb\xd7S}h\x1f\xe1#\x87\xb0\xa2\x11@C7\f\x84\u0410\xd2q;\x1f\xa9\xefM\x8dTB\b\x91\x99\x99I\u02d6-\xe8\u0673\xe7\xa6\xe6\xcds\x1e:\u55213{\xf7\xeeS)\x84\xa8\u06e9\xdaSO=a\xb4i\xd3\xda:\xfb\xec\xb1\xea\xd5W_\xe6\x9ak\xae\xf3\x06\xa6'\xe6?_\xbe\xf9f\xa18\xf9\xe4!J)\x95w\xe1\x85\x17\u03181cF\x87\xea\xea\x1a\xfb\xf2\xcb/7^\u007f\xfd\x15t\xddO4Z\xeb\xc5\xc9\u007fd\xdcf\x12\x06\x0f?\xfc\x10\xf7\xdcs\x0fR*\x02\x81\x00\x8e\x94\xd8\xf1\xaeA\t\x8dS\xc8l\u0749\xdcN\xdd\xe9|\xf2\bZu\xefO\xa8qj}\u03b6\x03H\xe2m>\x05\xf8}\xf5\xd1\t7\xf4,\xe3\xa1\x0e\x89[\xf6\xf68\xc7_\xefr\x85\xc0\xb4$\xb5\x11\x13+\xbe\x92\xd7\xd0\x056\x1a\xb6\x128\b\x1c\xa9\xb0\xe2o(\x94B9\xa0I\xe5\x164w\x14\x9a\x12h(\xa4\x00\xa1@\xb8\xadG]a\x8f\x8b<B 5PB 5\x81\xd0\u0709\\\xddg\xa0\xf9\xfch\xba\x86\x9b.i\x11=R\xcc\xe1\xed\xeb8\xb8~)\xa5;6PV\xb8\x8d\xaa\xe2\xfd\x98\xe1Zw\xf27\x9ev\xa9\xbe\xbfc\x95\x02\x84\xcf\xe7#%%\x85\x9c\x9c\x1c\u06b6m\xbd\xa7U\xabV\u04db4I\x9bu\xdbm\xb7\u007f#\x84(\xaf\xfb\xe3'\x9f|\xdc8\xf3\u0333\x9c\xf6\xed;xn\xdd\x13\xf3\x9f'\xf1\\_\x01\xa8\x9bn\xfa\xcd]\u04e7\xcf|h\xfb\xf6\xed\xb4i\xd3FM\x99\xf2\xb1\xe8\u06b5\xfb\xaf\xbap\xd6O\x89a\x18\u8e9fg\x9f}\x9a\x9bo\xbe\xb5\xfe\xca'\xb1q\n9m:\u04b4mgZv\xebO\xeb>CH\xcf\xcd\xc3\x10`9\xee*O\xe5H\xa4\x94X\xf1IN\r\xf0\xf94|\xba\x1e\x9f?\xfc\xfb\x8c\x12\xe9\x06\xa0\xe3\xf3\xa6\x02\xd5`\xf0*\x14Q[Q\x13s\x889\nG\xba\xae\xdav\xc0\xb6\x95+\xe0\x8eB\xd9\n\xddQ\xe8\x12\x84T\x88:{^\x97O\x1e\x0f\xaf(\x11?\x9b\xc4\xdfO\xe1\u693b\xb1pp\u217f\x10\xf1\t\xd0\xf8k\b@\xf3\x19\x88\x80\x0f\xcdo`\xf8u|\x86\u00ac,\xa5l\xc7:\x0em_\xc7\xc1\xed\xeb8\xb0m\x1de\xfbw\xe2\x9814\xddp_K\xc9\xf8\xa4\xef\u07fbu\x9f\xcf \x18\f\x91\x92\x92B\xeb\u05adHKK\x9bw\xc2\t\x1d\x17\x0f\x1atr\xc1\x88\x11\xa3\xbe\xa8\xfb;]\x0f\xb0e\xcbwL\x9d:\x8d[n\xb9\xd5\x1b\xa4\xbf\xc6\xef\xe5\xcfu\xc3\x1fy\xe4a\x03\xb0W\xae\\\xde\xe6\x96[n\x9d\xb8c\xc7\x0e\x00\xe7\xd2K/\u047bv\xed\x0e8\x9e\x90\xffT\x0e@\b\xa4t\x1dx\xe3\xe4d\x8e\x94\xbbF1-\xbb\x05g\xdd\xfc0-\xfb\f!f\x81m;\x84\xc3Q4%\xd1\\I\u018a\xbb\xdeX<n\x1d4tt\x04\x8e\xaa\x13T\xf7\u007f\xd9@\xb4\x1b\xaa\xbbj`?\xa4\x02\u06c2\u06b0$\x12\x95X\x96D:\x80\xe3\n\xb8\xee(\x84\xac\x13\xdc\xf8\xeb\u01c5\x17\x15\u007f\\\xa8\xb8h\xc7?[\x831#d\xfc\xf7u\x8f;\"\xfe{\xf79\xc4O\f\xc4\xdfCDM\xa8\x8e!5\rS\xd7P~\r#\x90L\x8b\x8e\xc3h\xd5e\x04fu\x19\x87vm\xa0hK\x01\xbb\xd6~\xcd\xee\xf5\x8b\x89\xd6V\xb9i\x9a\x9a\x86\xae\xdcYQ\x85\xac\xdf \u02f2\xb1\xacj\xc2\xe10\xc5\u0147\b\x85B\xc3v\xed\xda5l\u02d6-\\r\u0244\x0f\x87\x0e\x1d:\xf3\xf2\xcb'~.\x84\xa8l\u06f6=\x00w\xdey;'\x9ex\"\x17^x\x917X\u007fE\xfc,\xd3;\xde}\xf7\x1dn\xbc\xf17\xd2u.\xbe\xdbW\xacXqfee\x15\xbd{\xf7\xd2\xee\xbf\xff\u03e4\xa66\xc1\x8cw\x94\xf1\xf8i\xae\x8a4M#';\x8b\xe6y\xf9\x1c\xae\x0e\xb3\xbfp7\x8em\u046a\xcfP\xd2\xdbt\xa2\xa2\xb2\n\u02f6q\x1c\x85&\x14\xfex\b%\xe2(\"\x0e\xd4\xda\x02[\t|\xbe\xbae\xf2q\x01W\x02\x89\xeb\x80\x1d\xe5:c\x89\xfb\x98\x02\xa4\r\x8e\xa9\x90\x11\x87X\x8dCm\x85Em\xb5\x83S\xeb\xa0E$ZL!L\x85\xb0\x14\x9a\x03\"\x1eR\x11\xc4\xe3\xe0u\xd1\x19D}k\x96\xfa\xe5\xfa\x9a\xe6.VB\xc4\x17-\xe9\b\x8d\xf8\xfd\xa3W\x03u7M)W\xc4U\x83\xeb\x85\xf8\x95\x80\xe6H\x94\xed\xe0\xc4lb\xe1\x18\xb1p\f\xb4\x00\xa9Ym\xc8\xed\u0417\x96\x1d\xfb\x93\xd9\xe2\x044M\xa3\xa6\xbc\x183V\xeb\xeeW]C\x13\xfa\u07dd\xc5T|i\xaa\xe3H\x8a\x8b\x8b\u0556-[EUUe\u78a2\xa2s\x16/\xfef\xd0\xf5\xd7_w\xe4\x8b/\xbe\xd8\x02\xb0x\xf1\x12\x06\f\xe8/f\u035a\xed\rVO\xcc\xff\xb7)--\x13{\xf7\xee\xe5\xeb\xaf\x17v\xfd\uaaef\x9e/(\xf86\xa8\xeb\x1a\xf7\xdcs7\u00c7\x8f@J\u06db\xf4\xfc\x89\x91R\x92\x96\x96\u0189={!\xd2r\xf9f\xde\\\xaa\x0f\x97\x10\r\xd7\u04a2\xc7\x10\x02\xc9M0-\x13S\xbaU\v\x13u\x81)\xa1\xca\x12T\xdb`)H\xf0\xe9\x04tw\xa5\xa7\xac\x8bn\b7\xce]'\xecn\xefN\x894\x152,\x91\xd5\x0eN\x8d\x83Y\xe3P[e\x13\x89H\x94\xa5\xa8W\xfb\xb8\xf3\x16\xf1e\xf9\xaa>D#\xdc\xfct\u074dq\xfb\xfc\x01\x8cP\x10\u007fB\b\u007f(\x84\x91\x10\xc4\x17\f\xa0\a\xfd\xe8\x01?F\xc0\x8f\x11\xf2c\x04\xdd\x14\xc7@(H !\x84?\x18\xc0\xef\v\xa0\x1b\x06\x1aZ\xbd\x90\u05c9:\u01c9=\x8eB\xd8\xeee\x84c;D\xa3\x11L\xd3$\x98\x94F\xb3\xfc\xae\xb4\xe8\u060f\xacV\xdd\x10B#Ru\x183Z\x8b\x94\x0e\x9a\x86\x9b\xe9s\xdcITJ\x89\xae\xebB\xd7u*\xca\xcb\xed=\x85{\xb4\xe2\xe2\xe2\xdc={\n/\xbc\xe8\xa2\v;,^\xbcd\xf9}\xf7\xddW=k\xd6l\x1e~\xf8Am\u07bc\xf9\xde\xe5\xa9\x17f\xf9\xdfd\xc1\x82\xb9\u0525\"\xbe\xfd\xf6[\xa3\u05af\u07d0\x020`\xc0\x00F\x8d\x1a\x15\xbf4\xb5\xbc#\xfb\x1f\xc0\x8eE\x89\xe0#\xbf[_\xfa\x8c>\x9f\xd9o=\u02eeU_\xb3\xe7\xdb\xc5t8\xf5B\x1ca\xe0H\x87\x04\x1d\x92\xfd\x10\x8b\x82\xa3\x14\x8e\x04\xbf\x00\xbf\x13W]\x9fpSFp\xc5W:\xb8jo*\x94%Q\x96D8\xa0\x1c\x85\xb2%\xa6\xa3\x88\xd8no\xcf\xfa\x90\b\u2a17mPwE\xf7\xf9\xd1\xfd\x01t\xbf\x1ft\x81\xe3XH'\x86\xe9DP\x96\x85f[H3\x8cY[\x89\x15\x8b\xe2\xd8\x16\x8e\x1dC\xd96B\xd30|\x01|~?~\u007f\x10\u007f\xb0\x11>\u007f\"\x9a\x1e\x00\u0347\x1e\xf2\xe1\x0f%\xa2\v\x03iK\xac\x98\x89\x8a\xc5pl\u06dd\xb9=\xbaA`\xc7C?\x9a@\tI$Z\t\x9a\xc0\x1fJ\xa7\xcdIg\x91\xd3~ E[\x97\xb3i\xc9\x14\xf6o[IeY\x11\x8e\xed\x8ec\xa1\u05657R_\xafF\xd34t]7\x84\x10r\xff\xfe\x03\xce\xe1\u00c7}ee%\xe3\x0e\x1c8\xd8w\u07bc9w\r\x1b6\u20fb\xee\xba[\xe6\xe6\u62bd{\xf7*/\t\xe0W\x10\xfe\xfc\xb9n\xf8\xfb\xef\xbf\xdb\xea\xe5\x97_\x99\xbft\xe9\u0496Bh\xbc\xf2\u028bL\x9cx\x95\x97\xbd\xf2\x9ft\x02\x02\xaab6[U23\xe6\xaf\xe0\xc5\xdfM\xa0\xacp\a\x9dF\x8c\xe5\xf4\xbb\x9f')=\v'\\C\xcb$A\x93\x00\xec\xa8V\x14V+b&$*H\xd4\xdcE6\x9a_C\v\xb8\x82ET\xa2\xa2\x12,\xe9f\x95HE]B\x9e\x9b#\xae\x88\u064a\x98T\u023a\xd4\x17\x11\x9f\xbcT\x12)\x14\x9a\u03c7\x16L@\xf3\xf9\xb0c\xb5\xc4\"\x15\xc4j+\bW\x16SY\xbc\x9b\x9a\xb2}\xd4\x14\xef%r\xf8\x00\u044aR\xccp\r\xb6\x19E\u019bI+\xe5\x06\xf6\x15\xc4K\b\xb8\x8e^\xf7\x05\t&&\x93\xd0$\x83\xa4\xf4l\x92\u04f3Ii\x9aG\xe3\xb4<\x12\x933\t\x06S\b\x05\x1ac\xe8A\x1c\xd3\u008a\x85ql;\xbeJ\u050d\xe9\u0539w\x15\u03d0Q\x024C\xc7\b& \x84\x86\x15\xae\xa4\xb8p=\xdbW~I\u0476\u5517\xee!RS\x8eR2^`L\x1c\xb3}\x9a&\u0404@JG*%\xb5&M\xd28\xf1\xc4nj\xc0\x80\xfew\xdc{\xef}\x8f\xd7\x1d\xab\xfb\xef\xbf\xcf\xf8\xdd\xef~\xeb$'\xa7zN\xdds\xe6\xff[l\u0738\xf1\xe2\xc2\xc2\u0096\xb6\xed0d\xc8\x00\x86\r\x1b\x06\x80\xe3H\f\xc3[\xe9\xf9\x9f@\x01~C\u00e8\x8e\u04aa\xf3\x89t\x1dz\x16\xf3\xdf\xfc\v{V/\xa2t\xfb\x062\xb2\xb2\xc0/\b\u06cajKP\x1e\x83\x98\x03\xd2V\xf1\x15\x92n\xf8A\x85\x1d\xa4\x16?\x01[\n\xe1\xb81\v\xd5\xc0mK\xa9\xea#)\x86\xe1\xc6eL\xa9\u0710\x8cP(CG\x0f5\xc2\b\xf9\x89\x85\xab\xa8,\xd9N\xd5\xc1]\x1c\u07b9\x8e#{6pd\xcf\x16j\x8b\xf7aEkp,\xeb\u01e9\xb3%p\xcb\x05\x04\x13I\xcdjE\xd3\x16'\xd04\xa7\x13\xe9\xd9\xedHMoIJZ\v\x92R\u04f1b1\xa2\xd1p\u072d\x8b\xfaP\x90\x92\xee\x0fR9\u011cj\x84a`\xf8\x12h\u07a6\x1f\x99\u037bQQ\xbc\x8b\xa2\x1d+8\xb8\xb3\x80\xb2\x03[(/\xd9E\xa4\xb62^hL;f;t\xdd\xd0\x04\x8a\xf2\xf2\n\x16/^\"\xaa\xaa\xaa\x1e\x9b8\xf1\xf2\x93\a\r\x1a\xf4\xc8\xe5\x97O\\&\x84\xb0\xff\xf4\xa7{\xf9\xec\xb3O\xb4s\xce9W\n!\u0639s\x1b\xad[\xb7\xf3\x06\xb2'\xe6\xffy^y\xe5eq\xed\xb5\xd7)\xa5\x94\x187\xee\xc2\u02cf\x1cq\xb3(\xce8\xe3\fZ\xb4\xc8'\x1a\xadE\u05fd\x95\x9e\xffI1\xf7\xe9\x1a\x8d5\x8b\u018d\x1a\xd1i\xd0\b\n\xbe\xfa\x90\u0292\x03l_<\x936\xbd\ac\x18AJ\xc3\x11bRa\xd5\t\xb8\xed\u01b9\x95r\x85]\xd8\xf1\xec\x10#^|K\xa8\xfaKF7Y$\x9eQ\"@\xd7\xdc<r\x9fO\xe0\xd3\x05\x8e\u07cf\x13\fPc\u0654\xec\xddJ\xe9\x8ao)\u077c\x9a\x92M+9\xb2c#v\xb8\xfa{\xb7=\xadI\x13\xb2\xb22IKK\xa3Qr#\x1a'7&\x18\n\xe1\xf3\xf9\xea\xcb\x16(\xa9p\xa4C4fR]\x1b\xa6\xa2\xb2\x8a\xc3G*(;|\x98\xe2\xe2b\xa2U\xe5\u0626\x85mV\x10\xa9\xfa\x96\x03[\xbf\x05\xc0\x1fJ\xa2YnGrZ\x9fDv~\x0f\x9a\xb7\xe9I\x93\u0336h\xc2G,\x1a\u01b6\xad\xa3\xb5g\xe2\x93\x05\x02\x81t\x1c,;\x8c\xe9H\x90\x92Fi\xb9tn\x9aG\xfb\x93N\xe7\xc8\xc1\xed\x1c\u06b3\x86\xa2\xed\xcb\u067f\xb3\x80\x9a\xca\xd2x\xa3\x0e\x83\xba*1B\b|\x01\x1d)\x1d\u05ae[O\xd1\xfe\x03g\x14\x15\x15\x8d(((\xf8`\u035a\u056fw\xef\xdes\u02581\xe7I7\x12\xe5u@\xf2\xc2,\xff\x03\xfc\xedo\xef\f}\xfc\xf1\xc7\xe7\xad_\xbf\x81\x13N8\x81\xbf\xfd\xedmz\xf4\xe8\xe9\xd5_\xf9/\xa0\v\xa84\x15\xab*|\xec*\x8f\xf0\xf6\xdd7\xb0r\xda\xfbdw<\x91\t\xcf}FJ\xf3<\xaa\xaak\xb0l\x85\x13U\xc4b\x12\"\x12\u007fD\x11D\xe1fZ\u01d7\xcf\xeb\u008d\xdd\xc4S\x05\xad\xb8s\xf5\xe9\x1a\xba!\x10~\x81\xf0k\xe8!\x1fzb\x00-\x045\x95\x11\xb6,\x9b\u01f6%s9\xb8~\x05e[\xd7 \x8f\xcbd\xd2CId\u7de5]\xfb\xf6tn\u05ca\x0e\xadZ\u0432y\x0e\xe9\x19\u9924\xa4\x90\x94\x94DRR\x12\x81@\x00]\xd7\xe3c(\x9e\xc5\x1e\x8fQG\xa21\xaa\xaak(\xaf\xac\xe2Hy9\x87J\xca\xd8{\xe0 ;\v\x8b\u0631k7[7nd\xff\xaem\xa8\u0631}\x9f5\xcd \xb7m/Z\xb6\xefK~\xc7A\xe4\xb6\xeb\x87?\xd4\x18\xd3r\x1buH\xc7F\xc5\xeb\xa6+w\xd9\x04\x8ec#\x1d\v\xa5\x1c\x84r\x1b\xa8\xe8\x86\x1f]\u04e8)?\xc0\xa1\xddk\u0675a\x1e{6/&\\S\xee:uMw\xb3n\xe2a\x17Pn\x88G@vV3:w\xeet\xa4g\u03de\x9f^{\xedUogdd-\xae\u06fe6\xed\u06b2`\xde\x1crs\xf3\xbc\xc1\xec9\xf3\xff\f\xaf\xbd\xf6*W_}\r\x00\xabW\xaf\xba\xb0\xac\xac\f\x80\x01\x03\xfa\u04ed[Wl;\xea\t\xf9\u007f\x01\xa9 \xc9'h\uace8JkL\xbb^\x83(\x981\x99\u00c5;)\\\xb3\x94F\xd9y(K\xa0*m\x88J\xfc\xb6DJ\x81\x94\n\xd3Q(\x01\xbav\xd4UH\x01\xb6\x06\xb6\x00\xe1\x13\x84\x82:FPC\xf84\x8c\x84\x00F\x82\x8e\xee\x87\xca\xe2#l\xfeb\x1a[\xbf\x9e\u039e\xd5\xdfP]v\xa8\x81E\xd1I\xca\xc8&\xbf[O\xba\xf4\xeeO\x97N'\u042du.\xeds\x9b\x92\xdb,\x1d\xdf1Y\"\xeer\xd3\xfa2\x04R\x1e\x93\t\xe5f\xc1\b\x12\x13B$7J\xa4yv\xf61\x1e(\xec8\x1c,;\xc2\u03bd\a\u063ck/\xeb7nf\u00f7\xab\u067af\x15\x15\a\x8b\x90\u04a6p\xeb2\n\xb7.c\xdd\xe2\x8f\xc8m\u04d3\xb6\xddF\xd0\xf6\xc4\xd3HHi\x8e\xed\xd8\xd8f\xbc\xf3R]\x9c^\xb9\xce\x1c)\x91\xca\x01%\xb1cQ\x04\xe0\x0f\xa5\u042a\xebH\xb2[\x9dD\x8bv\xfdY\xbf\xec#\x0e\x16\xaeG\xc5\xdb\xeb)%\xe2\xb1t\r\xc3\xe7\a\xa5\u053e\xfd\x87TIiY\x93]\xbb\xf7\\\xb5i\u04e6\v\xfe\xfa\xdc3\xcf\xff\xdfon\xfa\xb3\x10\xc2\u06b1m;W_}\x9d\xf8z\xd1B5x\xd0\x10o@{\xce\xfc?K\u07fe}Vm\u0630\xb1\xa7\xa6i<\xff\xfcs\\r\xc9e\xde\xc4\xe7\u007f3\xd4\"\xa0$\xaaXU\x1d\xa2\xe0\xbbM\xbcy\xebe\xec\xdb\xf8-=\u03ba\x94\xb3\xeex\x05\xabV\x11\xad\b\xe38n\n\x9f\u012d\xaf\"l\xe5\xe6j\xc7\xf3\xb8\x95\x00\xe9\xd3\x10\x8du|\x89:\x81\x90\x8e\xcf'\xd0\f\x83@\xa2\x0fM@eq\x19\x1bf\u007f\xcaw3>\xa2h\xc3jb5\xf1\xc6=\x9aARF\x16\xf9]{\xd1u\xf0H:u;\x89\xbc\xdc\x1c\xba\xb4\u0320u\x8aA\b\x00\x1b\xcb4\xb1l\xc7\x15\xbc\xffG=\xf5c\xbb\x11\x1d\xfd\xacuY%\xa0\xd0\x04\xfc\u007f\xec\x9dw|UU\xba\xfe\xbfk\x97\u04d2\x13\xd2h\xa1\x86^D@)\n\x88\xbd\x01\x96\xb1\x97k\x1d\xb1a\x1f\xcbXf\xece\xf4\u07b9\xe3X\xc6^\u007f\x8c]\xb0PT\x8a\x02\x82\bH'\xa1CBB \xbd\x9c\xba\xcb\xfa\xfd\xb1\xf7>I(\x8ewF$\xced}>\x87\x84\xe4$\xd9\xe7\ucd5e\xf5\xae\xe7}\xde\xe7\xd5T\x15M\xd7A\xe8H\xa02nR\xbc\xbb\x8a\xcdE%,]\xb6\x8c\x85\xb3f\xb2j\xf1Bj\xcb\u02fc\u07c0/\x10\"\xaf\xfbP\x0e\x1d}\x01}\x0f\x9f@Zf\a,\xd3&\x11\x8f8]\x93l\x13\xcb4\x90\xb6\x89\x94n\u027f[\xf4$\xa5\x8dD\xa2\xeb\x014M\xa5z\xd7fV-\xf8;\x9bV~I\xa4\xa1\nE\xf3\xa3*\x9a\xa3\x9dW\x85\xfb\xde\n\xa4\xb4\xedx<&\xc3\xe9ij~~w\x86\r;l\xe6]w\xde\xfe`~~\xef\xef\x00F\x1d9J\xfc\xfe\xee;\xe5\u99df\xd9:\xa9[#\xf3\x037\x16-Z\xc0\x91G\x8e\x01\xe0\x95W^>\xff\x89'\x9e8\xa4\xa1\xa1\x811cFs\xec\xb1\u01f0\xbf\xf6^\xad\u35cb\u03b3\xfd\x82\x8e\t\x83.}\x06\xd2}\xc8H\x8a\xd7\xfe@\xc9\xea%Tm\\OV\x87~(\x8a\xc3A;\x05\x94N\xc1\x8e\xd4@Z\xb8\x05C\x8e\u02e0/\xa8\xe0\xcf\xd0\xd13T\x046\x9a\xee#\x10T\xa9/\xaff\xf5\x17\x1f\xb2\xf4\xe37(Y\xb7\f3\xe1P)\xbeP\x98.\x87\x1c\xc6\xe0\xa3Of\xd4\xf1\xa7\u043bO\x1f\xdae\xa6\xd19\x03rT\b\xd8\x06\x981L[\xe2)\x19\u007f\xea\tn\xafNH\xce\x17\x9b}\u0352`%\r\x14a\xa0\nAn@#\xb7k;\x86vm\xc7o\xc6\f\xa5\xf2\xb7\x17\xb1rM\x01\xd3?\xff\x9c93g\xb2~\xdd\x1a\x12\xb1\x06\xb6\x15~K\u0676\x15\xac^\xf8\x1e\x83\x8e\xba\x88^\x87\x9eLZF{\x8cd\x8cD,\xd1\xe8\xb4h7z\xd1x\x1d\xed\x04\n\x86\x11\u01f6u\xb2;\xf6c\u0504\xdb\xc8\xcd\xeb\xcd\xea\x85\xefS\xb9k\v\xb6\xb4P\x15\x1d\xdbvr\x11\x8a\xa3\x84W\xfc\xfe \xd1XB\xae][(jjjOI&\x8c\xe3f\u0318v\u07a9\xa7\x8e\xffd\u1885\xb2\xbc\xbcB\x00\xf2\xb9\xe7\x9ec\u04a4I\xad\x13\xfb\xd7Fy\xfe\x1a.\xf2\xd5W_K}\u07b6m\xdb\x1b\n\n\n\x8e\xa8\xafo\xe0\xf4\xd3O\xe3\x82\v.r\xb4\xc3?\u04a0\xb7u\x1c\u0623\x9d\r\xf8\\\xaa\xa4\x16\x9d\xf2\xcaZ\xd6/\x9aM\xb4\xae\x86\xbc\u0783\xe9\xd4{\b\xa6ab\xdb\x0e I!\x90\x9a\xc0\xf6)\u062a+\xd3S\x05\xfe\x80B\u042f\xa2\xa9\xa0\x06T\x02\xe1 \u04b0\xd9\xf8\xedl\xbe\xf8\xcb},x\xebi\xaaK\xb6a[\x16\xbe\xb4\fz\x1fy\x1c\xa7^\xfd;.\xb9\xe3\x01N\x9fp\"c\xfav\xa4\u007f\xb6\x8f.\xbaA\x86L\xa2\xd9\u03bc0\xed\x03\xdd$\x8e\x94\xfb\xa2e\xd9H\xdb\x00\xdb@U \xcd\x1f\xa0g\xe7<N9\xee\x18\u019f6\x81N]z\xd2\xd0`PQ^N<VOME\x11[V\u03e1|G\x01\xc1\xb4\f2s\xba\xa0\xf9\x02\x98F\x02\xe9\xedt4\xda\x10H\xaf\xbb\x92\x14H\xdb\xc62\x92h\xfe\x10\xed\xbb\r\"\xbbC>\xd2J\x12\xa9)#\x11m@(\x9a#\u007f\x942\xd5<[Q\x15!\x84\"kjk\xc5\u039dej\xf9\xee\xddg\xdes\xcf\xef\xd7|\xfc\xf1\x94\xf5\x9f~\xfa)\xd3gL\x17\x17_t1\bv\x99\xcf\x00\x00 \x00IDATO\xff\xf5if\u0398\xd9:\xc1[\xc1\xfc\xe7\x1dEE[\xf9\xdf\xff}\x1a)e\x9b\xbf\xfd\xed\xf9{\u05ed+\xe8\x18\x0e\x87\xe55\xd7\\-\x06\r:\x14\xcb2Z}X\x0e2\xa0\v\xc0\xa7\b\"\xb6N\xbd\b\xb2f\xfeWT\xef,\"\xb3}Wz\x1fq2\x96e9\xf2BM C\nvH%\x99\xa6`\xe9\n\xc2r\x8a\x88\xfc\x9a\x82\x8aDS5\xfc\x19!\xcawlf\xde\xcb\u007f\u2aff\xdeO\xc9\xdaeH\xdbB\xf1\a\xe9?\xf6\x14&\\{'\xe7\xdf|\x0f\xa3\x8e>\x82\x1e\xd9A\xfa\a-rE\x02\xd54\x1c\xe3*<;\x80\x83C=y\xc0nY\x06H\vE\x856\xe1\f\x8e\x18q\x18\xc7\x1cs\n9\xb9\xf9\xd4\xd4\xd4SV\xba\x1d\xcb2\xa8,\xdb\u0226U_\x11m\xa8\xa0MN\x17\xda\xe4vv]\x15\x8dT\u0751l${R\x04\xa9Db\xdb&RB\x9b\xb6]\xc9\xeby8\xe9m\xdaa&\"\xd4W\xef\xc42\x9dF\xd7N\x929E\xbb\bEQe}}\x83\xa8\xa8\xa8\xd0*+\xab\u03bc\xf9\xc6\x1b\xd7L\x9f1c\xfd\xe4\u0253\x99\xfa\xc9\x14q\xf9e\x97\xb7N\xecV0\xff\xf9\u01e0A\x87\xf0\xc9'\x9fq\xec\xb1c\xc7~\xf8\xe1\u01d7\x97\x94\x94\xa6\xf5\xea\xd5S\xdcr\xcb\xcd\xe4\xe6\xb6u\x16L\xeb8\xa8C\x02>\x15,\x14\xea\xf40k\x16\u007fK\xe9\x865\x04\xc2\x19\xf4\x1du\x12\xc1p\x1a\xaa_\"\xd2U\x94t\x15\xcb'0\x85@*\x02\xc5\x02\xdd\x06](\x04\x02!\x04\x925\xdfLa\xfa\xd3w\xb1j\xc6\xfb$c\x11\x00\xf2\x0f\x1b\xc3\xe97\xde\xc3\xf97\xdd\xcdQ\u01ce$7\x18 \xcd6\xc9S\x93d*f\x13?\x97\x83\x03\xe2\xfb}o\xa4t73\x03\x81Mvv:\u00c6\re\xe0!c\xc9\xca\xe9Bee\x05U\x95;1\x93qJ6/\xa5l\xfbJ\xd2\u04b3\xc8\xed\xd0\x1bE\xd3\xddJ\u0426\x15\xa5\xa4<\v<'I\xc7\xc2\xc2@\xf3\xa5\u046e\xeb :\xf7\x18B\x9b\x8cl\xea\xabJ\xa9\xafs\xc4\x02\x8a\xa2:\u0564Bq\xc4C\xaaB,\x1e\xb7*++}UUUgN\x9cxU\xe1\xec\u0673\v\xde}\xf7=\x1ex\xe8\x01q\xe7\x9dw1y\xf2\xe4\xd6\xc9\xdd\n\xe6?\xdf8\xf7\xdcs\xc5\xc7\x1fO!?\xbf\xc7y+W\xae<\xa3\xa2\xa2\x82\x91#Gr\xcd5\x13QU\r\xd3l\x05\xf3\x960\x14@U\x15\xaa-\x1f\x9b\xb6l\xa5\xe0\xdb\u0668\xbaN\x8f#\x8e!+\xbf;>\xbf\x81\xeeW\xb0qx\\\x81\xdb\xf4AJt\xa9\x92\x9e\u0786H\xed.\xe6\xbd\xfb\x14\xb3_\u007f\x88\x8a\xed\x1b\x01\bf\xb5\xe5\xc4\xcbo\xe4\xd2{\x1e\xe3\xb8S\x8f\xa3mZ\x10%\x91D\x97&\x1d|6\xed\xfd\xb6\xe3\xef\xf2\v!\xb8\xa2(h\x9a\x86\xae\xebh\x9a\xf7\xd0\xd045\xd5\xd1j\u007f'E\xdbv\"iU1\xe8\xd0>\x87C\a\x1fI\xbf\xfe#\xf1\xf9\xd2).\xdaB<\x1e\xa1\xbe\xba\x94\xe2\r\x8b0\x8d\x18\xb9\x1d\xfb\x11J\xcf\xc62\x93N\xe5g\xd3>\xd6{6\xb5\xb6\x9d\x1e\xa7\x96\x99$\x18\u03a5k\xcfat\xea\u069fD\xa4\x9a\xdd;7!m\x1bU\xd5R\xbdS\x05\x02EU\x95D<aU\xd7T\xfbjjjO\xbf\ubbbb\xea\xa6M\x9b\xf6\xfd\xd7s\xbff\xf2\xe4\u027c\xf1\xe6\x9bb\xea\u0529\xad\x93\xbb\x15\xcc\u007f\x9e\x91\x97\x97'\xbe\xff~\t=z\xe4\xffv\u04e6\xcdCjjj\x12g\x9du\xa6v\xf2\u0267\"\xa5\xd5j\xaa\u0542\xf8\x16]\x11D\xd1(\xad\x8e\xf0\xc3\xdc\xe9\xc4\xebk\xc8\x1f6\x9a\xbc\x81\x87b%\x93XR\x90\xb0\x04\xba\xea\xd02B@(=DZ DY\xe12\xbex\xf1\x0e\x96\xcex\r#\x11\x03\xa0\xef\x91\xc7\xf3\xdb?>\xc5o.\xbf\x8a\ue773Q\xa2\t|\xb6I\xa6.\xe9\x14\x90\xe4\xfa$\xaaH\u016d\at\xf8|>4\u034f\xaaj\b\xd7\xd9PJ\xdb}H\xb7\xb7\xa7\xe6\xf4+\xd5t4\xcd)\xe0\xd9\xef\xe2SM\xfc~\x83\xee\u077a2d\xe8\x18\xf2{\f\xa2\xbc|\x17\xa5\xa5[I\xc6\x1b\u0631y)uU\xc5d\xb7\xcf'\xabm7\xc7j\xc0\xb6HeB\x9bR/\x82Ta\x95\x94\x12#\x99\xc02mr:\xf6\xa4k\xfe`\xccX\x1de%\xeb1-\xd31\x1b\x13\x8d-\xedTUU\x12\u0244U]]\xed\xab\xaa\xae:\xf5\x81\a\x1e\xe8>\xe5\xe3\x8fg<\xf8\xe0\x83\xd6\u0529Sy\xe6\xd9g\u014c\xe9\xd3[\xe7w\v\x1f-^\u0372y\xf3\x06\u0473g\x1f)\xa5\f\x9dt\u0489\ud28b\x8bIK\v\xa9\x83\x06\rr#\x9d\xd6\xc4g\u02e1\x13@\x936m\x03\u042dG/\xd2s\xdaS\xbei-\x95E\x9b\x10@\xd4\x14\xc4-\a||\x8a@\x15\x92@z\x1a>\xbfB\xe1\x97\xd3\xf9\xea\xd9\xfb\xd9Q\xb8\u0519\x98iY\x9c}\xd9U\xfcv\xd2\rt\xcb\xefJ<\x9eD$\"\xa4\x05\x14\xc2\x1a\x844\x89.R\x01\xe9\x01\xa5H\xfc~?B8K\xa5\xba\xba\x82M\x9b6\xb3z\xf5j\xb6n\xddFEE\x05\x91H\x04]\xd7i\u06f6-\xf9\xf9\xdd\x190`\x00}\xfb\xf6\xa1]\xbbv\xf8|\x8e(2\xb1G1\x91t\x9b\\\xf8t\x81\xa64\x90\xdf\xddG\xe7\xff:\x8d\x11\xc3\a\xf1\xea\xab\xcf\xf1\xe6\x9b/\x90\x887\xb0\xee\xfbO\xa8\xad(b\xec\xe9w\x90?\xf0X\xe2\xf1(\x89D\xa4\x11\xc4]\x95K3^I\b\x14T,\u06e4\xa6\xa6\x92P\xb8#\xa3'\u0702/\x98\u0392\xf9\uf4ccG\xf1\aB\xa8\x8ak?\x8cDU55\x96HXK\x97\xfe\xa0J)/\xaf\xaa\xaa\n\x02\x17\x00\xdcx\xc3\r<\xf3\xec\xb3\xdcx\xc3\r\xad\x93\xbc\x15\xcc\xff\xf91g\xce\\\x01\xc8m\xdb6\xf7\x8dD\"=M\u04e4c\xc7\x0eb\xe0\xc0\x81\xad`\xde\xd2\xc0\x1c'!\x97\ud0ce\xedrh\u06f5\xa7\x03\xe6;\xb6QW\x17#j\xab$-\x13]\x11\x98\x96M(\x14\x00i\xb0x\xf2\xeb\xccy\xe91jv\x16\x03\u042d\xff\x10\xae\xbf\xedn~{\xe9y\xe4\xf8 a$0\x83NoO\x95\xc6f\x12\xf2\x00'8\x9d\x06\xd6\xe9\x80Ea\xe1Zf\xcc\xf8\x82\xa9S\xa7RX\xb8\x9e\xfa\xfa:\x12\x89d\xb3\xf9'\x84\xc0\xef\xf7\x93\x96\x16\"??\x9f\x13O<\x91q\xe3Ne\u0108a\xf8\xfd!\xe2\xf1H\xeayMA]QUTa\x11\xf0'\x181\xbc;C\x06?\u0148\xe1\x83x\xfc\xf1\xc7\u067c\xb9\x90\x92-\u02d9\xf6\xd6\xef8\xf6\xcc\xdfs\u0211\xe7\xa2(\x82x<\x82\xeb\xf4\xd2\xecd\x84\xe7\xad.\x1c\xe2K\u0692h\xb4\x81`(\x97#N\xb9\x01\u0757\u0192y\xef`\x1a\x06\xc2\xe7kTZJ\xd0\x14U5MS~\xf7\xdd\x12\x11i\x88\x9c\xff\xf0#\x8f\x88\xfb\xee\xbd\xf7b!\x84\u0675k\x17\x15\xb0\xa6L\x99\xc2o~\xf3\x9b\xd6\xc9\xdeJ\xb3\xfc\xdfG\u06f6m\xc5\xf2\xe5+d\u06f6m{~\xf7\xdd\xe2\v+++\xdbt\xef\u078dI\x93&\x89\xb4\xb4tL3\xd9z\x17[\x16\u04c2OU\xa9L\n\x96\xafX\u0156\x1f\x16\x92\x9e\u06d1\xbca\xc7!\x82\x19\x98\x96\x89%%\x81@\x00U\x1a\xcc\u007f\xe3\u007f\xf8\xea\xb9\ai\xa8*\a`\xf8\t\x13x\xe2\xcf\u007f\xe1\xca3O \xa4\x82iD\x11\xb6Ds)\x19\xb7\xc7\xc4\x01\x05q\xaf\xf9F \x90\u01ae]\xa5\xbc\xf8\xe2K\xdcw\xdf\x1fx\xf7\xdd\xf7\u063au+\x91H\x04\u06f6\xf1\xf9|4iE\xdaX\xfa\x1f\x8bQZ\xba\x93\x05\v\x160e\xcaTv\xec(\xa1G\x8fnt\xec\xd8\x19!\xc0r{\x95\xee\x19\xa9;\x15\xa8&>\x9f\xcea\x87\r\xe6\xb0\u00c6\xb3~\xc3V\x8a\x8b\xb7\x92\x885P\xbc\xf1;\xd2\xc3Yt\xee1\x14E\u0571,3\xf5\xa6\xefS\x13/\x1b?1M\x03\u0557F\xe7\xfcCQ\x05\xec,ZC\xd2H8\xfe.\x1eG\x83@(\x8a\x90H\xb9{W\xb9\xa8\xae\xae\x1eX^Q\xdeo\xf6\xacYS\xfb\xf5\xebg\u03593G\x1d7n\\\xabl\xac52\xff\xe7F8\x1cV\x00[Q\u0120\xf4\xf4\xb4.\x00\x9d:u\x12\xa1P\xa8\xf5\xee\xb5\xd0\xe8\\`\x93\x1d\xd2\xe9\u0439\v\xa0PWUNmu%9\xb9\x9d\x90R\x12Ho\x83\x99\xa8g\xce\x1bO0\xef\xf5\xff\xc1t\xbdT\x8e=\xe7\x12\x9ex\xe21F\xf4\xec\f\x18$\x13\xc6>[\xc7\x1dX\xaaH\xba\xc9L?\x8b\x17/\xe2\x91G\x1ee\xe6\xcc/R\x1e\u22a2 \xa5t\xf9s\r\xd3\xf5?Q\x145U5\xea\xd9\x02H)\xa9\xa9\xa9\xe1\x85\x17^d\u039c9<\xf6\u0623\x9c}\xf69\xa8\xaaJ<\x1e\xdf/\xa0'\x12Q4Me\u0318\x91<\xfb\xcc\v<\xf0\xe0\xc3|\xfe\xf9;D\x1bj\x98\xf5\xc1#\u0636\u0270\xe3~\x8b\xaa\xa84D\xebphtoW\x91\xcd{\xeb5\":\xf1D\x84\x80?\u0110\xa3/\u00d6\x92\xef\xe7\xbeE\"\x1e\xc5\xe7\x0f9\x85Tnd\xaf\xaa\x9a\xb0\xb1\xe4\xbau\x05B\xd5\xd4\xf3\xc2\x19\x19H)/\x11B$g\u03de\xad&M\xd3:\xf5\xe4\x93['{kd\xfe\u007f\x1b\xfd\xfb\xf7UV\xacXi\x0f\x1c8p\xdc\xe6\xcd[\x8e\u077d{\xb7y\xdcq\u01ea\xa7\x9d6\x01U\xd5Ze\x89-qR)\x02C\xd5X\xb1a;\x8b\xbf\xfa\x1cEQ\xc8?\xf2D\xb2\xba\xf6D\xd5|\b\xcb`\xd1\xcb\x0f\xf3\xed\xebO9v\xb4B\u5b097\xf1\xa7?=\xcea\xdd\xdac\x9bq\x92\xc6/\x9f\xd4\xf6\xf8qU\xf51s\xe64n\xbc\xf1&\x16,\xf86\x05\xe2\x9e\u007f\v\x80i\x9a$\x93\xc9\x14h{\tPUUS\x80\xdfT\xd1RYY\xc5\xe7\x9fO#++\x93\xe1\u00c7\xa3\xaa\xaa\xbb\x11\x88\xfdP<\x12)M:uj\xcf\xe0\xc1G\x11\x8dI\n\nV\x10\x8f\xd5S\xbcy\x19i\xe9\x99t\xeb3\x02M\u04f0\\\x9d9{)hd\xaai\x87\x87\xeb\x96e\xa0\xeaA:v\x1d\x88\"MJ\xb7\xaf\xc60\x92NR\xd7;\xfdH\x10\x8a\"\x84\xa2\xc8]e\xbbD}]\xdd@\xa1\xd0y\ua529\x9f\xbc\xf5\xd6[R(\xaax\xe0\x81\ax\xef\xbd\xf7Z'{+\x98\xff\xf4\u0467O\x1fu\xed\xdau\xb6\xdf\xef\x1fQZZzb}}\xbd\x9c0a\xbcr\xdcq\xc7\"\x84lU\xb2\xb4@\x9aE\x11 u\x8dU[\xcaX0}\nf,B\xaf#O\xa0\xeb\xd0C0c&\u07ff\xf1\x14\u07fd\xf6\x84\xa3\x9fV}\\t\xe3\x1d<\xfa\xd0\xfd\xf4o\x1f\xc6H\u01b0\xec\x83s\x92\xd7u\x1dE\u04595\xebKn\xbc\xf1f\n\n\n\xc9\xcc\xcc\xc4\xef\xf7\x13\x8f\u01db\x81s\xd3(\xbd\x11\x80\xed\x94U\xc0\x9e\xf3RQ\x14\x92\xc9$\xb3f\u0362]\xbb\xb6\f\x1b6\u048d\xec\x8d\xfd\x02\xba\x94`\xdbI\xb2s2\xe9\xdb\xef\b\xa4\xf4\xb3b\xc5w$bu\x14m\\JFf[\xba\xf7\x19\x01B`Zf3,oz]\"\xa5g\u013d6\x03\xcd\x17\xa2C\xb7C\xc0JPV\xbc\x163\x99L\x9d.\x9a<U\b\xa1\xc8\xd2\xd2RQ[W7\xf4\xa1\x87\x1fJ~\xf4\xe1G\vV\xad\\\xc9\xdau\xeb\xc5\xfb\x1f\xbc\xc7\x1bo\xbc\xd1:\xe9[\xc8h\xd1\xe6\u07f1X\x84\xf7\xdf\xff\xc0\x06H&\x93i\x91H\x04EQ\xc8\xca\xcaF\b\xb5\xb5\xea\xb3\x05\x93-> \x10\f\xa1\xea:F\xb4\x1e;\x11!\xe0\x83\x95\xef?\xcf\u04b7\xfe\x1b\xcbH\x82\xaas\u044dw\xf0\xf0\x03\u007f\xa0WN\x90x,\xc2A\xc2q7\xaa\xf6\xb1aC!w\xdf}\x0f\x1b6l$\x1cN\xa7G\x8f|\xc2\xe1\xf4\xbd\x12\xed\xd9\xd9\u0664\xa7\xa7\xa7~\xb6\x11(-L\xd3\xdc\u06e4\xcb\xe5\xe1\x93\xc9$\xbf\xff\xfd=\u03181\rP\xd0u\xfd\x1f\x9c\x16\x04\xc8\b]\xbb\x84\xb8\xe6\x9a[\xf8\xedUw\xa3\a\u04895T\xf2\xe5\xfb\x0f\xb3z\xf1T\xc2\xe9Y\xa4\x87\xd2\xd1u\xdd1\xd9J\xa9\xf8\xd9\xef&\x11\x8fGA\vq\xf8qW0\xec\xe8\vP5\x95d\"\x86e\xd9)\xf0O9\t\b!\x97|\xbf\x94\u0253\xdfy\xe4\xed\u0253\xcf\x03X\xb3z\x85\xfc\u04df\x9e\x14\x85\x1b6\xb4N\xf7V0\xff\xc7c\u04e6M*`I);\x1fr\xc8\xc0\xe3\xea\xeb\xeb\xd0u]\xe4\xe4d\xe3T\xbd\xb5*YZ\x1e\x8c;\xc5;\x01 '\u034f\xe2v\xc41j*X\xf3\xf1T\xbe}\xe9\x11\xe2\xf55\b\xdd\u03f97\xdc\u0243\x0f=@\x8f\xcc F\"\xbeW\x03\xe3_r\xf8|~\xa2\xd1z\x1ey\xe4Q\x96.]\x86\xcf\xe7#++\x9b\xea\xeaj\x8a\x8a\x8a\xf7z~CC=\x86\x91\xdc+\n\xb6m{\xafy)\x84H\x15\x13\x01\xd4\xd4\xd4p\xd7]\xbf\xa7\xb8x\x1b\xaa\xea\xfb\t&q\x82\xa0?N~\xf7 \xd7]w+\xd7^s\x1f\x81`\x98\xba\xea\x9d\xcc\xfc\xfb\x1f\u0670\xf2KBi\x19\xa4\x05\xd3\xf0i:\x9a\xaa!\x14\a\xd0=P\xf7\xca\xf9\x9b\x02z\"\x1eE\rf1\xe2\xe4\xab9\xfc\xd8\v\xd0\xfd\x01,\xd3\xc0\xf6\x1a`\v\xc5K\xae\x8ah,\xce\u04a5\u02d4\x8f?\x9e\xfa\u03b4\xe9\xd3\u007f\x030c\xfa4\xa9\xbb\xcd`J\xcbv\xb6N\xfe\x83<Zt\x02t\xf7\xee\xdd\u07a7\xc1D\"\x99fY6~\xbf*22\xc2{-\xa2\xd6\u0442\x00\u0775km\x17RP\xa4\x03l\xeb\xe7M#V\xf7\xff\x88\xd68\xa5\xe5'\xfd\xd75\xdc~\xcf}\xf4\nkX\xc9(\xf6Atcv\x80V\xe1\u33e7\xf0\u99df\x01\x90\x99\x99I\"\x91$\x16\x8b2t\xe8\x10\xbav\xedJVV&\xa1P\x88\xb2\xb2],Y\xb2\x84\x1d;JP\x14e/\xf0\xde\x17g\xee6`\xc60\f,\xcbb\xf5\xea5<\xf7\xdc\xdfx\xe2\x89?\xe1\xf3\xf9H$\x12?\x1aIK\x01>=J\xe7N~\xae\xbcr\x12\x91H=o\xbe\xf1\x14\x95\xbb\xb60c\xf2}\x9c}\u074bt\xea>\x04a[\u0130\x91\xb6\x8d\xad\xe0\xf6\vu\xb4\u4379P\x91re\x8cE\x1b\b\xa5\xb7a\u0109\x13\xc1\x96\xacX0\x05\xcb4\x10\x9a\xee\xde\x11\x05\x90h\xba.kj\xeb\xc5\xf7\xdf\u007f\xafdee\xbe\xbbf\xed\xdas\x0f\x198\xf0\u04de={\xb5v-j\x05\xf3\u007f<\xaa\xaa\xaa\xbcO\x8d\x86\x86\x86$8\xf6\xa5>_\xa0\x15\xcc[\xf0\xf0\xa0-S\x93\xe0&\xa8\xb7.[\x90\x02\xbd\x11\xa7\x9e\xc5\xed\xf7=\xc0\x90v\x01l3\x86y\x90o\xa3\xae\xeb\xd4\xd6V\xf1\xfe\xfb\xefS[[K\x9b6\x19\b!\xd04\x95+\xaf\xbc\x82\x1bo\x9cD\x8f\x1e\xbd\x9bD\xdf\x06_~\xf9%\x8f=\xf6\x04\xdf~\xbb\x10UU\xb1,\xa7C\xa9\xa7\x86\xf1d\x8a\x9e\xc2\u0176m\xd2\xd2B\xa8\xaaB$\xe2\x14\x10\xbd\xfb\xee{\\~\xf9e\xf4\xeb7`\x9f\xfe\xe9\xcd\xdeS\x1b\x14\x05B\xc18\xf9\xddC\\w\xedmTW\x973\u58d7(\u0776\x8a\xd9\x1f<\u0339\u05fdB \xad\r\xa6i`\xa9\x16\xa6\xe54\xbd\x964\xa9P\xa5\x89\xf2\xc5\x05\xf5x\xa4\x81P8\x8b\x91']\x8d\xb4,V,\x9a\x8ai&Q\x15\xcd\xdd\xe8\x1c\xe3\x05]\xd7eyy\x85\xf8\xe6\x9by\xbe\x8cp\xf8\xb5\xfa\xfa\xfa\x13\xc3\xe1\xf0r!D\x13O\x81\xd6\xd1J\xb3\xecc4\x8dVL\xd3LE8\x9a\xd6\xda\xe7\xb3eG\xe6\xce\xc7t\x1d|\xba\x9a\xa2\x1f\x00\xfa\x1d6\x92\xbb\xfe\xf8\x00\xa3{d\xa1\x99qLK\x1e\xe4k\x95\x80\xca7\xdf\xccc\u0672\xe5\xf8|:\xa1P\b\u02f28\xed\xb4\xf1<\xfe\xf8\xa3\xf4\xe8\xd1\x1b\xdb6\xb0m\x03\xcbJ\xa2(\n\xa7\x9c2\x9e\xc7\x1e{\x94~\xfd\xfa\xb8\xba\xf1\xc6\xdf\xe9\xa9^\xf6\xe4\xd2\x13\x89d3\xcae\xfb\xf6\xed|\xf8\xe1\x87xA\xca?\xdc$m\x87\xfe\b\xf8c\xf4\xef\x9f\u036d\xb7\xde\xc3\xf0\x11'\x00\xb0n\xe9t\xbe\xfb\xeaE\x14EC\xf7\x05\xf1i*\xba\xab\xac\xf1\xeco\xc1\x05w)\x91\xee\xc6#\xa5\xd3X:\x1ai@\x0fd1\xf2\xe4\xab\x198|\x1cB\x82i$\x1b7\x18\x97?\xf7\xf9\xfc\x94\x96\ucd27M\x9f\x91\xf3\xd0#\x8f\xbe!\xa5\xec\x04\xc8W^{\xad\xb5\xcdW+\x98\xef\u007f$\x93F\x93\x89\u072aZ\xf9\xb5\x8d\xdc\xecl\xb7\u035a3\xdaw\xcc\xe3\xc1\a\xefg\xc2\x11\x83\xf0[IL\xcb>\xe8\xe1\x9c\a\xac\xdf}\xf7\x1d\xa5\xa5\xa5\x84\xc3a\x84P\xc8\xcb\xeb\u0239\xe7\x9eC \x90F<\x1e\xc10\f\f\xc3\xc04M\x12\x89\x04\xb6m0f\xccXN>\xf9\x14\xb7xho\xde|O\xaa%\x99Lb\x18f\xb3\xa4\xe7\x9c9s\x89\xc5\x1a~r\xc3\f\xe9\xf2\u067a\x16a\xe4\x88n<\xf0\xc0\xe3t\xeb\xd6\a\x80\xf9\u04dee\u00ca/\b\x04\u00e8\xaa\x8a\xae*\u8aa3\xbaAx}Mid\xd2]\x91\x8b\rH\xcb\x01\xf4@(\x97Q\xa7\\G\xff\xc3N\u0136\r\x92\u0244\xdb|Z\xa4\x1e\xbaO\x17%;J\xf8\xfc\xf3\xcf\x0f}\xf4\xf1\xc7\u07d6R\xe6^u\xe5\x95\xd6\v/\xbe\xa4\xae\u07f0\xbeu\u2dc2\xf9>8 M\xdbk\xd1Ii\xbbG\xda\xd6\xd1b'\x95\x17y\x16\x15QSS\xedF\x9e\nw\xdd\xf9;\u039bp*\x8a\x11\xc30\x8c\x83\x0e\xe4RJt]g\xf7\ue76c[\xb7\x0e\x80\x9c\x9c\\\x84\x10t\xed\u0695\xbe}\xfb\x02\xd6>\xf9`Oz8l\xd8\xe18\t\xf9\xc6!<WB\xd1L\xe3\x97\xea/\xda4j\u07fcy\v\u02d7\xaf\xe0\xff\xa2\x12\xf6|]\x14\x11g\u0729\xc3x\xe8\xe1\xc7\b\x85\xc2D\xeb+\xf9\xe6\xd3?SS\xb1\r\x9f\xdfQ\xdah\x8a\x82\xae\n\x14\xa5\xd1\xcf\u073b\x1e\xa5\xc9\x03\xe9h\xdb#\x91\x06\xd22:2z\xfc\r\xf49\xf4\x18,3\x81a$\x91M\x1cpl[\nU\xd3\u5dad\xdb\xf9\U0008bbce\xfd\xcb\xd3O\xdf\x02p\xed5W[\x93&\xdd$\xcav\x95\xb5.\x82V0o>\xd2\xd3\xd3R\x9f\xfb|\xbe\xd4\x02\xf4\x16Rk\u04a5e\x0e]\x0f\x10\x8fG\xf8\xdf\xff\xfdKJ\tr\xf1\xc5\x17s\xd5o\xafDJ\x93\xa4a\xee\xd5~\xed`\x81\xb9\x10\x1aEE\xc5\x14\x15\x15\xe3\xf7\xfb\xc8\xca\xca\xc40\x1c*%\x18\f\xfd\x03z\xc6I\x94\x06\x83\xc1\xbd\xc0\xdc)\x1e\x12\xfb\xa0J\xec\x14e\bN\x92\u007f\xed\u06b5\xff\x14\x95\xe5\x045&\x97^r6\xb7\xdcz+\x00[\n\xe6\xb3\xf4\xeb\xb7P\x14\x15Us\xfa\xa6j\x8a@W\x9c\x9e\xa7\u04b3Vt\xd5-.{\x92\xb2I\xb0%D#u\x84s\xbar\xd4i7\x92\xdfo$\x96\x11\xc34\f\xa7\x85\x9d\x10^\xf7\"\xa1\xea\xba\\\xb1b%_}5\xeb\u0799_|1\x11`\xd6W_\xc8{\xef\xfdC\xeb\xc2l\x05\xf3=\x8e\xe9\xb99)|\b\x87\xc3>\x00\u04f4\x88\u0162\xadw\xae\x85\x9f\xa6^{\xed\xf5\x942d\u0630\u00f9\xfb\xee\xbb\b\x873\x89\xc7\xe3?\x99R\xf8\xa5\u01ae]\xbb\xa8\xa8\xa8@UU6m\xda\xc4\xee\xdd\xe5l\u0672\x95\xd2\xd2\x12@\xfdQ\t\xec\xb6m\u06e8\xaf\xaf\xdf\v\xe8\x1d\xaaE6\xfbZ\xd32\u007f\x0f\xf4\xe3\xf18;v\xec\xf8\xa7\xaf=\x99t\xe4\x91w\xdc~\vc\x8f>\x11\x80\u0173_g\xcb\u06af\xf1\xf9\u04f0\u0756q\xaa\x10(\u00a1X$`#\xdd*\xd3\xc6k\xf3\x14.\x96m\x13\x8b6\x90\u04f9?\u01de};]{\rs\xbay\xe16\x96\x16\x8a#}\x14\x8a0-\x8b\x1f~X\xc1\xab\xaf\xbd\xfe\u0127\x9f}:\x02\xe0\xd5W^\x96\x0f=\xfcH\xebbh\x05\xf3\u0191\x93\x93\u3b46J\u02f2w\uae8ei\x9a\xb2\xb6\xb6\xae\xd9q\xbeu\xb4\x8c\u0474\xf0\u6957^\xc60\f22\xc2\\\u007f\xfdu\xf4\xeb7\x10\u00c8\xb7\xa8{\xe6\x81jmm\x1d\x91H\x14\u06f6\xa9\xact\x14Tk\u05ac\xe1\xddw\xdf\al\x02\x81@\xb3\x8aN\xc7d+H}}\rs\xe6|MUU\xf5>\xc1\xfc\xc7\xd4){&G\xe1\x9f;\xac8\x11z\x82\xcc\xcc,n\xbb\xf5v22r\x89\u0515\xb3\xe4\xeb7\x89\xd6W\xa2\xfb\x82\xce\xc6\xe2\xd4\xe6:\u007fW\x8af\u0562\x8e\x0eG`#\\\xf5\x8b\xc02M\"\r\xf5\xb4\xebv(\u01ddu\v\x1d\xbb\xf4\xc30\x12\xa9&\u04e2Io\u044a\xca*f\u035a\x93\xfd\xdcs/L\x9e:uj\x1f\x80?\xfe\xe1>~\u007f\xf7\u077c\xfc\xca\u02ed\v\xa3\x15\xcca\xc0\x80Av\x87\x0e\xed5!DUaa\xe1\xa2p8\x8ca\x18\xb2\xa6\xa6\u0199~-\x98f\xf18S\xaf+\x8d\xcf\xe7K=4MsU\x06\xe2\u07ca*\xf2\"\xeeW_}\x95\x95+W\x010~\xfcx.\xbc\xf0\x02\xa0\xe5Z/4m0\xd1(\u0143g\x9f}\x8e\x97_~\x19!4B\xa10\xba\xae\xa3\xeb\xbak\x8bk\xf2\xec\xb3\xcf3\u007f\xfe\xfc}\x82vS^|_\xf7\xb8\xa9\x14\u0463i\xfeY\xa5\xadC\xdbH\x8e?\xe1x.\xbc\xf8*\x00\n\x96\xcd`\xcb\u06b9h\xba\x0f\u02d68i&\x97\xcbW\x1a#t\xefO\xca&\x9b\x83m;t\x8biZD\"\xf5t\xea3\x82c\u03fc\x91\x9c\xdcN\x18F\x1c[\u068dQ=\xa0\ubeac\xad\xad\xe3\u06c5\x8bz\xbd\xf0\xe2K\x1f\xaf]\xbb\xba7\xc0\x13\x8f?\u0395W\\\u054a\xb2\xad`\xeeL\xf8\x11#F\x00\u0436m[\xdb+\x9f./\xaf\xc0\xb6M\xa7B\xad\x05\x02\x9a\xdf\xef\xc7\xe7\v\xe2\xf3\x05\xd1\xf5\x00RJ\xea\xeb\uba69\xa9\xa1\xa1\xa1\xc1Q#\xe8\x81\xd4s|>\u07ef\xfe\x94!\xa5DU},\\\xb8\x80\x8f>\x9a\x02@\xe7\u039d\x988\xf1*\x02\x814\x92\xc9x\x8b\u0778\x02\x81\x00>\x9f\xaf\xc9\xe6\xea\x00qmm-\xf7\xdcs/w\xdf}\x17\v\x17.\xa0\xaa\xaa\x8a\x9a\x9a\x1a\xbe\xff~\x11w\xddu\x0f\u007f\xfb\xdb\vMk!\xf6\xe1\x82(\x9b}\xdcWd\xee\xf7\xfb\xc9\xce\xce\xf9WW\n\x86\x11#=M\xe5\xa2\v/\xa5g\xaf!\u0636\u024ao\u07e7\xb6\xb2\x04\u055f\xe6F\u070d\xcfol9\xd7\u0737E\xba\x91\xba-A\xda`\x99&\r\xd1(]\xfa\x8fe\u0538\xab\t\xa5g\x92\x88G\x9a\x9c<\x04B(\xc2\xef\x0f\u0206\x86\b\xdf-\xfe~\xe0S\xff\xfd\xbfS\xa4\x94}\x00~\xff\xfb\xbbZ\xf9\xf3_\x8a\xe2l\xc9\x17\xb7sg\t\xf7\xde{\x1f\x00\xfd\xfb\xf7\xd7JJJ(**\x92\xa5\xa5\xa5\x98\xa6\xe3\xfb\xdcbvEEA\u05ddb&\u00c8\xb3z\xf5r\xbe\xff\xfe{6l\xd8\xc8\xee\u077b\x89D\"\x98\xa6\x85\xaek\x84\xc3a\xf2\xf2:1p`\u007f\x86\x0f\x1fN\u07fe}\u071f\x95$\x93\xf1_]1\x94\xa7\n\xb1m\x83\u0253\xdfa\xf3\xe6\xcd\x00\x9cs\xce\xd9\x1c{\xec1\x80\xd5\"_\x93\a\xa8YY\x99dddP[[\x8b\xa6\xa9)\u0149\xa2(TTT\xf2\xc4\x13O\xf2\xe1\x87\x1f\u04ff\u007f\u007f\x82\xc1\x00\x9b7oa\u02d6-\x18\x86\xe1\xf4\x1eu9\xf5\xa6\\\xf8\xfe@|\xcf\u0476m[\xbaw\xef\xf63\xdc\x03H$\x1a\x18:\xa4?\xe7\x9d\u007f%\x8f?z\x13[\n\u6ce5\xf0[\xfa\r\xff\x8d\xd3+\xd7J\xba\u0478H\xb5\x99s\xaeQ\xa6\xe8\x16\\\x80\xb6\x05(R`\xdb`\x1a\x06q\x04}\x87M \x11\xade\ue53f\x92\x8cG\xf1\x05B\xde\x1b\x89\x00\x11\f\x86dMM=\xb3g\xcf\x1dx\xdf\x1f\xfe\xf0\xb6\x94\xf2\f!D\u0663\x8f=\xae\xa4\xa5\x85\xec[n\xbe\xb9\x15q\xffS\xc1\xbcc\xc7N\\w\xdd5\x12 ##\xbc\u057b\u6492\x12\x99L&\x85\u05d2\xeb`\x03\x99\xd7V,\x99\x8c\xf1\xd5W\xb3\x98<\xf9\xef,_\xbe\x82\xb2\xb2\x9d\xd4\xd4\xd4\xee\x17Hrrr\xe8\xd4)\x8f\x11#\x86s\u99572f\xccQ\xf8|AW\u07db\xfc\xd5P0\x8e[\xa0\x8fo\xbe\x99\xcb\xf4\xe9\xd3\x00\xe8\u07bd\x1bW^y\x05\xa0b\x181Z\xf6<\u02e3}\xfb\xf6l\u0672\x856m21\x8c\x9a\x94V\xdc;1m\u06b4\x89M\x9b6\xb9^\xe7\x0eMf\x9a&\xa6i\xeds\x83h\n\xec{\x82\xba'S\x04h\u07fe\x1d\u077a\xfd<`\xee\x9c2l&L8\x9d/f~\xcc\x0f\u02fef\xed\xe2)t\xe97\x96P\xb8-V\xd4Db\xbb\x8d4\x1acq\xdbkL\xd4\u060c\u03b5\x10\xb0Ql\x81\xb0\xc10\x12(\"\xc0\xa0\xa3\u03a3\xben7\xdf}\xf16\xa6\x91@\xd7\xfd\xc8T\xb0/DZZ\x9a,.)\x95\x9f~\xfa\xf9\b\u02f4\xee\a\xae\xbb\xf7\x9e\xbbm\x80\x0f>\xfa\x88s\xcf>\xbb\x15u\xff\x13i\x16wQH\x80\xcc\xcc\u0302d\xd2\xd8\x04\xb0c\xc7\x0e\xdb\xe1\xcd\x0f\xae<\u0476mt]G\b\x95\u014b\x17q\xc5\x15Wr\xd9eW\xf0\xce;\xefRXXHMM-\xaa\xaa\xee\xa57\xf6\x16{EE\x05+W\xae\xe2\xe5\x97_\xe5\x82\v.\xe4\xfa\xeb\xafc\u035a\x95(\x8a\xeeF\xba-_O\xefE\xe5\xa6\x19g\xea\u0529l\u06f6\x1d\x80\v/\xbc\x90A\x83\x86\x00\xcdU\x1d--27\xcd\x04=z\xe4\u04f7o_l\xdbIv6-\xea\xf1\x80\xd8\xcbqx\xd2\xc2h4\x9aR\x92\xec\xeb=\xf9\xb1\xb9\xa9\xaa\x8d\n\x99\u07bd{\u04ebWO\xa44\u007f\x96\xe5l\xdbQ\x86\f\xe9\xc6Yg_\x8a\xaa\xeal-\xfc\x96\xe2\r\v\x9d\xe8Y\xd3\x1dO\x00!\x90BA\xba2C\xa1\xb8\x92\u0166f\\\x12\x90\"E\xb7H\x04\xc9d\x02\u0157\xc6\x11\xe3&r\xe8\x91\u3476\x85\u0133\th,E\xf2\xfb\xfc\xac+X\xcf\xf4\x193\xaf\xbd\xe1\u019b\x9e\xf2\xae\xee\u0733\xcfn\xb5\xe0\xf8O\x06\xf3\x13Np\u0295o\xba\xe9\x96h\xbbvmc\xe0\xb8\xce\x15\x15\x15\x1dT0\xf7\"r\u06f6y\xf9\u55f8\xe4\x92\xcb\xf8\xfb\xdf\u07e5\xb2\xb2\xb2\x19X\xec+\u0269\xaa\xaacW\xea\x96[\x03\x94\x94\x94\U000b7ffd\xc8\xc5\x17_\xc2G\x1f}\xe0r\xef\xbe\x16\x0f\xe8\x96e\xa1\xeb\x01\x96/_\xc1\xb4iN\a\xf7\xfc\xfc\xee\x9cw\u07b9\x00$\x93\x89\x16{\xedB\b,\xcb\xc2\xef\x0f1t\xe8P\xc2\xe10\xb1Xt/\xe9dSu\u029e:\xf1\xfd\x01\xb9\xe7w\xbe/:\xce{NFF\x06#G\x8e\xc4\xe7\v\xfel\xc9a\xa7:\xd4b\xfc\xb8\xf1\f\x1b~\f\xb6e\xb2a\xf9t\f#\x86\xa6\xfb\x11( \x14\x84\ua078\u04b8\x8e\x14\a\xd0e\x93\xe8\x1c\xc0\xb2%\xa6ec\xd96\x91\xfaz\xb4`\x16\xa3\u03f8\x8e\xfc~\xc3H\xc6\x1be\xc2\x12/w\xa2\n\xa1(r\xe3\xa6-,Y\xba\xec\xf6?\xfe\xf1\xfe?\xed\x19\u0334\x8e\xff@0\x1f=\xfaH\xe9N\x82\x02\x9fO\xdf\x10\n\x05\xa9\xa9\xa9\xb5\v\n\nS\x8b\xe3`\x8c@ \r\xd34y\xea\xa9\xff\xe1\xb6\xdbng\xe3\u018d\xfb\\\u0626i\xa6\x00\xd9+&\xf1>\x06\x83\x01\xfc~\u007fJ%\xa1\xaa\x1a\xabV\xad\xe6\x86\x1bn\xe4\x85\x17^\x04\x04>\x9f\xbfEG3\x8e\xae\xdcb\xee\u072f\u0678q\x13\x00\xa7\x9dv\x1a\xfd\xfb\xf7k\x06n-}\x1c\u007f\xfc\xb1\xf4\xe9\u04d7\xba\xba:TUiv\x0f\xffQ\xa4\xbd\u03c5\x95\xaaX\x96\xfb\x05\xf3\xee\u077bs\xe2\x89'\xa4Ny?O\x90!@&\xe9\u07ff\x1d\xa7\x9c\xea4^\u07b6~\x11\x15%\x85\bE\x03UE\xb8\u0560RU\x9c\xc2SE8\x0f\x0f\u061b\x81\xb3\xb7\x99\x81e\x83mY4\xd4\u0551\u046e\a\u01dc=\x89\xdc\x0e]\x88\xc7\"N\xc4\xdf\xe4\x87uM\x17I\u00d4\xeb\xd6\x15\xf0\xfd\x92\xa5wN\xfe\xfb\xe4\u06fck|\xfeo\u007fkE\xf4\xffD0\xef\u0631\x8b|\u5557\x04@\x9f>}k;w\xeeB\"\x91\xb0=\xe9\xdb\xc1(@\xf1\xfb\x9dD\xe73\xcf<\u01e3\x8f>FCC\xc3O\x8e\x9aTUu9W\a\xa8u]G\xd34TUC\xd75\xfc~?ee\xbb\xb8\xf7\xde?\xf0\xf6\u06d3QU\x1d\xbf\xbf\xe5\x02\xba\xdf\x1f\xa2\xb8\xb8\x98\xcf?w\xb8\xf2\xcc\xccL&L\x18\x87\xdf\x1f\"\x99\x8c\xfd*\x16\x81e%\xe9\xd7o '\x9exB\xaa\x9d\x9b\xdf\xef\xdf\xe7\xe6\xdc\xf4\xb4\xf5cN\x87^\xe9\xfe\x9e\xf7\xdf;\xcd\b\x01'\x9ex\x02\x03\a\x0e\xe0gO\x10K\x89\xdf'9\xef\xdc3\xe9?`(\xf1\x86j6,\x9f\x81P\x15\x14UsA[`\xab\x02\u02efb\xeb*R\x15HE \xf7B\x04\x99\x92\xb8XR:\xc9S)i\xa8\xaf\xa5c\x9f\xe1\x1cu\xfaU\x84B\xe9\xa9NE4\xb1\v\xf0\u9e88\xc5\xe2,]\xf6\x03\x9f|\xf2\xd9\xfd\x8b\x16-<\n\xe0\xfa\ubb93\x1f\xb8\x06c-y\xac_\xbf\xee_\xfc\xf9\x82V0\xdfs\xec\u0631C\x02t\xed\xdau\x99\xa2(\x12\xf0\x17\x16\x16\x12\x8d6\xfc\xe2/\xc1\x89D\x15>\xfdt\nO>\xf9$\xd1h\x94\xb4\xb4\xb4\xbd\x9ew\xf2\xc9'q\xc7\x1d\xb73q\xe2U\xa9\x04\x97\x97PSU\x15)!\x1a\x8d\x12\x8b\xc5\xdc^\x92V*r\v\x04\x02TUU\xf1\xf0\xc3\x0f3o\xde\\\x84\u041a\xf9\u0534\x98H\xc0\x8d\xc6V\xacX\xc9\x0f?,\a`\xec\u06238\xf4\xd0C\x01\xfbW\x13\x95{\xa0{\xc5\x15\x971d\xc8\x10b\xb18\xc1`\xe0'\x95\xf3\xefo\xd3\xdeW\xa4\xdd\xe8^\b\x03\a\x0e\xe4\xca+/\xc7I\x10\xff\xbc}l\x1d\x9e\xdbd@\xff\x8e\x9c~\xfa\x04\x00\xb6\xae[@\xbc\xa1\x12U\xd7A\x11)\x9e\xdb\xd6\x15\x8c\xa0\x82\xa9\v,\r\xa4\xe2x\xa7\xbbt:\u04a3]\\Su\xcb+*\xb2m\x1a\xa2\t\xfa\x8f9\x8da\u01df\x8em\x19\u0636\xebc\xd3D_\xafi\x1a\xd5\xd55|\xbbpQ\u01bb\xef}\xf0\xb6\x94r(\xc0\xb9\xe7\x9c\xd3\"\xe6\xc7\u018d\x85\xfb\xfd^\u07fe\x03\xfe\xa5\xdf\u0777o\xff\xbd\xbe\xb6e\xcb\xc6\x03\x87M\xbf\x86\xc56d\xc8`\x00\xce>\xfb\xcc\xd5\x1f~\xf8\xe1.\xa0CQQ1\x1b7n`\xf0\xe0\xc3\xfe\xa1\x17\xf4\xcf\u0271\xaa\xaa\x8f\xe2\xe2\xed\xfc\xf9\xcf\u007fa\xf7\xeerrrr\xc8\xcd\xcda\xfb\xf6b\f#\xc9\xd1G\x8f\xe5\x96[nf\u0528#\xc9\xce\xce&\x91HPPP\xc0\x9f\xff\xfc\x17\xfe\xfe\xf7wH&\x13dffb\x18&\xf5\xf5\xf5\xa9\xeb\xb6,\vEQ\xf0\xf9\xf4\x94V}\xf3\xe6-<\xf1\u0113\f\x192\x84\x8c\x8c,,\xabe\xd9\x18\xe8z\x80X\xac\x81\u0673g\x13\x89D\x00'\xdal\xdf>\x8fX\xac\xe1W\xa3\x9dw\xfc~\x92\xf4\xee\u074f\x9bo\xbe\x89k\xaf\xbd\xce\xf55w|Z\xf6\xe4\u021b\xea\u01fd\xc20E\x11n\xbb\xb8\x1f{\xbf4\f#Izz\x1a\x93&Mb\xc0\x80AHi\x1e\x90\xbc\x88\xe1Jw/\xba\xf0l^}\xe9\x19\u028b\v\u0639u\x19=\x0e\x1bO\xd2H\x80\x90\b\t\u00b2As\xa3r\x01\x12\x05M\u06a9\x86\x18 \x91\xb6\xa7F\x97\u062e\\Q\x95\x02#\x99\xc0\fe0\xea7WP\xb6\xbd\x90\xf5\u02d7\x12\b\xa55\xd9\t\x9c\nQM\xd3)/\xaf\xb4\xe6\xcc\xfd\xba\xdb\x1f\xfex\xff\x93R\xcaqB\b\xe3\x8c3~sP|\u043f\xfcr\xa68\xe9\xa4S$@\xef\xde\xfd\xfe\xd1\xdc\u040b\x8b\xb7\x1d\xbbl\xd9\x0f\x9d~\xf8\xe1\a\xa3\xacl\x97\x15\b\x042\xbbw\xefv\x88\xcf\xe7\u03c8D\"\xbb7l\u0638\xda\xef\xf7%{\xf4\xc8W\x87\f\x19\xa2\x0f\x1e|\xe8\xf6v\xed:\xce\xde\xd7\xef\xf3|\xf1\x9f~\xfa/b\xe8\xd0!r\xec\xd8c\xfe\xb3\xc0|\u0528Q\x00\xf4\xea\xd5o\xfe\xf8\xf1\xe36\x86\xc3\xe9\x1d\x8a\x8b\x8b\xf9\xee\xbb\u017f(\x98{\x94\u039bo\xbe\xc5\xf7\xdf\u007f\x0f@,\x16\xa3\xb8x\a\xe3\u019d\u00ad\xb7\xdeB\xbf~}\xc9\xcdm\x0f@2\x19E\xd7u\x86\x0e\x1d\xc6s\xcf=\x83a$y\xef\xbd\x0fH$\x92)u\u011e\u05a9\u0264\x81\xdf\xefD4\x86\x91d\u039c\xb9\xbc\xf7\xde{L\x9cx-\xba\xae\xff\xecQ\u073f:v\xef.g\u07bc\x0584Xo\x86\r\x1b\xf6\x0f#\u05d68L\xd3DUU.\xb8\xe0<\u05ad+\xe0\x89'\xfeD\xc1 \xe3A\x00\x00 \x00IDAT]]\xad[\xca\xefT\x88\xee\xebu)\x8aB8\x1c\xc64M\f\xa3\xa1\xd9s\x9a\xde_]\u05f1,gS\x988q\"\xff\xf5_\x17\xe1\xd4\x15$\x0f\xd8\x06\x050\xa0\u007f_F\x8d\x1e\u00e7\x9f}\xce\xf6\xc2\x05\xf4\x1dq:BU\xb0\xb1Q$\xf8\xa5\xc0\x92\x12Kq\x146R\xb3\xb1m\x81\xb0%\xb6\x02B*\bl\a\xe8\xdd\r\xc0\x96\xa0HP\x10D\x1b\x1a\xc8\xed\u0715\xa3\u03fd\x92]E\u06e8\xad\xac$\xe0\x16\xf7y\u01b9\xaa\xa2 \x84\xaal\u07bc\x95\x99_|yBfV\xe6\x8bR\xca+\x85\x10\xf2\xfd\x0f?P\xcf;\xe7\xdc\x03Z\x1a\xbcr\xe5\x0f\f\x1e|X\xea\xff\x1e\x90\xbb\xefS\xa0\xa4\xa48TXX8z\u0252%\xf9\xb5\xb5\xb5\x87\xf9\xfd\xfe\xa3\x8b\x8a\x8a\xa2\xf5\xf5\xf5\xf6\xc9'\x9f\xa8\x1b\x86\xd5.\x12i\bF\xa3\x11\x99L\x1a6\b_(\x14\xd4\x15E\xc54\r\xa2\xd1X\\Q\x84\xbd`\x81_|\xf4\xd1\x14%\x1cNo8\u7733\xcb{\xf5\xea\x95\x00\xb9\xb0c\u01ce\x9b\x86\x0e\x1d\xb2}\u0420C\xe6ge\xb5\xdd\rp\xf3\u0377\xc8=\xe9\xbb\xff\b0o\u05eec\xea\xf3^\xbdz}\xb3j\u056a\xa3\x8a\x8bw\xb0`\xc1\x02&N\xbc\xaa\x99\xd4\xeb\xc0\x82\xb9\x8f5kV\xf2\xd1G\x1f\x11\x8b9\x15\x8d\u0468\x13-\x8f\x1e=\x8a1c\u01ba \x1e\u00f6mW\xfa\xe64\xf7\xcd\xc8\xc8\xe2\xb6\xdbn\xe5\xeb\xaf\xe7\xb1k\xd7.4M\xdb\xe75\u06f6M\"\x91$\x10\b\x10\b\x04\x88Fc\xbc\xfe\xfa\x9b\x9cv\xdait\xe8\xd0\t!\xcc\x16\x01\x94\xde\u01b6l\xd926mr\x12\x9fc\u018cf\xf0\xe0CS\r\x1c~m#\x91H\xe0\xf7\x87\xb8\xfb\xee\xbb0M\x83\xe7\x9e{\x9ex<N(\x14\xc40\x8cf\x16\xb6M\xefW\xd3\x13\u05be\x005\x18\f\xa4\xb4\xf8\x97]v\x19\xf7\xde{7\xe9\xe9mH$\x0e\xecI\u02f6\r4=\xc0Y\xe7\x9c\u0367\x9f}\u038e\rKH\u0517\x12\nf\x93HDQ\xb0\x91B U\x89\xa5J\x92&\x18\x16\xd8:(\xa6DH\xe9D\xe7\xaa\xe2\x14\x18)\xce\u05fcf\u03f6\x04,I]]\x9c^G\x1c\u03c8\x93\x971\xeb\xdd\u05f1L\x13U\xd3\x1c\xcb\x00\xe1Q?R\b\x81\xbd~\xfd\x06\xe5\x9bo\xe6_\x91\x97\x97W\x06\xdcs\xde9\xe7Ze\xbb\xcaD\x87\xf6\x1d\xfe\xe5I\xbdn\xdd\x1a1o\xde|\xa5\xaa\xaaRy\u8847e<\x9e0\x81f@.\xa5l\xf7\xc2\v\u007f\u02c8D\"c\xb6o\xdf6\xf2\x9ak\xae>\xb6\xaa\xaa\xaa\xe7\xb6m\xdb\xd5\xda\xda\x1aa\xdb\x16\x8a\xa2\x92H$\xb0,\v\xc30\x89\xc7cn\x9eC\xb8\xef\xabt,\x9c\xa5\xed\xe6\xc0\xf4\x80s\x10\x91\x9eT\xd7\x1f\x0e\x87sV\xacX\x010\xd8\xe7\U000d16dbK\xf7\xee\xdd\xe3\xb7\xdf\xfe\xbb)\x1d;\xe6\xbd\u007f\xd2I\xc7\xfd0h\xd0\xd0\"o\u04ff\xef\xbe{\xb5+\xae\xb8\xdc\xee\u0673\xb7\xfdo\r\xe6M\xc7\t'\x1c\xff\u0357_~u\x13\x90\xb1j\xd5\x1a\u05ae]\u01e0A\x83\x81\x03\x1b\xb1z7r\xe6\xcc/(,,l\xa6J\x00\xa7$\x1c \x12\xa9C\u04f4\xbd\x8c\x944\r\xfa\xf5\xebO\x97.\x9d\u0675k\u05cf\xca\xdbl\xdb\xc60\f\xfc~\xc7\xf6\xb7\xb0p=_}5\x8bK.\xb9\xec\x17;\x85\xfc\xe3\u0701\x0eX,X\xf0m\xca5\xf0\xf0\xc3\x0f'\x18L\xffUQ,{\x03z\x94\xb4\xb4\f\xee\xbf\xff\x8f\xe4\xe4\xe4\xf0\u05ff>CII\t\xc1`\x10U\xf5\xa3\xaa*\xc9d2\xb5\x89\xefi\xc9\u073c\xef\xa7s\xc2J&\x93dggs\xed\xb5\xd7\xf0\xbb\xdf\xddF\x9b6\xd9\xc4\xe3\x91f\xf3\xea@\x9d6|>\x9d\xa1#G\x93\x99\x95\u016e\x1d\x85T\x94\xae\xa6\xef\xb0\t\xd4V\xc6\x01\x81%,\xa4\x10(\xd2F*N\xa7\n\xa9\bPAZn\x8b9\xd7\xfaV\n\u0464\xdc_\xa2I\x81*!\x19K\x12\xb5\xd2\x18>\xe1|6.[\u0136\x8d\x85(\x9a\xe6\xb0-\x12\xa4\xf0\\\xd4Ql[\xca\u014b\x97\x88\x8cp\xf8\x8e\x05\xdf~\xbbl\xcc\xe8\xd1\x1fuh\xdfA\xd6G\xea\t\xa7\x85\u007f\xf2k+,\\K\xbf~\x03\x91R\xf2\xc9'S\u0122E\x8b\x94\x01\x03\x0e\xb1\x00\uf0542\f\xa4O\x9b\xf6y\xc6\u018d\x1b\xf2KJJ\x87M\x9cx\u0571555\xc7l\u0738I)/\u07cdiZX\x96I<\x1eO\x15\x8a\x99\xa6)\x15E\x11N#\x10\x87B\u06f3VDU\xb5\x94$\u007f\u03e0,\x99L\xb2{w9R\u06ae\xd8Ae\u01ce\x12\u05af_\x1f\xf0\xfb\xfd\x17v\xeb\xd6\xed\u0082\x82\xb5e\xf7\xdc\xf3\xfbWF\x8d\x1a5g\xfc\xf8\xd3\x16\n!\x12\x8f<\xf2(\u007f\xfd\xeb_\u052b\xae\xfa\xad\x15\n\x85Y\xb7n5\x03\x06\f\xfa\xf7\x05\xf3\t\x13N\x9f\xfb\x97\xbf\xfcu]I\u024e#\n\v\v\x99?\u007f\x01\x83\x06\r>\xa0Me\xbd\u0098\xfa\xfa\x1a\x96.]F<\x9e@\u04f4f=\x1e\xcb\xcb+R\xc7\xee\xfdo\b\xec3\x91\xd9\x14\x04\xbc\xd7\xe0\x1d\xfb\x03\x81\x00\xd5\xd5\xd5\u031b7\x9fK.\xb9l\x9f\r\x84\x0f\xceP\xa8\xaf\xafa\u0672\x1fp\xb8\xc7\xde\f\x1e<\x98\u007f\x87\x11\x8fG\b\x06\u04f9\xe3\x8e\xdb\xe9\u07ff\x1f\xcf?\xff\x02\xf3\xe7\xcf'\x1a\x8d\x10\x0e\x87IK\v\xa56\\\xa7\xefg\xa3\u4c39\xbb\xa2#M\x1d3f\f\xd7\\s\r\xe7\x9f\u007f.\xaa\xea#\x91\x88\xfe2zk\xefd\x90\u04c1\x01\x87\x8fd\u1b19\xec,Y\xc9\u19ddOL\xd6b&-0\xc1\xb6l\xb0\x04~U\xa0\xa2:RD\xd5y-\xb6\x05\xb6pZ\xcba:\xe0.\x15\x1c\x8d\xba\"\x10\x8a\xc46-jj#du\xec\xc5\xf0\t\xe7\xb3\xf3\xf9'\xb0L\xc3\xdd\xf0\x1b\xbdn\x1c;^!b\xf1\x18\x8b\xbf_\xaa\xf5\xeb\xd7\xf7Y)\xe5:!DA8-\u0316\xad[\u945f\xff\x0f_\xd6\x17_L\xa7_\xbf\x81M\u05cb\xf4\x00|\xfb\xf6\xad\x87|\xfe\xf9\xe7\xc3W\xaf^\xdd\xfe\x9ak\xae>\xbc\xb2\xb2*'\x91\x88w6M\xb3\xf7\xb6m\xdb(//'\x99Lb\x9a\x16\xb6m\xb9\xf5\x03\x8dEa \xd04]\xec\x19\xc4\xed\xfd9?b\x94'S\xbeQ^m\x82\xd360\x81\xaa*\xd4\xd4\xd4RXX\xd8!//\xef\xbeM\x9b6\xdf\xfb\xf5\xd7\xdf|\xf0\xe1\x87\x1f\xbcu\xce9\xe7N\xbb\xe9\xa6[\xac\x9bn\xba\x85\x17_|\x9e\x8d\x1b7\xff{F\xe6\xf3\xe7\u007f\xc3QG\x1d\x8d\x10\u00ba\xe3\x8e\xdf}SP\xb0\ue23a\xbaz\x16.\xfc\x96+\xaf\xbc\x1c\x9f\xcfw\xc0J\xe0\x1d\x9f\x0e\x9d\x8d\x1b7\xa6|G\x9aj\u01e5\x94\xacZ\xb5\x8a\xda\xda*\u06b4\xc9$\x1e\x8f\xeds\x12\x94\x95\xed\xa2\xbc|\xf7~\x8f\xe3M\x9f\ub045\a\xfe\xabW\xaf\xa6\xb4t\ayyy?\x1a\xd5\xff\x12\u00fb\xc6m\u06f6\xa5\xbc\xb8{\xf6\xecI\u07fe}\xd8_g\x9e_\xd3\x10B\x90H8\xf9\x8e\t\x13\xce`\xe8\xd0\xc3\xf8\xec\xb3\u03d86m:\xf3\xe7/\xa0\xb2\xb2\x12EQ\xdd\xc8L\xa4\xf2}M;`\x85BA\x86\f\x19\u00b8q\xa7r\xfe\xf9\xe7\u04eb\x97\xf3\xde\x1chje\xcf\xe0\xc1\xc6\xc6\x17J\xa3\xff\xd0\xe1,\x9c5\x93\x92\xad\xab\x88\x8bJ\u04bbeR_[\x0f1\x05b&\"ia\x1b\x02M\xd8H\xdbi\xe7\xa7\t\x05K\xb1\x1d\x8d\xb9\x05\xa6%\x91\xb6C\xb1\b\u06e1X,\x17\xbc\xccx\x82\xb8\x11\xa0\xe7\x11\xc7\xd3g\xf17\xacY8\u05c9^\x9b\x18\x05\b!R\r.**\u02993\xf7\xeb\x0e}\xfa\xf4\xb9\t\xb8\x0e`\u02d6\xad\xec\xdcUF\xc7\xf6\x1d\xf6z-\xc5\xc5\u06d86m:\xd7^{='\x9f<n\xcf\u0377\u05d3O>9~\u04e6-G^{\xedu\x03l\xdb\x1eTVVFYY\x19\x89D\xd2\xcdg\x18)\x16\xbf\xf9zSR\xc0\xdc|~;\xf7\u054b\u029dG\xa3\x0e\xdfk\x8a-]1~c\x91\x95\xe7&\xe99\x9b\x89\xd4)\xad\xe9\x89\u0272L6o\xde\u0136m\xdbD\u01ce\x1d\xcf+,,\xfc\xcd\xef~w\xdb\xfb\xe7\x9cs\u059bG\x1e9\xe6\xabk\xae\xb9\x1e\x80%K\x163|\xf8\xc8\u007f/0\xef\u0631C\x13\xaa\xe5\xb8\x17\xbe\xf9f\u078d\xbbw\xef\x0e\u035f\xff-K\x96,\u1a23\x8eA\xca\xc4\x01\x05\x92\xa2\xa2bJKK\xf7\x99\b\x9b;\xf7k\xbe\xfbn1'\x9f|*\x81@\x90d2\x912k\xf2\f\xb8>\xfex\n;w\x96\xb9\xaa\x06s\xbf\x1bGS@\xf7\x80\xbb\xb4\xb4\x94M\x9b6\x92\x97\xd7\xf9\xe0\xc7\xe4\xee\xe9c\xf5\xea5)\xd7\xc0\u07bd{\x92\x9b\xdb\x1e\u00c8\xff\xdbT\xf99\x00`\u0429S\x17\xae\xbd\xf6z&L\x18\u03d2%KY\xb1b\x05\x05\x05\x85\x14\x15\x15S]]\x8da8ADFF\x06\u077au\xa3_\xbf~\f\x192\x98\xa1C\x87\u0437o?@!\x91\x88\x1e\xd0\xd3\xe3\xfe\x02s\xdb4\xc8\f\xf9\xe9;\xe0\x10\x00vo\xddH\xd5\xce\":\x1d2\x94x2\x06\x9a@\xf8\x05VRAIZ\x88\x84\x8dL\x9aH\xc3FJ\a\xc4l/\x02\x176\xb6\x05\xd8\x12i:\x85D\xd8\x02Ew\x14.\xb1h\x946Y\xed\x18v\xea9\x14\x17\xac\xa2\xa1\xbe\x1e\xdd\x1f\xf0\x10/\x05t\xb6\x9bP.,\\\xcf\xd7\xdf\u033bDJ\xf9\x8a\x10b\xd9\t\xc7\x1f\xc7\xd6\xed\xdb\xf7z\x1d_~9\x93.]\xba\xef\xb9N\xd4Y\xb3\xbe8g\xfa\xf4\x99'\x9fq\xc6Y\xa3\xaa\xaa*\xfbVVV\xb2k\xd7n\xa2\xd1(\x8a\xa2\u062a\xaa\n!R\xddNS`\xbb\xaf\xc0\u010b\u031d\x8f\x8eE\xb0\xd7(\xd5m\x85\x8d\xf4\xac\u007f\xbd\xc6\x1e^\x03\x12ic\xe36\xf7p\xbf\xef\xd9\r\xe3Ya\xa7TOJ\x93\xa8\x1d,\u02d2EEE\xb2\xa4\xa4T\u07fau\xeb\xc5\xc5\xc5\xc5g=\xfb\xec3\xafM\x9at\u00fdB\x88\xda\xe1\xc3G\xf2\xd4S\u007f\x12w\xdcq\x97\xfc\xb7\x01\xf3;\xef\xfc}\xea\xf3\x93N:\xb5\xcd\xdbo\xff]]\xbdz\rEEE\xbc\xf9\xe6[\f\x1e<\x98p8L\"q\xe0\x00\xbd\xb2\xb2\x8a\xda\xdaZ\x14E\xa4\xfcF<P\xaf\xae\xae\xe6\xb1\u01de\xa0S\xa7<\x0e9d0{\x9a\x80}\xf6\xd9'\xbc\xfe\xfa\x1bX\x96E \x10H)\x1f\xf6G\xb7\xec\x19\xb5\xd7\xd4\u0532cGI\x8b\xb8\x17M\xc1\xbc\xa6\xa6\x86@\xc0O\xff\xfe\xfd\xf7\xb9\xc9\xfd;\x8cX\u0331-\xee\u0739+\x9d;wc\u0084qTUUSWWO,\x16sU*\x82@ @\x9b6\x19dee\xb9\x9e\xe7\x0e\a\xef\xd5\x17\xfc\u049b\x9c\x04\xa4m\xd1\x06\x18\u0437\x17\x999\xb9\xd4W\x95\x13\xad*G\xd7AE`\t\xa7x\b\x9f\x00]E\x04\x05\x18\x02\x12\x16\xc4,DR\xa0Z6\x8a\x00[*$m\xdbQ\xb3\xe0x\xb6\xa4\u0339\xe26\t\x9fI\x9d\x9a\xa4\u00e0\x91\f\x18}<\x8b?\u007f\x1f\xfc\x01/\x94\xf5\f\x19\u075f\x17\xd4\xd7\u05f3t\u9cb4\x0f?\x9er;p!@~\x13\u04f1-[6\x88\x1e=\xfa\u0213N:\xa5\xe9z\xe8\xf8\xfc\xf3\u03dez\xf9\xe5\x97]\xbfe\xcb\xd6\xc1\xa5\xa5\xa5Zee\xa5\x97\xb714MS}>\x9f\x02({6\xd4\xf6\xd6X\xf3\xfb RHo#\xb1M{\xaf\xf5g[\x0e%c\xfd\x9f<\x86\x9a\xffm\xab\xc9\xdaQT\x15MU\xbdz\x13\xe1\xf0\xf3\xd2\u07bau\x9b]^^\x11,))\x99TYY1DJy\xb9\x10b\xd3\x1dw\xdc%O;\xed4\xf1\xfc\xf3\xcf\xc8=7\xb5_\x1d\x98O\x9d\xfa1g\x9ey\x16\x00\xf5\xf55C\xee\xbf\xff\xfe\x0f\u05ef_\xef\x8f\xc7\xe3\x00\xbc\xfd\xf6d\xbav\xed\u01ad\xb7\xdeL \x108`\x9cr\"\x11wmO\x95\xd4\xed\xf1\xa2-)%\xf3\xe6\xcd\xe3\x82\v.\xe2\xea\xab'2z\xf4h\xc2\xe10\xe5\xe5\xe5\u03181\x83\xb7\xde\xfa\u007f\xec\u0739\x93`0\xf8\xa34\xc9\xfe\x12\x9c\xb1X\x8c\xda\xda\xda\x16\x03\u6595\xa4\xb0\xd0)\xb8\xc8\xcb\xcbc\xc0\x80\xfe\xfc\xbb\x0eo\xf3J$\x1c\xfa\xcc\xe7\xf3\u047e}\x1e\xed\xdb\xef\xef'l\xa7\x89\x83;\x0f\x0ff2\u061bJ\x83\xf2\xf38\xe6\xb8\xe3\xf9t\xea\x14j\xcaw\x82\x04U\x15(\x96\x03\u0336G\x1d(\x02\xe1S\x10\xba@\x06U'R\x8f\x99\u0204\x85\x86\x8de\xb9d\x85l\xf4b\x91\xa6D\x899\x96\x00I#F\xa8]\x16\x87\x9et6\xdb\xd7.\xa7\xacx\x1b\xbe@\b)\xad&\xe0\x99\xf2\xba\x91\u06f7o\x17\xb3f\xcd>#\x1a\x8f\x8d\v\x05\x82\u04fd5\xf5\xd8c\x8f\xea=z\xf41\xdc\xff\xa7\xbf\xf7\u07bb}\u05ef/\xbc\xf8\xa2\x8b.\xbcp\xed\xda5\x1d***\xa9\xac\xac\xf6\xfa\xb5J\xc5\xe11\xf4=\xd7~s\x8a\u0109\x99m)\x9b\xb9FJ\x01\xd86\xb6e\xa5(\xa6f \xa9\xfb\xd0C!B\x81\x10z \x88\x1e\f\xe1\x0f\xa5\xe3\v\xa5\xe1\xf3\a\x91\xaa\xcf\u9deai(B`\x18\t\x92\xb1(\u0246:\x12\xf55\xc4\x1bj\x89\xd5V\x93\x886\xa4\x92\xac\u0496\xae0Bs\xe7\x87T\x84\x10JCC\xbd\\\xb9r\xa5hhh\x18\xbdk\u05eeO?\xff\xfc\u04f3'L8\xbd\xe0\xb3\xcf>\x93\x17_|\xe1\x8f\xea\xf2\u007f\x15`\xee\x019\xc0_\xff\xfa\xccog\u0318\xd9s\xe5\xcaU(\x8a\"-\xcb\x12\xc9d\x92\xb7\xdez\x9bc\x8e\x19\xcb\u0631cS2\xb2\x03\xc3\x15\x8b}\x82\xae\x97\x10]\xbbv\x1dw\xdf}/m\u06f6\xc5\xef\xf7\x13\x89D\x9a\xa9W\"\x91H\xb3\xe3\xf6\xfe\"q\x0f\x04\xfc~\u007fJ9\xd1r\xe8\v\x85\xf2\xf22v\xedr\xf8\xff\xdc\xdc\\\xb7\xcaU\xfe\u06fb\xe2I)I$\x12\xbf\x9e\xebu\xe3\xc2v\x99\xe9\f\xe8\u06db\xa9\x96EEi\x11\x89H\x12MU1\x15\v\u06e5\x01\xa4t\x13\x9d\xd2%\xdc\x15\x10A\x05|:2\xa1 \xa2&:N\xa4\xeaq\u0136\xed\x16|J\x89L\xd8`@\u012a\xa5}\xf7\xc1\x1c2v\x1c\x15\uff88\xb4\xf7P\xfb8\xa5\xa5(B\x88\x9a\x9aZ\x96,Y\x1a|\xe7\x9d\xf7\xae\x91R\xce\x12B$\xcf8\xf3L\xed\xd3O>1\x00\xdeyg\xf2\xb8\a\x1f\xfc\xe3\xbd?\xfc\xb0r\u052aU+ihh\xa0\xa1!B2\u9038\xa6iBJ)\xf6\x06p\x1c{\x01o\x9d\xb9fa\xa9^\xacV\xf3\x80J\xf3\xf9\b\xb6\xc9\xc1\x9f\x1e\u019f\x96\x8e?-\x03\u007fZ\x06\xa1\xac\xb6\xb4i\u05d1\xb4\xb6y\xa4\xe7\xb6'\xb3m\x1e\xa1\xac\\|\xe1L\xfc\xc1\x90\x03\u07b8.\x94\xb8'\x16\xe9P\xa4\xb6\x91 QWE}Y1\xe5[\n)[\xb7\x94\xe25K\u067d\xb9\x00\xcb2\xb1,'I\xae\xeb\x1a\x9a\xa6\xba\x1b\x8c\x10\xb6m\u02cd\x1b7\x8a\xfa\xfa\xfa\xfe55\xb5\xd3\x16,\x98w\xe7\x981c?\xbc\xe0\x82\x8b\xe4s\xcf=\xc7\xf5\xd7_\xbfO,h\xf1`\xbej\xd5r\xc2\xe1\f\xf2\xf3{\"\xa5\f\x9dp\xc2\xf1\xa3\v\n\nH&\x93R\xd34\xe1M\x90\u035b7\xb3f\xcdZ\x8e:\xea\xa8\x03\xa6;\x0f\x06\x83\xf8\xfd\xfe\xfdz\xb1\xf8|>\f\xc3 \x1a\x8d\xb2}\x1f\u071f\u01c3\xef\xc9\xd7\xed\v\xc8\xc1\xd1rgff\x12\x8dFS\u0296\x83=\xbc\rm\xe7\xce2<\x1b\xe2\xdc\xdc\\:t\xe8\xe0\xc6w\xad\xa3\xa5\r\x87\xda\v\u0465Kg\x84\xa2PWVJ\xb4\xbe\x16\xdd\x1fBS\xc0R@X^\xcc,\x1b9\x11w:\nU \x82*\xaa.@\x15(\xa6D&m,KbI\xe7\xae+\xe0\xfa\b\b\xac\xa8A\xb4\u06a4\xef\u19f2q\xe9\xb7l+\xf8\xc1md!R\xc9C\xcfS]QT\xb9q\xc3F1o\u07bcqg\x9c>\xe1:\xe0\xe9O?\xf9\xc4L\xc4c\xdd\xee\xbe\xe7\xee{\xdf{\uff49\u02d6-\xa7\xba\xba\x8aD\"\xd9\xcc\x12\x03\x10{\xaa\xc0\x1a\xbf\xe7\xca(]%\x89m[\u0626\x99\nk\x03\xe9\x19du\xeaNF\x87.\x84\xdbw&\xabcW\xdat\xe8L0\xbb\x1d\xe1\x9cv\xa4g\xb5\u00d7\x9e\x81\x16LCSU\x04`z\u007f\v0l\xe7/\b\x01\x9a\x04\xd3v\xde7E\x80\x94\nR\x82?\x10\"\xd8&\x9b\x9cn\xbd\xe93\xfa84\x01\xbb7\xae\xa5\xf0\x9b\xe9\xac\xfej*\xdbV,\u00b2LG!$mt\xdd\xe76;q\xf6\xbd\x9d;w\x8aE\x8b\xbe\xcb\x0f\x85B\x1f|\xf9\xe5\u0309'\x9dt\xca+\x93&Mb\u04a4I\xbc\xf9\xe6\xeb\\v\xd9\x15\xbf.0?\xf4\u0421\xfc\xfd\xef\xffO\x00\xf2\xf5\xd7_;\xa3\xa4\xa4d\x80\xdbzM\xec\t\xd8k\u05ee\xc30\f\x97\xaf\xfe\xf9u\xe7m\xdb\u6493\x93M}}\xfd^\xfd\x1dM\xd3l:\u0270m\x99\xf2[\xd9W\xc2\xe5\xa7\xf0\xcbR\xcaT\xc5gzz:\xb9\xb9\xb9-\u6f94\x96\x96R[\xeb\x80y\x87\x0e\x1d\b\x04\xd2~&O\xee\xd6q \xc0\\U}\xe4\xb6k\x8f\xee\x0f\x10\xad\xa9$\x19i@\xf7\x87\x1c@\x15\x8d\x8a\x1c\xe1\xa4\xfaR`\xeeQ*\x02\x89\xa2*\u0220\x8a\x1dT]\xfd\xb8\xc42m,)Q\xa4\x13\xdd#\xc0\xb6\x05\xf1\xfa\b\xe9m\xba2p\xe48vm\xdf@<\x1eE\xf7;y$\xe1\x05\xd2B\"TE\xc4\xe2q\xeb\x87\xe5+\xb4\xe93\xbe\xb8PJ\xb9\xe0\xaf\xcf<\x93\u007f\xe9e\x97=\xb9l\xd9\x0f\xf9\u06f7ow\x03 \x81\xa2\x88f\x94US\u00f3\x94\x9d\xb4P\x90H\u01f6\xd70\xb0\f#\xb5\x0eC\x19Y\xe4\r<\x9c\x8e\xfd\x87\u04b6\xd7@\xb2\xbb\xf6!\xbbs>\xc1\xecvh>\x05lG\xb1\xa3\xba4P\u04b21L\x03\xd3H\xa2\xe2\x18\x8dY\xb6\xd3\x1a\u06e3\xd6U\xc5q\x93\x94\x80\"$6`\xd9\xc2\xf5\xa5\x01UU\x1c\x17N\xa9`\xab*y\xfd\x06\xd2e\xe0@\x06\x9fr.\xdf}\xf0\n\x8b\xde{\x89\x86\xaarL\x04B\x98\xf8\xfd>\x84\xb0\xb1m[\xa8\xaa\u02ae]e|\xf5\xd5,TU}y\xc1\x82o\xca\u018c9\xfa\xf3;\xef\xbcC\\v\xd9\x15\xf2W\x17\x99\x1bF\f]\x0fJ\x80\x0f>\xf8\xe0\xf8\xd2\u049d~\x97\xb0V\xf7\x04\xf3/\xbf\xfc\x92\x82\x82\x02\x06\x0f>\xacY\x92\xf2\xe7\x89F\xa1s\xe7.\xe4\xe5ud\u06f6\xed.ol\xed\x15u7\xe5\xd0\xf7\u03c3\xcbf\x1e\x1e\xfb\x02x\u03ec\xa9\xba\xba\xca\xddH\xda\u04af_\xbf\x16s_\xca\xca\xcaR\x1c~^^\xdeO\u069cZ\xc7\xc1%\xceC\x19Y\xa4\x87\xdb\xd0P]I\"\xda@ZN{$\x12U\x11h\x9a\x02\x86t\xacl\xed\xa6\x80\xeeT\x89\xba\x04\x82\u04c2BU\x90>W\x9f(\x14\xa4%\xb1\\6W\xd8N\xa8.5I2\x91\xa0\u03f0\x93\u067af\x11k\xbf\xff\x12[\xf77\x82\xb1\x10N\x85\xa9\x94\xf8\x03\x01e\xe3\x86M\u031e=wP]m\xed\x9b\xef\xbe\xfb\xde\xc0\x82u\uba29\xa9F\xd34\xe9IR\x9a\xae\r'pRP]\xc7Q\x89\xc0\x966\xa6i\x91\x8c\u01f1M\x03\t\xa8\x9aN^\xdfC\xc9\x1fq,]\x0f\x1bC\xdb\u0783\xc8\ua50f/\xa0\x12\x8bK\xa4i`$\xe3\x18qGo.\xdc\xfc/\xd2=uH\xf0\xb9o\x8d)\xdd\xcaW\xf7-U\x04\xa8R\xa2\xbaI]7\xbcCU\x15\xfc\x9a@W\\\xba\a\x89\xb4-,\xdb\"n\n\x14\xa0]~w\xc6\xdf\xf6\b\xed{\xf6g\xe6\xd3\u007f\xa4\xa2x\v\x00\xba\xa6\xa3\xfb|\x18F\xe3)\xa4\xa2\xa2\u009c3g\x8e\x16\b\xf8\xff\xec\xea\xf2\xb7\xbc\xf1\xc6k\xea\xe5\x97_i\xfd\xaa\xc0\xfc\x99g\x9eS\x00{\u04e6\xc2\xfe\xe7\x9e{\xc1\xa8\xba\xba:\xf7\xbe\xee\r\x1c\x9b6m\xe6\xb9\x17^\xe6\u017f=\x87\xae\aI&\xa2?\x9b\x8b\x8fm\x1b\xf4\xec\u0643\xee\xdd{\xb0p\xe1w?\xda\xe7\xf1\xc7<\xb0\xf7l^\xe0%D\xf6\x04\xfc=\vP\xbaw\xef\xf63v\xa4\xf9\xd7GEE%\x91H\x14\xbf\xdfO\u06f6\xb9\xad`\xfe+\x18\xa1p\x069\xed\xda\xd3P]I2\x16I\u9f85\x04MQ\xb0UW#-,\a\xbaE\x93~\xa1\x02W\xaa\xe8&L]\xac\xb7\x14\a\x98\xa5\xe5&B\xa5\x1b\xa1\x9a\x82D<Iz8\x8b\xc1c\u03e6x\xe3\n\xeak+\xd0\xfdi\x8dk\xa71\xb6\x11\x80\x9c5kv\xe8\xeb\xb9s\x06\x16\x15\x15\xa1(\xc2\xd24]\x95\xd2nV\xc0\xe3\x9d~UUs\n\x97\x10\x18\x96\x8d\x91L\x92\x8c\xc7\\zR#\xdc.\x8f\xae\x87\x8d\xa1\xf7\x98S\xc8\x1bx8Y\x9d{\x10\xcaH'\x123I\xc4c$\xa2N\xe5+R\xa4\xac\xd8=\xce\u06f6\xc1\x96\x8e\x15\x81-!f:\xad\xf5lo3K)r\x1cfQW\x04\x9a\"A(h\xaa@\xf5\uc445\xe7\a\xef\xa6 D\xa3F=Z\x1fE\xf7\x058\xe2\u070b\ted\xf2\xf1#7SQ\xb4\x99\xa4\x91D\xf7\xe9\xf8|>\x12\tG\u07aci\x9a\xb6s\xe7Nk\xf1\xe2%\xbd\x1fz\u807b\x80k.\xbf\xfcJ\xeb\xbb\xef\x16*G\x1c1\xcan\xccd\xb5\xf0Q]]\xad8\x89\x90\xf7G\x00\xfd] \u072f\x89\xf9\xbb\xef}\xc0\x1b\x1fNs9l\x95\x9f#g\xe8y\xac\xa4\xa7\xb7a\xf8\xf0\xc3\t\x87\xd3\xd9W\xd6|\xcf\xe3\xdf\xdeI\x99\u01afk\x9aJ \xe0\xdfo\x13\x83=\xc7q\xc7\x1d\v\xa8?[G\x9a\u007f\xf5\x94\xe2\x9d\x18\x02\x81\x00\xd9\xd9\u066d`\xfe+\x18\x19\xe9id\xe7d\x13\x8f4`\xc4b\xa9\xee\x13\xb6\xab\x8f\x16x\xa0\u04f4\xd0\xc7\x13i7\u2ba7\x1a7%\u0616t8\a[b[\xce\x03\v\xa7\xb24i\x13\x8f\xc7\xe9\xd4g\x04\x03\x86\x9f\x04\x12,\xd3H!8M\xdc'\x15U\x15\xbbv\xed\x92E\xc5;,\u007f UUU\xa5\xb4\xf7\n~\x9c\xa4\xa6\x82iY$\x92\x06\u0446:\"u\xb5$\xe31\x82\xe16t\x194\x82\xe3&\xdd\xcf\xf9\xff\xfb\x01\xe3\xee}\x96\xc1g\\F\xdb\u0783\x90\b\xea\xabj0\xa2\x11t\xe1PF\xaapZ\xe8y\ax[:\\x\xd2r\x94\x99q\x13\x92\x96$fB\xdcr^\xa6)%\x86\xed\xbeg\xd2\x01y\x84@SU\xfc\x9a\x82\xae:\xe5\xff4\xed\xbe\x97z\xfb\x9a`\x84\xa2`$\xe3$\xa3\x16\x83O\x19\xcf\xe9w\xfc\x89\xb4\xcc\x1c\x12\x89\x04\xa6i\x11\b\x04S\x9e\xfa\xae\xeaE\u0670a\x03\xeb\xd6\x15\\=m\u06a7\xe3\x01\xde}\xf7]\xf5W\x13\x99K)\xc9\xce\u0392\x00\x05\x05\x05=\u0768\xdc\xdc\xf3\xba}\x814\xba\xf4\x1dJy\xf1F\xea\xaav\xf1\xe4\x13\xffM\x87\xae\x87r\u0310nhD\u070d\xff_\xbf\x16\x80\xf1\xe3\xc7\xf1\xe1\x87\x1f\xf1\xed\xb7\v\u007f2\xf0y\u050b\xa3+u\"n\xa7\x11\xf0O\xe3\xf5{\xf5\xea\x95j\xc3\xd6R\xda\xc8y\x96\xb7~\xbf\x8f6m\u06b4\"eK^G\x0e$\x90\x95\x1e '3\x83u\x898f2\xe9\x02\x92K\r\x02\xaap|Yl\x84\xc3k\x8b\xa6 \u07ac\xa0\x11\xcbv\xc0\\\x91\xa0\x9a\xeeF\xe0VW\xdaB\xba\xa9T\x81\x11K\xe2\xcbl\u00e1c\u03e1h\xc32\xcavlD\xa2\xa7:\x86\xa6*)m\x89\xaaiB\xd3T\xd59\xd9:rB\xa7\x01\xb5H\x81\xa1\xed6\xc70\xe21\xa4\x94\x84\xdad\x91\x99\u05ddN\x83F\xd0k\u0329t\x188\x9c\xb4\xdc\x0e\xe8>\x05\u06c4d<\x86i\x99h\x02\x14UC\xb8{\x8d\a\u0216\x14\xa9\x86\x1b6{tW\xf2\xf6\x1c/\xf7@\xa3$S\xc1\x89\xb4}\x9a\x82\xae)(\n\xa8B\xa04\xba\xffz\xf9`\x8fU\x02!S\xf4\x8cpv(,\xcb\xc02T\x86\x9dy6\xb5\xbbJ\xf8\xec\u007f\xee!\x1a\x8d\xd0&\xb3\r\xd9\xd99TVVx\xa6_\xc24-\xb9j\xd5*\xb1d\u0272{\xa5\x94\x8b\x85\x10\x15R\u06a9\"\xa4\x16\x1d\x99\u007f\xfa\xe9\x14\xa5\xba\xba\u0192R\x86jjj\xfa\xef\u06b5\x1b!\x84\xb2g\x04\x98\u06f97\u01dcw7\xdd\x06\x8c\x06\xa0p\xd9<\x9e\xfe\xf3\xb3,^\x1f'\x12W\x11?\x13\xd9\xe2y^\x9fv\u0684\xbd\x1aR\xecI\x914}4MDy\x91\xb5cw\x9blf\u04b4\xbfq\xe1\x85\xe7\u04ed[\x0f\xa4<\xf8\x8e\x89\xceuJb\xb1\x98{\xc2\u041a\xa8lZ#\xf3\x96\x1c\x18\xa5\xf9u2BA\f\xd3\xc42\x12\x0e\x9f\x90j\v'Q\\5Fc\u0562\xdb\xfc9\u0144\b\xb7\u0273\xc0\xb4\x1d\xf0p\u0336\x04\xc2rM\xba\xa4\x9b\x10t\x1bA[\x96E<\x16!\xb3c>\x83\u01dc\x89\xae\xfb\x91\xaeX=\x15d\xb9\n)e\x8f\xc6\x16B\xa8(Bi\x8c\x82m\x9bd<\x8a\x11\x8f\x92\u06ed7\x87\x8c\xbb\x80\xa3o\xf9\x13g>\xf5>\xc7\xfd\xfeY\xba\x1e}\x06zN\x1e\r\t\x93\u02aa\x06j\xea\x1a\x88&-\x926\xc4m\x88\x9a\x92\xa8%\xa9KJ\"\x06DM\a\xd4\rK\x12\xb7\x9c\xa8\u0734\x1b#pO\xa9#\xf6\x00h[\x82\xa6\b\xd2|*A\u0761V\xbc\u04cc\xe2n\x8a\xba\x02~\x15|\xaa@\xf5z\xab\xcaF\xa32p\xe8\x18\x80D,\x81\xb4a\xf4\x05\x13\x19~\xfaE\x98\xa6IMM-\xa1p\x06\x99Y\xd9)\xdf\x1f!\x84\u0639\xb3\x8c\x82\x82\x82#\xdfyg\xf2a\xce\xfb\xa4\xfc:\"\xf3\xe5\xcbW*\x80=o\xde\xdc\x1e\xb1Xl\x90k;\xbb\x97\x8a%\xaf\xe7at\xedw4\u0246\x04E\x05\x8b\xa9\xad,a\xfe\x8c\xb7\xe8\xdas(\x81K/\xe0\x90Nq\xfc>\xf8W\xf3\xa1\x8e\xf9\x95\x8fK/\xbd\x94\xb9s\xbf\xe1\x8b/\xbe\xd8o\x04\xbf'\x00\xee\xcb\xe3\xfa\xa7P\x13\xf9\xf9\xf9L\x9cx\x15\xc0A\xf72W\x00MuZ\x8ey\x8bN\xd5tTM\a\x1c\x1d/\x12Z{\xf6\xb6H4O\xf1\u0376ma$\x13H\x17\xccE\x13\xe5\x8a\xf7\u007f\xa9\xb8\x06Y\xd2u\au\x13|R8`g\xb8\xe0+\\\x13-'d\x95\xa9\xed\xdcvs\xa9H\x81\x15Obj\x1a\xf9\x83\x8f\xa1\xeb\xcayl.\xf8\u07b1\xc8udx.\xed\xe0l\x14\x9ew\x8b\xf7\x90\xb6DZ\x166\xa0\xfb|t\x1c|$\x9d\x87\x1dC\x8fQ'\xd3a\xe0p\xec@\b\u06f4\x89\x18&v<\x8a\xb4\xadF{^\x9a\xf2\xd4\xce?V\x93\x90Cu\xbfg7\tElI\xea5y*\x1eM\b\xcc&\xbf\u03ef)\xa4\xfb\x14|\xeeRHu\xd6\x13\x12\v\xc7\xda\xc0k\xab\x8at*m=\x9e\xddF\xa04v\xe2\u00d3P\xc6\x1a\x12\x842\x82\x1c}\xe9Ml_\xb5\x84\x1d\x05+\xa8\xaa\xa9\xa1mN\x0e\xd1H\x84\x86\x86:\xc0Q\u022d\\\xb9\x8a\xbe}\xfb\x9c%\xa5\x9c#\x840\x17-Z\xc0\x91G\x8ei\u0651yeE\x85\x020o\u0782n\x96e\xf5\xb5\x9cb\x85f\t\x11\xdd\x1f\xa0]\xb7CAjt\xe9=\x8a!G_\x8a\xa6\xfb\x89\xd6\xedf\xda;\u007ff\u019c%\x945\x04\x11H\u051f\xe1\u055af\x82\x8e\x1d;q\xdbm\xb7\u042d[\xd7\x1f\x8d`\xf7\x17m\xef\xeb{\xfb\x03\xf5\a\x1f\xbc\x9f.]\xbac\xdb\xc6A\x8b\xca\x15G^\x8c)\x14j\x92\xb0`\xe9\x0fl\u073c\u074d\xccu\x84\x10\x18\x86\x81\x914S\xfe\xed\xad\xa3\x05R-B \x14\x05i\xdbX\xa6\xe7\x18\xe8\x16\xd2\xe0&\u294dg'%\x84\x82PDc\xbb;\xe1D\xe5\x86\xddX\\$\xbc\x877\xb7S2u\x99\xda\x04\xa4\r\xf1\xfa\b\xe1\u030e\f\x18y\n\x81`h\x9f\xa78\x8fJ\xf1|Ql\xdb\u00b6LT]\xa7\xeb\xe1c9\xe6\xd6\xff\xe6\xa4\xfb_\xe5\xc8I\x8f\xd2\xf6\xb0\xa3\x89K\x95XC\x84x4J\"\x9e aX$-\x97\xdep\x81\u0652\x8d\x11\xb7\xe5\tt\x9c\u032esZv\x9fc\x81K\xb9x\xd7@\ua512p\u007f^U\x04!\x9fJ\u062f\xa0\xb9t\x94\x94\"\x15\xad{\xf9\x84\x14\xf7\xeeF\xf86\x8d\a\x10\xd1d\xb3\xf3\"\u007f\xe7\xff\x92h}\x9c.\x87\x1c\u00a8\xf3~\x8bP5\xca\xcb\xcaPT\x95\xac\xac,TEK\x9dNjkk)))\xbdp\xf6\xec/\xdb\x01l\u06f6]\xb4x\x9a\xe5\x99g\x9f\xb3\x00\xca\xcav\xf6/\u06f5\xcba\u063c\x12iU\x03\x04\xd9y\xbd\xc9\xc9\xebC<R\x8fe\xd9\f\x19s1\xbd\x06\x1d\x8f\x10P\xbam9\x1f\xbd\xf9\x14s\x96l\xa7\xc1\x0e\xa1i\xe2_f\u03dd\xe6\x04&'\x9dt\n\xf7\xdcs\xf7\x8f\xf2\xc5?\x06\xe8?\xc5t\u9847\x1e\xe4\x92K.\xe1@v\xa4\xf9)@\xae\x00\xe5q\u061c\b\xf0\xf1\xbc\x15\xdc}\xef\xfd\xacX\xb1\x12P\x11\x8a\xea\x1e\xad\x1d\xe9\xd5\xffg\xef\xbc\xc3\xec\xba\xcas\xff[k\xed}\xca\xf4\xa6Q\x1b\x8dz\xef\xb2\xe4^\xe4&\x9a\xc16\x98bS\x13'\xb9$@n\x02\x81\x9bK\x80`r\x93Pb_\u01d8N\x8c1\xc1\xa6\xd8\xc6\xd8`\x1b\xdce\xc9Un\xea\xbd[3\x92\xa6\x97SvY\xeb\xfe\xb1\xf6\xdeg\x9f\x11\xe4I\x00\x97\x9bG\xdb\xcfQ\x9b\xf1\u0319s\xd6\xfe\u05b7\xde\xf7\xfd\xde7\x88\f\xfd\xd3\x0e\x82'\xaf\xd7\xcf%bpCZ=\xb6\xd1\xf1\xa3\x1a(\x93Q\xf51\xc8\x04vAT\nc\xf2o&B\xbeM\n_\x8f\xc6\xe5u<Q\x1a\x82\t5\x9e\xa7\x99<\xe74\xa6\xcd]I\x18\xd9b\xa4=k\x84\x89C\xb25~\xa9\x88\x92\x92I\xf3Wp\xdeG\xff\x915\u007f\xff]\x96]\xf51\x1a\xa6\u03a30:\xc2\xe8\xd0\x10^$\xdf\xd3)\xbc;\xfd3hS\xe9\xfc\xd3D\xa4\x88Z\xc2@\u06df'\x96 \xc6\xcf;L\x15\xda\x18v\xc9(IcVQ\xe7\xcadZ\xb6\xea\xebF_#\xd4\t\x1f\\\xb5\xa9\x98\xa4&\x98\n\xff`H^w\x81\x88F\xfca\u025a\xb73\xf7\xf4\xf3)\x15F9v\xf4\x18\xb5uu\xe4\xf2\x15\xaf\xa7R\xa9\u012e]\xbb\x1a\x8e\x1e=\xfa&\v\u00fe\u05fc\xae\x8b\xf9\x97\xaf\xbdV\x00\xa11&\u007f\xe4\u0211\xb3b\xb7\xc2\xf8RN\x06!\x04\xad\x9d\xf3i\x9c8\x13\xaf8\x8a_.R\xd78\x8e3\xde\xf0?h\xef\x98\aF\xb3\xe3\xf9\xfb\xb9\xf5\xa6\xaf\xf2\xd8\xe6>|\x91'\xe3\xfe\xfe\x05\xdd\x16\u0590\xab\xaf\xfec\xae\xb9\xe6s\xbf5\xc9=\u059d\xff&<=)\x96\xd1b\x1e[\xd8?\xf3\x99O\xf3w\u007f\xf7\xbf\xd1:\xa4T*\xbc&\xa3\xfcR\x80#\xa0\xab(xn\xc0\xe5\u0387\x9f\xe1k_\xfeg\xb6n\u0708\x9b\xab\x01\xa5\b4\x14\xfc\x900\xb4\xb8k\x18j\x82P\x13\x84!\xfad\x87\xfe\xbak\u03f56\b)m\x94\xdb\x18\x9e#5\xf4\x99\xae>VK.\x04\xa1\x11\x94C\x13\r\xc8\u0204(\x14X\\8^\xa2\xb1ml\x8c\x9b\xdb.^R\x1e-P\xd78\x89\x99\x8bW\x93\xc9\xe6\xd1:@D.\x82\"ZpZ\a\x94\x8b#\xd45\xb6p\xe6{?\xca\x1b?\xf3u\x16\xbd\xfd\xc34L\x9a\xce\xc8\xd0\x10##C\x16\u0089\xbeYhD\x02\x9d\xa4\xbb^\x13\xad\xdfT\x13\x9e\xa8s\u049d\xb1I?\xe2\r \xfa=\x88\xb2Ok3\x92\xa6\xbc$\xe7V\xfb\x99W\u03caT:}\x13\xd1\v\"\xe9\xf1I\xac\x12td\x8f\x9bl>1m\x10\xe1\u07e5B\x89\x96)\x93Xu\xe9{\xc9\xd55p\xac\xbb\x8b\xd1\u0451*\xe5[\x18\x06\x1c=z\x8cm\u06f6\xaf\x8c\xbf\xff\x81\x03{_\x9f\xc5\xdc\x18\u00e7>\xf1\t\x03\xf0w\x9f\xfe\u07e7\xbd|\xa4\xfb\xb2\x91\xe1\xca\b\xbd\x90\x12\xa4$W\xdf\xc8\u0139\u02e9ik'\b\x03\xb4\x0e\xf1J\xa3L\x99u*\xe7^\xf2\t\x9a\u06a6\x10\x06\x05\x9e\xb8\xef&\xbe\xf1\xb5\x1byd\xe7\b\xa3\xa1\x83#\x19\xb3\x8c\xff\xebW\xa9TB)\xc5\xff\xfc\x9f\x1f\xe7{\u07fb\x89\u014b\x17Uu\xe4\xe9\"\x1dc\x95\xe9\xa9Q\x1b\u079c!\x93\xc9\xe0\xbanR\xe0\xdb\xda\u06b8\xf1\u01af\xf2\x0f\xff\xf0\x8fH\u9f22.\x90\xff\x99b\xdeW\xd2<?\xe8p\xdfcO\xf0\xed\xcf\xfc%\xcf?x\x173O=\x8f\xa9KO\x87\xd0\xc7\vBzF<\u02a1N4\xf3\xf1\xe3dw\xfe:\xea\u0205\xb0\xce\u007fA\x80T\x0e\x8e\x9b\xb1\x1dfU'k\x12\xfd\xb8\x89\xd4(\xb1\xaf7\b\x0e\xd1)\xb8\x00\x00 \x00IDAT\xbc\x10\xbc0]\xa9l\x9foa\v\x91H\x1b\xd3V)\u0698\n!\x1a\x1a\xfc\x92\u03d4\u06671a\xdaB|\xaf\x94$\xf9H\xa5\xec`\x8d\xef3e\xder\xde\xfa\u026fp\xea\x87>E\xe3\xf4Ex\xe5\x12\xa3\x03}\xd6O\x1dY\xe9\x9c\xe3\xcd\xc4T\to*\x8a\x14D\x82g\xc7\x18\xb5I\xc9\x10\xc7v\xec&\xc5\x1b\x84\x91\xea\xa4!\xa7h\xcaI2R$|\x90H\x1a1\x128FF\x9b\x99\x92\x15,\x9eH\xd3c\"\xf9\xa7\x8eI\xe4D\xdeY\xed\x83\x1ek{\x02\x0f\x96\xbd\xf1\n\u67f3\x86r\xb1@oOo\xd5ty\x18\x86\x94J%\x8e\x1d;\xfe\x16cL-\xc0\u05293^\x9f\xc5<%\xe9k\xda\xf7r\u05e7\xb6\xed\u0611>'\"\xa4\x83\t\x02\x9a&Lg\xe2\xdceh\x13\xa0\x85u?\vB\x8fR\xa9\xc0\xac\xa5o\xe0\xd4\v\xff\x84\x9a\xbaf\xfc\xf2\x00\x8f\xde\xfe5n\xfa\xee-<q0d0*\xe8R\xfc\xee\x05]\b\x81um\f\xb9\xf2\u02ab\xb8\xed\xb6[\xf9\xc8G>\xc2\xf4\xe9\xd3S]9d\xb3Y\x9a\x9a\x1aijj\xa2\xbe\xbe.\xc9\xf6\xcc\xe5r\x94\xcbeJ\xa5\x12\x9e\u7854\xe2\roX\xc3]w\xdd\xc9G>\xf2\xd1h\xc3(\xbcf\x8e{BX\xc2wg1\xc3\xd3[\xf7q\xff\xb7\xbe\xcc\u02db\x9f\xa6\xa1m\"\x8b/y?\x8d\x1d3-\xfc\xe3\xfb\x1c\x1f\x1a\xa5\x14D\x04X\xea\x11\x17\xf5\x93\xd7\xeb\xe3\x9e\xf2<\x9fR\xa9\x84\xe3\xba(\u05ed(G\xd2 \x8c\xf9\xcdx\xbb\x01J\x91\x17K\xacp\x11\xd1\u007f:\xe9ze\xa5\x80\xa5\xfd\xcbM\xe4}n\x04\xe5B\x89\x9a\x86\x89L\x9fw\x16\xd9\\\xad\xf5K1\x10\xfa>Fk\xe6\x9d\xfd\x06\xde\xf6\xa9\u007fa\xd6y\x97\xe0\x87\x9a\xc2@\x0f~\xb9\x80W\xf6\b\xcbeL\u08c3\xc0\x8e\xe9\xfb\x1e\xc6\xf70\x81\x1f=\x02\xc2 \xb0|@h\x1f\xc6Da\x1b:\xd2\xc2\xc7\xda\xf0\xdf\xc2+\xc4\x1d\xb7#\x05\rYI]F\xe0\xa4D7\xf1C&\x9dw\xf5C2\xb6#\x8f\xcf?qa\xaf\x04ZXX&\x9e4\x15\xc9\xe6\xe9{\x1e5-\xb5,\xb9\xe82\xeaZ\xda\x18\x1a\x1c\xc0\xf7\x03\\\xd7M\x92\x95FGG9x\xf0\xe0\x94M\x9b^<#~\xfe\xaf;5\xcb\xc3[\xf6\x8b\v\x16N3\x00\xff\xfa\x9d\xef]\xbde\xcb\xd67\rG\x86N\x90J\x06\x91\x92q\xd3\x17\xd1\u06b9\x800(\x81cU\"\"\x14\xf8\xbe\x87\x11\x92\xc5g\xbd\x87\xc2H\x1f\xcf=z3\u0151\xe3\xfc\xea\xe6/\xe3\xe6\xf2\x94>x\x15+&\xb8\x8cw}\xa4\xb0\xbb\xf0\xefz\x83\x94\xcbe\\\xd7e\xe1\xc2\xc5\xdcp\xc3\xf5\xbc\xfd\xed\x97r\xc7\x1dw\xb2a\xc3s\xec\u07bd\x87\xa1\xa1AFF\xc2\u0102\xd3J\x11\xad\xe6<\xf6[\x99?\u007f\x1e\xef~\xf7\xbb\xb8\xf2\xca\xf7\x90\xc9\xe41&xM;r\x00\x13\x84\f\x84.On;\xc0/\xbfs\x1d\a\x9e}\x84\x9a\xa6q\x9c\xf1\xa1O\u04be\xf4lv<\xf5\bB(|\u07e7o`\x18?4d\x18sl\u0546\x93H\xcb\xeb\u5494JE\x86\x87\x87\xc9\xe4\xf2H\xc7M\x06\x85\xa2\x066\x1a\x0eJK\x11\xa3\xc6\u0280\xa7\r^\x84K\x88\x04\xaf0\x952&Rmm\xbcE\xa4\x8c\xa9t\xd4\xf6*\t\x81\x1f0g\xe9E\x1c\xd8\xf1\x14{\xb7=\x89T\x0e\x99\\\r\v/x\x1bg\xbc\xe7\xcfh\x1c\xdf\xc1\xc0\xf0\b\x81\xd6\bc\xbd^\xb41\xd6* \xf4#\xff\x15\"\xf5L\xb4\xa5\b\xfbg\x1d\x85B\x04\xa4\xa2\xdd\xe2\u009a\xb4\u0572B\x06G?_<\xd9i\x8c\xc1U\x82\u01ac\xa4&##\xe3\xac4\x142\x16\x86\x8a\xbfG\xdco\x8b\xb1\x1fN\xe1\xf9\x95\xff9\xdeFu\xca\xda#\xde\n\x851\xf8e\x98s\xe6E\xb4\u03d8\u01de\r\xebl\u4723\x92\xc2S\xf6\xcatww\xf3\xd4SO/\x06\x1e|\xdd\x15\xf3\rG\x06Y9\xa9\xd1\x00\xf4\x1as\xee\a\xaf\xfc\xe0\xff\xdal\x13\xae\x13xEHE\xe8{\u050d\x1f\u03d4%\xe7\xe1d\xea\t\x87\aQ\nK\xb4hkG\xe9{%2\xd9ZN\xb9\xe0j\u02a5a6=\xf9\x13\x06{\x0e\xf2\x8bo}\x01\xad4\x85\xf7\xbf\x8f\xa5\xad\x19:\xb3\x1eY\x19a]\xbf\xe3\xf3\xb6\x1e\xe7v\xe7\xbc\xe0\x82\x8bY\xbdz5\x9b6mb\xe3\xc6M\xec\u0673\x87#G\xba\x18\x1c\x1c\x8cB\x9a\xb3\xb4\xb5\xb5\xd1\xd99\x85\x8e\x8e\x0ef\u0318\u03aaU\xab\x90\xd2\xc5\xfa`\x17-\xae\xf9\x1a\x16\xf20\b\xd02\xc3\u04fb^\xe6'_\xfb\x17\xb6\xfc\xf2\x87\u0535\x8c\xe3\xd4\x0f|\x92Yk\xdeE\xc1\vP5\r\xb8\xf9:\xfcr\x99\x91\xc1~\xeb\xe1Aug\x1e\xc7n\x9d\xbc^\x1f\xd4\xe7\xf0\xf0\b=\xbd\xbd\xe4\xeb\x9a\xc8ds\xb6\u0324\x8a\x95I\xad9\x19\xfd]G\x05\xa7\x14X\u065d\x13O2#\x11\xd1{l\x03'D<\u0493*\x9e&E\xbbF\xe3\xf0\xc6\xe0\x97\xcb\xd45Mf\xde)o\xa1\xeb\xe0V\x86\xfb\xba\x19?u>K\xdft%\xf9\xb6\xc9\f\x0f\x0e\xa0\x84 @F\x10\x8d\xd5\xc0\xa3\xed0\x91\x91\xca\x16\xed\u0529!\xf6d'5\xa1j\xf9\u0644\xf2M\xac\tb\x1e\xc0\xaau\xe2\xc2n\xfb\ua332\x1dy\x8d##7\x9aJ\x017T|\x1f\x05\x95\x13J\xfa\xa3\x95Bn\xaa\x9eK\xe5\xb3L\x15$\x14\xff9q\x1e\x8e\x88P\x11h\x9a&\x8cg\xea\x92S\xd9\xf7\xfc\x13\x14K%\xb2\xb9\xbc\xb5\xda\xd5Vd\x10\x84!\u01cf\x1f?\xfdu\u05d9\xef\xe9\x1fefsm\f\xaf\x9c\xfb\u037b\x1e\xfc\xf1\x86'\u058d\u04e1\x9f\x9cq\x84T\xc9\xca\x1b?\xfb\x14\xa6,:\x87\xb0XB\x87\x1aGY\xb3\x04-L\xf2\x92\x95K\xa3dk\x9a8m\xcdG\b\xfc2\x9b\x9f\xbe\x83\x81c\xfb\xf9\xc5\u05ef\xc1\u01e7p\xe5\x87\xe8k\xc91'W\xa45+\x12,\xeew\xc5\xf9-1j!\x93\xa5KW\xb0t\xe9\n\x00FF\x06\x19\x19\x19\x89\xc6r]\x1a\x1b\x1b\xc8\xe5*CGa\u8f6a\u0650\xff\xd1\x15\x04!\x197\u02c6]\x87\xf8\xfa\xf5\xd7\xf3\xdc=\xb7\x92\xcd\u05f0\xec=\x1fc\xe6EW\xe0\xf9!\u0294\xc856\xe3\xe6k)\x0e\xf7S\xec?NV\x1a\x1b\xfe\xabu\x82\xb1J%\xff\xdbD\xc8\xfd\xff]\xc6\xed\xd5;8\xcc\xf1\x9e~\x1a\xa7\xce%\x93\xaf\x89\x94 i( E\xeaI\u06f9\n$A\b~d0\"\x84\xed\x85e\xba+\xaf\xa0\xc8X%u\x1c\x9cV\t\x84H \x17\xec\x1a)\x97Kt\xce=\x8b\xa9s\u05f3\xfd\xf9\xfb\x18:~\x84\x83[^$\xdf1\x93\x92\xafq\xa4\xed\xae\x8d\x01\x95\x10\x93\xd2B$&LHY\x11\xdd{!V\xab\x9eH$\x11\xa9gd\x12\xfc<)\xbcFG\xa7\tk\u022e5()\u027b.9iU'\bQ\u054d'\xaf\x8d\x11U\xc4\xe5\xd8\x12\x9d*\xf7c\u0781\n\x8e.\x10U\x9b\x83\x89\xbe\xae\x10V\x11#\x02\x8d\xceH\xa6.YE\xbe\xbe\x89rq\x04'\x93\x8dN%!Zkc\xb4\x16CCC\xf3_W\xc5\u0718\x12k\x9e\xc9\xc6Eq\xfc\xbac\xfe\r\xb7\xdf\xf6\x83\t\xdd\a\xf7V\x93\x9e\x80\x0e\x03jZ\xc73\xef\r\xef%\xdf\u070e\xd7\xdfoM~\"\x12\"\x99B\x8e\xce\xfa\xa5\xe20\xf9\xfaq\x9c\xf1\xe6\xbf\"\fC\xb6m\xf89C\xc7\x0eq\xff\x8d\xff@qh\x90\v\xde\xf7aF&7\xb2\u0414\x98\x90\xd5\xd6\xdfY\xff~\xe4\xa8\x1d\xd7\x1fM2@\xf3\xf9<\xb5\xb5\xb5\xc9\xc0\x90%0*!\x15\xaf\x97\x82\xa7\xb5!\x93\u0272i\xdb.\xbe\xf4\xa5\xebx\xf4\xee;\xa9o\x19\xc7\xe2w\xfe\x053/~\x0f\x81\xd6\x04\xbe\x87P\x92l}+\x99\xda:F{\xbb\x18\xe9=\x8aB'\xa4T\x95N\xf9\xb7$'\x9d\xbc^E\x80%:\x1d\x1d:\xd6\xcf\xf0\xc80\x1d\xcd-dr\xf9\xc8\xe7;\x9ar\x8c\x8b\xad\x88\x8ap\u0535J\xa1\bBA \x05J\tT Q\x0e`4\"\xf0\xa3\xe2])H\xf1z\xb6\xfauRU\xb0\xe2\x96\x1e\x1a\xf0}\x8fL\xae\x81\x05\xa7_\u0391\x83\x1b\x19\xe8\xda\u03ce\xc7\xef\xa5}\xd1*r\xed\x93\x18-\x160\xd2>w\x19\r\xed\x84F\x83\x88\x02'\xa4B\xb9\x0eJ\xaa\b*1Ud\xa7-\u4552jL\x94\xc7\x19oB\xe9IS\xc0\bI\xc6U\xe4\\\x85\x92\xd2nV\xc6$Dp\f\xef\xc2\u0621\xb8\x13;\xed\xb1\x9dz\xa5\xc0\x9b\x04\xd3\x1a\x13\xa7Q\xe9\xe7\xa3\xc1+\xad\xed\xfd4n\xfa<\xf2\xf5\x8d\x14\x87\xfa\xad\x8b\xa2\x90\x84\b\x84T\xa2X*q\xe0\xc0\xc1\xe2\ub998\x1f\x19\x1a\u5fae\x8c|\xe04\xa1\x8d1\xe2%\xc3\xf5?\xf9\xe1\x0f\x97>t\xfb\x0f\x13\xcf{!\x95\xf5i\xd0\xf6X8\xed\u07372\xe1\xac5\x94J\xc2\xfe`\bth\xb1<\x19\xb9\x02\x89T\x01)\x95\x86\xc97\x8c\xe7\xccK\xfe\x1a'\x93e\xcbS\xb73\xdcs\x84\a\xbf\xf3E\x06{\xba\x19\xfd\xd3O\xe0\xcd\xef`\x8e\xf6\x99\x9a\xf3\xad\xe5\xe5\xefY\xd0\xd3E\xda\xf7\xfd\x13,q_OE\xdcz/;\xf8\x81\u6a67\x9f\u57fer\x1d\x0f?\xf0 \xcd\x13\xa7\xb0\xfc\u028f\xd1q\xe6\x9b\xd1BP.\x95PB`\xb4&\xd7\xd4F\xa6\xa6\x1e0\f\xf6\xf5R(\x14\xc8fs\x95\xee$\xf2\xa989\xe2\xff\xdaw\xe5J)\x86\xbc\x80\x03]\xc7\b|\x9f\xda\xc6\x16T&G\x10\x84)\x89\\\x05K0\xc9H\xbfD\v\x89'\x04\xc2u\x11J\xa2\x82\x10\x95q\x80\x00\x11D\xbai\x01&\x12L\xeb\x04\xfb\x15\t\rZY\xe7\x95\x13\x80\x1fj\xf0<\xc6MY\xc8\xf4E\u7c79\xff(G\xb7?\u03c1\rk\x99\u007f\xc9{-\x84a\f\xa1\x16\x84R \x94\x83\xe3f\x11\xcaE\x1bM\xe8{\x04\xa5\x12^\xe0\x11ze\x82\xe2\bAq\xd4\x12\xa0~\x19\xed\x951:@\x87\x9a0\b\x18\x1b\x9cb\xa2\rF)\a\x95\xc9\xe2\xd4\xd6S\xdf\u0502jmA\xd7\u0510\xc9\xe5psy\x9cl\xce\x16\xd70\xc0\u8832Y\xa5\x02\x9f\xd3K\xbc\xba\xdb6\x15\xc8)!\x98+\xffF\x15\x1cS\xbd!\xc6\xde0u\ud4e8il\xa6\xf7\xf0>\xb4\xd6(\xa9\x92S\x8e\xe7\a\f\f\x0e\x8a\xd7M1\u007f\xf28\xe2\x1d3\xa56\xc1\u05dc\x8d%n\xb9\xef\xd7O\xbe\xe7\u07ef\xfb\x02\xc4QSR&]\xb91\x9a\u018eY\xcc~\xcb\x1f!\x9b\x1b(\xf4\x0f\xa2\xea2\xa8Q{,\x11\x84 t\x12\x1b\x15\xbfa\x00\xe5\xf2(\xb5M\x139\xe3\xd2O\x92ije\xe3#\xb7P\x18\xe8a\xfd\x0f\xbf\u03b1\xfd{\xe8\xfb\xcb\xcf1\xb0r%\xbdy\x98\x93/\u04d4\x93\x04z\xec\xfe\xfa\xfb\x17\xf6\xd7\xd5\x15u\x01n&\xcb\xe8h\x81\xbb\xef\xfe%\xff\xf7\x86\x1by\xe1\u0157\u8637\x843\xde\xffW\xb4/_\xcdh\xa9L\xe0\x95\x93\x13\x8f\u059a\x9a\xd6vj\x1a\x9a\x018\xde\xd3C\u05d1nf\u035a\x811\x12\xc7Qd\\'\xf2\x948YP_\xebj.\xa4\xa2o`\x80\x03{\xf7`\xb4\xa6\xb1}\x12nM=\x85b1\x9a\xb4\xa4\xe2\xfd\x1f\xfb\xa4 \"\xfbWM9\x10\x11\x84\x11!\xe2R!\x1d\x89p\x01\xcfD\u49b4\xddx\nX\xf9M+>\u0351\x06\x81G\xb6\xae\x819+\xde\xcc\xcb;\x9f\xe5\xf8\xa1m\xec]w/\x13\x16\x9dJ\xe3\x94\xe9\x84a\x80v\xf3\xb6\x93/\x15\xf0\a\xfb(\r\xf53r\xfc0\x83\x87\xf70\xdau\x90B\xcf\x11\xcaC\xfd\x04^\x89\xd0\xf7\xac\xef\x8b\x0e\xaa\xa6[#l&u?Vj\x83\x88\x88P\xe1\xb88\xd9<\xf9\xfaF\x1a\xc6M\xa0\xa5c:\xadSg3n\xfa|\xea\xdb'\x90ol!_\u05c0\t\x03|\xaf\fJ\"\x1d\x17\x19\x9f\f\xc6\xf4\xdfT\x9dE*8\x8d\x10\xa2\xaa\xa6$\xf8z\xa2\x8d\xafl\x05Z\x83\x93\xaf\xa1\xa6\xb95B%Bdd\xa7!\x84\xf5\x8b\x17B4\x19c:\x84\x10\x87_\xd3b~\u02ce\x11\U0004e675\x06`\xb3\xf7\x17\xff\xf8\u0736\xc3W\xde\xfc\x95\xcf\xd3\u007fx\x9fA\b)b\x9c<\xfa\x81\xa5\x94L;\xf72Zf/\xc5/\x87h)\xd0\rY\x8c\x12\xa8\x11\x1f\xe1\x03\xa1\xed\b\x8d\xd4\x15\x1aY\xc4\u064d\x05\u071a&V\xbc\xf5\xa3\xe4Z\xdby\xe9\x17\xdfb\xa0{?;\xd6\xfe\x82\xa1\xe3/s\xf4C\u007f\xcd\xe9o\xbe\x9c\xd1\xf6Z\xe6\x86\x05&\xe4\xad\xd7q\xf0\xdfP]g\x8cA)\x85\xeb\xe68t\xf8\x10\xb7\xfc\xe0\x87|\xf3\xdb\xff\u0191\xaen\xe6\x9c~>g\xbc\xf7c\xb4\xcf[\xc1\xf0h\x01\x1d\xf8\xa9\xde\xc1\xfe\x9aoh\xa2\xb6\xb5\xddb\xb1\xbd\xbd\x1c>|\x98\x85\v\u7864$\x93\xb1\x99\x86\xe2d_\xfez\xe9\xcd\x19\xea\xef\xe3\xe0\xee]\xb8n\x96\xfa\xb6v\xa4\x9b%\x18\x19E\vI\xd97\x14\xfd\x10)\x05\x19G\xa2\x94\x81\xc0\xe0\xa1)\x04\x10b\x13\xe5\x851\xc8x\xbc\x11cG\xcc\x1d\xd7f|\x1a\x1dy\xa3\xc70\x86I\x8a\u007f|*\x8d\x15(\xb1\x02\xc4\x18\b\xca%\x9a\xdbg2k\xe9\xc5\f\x1c;\xc0\xb1]\x1b9\xf2\xecZ:f\xcdg\xa80L\xcf\u07ad\f\x1c9\xc0\xe0\xa1]\xf4\xef\xdf\xce\xd0\xe1=\x94\x86\xfa\b\xcb\x05\x82r\t\xed\x97O \x19S~\x1fv[\x12\x95M\xaa\xfa\x95\x89\xe0 \x1dF\xb8\xba\xfd\x8c.\xa1P\x99\x1cN&\x1b\u017e\xcdb\xf2\xc2S\x98\xbc\xf0\x14\xc6\xcfZ@\xe3\xf8\xc9\xd6\u07e6P\xb0A\u03ae\x83P\u059a7\u0749\x8b\xb1P\xcc\tR\xd0\x18\u007f7i\xda8\xf9\fk\xaf\xebP\xd3\xd8\x12y\xcfW\xc3\x05e\xcf#\x9f\u02f5\x06~i\t\xf0\xda\x14\U000d738d0\xec#\u039e\\g\x00^\x1a6\u007f\xbd\xad\xab\xf8\xa9[\xbez\x1d;\xd6\xfd\xda\xc4o\x03\x91#\x98\x10\x02\x1d\xf84\xcfX\u0234\x8b\xae@\xe5k\xf1\v\xc3\b#0J\xe1\xd7f\xd0\x02\x9c\x11\x10:\xb4\x93V\x91-g\xa5\x15\x10\x18!(\xfb\x05d6\u01e2\v?Dm\xcb$\x9e\xff\xf9W9\xb6\xfbE\xba\xb6=\xcf\u03ff\xf4I\x8el{\x91s\xdes5\xbd\v\xe617\xf4\x99]\x13P\xebZ\xd8E\xff7\xaaL\xb9\\\r\xc6h\x1eyl-\xdf\xfc\u6df9\xff\xbe\xfb\xf0\x85\u00eaK\xdf\xcf\xf2\xb7\xff\x11\u0353gP*\x8cb\xc2\xd0\x12P\u046b\xa8\x845br\x1c\x87\x96\xc9S\xc9\xd5\xd4s\xb4\xbb\x9b\xbd\xfb\xf6R_WK\xb1XD)\x99`\u007f'\xaf\xd7\xfe\xf4\x050\xd8\xd7\xc3\xc1\xdd;i\x1e\xd7NC\xebx\xcaAH)\u0414BC9\x88\x92\x854\x94\xb5\u5374\f\xf0\x8dU\xb0\b'\x9a\x88\t+\x86[\xf1\x9b+\xa5\x8bQ!\xda\xe8$9\xc8\xf2U\"\"\x11+\u05b8q!O\xab@\x02\xcf#WS\xcb\xf4\x85\xab9\xb0u\x1d]\xfb^b\xff\xd3\x0fP\xd7\xd0H\xcf\xf1\xc3\xec\xdb\xf0(#\xdd\a\t\xfd2&\f\xac\x1f\xba68\xb9\x1c5m\x13\xc97\xb7\x93kh\u00adm\xc0\xc9\xd5\"39\xa4\x93A(\x85r3\xb6\x19\x14$]x\x02\x99':p\x8d\t<\xfc\xd1aJC\xfdx\xc3}x\x83\xbd\f\x1f{\x99\xe2`?\xa5\xe1~\x06\x8f\x1cd\xff\xb3k\xc954\u0471x\x15\xb3\xcfZ\xc3\xf4U\xe7\xd2\xda9\x9b\xc0+\x12\x94KH\xa9\x10\x8e\x83t\x1c\xdb\u96f8\xf5\x11\x95\x01\xac1\xc7\xfcx\x8f\xd1\x11dc\f)\x8d\x8e\xfdM*E\xae\xa1\xd9\xdaf\xe8\x94\a<\x10\x86\x1a)e\xaeX*\xb6\xbc&0\u02c6\xae\x11\x06=\u0139\x1d\xb6\x90\xef\xf3\u037b6\xf5\xf0\xa5{~\xfa#\x1e\xfd\xc1\x8dQ\xed\xb6lf\x85L1H7\u00cc\x8b\xdfc\xbb\xf2\xe2h\nB\xb1\xdd{X\x13\rA\b\x0f\x8cF\x9a\xc8\xf6&u|\xb4\x83jV\x1a%1\xccX\xf5f\x1a'Na\xe3\xbd\xdfb\xe7\u06bb\x19\xed\xeb\xe6\xf1\x1f\xdc\xc0\xc1-\x1b8\xe3]\u007fF\xd7Eof\xb8\xb3\x89Y\u01a7\xcd\r\xc8\bC`\u018c=\xff\u007ft))q\xdc\f 9t\xf8\x10\xff\xfe\xc3\x1fs\xcb\x0f~\u020e\x1d\xdb\x197y:g\xbf\xe9C\u033b\xe0r\u0716F\xfc\xe20FWf\xd6\u048bPk\x8d\xc20\xaes\x165u\r\xf4\x1d{\x99\x9d\xbbw\x01\xe0\xba\xcek\x1e\xa0q\xf2\xaa\xc0{\x18\xf0\x03\x8fC\a\x0e\xd0\xdf{\x8c\x19\x8bW\xd0\xd0:\x8e\xc1\xd12\u00de\xcd\xca4Q\xa1\xd3X\x13-\xdf\x18L\x18\u0372K\xa7\"\xdf3\x06\x19\x8e\t\x95\x90\x02\xa5\\\v\x8b\x1aM\xa8u\x05h0Q\x11OKB\xc4\x18Y\x9e\x81Rq\x94\xe6q\u04d8\xb5\xf4\"z\xbbw\xd3wd7\xeb~p-~\xb9\x801\x1a\xe1\xb8dj\x1b\u0237\xb4S7\xbe\x93\xda\tS\xa8\x9f4\x9d|\xebD\xf2\xcd\xe3\xc8\xd67\xe2\xe6\xebPY\xab\x9f\x17\xcaI\b\\De\x9c^P\x19\x15M\x80\x11a\xe1\xa1\xd0+\u23ce\xe0\x8f\xf4#\v}\x04}\xdd\xf4\x1f9H\xcf\xdem\x1c\u0779\x91\xc1\xae\x83\x14\xfa\x8e\xb3\xfd\x91{\xd8\xfb\u0323t,Y\u0172\xb7\\\xc5\xfc\xf3\u0782[S\x87_*$Cs\xcaq#hX\x9c@pV\xce\x04\xd5\x1fM \xb1\xb1p\xba\x14\xb8\xb9\x9a\x14\xa9\\)\xf4\xae\xe3\xe0y\xde`}}\u04de\u05e4\x987d`e\xab-\xe4]\xa5\xf0\x8am\xa3|\xef\xf1\xc7\u05b9w\xff\xeb\xe7\xed@\x80\xdd\xee+\x18\xb3\x90h\xbfD\xc7i\x173\xe3\xe2w[\x02\xa4\\Np\xf4\xcaiE\x12\xe4\x15h\a\xe9Ysb\x13\x92\x1c\xe9*>\x956\xa7P\a\x1e\xda\xd3LY\xb8\x8c\xf1\xb3\xff\x0f\xe3f\xce\xe6\u017b\xbe\xcf\xe0\xd1\xc3\xec}\xf61\x8e\xef\xdd\xc1\x8e\xf5\xbf\xe6\u0eeef\xe5\x19g2\xbb)\u03f4|H\xb3\xf4QBW\x19\xe9\xbc\xde\x0f\u064e\xa3\x90\u02aa\x85F\xcbe\xee\xbc\xeb\x1en\xb9\xf9\x16\x1e_\xfb\x18A\xa0\x99\xbb\xec|\x96_\xf8>\xa6.9\x9b\xc0\u04d4\xba\a\x10\xf5\n2\xc2\x06\x16D\xb7g\xec\xe6\xe1\n;X\xd1:u6nM\x1d\x00\xbbv\xed\xe6\xe0\xe1\x97\xe9\xec\x98L\x10\x04'%\x89\xaf\x13(MH\xc5\xf0\xf0\b\x9b6m\xa1T,2\xb5\xb3\x93\xd6q\xed\xec\x18\xf1\b\u3390\x8a_\xb9L\x95\x1d\x13%V\xc4\xf7\x9a\x88\x9d\x12\x13a]T\x18\xa5\x83t2h\x1d\"D\\pL\xc5L\xcaD\xb2A\xf1\x1f\xc5,\n\xa6\xcd=\x8b\xfd[\x1f\xe7\xf0\x9egq\x9c,H\x89R.n\xae\x96|\xdb\x04&\xad\xbc\x80i\u7f55|\xdbdT&\x17\xf17!h\x8d1\xf6w\x1d\x04\x10\xf8Ie\xb4\xcf\xddDj\x96\x8az%\r\xbb\xe80@\x87>N6\x87\x93\xef\xa0&;\x9d\xa6\xba\x1c\"\xf0\x18\xed=\xc6p\xf7A\x8e\xed\xde\u010e\xb5\xf7\xf3\xf2\x96\r\xf8\xc5\x02\xbb\x9f|\x90\xae-/px\u04f3\x9c\xfd\xa1\xbf\xa6q\xfcd\xca\xc5QL`'beR\xd0S$\xa8H\x17\xf6\xdf\"e4&\r\xb1c\x8c@erV>\x99\xf8\x1d\xd9G.\x9bedd\xa4K\b\xf9\xe4kR\xcc7\xf6\xc7<\x9an}|X\\\xf3\xe2\xf6\xae\x9a\x9f|\xe5\xd3f\xb0\xeb\xa0H\b\xcf\x18^\x91\n\ud5e9i\x9b\xc8\xdc\xcb\xfe\x94\x86)\xb3\xf1F\x86*\x8b\v\xa2\u022b\x10\x11\x86\xa8\xb2\xc6)\x9b(%E\x82\xd4cH\x97hzMG\xde\xceyA\xc00\xb5-M\x9c\xf1\u078f2i\xdeR6\xfc\xf4\xbb\x1cxa=\u00fd\xddl\xb8\xeb\xfb\x1c\xde\xfc\x1c/\x9e\xffVN\u007f\u06fbY\xbah\x01\xb3\x9asL\xad14\xcb2\xae\xd1\x15\u00de\xd7\u064d,\x85\u0779\x91\x19\x00\xfa\x8ae\x1eY\xf7\x14?\xbe\xf5V\x1e\xb8\xf7\x97\f\x1c;\u02b8\x8eY,>\xedm\xcc]y\t\xf5\xad\x1d\x94\a\x8b\x04\x04\xe0(\x8co\x90\xf5\n\x99\x17d\x1cA\x10V\x16\\F\x1a\x1c)h\x9c4\x95\xfa\xf1\x939\xba\u007f\a[\xb7\xed\xe0\x85M[\xe8\xec\x98\xf2\x9fr\x84<y\xbd\xf2]y\x18\x868\xd2ahh\x98\xe7\x9f\u007f\x01\xc7q\x98\xdc9\v\xc7m\xc0/\xf6#\x1c\x89N\x87\x11\xa7<E\"\xe64\x82)\xa4=\xf2\a \xab\xc8\xc4Ja\x92\xcaA*\x17\x13\x86\x15\x1dK2:o\x92oR=dc\x1b\x04!\x04\x81W\xa0e\xdc4f.<\x8f\xa3\x87\xb6\xa0\xc30\"g\xb5\xed\xd0{\x8f\xf2\xf2\xb3\x0f\xe1\xd6\xd41\xf3\xa2w\xa1\x85\xc0/\x17\xa3|\u0354\xd4PJT&\x8f\x8c|\xdb1a\x02\xf3\x98\xc8F1\fC\xeb\x03\xe3\x95(\r\xf6\xa12Yr\x8d-\x84h\x8c\xe71\\.bJ\xc36|\xa2\xa1\x89\xc6\xf1\x13\xe9X\xbc\x8a\x99g\xaea\xdb\xc3?\xe7\xb9;nb\xf8x\x17\xe5\xe2\bO\xfd\xf8\x1b\x18\xad\xb9\xe0#\x9f\xc5\xcd[{\x02B\u06d0\u02884\xae\xe80NT\xf5\xc4V\xf0\x90ruL\xe9\xe1\xa5\x148\x99\x8c}-\xc2\xd0Z\x13D\x03\x01\x8e\xa3\xc8d\xdc$\x14\xf8U-\xe6\xbbz\x87\xd5\xec\u05ba\u0418\xc1\x86u=\xe2\xd6\xed\xc7\v\v~t\xdd\xe7\u0341\xe7\x1eO\xde\b1&\xdeSe\xf3\xccz\xf3\xfb\xe9<\xe7R\xc2XQ\x01\x88\xc8s\x810\xc4)\x858\xc5\x00\xe5iD\x18\xb1\xeeJV\x8c\xf6\xab\xec\u0362\x1dR\x82\xca\b\x94+\tJE\x9cL\x8e\xd9g\xbf\x91\ts\x96\xb0\xf5\xc1\x9f\xb1\xe5\u05f7st\xcf6\xbawo\xa6\xe7\xd0^\xb6<\xf2K\u67bd\x86S\xdfx)\v\xe6\xcfc\xfe\xc4f\xe64\t\xeaM\t\x11\x1d\x0142uC\xbc\x8a\x1dX\xb4\x8d\x9b\b\xb7\xccf2\b'GIk\xf6\x1f9\xcecOm\xe0\xe7?\xbd\x9dg\xd6=F\xef\xe1\x83\xe4\x1b[Yv\xee;Y|\xe6\x15L\x98\xb6\f!\x1c\u02a5\x82\xbd\x81d4\xb5\xe0\t\x8co\xa0V\x92\xa9U\xe0(4\x90\x95$\xc9*\xd9\xc6\x16&/=\x9d\xbd\x1b\x1e\xa3\xa7\xeb0\x8f?\xb5\x81K\xdf\xf4F\x94\x14\x91v\xf9\xe4\xf5Zv\xe5ah0\x04\xec\u067b\x97\xed\u06f7\xd3:~25\xeds\xe8\xed\xf5Q\xc3!8\xc6\xdab:\x02\xa3\x04:\x8a\x8d\x8b\x83\"\xac\t\x96J\xf0\x10\x11\x18\xc4\x18\xab\u0724\xaf\x14\x12\xa9\\\xb4\f\x10Z\xdbD\xa2\x14\x962\x16m1\xa9\x1b\xc5\xda\xce\xda.{\xee\xa2\xd5\x1c\xdc\xf1\x04\xfbw=\x83\x92.\x06C\xe0{\xf8\xa5\x02#G\x0f2xp'RHf\xbe\xe9}dj\x1a\xac\xea#\f\u0441oC,\xfc\x80\xd2\xc0!\xca\xc3\x03\x8c\x1e=H\xb1\xb7\x1b\xa1\xa2\xf0\x94r\x11od\x88\xd2\xc0q\xbc\xa1~\xca\xc3\xfd\x04\x85Q\xda\x17\x9d\u0182+>L\xae\xa1\x95\x902\xa1\x86\xb2\x16d5\xc8\xc0\xa7<j=\xd5[\xa7\xcc\xe4\xec\x0f}\x9c\xb6\xa9\xb3y\xf0\xc6\xcf\xd3\u007fx\x1fR(^\xb8\xfb\at,=\x95eo}\x1f\xa5\xa1~k/\x1c\x1f\xdd\xe3\x13\u0258\xae;60K\xac\x86\x8dA\xc62\x03Qq\xbe!\xb1)\u0589Z(\xc6\xfa3\xd9,S\xa6t:\xafz1?6<\"\xda\xeb\xebB\x80g{\xf2\u007f~\xb4\u031a\a~\xfa\x03\x9e\xb9\xe3\xa6\xe8\x04oc\xa2\xd2\x15\u0284\x01\u35de\u02fcK\xff\x14\x95\xc9\xe0\x8f\x0eG\xedx\x88\tB\xa4\x17\xa0\x8a\x01\xaa\xac\x91\x9e\xae`aBF\x0f\x93`eF\b\x9bo(\rZ\x82\b\x81\x82F\xba\x122\x82\xc0/\xa3\u00c0\xba\xd6\xf1\x9c\xf6\x9e\x0f3\xeb\xb4\xd5l\xbc\xef'l{\xf4\x17\x1c\u07ff\x8bc\xbb7\xd2{p\x17\x9b\x1e\xfc\x193W\xadf\xd9\xf9o\xe2\xd4U+X2\xb3\x839-5\xd4\xe3\xe3\x86\x1e\xa1\x8e\xdd\xd1xE\xbbS\x13\x1f\xa3\x8dA\b\x83#\x1dT\xc6N\x95\xf6\x97<6m\xde\xcaC\xeb\x9f\xe6\xbe{\xeef\u02f3OS\xe8\xebFek\x99\xb9\xe4\x1c\x16\x9f\xf5N\xa6/X\x8d\x9b\xab\xc3\xf7\xca\x04\xdeH\xb5\x9d\\L^\x155\xc6\xd3\b\xdf \x1a!\x93\x93d\x95\xb4Y\x8aR\xa0j\ua63a\xfcl\x9e\xad\xfb&\x85\xc1>\x9ez\xf6yv\xbc|\x94\xb9\x93[)\x16K'@a'\xafW\xef\xd2\xda\xde\xf8\xe5\xb2\u01c6\r\xcf\xd1\xd7\xdb\u00fc\xe5\xe7\x90k\x9b\xc9\xc0P\x11\xe9\x1b\x84\xaf\xed@\x91\x12h\u01e0\x95\xc48\x12\xed`\x8b\xbcR\x18e\x87td\xa8\x11\xa1\xa9\xaaGBT\xf5\x93H\xa5\x90\xcaA\x87~\x95\x83.\txc\xaa\xb0|\x91\x82b\x84\x10\x94\xca%\x9a[;\x98\xbf\xe4|\xba\x0fo\xa30:\x80\xebf\xad\u076eT\x88l\x1eod\x90\xcdw|\x03\xaf8BC\xe7lr\r-d\x1b[\xc85\xb7\x93o\x19\xcf\xc0\xfem\xbcp\xf3?s|\xeb3\x04\xc5\x02\xa1WD(\a\xe5f-1*\x15\xd2u\"\vm\x89p\x9ch\x94\xbf\U00084170A\u03a5\x00\x1c\x19\xf9\x97\x87\x01\xc5\xe1\x01\xb25u,Z\xf3\x0e\x8a\x83\xfd\xdc\xfb\u5ff1N\xad\xe5\x02\xc7\xf6lG\x87aEzhl\xf1\x8d\xcd\xc8\xcc\x18\xe2uL4M\x85R\x88a\xa9\xb8\xe9\u0506\xd0+\xd9F+Eh\x1b\x03\xb9l\x96\xce\xce)\xaf\xfe\xd0\xd0\u06a3H \xdc\xdd3t\xf1^\xdc\xcf>\xb9\xfe\t~\xfd\xad/j\xc2 \x19\f\xaa\"\xd9B\x9f\xda\xf6\xc9\u033f\xfc\u007f\xd04m\x16\xe5\u0442\r\x8a\rBD9@\x15m7.\xfd\x10\xa1+\x13\x9fF\b\x84\x0e\xab(\x86\xc4`G\xa4\xce5\x1a(\x1b\x18\t!\u0087\xb5\x0e(\x17\x86Q\x8eK\xdb\xf4y\\\xf0\xe1\xcf0\xf3\xb4\xd5l}\xf8n\xf6nXG\xcf\xfe\x9d\f\x1c\xde\xc3\xf3G\x0e\xb2\xf9\xc1\xbbX\xbf\xec4\x16\x9cq>KV\xac`\xd5\xc2Y,\x99>\x89\xf1\xb5\x0e9\f\x84\x1e^`1\xae?dQ\x8fG\f\x1c)q\\\x17p\xf1\x81\x97{\xca\xec=\xb0\x8b\xed{w\xf3\u0533O\xf3\xe8\xaf\xee\xe5\xc0\xf6\xcd\xe0\x15q\xb2uL[t6\xb3\x96^\xc4\xcc%khh\x99B\xe0y\x94FG\"WCSE\n\x99h\xc4Y`\x03{\u0168&+C\x8c\x92hW\xe2FR,#a\u072c\xf9\xb4u\xce\xe4\xe0\xa6>^\xda\xf04O<\xbf\x89\xb9\x93/\xaa\x1a\xda:y\xbd\xfaW\x10h\x10\x92\xe1\xe1\x11\x9e|\xf2I\xbc \xa4u\xda|L];\xe5\xbe\x11{\u007f\xe8JR\x90\f\r\x9a\x10\u0436\xa0g#_\x11\t\xc65Hm!\x96\x98\xb4\x94P\x05\x1f\xc4\xe8\xafT\n\xa1\x1c\x8452O\xec]\u01de\x8eI\xe3\xc7\xd1}\xa9\x81\x92\xe73w\xf1jvo~\x94-\x1b\x1fAJ\xc7\u00ae\x91L\xd9\xc9\xd7Q\xe8\xed\xe6\xa5\u007f\xff\x17T&\x87[[O\u0744\xa9\xb4\xccZ\u0338\x05\xabpj\xeai\x984\xcdz\xc0\x14G\b=\x8f\\S+\xf9\x96v2\xf5\u0378\xf9:\xb2\xf5\x8dd\xeb\x9bps\xb5H\xd7%\xd74\x0e\xb7\xa6\x81 \xf0*\u0773\xb6\xb9\xa09mpb\xbc\x1d\xf0\n#H\xc7e\xd1\x1b\xae`\xcf\xd3\x0f\xb3\xf9\xc1;q\x9c,M\x93:\x93\x0e\\V\xba\xad\x13\t\xe04\xb7i8\x11z\x12U\xb80ZkJ\xa3\u00f6sO\rve\\'\xfe&\x8f\xbf\xaa\xc5\xfc\xf9\xeeQ\xb1bBmh\xf6\xfc:\xf7L\xa6\xfeo\xb6o=\\{\xd7\xf5\x9fg\xf0\xc8~\x19\x9b\u0724\v\x9e\t\x03\x84\x90\u0338\xe0\n\xa6\x9e\xfb6\x02/\xb4\x129O\xa3\n>\xaa\xe0#\xcba\xc5\xc1-R]\xd8\x01\x01\x8d\bmjI\x1c\x95m\xa4\xc2\b\xab-\xb4\x8bP\xe0(\x81\x02L\u0672\xf6\xa2\xc1\xb1\xba;\xac\x1dg)\xf0Q\x8e\xc3\xd4\x15g\u04f1p\x15\x87\xb7l`\xd7S\x0f\xb3\xf7\u0675\x1c\u06f3\x15o\xb8\x9f\xed\x8f\xff\x8a\x1dO<\xc8#\x13;\x99\xbap9s\x17-b\xf9\xc2\xf9\x9c\xbal!\x8b\xe6\xcc`\\mM\xbaW\xc2\x18\x9d\x04V\xc4\xef\xcao\xaby\xa2*\xd8VDV\xb8\x95\x93K\x00\xec9\xd6\xc7\xc6-;xi\xe3v6m\xda\xc2\xd6M/\xb0o\xfbF\xcaC=\x00\xd46\x8dc\xf2\xf2\xf3\x99\xb6\xe8\\\xa6\xcc?\x8b\xa6\xc6NB?\xa0T\x18\u0184a\x92rb\x17j\xd4%%\xfffC\x1c\x05\xa0B\x83S2\x04\xa3\x86 \xa3Py\x97\xacR\xe8 \xd7\xd4\u01b4SWsp\u04f3\x8c\x1c?\xc2\u06a7\x9e\xe1mo\xbc\x88&G&\xb9\x89'\xafW\xbf+\x0ft\x88\xa3$;\xb6\xef`\xeb\x96-\u0535L\xa0}\xfeJF\xb5\xb5]\xad8\x01\xc6)C\"I\x90\x17\x81FZ'\x14\xf0\x02BW 4\u0220\xd26\xc6E\xca6O\x95\x82%\xa4c\xbd\u024d\u0104\xe6\x841\xf6\xb4\x8a\u00cc\xd5u\x18C\xc9\xf7h\xa9og\xc1\xd2\v\u0637k\x03\xc5r\x81L&\x97nJQN&\xc1\xbc\x83r\x81BO7G7=\xc9\xee_\xddJ\xcb\uc974/\\\xc5\xcc7\\I\xf3\xccEH7\x83r\xb3\x89\"\u013a\x12Z\x88VD>\xe3a\x10\x10\xf8^\x15!\x89\x90\x84\xdaP\f\f\x8e2\xb8\x91\xef\xad\x04\xbc\xd1!\x9c\\\r\xe7^\xfd)\x1a'vR\xd3\xd8\u00ac3.\"\xf4\xbcd\xe8'\x9em\x91\xe6D}y\xfc\x9a\xc4\x19\xaa\xb1\x90\u008c\x11\xbfHi\xdf\u02d1\x9e\xee\b\x86\x16),]RSS\u00e2EK^~\u054a\xf9\x86\xee\x11VL\xb0\x83A[&\\\xfc\x8e\xfdG\xcbk\xee\xfe\xb7\x1b\xd8\xf3\xe4\x03Q\xb1\x92U\t\xd3\xf1.>~\xc9\xe9\u033f\xe2\u00e8l\x8e``\x98l)D\x16}d)\x88r\x99t\xb2\x83\xc5\xc7\x11\x11\u0623`\x92I\xa8e\x1c\xe4Wy%\xc3Hq%\r2\x8e!)i\x8c\f\x10\xf5N\xb2\xad\x1ac5\xb0\xa1\uf8e4b\xda)g\u04f1\xe4T\x16\\\xf06\x0eo|\x9a}\u03ed\xe3\xe0KOS\x1a\x1ed\xe0\xf0>\xfa\x0f\xef\xe6\xc5_\xdd\xce=\xad\x13\x98:k\x1esf\xcfd\xe1\x9c\x19,\x9e;\x93\xe9\u04e60i\xe2\x04\u018dk\xaf2\xd8\xfa\xcf^~\xe8\xd1\xd5}\x8c\xae\xa3\xc7y\xb9\xab\x8b]{\xf6\xb1}\xd7\x1ev\xef\xdd\xcf\xce\xed\xdb\xe8\u06b7\x1b\x82\xb2]\xe8\xcae\u0714\xb9L]v\x1e\x93\xe6\x9e\u0284)K\xa8k\x9c\x84\xf6|\xca\xc5\x02\x84:\xc9f\xac\xe0Yc\b\x9a\u06379\x9a\xed\x16Q\xfa\xba\xe3\t(J\u00bc\xc4S\x90-\x97\xa9\xa9\xabc\xfa\xa9\xaby\xfaG\u07e4<:\u0313\x8f<\xc8\xf3W^\xc5\u014b\xa6a\xbc\xc2\xefl\\v\xf2\xfa\xdd/?\x88B\x8d\xb5\u6847\x1f\xa6\xeb\xc8\x11&/?\x97\xe6\xd9K)\x95<\b-\xf6-\f\x89\xef82vB\x8c\xd1Z\x89\x8a\xf8\x13U\xb2\u0713\x8cn9\x13y \xe98\x861n\xaa\"\x0f\x13\xa52\x18\x13F\xa4\xa3\x8eh\u0394@\xbd\u2ddb@1q\xeeg\xa85\x05\xcfg\xfa\x82s\x98\xdcy\x0f;\xb7?m\vo\x94\x04\x11\xdb\xef\x1a\xa5\x10FYO\u01e8\u0287^\x89\xee\x97\xd6\u047b\xebE\xba^Z\xcf\xf8%g\u04b1\xeaB\xc6\xcd_\x89r3\x98\xa0\f~\x19\xc2 \xf1^\x89\xa3\xe5D\xe4\xefbR)E\x00\xe5\xd0\xe0\x866\xffS\x1a\x83\x89\x14*~\xb9H\xeb\xd4Y\x9c\xff\xe1\xbfKN\r\xa1\xefY\u062a*\x9c\xe27\x9d\xabM\u22de\xda\xe7*\xc0DdC,$\x04^\x89\x81\xeeC\x89 \u013e\x01\x9a\\\xae\x86q\xe3\u0195O9e\xf9\xab\u04d9\x1f\x19\x1c\xe1\xd1cQ\xd3\uc3f6=Q\xe0\x8bk\ufedb\xb5\xb7}+*\x1a\u056ezB\b\xc2\xc0\xa7v\xdcD\x96\xbc\xfb\xafh\x9d4\x8f\xf0\xd80\xd9Q\x0f\x15\x00\x81\x8e<\xb2M%4V\u0605)=[\xc8\x11\xa6\xc2\x06\x1bK\xc4\bm\xc7d\x8d\xb4/\xa4\xd4 C\x99\xec\x84B\x02%\x03\x8e\x86\x1aY\x95\xb8\r\x10\xe8\x90`h\x10\xa9$\x93\xe6-e\xe2\x9c\xc5\xcc=\xf7\xcdt\xef\xdc\xc4\xe1\xcd\x1b8\xb8\xf1i\xfa\x0e\xed\xc3\x1b\x1d\xa1\xd8{\x8cm\xbd]l{\xfa\x11~\x8e\xa0\xb9m\x1c\xe3\u01f5\xd1\xda\xd2D[[\x1b\xadmm475\xd1\xda\xdaB}}=\xd9\\\x0e\xd7\xcdD\x836v\xc4\u0646\b\x94\xe9\xeb\x1f\xe0\xe8\xb1\xe3\x1c;~\x9c\xe3=\xbd\xf4\xf5\xf7\xd3\xd7\xdb\xc7\xf1\xa3]h\xbf\x92\t\x9a\xcd\xd5R\xd36\x81q\xd3\x17\xd11\xff,\xc6O]B\xcbf\xa3\xc1\x82\x00\x00 \x00IDAT\xa4\x99d\xf2\x8d\xe8\xd1\x12\xa5\xc1A\x9b\xcen\u0184\x0f\x881\x06\x9ecL\xf8e\xf4\x9e\x18$Z+T\xe8\x90)I\xfc>\x8d_+p\xb3\x06\x9c\x80\t\xb3\x171u\xd9\xe9\xec\\\xff\x00;\x9f{\x8aG\x9e\xda\xc0\xf2\xf9\xd3hS\x12\x1d\xe8\x93\xc3C\xafvW\x1e\x84(\xc7\xe1\xc0\xfe\x03<\xb1~=\x81PLYt\x06\x8e\xd3@a\xb8\x80\x0el\xc7h\vz\xa5\xa0\x1at\xe4\xa9#PH\xa4\x0fF\x9a\x8a\xe2BG2;i\xd0J`\x14\x84R \x8d@\x06\xd1\xf4\xa7\x14\b\xe1 \x8dk!\xbctdb<\xb6n*^\xe3\xe9\x8f\u0152A/\b\xc8\u05f6\xb1h\u0645\x1c\u073f\t\xdf\xf7q3\u0654\xe5m\xf5\xf0LlK\x81\x90(7\x8b\x0e\x02z\xb6=G\xcf\xf6\xe79\xb8\xf6n&,9\x93)g\xbc\x81\xc9\xcb\xce\"_\u05c8\x0e}\xc2R\x11\x13\x06\xf60.L\x14\xe5f\xed~U\xd4\xc8H \xc4P\xf45\xae\x14d\x85\x8c\x8c\xe4l\xf3\xe9\xfb\x1e*\u04b2\x1bm3\v\xd2F\x06R\x8a\u02ae0F\xfd\x93D\xdcE\xbfK\xaa\x05\x8b\xb1%\xc9\xf0\xf1.\x86\x8e\x1dI5\xbe\xf6g\xad\xa9\xc9\xd3\xd1\xd1\xf1Rg\xe7\xf4\x97\x00v\xed\xda\xf1\xca\x16\xf3m\x83\x88\xabf[M\xf9N\xa7\xe6\xcf7<\xb7\xb9\xe3\xe7\xdf\xf82\xde\xc8P\xc5C8\xfd\x86\xea\x10\xe5\xb8,^\xf3G\xccZ\xb4\x06ul\x14Y('\fzl\xfc\x9b(-\x05\x88\xd0 }\x1d\r3\xa4X\xe3d\x81\x88\xca\xc8l\xec\x02d\"\xf1\x94\xa8\u021a\x84\x06S\b\x11\n\xc8\xc9\xea\xe2\x86\u0571k\xad)\x0e\r\x80\x10\u0536\xb63\xe7\xac7\u0439\xfc\f\x96\r\xbe\x97\xfe\xc3\xfb9\xb2\xfdE\xbavl\xa2g\xff.J\xc3\xfdx\x85\x11\xfa{\x8e\xd1\xdfs\xec\x84\xd7F\np3\x19\x1c\u01f58c\x14{d\xb4&\fmZJ\xf9\xb7\x848g\xf3ud\x9b\u06e8in\xa7m\xd6b\xc6\xcdXF\xfb\xe4\xf9\xd47M\"\x9fkB\xe1\xe0\x97\n\x94z{\x11Z$\x1d\x80\xd1&\x05\xab\x90\u023a\xaa\xe2\x1e\x93p]S!\x92\xa5\x02\xe1 \xb4\x83(\x83\xf2B\xf0\fA\x9d\xa4\xe4\r\xd3\xd8:\x89\xb9\u7f45\x9d\xeb\x1f\xc0xE\x1e\xfe\xe5=\x9c\u007f\xe1\xc5\\<\xbd\x11',\xe0\x9f\xac\xe6\xaf\xf8%\x84\xf5W\xf1\xfd\x900\f\xc9fs<\xf2\u0223l\u077a\x95\xd6\tS\xe9\x98}\x06z$\x80r`\x15\xbb&\x1d*n\u05de\x8c\xd15%\x90Q\x97NhR\x1dqtOi\x8b\xf7j!\b]\x81\xd1\x12\xa1\xb5\xbdO\x8dU\xb6\b\xe5 B\x1f\xa3\xd3\x16\xafT\x85>\x8b\xb4{a\xfcA#\b\x82\x10\xdfHf.8\x8b\xc9\xcf\xfe\x82}\xfb\xb6\u0635+\u0485<\xa5\xd1\x16\"j\xdc*\xae\x83\"\"n\x87\xbb\xf63\xd2}\x88\xae\x17\x1fg\u0082UL;\xeb\x8dLZ~.\xf9\xc6\x16k\xd0U*\xe2\b\xaa\xf0nA\xf5p\xa0\u0586R\xa0q\x94\xc0!\n\u0280$pF\x18\x91\xd8\xf1\xc6E\u061e$*\x83C\x82\x94\xbdA\xb2a\x9c\xe8\xfd\x14\x8d:\xa2\x94B\n\xe8\xda\xfe\x12\xc3}\u01d3\xcd\xc1\xdapHr\xb9\x1cs\xe6\xcc~\"\xfe\xfff\u03de\xfb\xca\x14\xf3\x83\x03#t6\xd5qag]T[\xcd\xe2\u06f7\xf7|\xfa\xc7_\xbf\x8e\u00db7\xfcFxE\nI\x18\x86\xcc8e\rK.\xf8cra\x0eot\xb8\xc2\xfe\xc6$\x89\xa80\xc32\xb4\x8c\xbc\b+\v\x04\x93\xda\xe5\xa9\x18\xea\x98\x04\x96\x13\bi\x1d\xc9|$\x19a\x902\xaa\xae\x010\xaa\xed\xa0\x84\x9bJ\x1eO0\xb0\x94\x13b\xb1@ \x04N&K\xcb\xe4\x19\xb4t\xcc`\ua2b3\xf0J%\x06{\x8fs\xec\xc0^\xfa\x0e\xef\xa6\xff\xe0\x1e\x06\x8f\x1e\xa68\xd4Oyx\x00\xbf8\x82_*\xe1{%\u029eO\xb9<:\xf6\xb6\x04\xe5\"3YrMM\xb8\xb9<\x99|-\x99\xdazr\r\xcd\u050f\xef\xa4y\xea\\\x1a\xa7\u03a6~B'\u0646\x16\x1c\x95%\xeb)\x18\xf5\u0445\x02a\xb9h\x8f\xd2(\xabJ1\xa6z\xdb7\xa6\x1a\x97\xa7\xe2\x05m\xa8V\x04\t\xa9\x90\xd2E\xaa\f\x02\x05\x81]n\xca\xd8\x00g?\xab\xc9\xe7\x05S\x97\x9d\xcf\xc4\x05\xcb\xe9\xda\xfa\x02/<\xf4\v\x1eZ\xff^fO^C\xa7\x94\xc8\xc87\xfb\xe4\xf5\xca]\xf6T\x17R\xf6|\x94r8p\xe0\x00\xf7\xdd\xff+\x06FF8\xfd\x9c\xb3hn\x9eN\xe0\x85\t\xe9Y9\x8e\x99\xe4\x18&\x8c\xb4]v\xe4RJ\xc5f%)\xc2I\x90\xb1 \x95\x9c\x1c\xed\xf9\xc2rUV\x8b\xa0,, C\xdb\u045b\xf4]\xa9\xab\x03\x89\xc6\xe8\xd6\rP\xf4<\x9a\x9a:X\xba\xf2M\x1c9\xb2\x97 \bP\u02a9\x10\x86\xa6\x02\x87\x86a\x88\x10hG9\"\fC\xa1\x03\x9f0\x88\f\xf7\xa4-q\xc3\xdd\a\x19=v\x84\x97_x\x9c\x89KNg\xfe\x9b\xaeb\u0082Udkj\xc0+B\x18\xa2\xa4\x8d\x88\u04c0\x17\xa6\x03\x9e\x05\xa1\xb1\x01\x1d\xb5\xd2$\xd8u\x1c\u0111l\x88\xa2\x12\xa5\x97\x14u\xaa+\xb6\x10)\x188\xc5\x05\x1b\u049b\x1cH%\t|\u0631\xeeW\x84QS'\x85\x04cp3\x19:;;\x99>}\xfa\xa3\xe95\xf0\x8a\x14\xf3\u03a6:\x8e\x8c\x96\x99Tk\xa7\x0e\x9f\x1d\xe5\v\xbf\xba\xeb\x8e\xdc\x13w\xdc\x12U\xee\x8a\x13\xa2\x05\xfa\x15:\xf4i\x9d<\x9b\x15o\xf9\v\x1aZ;)\x8e\f&8\x9a\x88\xbc\x1eLD\x02\x1a\xa1\x11\x81%d\x84\xa6\x92p\x93\xb2\xbe5b\xcc\xd1&\x15\xfddM\xf25^`\xacw\xb1\x93\xac*K\x88\x8e\x82hP\x95x\uf60d7\xa6*B\xca\x18CP.\x13D\x13\xa9\xc2qqs5\xb4M\x99N\xd3\xe4\xe9hs\x01A\xa8\x19-\x96\x19\x1d\x1a\xa0\xd0\u007f\x9c\xd2@\x0f\xe5\xe1~\xbc\xd1a\x82b\x81\xc0+\xa0\x83H\x9b+%N&\x8b\x93\xad\xc1\xa9\xa9#[\xd7H\xa6\xb6\x9elC3\xb9\xa66r\xf5M(7WI\x8f\x05t\x10\x12\x84>~P\xc0\xf1}T\xa0\x13\x85B%p0-\x1c\x88\x19\xf6\xf8\x06\x16iuY\nw\xb27\x82\x90\x0eB\xb9v\x80+\xe9H\xc0\x04\x91\xb1\xbf\x94\x94\x06\x86\x187~.s\xcfz\v][_\xc0\x1b\xee\xe3\xa1;o\xe3\x8cs\u03a5\xa5#G\x9d(\x9c,\xe6\xafpW\x1e\x04\x9a\xb2\xe7\xe3\a\x01\x99l\x96G\x1f[\u02f3\x1b6\xd0\xdc6\x89\x99K.@\xca,Aa4\x9daV\x89o0\x15\xcc\xda\xe2\xdb\xf2\x04\xf5\x85 \x95t\x9f\x92\u0449\xd0~\xaa\xebH\x9c\x88 \xb1\x84\xaa\"t\x1ckb\x15\xc1\xa1\x15\xb5Fe\u011e(\xad\u0224<\x94$\x06?\xf0(gj\xe9\x9c}\x1a\xe3\xda\xef\xe6\u0211\xbdQ-\x90\x91\u72c98\"I]}\xab)\x16Kr\xa0\xf78H\x11*\x89\xc4\x18\xa1CKrZ\u0264\v\x02\x8a\x83\xbd\xec[w/=;^b\xc69\x970\xef\xc2\u02d80s.\x19GQ\x1a\x1d\u0173)\x17d\x94\xed\x9eCc\vy`\x04~\xe4c\x93\u02d0H\x18IQr2vG\x15\xe2D\xd1NU\xf1Oo`c\x06\xfd\xa3\u04f1r\x04\xc7\xf7\xecd\xdfs\xeb\x00R\xf9\x9f\x06\xd7\xcd\xd0\xd9\xd9\xd9=y\xf2\xe4g\x01\xae\xbb\xeeZ>\xfe\xf1O\xbc2\xc5<\f\n(\xc7\x16\xf2\x97\x8d\xf9\xd8m\xf7={\xd9\xdd\u07f9\xde\xc6s\x83\x90c\x16\x8a\xd6!\u065a\x06\x96\xbd\xf1O\xe9\\|>^i$\xe5\xbef\a\x19b\xa3,!%\xb2,\xec\x00C*\x930\xc9\xeeHgIUy\nW\xceR\u0080\t\xad\x87\x84\x0e\xac\x86\xb5*\xe2{Tc\x14\x88\xdaH\xab\xae\x89\xa4V\xd5T\x86I\x919Bk\xf0\xca@9\xe9j\xa5T\xb8B\x91\xcfg!3\x81|\xdb\xe4J\x8cZ<( \u04a9%\"\xbe\x13\"c\xba\xf8\xe8\xa8#\x9bQC\xa8C\x1b^\x1b\xb9\xd4\xc5x\xa1q\x05\"\xa7\x90e\x93\xe8\xea\x05\xd5\x1dy\u0711I!\x93\x9b\u0264N=\t\xeb.\x94\xbd\x01\xa4\x8bRN\xe4_a\xaa\xa6\x04\x85\x89\n\xbag\bU@\xb6\xd6a\xf6\xca7\xb1\xf9\xc1\xdb\xe9\u0677\x9d\xadk\xef\u7a67\x9faJ\xeb\xb9,\xccJ\x94\xd0'\xc9\xd0W\xa4\x90\v;%\xe9\xfbx\x9eG6\x9b\xe3\xf0\xe1\xc3\xdc{\xdf\xfd\xf4\x0f\f\xb0\xea\xfc7\xd02y>\xa1\x1fB\xa0#\xbf\"\x12\x88\xc3\xca\f#\x92\x1b\xab\x9a\x12B\xd9\xf7WX\xb23N|\u0462:?GG\xd0BNJ\xb2Y+$ 4\x18\x19}L\xabJb\f\xf1\xa8\xe9\x18\x9f\x12\x93Z\xfb\xa9/n\xb4\xa1P*Q\xdf<\x85y\v\u03a4\xbbk_J\x9e\x17Y\x0fD\x039\xabW\x9f+::\xa7\xbd\xfc\xc2\u018d\x93w\xef\u06a9\xf6\xef\xdcn\ub0b4~&:4\x11\x89j\u05f5\x00\x86\x8f\x1eb\xe3\u03feC\xf7\xc6\xf5,\xb8\xf0r\xe6\x9f\u007f\tM\x13;\x19-\x96(\x95\xcbv\x03J\fj+\x9a\x1b/4d\xb4\xc1Q\x95\xe7\x11\xf3\f1\xf9\x99\xa44\xa5\x92\x8a\xe2B\x9cL\xacF7\xa5L\v\u0222_\xa5P\xb8\x19\xd8x\u07cf9\xbe\xdfz\x1eIe}\u0305\x90477\xd1\xd8\xd8p\xeb\u99dfy\x04\xe0\xf2\xcb/}\xe5\x8a\xf9\v}B\x02\xda\x18s\xfa\xdd;\xfb\xfe\xe9\xce\xef|\x95c{\xb7G\xb4\xb6\xb5r\x14VRb\x85\xf5\x02f\x9dv\t\v\u03bb\xca\x16Y/\x88\n\x87\xb1\xb0J$\x19\x14\xda \x03\x81\xf4\x05\xf8Q\xdd3q\xe7]\tI\x8d\xb3\a\x8d\xa9:\xc0$\xb8[\x14\xa7\x12\xad+\x83\x0e\xc2h\x047\xeaV}\"\x8a\xdbA\xd4\xc8\x13:r\xfb\xb5S\xbe\xb81\xfb,\xd2\u040e\x86P\xa3M\x00\x06B\xdf\xee\xee1\xd6fDu\xa3R\x95P\x12\x1dI\rc&X\u0349\x98\xb7I\x1d_\x83\xacDf\xa5\xdd\xe8b2\xcaT\ba#H\x05\xf6\x926g\xab\u0605FS\xb8\u05ab\u0669$\xb1\x8c\x91\xc0\xc6I\xe4\u06b7)\xbd\xe5\xa1a\xa6\xccX\xc1\xfc3/\xe1\xf1}\xdb)\xf4v\xf3\xf0m7\xb1h\xe5\u9d0c\xcf2\xc5-\x9e\xb4\xc4}\x85\xf0\x95 \b\xf1\xfc\xc0F\x9f)\xc9#\x8f<\xc6\xfa\xf5\xebii\x9b\xc8\xecS\u0788r\xeb(\x15KQg\x1b\xdd\x1f\xa6\u04bf\xc4P\x8a\x10\n%\xdd$\x872>\xb9\x19a\tO-\xa5%M\xa3\xc9F\xa3\xb0\u0120\x13\xf1|\x0e\xa9p\xe2\xc8$/\xe6\u0174\xb40\xa6\xd1T\a\x8f&r\x96d\xb6!n@\xfc\xc0C\xe4\x1b\u9735\x92\x86g\xefchx \xcab\x96\x897\xb8\xef{\x94\n\x05\xae|\xd7;~\xfa\xa1\x0f\xff\xd9\xfao|\xe3;\x97n\u06fa\xe5\x8a\xed\x9b7\xe5\x8e\x1e>@\xe0{\x91\xaa&\x88\x1cU\xa3\xcdJJ0\x9a\xae\x1d/\u0473\u007f'\x877>\u0242\v/\xa3s\xe5j\xea\x9b\xda(\x97J\xf8\xbe\aZ#\x05\xf8\xa1\x85H\xe2\x00x7\n_Q2.\xe4\xd2n\x84\xca\xde?FV\":D\x95\u0657\x957J\x11)i\xc6\xeem\xc6P\xd3\xe0\u0435}'\xcf\xff\xe2G\x16>q\x1c\x94\xb4V\xbeJI\xa6M\x9b\x1e.[\xb6\xec\x81\xf8\xff\x99>}\x96=\xa5\xfc\xa1\xd7\u05be\x82\x91\v\x9b\\\r\xf0\x96\xff\xf5\xf9/\xfd\xf0\xdb\xdf9\xe5\xeeo|9\x04T\xea<E\xack5:\xa4c\xe1\x19\x9cs\xd5\xe7ih\x9d\x867:R\x91\xebD\xd2'\xa1\x05\"4(_\xa3|PZ\xa4 \x841\x05\x8e\xb1\x12\xbbT\xe7\x9b\xcasE\xa4vTc\xacdQ\u01dd\xbe\x01?\xfa\xb7\xacD\xb8c\xb2\x00\x93\xef'*P\x10\x8c\x89\xa2\xaa|\xcc\x0f\r\xe5\xd0$2\xb0\x18\xb2\x11Qwm}\x8au\xd2m\xa7C\x91\x93\x82m*\xf7@|<\xad\xcal\x8c\xb0K-,\xfc\x14\x0fx\xa4\xd5`UZ\u05b4\x061\n\xb3\x95\xd2A\xban2)g\x17\xfc\tMT\xa2vI\x9e\x8b\x10(i\xc8\xd5\xe4\xc9\xd67s`\xcbz\n\x03=\f\x1e\xef\xa2q\xea<\xda\xe7\u0367Y\x84\xd4:\xe6$\xdc\xf2\a\x86WB\xad){\x1e\xbe\x1f\x90\xaf\xa9a\u03de\xbd\\\u007f\xfd\r\xec\u0739\x93\xa5g]\u01bcS/'\u0502 \xb6j0\x11\xb4\x96\xea\uc171\xd3\xd7\xcaq\x91\xcaI\xab\xe8l\xef\xa3\x04ZEo\xba\x02\xa3$AFb\\A\xad#\xc8\xc4\x1f\x13\"\x82\xf8\"\a\xc6P\xa3uh\xbb\xe3\xf8\x84\x9d$\x17\x8f9\xe9\x91\xea\u0423nAG$|]\xae\x86\xfe\xa3\xbb\xe9:\xb2\a%]\vgD'\\)\x04\xa3\x85\x02\x8e#7\xbf\xfb\xb2K\xbf\xf4\u033a\xc7~v\xedw\u007f\xf0\x8c\x10H\xc7\xcd,,\x95\u02b2\\\xb6\xf6\xb9\xb6V\x1aat\x98</k-\x1b\xd2s`7\x876=M\xef\x81]()i\x9a<\x8d\x9a\x86&\xeb4*\r*^\xf4Ql\x9bR\x8a\x8c\xe3\xa0\x1c\x85r\x14\x8e\xe3\xa4|\u0355-\xda\"M\u041a\xe4\xf9*Qi\xae\u0318\x8d9[\x93\xc7\x04p\xdfu\x9ff\xfb\xda\xfb\x91R\xdab\xae\x14\x80inn\x16\x17^x\xc1\x86O~\xf2S\xd7^s\xcd5\x85\xad[7\x89\xaf}\xed\x1b\x89\"\xe6\x0fzm\xef\x1eT\x00\xbd\xc6\\\xb5\xee\xa1\xf5W\xfd\xfc\xa6\x1b\xad2u\xec\x14d\xe4\x02\xd68\xbe\x93\xb3\xdf\xf1\xb7\x8c\xefX\x8272\x12\x8d\xf6F\xc56\x04\xe5\x19T9\xc4-j\x94\x17\x17)\x81\x92.*\xc2r\x11\"96&\xaf\xb9\x10\x91\u02df\xf8\r+\xa6R\x95uh\u0426\xf2A\x11R\xc1\x15K\x1a=\x18`\x02\x92\x01\xa4\x8a\x06\xfb\u0109\xaex9V\n\xb1\x1dN\b\xb4FkK\x18&\x8c\xbb\xae\xb8\u02c9\x14\xbc]\U00069c0fP[\x88\xc5FhE\x12*c\xd9\xf6*\xd7\xc6\xc8HH\xbb\x02\xbfF\x1286n+\x8c\x8e\xbcUi(\"\xea\xab\x13\r\xa2\x1d\u00d6\x8e-\xe4(\u01e6\x80\x13\x1d\x99SG\xc1\x84\x9fH\x9fTB\x83\xf6\xc1\x1b\x19a\xe2\xf4\xa5,^\xfd.\x84\x14\x8c\xf4\x1e\xe3\xd1[\xbf\u0266\xdd\xdd\xec\xf7s\x94C\x13\xb90\x9e\xbc\xfe0\xf0\x8a\xa1\xec\xf9x~@&\x9b\xc1\xf3<\xee\xbe\xfb\x17<\xf3\xcc3\xb4O\x9a\xc9\u0715o\xc6\xc9\xd5\xe3ye\x12\xec.nXte`G`\xb3>e\xe4\x8bdHd\x1d\x95H\xb9\xd4\xfd\xab\x1d(\xd7Jd[\x86\\s\x06\x91\x89&4\x1da\xff\xec\xdaiN#\x04B\xc9\xc4J\xc3(\xecC$\x06\xa6\x89-E\xfc\u0426\xb2\u07a5\x90\x94\xbc2\xd9\xc6\tt\xce\\N\xc6\xcd\u0606H\xa4\bF)\x18\x18\xe8\xe7\xf8\xb1\xe3o\u0771s\xd7B\x80\xb7\x9fs\xca\x03\xdf\xfe\xca?~\xe0\xfd\u007f|\xf5\x9b\xdey\xd5\xfb\u007f\xb5\xf2\u0333i\x9d0\t\xa4\x93\x0e\xdb\u0104!\xa1_\xb6\x1e\xe9\x02F\xfbz\xd8\xf2\xe0\x1d\xfc\xfa\xfa\xbf\xe5\xa1\x1b>\xcd\xd1-\xcf\xd0\xd0PO\u02f8\xf146\u0593\xc9f\xc1\xcdbT\x862\n\xed\xb8\b7\x13=\\\x90\n\x13E]'gkQ\x11p8R$J\xc5Tnv\xd2df\xf2y\\\a\x1e\xbb\xe9Z\x9e\xfa\xc9wm\xb7\xed8\x11\xf4\x85\tC-\x16-Z\u013cys~$\x848>n\\\x9bZ\xb0`qr;\xfdA;\xf3\xa7\x0e\r\x88\xf3\xa75\x85\u0198\xa6\xfb\xb7t\xfd\xeb\xbf]\xfbOSv<\xfdh(b\x11uJEa\x8c\xc6q\xb3\x9c\xf5\u058f\xb3\xe8\xac+\xf1K%K\x04\xc6<\x896\xa8\xc0X\x82E\xa7\x18\xe0\x8a\x99\x83=.\xc5\x1dA\xfc\xfbX\xe8\xa3J\xd5)\xaa\x8c\xf1\x13T#6\xaf7\xd5\x04\x86\x9d\x8c0V\u0652U\xd5#\ufc5f\x82H\xdd\x14\xa9\x82\x19w\u035e\x86b\x90\xf2\xd8I\x9f\x0e\x12\x83|\xfb\xd6[\xa2h\f\xfcq\xc2\xc8/'<\u007f!R7\x9c\x10v\x98\xc3\u0122\x01\x11I\xc6R\xafQ,\x9b\x8a'\xf6\x1c\x17\x91\u0260\x1c\xbb \xb5J\xe9cS\xafk,\xbdJ`\x19Y\xbdO\n@e2\xb4u\xcc\xe0\xe5\x9d\x1b\x188z\x88\x81#\a\xc9\xd471a\xf99\u4961\xd5\xd5'\x90k'\xaf\xffb!\x8f\u0580\xe7\x05\x94\xca\x1e\xc6@]m\x1dO=\xf54\xd7^\xf7\xaf\xf4\xf5\xf5\xb1\xf2\xfc\xab\x98\xb9\xfc\u0354\u02be\u0748\xe3\xb5l*\x837\x96l\x94\b\u9814\x9b\x14\xf3*\xf6.\uaa0d\xac,\xdc\xc0\x11\x98zEc\xa3C\xaeVAF\x8e\xa9Pv\xdd\xfb\xc6j\xd75\x1a\x1d\xf9$Y\x8bQ]M\x10\xa61\xe34Y\x18\xe1\xcc\xca\u0252s$G\x0fm\xa1\xbf\xefh\xc4\xe3Tn\n\xad\r\x19\u05eds\x1c\xe7\xc1\xfb\xef\xbbo;\xb8\xea\x9ak>\xa7\xef\xbd\xf3\xa7{\x9f]\xbf\xf6\xd6\xfaq\x13\x0f\xb8\x8eZ822\xd2R,\x96D\xe0\x95\x01aD\n\xff0QL\xa5T\x8a\xd2\xf0 \xc7vm\xe2\xd0KO\xe1\x15Gi\x9e4\x95|\xdbxL\xb6\x16\xa3\r\xa1\x94h\xe5D\u0779\x8ax\xaf\xd4t\xa6\x00W\u0187^\x81\x12\x16\xeb\xafVb\xa7g_\x05\xf9\x86\x1cJ\xc0\xba\u007f\xff&\xf7|\xe5o\t}\x1f\xc7q\xad@CJ\x82 \xd4\x13&L\x90+V\x9c\xf2\xe0\xe7>\xf7\xf9O]s\xcd5\u07bd\xf7\xfeB\xde|\xf3\xf7\x93r\xf0\a\xed\xccO\x9f\xd2d\x00^\x1c\xe1\xf2\xfb~\xfc\x833\xd6\xdf\xf5C\xbba\x981\x9dlT|\xe7\xad|+K\xcfy?B+t\u0673\x9a\xf1\xd0 \x83\xa8\x90k\x93\x14\xf2j\xb2\xa4\x82\xb5\xc9(\xe8U\xa9\fJ:Q\x81\x97\x95\x02O\xcac2)\xba\xd5\xf5D\x87\x10\x84\xb1A\x96H&\xe0@@ \xa0`\xc1-\xa1RE0\xaadf\xccfB\x14\xf6j\xb0\u01f1b\b^\x84\xd1K\x91\x86\xbe-\xf9\x14K\x9f\xd2A\xe6c1\xed\u050cDu!\x17\x15hE\xa7:h-\x05~N\x11\xba\x12\xa3\x04F\u026abo\x84\x04\xa5\xec\xa8s&\x87\xccd\x11n\x06\xa3\x94u\xcfs\x05AF\x10:\x820\xfa\xbbQ\u0092a\xa9<\xd3D\xd2(m^d\xe0\x19\xc2b\x89\xd6\t38\xe3\x1d\x1f#\xdf\xd0L\x18\xf8\xac\xbd\xf5\x9b\xac\u007f\xf8!\xb6\x15\xb3\xbc<\xaa\x91\x9ctT\xfc\xbd`r\xb0\x83e\xe52A\x10\x92\xc9dx\xf9\xe5#|\xeb\xdb7\xb1s\xc7\x0ef,<\x93\x85g\xbe\x03T\x8e \x8c\"\u03f5\x1d\x96\x8bo\x85\x8a<N #\x99pZ\xfcDJ|\"\xb0\xf7\xa5\b\xa3\xf5\xe5J\xea\xf2\x8aZ'\xb2\xe2\xa8U\xc8V\x17\xd1\xea\"\xea\x1d\u00ac$pA\xe4\x14\"\xe3\"\x1d\aGH\x1c)P\xaab\xdf!RSj&u_\xa4\xbe=BH\x8a\xe5\x12-\x13\xe7\xd19c\x99\xdd\xc8t\n\xeb\xc4\x12\xc0{\xf6\xec\xe5\xe0\xc1\x83W\x1bc\xf2\xe0\x87\x1b6o\x8b\x90O\xa1\xdfs\u025a\xef\xdd\xf4\xf5\xaf\xce~\xff\a\xde\xff\xe9\x8b.\xbe\xa8w\xe6\xdcy\xe4jj\x84\x91J;n\xc6\xc88cX\x87\x84\xbeg\u036d\x82\x80c{\xb7\xf1\xf07\xfe\x81\x1f}\xea}l\xb8\xfd&\x8a\xbd\xdd\xe4\xeaj\xa9\xc9\xd7 \x8dI\u049at4\xc3\x11\xd1T\xa8\xf4\xcfV\xf53\x99\xea|\xd4\xe8\xf5\xadk\xc8\x12\x16}\x1e\xfe\xc6\x17\xb9\xf3\v\x1f\xc3/\x15\xadE\xb7\x92\x91\x8d\x87\tkkk\u054a\x15+JW\\q\xf9\x17\x85\x10#\x9f\xfb\xdcg\x9c\u056b/\xa8J\x81\xf9\x83u\xe6?{\xf4)\xf1\u36ff\x8b1\xa6\xf9;\u07ff\xed[\xff~\xe3\xb5\xe3G\a\xfb\"VWU\xfc\u0123\xe3{{\xc7\x02V\xbf\xe33\xb4N\x9cK\xb90RU\u016a\xcc\u0628\x16j\x8eEJ\xa2\xe5\x98\n~\xae\xd6|\xa6\xf1\xf9\xaaH\x8f\xd4G\xe3\x89+G\t\x94\xac\xdep\xec\xc6\x00\"\xefX\xfc<Z\xfc\x89\xbc2\xb50\x85\x8c\xe7\xc6\xec\xdfK!\x14\x02S\xe5A\xa1\x11\t\xb9\xa9\xa9\xf8V\x98\u07d0\xf0md\xe5y\x8b1I\xe0\xc2\xd8\xc9;\x11O\xf2\x85\x06\x19\x82\n@z\x06\xe9[O\x15\x15\x18\xa4\xb6\xa7\x0e)\x04\"\x92i)\xc7E\xb9\x99\xc8\x01\xb12\rk\xe2\xae\\F\x18\xa7\x14\x15eL\xf4}\xd3oB\xb4\xb7%c\xd1\x12;\xd4\xd0<y\x16}\xc7\xf6\u0475\xf3E\xca#\x83\x8c\xf6\xf72y\xc5yd\x1b[h\x91ej]y\x12?\xff\x1d:r\xb0\xa9\uc972\x87\x1f\x84(G!\xa5\xe4\xfb7\xff\x80\x9b\xbf\u007f\v5\xf5-\x9c}\xc9_0~\xe6\xa9\x14\n\xc3\xf6\x84\x1a\x91\x96\x95[\xa6\x92\x8b)\xa5\x83\x94c\x93qRS\x8a2\x82K\xe2\x10f\x05\xb2\xc1v\xe5\x99\xe8\xc0\x1d\xf352+!'m\x03 \xc0\x91\x96 U\u00a0\x84FI\x83\x02;\x89Le\x88M\xa4\xe4\x8a\xe2\x84S\xb6=\xc5g\xf3\r\x84\x85>^>\xf0\x12\xa5r!\"\x1c+\xcfW\xeb\x90q\xe3\xda\xe6ds\xd9;o\xbb\xf5\xb6\xaeo\u007f\xfdk\u0737\xf6\tf\u031e#\xd6>\xf4 \x00\x8f<\xf8\xe0\xba\xc7\x1ey\xf0\xa1\xc1\xa1a\x84\x10\x1d\x9e\xef\xd5\x0f\r\x8f\b!\xa4\x96R\x89\xc4\xdf)\xad\xd8\t\x03\x06\xba\x0er`\u00e3\xf4\xed\u07c9R\x0e\xf5\xcd-\u0536\xb4Y\x8f\x980\xc0\x11\x06%\xad\ubd8c\xeeSM\xac\xb2K\x87R\xa4\xd1\x02\x83r]j\x1b2\xf4\x1c8\xc4\xfd\xff\xfa\xf7\xfc\xea\x1b\xff\x94\x88C\\\u05cd\xe0\x15a\xc0\xc8\x05\v\xe6s\xf9\xe5\x97\xddt\xe5\x95\xef\xbd\x01\xe0\xfb\xdf\xff\x9e\xb9\xfe\xfa\x1b\xf8\x83\x17\xf3\xdbn\xbf\x9dw\xbey\r\x00SW\xad\xfe\xe0O\xbe\xf7\xed\xab\xb7?\xff\x94]6\xd1\x00B\xa50\n\xb2\xb9Z\xcex\xe3\u01d8\xb7\xf2R|\xafhM\xe4\xc7\xe4U\xff\xb6\xa3\xb8\xf8\x0f\x96z\xec\xbf\x1c\u007f\x9fd\xca,u\xb0I\x13\xa2U\r\xae\x10(!\x927C\b\x81P\")\xa2\xd2\x11\xd6\xcc[\xc9\u0502\x8b\xb5\xa5\x95i/\x1dg\x8dj\x18\xf6Lb\xfcobC\x9d\x94\x83\xa3\x11\x15V6\xfd\x86\x8b\u0602 *\xd020(/\xe2\x0f<\x83S68\xbe\xc1\xf1\f\xaa\xa4Qe\x8d*\x1b\\\u03e0J\xd1\xe7\xf8\x06\x11\n\xa4\xb6Gi),.\xaeT\x06\xa5\xa2\"\x1eMz\xa4\x9a\x1c[\xcc\xd3p\u0558l+a\xa8\xba\u046a\xce\xfd\x11,kB\x9fl\xbe\x9e\x86\x89\x9d\x1c\xde\xf5\f#=\xdd\xf4\x1c\xd8\x05\xd9<\x13\x96\x9dC]\u01a1=\xa3c>\xee\xe4\xf5_(\xe7\xbe\x1fR*\x95\xf1}\x9b\xa6S[[\u01e3\x8f>\xc6W\xfe\xe5Zzz\xfb8\xf5\xc2+Y|\xf6\xbb)\x95}\xc2\xd0J[e\\4\x93\x81\x16\xa2\xf0a\xebA.\x1d\xf7\x84BN\n7O\xbb\x8f\uab24\xa6-CM\xde\x0e\x18U\xa9\xac\x88< \\\x89\xcc)\x9c\xac\xb4\xc5\u0704\xa0\xc3\xd8\xf1?\x81\x1bBm\xc64h\xa2\xca\u0714\x14\x1c\xa9\x81\x86\xda\x1a\xba\xf6o\xe4\xf8\xb1CH\xa9PJ\xa5,\x024\xc5B\x91\xfa\xfa\xfa\x17\x1e_\xbb\xf69\x80/\xfe\xc3\xdfs\xd9[\xde\u00b57|\x95\xd0\xf3\u0637o\x1f\xd7^{]\xd7\xfau\xeb~q\xf3\xf7\xbe\xf3p\x10\x84\xadm\xad\xad\vF\x87\x87\xc5\xf0\xf0\xb0\xad\x1d\xc8\xdf\xe8r\x1axe\x8e\xef\xd9\u0281\xa7\x1f\xa0o\xffN\xbc\xe1Ajjkhnk\xa7\xb6>\x8b+\x9d\xe8\xa0,*[a\xea\xd4n\x930\xad\xeaE9.\xf9\xfa\fRJ\xb6>\xf2\x00w\xfd\xf3\xdf\xf0\xdc/\u007f\x9c|/\xa7\xba\x90\x8bi\u04e6\xb1f\xcd\xc5\xcf|\xfa\u04df\xf9\xe3k\xae\xb9\xa6x\xf3\xcd\xff&V\xaf\xbe\xf0\x84[\xe7\xf7\x96&~\xff\x96\xefs\xe5\x15W\xc4\xd8\xef\xacw~\xf0O\xfe\xfe\x99\xc7\xecN(\xe2\xc2\x1a\xb5\xdbR*\xc2\xc0c\xfa\x82\xf3\x98\xbf\xf2R\x00\xc2 H\x82\x9b\xd3\xed\xa90'\x1a\xe0S\xa1\v\u007f#\x8a\x98,Y\xa9P\xd1\xd1\xd1\xc6JE\t\x1dq{\xa1OTtTO\xa1\xa5gm#\u054dg\x10%\x83\xa8\xa9hD\xab\x95\"&\xd1Q\xfb\x1aF|\x83\x1f\rD\xe8\xcag\u06db)\xf6\xb9\x88d\x83\x96\x17\xa8\x98\x19\xc5$\x95\xd4$#\xd2\xc9\xe4^<\x82\x1d\x9b9\xe8\x8aBE\x88\xb8s\x16\t\x14$\xa5\xb4_\a\xfbzXZX$\x01\x1d:\xd6\xfd\xa6HRK2\xc9$?\u057a6ZH\xc5(\x13\x195\xa5<\x9aSu@\x1b\x83\xe7\x01\x03CL\xecX\xc4\xd9\xef\xf98w_\xf7Q\xcaCC<q\xdb7\x994g1\xad\xef|7\xadY\xc1\x94\xac\x1fC\xb9'\xaf\xff\xc4\x15\x04\x01\xa5R\t\xdf\xf7\t\xb5\xa1\xae\xbe\x8e\x1d;vr\xe3\x8d_g\u07fe\xbd\xccY\xba\x9a\xa5\xe7\\\x89\x91Y\x82p\xb4b\xb3:\xf6\x14\x9a\xbaO\xac~\xf9\xb7\x8bFM\x1a\x02\x14\xe0\xe6\x1459\x99D\bV\x11I\xba\xb2\a8\xae@d\x1ck\x8d\xa1\x02d\x18\xcdFDr<%@iC\xe0[\x05\x97L\xddK\xe9\x1e!\xfe\xd5\xf7}\xea\x9b:\x980y\x0e\xfbv\xbfH\x18\x068\x8e\x8a\x8a\xa6\x95g\x8e\x8e\x8e288\xf0>\xe0[\x00;w\xec\x00\xe0\x13\u007f\xf91^z\xe9\x05\x96.]\x9e\xfc\\\xabW_\xf4\xbc1\xe6\x03?\xfc\xe1\x0f\xee\x986\xb5\xf3Ov\xec\xd8q\xe1K/md`p0z\xbd\x94\x95-kS%C\x1e\x1d\xe8g\xf3\xaf~\u02ae\xb5\xbfd\xca\xe2UL]v&\xd3V\x9c\u0174\xa5\xabh\x9a\xd0n\xa5\x8ba5\xc1\x19\xf3ZRY\x88h\xa4o\x80\xfd\xcfmd\xf3Cw\xb3\xe1\x9e\xdb\x18Ly\xaf(%\xad\u07cbMc\x13\xed\xed\xed\x9cz\xea\xaa\x1d\x1f\xfb\xd8G\xaf\x16B\xf4\x02,_\xbe\xfc7\xbeY\xbfw1\xff\xecg?\x9b\xfc\xf9/\xff\xe6\x93\x1fY\xff\xc8\x03\x13\x83r\xc9v\xb31;n\fR:\x84\xa1Gc\xeb\x14\x96\x9e\xf3~\xeaZ&S*\x0e\xa7\n9't\xe7I\x896\x95\x9d\x9bTa\xac\x02\x95S9\xd8\xd1N\x82T.B(\x8c\b\xd0\xc2&\x88W\xa6g\xac\xec\u0764\xc0\xe9\xf4\x10ME\xdc\x1f-\xfc\x10(j\u06dd\xbb\"\x19T\"\xe9\xba\x05F\x1a\xfc\x10\x86}M94Q\x87\x1d\x15\xe8\x88\x0f\xb0\xb0\x88\x88\xd2X\xe2\xc1'\x11'\u070d1\nK\xfdt&u\xbeHOC\xa7\xb8\x01i\xa2\xa2\x1du\xe2\x91O\x01B\xc5\x03I\xa6\xca@LK\x119\xe0\xc9dH\x84\xe8\x18\xac\x04H\xd7~M\x95z\xed\xb5\x16\x11\xa9e\x12#}\x1d\xdd\xc1\xb1\t\u007fh \xf0|\xc4h\x81\x05g^\xc6\xd1\x03[X\xfb\xbd/Q\x1c\xe8\xe5\x81o\xfc#\xed\x1dSi\xba\xf0tj\x95\xa6\xd5\r#\xa2\xec\xe4\xf5\x1fI\x10+\x13\x9e!A\x10\x92\xaf\xa9\xa1\xaf\xb7\x8f\xaf\u007f\xfd[\xac[\xf78\xe3'\xcd\u4d0b\xaf\xa6\xbeu*\u00e3#v87\x9eGO\xcf\xc0GJ\xa6\xf4)V0\xb6q:\x91\xa3\xd2\x00\x8e [\xef\xe08\xb2bn\xf5\x9b\x98Y*>/d$\xb21\x834\x01\fht\xd1@`UR\xd2\x11v\xbe\u00c8\xe4\xebUZ\x9e1\u007f3\x86\x92\x96L\x9e\xb1\x9c\x86\x97\x1e\xa2\xa7\xe7\b\x86\xac\u0154\xad/\x93)\x97\u02e2\xab\xab{U\x10x3\x1d'\xb3\xe7\xc2\v/N\x9e\xda\u04a5\xcby\xe8\xa1_s\xe1\x85k0\xc6\xf0\xd5\x1boPB\x88\"\xf0#c\xcc=\xd7_\u007f\xed%\x1d\x1d\x93\xfff\xf3\xe6\xad+\xf7\xec\xd9C\xa1X\xc0D\xba}m*\n\xb5\xb8\xe1,\x17\v\xec~\xe61v?\xf3\x18M\x13:\x980k\x01\x93\xe7-f\xd2\u0725\xb4M\x9bMMC3N&\x83#\x05\xbeW\xa68<\xc4\xc0\u04579\xb8\xe9Y\x0eo}\x91#;7\xd1\u007f\xe4P\xd5=,\x95\xaa*\xe4mm\xe3X\xb6l\xf9\x81\xcb.\xbb\xec\x1d\x9d\x9d\u04f7\x00\xdcr\xcb\xcd,]\xba\x82?x1\xff\xc2\x17>\xcf\xe7>\xf7y\x00\xee\xf8\u065do\xbb\xe6\v\xff\xe7O\xbb\x0e\x1f\x8aX\xe1\xc8GA\x9b\x84\x95\x96B1\uf5372m\xc1j|\xafT\x95\x9c=\xd6M\xac\xaaS\x16\x86\x8a\x9dd\x95\xa9C\xd5\xfa\xab\xea\u06a3\x91\xf5X/-\x8cFF\xc1\xafFT\x8c\xf3c\uf4b8\xb3\x16\xc6Nr\u0274\xd66.\ua041\x92\xb6\xea\x96\xd8\x18\v\x83\xb1\xc1\xe4\xe8\xc0P.\x85\x84e\x8d\x1b\xe5%&\x98v\u0509\x93\x1e\x9f\xaf8\x83Uu\xb7\xc9\"\x8e\xd4-\x15[\x83\x94z\xc4\xc8H~)+\xd3{\bl\u53a4\x9a\xe67l|\xa2\x8a\u07cad\x8b\x95\xe1%G\br\x11w \x85\xdd\x0f$\xc90\xaa\ud0a4$\bt4\x19K\x1c\x17i_\xcf\u8865\xc0+{\xe4\xca\x19\xcex\u04df\u04f3{\a[\x1f\xbf\x8b\ue75b\xf8\xf9\xbf\xfc\x1d\xed\x13\xbeG\xc3\xf2Nra\x89\x1a\xa5\xf1\xf5\u0262\xfd\xdb$\x88a\x18\xe2y>\x9e\x17\xe0\a\x01\xd9\\\x8e\xb2\xe7\xf3\x9d\xef\xdc\u011dw\xdeA\xbe\xb6\x81\x95\x17}\x80\u0273O\xa5P\x18%\f\xc2\xc8\x02\xda$\xa6r\t\xe7\x92\xccW\xd8\xe1\x16Q\xa5\x81H\xb9\xf3\x88\xd4L\x82\xb1\xfc\x89S\xa3\xc8\xd5\u02a8\xdb\x14c\uee0a>\xbcJuf\xacp@\xd6: 2\x18\x05\f[\x82\xc5\bc\xe7\xf7\xc4\x18W\xdc(\xfdb,\xd4\xe7\x05!\xed\x9dKhi\x9bB\xcf\xf1\xc3h\xadQ\x11\xdf#\xa5\x12\x85b\x91}\xfb\xf6\xca{\xef\xbdw%\xb0g\xecky\xe1\x85k\xd2Ma\xf8\xf0\xc3\x0f\xc9{\xee\xb9\xc7\x11B\x8c\x02?6\xc6<q\xe3\x8d7\xfc\xe5\x83\x0f>\xf4\xc1-[\xb6\x8c\xeb\xee\xeeft\xb4\x80R\x8eA\"\xaa%\u0155\xaeo\xa0\xfb0\x03\u0747\u067e\xee\xd7\xe4\xea\x1a\xa8mj!SSg\x9d\x1e#'X\xafT\xa484\xc0p\ufc6a\xf76&_\x85\xact\xe4Z\x87b\xe2\u0109\xac\\\xb9\xf2\xc0y\xe7\x9d{\xf9\xdb\xdf~\xc5\x16\x80\x1bn\xf8\xbf\xe2\x03\x1f\xf8\xd0o\xed{~\xafb\xfe\xd9\xcf\xfe=\x9f\xfb\xdc\xe71\u01a8\xcb\xdf~\xf9\xe7\x0e\x1e\xd8_\x8b1HiI\x99x\xba3f\x8a;f\x9f\xce\xf2\xf3\xaf\xc6\xcd\xd6Q*\xfe?\xf6\xde;\u0732\xab\xba\xf2\xfd\u0375\xf6>\xe7\xe6{+\xe7*\x85RD9`\x81$\x04B \x92\rn0m\f4B\xed6\xc16m\xbb\xbb\xdd\xee\xee\x87\xc1&\xf8\xb5\x03`c\x03\xc6 \x82\xdbX\xc6&H\xc6`L\v\t0 \x19$\x94\x91J\x15\xa5\n\xaa\\7\x9e\xb0\xf7^\xf3\xfd\xb1\xd6N\u772a\x92\x84x_\x9b\xea\xfa\xbe\v\xa5[7\x9c\xb0\xf7Xs\x8e9\xe6\x18\xd3 \xa6>\xe7D\xfb\\\xc4J@\xef-\xc6\xf5\xd8\xe3\xfe\x9a\xc3M\u0e6d\x05\xa2B\xff\xed\x01\xbd\xbe\xc8\xe3\x03\xa2\rX?\xc0\xc1\x80D\xc6\xf7\x85N\x83U\xae\xcfN\x94\xc4y\x8b\u05eeC;J\xdaq\xd0u\f\x17\xaa\x01\xed\xd1[Ulu\xb4\"&\xaa\x96FR\x1a^\xf9\r\u03bc\u0291\xca\xd6^\x0e\xe2\xb64+\xab\f\xa9\x8a\x90\t\xa9\x94\xf4\x15\xb5\xa5\x0f\xec\xf0\xab\xda\xd2\xf0[\xa3\x00\x91\x85!\xeb\u0346\n]O\xb1\xecPv\bF\x15q\xc6o\x1c\x86\xe9\xbd\x03\x92\xc4!\x89\ay\u007f0\x1a\xba\xd3\xf3LN\xad\xe6\x9a\u05fe\x83\xf9\xc3\xfb\xd9q\xff\xb7\xd9|\xc7\xd7\xf9\xebw\xfd'\xec;\xfe\x18s\xf6j\xcen\xce\u04f4\x92\x1b\xf4\xfd\xdf?5j%\xa3\xd3\xe9\x86\xc5 o\xb3\x9a$)7\xdc\xf0)>\xf2\x91\x8f\xd2M2\x9e\xfd\xfc\u007f\xc7Y?\xf5r2'dIRvn.\x18;Uc\f%l@\xda(t\xcez\x94\xe1Hy\xdbe\x06h\n\xcd\u0248\xa8a\x8aJ\xba\x8fZ\xae\xeaj+\xffh\x8c\xc1X\x83\x0ey\xf7M\xc4\xc1\xacCS\x83D\x95\x03!-\xfd\x8fLAe\xe6A\x18~\xc8\x19\x8d-c\xdd)\xe7\xf1\xe8\xb6{H\x92\xa4\xb0\xa0\x15\xe3\x03\x8fgg\xe7\xe2\xef~\xf7\xf63\x9e\xc8k{\xf5\xd5\xcfw@\xf7\xc3\x1f\xfe3y\xcb[~YE\xe41\xe0\xbft:\xad\x0f\xbe\xf3\x9d\xbf\xf3k\xb7\xdf~\xfb/n\u0672u|\u03de\u0752e\xae\xdbh4bU\x95,\xcbJm|\xa5\x93\ah\xcf\xcd\u041e\x9b9\xee\xef6\xf9\xba\u007f\xf8\u007fk\f\xaa\x9a\x1ac\xa2\xd3O?\x8d\xcb/\xbf\xfc\xa1\u05fd\xeeu\xaf\xba\xe2\x8a+\x1f\x00\xf8\xe5_~\xab\xbc\xedm\xbf~\xcc\x06\xf6)\x81\xf9g?{#\xaf~\xf5\xcf=\x05\xff\xf0\x00\x00 \x00IDAT\x17\x15\xeb\u007f\xff\xef\xbf\xf5\xae\xfb\xef\u007f\xe0\xe2#\x87\x0e\xf9\x9a\xd1\xd6WC\xb24alj\x05\xe7_\xf9:\x96\xad=\x9bvk\u059f\xe9Zsw\xaa\xd4\xdeR\xd7\xe4I\x8f\x1a\xb5\xee\xe4\u0783\xe0R\xf3\xe9\xeeUhK\x18\x94\x92\x0fO(\xb7u\xb4R\xfbK\x84\x97QY\x81X\x8a\xe9\x9ev\x80T\x90\x18\x9c\n\x92x\xdf\u7d2b\xb8\x14\xe2\xa0\x1c)x\xff\x8a\xefJ-\xbe\xae\"\x0e\xad\xaa\x05j\xa6G\xf9\u07f5^\x9dKE\x8d\xa3\xbd\x0ex\xda\xeb\x8aW\xbfU\x9d\b\x1a\xf9OD\x91!\x1a3\xd8E\rl\xecA\xdcJ]\x9aFO\ua560X\x04\u027c$\xcbW\xee\xe1\x86K\x95d.\xf5V\xab\x99w\xa1\u031c2?=\xc3\xca\xf5\xe7\xf1\x82\u05fe\x93\x9b?\xf4\xab\xec{\xec!\xee\xfe\xa7\xbf\xc3D\u00d8\xff\xf1\xfb\x8c\x9c\xb3\x92\x8d2\xef=\xe7\xff/~\x17\u007f\x92$\xa5\xd3\xe9\xd2MR\xd2,%n4\xc82\xc7g>\xf37\xfc\xe9\x9f~\x88\xb9\xf9Y.\xbc\xfcg\xb9\xf0\xaa\xd7a\xa3a\x16\xe6\xe7Kh\xd6z\x17V\\?\xb9\xa2\xc9D=\xb3\xa6\xfe2JC\xd7\xe6\"C<\x1a\xd1\x18\u036d\x9a\xf5\xb8\xb2\x1b\xa1\xec\xa6ErE[\xe2\x1b\xc7q\xef\x85d\xd5\xf9\xeb\xcd:\\\xaad\x9a\x15\u02b2\xc2>BJ\x95[FB\xd7\u016c>\xe5bFn\xbf\x89C\a\xf7\xe2\xe2\x18\x1b\xf9\xc8\xc94M1\xc6\xd0\xedv_\x04\xfc.\xc0\xe6\u035b\u0638\xf1\xf4c>\u0737\xbc\xe5\x97U+\xf1\x8e\xcd\xe6\xf0\xa3\xc0o|\xf5\xab_\xf9\xc7/|\xe1\v\xbf\xf3\xe0\x83?\xbcl\u01ce\x1d\x8d\x9d;w\xa1\xaai\x1c\xc7VUE\x8b<\xcez,\x9e\xcb\xdb\xd8*u\x94\x0fw\x03x\x17\x15\xb9\xf7t\xd14M\xdd\xc4\xc4Dt\xca)\xa7p\xed\xb5/\xfc\xa7w\xbe\xf3\x1d\xff%\x8a\x9a\x0f\x00\xfc\xfa\xaf\xff\x9a\xbc\xff\xfd\x1f8\xee\xad\xf1\x94t\xe6\xaf~\xf5\xcf\xf3\xc1\x0f\xfe\xb1\x05\xb8\xed\xb6\xafo\xbc\xed\xb6o\xfe\u008e\x1d;\x00\x12k}]W<Q\xa7\x881\x9cv\xc1\x8b9\xfd\xe2\x9f!\t\xc1\xc9%4\x94\x95\xb6\xe6\xe9\xd4\xf4xn\x86\x96O\xab\x8b\f\bN\x85L\ri\xfe\xe1\f\xa9Z\x12\x8dH4\"U\x1b>\xf2\u007f\xb7t\x9d!q\x11\xa9\xb3\xa4\x1a\x91jLFL&M2\x19\"3C\xa4v\x88\xcc\f\xe3\xec0\x1a\r\x03C\x18mbhb\xb5I\x94\u0158,\xc6h\x03#MT\x9b8i\x10\xd9!\xa2x\b\x1b7\x89\xa2!\xa2\xa8\xe9?g\x87\x88l\x83\xd84\x88\x8cW\x93\u0628A\x145\x88L\x93H\xe2\xfa\a\x11\x111\x111\x96\b+\x91W\xa4`\v};=\x1dM\xf5P\xa2\x92$$\xc5\x10\x95B\xd9`RG\x94)C\xaa4Rh\xa04\x9b\xc6W]\x91\xff\x90\xd8x)\xe6\x90A\x86\xa4\xf8\xbb\x19\xb2\xd8a\u007fs\u01e3\x16\x19\xb20d`\xd8\x12OD\x98\x89\x18\x9a\x96\xb0}L\x16\xfc\xa9\x17ff9\xe5\u072b\xb9\xfa\u07fe\x9d\xc9%k\x00\xb8\xeb\xcb\u007f\xc9_\xbd\xe7\xbf\xf1\xad\a\xf7\xb1\u06cd\xfa\x83\xe2\x04/\xcd\xf3C\xb3\x9b\xa4\xb4\xda\x1d_\x91\xa7)\x8dF\x13k-7\xde\xf8\xb7\xfc\xd1\xfb>\xc0\xfe\x03\xfb8\xfb\xa2kx\xd6K\xde\xc2\xd0\xf8R\xda\v\xf3\xfe^s=\x90\xac\xd5\x05\x1c\x0f\xaa\xc6D=\x86W\xd2W\xf8\x14\xdeXV0MCs\xccb\x1b&tj\xdaS0\xd5|i\x8bR\xa3J\x8b\x16\xdar\r\x8a\x8e\u0448xQL<j\x89\"!\x8e\r6\xaa\xbb5*\xf5\xbd\v\x17\xac\x94'W\x9d\xc1\xa2\xa5k|\xe4\x9a+}\xccm\x14177\u01d6\xcd[\xa6Tu\x1c8.\x90W)\x0f\x80O|\xe2\xe3\xc5\u7bbd\xf6\xc5\xff\xf4\xe1\x0f\xff\xf9\xf3\xdf\xf0\x867\xfc\xf2e\x97]\xf6\xf7\xd7\\\xf3|\xce:\xeb\xccH\u0108s\xae\x16\xe9X\xfd\x88\xac\x8f\xce3Q\xe4\x95)q\x8c\x8d\xa2\xb0\xcdi\u02ea\xbc\xa4\x99e\xed\u06b5\xf6\x9ak\xaeI\u007f\xf1\x17\xaf\u007f\xf7\xbb\xdf\xfd\xdek\xa3\xa8y/\xc0_\xfe\u59df\x10\x90?\xe5\xca\xfc\x96[\xbef\x9e\xff\xfc\x17d\xaa*\xd7]\xf7\x86\xff\xbcy\xf3\x96\rI\xb7\x8b\xb16\x92@\xaf\x14\xa7\x94KY\xb9\xe1<.\xb9\xfa?0<:\u51deG\xa5Gr\xefq)&\x91%\xaf[\xd1X\x8bbqD\xe2\x88\xc5\xd10)V2b\x93\x11\x89\u00c8\xd6\x02\x9bJ&0\x18\u072b)\x04-N\reR\xa1\x14\x87\x841\x06\x1b\x1b2\x1b\x87\x03#\"S\x13\xbe\x1e\xc8\xfc\xaa|\x92:\xba\xce\xeb\xcbM\xa8\"L\xe5\xa7y\x99\xa3\x06\u04efj\xc5\x1c\x1e\x95\xabf\u06d6\xb7\xc0\xc0-!\xa9\u01fdIM%\xd3\x1fP5pmT<\xb8G\x0eL\n\xa6\xed\x90\xf9\f\x86-\xb5]\xfbJ[\x91\xbfv\u0554qS\x1dQW\xd8$\xdb\x14\u04a6\xf19\x92F\xc8DqN\xe8v:\xa0p\xcee\xaf\xa4\xbb0\xc7W>\xf5_i\xcd\x1f\xe1\xce/\u007f\x12#\x86\xf8\x1d\xef\xe5\xa5\x17\xac`\x95i\xa3\xa9\u00dd\x90@\ue2e0\x1c\u023b\xdd\x04\xe7\x1c\u0366\xcf\xc0\xfc\xf4\xa7\xff\x8a?\xf8\x83\xf7\xb1o\xdf\xe3\x9c~\u0795\\\xfe\xd3ocb\xd9\x06\xda\xf3\xb3!\u078f~\x806\xa1\x1f\r\x1b\xd3&X`\xe4r\x01A\x06z\x1a\xa9\x88\x1f\x90GBc\xd8\xf8\x83\xdb\x1cM\xf5R\xad\xc2{\xaf\xc6RY\x95\xdblHX\xae\x91\xa1\xc8\xdf\xdb\xf8`\x18\x13y\x03=\xa7\xa5*R+j\x10\x102\x97\x11\x0fO\xb2\xfa\xe4\xf3xt\xeb\xbddY\xf09\x0f\x1d\xc0\xec\xdc\x1c\x87\x8e\x1c^\xf6\xe0\x83\xf7=\v\xf8\xa7'\xfb\x1e\xbc\xf1\x8d\xff\x9eO|\xe2\xe3l\u07fe\x83\xdf\xf9\x9d\xdfED\x16\x80\x0f\x01\x1f\xfa\xc2\x17\xfe\xee\xd5_\xfb\xda-W\x9ey\xe6\x99/\u07bf\u007f\xff\xa9\x0f?\xbc\x89\xd9\xd9Y\xb2,+\ue0fc\xc2/\xabq)l\xa7\x15\xaf\n\xf3n\x8f\x868\x8eY\xb6l\x19g\x9cq\x06\xeb\u05ef\xbb\xf95\xaf\xf9\xf9\x0f\\u\xd5\xf3n\xcd\x1f\xcb?\xfe\xe3\x97y\u044b^\xf2\x84\x9b\xd5'\r\xe6w\xdf}\x17\xef~\xf7\xbb-\xe0n\xb8\xe1c/\u07b1c\u01db\xf6\xef\u07c7\x18\xa3\"\xf5\xba\xcae\x19C\xa3S\\|\xf5/\xb2\xe2\xa4\vH:\v~S\xaag\u02cb\xca\"\x8d\xf6L^\xacu4\xa3\x94\x86\xcd\x18\x96\x84\x86\xa4\fI\x97a\xe92b\x13\x9a\x92\xd00)q\x0e\xe6&+9\xac\x82\xb7\xf6\v*V|[\xe7\x804\x04\x19f\xea\u056fF\\q\x11:\x04U\x03\x91\xc5\x19K7\xb3$YL\xd7\u0174\\L[c:\x123\xa7\x11F\x84Fp\x8bS'\xb8\xcc\xe0\x9c\xff\xb9\x19\xa6\x18h\xf6\xc4\x1e\xd6\x14]\xa6\xea'\xaeT\x92\xed\xeb\x19$\xd5\xd7E\n(\u05c1\xf3\x02\xa9\xef\x9a\xd1\x176\x98\x17X\xa9C\xe6\x1dL(\f\x1b\xfa\xcd\xcd{\x84\xa1R\x0e\xba\x8cJ_a\x16\x19C\xbbaH[Bd\xbd\x8aG\x9d7-\xeb\xb6\xdaH\n\x17^\xf9z\xd2\xd6<_\xfb\xec\xef\u041a\x9f\xe6{\xffp\x03Y\xda!z\xe7\xef\xf1\xaa\xcb\xd61f\xbb$\xdd\xf4\x84\x92,V\x81\xbc\x9d\x03\xb9*###\xb4Zm>\xfd\xe9\xbf\xe4\x83\x1f\xfc\x10\xfb\xf6\xede\xe39\xcf\xe2\xaaW\xfc'\x96\xac:\x9d\xf6\xfc\\!\xed\u04f0}\x9c\x83_!\xd5\x15?\x18\x97P1J\xbf\\\xa0\xf6~kP99+D\rCc\xd4z7\xce\xda\xf5U\u05dd)\x83\xae\xd9z%\xa1H\xcfj\x82b\x9a\x06\x9d\x8cQ#\x18u\x98Lp\xa9\xef\xe8\xb3\xf0\x18sS-\x05\xd24\xa11<\u02aa\xd3/f\xe8\u06dfgnn\x16\xa7.\xb8\n\nY\x9a17;?~\xdbm\xdfX\xffT\u07cb7\xbe\xf1\xdf\xd7\xfe\xfb\x93\x9f\xbcA\xae\xbb\xeez\xfd\u065f}\xd5g\x81\xcf\xee\u0739\xfd\xec\x1bn\xf8\xe4E\xe7\x9dw\xfe/\xec\u07bd\xeb\xca\xc7\x1e\xdb9\xb6g\xcfnZ\xad6Y\x96\x9bx\xd5\r\x05}\xd5ni4b\xc6\xc6F9\xe5\x94SY\xbcx\xc9c\x13\x13\xe37?\xebY\xcf\xfa\xf6u\u05fd\xf1\x8bA]\xc3_\xff\xf5g\u456f\xfcY\xfd\xe2\x17ozR\x8f\xfbI\x83\xf9[\xdf\xfa+\xf2\x9d\xef|'QU\xfb3?\xf3\xd3o\xbc\xf7\xde\xfb\x82\xca\u0358\xdcs\xa5\xfa\x86\x9eu\xf1\xcfp\xe6\xc5?\x83s)i\x9a\x14\xff\x96\x05g5*\xa0dEi\u0614!\xd3e,\xea0\x16\xb7\x19\x8a\x12\x86\xe2\x84f\x94\x10\x9b\x8c\xa6&44#\x96\xd4W\xe1\x15@s\xf9\v\x17\x92L\xf2\u007f1E\r\xa2\x95\\g\xc1\xa9\t\x16\x95a\x80n\xb4\xc2\x15\xe6\xaelJf\xfd\xae|\x86\r\U0010d84b\xa1E\x8c\x03\xac\x962=\xd4\xcb\xf7<\xa5\x13\xd1\xca\x1a\xb4\xb3\x98D-i\xc8\xd2L\xd4\xd0\xcd,\x1d\x17\xd3uQi\xf4U9\x80\xf2}Q#\xa5\x11W\u0756@\x8f\xcaWr\f-~\x11\xc2\xe1r]\xa1@'C\u06e1:\xef\xfbV\xad5N\"\xa5}\xa3\x88\x86%\t-\x00$\xb2~\x13\xb0\x13\xf2V%\x8fn\xf1V\xf6t:m\xd4)\x17=\uf348\x8d\xb9\xe5o\xdf\xc5\xdc\xf4>\xee\xfa\xea_\xf1\xfbs\u04d8\xdf{\x1f\xff\xf6\xca\xd3h6\"\u04a4M\xe6N\x1c \xeftSZ\xed6\xddn\x02x\xbf\x95\xfd\xfb\xf7\xf3\x17\u007f\xf1q>\xf1\x89O23}\x98\xd3\u03bd\x9c\xe7\xbc\xe27X\xbe\xee\x19\xb4\u06c1ZQ)\u02e3\xea\x06X^\x1d\xe6\x89Q6\xea3\x88\xeb\xab\x02BI\xec\x8c@$D\u00c6h\xccz\x95\xabj\x0f\x0f_W\x95Uw\x1c{y\xf8\xde\rfo#\x10$\x89\rA',\x96\x98\x86\x80\xeb*\x9a*Y\xe6z\xe8y\xc1\x89\x92\u0186\x89u\x1bY\xbcj\x1d\xb3\x0f\xdd\xe7\x15&\x01\xcc\xd34\xc5X\u04dc\x99\x99\xbd\x1c\xf8\u060f\xfa\xde|\xf4\xa3\x1f\xe1\xba\xeb\xae\xd7/}\xe9f\u067au\xab}\xdb\xdb~-[\xbb\xf6\xa4\a\x81\aU\xf5+7\xdc\xf0\xf1\xa5\xd3\xd3\xd3\xcf\u0777o\u07f3\xf6\xee\xdd{F\x9a\xa6\x97\xee\u0673\xc7\xce\xcd\xcdfI\xe2+\xf6f\xb3!\xabV\xad2\u02d6-\xdf\x1fE\xf6\xd6\x15+V\xdc\xdbj\xb5o\xbb\xf6\xda\x17\x1e\xb8\xea\xaa\xe7=\xfc\u044f\xfe\x05o|\xe3\xf5\xbc\xeb]\xbf\x1b\xbd\xf5\xado\u0396,Y\xfe\x94\u0298'\x05\xe6\u007f\xf5W\u007f\xc9k_\xfbz\x05x\xff\xfb\xdf\xf7+[\xb7n}\u0561C\x87\x101&\x0f0\xc8\u06dd,\xed\xb0b\xfdy\\\xf0\xdc724\xba\x88n\xa7\x85SC\xa6~\xa8bP\x1a&e\xd8&LF\v\x8cGm\u01a2\x0e\xc36a\xc8vi\xda\x04k\x1d\x91u\xc46\r^\xc9B\x949\xbf(\xa3y\xe8jYaD\x92o\\j\xe1\x87.\x95\xaaV+2-\xff\xb5\xfe\x00)\x82\xe6*b\x13'%\xf4[\xc90Vi\x900\f\xa5\x99\xb7U\x0f\xb6A\xe1\x81+I\xbe\xdc0,\u0448\xc4Y25t\u0552:K\xa2\x96\xc4YZY\x83\xb9\xb4\xc9|6D;\x8b\xe9d\x11\xed,\xa6\xed<\xf8g\xcex\x85Gx\xbd$\x1cL\xf5\x84\xf3\xa3\u0718\xb9t\xac\"\ub522\x82\xf2\xca\x112\xf5+\u0459\xc2B\x06\x13.\x80o\x0f\x87Z\x1d\xa6\xa9\x1fJ\x99\x1a\u0397z4\x01\x86\x87,:bI\xdb\xce\xfbT4(\xaas\x14\xba\x9d\x16\x99\x89\xb9\xe09\xff\x8e\xb81\u00ad\x9f{7\a\xf7n\xe3\x87\xdf\xfe\x12\xefz\xdb\f\x87\xfe\xeb\xefr\u077f\xb9\x8a\xa9\xc6\b\x92.\x90\xb9\xe3\xcf\xdc\xfe5\x03\xb9s\x8eN\xa7\uba55$\xa3\u0448i4\x1b\xfc\xf0\x87\x0f\xf1\xa1?\xfb\b_\xfc\xe2\x17I\x92.\xa7_p5W\xbe\xfc\xd7X\xbe\xf6,Z\xf3sE`H>0\xec\xe5\xbf\xf3hF\x9f\x16\x15\xd5b\x1a\xebB\x83\xf2\xfbT\x84\u0302\xb3\x105\x84x\xccb\x1af \xaf\x9e\u007f\x97\xab\xca\x19\xe9\x8dB+7\xcb\nc6\xa9\u020d\xf3\an\x053f\xbd\xc4u.C\x13\x87I\xfd\x02\x8es\x81\x8e\x14\x1f0\xddM\x13\u2265\xac9\xf3Bv<|\x1fY\x96\x95\x1d\x87\x88v:]\u067am\xdb\xe8\xd3\xf1\xfe\xfc\xd2/\xbd\xb9x\xb9\xde\xf6\xb6_K\xb7l\xd9$\xdf\xfc\xe6\xb7\xe2\x1bo\xfc\xac\x84%\x9e\x83\xc0\u00ea\xfa\xc9\xe9\xe9\x83c\xb7\xdcr\xeb\xe4\x9dw\xde%{\xf7\xee\xd5V\xab\x8ds\x8e\xf1\xf1Q\xb9\xe0\x82\v\xe5\xf2\u02df\xdd9\xff\xfc\v\x0f\x06\xea\x86\xf7\xbe\xf7\xf7\x00x\xd9\xcb^\x1a\xfd\xc6o\xfc\xba\xbb\xfa\xeak\u04b7\xbf\xfd\xb7\x9f\xfa\xb5\xf4T\xbe\xe9;\xdf\xf9\xf6\xea\xf7\xbc\xe7=\xdf\xfd\xfa\xd7o]\xdfj\xb5\x82\xbfGXo\n\v(\xcd\xe11\x9e\xf7s\xef\xe0\x82\xe7^O\xbb\xe5\xe9\x15c\x1cC\xa6\xcbx\xd4f2n1\x15\xcf3\x1e\xb7\x18\xb3\x1d\x9a&\xc5\x18_\x89\x1aQR1\x18\xeb\u0273\xcc\x18\xc4(F\x15\x9b\xf9\xe0X_X\x8a\aS\xa9\f^\xd4ar}uu\xa8\xaau\xa6\u064a\xab\x0f\x12\xf3!k\xc9\x0ec$\x0f}\xf6\x15\xba\xcd\xd70\x8b\xc8(\xadK\xb2\xd4oFV\x01\xbd\x9e}(\xc1\xb6\xd6\xf3\xf2\x8a\x1f\xe0v3K\xe2\":.\xa2\xed\x1a,\xa413\xe9\b\x87\x92Q\x8e$\u00f4\xb2\x06Y\x18\xe4\xaa\xfaX-\x11\x87\xad\xcc\x06t\xc0[YU\xee\xf7\xd6\xf4\xf9\xf3\x1a\x8a\rQ$\u0218\u016ci\u0090\xad\u0336\xf4(\x17J\xdd'\xa7\\\xf5\x0f\xfa \x11\xba\xad\x8c\u03be.\xe9B\xe6_\xd1\x04\xa4\xebB\x86\xa8?\x15\xa2\xb8\xc9Ps\x84\xed\x0f\xde\u02ad\x9f\u007f\x0f\xdb~\xe8#\xb2&W\x9c\xca/\\\xff\x1b\xbc\xe9\xba\xd7q\xe6\x86\t\u0439\x90\x1a#?A \xee/\x93$H\x0f\xfd\x8a\xbe\xf7$\a\xf8\xdf\xff\xfb\x16\xfe\xf4O?\xcc\x1dw\xdcA\xa39\u0139\x97\xbd\x8c\xcb^\xf4&\x16\xaf\xdaH{a\x864I\xcb\xf9\x8cJ\xb0G\xf6T_\xfe\x1e\xf8\u0160\xc8\xe7\xb7\u06a8\xef=T\xd5>\xabB\x17\x19\xd2\xd8+\xb8\x86'\"\x86\x975\xb09\xc52\xb0\x1b\xec\x8d%\xee'\xf7\xc2\xee>I\xbbE\x9a$a#\xdbk\xb6\x9d\xba\xc2\xd8G\x83\x8d\xb3.d\xb8\xd9\f\xd7\xf5\xd6\xd1Y\x9a\x87\x94\x85\x1fl-c\xcb\xc7\xd9\xf3\xd0W\xb9\xe9\x0f\xff3\xdd4\xa3\xd1h\xfa\x19M\x96\xb1x\xf1\x14\xe7\x9fw\xce?~\xe9\xef\xbf\xf4b\x80Gwlc\xfd\x86\x93\u007f,\xef\xe3\xddw\xdf)\x17\\p\xf1\x93.5>\xf8\xc1?\x91\v/<_\xaf\xb8\u2aa7\xed\xb1<\xe1\xca\xfc\x86\x1b>&\xd7_\xff\x8b\n\xf0\xb9\xcf}\xee?\xdfs\u03fd\xeb[\xadV\xb12\xae\x85\x1e\xd9\xe0\\\xc2I\u7f00\x93/x9I\xeah\xd2fj\xa8\xcd\xe2\xc6,K\x9b3LF-\x86l\x12\x86\x95.\x00k\xa9\xadQ#\x88U\xc4\xf8\xab\xd5\xe2\xbc;Cn\x8e\xdf\x13\u03e3\x02\x11\xce{P8oA\xa99\x8dS\xc4\xc7\xf5\xd8\xceV\xeb\x87\xf0W\x93[\u0245\x03B\xc2\u007f\x8bh\x1f4\xd6d\x81\x15\xfd\x9eV\xdd\xd2\\8\u07e8\xef\t\xe5\x81\u0222\x10\x89\xa3iRT\u06e1s\xf0|{\xa2\x1e\xd8\xe7\x92&3\xe90s\xe9\x10sY\x93\xb9\xb4I;k\xd0v1\x9d,&US\x1c\x80\xa6\x10\x97k\x11\x1a\xa1\x03\xd9K\x8a!T>\x84%QoY0\x94'\xa9K\xb1*\xcd1H\x1bz+\xf4\x9c2k\bQ\xd3\xe0:\x19i\nQ\xee@ \xe5\xe9\x99$\x1dP\xe5\xa4s\x9e\xcf\u02e6\x96s\xdb\xe7\xdf\xcb}\xdf\xfd\x1c\xd3{\xb7\xf0\x91?\xf8M\xb6\xfc\xf0>\xde\xfc\x1f~\x85\u02dfy6\xe3\xa3]\xa2(\xf5\xaf\u047f\xf2*]\x80,s\x85\x85m\xa7\xdbE\xc4099\u026e\u077b\xf9\xdc\xe7>\xcf'>\xf1)vl\xdb\xc6\u0112\x95\\z\xf5/p\xe1U\xbf@sd1\v\xb3Gp\x9a\x15\xf5o\xd9\fJmP\x98\x87q\x8b\x8d\x91\x82'\x1f !\xac\x8cR\xd4\b\xce\xfa\xfd\x83F\xc3\xd0\x18\xb3\u0626\xfch@N)f\x10c\xbc\xfcT\f\x98P\u03c7\xa7\xa2&l\x83\n\u0208/\b\x99\xf1\xdeDe&y\u0634\xce2\\\n\xe3+Nb\u046a\xb5\xec\u067e\x05\xd5\x06\x1a\xf6Ifff\x19\x19\x19=OU\x9f+\"\xb7\xed\u06ff\xff\xc7\x16r\xf5T\x80\x1c\xe0W\u007f\xf5mO\xfb\xe3yB`\xfe\x8ew\xbc\x9d\x1c\u023f\xf1\x8d[/|\xf7\xbb\xdf\xf3\xeb\xfb\x0f\x1c\xf0\xb8k\xad\x94\v\xbfB\x9a&,Zy:\x17_\xf5Z6,\x1fa4\xd9\u02b2\xb1y\x964f\x19\xb1\x1db\x93\x16tAUl/a\xffW\x83\x0e\xdaX-\x92uL\xa5as\x01\xd0\x05%B1\xeaC[\xadj\xa1V\x91Z\xb1\xec\xe5q`\u020aX\xaa\xd2\tP\x04\xacq~\x81\x06\x10\xe3j\x1b\xa5\xf5\x14\x94\x92{\xd7\xdeK\xba\a\xd9D\xf0{\xf0\xa6\xde\xd2JE>\xe6zt\x04&\x1cj\x16\xa1I\xc68m\x966\xfc\xefs\b\xa9\x8bh\xb9\x98\xf9\xb4\xc9L2\u0311d\x84\x83\xddQ\xa6\x93\x11f\xd2a\xba\xce\x06.\u0495@-=\x83\xd7\xea^Gx-\x9c\x06\xd7\xc5N\x99cU\x8clU\x06\xc8\u0434\xf4\xb3\xd1j\xd6G\xa9\u007f\x17\x03v\xc8`\xe6\x8dW\xa78\x17|B\xea`\x90\xa6]\x16\xe6\xa6Y\xb2\xfa\x19\xbc\xe8\xf5\u007f\xc8\u0112\xb5|\xff\x96\x8f\xd3i\xcd\xf1O_\xfc\b\x9b\x1f\xfa\x01\xaf\u007f\xfd\xaf\xf0\xaaW\xbc\x82\x8d\xa7\x8e\x11\xd9\x0e\xe0\x82g\u01bf\xcej\xbc\x9b\xa6t:\t\u076e7\xc3\x1a\x19\x1e%u)\xb7\xde\xf6\r>\xf5\xa9Os\xeb\u05ff\xce\xdc\xfc<\xabO>\x9b\x9f\xba\xf6\xdfs\xe6%/\x05c\x99_\x98\r\x87\xb4)\xfc{\xaa\U000196e0i\xd0u[\x1bC\u0413\xf7\x8f\xb4\xc3\x15]\xd9\x15r\xd6\a\x9a\x98Hh\x8cx\xae\\\xe4h\xbar\xa9\u02e6D\x06\xb6\xfcZQn\xe5v\xc99\xe5Y\xec^h\xe9\xcb+\xaa\x9e\xff\x1e\x01\xe3\xac\x1f\xce\xe7a(\xb9U\x84\x13\xba\xf3\x1d\xa2\xc6\x14K\u059d\u02ae-\x9bp\xea0j\x8a\x8e\xe3\xf1=\x8f\xa7\xb7|\xfd\x96\x04\u0ef7\xdfqB\f\u045f\x10\x98\xaf\\\xb9\xb2\xf8\xfb\x8d7\xfe\xcd\xff\xb8\xe7\xde\xfb\xe8\xb4\xdb\x18\x13\x891\xd6k=\xf1-N\xdc\x1c\xe6\xaa+\xae\u19df\xb9\x8e){?\xa3#s4M\xeaU&5\x11S\xd8N\xab,Gj\x0e\x80\xd6S\ty\xa5\xaa\x05\xf0\xf9*\xbdAF\xa4Y\xc1K\xe7\xa1\xce5hT\u03e7;\xf1\xc6R\xb9g\x9b\xa0\x18\xeb)\x1fc\xb4\xcf\x1d\xdfU\x00\\\x06\xd0\f2\x803\xa4F\xd8\xf4\xaf\xcfk\xb5\x1f\xc8}\xb0r\xbe>\x1f\xd4\xf6\x82l\xe0\xfb%\f\x1a\xad8\x1a\x921\x12uX\x1c\u03e1\u00de\xa2ie1G\x92\x11\x0et\xc69\xd4\x1d\xe5p2\xcaL2\u0302k\x92\xfa\x8d$\xff3\xf2\xdf]\t\xf3\xcc\xfd\xd4s\xcb\x01M\xfc\xf2\x86D\xd5\v\xa3W-S\xbe\x83Z\x1b\x8c\xf6v,\x826\xbdf]\xbb>\xcb\u0569\xab\xf8h\x97\xff\x9fe)\vsG\x18\x1a[\xc6U\xaf\xfc\u007fX\xb1\xfe\\\xbe\xfb\xe5?a\xcf\xf6{\xd9\xfa\xd0\x1d\xfc\xbf\xefy\x98\x1f\xdc\xf9\x1d\xae{\u00db\xb8\xe2\xd9\xe735\tQ\xdc\xc5e\u067f\x1aP/\xd4*\xdd\xc4\aF8%\x8ab\xe2\x86a\u04e6M|\xfe\xf3_\u099bnb\xeb\x96\xcd4\x1aM\u03be\xe8\x1a\x9e\xf9\xa2_d\xf5)\x97\xd0I\xbat\xdb\v!\u4efa8\x96'?yE\x94\xe6\xaa\x151\x9e\xfa\xb4qE\xef]\x16'\x94S\xa2R\x8d\x12xr\f4\x86\f\xf1D\xe4\xe7'z\xac\x81{\x8f4\xaa\xe7_\xb4RIh\uf234\x92\xf9\xe9\xfa\x0e\b\xdf6\x9aQ\xeb\xbfo&-\xae\xad\x00\xd7d\x9d.\xcd\xc6\x18\xe3\x8b\xd7x\u05be\x90\x05B\x96e\xb4;\xed\xc9\xfb\xee\xb9g)\xc0\xc2\xfc\xfc\t\x11?\xfb\x84\xc0\xfc-o\xf9e\x00>\xf5\xa9\x1b\x9e\xfd\x81\x0f\xfc\u0275\xd3G\x8e\x14Uy\u0357A3\xce\u06b8\x91\u05ff\xe8RN\x9f\xd8K\xb7\xbd\x00\xa2\x85\xf6\x1b\xb4\x00\xe5\xfc\xe22\x81\u05b0\xe2<?nK\xf5\xb4CH\x89\xfc\xe0\x93\x94X\x1d\x11\x19\u05b9P\xadKm\u04ed\xf8\xbe\xe2\x1a3E5\xefr\xb3\a\xa3\xbe\xfa69p+\xbdK8ZQn\xf4zO\u0531\u007f\xf0\x05^\r>.\x979Cwa*\xdby\xf9\x9c2\x1f\xd4\x16^'Z?-\x02m\xa4\xd4Z\x00\xac8\u01a3\x0e\x13Q\x9b\xb5\u00c7\u9e88\u0664\xc9t2\xc2\xc1d\x9c\xc3\xc9(\xd3\xc90\u04e9\xe7\xddS5\x98bh\xac^d\x92{\xe78\xc5%\x0e\xd7\xcdhD\xb6\xbc\xf1e\xf0-\x9c\x1bj\u55a6\xd5\n>\xb7\xa0qF\xc8b?0\xd5pP\x9az\xc0^e\x96\xa1\xb4\x17f\xb0\x8d\x06\xe7\\\xf1\x1a\x96o8\x9b\xef}\xf5\xc3<\xf0\xdd/\xd0^8\xc2\u035f\xff0\x0f>p\a?\xfd\u04ef\xe5U\xaf\xfc\xb7<\xe3\xec5L\x8cCd\x13\xd24)d\x8c\xff'\x02{1\xe4\xec\xa6d\x99\xa3\xd1h\x80\b\xbbw\xed\xe6K\xff\xf0e\xfe\xee\xef\xfe\x8e\xbb\u007f\xf0\x03\x92\xa4\u02d2\xe5k9\u7c97s\ue56fat\xf1:\xe6\x17\xe6H\xb3^gQ-=u\xb4\xea\xc1\x13\xb6\vm\xe4\xf5\xe4\x95\xd9F\xff\x9et\xa5\xbf\x14p\xd6x)b$4\xc6\"\xec\xb0=\x06\xbdBeVt\xb4\xc9\xdb\xd1.\x9c:\xfc\xe7y\xb9\xb9\xec\xd5I\xe9\x0e\x8a\x11\u0730A[\x02]\x17\x16\x87$\u0200S\";\xc5\xf8\xc4j\xaf3\x0f[\x98\xde\xee \u0262\xc8N\xce\xce\xcd]\x04\xdct\xe8\u0421\x13b\x15\xed\xb8`\xfe\xe8\xa3[e\xfd\xfaSTU\u01ef\xbf\xfe\xba\xdf\u06f1\xe3\u0471N\xa7\x83\xb1\x91\xe4\u0546\x15%IS\xc6\xc6\xc6\xf87/\xb8\x8asNZL\xb7;\x1f\xb8\xdc|PG\x9f\xc9S\xfe\x9e[\x93\xf9ac\x14\xacV\xd5W\xe5\xa2\x10k\x86\xa8\xd2$\xf5\x03H\xadT\xf6\xf4.\xd6\xe4K:e\xb5\xab\x95\u0163\xd4\x18\x1f\xcc\n\xc1Y\xd9W\xfa&\x1c2B\xa9\x98\xf5\xa0W\x17ZQY\u0411ZUr\x94J]\x06\xb2\x1b>\xb1\ajUj\xde=\xa3G\xd1\r\u0200\x9bD\xa9t-B\u04e44\x9b\tK\x9a\xf3\xacs\x87ie1sY\x93\xc3\xc9\x18\xfb:\x13\xec\xedLp\xb8;B'\x8b\x83\n\xcd\x0f\x9fr\x1c\xeev\x1c\xdar4\x86\xad\x9fW\x1c\xe5\xa8R\xf2<R)X\xfa\x1e\xcfb\x9cS\xba\n\xed\x8a\x0f\xbaV;\xea*\xf0V\x82\xb0\xb3\xa4K+KY\xb2\xee<\xaey\xfd\xffd\xfdYWp\xe7\xd7>\u03ae-w\xb1\xf9\xe1\xbb\xf8\x93-\xf7\xf3\x8d\u06fe\xc4+^\xfe:^\xfc\xe2\x97p\xc6\xe9+\x19\x1f\x8f\x81 }\r-\xf9\xff)\x1a\xf5\x1c\xc8U\x85F\xa3I7\xe9\xb2o\xdf\x01n\xbd\xf56>\xfb7\x9f\u5bbb\xee\xe4\xf0\xa1\x03\x8cM,\xe6\x94s.\xe7\xfc+_\xc3\xda3\x9f\rv\x88\xd9\xd9#%h\x86*E\\X\xe8\x91\xea\xec#\f\xf9\xc5`L\x8c\xb1q\t\xfeG\xad\xa9+\n\x94\xbc*\x0f\x16\xb7v\xd4\x06\xa7\u0363T\xe5\xc7RR\x95\u00e9Z\xdbVv\xa6%}Z\xfdr\xcde\xc5Z\xce{2\xf5\xb4+\r\x03\xad\xb0\xf2\x9f/\xe08\x05\xb5\x8cN\xae`dl\u048b,\xac\xb7\xad\xb5\xc6\xea\xbe}\xfb\u067d{\xd7Ya\x89'{\xfc\xf1\x9d\xb2r\xe5\u069f\xe8\xea\xfc\xb8\xe1\x14\x87\x0e\x1d\x92{\uee57ukW\xbf\xf5+_\xf9\xc77m\u0672\x05D4\x8a\xe2B\xfb\xe1\u0375\x84k.\xbf\x8c\xb7\xbc\u654c\fY\u04b4\x8b1\x1a\\XC\n\x8f)\xe5\u007f\x0e\xc1\x18%6\x19\xd6(\x1aU<J|\x19\x87qJ\xac\x19\rR\xa2\x8a*]\xfaj\xe9\n\x81\x93\x0fS\f!2\xcd\xff\xfe\xd4X\xba\x12\x05\u0661\xb7\xb5,@\xbf\xc7\xd5@\x82\xda%\a\xf7\x9a[a\x8fk\xa3\xf4\fc\a\u04075\x80\x97\x92 \xaf{\xb5\x14\x9f\xa3\x12\xa6[\xba\xddIe\u05f3\xe8^\xab\x19\x9cU\xffu|74d\x13&\xa26K\x1b\xb3\xac\x18\x9afys\x86\u0278\xe5\xb5\xf3\xea\xe5\x92Y\x98%d\xa2\xa4\x06\x1aC\x96\xe11\xbf\f5\xa8\x12\u02c1\xdci\x19D[\xb8_\x86\x8f\xd4\xc1\\\u2f27{\xaa\xd8\xc4\xdb\xff\xdaL\xa9\xe4\x83\x15\xaf\u007fm\x19)\xbc\x17Y\x92\x107\xc7X\xb3\xf1\"6\x9e\u007f\r#\u32d9=\xb4\x9b\xb9\xe9}\xec\u067d\x9d\u007f\xfe\xf6\u05f9\xe3_\xfe\x85\x83\x87Z4\x9aKh4\u0188\x1bC\xbej\u0574P8\xfd\xffm\u0765!(!_\xf7\x8e\xa2\x18E8tx\x9a\xcd[\xb6\xf0\xa5/}\x99\xf7\xbd\xef\x8f\xf9\xccg\xfe\x9a\a\ueed7\xcc\xc1\xba\xd3.\u44ab\xdf\xc0\xa5/|\x13K\u059dG\xe2\xa0\xd3i\xd7\xfd\xf4\xb52\xac4e\x88\x89\t\x96\u01de#o`\xa2\x86\xf7A\xe9\xd3\x1d\u02601\xb8_\x0e\x8a\fY\uc8c1\xecT\x84\x1d\xb3D\x12t\xe0\x03G\xdf\u04bf\"\xd4\x13\x01V\xa3(\xf3\xeb#\xcb|\u05a6Vh\x91\xdc\u05e4\xd2\xc1z\xe7^\xc59\x9f^\xe5:J\x9a(i\x16\xb6\x8dss\xaaF\x03\xb26{6\xdf\xc9t\x9e\r\n\x18+,,\xcc\xcb\xe2\u014b\xcds\x9f\xfb\x9co\xfd\xd1\x1f\xbd\u007f\xef\x99g\x9ein\xbe\xf9\xef\u007f\xa2\xc1\xfc\x98\x95\xf9=\xf7\xdee\xce?\xef\"\xa7\xaa+^\xfb\xda\u05fcv\xd3#\x8f\x00\xa4\x91\x8d\"#\xa5F4u\x8e\x95K\x97\xf2\xb3/|\x1eK\x17\x8d3\xbb\xd0\xc2\x19A\xa2\xfcb\xcc#\u01f4\xc8\xef\xb3\x01p\x8c\xd12q'\x84X\x9a\\?\x1bj\xbfc\xd7\x18T\x94$\xa59P\x0ejF\xb5\xa0\f\x9a\x95\x9b\xbc!i_\xcb_\xe4\x1d\xf6r\xe5\xa2\x03\xf4\xb3\x14\x1e1\u054d8\xe4\xe8\xecb\x1f\xb4\x84\xc7K\xdfQ\x12n^W\xf1\xcf\xea\xe9>\xd4T~\u007f@\x11\xa9\x01\nEt\x9e\x11e\xd4v\x18\xb1\x1dV4f8i\xe4\x00\xfb:\x13\xeciO\xf1x2\xc9\x117BK#T-MBu4 \x06(\u007f\\\xf9AX\x030\xf17g\xea`\xbe\xeb\x98K\x1c\x99z\xb32\x97o\xf0U\xed\x84+s\x93j\xadX0\xbb\xaat\x17\xe6\u0212\x06\xa3S\xeb\xb8\xfc\xe5\xbf\u0269\xe7]\xcd}\xdf\xfe,\x9b\xee\xfa*\xfbwm\xe2\aw\xde\xc2=w\u007f\x9b\x9bnz&\u03fd\xea%\\\xfa\xcc\xcb9\xed\xb4SY\xbf~\x19\x13c\x06c\xbb\x88\xa4\x15J@~L\x00^\x1a.\x19c\x18\x1a\x1a\x06,Y\x96\xb0\xe3\xb1\u01f8\xf7\x9e\xfb\xf9\xd6?\u007f\x87[o\xfb\x06[6oaf\xe60\xd6X\xd6m\xbc\x80\x8d\xe7^\xc9i\x97\xbc\x94\u0255\xa7\x93e\x8eVk\xde\a\xaaH\x91A\x86:\xa9\f7)\xe8\x95\"\xcd&O\f\n\x15y\xfd\xaa\xae+\xb1*\x11.\xbe\x832!\xe3\xd5\bi\x03h\n\xc3F\x8e\xbe\x96\xa6u\xb6\xbd\xa0\xe3BA\x93\xef\u007f(up\xef\xe3\r{7P\xb5\xfc.\r\xee\xa5\xdd\f:\xed\f\xd7J\x91\xd4\xfb\xf6D\x95\x94\xaf\xa4\xdbab\xd1*\x16\xafX\u03ceG\xeeF\x1b\xc38\x1cVD:\x9d\x0es\xf3\xf3g\xddx\xe3gO\x03\xee\u0773\xe7q\v?\xd9.\x11G\x05\xf3\xbb\xbe\xf7\x1d\xce?\u03db\xa0\xbf\xff}\u007f\xf4s\x0f=\xf4\xf0%\xd3G\xa6\xb1\x91\xb5\xd6V\xc7\x16\x8a5\xc2\xf3\x9eu1\x97\x9c{&I\x96\x90\xe2\u021a\x96\xc8xm\xb6\x84\x05\x1d\u07d3\xfbIvL\x1alUC\x85\x1b<\xbf\xab\xc0j\x8e\x9aI_\xb7\xe9\xf4\xd5x/\x05Q\xfaN\xf8'\x9aa\xaa\xd5v\x0f`\x0f\xfa|\xfd'j\xcd8_+\u0740\x1c\x85)\xd4c3\x88\xc7\xe9\xcfC\x84\x9b\x96R\xc1\xa2\a\x19\x14\x84\x9a\x83\xb7\xeb}\xd4\xf5\xd7+2)K\xe3Y\x16\xc7sl\x189\xc8\xe1t\x84=\xdd)v&\x8b8d\xa7\x10\x1d#u\x06\x1bUm\x05J\xb8uZ\x06S\xf7V\xa4\xdd\f\u6e8e\x85\u0115C\u047cC\xa2\xb6@\xd838\xa5\xb7\xe7.Z\x98,Ih\xa5G\x88\xa2\x06\xcb\xd7_\xc4\v6\x9c\u03f9\x97\xbf\x92\a\xef\xf8{\xb6\xdc\xfbuvm\xbd\x87{~\xf0M\xee\xf9\xc1wX\xb5\xfa$\u03bf\xf0r\xce;\xf7\x12\xce:\xf3,\xce:\xfbTN9e5##\x16#)\u05b8\xe0_\xdf;\xfdx\xe2CT\xe9y\u074d\x91\x90\x16\x1f\x85\u7532c\xc7c<\xf4\xf0#\xdcs\u03fd\xdc~\xc7\x1d|\xf7\xbbwp\xe0\xc0\x01\u04b4K\xdc\x18b\u0769\x17p\u0499?\xc5\x19\x17\xbe\x80\xa5\xeb\xcf!q\x86v\xbbE\x16\xf4\u05e5o\x8f\x94iV\xb9@\xc0\x14\xe1o\x14f\xc8&p\xe4b*Wf=.\xb1\x8f\xeeSBU.>\f\xdcB:li\xc6&\x80\x82\u053f\xa3G~:\x88:\xaf\u0280k\x15y\xf86c,N\xd2\xda04\xf7W\xd7\xca\xf6j\u6105\xaec\xae\xe3H\xda\x19\xcd\xc4\xd1\u0410tU\x19\u00a7i\u00a2\xa9\xa5,]\xb1\xbev\xb5\x87\x82\xdf\x1d>|\xc8l\u07fe\xfd,\x80\xb7\xbf\xfd\xb7\u007f\xe2w\x89\x8f\n\xe6\xf7\xdds\x8fg\xe9TW\xbe\xeeu\xbf\xf0\x86M\x0fo\xf2\a\xb9\x11\xbf\xb6\x1f\x02bS\xa7\x9c\xba~-/\xbf\xf6y\x8c\x8e\x8f0\xdf\xed\xfaTxc0\xa4Xu\x1e\xc8\xc9\x17\x19\x94H\x9d\xaf\xbe\xc3\xe2\x8f8\u05e3 \x19D\xcaI\xed\xfa*+\xf0\xc1\x15pu\xa2.(1Ye\xff\x91\x01\xb6\x9f\xfd\x9f\x91~8,\xe0\xfb\xa9\x9a\xb5>\xe9\xe6_\xeaC&=\xc6\xd7iX\xcb\x14W\xef\x12\xea\xc9Ee\x18\u0148m3\x1auX\u079c\xe1T\xb7\x97\x83,\xe2\x80]\xc6\xfet\x193\xf1\x04\xa9D\xde/#\xf0\x94N\x198Ps\xaa\xb4S\xc7\\W\xe9dZ?g\x8c\xa0qE\x02\xa7\xfd\xd4XM\u03a95c\x91B\xf5\x9a&]\xb2,\xc1\u0688\x95'\xff\x14\xabO\xbe\x84\xf3\x9e\xfdsl}\xe0\x9bl\xbe\xfbk<\xb6\xf9\xfb\xec\u067d\x99=\xbb7\xf3\xb5\xaf\xfe\rk\u05de\xca\xc6\xd3\xce\u1b33\xce\xe3\xb4\xd3O\xe3\xb4\xd3N\xe5\xe4\x93\u05b1b\xf9$\xe3c\x11qD!\xe9)\xaaU\xc9\x13e\xfay\xef\xf29\x19\xeaf\xa3\x8e\x03\a\x0e\xb0e\xcbV\x1e~x\x13\x0f\xfe\xf0!\x1ex\xf0\x87\xdc{\xef\xfd\xec\u07bd\x8b,\u02c8\xacedl\x82U'\x9d\xcb\xfa\xd3/c\xdd\xc6KY\xbc\xeat\xc4\xc6,\xccuH\xb3\xcc\x1fzZ\xd3y\x96\x9d\x9b\x04\xaf\xe1<K\x17A4\xb8\xf4\x05\xf3\xaczoS\xbf\x92u\xc0b\x99\xb7\xb6\xf5FZ*\x905\rCc\x11c\xb1\xc1\x86\u074a>\xab\xba|o\xa1\xd7\xee!7d\xab\xf8{\x9b\x9e\xd3Z\xfcp\xccW\x02N\xea\x93\xd0\u028f\xcaTi%\x8e\xd9VF\xe2\xbcL7\xf76\xd7\xfc\x1a\xcf\xd4o\x8a\x8a\"&f\xf1\u04b5\x8c\x8c\x8c\x91d)Q\x14\xf99\x9e\xb5\xb2g\xcf\xe3\xcc\xcf\xcf]\xa4\xaa\x93\"2}B\x82\xf9\xe6\a\xefd\xe3\xd9^\f\xffG\u007f\xf0{/\u007f\xf0\xfe\a.\x99\x9b\x9f\xc7\x18cLem\x1f\x81\xd8Z^\xf2\xfc+9\xfb\xec\xd3h\xa7]2\vj\fq\x90\x0fVR\x12|\xc6e\x05\xc8sS)\xe9\xc9\xc3\xe9\xa3\"jU\xb8\x1c\x15\t\xb5oPW\xd5>k\x1f\x84\xf4\xd6\u294b\x9c\x1es\u03a3\x95Fq\x90\x19m/\xe4\x1d\xebsOk\xe3_\xf0\xa9Z\x13\xc4\x14\x80\xdes\xf3\xe4\xaf|$\x8e\xa9x\x81)\xd3f\x9d\x1cb:y\x9c\xddf9\xbb\xa3\xe5\x1c1\x93$\xc4\xc1\xa3\xd9U\xba%\x0fz\x9dLYH\xfd\r\x98\xb8\xf2F\xae\xfe*'\x12\u031b\U0008391e\xd4R\x02\x98\xf6(\x93\xa4^\x14:%u\t\xe9\xcc462,]s6K\u05dc\xc5i\xe7_\xc3\xe3\x8f\xde\xc7\xceG\xfe\x85\xc7\x1e\xbe\x83\x03\x8foe\xe7\xceG\u0631\xfd\x01n\xfb\xfa\x17X\xb2l\x15\xabVm`\xf5\xeau\xacY\xb3\x9aSN\xde\xc0\xa9\x1b\u05f3j\xd5r\x96-]\xc6\xc4\xe4\x14C\xcd!\xe2\xc8\xd2lZ\x1a\x8d\xa8\x06\u058a\xd2\xedt\x99\x9d\x9d\xe5\xe0\xa1C\xec\xdf\u007f\x80\u077bw\xb3}\xdb\x0ev\xec\xd8\u03ae\xdd{\u063a4\xa7\n\x00\x00 \x00IDAT\xbe}\a;v<\u0291\xc3\a\x01\x88\x1bC\f\r\x8f0\xb9d5kO\xbf\x84u\x1b/e\xf9\u06b3\x18_\xb4\x864\x83v\xbb\x85s\xed\xf2\xc2\xcez{\xa0\xb0OQ\x84/\u7b85\x06\xa3>\xecA\x8c\xc5`\a\\Ez\x9c^\xd1'e\x15Uy\xc3\u0418\x88\x19i\x1a\x1a\x86b~Q\xab\xf0\x85\xbay\u0500\x8a\x9c\xaa\xc1\x9dT\xa6\xdc9\xa0\x1b\x1f%\xa9d\x15)n\u0391\xfb\xfflg0\x9fx\xa5U\x1e\xfc,\x15\xea\xd0\x05\xff\xfc\\\xff\x9e\xa4)K\x96\xaebjj)\xbb\xf7\xee&\x8a\xe2B\xf2\xdciw\xd8\xf9\u062eE\x0f<p\xdf(pb\x82\xf9\x1f\xbd\xff\xcf\xfc\ub9fa\xec\xf5\xaf\xf9\xb9_zx\u04e6\xd0R\x9aR\vm\x84$I\xb8\xe0\x19g\xf2\xfc\xe7=\x8bh\xb8\xc9\xec\xfc\x02\xc6X\xef`\x18\x8c8\\x\xb7\x8d\v\x1f\xd5w[{\xe5}\x955\x9d\x9a\x87\xb9<%\xd4\xd3\xda\u0630\xce\x16\u66d6\x1a8e\x82l\xce\xf4if\xfb\x97\x84\xfao\x1b)\x8e\x05\xcf\xf2\xe7\xbf\xd3T\xf4\x1f!7T\xa4/\x00\x8f\x1f\x85\x8e9\n=\xa3\x15\x11x\x11y\xaa\xbd\xa7\x99T\xdad\xdf^\x8fH\x87\x11\xf6\xb3\xa4{\x84\r\xe9n\xf6D\xcby\u052cd\x0f\x8bHL\x8c\xa8\u00d2\xe1\x1c\xb4\x12e.qt\xb3\xa0\x00\xaa{y\x15\xb0\xa4\x86\xb0\x02*a1\xacR\x98\xd5\\\xf4\x82\xb9Y\xcf\xeb\xa3Tl\x11\xf2\xf4\x9b\xd41\xd7=\x82\x901>\xb5\x8a\u0265\xeb9\xe9\xcc\u02d9\xbdb7\x87\x1e\xdf\u009em\xf7\xb0{\xfb=\x1c\u07bb\x8d\xb9\xf9i\xee\xbf\xffv\xee\xf9\xc17\x111\x8c\x8eN2\xb5h1\x93\x93\x13L\x8cO2>1\xc5\xc8\xc8\x18CCC\f\x0f7\x19\x1e\x8ei\u013e%hw\xba\xb4[m\xe6\x17\x16\x98\x99\x99\xe6\xf0\xe1ifff8r\xf80\a\x0f\x1c$I;\xa1\x03\x89\x19\x19\x1bg\xe9\xea\rL,Y\u0272\x93\xceb\xd5)\xe7\xb3x\u0169LL\xacfhh\x8a4I\x99_\x98\xf7^#b\xe8[\xe6\xd1r\x10\x9e\xf3\xc2B\x1e}\xe8kT\xa3\x12\x92\x82\x82\xb7\xbd\x1bTvT\x87\x90\xd2s\x9d\x86n\xcaz\x19\xb0\xb3\x06;\x161<j\x89\r\x03\xc9\x19\xed)D\xa4\xd2(\xeb\x00&T\xfb.1-\xa6QbL\xbd\xab\u00ebVP?8og\x1ab\xe0\fY\xa6\xc5,\xa8\xba\xbfP\xca\u05c5,I\x19\x1d\x9dbdd\x1cuY9\x93Q\u03db/Y\xba\xe49;w\xee<\x03\xd8}B\x82\xf9\x87\xff\xe2\x06\x05\xf8\xf8\x87\xde\xff\xb3\xf7\xdd\xff\xc0E\v\xadv%\x1d#\xb4[\xaa\x8c\f\r\xf1\xe2k\xae\xe0\xd4\xd3Nf\xb6\xdd%\x8d\x9aEE.\xb9\xfdN\x00q\xa9\x82\xcb\xc0\xab\xa0\xa2Q\t\x91f\x88<A\u042e\x03B\xee\u0218\x03\xa7\xe9\x19\xd6(\x90`\xc9B\x8c\x9b%\xf3v\x005S^z\xa6\xf2U\xc0\xaev\x98\xf9W\x94\xc0\x9d\x8fl{)\x19\x11*|{\xbd\t\ueb45\x06\xc5\x06\u0213\x04\xf5\xe2\x97:-\x8d\xb0\xb4\xda\u0754RN1AQ\x122\x15\x1b$,\xcb\x0e\xb3\xc8\u0370F\xf6\xf0\x98]\xc1\x0e\xbb\x9a=Lq$\x8bh'>X\xd89\xad\x9d\xb5\x03;\x19)\xa3O\xeb<uYa\x15\xaa\x8d\xda\u0560\x95\xe3\xbd\\\xac\xd2B\xf5\xe0_\xf3vk\x014\xc3F1KV\x9c\xc6\xe2\x95\x1b\xd9p\xd6\xe5t\u06f3\xcc\x1c\xdc\xc5\xc1\xc77sh\xcff\x0e\xef\xdd\xc6\xec\x91},\xcc\x1dazv\x96\xfd\a\xf6\x93t;\xb8,\xe1\xe8A\r\xbdG\xae\xc16\x87h4\x87\x18]\xba\x9c\xa1\xd1qF'\x97\xb0x\u0769,Zw*\xcbN>\x83\xc5\xcb\xd6\u04b0c\x183\x02\xa9\x90\u0336\x98\x999\x12\xb6\x1eM\xbd\tQ_\xfbK\xd1\u0494\x85\x8c\xe4\xe1\xaa\xc1,\xcb\xe2o\f\ttK\r1u\x90\xba\x8b~\xff\x15\xbc\x141\vC\xcfh\xc4\u041c\x88\x88\xad\x10\x99\x90\xe5J\x9f\xfbN%\x01\xb1'\xc36\xbf7\xf2\x9c\xdf\xda\x1c\xaaz\xb5W\x12w\x8cx\x0f\x19-\xfdvT\x95N\xeaH3\xafV\xc9[\x15\x13\xd4m\u04ab\xce\t\x85\xa1K\x1c\xcd\xc6\x18\xf1\xd0\b\xe41\x95\xe1\tdiF\x96ef\u07fe}\x8b9\x01\xfe\xf4\x81\xf9G>\xfc\xa7\xf2\xe6\xb7\xfc\x8a\xaaj\xf3-\xff\xe1\x8d\xffq\xeb\xb6G\x01\xb0\u0592+X\xac1t\xbb\t\x17^\xf4\f\xaey\u03a5\x18\x11:\xce\x12\x19\xa1\x11\xf4\xe0y\x80\xb1uZ;\xb2{\x17_T\xca1e1\xc8|\x92 \x9e\xff=\xc3\xd0\xc1\x1b\xdeG\xb8\x02\xa0\xb5OR\xe5\a\xb3\xb6\x10\xc3y_\x13\xd3\xf3\x95\xda\xc3<\xf6\x91%\xa1\xa2w\b]\xe2`1\x90\xd5\x06\xb7\xdeU\xc6\x06=\xbb\x1b \xab\xa4\u03d6T)Ez\x83\x8e<\x8e\xf3\xb9~0\x95\xb0*->\x86\xab\xfao\x96\xc2\xc9N\x8d\x9fi\x94\u06f4J\xa4\tK\xdda&\xb3Y\xd6\xcbn\xb6\xb2\x8c\xfb\xb2\x95<\xea\xa6\xe8h\x03+\xfe9\x17G\x9f\xd4+\xcd\x1c\xc4]8\xefr\x83\xaf\x1c\x99\xb52\xf8\x10\xad\xce?\xfdB\x99\u04de\xbc\x8c\x9e'\x9dyS\x11\xefS\x9fet\x16\xe6\xc0\x18\xa2x\x98\xf1\xc9q\u01a6V\xb3b\xc3\x05\xa0\t.Kh\xcf\x1fa\xe6\xe0.f\x0f\xef\xa55\u007f\x88\x85\xb9\xc3,\xcc\x1e\xa6\u04de'\xe9\xb6H\xba\x9d`\xd5\xec\x13\xb2\xc4\x1aL#\"j6i\f\x8d0<1\xc5\xc8\xe2e\x8c,Z\xc2\xd8\xe2%L.[\xc5\xf8\x92\x15\xc4\u00e3\xa8X$\x8a0\b\xe9L\x87\xee\x91\x16\xddVB\x9af%\u0329+\x02\u0335\xb7\v5R\xef\xf5\x82<\u0408\u0146\x94)\x0ft\xe1\xea\xecuZ\u8ece\xa4\xcfD_E\xc8bCf\r\u0450\xa1\xb9(&j\n6\xa75\xa8[\xd9\xf6\xa1\xb9\xd6\xff.}\x95\xbfV\x92\x8d\x06\\\x9ba\x05Z\xabn\xbd\b]\a\xad\xb44\xb6\u00c8\xb7\x98\u02028\"\xffy\xc1\x8c&\x97j\xaa\u02c8\x1ac\u010d\xd1zL\x1b\xbe\xf8\u073b\xf7q\xee\xbf\xff\xbe\xe4\x84\x04\xf37\xbf\xe5W\x14\xe0\xbd\xef~\xe7\xd9\u07fd\xe3{g/\xb4Z\x1e\xccM\t-\xce)\xe3c#<\xff\x8aK\u0670~-\xfb\xe7\x12\x8c14Ih\x90\x16~)\xb5\xe4\x13\xd5\xfa\u025a\v\xb2\xf3\x05\b\xe9+\x04\x8e\tR\xdaC\xa5\xa4\x18\xba\x1a\xd1\x15KLV,\x03U\u056b\xd6\xd7@\b\x10\x91\xfa\xea\xbdpI\xac\xfb\x0f\xea\x00~{\xf0c\xf2\xdfms\x97\xa0\xa2\xa1\x84|\xe3\xd5\xd4l\xbd$\xac\xda\x1c\x9bb\xe9\r\x0e\xd0\x01\xdc\xfc\x13\x16\xdb\xe5\xb3\x06\x13\xd2\xd2+\u06e7\xb9'Mi;\xa0\xb5\xda\u0285\x1e\xcbi\xc6X6\xcb9\u0332\x81\xddl\x91\xe5\xfcPV\xf3\x98.f\x8e!\x9f\xfc\xa4Y9\xbb\xec\rL\x92j\x958\xe0Y\xea`2\xab\x8c\x9c\xd4\x1e\x85F\xf8y\xe1\u07camT\u007f\x81\x92t\xdat\xdb\xfe\xf7Yk\xbdEj\x1c3\xbax\x94\xd1EkX\x1dN\a\t\x8f\xcb9\xc5e)Y\xda%s\xa9/\x88\u0160V\x90\xe1\x88hx\x88\xa8a!\xb6d\xc6'*EQ\xa9\xc5vY\x86f\x19Y\xda&I3\x9cq\u0210b3AS!u\x8afZ\fX=X\a\xf0\n\x87h\xe9l,\x15P\xb3X\x13\x87\xcc\xce*\xb0J\x9f\xda\xe8h\u05d4V\xba\x1cg\x84\xcc\nQShN\xc5D#\x16#\x14`\x9eW\u0245+\xa8\xf6\xf0\xe3U\xfd~\xcf$\\z\x93+zd\x04B\x99PU}/3\xf5@\x9e\vy\\\x00s#\x82\xc9*\u0770\n5\a\x8e\xd0\xf1;bT\"\u007f\x8dT\x8bG\x81\xf9\xf9y\xf6\xed\xdb\xcf\t\a\xe6\x9f\xfa\xd4'y\xc3\x1b\xae\x03`\xdf\xfeCo\u07f9k\x0f\x99sXk\xc3p#7\xd3J9\xf7\u0333\xb8\xe6\x8aK\xe8\xa6\xfev\x1f\r@nU}U^\xbb\x93\xfbU)\x1a\x8c\xf0\x9d\x19\xdc\xdc\x1e\xab\x12\xad\x02\x9c\v@\x9e\xe1}\x1c\"\xf5qryU<\b`\a\xc1q\x17S9\x00\\52\xf9\xa8\xdd@\xfe\x15&\xa8eJ\xbe\xbc\x94n\xda\xf0Y\xa5\xee\x8dq\xac\x01\u0560\xdf*\xa1\xf3\xc8B\x0f!\xc1\xa3f\xb0\xf9\xed\x80?\x86P\x05k\x1f\x87Z\x85J-8o_5\xbb\xa0\xf9\xd5J\x8b=)m.\xe6QNc/[X\xce&V\xb2C\x970-\xc3\x18\xbcZ\xa96\u0516\xea{\x96k\u0635\xae\x8d\xcf\x1f\x912\x80n\xa1\xd8\x1bp\x85\x9e\u065fD\xe2\xfc\x01\xed\r\xd5\xf2MBW\xbe\x8eNq\x9a\xe0\\\x82K\xc2s\x0e\x89\xee\xd6HI\xbd\t~\xf1&\x1e\xf6[\u00a2\xb8,|}*\xe8\x02tZ\t4\x12\x88\xc0\x841o\xea\x1c\xear%\x87\x16^1j\x05F-\x91\xf5\x1d\x86\x11p]G\x9a\xfa\x9f\xab\xf9i*ZZ4Ky\xb5J\x11\xb5fJ\x1f\xf2Z\xf4\x9f\x1eC\u07a4\x03\x8a\x1e\xff\xebR\v\xb6!4\xc6-f\xcc\xebD\x82\x1d\x12=\xfbp\xa5\xc3\xf31\xab+\xad_\xd7rl\u05af\xa6p\x14A\x9d\x1f\xa2w\xb3\u04b7\u0204=\nu`\\\t\xfe\xf9\x82b!\xcc\x14\x83:o\xbcU\xdd\xf8-\xe7EB\x969\x92$=\xf1\xc0<\a\xf2n\xb7\xb5\xf2\x85/|\xd1U\v\v\v\x9e\xc26\"\xa5\xb63crb\x8c\xe7_y)\xeb\u05eeb\xba\xd5a\x98\x94\u0607\xa4\x15Ugq\xba\x17\xc5q\xb8\tM~sW\xe4jO`\x188\xc8\xcaU+cF!\u00c8\x90\x15\xd3\xfdrE\xc2\xf4\xd4\a\xf9\x1a\xba\xf6\x1aF\r\xf0_9\xba\u0250\xf4\x18\x87U\xb74\xb5\xa7&\xa9\xd7\xd7R\xfb}G;&\x06\xcf\x04<\xa5\xe3\x87a\x16\r\x1d\xc1\x13\x1c\x9e\xf6\xe53\xd6o\x00\x97?\xa2\xf0\xc68u=L\xb2G\xda|\xc0;N\x87\v\xe41Nc\x1f\x8f\xcab6\xebr\xb6\xb1\x8c\x83\x8c\u1430\xb5\xab\xc5T\xa0\\t:\u01aax\xcd\u0295\x92N\b\ufa69\xe4\xa9J\xde\xd9i\xd9z\xbbJ&i^i\xbaj>\xa67\xf6\xf0\xfbiR/2\x8b\xdf\x19\xac\x94]\xde\xdew\x02\xfdg\x05\xed(f\u0220\x8d\x88,\xb6%\x90\xe7\x86i\xb9\xd41\u007fZC\x06\x91\x88H|\xe1\"\x89\xa0]G\x96iPrI)I\fT\x8b\x02V\xbc$\u03c6\xf7z\xe0\xcaE\x0f\xf8\xd6\x05\x05\xfd.Bi\bgn\x8cZ\xcch\x14\xf6;\x14c\xfd\xe1\xe1(3l\xa5\x17\x82\aJ\xcc\xfa\x03(\x8ev\xe7\x94~\xa3\xf5qY\xa6\xd0I=\xfdc\xaa\xc3U\xc1W\xe5\x15*\xa7W\xb5\x9c?\xc6j\u982a\xb5\xd7 \xcb2\xba\xdd\u0389\x05\xe6_\xfb\xdaWy\xc1\v\xae\x05\xe0\x03\x1f\xf8\x93\x9f\u06fcy\xcbD\xab\xdd\xc6Z#\xa5\x1cQ\u025c\xe3\u0513\xd6p\xcd\x15\x97x\xbe\xca%\x05\xdf\\\xb1\xaa\xa6w\x9b\xba\xba\xf4p\xd4\xd3Z\xfb\xa9\x96\xa3\xa1\x92\x14f\\\x8a\xadU\aYm\x01Hj\xff&\x94A\xf5R\x8c-%\xf0\xe7\x1c\x0fd\x8eR\xf5T\x0f\n\x83\x1bx\xf0\xc8\xc0q\xef\x13\u05ea\xfb\x0e\xc0\xd1\b\x9d@\xb9\xb2e\xca\x03\xf4GT\xc3\x14\xac\xc51\xd46\xd5\xe5\xa9\xfc&\x1a\xa1\xcbY\xec\xe6$9\xc0>&\xd8\xc626\xb1\x82\xdd:EBDD\x86\x13Wn/\xf6\r\x97{\xdfs\xad\xad\xc5he\xde\"aP\x9aW\xebN\r\xc6e\x85iZ\xd1\f\xaa\x92U\xf0E2-\xbeW\xaby\xabT6X\x1d\xe5\x82VX\x1d\xaf\xea\xe3\x9d\xf3\x9f\x88\x1a\x82f\x12\x12\x90\xca\x1fT|\xbd\xd6\xdfmi\b:i\xb1\r\xc1\xb4\x14\x8d\x1cY'C\xb3\xde!q(\xe1\xadE\xd5\xfa\u047d5\x85\x84\xb3\xda9\xf5]\x1c\x85LE\xfa.YU\xbc\xb5\xed\x90!\x1e\x8f0\xe3\x11&\xf2\a\\^\xfa\xb8^)kM\xf6$\xf4\xa6Ni/\xf1%\xbdc\xd3\xd2\ufa38\xb6D+\xc0\xeb?:\x99zI\xab\x8a\x1f\xc2W\xc0>\xe7\xcb{'W*\x82q\x14IK\xfe\xec\x1d@A\x1a\xc19\xf5A\x1e'\x12\x98\xe7@\x0ep\xf7\xddw\xbf<I\xba\x11\xa1\x95)NQ\xe7h6c\xae\xb8\xe4\\\u05af^J\x96t=?.=\x9b\x93=\xc3\x1c\xe5\tJ\v\x9f0\x12i%\xb2\xd0\x15\xaa\x06\x1b\xfe\xe6z\xb7\xd7\xfa\x86\x8d\xbd\x8a\x95\xfa\xb0P\xf3)z\x1fD\xeaQ\xf9\xfb\xaa;I\xef\xe8g\x10\x90?\x19\xe0\x95>@w=u\xfe\x8f\xcf\xe1S\x8e\t\xea\x9e\xffw\xc1\xad\xa9!)'q\x805r\x98\xb3\xd8\xcdfV\xf0CV\xb1\x87I\xbab\xc1@S\xd2\u00b7\x9d\x819IZToZM\xf2\x93\xbaJ\xdf \x181\x1e\x8d4D\t\x86.E\xb5b\xbd\xaa\xe5 Zz\xd0H\xa4N\x03i1\xa9\xf5\xc3\xe2\xda\x01\x13\x02ql\xe4\xaf!\x97\xa9\xa7\x00l.\xbf\u0492\xcf\x12WZ\xb4\u57cf\f\x8c\xfa\xb9E,\x8aSC')\xb70\xfc\xf4\u0440\xf5\xb4J\xbe0=h\x06Y\xbamV\x1e\x9f\xe4\x8b5\x15\\\x95\x12\xdf\u0350\x10OX\xccT\x04\xc1#\xdf\x1a_\r\xbbL\xc3\xfc@\n\xb37=j\x9aEO)S\xbc\x9cR(\u0574\x06\xe8\x15\xe1\x83+\xfd\xcd\xf3\x85\xc3v\xea\x818\u03fa\xcd\v4\u3d28\xcc\xcb\x01\xb9T\xde3\u03df#~\x86\xe7\xdc\x13\x13J\x9cP\x03\u043d{w\xaf\xbb\xe6\x9a\x17\x9e|\xf0\xe0!O\xb1\x88H^\x95\xbb\u0331~\xfdj^\xf2\xbcg\u04b0\x86\xb9N\xb7\x06\xe4\xbdn\x88G\x83\xab'\x10K|||\xaf-\t\xd6o\xd5^H5}U\u07e0){\xce\xc3\x1e\x8dq<\xca\x05\"\xe5\xf0\xae\x94n\x1d\xfd\xfb\xe4\x18\xc7\u00d3;\xef\xb4\xe6\xb5\xf1\xe3\x02\xf2\xe3\x01:\xa1S(\x80\x03\xbf0\xb6\x86#,\x939\xced7\xdb\xec2\x1e\x8eW\xb2?\x9d IlH+\xcfz\x16e\xfa_#Wl\x83j\x9d&\ntJ\xae\xc216\xf6\xab\xe2\xaeLF\xf7\x9c\xbf\xab\r\u07b5\x8aR=\xd9\u01c5\t\\@\xedb\xd2R\xb1\x18\xf0\u0671\xa6\xb4X\x90\xd2\x18>7\x8e\xd2\xde\"\"\xb7f%$h\x8d\b\u0608\x86q\xd0vtSj\x9d\xa3*H\xe6\x02p\x95\xa7\x99\f\xaa\x9b\xfb\xf60*:\xfe\xd0\x01;\x01\xd304\xa6\"\u0322\x18m\x94\x12!U\x0f\xa8\x05\u0154SX\"\xd8 U\xad\xf2\xe5\xf9 \xba\x06\xd0E\xaeD\xa9\xae\xa9\x96Py\xc5o\xf2\xb9G\xe8\x96R\xa0\x9d:\x12\xa7\xc5<$\xa7]\x14\xc1\xa6\x0e\xeb\xf2\x83\xd8o5\x9b\xe0GcB\xf9\xae\x94[\x1cZ\x9f\x0e\xf4w\xfc'\n\x98\xdfy\xe7\xf7\xb8\xf8\xe2K\x01\xf8\xf4\xa7\xff\xd7\xcb\xe6\xe7\xe7\u05e5i\x8a\x91\"\xaf\x06uJ\x14E\\\xfc\x8c\xd3\u0638a\rY\x96\xf9\xb0\x01\xaaaaz\\\x84>\x9a\xdb\u02a0RU\x9e \xd8\xf4%\x81\x0fPz\xc8q`\xb3\x1aKE/\x1f\x9fK\xf7\xd0J\xa8CUc\xab\xc1\x97\x9dB\xae\xd5ov4\xb8B\u007f\xd2M\xc9q\xe6\n\xfa4\xfc\xcc'W\xa1SL\xcb\xf2\xae\xc7\xf9\t\x06\x06\xc7R\xe6Xd\xe69ed\x1f[\xcdr\xb6\xe8\n\xf6\xbbq\xda.\xc6\xc7g\xb8\xa3\xf4?A\xf7\xa3e\x17&Z\xe5\xfd\xb5R\xd6z\xf9\x9eD\x06\xa31\xaa.|(\xea\xb2|\xc70\xd0$\x01\xb0\xabUs1q\xd5\u06ba\x8d\xa1\n\xfcBd\xfc\xd0TB\xe0I\xf1\xb5\xd2S\xa9R\x82{\xfd\x00\x0f\xb2\x91\x91\b\x89\x95\u01bc\u00f4\x1c\u074e\x06P\xcd/.\t\x83\xe0 &\xb0G\xa9\x8e+\x06s\x85\xc2\xc5J\xe1u\xae\x026\x16\x1a\xe3\x11vq\x03\x86\xbc\x99]\xeec\x8f\xfa\xeb62\x82-\xb67\u00dd\xac\xa1\xeb\t|\xa9Sj\x96\x0eE'\x13T8\x92\x1fD\x94A%U;\u072a\xa7\x8a\"\xdeD+\v\x11\x8f\x15jJ\xc5\xcf+l\xe2\x15q*\xf9\xe6x\xa0\\\x8c\x0f\x06\xafJ\t\x9cs\x14\x9cU\x8f\xd8\xc6\x1aK\u0708O\x1c0\u03c1\x1c`\u01ce\x1d\xcf\x1111\xd0\x11#\xcd\xfc\xc6r\xea\x18\x1d\x1e\xe6\x99\x17\x9c\xc5\xc8P\x83v7\xa9\xe7\a\xa2\xc7<\x05\xf5I\xa0\u0493\x01!\ud66bs\x94\x15\xfbz\x04\x9c\xd6\xf3@\xc5`m\x8c\x8db\xd4Z\u007f\xc3\xe6\n\t\x97\x91e.\xe8\xeb\xfd\r\x1eE\x9682a\x131#I\x13\xb2\xa4\xeb\xdd\xee\xe85\xa8\xd2\xe3\xd6\xd0Oy1\xe88\xac\xbe\xf6\t\x1c\x9f\x9eJ]\x8f\xfa\xde\xf5\x1fay\u03f4\xc4\xcc35\xbc\x83S\xe2\xfdlm,g\xd3\xec*\x0e\u034f\x92\xba\x88X\xb2\x01A\bU\xa9\xa2T\xdc\xfa\xaa\U000b6cbc.L#\xf2\xb4\x1d|0\xb5\x1a\x87\xe2\xc1]\x8c\v\xc3M\x17\xec\x85u\xc0u+}v\xc2\x1a\xacQ\xac\t\x8bs\"X\xe3\xd7\xe9\u0542f\x0e\u056c\xa0A\\\x95\xc8\x13jFi\x84\x8a\xdb4\r\x1a\x19\x88\x1d:\x9f\xe1\xda>\xd72\x1f\x14kF\xcd`\xabj\u00ec\x15]zAg\x86\xf0\x12\r.\x95*\u07b1\xb21\x12\xa8\x95\xe1\xf0\xaa\x99\xca\xd05,\xa2J\x1e~R\xe4\xf8\xfa\u1dc4\xa1\x83J\x91RW\xd0R\xd2+=\xad\xddo\x95\xc0\x89\n\xaek\xb8\x17\x12\f\v\xa9w\xd8\xcc\xd7\xea\xf2 \x18\xf0rD*4\xb78\xf1\xea\x9d<0]\xa4\xa6\xfc\u0272\x94,K\xfb:e\x14\x8c54N$0\u07fau\xb3\x9cr\xcaFU\xd5E/z\u044b\xd6\xed\u06b5\u02ff\xa7\x95\x8aC\x15\x16OMp\xc6\xc6\xf5\x18\x1b\x93d\u0770\xf03\b6\x8e\x0f\xe0G\x03\x86\x1f\x1d\u0334\xe6\xb7Ru\x13\xf1'\xb5?\xad\x8d5DQ\x8c\xb1\x11\x9dn\xc2\xdeC3\xec;x\x84#\xd33\xcc\xcc\xce177\xcf\xfcB\x9b\x85v\x9bN'\xf1\x12M\x11\x8c\x11\x86\x87\x1aX#\u0111e\u0572\u015c\xb4n\r\xa7lX\xcd\xe2\xc9q\xb2$!M|\xa4Y\xear\xdd\xeb\xb1\x0f\xa2\xbal\xf1\xe9\xeb\t\x95r'O\x8a[\xe9\xe9\x01u}\x92\a\x80\vJ\x94%\xf1\x1c\x8b\xc6\xe7Y\x1b\x1fbs\xb4\x82ms\xcb8\xd2\x1d\xf1\x8e\x8e\x15W\xcbz\x180T\xa7\xa0J\xff\xa0</\x12\xfb\x1e\x9b\bF\x82\xf9\x12\n6\x97\x0f*\xaaY\x00\xfa\\1S\xfa\xad\xd7|Hr:'\xaf\u028dE\xa2\x18i4\x10\xabh\x9a\"\x89\x16\xdd@_R\xf2\x806P\x03\xdfnF\rqC0-Cw!#\xed\xf8\u007fs\x86RSm(*\xd4\xe2\x8f\xf1&Y\u0396\ubd5a[\x17\x1b\x0f\xe4\xcd!C4\x19!\xa3\xb6b\xb5\xe0\x03`Ll\x8a\b:\xa5\xaa\u007f\x0f3T\xa7%HW<\x91\u051f\x85d\xc1\xee\xd8U\xc1<T\u84bf\x8f\xe2A$2\xa5\n)Ue!\x81$\x1cj\x1a\xba\x03\t\xd6\u02a0H\xea\xe7\x11\xfe\xd0\xf1\x15z>\x9d\x17\xcdu\xf9\xf9\x90\u04d0t[\xa4\xddN\xe9\xe3RI%n\xc41\xa3\xa3\xa3'\x0e\x98\u007f\xe7;\xdf\x05`\xf3\xe6M\xe7\x1c:th}\xbb\xdd\xc6\x18cD\xeaV}g\x9e\xba\x9e\x95\u02d7\x929\x17\xde\u0132*\x10\xd1\xe3\x80\xf8`A\u078f\x02\u40f2~\xf2P4W\xf9\x00\xc1F\x96(\x8a\x88\xacg\xd1\xf7\x1d<\xcc#[\x1e\u246d\x8f\xf2\u062e\xc7\xd9\xfd\xf8~\x0e\x1c>\xc2\xfc\xdc\x02s\xf3-f\xe6\xe6\x98_h\xe1\xb2\uc60fa|l\x94\x95\u02d7\xb2v\xf5rN;e\x03\x97\x9e\u007f\x16\x97]|.\x8b\xa6&1Y\niB\xb7\xdb\r9\xa9\f\xf0y\x91\x01\xa0\xaeO\x19p\xab\xf3\x02W1\x1b+f\a\x05%\xf2\xa3u\x02\xf2\x14\xe8\xc8\xfc\xbd0(\xab\xe2#,Y4\u01c6\xe6\x016\u03ef`{k)s\xc9P\xe0\xa5]9H\x13\x1d\x9cC\xa9\xd4\U000a628d]\xa9S%9x\x16\xda\x18\x01c\xf3OG~\a@\u0577\xf7\xf9\x81\xe1\xb4F\x91\xe4\xbc-\xc6gk\x9a8\xc2\xc41\xa6\u0440\x18\xe8&\x1e\xc8]\xeaW\xca+A)\xc5;*u7I\xadX\xdcJl\xb0\x9104dH\xdb\x0e\xed(\x9a\b\xa6k\x82[b}8_X\v\x9bJi,e\xc5j\x8d0\xdc\xf49\x9ef<\xf2\x86]\xf9\xb0\x90R6\\\x04\x82x\xb5fy\xd0T\xa8DU%\r\xbe)i\x00\xfa,\xfc\xb7+R\x81\xca*\xdd\u4389\x15\xf9\xa0\x15\xc1\x86\xa7\x91*\xdes\xa5\xe2K\xae\x95\xca\x1d\x85(\xf5\x03\u043c\v\xab\x9a\xf2\xf5\xe8\x9d1b\xe8\xb6\xe7\xe9v\x17JW\u01fcCSel|\x9cU\xabV\x9b\x13\x883\xff\xbe\x00z\xf3\xcd7\xaf\x9c\x99\x99^\x1c^\x8cZ\xf3\x1cG\x96\x8b\xce=\x83\xa9\xc9\t\xda\u0764\xa2\x1a\xad@\xb4\x0e\x8eR\xeb\xd7w<\xb5\xe1\xdf\xf1i\x96R.\x97/\xd84bK\xb3\xd9\xf4i/G\xa6\xb9\xff\x81M\xdc\xfe\xfd{xx\xf3v\xf6\xec\xdd\u03c1\x83\x87i\xb5;\x81:\x89\xe9&yjzV\x02p~\xe1U\u03efp\xa1\xcf\xce\xcd3;7\xcf#[wp\xeb?\u007f\x8f\xcfNNp\xea\xc9\xeb\xb9\xf8\x82gp\xcds~\x8a\xf3\xce<\x99\xb1\xd1Q\xc82\xda\xed6Y\x96URu\xf2g`zh\xa1~\xc5\u0353\xedN\\\x10\x8c\xd6\x01^C;[7Q2uB\xe3G\xe2\xec\x8f\xfd\xb5\x95\x90k\x11\xaf~\x199\xc8\xf2\xe6,\x1b\xda\ayxv\x15\x8f\xb6\x96\xd0u\x91W\xe8\x19W\xb1U8\x96\x8c\xb1\xfeI\x95\x9a\u0322\xb6\xd2^\xd2\xcd\xc1#Dm\x19,\x9e\x9f \x01$\xf3\xed\xd0B\x06n\r&\xb2\xd8\xc8bl\xe4\x95'\x91\xc1:\u023a]\u0124\xe1,\xa8\x9a\xbbU\xcaE\xca\u02bd\xd8\x00\xd5\xd2\xfa\xd64\f\x8d\xa6\xf5\xdcp\"\x98\xc4\x12\xa5\x11\x92\x19\xd2\xc4y\u0146\x84\xf0e*\x19\x00A\xf6\x87\b6\x12\x1aV\xfcf\xe7d\x84\xc4R\x94\xcfN)\xf6+\xea\x81\xe3!\x99\xcb)\x99\x83,\xf5\xa9>\x9d\xf0\xff\x89\u02fd#\x01+Hd\xfa\u0788\xbe,#-\xd55\xce\xf9Ak\xc1<I%*\xaeJs\x8b@\u2434j\xa5+\x05\x90\x17\x18\xe3*\xf4\x15B\xbb5K\xd2Y\xc0\x88\xad\xf9\xd5(\xca\xd4\xd4$'\x9f\xbcAO\x180\xefv\xbb\x02\xb0s\xe7\xce\U000ccc63@*\"Q)&p,^\xb4\x88s\xcf8\x85\x91\xa1\x06\xb3\v\xed\x1e\x05\xaa\xf6\xf0\xd6eK\x97\xa7\x87\xd7(\x9b\xa7\xa9\"\xaf\xc3D\x9e\xf5i\xbc\x1b\xa2\x8d\x18\x1d\x1e\xc5\xe0x|\xef~\xbe\xf9\u077b\xf8\xc7[\xbe\xc5#[vp\xe0\xe0a\x924%\xb2\x968\x8e\x18j6\x18\x1f\x1b\xf5\x96\xae\x87;\xa4iZi\xb3K\xe06\xc6\x10\xc7\xcd\x10,\x9bAeu8O\x05?2=\u00ddw\xdf\u03ddw\xdf\xcf\xdf\xde\xf4U.:\xefL~\xfeg\xae\xe1Y\x17\x9d\u0352\xa9q\xba\xdd.\xadv\xa72@\xd3\x1a\xad\xa0=\xc0\xf7T\xabg\xad\x1cn\xb9\xd9X\xfe;\\\xefZ|\xb8\xc5\u034fH\xf1\x1c\u007fHZr\xae\xf9\xbaA\x860d\x13N\x1d\xdd\u02f2\xc6\f\xdb\x16\x96\xb1ua\x05\xfb:\x93t]\xe4\xad\x12\xc41\xd8f\x98\xbeW\xabp]\xad\xccF\xa4\xa7\xfb)V\xc5T\x8aE,\xe9\xf1j/\x0fo\xa9\x03\x94\x94<r\xfe5\xc6Z\xef\xf2Wx\xa3keyE\xeb!\u0185[\xa4\xabk\x0fs\xbe\xda\x18L\xc3`\x87-Q\xd4 6C0\xaf\xa4\xb3)\u074e#\xcd\xc1?-\xb7@\v\x8aI\x15\x9b\x81\xcd\u007fm'\x14#\xd6[f8z\x17v\xbc\xa4/sy\xf5\xedc\xfe\u04ae\u00e5\xae\x88\a,\x14=\xb9|P\x14\xb1\xd2\xfb\xd0\xcb\xf7\xa6\u2a18'u\x19)m\xb7L\xe82\xb2BLZ\xbe\xf2\x92*$\x1a\xf2N\xb5\xa4XDzb#\xc3\xebe#\xe6f\xf6\xd1^8\x82\x89\xa2\xb0|\xe4_\x80F\xdc`~~~\xcb\xc4\xc4\xf8\x96\x13\x06\xcc7mzD\x00\xbe\xf9\xcdo\xd9\xfd\xfb\xf7\a\xfc\xd2\xe2buN9c\xe3z\u05af]E\x92\xf9\xb5\xe9B\xc1\xa2=&Z=\b\xad\x9800\x19\xec\xcb!O\x114\xaa?\xa1\xa4T\f\xa9\x13L\u0720\u0648\u0679{\x0f\xb7}\xeb{\xdc\xfc\x95[xh\xd3\x16\u069dnQ\x01\x8c\f\x0f\x95\xa9\u9744n2M\x969:\x9d\xa3o\x8by\xd3{C\x96e\xa8s\xc4Q\xeceVY\xea\x97I\"\x83s\xae\xf88|\xf8\b\xb7|\xe3vn\xf9\xc6\xed\\\xf1\xcc\vx\u00eb_\xca\u0557_\xc8\xe8\xe8(\xf3\xf3\v\xf5j\xb8\xd0E\x97+OR\xd3u\xe8\x93\x00r)4\xe9Z\xacs\xb9\xa3P:\x14F`\x90=-C\u0601\xfd\u0660\xac\x11\xa9\x87s/j\xcc3\x16w\xd80r\x90G\x17\x96\xb2ua9\xfb;\xe3\xb4]\x84\xc1a\xc5\r\xbcbt\xa0\x1d\x1a\x83\xe5\xb1\x15]\xb4\f\x98\xdf\xf6=\u007f-\xd7\xeds\xa9\x9d\xe4<o%\xc3\x14\xa9\x1f\xfc9\xa0\xd7\x1f\x8eT\x853\xa4\x81{\xb6&\xf0\xca&\xf0\xd9\x02\xd6\x1a\xe2\xe1\x06\xf1P\x8c\x1b\x03\x9d\x880\xb3)\xeeH\x02\xad<\x0e\xb1\x10\xb1P1%\xf5?\xbf\x9d\xa1\x89\x83\u0620C\x86\xacipVH(\xd5,I\xe6+\xef\xd4\xe5\xef|\xf8c%\xc4\xd2Ul\x03\xf2S8\v\xc3Q-+\xf4>O\xa1\x9e\x04\x8b\\)f\xa4tU\u0334\xbe\xdf\x14\x80\x06\xd3uu\xab\x82\x9a\xbfW-x\xd6\xdf\xf1b\x98\x9f\xdeO{a\x1a\x1b\r\x05\xae\xc7\u007fw\x14\u01d2en\xcb\xe5\u03feb\xdf\t\x01\xe6\xdf\xff\xfe\x1dr\xc9%?\x95\xaa\xaa\xb9\xf0\xc2\v7\x1c<x\xb0\x14\fT\u07a8SOZ\xcf\xf8\xa2\xc5>\x12\xac\xb2uXT/\xaa\x83\x82C\xca\x16_\x9e\xccx\xf4\x89\x83V\x16@\xdc!8\xb1\x8c\x8c\f\xd3\xee$|\xed\xd6\xdb\xf9\xcc\xdf\xfd=\xdf\xff\xc1\xfd$\x897M\xb3\x91er|\x9c\xd1\xd1Q\x8c1\xcc\xcc\xce07\xb7P\xab\xbc\xa3(\n`\xac\xfd\x8fQ\x95\xd6\xc2B\x81\b)e\xfaz\x1e\xde\x1b\xc51.P4Y\x96\x91\x05\xce\xfd\x9f\xff\xe5nn\xbf\xeb~\xae\xff\xf9\x97q\xfd\u03ff\x94\rkV2;7_.;h\x95\n\xd0\x01\xa0\xf5\xc4\x01]B\xc5\xdbk\x1cVMrr\xde\u06f2\xe0\xe7\xf5)\xd2:\xc7;l\a\xbe\xdbR.\xbdHOXF$\x19\x8b\x1bsLD-\u058e\x1c\xe2\u0445%l\x9d_\xc6\xfe\xee8\x9d,\xc6J\x16\xfc\xe7{\x0f\xb9:9\u0557\x88#u\xf8\x96\nG\x9bs\xad\xfdFg\xdaw=;J\xa7\xda\xe2\x00Q\xe7\x15-\xc1\x06 '\x9d\xfb_S)*L\x05Z\x99#s0\u0590bXh\x82\u0574\xb5\x96\xc8z\aFg\x14\x1d1hf\u04594x\xc7K\x90\xeeU\x0e(\xe3W\xf6Q\xa5\x91\x80\xa6\x8at\x95l!#1B\x1aA7\x12\x92HH\fE6kI\x93\xf8\u06575`\x9a\x86,Q\x9cG\xfa\x82K\x0f\xedT\xc0L\x87D\xa67\xcf|\xa0\xc0\xc9\xef\x06H\xed\x8c\x15\xa4\u0432;QL\xaa\x98\xc4\xd5_lWJ&\xa9\x1c^\x85r\u0265\xb4\x17\xa6I\x93\x84(\x1e\xae\xa5\x17\x18#\x9c~\xc6\xe9\x13\xcbW\xac\x1a>!\xc0\xfc\x96[\xben\x80l\u05ee\xc7N5F\xce\f\xa0&\u057d\xc2f#\xe6\xe4\rk\x19\x1b\x1fgvn\xc1'w\xd7\xe0z0<W\xdb\xfc\xa7c\xe0YW\x80\x18\xb2\x00\xe6\x19\u07bc\u007fll\x84\xc7\xf7\x1f\u14df\xf9\x02\x9f\xbb\xf9\xab\x1c:t\xb8,6\xace\xd1\xd4\x14\x93\x93\x93\x8c\x8e\x8e1<<D\xa7\xdb\xe5\xf0\xe12\x80$\xe7\xb3\xe38&\x8e\xbd^ya\xa1U\xff\xddZ\xa7U\xf2C@U\u0272\xccWL\x95\x96Y\x8a\r5G\x9a\xa6|\xf4\u007f}\x91{\x1e|\x84\xdf|\xcb\xeb\xb8\uc8b3i\xb5=\xad\xd3\xeb$\xa7\x03\xba\x97'Z5W\xfdbL\x8dX)\xdf\a\x13\u876c\xf0a\xa7F\x9a\xfd\xd83\xeds\x9a\xc2\xf4\xaf:*\x10\x19\u01d2\xc6,\x13Q\x8b\xd5C\x87x\xb4\xbd\x84-s+\xd8\u05d9 U\x0f\xfa\xd2\x03\xe8}\xae:\x15\"WzC\xa8k\x03\x90J}\x1f\x14\x16n\xc0\xe3-\\&M\x9dKpi\x86\xcb\xd2`o\xab\xf5\xfb\xa2j\xe1[8#zc)T\x19\x8a\f\xa6Fa{@\x8f\xac\xc5Z\x13V\xd5\x05\xd7\xce\u0226S4\xf5\x8a\x95\\\xa1\x97\x03\x9d\x06.]\x05\x92D=\x90K^LK\xa0\x80\xbc\n&\xb2\x824\xc4W\xeb\r\x13l\x90\xebY\x9fV\x80\xd8\x17H\x0e\xe7M\u01ea\x9d\x86S4\r\x87\xa4\x95\u06b6n\xcd\x17I*\aE\xe8\xfdL~\x9c\x06 W\x11\xc4\t&Q$\v\x94\x91\xcb\a\x9f\xc5\xf4\x13\u0514T\x95*\xc6Z\xba\xdd\x05Zs\x87k\x94\x98\x88\u0c8c\x91\x91a\\\x96\xdd\a\xec<!\xc0\xfc\u0421\xc3\x02p\xd3M7\x9ffmtN\xadA5\u07a8f\xe5\xf2\xa5lX\xb7\x8a\xc8\x1a2\x858\xac\xd1\xcb\xd1\x17\x1d\v3+\xa9\x00\xc7\xd33\xec\xf4K\x11\xa9\xda\xe0 \u8946\xa3\xe3cl\u06b6\x93?\xfd\xf3\xff\xc5?|\xf56@\xb1QL\x96\xfa\xaa|\xf5\xeaU<\xf7\xb9\xcf\xe5\xc2\v/`\xfd\xfa\xf5,Z\xb4\x88\x1d;\xb6s\xdbm\xdfd\xe7\xce]\xec\u0673\x9bm\u06f6\xd3n\xb7\xe9v\xbb~N\xb0x\t\x13\x13\x93\xec\u077b\xb7\xafe\xcei\xa827RI\x92\xa4\xf8\\><\xf5U\x96\xc1\x1a\xe3U@\xceq\xc7]\x0f\xf0\x1f\u007f\xfb\xfd\xfc\xb7_\xfdw\xbc\xec\x9a\xcb\x11\x91\xe0\xec&\xb5\x9c\xd2\xfc\u0412\xd2\x11\xfc\t\xe2d\x90\xe1\xf5\x01\\p\x1e\xacU\xe4n@\x16\u04cf\x05\xbb\xfb\xc1\xd1\xe4\x8b \xbdu\x81\a'\x14\"IY\u059ce\xaa\xb1\xc0\xaa\xe64\xdbZ\xcb\u063e\xb0\x94C\u0771`\n\x15\f\u07a4\xbf<\xac\x99\xf9\xf6\xc8l\xfb\x00=\xff_\xa75\x9fm*Fa9\xb5\x12\xca\xe8pP;\\\x9a\xa0YVKw\xea_?\u0329\x1a_\x8d\xa30\x12\x19\x0f\xacR\x89H\x93|\xaeaH\x9c\x90\x89\xd2\xed8\x92\x83\t\xc9l\x8a&Z0\x1e\x1aI}\xdb9\xf3+\xf0>K\xb3r\x00\xd9p\xad\xe6\xcfO\x15\x9b\n\xdaq\xb8\u06106=\xb0k$\xc5p\xba\x1cx\x12R8]\x91\xf8T\xbc\xbeN!\xad\x04k\xf4X\xe0\x1a\xa9\x843\x8a\v\u0665\xe1,\xc8\xc3,\xf2\f\x83T1\x9dJ\x9c\xa4\xcb3\x82\u02d7T(\xa9_\u007foY:\xadYZ\xf3Gz\xcc\u0494,K\x98\x9a\x9ad\u7b9d\xdf\x13\x91\xec\x97\xde\xf4K\x8d\x8f\xfe\xf9G\xbb?\xd1`>==m\x00\xb6m\xdbv\x92\xaa6\x8aN2\xac\xdef\x99c\xdd\xea\x15\xac\\\xb1\x8cV\xe2\xfc\xc6\x18Y\xc9\xd9\x1dc\xd8%\xc5e\xf9\xf4\x01y\xe6c\xa2\xfd\x96[\u042a\x8e\x8e\x8d\xf1\xe0#\xdb\xf9\xfd?\xb9\x81o}\xfb{\u0638A\x1c\x19\xda\v\xf3\x00\xbc\xe4%/\xe2\xcdo~3\x97\\r1\u02d6-#\x8a\x9a\xc5\xcf}\xc5+^\xc1\xcc\xcc,\xd3\xd3G\u0633\xe7q\xbe\xff\xfd\xefs\xe3\x8d\u007f\xc3\xfd\xf7?\xc0\x81\x03\a\x98\x9a\x9a\n\x11V\xd9\xf1\x1f\xa3j\x0fo\x1a\xb6\xd3\xc2.\xa1\x84e\x13\xa7\xca\xce=\xfb\xf8\xad\xf7~\x84\xf9V\xc2\xcf\xfd\xf4\xf3h\x1aC\xa7\xdd\xed\xc9\xc0|r\x15\xf9\xa0?f@\u022f\x14\n\x16\t\x16\xbdZR0OsY~\xac\x1f\xa7\x86\"N/_\xa1\xd7\xca\xd6 \x15*&2\x8e\x95C\xd3,j,\xb0f\xf80;\x16\x96\xb2}~)\xd3\xdd!\xef\xd0(\xae\x9c\xcb\u0220\xf7\xa6w\x96\u04eb\x89\xa9\x1eh=)>\xc5PS\xfa\xaa~M3\x0f\xe6\xce\xd5N$? 5\x18\xebm\xe0\xc8\x14\xe7\xb20T\xf4\x1cyd\xbceK\x8e\xe2\x12~h\x06$\x99\xd2\xe9d$\x89\"\xd3)2\x9b\xf9 \xe3\xf0d\xa4\xfaV\x85\x83\xcf8*1r\x94\xe1\x11\x198[\x1e\x90\xb9\x85\xae(\x98,\xc3v\x85\xac\xad\xb8\x86\xe0\x1a\x824\fD\x94\x9e\x97\xd6?\x17\xe9:4\xed9\xf0\x83O\xbb\x982tBzl0\xf2m\u06b2\xf8\x97\x9aK\xa5(\xd0\r*\x16\xf5\U000c0a85\xb6jM\xf2\x12\xdec\x1f\x1e\xd2\xed\xcc\xd1i\xcf\xd6<\xa4P%MR\x9d\x9a\x9c\x94s\xcf9w\xfc\xe6\x9b\xfe\x1eu\xee'^\xd1\x12}\xe4#\u007f\xde\x05\x98\x9b\x9b=\xe9\xb1\xc7\x1e\x030Z\x88\xf4\xfd\xab\xbdr\xc52\x16OM\x92%\t\xb1\xb8\xa2\xea\xabUY\x03\xd6\x02\r\xfa#i\xa6\xfb\x95\x19\xf8\x19L\xd3\x00\x00 \x00IDAT\x86.Q\xe1i\x8d*c\xa3#l\u06f5\x8f\xf7}\xe8/\xf9\u05b7\xee`dr\x92\xd8Z\xe6g<}r\xfd\xf5\xd7\xf1\xeew\xbf\x8bU\xab\xd6\xe2\x95;-\x16\x16fQ\xf5C\xa6\xc9\xc9I\x16-Z\x02\b\xe7\x9d\a\xd7^{-\xaf~\xf5\xab\xf9\xfd\xdf\xff\x03>\xf6\xb1\x8fs\xf0\xe0\xc1\x10\x97\xd7\xf3\xec\x02\xb7\u9723:,\x1e\x04\xf0\xf9A \x81\xb7\x8f\xc4\xe02\xc7\xec\xdc\x1c\xbf\xfb\xfe\x8f142\u032b^\xfa<27G\x92$E\xcdl\xa4\xf4\x16\u0441U\xfb\x13H\x19\xa2\x8eo}1^\xbd\a\xad<}`=H\xe1\"\xfd\xa7M\xb8\u0524\xcc*\xed1m\xf3]\xa2_\xa6id\tk\x86\x0f\xb1\xb41\xc3\xda\xe1\x83l\x9e]\xc1\xf6\x85e\xb4\xb3\x18+>]\xaag&YqN-\x97k*\xa3\x9e\u0497\xa7w6\xa1u\x85Q\x9e\x1eo\x83\t\xb8:\x87K\x124\xcd\x02\xc0jA\xc5\xd9(\x06c\xc8B\x97G\x14\xa3b\x8bMb[XDT\x06\xf9\xea]\xfe:\x0e\xda*d\x89bgS\xe2\x85\f\x93\xe5+\xed\u07a7\xa4\xf0\x8fq\x83\tN5R\x02b\xbenoKUHMq\xe2 \xea8\xe8\xf8\xe1\xa16\x8c\xff\x88\r.\n\xbe5\xc6K'\x1d\x9e\xc2\xd1\xfc\xec2~1\xce\xd4y\x16\xaa\xf3J\x18\x14\x82\x9eK8\xfd\u01a7\xe9:$+\xccX*\xd66=I\x00A\u032e\xea\xedE:\xadiZs\x870\xc6R\xb5hTU\x19\x1d\x1de\u035a\xd5\x1d\x80\x95+W\x9c\x18j\x16U]\xf5\u05b7\xbe\xf9\x92\xb9\xb9\u067e[\xd1Z\xc3\xdaU\xcbX41J\xbb\xd3%\x92\xca\xd0(\xf8G\f\uea5f\x1e \xa7\x02\xe4\xa5\u046e\xe7\xa1\x1b\x8d\x06\xfb\x8e\xcc\xf1\xc1\x8f}\x86[o\xfdg\u0196.crb\x82\xf9\xc3\aH\u04c4\x97\xbe\xf4%\xfc\xe1\x1f\xfe\x01\x8b\x16-\xa5\xdbm\x15\x83JkK\xb3\v?\x1c-S\xa5\x9a\xcd&\xa7\x9dv\x06\x1f\xfe\xf0\x9f111\xce\xfb\xde\xf7\x81\x822\xa9\xfei4\x1aDQD\xab\xd5\xea\x931V\x01_{\xac9\x9dSo\x05`=\xed277\xcf\xff\xfc\xe0'X\xb3z%\x97\x9ew\x06N\xe7\u0086\xa2T\xb8B\x18\xb0\n\xd3'0<^\xf73H\xc9.O\xcb \xfa\x89\x02\xfaQ\u010bR\x16\x04\xd2[\x1d\x88\x14\x99\xb0b\xbc6\xc7:\x88m\xc6\xc8\xf0\x01V4fX3t\x98\x87\xe7V\x05>\xdd\u0516\x8e\xaa\xd4Sm^_qZ\xef\x9bs\xd7\u01a4\u055f\xe0\x15 \x9a{\x91Wl\x1c\xb4\xc2\a\x18kI\x93\x0e{\x1fy\x80}\x9b\u007f\x88s\x19\x93kOa\xc5\xc6s\x18\x19\x9f \xa4^\xf8\x85\x9c\u04359\x85\xaeS\xda\x0e\x92\xc8\u03c0\x1a\v\x19\xf1\xbc\arq\x81O\xce\xc1\xbc7\xb0\x9b\xba\u04e1\x14\xe9\x12\xfe\xf5\x13\xa5\xf4twR\xd0\x1cyu\ud36c\u00b5\xd1q\xa88L\f\xb6ap\x91\x906<\xbfN\xb8n\xb5\xdaI9\x87\xba\x10\xfa1\xd0)I\v\x17H[\x8dc\f\xef\xbb\xc9\x14\x9b\x849E\x90$\xaa\xcb\xfd\u07a9\xa5~ke\xbadl\xc4\xcc\xe1\xbd\xcc\x1c\u068d1\xd6\xd3>^J\ud187\x87\xcc\xec\xec\u0716\xd1\xd1\xd1;\x00Z\xad\xb6\xfbI\as\x03\xf0\xf8\xe3;\xd3M\x9b\x1eiW;\x11\r\x14\xc1\xe8\xc80\xabW,\xa3\xd9h\xe0\x82\v]\x8d\xa9\xcb#L\xfa\x86w<M@^\xbf\xd7L\x80uk\x04L\xccM_\xf9&7\u007f\xf9\x16\xec\xf0(K\x97\xad\xc0%\x1d\x8e\x1c>\xcc\u06b5k\xf8\xad\xdf\xfaM\x16-ZJ\xbb=\u007f\xcc\xea\xb9\xfa\xa7\xd3\xe9\xd0\xed\xb6\x88\xa2&\xbf\xfd\xdbo\xe79\u03f9r`U\xdel6\xb1\xd6\x1e\xf5\xe7\xd6\x16\x8dz*\xf54s \x86F\xec=#v\xed\xde\xcbG>\xf9\xb7\x1c<2K\xa39\x84\x18\x9f\xb7\xeaz\"\xb7\x8e?\x16>\xfek\xd8o>\xf6\xe4\x17\xfc\x95\xbe\x9c\x80'\xf0(\xe8s\xd7\u0523\xf0\xe8j\x14\xb5\x12\xbcG@\xabv\u06a6\x944\x1aq\x8cEm\xce\x18\xdf\u00d5K\x1e\xe6\u00a9G\x99j,\x90\xa9\x1f\x8eW\x96\"\xfbsV\vu\x86\x14\u04b9\xa3\x0f\xf4\xa5\xc2g{\x83\xa8\x99N\xca|\xab\xeb;)\x97\x15\u065e\xbe\xdd7l\xff\x97o\xf2/7~\x84\a\xff\xf7\x17x\xf8\x1b\xff\xc0\x03_\xb9\x91G\xbf\xff\r\xd2n\v\t\u05cd\xaa\x97\b\xb6Re\xbe\xebXH\x95D\f\xd8\xff\x8f\xbd\xf7\x8e\x92\xe38\xefE\u007f_Uw\xcf\xcc\xeeb\x01,r \x88@\x80A\xccA\x04\xb3$\x9a\x92\xa8@E\u0496D\u0274l_Y\xbe>\x96l_\xf9\xfa\x9e{\xed'\xd9\xef\x1d\x9fg\xeb:\xe9]\xcb\u05b3h_\xcbV\"MR$%J\fb\x00s\x06!0\x00 r\\\x84\xcd\x13\xba\xab\xea{\u007fTuw\xf5L\xcf\xee\"\xf0<\x1f\xc1s\u0392\x8b\xdd\xd9\xd9\xd9\x0e_}\xf5\xfb~A\"l2\u00ba\xca0p2\x16\x13\x87F\u0198!?(\xdd/\xe2\x9c\xd1GrV\x88+\xfa\xa4Sow\xce$\xfa\x94a\xe19\xffR(\x83\xa0i\x10\x8d\x1bD\xe3\x1a\xd5\x11\x85\xeap\x82\xa8i\xdd\f)\xcb\xe8K\xb38\xb9\x80e\x15\xdc=3\x8b\\\u4ed2\x141ag\xaa\xe5\x06\x9e\"\xc3\u033d\xdd\x15\xf9\xc3\xcd\xd4)S\xc0h\x85\x91\u00fb\xd1l\x8c\x822Y\xafe\x17\x85a\x809s\x06\x0e}\xe63\xb7\x1c\x06\x80\x8b.\xba\x90O\x8ab\xfe\xdcs\xcf\u03cb\xa2\xca\x1a\u02ea\xc8\x1f\xda\x18\xcc\ua7c1\xb9s\x06,\x8f\x1f\xc2\xcf\xf1*/\u0669\u007f\xc2\t\xf3\x18\xf1\xec\xb1R~\xb0\u327f\xf6\xc6\x16|\xef\xce\x1f\x81c\x85@\x12\xf6m\u07c2}\xbbl\x00\xf5\xfb\xdf\xff~\\y\xe55\xd0:\x9eV\x11\xef\xbc\xd1\x15f\xce\x1c\xc0M7\xddX\xfa\x9c$I\xd0l6\v\xf8x{1\xef\xf6\xef\x94\xd9\x02BV\u041f|\xe6E\xdc\xf7\xe0:DQ\x05\u0489\x1f\f\x93\a_\xf11\x1f\xd3\xc9\x13\x9b\xe8\x04\xcc2\xa6ge\u0325\xef\x87:\xb9\xe1i\x05\x16%\xbe\xda\u0085P\vd\x1e\xd8\x01\x19\u0329\x8c\xe3\xfc\x99\xdbq\xd5\xc0\x1bX\u077b\x1f\xa1\u0408Y\xc2\x18j\xdb\xc1P\xa9\xc8%\xfb\xbc\x8d\u0398\xef2s\x06\x86\x01c\"N0<\xd1\xc4\xc8D\x82zK\xbb\u0754\x86\x8c*8\xbc\xebMl\xf8\xc9\xed\u063f\xf9g\x98\x18>\x84\xfa\xd0A\x1c\u0679\x05\u06de}\x18\xc3{v@\x86\u0591>a\xa0\xa9\x18-\x05(&\x18!@2@\xd4\x12\x88\\G\x0e\xb8n<\xfd<-\xca\u0736*\xa6\x05\x10\x19\xa4\xecX!\xa978 \x94}\x9dla \xce\x16\u0334\xcdN\x03\x93\xd3\xd7\x13\xca@$\f\x910d\u04e02\xa6Q\x1b\u05c8\x9a\x06B\xe76\xb4E_k\xdbY\u06cf|x)|\xc8%\xe5\xdbk\x06Z\xc6\xce\x03\x98<\x16\v2\x87\xd2\xf6Z@ H\x19\xa211\x84\xc3\xfb\xb7\x80\u074e;}\x8aVZ\x0f\f\f`\xf6\xec\xd9\u06c8h\x1b\x00\\v\xd9e'G1\x1f\x1b\x1b[5s\xe6\x8cU)\x1f\xdb/j\xfd\xfd}\x98=\xab\xbf\xe0Q\xc2\xecd\xf3\xae{4De\xb5\xf7\x84\r\xd0l!\u03e1\x96\xb0RE\xbd\x11\xe3\xbe\a\x1f\u014em\xdbA\x81@k|\x14\xadF\xbd\xd0a7\x1a\xe3\x19m\xf0h\x1f)\u03bdl\xd9)\b\x02Y\xda\xc1+\xa5&\xc5\xca\xcb~o\xfau\xcbC\xb7\x17a\x14\x06\x98\xa87p\u03cf\x1f\xc1\xe0\xfe\x03\xe8\x89B\x80\x04\x98\x84c\xb3\xe4v\xab'\x92i\u00a5{\x9f\xe9/\x0e\x1eWa\xda?\u05feK\x98\xca\u317a\rN]{g\xa5\xf8\xb6\xfb\x8bHci\xed\b.\x1b\u0602\xb7\xcf\u078a\x05\xd5\x11(\x12HXf\x96\x00\u0183\xac\x8a\xd3\x03W\xe8\x8bI%\x056E\xaa\xa2\xd4l\xa0T\x82VKa\xbc\xa50\xd4\xd0\x18j\x1a4b\r\xc8\x10{__\x8f\xfdo\xac\xb7\xf1t\x89\x85b\x92f\x1dC{\xb6a\xe2\xc8 \x88$\x12m\u0408\x8d\xa5(\x12A\x93\xe5\xe5\u0216@Xgk\xdce\\A\u0576\xbb\xee(\xe2\xd3\xd9\xca\"g\x87\b\x9d\x17u\x19[h\x03\x9c{\xbe\xf8\x8b\x85\xff\xb3\xc20\xa4\v\xe3 \xc3\bbFe\u00a0:n\x10\xb5\xac\xc8'\u0760\xa7\xb9\xab\xac\xed\u0417\xb5\xb1\x9fgktq&!\x13\x03R\xce\xee\xd6\x13\\\x11\xfc\u0646g\x94\xee\x16W\x19D\x98\x18=\x8cC{7e\xc3\xcf\xf4\xacj\xa3\xe5\xc0\xc0l\u031f?o=\x00\xfc\xf1\x1f\u007fY._\xbe\xf2\xe4(\xe6\x1b7\xbe\xaa\x06\a\a\xb3A\x9f\xaf`\xeb\xed\xe9A__/\x8cq\xd1j\x1e\xf9\xdf\x0e\xcf\xe9-\xf3~7Y^g\xfe;\x04Y\\{\xeb\xae}\xf8\xe9c\xcf\x00\xac\xc1\xaa\x93q\xf4\xd0C?\xc5#\x8f<\n\xa2\x00\xc7P\xcb\x11\x046\xb7c\u02d67a\f\xa3R\xa9\xa0\xaf\xaf\xaf\xd0]\x9b.\xf1&e]y\xe9\x82BdS\xe3I \f\x02\xbc\xbey+\xd6=\xf5<z\xab\xa1g\x1bzb|\x14\xa9t\xad=\xf6\x84\xa2b\x8eT{\xb6ig\xc7\xde\x1e\xd0L\x1d{\x83\xee\u007fkY\u007f\xc0\xe4D/d\x1d\xf9\x02h\baS\xafz\x83\x16\xce\xea\u06c3+\xe7l\xc2Y\xfd{\x10\x89\x04M-\xa1\x8c\xf0\xc6\xc0m\xbf\x81\x8a\xdb\xf9\u00bb\xa7\x9c7h\xc06\x1cZ[:\"\xd8F)N\xc4\n\xe3J`xx\x04\x83\xdb\xde@\xab>nAA\xa3\xecN,\x8eaT\x02\b\x89\xa6f4b\x03\xc5\x04&\x81\x04\x84\x04\x04\xd1\x12\b\x1b\xce\x02\u0597\x9cr\x8emw\xc2VS\\%\u073ek\xb6\xf0\x8c\x15\xe80d\xcbv\xde\xc4m\x8b\x19\x173VH\x1b\xdb\xd9\x1b\xc7\xccd \x88\r\xaa\xe3\x1a\xb5\t\x8d\xb0e t\t#\x93m\xf7M\x86\xddB\x98\x8f\xf2Y3\xa8e\xa1#\x06\xdbP\x0e\xb7\x80\xb1\xe1\u04acS\"\u01dc\x010zd\x0fF\x8f\xec\xc9\xee7v\xbb\x820\bDT\xa9\x1e\xbel\xed\xda\r\x00\xd0\xdb\xdb\xfbs\x8f\x97g\xc5|\u03de\xbd\x18\x1b\x1b/\x14\xa1\xf4D\xf6\u052a\xe8\xed\xadA\x9b\xd4ZV\x14\xd8%\x9aDi\xfft\"\x18,9\xb4\xe2\xba$X\xdb\xd9$Qx\xe9\x95\u05f0w\u07c1\u049f\r\xc3\x10\xbbv\xed\xc2]w\xfd\x00Z\u01e8\xd5z;\x86\x93\xdd!\x16\xb8N\\\xe2\xe0\xc1}\xb8\xed\xb6\xdba\x8c\xc1\x9c9\x03\xe8\xef\xef/\fO\xfdb]\xc6x\x99\xb2\u023b\xf7$\xa4D%\n14:\x81'_\u0608\x91\xb1\t\bY\xec\x96\u028b\u0431\xc3-8Nw\xc6\xe9\xa0\xf7<\t\xd43\x95?\u03d4E=\xc3\u05d1:2\x81\x04C\n'\x1b\x17\x8c\xf9\x95\x11\\2k+.\x1d\u0602\xf9\x95Qh\x16HXt.\xeel\xfd\xc8\xd9\xd8\x0f\x93\x89\u007f\x00_\xe3\xcf00:\x86\xd61\xb4\u0459\xe5-\x01\x10l\x80\xb0\x8a\xc1]\xdb1\xb8e\xa3\xf5\xc6\xf7\xa6\n\xba\xd5@\xcf\xdc\xc5\b\u7702\xb1F\xcb\xc2* (\"(\x10\x82\x16\xa1\xda$\x04\x9ar\u1303X2\x8eu\xdb\x11\xe2\xc9\x0e&u\xc20\xed\xc5\xd1\x0e\x1e\rdl R\U000bbdc0\x907t\x04\xac\xa7\xb8L\\QO\xb1k\x06\u0096AuB\xa32\xae![^\xb4\x90scd\xc30:\xb7\x83Na\u007f\xa9\fD\xa2s\xe8\xc8\x15\xfd\xfc=\xd87[8\x1fD\x10A\x00\x954ph\xef\x1b\x88\x9b\x13\x10Bf]\xbf1\x86{{{10{`\xd7G>\xfa\xf1\x97\x01\xe0\xf2\xcb/\xc3IS\u0337n\xdd\xdaj4\x1a\xa5\u017c\xb7\xa7\x8a\x9ej\xc5%\xebp\xe6H\u01c0\x1b4\x15\x15\x1f'\x14\x06\xf0H\r\xa9<=\f\x024\x1b\r\xbc\xfc\xcaF$q\xdc\x15\"\x91R\xe2\xb6\xdbn\xc3_\xfe\xe5_\xc1\x98\x04\x95J\x0f\xaa\u055eI\xf1s!\x04\xaa\xd5\x1eHY\xc1\xd0\xd0!\xfc\xe1\x1f\xfe\x1fx\xfa\xe9\xa7\xd1\xd3\xd3\x03\"\x01{\x8cDa\xfb\x97\x0e@}X\xa5\xfd\xff\xdd\x06\xa2\xa9\xe4\xdf\xf2\xcf\xed\"\xb1y\xfbn\xbc\xb1c/(\b\xf3F\x8c\x8e\xbf\xc0v\xde\xcb\xdcm\xea1\xed\xd7,-\xc0\xdc\xd9G\xfbW\aO\xf1zG\x15\x9dG\x80\x91\xf6#\xa5\u03f1d\x904\x10\xc26cU\x19cM\xdf~\\=g\x13\xce\xe8\u06c7\x904\x12\x0e\xb2\xdcX6\nF\u06ee\xd9h\x05\xa3\x95\x15\x00\x19c\x8b\x88\xfb}\xc6\x18(\x1dC\xe9&4+K746\xf4\x02N\xcc\"%\xe1\xc8\xf6\xd71\xb2{+d\x18Z\xbb\xc0\xecZ0\x98\xb9\xe2LD\x8b\x96#Nb\xb0\x14PD\xd0L\b\x9a@\xa5\x01H\x96 \a\xed\xb0+n0%\xf88\nU\u0583\xe1:q+\xa2I\x86\x1c).\xaes\b\x86<\xdf\xf0\x8c^$rA\x15\xa50\x8d\xe79N\xb0\x01\xcbA]\xa3:\xaa\x10\x8d\x1b\x88\x98\x8bR\xfe\x14z\xa1|6!2n9\x1cf\xee`Hmrr\xb9\x9f[\xe7\x86\x01RFhN\x8c`\u07f6\x97aX\x81\x84\u022c\x01\x94R4o\xfe<\xacX\xb1|\x1b\x11\xed\x01\x80\xb5k\xaf8y\\\x13W\xacX1\xe7\xe0\xc1\x83mrg\x06\x91@__\x1f\xaa\xd5j\xb6\xaa\x8a<\xab+\x0fa\xed0\x14:1\x83O\xe6\xdc\u0396\x1d\x8fIJ\x81\xb1\xf1:\xb6l\xdd1i\a\xcc\xcc\x18\x1a\x1a\xc6\xff\xf8\x1f\u007f\x84\xad[\xb7\xe2s\x9f\xfb\x1c\xce>\xfblDQ\u037b\x9a\xd3\xddW\xdem\x8f\x8d\r\xe3\xb9\xe7\x9e\xc3_\xfc\xc5_\xe3\xbe\xfb~\x840\f\xd1\xd7\u05cbF\xa3\x81\x89\x89\t\x18\xa3\n\xf8w\xd9\xe7\xdd\xdeS;,\xc3\xccPJ\xd9E\xa4RA\x14\x86\u0633\xff\x10\xde\xdcy\x00\xabN[\x05\xc3\rOP\xc7G\r\x8a\xe4\xae+\xe4\x05w\x94\xe1\xde\xc7\xd7\xed\x17zF\xe2i\xf5\xed]\xbd<\xa6\xf9\xbe\n\u040c\u021d\x03\xb3\x81.3\xa4+J\x02\x1a\v\xab\u00d8\x1140\xa72\x86\xd7\u01d6\xe0H\\\x03\x99\x040IfA\x9b\xdf\x00\x1e\x01\x9d\xac\xb3\x9f6\t\x14+(\xd2Pnn\xc4Y\aK\x10Q\x15c\a\xf7a\xf7\xf3\x8f i\xd4!+\x95\xac\x90\xeb$F\xcf\xc0B\xcc[y>$\"$\xba\t\x04V{\x1f4\x19QL\b -\xbd.-\xc0&\x87U\xc8cw\xb4w[\xbe\x10\xa7c\x89%\x14B6:\x96N\xff\xe9\xda\u044c\xdb\x17\x03\xedL\xca\xd9[\xfc\x8d\xa5\x87\x8alwdM\xceSk\x01\x91\x18\x04\x82\x90T\b*\xb2\xb35\x18\v\xb7\xb0K\u0720XC45\xa0\r\xc8\x18\x90vN\x93\x86Q\xd0\xf702\x85)9!\x163chp+\x0e\xee}\x1di\f\x94\x15\x9fYk\x8d\xfe\x19\xfd\xb8\u448b_\xec\xb6#\xfe\xb9-\xe6\xcc\\\xf9\xda\xd7\xfe\xfa\xba\xc7\x1f\u007f\xc2Q\xab\xd2H)\x06I\x81J\xb5\n\x11F.\u02638\x1cI\xf3\x1b\xa9M\xa4q\x82Jyf\u07aa!m\a\xe3\x9c\xea\xc7\xc6\xc70661)\x1e-\xa5t\x8e\x88-|\xfd\xeb\u007f\x8f\a\x1ex\x107\xde\xf8q\\~\xf9\x15X\xbat\tf\xce\xecG\xa5R\x05\xb3\xc1\xc4D\x1d\x87\x0e\x1d\u009bo\xbe\x89\a\x1f|\b?\xfe\xf1O088\x88(\x8aP\xadV\xd1j\xb5\xd0t\x1e*\xedB\xb2\xf6\xae<\xc3\xef\xa6\t\u0527\x05\xdd0#\f\x03\x8c\x8cO\xe0\xf0\u0430\x1d\xdcN\xa3\x1b\x9e\xea\x18v\xf1\x13<a\x90J'\xc2\xec'^r\x9bUr'R\u007fBo\xb3BZ\xbd\xfdL8\xf1\x1b3\xa1/h\xe1\x9c\xfe=\x98\x1d\xd6\xf1\xfc\xa1\x85\xd83\xd1\x03\x06#\xa0,t\xceI\xdd\x18\f\r2\x02\x06\n\xac\x15\f)h\x97\xf8c\xfc\u25aa7E\x80\xe1\x9d[px\xf3+\xa0\xc0\xcdi\x8c\xf3\xf3V1\x06N=\x13\v\x96\x9f\x0f\f\xd7!\xc9\xca\xd9E\x02DM@\x92\x04\x85A\xa1\x90\x921\x85\x98\xc3\x0e,\xd0;\xe8L%K \xe73\x00\xe2\u026c\x89\x19\x1d\xb2X\u07fa?\xf5L\xf7\x95\xc9^\x88\xb3I\x8f\x81\x82\x15\x10\x05\x96\xc6\x19\x18\x03\xa1\x00\x11\v$5\x01\x1d\u0602n\x17\a\x03\xd9L \x12\x97\u03ea\x8d+\xea\uc0ba\xd9\xdbY\x14m\x1a\x88$t\x12c\xdf\xf6\xf5h\xd6G\xb2\x9dA\x9a\xe9\xda\xd7\u05cbY\xb3f\xed\xfb\xe0\a\xde\u007f/N\xb2G\x00 \xaa\xd7\xeb\xb3\xe28.x\x8d\xa4\x85\xc6xqN\xec\x05\xf5\xb6[A\xe5\xcf8\x91\x18\x10g^\"v[l\xb1\xb3V+\xcf\xdb,+\xe8\u030c(\n\x11\x86\x01\x9a\xcd\x16\x8c1\u0632\xe5M\xfc\xe9\x9f\xfe\u07e8\xd5jX\xbe\xfcT,]\xba\x14\xfd\xfd\xfd\xd0Z\xe1\u0211!\xec\u0631\x03{\xf7\xeeE\x92(H)2\x1e\xb9R\t\x94\u0496O\u0705\x9d2\u067f\xa7\u04f9k\xadQ\xaf\xd7\x11\x86\x12:\xd1h6[^D\xd6\xe4\u066aS\xb6\xad\xa5\xfd/\x9f\x90\xba\xe9\xef\xfa\xd3\x10\xe7t\xaa\"\xbc\xebc:\xc1\xd5\xc7\xea\"@S\rh\t\x10\x82All\xb2\x0f1\x16\x84\x838\xafo\x04\x15^\x80\x1d\xf5\xb9HL\x88@\x98\xec/I\x17\"m\x94\xc5\xd1YC\a\x8e\xcd\"l\xfa\x0fs\u0389\x11A\x05\xad\x891\x1cX\xff$\x92\x89\x11\xeb\xab\xed\\7Y)\x84\xb5>\xcc_}1j}s\xa1\x1a\x13\b \x00e Y@\xb2\r\xba \x9b\x9e\xec\xe2x\x8c\xc5\xcd\x1d\xae\xdd\xee[\xe3S\x83\xbb\xda\fS\x11\xf2/\x1ea*\xc1\\\xa8}e\xc8\xe1\x96IN\x90\xe0\xb4\xe3v\x87N[\xa6\x11\t\x824\fj\x1aH\u0148k\x02\xa6F\xd0\x06\x10-\x05\xd9T@\xcat\xf1\x84P\xc4%\xa3t\x8f\xf9(\x84D\xdc\x18\xc1\xbe\x1d\xaf@%-\x9b\u0104\xdc\\k\u05acYX\xb6\uc517\x06\xe6\xcc\u007f\x19\x00n\xbd\xf5\x1f\xf0\xd9\xcf\xfe\xdaIS\xcc\xcd\xf8\xf8\x84\xb2\xea\xc8\xf6\xb9\x90\x8d\x87#O\xfak\x9d\xce\u0716\x06o5\x14e\u007fG\x00\xbb\xb5\xd5,\xc0\x9cg1NVP\x93D\u0658\xb8\xcc\xd2\xd6~4\x1a\r\xbc\xf6\xda\xebx\xed\xb5\xd7\xdb:y\x01!l*Q\xfa\xbbS\v\xdb\xd4\x1dq\xb2\x9d\xc0d8<\x11ux\xbb\xb4o\xfd\xb4\xd6v\xf0Jde\xdf\xe9\xce\xe7X\xecR\u0607?\xca\xfb\xe7\x13a\xc1R\x9c\xb1\xe5\x11}HM\x99\xb2!+OVk\xba\x0e@\x8f\xb7\xb8\x17\x1aV\x01\x10\x1b\u8111$\x06\xb3\xc2\x18\xe7\xf6\xc7\xe8\x93-l\xae/\u0138\xa9A\xa6>Bl\xb1p\xc3\x16;gg\xfc\xc5\x04ha\xa09\x8b\x9b\xb2\xc7X\x06\x18\u06ff\x03\a6>\xe3\x1c\xfd,<c\xbb\xf2\x04\xb3\x96\xac\xc1\x92\xb3\xaf\x02\u01c9\u015c\x99!c\x03\n\xa5\xfd \x99moEb\x99&\xe4\x0f\x01y\x92\xa3B\xc7rd\xb8m\f\xde6E\xed\xf6\xfb&3cb\x82\x1f\xf0$`G\x06`@*F\xb5\xae\xa1\x8c@R%PC\x01-\x9dY\x06\x93i_9\x8ao\xbb\xfd\x8a\x1d;\xbc\a#\x87w\x81\xd9@P\x98\xef@\xa4@\xadV\xc3\xc5\x17_\xfc\xec7\xbfy+\x00`\u0252%'Ug.*\x95(\xb0l\x8c\xce!]\xa2\x8dU\u04f9[W;\x0f\xb5\xc0\xf3\xc76\u07ad}\"}\x9aRC\xa8\u020d?\x15B\x18\b\xd4j=\b\u00e0\xf4\nK\xa3\xbe\xe28F\x92(\x10Y\u0225\xb7\xb7\x17J)\xe7\x88\xc8\x1dC\u0254\x8db\xa1\x14\u04f1K)[8\u02be\xef\u007f\xcdz\x9c\aY\xb1n\x1f\x86\xfa\x18\xba\x94\x12\x86\x81J\xb5\x82\x9e\xbe^\xebvF\xf9\xed6\xd5}[\x0e]\xb0\xe7\xfbGm\xde\x18|\xc2\nz\xcem*f\x98\x9a4U\xa6\v^\xde\xfe\xbe\u0465_\xc4\x14\x85\u007f\x1a[<\xc0\x00\x891\x88\x95UkJ\x10ze\x13k\xfa\xf6\xa1\x16\xc4\xd88\xb1\x14\u00ea\u05f9\xbc\x1b\xb7\vt\x1f\xc2\u01b2\x19\x10\x8ct\x16e\xce}\x90\x82\x00\xac\x15\x0e\xbd\xfa\x1c\x1a\x87\x0f8\x9c\xdaR\x16M\x92@\xd6z\xb0\xe4\x82w\xa0\u007f\xee2p+\x86\u0400d\x82 \xe1\x02 \x02\x90\xb4!\x9dB\xdbB\x9e\x8es\njN\x9ad\xeaL\xd3\\\xe1\v\u01efK\x11\xf7\xfcMJ\xa7\xa7\x05\x03\xb2\xe2l\x92)wo$r\x89G\xc6v\xed\xa4\x81\xb0i Z\x06\xa6\xe9\x8c\xc9REQA9\x9a\xbaVr\xdbN\xc45\x92\xcc8\xb8\xefu\v\xb1Pj`g\xdb\u02be\xde^\u031f?\u007f\xec\x82\v.\xfc\xe7\xf4\x1d\xbf\xe7=\u05df4\xc5\\\x000}}}:-\x8e\xed\xc5|\xbc\xde@3V\xae[\xcco=M\x02\x86D1_\xf2-0O%\x17\xa3\x10\xc2 d\x9b\x93\xd2\xdb\u05cb\x81Y3;\xe0\xc3\x0e8\x91M\xc6\x18\x01r\xee\xb8\xefh\xa8\x94\xca>\xfc\xdc\u03f4\x93\x9fL\xe19\x9d\x0e=}\x1d\xff\xb8\x96\rd\x02)\xa1\x12\x85\x19\xfd\xfd\x98;g\x00P\x89\x8b\xdc\xe0\xa3,\xac\xdd\x06\xa1\xd3h\xb0\x8e\x037\xb7;(ktea\x96\xdc-3\u035d\xe4B<F9\x8d\xb1\xb3\x80S)\xc5q\xda\xfc{\xd7\xd9%\x86\xd1T\x06\xb1\xdbm\xba8\x13TD\x8c\xe5=\aq\xc1\xcc\xedXP\x19\x82fF\x02@\x933\x81%\x06\xa4\xf3\x10\x97\x04\xd3\x16\xa2,\xc3\b\xcd\xe1\xc38\xb0\xe1)\xe8\xa4i\xbblv\xbbZ\x15\xa3o\xd1r,\xba\xf8\x9dV\xc3n\x18\xc2\x10\x88\x05\b\x02\x02\x815\x88r\xa6\xdfT\x12T\x92\xc1\xf2\x93\x14g \xa7\xf0r74\x8d;\xa1\x19*\xdb\xc1\xa4\x04s\xf2\u03d4?\x11\xednKIi\u051bv\xac\x17{\xe8\x9c\xf2\u050a\x8f\x82F\x02\xd9H\x80\x96\x8d]$\xce\x191y\x00F\xa7\xae\x98\x9d\xe3\xa8J\x1a\u063f}=\xe2\x86sJL\xf9\xff\x86\xd1\xd3\u04c3s\xce9gd\xed\u06b5\xdb\x00\xe0\xa7\x0f\xff\xf4\xa4\xc2\xcc\x05\x11M\x1c92\xf4Hoo_\xd61\n\x91\x9f\xacCC\xa38<\xdap\x13c\x91\x8d&\x99\x84\x93\xe4\x8a\xc2BM'\xb8X\xa4\x97\x9f-\xe8\x1aP\tzk5\x9cq\xfai\x90R\xba\x01\xc9\u4152\x99Q\xaf\xd7Q\xaf\xd7]\x81FV\xa8\xfd\xe7\xb4\x17\u0763-\xe4e_O\x17\x8a\xc9\x06\xa4B\b\x90\x10PJa\u1885X\xbel1L\x12\x17\xb2\x0e\xdbi}\u0736\x1b)DV\xb6a\xaa\xa5\x93\f>\x91\xe7\xa73\x9b\x91\xdb \x16\xf6\xfe\xc5m\x03\xb72\x04\x17m\xcf/K\x02\xa2)\x8azZ\x9bbePO\x14Z\xda@\xbb\xf0cCl\x8d\xbb\x84A$\x13\x9cR=\x88\v\xfa\xb7\u151eA\x18\xa1m\xd6&\x19\x1b\xf8\xe0B\x1f\x8c\x042\u0751\vxf0\xf6\xbd\xf8(\x86\xb7\xbf\xe6-\x1e\f\xa3\x15\x82J\x0f\x16_p\rj\xa7,G\x12\xa5\xbclw\x8f\x91\x84\x10\x81\x1d\xb8:\xb7E\x169\x13\xa7-\xdf\u00a5u\xf9\xd6\xc0T>\xbc\xf0W\xba\xec \xe5]6u$\xf7\x96X\xb7\xb9\x00\xeaN\x8eZ\xf9\xcc%\xb5\x0f\u0228\x8b\xe9\xa03\xb5\xd9Mi\x96-\rn% \xa5A\x89\x06%\x16b\u024c\xd4R\xa5g\xa7\x8e\xdf\xd5&\x89\xa1\xfd[ph\xdf&h\xadlg\xee.~!\t\xb5Z\rk\u05acy*\xfd\x99w\xbd\xf3]'W1\xb7\x85E\xef\\\xb2d\x91\x17\xb8\x90\xe3\xd1\xfb\xf6\x0fb\xf7\x81C\xa0 \x82f@\xb1\x80rFF\tK/\x1c\xf8\xd8\x13\xe5\xa7[\xd0\x05\x18P-\xf4\xd6B\x9c\u007f\ue648\xa2\bR\n\x04%B\x9e\xb2!c\x8e}s)~\xcd\x05Iq\x97\xf72I!?\u0582/R:\x90\x90Xu\xda*\x9c\xbax\x81\r\u0560\xce>\x94\xa7\xdeEg\xf1_\xed\x16\x00\x85e\xe1-ak\xe5%\xbbH\x85\xccc\u0734o\x8cL\xe5FbT\xc0\xf6\x8b\x85\xbf\x9dr\u074d\x9b\x9e^\x8dqb\u040a5bm\xa0a`\x88\xed \xd3a\xe0\xf6s\x03\"\x8d\xb9\xd10\u039e\xb1\x03\xa7\xf6\f\x02\xd2 \x11\xd6\x02V\t\x82\x16\f\x0e\x8a]\xb2\b+\xa8\x1f\u0687=\xcf=\x00U\x1f\x87\x90\x01RoXf\x83\x9e9\v\xb1\xe0\xbc+Aa\bE\x06p\xf3\x13!$\u0205@\x93\x8b\xe31.~\xad|\x97EE8\xa2D$D\xe8\x92\xcc\xc8\u0716\xe1\xc6m;\x1f\x9e|\x95\u6a7e\x99/<\xa9\x17LZ\xc8ar\x0e<\x19\x9d\xd9\x05\xb31\x99\x0f\xba=^\u02024\xc8[\x10\xb3\xe1gz\x92\x05ap\xf7\xabh\x8c\x1d\xca-o]\xf7\"\x84\xc0\xa9\xa7\x9e\x8a\x8b.\xbeh\f'\xe9C\x00\xc09\xe7\x9cs\xa8V\xeb\x19I;]\xbf0\xed\u067d\x1bol\xd9\x0e\x11F\x0e\xf2\xb0)6\x99y\x0e\u02b9\xe6o\xd5@\x94\xb5BU\x18\x9c\u007f\xe6J\xac\\\xbe\x14Z\x1b\xd4*\x95\fC\x9f\x0e\xe3\xa4\x1d\xf2(\x13\xfc\xb4\x1f\a\xff\xdf\u077a\xff\xa9\x86\xa4\xed\x8bF\xf6\xbaB\xa0\u054a1{\xde|\xac\xbd\xe4\x02T\xa5U$\u0494\v\\\xb7\xed4g[SP;^z\xfcA!\u04c3y\xb8\xa3\"p\xb7\xad{\x17\u0225\xd0\xd9Sw\xbdj\xc7\xf2\xec<TZJ\xa3\x99h\xc4\xca\xc1e\xf0\x9c\x18\x05`\xa4\xfd\xdc\x00\xd0d;\xf6\xd9\xc18\xce\xed\u0749\xd3\xfb\xf6AV\f\x1a2\x80\t\x00\x13\x10L\x90\xd3\xe0HH\x18\xad\xb1\xeb\x89\x1fbd\xfb\xeb\xa0 \xcc0v0C\x86\x11\xe6\x9cq!f,Y\x05h\xab\xb2\x04\b\x90\xd6\xdeV\xc8\xc021&\x9b\x00\xfb\u01e0l\u0562\x1cb\xf1\xff\xdf\xf9ze\xe9\xbcT\x84E\xb9K\xe5\xa6.\xa0V\x99\xa4\x97;M\xbe\xd2,Tc48\x8d\xd6\xf3\xa0\x99\u0531\u04a43\n\xff\x85\n\r\x02A\xab\x18\u00c7\xb6\xa3\xd5\x18\xb7B!\xb2\xac\x190#\x10\x01.\xba\xe8\",Y\xb2tN\u0588\x1e\xd8\u007f\xf2\x15\xf3\x9bn\xfa\u012e\x993g>_\xadV\v\x85\x8b\b0q\v\xeb_y\x15\aG\x1a\x90A\x94y@\v6\x1e\xa6k\xcf\xde[\xc9\xcd'\xef\x06O\x9a\r\xacX4\a\xef\xbf\xf6\n\x1b\xfe`\fz{z\nR\xfb\xfco\xa0)\vy\xd9\xd7\xcb\x16\x81\xe9`\xe4Sa\xe9e~.\x96k\xaeq\xf6\xd9g\xe2\xf2\v\xdf\x06\xd5j8/\xe7\xee\x1b\u074ecCE\xf7C?\u00ac\fu~\xabe\x14\xbe\xea\xb3\xe8\xc7RfH\x8bB\xf9.\xff\x9by\xca\xe3\x90b\xf2\xc6\x10Z\x89F+\xd6HT\n\xa7\u5fd7Hd\xfe\x1e~\x97\x9f\x9a\xc6\xf5\xc9&\xde\u05b3\x1bg\xf6\xecA5J\x90\x04\x12:\x12v!p\xe7KTj8\xf4\xfa\v\xd8\xfd\xf4O\xa0\x93\x96\x1d\xcei\x9d\xe5\x99\xd6f\xcf\u01d2K~\x01\u044c~p\x1cC*\x9b\xedIA\x88 \xeaAX\x9b\x81 \xea\x01U\"\x10\xdb\xfc\xcbT\x00\xd5^c\xb9\xc4\xd1\u0187\xc1}\u031b\b%\xfe\xc4\xed\u017bDB6i\xf1\xc7\xf4a\xba6a\x13ke\xad\x12\xc8\xd9%\xb8\xc5.\xb3Vg\xbbS\xe2\xc22OY~if\xeb/\x04\x9a\x8d\x11\xd4G\x0f\xc3h\x95\xeaNA\xb0\x011Q%\xc2\xd9g\xbf\r\xbd==k\x98\xb9\x17\x00\x16-Xx\xf2\x15s\"\u06bfx\xf1\xe2\x17\x97,Y\f\xad\x15\xa7t\xba\xf4\f\xbf\xf0\xc2\xcb\u0630y'\x82\xde>$\x06.C\xb2\xbd{x\xeb\x1f\xe9\xefH\x92\x04Q(q\xdd\u0557\u0f37\xadF\xbdaC\x97\xa3\x8c\xe125d\u04ad\x90\xfb?\x9f\xb2QR\xdf\xf22\x1c\xbd\xfdg\xa7\xea\xc8\xcb\x16\x0e\xa5\x14f\u03dd\x8b\x8f\xdf\xf0n,\u83e0\x9d{e7Z_g!\xf7\x90f\xf2h\x89\\\x84Zh:p\xcdQ\u0737\u075e\xe7g\xc3\xf8\v\x89\x81\x80\xcaF\x8f\xd41\a \x94\xd1]=\xdf\xf16\x90\x80J\xd0[\u0144\x96\xd2h%\x06\x8960i\xf7\xe7\x89hR\xf4\xc1\xea(\x9c\x96\xc2\xc0:\xfc\xb9\xc56\"\x85\u0555}x[u\x17*Q\x82$\xb4\x8d\x02k\r\x11U\xd1\x1c9\x82]\xeb\xee\xc5\xc4\xe0\x1e\x90\b\\\xa1\xb2\x1d\xa5\x90\x01\x16\x9c{\x05\x06N?\u07fe+e \rA\x86UT\xfaf\x81a0\xb8\xfd\x15\xec\xd9\xf2\f\xc6G\xf7CBXO\x16\xe7\u007f\u4670\xa3\x8c\x8a\x98\r;}\xaf\xf7)\xb8\x9e\xed;\"\uee8b\x9atDZ(\xd4\xe5\xa3j\u01ddJ\x83\xa6\x8d\xa3x:\x98\u02a4\x10Jz\xdc\xe1\xabx\xd3y\x1c\xb2\x9cU\x16\x9cE\xf0%q\x1dq<\x81v\x034f\xc6\xec\u06730\u007f\xfe|0p\xca\xfe\xc1\xc1\x15'%\xcc\xf2w\u007f\xf7\xb7\x11\x00\x9cy\xe6\x19\x8f.[\xb6\f\xb6\x86\x11\xfb\xc6Q\x87\xf6\xec\xc4\xf3\u03ff\x88Fl d\xd0&\b\xe1\xd2\xc1\xd4[]\xd0\x1b\xf5:V\x9f\xba\x18\x9f\xb9\xf1}\xe8\xed\xa9adtlZ\x01\x14~\x10s\x19\x9c\xe2\x17\xdd\xf6\xe7v{\xbd\xa9:\xf5n\x98\xbaOO\xfc\xf0\a\u078dw\xad=\x17\xbaU\u03feF\xd3=\x9e\x85y\x11\xb51\x02\x90m[\t\xc7&\xe3'\x0f~\x9d\xee\xf3E\xa1\x88\x13\xb4#\xfc\t/\x98\xba\x9b\x11\x17M\x8d\xd2vl\xfc\xc9)\x00\x13\xa5\xd0J4b\xcdP\xa6\xe8]\x9e{\x85pV\u0233L\x06\a\a\xe4\x9f\x03\x01i\x9c\x1a\x1c\u009ap?\"RPL \x19\x02`\xecy\xea>\fn|\x06$\x84\x83\x0f8\vH\u8677\x18K.{/*3\xe7@\xc7-\x04\bP\xad\xf5\xa3\xda7\x80\xc3{7c\xddm\u007f\x8cG\xbe\xf3\xdf\xf1\xcc=\u007f\x81W\x1e\xf8&\x0e\xee\u0610\xf9\x9bP\xfa\x9e:'\u02c5\x93g\u00d7)\xd3\xf90g\xc1B\xc5\xed\x19O\x1f\x18+37\u0387\xa2\xed\xab\x03\x95\xee\xf4\n\xbf\xde0\xd8\x1bhZh\xcb\x0e\x94\xdd\x06\x06B\n\xab\x1b\x91\x00\x02\xfb|f\x86\xce1\x00\xb7x1\x94N\xa0un\xbf\xe0\xdfS\xbd\xbd}\b\x82\x10\u0198\xc0\x18\xb3\xf8d,\xe6\xc1\x19g\x9c\xae\x00\xe0\u04df\xfe\xe5\xd7\u05ed{\xfc\x95\x81\x81\x81s\x87\x86\x86\xb5\x10\"\xf0\xb9\u044f>\xfa\x04\xae{\u05d58g\xc5B\x98\xc6h\xc7\xcdM\xc7\xd1\xd1\x1d\xed\xf6=\xbd\xd0T\x92\xe0\xfaw^\x867\xb6\xec\xc07\xfe\xe5.\u0109\xea\x1a\xbe\xdc\xceR)+\xc8e\xf0J{`\xc7d]\xf7d\xde\xe6e\xbc\xf5\xf4\xf3+\xaf\xb8\x14\xb7\xfc\xe2\a\xd0\x1b0\x92\x89\xb8\xa4\x93\x9a^\x05M3\x1f\xb9\xacu\xe5Ni\xfdQ/\xba\u053dw+{rj1\xe2\x17x\xea\u03a1(_@\u0494\x9f6V\x8f(\xbc\x0f\xb2\xb1kJ#I4\x942\xce\xf8/}\xae\xb7#1\xb9\xb29+\xdc\u0739\xb0\xd8\xee\x92Q\x81\xc22\x1c\xc6(j\xd8&\x17\x82\xc3\x1a\x06\xd7?\x86\xed\x0f\xdf\x01\xd5\x18w\xb0\x8e\xce~JFU,\xbd\xecz\xccY}\x1e\x8cJ\x10\xf5\xf4\xa3\xda\n\x11\x1f\x1e\u0096\xf5wc\xfdO\xbe\x81\x83\xdb\xd7C\x06\x15\x04\x95\x1e\x1c\u067b\tZ\u0168\xbd{.f\xcd\\\x04\xc4-\x97\r\xea\x18\xd7e\x91vm\xc3\xc2\x02\x15\x9b\vzN\x97\xf9Yf\xa4\xc0%M\xb9\xe7\x8cE\xed{8*\xc1\xdcK5\xa4\xb9\xf5\x1e\u5db4\fc\xbf!\xad\xb9\x98\x88\b\x81\x04\xa4\x84\v\x87\xb7_W*\x8fLd'\xf6\x82\xb1\xdd:\t\x99\xbd\x96/\xf3\x17\x82\xd0l6\xa1\xb4\x02\x119\u01d8\x93\xb03\u007f\xc7;\xae5\x1f\xfa\xd0\r\x92\x88\xde\\\xbdz\xf5\xed\xa7\x9d\xb6\nJ%$\x84(\u063an}\xf5U<\xfe\xf4Kh*\x03\xc8\xc8c\xb1\x14/$?\xaa\xeb-\x85\\\xc8\x0eDf\xd4*\xf8\xfc/\u007f\f\x1f\xff\xe0\xb5\x16\xcb\x14\x02A \u074a/\xa6\xd5A\x97\r?\xa7\xd3}\xd34(\x91\x9d\x03R*\x18\xef_|\xd1\xf9\xf8\x83/\xfc\x1aV.\x99\x87\xa41\x81\"1\xef(\x8e\x87o2W\xb2\x9aNWV?9\xccB]Xp\xd4uqH\xa5\xfd\xb2\x00\xa1\x14\xa5\xfe<\xe5\xfa\xc1\x93\xa2\xea\x86\x19\xb1\xeb\xc8\x13m\xd0NXJ\xc3s\xb4\xb1\xf4S\x93u\xe6^A7\\$\x8b8\x88J\u00a0\x0f-\xac\xa0\x83X\x1c51\xba\xf3\rl\xba\xf7\u007fcl\xdf\x0e\x80lt\x19\u0600\x84]\xac\xe7\x9f{9N\xbd\xe6C\bzf \xa8\xf6@\xd7'\xb0\xfd\x99\x1f\xe2\xfe\xff\xf5y<\xf0w\xbf\x89\xfd[\x9e\x87V\tT\xdc@c\xf4\x10\x86\xf7m\xc1\xf6\x97\xeeG\xfd\xf0V\xf4\xf5VQ\r\x05\xa2\xc0\xfa\x10Q\x19w\\x\xe1q\x18\xd1\xfd\x00\x00 \x00IDAT\xd7\x1eP\x9e\x96\xaf\xa2-.\x95br\xdc\xd1aSgW\xc0m\u06c2\x12\xdf\x00\x9f\x19\xd6\x16\x14W0@3\x86\xf3\xfd\x18\x01\x14\x10\xc2~\x89hN\x80\xb0*\x10HB \ba@\x90!\x15h9\xe9.\x93\x89\x11\xd5z\x11Vz\xbc\xeb\xdc\xcd!H`hx\b\x87\x0f\x1d\x063\u01f5j\xed\xe0IY\xcc\x01\xe0\xf3\x9f\xff\x1c\x03\xc0\x97\xbe\xf4\xfb\xff\xb0`\xc1\x82W\xe7\u035b'\xb5\xd6I\x8e\x9d\x03\x80\xc6c\x8f?\x8d\xbdG&\x10Uk@\u6652\u039a\xf3<A\xf1\x16\x16\xf4\xc2n\x93\b\x8dF\x03\xf3\xe7\xcc\u0117~\xe3S\xf8\xc8{\xdf\x01\xad5\x946\b\x83\xa0\xc0\x97'J\x83e\xb9k!\xf7\xc39\xa6b\xaf\xf8,\x98\xc9\xf0\xf9\x82\x0f\xb3\x10\b\x03\x99\xdd\x17\x97^|\x1e\xfe\xe8\xf7~\x1dg\xaf>\x05\x8d\xd1!\x90N\n\f\xa1\xa39\x16>\x83\x8f\xa9[\x97{l\xbb t \xa3\x9dI\xa2\x9d\xc8k9y\x90\xc0^h\fO\xb9\xab+[\u0532m<\x01\x86\xad\xaa3Nlr\x931~\xa2\xbbU\x19\xb2a\xe8\xf4\x1c\xc1\xb3d5\x1e\u0312\x0eA}\xa6Qv\xae\x193\"\u00bc\xe6~\xec{\xe4v\x1c\u07ba\x11B\x06\xd9@\u03fe\x9e\xc6\xccS\xd6`\xcd\an\xc1\xc0i\xe7\u00a8\x18\a6<\x85\x17n\xfd\n\x1e\xfb\xdb\xdf\u014e\xf5\x0f\xd9\u008f\xd4w[g\u007feR\x1fA%\x14\xa8\xd4$d\b\x04\x92\x10\n\x82\xf4\xb8\xe4)\xcf=KXB.2b?j\u03c3\xda\v;\x9aB\xc25M\r\xb3L\x87\xcd@\xe5\v.\xda\x03\xa6\xdd{c\x00$\t\x95\x9aD83\x80\x9c\x19@\xce\b@\x91\xb5\xb0\x15\x82 #\x01\x11\n\x8b\x97S\xbe\xa34l\x10\xf5\xf5\xa3\xda7\xdb\t\x8cR3\x17\xfbs\xf5\x89:\xb6n\u074aV\xb3\u065a=k\xd6\xf0I[\xcc\xdf\xfb\xde\xf7\x9b\x83\a\u007f\x06\"\xdaw\xdey\xe7\u07fex\xf1\"\x18c\x82\xf6\u0405W_Y\x8f\xf5ol\x87\x96\x15\xb0\x88\n\x81\xc3\xfe\xb0\xe6X\xb1\xd9c\x83]\x18\x13\xe3\x138e\xe1\\\xfc\xe1\x17o\xc1-7}\x00RJ\xb4Z\xb1\u06c2\t\xaf\xf0\xa2t\x88\x99\x16\xe70\f2FL\xfa\x9ct\bZV\u0627\x1b\x19GD\b\x83\x00Q\x18@\x1b\x03!%\xde\xf7\vW\xe2\xff\xfa\xaf\xff\t\x17\x9d\xb9\x1c\xad\xf1\x11\xc0$v\x11\xe4\xe3\xc8\xfa,\xe1\x06w\u04d3L\a\x06\xa3\xd2\r\xb6/\xe4!\xb4k4\u065b\xa4\x14\xf0m?K\x93\xa6\u0388\x9d\f\xbeI_[k\xb6<\xf2\xc4@i\xce\nq\xdaYg\x10\x8a\xd7yk\xb6\u0676\x99\xba7\xed\u0239\x04KwEM\xca\x00\xa1\x14\xd8\xf9\xe2:\xecy\xfe\xe1\u0316\u0546Y\x18\xb0V\xe8_r\x1aN\xff\xe8\xe70c\xc9*\xecy\xe6\x01\xfc\xec{\u007f\x8d\xe7\xfe\xd7\x1f\xe0\u0347nCc\xfcH\xe1\x04\xa5@Xz\x8d\xac\xb9\xecz,Ys!\x94j\x81\xa2\x00\x14\x10\x84\x04Bi=\xd2\xf3\x18{r\xd0\x05\x15l\xe3\v\xd4\xe0\x8ek\xa0\x18p\u0285E\xb6\u06d5Q\xb6\x9cs\xd7U\x9e\xdb!3\x8f\xff\x9d\x82\xf9i4]\x10\x10\xc2^\tY\xb5\xa1\xe5\xa8\x12\xb8O\x80+\xb6\xe0KA\bB\x02\x059L\xc3l\a\xcf2\xac\xa0\u007f\xeeR\x04Q\u0545h\xa7\xf5\\@%\t6l\u0600\xf1\xf1\xf1\x04@|Rb\xe6\xe9'\xf3\xe6\x9d\r\x00\xf8\xf2\x97\xbf\xfc\u05ef\xbc\xb2\xfeS\a\x0e\f\xae:x\xf0 \v!\xc8hmW\xc7\xc68\xee\u007f\xe0a\\\xfe\xf6\xf30/\xea\x81i$n\xa8E\xe0\xe3\b\x1cFigw\x14\xc396\x18\x9b\x98\xc0\xbc9\xb3\xf0\a\xff\xf9f,\\0\a\xff\xf8\xdd{\xb1\xef\x80\xddm\x05\x81\x8df\xb3\xa2!\xd3u\x90g;;sTC\xce2\x1c<\xe5\x8e\vB\xb6c\xd1Z#I\f\xe6\x0e\xcc\u00a7>\xfa^\xdcr\xd3\xf5X0w6\xc6\xc7\xc6\xf2N\x9c\x8fn\xd2\xd0\xce5\xeff\x99;\tb\xda\xf5\x98\x97\ty\xca;u?\x8a-\x85[\x8aT\xc0b\x01\xe0i_\v(\x81s\x00\x02\x1b\x03e\f\x942P\x86a<\xd6\n\xbcn.\xeb\xb8\xfd\xee\u06dd\u007fk\x16\x87,t\xc5\xd7\xe5\x14\xf1X\x81J%\xc4+?\u06c8\xc7~\xf2c4\xc6G\x10\xc8 \v\xb0\x00\x00Y\xa9\"\xac\xf5\xe1\xf0\x1b/a\xd7\x13?\u0111-\xaf`\xfc\xc0.\xdb};\xac\x97\xbd\xa0\x8a\xec\x98\n\x81\xb7\xff\xe2o\xe0\x1d\x9f\xfdo\xe8\ud7cf\xf8\xd0((\xb0v\xbd\xc4\x02\x82\r\x02\x10X[H(u\xea\xed\xd8y\x15`\x95\xfco\xf1=\xc8;\x05}\xdcf`E\xe8n\xb0P\xb6?\x9b\xfc\xdc1\xe5\xda\x02\xe3\xbar!\x80\xa0B\x10\xbd\x02$`=\xd0\xc1@\x85l\xe6\xed\x98\x01\x9a\x1a$\x800 \xb0\x01\xb4rW\x98]\x851g\xe9\x19\xa8\xf6\xcd\xc2\xd8\u1f50A\xba\xc6\x11\x12f\xbc\xfe\xc6&l\u07f1#:\xf3\x8c\u04eb'mg\x0e\x00_\xfb\xda\u07e4\xc5\xe9\xc85\xd7\\\xfd\xed\u014b\x17AJA\x00Xx\xfc\xedg\x1f_\x87\xa7\x9e\xdf\b\x84\x15@\xe4~-\xbe\a>\xb7\xc1q\xddvle\xf9\x90|\f\x85,\x1d\xfc\x8d\x8fO \x8a$>\xfb\x89\x1b\xf0\xe7_\xfe\x1d\\\u007f\ud568U+PJ\xbb\xf0e{s\n\xf7\xc6|(\x85\x99\v\xde,\x19\xde\xea\xbc]\xcaR\x84\xca:\xf0J\x14\xa1\xbf\xaf\x17\xbd\xb5\n\xc2@\xba\"n\xe5\xfc\x97_|\x0e\xfe\xe7\x1f\xfd\x16~\xe7\xd7n\xc4\xfc\x81\x99\x18\x1f\x9b\xc8\xfbL\xe6)C\xe1\u0288\r\x93A)e\x86\xa74\xc5\xebLVP\xc9y\xae\xf8\xc3\u0254f\xa8\xb3\xef\x14\u007f\xdex\xbd4\x1d\x85B\xb8\xdb{\xd3\xc6 q\xb0J\xa2\f\x8c6\xc5\x01&\x17\xab\x9d\x0f\xa1\xd8N\x1c\x9e\xab\xa4\xbdn\r\xdb@\x04\xbf\x90\xb3[\x84\xab\x95\n\xb6\xed\u0703{\xee\xfd\t\xf6\xee\u0647@\x06 \xe3(\x8c\x9c\xefE\x87\xb7\xbf\x8a\xd7\xef\xfa\u007f\xb1\u3c7b1\xbe\u007f\xa7\xed\xf0\xb5\x82Nb\xdbE\xb6\x1d\u0359\x8b\x97\xe1#\u007f\xf2\x0fx\xdf\u007f\xfd\v\xcc^\xb1\x14\xaa\xd2\x02\u034d\x80>\t\xaaJ\xa0\"\xc0!A\b@z\xd3^r\x01\xcb\xd4n\x8a\xc5\xc5\x15=\x8dQ\xeb6-\xe1\u0098\xbam\xc8\xc9\u076e\x94\xac\x15n[`\u06f6\x87\xd9\xf6\x8b\xac\r\x88\xf3\x9e\t#B8#\x80\xac\x884\xe7)_mC\x02f\x04\xe0\x1e\t!\tR\x10\"ae\xfaLn\x80\xaa\rf/X\x89\xdeY\v\xb3\xc8>\x9b\xf4d\x11\x84\x83\a\x0f\xe2\xd1G\xd7U\x01\xcc:\x19\x8byV\xa5\xef\xbb\xef>\xe4\x9f\xff\xf8\xf9\xe7\x9e{\xe6\x86\u077b\xf7\u032f\xd7\xeb$\x84p\x1d\f\xa0[M(\n\xb0\xf6\xca\xcb\xd1\x1b\x00\xa4Z\xb9\n\x14>\u038cLxT&\xbb\xe6)\x98\f\u01c2\xa5\x83\bIb\xb7_+O]\x8aK/>\x0f+\x97/\xc5\xd8\xc4\x04\x8e\f\x8d\xa2\x15\xe7\x9e\xe4\x16O\x9fj/P\xc4\xcdE\xdaq\xb7a\xf1A \x11\x06!zjUDA\x00\xa5\x12L\u051bH\x94F\x10\x068c\xf5r\xfc\xc6\xcd\x1f\xc1o\xdd\xf21\\|\xee\xe9H\x92\x04\x8df\xcb.*<=4{2\xfa\xe7\xd1`\xe2t\x14\xc7sRn{\x16\xea-\x9c\x8f9\n\xe5\xc1\xef\u0667\xc2\u01e7\xfb~\x94f$Z#Ql\x87\x99\x1e\x94\xd2\x0eo\x15\x98*\xe9\x87\xc9\v\b\x15v]T0\xb8\xb1\x9d%\xa1Z\xaba\xff\xc1#\xf8\xce]\x0f`\xc3k\x9b!\x88\xa0\xb3\xc5\u0763X\x1a\r\xa3\x14H\xda\x0e\xd3\x18\x03\x9d\xb42\x8c\xdc\u007f\x84=3p\xce{o\xc2\xf5\xbf\xff\xe78\xfb\xba\x0f\x00\x14 \x9e\xa8[Ec(l!\x0f\xc8S\xef\xe7<x\xe3\xe9\x06:\xf4><\xd5\xe2\xdc\u05a3\xfb\xdc\xc7\u051d\xd3\xf7\x04\xa0N\x02{\xd1\x1e\x82\u0684`y1OU\xb6 \x82\x11\xd6\a'\f\x80\xea\x8c\x00\xb2O\u61da\xdbV[\x01P(l=\xf1h\x9a\xc6\xc1a BT\xe9\xc1\xc8\xc1\x9d8\xb8s\xa3\x1b<\xcbl\b\x9a$1\x94J\xa25kV?}\xeb7\xbf\xf9\x12\x00l\xf8\xd9\x06|\xfdo\xbf~r\xc1,\x00\xf0\xcf\xff\xfcO\xf8\xccgn\x01\x11\x8d\xddy\xe7\xbf\xfd\xfe\xe6\xcd[~8::\x9aejjG\xd3{\xee\xc9'p\xff\xe3\xd7\xe2c\xef\xba\x18\x11\xd5!\xd8\xf6h\x06V\x19\xea|z\\\u03ae\xbd\xf0L!\xee\xc4\xcfvo\xff\na\xf2h\x94\x92A\x98\xf7oA\x04\xc3\x1a\xcdF\x1ds\x06f\xe1c7\xbc\x1b\xef\xb8\xe2b<\xfd\xdc+\xf8\xc1\x8f\x1f\xc1\xc67\xb6\xe0\u0411a\xc4q\x92]\xb3\xa1sS\u0303c\xadC\x9b\x10\x02\x81\xb47g\x10H\x84a\x80@J0\x03\xcdV\vq\xac\x1c\xceg\xcbV\xa3i\vx\x14\x04X\xb0`.N_u*\xde}\xf5\xa5x\xe7\x15\x17\xe2\xd4Es!\x880Qo@+\x93\xfb\xb1\x1cC!o\xff&s\xf7\x8ez\xba\f\x16\x9e\xe2y\x9d\xb4F\xfb_\xe1\x06\xe1\x94\xf5\xe5m\xbcc\x86\xe7\x13s\xec\xf0\x9b\xd2\x06q\xa2\xa1t^\u023b\xc17&c\xaaX\x9a[\xb6\x8bB\x1e\x05\xc7\xce>\x04\x19\x14\x96\xe3\u0646\x81 \xac`\xdf\xc1a|\xef\xae\a\xf0\xcc\v\xafd\xe5Li\x95u\x85\xfe\xae\x10\xb0\xbb\x04\xad\x94\xe5B\x1b\xed\"\xcel\xd1\uf67b\x10\xa7]\xf5>\\\xf8\xe1_\u018a\xf3\u05e2\xda\x17A55t\x12\u06f8\xb8\xb4\xb0I\x003$\x10\x11DU\xc0\x8c\v\u0206\x06\xb7\fLb\xe07\xf9T\xb0\x9f\xf5\x8ac[t\\\xc7}\xd7\xee\x91\xce\x00\x89\xa2V\x81\xb8\x1b\x82\xce\x1e1\xb1Dn\xeae\x86\u06a2.\x11\bF\xb5F\x90\xfdd\xff>\xc3\x1d\x17Zv<%\x03}\xd2>o\x9c\xed.\x84\xac2\xd7h\x8d \x8c\xb0\xec\xac+\xb1m\xfd\x03\x18\x1f\xda\x0f\xe9\u03ad\x10\x84\xb8\xd9\xc4\xe0\xe0 ~\xfa\xf0#\xb7\x00\xb8\x15\x00\xce9\xfb\x9c\x93\xaf3\a\x80;\xef\xbc\v\x9f\xfe\xf4\xa7\xe8\x95W6\xe0{\xdf\xfb\xfe\xe6\x9bo\xbe\xf9\x8a\u077bw\xaf\x1a\x19\x19q~\xdb\xf6\xc0\xa9V\x03\a\x87\xc7q\xee\x05\x17`\xc1\xac^h\x15\xbb\x9c\x14\xeeX\xa9\x05\xa5\x1d\x1c\xa6t\x05i/\x06G\xd3E\x16;t\xbb\x93h%\x1a-C\xe8\xef\xeb\xc1\x19\xabW\xe0\x1dW\\\x84\v\xcf9\x03\xf3\xe7\x0e\xa0Z\x89\x10\x86\x12Ji4\\\x1a\x11{\x16\x9c\x96yb\v\xba\x10\xd6\x14\x89\xd9 I\x14Zq\x828I\x10+\x05\xad\xed\xb6?\b\x02,\x987\ag\xaeY\x89\xf7\\{\x05>\xfb\xc9\x0f\xe3W?\xf9!\\v\u1658\xdd\u07cb$\x8e\xd1h\xb62\x95 xz\x1e\xe54I\xa7\u0716\xa8\xd5q\x1c\u02e4\xf1S\x15\xf1\xc9\xf6+T\xb6s\xc9(\x87T\n\xed\x1co!\x87W\xc8\x13e\xa0M\x1b\xe5\xd3\xfd7E\xc7\xd8%c\xa5\xb4C\x98\u0734)\x03\fr\x97\xd7\xccy/\x9f\xd9\x11\u00a8\x82##u\xdcv\xefCX\xf7\xf4K0ly\xeb\x16.3\x05\xb0\"\xdd\xea\x1b\xada\x8c\v\x84v\x1d#\xb3\x81\x88jX~\xcd\rx\xe7o\xfd\x9f\xb8\xf23_\xc0\xa2\xd3V@\n\x89\xa4\x11\xdb\u039d\xa8\xb0\x9b\xc9p\xfb\x80@U\x01\x11\u065d\xa0$B@\x94u\xab\x85\xf9J!\u05c1\xa6X\xa2i\x92\xe99O~\xc6\u067f\u6f06\u0311\xde-\u00db`\xd2\u0667$ \x14\xa8V\tQ\xbf\x00E\xe4\xe6\f>\xc3\xc6g\x0f9\xf5\xa8 P(\xec\xb9L\x87\xdb\xee7J)Q\xe9\xed\u01e1]\x1b12h\xe9\xa1\xf6\xfc\x11 \x88\xe3V\x93\x84\x103\u05ad{\u426f\xfe\xf9Ww\x01\xc0\x0f\xee\xbe\v\xdf\xfd\xcewO\xae\xce\x1c\x00n\xbe\xf9f\xfe\u05b7\xfe\x15\x00\xf0\xb1\x8f}\xf4\xcb\x1b7\xbe\xbafdd\xe8\xd4\xf1\xf1\t\x04\x1c qE\xef\x8dg\x9f\xc4\xf7\xff\xed^,\xfd\xf5\x9b03\xac\xa2\x157\xec\x1c\x03\xbap\xf2\r\x8a'?s='\x1f\x9f+\u0798\xdd:\xef)\a\xa1)\xae\u02e9\u007f\x8cF\xac\x12\x8c\xaa\x04\x95\x00\xa8U+\xb8\xf4\xa2sq\xe9\xc5\xe7ahx\x14[\xb7\xed\u0126\xad;\xb1u\xc7\x1e\xec\u06bd\x17\x87\x8e\fatl\x02\x13\x13\r\x8c\xd7\x1bh\xb5b(\x13g7\x8f\x10\x840\fQ\xabV1\xaf\xaf\a\x03\xb3\xfb10{&\x16\u039b\x8b\xe5\xcb\x16\xe3\xf4\xd5+p\xc6i\xcb1o\xee\x1c\xabtT1Z\xcd&\x94\xd6Y 62\xc6\xca\xe4\u007fYG!\xa7\xa2\xa0\x8f<:0g\xdd9\x15\xc6ZeCOB\x99@f\xfa\n\xcc\xf4sS\xe2\f\x95E{u\x89\x8c;\x96G\xa2\r\xe2\xd8 q\x1dy\n\x9f\x14\xf8\x17\xe9V\xdc}O\xbb\xffS\xc7\xd5d\x1c\xd4R\x98\x9e\x17p\xdeZ\xa5\x82\xfd\x87\x86q\xfb\xbd\x0fc\u0753/d3\x14\xed\xf2_\x89\xf2\xe0\u2b10\x1b\r\xa3\x13h\xad\xb2\xfc\\f\x8d\xfeS\xcf\xc4\x19\xef\xff4.\xfd\xc5\xff\x84\x85K\xe7\x80\x12 \x99HlxEV\xccr;Z\xff\x9cQ\u0298\xe9\x11\x90\x01A\x84\x04\x1eW \xc1\xe0\x86\x81J\xf2\xa2^\x9e\xa1\xcd%g\x87\xbao\xb9\fw\xf2\x89\xdb\x02*\x88\xbaL\xb5\xc8\x19\x1e\x93o\xfae\xb3R\xab\x91@\xb5_\x02\xbd\x1aF%\x85\xd0\xed,!(C}(\xd7K\x10@\xbd\x02\xa4%\xc8%/13\x14+D\xb5~\x9cr\u0595\u063b\xe99\xa8\xb8\t#\x04\x041\xa4\x1046>\x81}\xfb\xf6\r|\xeb[\xffz#\x80'\x00\xe0C7|\xf8\xe4\x83Y\x00\x9b\xccq\xc7\x1d\xb7\xd3G?\xfaq\xbe\xf0\u008b\x9f\xfc\xf6\xb7\xff\xe5\xbf\x11\xe1\xdb\x0f=\xf4S0\xc0ZI2\x8e_\xfb\xc0=\xf7\xe0\xfc\xb3V\xe1\x86k\xd7\xc2\xc4\t\x88\x15$\x11B\xe8L\x05\xa6\u0754\xd5\x06t\v7\xec\xcb?'\U000b71b9Y\x97\x1f\x14>]@\"\u007f\xbeM\x92\xb1#\xb9\x04\t$\x94\x02\x8cj\"q\x98w\u007fo\x15\x97\x9cw&.\xbf\xf8\x1c\x18\xadq\xe4\xc80\x06\x0f\x1d\xc6\xd0\xe8\x18\xc6\xc6\xea\x18\x19\x9f\xc0D\xbd\x89X\x99lK/\xa5@\xb5RAoo/f\xce\xe8\xc1\xbc\x81Y\x98;g\x16\xe6\xce\x19@\x14\x06H\x94\x82N\x12$\x8d:\xd8(\x10k\x14\x93QyJ\u018ao\xc91\x15\xbd\x93K\t0\x9d\x0e\xe8\x93\xcd*\xcaX-\x8c\xa9\xb8\xdf\xe5\xfb\xa9N\\\x9f\x8f\xbf\x90;\x8f\x15m\x8a>+\xec\r\u074d[\xe5\xd8+\xea\xe9n\xb0 i\u271cW`\u007f\x00\x10$Q\xadF844\x8a\xef\xde\xfd \x1e}\xeaE\x18c \x85\xcc\x02\xb7\x995X\xbbW%\x8b\xe3\x1a60*\xb1\x96\xc5\xeeQ\u96cde\xef\xfc(N\u007f\u07e7p\xfa\u06ef\u00ac\xde\x00z\"v\x05\x93;\xa9(\xdc~ls:!\x81\x80\x88\x80Y\x04\x84\x04!\x05$'\xd0F\x83U\xdb\u0312\xf3\xb3GGA'`\ued01!t\xb2f\xca2~\xb3\xb6\xc4\x17$\x11\xa0% #BuF\x88\xa0\x9f\xa0\xc1\x80&KQ4\\\xec\u0385\xb0Ceo\xdea\xbb'\x80\xfa\x02H\xc3\xc0\xb8\x86\xd66\xf4\x03\x82\xb0`\xf9y\x985\u007f\xb9\xc3\u0383\xec\xaf\x0ed\xc0\a\x0e\f\xd2\xd3\xcf<\xb3\x96\x99{\x89h\xe2\x99g\x9f\xa6K\u07fe\x96O\xbab\x0e\x00g\x9ey\x06\xff\xcd\xdf\xfc5\xfd\xf6o\u007f\x81?\xf1\x89O}w\u01ce\x9d\x17\xef\u0739\xf3w7m\xdaDA\x18\x98VK\v\x00\x18?\xbc\x1fw\xdc\xf9C\x9c}\xe6j\x9c\xbeh&Z\xe3#\b\x1c\x13 \xed\x92\xd1\xfd\xb0\x02\xd7\xd1\x11\x15\xbe\u05ae+$.z\xefOBq\x9d\xa4(rF[\x94\x99\xb0\u0240\xb4\xb6)\xe0\u0292Qc\x87\xeb\xcf\xec\x8d00s)\x02!!\xa4\x04\x89<\x88\u00f8\xad\xb0v]\x85a\x86QvK\xad\xb5F\u0728\xa3U\xf7L\xf5\xc9\xce\x0e:\xcb\xeb\u051d*\x95\u031e|\xeb\x84BG\x0e\x94\xda'\x91\x1757\x99\a\x13w\xe9\u0727\xc2\xdd\xfd\b\xba\xf6\xe1X\x99\xac\xe8\x98\n\xb9J\v\xb9v\x18\xb9\x1d`\x1a\x8f\a\x9e\x0f=\xdb\xff>G\x93\xe4\x1c\n!/\u07cc\xbc\x95\x92a\x17\xf7j-\xc2\xc8\xe8\x04n\xff\xe1#x\xf4\xa9\x97\xa0\x8d\x81\x14\"\u03cf\xd5\x1a:i\x02\xb0.\x9d\xa9\xb1\x96\x0f\xf9\xf4\r,\xc6)\xe7\xbf\vK\xdf\xf9a,\xb9\xfc]\x98?o6\xaa&\x86i6\x8b\u0175=z\x8d\\0\xb2\xdf\u9ca7\xaa5\xb0.\x8f}\x01H\n\xbb;PV\xcd*\xdc\\\xa0]\xb2\xcf\x05\x1f\xfb\x829BfN\xc5\x1e\xbe\xcep\\u\xea\f\x15\xa4.\xe6l\xedWX\xca}\u05c2@\x01\xa1\xd6\x1b \x9c\x19\x80\xab\x04\x8auf[\x9b*XS\xc8\xd1\xe6'pg\xdc)\xac\xc8H\xf6\x05\x16F\x9b\xb0;\\\x9d\xc4\u86fd\x10\xa7\x9c\xb1\x16\awnty\xad\x96\xa5&\x03I\x83\x83\ay||\xe2\u04bb~p\xe7\xaf\x03\xf8\xab\x17^xA\xb8\xbe\xf2d,\xe6g\x03\x00\xdf\u007f\xff\x8f\x03\"R\xcc\xfc\x95#\x87\x0f_1>>~\xe9\xb6\xed; \x85\x84v\x93\xfa\xf5\xcf=\x8b\xbb\xef{\x18\x9f\xbf\xe5\xe3\xa8Uj\xe0\xa4\x01C\x021\v\b\x17#f\x03~\xdd\xc5I\x1e\xc7\xd7\x15wIE\x8e:Q\x9b\xf7\x86\xe7?Q\xec:i\x12\x94\x8f3\x9f\x88\x00\xda+7E\xdd;9\v\xce8\xd6\xe08\xbf6\v\xc6U~\a\x82b\xcee\xfb\xfb\x05\xf9\xdd :n\x88\xc9\x17\x1f*\xfa\x17qI\xa2\x10\x8a\x05\x9e\x8fb\x91\xa3i\xfe\x9bJn_.-\xf2\\p\xe9\xa0c\u0499vB>\x896\x88U\u0691[\u06a0q\xaaM\xe31\x1c&\x03\x17l\x01\xf7<N\xb2_\"\n\u007f\xb7 B\xb5Z\xc1\xb0+\xe4\x8f<\xf5R\u0591\xb31PJ#\x8ec\x18\xd5\xc4\u0095g\xa1w\xe1\x1aL\x1c>\x80\xe6\xd8\x11\xb4&\x86\xc1\u0318\xbf\xf2\x02,\xbf\xf0:\xcc=\xedB\xd4V\xaeFu\xe9<\xf4\x06\t*I\u074a\xee|\xba\t\xba\x0f&\x88\xda\xf6T\xfe\xf7\x8d\xbb\xcej\x02\x8c\x10\xb1b\x18V\x88Z61\x89\x8d\xdf\xf8\xe4\x05\xddU\xe9\x8e3\x96z\xb6p\xfb\xc04+\xe8e\x9e\x94\x93\xc08\xc2\xdel\xc6\xf9\xafT{\x02Tf\x05@\xcd\xd2\x10!\x03\x18\xa1@Bg0hv\x93\t\xb8@haC+\x90[2\b\x02(\x12P}\x12Z\x19\xc8&\u00e8\x18Q\xb5\aKN\xbf\x04\x9b\x9f\xbb\x17c\xc3\a]XE\xba\x83\x96j\u01ce\x9d\xe1\x03\x0f<x9\x80\xbf\xfa\xcd\xcf\xffg\xbdc\xc76:\xf5\xd4\x15|\xd2\x15\xf3\xf4\xf1\xeew\xbfW}\xf1\x8b_\x14D4\xbai\xd3\ubffcs\u05ee\xfb\xeb\xf5\u01b2\xfd\xfb\x0f\xc0\x187\xa9W-\xdcu\xfb\x1dX\xb5r9>x\xedZ\b0t\xdct|]\x01\x05'*r\u05f2L\v;Q\xdeC\xb1\x87\xc0\x12Cp\xfb\x8d\u0649\x01s\x89/7\x95l\xffDv;{\x8aEo\xf9/\xa4\xbb\xa4-qfND\x9eq\x90\xc9::fn\xebx\x8a\xfd,\xe5\xab\xd64q\xfe|P\xdcm\aRz{\x95\xee\xa4;\x91\xe2\u92b1:\xed\x93\xfc$ .,h\xd4\xf1\f\x00\xc7\u0451\xa73\x81D\x19\xb4\x94\xe3\x90;h\xc5/\xe4\xc6\xf8\xe8\xbc\xd7\x15\xb2\xb7\x843w:*\xb6\x1fC7\x03\xa9U+8ph\x18\xb7\xff\xe8\x11<\xf2\xf4\xcbH\x94\x82\x94\xc2v\x80Z#Nb4\xeb#Xu\xd6y\xb8\xe2\x96?Fm\xfeyH&F\xa1\x9bu$\xad&d\xb5\x8a\xdeY\xf3Q\x9d5\x0fI_\x15I\xb5\x85\xaa\x19C\x0fKH):\xad\xc2\u0476\xe5\xccL\xc9\v\x88\xb9-\xec%\x97\x97\x00 #\x01\xd5\x1fX\x99\u3a36A\xc9\xcc\u0747\"]\xc06.1\xcf\u028b?\xb5\xdda\u0735=`\u07cf\\\x10\u00aa@uf\x00\u0457\x97\x17!\x05D\x108K\\\x06\x9c\x101SQ\v\x91N5\x1c>\x96\x0f7\x04\x11\u008aD2\x03`N`Z\f\x95\u0118\xb3h5\x96\xae\xb9\x04?{\xf2N\x04a\x04\xc3\fA\x84\xa8R\tv\xee\u0705\u077b\xf7\\\xf7\u063aG\xae\xbe\xfa\xaaw<v\xff\xfd\xf7\a\x00\x92\x93\x86\xcdR\xf6\xf8\xc1\x0f\xee\xc0W\xbf\xfa?\xf1\xb5\xaf\xfd?\x87\xff\xe6o\xfe\xaa\xbee\u02d6\xf7\xee\u06f7O\x1a \x9bL\xb7&\xc6\xf0\xfa\xe6\xadX\xb6b%N[~\x8a\x1bt\xe4C\x1e\x86\r\u007fV$2 NQ\bC\x12\x02\x8c\x904\xa4\xbbx\x05\x8a\"#\xe6\xe9a\xe5\xd3\x19\u06b5s<hj\xba\x06:\x8d>\x8b\xa51\xeb\xbb\t\x8e\xdf\xd5\xd9\xc1\x94\xe1\x91\xe9\xff\x05\xca\xe7N\xfe\x93\x89<\u04e5\xb6\xf8\xb4no\xfehL\xb5\xa8\u02f3}\xc0\xc4x\x8e1\xe5\xf5\xe2\xf8#\x03\x13\xa5\xd1Lre\xa7\xf6?<c\xac|~\xde\xe9r\xc9m\uad54\xa1\x92fEZGB;\x9b\x89\xa2\bo\xee\u0707o\xddy?\x1ey\xe6e$\x89r\xbe)\xec<~4\xe2f\x1d+W\x9d\x86w\xfd\xca\x1fa\xf6\xeak!\x83>\xf4\xf4\xccF\xdf\xecE\x981\xef\x14\u0318\xb3\b\xb2\u0683\x96L\xa0k1\xfa\xfb$\xfa#im]SC\xac\xec\xb2)\x02\\\xcc\xe4\xe5^\xe6\uf66d\xd9K\xa6\x1a%\xf8\x0ev\fA@\x18\n\xe8\x90\x10K\xb7P\xb9\xc0d\U000b72e5K:\xa1d\x85ikh\xdaH.\x1d@Z\xfagP\xceZqL\x96\xa0G\xa26\x10\"\x9a\x1dX\xde}\xda\x12e\x17\xb1\xcf\x1a\xa3\x9csN\u0536fPA\xab\"\x84\x1d\x02+\x02X1\x90$\xe8\x9b9\a\x9c\xb4\xb0\xeb\x8dg\xa1\xb5r|s\xfbbI\xa2LT\tjc\xa3\xa3;\x9e}\xf6\xb9G\xef\xb9\xe7\xde\x13\x1dO\xfc\xef\xee1\xa5U\xe4\x82\x05\x8b\xf9\v_\xf8m\x00\xc0\xb5\xd7^\xf7\x8d\xab\xaf\xbe\xfa\xcf\xce?\xff<\xf4\xf6\xd4 \x82 +]\xfb\xb6m\xc1\u05fe\xf6u\xac\u007fu3\xaa\xbd}\x99:\x94\x19\x99BP\xb1\x84\xce\xf0\x03\xcbK\x97\x04\x04\x94\x1761\x9d\x81_\xc9\xd73\x93\xfb6\x1de\xc1\\n\x92B\xceSa\xf3T\x9c\xcar\xbb\xae\xb1\x8de\xd1\u0669\x96'\xe8LZ\xc81\xb9\u043a\x13\xbb\xc41\x16T.\xed\xda\xd2\x1c\xa9\xf4\xfc\xa5\xf9\x9d\xa6C.\xc2\xc7\xc5\\a\xb0\xed\u0213\x9cKn\x8c\u0166\xd3b\x9e\x1ac\x15\xfe>\xceY<\u0145Ix\xb517\x81\xb3X\xb1\xd5\x15T\xa2\n~\xb6i\x1bn\xfd\u07bdx\xf2\xf9\r0\xc6r\x95\xad(\u0206p\xb7\x1a\x13X\xb1r9\xae\xff\xe5/a\xe6\xcak\xd0\x18m\xc04&\xa0\x9a\x13\x88\x1bc\x88\xeb\xa3h\x8c\x0f\xa3a\xea\xe0\x19\x84\xfe\xfe\x10\xbd\x01\xb9\x05\x9a\xba\x9c\xb0\x9cuC~\xd7\xec\xfcGT\x12C\xb5\x9aH\x9a\r\xa8f\x03\xaa\u0544\x8acOIj_\xbb&\tsj\x12\xfd\x03\x01\xc4\xfc\x10jV\x80$\x12.\u06d4\u06ba\x1c.\x1d\xccPG\xf4\x1by\x83T*\xccl(\x03\x17\xdb\x1ayw\xc32\u0641g43@8+\x00\x82\xb4\xb1\xf1\x82\u02e5\x84\fB\x9b\x89 \x05\x84\x14\x16/\xf7S\xc1<\xf1\x96\xbf\xf4\t\x00\x91 \u022a\x04\xf5I\x84U\t21\x16/?\a\x8b\x96\x9f\x03\x157\xad%\x80\xdb\xc1\x85QH;\xb6\xef\u0091\xc3C\x1fd\x93\xac8\x190s9\x9d'\xfd\x97\xff\xf2\xbb\xb8\xed\xb6\xdb\x01\x00\x0f>\xf8\xe0\xcb\xeb_~\xe9\xc2\xc1\xc1\x03\xab\xea\xf5\x06i\xe6\xec\";28\x88m;\xf7b\xd5i+\xb1t\xd1B$Jyy\xb2\xe4\x8a5#\x00[\xb8\x85\x01I\x8c\x88L\xae\xb0\xf4\v3O\xd98w\xed\u01cfv\x19\x9e\nO\xee\xfe[\xf2\xff\v\x0f\x1b\x17(Wm\n\xbf\x1b\xa7\xeeE\x1c]\n\xbd\xefg\xcdDG\xfd\xbe\xa7*\xe59D+\xa1!]G.Jv64\xfd\xf0\x8ci\f;[\xb1\x85V\xb4\xb6]\xb86)[\xa4\xcd>\x98\xba\x9f\x8d\\L\xe8\r\xff2\x15\xb2\x84!\x89\u0219\xa9=\xf9\xc2\xcf\xf0/w\u070f\u05f7\xec\x00\t{\xedi'\xfaI\x92\x04q\xab\x893\xcf:\x03\xd7\xdf\xfc\x05TW^\x87\u047a\x85\b\xadq\x9b],\f\x01\xaa\"@\xb3\x03\u0318i\v\xb9\xec8 y\x81J'\v\xa9}\x80Q\n&I\xa0\xe2\x16T\u0482j5\xa1\xe3\x16t\x12\xc3\x18eM\xbc\xd2\x04#cR\x16v\xa6\xb4\x0e\x88P\x91\x02\x95H@\xf6Hk\u0125\xd3E\x8f<7[\xcam\n\xfcXXj\u03dc+\x0e\xec\v$\x84\x0e\x88\x8f<\x81\x90\xb5\xae\x8df\x85\b\xe7\x86\b\xaa\xd2m6J\xae,!,W\xde\xcb\a-t\\\xdc%\r\x89r\xd6M\x10\tDB\x00\xcd\x043\xfa\xe6b\xec\xf0^\xec\xda\xf2\x02\xac\xed\x88=\x97B\x10)\x95`\u018c\xbe\u015b7o\xfe\xfe\xbau\x8f\xef\xfa\xbb\xbf\xff:\xdd{\xef\x0fO\xeeb~\xdbm\xb7\xe3\x87?\xbc\a\xdf\xfe\xf6w\xf0\x95\xaf|\xa5\xf1\x8do|\xe3\xf9\xd1\xd1\xd1_\u077bgOPo4\x9cT\u06b27\xf6\xed\u074b\xdd{\a\xf1\xb6\xb3\xd6`\xe1\x82yH\xe2\x96\xdf+\xd9\x13\ufdb9\x12\x8c\xc0\xf5\xea\xe9\x10\x91\xdb:m\xfawx\u040aB\x1e\xbf\xb8qG\xca\x0eP\x12\x14C\xdd!$\x97\x96\xd5Q\xb4:\xdc\x0e3\t\xf6\x89)\xaa\xdc\xe6\xc3\xd1D\x88:\"\x00\u0085x\x1b\b\xb2;)ry\xaf\xe2x\xcd\xd5\x18\x88\xb5\xed\xc8\x13\xa53.y\x86\x8f\xa7|\xee.\u02d4\u007f\xdb3S\x86\u074a\\q`U\x88$`H\"\xa8\xf4@\x1b\x8d\a\x1f{\x16\u07fe\xf3\x01\xec\xde\u007f\xd0F\x02\xc2\xc2*Zk4\x1a\r\x04\x02X{\xd5\x15\xb8\xf6\x93\xbf\x05Zr\x15\x86\xc6\x00i\xf2\x80m\x12\x02,\x04tU@\f\x84\xe8\xeb\x0f\xd1\x13\tH'+\xb7]\xa6/\xcfLC\x8d\x9dB4\x8eaZM\xa8f\xd3v\u07ad\x16t\u0702Q\xca\x1ax\xb1K\xaf\xef\xf0\xff\xc9}|\xb2\x8e\x16\x80$B\x14\nD5\x89 \xb2\v\xaf-\xea\x1e\xba\xc3\u014e\x82D\x97\xe5\x9f\U000ee768[\xa3\x90c\xe5F\x10\xa4\x04\xc2\xfe\x10r^\x84\xa07\xc8]\x1c\xcb`Ka\x8fQjM\x9c\xedR\xb2\x85\x9a\xbc\xd0\xe7\xce\xf4\x1bA\x96\"L\x02\xe0\x86F\xc0\x01X+\xec\u0776\x1e\xf5\xb1#\b\x82\xa8x\xcf\x18\x8dE\v\x17\xecz\xfe\xf9\x17\x1e\xfdy.\xe4\u04c2Y\xd2\xc7\xfb\xdf\xffA|\xff\xfbVEu\xdey\xe7\xbfv\xd3M7~\xee\xdak\u07e5\xe7\u03db\x870\x8a \x830{\xee\xd3O=\x83\xbf\xfc\xdb\u007f\u00ae\xfd\x87\xd17\xa3/\xe3\x05\xa7%\u0278N/e\xb6h&hCP\u01a9\xc9\x18\xa5,\n\xfewV\xd0\xf3\xa8Fg@E\x9c\xdd\u0113\xc1F\xecc\xbfmE|\xda\xed3\xbb\x1b\x86\xf9h\xeb\xe7\xa4\xff\xb69\x9d\x12\x89]r\x11@!\x84\x86$\x03\xc9\xda\x0e\xab\x19 6\xc7~F\xdc)\x8e\x95F3Vh\xc5\x1a\xb1b\xa8\x94On\xfc\xce\xcd+\xd8\xe9U\x94\x168\xcf \xcb\xc0\xa7(z\x16\xb1L`\x92\x88\xaa5\x8c\x8eM\xe0\xce\x1f=\x8c\xef\xfe\xe0\x01\x1c8x\x04R\bh\xa3\xa1\x12\x858NP\x1f\x1f\u01cc\x9e*>x\xe3\xc7\xf0\v\xb7\xfc\x1e\x9as\u07ceC\xc3\x1a\x82\xb5\x85\x05\xb5\x82V1\x94\x8e\xa1B\x86\x98\x15\xa2\xb7?@o(\xac\x02\x9a\x19\xac\xadD_\xb5b\u8e05\xa4\xd9@\u04a8#\x99\x98@k|\x1c\xf1\xf8\x18T}\x1c\xaaY\x87\x8a\x9b\xd0q\fVy\x17n\xb4\xe5\xb2\x1bcla\xd7\x1aFi\x98D\xd9N\xde}h\xa5\xdc1r\u05c2\x01d@\xa8\xcc\x0eP\x9b\x1f!\x9a\x1d\x02\xa1\xc8!\x17\x81\x82\x01\x96\x95\u06a7\x17\x9d\xef\xf5\xc2\x05\b\xab\xfc\xbe\xe3\fb\t\x04\x10\xf5H\x88\x81\x10\xb2W\xe6\v\xfc$X)\ta\xe3\xf7\x84\xc8\xe1\x15\xe1\xcf6\x04r\xae\xbc{\xa1t`\xe2\u4ed6o\xceh\xc5u\xcc]z\x06\xe6-Y\x03\xad\x93\x82\xbf\x0e\x91\xc0\xe1#C\x18\x1b\x1b\xff\u022e]\xdb\x17\xd8\x19\xe0\x9d\xf4\xf3Z\xcc\xe5\xd1<\xf9\xb6\xdbn\xc7K/=/\xfe\xfe\xef\xbf\xc1\xb7\xde\xfa\x8f\xeb\xbf\xfa\xd5?\xdf\xd6l6o\u063f\u007f\xbfh4\x9a\f\"2\x8e_\xbdm\xdbv\x8c\x8eO\xe0\xfc\xf3\xcfE\u007f\u007f\x1ft\x12\x03\xccH\xed\x99S\x87\xba\xb4\x935>\u036e,\f\xe2\x18\xa0\x92\xff_Z\xf5\x927CS-\bS\x04y\xf04\x82\xd2\t\xc7\xfe\x1cn\x1bz\x02V\xf8UC\xe2\x86\u04e6\xb0(\x1d\xaf9\x9a1\x8c8\xd1h\u0116K\xae\x8d\x1bxj\x93+e\u0459\xf1\x99\xee\xb5\xdb\xf9\xd1\xfe\xf85\xb7(qL$!\x11Vj\u0631\xef \xee\xb8\xfb~\xfct\u0773\xa87\x1a\x10B Q\x89\xa5\x1e&\t\x92V\x8cU\u02d7\xe0\xc37\u007f\x1a\xa7]w3\xf6%K0>\x9c \x022X\x85a`\xc00! g\n\xd4z\tU\x18\xc0\xc9\xf8-d\x92v\xda1t\xabe!\x948\xceqo\xa5l\xc16\xd6\x05\xd0?\xb9\xd46%\xf1\xfd_\n,\x1dW\xf4\x84\xb4\xc6o\xe9,\x87\xdc\n&\"\x01\x8a\x84\x9d;\xb4l\x97O\xa9\xaa\xd2?\xd7\xee\xa4R\x99\u027d\xc7\xc7\xcf\u0198~(6\x01R\x12\u008a\x00\xe6D\bf\x87\b\x04\xd96\x8d&\xbf\x963\xa7R\xe3\\\x0f\xb9\xe8\x1a\u019eL\x97\n\x9d\\\xaa\xae5@\u02c0[\f\u048c0\xea\xc5\xc8\xe1\xbd\x18\u0739\xd1y\xe2Pv\xce\xc0\x06Q\x14.\u073dg\xf7\xb7\x9ez\xf2\xa9\x03\xdf\xfd\xee\xf7~n;\xf3\xe0h\u007f\xe0\x82\v.6\xeb\xd6=*\xae\xba\xea\x1a\xf3\xcew^\xfb\xad\u035b^o\xd5j\xd5[\xef\xbc\xeb\xee\u0783\x87\x0e\x19\x80\x85R6\xe0\xf6\xae\x1f\xfc\b$\x04\xbe\xf8\x9b\xbf\x82\xc5sf\xa0>6j\xbb)\x12`\x92\bX\xb7\t`\xc8R\x00\x8b\xd6\r\xedb\xbd\xa3\xc2~\x8f\xb7\bN\xf5\xbb\xd2 a\xff\x8by\xacW\xc1\x9e\xaf V!tr\xc6A\x1d\xf7S\xe9\x0e\xe5h\ni7\x1e~Y\ta\"\xc8\xcc{$\xdd\xd2\u04d4\u01fe\x9d>\u0775\x88\xb3\xed\xbe\x13e}s,Fn\xa9j\xda\xf0\xa4|\x98\xe2\x0e\xadS\xaa\xc8m\xef\xc00C:\xc8\xe3\u535b\xf0o\xf7>\x84\xd7\xde\u0612m\xd5ce\xdd5\x8d\xb1\xa6gW\\\xf1v\\w\xe3/\xc1\xac\xb8\f\xdbFfB\x0f7QqT70\xdb\x10b6@@\x88f\x00\x95\x9aB\xa04r\xe1g\x8aU;\xf8\xc0\x15\xaa4\x9e\xae}H\x9c\xa6!Y& e\x05\xb2\xa8\x9c\xf5\xf8\xfe\xc6\xd22\x89\xb5\x15\x10\xb1\x00sP\x84\xb9\U0008a360W\xa2**\x80\x00\xf4\x91\x04\x1csg\u01b7A!g3\xe3\x98\x17|_\xa835\x0e\x80\f\x04\u0090@\xb3\x02\xd0@\x88 @F)\xb6?\xc3]\x9b\x12\x9b8$ \x03\xeb\vo\x8c\xce\x17*a\xbfg\xb7[\x94\xef\xc0\xd8W\xde\x02H\f\xb4\xd2H\f\xc0\x94`\xe9\ua2f1\xe9\xc5\xfbph\xef\x16\x84Q\xd5\xd5\x0f\x01\xa5\x15\xf6\xee\u06c7\xbe\u07be\xcf\x02\xf8\xe2\xcf3\xcc\x12\x1c\xcb\x0f]u\xd55\xe6\x81\a~\"\xae\xbb\xee=f\xf5\x9a3\xbe\xff\u66dbxdx\xf4\xd6\x1f\xddw_\u07e81LD\x94$\t\xc0\x8c;\xef\xbc\x17:\x89\xf1{\xbf\xf5+8u\xf1|\x8c\x8f\x8eBi\xe3\x06E\xe4.t\x82\xc86\xd3%\xfa\xc4\x13\xd8rwS=\x1eMa/\n\x88r\xbc\x9c=\f\xc1\xff\x1c\x9e\xe5g7\x1b$\u007f\xc03\xd9`\xf4X\xf8U\x93\xcb\xf49\xf3\u02a1\xb6R\x99)t\xb9\xfb\xb1\xe2i.\xa6\u06b0\r\\N\x95\x9d\xcaX\xba\xa1\xc9\u04c1\xdam\x1b\n\xdcv\xcfG\x9e2\xbadn!L\x9e\xaa\x98\x19\bC\x89@\x06x\xf2\x85\x9f\xe1\xbbw?\x84\x9d\xbb\xf6@Jk\xbf\x1a+;TTI\x82(\fp\xf5UW\xe2\u068f\u0708#\x8b/\xc4\xd6\xe1*\u0091\x16*\xdad;G\x8b\xef\x02\x88\x80\xa0_ \xecaH\x9d\xc0$\xbeN\x92\xb3\xf3\xc7\xe9\xcfx\xe2\xa6\xe2\xf1\xe2\xdc\u007f\xa4M\xb0S\xb0,\u02c4c\x9c\x15Hr\x8b\x06\x1b\xdb\u0676\ufb6dw\x8c}\x05Y\x13\xa8\u032f\xc0\x04\x02\xeaP\f\u0772\xb1z\xd4\x01\x99g\xb9\x9d\x00\x00 \x00IDAT\u0561\xcde\x8d\v\x17\x1f\u00df\xd83d@\b\x02\x82\xe8\x91\x10s\"\u020a\x00\x99\xa2\x81\\g!\xf7--\x9c\x96CJ\xabh\u054e+C\x9c\rk\x99\xa8p\xbc\bN0\x06\x02\fC\u0156\xc2j\x1b$\x8d\x81E+\xd1?\xb0\x04\x87\xf6lvL\xa1\xdc\x1a\xa0>\xd1\xc0\xc1\x83\a\xdf\xcd\xccU\"j\x1e8\xb0\a\v\x16,9y1\xf3\xf6\xc7u\u05fd\u01e4\xf8\u04eaUkn\xbb\xfe}\xef\xf9\xb5\xcb/\xbb\f\xfd3fP\x14\x85\x1c\x85Qv\x83\xdd}\xef\xfd\xf8\xef\u007f\xf2Wx\xfc\xb9\r\x88\xaaU\xf4\xd4*\x8e\xb5b\xe9n\x86rq\x05\xa1\x18\x12\x9c}\xdd\x13\xa6\xd1\t(\xe8e]*\x97\f\x1a\xdb?LF\u0663\u008e\xc2d\xd1\xc5\xc5!g\xa1\"S\x113o\x87\xbc\xa9=\n\xac\xa3H\xf21\x17r`\n\xf9\a\xb3g\x00V\xb2s\x98\xe45'\x9bg\xa4B\xa0fK\xa1\xd92h\xc6\x06J[\\<\vW\xce\f\br\xd6\au\x8c\u073c\u063e<E\xad\xe0:\x98\x0e\xa0\xab\x95\x10\x81\x94x\xec\x99\xf5\xf8\u07f7\xfd\b\xdbw\xee\x86p\xdd^\x92X\x98#\x89c\xf4\xf5\xf6\xe2C7\xbc\x0f\xd7\u007f\xf2\x16\x1c\\p\x016\x8dU \xea\n\x91\xd6)\xa5\xdb\x05ZXH\x84\x04CT\ud015\x95\xce1m\xf7\u007f\xa4\xec\x13\xe73\xa23\x9e\xb8W\xac9\x0f\xd3((\xfa\xb9\x9d\x86\xea\xd9C\xf8\x17J\x1b\x90]\u0282\xca\n5\x10V\x05\u00b9!*\xf3\"D5\t!QN\xdd\x12(\x1a\x03!\xa7\xfa\x16\xba?I\b%AT\x04\u011c\x10\xb27p\xb5\xdf\xebV\n\xd7>\xb9B\xdef\x95\xcb6\x81I\x04\x01HH\xbb\xdb\xc8\xdc+\xc9\xfe\x1bT\\W\xd86}F\x19$\xb1\x816\xe9\xa2n \xa2\bs\x97\xacAT\xed\xb5\xe7\xcb->\x82$\x12\xa5\xb1c\u01ce\xd5/\xbf\xfc\xdc\"\x00?\x97\x85\xfc\xa81\xf3\xf6\xc7'?\xf9\t\x9cw\xdey\xf4\xe0\x83\x0f\xe1\xb6\xdb\xfem\xe3\xef\xfc\xce\x17\x16\f\r\r_\xb2\xff\xc0~\xeb\a'\x88\x8c\x1b\\\xec\u0735\a\u03fd\xf8\n\xa4\x108u\xd9R\xf4\xf7\xcf@\xa2\x8d-\x8c\f\xebW\xec\xd6\x16Q\xd8bR\xe6'\x91]s\xe2\xc4LC\xb9C\xe5V\xd6\x05\xe7\xfc\x94n\xae#^)j+?m\xc0\x01\xb5\xd5\xf5\x92\xd0\xf3n\x90E\x19\x0419\x0e>\x05V\x8f\x12\x96\r&g\xc5\xf0tw\b\xee\x05\x94\xb6\xd8x3\xd1V\xd5\xe9\xe4\xf9:\xf3W!\xcf\x12*\xff\x1c(\b\x11\xbb,b\xe4\xf9k;j\xa8 \xd4j\x154[1\x1eX\xf7,n\xbb\xe7\xa7\xd8?x\x18a\x10\x80\rCi\r\xa3\r\x1a\x8d&\xe6\x0e\xcc\xc6\xc7o\xfc\x10.\xb9\xfe\xa3\xd8Z[\x89\xad\xcd\x1a\x82\x98QmiH\xe5e4\xa4\xb9\xa0\xc4@\x00\x04\x11l\x82|\xa1\x9b\xf6\xbc*\t\x85\xecQ\x12T`\xc1\b\x19 \x8c\xaa\b\xabU\x04a\x04\x19F\x96\x1dS\x18(S\xc7\xf5\x05\xe1\x162a}\x83D !\x82\x10\xe4\xaa3y'\xc5\x17\xf8\b\x06D@\xa0\xaa-\x98B\xc3v\xd1\xde.\x87\xfc\v\x92\xca\aA$l7\x1e\x04\x02\"\"\xc89!\x829\x91\vW\xf6\xad\b\xbc\x06\xbc\xf4J\xf1\x96\xea,\xe5\u02e4\xc9!E]\x95?\xe8N\xcf\x03\x80V]C7\x8c\xfd\xdb\\\ve\xed\x174v\xbd\xf1,\x9a\x8dQ\u0220\x92\x89\x88\xb4V`c\xc4\xf2\xe5\u02df\xbd\xfb\xee{6\xfc\xc7\x00\xb4\xe4\xf1\x9d\xef|\x17\xd7_\xff^\xdc\u007f\xff\x03\x00\x80{\xee\xb9\xf7\u01fbw\xef8\xad\xd1h\x9es\xe8\xd0!J/n\xe3\x9c\xe6FG\xc7\xf0\xcc\v\x1bp`\xf0\b\x16\u031f\x83\xc5\v\xe6B\b\x91\x19)\x19\x88\xccMQ\x90_j\u06f8\xcd\x1e'\xfdXg\x95\u9ad9l\xfe\u07aet\xa4\x82\xea\u0447W|\x9b0\xf2\x1c@\xba{\xb1{e\x8a&\xe7\xb4w\xe88\xa6\xb9\xbb8\xdeY\xc0t\x06\xa8\xd3}h\xc3H\xb4\xb1~\xf2\x0e\x1b\xb7B\xa04\x00\xa4\u0762\x81\vXr\x0e\xd9z\xdb|\xcf\x11\u0424\xb0K\xba \b\x81 \bP\xadD\u0635\xef \xfe\xed\x87\x0f\xe3\xc7\x0f?\x8d\x91\xd1q\b\x17.\x92R\x0f\x9b\xcd&\x96.\x9c\x87O\xdc\xfcK8\xe3\x1d\x1f\xc0\xab\xb4\x14{\x92*\xa4aT\x9a\x1aA\x92\x17\x12\xf2\xa8\x91l)<\b\x88!B\x80d\xfe\x9e\n\xe7/\x1d\xbe\x91\xb0\"\x19ae\xecA\x18BV\xaa0I\x82\x03\x9b7`\xcb\x13\xf7c\ubccf\xe2\xe0\xd67 \x84D\xcf\xcc\x01\x17f\xe1\x0f3\xbdE\x9e,\xbf\u074aml!\x97A\xe0\f\xe1<\x0e\xbe\xa7\xbb\xf1wz\x90\x04\xaaX\x93.\xa1\x1c^\x9e\x0e5\x99\x8ab8\xa2B\x87.$\x10\x84\xc2\x05-\x13\xc4\xec\x10r~\x04\n(\x17\x06Q\xb7\x96\xa1\xfb\xfe\x90<og6\xa6\xc3\x054\v\xd8\xd66\xf8Ci\x83FK\xc344(\xb1\xb3\t\xc1v\b*E\x88\xa8Z\xc3\xf6W\x9f\xc0\xf8\xd0\x01\x04\x0e\x19 AH\x94B%\nq\xca\u04a5\xad\xc7\u05ad\xbb\x03\x00\x1e\u007fb\x1dn\xbd\xf5\x1f\xff\x033\xf7\x1f\xbf\xf7{_\xcaC\x8c\x8943\xffZ\x18F\xa8\xd5j\x9f|\xfe\xf9\xe7\xc1c\xe3\xa0\n\xa1\u0572\xc1\f\xcdf\x13w\xdc\xf3\x13l\u067a\x1d\x9f\xfa\xd8\xfb\xf0\x9e_\xb8\x123f\xf4\xa11Q\xb7\xb44\x10b\x04\b\u0666JJX\xf9rf\xa2\xe2\x17\xf1\x94\x9b~\f\x86S\xbe;E\xea\f\x93 @\x02\x89\xc0\xc9e\u049e\xdc\u03ef\xcc\x13\xae\x18y\x881w\xc0\x12T\xc2\x12\xc9\xddh\xca;\\\xc6$\x98\xfa4\x8b/\x9f\xa0\x01\xef\xb1\x14q\xc3\f\xe5(\x86\x896\u06403\x93\xe2\x1b/&\x82\xbcA\x1fS\xc6\x15G\xc1\b\n\xb9!\x13\xc1\xfb~>\xf8\f\x02\x89(\n\xa1\x94\xc2\xd3/\xbd\x8a{\x1f|\x12\xafm\xden}\u0209\xa0\x13e\xe9\x87J#n\xb5\xb0r\xd9R\xfc\xe2\xa7n\u0092\xb5\xef\xc1\xcb\xf1\\\x1cIB\x04\xc2 L\fd\xe2\x16\x88\x94\x1dbl\x04\x0fI\x17\x15(\x81\x80%\x02- \xc2 \xf7\x01/`\u0294\xc1G\xa9\xc531P\x1f>\x82]\xaf<\x8b\x9d/=\x81\xc17_\xc5\xf8\xa1\xfdPI\x82JO\x1f\xf6\x9ey>.\xb8\xe1\xd3Xx\xfa\xb9\x801\xd9\f\x83\xdb|\xe13\x1e\xb6\x83%\v\x1eZ\x93\f\x1e\x01\x17\x06\x1d\x10\u012c\xd0r\xf1\x87\x12P\xc3\xc2]>|\xc5\xf6D\xe6l \xb2\x85<\f\\JP_\x00\x9a\x17\x82B\xe1<T\xba\x14r/0\x8cPf\u06563[D\x10\xc0h\r2\xc6QI\x1d\x1f]\b04\f\xacG}#6\xd0-\x8dP1\xda\x13\xbb\xb5\xd2\b\xc3>\xcc]\xb4\x1a\x83\xbb^\x831\x1a\"\xc8\u0565q\x92`\u04d6\xcd\x17\xa6\xbf\xfb\xca+\xae\xfa\x8f\x01h\xe9\x85\xe2,5\x17/^@D\xd4`\xe6\xdf\xe8\xef\x9f1X\xadV\xbf\xb8a\xc3\xcfp\xe0\xc0~03\xc7IB\xa9\xb8\u854do`\xcf\xde\x03x\xed\xcd\x1d\xf8\u055b?\x86\x95K\x17\"N\x12\xa8$\xb1\x1d\x1d\vH6\b\xa1\x10\xb0C\xa9\xa9\x8bS\"\x15g/G\xa7\xfcd'V\"w@4\x82\f\x15/w;\x14\x05.H\xb1|\xa6\xaf\xa3=\x01t*d)\xf3\xf6\xe7\xe3(\x9e'\xa2\x90\xd3q\x16\xf0\x94q\x94\xe6s*\u05c5\xa7TC\u035c\x8bC\u041ejFY\xdef\x16\x15\xe4\vm\xbcX7\xf6\xd8\x1a\xe4\xb6\xfd\xd5J\x15a \xb1{\xffA<\xf8\xf8\xf3x\xf8\u0257p\xe8\xc8\b\x84 \x186P\x89\u016e\x95\xd2\xd0*\xc6Y\xa7\xaf\xc0G~\xf1&\xcc=\xf7j\xbc\u051c\x8b!\x15 $\x03\xd2\f\xd1\xd46\x04\x81$d\xe4\xe0\x8bD\x83\xb5\x82\x86\x82Jb\x98\xa6\x027\x19aS@$\x01\xa8\" D`c\xcd\x18PI\x8c\xe6\xd8\b&\x0e\x0f\xa21z\x18I\xa3\x0e\x93\xc4\x18\x1d\u070b}o\xac\xc7\xd0\xeemh\x8d\x8dX\xf5fda\x16\xad\x12\xec}\xf5E\xcc^\xba\x02\xb3\x97\x9d\x86\xb0R\xb5\xf8\xbb\u020b\xac\xf0\xf8\xd7$\x85\x85K\xb2,Z\xce\x06\x9fS^\x1bi\x82\xd1\xcc\xc0\xed<\x14$3\xa0\xbdE6\ud4055\xf4\xaa\x046\\\x19\x82\x80\x1eKCDE\xe4\ucb2eq'\xf0\x98F\xdc\xe5\x9at\u0443BB\x04\x12l\xa4m\f\xd9X\xdfsw\\\x8d\x01Z\x9a\xd1L\f\xa2\x84!\r{a\x16\xee}$\n$\x02,8\xe5llz\xe9'\xd0*\x81\x91\x01\x04\bA\x10bl\xbc\x8e]\xbbv/x\xe1\xc5\u7bbb\xe8\xc2K\x1e\xf8\x0f6\xcb\x14\x05\xfd\u99df\xc4\u06b5\x97\x83\x88\xc6\x00\xfc\xce\x13O\xac{\xfa\xae\xbb~\xf0\u01cf=\xf6\u061am\u06f6\xd3\xe8\u0628VJ\t\xa54\x81\x19\x87\x87\x86\xf1O\xffz\a\xf6\xef?\x88\xcf\xfc\xd2\r8e\xf1\x02\xf4\xd5\"\xf4\xf7\xf7C1\xa1^o 1\x12\xa1\x90\b\x9chEd\xae\x8a\xdc\xe9}\\t\x01\x9a\xd4\u00ef\xddN\x97\x00DPmE\x9a\xba\xbe\xceT_K\x87\xa2>\xf7\xc5\xef,i\x1a\x05\xf2h\xa0\x93N\xaa!\x1f\xd7b0mHE[HE)\xceB$\xb4\xb6\x18\xa7f.\xaa\\\x19\x1dA\x1d\xa9\xbc\x9d<\u0715\xbc\x00\x8aB2\x8d\u00e1\x830@%\n161\x81\x176l\xc2=\x0f<\x81\u05f6\xec\x80R\x1a\x950\xb0\xbf\u07e4\u0656\x84\x9e\x9e\x1a\xce9\xfbB\xbc\xf7\xa3\x1fEe\xf5Z\xac\xaf\xcf\xc0\xa8\x91\b\xa5\x01\x91@\x90\x00\x91\b!\x03\x03\xd5j\xa01v\x18qs\x1c\xcd\xd1\u00d8\x18:\x80\x89\xe1\xfd\x98\x18\u068f\xb81\x0e\u05b1\x85*\"\x89\xa0VEP\xadA\b\x89\xa4\xd5Bsl\b\x8d\x91!\xc4\xf51$-+\b2*\xb1>+I\vB\x06\x90a\xe82?M\x16\x1bW\x1f>\x8c\xc3;\u07c4j5\x11\xd5zm\x97\x9aA6\xb6C\xb5B\x1b\x99\xbfF\x10\xa2`\x14=\xcd\u055b\x00\x88P\x00\xb3C@\x10\xe4\b\x01\xb1\x014\xa0\x13\x83\xc4\x15_)\b\x95\x80\x10Fv\xf1@E\x80g\x05@U\x80\x8d\v\x9dqP(qq\xb8\xef\x13\x00\xda=8\x19E\xd6V\x16\xb0.\x03\x18\xa9AZ[k\xect7G\x84D\x03\xf5\x96\x86I\f(6Y\x8a\x14\f@\x9a\x9dy\x9a\x86\x90U,Xr&z\xfa\x060:\xb4/\xfbE6\xec9\xc1\xf0\xf0\xe8\xec\x87\x1e|\xe8b\x00\xffQ\u0327z\xac]{9\xa7\x90\v\x00\\q\xc5U\xdfc\xe6\x9f\xfc\u065f\xfd\xd9o>\xf1\xc4\xe3_\u06bcy\u04ec]\xbb\xf7\xa2\xd5j\xb1J\x12JO\xe4\x8f\x1fZ\x87\x177\xbc\x8a\xd3V,\xc3\xcae\x8b\xf1\xb6\xd3O\xc3\x05\u7781\xd3O[\x0e&\x89\xb1\x89:\x9aZC\x92\x84dv>\xe9\xd6\xf6\x899\x87.\x98\x8f\x06\x05\xee\xe2nX\xb2%\u0325\xc9\xdc\x01\x9f\xf8\xd1\x1aT\xd2\xf5\x17Cp\xbb\x8f\x0e\xdb\xe1\x96\xf6\xa7uc\xa2\xd0q\x94d\x9a\xe6B\u046d\xf0+m\xe9a6\x9b\xb3]\x8a\xdf)\x1fL-\x00\f\xa7v\r\x94\xb3=\x88\x8b\x8e\x87(H\x80\x00\x00Q\x18 \x8aB\x8c\xd5\x1bxu\xe3\x0e<\xb8\xee9<\xf1\xfc\x06\f\x8f\x8c#\f$\x82@Bi\xe3\x15\x16F$\x03,;u\x19.\xba\xe6]\x98\x98\xb3\x06/\xef\xd7ha\x02\xa1\xb4\xe97Fk\xa8\x91\t4\x8e\x8c\xa21<\x88\xe1}\xdbpx\xcf\xeb\x988\xb2\x17qs\fI\xab\x01\x1d7]\xb6g\xee\xfc\x92\x05-\xa4CP\xa3-\x17\u06a7\xf39\xae9\tB\xa5\xa77\x17\x1f\xa52}\xad\x11\xd7\xc7\x11\xf5\xf4a\xf1\x19\xe7\xa3\xda7#\x17\xbdd3I\xab\xd4$\x87\x93\xa7CSHYlb&=Y\xd4I1\n\bbf`\v\uaa06hh\u02002\xbb\xe1\x00\xb0\t?\x86\x81\x8a\x80\x98\x19\x80{\x84\x9bW\xf8\xd7iz\x8fP\x9b\xf6\"]\x9c\xfd\xa6\xc8\u3d34[\x05\basS\xc9*\u008d\a\u02754#\xd1@\xa8l\xf1\xce\xe6\x02i4`F\xff4\xe8\x9d1\a\x03\vWal\xf8@\xfeur\r\x03\t\xec\u077b\xef\x1d\x00\xfe\x14\x00\x9e|\xf2q\\~\xf9\x95?7\xc5\xfc-\x13M\xfe\xe3?~\x93~\xe5W~5\xbb\x95\x1fz\xe8\xfew\xdf}\xf7\xbd\u007f\xf2\xf4\xd3O\xbf}\u02d67111\x818I`t\xb9\x99\xd9Y\xa7\xaf\xc4\a\xdes\r.\xbd\xe8<\xbcm\xcdJT\xab\x15\x8c7Z\x88\x95-\xe6\x15$n\\\n\x87zs\xdb\x14\xfd\xc4<\xba\xb1(\xb8\xf0yqPk\xda\xfa\xe2\xb43\x17\x05^\xcc4@}/v\xfd\xe8f\x01\u04c7X\x8eZh\u0140r\xcc\x10\xad<{Z\xe7p\x98\xfa\xa9\x982\x17\xc9\xff\x8f\xbd7\r\xb2\xe4:\xaf\xc4\xcew\xef\xcd\u0337\xd5^\xd5\xfb\n\x10\r\x80X\tb\xe3Np\x81(B\x94DK3\x1cihY\xd684\xe3P\xcc(\x183\xa6\x15\xe1\t\u02e3\x88\xb14\x94\x1c\x1e\x85\xc2\x16I{,\xc7\f\x15Z\fQ\x96\xc4m8\xdcA\x90\x00\xb1/\xc4\xd6@7\xba\xbb\x1a\xbd\xd6\xfa\xf6\x97y\xef\xe7\x1f\xf7f\xe6\xcd|\xf9\xaa\x01nSptF\x14\xd0\xdd\xf5\xeaU\xe6{\xf9\xce\xfd\xee\xf9\xcew\x0e{\xbe;\xec\xf1\xe1\xa9\xee\x98\xf3+M\xfd\u0749\b\x81\xab\xc4/\xaem\xe2\xd9\x17O\xe2\u0467\x9f\u01c3\x8f=\x83\xd3g.\xd8\u079ep\u0563\xbfj\xc0r\xf4J\n\xcc\xce\u0361\xb9\xb4\a\xa3\xda,X\xd5\x10F\x01\x94\x8a\xec4\xe7h\x04\xbd\xb9\x81\xde\xca9\xf4\xdb\x17\x10\x0f{\xcez\x16\xde|@\xb1kM$\n\x00U\x90(ybs.v\xf3\n/z\x12[/\x96\x1dW\xbe\x11o\xfa\xb9\xff\x12ox\xeb\xddPa\xe4\x85<\xbb\xa6\xa7\x14\x10A\x00\x15FPadm3\x84\u020a\x18P\x15oWV\xb5\xe7Z}\u00dc\u075f\xd6\xdf\xc4\x00\xe7G\xa0N\x02c\x80\x81{OC\x82\x95 \x06\x04\x9a\r@\x8b\x01\x8c\xb4 \xcdd\xfd`R\n_PQ\x1b\x9fs\xe5<\xa6h\xc9*\xf3\n\x9fw\xa3\x13\xc4\xfd>\xe2Q\xec\xb2U\r\xfa\xfd\x18\xeb\xed>\x92~\x8c\xa8\x1bC%\x06R\x00\x01\x03\xd0\fh\xeb\xf1\x04\x86\xf5e!\xc2c\xdf\xfaS<\xf2\xf5\xff\xdb6\xa0\x9dm\x80N\x124\xeb\x11n\xb9\xf9\xfa\xa7?\xff\xf9\u03fd\x87\x88.\\\xae\xcc_\xe5q\xe3\x8d7\xf2'>\xf1\t|\xfc\xe3\x1f\a\x00\xbc\xf7\xbdw\u007f\x99\x99\x1f\xfaW\xff\xea\xb7\xff\xf1\x13O<\xf9?>\xf3\xcc3\xf5W^9\x83~\xbf\x8f$I\xc6~\xfe\x99\xe7\x8f\xe1\x99\xe7\x8f\xe1\xe0\xbe\xddx\xe7[\u078c\xf7\xbe\xebN\xdcz\xf3u\x98m\xd6m3U\v\xd8E\u06a0\x18%Q\xb2\xea|\x95\x15ieu\xea\u0758E\xba\xa3z\u0528l\xc0UUR\xb3\x9f\xc4\x0e\x14$e\x93\x90\xb6*\xddh\xf2>\xa2z\xff\xf0\x83\xae\xe2)\x96\x19cUI\xb1\xb1\x94J\x9e\xfaS\xde\x15\xe5\xd5P\nt\xe9LA*\xee\x14p\xd9S\xe9\xf5\trS\x90\xec\xf9WK\x04JA\t\x81\xf3+kx\xfa\xe8q|\xef\xf1g\xf1\u0533\xc7pqu\xc3\u01baI\x91\x9f\xa5\x1f>\xec\xe8\x1d\"@\x1b\x83\v\x17.\xe0\uc673\x16tDn\x86\x95\xf3Y\xe9X\xbc\xafe\xa7|]\xa0\xf1\xb6t\xae\xc4\xf0\x16\x0f6\u067f1\xe7u({\x1aq\xa3\rt<\x04\x18\xb8\xf6\xae\x0f\u1dbf\xff\x8f\xb1\xf3\xaa\xeb0\xec\xf5`t\xe2\x940\xf6<\x85R\x10A\b\x15\xd6 \xc3\u0426\xe9\x00\x85\xa1\x98R:u\xc5\xddIYsZ\xfb*\"\x01 \xb6\u065aF3X\x10\x928o\x88j\x00\xd2]\x9b\x88\rL[\x83[\xd2:\xade}\u07f42v\xaa\x17b\bL\x8a*\xc9\xefI\xa2\xea\x1b\xdd\xf7/O\xafch\x18\x9a\tR3\x843\x0fK\xef=2\u0206\xaa\x88\x81$\x89Q\xab\xb7\xb0\xb0\xebJ\xc80\x82\x8ec\xa4\x01DRH\xf4z=\xac\xae\xaf\xed\xfa\xd2\x17?\u007f+\x80/^\x06\xf3Wy\xbc\xf9\u0377e\u007f>}\xfa$\xfd\xcb\u007f\xf9\xdbDDk\x00~\xef\xcb_\xfe\u04a3_\xfa\xd2\u007f\xfc\u0503\x0f>x\xe8\xc9'\x9fB\xbb\xdd\x1e\xc3\u04f4\xa9zb\xf9\f\xfe\xc3\xff\xf39|\xe9k\xf7\xe3\x9doy3>\xfc3\xef\u016d7_\x8fz\xad\x05\x13\x0f\xc0\xc9(\xe3\x18\xdd~\xad0\xfcR\x05\xdaU\x80\xe6\x9b\u0231\xa7\xbc\xc8e^T\x01\x9d\x19\xc1\xe3Et\xf9|~9\x93\x87\xe1\xe7\u07fd:@\xe5B\xd5\u03d7`\u0479:\x93h\xe2b\xb6\x15o\x0f\xd8\x0fN\xa2\xd9\xd2)n\xd8'\xd5P\xe7\xa6W>WJn\xea\x913\x8b\x00O\xba\xe0\xc9\xf9(\v\xa7'\xa4@.\x10H\t\xa9$\x8ca,\x9f9\x8f\x87\x9fx\x0eO<\xfb\x12^<\xb1\x8c\x15\xd7\xdc\x14\x04h\u0599\xd5+\t\xdfm\xdbQ7^\xfe\x9e\x90.\xd75G\xe1\u0716\x96\xb7&\x99R|\xce\x1f\xe2\xe0*\x05{\x81\x82\x97}6z\xee\x82[\u04a1\xa3\xf4\xcb\x18\x83Zk\no|\xef\x87q\xe7/\xff\x06\x9a\x8b;1\xe8lzT\x83\x93\x1f\x06!d\x18Y\x10\x972\xcb\xed\u012b\\\xa2\x99\xd2\xe0t\xbbK\xd2\xc6s\x97\x14\f3b`-\x01w\x12g;\xec\xde\v\a\xa4\x9a\x80>\x01\x92\x19r` \xe2\xd8V\xc2S\n\xc25@\xb3\r\x87\u06cd\x18\xa6\xcc{\x89\xb2\xa6lu\xd1A\x15\xea\x17\xc0\x99\u007f\xb9k\x1d&\x8c\xa1\xf3\x99W\xda)r\xd2\xdfk\xac\xda\u021ff5l\xef\xcf\xd6\xccN\xb4fvb\xfd\xe2\xc9\xcc7\x9e\x04!\x1eiN\x12\xbd\xf8\xf0\xa3\x8f\xdc\n\xe0\x8b\x9d\xce:\xb5Z\xb3|\x19\xcc_\u00f1w\xef\x01\x86\xcd\x14\x95w\xdf\xfd\x01}\xf7\xdd\x1f\xf823\xdf\xfe;\xbf\xf3?\xfd\xdbVk\xea\x97\x1f{\xec1\x9c?\u007f\xbeP\xeaf\x8d\x11a\xb7v\x17V\xd6\xf0W\x9f\xfb\n\x1ex\xe4)\xfc\xd4{\u078a{\xde\xf7v\\s\xd5!\xcc4\x1aP\xd0H\xe2\x18q\x1c\xdb\u02a2\x94\x95I|\u9294\\\xf52r\x9e\u06a9\x97v\x9ar\"\x05Y\xf5@aK\xe9\u03f9S\u05bc\xf3\x15\x18\x94i\xa3\xd9Q-T\x92t\xf0\xe4\x91\xd4Kp\xecE.\x1e\x85\xdd\u026b\xa9\xc6'%-1[\xad\xb8\u05b9\xccP\xeb\xbc\n\u03f7\xec\xe3K\xce\x18h\xbb\xd2^P\x1e\x1dB\u0796\x842><@\x10(\f\x86#\x1c;\xb6\x8c\xef<\xf24\x9e\xf8\xfeQ\x9cx\xe5,\xba\xdd~\x0e\xe2I\x82D\ub8bf\xb9\x11\x05\xc4\xf5\xdb\xd0i)\xca^\xf6l\x1a\x98\x9cJ\xfc\xf2\n3\x05B*\xc4\"g3\xaa~@\xab%\xc5\xf3\x85_k\x18\x13\xc3$\xb1\xa5J`w\x17\xb6i\x19@\xd6\xea\b\xeaM,]\xf1F\\\xf3\x9e\x0f\xe1\xd0-oG\u051c\u00b0\xb3\xe9\x02\xc4\xedh\xbb\x90\xca\xd1*5\b\xa5<\x83\xa2\x92-\x00\x97\x03\x1cx\x8c\x0e3\u0657U\x17%\x06\x88\x8d]\x98i3\x81\xe8h\v\xd0\xc6\x01%\x91}I\x8c\xb5j\xd1\x00\xe2\u0600b\xb7\xe0\x8e\fxh\x80\xf9\x00A \x10\xc0R,\x82r\xd2)\xad\xfe%\xa5!\x1d\xbc\xe5}\xc7^\xd4\"\x93\xe5\xcdA\x02\x9a\r\x06\t\u00f0\x05r\x99x\xbd\x14\xc3 \xe3\xf9\xb6\xe7\xac\x17\xb4\x8eQk\xcc`a\xe7a\xac_8Q\xbc\xaf\rF\xbdn7z\xf9\xf8\x89\xeb\x00\xa0\u055a-\xf4\xf8.\x83\xf9k8\xee\xbe\xfb\x03\xfa\xbb\u07fd\x9f\xde\U00096df1\xe3\xac\xfe\xe1\xe7?\xffw\xf7\xdd{\xef_\xfd\x0f_\xff\xfa7\xf6-//S\x92$\f \x91R\x06\xc6\xe4\u06fe\xac\xca?s\x0e\xff\u05df\xfe5\xbe\xfe\xed\xef\xe1\xe6\x1b\xae\xc5\xfb\xde\xf5\x16\xdct\xf5\x01,M\xd5\xd1j\xd4 \xa5@\x1c'\x8e\x8fw\xa6Q\x82\xbcq\xf0q\x0e8\x05\xf2\xcc\xfc\u0254\x12\xe0\xe19\x1c\n\xca\x00*\xad\xd8\r\x17\xedk\xa9d\xfa\x9fZ\xfd\xda\xf1\xe2\x1c\xf0S\xb9b>\x04UE\x10\xf1\x96\x11p\x97\x8c\x87\u02ea\u03ca\xb0\vWys\xf6\xe1\xe7\f\xc8\x13\xe34\xe2\x06^\xcc\x172\x10-\x0fx\x8c)\xd2\xc0\xce\x10*\xd7\x031\xfb\xe7\x04h\xb6UW\x14\x86H\x92\x04/\x1e}\x19_\xfd\u03a3x\xe4\xc9\xe7q\xe6\xdc\x05\f\x86C+\x8bc\x83\xd10\x81N\x9b\x9b$2\x8d\xb2T\xcaM\x0fR\x9e)Ib\x82\x1c\xa88\x11@.\x8f\x16\xe5k\x01g\v2\x15\xc6b\u0269P,`\xb3\xb1v\xb5iU-\x82\b\xd1\xcc4\xc2\xe6\f\xa2\x99\x05D\xd3s\x88\x9aS\x98\x9a[\xc0\xec\xee\xfdX8t5\xe6\xf6]\x81\xe6\xec\x02\x88\xadU\xae\b#\x90T\x80\xfb\x12nQ\x13\x82<>~<\x9d\xb5j\xbf\xc9^\v\x82a\x81{\xa4\x19\xa3\x84m\x13\x91\xad\u03f9\xeaj\x04\x1d\x9d\xb9)J\r\x906\x85\xf4:2VVn\x90F\xf4\xb8\x02G\xc7\xd0\xcc\xe8N\xdbs\f\b\b%\xd91\u007fApsD\u064e\xc06\x8bKq\x90^hz\x96\xb1\x9b\xaa_\xa4\x02d\x82x\x94`\xe8V#\x19[\xa5\xa6I\xcd\u0334\xef\x83\u3337\u049d\xa4\x8eQo\xce`i\xf7\x11\x1c\xfb\xfe7=\xd5\x14\x10\x06R\x9d>\xfd\n\xd6\xd77\xaeg\xe6\xeb\x88\xe8\xfb\x0f<\xf0\x1d\xe1.\xf32\x98\xbf\xd6\xe3-oy\x1b\xff\xe1\x1f\xfe\xaf\xf8\xcd\xdf\xfc\x18\x01\xe0{\xee\xf9\xd0'\x99\xf9O\u007f\xeb\xb7>\xfe\xb1\xaf}\xed\xeb\x1f=}\xfa\xf4U\xe7\xcf_\b\x1c\xa8kr\xdd&\xe6bK\xfe\xf8\x89\xd38y\xfa,\xbe\xf5\x9dGp\xf8\xc0\x1e\xbc\xf9\xfa#\xb8\xe5\xfa+q\xc5\xfe\xdd\u06390\x87\xd9\xe9&\xea\xad\x10Z\x1b\f\x86\xb1\xe3\xe5\xd3j\xae\x18Ll\x00\x9b\x06\x9f\u4cba\x8cB\xf0@/\x05\xea\\vE\x13Io\xf6V\x8e\xbc\x92/\xf2\xeb\xc2q\xc4R\x90\xb5\x0f\x15y|\x96\xc8\x02}\xe9\x92\x16\xb9\x97\xecX\x96\xcf0\xab\xd6,\xff\xad\xbd\xf1\xf3\xb4\x92\xcb\r\xb0\u0704\x1e\xa8\x04\x1b\x18k&\xa4\u065a)\xd02\x8a\xf9\x9c\xe9K\x92\xaa9\xeaQ\b\"\x81S\xaf\x9c\u00f7\xbe\xf7\x04\xee\xfb\u07938\xb9|\x16q\x1c\x83\x88!\x05C'\xf6\xbd3F\x17\x16\xf6\x99f\x1d,\x02\f\x13\r\b\xe5Y\xa9::\x82\xca\xd1\xd4\xee}O+u\xe1\xde\xff\xf2\xf6\x82<\xbb]r`\xa15\x92d\b\x9d\xc460\xb8>\x8d\xc6\xd4\x02\x82Z\v\xaa\u0442l4\x10\xcd.\xa2\xb1\xb8\x1bS{\x0ecz\u07d5\xa8\xcf\uf08cj\x10B\"\f\x15\x1aQ\x80Z\x18@\bB\x92\xc4\xf6\xfd\xaf\xd5\x1d\x85\"\xa1\x85Mv\"a\x13\x84\xc0\xe3\xadl\x86\xbf\xeb\xc9{/\xc6\x03q\xe3\x16\xc9X\x03\x83\u0120\x9f\u062a<\x9d\xef\t{\x1aa\xcf\x05\x002 \x13\x06\xa5#\xfe\xeey9\r I\xe1Rx\x8e\x8e\x9a!:\x1a,\bqCbD@?a(\x01\x04\x82\x10)\x81@\x00J\x10\xa4p\xe7lJ\n-\"+/vU\xbd\u007f\xee ;\xa0\xdfO\xec\xdfe\u0090\t\x17\xed\x1e\xb8L\xdb\xe4K\xb4\xd1\tT\xd0\xc2\xcc\xc2>\x04a\x03:\x199+\x04{K\x8c\x86C\xac\xac\xac\xcc}\xfd\xeb_Y\x04\x80\x87\x1ez\xe82g\xfe\xc3\x1c\xbf\xf9\x9b\x1f\u00d7\xbe\xf4y\xfe\xdd\xdf\xfd=\xfa\xe67\xefc\xa7K\xff\x1df\xfe\xc3\u007f\xf6\xcf\xfe\xe9o>\xf2\xc8#\xbf\xb0\xb6\xb6~\xe3\u0253'U\xb7\u06ddH\xb5\xe9Dceu\r+\xabkx\xfc\xe9\xe7\xf1\xe7\x8d:\xf6\xeeZ\xc4\rG\x0e\xe1\xea+\xf6\xe3\xf0\x81=8\xb0g\t\xbb\x97\x160?;\x05%\x05\x86q\x828N\x9c\t\x12\x9c\u0740\vFH\xb5\xd1i\x13\r\xc5(\xb2t\x84\\x\xe8J\x9c\xfbqT\xc1:gU\x15gF]\xec*>\xe1l\v\x04\x11F\xe9vU\xb8x=7\x8aL\xd9v\xd5U\xf0\x8en \xaf;[H.\xe2\x94#-\xce\xcc[_\v\xf6>8\x9c\aA\xf86\xa7\xde8\xbdU.\xe4\\0{WS\xf8l2\x8fy\x8f\xb3\xdf\xe0\xf4\x99\xa8tj3\b\xb0\xb2\xbe\x89\a\x1e\xfd>\xbe\xfc\xad\x87p\xf4\xe5eG\x93i\b\xc1\xd0:A<\xb2\x15\xb0\xf1\x16\x84\x85\xd9)\\q\xcb[\xb1\xef\x8e\xf7\xe3\xc2\xc55<\xf1\xf9?E\xfb\x95\x97\x01\x00A\x10\x81\\t\x98]L\xc8k\xc62\xd8P\u039b39\x90\x19\xb7\x84\xe4Tj\xa8ch\x1d#\b\xeah\xcd\xed\xc1\xd4\xfc>\xcc\xee\xb9\x023{\xaeDki?jsK\x90K\x8b\xc0L\x13*j@H\xe5M(\xe7J\x8e\x04@;\x06\x86\x9c\xa0\x11)\xd4ju[\xc9;\x15\x0e\x91\xb0\xc0\n\x86\x12\xbe\xa3\xa2\x0fU\xa5\x05\x94\xfc\xc57\u007fOc\xc3\xe8'\x8c~l{\x1d\xbeY\x9d\x1a\x18\x84=\x03\xa1\x1d\x90\u01f6\xa9\x98V\xe8>\xedg_\"\xceR\x83X\xe44\"%\x8c\xa8\xab!\b\x186\xa4\x93\xa9\x02\xb1f\xf4\x13\r%\b\xa1\x00\x02%\xec\xff\x85\xf3\x96\xe1\\\x0f\xa6\x992/\x11.\x15\tCc\x1b\x9fd\x00\x15\xc36>\xc9\x1a\xf2\x11\x93]xD\xfax\xe1\x8a\x1d\xce\xc4\x05\xcc\x1a\xf5\xd6\x02\x9a3;\xb0~\xf1$dn@I\xa3\xd1\bI\x92\xecy\xf2\u0267\xae\x05\xf0\xcd3g\xce\xd07\xbe\xf15\xbc\xfb\xdd\xef\xb9\f\xe6?\xe8\xf1\x81\x0f\xdc\x03\x00\xfc\x89O\xfc\x1b|\xfc\xe3\xff}\xfa\xa1\xdbp\xa0\xfe\xe9O|\xe2\xdf\xfc\xbd\xaf|\xe5+w\\\xbcx\xf1M\xab\xabko\xbcp\xe1\x02\xf5{\xbd\x89M\xbb$I\xb0\xb1\xd9\xc6\xe6f\a\u03fft\x12\x81\x92\x98j5qx\xff.\\}\xc5~\xbc\xe1\xe0^\\}\xc5~\x1c\u063b\x03{v,`\xba\xd5p\xcd=\r\x8c\x12\b\x9d'\xe80\xa7\xe3\u031c\r\xb5\x14\xa8\x89\u031b\x9c\xf3\x06\x90\xc3\b]\xb007\xd9$\x93\xd7\x12t\x80n}2\xa4I}hr\xd0\xcf\x03+<\xbf\rg\xf3*D\x1aO\xe6\xd1\xf6\x94\x876hf\x1b\x19\xc6~\x02\x0fg\x95O1\u0601=f\xd8\a\xe2\xb1\xfek\xc1\x1f\xc5\xdf\u044cW\u0754\ro1\xb8\xe0\x86)\x85D-\f\xd1\x1f\x8d\xf0\xc0c\xcf\xe0\xab\xf7?\x82\x87\x9ex\x16\xdd^?\xb3\x9a5\xdaN[\xdaj<\xff\r;\x17fp\xed\xdb\u07cf\xddw\xfe4\xe6n\xbe\vb\xf7aLo\xf4\x10\x1c\xba\x01\u01fe\xfcg\xb8\xf8\xec#\xe8\x9e_\x06b\xbf\xa9M\xb9#\x1f\u0195*Y3\x8d\xf2\xf7\x8b@PA\x1d\xad\x99]\x98Z\u060b\x99\x85\x83\x98\xdbq\x18s;\xaf\xc0\xcc\xce+P\u07f9\a\\\x0fa\x8c\x86\x96\x8c$\x12\xd0!Y\xd9\x1e\xb4\xf5\xe0\xce$M\x02p`\xcdB`(m\xfa- Q\x93n'\xe6^#\xe9^?\u9a38K\x1a;0C\x1b{\xbf\xc1Q\x1a\xfd\x84\xd1\x1d\x19\x8c\xb4?=k\xd5Bj`,\x00k\v\x922f\x88\x84=\x0f\x1a\x14\xf5/\x8e\xff \xb2\x14\x87\xf6\xfb\x94\f\x88\x04\bz\x06F\x10t]\x14*\xad\xc40b\x03\x90\x03\xf6H\x12j\xca\xfe_\n\xbf7\xe1\x9e\xd7S\xe4\x8c\f\xa3\x97\u061d\x84t\u7656\xf0\x04\x80\xd2\x05\xb9\x9c\x15\xea\xed\xb6\xb5NPk\xcc`jv\x17\xd6\xce\x1fw\xbb\r\x06\x98\x89\x99\x93n\xb7\xa3\x9e|\xf2\xa9E\x00\xe8\xf7\a\xe2\xdd\xef~\x8f\xbe\\\x99\xff\b\x8e\x14\xc8\xff\xe0\x0f>\x81\u007f\xf1/>\x8e\xcf~\xf6^\x10\xd1Y\x00\u007f\x04\xe0\x8f\x1e}\xf4\xa1k\xfe\u077f\xfb\x93[VVV\xff\xeb\x97^|\xf1}'N\x9e@\xbb\xdd\xc1p8,|\xd8\xfdJ\xd8\x18\x838f\xacmlbmc\x13\x8f<\xf5<\xa20\u011e\x9d\v8\xb8g'\x0e\xee\u06c9\xc3\xfbw\xe3\r\a\xf7\xe2\xe0\xbe]X\x9c\x9bA-\x8a \x94\x1d\x821\u0330\x8e\xa6\u059c\xc9h.\x86\x9dL\u0a33\x01!\xbf\xd8c\xf6\x92\x93\xbc!$\xa4`\v\x8c\x91\xb6Y\x95h\xe0u\x87\\Ui\n\xbc\u007f\xce\xf2\xf8q_9H\x1b\xe61\xe0\xf2\u01f0\xfd\x19W\xeb#\x9d7\xa4*\x98\x94L\xf7c|\xf6\x99\xf2\x9d\a\xf9i\xf0d\xd5\rRZ\x99\xa11\x8cg_:\x81\xaf?\xf08\xee{\xf0\t\x9c\xbd\xb8\x9a-L\x16\xc4c\x8cFCho\xf6`\xef\ue778\xfe\xdd\x1f\xc0\x9e\xb7\xfc4\xa6\xae{\aF3{\xd0\x19&\xc0z\a2Px\xc3\xdd\x1f\xc1\xee7\xbd\x13\x1b'\x9e\xc3\xe6\xf21l\x9ez\x01\x1b'\x8f\xa2{~\x19\xfd\x95\xb3\x88\xbbm\xb0N\xf2IJ\x12\u05a4*\x8clcR\x86\b\x82\x1a\xa2\xda4\x9a\xd3;05\xbb\v3K\x870=\xbf\x0f\xcd\xd9\x1dh\xcd\xecFT\x9f\x86\xd61\xb4\xb1v\x13\tb\x98@\u0606]\fH&P `j\xcaV\xd9BZw*\x91Rz\xf9\xffcfl\x0e5FZ\xa0\x19\nD\n\x10\\}Gqe\x13\xdcI\a\r\xb2]d\xac\x81^l\u040dm\x8f\xa3\xb0^\t@\xc4\x06AGC\xc6v7*c\x03\x11{\xd5xy\xf5N\xdf>c\xf9n\x86\x05t\xf6\x82V\xc0\x96\x9e\x89\xba\x1a#\x02\u26a8\xf4\xeb\x1fiF\xac\x19\x83\x84\x10J\xa0\x11\b\u02f1\v.\xb65\x98\x900\xd0\x191\x86\xc6\u07a3rd\xad\x16\xb2\u05cfE6\x8b\x90G\x8cZ\xc2\\x\xeb\xb3\xd11\x1aS3\x98\x99\u07d5\xd1o\xe9g3PJ\x9cy\xe5,\xae\xbbnxC\xeao\xfe\xd0C\x0f\x8a\xdbn\xbb\xe3u\u03dbo\xcb6\xeeW\xbf\xfae\xfa\u02ff\xbcW|\xeaS\x9f\xd6y!\u008dO~\xf2\u007f\xffo\x9fy\xe6\xd9\xdf~\xf8\u11e7\x8e\x1d;\x86N\xbb\x8d$\xd1\xd6\xf3e\x82)\xbe\x10\x02J\xda-\xad1\x96Z\x10\x044\x1bu\xecX\x98\xc3\u03a59\xec\\\x9a\xc7\u03a5%,-\xcec\xc7\xc2<v\xeeX\xc4\xc2\xc2\x1cj\xb5\xc8\x03\x1b\x03\xad\x1dw\xab=\xadqa\x94\x99K\x1bl\xca8\xc8LaNEK]\xf2*g\xce'M\n\xc3(\xfeg\x88*\x02E)\x9b\xd2\xf0\x831\x8a\xfa\xef\x8a\x10\xf6\xac\xfa\xcf\x15{4a\x81*\xac\a\xde\xf3PQO\xef\xfdQ\b;\xec#\xa5\xc4(Np\xec\xe4\x19|\xeb\xc1'\xf0\xd0\x13\xcf\xe1\xc4\xe9s\x88\xe3\xc4z\xa8\x18\x8dD'HF#\x8cF\xc3\xec5\\X\x98\xc7\xcd\x1f\xfc\xfb8\xfc\x9e_D\xe3\x8a\x1b1\xaa/\xa07J\xc0\xa3A\xeec\xeev#2\xacAE\x11d\x00\x8c:C\xf4\xd7/b\xd4Y\u01e8\xbb\t\xdd\xefB\x0f\x870I\xec\x02#L>\x16o$\x1a}B\xa4\x15\x94\x88\x10\x85M\x84\xb5&T\xd8t\u05a91L2\xb2\x8dNA`!\x00%\xc1\x81\x80\x8e\x14L(\x01!,\xff+\x04\x92\x86B\u04b0N\x86\u9389\xb9\x1a\x96\x99m\xe3\xb0\x11\x10\xeaJ \x90\xf9\xa0\x94\xf4$\x82\xe5\x06\xa7\xa3\xa0\xed\xbd\xec\xaa\xf1\xde\xc8`\xa8y\xec\xfe`\x02\x84\x01\x82\xb6F\xd0\xd3\x10\x86!b\x86\x1cq\xd6\x14\xcf\\\x0f1i8\x8d`\x84\x15\f\xb1\xeb5\xd8F\xa5\x00\xa4\x95\x12&\x11a\u0612\xd05Q\x88\xa1\xe3\n\x95\xad\x10@\xa4\b\r%\x10\xcaT\x82lw\xbb\xbd\x98\xd1M,\u0146\xde\x00\xc1\xc5.\xe4\xc0\xa6\x96q\x92\x80\xfa\t\x10\x8f\xc0.\x93\x95\xbc`\x12\x99\ue01d\u02a81=\x83'\xbe\xf3\x17x\xe0\U000df10a\x1a A0\xf1\x10\xa3a\xd7H)\xc4\xdd\xef\u007f\uf4df\xfe\xf4\xa7>\xb2\xb4\xb4\xeb\xb9O\u007f\xfa\x93\xf2\xd7\u007f\xfd\x9f\xbc\xee\xabs\xb5\x1dO\xea\xbd\uff5b\x01\xe8\xe7\x9e\xfb>}\xf5\xab_\x13\xbf\xf1\x1b\xff\x94\x88\xa8\a\xe0\u007f9w\xee\x95{\xbf\xf0\x85/\xfe\xd2\x03\x0f<\xf8\x8bO>\xf1\xe4-O>\xf5\x14\rG\xa3L\x97^>\x8c1\x18\xb9h0!\x05\xa4\x14\x90B`0\x1c\xe1\xf8\xa93x\xe9\xe4i\b!Q\xaf7\xd0l6\xd0l4\xd0j6\xb1\xb40\x8bC\a\xf7\u16ab\x0e\xe3\x8a};13\u0570\xe3\xe4A\bU\x93\xaeq\xe3\x05,\xa4\xd5\n\x91\xa7\x84\xb0\x1a\xe71\xb5\x01\x8a\x86\xec\xecM\u0325\xdbg\xe3\xbaF\xb9\xe6\xdd\xe4A\xbf\xde'%\xeb\xe6\xbb'2\x15\nG\x1aSux\xe9=\xe4\x19&\xf9\xfc{\x19\xd0K\x95\xba`\x1fp\xf2\u6c12\x12\xb5(\x80\x10\x02\xbd\xfe\x00\u03fft\n_\u007f\xe01<\xfa\xf4Q,\x9f\xb9\x88\xd1h\x04\xa5\xec$G\x1c\xc7\xd0\xfbeH\x9c\x00\x00 \x00IDATI\x8c8\xb1\xd4\n\x00\x04a\x88\xeb\xde\xf7a\\\xff\v\xbf\x8e\xa9\xabo\x83\x0e[X\x1b\f\xc0\xed6\xa4\x80\xf3\xfe\xf6w\x04\xf6C:\x1c\xf6\xc1`\b\xa1Ps\xcdH!E\xee!oRE\x86\x81a\x031\x18!\u060c\x11n&\x90#\r\xc4\x1a&\x89\xc1&A2\xec95\x8etS\x98\n\x10\x04\x0e%X);\xd0\x04\x01M\x02Z\u0694\x1db@\x0e\b\xa4\x80\xa4\x96\x0e\xeeVF!g\u02a8\xd806\x87VO\xdd\f\x05j\n\x19\u0152\xca\xfa\xd2j7\x9f\x9a\xb5\x80\x15\x1bF7\xb6\xb4J\xb9\x1a\xf7\x95\xb3AW#\x18\u0608<J\x182\xe6<]\b[[Ld\x16\xb8\u0300\xd3\x0f0\xa5\xd7f@F\x80%C\xb9\na(\x00\x1d\x89\xb1J\u045fk\xd2\f\xf4b\xc60\u046eB\xa7l\xaax\xe4\x94UBH\x90\xb6\xde\xeb\xf6\x17\xb2\xdd%\xe4\x19w\u07adL\xdeN\xd1\xf5\xaf\x8c\xa52\xebS\v\b\xeb-h\x1dCP`\xfb\x14$\u0120\u07c71|\xe3\u007f\xfa\xf2\u007f:\x02\xe0\xb9N\xa7#3\xd1\xcee0\xff\xf1\x1c\xd7\\s\x1d\xa7/\xf2g>\xf3\xef\xe9\xa3\x1f\xfd\x15\u07b9s\xcf\t\x00\xbf\xc7\xcc\u007f\xfc\xb1\x8f}\xec\u007f{\xea\xe9\xa7\xff\xe1V\xcf!\x84\xa3\x05\xd8\xc0$\x1a\x80\xcen\x80T9\xc2\xcc\x18\f\x87\x18\x8eb\xac\xacm\x82aG\xc4\x1f|\xec\xfb\x98\x9aja~f\n{v-\xe2\xf0\xfe=8\xb0g\av,\xcea\xbaUG=\x8a2O\x10)\\\xb5m\xf2\x84y\xadmXp\xa2\r\xe28A\u007f\x14\xa3?J\x9c\x0e\xb7\xa8\xb2\b\x02\x850P\b\x02\x05\xa5\x94\x9dnt\x93\x8aRX\x90\xac\x87\x01\x820\xb0\xbf+\xf5zv\xc6V\x89\xfb\xbd\x99\tQ!\xb9)\x9b\xe2(*<\u01a2\xb9\xa8z\x14\xc97\xbeB.+3\xae\x19,%!P\n\x81R\x88\x93\x04\xe7V\xd6pr\xf9\x1c\xee\xfb\u0793\xf8\xde\x13\xcf\xe1\xec\x85\x15\x8c\\%N`\xf4\a#\x9b0o\\\u2f63Uv\\\xf3&\xdc\U000abfc5\x83\xef\xfcyP=D\xbf\xaf\xa1\xbb]\xab\xec\x91b|w\xe0\xb7\\\u04e1 \x00&\x19\xc1$\xa3\x12(\xe5CJrd\xa06c\xa8n\x02\x8c4Lb\xab<;\xb4\x13B\xaa\xa8\xc0\xa5\xdb\x12\x97\xc0\x81\xb0\xf4\x89\x93\xee\x89! \xb4q\x13\xac\x96X\x16\x9b\xdaR\v\rY\b#\x99$+e\xd8\x11\xfa\x91\u05b6b\r\xac\"D\x11\xf9Im\x19\u0367\r0L\x18\xdd\xd8*U\xc0\x13\xbcY\x18\b\xfa\x06AO\xdbsM\x81\xdcp\x85\xe72\x8f-\xe0e\xf5Sj~\x96%~ymK\xe9\xaaa\x100$\x82\ti\uceb3\"\xc0\xbb\x8e\xbe\x19\xb7#H\U000e90c4@i\xff!\xfd\xbd\xec\x87\xc5\x18\x17\xe8\x01/!>\xed\x93\x10t\x12\xa31\xb5\x84\xe6\xec\x0e\xac\x9d?\x01\xa9\xac\xc1\x99\r\xf3`>w\xfe<=\xf6\xf8\x13\xfb\x00\xe0\xaf\xff\xfao\xfe\u007f18\xa4^/'\xfa\u044f\xfe\n\xdf\u007f\xff}\xf8\x9b\xbf\xf9\x1b\xfa\xc4'\xfe\x80\x01t\x1ez\xe8{\xbb\xfa\xfd\xfe\u011f\x99\x9e\x9e\x02k\x8dv\xb7Wb&\xb8\xf0\u007f\xfbB\x10\xa4\x12\x90\xceG\x99@H\x92\x04\xab\xab\xebX]]\u01f1\x13\xa7\xf1\xe0\xa3\xdfG\x18(\xd4k\x11\xa6\x9a\r\xcc\u037405\xd5\xc4T\xa3\x8e(\n-\u007f9\x8am\xda{\xac\xd1\x1b\f\xb0\xd9\xeeb0\x8c\x11'1\x86\xa3\x04q\xa2\xb3\xada\xda\xe0\xb4\x15\xad\x82\n$jQ\x88F\xbd\x8eZ-\xb4\xff\xa6\x14j\xb5\x10\xcdz\rK\v\xb3X\x98\x9b\xc6T\xa3\x810\f\x10\x86\x01\xea\xb5\b\xf5Z\r\xf5z\x1dQ\xad\x06%\t\xacu\x9a\xae\xe2\x00\x1e\x05c\xbd\xdcn G|\xe6\x92\x05\t{4\x8e\x10P\xca\xeehD\xf6\xe5*\"\xad1\x18\x8epqm\x13\x17.\xae\xe1\xf9c\xa7\xf0\u0413\xcf\xe1\u0157\x97qqm#\xa3S\u0229\x8f\xb4\v\xf05i\x90/\x80\xe6\xd2^\\\xf5\xc1\x8f\xe2\xda\xff\xe2\x9f`\xe6\xe0!$CF\xbc\xd9\x05\x1c%\xe6\xef\x0e\xaajH\x1f\x93\xb8\u0510\xcd\xf6/\xcc\x0e\xc8\x19\xe1\xa6F\xd03\x10\x9a `\xabo\b?\xb0\xb0b$K\xd8\xecI\x03\xdb\x03a0(a(M0\x92a\xdc\x00\x81\x18\x01\xa1\xd1 \x06\xe2\xba\x04\xcb\xf1V\xc8\x18\xa0{\x15\xebH3\x1a\x01\xd0\f,\xf5B\xb9t\x05\xb1\x01:#\x83^l\x90TU\xe3\xfe\a{`\x10t\xb5\x95\x1d\xb2mv\nS\xe2\xcch\xf29\x15-\x12\xca\n\x1dW\x1c\v7|\xa5\x19$\x009d\x84\xd0\x18MI\xe8\x80&)XK\xef\u0378\x92V&\f\xa9\tl\\\xe4\x9csG\xf4\xcf'\x9d\xfcMUVy7\u01e9\xc6t\x82fs\x1e\x8d\xd6<V\xcf\x1esiM\x96^S2\xa0S'O\xa2\xd3\xe9\\\xcf\u0312\x88\xe2\xcb`\xfe\x13>\xdef\r\xe5\x19\x00~\xeb\xb7>\xfe\xae\v\x17.\xbcWO0\xea\xaa\xd5j\xf8\xd5_\xfdU\\\xf7\xc6k\xf1\xf0\xf7\x1e\xc0K\xcf?\x8bg\x9f?\x8as+\xeb0\\\xad\x86a\xa7~\x10d\a^\x04\x89L\x1eH\x00\xe28\xc6h4B\xa7\xdb\u00c5\x95\xb5,\xce\xcb\xdfG\x8e\xd1\x14\x9eJ\x80\xbd\u01a7?\xafY\u04a2\xb9\x9b]\xe4zv\xb2\u0a64\x95\xf4EQ\x88Z\x14\xa2^\xafaz\xaa\x85\xb9\xb9\x19,\xcc\xce`nv\x1a\v\xf3\xb3X\x98\x9d\xc6\xecT\x1d\xf5(D\xb3Qszn\x02\xb3\x86N,U\x834\xa6\x8cr\x85G\xea\x13nw\x03\x02\x81\xb2\xe9\xef\x83\xe1\b\xdd^\x1f\xbd\xfe\x00\xfdA\x8ca\x1c\xa3\xd3\xed\xa3\xd3\xeda\xb3\xd3\xc3\xca\xea\x06\x96\u03dc\xc7K'^\xc1\xb9\x8b\xab\x18\x8eF\x96\x0er\u05db\xc4\xec\x00<\xc9\x00\x1c\x00\xa6\x16\xf6\xe2\xd0-?\x85\xab>\xf8+X\xbc\xfd\x1d0\x01a\xd0\xeeY?\xefR)KeN7\x1d<)\xb9\x8b\xf9\xfd\x85\x02R\t\xeb1\x12\xb65T\xdf5\x005;\x90Hw&[E\x9c\xb8II7\x1e\u039e4U\xb8\x0e!K\xcb/\x8b\x84\x10\xb65(f\xc4M\x01\x1d\x8a\u0262\x94\x12\xb8i\x03l\x8e\fb\xc3h\x05\xc2\xd2\x10\xb0Z\xf1nl\xa7\"\xb94\xa4VFb94\b;\xda59\xd9\xc6\xe0\x19.\xc8E\xfd\xac\xd9\t\xb0\x8a\x02\xda\x13\x17\xe5K\x94N\x8d\xda\xc5M\x18\x00\x9a\xa1\x86\xaeBoI\x98\x90^\xd58N\xf6\x16\xa6\xebfl\x157\xec\xe5\xa3\xda\xe7\x19\xf7\xd5e\xce\xfbE\u067bG\u05b4\xab\u079cC\xb35o)!JS\x93\x14HJ\xb4\xdb\x1dlll\xdc\u011c\xec\x06\xb0|\x19\xcc\u007f\xc2\xc7\xfd\xf7\u07d7\x02:\x9e~\xfa\xfbw\x9e8qb\xac\xdaN\x8f\xa5\xa5E\xfc\xf2/\xff\x03\xdcq\xc7[\xf1K\xbf\xf4\x0f\xb0|\xfc(\x8e>\xf90\x8e>\xf74\x9e;z\x1cO=\u007f\f\xc7N\x9d\xc5z\xbb\x9bU\xcb:\u06da\v@*[\x89\xa6C<eg\xbe\xd4Od\xcc^\x94\u01abBx\n\x13_\x93]\xd0g\xa7#\xe6\xb6\xda0.F=\xdd\xd2j\r\xc4q\x82\xc1p\x04\xd1\xed\xe5\x8bM\x16~k\xa9\xa1f\xb3\x81\xb99\v\xe8\xadF\rs\xb3S\x98\x9b\x99\xc6T\xb3\x8e\x1d\x8b\xb3\u0635\xb4\x88\xa5\xf9\x19DQ\u0359`q\x81\xe3g6\xe8\xf5\a8\xb7\xbe\x86S\xa7\xcf\xe1\u0339\x15\xac\xaeobec\x13\xedv\x0f\xdd\xfe\x00\xfd\xc1\b\xedn\x0f\xbd~\x1f\u00e1\u0749h\xadsN\u05f8?\x1bc\x17\x10\xb7KH\x8f\xf9=Gp\xe4\xb6\x0f\xe1\xf0\x9b>\x80]\xd7\xdc\x01j\xd4\u047b\xd8A\xd2\x008\x14\x95\x01\xa8\\\n\xf7\xf0m\x1a\x88\xb6n\xef3Y\x05G\xd8\xd6\b\x06\xc6R\r&\xa5\x1c\xb82\a\xb6 F\xa5\xbc\xca$O\xd2d\x03\x14\\\xc3\u05cd-\xb0\x15]\x804\xdb\xe1\x1c\u0348\x9b@R\x13\u0145g\x12\xa8\xb9\xe7\x1b$\x96:S\"w\xaa\xd4\x06y\x90\xf5\x84'\x101#\xec\x1a\x88\xc4%\x1d9\x90%FiBw\xb2\x1d\x1d\x97UR\xec\xe3\xa8'ka\xef\xe5q[;b\v\xe8L\xc0\x88$\x8c\x12\x93\xb8\xb1jZ\xd4 W\u06a4U\xb8\xc9\x04]\xee\xf3\xe6\xfc[L\xeeL\x99\xf6\x15\x84\xa3MM\xa2Qk\xb4\xd0h\u0343\x84t\x16\xb8\x80\x94\nB(\x8cF#\xc4q|\xeb\xbd\xf7\xfe\xd5.\x00\xcb\x0f<p?\xee\xbc\xf3m\x97\xc1\xfc'u\xfc\xee\xef\xfe^\n\xdc\xc1;\xdf\xf9\x8e_6f\xf2M\xb2w\xef>\xec\u0673\a\x80A\xbd\xd9\u01357\u074ekoz\x13\xd0>\x83\xd5S\xc7\xf0\u02a9\x97qj\xf9\fN\x9e>\x8b\x93\xa7\xcf\xe2\x85\x13\xa7q|\xf9\x1cN\x9f\xbb\x88\xf5v\x0f\x83\xe1\bZ\x97\xbb\"\"K\x11\u03ebv\u1188\x8a9\x9fy%>>*N\xbe\u0169\xd7\xc8\u02dd\xf8\xbcf.\xe5\xfc6g\x9ab\x064\xd94\x18!A\x8c\x8c\xf6\xb0\x15\xc7&\x8eq>d\xa4\x94\xa5\x86\xe6fZX\x9a\x9f\xc3\xd2\xc2\x1c\xe6f\xa71;\xddD\xa3Q\x87\x92\x12Zklv:XYY\xc7\xea\xfa\x06V\xd77q\xe6\xfc\n\xd66\xdav\x98G\x97\x06\x83\x1c\xf0\xa7\x15x^\x89\xbb\u01b0\xb6C7\xa9|4\xa8\xb5\xb0c\xffux\xc3-\x1f\u0121\x1b\u07c3\x9d\x87o\x86\x10\x11F\xbdM\xe8n\x0fA @#\x81\xe1L\x80$\x12\x05Kc\xc03(D\x01G&\x03c>(\t\x91\xc0\x02y\xca\x1fk\x86(\f\u028c\xc1wE\xa9_\xfd;\n\x14\x90\xb6*r\x1d\xe4\u0563\x1c\x1a\xfb\xbb\x12\x89\xa4!ad\x81\xa6\x1e\xe3\xd3\xfd_\x17;\xbdv\n\x98[Y\x880Yz\"\xecj\u0221\x9b\x99H\xc3\x1bR\u0547\xd74\xa7\n\u0687\x8a\xec\x9a#\xaa\U000f26483O\x98\xbc\x9bO)]\x9e\xff\xb0a\xa8\x81\xfd\xe4\x8cZ\x04\xad&\xa6\u064d\xbd\xb4\"a+I4\xb0\rh$\xb6\xd9\xee/=\\\xbc\x0fQ\xb8'\xf2\xc9U\"Bsf\x11a\u0530>\xf1R\x82\xa4\x82T\nF\x8fp\xfe\xfc\xf9\xf0\xa1\x87\x1e\x9e\x02\x80\u007f\xfd\xaf\xff\xe7\u02d5\xf9O\xf2\xf8\xdc\xe7>\x0f\x00\xf8\xecg\xef\xbd\xee\u0739s\xd7UY\xe7\xa6\u01d5W^\x81\xfd\xfb\x0f!I\x86H\x92\x04\xf1h\x04&\x01\x11.b\xf6\xcaY\xcc\x1f\xbe\x16\xd7\x0f7\x80\xf6\x1az\xed\rln\xaec\xb3\xdd\u0145\x8b\xabx\xf1\xc4+x\xe2\xf9\xe3x\xf4\xfbGq\xf4\xc4+X\xddhc0\x8cm\xcf\xdc\x15\x98\xaf\xad\xf5M\x90*\xb4\xcd\u0634\xbc\xa4t@Hd\xa1\x05 \u06f0e\xa7\x90I?\x18\xbe\xb9\xb6o}k\xaba\x032n\x98HHk\n\xa6\x14\x94G/23z\xfd>:\x9d.N\x9c:\x03\x80\x10\x84\n\xb50D\x10(\xeb:\xa8\r\x06\xc3!z\xfd\x01\x92D[e\x88\xa3w|\x90N\x17\x14\xceF\xfeM\xd1C$=m\x93@\x06\x11\xa6\xa6\x96\xb0t\xf0z\\u\xf3O\xe3\xc0\xd5o\xc3\xf4\x8eC\x10*D\xdc\xeb\xc1\xe8v\xb6\x18R\xcc\x10Z\x83\fa0\xa7,\xa0W,\x86\x85`\x03.J\xec\xc7\x12\x9a\x04\x814#\xea$\x19\x90\x93\v\x00\xceS\x16\x8a\xae\u06d5\u068e2\x8ap\x1emG\xc6n\xe4\xf2\tIKk\x18IY$'%@\xd8\xd5\x10\t#n:\xfa\xe1U\x98\xc9\xd3%\xf8e\x1f\u0205\x01\x82\x9e\x86r;\x8fl\xf4^\x97\x16\xad\x94&\xc2d'\xd1\xfc5\xa6bk\xb420%\ud193\xa5FR`w\xe7C\f\f\x1d\x87Nf\xeb\x8b%\xb6\xcdi\xa1\xd9J \xd3\xe9X/\xac\x84\xbd\xc72\x8d\xf9D{V\x04V\xc9Vo. \xacO\xa1\xdfY\xb3\x86f$ U\x88x4\xc0\u0253\xa7\xf0\xfe\xf7\xbf\xef=\x00\xbe\xfew\u007f\xf7\xf9\xcb`\xfe\x93:\u039cY\xc6\xee\xdd\xfb\x00\x00_\xf8\xc2\x17\u007f\xf6\u0739s\x98\xa4-\x97R\xe2\x96[nq\xa0b2\u054a\xbd#\x04b\xaa\x01\x14\x81\xeaS\x90\xd1\x0e4\xe6zh$\x1d\xec2#\x1cIb\xbcm\xd4G\xd2\xedb\xb3\xd3\xc1\xcb\xcbg\xf1\xcc\u0457\xf1\xe4s\xc7\xf0\xfc\xf1e\x9c\xbd\xb0\x86\x8dN\x17\x1d\xc7!\x0f\xe3\x04\xc3QR\x18t\x91B@)\t%\x05jQ\x00\x90@\xb77\xc4(\x1e\u066a^\b\b!a`\xacQ\x94Kf1\u01a0\x16E\u0635k\x17\xc2(\x82q\x89\xf2\xc3\xd1\x00\xf1(vZ\xec\x11\xe2$\xc1h\x94 \xd6\x1aqb\x15%a\x10@\x90\x80\xd6\x15\xed&_\u0152\u068b\x0e\x86\xe8\xf5\xfa.\x1d\a\x19\r\xe2\xeb\x9b\r\x19\x90.\u058by\xb2\x0eg\xbax\u03a6f5\x8c1\x88\xea\xd3\xd8s\xe8&\xec\xbd\xeav\x1c\xba\xe1=\xd8u\xc5\u03687\x16@\x10\x88G\x03\xc4\xfd\xae\xe5\x9aS\xe3a7\x80\x02M\xa0\x8e\xd5\x15\xf7\x17\x03\vz\xba\xbaI\xe61\x1f\x05\xed6\xf2\u034d\x05\xf2v\x82\xc0M>\xc2\x01\x1c\x95\x00\x8e\xb6\xe4h\xb6\xc0\x1f\x93\x03\vL>\x1cF\xda-\xb9\xa9d\xc9X^=\xe8k\u02041jJK\xbb\x88\x92\xf1&.\xf9+'\x82`\xd03P}\xe3\x1a\x9e\x94/Zi\xdf\u0420\xb0k\x9c4\x92\xe4\xb3+9\xb7QvQ+\xe6\u0125\xf4\x910\x06\xd0\xc5\xd7M&\tH3\x06\xb3\nZ\u0456\x15\xbaH\xac\xdd.\x19\x17\x00\x92\x1a\xaa\x15\xe6\xde\xdct\x03\t\x10\xe5\xc5U\xc1\x84\u051d\x93I\x124\xa6\x16\x10\u0567\xd0k\xafdW*\xa4\x02\t\x85\xcd\xcdM\x1c?\xfe\xf2\xad\xcc<OD\xab\x97\xc1\xfc't\xa4@\xce\u0335\x9f\xfb\xb9\x0f}tk\x8ae/n\xbb\xed\xcd\x13\xca\x18\x93\x99T\xb3\x90HD\x1d\x89\xac\x03\xc1\f\x90\x8c@j\x04\x19\x0e\xa1Z\t\xe6wj\xcc\x1f:\x82[n\xbb\x15<\xe8\xa1\xdf\xed\xe2\xfc\xca\x1a\xce^\\\xc1\x85\xd5\r\\\\\xdb\xc0F\xbb\x8b\xcdN\x0f\x83\xc1\b\x86\x19JI\xb4\x9aM\xabYo6\xb1w\xef^|\xf7\xb1g\xf0\xc7\u007f\xf2\xe78\u007fq\xc5\x15\x8c\xd2\xd9u\xdaiN!\xed\xec\xf4p0\xc0\xee\u077b\xf0\x8f\xfe\u046f\xe1\x9ak\xae\xc9\u0498666\xd0i\xb7\xd1\xdeX\xc7\xfa\xfa\n\xba\x1b\x1b\xe8t\xdahon\xe0\xc2\xca*\xce^X\xc5\u064bk\xd0\xed6(\b\x10\x86\xa1m^f\x82\x04\xbf\xb2v\x9bQ\xe3{p\x97'\xdc\xf3*\u06ceG\x9b\x8c\xf7\x96R@\xa9\xc0N\xda:E\n{\xde\xdd\xe9\x13\xde\xf13\xff\f\xd7\xde\xf9\v0\x89\x8dS\xd3#;\xb8\u00de%\xad\x1f\x94\x93\xc9#\x13 \xechp@\xe8-\x04[\xf2\xccU \xe4\xf3\xe9AO[EG*\xe1c[\xc1\x96\xcb\xee2\xddP\xf0\xa2'\x94\x02 \xbc\xfeFy\xcd\xcc\xc7\x12!\x98a `T\x9e\xbeC\x06 c\x10i\x86H$\u2980\x91Te\xbb\xf2\x9a\x00]\r\fT\xcf5<\xb3\x99z\xce\x16\x9b\xaa\xe9\x9d*\x9a\xc5_,\x8btU\xeeMO\xbe\xe9Z\xbai\x14\xf9\xe2&\xd2\xd73}\x0e\rD\x1d\r\x10a0+\v\xd7[\xb8V\xaf*O\xe9\x1b\"\x012\xf9 \x1c\x15,,+\x9a\xa1\xeewKw\x83i6\b\x1bM\x84a\u0765G\x190\v\x80\xac\xaa%N\x12\xb4\xdb\xed\xbb?\xfd\xa9?n\x02X\xfd\xf6\xb7\xbfEo\u007f\xfb;\xf92\x98\xff\x18\x8f\xbf\xfd\xdb\xff\x17?\xfb\xb3?\x0f\x00\xf8\u02ff\xfc\xf3_|\xf1\u0157\x0e\xb9@\x8b\xca\xe3\x8a+\x0e\u399bn\xb6\xb4\u0224\xe6\v3\xc0:\xbfcI\x02A\x03\x1c6\x900\x90\xc0~_\x98\x04\xb2\x16CL\xc7h\x98\x11\x0e\x1dHpH'\xf9\xa8}V*\n\xe7\xc5!\x01\x15\x022\x00d\b\x84\xf3\xe8\x87\u007f\x01\xf3'\u007f\x91=\xdc\xd2\x13:\xa3I\xa4R\x102\x00L\x02%%\xae\xbd\xf6j\xbc\xfd\xedo\xc3\xe6\xe6f\xe6p\u021cs\n\xc9p\x80Q\xaf\x8d^{\x1d\xab\x17\xcf\xe3\xe4\xa9e<\xf5\xcc\xf3x\xf6\x85\x17q\xf2\xf4\x19\x9c=w\x01k\xab\xeb%2\u0213tIi\x1b\xba\xe4\xbb4\xe6\x8dd6V}B\x04\u0522\b;w,b\u03de=\u0635s'@\xc0\x13\x8f?\x89\u04e7\x97\x01\u0599w\xb7\xcfs\f\xba\x1bX?\u007f\u00a93b$\x83\xbeK|O\xb7\xda\xc5Qr.\xd9\x13\x90f\x84\xed\x04IH\x18N\xa9\xca\u0127*\xd4\xf3\x15\x11\xaao2j#\xf3Ow\xf6\xa9\xe4\x05*\\r\x04\x9a_\xdd\xf7}\x95H\x8aod\f\x88\x05t\xe0Q.\xec,];\x8eviI\xe8\x90\n\xd2\xcbW\v\xe8L\x80\x1a\xda\ub509\x17rlR*\xc9\x1ad\tT\x84A\x8ceUy\xe2C\xf2c\xde\xc6\xe5/\x05j+\xe36\xec\xa0Nz\r\xe9N\x80\u025eO\xd0\xd30\x02\x18MI\xbb\xc0q\xf1=\x13\xb1\xab\xcau\xdep\x15d+\xf0\x14\xc8\xd9\u07ce\x19\xd7\xc4u;\u0374\x1a'o'J`\xa8Z\x13A\xbd\x99\xdd\u06e9m\x85\x94\x01t\x1c\xe3\xc5\x17_\x14w\xdcq\xfb\x8d\x00N\xbd\x9e\x81\xfcu\x03\xe6)\x90\x03\xc0\x97\xbe\xf4\x1f\xefn\xb7;A\u0784\x1b\u007f\xfdo\xbf\xfdvLO\xcf\u0098W)\x1fe.\xf2\xa1\x19\xaaH\x18)aT-\xff\x00\xb0\xb1_\xde\u0523eo\xdcG\x86\x84\xdbn\xdbG(\x002\xaaCJY\xa9\xbca\xb6\u0737\xfd\xbd\xc6\xf9\x93\x8c\x9c/L\xe2Gdf\x8a\x93\xb0\xdeD\xd4hazi7\xf6\\q\rn\xba\xdd\xe0\u00ff\xa8\xb1\xb2r\x11/\xbet\x1c'N\x9c\u00a9\xe5\u04f8\xb8\xb2\x82N\xa7k\a\xa2\x9c\xbcpc\xb3\x9d\xfd{\xa2mU\x9dv\xf9\xc30@\xa3\xd9\xc0\xd2\xe2\"\x0e\x1d<\x88\xab\xaf\xbe\n{\xf7\xee\u016e]\xbb\xb0o\xdf^\x1c<x\x00'N\x9c\xc4?\xff\xe7\xff\x1d\x8e\x1f\u007f\t\u02bb\xa6\xf2q\xfc\u026f\xe1\u01bb\xfe+\x84\xf5i\x18A\x99\xde:\xdb\xees\xa9\xa2\x857\xdc\x04\xfb\xe1\x8e\xda\x1aF\n$u1\x06\xe8\xe5\x14x\xdf\xdeD\x0es \xf7\x91\xd1R\x10\xe3,Ju\x95:)\xabjkb\x86\r\xe7\xd6\b\xce\a\x05,`\x82\u0519\xd2\x01\x9ea\x04}K\xff\x8cZrL\xed\xf2j\x80\\$\x8c\xa0ke\x96\x80\x1b\xb5w\x1c=\x00\x98t\x98\xa9\x1c\u00baU*I%A\xef5\x1f\xfd\u0184\x93\xefd\x03E\"\xc3\xf5\xfc'\xdd\xf7D\xc2\bz\x06L\x84x\xca\u06d1P^\x95\u02c4\u11feP\xea\xf4\u51842\xd5JV\xa9\vgfWm_\xcf`\x04Q\x1dA\xd8\xf0z,\xf63\xa4\x82\x10I\xdc\u01c5\v\x17p\xee\u0739w\x00x\u0753\xe6\xaf\v0\xbf\xef\xbeo\xe2\x1d\xefx\x17\x98\xf9\xc0\xddw\xbf\xff\xb6\xf5\xf5uL\xda%\xce\xcf\xcf\xe1\xfd\xef\u007f\x9f\xad\n\xf5\x0f2\xa1;f\x13X\xaa\xfel\xb50\xf6\xab\xb3F\xa5.\xfc\xac\x8a\xecdd\x10\x04c\x1d\xf8L\xf2\xe84\xe0\x96\x05J\x10H\x81(\n\x11\x85\xa1\u05ec\xca\x03t\v\x897\xee\x1f\r\t,\xee\x9f\xc1\x9e\xc3G\x10*i\xf5\xe1\x83>\xfa\xfd\x81\xd5\xc7\xc71\xfa\xfd!\xda\xdd\x0e\xd6V\xd7\xd0\xed\xf6p\xe1\xe2\nVVWQ\x8bB4\x9b-LOOaff\x06\v\xf3\xf3\u063d{\x17v\xef\xd9\r%\xc3\x02\xa0\x19\xc3h6\x1b\u0540\xe3}\xa2.,?\x83ao\x13\xb5\xd6|\xb6) S\xad\xe4\xe0B\x022y\x1fpv[tk\xe4\xe4\x03\x05U\x045A\xb8E\xa0\xe3\x14\x1d&'L\x84\x01D\x89\x9a+\xe7\xbf\xfaCU<\x11\xecP$\xec\xbd+\xc8\xdf%\x9fr\"\xc8\u0100\x98\xa0\xc3<S-\xa5\"\xe4\xc8 j;\u06a5\xe1@\xae\xd4s\xac\x02rb \xec\x19(w\x9d\x9c. Nv\xc9\xce\r\x91\fg=\x99\xccLml;\xe3]\u05eb$xrz$\x9f\x91H\xa7`\xcb\v,9u\x960l\xad\x05\b\x18\xb5\xf2k\x15\x86\xa1F%Z({\xaf\xc9\u04d0{\u0397\xbe\xcf(;\xa5\r\n\xf9\xe0v\xd7+\x03\x84\xb5&\x84\x90\xd9k\x04\x10\x84P \x92\x88\xe3\x04\xedv\xfb\xed\xccLD\xc4\x0f>\xf8]\xdcq\xc7[.\x83\xf9\x8f\xeb\xd8\xd8\xd8 \x00\xfc\x99\xcf\xfc\xfb\x0ft\xbb\xddk:\x9d\x0e\xa4\x94\x94s\xc0\xf9q\xfd\xf5\xd7\xe3\xb6\xdbn\u035a\x9f?\xfa\x83\xc7\xcc\xf1/u\xa4\xe1\n\x85f\x8f\x1b\xc9O\xc1<=W\x02\xa3^\x8fP\v#\x98V=W\xba\x94T\r\x13\xf3\x9f\x19\x18%\x89\x05\x91\xb0\x8e\xe9Z3\xf7A\x17\x02\xb9\x8b4\xd0\xedn\xa2\xd3\xe9bn~\x0ea\x10\x00\xf0+m\x8d8\x1ea\x10ws\xafv\"\xec\u0631\v\xd7\\s\r\xbe\xf0\x85\xad\xf3p\x87\xbd\r\xac_8\x8e\xe9\x1d\a\x1c\xf7\xa9\x8bzB\xaeh\xf9yZ?\"\xbb.\u02a1A\xe8\xc0/\xad\xd0=?\xb2\xfc\xda\xc9V\xdeaGC\r\xd8\xf1\xd3\xde\xebm\xbc\x99\xf8J\x19\xe28\xe5P\xdd\x13%\x94'\x97\xa8\x9c\xef\xea+/8\xe7\x93e\f\x18i\x95\x1a~\xcf@\x8c\x80P'\x19\xa0'\x91\x18\xabp\u02e7\x915<\xd3\x06d\xdah-{\x94\xbb\xc1\x1e\x9atY\u07a9\x17\xbc\x1a\xc6\x1a\xa5\xbe\xd8\xdc\xf9\xa5\xf89\xa0n\x95)\x18\xba\xa5\xef\x95qrA\xe7Q\x1e\fL\x01\xd0\xd5\xc8\xca\x11\v\xc6\x12\xee\x9c\x04\xa8@\x162\xb9\xb6W\x99=,uA\xd2A?A\x12Q\xad\t\xa9\x82\xcc?)-\x88H\x06l\x18\xb4\xbe\xbe~\x13\x80\x10\xc0\xf0\xde{?{\xb92\xffq\x1d.\xa3\x8f\x01\xe0\xfe\xfb\xbfs\xd3\xc6\xc6\x06\x88(!\"U\x06r\xa5\x14~\xfe\xe7\u007f\x0e33\xf3`N\xb6\xcd5\xc4\u039f;\x05D\xf2\x06\x91R\xfa%\xbd\x96\u010d\u29c0\x9fz\u02cc\x93\u00d3K&\x9f\re\x9d\x14\xa3\xbf8\xe7\x0eG\xa3\xa1\xb5!0\x06\x83\xc1\x00\xfe\xe2\xe8\x9fgff\xe4\x16\x9c;\xef\xbc\x13\xd3\xd3\u04d6\u04dft\xcd\xc3\x1e\xd6\xce\x1e\u00fek\xdf\x01\x01\x01\u05ba$s+\x0e\xe2p\xa9$\xe6\xd4\xc3\xddyZ\xa7M\xb4\xa4N\xb9%k\xa9\xb8\fz\x1aA?\x1fa\xf7\xb7\xfad&\x039\x95\xaa\xf3j\u04ba\xb8\xf8PE\r\xcf\xf0\ar\xc6w-V\xa5\xe1\x93\xea\xf9\x0f\x92\xb6<?%\fj\xb2\xb5\x02(\a\u007f\xbb\x1fUC\xcbA\v\x9b\u049c\rh\x8a\xc4y\x94\x9bTzY:\xb1TN\x99\xb1\x89\x159nTR\xafT*\xe0K\x04\xff%z\r)0\xa7\xfe*\xd0\f\xd5\xd7`\x01\xc4u\xe1t\xe5\x9cW\xe2\xc8\u04cf\u021f\xae\xf6\xa98\x93\xae_\x9c\x85W\xa7V\xb8%\xa7^\x04Q\vB\x05H\x86\x03/$\x05PA@\xc3Q\x1f/\xbcp\x94\xef\xbb\xef\x1b\xd7\x02x\xfc\xf7\u007f\xff\xf7_\xb7`.\xb6\xf3\xc9}\xef{\x0f\xe0\x8b_\xfc\xbc\xcd\x14a}\xd3\xfa\xfa\xfa\xddg\u039cA\x10X\x02\xb2\f\xe67\xdcp=\xee\xb9\xe7\x83\x0e@\xb7\x8f\u0742U\x83\x98\x02\xb5\xa2\x94D\x18\x86\b\xc3\x00\xd25$\x01`0\x18\xa0\xeb\xbcd\xb2\xc6\x0e\xe7\tA\xec\x15\x98\x93\xbe\x8a\xe0\ue0f2\xc8\x16\x10!\xadSd\xbd^/\x9c\x97\x942;\x9fr\xd0-\xb9O\xc1-\xb7\u070c\xab\xaf>\xb2\xf5\"l4:kg\xdd(5\xd2`\u0209\xe7X\xacx\x91i\rS)\xa1\x1c\x1aD\xed\x04j`\x8a\u041a6<\x9d\x17\x89\xf0\u0324\xc8G\xe8\t\x9e\xe1\xe3\r\xc7\xd2$\u0418/@\x99\xf2'g\x04@\x05&\xa6\xfc\v\u04a6+\x19[9\xdbp\b\u05f0t\ra\xe1\xc6\xe1\xa3M\x8d\xa8\xe3\u4525\u0758\x8c\x19a'\xb1\xa1\r\x8cl\xb1SCc\xadm\xc7v <F\xd10\x95\xae\u008bB,<\xd0\xe7z\xa8\xf4vy\xefW\xf1\xdby\fK\xc1r\xc1_\xb8\u063e\x06AW#\xda\xd4\x10\xc3\xdc:\x80\xb4\xb7\xbb\u0206\x95\xbc\xe9k\xce\xdf\x13.u\xbe\ty\xfaRZ\xb4\xc00\xa2\xfa4TP+\xd99\x1b\b!\x91$\x8cv\xbb=\xf5\xf8c\x8f\xbf\ue8c6\xb65\x98\xdf~\xfb\x9dx\xf9\xe5\x97\x05\x00|\xe63\xff\xe1\xc6\xf5\xf5\xf57t:]H)E\xf9\u62e2\x10\xf7\xdc\xf3A\x1c:t\xa8\xc4Y\xfe\xe7?\x92\xa4\xa8COu\xe6\xf5z\r\xf5z\x03J\xc9\f8\xbb\xdd.666~\"\xe7\xf5ZS\xc9S\x8f\xef\u00c7\x0f\xe3\xc0\x81\x03\x97||o\xf3|\xb1\xea+\x15v\xc5\x05\xa8\b<i\xb5I\x0e\x00\x89\xedV<h\xeb\xcc\xff#\x05\t\xa1\xed\xe4\xa3\x1a\x8d\x0f\x90\xa4\xcd\xc6q\xcb\xd7-\xb8\xf1W\xc5\x18\xa3\xb0\xa3\xca\u0399*\x00-\xc5L\x93'\xcb\v\xcd\x10\xb1\xc9\x12\u007f2k\x01\xb6\xbc\u007f\xd0\u0548\xda\xda\xd2\x0f\xa9E\x80\xb1\x96\xb6i?\xc0.\n\x06\"6N~\xc9\x05\xb9g\xa1\x97\xc1\xbc\u014e\x8e=\x15\x8b_N\x97\xab\xec\n\x13\x9c\t\xfd&\xae\xd0\x1f\xfa\n\xa6t'a\xdf7\xe3\xed\xa0r\xeb\x01\u02e1\xa7\xc1\x14\xfe\b\xac\xb7+\xa2\x1c\u0229\xd4\xc2b\xe3f7\xea3\b\x82\x1a\x8a\x96aY\xae\x17\x12mp\xf2\xd4\u0277\xa6?\xfb\xc2\v\xcf]\xa6Y~\xd4\xc7p\xd8C\x145\f\x00<\xf5\xd4\xf7oXYY\x013'\u032c\xca|\xf9\x9e={p\xf7\xdd\xefG\x18\xd6\x11\u01c3mu\x1d\xfd~?\xdb)\xf8\xd4E\xb3\xd9B\x18\x86\xd0:\xc1h4\xca\x1e\xeb\x1a\xbc\xdb\xf20\xc6 \bj8p`\xff%\x1f;\xe8m\xc0h\x03\x91\"o%\xe7_\b\xb5\xcc=V\xbd\xe6Y\x96\xbda,\xc5@\xd0\x18\xc2\xfa\x9d\x10[zB\x0e\xbd\x91\xf5\u0313e\\\xcd\xc1\xd5L\xc0\x04\x9c\xa3\xf1^g\x15\x1d\xe3\x11[\xfe\xa3*\x1d\xc2M\xf1y\u0205J\xb0\xb4\xd2M\xe3\xe8\x17\vt\xc6\r\x19\x01I$\x10\xf45\xd4\xc0NU\x92N\x17\x81*\x8e\xc8\xef\xed\xa0\xd2{f\xfcJ&\u0343\xfa=\x8e\\\xc0\x88R\x03\xba@:\xf9\x95\xbf\xe3\xd1\xd9\u04e2s*\xfc2p\n\x16O?\xc0\x1e%\x95Z\u0792\xa8x\xf2rR\x91\xd7\x1b@\xaef\x01\x03a\xad\x05\xa9B\xef\xc7)\xdd\x12A*\u0143\xc1\x90\x8e\x1f\u007f\xf9:f\x8e\x88hx\xe4\xc85\x97+\xf3\x1f\xf5q\uff5f\x15\x96\x16\u3e53'O\xbe\xf1\xf4\xe9W\x10\x04J\xa4^ ~\xa5\xfb\xaew\xbd\v\xd7]w\x1d\x00\xfdcj|\xfe\xe0G\xbb\xdd\xc6`0(\x809\xc0\x98r\x8e\x87R*\xe4\xd1b\xc0p8\xda\xd6=\f\x00\u063f\xff\x00\xa2(\u06aahE<\xe8\x01\xc6\xe4\f2\x97\x84\u0528\xb2\xf0\xab\xa2[\xf2\xc7\b\u00d0C\xa7X\x19\xd9\u051c\xa0\xaf]e[\x1a\vu\xe0^V\xd0T\xa7)Up\u0095\xc9;\xe3\xe1\xd5)\x9c\xa7\xbe\x8b\\\x1e\x83\xaf\u068a\x94+\xf6t2\u0564\x8b\x92\xfd\xb3\xa5\x974\xea\xeb\x89uB4(6w+\x1a\x80\xd9\x16!\xf5\xab\xa7\xf2\xe3h\x02\x17\xce\xc5\xef\x8f\xfdL\xfe\xbdI\xe2L*?\xbf\xef\xf2\xe0\x14/Y\xe5\xads\xc33\x91\x18\bc\xb2\xc57\u02f8\xf5\xa7\xb7Q\xf9\x16\xa3\xb8\x01b\x18\xef^e6\x88\xeaS\x90A-[\xf5\x84H-4\bA\x10Q\xb77\xc0\xa9S\xcb\xf3\xdf\xfd\xce}w\\\xa6Y~L\xc7\xc6\u01ba\x02\x80\xcf}\xeeoo%\xa2{677@D\xc27u\xb2U\xf9n|\xf8\xc3?\x8f\xf9\xf9%l\xe5o\xfe\x93>\u049b\xf0\xe2\u014b\x8ef\xf1\xd2\xe2\x19XXX\xc0\xe2\xe2\x12\xa4\x14\x85\x9b6\xbd\x86\xd7J\x83\xfc$\x8f\x03\a\xf6cvvv\xe2N\x1b\x00\xe2a\x17Z\xc7VZF^\xfd\xc6\xe3UVUm\x9c\x8e\xc9Sb\xb2*\x94\x9c\xc4P\x8e\x18\xd1f\x82h3\xc9\xd2s\xc8\x14<(\xf3m\xbb).\x1a%bd\x8c\"\x98h\x81[\xe2\xceil)\xe0\U00045a7a\x9e/^s\x89\x82\xb1\xf6\xafy*\x85\xdf/ ]\xc4V\xf6\xa5\x92\\\u0352\xe4\x97E\xd5\x15\xfaV\x8c\xe4\x18\xc5R\xf4\n*\xfe..\x18;\xfbt\fy\x14P\xba\xab\xf0\xcd\xce\xc8 7\xec\xf2{&\x94\xe7\v Wv\xe6\v\xa6\x97\x9a\x95\xf7\x96\u0606\u0478>p\u0618B\x10\u05b2\xe9\xe7l\x91\x80}nm\x80~\u007f\xb0\xe3[\u07fa\xef\xb6\xcb`\xfec:\xbe\xf2\x95\xaf\x02\x00\x9e|\xf2\xc9\xfd) \xa6\x1e&>\x98\xdfu\xd7]\xb8\xf3\xce;\xb0\x9d\x92\x9f\x98\x19RJ\f\x06=\x9c;w\xce\xed (\vv`6\xbc\xb8\xb8\x88\xc5\xc5\xc5\xec\u3402w\xa7\xd3\xde\xf6`\xbeg\xcfn\xcc\xce\u030c\u05fa^E8\x1at0\u8b3b\xc1\x92\xbc:\x1foA\x96A&\xafb\xd3\x11|\x91\x18\x88\xc4d`G\xda\x0e\xa1\x84\xa9\xef\x8aO\xf1\xfaO\xa7\x91+;x\x12R\x95\xbb{\x15 _Q\x02\xf3%8\xf7\x94N\xf0\xab[\xda\nI=u#a\xbcj\x17\x89\xe5\xd9\xc9\u016be\x8a\x98\x8ak\xcb\xcd\xcf\xf2\x05\x94+\x17\x13.]3UL\xc7\xfa\x12L\xaf\xa3K\x97\xc0\u007f\xef/i\x837\xdd}X\x130d\x8d\xf1|\xb9\xa3\xc2\xe2d?\x18\"\u05da\xfb\x97\xe4}>\xb2\u0701\xb1\xb4sF\x10\u05ad\xd6\xdc\x19\xd51\xe7=\x9c\xd4\xcb?I4\x8e\x1d?\xfe\xe6\xcb`\xfec:>\xfb\u067f\x1e1s\xb3\xdd\xee\xdc\xf5\xd2K/e\xda\xecL\x93M\x84\xc5\xc5E\xdc}\xf7\xdd\u0631c7\xfa\xfd\xbe'\xe5\xfb\xcf\u007fH)\xb1\xba\xba\x82W^9\xe3\xe1\\\xf6\xa1\xa1\xe9\xe9\xe9daa^KY4\xac\xe8t:H\xe3\xed\xb6S#7\xa7Y\x18\a\x0e\x1c\xc0\xae\u077b*Y\x96\xf4\x03\x95\x8c\xfa\xe8wW*\u22f6\x9c\xa1D\x85p8\xab\xca-\x18X\x0f\x0f\x998\xe5CR\f]`\xd7=M\xab\xdd1)P\x01Z\xab\xd4*\\\xcd%s\xd5u\xe4\xb1\x19T\xb5\xb3\x98D\x85\xa0\x94\xfa\xe4I\xf7\x84W\xb9\xca$\xe7\u01b3\xa6\xb03\r3\"\x9f&\x9d\u073b\xad\x18\x10\xaa\u0715x\x8f\x13\xe4\xd3\xe4[\x97\xf1\x13\xa2\xf1\xaa\xa2\aS\xed{A*\xca\\\x99\x15\x9aSE\x15\xd4\x11\xd1\x18;\x97[\xafsa\xc6K\x1b\x06\x84Dcz\x11B\x05\u0678?\xdc\xf0\x143 U\xc0k\x1b\x9bX>\xfd\xca5\u033c\x04\x00\x0f=\xf4\xc0e0\xffQs\xb3\xcf<\xf3\xd4\xe2\xf2\xf2\xf2\xf5\xa9\x17K\xd97\xfb\x86\x1b\xae\xc7-\xb7\xbci\x9b\x9e\xbf\xc4\xea\xea*.:\x83-\xf6R)\xa2(\xc4\xe6\xe6\u01a3\xccx\xb1^\xaf;\x89o\u0299\x0f\x91$\xf16\xae\xcc\rv\xef\u078d\x03\xfb\xf7W\\w~\x1d:\x1e`\xd0]\xf7\xb24=\xdeSL\x029_\xcb]\xaa{9\xa5S\\\x95\x97\xd2'\xee\xef\xb6\x1a\xf6\xb7\xff\xec\xe5\x97Va\xdb\xe4\x99\xcfJ\xe8\xa5\xf2\xa2S\x04\xc1\xf2d%\xf1\xf8\xba\xc0\xd9uQ\xe15K\u007fF8n\xc0\xf2\xc8\xc8\x1d\x1eK\xe1\x12\x82\x91}\u007f\xe2\nI\x84KxB\x8e\u007f\x9f\xca\xc0?\xb9M\\\xa4\u04cb\xa4\x13\x97h\xa4\ft\x99KE}\xc9*\xc0\xf5\v\xb2\xc1*\xf7\x021sa\xea\x17\xdeD4;\x9bg\xf6\u04d1\xdc{\x94\xba\x826\xa6\x16 \x83\xc8\xd9L[\xf9\xadt\xf2[)\x15u{\x03\xb4\xdb\xed\xbd\xdf\xf8\xc6W\xdf\f\x00\x0f<\xf0=z\xfe\xf9g.\x83\xf9\x8f\x92o\xfe\xeew\x1f\xb8\xa2\xdf\xef\xdf<\x1c\x8e\n\x14\x8b}\x13$n\xbf\xfdv\x1c9r\x04I2\u0716\u0dfe\xbe\x81\x8d\x8d\xf51\xc0SJ\xa1\xd3\xe9.\x03\xb8\x10\x04AA\x156\x18\f\x11\xc7\u0276\x05sc\f\x84\bp\xe8\xf0!\x04a\xe8\xde/Q\u0612\x03\xc0h8@o\xf3b\xb6\x95\x1d\xa3A\xca3\xf4\xa8\xaa\U000386be5\xe34\xae0\u0784\x9f\x86\r\xd64\\fn\xc6+\xd3J~\x9b*P\x8bQ!\x8es~+\x13\b\x94\t\x8bU\xc1E\xdd\xef\xe4\x19/&\x8d\xb9\xe8\x1b\xe4-\x19\xe94#U\xbdp\x15\xd5\xebX5=\x19\xc1+\u007f\x80\xb6\xdaHU\xf6\x19J\xa6\xf3\x85]VYic\r\xc1\x8c$\xbb\x18g\x93\xab\xe9\x1f\u075bm\xf2\xd7\xc3.\fn_\x94\xe9\xd1}\xab\x02\xcf7\x97\t\xf5\xc6\x1c\xa4T\xf9\u0535\x90Y\x1c\xa3\x10\x02Z\x1b6\x9aw<\xfa\xe8\xa37\x02@\xb3\u0660\xab\xaf~\xe3e0\xffQ\x1e\x17/\xae\xdc\xf1\xd2K/\x15\x12m\xd2\xcaw\xff\xfe}x\xeb[\xef\x84R!\x92d{\x82\xdf\xc6\xc6FiR2o\x82\xee\u0631C\xd6\xebuYV\xdf\f\x06\x03\f\x062\b\u0272\x00\x00 \x00IDAT\x83mE\x19U\xed\x9a\x0e\x1f>\x84\u9a69\xb1\x0fl\xba\xd8&\xc3.:\xeb\xe7,\xb0\b\x0f\\\x04\xc1\x10U\x83\xdd\x16\xdc6y\xab\xa1\x9f\x9eC\x1e\xc0g\x14E\x95\x05,\x97\xaa\xec-\xe9\x0f\xae`\x87h\xac\x06\xcf]B\x18[\xea\xd4y\x12oQE\xcf\xe4\x8dP\xcaV?\xf7{\xd3T\xab\u0523\x9e\x8a_\xd5\x05\xb7G@\xf3$\x90/7\xa7/\xd1[(}\xffR\x8f\x18_\x84\xb9\xb0\xf8\xb0\xb0\xbb5#\xc9\xd2G\xde\xee\x80Y\xdbIO\x14\x15\xe5\xe9k@yW\xd4\xfa\xec\xa7\xf3\xfeY\xc5\x0e\xd4[s\x90\xd2N;[\x03/k\xd6k\\\xffA\xca \xd9\xd8l\xe3\u0631\xe3\xd7\x01\xc0\xaf\xfd\xda\u007fc\xb6\x1b\xc5\xf9\xba\x04\xf3\x17^x6\x05\x85\xc6\xf9\xf3\xe7\xefX]]-LC\xa6\xc7\xcd7\xbf\tw\xdcq'\x00\xb3m\xab\xd8N\xa7\x83^\xaf\xe7\xed8\xd8\xf1\xe9\n\xbbw\xef\x0e\xa2(\x94eJ\xa5\xdb\xedf\xfc\xffv\xbc\xa1\xd2s\u06b3g/Z\xadVa'\x95]\xa7\x10\x88G=\xf46\xce\xe7\x8a\v\u05c02\xe9\xcc5Uh\xb1\xc9\x17\xf6Q>8\xefeQ\x92\xcb\xde\x1c\xab&\x9dv9\xe5\xd0s\x19\x1bc\xa2\xeb\n\xf3\x18\xc8\xfa\x93\x9dE\xb8\u326al\xa2\t\xca\xf2R(\x84O@\x8c\xd7\xc3\xf9.B\xf8\u050a3\x912 (E\b\x03\x81P\xd8\xec\x8b\xc9q\xa0<q\xb1\u068aIz\x8d\xfb\xe71*\x87'\xf9\xed\xa0\x9a\xb11\x04hE\xd0\xd2\x02:$\x81\x94\u02f5ufY\u033e\x04\xd4\xf3\xdaO\xd5+\xee\x9e4\x99\xaf>r\xa0vE`\xad\xb5\x00\x19\xd4\xc0Z\xc3\xc0\a~[\xf1\x87a(\xce_\xb8\x88\u04e7\xcf\x1ca\xe6\x03\x00\xf0\xb9\xcf\xfd\xad\xb8\f\xe6?\xe4\xf1g\u007f\xf6g\x00\x80\xfb\xef\xbfof}}\xfdm\xa3\xd1(\x03\xf2\xb4\x8am6\x9bx\xe7;\u07ce\x9d;w#I\xe2m\xfb\x02\xf7\xfb=\xc4qR\u079br\xad\x16A)\xb9<\x1c\x0e/R\xe6\xe4fo\u0635\xb5ulll\xa2h|\xb5\xfd\xc0|ii\x11\xadV\xb3\xa2\xbe\xb4\n\t\x93\xc4\xe8\xb5/ \x89\a\xce\x1a\x18\xa5\x00\x1f\xb6\x91k4\x89\x06\xc8a\x8f\xfd\xea\xab\x04&\\\x1a{\x1c\x93\xe5y\xcd4\xa6I\xac\x82\xaf\x14/\u007f\x8b\x8b\xe7\xbc\x05A\xe1\xab\xce\v\xc3<\x15\x89B\xd5\xc4\xc6\xf8\xa4,\x180\x12\x90M\x89\xa8\xa5\x10\x86@ \tJ\xe6>\xf4\xc4\x15\r\u05can$\x8fq\xe2\x97F\xf7\\\xc7R\x150W\xfe\x15\xc5&\x01c2{\xc5\x00H\x11\u0114\x84lI\x04\x01!\n\b\xb5@ \f\x04TV\xa1;\xddb\xd5$+\xe5\x9c:\x17\x9a\xb1\x949*\x02@\x106\x11\u0567\xb3\u0140\xa4\xcc\x1dH\xad\xfcQ\f\x87#lln^\xfb\u007f\xfc\x9f\x9f~\x03\x00\xbcp\xf4\xe8e0\xffa\x8f\xdf\xfe\xed\xdf\x01\x00\x9c={\u6593'O-\xd9!\x1a\xce\xfcM\x00\xe0\xdak\xaf\xc5\a>pw\xc6\xe1nW\u03bf\xdd\xeed&[)\bZ\x1b\xd9&N\x9dZ~\xe4\u0529S/*\x15xJ\x17`mm-\x1b\xe9\xdf\u0395\xf9\x8e\x1d;0==3\x0emi\xf1\u010c\xc1\xe6\n\x86\xed5\xab\x15N\xa9\x11m\xdcT \x95\xfa\x88\xc5\xe4\xf7\xb1\xd6${\x9c\xabo\x8d\x9am\u0569dKR\x04\x95,6\x8f\xb6\xc6\xcf\u025c\x0f\x15\x95\x13\x19\x85K\x15\x04L\xe9\x19y<gt\"xzC6 \xa7ZiJD{\"\x04\x8b!d$A\x82\x90Z\xe8\xb3\u007f}\x19h\x93w\x05v1\xb41u<A;8\x89\x17\xa1\t\xec\x17c\x12AUY\xecsi\xffAn\x97Q\x13\x88\xe6\x034\x96B4[\x01\x1a\x8a\x10* \f\x05\x02E\x90\x82\xb2\x1dV\xb6\xa0W%\xfae~\xff9\xfd\x92\ue10d1 \xa1\u041c\xd9\xe1b\x13\x01!\x03{O\xe6\x13\xd9\xc4$x\xb3\u075ey\uaa67n\x06\x80\u007f\xfb\x87\u007ft\x99f\xf9Q\x1dG\x8f\xbex\xd7\xf2\xf2\xa9\xac\xf2\xf6md\xef\xb9\xe7\xa7q\xed\xb57\x809\u0656\xf2=!\x04\x8c\x19as\xb3\x8dr\xf043s\xb3\xd9\xc4\xec\xect\u007fccs\x98\xe7n\xd2\x04\x9e}{\x82\xf9\xdc\xdc\x1c\x16\x16\xe6|[u\xf7}\x93\x01\u06f0\xbb\x81A{\x1d\x82\xf3\xc6V:\xaa]\xfc\xca\x05\u02570\x85\xf4>\xacN\xf5\xe0\x9agF9n\x9e\xaa\xc2\xe0\x90\xed\x02\nU$\x97\x99\x80\x8am\x02\x95\x1a\x9e\xa5\tN\x06\x17\n\u0182\xbb\x1f<\x1e\xa6b\xa2t\\UB\x85\x06\xb1\x0e\bIK\"\xdc\x11!\x9c\r f$h1\x80\x98\x92\x10\x81\x00I\v\xeae\x0e=\xd5a\x17TC\\E\xdb\xd3\x18\u060eW\xdf\xc5\xf0\x16\xe6I\xb2E\x14'@\xa9\x94?\x97>\x8b\x80\xa3\x8b\bAK!\x9cRv\xc71\xab C\x01!\t\" \u021a\x80t\xa9D\xcc\xc5\x1d\x12\xfbrt\xdf\n\xdf\x05\x84\xa3\xe0\x9eh d\x80\u9e7d\x10$a\xb4v\x01\xebA\xf6\x9c\x86\x19R\x05|\xe6\xcc9\xf4\xba\xbd\x9fb\xe6\xc5S'N$\x97\xc1\xfc\x878^~\xf9X\xf6\xe7S\xa7\x96\xdf\xea\xab:\xd2\n|\u07fe}\xf8\u065f\xfd\x10\x00\x8c\x01\xe5v\xaa\xccG\xa3\x18\x9dN\xb7\xb0s \u05d4i4\x1a8t\xe8p\x90$\x894.\x9d&\xa5Y:\x9dv68\xb4}\x9b0\x8c0\f\xb1\xb8\xb8\b%U\x11<\xbd\xff\x8d\xfam\f;k\x99\u01ca\xc8\x1a\x96\\P5\x14E-\x13\\\x14\xab\xceB\x10\x92\x9aDR\xb7\xde\xd8,\x8b\xcd@\x1f\x18\x99\xa80\x0e\xee+\u034b\xd4CU\x1d\u0293Kx\xf67\xf6\x1e\xd7N\xbeR\x851\xa9\xf6\xcd\x11\xa9\x88\x8a\x86\x80$\x10\b\xe7\x034g\x94\u0376\x04A\xb4\x14\u010e\x104\xab\x104\x04\xa2\x86DX#\xc8\xc0\xaa3\xca\x16\x88\\-\xb6\xc1\x98\x82\xe7UD\xe4\xb1'\v\xe2rhK\xc5N\xa6J\x97.\xa5@(\t\"\x14\x10S\n*\x10v\r\xae\vPS:\x87D\x86P\x04\n\x90\xc9\x14\x99`\x1b\xe7\xee\x1a\xb3\xf5?\xf3\ua9ca\u05d6\xc0\xc6@H\x89\xa9\xf9\u0750*\x84\x8e\x87\x10BB\x85\xb5\x8c\x8f'\x10\xa4\x90\xd4\xeb\rq\xf1\xe2\u0291o~\xf3k\xfb\xf1:;\xb6\x1d\x98\x1f:t\x85\x03\xb4\x8d#\x17/^\xbc15\xa0\xf2\x03\x9c\xef\xba\xeb\u0778\xf1\xc6\x1b\xb7-\xc5\x02X\xbf\x98\xd1h\x84^\xaf[Y\xed\xd4\xebu\xec\u06b5\xdb-F\xc5\b\xba~\u007f\x80~\u007f\xb0\xcdo\x1d\x03\xa5\x14v\xed\xdam\u524c\x92\x0e\x98A$0\xe8m\xa0\xbfy\x01\u0485Xg\xfc\xae\xa3A}\x8e=W`O\xe0\xae=\xe0Kk\\\xa9\b\xc1\x9cB\xb8+B4-\x11\x04\xae\x9a\x93\u00abP=m\xbb(\xf3\u9503\x00y\x89N\x15\xa0\xc7>\xe0\x15x\x96-p\xb24\tC%v\x19%\x83)\xbf\xfa\u0512\x10\xce(L\xcd\a\x90\x8a\xb2\xea\x94\x00P]\x82\x16C\xc8\xf9\x10\xb2%\xa1\x9a\x12a3\x05v\x01\xa1\xc4Xq\xfc\xc3\x05\x9e\xd2$W\u0742\xb4r\xac\xd7P\x9eG\x12\x84@\x00R\xa6\\\xb9\xb0k\x9e\x81m~\xb6\xa4\xbd6W\xd8hg>f\xdf?\x02K\xf7\xe5/Zd\x1b\xa6\xe9i\xd8\xde\x1a\xe7\n8m@$\u0418^B\x105a\x92\x11\x88\x04\x82\xb0\x9e5p]\u07ca\x12m\x10\xc7\u0261\xfb\xee\xfb\xf6^\x00\xf8\x8b\xbf\xf83\xba\f\xe6?\xe4q\xff\xfd\xdf}\xfb\u0673\xe7\x9a\xddn\u01fd9:\x03\u024f|\xe4\xefA\xa9\bI2\u07364\x84\xb5\x8b\xd5^s\x96\xb2b\xcd\x18\u00edV\v\xfb\xf7\xef\rF\xa3xl\ua0d91\x1c\x0e\v\x00\xbf=\xafQb\u03de\u0748\xc20\xf3\xd1@\x01\x9f%F\x83\x0ez\x9d\x15\xa7C/\x19\xb2O\xf2*\xa9\xa0_\v\x01\ri\xb8\x80\"\u0526\x14\x9a\xf3!\xa6f\x034\x97B\xd4\xe6\x03\x845\t)\x01#,\x18he\xbf\xd8\xe3\u05ab\x16\x86q=J\x95\xab\xa0w\x19c\xdb\u007f\x06ck\xba\x82_\x05l\x02\xb6*W-\x85\xd6R\b\x15\n\x97\x1f\xee\xe9\xaa\rCD\x02b.\x80\x98Q\x10\x91\x80T\x02A] \xaaK\u051b\x12Q]Zj\xa9\\\xa5W\xf6\x05\xb8\xe2\xef[xLr\xb1/Q\xb4\xdf\xe2\xca>\n\x13 \x05!\x10\x04D\x041\x13\x80\x94p\x13\xacNG\x1e\x120\xa3@\x91\vr\x16v\xc7\x01o\xc7e$`\x04\x175\xf5Y\x95^\xbc\fv1zF3\xc2h\n\u0359\x9d0\xc6\xc0h\x9di\xcd}\a\xc6 \bp\xf4\xa5\x97\xf0\u02993\xb7\x00\xc0G>\xf2K|\x19\xcc\u007f\xc8\xe3\xf1\xc7\x1f\u007f\xc3\xfa\xfa:\xe28v\xa1\u01f6\x8c\xbb\xf9\xe6\x9bp\xd3M7\xd9U[\xebm\xfd\xe2\xa6\x15\x02{\x83\x1f\xcc\xe0(\x8a\xd4\xc6\xc6F\xbb^o<U\xaf\xd7\xeby8q\x0e\x18v\xa4\x1f\xdbzp\b \xec\u06b5\v\xb5ZTIK\x10Y\xb3\xad~g\xd5\xf3\xb2\x9b\\\rV+\xe9r.8\x93A3AJB\u0610\b\xe7\x15\u009a\x80\x02C\xd5$\x82\xc5\x10\xe1B\x80\xa0!\x01E\xd0d+\xdcT\xbfl$\x81\x95\x03t\x81\x92/I\x85Sb\xe5_\u02a1\x11\xb4%{\x81\n\u00a6\x9c\x0f\xe1\xafq\x9a\x19\x14I4\x96B\xa8\xa6\xcc\xf3;\xcb\u02dea\x90\x02\u4302\x98\x0f \x1a\"+\x18d@\x88\xea\x02\xb5\x86\x84\x94\xe4\x16\x03L\x1e&\xe2\xf2\x8b<\u10b8\xb8\x1c\xb1G\x92\xe5\xeb\xf3x\x9b\x97\t\x10d\xabr\x92\xb0TQCT\x8e\xfdS$\x80)\x05\x04\x04\tF\x10\x10T\xe8\x02*\\C\xd4\b\xbb\xe0\xa5\xd6\r\xf9g%\xd7\xe1\v\xca-\x8cu\x92@\x85u\xcc\xed<\x04!\xadlV\b\u5a16|GDRbmm\x13Z\x9b_4\xc9p\x0f\x00<\xf4\u0403\x97\xc1\xfc\xb5\x1e\x8f=\xf6\xb0\af\xed;\x8c\xd1.?\xd3d\xc0\xfd\x9e\xf7\xdc\xe5\u0329\f^\x8f\a3\x9b0\x8c\xb0\xb6\xb6\xfer\xb3\xd9z\xa9\u0468\x85y\xc0s\xfe\xb8^\xaf\ufa0a\xed\f\xe6\xc0\xee\u077b\xd1l6\xbd\x81\x0e\xff\xb3N\xd0I\x8c\xee\xfay\x8c\x06]\x90\x90@\xc9$v\f.x\x1c$\x8b5!AH jH\x043\n\xa2)\v\xce|\xa4\br.@8\x1f j*\xa8\xd0\xf2\xb1ieh$A\a6\\\xd9\u0212\xf7\u01d8\x81k9\xad~\x92\x94\xe2U\xf4P\xbc\x85)\x83?.\xf1\xf1)\xa8\x93\xa5\x8e\x82Y\xe5R\x88\x8a\x96\x01\xec{\x95\xbb\x1c4j)\xd0|\x00jH\x90\xcc\x01;\xacI\x84\r\t\x11\x12\x8c\vv.\xbe\u063e\xddm1:\x88\n\xb4\x10U\xd2]D\xe5\f\xd4j\xbf\x17\"@\t\xabNA$A\xd3\xca\n\xe5\xb9\xc2!\x8c\x004$\xa8!!$ \x89!%\x01\x8a`\xbckO\x9b\xbb\u0185\xd1d\xbaq\x1e\xff\xddl\x12\xa8\xa0\x8e\x99\xf9\x03\x90*@<\xe8B\xaa\x00a\xd4p\xe7m\a\x89\x88\x04\xb4a\x9c^>}\xc3'?\xf5\xe9]\x00p\xdbmw\\\x06\xf3\xd7z\xbc\xe9M\xb7f\u007f>~\xfc\xe5\xb9^\xaf_\b;\x06l\xfed\xad\xd6D\x1c\x8f\xf0z>\x1a\x8d\xba\xba\xf2\xcaCJJU9\xe9ii\x16\xb3\xadi\x16\x00\u0635k\x17\xe6\xe6\xe6\x9cE)\x8d}\x98\xd9\x18\xb4WO\xa3\xbf\xb9\x02!TA\a\\\xa5N\xe6\x02\x90\x97\xab}\a\xe4u\x89\xa0\xa5 f\x02@Z\xc9c\x01\x9f\x14AN+\xd4\xe6\x034[\x12\xb5P \x90\xb6R\xcb\xf8h%\xa0C\x01\xdej\xf2\x06\x93H\xe2\x8a\xc7l\xe5\x9a[\xad\xad\x19_\xcel\xe61DC\"\x98\x0f,(\x1b*\x00\xb9\xf103[z\xd8y\xbd\xd6$h!\x00MI;x\xe3\x1e\xa4\x02BT\x97P\x01\xc0i\x8a=\xa1\xdc\xe4\x98@\x00q\xd5*\x9b\xe3n6\xfcU\xa5\x9e\xf7\x8c\x81\x89 \xa5}ohZ\x82\x1a\xa2\xba\xdf\xc0.\xf89\xe5\xcf#@\x10CR\xdab\xe5,z\x8e\x05\xdb0\x8f\xec\xb51\xee+U\xa8\x98\f\u05d95\x88\x14\x9a3\xbb\x11\xd5g\x90$#\x00\x04)\xc3,\xfc\x82\\\x9f\aDx\xf9\xc4I\xb4;\x9d\xd7U\x94\u0736\xa4Y\x8e\x1e}\xf6g\xd6\xd7\xd7\x0f\xb6\xdbm(\xa52\x8a\xe5\x96[n\xd9\xf6\x8d\u03f1\x8a\x8c\xaa\xff-\bB\x04A\x1dJI7\xe9\t\xf8\x01\x15\x9dN\a\xa3\xd1h\u06cf\xf4\xcf\xce\xce\xe0\xe0\xc1\x83PJ\xa0<\xff\u01ce\x1f\xd8X9\x89\xf6\xdaiH\x15\x8cU\xabT\xfe \x13\x15\xb6\xee\x19^\x01 \xc1\b#\x81\xb0! f\x94U?\x94'@]\u04cf\x14A\xcd\x06\b\xe7C\xd4\x1a\x12\x8dH\xa0\x1e\t\x04\x82\x9cc\x1f\xc3( \t\x85\x954z\xa4|\xbeo\x98\xe0\xa4H\x15Z\xeaI:\xed\x82+ O\xa0c\x9cZ\x8b\x192\"\xd4v\x84\bZ\xaa\x10f\\\x984\xa5\xaa6\xad\x03\xb9P\x00s\x01hV\x01\x91\xbd.A\x84@\x11\xa2\x86\xb4\xfc{\u5fa1\xca[\x86's\xe0\xe9>\xa3\xf0\x92P\xf5\xf3\t@)XuJS\xdas\x134Y\xaa\xee\x16(\x0e\x004\xad\xa2\x85Rq\xbfa\x10\xe7\x03D,a+\xfcBw\xc26=\xcb=\x11\xd61\xea\xad\x05\xcc,\x1e\x86\xd1\x1a\xc3A\a*\xaaC\xa9\xc8=.=s\x81\xd5\xd55\x1c\u007f\xf9\xe5w1st\x19\xcc\u007f\x88\xe3\x1b\xdf\xf8\xd6\xc2\xc6\xc6f=\xedF\xa7\x14\xcb\xdb\xde\xf6V\x1c<x`\xdb\xc5\xc2\xfd \x87\x94\xf6\xa5\xb7\x03CN\x99\xec\r\x0eu\xbb]\x8cF\xa3m\xedi\x0eX\x89\xe5UW]\x05\x95\x01u1\x1fS\b\x81\xf6\xda+\xd8\\[\x86\x10\xaa\xf2\x03L^eJ\x13\\>\x18\x80\x90\x02A$ \x9a\x12\xd4\x14\u0632\x1cf\xab\x83\x14-\t9\xa3 k\x02\xa1\"\xd4\x03B- \xfb\xf97\xae\x99\x16\x88\xdc\x0f\xa4\xb8ba\xe2\x84\xd1V&]\x99/9\x15(j\x1a{\\i\xb1\x92\x84p.@8\xa7\x1c54>y\xeaOb\xd2\xd8\xf9\xb0\xe5\u0195k\"\xce* \xccJW\xdbg\xa8K\u0210\nz\xf4I\x84?a\xf2\xda\u00d4S>\xbc\xc5N\x86\x85U\x1cII\xa0@\x80f\x03P]nyO\xa5.\x88\xd0\xda.H5\xe7\xa3b\t\x95\xac\x02O'?\r\x8c\x1d\xfb\xf7\x16\x1f\xbf\xa5\xcd\xc6\xc0\x18F\x12\x0f\x11\u05a60\xb3x\x10\x00c\xd0Y\x87\njPQ\x03\u0326d\x8fK\u8d3b?u\xfa\xe5\xa3!\x00|\xf3[_\xa7\xcb`\xfe\x03\x1cI\x92\u0726\x94\xac\xc7\xf1\xc8v\x9eMZ\x99\xbf\taX\xc7h\x14\xbf\x8e`\x9b*+\xf5\x14\u0327\xa6\xa6\x10\x04jLOnm\x00\xe2m[\x99\xdb\xddQ\x82(\x8ap\xf8\xf0!DQ*O\u0307P\xec5\t\f{\x1b\xd8\\}\x05Z\xfb\x1e4\x8cr\x13\x8d&#\xbdU5\x84\x04\x11\t\xa0\xa9\x00)&\xb3\x1f)mj\xec\x1d.\xa6\x14\xc4l\x00\x8a\x04\xa4\x04\xa2\x80P\x97\x84\xc0\r\xdah\t\x18\xe5\xa9\\\xbc\xc5e<\x1d\xf9\xd5|\xbb\x8a\xed\xa7\xc2w\xfc$\n\xbb\xeb\x00\xa2)\x85p)\xb4\xea\r\xe6\xca\n\x9a\xb6\xb8\xb7\xb2\xbf\xa7\u0297\x96\x02\xcd\x05v\a\xe3\xaaW)-\x8f.BQ\bw\xf0\x17\x84-]\x12\xdd\x0e\x8a\u0299\xae<~\x9eL\xee}\x13\xee\xf7OK\u0434\xac\xfa\x81\xf1\xb5\x80\x19Fk\v\xcbu\x01\xad\xa8\x90\x13j\xc1\xdd\x01\xbb\xb6`n\xd8@\x1b\xe3vC\xaer7\x9c\xed\xe4u\x12C\xca\x10\xd3s{\x10\x84\r\xc4\xf1\x00\xc6\r\x14\xf9\x94\x93\x10\x12q\xac\xf1\xfc\v/\x04\xdf}\xf8\xd1\xdb\x01\xe0]\uff0b/\x83\xf9\x0fp\xf4\xfb\x83}\xcc\x06I\x92p\n\xe4\x87\x0e\x1d\u0111#G&R\x17\u06cbZ\xb1\xa1\x12J)\x84aP\xf9\xfd\xd42vqq\x11a\x18\x8e\x81y\x1c'\u06deJ\xd2ZC\xca\x10\x87\x0e\x1dB\xb3\xd9\x04\x8d\u036b\xb8\x0f\x92N\xd0^[\u01b0\xbf\t\x92j\xecs<1<\xce\xeb5\x92\x84mf6%P\x17\x97\xd4L\x97{|4%!\xe7\x02\x88\x9a\xb4\xcd8E\xa8)\xb2L\x04;\xb3\xa7\xb0\u031f\xfb\x13G\xfe\t\xb9\xa6$U\xc78\xd3\x18\xdf_0\xcb\xf5*\\\xf7h\u05e8\x8c\x96\xacf<\xdd\x11\x14\x1d\x82+\xf25'\x82:\xf2\u0160)m5\\s\xce\\l\xd7\xc10\x12\xa0\x80,%?&\xe8\xe1\xb1\x19\xa6\xe2\n\x96n*\x8a*\xa0\u00a2E\x16]\xa4 (bP]B\xcc\a\xa0\x80r[\xe2\xca\xe7\xf7\x831\x9cQ\x96\x02\x92\x1a\x90\x10g\xcdN\x18\xb7+H)\x97\xb4]K9\xc5\xc7\xc6d\xbd\x01\xb2Sy`N\u041a\u074d\xe9\x85}`\x93`4\xe8@\x05a\x1e\xf8\xec\xceA\x1b\xc6\xda\xda:^|\xf1\xa5\xb7\\\xa6Y~\x88cuue4\x18\f\x01\x10\xa5\xc3BW^y%\xf6\xed\u06c7\xed\xdc\x14,V\xad\x06Q\x14\xa1\xd9lU\x9e/;\xbd\xe2\xd2\xd2R\x10E5\x18w\xa1\xe9c\xe38\xb6\x9e\x12\xdb\xf8Z}E\xcb\xc2\xc2B%\u0352n\xc6\u06eb\xaf`\xd0\u07c0P\n\x13`\xa2\x1a\xc5\x1df\xaa@@\u0525\xad6\xabT\x10\x18\xe7\x97\xcb\xcc\b\xb5,\xa0\u021a\x84 \x82\x14@\xa4\x04\xea\u03b0J+\xabt\xa9<1*k\xce\xc7\u03c1\xb6\xa0\x0e\u0199\xe8\x94\xcf\x06\u0080\x10\xcd)\x88Y\xe5=\xfc\u0485 OlV\xe6\u007f%fPC\x80\xe6\x02\x88\x86\x84P\x04\x99r\xe85\tR\x02\xc6\a\xeeB\x9a\x0f\x8d\xc5o\x94\u007f\x0f\x8f-d~\x8f\xc35=\x03\xf7\xfb\xeb\x12\xd0[Y9\x96^'\xf6<\xcc#\x01Q\x17\x10\xa2\xd8*\x17\xe9Tq:8$\x04\x84\xb7\x98\x12R\x99\xa2\x80 \x01\x9d\xc4h\xcc\xee\xc4\xcc\xd2!\x18\x9d`\xd8\u06c4TA\xd6\xcfI3D53\x12mp\xee\xdc\xf9w\xa5g\xf4\xf0\xc3\x0f^\x06\xf3\xd7z\f\x87\xa3l\xe23\xb7[\u074d\xa5\xa5\xc5m\x9e\xc0S\x04\xba0\f1==]:\xdf\u051a@\x13\x00Q\xafG\x17l\xd1P$mm\xdaP\xf2:\xb8V\xc6\xcc\xcc4\xf6\xef\xdf?^\x99{ \xb1\xb1z\n\xfd\u038a\xfd\xd0\xf0\xa5\xdcW\xbc\xa7 @*\x01U\x13\x10-\t\x8e\xc4%p\x8e'>+\x11\xac\xdcm.\x80\xacK\xeb\x01B@(\b\ra\xa9f\x1d\n\v\xe8\x84\n\xbd\xb8\x1f\r?I\x01R\x95Z49\xb8\"\b\b\xe1t\x00\xb1\x18\x82\x14Ys@\xc7Qp%\xe8\x95'-\xcb\xf9\xa5\x9e\u076f\xff\xad\xba\x80\x98U\x10N\xbah\x9b\xa2@X\xb3>(LEs\xafI\xec\xc7\xd8N\x83\x8a\x8b({\u0222\x84m\xbe\u04b4\x82\x9cQ\x15\xd4\u04c4EW\x00`\xe3\xeck\xed\xffCEh\xce(4Z\x12\xf5\x10\b$l#\xd4x\x80\x9f\xf2\xf8i\xc2\x14\x18\x865\x8cvT-3\x8cI\xa0\xa2\x06\xa6\x16\x0e@\x86u\x8cF}h\x9dX\xd9,\t/\xb1Np\x1c'X^^~#3\xd7\x00\xe0\xd6[\xef\xb8\f\xe6\xaf\xf5\x90Rf>%)\x98OOO\xa3^o\xbc.T,)\xcdB\xa4077\x8b \b\xe0W\u0756\xa20\xd8\xd8X\t\x9e\u007f\xfe\xe8\x97766\u06f5Z\xbd\x10H;\x18\f\u0728\xff\xf6\x06s\xadc\xcc\xce\xce\xe2\u0211\xab\xac?\x06\xa1\u020b;:\xa2\xbd\xf6\n:\x9d\xf3v\xab[\xa60\xaa\x02\x9d\xddC\x94\x04TD\x90-\t\x9aRn\xc2o\x82\xcf\t\u00c5\xf5\xa2Z\x80\x91\xfe[\u04daU\u0256\xb2\n\vE\b\x94@]Y\xd3*\x1d\t\x98\xc0sW\xf49\x9b\t\xa1<\x05m\xfa%\x17\x18\x02\t L\x1b\x92\x8b\x01\xe0\x86\x83\xf2\x11\xfc\xaa&g\xfe/\x95\xfd\x05\x1e\xcf\x02%\xa2|\u0769\t\xd0|\x00L\u0641*!\xad7z\xbdf-g\x89\xc6s83\x9f\u022cb/\xee\b\xd2\xde\x04yzI\u06ec&H)\xecnh)\x80\b\xa8b\xc7Q\xd4\xed3\xf2\xb8?\xd6\x1aF'\x96\x0f7\x06\x82\x18A\x04\xa8\x96@P\x13\x88\x027\x80\x94\x017\xf2&\xa9\xf1\x16\x027=\x9e\xfa\x9a\x83\x01\x9d\u0118\xddq\x18\xd3\xf3{\x91\x8c\xfa\x18\r{\x102\x80H\xc7G\x89\xa0\x94\xa2N\xb7\x87\x13'OM\u007f\xfb\xdb\u07f8\xe72\xcd\xf2\x03\x1e\x8dF\x1dR\xca\xd2M)\xb6=\xb0\x95h\x14\x00\xc0\xcc\u0334m\x0e\x96\x8e$\xd1t\xe1\xc2*\x1a\x8d\xc6@\b\x91\x94+\xf0tXj\xbb_s\x92h4\x1a\r\xec\u06f7\xcf)Z\xaag8G\xc3\x1e\xba\xed\x8b\xd0&\xa9l\x82\x8e\xcdQ\x12 \x89\xa0\xa4\xb0\xb4\u0234\x0f\b\xd5\xceQ\xa9\xcd\x15\xc0\x93\xeb\xf3\xf4W\xd6\x04h^\xd9\xe6\xa8\x04 \xac?xMX\xa0M\x02\x01\x13\x8a|\xb0(\xe5^\xb7|?\xe8U\xbc[\xf6\xf9\x02A\b#\x011\xa7\x80i\x8fz\xe2\\\xbf]\xad+a\xcfY\xb2\xaa\x84\x9eddnA\x17\x01\x81f\x953\xb4\xb2^)2\x14\x88\"\xfb%E\x89P\xa9Xo\xa9\xfc_\u02af\x9d\t\x80$(a\x9b\xd5b1\x80\xac\x8b\t@>.+\x05\xb9f\xa5N\xc0\u0680\x8dv\x92D\a\xc6\x01@!A(\x810\x90P\x822\xee<S\xbf\x19\xed*\xf1\xb4?\x95\x1a\u0739\xe7\xd7#L\xcf\xed\xc5\xcc\xc2\x01\xb01\xd0\xf10\xf54\xcf_b\x12\xd0\u06a0\xd3\u9d3e\xfd\xed\xef\u0718\xf7\xb2\x06\x97\xc1\xfc\xb5\x1csssQ\x14E`\xce;%\xb6J}\xfdM}\xb6Z-\xd4\xeb\xf5\xe2N\xf8\xffc\xef\u0363,\xbb\xca\xfb\xd0\u07f7\xf7>w\xa8[Swuu\xf5(uK-\xa9\u0552\x00\v4\xa0\x81Qf\xe6a<\x80\x00\x93x9\xb1\x8d\x89\x87\x84\x10\x9c\u007f\xf2\xfe\xc9K\x9c\xbc8\xce\xf3\xb2\r\xbc\xb0\x1c\x9c\tX\xd8\x01\x83\x9e\x102\xc6\x0fd\x03\x92h\t\xa3yhuK=O\xd5\xd55\xdc\u1733\xf7\xfe\xde\x1f{8\xfb\u073a%!/\xbf\u040a\xea\xacURu\u056d{\xcf\x1d\u03b7\xbf\xfd\xfb~\x83\xff\xc8\x14E\x81}\xfb\xae\xcc\xda\xed\xb6\x1c\xee\u078a\xa2\x80\xd6\xe6\x82/\xe6\xd6\x1a(\xd5\u0116-[\xd0h40\xdam\x10`]\xa2\u05dd\x87\xe6\xd2\x05\x03\xd4:\xb3a\xba\x9f\u00f33\tg\xf3:\xa9\xa2T\xbd\xa6\xeec\x1a\x01\x05$\xf0\x04\x0fA\xde4T\xd8\x1a\x02\xb4A\x81&\x14D\xe6.\xfaL\x12\x1a\xbe\xa0[E\xb0\xa9RT8\xb5\xe1\xaa::T\xf0h\xcd e\x1f\x94 \b\rE\x10\x93\n4S\xc1+\xa9\x00sT\x02Sb\t\x81\xe7\x97 \r\x95\xfa\xa1\xe7N\xbe\xa0cR9\x11\x8fpP\x8bj8\ua9d4U\x90G\x88s\x1b\xbe\xb3\xe1\xfdA\xb4i!o\xa2\x95\x11\xc4F\x055\x95\xd5\xf8\xe8\xa3\xf1\x9b\xfaN.v\xd26\xd0\x0f]\x97\rk\x01\xc1\xe0&9\xda)\xac\x0f\xe7\xf0\xcc\x16k\xc1\xda8.zl\xaal\xfc^\x04\xd8\xc72\x94ja\xc3\xe6K\xd1lO\xc0\xe8\x1c\xd6j@\u0238xy\xb1\"\x17E\x81\xe3\u01cf\xbf=\x9cn\x96\xb5\u058b\xf9\x8b:)!\x9e\xb1\xd6B)E\xa1\x8b[ZZB\xbf\xdf_\u0571_\xe8G\xbb=\x86V\xab=\xf2w\x83A\x1f7\xdexC699!\x86#\xf1*\x98\xe5\u009f\r\x00\xc0\xec\xec,:\x9d\xb1\xd5\xe5\xc5\xeb\u05ad5\xc8{K0\\\x823\x91fP\xa0\x96\xa4\xe9\x8bA&\x9d\x9d+\x8fI\xe7\xd3!B\x9e\xa3\xaf2\xc4\xd1pkTM\x8dl\x88U\xbfO~`\x01(\xe1:\xd5\t7\f$\u12ad\xf4\xf7a\x15\xc14\xbdR\x14\x14\xdd\xfcj@|(n\xabL\xc4\xea\x10\x05\x93\x83\x1f2E\x0e\xbb\x9ei8\x0e\xb5\xad\xa0\x82\xf8\xf4j\u0557Gw\xe8#\x81\x9e\xbai\x18Q\x8a\x87$\v\x99\"\xc7r\x99T\x80r;\x13\xca\x04dS\"k\b(\x95\xb4\xca\u00b3n\u05b0\b\x8e*K_\x04\x95$\u0229\fjc\x96\u030d+\xc6Im\xf1\x1e\xe6wz~9\x9b\n/\xaf \x12\xcfb\x91N\xf0\xc5`\bb\bI1g6\xae[\"\x9d\u06cc\xb04\xb6%f\xb6^\x8e\xf1\xa99\xe82\x875\xdaw\xe6\x14\xbby\xa5\x14\x9d=s\x06\x8f<\xf2\xe8\xe6s\xe7\u03bcj\x1df\xf9[\x1cJ\xa9\xfd\xd6r\xb7\u0468\xe0\x89\x03\a\x0e\xe0\u0529S\x90\xb2\xf1\x92Q\u007f\xbab\xdeF\xab\xd5J0\xf3\xc0\xf7\x15$\x84\x90;w\xeez\xaa\xd9l\x9ck6\x9b\xb5\u03bd\xdf\x1f\xa0,_:|\xfa\xa9\xa9)l\u0738\xd1C\xe4\xc3v\x87\x04f\x8b\xa2\xbf\fkJgO+\x87\x82\x13\"F\ue2dd$\xa0)\xc0S\xd2\tG,\xeav\xe2UtP\x1a(\x14\xbb;Ak\x80\x1ea\u06ddt\x82\x94\t\xd0T\x06\x9aR\x10\x99\x80\xf4\vJ\xac;\x020\x99\xa8V\t\x1a\u00a91\xdc\xfe\xf3PG\x8dJ\xb4\xd3$\u020e\xb3\xafE\xa0!bDm\xe3\x90\xda1\\\xbbG\x81/\xc3\x05\x96F\x92\x06k\x1d\xb4u\"%\x9a\x92\xa0\xa9\f\u021c\xf1\x18Ig\xd2\xd5P\x02*]O\x12\x16\xce(\x83\xe2`Q+$ \xc7\xdd\x1c\x802\xb1\xea\xbcG\xa6\x15%\xef_eN\x97\x0e@\x1d\xccb=\x06\x0e\x82\xa3Z*\xb7\x83\x12\xd2\xef.\x84\xa3\xfc\x92W\x11\xdb@]\xf4CR\xf6n\x89\xe1\xff\x9d\xa99L\xcf\xee\x82\x10\x12V;C?!\xbd\x93\xa2\xef6\xba\xbd>\x16\x16\xce\xed\xfc\xecg?{C\n\x9f\xae\x17\xf3\x1f\xf1\xb8\xed\xb67\xcf\xcf\xcdm6Y\u0588\xea\xee\xfb\xef\xff>\xee\xb8\xe3N7\xc7i\xb5\xf0RI\xceN\x8byz\xd5\n!!\xa5\x90Dt\xa8\xd5j\x9d\xe8t\xc6\xfds\xaa\x02*\xf2\xfc\xc2W\xba\x06\xf5\xea\u018d\x1b\xb0m\u06f6h\xff;b\x88\x80\xa2\xbf\f\xa3K\x10I\xb7}W\xae\x80\x84\xad\xbc\x94\x84,#\bE\x80\x02xR\x82\xc7%\xb4e\x94\xb6N\x88s]\xa7+x\x14\xc1Z\xae\x93\xe6\x88kt\xb9\x14\xaap\x85\xb2\x12\u0650\xf2l\x8f\r\x95Z\xb4\xa5\xc8\xd58v\xe7g2\xaa\x02.j\v\x11\xd7\xf0c\x1e^\xc8\xe0\ng\xd6\x12\x90\xbe#\xa7\r^\xa8bQs.X\x9bQB\xabb\xa9y\xd5\"B\u03c3\xdb\xd7=^\x9c\xe4\x9da\x05\x81'=\u44b9\xe2H\x19\x81\u048cQN\x92\x93\u05a2\xb9{\x18J\x8dI\xa8M\x19\u460c\x8b\x00%\xbb\xa2\x10\xc2<\u0681\u04a7\x18\xd9\xca_\x98\r\x83\xad\x89E\x18\x81?.\x1d]\x91}\xe83\xc8\u07f7\xf7\xe7t\x8fc\x9cR\x14U~\xb0\xf1\"Dk4\x88$6_\xf4\n4\u06d3\xd0e\xee\xc2S\x84\x02\x91\xf0<u\x03kM\xd9\xedv\xc5C\x0f=r]\xf8\xbc_\xa8\xb5\xe7\x82,\xe6\x97\\r\u065f\xef\u0739\xf3\xc0\xcc\xcc\f\x88D\x84Z\xfe\u077f\xfb\x1d\xdcq\xc7W D\x86V\xabs\xc1\xa6\u05e7E\xae\xd3\x19[\x05?\x00\x801\x1a\xa7O\x9f\x06\x00\\z\xe9\x9ef\xa7\u04c11&\x8a\U000965d7\xd1\xef\xf7_\x12\xc5\xdcZ\x8d\xa9\xa9il\u077a\xb5\x96\xbf8l@\x95w\x17at\x0e\x9fs\xe6c\u037c\xf22#\u0226p\u0635$P\u01e96\xa1\x04r\xcd\u022d3\x99\xa2Z\xe5\xa3U\x1d0\x8d\xe8\xc4G\x03\xb5\xc1X\xa9bP\x10\x11\xc4d\x06\xb91CcL\xa2\x99\t\xb4\x94@[\t\xb4\xa4[dB\x92\x11\x10\n\x1c\xd5pn\x1e\x02\xa9C!WmW\xc8\u0174\xf7M\x11p\x1dc\x12T\xbd\xe6\xc1\xb4\x1a\x03\x1f\x82\u04c7\xbbt^\x13\x9f\xf63(\xcb\xd0\x1e\x86f\x01\u0414r\xb0KS:\xb8B\xf9\xf7A\f\xfd\xe1H \xdeY\xd2f\r\x81\xc6\xc6\x06\u0114JnZ\xa1\xeb6\xc9\xf2\xe0\x98\x182t\u007f\xb1\x88\x06\x88\xc5\xd4 \x96\xf8\x05\x06+g\xb6\xe5^\"\u007fG\x89L\x94@\x10\x95\xa5fT\xa3\x8a0\re\x8b\r\xdb\xf6`|\xe3\xb6\xf8\xb8\xe13\xc3^\xb0\xa4\x94\xa4\xb3g\u6c7c\xbcr\x033_\x06\x00\xdf\xfd\xee_\xd3z1\u007f\x81\xe3\x87?|0\x14\b\xbe\xee\xba\xd7|a\xeb\xd6-\x18\x1f\x1f\x8f*\xca#G\x8e\xe0\x1f\xfc\x83_\u00a7>\xf5\x87\xe8v\x97\x90e-\xb4Z\x9d\x18\x04q\xe1\x15v\x8b\xf1\xf1qt:\x9dX\xf8\xc2d}0\x18\xf0\xd3O?\r\x00\x98\x99\xd9x<\xcb\x14\x88D\x04\x00\xfa\xfd\x01B\xca\u0485^\u03356\x98\x9e\x9e\u008e\x1d\u06e1\x94\xac\x158J'\x90\xe1\xfaU\xbe\xb3\r\xdc\xe0\xe0\u06e1\b$\x05\xa8! &%DGB\x06\xebT$\xd71%\xfe\xd9k\x18N\xd5\xca\xf6\xdaNX\xfe\xd4\xea\u014a\xc6%\xe4\xc6\f\xaa#\x915\xc8w\xe9\xc2y\xba4\x84S\xfa\f\x19N1\rei&6\x04\xaa)\\1\x1f\x97\xa0)\xd7\x01\xaf*\xb2k\u060c\xa7\v\x16\x0fc\x12\xabj4\x0f\xc5\xd6\xd5\x1f\"\xb8\x97\x18\xbf\x1b\x10\u00a9AE\xa8\x04\x13\x12\u0618\x01c\x12\xdc p\x96\xfa\xbe\xfb\x00\xee\xa4\v\x0f\xef\xa7\x11~\xe71\xad 7fn\xae\xc0\x95\xe2U\xf8B\x1es\xb8\x13\u03da:=\xd1\xf90Y\xad\u0757q\x9duE3\xb4n8\x1a\xa0\x13I\x10-\x01\xf2\xce\u0281\xcdR\x83\xd4D\xb2c\x1b2\xf6\xb4VC\x8dM`\xf6\x92WB5Z\u043a\x88!8l\x8dc^\x81D\xb7\xd7\u0179s\xf3;\xef\xb8\xe3\xcf\xf6:\x94\xe0\xfe\xf5b\xfeB\xc7+_ym\xfc\xfe\xf6\xdb?\xf8\xf9W\xbc\xe2\x15\agg7\xa1\xd1hF\xae\xf6\x993g\xf0\u044f\xfe\x1a~\xf1\x17\xff!\xbe\xf8\xc5/\xe0\x89'\x1eE\x96)\x8c\x8dMDY\xfc\x85R\u052d\xb5\xe8t:\x18\x1bk'\u015c\u0207m\xa8^\xaf\xdf\x04\x80\xa3G\x8f\xdd5\x18\xe45\nc\xc5f\xb9\xf0\x8b\xb91\x1aY\xd6\u009e={0>1Q\xcd4\x88 \xa4\x84\x94\n\x82\x04Z\x9di\xa8\xac\xe9\xe9f\t\xea\xc1\\\xcd\xc0\x04\x1cF>\xae\\\\\x183\x94/:af\x99:\xf5\xb9\xf7\x9bF\x16?pR\xf2\u05fc\xfc\x86\x13t<&;\xe6\n\xba\xecH\x90r\x85/\x13\u0085\x10\xd7d\xff<\x94\xe7@\xb5\u007f\u02cc\x905\x04dS8\x8c\xbc)j\xf8\u007f\x8c\x81[\u02dbf\xad8\xb6\x11~\xf0\xc3I?\xe9e`\xbdK.\xfb\xc7S\x92\xa0(q,\x0f\x1ds[\xc0N+7x\u039c\x1f\x8ejJ\xc8\xcc\r\x87S9\xbf\x15\xf0A\xda\x02j\\A\xcdd\xa0\x86g\xe6\xa4^\x91\x01M\xe2\xe1\xd79\xa5\x1b\xb9\u0398u\tk48\xc0*)~\x9e|\x05?\x16d\x00)\x00\x1eR1\x9ek\x1e`5\x1b=n\xea0\v\xb3Ei4\f\x03\x1bw\xecE\xa3=\u9b54)\x16sv\x9fcQ\x14\xa5VJM\x1d;v\xe2\x06\x00\xe8v\xbb\xeb\xc5\xfcG9\xee\xbc\xf3\xab\xa1H\x1c\xbd\xe9\xa6\x1b?q\xc5\x15W\x94\xe3\xe3\x13h\xb7\u06f5b\xf7\xc5/\xfe\t\xde\xff\xfe\x0f\xe0\xfd\xef\xff ~\xe37\xfe\t\xbe\xf4\xa5?\xc5\xf2\xf22Z\xad\x0eZ\xad\x8e\x17\x1e\x89\x1f\x9b\x822\xf8\xb0w:\xe3\x18\x1b\x1bK\xb0rAy\x9ec\xf3\xe6\xd9\xcbv\xee\xdcq\r\x00l\xd92WfY\x16\xa5\u0100\xa3b^\x88\x03P\"7h\x12B@J\t\xa5\x14\xb2\xccm\xabw\xed\u0685,s\x03j\xe1y\xbbB*\x88,\x83l4\xd1\xecL\x82\xbc\x02Tp\xe2Sb\x00.-\xacq\x96\xa6<&\xc1\xde\xc3#4\x86\x888(\xa2\xfc\x9c_\x88\xe4\xf1\x02\x1d9^\xa0\xbeS\xdb\xcb\xd0;N\\$\xa4\xc3\xf5\x95$\x84H\u04c0\xa1\xa7\x8f\x19\xfe-\x95\xa3\xfb\x89\x967\xbdZ\xcd@\xad\t\x84^\xe4;1\xa2\x93\xe7\xbammH\u0671\xce^W\x80\xbc\x8d\xc1h\xbe|@3\xa8%\x9c\xeb\u2e02jJdaA\x92\xc1m\xd1\xedDB\u0407h\t\xa8i\x05j\xc9\x1a\x1e\x9e\x12i\bnA\xaco\xd9\ua2f0\xd1\x06\xa6(\\Wn\x8d/\xa8\xa6\xf2_I\xbe\b\x0e#bb@q\xa2/p\x98y\xa4*\xc2\xc2x\x13.\xf6!7\x96\x03\xben\xc1l06\xb1\t\xd3s\xbb@B\x80\u0640\xad_\x04\xb8Zu\u03df?\x8f'\x9ex\x82\x00\xe0\xe0\xc1C\xeb\x98\xf9\x8fr\xec\u06f7\x0f\x9f\xfd\xec\u007f\"\x00\xf8\u065f}\xff\xff\xb8\xed\xb67\xff\xdb\u05fe\xf6\x06\x8c\x8d\x8d\xa1\xd9lal\xac\r\xa5T\xbc\xfdC\x0f=\x84?\xf8\x83?\u0107?\xfc\x9c\xd2\xd3\xda\x00\x00 \x00IDAT\xf7\xf0\xa1\x0f}\x18\x9f\xfc\xe4\x1f\xe0\u0211g\xa1T\x13Y\xd6B\xa3\xd1F\xb3\xd9F\xb3\xe9\xba\xfb\xe0\x1f\xfe?\xa7\xc83\xa4\x14\xd1\xf90\xc8\xf6\x8d1\xba\xd9l\xa2,\xcd\x0e\x00\xb8\xf6\xdak\xdb\xe3\xe3\x1d\xdf\xd1V\x17\xf6\u0673g]Q\xf81\xd11]\xe1\x16PJ\xa1\xd9l\xa2\xd9l\xa3\xd1h#\xcb\xda\u0232\x16\x94jB\xca\f\xd62~\xf8\xd0\x0f\xf0G\xff\xe9\xb38\xe3\xe7\x00 \x82\x94\n$\\zK\xa3\xd3A{n\x1b\xa8\xd5\xf2\x83\xac\n\x12\x11p\x81\u077a\xb0\xc8\x19\x184\x05\n\xf6A\f\xd1\x1a\xb7^\x1c\xd2\xee\x94\xd2\xeb<\xc1Vb\xafK/\xae\xa6\a\x9a!Y\xb8l\u034d\nb\xdc\xcb\xe0}Z\x8eLW\x19\u0090\x01\x95\xc7\xc9[\x12\xa2)@-\t\x1a\x97\x80\xac\x9a\xd1:\uf15egQ\xa1\x17t\xb2\x19B\xb5\x93\xe2\xcc0\xbe\x0eJr\xc3Lz\x9e\u05c2*<\xc6\x19\x92M+g5\x9c9\x93.\x12\x95\n4\u0707\x90@c\\B\x8cK\x17\xe3\x96t\xe5<4\xba\x88 \"\x8fp\x8ed\x86-K\x18]\xbaa\xa7\xa9\xe0\x15\xd808\xf5 \u007f\xf2of\xeb\x87\xe7\x0e\xce\t[>\x1b\x86\xa1\\\xd9\xf4R\x98eQ\xb2\xb33\x1a\xaa\xd9\xc6\xc6\xdd\xfb\x00!\xa0K\rcu\x14+1;\x87\u04d5\x95\x15<\xf7\xdca\xed\n\xfb\xe2\x05Y\xcc\u0545vB\xbbw\xef\x01\x00~\xcdk\xae\xa5\xfd\xfb\x1f\xe4\u007f\xf4\x8f~\xfd\xb7\xdb\xed\xf65\x00\xdes\xef\xbd\xf7\xa1\xdb]\x81\x94\x92\xad\xb5d\x8c\x89)\u073d^\x1fw\xde\xf95\xdcu\xd7\xd7\xf1\xd9\xcf\xfe1\xae\xbd\xf6Z\xdc|\xf3M\xb8\xfe\xfa\xeb\xb0s\xe7E\xe8t\xc6 D\xe6\x8bc\xfa\x81\u05c9\xda\xf2\xef\xbc\x1c&\xd3\xfb\x8a\xb2'\x84\x10EQ\xa0\xd7\xebZ\x00\u0631c\xfb\x9f\x13\xd1B\x96e\x1bz\xbd\xea\x12x\xf2\u0267P\x96\x03dY\xf6\xa2\xf3N]\xe7,\x9c\x87\xf8\xdf\u025am\xb0\xb4t\x1e\u01cf\x9f\xc0\x91#Gp\xfc\xf8\t\x9c<y\x12\x87\x0f\x1f\u0191#G\xf0\u88cf\xe1\xe0\xc1\x83\xf1y\x86\xe1 3C\x00h\x8dOaj\xcf\x1e`C\a\xf6\xe42\x94\x97\u07a7\u0233e@\x17\x8c\xa2\xeb\xba')\bMIh\n\x8a\xc5 \xad\xa1i?Z\xeb.\x87\xa0\x98\x17W\xc69v9!\x10\x813\xc7\xc9f\x00\xb4\xc8qg\xc2\xe4Ph\x81!;X\xe9\xb0uj\xf8Y\xc0\xb8\xc7\xc9m\x15FM\xab\x1e\x92\xd6\x00\u007f\u0590\xbf\xbf\xc0\xf9[\xb8\xbaG\xfe\xf3N\xbc\x06\xda\x1e\u051b\xecY q\x81d\x88&\x81\xa7\x14\xd8j\xf0\xb2\xae\xf8\xf0\x1c:AFF\x02\u0658\x9bs\x00\x80\xb1\xec\xe8\x82\xc3\n\u0580\x97\x87\x957\xe5\xa32`u\tS\x16\x11+\x8f\xa2!O)D`\xb2\xf8\x02\x1d\xbd\u030d\x05$\x83U5r\xae\xa2\xf5\x1c\xb6DI\xf3Vy\x88\x91_|4\xa81\x86\u03b6]h\x8cM\xa2\xb7p\x06\u031e\xb1B\x95\xe2\xb8(J,..\xb2\xbb\xb6.L\xc3$\x85\v\xf4\u063f\xffA~\xfd\xebo\x15D\xd4\x03\xf0S\xbf\xff\xfb\xbf\xf7\xb1F\xa3\xf1\xaf\x1f~\xf8\x91\xe6\xe9\u04e7\xa9\xd7\xeb!\xcb2ffbfdY\x06k-\xf2<\xc7\xfe\xfd\x0f`\xff\xfe\a\xf0\xb9\xcf}\x1e\xbbw_\x8c]\xbbvc\xf7\xee\xdd\u0633g\x0fv\xec\u060e\xad[\xb7`nn\vfg7a||<\xaa\xba\xcar\xf0w^\xd4\xd7\xc0\xf0\xd9\xc3\x14\x12\x00\xf6\xec\xb9\xe2\xc9-[\xb6\u0627\x9f>\x00cl\xfc\xa4\xff\xf0\x87?\xc4\xe2\xe2\"6m\x9a\x83R\xcaE\\\xbd\xd8\x12l\n\x94e\x89\xb2,\xdd\x16\xd3\x1b\xf5\xaf.\xfeT\xf3\x86)\xcb\x12\xcb\xcb+8y\xf2$\x9e~\xfai<\xfc\xf0#8x\xf0\x10N\x9e<\x89\xf9\xf9\xb38wn\x01\x8b\x8b\x8b\xab\xa0 !\x84\x8bw\x13TQ-\x85\xc0\xc4\xec\x16l\u06b5\v\xa2\u04c0!\x86\xb4\x94^\xe3\xae\fy\x86\a\x9d\xd70Z`0&\xd1\x17\x04E@K\xba`\t\xe1;+Z+K\"tb\xb6\x1a8\xf3*\xb1\xca\xf3\x15B\x1ajS]e\xe4\xcc\a>\x18G\x97C\x11\f\xa1\xbc[\x9f\u007f|+\xe0\x84Ha\xa0;&\u0717/\xb0\xc4\u0568r5.\x9e&\x85R\xa2~J\xb924\xf2\x8c\xe3z\xc0\xe4!\x06\x1f\xecL\xf0\xd1s<\xa2\x9b\xf7\xc3\xc1\x04\xbe\xaa\xa0\x1f\xbf\xc06%\u0318\x85Y\x04\xac\xa9\xcf\x17\x049\aFa\xbc{\xa1?5c\x1dN.\xd3A\xb1\x1fZ\x83\xc3{R\xedI\xac\xd10y\x0eS\x140\xba\x04[\xedZ\xfc\xe4\u06b1\xfe\x04+\xd3,[\xb9*\x02n\xb1\x94\x00\xca\xc4x+\xce\u0753E\x88\x03'\xde\xc2\xc2\xe1\xfd,,\x1a\x13\x1b1\xb1e\x17z\vagY\xf1\u0549\x88\x8a\"\u01d6-\x9bw\x00\xc0\x9f\xfe\xe9\xff\xd0G\x8e<K;w\xee\xe2\xf5b\xfe#\x1e\xdf\xfa\xd6=\xd6\xf3\x12\xf9\xd7\u007f\xfd7\u007f\xf7\x81\a\xee\xfb\x9b/\u007f\xf9+\xff\xfa\xde{\xef\xbb\xee\u0631c\xe2\xf8\xf1\x13T\x14y\xd1l63f&c\f\x84 \x94\xa5\xeb\xb6WVV\xf0\xf0\u00cf\xe2\xe1\x87\x1f\x05\x00t:\x1dLOOabb\x12\x1b6L\xe3\xe2\x8b/\xc6\xf5\xd7_\x87\x9bo\xbe\x19W^\xb9\x17\x13\x13\xd3\x00\x18y\u078f\x9dp(p/\x16\x96\xf1\nV\f\x06\x03\xf4z\xdd\xda}\x01\x84F\xa3\x11Y.\x00\xe4\xf6\xed\xdbOH)f8\x99\xe6\xddw\xdf\xfdx\xee\xb9\xe7\xb0i\xd3\x1c\xa4l`ii\x01\xddn\x17\x83A\x0e\xad\u02f8\x05t\xb8u\x16\xe1\xa3\xc1 \u01c9\x13'p\xe0\xc0\x01\x1c8\xf0\f\x8e\x1e=\x8a\x85\x85\x85\xa8*]\xdd\xe5{S$\xff\x1c\x8d1\x18\f\x06X^^\xc1\xd2\xd2\"\xce/.aqq\x11\xf6yv\aB\b\b\x95\x81H\xc0Z\xe3!\x1a\u9f22\x1bMl\xbb\xea5\x98\xd9~\t\xca~\x0e\x16\x04\xab\x00a\xaa\xe2\x14\xa5\xf8\f\xa8\u0082\x96\x19\xc2\x00yG`@\x84B3\xfa\x9a\xd0\xc9\x18-%\u0410\x14\x87\xa2qx\xe7\xb1\xe1\xc0N\vN\xb9\x15\xe6\x1e\xba\xb3Q\x85\x8dc\xbdI\xfd\n\xe3\u06d1\xaaE\x1d\r\xde\x15\xeb\u042d\x1a\xc0\x04\xb5a(\xd8M\x01LHG\xbf\xe4Qv\xb5\xb4z\x96IC\xbf\x1b\x81\x8e\xd3Z#\xd2d\x9e\x10\xa4\ubafb\xfb\xa1\xbf\x8c\xb5\xaf\xb2\x8cu\xfe(\x8c\x82\x19yia\xfa\x16\x82\x192&bq\xb5\xc6\b\x00}\x03,k`:s\x96\a\x89\x86I\x10\x0f\xa9Y\xa9\xfe8\xc6@\xe79L\x91\u00d6E\x84W\x90\xc8\xf9\u00d0\u0630\xf5E\xbe\x1ab\x82\xfd\xfb.\tF\x02\x9a-`\xac\xb7\xbd%\b\xff\xees|lO;\x14>\aV\t\b\xa3\x91\xb5'1\xb5\xfd2\x9c~r\xbf\xff<+\b\xa1\xc2g\x99\x8a\xa2\xc0\xec\xec\xecnf\xdeBD'\xf1\u0084\xd2\xf5b>\x8c\xd9\x02\xe0\xdf\xfd\xdd\u07e1\x8f}\xec\xe3\xfc\xeaW\xdf\xf0\x97\xcc\xfc\xe6\xff\xfe\xdf\xff\xeb\xffv\xcf=\xf7\xfc\xd2\xe1\xc3G\xdfx\xfc\xf8\xf1\u01a1C\x87\xc0l\xb5\x94JJ)I)\aKh]\x87P\xba\xdd.\xba\xdd.\x80\xe3\x00\x80\a\x1f\xfc\x01\xbe\xf1\x8d\xbf\xc0\xb6m[\xf1\xa67\xbd\to{\xdb[q\xcd5\xd7`\u06f6- \x1a\x8b\xf0B(\x80U\xd7N\x95q\u03e8\x8e\x87\x19\x8dF\x03R6\xb0\u007f\xffw\xf1\xc8#\x8f\u059eO\xa3\u0450y\x9ek!\xe8\xa0\xffy\xf1\xab\xbf\xfa+w\x18c\xaf\x96R\xc6b;??\x8f/}\xe9\u03d0\xe7\x05\x1ez\xe8!\xfc\xe0\a?\xc0\xc1\x83\x87p\xee\xdc9\xb8\xb0k\xa0\xd9la|\xbc\x83\x89\x89\t\xb4Zm0[\xcc\xcf\xcf\xe3\u0109\x93\xe8vW\x90\xe79\xca2\x9c\xbf\x19\xda)P\xf4\x1c\x0f\u07ff\x18\x8a'\t\x01!\xa4S\xcdI\x05!\x1c~EF;xE)\x90\x14\x98\xb9x\x0f\xf6\xdc\xfcV\xa8\xc64\xfa'\xcfC\x90\x80\x95\x1e\u03b0U:\x1bK'\x19g\x02d\u0260\x15\x03bF1.a\x05\xa10\x8c\xd202a\xd1\xca\x04Z\x8a\u0410\"F@\xba\xady(h\xec|\xae\x87\xdc7WA\x15\x9e\xed`9\x91\xfb\xd7\xec\u0513n\xd8\xd3\xe10\xa9\\\xa1X6\xe0\x81\x01t\x05MXCN\x8cSX\xb0P0\xd2CM\xab\xb2,|a\f\x16\x05\xb5\x1a\xef\u07cb\b\xf7\xa4[\xfe\xba\\?-\xd6a\x14\x11\xb1\xf1\xe1\xa0\xe9\xb0\xc8P\xeaT\xb8\x8aM\tm\x19}m\xd1-\x19\xb6k\xd0X1N\xdf\x14\x16\xaf`\xe3\fr\xe7h\x18\xb4l\xdc\xef'$\x023\xd1V(\x8e\xa7L\xa6\xe9\xae\x04k\xdc\xc0\xd3\x149LY:Z\xa0\xc7\u016d\xad[\xe0Zk1l\x04C\x9c:(8\x89\xbf{\xc1\xb8\xc2\xe6\xfd\xfb\x1b\x94\xa3a\x12\xab%\xa0\xa5\xff\xbb\xa2\x84\x18\xeb`|\xd36d\xcd6\xcaA\x1fBH\x90l\x00\xa6\x04\xc3\xc0\x18\v\xa5\xb26\x80\v6\x13\xf4\x82.\xe6\xe1\xf8\xd8\xc7>\u039f\xf9\u0327\xe9\x97~\xe9#LD]\x00\x9f\a\xf0\xf9;\xee\xf8\xca\a\xbe\xfd\xed{\xfe\u0791#G\xde\xf6\xec\xb3\u03e9g\x9f}\x16\x83\xc1\x00D\x8c,SPJ\xc2\x18\x05c\u071bQ/\xc8\xce3<\xcfs\x9c?\xbf\x80\x83\a\x0f\xe1+_\xf9*^\xf3\x9aW\xe3u\xaf\xbb\x15\x97_~9\xe6\xe6\xe60;;\x8b\r\x1b\xa6166\xf1\xa2\xceY\xeb\x1c_\xff\xfa\xd7\xf0\u06ff\xfdoq\xe0\xc03\xd1\x1e\x16\x80m6\x9b\xb2,\xcb\x03\x9b6\xcd>\x19n\xff\x8aW\xbc\xe2\xf4]w}}U!\xfd\u0527>\x8d?\xfc\xc3Obqq\xf1y\x16=8\xfa\x9f\u007f\x8c\x88iz\xe8$\xfc?\fR]\xb3\x13\xf2U\xd9\xc3.\xbc\x9a\xa7;bq%\xe1L\xfe\x11\xd9B\"^_\u0194\xf1\x82\x93J\xa23\xb5\x01\x9b\xf7\\\x8d\xab\xdf\xf5al\xdc{#V\xe6{\xe0\u0702\xd8\xfb\x1bJ\xc0\x12\x83\x02\f`\xad\xc3.|\xbc\xa7`\x8b\u018a\x1bD\xe6\xe3\xd2\xd9\xd22PX\xa0\xc8-\xba%\u0412\x16c\x99@S\x0e\x85\v3\xc5b\u00a3\xe4\xf7\xb4\x1ax\xb1pp\x81\xa5\x91\xe4\x16\xf7\xba\x19\xef\t3.\xdd\xcfK\xeb\x8a9\x85N\u0531G4\xbb\x8eU-\v\xf0\x84t\v\x95M\x89\x1c<Zk\xef!\b\xe3\u03ff\x1a\xfer\xb4\xc7\x1dAV\x84\x89\xefy\x1d!\xaaM\x15(\x88r\xe0\xc56T\xf3\u0231\f\xe4\xa5EO[\f,\x83KF\xabo\xa0\xb4\xdf}\b\xb8\xc5\xcc\xc3(L\x0e\xd6p;N\v\xb1\xa8\xbd\xe0K\xd6c,\x98\xeb\x93W\"\a\xad\fr\xe82\x87.\x8a\x04/\xd7Ug\xee\xa5\xfcQ4\xc4\x0e\"\xb4!\t\xc9\xc7\u00b1\x0f\xe1h\xf8HA\xee\x01\xa6tx9{\\\x85\x02F.\\\xeek)\xdd\xeb+\x8c\xeb\xd2\xd9X4:\x1b\u041a\u0704\xa2\xff\x1c\x84\u02a0l\x03F\xf7aJf)%\xba\xdd\xee<\x80\xf3\xe9\xc7h\xbd\x98\xff-\x8e\xb7\xbf\xfd\x1d\xfc\xe9O\u007f\x12\x1f\xf9\xc8G\xe3\xcf\xde\xfd\xee\xf7|\x1e\xc0\xe7\xbf\xf9\xcdo|\xf0/\xfe\xe2\x9b7\x9f8q\xe2\x8d\xe7\u03df\xbf\xf2\xc0\x81g0??\x8f\xa2( \xa5\x8d9\xa2\xcc6v\xec\xc6T\u0662\xc6Xt\xbb]\xf4z=\x1c9r\x04w\xde\xf95LOOc\u01ce\xed\u0631c'\xb6n\u0742\xed\u06f7ann\x0e\x9b7o\xc6\xc4\xc48\x84\x90qq`f\x14E\x8e\xc1\xc0-\x0e\xa7N\x9d\xc2C\x0f=\x8c\xbb\xee\xfa:N\x9et;2\xa5TP\xac\xb2\xb7\x8c]y\xc3\x1b\u07b4\x10\x9eK\xa3\x91}gnn\x0e'N\x9cDQ\x14\xb1\xa8.,,<\u07ee\xc5\xc1\x1b\x9e&XAB\xd5\xef+\xc3)\x8f\x8bZ[\xfbZ\xabx\a\x8f\v\x12\x94\xd8\x0f\xb3\xef\u0185\xc3\x14\x85\x80j4\xd1\x1c\x1bGgf\x16\x13\xb3\xdb\xd0\xd90\x8b\xe6\xf8$Tg\x12\xed\xd9\x1d\u0630\xfb\x1aL\\\xb4\x0fK=@,\xf6\x91\x19\xac\x9a\x00\xd6\xc9\r\xec\xf2%\xfdV\x98\f\x90u\r`\x81bR\xc24\x84\xebf\xddn\x1a]\xcb(\xacu\x98\xba\xb7\x96M\x85!\xa1C\x14\xabL\xb9\xea]+E\u067a+\xa4\\\xf3#\xa9\xa2\xc8\x02D\xcb\xc2]=\xc1\xc6\xc3Z\x8e\x1eV,\x1dvn\xfa\x16\xda\x14\x10ZAN(\u020c|\xe3H\xf1\xf1\xac\xc7E\xc2p\x17\xfe\xb1-\x1c\x13\x05a\xf8\xeb_2\x9bb\xdbT\x15a\xf6\x03\u3c01\xa0!*|x\xba6\xb2\x88\xc2\xca\xe0\"\xd2\x06\x861(\x19\xb9\xb1n!\" \xcb\x19\xb2`P(\x98\x02\xb0YP\n\xf9\x9d\x88f\xcf<\x12\xc8\x18PK\xae\xa0sS\xa4\xebfmg\xc2ZC\xe7\x03\xe8|\xe0\x18,\xa13\u05fe\x90'\x9f\u05fa\xd9V\xf5\uf2ad\xe3v7\xc2\u007f^\xdc{\" \xc8\u0090\u007f\rQ\t\xbb\xb4\x00J\xc1q\xe1v\x10\x8e\x00\x1b\v\u065a@kj\x13\x96N\x1c\x82 \t)3\xf7;\x06g\x8d\x06\x0e\x1e:\xf4\x04\x11-\x02\x10\x8dF\u04ee\x17\xf3\xbf\xe5\xb1c\xc7E\xab~\xf6\xf5\xaf\u007f\x8d\xde\xf6\xb6w\xf0\x9b\xdf\xfc\x93\x9f\x03\xf0\xb9S\xa7\x8e]\xf9'\u007f\xf2\xa5W\xed\xdd{\xc5\rg\xcf\xce_\xdb\xeb\xf5\xae\x9f\x9f\x9fo\x9e:u\n\xddn\xcf\x0f\x01\x1d\xf4R\x14\x05\xfa\xfd\x01\xf2|\x00c\x92\x0f\x871\xe8\xf7\xfb\xe8\xf7\xfb8q\xe2\x04\xbe\xff\xfd\xfd\xa8\nn\x03SSS\x9e\x19#\xfc\x82`\xc0\xec\x06\x86E\x91#\xcf\v\xf4z\xbd\xf87\x81\x8b\xad\x94\fjI\xbe\xe4\x92K\xb0o\u07d5O\x11\xd1\xe1p\xbbV\xab\xfd\xec\x8e\x1d;\x8e>\xf9\xe4S;F\xe3\xda\xf5\">\xfc}\x1a1\x97B)\xe1{_\xb8\xf9\x85HzD\x04\xa9T2\xfd'\x80\x84\x13\xffd\x19\x1a\xad1\x8co\u068a\x99]\x97c\u64bd\x98\u06be\a\x9d\x99Y\xb4'\xa7\u045a\xd8\x0096\x01-[0B\xc1\x88\x06\x8a\xdcb\xb9?@\xd6+\xd1.Qc2\x90\xf5A\x04\xc1\xf1\xce\xc0o\xe3\xb9R\x1b\u00b5\xca\xd9\xc0\x80\xc0\xc8'\x15LC\x80\xa9\u2a57\x86Q\x96\x8c\xbee\x8cenP\x9aIJ \x16\xdfuS:(\xad<\xae\xeb\x905\xd5\xcc\x0fC\x17\xccL\xae`\xfb\xdd>k\x06V\f\x88\xbdp\u01ba\xc1\x9f\xf10\x06\xc1y\x8c\xe8\x9c\xc1\xe7J\xa0\xb0\xa0I\x05\xd5\x10\x88\"R\xaeZ\u22bf\xed\x9e/\x0f\x85\x98\x86\x1al\x93?c[)+\x89\u073f\x1dN\\\x87t\x1c\xb3eH?J\xee9\x15\x86\xd1--\xfa\xda\xc2\xf8\xa1)\t@\u632co\xe2L \x0e\xab\xc3\xceE\x00d\u072e\x89\x19\xd0\xd6u\u045a\x19\x12\x80\x9c\xc9 2\x01k\xbdM\x82p\x85\u0616\x1a\xba\xc8aB!\x8ftD\xed8\xde\xc1{\x85\x19ll\f\xa7\b\u0316`o\xcb\x1e\x9b\x93\x82<\x1f\xdci\x15\xb8t\xb7\xa3j\xb5\x84\xa7\xa0\xc0\x92t\x1d\xb9\xa8\x16n\xf2\u05c71\x06\xb2\xd9B\u0599\x8a\xf7\x97\xc2TB\b\xf4\a\xc5\x00\x00^s\xfd\r47\xb7e\xbd3\xff\xbb:\xee\xb8\xe3+x\xdb\xdb\xde\xc1\x0f=\xf47\xf4\xe0\x83\x0f\xca_\xf8\x85_4ss\xdb\x1f\a\xf08\x80\xcf3\xf3\xb6\xff\xf6\xdf\xfe\xcb\xdc\xe1\u00c7ggf6\xbe\xe9\u0739\xf3;\x1ey\xe4\xe1%)\xd5E\x1b6lx\xeb\xc1\x83\a\xd5\u0463G\xb1\xbc\xbc\x82\x95\x95\x15,//#\xcfs\x0e\n\xcd\xe1\x8e\xd5u\xdf\x05\u039c9\x833g^\x98\x16\x18:q)E\x847\xac\xb5\xbcy\xf3\\v\xf9\xe5{\x06\xb7\xddv\xdb\u007f\x01\x80\xc7\x1e{\x98\xf6\xed\xbb\x86?\xf8\xc1\x9f_z\xf2\u0267\xfe\xea\u0211\xa3\x1fx\u4447\xd1\xeb\xf5G\x16\xde\xf4\xbc\xc2\xf7C\x85\u007f\xe4\xdf\x11\x11Z\xad\x16\t!\xe2\x1c!\xfd\xbb\xb4\xf8K\x95\xa13\xbd\x01B5AY\x86\xf6\xe4\x06\xcc\\t\x19\xb6\\~\r6\xee\u078b\xf6\u62d0M\u03601>\r\xb4\xc6\xdd6\u057a\xf3\xe8\x97\x1a\xa56\xb0\u0682M\x0f\xb64\x10\xc6uy\xc2$\x92\xf9\x9a\xf2\x93]\xa7g\x03\x8c\xe0)e^\x90\x130c\u0577\x00k\x14\x13\n\xa6\xe9\v\x9cE\xbc\xb8\xf3\u04a2,\x81AF\x18oJ\xb4\x1aU\xa7\xce5x\x82V\xe1\xc4\xe9\x8bfb\x91\xe4\x88)k\xc3\u0425+.\xc62d\xcfB,\x1b\x90u\x85-\x868\b\xef\u07c2D\xe0h\x00\xbbl\u07023\xa9@\r\x82\x02\x90\x81\x90\x81#\a\x9c\xfd\xf3\x14*a\xa2\xa0\xdaYp\xbaCH\xb6\xfa\x82\xea\u04c0\u06ae$\xb0K\xb8>-\xd0\x06\xe8Y\x87\x8b\x97&Q\u0252\xdf\r\xf5-T\xe1vI,\xfc\xfba\x00\xd2\xd6\xe3\xfc~\x87\xe1'\xa8\x82\x00\xd6@I\x8cr\xd9@\nB\xb61\x83\xf23\x83R\x1bp\xe9\xf0q.\x1dk\xc5j\r\xa35\xd8w\xe4)\xac\xc2\u0182\x8d\x89\x83P\xa7\b\xe5\xa4kO\xb6\x1b\xecp&1\xb0\xb0%;s-\xef\xfbB\x11\xa7r,\x1f\xaey\xe1x(\xce/ \xa42H\xcflcx_u6\x00;\xe7\x97M\xb3\x9b'\x01`\xff\xfd\xf7\x99\x93g\x17\xd6\a\xa0\u007fW\u01fb\xdf\xfd\x1e\x8f5\xbf\x8a\x01\xe8\x13'\x8e\u04b7\xbf}\x8f\xba\u77bf\x12\x9f\xfc\xe4\xa7\n\":\x1e&\x9d\xcc\xfc\r\x00-\x00\xdaZ\u04fe\xf7\xde{7\x1e8p\xa0\xdd\xedv_\u007f\xe4\u0211\xf7?\xf6\xd8c?q\xf8\xf0\xe1\xa9n\xb7K\v\v\xe71\x188_\x14\xb7]\x17\x15\xc5\xcdo\xfd\xa8>?\x1b\x8aJ\xab`\x8e\xa4Sf\"A[\xb7n\xa3\x9bo\xbe\t\xb7\xdcr\u02ff\u0677\xef\xea\xbb\x01`\u07fekB\x90s~\xd7]_\xfb\xbd,\xcb\xde=>>>\xfe\xc0\x03\xfbiqq\xe9Gz-\xaa\xee_\xa1\xd9l\x12\x11\xa1\xdf\xef#\xcb\x14\x8f\x8d\x8dQ\xbb=\x06\",l\u07fe\xddn\u06f6m\xfe\xfc\xf9\xf3_\x1b\f\xfao\xfc\xde\xf7\xee{e\xea\xff\x12\x1c\xe1\xc6&7\xe2\u019f\xfbUl\xdc\xfbJ\xa09\x86\xf1\x99-hM\xcd@\xb6;0\xaa\x05C\x02eYb\xa5,\xa1\xcf/:\xb8*\xf0\x95\xa9JI'\x02\x98\x04Di\xdc@3TTI\xd5\xf7\x9c\x14\xf6\x00lp\xc5\xf1vX\xad\xc3iI\x00j`AF\xa3\x98\x94\xd0-\xe1=^b\x1d\x85\xb5\x8cA\xc1(5\xa3m\x04&\xda@K\xc8\xc4\xe3|m<\xbc\xb4\fm\x19\x03mQ\x9a\nk\xb6\xceb\x1b:wb\x16\xa9\x19\x8d\x15\vU\xb2\x87\xa1\x82\xa0\x06\x95\xe8\xa66\xcfd\bKP\x03\xbf\x18\x8dK\x14\x19\x01\xdaB\x18\x8bL\xb8\x9d\x84\x92\x04\xa5\b\x92\x9c\u06b2\xdaI\x84\xe2\x9d\xf8\x8cp`\xab\x84q\"\xc7\xcf)\xaa\x97..H\xe9B\x95kF\xcfX\xe4NG\x19\xcd\u0442Y\x95\xea[\u0201\x89wB\f@3\xa4\xf1,\x16\x03\b\xe6\x88\xd4 @\x16\x9eQd4C\x9f/Q\x12\xa31)\xa1\xd8\xc0\x16\x8e\xb1\xc2Z\x03F;\uee9fc\xc1\x98\xaa\x90\a>\xb9\x87A\xd9$\x1cs\xb6\x91c\x8e\x80\x99\xb3\x05\x97\f;0@a\"}2:4\"\xb11`@X\x86%\xaa\xbbf\x06\xdez\x80\x1d\xbdk\xa21\xda)G\x01\xc7\xcc\xf2\xa1\xcf\xd5\u02b7\u0799\xff\xffrl\u077a\x83\x01\xd4\xd2\x1c\x9e~\xfa\t\\v\xd9^\x90\xe3\xa2\x05\v\xc2\x12@\xa8\x92\x8f\x03\xf8\xf4\xf1\xe3G\xf6\xfd\xf1\x1f\xff\xe7\x9f9|\xf8\xf0\xcf=\xfd\xf4\x81\xd9~\xbf\xbf\x19\x80XZZ\u00993g\x90\xe79\x8a\xa2,=.-\x84\x10\x94b\xd1\u00d0\a\xbb\xc3Z\u02d4e\x99\u06b0a\x9a\xb6n\u074ak\xae\xb9\xfa\xec{\xdf\xfb\xde\xdf{\xeb[\xdf\xfe\u007f\x00\xc0\x1f\xfc\xc1\xef\u04ef\xfd\u06af\xf3\x17\xbe\xf09\xba\xfd\xf6\x0f\xf2\xdb\xdf\xfe\x8e\xfb\xfa\xfd\xe57\\t\xd1\xce\xffs\u01ce\xed\xb7<\xf1\u0113\x8dS\xa7Na0\x18@)\x85\xb2,K\xa5\xd4\xf2\xe4\xe4\xa4PJ\"\xcb\x1a\x12@\xbe\xb0p\xee(3z[\xb6\u0309]\xbbv5\xa6\xa6\xa6N?\xf5\xd4S\xff\u03d5W\xee\xed\xddt\xd3M\xadK/\xbd\xd4LMM|\xf3\xaa\xab^\xf9\x8c?\xbf\xe6\xdf\xff\xfb\x1f\xfe\xcf\xdf\xf9\xcew_\x19,k\x03\x9c\x026 \xa9\xb0s\u07ed\xd8y\u02ed\xe8\x19\x86\x06A\x1bF^\x14\x18\xf4s\xb05\xb1\xd0H)!\xa5\x88\x1d\xec\xaay\x1e[d\x85u\x83&O\x13$\x9bx\v&xFJ\x19\x8c\x9e\xdb)\x89#p\xa7K\x8b\xe6\x12\x83\x8cB\xd9\x11\xb1[Mi\x88\xdaX\xac\f\x18%\bc\xd6A/JT\x9d\xab\xf5x\xb5\xb5\f\xc3@a\x18\xb9\x1f\x06\u06e4\xcb\r\x1d\xb01\xae\x90\x90\x01T\x9f]!\x0f<f\x06\x84\xae\xd8\x16\xc2T\u0404\x15\x04\xab\x1ch,\r@\x03G\xf3\xcb;\x12\x85 h\x03\x94\xdaV\x83l%\x905\x81\x86r\xe7\x9by\xb5i\xe0K\x8b\xb0\xb3!D\x8a \xa3>#\xa9\xcdU\xfdBg\xd8-p\xfd\u00a2[X\x18\x01\x88L\u01a4\x9ep{\x953\x1a=S=\a\x06\x84f\x90\xf6\xbb(\xeb0\xf4\xf0^\xa4\xf3\x06\xb7\xbb\xb2\xb1\xf8\x17g\v\xf4\a\x80j\x1ad\xa6pE\xdcxz\xa1\xadb\xe1\xc2\xc03\xf8\x98#t\xe2\xa6\xe2\x92G\v[\xe3\xfd\xcd\x03,ZZ\x98\xae\x86\xcd\xdd\xcf\xe3{b\xc9\u0767_\x04c\xb0\x89\x00l\x18\xe4\u05f6#\xc2\x0f_\r\xa4Tn\xbeV\x16a\x17\xc0\xadV\v\xcdv;\x0f\x9bo\xb7\x8f\\/\xe6\xff\u04ce\xcb.\xdb;\xf2\xe7\xcf<\xf34.\xbd\xf4\xb2\xf8\xefm\xdbv>\x06\xe01f\xfe\x97\xf7\xde\xfb\xdd\xeb\x1ex`\xffM\v\v\xe7_\xf5\xec\xb3\xcfN//\xaf\xec[^^\xdeND\x9ds\xe7\xce\xe1\u0739\x05\xf4z\xbd\x04/\xaf\x8a\xb9\x10\x8e1\xd2\xe9\x8ccjjRNMM\xa1\xd9l\xf6ggg\x0f]u\xd5U\xdf\xfc\xf0\x87\u007f\xfe\xd3ss[\x1f\x03\x80\xf7\xbd\xef\xe7h\u04e6\x19\x06\x80\xdbo\xff \u007f\xedkw\xd2;\xde\xf1Nn\xb7'\x1e`\xe6\u06ee\xbb\xee5\xb7\xdfu\xd7\xd7\xdf\xf5\xdd\xef~\xaf\xd9\xedvinn\xae\xdbh4\xbf{\xf3\u036f=\xf2\xaew\xbd#k\xb7\u06f0\x96\x1b\x9dN\xe7<\x91\xba\x87\x88\x06\xcf=\xf7\x1c\xee\xbb\xef\xfe\xf8\xbc\xbe\xf5\xado\xe3S\x9f\xfa\xbfG\x12\x8c\x17\x17\x97D:+H\x9b\r]j\fV\xfa(\x16\x80>\xaf@+G\xb7\xd3\\\x9f]\x06\x88\xe4\xf9\xecE\x84f\u021c\xbd\x89Q`\xa9$\x86F\x89\xc9\x16\xfb*\xcabd\xc4g\xac\xea\xc2\x12\xa8`4\xac\x06A\xa2\x1c\x93\x18\x8e\xbe\x16\x9e\x9fg\xc0X),\x06\x06hJ\x82\x0f\f\x82\xb1\x0ek7\x89\xe4=\xb0A\x86\x1e\xce\r\xf0\xfc\u03f2\x82\x91\xe5\xbe`\t\xf7\x1aH\xcd\x0e~\b,\x99\x94\xefg<\u03ac\xdc\x0e\x83\fC\r<\u04e2I(%\u0570m\xb0\x13\xe7\x14\x96=\xd4\xe2DS\xd2\xf3\u01a5p\xcf\xc1Y\n\xa0bQ'\x9c\xf2\xb8T\xfa\x81mQZ\xf4s\x8b^\xcen\xe1 @HQ\xdfa\x12 \r\x90\xf5\rD\xe9\x19 \xcc\x0e\x177\u026e*\x95\xd8&\x19\x9b\x1c\x02 `\x00m\xc1\xc2\x02\xb9\x81\x19\x18\xf4\xdb\x06J\x19\xb4\xc8\xe1\xe9\b\xbe*1\x1e\x8e\xa3\xdd-\x12qP\xc57\xf7\x81\x15\x89X\xc8\x18FQX\xe8\xae\x06\xe5\xa6fU\x1f\x98J\x14w\x1d\xceg\x80-\x03\x86\xc1B\xfa\x1cU\xf6\r\x84\xdbY9\xb7\xc6\x02\xc2\u04c3\x1d;\xcb\xdd\xf1X\xa7\x83\xe9\r\x1b\a\x00\xf0\v\xbf\xfc\xab\xb4u\xd3\xf4zg~!\x1c\xa1\x90?\xf6\xd8\xc3x\xcb[\u0789\xa3G\x0f\xa7\xdd\xf5\xf7\xfdW(6\xaf\xf8\xeaW\xbf\xbc\xf7\xe8\xd1c\xaf}\xf0\xc1\x1f\f:\x9d\xb1\xab\x1a\x8d\u019e\xc5\u0165\xb2(\n\x18\xe3\x042Y\x96\xa1\xd5j\u0271\xb16///?\\\x96\xfa\xe8\x15W\\Q\xec\u06f7\xf7\xe1w\xbd\xeb=\xf7\x12\xd1s\x1f\xff\xf8?\x03\x00\xbc\xf7\xbd\xef\xc1\x87?\xfc\xf3\x1c\xa0\"\x00x\xc7;\xde\xc9\u007f\xf5W\xff/n\xbd\xf5\x8da'\xf1y\xffU;>\xf3\x99\xcf\xfc\xad\x9f\xf7\xa1C\a\x82]BK)IB\xb8\xc1\xdd0\xe8`u\x89\xbc\u06c7^\xd10\xd0>\xb6\xcdC\x1d\x9c$\xb1F\xaa\x9cg\xba\xa0^\x1c\xc8\x02\xaa\xe0\xd8\xe5\x11\xfb\xa1\x19\b$\xb8\x1e\xda3\xd4\xd2\x13\xb9A\x1bQ=U,\x82\xd1\x02\x10\x1ah,\x1b\xc0\x00&\xab\x84\x8eQ\x96\xe2\x17X\x80}\a\u0315\xfd\xe9pr\x19\xd5a\xb3d\"\x8a #R\x1a\xc8r\x1b!\x05b@\x1a\xf7\xfc\xaa>\xad\xae\xd4$\xf8\xae\xd6\x12\xac\xf4E\xdd\x12Tn\xd1,\x9dK\xa1n\xb8\xd2#\xe06G\x94\x88?K\u02c8\x1a[r\x9d\xb9c\xbe\xb8b.C\x81\x17\fEa1r\x85\xbcdF^X\xe4\x03\x8b\xbc0\xfe\xf5&\a\xfb\xd0\xea\x18S\x99[\x88\xbc\xfa@\x90\xf5\x1c\xf2Z\xa6\a\xd5\xf0*f\v\xd6\xd6\v\xe8\r\xac\x1fb\x82\f\x98\f\x044\xa8\xb0\u8d41\\0Z\x02\xf0\x96\xe9\xc9{jcQG*\x18\x8a\x85\xdc\xcd*\xc2\xe3j\x03\xf4\xfb\x1a\xbao \n\v\x19\x86\x99\xfe=g\"\x18\xb8\xc5UD\x18T@\xa02\x1b\v\x10Tx=!\x04t>@\xd1[\xf2\xbb\x00\xe3rA\xfdy\x8e\x8fOb\xe7E\x17K\x87\x02l\xbb \xeb\xda\u02f2\x98\x87c\u07fek\xe2\xf7\xa7N\x9d\xc0\xdc\xdc\xd6Q\uc387\x00<\x04\xe0\x8b\xbe\xb8w\x00\\\xe2\xe1\x9a\xe1\xfeQ\xfa\x06\xebq\x1a%3\x04\xf0\u05b7\xbe\x15\x9f\xf8\xc4'\xf0\xda\xd7\u07bc\xeaw\xb7\xde\xfaF\x1c=\xfa\x1cv\xec\xb8\xf8E?\x97\x83\a\x9fF\xa3\xd1x\u07bf\xf5\x85\xdcw\xa0>\x1e\v\xa6>\x00\x00\x90\xf7\x96\xd1]8\xeb@\xabA\t\xce\b\xba)`\xd9\x0f$\x13\x18\x82\x13~\xb5\x1drV\x92\u05ba\xe2g\xfd\xb0\xcc\xfa\xa2\xe7)\x11\xf4|#\xdbT'\x13\a\x9d\x9c\xdc\xd6A\x17T02m\xc1\r\x81A[\xc0\u02aa\xa0\u04c8D\x1c\x1e\x01y\xa64>\u02d1\u679c\n\x83J\x8b\xac\xe7\xb0\xffpr\u00b3o\xc8\x0e\xe1\x1a\xc3\xd6\x00\xfe\x8eC\u0383\x95N %4\xa3\u0673 \x16(\x9b.FO\f\xf1\xdf\x05\ra\xfbn\xd6\a\xcd\xd1\xd36\xce'\x94\xa0\x1ac\xc70\xc3h\xf7\x05\x10\x04U\\\xc1\x9a.\u04cb\xb4T\xdf\r\xab\x01\a\xa5\xb8E(Y \xd9\x01O\x96C\x87\xec\xbai\x1b\x92~\x9c\x91\x80\u01f6\r\x98]3 4C\x82P6]\x91-\x89\u0410\x80\"\xe7\x9e\x19\xb8\xe2A\xbe\x1f\x86\x9fV\xdb8\xec\f_\x85\xb6\xe8\r\\\u01df\x95\xbe\x90'\xde6\x14\x06\x9d\x91zX\xa7\xc2:h\x8f\xa3\xe5\x04E\xc30\x05\x9d\xf7\x90/\x9fw\x9b\x03S\x82\x8d\r\x8c(j\xb7\xdb\u0631}{\x1f\x006\xcdlZ/\xe6\x17\xf21\xaa\x9033\xee\xbd\xf7\xbb\xf4\xe0\x83\x0f\xd0\xd2\u04b2\xbb\xb6\x9ch\xe9\xe1\x17\xba\xbf\x8f\u007f\xfc\x9f\xcaM\x9bf055\u016fz\u056b\xf8\xc6\x1bob\"\xc2\xddw\u07cd\xbb\xef\xbe{\u037f\xfb\xdb\x14r\x00\xb8\xe4\x92\xcb^\xd4\xed\x83#b\r\xef\xf7\xb4\x0f\xf2\xdd9\x9b\x12\x18\x14\xe0e\x06\x93\x02\x87|DO\u007f\v\x85Cx\xdb\u046a/\xa5\xd8\xe9\xa9\xc2\xe3\xab~\xcb.\x18#r-\xeb\xb5/0Z(\xf8\xb7\xc8\x04\xc0\x0e\xd7f\x80\xa8\x85S\x916z.\x95=\xefH\x87Q\xfb.7\x8c\aG\xfa\x13&4\x97\x10~\x1c\u050b\x14:r\x02Dn\xa1V\f\xd4\xc0-L\xc1cE\x9a\xb4`\x8c\x1a\xab\x0e5\xf8\xec\u0395\xe0pzA\xfeu\xe9\x19\x10\x00=&\"\x85e\x94\xab\xef0\xf3&\x8d\x1ce\x8f\xfb#\x11\x1aU\x8f\x9d\xec\x16\x86m\x81\xc3\x0e\xaao\x1d\xa7\u0727jS\xc4\xcc#\x01\xdf\x15n\xad\xc1\x1e[\x8e\x8a\xe2\xf0\x86\x04\vZ\xdf\u055a@Q\xb2\f\x05\x02\x93\x80V@\xdfZ\xe4%\xa3)\x81\x86\x00\xa4\xd7\xe4[c`\xb5\x01\xeb\xb0X\xa0\x1a|ZF^\x18\xf4s\x03S2\x1a\x06P\xa1\xb3\xf0|Q\"\x87\x93\x93\xb7i\x11\xf1\x9d\xaf\"\x9d\xd3l\xec h\x12B@*\t[\x0e\xa0\a]\a\xcf\x19\xed\xf1{a\xa5\xca\xe4`\xd0??>\xde\xf9\x1b\xbf\x1b\xe1\xf5b\xfe\x12;(j\xa2\xe3\xf5d\x9f}\xf6\x19ZYY\x11\v\v\xe7\xbdO\xca\x00B\b\x8c\x8f\x8fcbb\x02\x13\x13\xe3\x18\x1f\x1f\xb7;v\\l.\xf4\xe7\x96\n\x8d\\\xf1\x930\u05a2=\xb5\t\x9d\xa9Y\u75e15\xa8\xcb\x10\xc2\u008c+\x80$\x94\x94h*\x89\x8er\xe2\x96\xdc\x00\xb9q\x8d\xbca\x9f#i\u06337\xb8\xea\xf4\xc2\xe0\x93it\x15O@\xea\xa0t\x8c\xc6Tr\xa8\xa0yqQ\xb0\x9c&\x06\x1a\x03\x87e\xe7\x1d\xe1|Q<\x9f\x8f}g&\x92\x87K\xad\x188\xed\xf6\x87\x02\x96\x85f\xa8e\x03\xd5s]k\xb0\t\x10\x9aGx\x82\x8f\xf6_IU\x9b\xe4\xbbHa\x1cvm\x15A0\xd0\xe8:\xb8\xced\xfe\xb9\xda\u054b\x01\x0f}\x9f\n+\x13K\x97:\xc7|8\b\x9a\x87\"CAn\xd1\xed\xdb\xc8\x1b'c\xe12\xe5\x9c\u0692|\x00\xb2\xb5\x16\xec\xbd\u01a3B3\t\x99p]9;u\xa7\u05700\xb0d\xbdI\x8e/\xaem\xf2\"+\xa0o\x19\x05\x80\x8c-\x945\x10\x9c\xb8$\xa6P\x8e\x05\xf2\u0720;0\xb0\xa5Ef\xfc0\x99\xa8\x06\xd5\t\x1b:\xf2J5\x15\xf8\xe8Lu\xfc\x8c\u00c4]H\a\xcb\x18\x8d\xb2\xb7\x04\u05b9\xeb\xee=\xc3\x06\x04\u03b2\x06\xc6\xc6\xc6\xe6o\xbd\xf9\xc6S\x00p\u026e\x8b\xd6;\xf3\xff\x15\x8e]\xbb.\xe5:6\xf1\xd2<\x94R$j\u05a0.H\xc2\xe8\x12\u04db/\xc6\xcc\xd6Ka\xf2\xbe+D\x85\x81Xf(\t\xa0\xcdh\x801\x91\tlk\x13\xba\x86p\xaa\xcfCb\x14@\xf6-dnk\u04514\xf4\x1dV\xf7\u02a8\u0665V\x81\x91\x80\xf1\xe1\xcfI@M\x10\xda @\x1d`'.bF.\b\xdc\xf0\xde,\xb6j\xbd9\x8apFy\xc2r\xec\xc8\x1d\xf7\x9d\xa1z\x06\xaak \x8a$W\xd2\xfa\x0e\x9b0zQJ\x8a:y47\x9a\x81y0\\\xa4\x18\xbb\xa7j\x8a\x9e\x81\x96\x84\xa2\xe3\x02\xaf\x89\xebU\x9c\xd6\xda\x04\xd0j\u007f\x95\xd5\u007f\xc3\xf5\u074f\u01cdEi\xd1X\xd1P\x03\x03\xd2\xd6\xd1\x04=\x93\x04\x91.h\x00\x1f\xda\xc06\xfcn(\b\xc3\xe3\xe76t\xec\xde\x13\x9c\x03\xe3F\xc03b\x04\xd0\x12Q,Uh\x8b\xbc\xd0 \xb6h\b@\t\x06\xac\x89|sk\x19EiQ\xe4\xce\xeeZ1A\xf9\xd7\xce\x18\x87\x8b+\x12n\u0776\x9e\xba\b\xae\r\xf6\u067f\xa7B\b\xb0p\xb9\xa0L\x02\xf0\xcai\xa9\x14L\x99\xa3w\ue933\x15\xb0\x1a\u0194N\xf0\u0116'g&09>\xf6\xdc\xc6\u0249\xc7\x01\xe0\xdaW]\xb5\u0799\xaf\x1f\x17\xcca\a\x83\x81\xa6\xe4\xc2v_\x0ev\x99\u06b4\x03\xe3Ss\xd0y\x0e\xc1\x16L\x04QZ\xd0J\xe9\xdc\x0e%\x81\xacA\xc9\n\xda\xc2\xcb\xdf\x13\xcf\x13\xed\x04\x1c\x94\xd0\xdb\xc8>_\xe1\x1b\xb6{\xad\xb7\xa1\x01g\x0f\t\xf1\x9cH\x1c\xa9\xb6\x89v\x9445`@\x18\x94\r\x11;\xfapq\xa7\xdd\xfe\xa8\x90y\xf6\x95\x91\xc8q\xdaU\xdf@\x1a\xae\u01fc\x99\xb5h\u018c\x11%tu\xdf\x1e-\xbd9\x0e\x14\t\x0e\xda\u023a.R\xaf\x18\x97\xe0\u0107\x9cj\x03\xe7:\xee\xcf<bm\x1c2\xf5\x8a\xc1\x0e^\xcd\n#\x1c\xcdr\xa9\x84\\\u0480\xb1\xce\xdaW\x9bD>o\xaa\x82\x1e,i\x13\xf1N\x98'T\x01\x13\x15\x8e\x0e\xb6\x91Ji}Q\xb6l!\xfa\x16\x86\x85\x8b\x04\xd4\x1e\x1b7\xae\x1b\u05de\xad\x12\xe4\xf9\x96\x19\xd68\xe6\n3#cB\xe6\xc5d\xde\xe38J\xf2\x03\xccRE\xcbzk\x83\x10q\xe4\a\v\x04\xf2Fo\xce$\x8e\x84\x80P\r\x94+\xf3X9y\b\xd6j\u8c80\u0445\u007f\xad\xd8n\x9e\x9d\xc5\xd6-sG\x88h\x05\x00v]|\xc9z1_?~\xbc\xc7\x03\x0f|\x1f\xaf~\xf5u\xc8\xf3^\xb3(\x8a\rU\u01a8\xb7r\xb2\x16RJL\xcelA\xa62\x94\xdd~\xf4\x90\xb5\x02@nA+\x06F\ttK\v\xddw>&\xa5\xe7f\a\xbf)\x99[\x88\xc2\"u\x06\xac\xb7\x94#@s~\xbei\xf6\xc3_Q\x00\x00 \x00IDAT\xa8\xef\u01bds\x96\x15nX\u82b7\xdb-\xb3\xad{9e9C.\x1b\xe4D\xce\xcf%\xe9\xc4y\x04lQ\xc1-\xde\u042a\xe4\xaa#\xb7U[\x1d\xed\x06\xaa\xdc\xf75\xbcP\xd3\u0166\x8e\x9bG7\xc6Z:}\x80_\x18\x8d\xae\x06\xc0(\xc6\xd5\xd0b\x94@7#\x1f\xaf\x1a$\xc6\xf5\xd1X\x87q\x97\xdaK\xdb\x19$\x01+\x04Ta!\u03d7@\xee=\xe0\r{\x01\x8f\x89\x1e(\xd1\x0f,\xfc\x8c\xab\xc2\x1e\x87\u00fe\x98W\xe1\u02ee#\x8f\x03\bN\xa8\xa9\xa5\x81\xb0\x06Zy\v\x8a\xa8nr\xf7\x19\xbcj\x8cGA\x02\xe2\xa2\x18P\x815\xe4\x1f[\x10A\x06;\x84\x04o\t\"(x\xbfu\xeb\x19.\xcc\x16\xde\u0165*\xee\xc2\x05\x90\xaf\x9c:\x8c\xe5\x13\x87\xc0\u01a2\xcc\xfb0F{\xc1 \x1a\xadV\x13\x97_~\xf9\xa3\x17\xfcn{\xbd\u013d|\x8e\x83\a\x0f\x11\x00\xfe\xcaW\xbe2\x93e\xd9\xcd5?\x17r\x17Tsl\x12\x13\x1b\xb6T\xb8\xa5\xb5`\xeb\xd3\u7640\x9e\x86Q\x80!\x89\xc2\xe3\x00%\a/oDY\xb50\x88\xb1[\xc18k\xb4x\xfe\xf9\x8a\xfcj\xfbB\x0e\xf0\x8bq\x8c\x10p\xc5(a\xf2\xaaD?DT\x03\v@c0\xa1`\x9bT\xc1\x11C\x0f\x1f\x9d\xbcC!\xb7N\xce.\xca\xcad*.(5N>\xaf\x02Uj\xb0C\xba\xe3H\"\xf0j\u05b5\f\x10\v\xe7\xaaH\xd5.&\xeb[\x80\r\u028e\xf0\xe6V\xd5\xcea\xd8J\x97B\x18r\xf4\xff\xae|Gl\xaea\xcb2B# \x02\v\x82*-\xb2%\r10\x91\xe7M!<\u067f\xef\xa9t\x9e\x83\x978\xd8u\xec6\xc9w\xa2Du\xe9\xcdZ\xc2\x10;\x1ac%\xd9~\u04baA\xb2\t\x1cO;\x94\xfb\x17T\xa7\xfe\xf5\x94\xccP\xda\x0f\x9b\x03\x87\u072b9\x11\xe9\x96>\xc0\x82R\x9f\x1a\xe1\ny:\xbf\x00E\xd5.\bP\xcd&t\xd1\u00d9\xc7\xefE\xb1\xb2\x80|\xd0E\x91w\xdds\x90\x02\xe3\x9d1\x8c\x8d\x8d\x1d\xb9\xed\xb67\xfd\xc5z1_?.\x98\xe3\xe7~\xee}\f\x00\x87\x0f\x1f\xbd\xe2\xe8\u0463\x1d\xf6pBhj\x98\r:\x13\x1b19\xb3\x13\xba\f9\x8c\x1c\xa5\xe2L\x02\xa4-xE\xc3H\r\x9b5\xc0\x02\u0436\xa2\x83\xc9\u04b1\"R9(%6\x1a\xb4*d\x99F\xa3\x15D#\xb1aw\xf1Wj\xd1\u062c\x86\x02@\x1c%\xfe\xf0|\xee\x96\xd5\xc8'%L\xab\x12\xca\f\xd3\x0fA\x1c\xe3\xc4D\xe9\xf0\xfeXP\xfc\x13\xa0\xc8J\\K\xca]\xcbMJA\xf2\u06a8\x80\x12\x06M\xa0\x04\r\xc3\xef\xc2\vx\x82\x9f;gT\x9d\nP\xa5\xf0\x04I{\xf01\xf1?'x\xca_a\xc0\xdaT\xe6QB@\xb0\x80\xea\x1a\xa8\xae\x87W\x9cK\xd8*\u07df\x00\x9b\x04\x13\x1c\x8e0\x8bM\xd01Nr\x908v\xc4Q\x1d\xed\xb9\xe3\xae+\xae\x1c\u0365e\xd8\xcc\xdb\xd2&\xbe@\xe4!!cl\f\fQ6\x14*\xaa\x99\x93\xa5a\u0615\n\xb6\x82\\l\xb0\xe9\x8d\x0e\xa3\xae)\x11$\xc1R@6Z\x00I\x9c~\xec\xbb8\xfb\xf4\xf7\xd1\xef\x9eG>\xe8\xc1\xe8\x12B\bXcx\xf3\xe69\xba\xfe\xfa\xeb\xe7\xf7\xee\xbd\xea\a\xeb\xc5|\xfd\xb8\xe0\x8e\x13'N\xbc\xf1\u0631\xe3U\xac[\xa0\x9e\xc1\xa23=\x87\xe9\x8d\x17\xc1\xe6eT\xf5\x81\xc8yZ\x04\xac\xb7`\x88\x15\r\u06f60M\xe1\xb0M\u007fO\"\xe7\n+\x8f\x85kh\x1aGk\x10\xcb\u04e4\xddx\xfb:o\xbb\x86\xbd[$\x9d.\xfb\xce\xd9ue,\xc8\x1b(\x11Ta\x81e \a`Z\"v\xe8<40\f\xb6\xa8j`A%GC)'\xf6\xf4~25o\xd9a\x80\x85VA8\x15\xf0\x9f<M\x9b\x90g\xc2j\x17\xe6\n\x89\xf3\xa5\xb0@\xe6\x19:\u0178\xb3\xff\x05[\xb06Il\x9a\xad\xba\xe6\x14\xdb\x0e\x10\x8b6Q\xfe\x1e\x8a\xac*\f\xb2\x15\x03*=\xbe\x1d}u\xbd\"\u0606\xe2]y42\xd7\x03#\xc2\xebm\x83\xa3\x0eW\xc3\xd0\u040d\x1b\x138\xe76\xd9\x121\xac\xb7\u0115\x02\xb0\xd2\x17x\xebY'\xd6:\f\u076f\u007f\x99u\xc5<\x18f\x85\x05L\xacN\xaf\xf6\x1f\t\xbfK\x10\"\xb0\xde!H\xfa\xbf\x0fb!\v\x92m\x90T8\xfd\xc4}8|\xef\x9dXY8\x8db\xd0s\xaaO\xb70p\xb3\u0664\xb9\xb9\xcdx\xf5\xab\xaf\x8d\u2f75\xf4(\x17\xc2!\xd6K\xdb\xcb\xe3x\u6667B\x91\xe8\xe4\xf9\xe0CeY&\xb6\xb9\xae\x1b\"\x12\x98\x9e\xb9\b\xe3\x93[a\xb5\xae\xb6\xd2\xecq\x14\xed}\xd0\r\x83rvI;%bP\x01iv\x1d\xed\xf0\xb6\x19\x89D\xbfFf\xa1\xb5\x91\x96\xe1\x82I\xb5\xf8\x9f\xb8P\x84\xc7J%Zd\x87\xa8\x8d\u059dWs\xd9@\xe664\xed#G\x97\"\xb7\x8eO\xceU\xc7\u01e9K\x17\x8f\xfa\xab\xe1\xd3\x1fAQ\xe4\xd1{\x11\xb2~\xa0\xca\u4564\xc1\x03\xc5?\xb0a\u023eFv>\aus\xe7\x17b\n\xa7\x03\xd0\x05\u0614\x0e\x13\x0f\xe6U\xb6\x8aV\xe3\x98.\x15\n\xbc\x85\xd0\x16\u064a\x86\xccMe`eS\x88\xc3e\xe0\u054a3\xb8\u05a9W\xde\xf8\xec\x98\x1f\xfe1l\x90\xe6\x1b\xeb\x04?6\xc9\xe9\xf4\xefQTezz'\xb4\x1brF\u007f\xfd$\xbfSZg\x95\x00\xcb0\x9e\xa9B\x00$\xeaA\xe0\x11\xff\xf6\xda\t!\xa4\x1f\xde\x10\x84\x94\xd1\x04(\xec\x84T\xbb\r\xa1$\xce>y?\x9e\xfb\u0397\xb0|\xe60\xcab\xe0\x14\x9f@p9\xa5\xd9\xd9Y\\s\xcd5O\xde~\xfb\a\xff\xaf\xf0h\x17j!_/\xe6/\xa3\xe3\xd2K/\a\x00|\xf3\x9b\u007f>\xf7\xcc3\a7\x17E\xeeY\x01\x0e^\xb0\xc6@eMl\x98\xdb\x05!\x9b\xce\x1b\xa3\xa6\xbeC\x84]\x9c5\x1eCu-\u050aF\xd8\xc1\xbb\xc1\xe7\x90\xe9v(\xe4r\xa8\xa3]\xa5\xe3\x1f\xa2f\xd4\xc484:\x95\xc7:>\xbbH\x1e/\xa6\xf2$\x18s\x18\x94\xca\u0722\xb9d\x90\rx5:O\xa1+w\x1dk\x8a\x92\b[Q\t\xa96~\x1cm S\xa3v\x0fS\xea\xb9>\xf4%\xeb-\x01<\x1f:\xf0\xf1\x85\xb6@Q\xba\\\u0322\x80X\x19 ;\u05c3\xe8\x0e\xbc\x87\x88\xb3\x8e\xb5Z;K\xc7\x00\x97p*\x87\xaf0hf\xebT\xac]\r\xd9\xf7\xf0\x8a\xef\xe6\x899\xc1\xc28\xc2'q\u01d6Z\xce&\x9dz\xbdsw\u007fk\xb5\xf1\xbeE\x1c\xb1\xed\xcaL\x8dc\xda\x12\xc0 c!Jw\xbe\xe4\x87\xc1\xe1\xe5\x91\f(\x1df\x16\x15\xfbH0U\x9e+)\xa7\xde\xffCx\x97S\xe7y \xdc\xe03(B\x05!k\x8d\x81m\x89\xe3?\xf8\x06\x0e}\xeb\vX>y\be\xdew\x8aO\x0e\xc6q\x12\xcdf\x13{\xf7^\x81w\xbe\xf3\x1d\x9f \xa2\x02\x00\xbe\xf8\xc5/\xac\xc3,\xeb\xc7\x05\xb4z\v\xf9\xe6s\xe7\xe6\x91\xe7E\xb4\xbc\r\x95\x85\x84\x84h4\x87\xe2\xba*\xa5%\x05\x01L\xa0\x8d0\xa0z\x06\x99\x00tK@\xe5\\\x85\x19\u0512\u042b\xa1\xd5jT\x85\x86\x18}k\tq\xea\xaaE$\x14\xf4X\xb8E:\u0324!%\xb7\x83\\dn\u0440\x06\x93\x84nz\x8d\xa0\u007f\rT\xe1\x15\xab\x11[q\xf0\x920Im\x16XM\x00\x1f\xb9\xb5\xa8o\xffk\u03c6\x91.3\xee|\r\xbb\x01.\x12Z\xa0\xe7\xbd[o\n%\xb4E\xa6\t\xb6C.B\xcfw\xf6\x8c49\x89+\xd1&\u06da\x8dm\x96[\xa8\xdcQF\xbd\x02?\x89\xacOv\"\xf1g\xc904\xe0\xf3\xb1\x90\aY|\b\u00a6\u02b66\fJ-j\xf0J\x94\xeb\xa3R_*r\x81\x1e\x9a\xaa\xe1\xabd\x82\nj\xe1\xd4R:\b\x8a\x82\xc68}\x9e\xd5\b\xb6\n\a\xf7\x8b\x8c\x90\nY\xd6\x06I\x81\xe5s\xc7q\xe4\x89{p\xf2\xc9{Q\xf6\xbb1\f\u00c5\x9a\x13\xa4\x92`\u02f8\xea\xaa}x\xdd\xeb^\xf7\x1f\xdf\xfd\xee\xf7|\x15\x00>\xfa\u044f\xd2\xfb\xdew;_\xd0\xd7\xf6zy{y\x1dKKKse\xa9\xfdE\x97\x94 \"Xk0\xc8W\xc0#:M\x1f\xc1\xe8qj\xcf\x06\xf0[\xe5F\u03c5\xfe\x8a\xe8Y\xee\xf1KOidO\xfe%\x1e\xeaVGu\xdb\xc35\x91\xab\x01e\xed\a4\fkT\nS\b\xaf\xe7\xf6\u007f\xc7IJ\x0e\x19\x86\x1cX4\x96-T\x81\xc8[\x17\x9a!\xbb\x9e\xc1\x82j\xb8'-Cr\xe2OO\xcf\u007f\xb2\\\u06d2\x8c(\xf3\xf1iT\u07be\u0387D\x83\x8b\x01l\xeeS\xeaKO',5\x10%\xf4\x16\"7h,\x97\xceo\xdc\x06/\x14\x93\xb0M\xc2\xe3\xa4\xee\x86\x16j\xa0\xa1r\xebw\x19\x1c\xe1\xa7t\xf7\xe5\xfcTL\x02\xa7\xd8X<\x81\x14\x8e\xe1\u029b\xc5T\xd0\xca0\x9eN\x1e\u06a9Ac\xf0\xec&[9-\x8a\x84E#\xe0\n\xb9\xb4\x9ez\xe8\xc3]\x04Q\xfc\x8aiP\\}v)\x11\x84!\x0e\xb9%\xb2\xac\x05\x10ci\xe1\b\x0e\xfd\xcd\xd7\xf1\xc87?\x8b#\u007f\xf3M\xe8\xbc\x0f\xa12\x17\u007fH\x0e\x8e\x91J\xc1\x1a\x8b\u077bw\xe3\xcdo~\xd3\xe3\xff\xe2_\xfc\xef\xff\f\x00~\xe6g~\x9a\xde\xf2\x96\xdb\xf8B\xbf\xb6\xd7;\xf3\x97]g^\x850\xd7\n\x8d\x10\xb0\xb6\u0120\xb7\xe8\xd3}j\x16\x84C\x05\x98\x12\xcbU_\xd0KS\xf3\xbd\x88\x05\vU\xe8A\xed\xf1hDXD\x8d\x88\x9d\fCiD\xe7;\xb2)\xa6\x8a\xd1\x12\\\xbah(\x17\xde\xe3\xdf2\xb7h,i\xe4S.(Z\r,dak\xc3Z\xb2n\x0e\x00\xfc(Y\x04#h\x97C;\x8d H\x8aV\xae\xa1\u02e5*\x12-(&#VM\x95\x0fwX\x91d\xc1hXF9&\xa0\x9bb\u0235r\xa8#f@\xe5\xce\xf0L\x1a$\x90T:\u04f0C\xe9=~Qb\x8eC\xc7\b\xa9p\n{P\xb4\x92\x8d\xae\x87\xa8\xbcpl(\xe4\xfe\xef*Q\x16\x92\xa1)A\xb1c\x9e\x18\xe1(\x88\x8a\xadw\x00\xa0\xca?\xc5&Cu\xaa\xad\xe4N\xd1\t\xf6\xbeA~\xc1\x90\x19\u02a2\x8f3G\x1f\xc6\xfc\u0267\xb04\u007f\x04\xcbg\x8e\xc0Z\x03\xa9\x1a\xee:\x00\xc1J\x05\x995`m\u0266,h\xc7\xce\x1d\xb8\xf1\xc6\x1b\x9e\xfc\x95_\xf9\x95\x9f%\xa2%\x00\xb8\xe5\x96[\xf8\xa7~\ua9f1^\xcc\u05cf\v\xeah\xb7\xdbOeY\x06)E\x15\x1b\xe7\xc4\x11`mP\xf4V\x92Z\x1a\xe8p\x14)^\x84\xaaH\x06\x95\x0e\x99\xca\xe6\xd6J\xbf\xe5\x0eb\x11/\xf4\x11\xb6Nba^\vv\xa6\xd1?\xe7\xa1\xda\xe8\u04cdyDkO\u05a9P\x8d\f9\x90ud\xc4\xd5s\u03eb\x16\f\xd3\x10\x90=\xafX\xb5\xc9I\xfa\xe7\x14\xec|\xe3\x02E\xc3\u0388\x8cQ\x88z=\xc0\xc4\x15<\x1b\x14\x96\xa8\xa0\v\x88\n\x1a\x89\u007f\xe8\x17\xb6\xb8\x96Q=\xf8Zj\x06y\xb5\xa8n\x8a\xdaZRQ\x16\x1927\xce\u007f\u0778!v\xf5<\xb8\xce\xebDU`+\xf6\x89\x8d\xafU\u0569W\"*\x8e\v\xa8\xf5b\x1fNT\xa9\xd503x\xa8\xb07\xacw\v\x96\xcb+\r\xcfI\xb2\xc3\xc33\xdfp\xb0\x17\x1a\xc1CI\x91zjy\xd5\xce\xd1c_\x11Z#!Q\xe6=\x1c;\xf0=\x1c;p/\xfa+\xf3\xaeOWM\xa8\x84\x11\xc3\u0110RA\xaa\x06\x0f\xfa+\u0632u+\xae\xbf\xee\xfa'\u007f\xf1\x17\xff\xe1{w\xef\xbe\xf4q\x004?\u007f\x86gff_\x12\xd7\xf6z1\u007f\x99\x1d\u059a\xefl\u06b4\x11Y\xd6\xf0\xb8y\u0569\x1b[\xa2\xe8\xaf\xc0\x98\x12\x14$\xd0q\xe7JUWe\xd9\x19MyN\xb7cdT]\x19+D\xae4\xfb!\x1fE\xee\xe2\v\xb4\xb8<\xe4}\x9b0Ix\xa8\u00a7!\fq\xdc\x19\xdc\x15\xb5ss4\xa2:\xe7\xaa\xe5\xe6\xb8-\u03fa\x16Y\x8f\xeb\x186\rm\x0eR\x1d=\x85x\xbbZ\xd4r\r\xcbO\xb2#+\xfc\x98\x11=Ol\xe899Y\x1b\x12ug\x8a\xabGn\xb9q\x11u\x10\x15\xebD2\xd0\xec\x1a\b\xcb([n\xb7D\\A\"r`\x90\r\x8c{\xaflE\x19\r\x90S\x85.\xa36\u8904\xc1b\xb9\xf2\x19\xb7\xde3%\r\xa7H$\xa2\xfe\xed\xe3jw\x10\x96\x8a`\x9aOT\xb1g\xa8Z\xf6\xd8r\xe5;\xceT\xc9\xf1}\xe1%\"? v\xb4\u01b8\x03\xe1\n\xdc\x12\x14\x18Y\x0e_[8u\x00\u01df\xb9\x1fy\u007f\t\xb2\xd1Nf#\xfe~\x02\x14D\x84R[\xda4;\x87\xd7\xdet\xe3\x93\xff\xf4\x9f\xfc\xc6{o\xbc\xe9\x96\xc7\x01Px\xec\x97\u032e{\xbd\xbc\xbd<\x8e\xa7\x9f~\x02\x00\U00016dfc}qvv\xf3q)e\x92\x14C\xb1\x9b\uebdcG\xde_\xae\"w\x12\x8b\xbd*\u6362\xbc^\x98P$\xd8[\xbc\xfad\x1a\u02f5b\x94\x0eBG\x17\xf1\x14\u007f\x19\xfe9\x8d\xbei\x8aj\xac\xc6x@\x06\x90E\xa5\x10\r\xe7\xe5,\x00\xdc\xffe\xc9P\xb9\xb7\u007fM\xf3\xe2\x14\xd5t\xfeU\x97]\x89cj\xcf\xc6\x17\x13k\n\x18=\x80\xd19\xb4\xcea\xb4\x0f/6\x05\xac)amj\x1f[%\xe7 \xc2\xd1\\1R\x12<\x1b\x9e\xe1\"K\x860\xde\xe1P[\xc8\u0720\xb1T\xa2\xd1\xd5 cb\xa7-\n\x83\xacg\x1c[\xc4\xdfV$y\x9a\x96\x13\u02a2\xef\xc2\x19)\xab%\x04){\xcfr\x9b\xc07IW\xcf\xdes%(b\x839\x96+\x84\"R_\xab]C\xaa\u016c\x16\u02cc\x05T\xf0h`\xef\xcb\x12>k\xe1M\xf1l\x15\x11\xfe/\x84\xf3V\xa1\xa4+\x97\x12\xba\xe8\xe1\xec\xd1G\xd1[\x99w\x8d\x8a\xad\xf2=\xd97\v\x82\x84\v\xea\xd6\x1a\x17\xed\u0709\xf7\xbd\xef\xfd+\xff\xe1?\xfc\xeeo\xf9B\x8e;\xef\xbc\x13ke\x12\xac\x17\xf3\xf5\xe3\xc7z\x84\b=\"Z9}\xfa\xf4\x97\x01\x80|vg\xca\xd3-\a]\xe4\u0765\xea\x02\x1a\u00a9E\x9a>\x13\vd\xbd\x8c\x86\xa81w-\xd2\b~\xf9\x1a\x85\x9c\u05c0\xa1\x87\xb0sZ\x033\xafq\u0343J0$\x01\x05\x9es\u02b6\xb1\x15O=\xdc\x16\xd6eDj\xe9%4f\x88#\x9e\xbc$A1\xca X\x1f\x00lt\xe1\xa4\xf5)4\x118\xd8<\xe42\x98b\xf95\nPb`\x1537\xabs$\xed\xa9\x8b\xda\x15[*-\xb2e\x8d\xac\xeby\xe6\xda\x19\x84\x91\xf6\xd2zckB.$E\x9c\xb9\x12\x04\x85\x02n\xbc\xd0'\x0e@=\xc6]\xa9wiUh\x84e\xaei\xc3\u023b\x18V\x8aYJ0o\x11?\x1b\x10\x02\x12\x14\x13\x80d0|K\x06\u035c\xe0\xe5B\b\xa8F\x1b2kA\b\xe9<\xf9\x93\x0e^\xaa\f\xc5`\x19K\vG=MQ\xc4\xddN\x1c\xce\xfa]\x811\x06Zk\xbc\u136f\xc7o\xfe\xe6o\x1c\u0771}\xfb\xb7\u00a3\xbe\xf3\x9d\xef\xe4\x97\xda5\xbe^\xcc_\x86\u01ee\u077bt\xa6T\x8d\xbf\x1b\x14r\xbap\xd1Y\xa8\rI\xb9\x06\xaf\n\x06\x84\xa5\xa1`\x06\xaa\x15R2\x88E \xd2\xe5\x80\xe7/\xecT\x83\x9e\xd7f\xba\xa4\xb5/\xfa\x9b\xd4\rvca\t]\xb9\xa5\u06a2\x11\n{0\x80\n\t4\xc4\f\xa3\x00\xad\x9c`\x87-\xaf\xe9$\x93\x9e\x905\xa5\xe7*\xdb\x1a%p\xd4*U\x1f\x89\xa6\ue0e8\u007f\x9f\xf2\xb3\xfdW\xe0\x8f\xbb<K\x1b\xbfH[g\u05fbRB\xf5J\x88\xdcq\xcf\xc3b@\xe0$\xe8\xc1\xc6\u0162V\x90\xa3}m\x85\x91\x10\x8d\xe0\xd4sE[\xa4\xa1.\xbbF\u4a7d\x06\x14\xf38\x1d\xc5\xd3\xf9\xcd;\xe4\xcd\xdb,\xf8\xe7\xc5\t\x03I\xf8BO\x00\xa4g\x9f,/\x1c\xc3\xca\xe2\t\xb7\x03!\u007f\vA\xb1S\x0f\x85;\x18j\x11\x11d\x10\x13\x81\xfc{\xca.\x88\x9c\x19\xa7N\x9e\xc4\xe9\u04e7\xce\x11\xd1\"\x00\xdc\xfd\xe7w\xbf$\xaf\xebu\xcc\xfcex\xccl\xdc(\x85\x14\xae HT\xa0\x817\xe9\xd7e^\xebr\xabb\xc6\xd5\x169\xe5w\x0f\tc\xaa\xe1\xa7/W\"\x91\xe1'^0\xa3\x8a4\xa5\x83<Z\xa3cO\xb1t\x1e\u075eW1g\x1c\xe3\xce(\xca\xed+\xa0d\xb8\xdb\x176\x14\x15D\xbb\x80a\xae;'-\xa8\x83V\xb4Sd\x06\xe5$\x0f+A\xa9f\x80\x85\x11i7\xa9\x1dT\xcc\xe6Ko\x1b\xbf\xb5U\x97\x19^\xaf\xe0\xf3^22\xe3\x1d\f\x93\x1d\x87+\xd4\t[\xd3\xff\xb7\xe6G\xce\t\f\xc1\x0e\xd7G\x12\xe5VS\x8a&&\xeaL\x15N\x9e\xf2\x86\\\x13/\"\xf6\x1en\v\x0f\xb7\xc5b\x9d2k\x02\\\x15\xfc\xc7\xc9Q\x14\x89\b\xb2\xd1\x04\xdb\x12\xc7\x0e|\x0f\u01df\u074f\xd6\xd8\x14.\xbe\xe2\xf5\x98\x9e\xbd\x04\xe4\\\xdd\\\u0291)16\xb1\x11[.~\x15\x9e{\xe2\x1e\xe8\xb2\xef\xe4\xfc\xc2\x17\xfd\x00\u01d0\v\xc6&b|\xef\xaf\xef\xc1\xefg<\xcd\xcc3D4\xff\u05b7\xbc\xf5%y]\xafw\xe6/\x93\xe3\xfe\xfb\xef\x8d\xdf\xcf\xcf\xcf\xeb\xb2\xd4U\xed#\x8a[h\xaa\r\x1c\xab|E\x1eQT\xe3P12\x18\xeaTF\xc1\xf5\"\xbe\xa6\xce&)\x00\xe9u=\x12nI\x19\x1f\u00eb\b\x11\xb8\x9eP\xba\xeaN\xa8v\xfbp\x17\xf5T\x1b*\u0639\x16\"2\xf7\x86\xa0\x960\xb830\xba\x841\xa5\x0f\xce\xe3j'R{\xd2\u0460v\xcdn\x1d\xa8\v\x8a\xe2\v\x91\xc2\xf2\xb18'L\x98p[\x1f\x80,4 KD\xa1\x13\x12(\x87\x13;,\x8e9\xa9\x9e\x85\x14\xbab\u02f0\u01b1n\xac1\xee{c\xc0~\x97\xe2d\xf7u\x1ac\xf0\u008f\xe2\x1e\xcf\a\x8f\x9e\xf3\xfe\u70bcca:\xe6\xb0u\x8b`\"\x02K\t\b\x01\x05\x01)\x15\xb2\xe6\x18t\xd1\xc3\xc1G\xbf\x81\x03\x0f\u007f\r\vg\x0eb\xe1\xf4!\xac,\x9d\x02I\a\xb3\xd8\xe8\xa9n\x905Z\xd8u\xd5\xebq\u054d\xef\xc7\xc5W\xbe\x0e\xd3s\xbb!\xb3\xa6\x87\xc0l\xdcG\x18k`u\x8eS'\x8fb\xff\xfe\xfb\xf7\xfd\xfb\u007f\xff;\xbf\xf0R\xbe\xc6\xd7;\xf3\x97\xc9\xf1\xb9\xcf}.~\u007f\xe2\xc4\xc9+\x8b\xa2p\x17\x90\xa8\x04@\xbe\x1f\x8a\x98fZ\xfdhT~dr\x1b&W\xb8\xb9\xc6Cv,\x05\xf6\xdd\x15q\x02\x81\x8c\xf07\xe7\xc4X\x8bW7\u0135`\x86\x91\xb5\xbe\x96\xe2\\7\xa6eJ`\vB\x8d\xeaG\x89\x1a\x95\x85\x8f\x1f\xd3\x1e/\x0f\xf7FTu\xdb$\xc0!\x8d\xc6\x18\xdf\xf5s\u0176\xa1d\x87\x92\x9eR-]\x82\x86^N\xaa\xbd0\x9c\xa4,E>w\xb4\xa2Mp\xf7\b\xb3se\xcb\uba60qTKCV\xbdT1X\xd2\xf3\x8c\xbc\xf7\xf8\x8es\x92\x9b\u0249i\xa5\x0f\xd3\x18\x9ac\x04\xdex\xfc>\xb0A\x98*o\x14f\xefxY\x050\x13\x10#\xfe,\x18\x86\x80\xa6\x90h\x90\x04\f\xe3\xfc\xd9g\xf1\xdc\xe3\u007f\x89SG\x1f\x8a\x9d\xf6\xd4\xccE\xd80{i\xf2\xdcS\x96\x92E\xa39\x8e\xed{\xae\xc7\xdc\xc5\u05e0,{XY>\x8d#\x8f\u0743S\x87\x1ev]:\t\x94\xf9\x00\xf9\xa0\v\x80\xb1\xb0\xb0\x80o}\xeb\xdb\xefd\xe6\xdf'\xa2\xe2\x91G\x1e\xc2\xd5W\xbfb\xbd\x98\xaf\x1f\x17\xd6\xf1\xd8c\x0fc\u07fek\x00\x00\xf7\xdc\xf3\xad7\xfc\xd6o\xfd\xf37\xe5y\xee/t\x91\\\xb6\f\xa92\b\u0568\xbalN\xbd\xbb+\xd3\xff8\x90d\x97\\\u00de_\xee\xac\aQ\xf3\x05!\xeb xJ\xf0\xea\xb5\xfc\u0369V\xd8\xeb\xf8\xf7\xea\f\xcc\xd5\xeeU4\xac\xbe\x8c\x9cw\xacR\xfe\x04?tG\xa3s\x85\x9c\x05\"/\x9d\x86r\x90+*\xa4\x81\xb1%\xac\xd1\xd5\xd9\xc7\x13\xe6zd\x1b\xf1*J\xe5\xf0s\xae\xadp\x91\xceX\x9d;\xa7\u06e4\x14~\x11\xe9\x16\xdb\xdf6P3\xadO\xeb\x11\x04\x96\u038dP\xd8*$48\x15F\xffr\x0e\x83lJ\xe8\x96\xee\u0770\x81R\xe8?\fb\x84\xb7\x0e{\xf6\ty\a\xcd`\xaf\\\xadU\t/?\xf8\xa5\x84\xb5-\fO\t\x10J\xa23\xd1F\xc3\x02\xfd3\xa7q\xec\xc0~\x1c{\xe6~\xac,\x1e\a[\x83\xe6\xd84\xb6\xef\xbe\x0e\x17_\xf9\x064\xc76@\x1b\x97\b\x94f\xd9\x02\"F\xd7\t\x99\xa1\xdd\u0684\x89\xd9\xed\x18\x9f\x9aA1\xe8b\xfe\xf8\x01W\u030b>\xac. \x84\xc0\xe2\xe2\x12\u039c9u\xe5\x9dw~\xf5\x16\x00\u007f\xf9\x8do|c\xcd}\xe4z1_?~lG(\xe4\x00p\xf7\xddw\u007f\xfc\u0631cY^\x14P\xaa\xe1\x99\x03\x14/\xeaFk\x1c\xad\xb1\xa9*\xf1<\u059dJ\x91I$\x12\xf1\x90\xff\x9d\xf2L\x84P`5\x83J\vmC\xf8\xf2*,au!g\xd4\xc53k^M<\xac\xea\xc7\xda\x0e\x8c\xf5\x01f*:r)>\x04\xf6\x90\x04\xab*/\x94=\x16\r\x93\fm}\xf2<\x9b\u0485\x1a{\xe1I\xec\xa4y\b\u02e7zi\\\xf3\xd9$\x90IM\x12\x13\xc9\xdau\xef\xf6T\x94D\x02qH[\x87\xa3\xb8\xf6\u007f\x8a\xef\x1dy=PJ\xd7\xf34D\xaf8\r\x81\x0f\x15\x86\x9e<\xbf\x04.\xa2\xaa\xf7O\x05\xa7q\x19\xa0!\xc0)\x8a\u0390\x84H\xc0\xc7\xc4\tBc\xac\r\xd9\x16\xc8\xcb\xf38v\xe01\x1c~\xe8\x1e,\xce\x1f\x81.\xfbPY\x1b\x1b\xb6]\x89\x8b\xf7\xbe\x1e\xb3\xdb\xf7A\xc8\f\xba\x1cT\x8c\"F\xa2\xf8\r\x90\xa1\x1bp\x9ab\x00A@gr36\xcc\xed\u00b9\x13\xcf\xc4YG`\xc1\x94e\xa9\x1b\x8d\xe6\x96#G\x8e\xbe\x1d\xc0_\xf6\xfb\xfd\xf5b\xbe~\\X\u01d93'0;\xebl;\u007f\xf0\x83\xfdo\xff\xc4'\xfe\xf9O\x9e=;\x0f!$\x93\x10DB\u0536\xf3\xad\xce\x06\xb4;\x1bk\x8c\x860\x8f\v[k\xf77\xbe\x83\x16\x04\x92@&\tR\x917\xa9fP\u18fd4\xfb\x82>T\u022b\xfd\xfbPGMC\x9d\xf0j\xf8\xbb\x12\x86\xd7d\x93@m\x81I1\xee\u0564\x9bXtd\xf5\x05fp\xe1=kb\xec}T\xb3\xb8A\xa7\xd5\x15?;Q\xb3\xd6 \x13\xa2\xd50\u03ea\x85\x86*\xc9<W\x81\xc8\xe9\xb3\u4d10'\xf8yjW\xe3\xd6\\N|\xd0\xfd+\x14\xd7%\x02Y[\xbd_\xc1\x13%\xb0X\xac\xad-\x85\x9c,0\xd5\x0e\x01\t|bk\x8b\x10\xa7p\xcf(\x9f_\xae\xa3[\xa8\x911\t*k \x93\r\b\xa1\x91\x0f\xce\xe2\xd4\xe1\x838\xf6\xf4\xf71\u007f\xfc\x19\x14\xfd\x15\x10\x11&\xa6\xb7a\xdb%\xd7a\xc7e7\xa339\v\xads\xe8r\xe0\xeeEP\x9c\xcfT\xf7\u02c8\xbc\x19\x87\xe1@@\xa2\xb7|\x16K\xf3\xc7\\T\x9c5\xc3\xef\x8a-\xcb\x12\xa7O\x9f\x19\x03\x80s\xe7\x16\xd61\xf3\xf5\xe3\xc2:B!\a\x80/}\xe9\xcb\x1f=x\xf0P\xa3\xd7\xebCe\x19\xa5!\u0396\x9d\x05\xee\xe4\xc6\x1dh\xb4\xc6at\x19\u0558iWK!7\x11\x95\x9b\xa2R\x84L\x02\x94\t\xf0\xa4t\xe1\x15K\x1al}\xea<\x11J\xc3q\x98:*h\xad2\xac\xad\xeb*k\x1du\xf0\xf7`\xc2\xc8\x14\b\xc2PU\x1dz\x9c\xa1\xa2.\bPd\x80\x16\x81\xdb\x04\xd1\xf79\x98p\xae\xb2a\xb1r\u007f\xc70TQ\xfa\xa2w\x8d]#\xab\x82Fd\xd4\xf1P\xe7L\\\xa3\b\xa6\xb84\xa7.\x88\x9c.\x1a\xd5+\x13\x99$\x82`\x85w\x17\xf41w!e\x87\u0233\x8a\xac\xe3\x82S\xc26J\xef8\xfa\xb9\x87$\xb7\x14\xf2\xaa1U\xfcp\u04cf|\xeb)\u054888\xfb@\u620bK\xbf\x9b#\x01H\t\x91I\x186(V\xcecq\xe1)\xac\x9c{\x16\xa7\x8f=\x8e\x85S\xcfA\xe7}0[t&7an\xd75\u0636\xeb:L\xcf\xee\x01\x84DY\xf6\u076e(\xb2\x96\xc4\xd0\\b\xf5\x9a\"d\x06\x06\xe3\xf8\x81\aq\xee\xe4!\u05cc\xb0M\x82\xcc}<\x9d\x94\x90R\xf8\xef\xc5z1_?.\xac\xe3\xfb\u07ff\x0f\xd7]w\x03\x0e\x1cx\xf2\xda_\xfe\u53fc\xfa\u0529S\x90R0\x91 \"\x17f\xcb\u0582\xb5Fkr\x16\x9bw^\u5de7v\x15\xb2\x11\xe8fQ\xf2.\xdc\x00UJ\x82\x10\x04j\x10\xb8-\x80\x16\x9c\xea\x10\xec.6\xed\xca@\x99fa\"\x8d\xfa\xa9\xa3\xe1\\A\xab5\x98\xb8\xda\xc0s\x95(3t~\xb4\xc6\xf04\x14\xbf\U0003d400\x94\x06R\x02\x18\x03l\a \xc9`\xcd\xe0\x9c<\x06LU\x19\xe3\xba#\xa2\xc3\u05a9V\xc4R\f\xbf\x86\xb1\xc7\xf3I\x02\xd6<\x0f\x9a\x92\xbf\x8b\u007fS\x1b\x84\xa6\x88QB\x89\x1c\xde\xd3\b\x00\x8a\xa2\xd25.\x8b>)\xca\xd6\xc47\x1c9\xe2\xd5\x1e \xb1\xb3M\x9eD\x05\xb3\xd8U0\v\x00_\u0607\x86\xcc\xe4\x8al\xe0}\x93T`\xab\x91\xf7\x97\xd0\xeb\xcec0X\xc4`\xb0\x84\xc1\xca9\xf4\x97Nc\xf9\xec\xb3\xe8.\x9e\x811\x06D\x02\xad\xce\x04f\xf7\\\x8d\x8b\xf7\u0744\xd9\xd9\xcb!\xd1F\xbf\u06c3\u0579\xf3\x9d\x17\"IY\rV\xb7\x15k&\xce\x1a\xbc\nU\xca\f+\x8b\xa7p\xf4\xc0}\xd0\xc5\x00\xaa\xd1r\xf1yBF\x16\x8e\x14\xa4\xb2,\u00e6M\x9bN\x03\xc0\xf4\xf44\xaf\x17\xf3\xf5\xe3\x82:\xae\xbb\xee\x06\x00\xc0\x1f\xfd\xd1g\u007f\xe6\xf4\xe9\xd3[\xbb++Ve\rA\xe1B\v\xc3(v\x91q[.\xfe\x89(|\xa9\xa9@\x13\xa3\xadt\xa8%\x04A\t\xf7\u007fd\xe4\u0094\x05\x01S2F\xac3\x18\x8a\xad\xcb\v5\x95!\xd3\u8ac5V\x99$2\xd7\xfb\xf6\x80\xe9bx\x00J\x18\n\x19N;K\xf7#K\x00\tF&\x18\x92\x18\xdc\x04\xb8I\xee\xfb\x16\xc0\xe3\xe4\u0515\xc6\x0fD}A\xaf\x89z\xbc\x83!%])s\xe54\x99\x92\x13+\xb7\xc6\x04\x13\u786e\x9b\xea\xdd\xf1\b\x14\xaaF+$$\xb3\tA\x90\u0085X\vE\xdew^\xban\u0738\x9d\x86\r\xa6VBx\xa5'\xd5\x17\x87\U0003ace3\x1e\xa6\xf1l\x95\x1a\xb5\x1e$\x1d\x19$\u0273$\x92\x90B\xc5\xfd\x85\xd1\x03,\x9d=\x8c\x85\xb3\x87\xb08\u007f\x18\xfd\x95s(\x06\xcb(\xf2\x15\x94\x83\x15\x98r\xe0\f\xb7T\x06\xa9\x9a\xe8LOc\xf3%\xfb\xb0u\xefO`\xe3\xae=\x18\x9b\xd8\b\xbd8@\xb1\xd0s\u063a\x10u\x1dAt`LX5\xbe\x90W#\x1e\xb7\x8b<s\xf41t\x97\u0382\x84\x84\x14\x02\xa0\u0327\x12\xb9\x1b6[m\xa1\x94Z\xb8\xfa\xea\xab\xef\a\x80\xad[\xb7\xadw\xe6\xeb\u01c5s\xfc\xf5_\xdf#n\xb9\xe5u\x96\x99w\xfd\xfc\xcf\u007f\xe8\xa7O\x9e<\t\xa5\x94!!D4\xd8b\x97\xec.T\x86\xb9\x8b_\x89\x89\x8d\xdb\\|\x96\xf5\x85\x83\xd2B\x85j`\x1a\xb6\xe5\xd2s\x95\xa5\x005\x85\xb7je\xa0)\x80I\x99\x10\xb5\x9d#\x1e\xb3\x830\xb8\x16\xceP\xa1\x9d\xab\x80\xedT9Z\xc3\xdc\xebc\u0151~H~\xa2\x1ay\xe1\xfe\ue534\x90\x12 \x05`\x8c\x00U\xc1%\xd4\x06`\bb\x89A\x85\xb3f\x85\x00\xac\xf5FV\x94\xac0\x89\xf5\xe3*\x1b\x8f!\x8b\x82\u0618\xdb\xcab\x96\xbc\x83`2MD]\xd2?\xf4|\x89jCG\x10AH\x82P\x80Pn!\xa5\xb6\x04\x8dI\xa0g\xc0]\xed^y\xe9\x829X\x00\xd0\x02\xd6$+Qub\xf5\xedP\f\xb3N\a\xae\\-\\\xe1y\v\xd7\xf9\x86\u075c\xd1\x05\x06\xdds8}\xecQ\x9c9\xf6\bV\u039f@\u007fe\x1ey\u007f\t\xd6\x1aW\xf0U\x13*k\xa21\xbe\x01\u0371\x0e\xa6\xb7\\\x84\u037b\xf6b\xd3\xce\xcb0\xb6a3\x1a\xed\x0e\xac1(z]\xf7\x1e5\x04l\xe1\xc3:l}\x98\xbb:\xc9))\xf4\xfe6\xba,0\u007f\xf2\x80\xeb\u02b3\xa6S\x89Z\x1d\xbdc\xb4\xd6\u0632e\x0e\x13\x13\x13\xfbo\xb9\xe5uw\x01\xc0\xa5\x97\xee^\xef\xcc\u05cf\v\xe3`f|\xe8C\x1f \x00\xf8W\xff\xea_\xfe\xe4\x93O>\xb9\xf7\u0739\x054\x1aM\x05\x12\xae\n\x03Q\xbe\u0759\u070c=\xaf|;\xa4j\xa2,\xfa5\x17\xabp\u0344n>:\x0f\n\x17\xc5\x05\"\xa0\xe1\xbejT\xb4\t\xe5\xfc7\x16\xb5\xdf\x0232\xaf.\x8c\xb8\xf7*`\xbc\x8e=\xd7\vI\xa0\xb2\r\x03\x19\xcf\x17\x0e]\xdd5\x03P\xc4P~\xe0\xc9m\x80[\x89\"2\xc0\x1b\x1dw\xfe\xbc\b\xd8\x02\x10R\xb8\x8a\ue855\xe8\xefB\xe9yP\x05]$\xbcm\xa4\x8cE\xae\xf4\xa7\x18\x82\xd6\u04c4{\f\xdd\xf5j\xa6\x8a{>R\x02J\x12\xa0\xdc\xc2$[\x12bB\x82\x1b.\x9c\xc32\x83z\xda{\x98\x93\xe3\xfb\x03\xd0\xcc~qJ^_\x1b\x98\xf2\xc2u\xc1CR\\w[\x0fn\xb1\x05\t\xef\x03n\f\x06\xbdE\xe4\xfd%t\x97N\xe2\u0729\xa7\xb1p\xea),\x9d;\x86\xbc\xbf\x04!$\x9a\xedILo\xbe\x04Yk\x02\x8d\xf68:\u04db0>\xbb\x19\xe3s[0\xb1i\x0e\xad\x89\rh\xb4\u0690B\xc1\f\n\xe8\xfe\xc0\xc1t\x1e\u02b3\u02a9\x88a*\xb7E\x11\xb8\xae\t\xf54\x8d\x90\x8b\x88\x94\x90(\xfb]\fV\x16\x9c\xddB\xd6\xf0\xe2(\x1bEf\u030c\x99\x99Mx\xfd\xeb_\xf7\x9d/\u007f\xf9\xcf\x00\x00\xb7\xde\xfa\x86\xf5\xce|\xfd\xb80\x8e\xaf~\xf5\xcf\xc4\xe7>\xf7\x05\xc3\xcc3\x1f\xf8\xc0\xed?\xfb\xdcs\x87!\xa5t\xad\x91\xc7\n\x9dO\x87\x01\x83\xb1\xf3\xf2\xd7b\xeb\xaeWE'\xbf:\xd4\xe1\xbaRA\xd2\x0fL\xfd\xc6\xdb\xc3+\x9cB,i?#\\A\x87\xf7\u0486e\b\x16\xc8\x04P\x94\xb6\x8a.C\xbd\v\xac\xb3X\x86\v\xf6\xa8\xf8\x9e\x14?\xe6\u06b6\xbb\x86\xf9\x13 \x84\xf7\x0fo\x00\u0726J\xab\x13 \x8fP\x14\xc6\x00a\b\xb4L\b\xc4\x1e\x04\x9ae-\x0e\x82W[\x9b\xd30F>\xb4 \xd10\xb0N5f\b\rC\xed\xb12U\xab\x9f 89z\x80X\x1a\x02\xd4\x11\xee}\xb0\x8efI\x1d\xe9\x1ds\r\xc0\x06V\xfb\x9cSE\xd0\xc6\x11:\"\x84\"$B\xb5w6\xb4aA\xb2\xa8\xf3\x87\b\xa4$L\x99cq\xfe9,\x9e;\x823\xc7\x1e\xc1\xe2\xd9gQ\f\x96\xa1\x8b>\x88\b\xad\xb1\r\x98\x9e\u074d\x89\r\xdb19s\x11&gw\xa1=\xb5\t\xcd\xf16\xb2\x8e\x82\x1c\xcf \x9a\xca-\x1a\xd6\xc2\xda\x12\xe5\xa0\v\u03ad\xe3\xbb3`u\x12&\xad\x84O|\xaaXU\x11+G\x02\xbf\x90\x83\xc6\u020b\x94(\u0495\xfc\x1cDkg\xfef\xc3\x02l\xd1\xe9\x8ca\u02d6-\xa7?\xf2\x91_\xb9\xeb\x1f\xff\u33fdd\xaf\xf9\xf5b\xfe\xbf\xe8\xf1\xd0C\x0f)\x00\xff\x1f{o\x1ak\xd9u\x9d\x89}k\xed}\u03bdo\xac\xc7*\xb2XU,\x92\")Q\xd4\u040eE[\x92GIv \xc5v\xcb\xee\x8e\xed\xb4\xe1\xb8\xe18v\x8c\xfe\xd5\b\x108H#\x06\xd2H\xfe\x04n\xa3\xff\x04H\x1b\xe8\xd8\b:A#\xed\x8ch\xb8\xd1vK\xeeA\xb2-Y\x94<\xc9\x16\xe7\xa9X\xa48W\xd5\x1b\xefp\xce\xde+?\xd6\x1e\xcf=\xaf\xa8?,J\uaec1G\xbeW\xf5\xea\xbd\xfb\xce;g\xed\xb5\xbf\xf5\r\xcb\xdf\xfc\xcd\u007f\xfc\xd1\x1b7n|\xea\u06b5kh\xdb\t\xeb\x8d\u0369\xb8\x88w\xd8\u07bb\x80\xf7~\xf7\xdfD;\xdd\xc1r~\x98\vbY\b\x99\xf5\x81\x8f\x10\v)F\xab\xc5\x04\x90\x86\xebf5V\"\x03\u040e\x85\xf49\x1f\xd4B\xedG\xbb\x90\xbc^\x0e\xcfV(\x84i\xf0\x9a\a\x82\xe5\xc00\xe7\xc1Q\xfd\xba\x87\xfe'\x00\x98\x04lD\xef\xfa\xa9n@\xa7A#D\x00m\t\f\x04r\f\xf4\xc1\x00\x8a\xca\xd3\x01\xc9\n\xf7;\xa6\x1d\t\x95HF1<-R\x84ra\xafs8+\x9c\xbc\n\x80\xce\xc1\xc4\xd62\xd8\xe8\x06\u028d\xc2+\x98h\x91J\xa7\x83P\xd0\x01\xc5\xcf)\xf8\x92\x13ge\xae\x16\xbe\x12_\x96\x14\f\xa1\xd7Y\v\xa5\xf8 \x02b\x1df\xbev\xf5/\xf0\xf8\x9f\xfc\xbf8\xb8\xfeu\xb8~\x01\x88\xc7ts\x0f\xb7\xdf\xf5~\xdc~\xe1!\uc77f\x0f\xbbg\xef\xc6\xc6\xf69e\xdb@@-\xd0l2\xcc\x16\x03\r }\xa7\x18}\xe2\x9dS\x86\x92\xa2A\x1a\x91\xdaB0\x81\xac(\x05\xb1\x18\xb2f\xedZ\x91\x10U\xda 3a\xba\xb1\x8d\xe9\xe6^\xf0\xac\xe9\xe1\\p\xe7'\x86x`\xef\xcc\x19\xdc}\xf7\xe5\xafN\xa7[\x8f\xe8I\xf6\u007f\xc0\xaf\xfe\xea\u007f\xb7.\xe6\xeb\xf5\u03af\xaf|\xe5\x11|\xf7w\u007fd\t\x00_\xfe\xf2W>\xf1\xf4\x99\xea\x13\xc8\x00\x00 \x00IDAT3\u03c0\x98\x05\x14x\xe5D\xea\x9c\xe7\x1d\x98\x19\xef\xfb\xf0O\xe2\xe2}\x0f\xa3\xeffy\x00V\x15r\xed\xdc\xc8T\x92C\x18\x0e\u0130\x86A\rUP+S\xd1\xcd6\x04:cUH\x14\xe4\xe2\x16\x04\x0f\xc0\xf5\xa8\a\xa2\xc3\xc6{4_ndh\x1a\xe5\xf9\x05\x8e-\xf5\xe1B)\u0261\x90\u01ee|\x85\xdaX82\x1a\x06h[1\x12\xdaw\xe8\x8d\xc2\x11zB/B\x1a\u00a0\x14\xa1\b\r\x1b\xf6\x8c\x9bS\xfawCX%\xe7\xaaJ\xadlM\x15?\x98\u007f\u015f\x83\x94\x92H\xb1\x90o\xf0hl\xaaX\x00\x9b\f8\x06y\x17NC\xf1D\xe5\xd5\xe1WrPC\xe5\x99P\x9c\x1c\x88c\xf8\a\xc1\v\xe0\x9c\x83\xb1\x13\xdcv\xc7}\xd8\u0739\x03\x1b[g\xb1w\xfb}\xb8\xfd\xd2C\xd8\u07bb\xa0\x9f\xe7z8qp\xbd\a,\xd0Z\xa3\x8c'\x13\xbdd2K\x88\r\xeb}\xd4\v\xa4\xf3\x05\xaf\x9e5\xb8\"\xa41I\f:\x19\xccLjQ\x1b\u00acD?\xb6\xed\x14g\xef\xbc\x1f/>\xf3\b\xfa~\xa1\x0e\x8c\xc6(\x13\xcb\x1ax\x11\x18c>\x1f\xbf\xde\x0f\xfd\xd0\x0f\xad;\xf3\xf5\xfa\xe6X_\xfc\xe2\x17\t\x80<\xfa\xe8\xd7>\xf8\x0f\xfe\xc1\xaf\xfd\x9d\x97^|\t\xd64\x94\xbc\x9d\x83 \xa4\xef\xe6\xb8\xe7\xa1\xef\xc7C\x1f\xf9\x8fam\xabX\xf9\x00\xd0 \"\x80M\xa2qA\x02\x9b\u00c4\x87\xc5\x10\xa4e\x90\xd1b\xee\x11\xc2p8\x1c\x00$\xccD\xa7\f9cA\u0483\xe6\xc5@\x14\x8a\xdf\xc2K\xee\xfa\v||\x05J\x11\x19M\x9a\x1b2\u05c7\xaaO\"e\xaf\xc0@\xa9\x93v\xc0F\x17\xacl\x1a\x04Q!\u0536\x9ao\x91\xf3\xe8\xbd\xd2\"\xc5\xc5\xf4$\xaa\x10\x15B\xe9\xa8(\x15B2\xf4a_\xd9\xc0bT\x1c\n\x91\x10\xd5\x1b\x9b\tTP\nb-LI\x8b5\u05db_4\x10\x13\xaf\xbf\x00\xda4\xba\x19\xf4\xda\xf12\v`\u0507\xdd;$.}}\xa4 T\xd9\x12\x1e\x1aDa\f.\xdc\xfb\x9d\xd8\u067b\b\xb6\r\xa6\x9bg\xd0Nwa\x9b\t\xbc\xeb\xd1-\xb3\xa0\u01c7\x13L\xd32\u0314AS\xbdW\xe0\xc3=\x02B\xa1A\x03&\x16\xd2\xf5jY\x1c\x94O\x86\xd4A\xd1#\xde\\a\x04\x1cB\xc9K\x83\xaf\x94PT\x84\x8e\x13[\x9c\xbf\xfb\x03h'[X\xce\x0f\xe1\xd9\x06\x91\x11\x81\x8d\x85\xf7\x82\x83\x83\x83\x97\xe2u\xfe\xbe\xef\xfb\xc1u1_\xafo\x8e\xf5w\xff\xee\u007f)\x00\xf0{\xbf\xf7{\xdf\xfb\xd8c\x8f\xefz/`c\x82%\xa9\x16\n\xd7-\xb0\xb1s\x0e\x1f\xf8\xfe\x9f\u0159\xdb\xefA7;JY\xa0\x89\xb1\x1d:\x1b\xf5\x9b6\xe9A\xf7&C,b\t\x98P1\xac\x8bc\xb4\xc21/\u05ba-\xa3\xb8\xb9\xd3\x02\xce\x00\x1aa,{\x9f\xf5\x9c\xd1#DFb\xd9N\xe9\xcaSP\u0100\xa0Q\xd6H\x06\x94\xb52!\u0224TK\x16\xa2\xf3\x01\xa1\xa6\x82+\xb6\t\xa6S\x0fqu\xc7\xd59\x82/\xbdfB\x11A\xc1\xd7N\xe3N\xaa\x1b\xffj\xc7\xc9f39^u\xf8\x93\x87\xc2jX\x8b\x1f\x19\x807\x8c\xce$\xa2\xc7L\x04g\x06\xf2\u007f\x91\x00-mi\x87\x8ec\ar\u0280i\xc0\xf0,\xf0NB\xfe\x85$oxfJz\x83d\xe4\x15\xbe^;\xddQ\xa5\xb0\bDz\x888t\x8b\xe3\xe2\x17NI9k[\x86\x9d2h\x83A\x96C\xe0rqc\x14\u061c\x18\x81\x01\x01\xc7\x0e\xe8B\x188\xeb\xafN\x85gT\x18\x8a\xd1\n\x8bI7n^\xb1\xcc\u07fe\xed\x12\xb6\xf7.\xe0\xe8\xc6\u02da\x9c\x14EV\xe1\xb3\x1ak7\xbe\u055f\xfb\xb5\x05\xee\xb7\xd9\xfa\x97\xff\xf2_\x84\x1a$;W\xae\\\xf9/\x9ey\xe6Y\xbd\xb9\xd9\x14\x93\u007f\xf5\xa5x\xe0\xe1\x1f\xc1\xbb>\xf0\t\xb8\xe5|0\x95C\xe2iG\u0447\xb2XD\xbd\xa8\r\xa5\xc1\x1b5\x19{.\xc2\xe5*\xa8\xa1\u00a1\xb7-h\u01c0\xac&\xd3Y\xab\xf8\xafX\x827j\a\x10\x1d\nW:p\xa9;\xefa\xd7.9]l\xd0\xd5\v\x98Dq\xda-e\u007f$8Z\xe4-\x1d8\b\x00M\rh\x87aZ\x85_\x8c\x91\xa0\x9f\na\n\xac4F\x89S\xc9\xf25\x0f\x8d\xbeb\xa8\x03\r\xaf7F\x15\xf1\xf1\xd3\f\x87!3\x11\xa8a\u040e\x05\xb5\\\\\u3890\x17j\u0304E\xb7\f\u07b1\xa0M\x03\xb2a\x98\xc9\xfa\xc6Q\xf8\x15m\x84\x933\x1a\xd5v\xbe\x01\xaf\x12\xf1\xe8\x963\xf4}Td\x02d\x03\x14\x17\x18NB\x80i\x18\u0366\x85\u0659\xc0n4`\u00ea\x06\xb5\xe1\xcdpbIqp3\xa4\xa9\x05m5@\xcb\bLX4\x86\xd0X}\x9d\xe9\x0f\x83N\xa2\xcc\xe9L\x14ZP\x8e\xad\x13\x81\xb5-vo\xbf\f\xa2`\xc2\x15\xec+z\ufc71\xb9\x81\xcbw_\xfe\xd1\xf85\x9ex\xe2\xd1ug\xbe^\xef\xfc\xfa\xb1\x1f\xfb\xb4\x00\xc0o\xfc\xc6\xff\xfc\x1d\x8f>\xfa\xe8G\xe6\xf3\x85\xe2\u0764Gj\x88G\xbf\x9c\xe3\xf6w}\x10\xef\xff\xf8\u007f\x8a\xb6\xdd\xc2\xe2x?\xc9\xfa3\x9dC\x1f\x06f\vf\x9b;\xd8\u062d\x87B -\xd7\xee}A\x15\xba\x02\x8f\xc4V\xd9\x00\xb4\u06c0;\x81\x1c\xf5\x00\x01\r\x03\xbd#t^s;\x99bp\xef@\x918\xa8v\xa9\v.\xc4D\xa5\xadJzM\xa4\\k\x9a\x12dR@\"e;\xef\a5\xbdpK\x04\xd4\bJ6\f\xa4\xf3`q\xc0\x12\x89\xfb\xdd\u01df\xcd\xc7\xe0\x03_\xbd\xbeX\\\x93;a9\xa0\xa3\u06b7\x86V\xc0{\xfd{f\xe8\xc0\x93\x01\xb2\x04l[`\x83\xf3\xcc`\xe0\u03d2,y9t\xdaqc\xdd\b\xf7\x02\x11p\u0483:\xc0\x04\xa1\x97x\x01;\xc0\xc5{\x05\xc55\xaa\xc2@\x94\x11\x14y\xde\xe5\xe7\x88d\aCb\xc0n2\xecn\x03\u07b6\x01v\xf3\xe9s*\x9c;\xfe\x9c\xf1\xf7\xb8\xc1)@\x9c;\xbdn\r1:\x84\xccQ\xcf\xf5\xb6^\xbc\xbe\xa4\x04-~\x99l,6\xb7\xcf\xc6<\xbb\xf0m\x19\x86\x8d,\x17Kz\xfe\xf9+\xf7\xc5\xcf~\xef{\u07ff.\xe6\xeb\xf5\u0373n\xdc\xd8\xff\xf9g\x9f}\x0e\xbd\xf3`\x13\x8a\xb1wp\xdd\x12\xa6\x9d\xe0\x81\xef\xf94\xce\xdf\xf3\xd7\xd0]?\xc8~\xe65X\x1e:&[\x17\xbd\xb2\xe0\x9b\u0615S\xf2<bR\x15b\xb6?\x1f\xd0\x15=@-\x01{\x8d\xd2\x15O\x1c\x98\b\xad\x12\xe8\xe0\xbcv\xd1e\x1aN\xe9\x12\xb8\x12\xe1F\x85\xd0\x05\xb9(\u050dd\xe8\xca7\x90\xbar\xfd\x11\xe4\xf4\xa6|$\u308d\x81l:H\xef5W\x14\bC<\xc0\xa5\xc0k\x19\n+Q\x99\x98\xa4bB#\x93WZIgC(\x8a&\xce(X\a\x9e\xbcm\U000b597cAD\xf5\xa3G\xbd\xd1\xe5\u0670\x80\xa6\x06\x1c\n6\xc1\xab3d:-\x10\xc8\x01\xceyxY}\x99\xb5\x0fM\xf8\x1e\x1c\xac\x94\xa3\x8bd\x80?LK\xb0\x9b\x16\xbci\xc1\x8d\t\nS\x14\x02\xab\xbc\x19\x94\x9e\xfa\xf1g\xe2\x8d\xd0\xe1\x1f\xf7\x9a{\xea\x152\xf7\xac\xdf+\r:#C(\xb8v\x0e\xdc\xdb\xe1\xc5\u00d0A;\xdd\x0e\f\x16\x97\xe6\n\xc6Z\xda?8\xc0\vW\xae\x9cy\xe3\xcdW\x1f\xbe\xfd\u071d\u007f\xba\x86Y\xd6\xeb\x1d_\x9f\xfd\ucfca\x0f4?\xf9\xd4S?x||\x92:l\x1f:\xa2\xbe[\u0dbb\x1f\u0103\xdf\xf3\x13\xc0\xa2\xd3\x1b;\x03\xc4\x05\x19$\x14r6\x15\xd0+\xa1\xa0p\x80Yb\x8a=\r\xa8e\xc3X\xb8\x12;\x87\x00<e\xd0\x19\x85\t@j\t\u0432\xf2\xa7}e#@5\x94]\x92-\xcaa(Q\xc6\u024bM\x89\x10\xe0\x90 j*_S\x15\xeaSvx\xa7\xe4\x93\x121\xc8\x1a\u015e7\xf4\xe9a\x12X\xf2`)\xfc\u06c7P\t\xa12,+\x1f;\x19\xc0\x03\xe5&\x15\x83\x1b\f\xe9|\x81\b\xe0\x96@;\x16hy\x00-d\x8c\u0757f_e\x88\x04EN6@\x13\x06\xef\xb4\xc0\x94\x03\xe4\x15\u0330\xc2\xef\xd6\x04\bD\x8f\x04\xfa\x96r;cV]\x80\xef\x925\x04\xe7\x8fMc`7,x\xab\x01\xb56)\x87\x13\u0155\xb9\xc6\u0283\xc3c\xfa\xb9)@0\x1b\x06\xb4\u0640\xad\x81\xb1\f6\n\xc7\xc4\xe8\xb9\xe1p<\x8a\x9aT\fW\xd8\xf7\x82`L\x9bN\xa0\x89E\xc3\x06\xcbE\x87\xe3\xe3\xe3\xf3\x9f\xfd\xccg?\xb2\xc6\xcc\xd7\xeb\x9bb}\xf2\x93\x9a]\xf8\x97_\xfd\xb3w]y\xfe\xf9w-\x96]!\xae\x10\xb8\xbe\x83i'\xb8\xe7\xe1\x1f\u0199\xdb.C\x16\xf3\xccb\x18\x14\x126\x06ll]d\x8abI\x1c\xba\\\xa3\x85\x86\x8b\xbc\x8a\x8cs\x94\x82}AM\xd2\x06h\u04c2\xb6\x8d\xb22\x88\x14\x17\r\"$g\x02k\x86\x8atw\u0501\xd0BE\xcd\x1a\xc3\xd0\xe3\xa7F\x06KS\x1f0\xe4\xe6)n\xa3x\xbd\x16t\x026\t4\tu\x8e\x04\x86\xfc\u02b06a\xe2\x05\x9c\xa0X\xb2\xa4\xacO\x12*\xb0qZ\x11Gq\xf4^\x89FZ\x9bV\a\x89\xf1$P\xfdz\x02C#}\xa5\xe8\xac\x18_{\x1e\xae\x82\x00\xda`\xf0\x96\x05\xb5F\xf1k\u03b3\x106\x14\xe0\x97\xf0\xcb5\x8c\xf4BH\v*\aK\x88\x14\xb9\xcc\f2\x06\xc6\x1a4\x13\v\xb3\xd9\xc0l\xd80|\xcf\u007f\x1fo\x16b\x0e\xf1r\\\x17\xf1P\xe8%Z\x18l\x06\x8b\x82\xf0\x12\xac\r\xc1D\x91\xdaH\xa5\x1dqpk\x1c\xf0\xf9!\x9a\x0f\x1a\xff4\xde*\u030c\xdey\xf4\xde7\xcf?\xff\xc2G\xbf\x95\x9f\xff5\xcc\xf2m\xb8\xfe\xe8\v_\xfc\xfe\x97_~e\xbaXh\x8a\x8a\x84\xecH\xd7/\xb1s\xe12\xee\xfd\xceO\x80\xe6\x0e\u03b9\xcawE\x12\xa7<;\xca\r\x1a\xdd\x02/\x0f\xbc`\x1at\xddT\x97RZ\xa9\x94\x89\x12\x016\xa4\x96\xb9\x9d@\x8e<X\x80\u01aa\x19T\x1fC!\xdcX \xe80`\xb9\x00*\xa8B\x8a\xc0\x10P+\xca+\u720d\x13\"\x1a$\xa5\x0f\x8a\xd4\x0e\xb6+\xdf\x0eH\x91c\xd2x\xc8V\x10=-\xa0\xb4GR\xb8\xa58\xe8h\xd5q\x92\x82\x84\x13\xb52\xa1S2j\xa1\xab\xff4\xe6f\x86a\xf3\u0504\u036f~q\xa5\x97x4\xc0*\x8d\u0232QV\xe8\xe0#\x0ee\x00\u06b2\x80\x10\xfc\x89\x03z\u0162M\xf4<w:I\ue4f5{\xb4\xfd\x95!\x8b?\xfd<\x86\x01\u06f0Z\vl\xea\u026b\xa2\x8d\x06!R\u029a\xa5l\xa2V\u0307Wn\x19\xda \x88c\xb0\xf8`\xf5\v8O\x9aGZ\xde\x04!l\x9c\x06\x9e\xc7\"\x1e\xce\xf5\xa9\u0448\xb4Q\x02\x81\r\xcb|>\xa7g\x9ey\xfa\uce98\xaf\xd77\xd5z\u9957\xbe\u007f\xd9\xf5\u4703\xb5MrA\x04\x11v/\xbc\v{g\xee\x06\xf5.p\xbb\aC+\x10\x88,\x88l\xfd0\x84NX\x8a\x937l\u044d\x97\x14\xc1\xdac0\xb3\"D\x8a\xce=\xc0\x11\xad\x01\x9d\x01\xc8\xf5\xc0\xdc\xc10\u0436\x04\xdf\t\x9c\v\xc5\xcc\xc5H\xb3REIE\x1d\x91\xaa\xb0\u01d7\u0361+\x97i\x80YJ\xba\xa3\xc4\x01\xe5i\x8d\xb8r\x9b\x87S\xd1\x14\xceA\x81\x92\xe9(\xe1\xb9`\t\\\xec\x02\xad\xa6\\\xd0\v)j5\xac\xab$\xb0\x85\xf0\x89)\x0e\x94\xa1*\xcf-\x03\x9a\x98\xf1}\xad\x84\x8f\n\xd7\xc0\xe0\xbb0\x88p\xe3\xfc\xc9\r\x01;z\xba\x92yNZ\x82S\n)\\\xf4r\xa7`\xc7 u\xb5\xcd\\J\x18#\xb0\x96\xc0m(\xe4\x1bF\xf5\b%\x9eE\u0671\xb1\xe2\xe1\x17\x1b\xbfO\x03J\xaa\u007f\x86\xcd\xf0\xfe\u072b\xc8\xc8\n\xd0{8W'\x1f\u9f55sGc\xb7\x1e=g\xe25\x97p\x0fXki\xff\xc6\r\\\xbfv\xfd\x9c\x88\x9c!\xa2\xfd\x17_\xbc\x82\u02d7\xef]\xc3,\xebu\xeb\xd7s\xcf=\x93\xde\u007f\xf5\xd5\u05fe\xcby\xa78y\xf4\xb6\xf0\x0ev2\u0145{\xbf\x03\x1b\xb2\tY\xf6%\x18\x8c\xa8\xf2ccal\x9b\x02(\u02beZ\xe2CK\x81Qa9w\x81\xc3\xe69=\x9a\x94\\\xaeJL=w`\x02l\x18p\xc0\u03c95~nbT\xae\xee,\xc17\x04_\bKd\xb8I\f\x11\x96\x88Q\x13@-TTc\xb0\x12G\x97\xbc\xba\xc3?\x12\x1a\xd6\xc6\xf1b\x1f\xad\x83\x01(\xa3d\x8b\x14w6\x04k\x04\xcc\xd9\xd8K\xaa\xe8\xf9A\xcb_2:\x06\x17\x90\x89`\x8cZ\xb4\x92e`\xd3\xd4\xe2 \xe44\xfb\f\xe7 \xd1\xf1rVk\xf8\x1a\xc4a\xf3\xaa\xbf?\x91\u0489h\xbb\x01O\x03\xb5\xd00\xb810\xad\x01O\x14\xff&\x13\xb2D9JP9\xd3\x14I4\xe8\x83\x19l\rx\x83A\x9b\xac\xf3\x14\x19$TQ~\xbf|\x8b\x94\xc5\xfcu\x91\x86\xa2\x11\u07e6hO0Q\xe1\x11\xd9p3F(\xa8\xbc\xc4\x15\xa1\x8a\xaa\xa0\xf1\xca\xd0\rj\xc6utt\fb\xbe\x0f\xc0\x87\x00\xe0\xf5\xd7\u07e05f\xbe^\xef\u023a\xef\xbe\abW\xb6\xf7\xc2\vW\u03dc\x1c\x9f\x80\xd9$:\x9c\x17\x8fv\xba\x85\xdb\xefx\x00\u0187\x89>\xd5\xdcp\xf5\xe0j@l\u01cbL\xc0\xaf\x99\xa2\xb1\xd6*\xfb\xf0\xadJ!\xd5\xe54Uu\xda2\xcaA\xb7Z\x9ct \xaa/\xc3\xd9\x10\x18] \u00eb\x1b\x87\xac\xf6\xd6F@\x93\x80\x95\xaf\xee3yc\x10\xacJ\xe8k\u06eb\xb1\v\x11pb@6\t\x98\x86\x82\xde\x00l%3z\xa2\xb3\xdf\n!>^\x02Z\xb9*\x89SN\x01^\x99\x04\u0738\xc9hx\xe5\xea\x1d\x8bc(\xe4\x1c\xb0i-\x949\xc0\x99\xaa\x01c$4\x86\xdf\xfd4\\\xff\x86\x03\xb4\x13\xe06k\x14S7&\xa8\x81\x8b\x90\x0f\xa1\x84n\x18CZ\x90\x1bV\\\u007fb\xd2\xd03\xfb\xa5P5\xac\x15p\xa0\xbf\x9a\xb4A\x12\x13L\x84\xb2b\xfaR\xe9\xb7b\t\xb4\x11 >\xa3\x9b\xbcg\xe49Da\xc2\xe5\v\x8fx\xf5{\xf1\x15\xbb2\x0f\xe7\x19^\x04'''\xf4\xf5\xaf_em\x88^]\xc3,\xeb\xf5\u03ae\xc7\x1e\xfb\u068f\xccf\xb3\x8bG\x87G`2\x15M\xae\x99la{\xe7\x02\x98\fzY\x1d\xd61\x99@c\xc4j\xe4\x19B\xdeg\x1c~6\x14\x98\rC|\x93*\xc9=Um8*\v\xaf\xe8\xf3\xa1\xd1g\x02\xda5 \xe7A\a\x02t\x82V\b\x8e\x80\xa5W\u0699\x0f\x18xY\u0487q\xc9\t\x8ae\xa8\xa9V\x18|\u0280\x8e<\nU\u0230\u04e7U*\xe4\xe04\x03\x0f\xc0\x00\xb2\x05\x90\xf3\x80\a\x8c\xd5\xef\u0457\xedy\x84#\xdc\xc0\x18jU+\x14\nr\xf0'o\t\xbc\xd5@6m\xf2G\xc9\xf8u\x86L\xa8\x18\xb4f\xd8K9\xe6\xc3@'\xa9\xcfMH\x86/\x9b:\x90\x95\xc3^\xed\n\x90\x19C\u0300'V\x8f\x14\x93\xbd\xcdI\bM\fy0\xca\xc5\xf7\x1bV9\xf1\xc1\xdd\x11)\x12/\x02!A#\xccT\xec\u0152](\x83\t\"\x93\xfa\xf8\x88\x1f\xd0=\x1b\x06m\b\xfa\xce\u00c5\xfb2\xfd\xb6$x\xd8H\u212aj\x19\x9a\u16b7NN\xf7\xae\u0386\x18\xf3\xf9\xdc_\xbf~\xc3\x01\xc0\xfe\xfe\xfe\xba\x98\xaf\xd7;\xbb>\xf3\x99\xcfto\xbc\xf9\x86O\xe6LI\xfc\xc1\x98n\xecbs\xfb\x1c\x04\x94p\xc9\u071eS\u8fb8\xe0\xe9\x16\x85<(\xfa\x88C\x10\x82\xe5\x04\xfd\xd2J\xdfM+\x01\xbe\xa9#\xa5<\x1a\x95\xd2\xdeV\xb4H\u040e\x05\xf7\x02\u007f\xd4\x03 L\x04\xf0^\x14r\x8e\xaf!\x16\x04\xa2\x15\xe7\xd9\n/\xb7\x9a\"T>\xd72R\x98\xa5\b!\x93A\a\x1f\xf3\x96\xb8\xf4&g\x02\xb3\x81\xa7\x10\xec,\xba\xb9\xc9V\x18v\xf6\x801\x1e\xde3\\\fS\xe0\\\xf8U7_\b\x8a\x90\xf3,\x05\x019\xe0\xe0\x86\xb8\x99;f\n\x1bB\x19\ueb1b'UB\x19*\x87\x9c\x83\xbc\xeb\xf2\xf7J$U:\x121\x03\xdb\x04\x11\x86\x9c8\xf8N@^\a\u04d0\u0e98X+jUkC\xd2\x14\f\x01S\x03\xbf\xd3\xc0Y5\u01f2\u0308\xe1@\xbe\b\xf3H\xf4H\xa2\xc2D,w\xe1\xe2)Q \xc5\xfb\xc2q2of\xae!,\x1a\x86\xef\x04\x86\x00\x98`\xb3\xdc\xfb\x94\xad\x9a\xad\x90\x87\x16\x00\xf1\u0102\x90\x96\xc5pT\xf8\xd9\u007f\x8b\xaeu1\xff6[\u05ee]\xbb\xec\x9d\xdfr\xce)%,\x0e\xc4\xd8`\xeb\xccyL&\u06c5\xaa\xa6\x18>E\xc9?h\f3Q8\x81\x00K\x8aU\x92-\xfcP\xca\u007fB\x92\x1c\x04W\x02%GQ\xc8:\x81\x81&\x04\u0675\xa0Nt \ua056\t\v\tG\xe4\xb1/]\xd2\x16C\x81#\x16\xd0\x14\xc0d\xe0\x0f.uC\x9e\xde$\xabH#\xc9\xd0\b%\x9c\x99\xc8\xe7\xa86 ][\xefz\xc0\xa9<\x1c\x13\x026\x058Vq\x8b1\x02/\x9c\xaf\x9f\xc4\r\x82s\u0403\x97\xca\x13\x87)@\x16-\xc1l\x1a`\xa7Qs\xaa\xb2\xe8\xca`JP2\x8a\n+\x81X\u0529\xa2\xeaQ\xb1\u007fsm\xbb@\xba)\xd1n\xf8\x1d\xfa\x1e\x0e\xac!\xd7>\xc0\"\xb1\u007f&\x85UR\xfek\xc3\xc0N\x03lZ,\xc2\xe7\x9b \xed\xd7Z+\xd9Q2\xfe\"\x88rP4\xfb\xc0Z\xa5\xa0\fR2M2{C\xc1Z\n\f\x16\xde`\x18\x11\xd0\"\xdc\x1b\xbe\xb8\a\x92\xd9X\x0e\xa2\x06e(\x87\x8a\x98\xb9H\x87d\xc3h\x1b\xc5\u4b35\xebb\xbe^\xef\xecj\xdb\xf6\xfb\u06b65}\u05e15&\r\x19\xd9\x18\xec\xec]\x80m\xa6)\x99\xbc\xc4Q#\x15QF\xea,R\xd2{\xe8\x1a\x83\x8fv\xd5\xecq\x8d\a\x8fR\xd7V\xb6\x8a\xf0\xa0E\xf5_(8\xbc\xc1\xf0g\xacv\xb2\v\x0fk\xd4\u072b\x8b\xec\x12\xa6D\x8e`PEnI\xf4?\xab\x99\x9ed\u01af\x93\x14 \x83T\xddyP\xe6\x83 \xa1S\xa60\xc43\xe5\xcfE\x04\xb6\x16d\x18\xd2;x\xd7\xc3;\xa71t^\x80c\x82a\x817\x02\xe79\f\xf1\xd43\xc0s\xe0f{\x0fr\xb1#\u0542n\r\xa3i\x18\xbc\xc1\xe03\x16\xbc\xdb\xe8\xbc \x9ap\x15\xfcI*v\u04c8E3\xd5\x13\t\x891uq\x00(\x12h\xdeC9g\xf1\xab\x9a\x10d\x97!\x0e\xe0\x99\x03\xbc\x0f8|d\xbaxx\xca\xcc\x1e\xa5MZ\xf0N\v\xb1\xa4\x8c\x17P\x10\xf6Pm\x89<<\xaf\x89@\\\x1f\xd8'\x94n&a\xc9x\u007fiw+y\xd87\x99\x90n6\u0783:=\xec\xa4\xcfd^\xb1\x1b\x8a\xf1v\xe5\xecE@\xc9\x17\x06\x02\xf4\xce\xe9&\xed\xfd\xba\x98\xaf\xd7;\xbb\xdex\u336d\xae[\xe6\x1b?\xdd\xd0\x06\x1b;\xe7\xc0\xa6I7j%V\xa1<8\x1a>\xdc \xc03\xa5\x00g\xb2\x94\n0\x15\xc1\xc9\x15\xd7;\xcd6\x87A\xc0\xa3\x8ft\xe6\x1b\u01c7u\xdb\xc0\xf7\x1e\xf0\x1e\xa6#\xb4\x81M\xd1G\xfc4\f\xf1\xca\xf9\xa1\x14\xf8\x88L\x83|\x1f\u046av\xc0,\t\x0f\xb3:\x05ReEK\x10\x98\xd0\xfe\u01df'\b\xdf\xf5H_\x15\xd40\xf43\f\xea\x1c\xbc\xef![N\xa9}3\r\xb7p\x88~\xe7\x04\x11\x82k\x00!F\xb3\xa0\xe4?\xa3\xc3F\x86\xb5\f;50\xb75\xa0\xbdF;^\x19\x04\x8aV]y\x8e\xf5\xe3\xf0;\xf4\xa21\xda\x10\xc0;\x1f\x92|\xbcF\xa5En7\x97G\x15Z\x9ddo\x1a\x18a\x00K\u0639\x83a\r\x87\x96^\u0099\xc2k\xd7m\x83\xe9\xd7n\x03\x99X\xb0al\x98\xb0\xf9\x15\xc3Wp6\xf2RA\x1aC\x9c\x87\xeb:\xf4\v\xc0K\x9f|\xed\x01\xa3\xd4W\xf6\xf0\xec\x01'up\x93\xa4\xfd\x1c\xd2\x12\xfc\x86\x81\xb8\x1eB:\fe\x9fKu\x05c\x19S\xb8K\x04\x8fv\x11\x90\xa8+\xe8b\xb9\xc4k\xaf\xbd\x06\x00\xb8~\xfd\xfa\xba\x98\xaf\xd7;\xbb\xae\\y\xa1\x9b\xcd\xe6 c\xb2\x98\"\x14\x8b\xe9\xf6m0\x8dM\x8eq\ts-\x1b\xe3T\xa4\x8b\xee\x95\x011*\b\xe1\xe8\xc7\xc2\xf5\\\x8f\xa2\x87t,:e2\x83\fl\f\xa5<\x0e\x0f\a\x91\x94\x9eV\xda\r\xdd\xf9A\x0f\xb6\x04\xdb\x10\xbc\x10\xbc\x93\xdc\xd1sV6\xa6\x8e\xb3!\xd0.@\xad\x9a\u056e\x84^\f\xe4\xf6\f\t\x12\xf8:\x9b\xb3\xe6\x8c\xc8\xf8n\x15?\xd7\x18\x10\x1b\xb0cx\xeb \xe4\xd4\xfeu!\x80c8\xd1\v\xe6\xa1\xd4E/\x00,\xa7\x94$b\x8bfb\xd0N-\xec^\x03>\u05c2[^y\xad\xd5V\x1bX2D\x92\xe4\xfe\x01\x91Nv0\xe2<|\xef@\xceA\u0125\x81\xacZ\xd3\u02c8\xd50EZ7\xb0c\x000x\xbf\x83_zH/AD$ \x16\x9de\x18V\x13\xad\x9d\x06\bE\xdaZe\xa8\xb0QX\u00c7MS\x88\xf3\u07c7\x1d\x9e\xfb^}\xf0\xbd\x878W\xa8\x889X6\x0f:\xf3\xf8\u07c2\xa7\xefZ\u00b2!\x15=\x05\x02\x8d\x06\x92K\x1a\u019b\u05a0\x99n\xa9E\x05\x96\x85\x1f\f\x87a\xb9\xc5l\xb6\xc0\xd3O=\x05\x00\xb8z\xf5\xc5u1_\xaf[\xbf\x8e\x8en`{{\x0f\x00pxx8Y,\x16\xc9\xfa3\x1d\\\xd9`\xb2\xb5\v2\r|\xe7R\xe1\xaelk%&\xf6He1\xeb\rrWn\xb4=MRy\x1a\xcf\xdb\x04rr\xa4\x10*E\"\xc6R\x85\xa4,\x8e1}\x99\x81]\x8d\x9cC\x0fp\xaf\xa2\x94N$\x85\x1e\x10)\xe4c\x88B\xbe\xa7\xe6\x8e\u04b6W\xc1P\x14\xf3T\x91?\xf5\xb9\x80\x13\b/\xc1\x17\xab\x84\x85\ua23a\b\xbbTXsa\x88E\u0702!\x90\xd6\xebP\xee\u0423\xe9\bB\x06\x10\x81\xf3:\xa8sN\u09aa\x18U\x81\x92\x01O-\xb0\u05c0no`&\\c\u0660:\xfc\xa2\x00\xaa\xa2\x9a5\xa3\xe2\x94^c\u0083\x9dQ\xab\xda8\x88L\xd6\n\x18\x19\xfceoq\xec\x1a\b\fx\xbf\xd3a${\x15\xeb\x10\xe0\x9d\xa0\ac\xd94h\xd8b\xc2\fc-L\xdb\xc0X\x930q&5P\xeb%\xe6MPr\x824\xb6\x81\x99\xb4p\xddB\x87\xf2\x92g/Y\xa2 \xd9\xd5R|u\xa3\x91\x10\x9c\b\x16\r\xc1t\x04\xebD! .|\xe6\xc3\t\xaa\xd9\xdc\xc9\x16\x15\x05\x04\xc3`\x881X,\x97\xf2\xecs\xcfy\x00\xf8\xfa\xcb__\x17\xf3\xf5\xba\xf5\xeb\xf9\xe7_\bl7\xf9\xe0\xdf\xf8\x1b?q\xff\xf1\xf1\xf1@_/0\xb6A\xbb\xbd\xabFQ]_\xc3\u0525'Hx\xd0S\a\xc8HF\xfe\x86\x82\x80\x85)\x15\xb5\xe1\xfc\xb3\xf4\xe5\xa6*2\x1d\xab\xdcu\x1a\xa0\xe8\xd1\x1a\x15E\xe5l\t\x14$\xff\xd4{M\xa0q*\xe36\xa1\x80F\x95$\x99(\x80a\xf5\u0096^;P\x89\x015\x8a\x1b\xfb\x01T\xa2\x98\xb5T0\xcb\xe0\xf2\x9d\xba\x04y\xa0F!\xb0\"\xf2\xb8\xe5L\xa0\xd9\x1dQ\xb2\xa1\xd5z\xaa\f\r\u01c07\x04\x17\xd2vx\xdb\x02\xb75\x10\x9b\xb3<\x93\x87\v\rLe\x86\x1cK\xca\xd2~\x19\xd9Y\x89\x19\x863w;n>\x88CXHE\xc9D\x80\xfe\xb9\x05\xc4z\xf8n\tYv\xf0\x8d\x16Vo\x04\xd6\x03=\x04\vg1\xeb\f6\xc8`\xbbi`Mvt\xf4\x14\xe5\xff\x04\x1b\xae\xbb\xf3\x81\x9c(\xba_\xab\u0372\x81\x90\u02ef!\x89\x89\f\xbcs\xf0\xe22\xfe\x0f\xa0\xbc\xb5,\x01mC\x1a\x9d\xe7\xe3\xf0V\xed\x94)\xa9\xac\x18\xcd\xc66\xd86I,\x86\xf4;\x03\x96\xcb\x1e{{{\x97/]\xba\xf4\x9d\x00\xfe\xe0\xde{\xeeY\x17\xf3\xf5\xba\xf5+\xe2|o\xbc\xf1\xdaE\xe7\xdc^\xd7u\xa1\x13.\x86?\x93\t\x9a3\xbb@k!\xb3y\x91?\x19y{\xb9\x13\u00a0\x9e\xa6\x0e\x86D\x85B\xb6\xb6\xb6\xae\n\xb5\fA\x81S\x99\u06ab5\x92h\u0624\xeb\xc7S\x03\xd9\x16`\xe1\xc0>\x04\xfcz\xa5\xfaq\u8b09\x95\x93M\x93 \xb3gh\xaa\x0e\"\x05/|\x8b`l\xe5\xbd\u2fa9\xb8\x95\xa6\x89\x95\x81\xd7\n\xf912,\xb3\u0425\xa0\xbfQ\x12\xc4\x04\xdcgK\xb9\xe5\x98\x03\xe8\x03\x17=\xc0I\xd6k\x98\x05\x87@l\xb3caZ\u029bd\xe1s#R\xb2\xb43\xa3e%K\xf4-\xaetR\xe1\x06Q\x90\x1eMdt@\xcca\xb0\xed\xbd@Z\xb5\x11\xe6^\xe2\x88R\xf1i\x02Z\a\xb8%\xe3\x18\x84\x1e\x0e;\x04lNM\xa2%\xe68<\n\xac\xa1B\xd0\xe3\x01O\x04\t\xc2!xWX\xff\x1a0;xb@\\\xb5\xe9\u01efG\xa4\xf3\x14\u00c0\xb7\f\x0f\xa0w\x02\xe9}\xa2gF\x05\xa9m\xa60M[\xfe\x84z\x12\x15\x0f\xd7\xfb\xe5t\xb2\xd1z/\xef\a\x006\xf6-\xb6\xf1u1_\xaf\xb7a\xc5a\u0375k\u05fa\xf9|\xee\xca\a9\u0791\xa6ia67 \x13\x86\xb7ZX\x84Vs)s6X\x9d\xab\x99\x1a\xee \xe7\xa6\xc1\x103\xc16\x95eG\xe9\xad1,\x16\xe3E>\u05dfh\xea\x14:\xa8-\x03,-\xd8uh\u0200z\xafy\x96\xe1\xb5q\xb4\x9emIa\x00\f\x92|JhB\x04\xc6\x10\xc4S\xf0\xad\x11$\"z\xf5\x1a\x86\x83G\t1zA&?`\x84\xac\xd8\u007fK\x88\xd5\xdb&\xc0yH'\xa9\vN\x9d\xa1Wk\x04\xbfe4\xceN\ua409lD%\x95=\x80\x14\x97\x89\x90-\xd2E\xb0\x1a\x9e:\f\xc5NB&_|-\xca0\x93\x14\xa76\x0f\xc8\xcci\u0232!H\xe7\xe1\xbd\x16cb\r\x880N0\x99{\xcc-0\xef\x19\xfd\xb1\xc3\xc2\x03\xdbSFk\x92\xa7b\x15=\x17\xe7\xaf\x1e\x80\xf3@\x17a\xbeP\xec!z>\x122\xc9z7v\xf8\xd5\xcc \x88\x8d\x98\xf4t\xc5\x1b\f:!,\xe7\xe15\x16n\x8a\xb6\x9d`\xb2\xb9\x8b\xa3k/\x17\xf7g\xc0\u052d\xa5\u00e3c<\xf9\xd43\xc7\x000\x99L\u05dd\xf9z\xbd\x13\x98\xf9\x11\x00\xe0\u018d\x1b\xe8\xbae8FF\xa3\xa1\x10\x1c\x11d\xfa\xde\x00\u0796E\x83\x82;\x1eV\x83$R\a(\xb9xq\x0e\x83(a\x1a\x06\x06f\xe2\xf5\xb7\xa8+I\xc1/\x1e|>\x86\x1bD\xa8M\x86\x01\xbfc\xe1\x9d\xc0\xec\xf7\xabX\xbd\xf7@\x17^\x9f%\x15\xb8T\xa7\x05\xa9g\xac\xb1\x9b\x17\x0e\x02*JA\x13\xa9\xd8\r\xbcu\x89sDY\xe93\xb3\"\xfb/\xc25\x88\xf5\xf8/\v\x01\x0e]\x1d\x8c\x03\xa8\xc8\b\xac\u0664!\xb7BLm\xcd{Z{(\xc5\xe1\u01e10L\x93\x02\xf2\x1a\xfb\x9d\x8e\xbc\x17\xb7W/\x929\xf7\x80\xb2WN\x1c\xd0I*\xe2\xce\xe7\x0e96\xf5\xb2\xf4h\x16j\xbb\xe0\x9c\xe0h\xee\xb0\x14`\xa3!lZ\xed\x9e\xe3\xe5\xe4d\u02dc\xb3F\xbd\x10\xe6\xbdn\xca\rS\xa2\xcc*\x13\x86\x835E\xf9\xeb\x94<\a\x15\x9f\u007f7.$\x19\u016f\x11\x93\x87\xe0a\x9a\t\xa6\xbb\xe7B7\xae?K\x14\xd5\x1acpxt\x8cW^~e\x02\x00\xff\xf5\xaf\xfc\x8aH\x05\xfb\xad\x8b\xf9z\u0742u\xe3\u018d\x00\xb3\xbc\x81\xbe\xef\a\x1e\xe4\x91\u007fl\x82]\x88\x87\x04Iv|\x9e\xcd\b\x9e]i;$\x91\v\x92\x89T\x8d>\x04\x18\xa0d\xa9\xe0\x14\x0f\xa9\x9a\x8e\x81\x9aK!\xa3]z\xb2@\xb7\xcap\x91N\xc0\xfb.\x83\vqo\x12\x01\x16\x02,=\xc4\x0e\x1aT\xa9Y\x119\x9c(gE\n\x17\x8a\u039c\u021c\xb22A\\Y\x11\x94Cd\x91\xb2\u04e3\xcc4Ah=\xbd^w\n|\xca\xc4$\x89\xb5v)\xf0\x8d\xa07\xd9>\x9c\a\x9e\xe5\xb2\n\x85\x87\xce\x14\xd5\u0303\x8b\x80\x90\xe8\xdcX6\xe7\xe9\xc7K\u009d\xdc\xf5KTkF\xab\x85\x99\x83\x9f;\xb8\xa5\x87[xx78\xa8\x84\r\x9f\x1d\xd0\xcc=\\C\xe8'\f\xf1\x82e\xef\xd1y\u00bc\x03\xb6Z\xc6\u0532b\xe4\xd1}\x92\xf2\u0463i4lZzI\x14E\x15\x101`\f\xe0\xbc\xe2\xe1\xc80\x95Gq\xd0\x11\x00\xbd\xc0\x1d\xab\x8b\xa2\x04\xee\"\x89\x86\x85;q\xa0\xa6\xc5\xf6\x99\v 29\x9d(l\\\xd6\x1a\xbe~\xfd\x06\xb6vv\xbeOD\xdeMDO\u007f\xf9+\x8f\xf0\xea\x91m]\xcc\xd7\xebm\\\xb9[L\xa8o\x91n\x13\xdd\x00\x95\xa2\xe6{\x0f\x81WA\x87\xb6Ia8\x88\x8a\xafM%\xf6-\xd0\a\x89\x18Q\u0418fp4,\u0265:h\x00<\x14\xb4\xbe*$X\xc6Fv#\xcb+l!\x9b\x06|\xe4\x82JD*\xdb[\x9a{\xe0\x10\xc0n\xe9\x94\x18\x8a,\xe5\"Z\xee%\xe9\x8c\x10(\x10\xf92J\u0751\x97@6\u0556_\xa5\xf5oe*%\x80\x9cx\xd0\xccg\tyX\xbd\xa8\xb5\xac8\x0f:\xea\x01\x03\xb8M\x13\xca\aeXb\x18Y\x11\v\xb8\x94\x87\x88\xdce\nj\x1dV\x91\aR\xed\xa8\x12:\xd3\xf85bW\x1eA\x1d\xd7\v\u0731\x83\x9bi!G\x9f3Y%\xf8\xbe\xe4\x03\x00\xc1\xf4\x82f\xee\xe1C87y\xc5\u0717\x0e\xe8\xe7\x1e\xf3F\xb0i\x19\x1b\x96\xd4y\xb3\xb8\x8e\xd1q\u0449(\x1f\x1e\x02\x1f\xa5\xf6.\x18|1A\x1cU\xa7\b\xef\x83\xef\xcaR '\x0en\xe1\xd5\x0e\x80\xc2\rCq\x98\xeba\xed\x04[\xb7]\x00\xb3\x85x\x970\xf7\xc0\u28ae\xefq\xed\xda\xf5\xb3\u007f\xfe\xe7_9\a\xe0\xe9?\xfd\xd3?\xfb\x96rN\\\x17\xf3o\x83\xb5\xb5\xb5\x05\x00\xd8\xdd=\x83\xa6\xb1\x15\x94\x91Q\b\a\x17Zg\xf1Y\x99W\x9e\xe3%\f\v1\x92\xb5\u0245\x97\x85\xbeIb\x89I\xe1%=\x90\x0f\u055e +\x8aCY\x1dv\x9e\xd2\xcc{\xc9](H\xbbt\xf4\x12\xb3\x12\x10\xea\x06\x8c\x03\xe8\u062b\xf9\xd56\xe7\xe1\x1b\xe5\xee\xb9T|Fiz\xacR\x14v\xa7\x98Z_\xbd&\x1a\xf7\xa1\xc90\x05\xad\x82D\x9d\x00\xc7\x0e\xb2\x10\x90SJ^\xec(\x97\x02\xf4\xe1\x1d\xea\xf5\xf3=\x11x\xcahL\xceT\xa5\xe2\u0691T\a\xaej\x10-8\xd5Q\xb7\x86\xbe\x90F\x11\t\x1f\xf7\x92\xdf\aT\x9c5?v\xf0G\x0eXx\xf5l/TW\xb9\xb3\x0f\xc6`a\u00f0K\x81\x9f{,6\xb5;\xe7\u008fg\xd6\t:\xe7\xd0y\xc6f\xc3hM\x11\x10R^\xe0b\xa0\x1c\xd9A>X\xf8:\x0f\xf4\xbd\xa0\xef\xbd\xd2\x1d\xbdW\xe8m.\xa0.\x04\x0ez\xa9g\x0f\xa44\"\xdbN\xb0}\xf6\"\xda\xcdm\u030fn\f\u0384D]\xef\xd0\xf7\xee]\x8f|\xf9O\x1e\x04\xf0\xa5\x97_~e]\xcc\xd7\xeb\u05ae\xf3\xe7\xcf\x03\x00.^\xbc\xd0L\xa7\x1b\xa6\n\a\x0e\x0f]\xdf-\xe1l\x0f\xd9d\u040c\x13\u034b|\xf4\xba\x88\xe7\xe7,d)\x8b'\xb3\x0e\xfe\xa4\xc8\n\x95\xa0^\fv\x86`\x89r\xf2\xec\u0597\n\x10\x8d\x90\xd1O\xa9\xdc2\x98\xd9\xc5\xe3\xbf\x10@N\v\xa4\x84\x82.\xbd\x84\x87\x1a\x80\u04ce\u03c8\xc0\x1c(\u03dc6\xa3\xf2\xb2\xe8\x9c\aa\x10R\x85\x86Rm\x8aM\xd9\xe50y\x84\xa0\x8c\xb2\xab\xc5P\x05D\xaf\x1d\xe6Qx[\xc6\xe0\n\x86\x17\xe5];\x0e~[>lT\xc7\x0e\x1d\x01\x1e\x16\xdc0,\x11\x1a\x124&\xe4rV\xae\x88X\x11\x14\xc5\xea\xee\xb3\xcd\x14\xa8\xc4\u05fddc4\t\xb1|\x11s\x0f\x17\u018b`\xe9\x05\xb3\x85G\u007f\xe4`\x97\x02\xeb\xea\xefYN;d@Ae\xa7\xddy\xdf\x10\x1c\xa9\x1f\v\xdb\xfc\xba\x9d\a\x0e\x17\x1eK'\xd8j\x18\x9b\r%\xc36?82\xa9s\x80\xa0\xf3@\u702e\x03\xba\xa5G\xb7\xe8\xe1\xfa\x108\xed\x05f\xee@}T\ud19d\xaa\xa0\u062a\xca\x13\x90\xdeac\xeb,v\xce]\xc6\xec\xe0\x9a\n\x95(:4\x12\x11\x91;8<4_\xf9\u029f\xee\x01\xc0\x9b\u05ee\U0007a62f\xd7-]\x97/\xdf\x05\x00\xb8\xff\xfe\xf7\xbch\x8c\xb9\xd64\xed\x9d\xfd\xa2\xcb\xcf\x05\x11\xdcb\x8e\xc5\xec\x00n\n\xc8\x06CN\xfaB\xf4\xa2\xc5\\\xbcW\xe2o\xe9~H\xc8\x11k\x94'\x9a\x12\n\xba\x17\xc0\x05\u05fb&(\xf6R\\[=\u007f\xac\xbaX*\xf1p\xac\u01b6y\x19\xf4\xbf\xf1k\xb9\x80\x89\xfb\xe8\xa1\x12\xbbv\xfd;\uf05e\xb5\xa80<\x98\x18<\r\xa1\xc8Bu\xb0\x05Q\x96J\xa6\x02?\n\xe9Ws\b*\xe2\u0688\xea\xd7.\xe9\xe8\x0f\xc8\u0703\x0f\x1cp\x02\xf8>\x9ayi\x01\xf1\x84\",B\x92\xa8\xca\xce<\x16\x8d\xc7b\x9b\xd0A\xb0\x00`z\x825:\x18l\r\x05\u0719j%\xfe\x00W\x8f\xa9N\\0c\xbcD\a\u00c8YQ\xc2\xfe\xbd\bz'\x989\xc1\xac\xd7\xeez2w0\xbdO\x1d9\x95\x1e\xe0X\xfd\xfdD\x1c\x9a;\xa0\x9dy,\f\xa9\xf0H\xa8\"\u0590\x00\x8b^\xd09\x8f\xce\x01\x1bF\x1d\x19\xf1\xb2~\x8c\x00\x00 \x00IDAT\xbd\u03ff\xf7\xde\x01\xb3\xcea\xd1y\xb8^\xe05\xd5\x0e\xce\xeb\xc9\u0487\x19\x89]z\xd8pj\x90h#_\xdcx\n\a\xe9\x89\xcb-\xe6\x98l\x9e\xc1\xd9K\x0f\xe2\xb5\xe7\xfe\"5/\xf1\x04\xd6\u0616\xbf\xfe\xf5W\xf0\xe0\xbb\xef{XD\x1a\"Z~+\u0541u8\u0177\xc1z\xdf\xfb\x1e\n\xb38z\xcc;\xf7\xfc\xf6\xf6\xb6\xfa\xaf\x14\x86L\xbe_`v\xed58\xe9\xd0oY\xb8\t\x87pa\xed\xd0<\xbc\xfac@\xbd\xc3\xd3s\x1bR\\$\xfc9r\xc0L\u0228,:\xea!\xfb\rU\x06\xc0\xcaG\xd5\xe7\u0240\x12\x87S\x86~]\xe8r\x9d\xc0\xf7\x02\xd7+\x03$B\xdc\xec\xf5\xe3\xae\a\x163\xe0\xe4\x06p4\x03fN\xd0I\x0e7F\xe2<\x97\x85\x9aV\xe97$\xab\xaf\xbcP\xe6D\xf6\x8b\x17\x81s\x0e]\xef\xd1y\x8f\xbe\xf7\xf0\xc7\x02wBp\x1d\xc1{J\x9f\x1b_'\xc5\xea\x1a\xac\x03\xd8\x01\xdc\t\xda\x13\a\xd3\xe7!d\xe7\x05\xb3Np\xbc\xf48Zz\x9ct\x82E/\x8a/\x17\xb8\xb9H9\x89(\nZP\xcc\xc6k\x9c\xdf\x04\xcey,\x9d\xc7q'\xd8_x\x1cu\x82\xce\t\xcc\xc2\xc3\xf4\xa2F`R\xc8\xe7e\x04\f\v\x03W.\x14\xbev\xe9\xd1\u0303\xaf\x8a[\xa5\xd3pPv\x1eu\x0e7\xe6\x0e\a\v\x87\xe3\xa5\xc7I\xe7q\xb0\xd4?;Z\n\x16N}\u1f68\x8a\xd9Z\x03n4$\xda:\x82\xedC\x01\x17\x02\x87\x1f\x8cD\xe1\x1d\x16$=\x82x\a\xe9{X3\xc5\u079d\xf7c\xb2\xb9\v\xef\\:\xbfH\xd8\xd0Ofs\x1c\x9f\xcc>p\xfd\u06ab\x97\xbf\xd5\xea\xc0\xba\x98\u007f\x1b\xac\xe9t;\xbd\xff\xc0\xbb\x1f\x90\xdd\xdd\x1d\xb8\xbe\x97\x12\xb7p}\x8f\xe37^\x86_.\x81\xb6A\xbfi\xe0Z\n\x1d\"\x82\x90\xc3\xd5>\x18a\xe8T\xce2)\x14p\xa6p\xf4g\no\x99;\x1c\xe1\x199e\x92I\x83\x02\xee\a\x03\xd8!\xf7\xbdt\x00t\v\x8f~\xe9\u0447\xff\xfb\x98\xff(\x9a\u00e9\xff\x97\xf0 \x03\x98\t\xba\x03\xe0\xc6\xc2\xe0ZG8\xea\t\v\x1f%\xf4T\xc0%\xa5 ^*+\xf0\xe8\xab\x18i\x9a\xf1T\x12\xdd\b\x9d\xf3\xe8{\x87\xcei\x81u \xf4\vF7c\xb8\x9eV\x87\x8f\xe1\xd8\x1fO\ni\x18\x1d^\xbbY\n\x9a\x93\x9c\xd1Z\xb2F\x97N\x8b\xfa\xc1B\xdfN:\x85,\xa2\x16)\xbfI*R^j\xa6J,\xee\xcb^p\xb8\xf0\xb81\xf78X8,\\\xa0\xeby\x80;\x0f^\x86k\xeasW\x9eSz\xa4\x1e\x8bH\xa9\xc2\xd7\xe1g3\xf3\xb0s\u5967\xc3P\xf1\u03e3\x1dD\xe7\x04'K\x8f\xa3\xa5\xd3\xcdj\xa9\x1bX6\x0fCrP\x8c7\x1a;\xc0v!{\xd5\xd7\xe0\xbf\xf4\x1e\xe2\xf4M/\x8c$\x9bg\xdf-\xb1{\xee2\xce\xdcy?|\u07e5P\xedxk.\x97=\x98\u0307\u007f\xef3\xff\xfa\x01\x00x\xf4\u047f\xfa\x96\xc1\xcd\xd70\u02f7\u067a\xfb\ueedfi\u0513\x99\x04\xd9\xe0\xd9\xf7=n\xbc\xf8,\xdcb\x06\xbb\xb5\x8b\xaea\xf4\xdb\x04C\x0e4\xcfT1\a\a\x8e\x14<\x92\x14zL\x05K,\x9d\x95\ubf00\x02\xad\xa0\xf4\xac3j\xd6\u02f0\x1b\xc7\x00F\xa9\x1a\xdfb8\xd7;\xc1\xe2\xc4A\x0e: \xf0\x9e9\xc8\u096a\x109L\xc2\a\x8c\xdd\xce<z\u00d8o2\x16\f4\x0e\u0630\xc0\x86\x01\x1a\n~\x88\x15\xb6\xe2Wp\xe8\x01\x05^\x8b\xa4\x0f\xd8wP\x18\n\x18=\x18]G\xc0\xb1\xa0Y\xa8\xa4|\x84\x89\x9e\xe6\rTX\xf8\xa6k\xdd{4\u01ea\n\xed6x\xc5\xd4\xd0\t\xe0D\xb1\xe4\xb9\x13\x85_\x98\xd0\x18Bc\x82M1Q\xf6='d\xf6\x87\xd3\u03bbs\x82y/\xe8\x00\xb5h@a\u0535p\xb03\xddTHj\u06c3\xd1\xe94\xb279\x87_\x98'\xc0t\x82\xf6$\xb0[lpQ\x1c\x8a\x82\v\xae\xb8/\xf4\x11%\xd5R\xfd\x82(\xc1~\x1cpr\xd3\xf9\x9c\x13\x1d\x8a\u007fd\xd8\b\xa5@\xbc@\x99U>\xbf\xef\x97\xd8\u07bb\x80\xf3w\u007f\x00\xaf_\xf9j`\xf4P\xe2\x9e\v\x80W_\u007f\x03\u007f\xf5\xb5\xc7.\x02\xc0\xfb\xdf\xffAY\x17\xf3\xf5z\x87\xba\xf4\xe9\x1f:\xe7~\xa1i\xec\x8ew^}\u02a1\xc7\u0323W_\xc2rv\x8c\t\x1b\x104,\x19[\xd0\xe0\x85\xb9\x1e\x93\x1d{\x80\x03\v!\xa3\x00\x11W\xc9L\x16\u0285\x8e(\x17x)\xcdGiP\a\x06\x05{\u0629\x97\x03\xd0\xe4+\xee\xb5\xe8\x1c\xcd\x1c\xfa\xfd\x1e\x93\x13\x8f\xb6\x0f\x1d#ibO5[\x95\xb0\x01\xf9\u8ded\xf8\xf9\xf4\xa4\aL\x83~\x83\x94E\xb2\x04f\x860eB\ub056\x05\x86\x82*r\xe8\xeb\x8e2\x18\"\xc0\x13\xa1\xa0\xc6b\xe2\xc0\xe8`\xb0\x14\x03\xcc<\u06a5\x86YP\x1c\xbe\rmi\u00b55^\a\u007f\x1eY|K\x02P\a4'N\u00ec\x1b\xca\xf4\x93\xc1\xcb\x12\xd1n\xbds\x02\xe34\xf9\xa7\t'%\x13\x8d\xc8\u0086\xd8-=\xbaN\xf9\xdf.\xea\x97L\x10|E\xcfz/\xb0s\x0f\x13^\xff0R\xaf\u0718\xaa\x01w\xf8\x05s\xfcf\x81'n\x96\x1evF\xe8ZN\xa7<ZqX\xf6yn@R\x8b\x83H\xb7f\x82@X\xc0\x8e`\x96\x00\xbb\x1c\b\u0392\x95\xc0\x948\xec\x94N\x89y\b\x1f\xaf/\xe1\xcc\xf9wac\xfb,f\x87o\x82\x9biz-\xc6\x18\\}\xf1%|h\xfe\xc1\xef\xf8V\x13\r\xada\x96o\xb3\xf5\xc9O~\xf2\u037d\xbd\xbd\x05\xb3J\u0563k! X\x1c^\xc7\xc9\xf57\xb2\xc0E\x00g\x19\u0766\x81\x9b\x10\x9c\x01<\xc5\xd2\x12n\x10\x8fp\x94\x95\x14\xdb\x06_\xfb\x84\xa4\x87RV\xc5\xf3\x83:[\x9b\xf6\xa1\u019d3.\xadP\xc0\xb2W(\xe1\xc6\xc2\xe1d\u1065\x87q\xb2\xf2}V\x1bFI\xd49\n\xbc\u0148E\xdb.s\xab;'8X\b\xae\xcf\x04\xd7g\xc0\u0452\xb0\x14N6[\x92\u07b40y\xaf\xc7\xffX<E\x00'\x84%\fN\xa4\xc1\x89X\xb8\x0e\xda\u057a\x9a\xcaW\r'\x91\v\xa7\xe9\x14\x9b6N\xaa \x10\x88\xc0,\x14n!\x87\xcc\xef/\a\x8e\xc5\xff\xa1\x89uX8\xc1q\xe7q\xb8p8Xx\x1c.\"$\x13 \x8c\x85\x87\"p\x83IF`\xf5p'\xb0s\a\xee\xe54\r\xd7Hg\x9eOF\xc9Z\xc1k\a\xcd^\xd9-f\xe6\x14;/\u0607\xb9\x1f\xf0\xc9?eh%\x11\xe3\xf0b\u03a7Y\nl\xef\xf3\xcf\x1e\\(\xa3ChY|%\x85r\xf8j\aq\xcb9vo\xbf\a\u06f7]B\xdf-\xc2\xd0\u06a7(\u0093\x93\x19\xf6\xf7\x0f?\x02\xe0N\x00\xf8\xf2W\x1eY\x17\xf3\xf5\xba\xf5\xeb\xfd\xef\xff\xe0\x17\u039d;\xf7\xc6\xce\xf6\x0e\x9c\xebU\xea\xac\xe7N,g\xc78z\xf5j\x90?\x1bm\xb6Az\x9c\u07f6\xe8'\xac]\xa2\xb8\xf4`\xc6\xeeRz\xed\x92{\xe5\xbd\x148l\xae\xc2%t24&\x1c\x1bl\n\r\xc2*\x82\xc4z\u953ev}\xeeq\xb8\xf4\xe8|P\xe9{$\fw\x05Q\x1f\xa8;S7\x16pt\b`\x16\x1e\u0371\xd2\xd8\xe2 \x17\xd0\xc2|\xb4\x10\\?\x11\\\x9f\x11\x0e\x97\x8c\x85#\xb8\x98?\xeaU\xe0\xd3y\t\xb8\xb8\x1e\xfb;0N`q\xec-\x96PFh3\xf30]\xc0\x88]\x06\x94ip@I#WO\xa1\xf0\xe9\t\x82b\a\x1c\nU\u011d\x87\x85|Xai\xf0u%\xc01K'\xe8\xbd$H9\xf9}Sa$\x16\u007fg\"03\x9f\u1555\xcd2\xb3\xf3\x05\xa7\x15z\xc9\x03^\xe8\xef\xca\xf6\x1e\xed\u0301;\xc9\x12\xfd\xf0\x83\x88s\x90\xde%\u06a4\x1f1<\x8b\u0775Y\uad48\xa9D\x14\u007f\xb7T\x06\x87\x87\xfb\xc8\xc7B\x1emh\xf2=\xe2\x9d\xc3\xc6\xf6Y\xec\xdeq\xb7f\xb9\xa6\xcdDW\xef\x1c\x0e\x0e\x0e?\xfa[\xbf\xf5\x9bg\x157\u007f\x9c\xd6\xc5|\xbdn\xd9z\xf2\xc9\xc7\xc2\xf3A\xb3\x87\x1ezh\xbe\xbd\xb3\xad\x0et1a\x86\x80nq\x827\x9f\u007f\x02\xde\xf5\x1ay\x96\xfcE\b\xd22\xba-\x037\t\x93\u007f\u7a87\x99\x9c@\x16\x1e\xae\x0f\x98-\xb2\xce9u\xe8\xc9f\x16\u0574k\x14B\x89\xb2\xf1\xf0q/\xda\xf1\x1eu\x1e\xd7\xe7\x0e\xfbK\x8f\x85\x93\u031c\xf1\xa2\xec\x8a\u00a8\x8ad\xd0\xfb\v\nA)\x15Gp\xfd\xbb\xd8%\xdaY\x96\x84S\x81 9/\x98-=\x0e\x16\xc0\x8d%\xe3\xa0c\x1c\xf7\x8aK/\x1c\xd0{RL\x1c\x063\xb18\xf6\r\x16\xde\x04a\x8b('{\xe1\x146\x90|\xe4\x90\xc1IE\x068\t\xf9\xf0Vp4\xd3|\xd1iwn\x96z\xc2\x12)q\xe6\u054d\x92\x06oLyp\x9dy\xf2\x94<\xc5\x11\xf4\x02B\x00/=\x9a\x99\x039\x19d\xac\xd6g\xad\xfc_\x19?\x19\x85\x91\ny\x81\xf1\xca\u04b1s\x81=v*\u02e7|2\xf4\xce\xe9\xfd&\x1eU<w\xa8\xec\xe9:t\x1ev\xe6\xd4`-\xc4\x05r\xf8\xbf!\xceXy\x11\x8f\x17\x1d-\x93\x127u\xec\xfaL\xec]x\x00\xd3\xed\xb3\x10\xd7W?\x87\xf7\x82\x17_zi\xf2\xda\x1bo\xbc\x17\x00\xfe\xb3\x9f\xffyY\x17\xf3\xf5\xbae\xeb\xc1\a\u07d7\xde\xdf\xde\xde\xfa\xacZ\xb4\x1a=>\x86\xc1\xa0[.p\xfd\xf9'\xd0\u03cea\x8c\xa9\ak\x02HCXn\x19\xf4\x8d\xde\xf0\xa9\x93\x11\x1d\xf6a\xee\x80\xc3\x1e\xbe\x93@ P\xec\u060b\xfa\\y_\x02/\xf5\xf0-\xf1@\x12\xcb-\xc0\x14\x01\xb68\\\n\xae\xcf=\xf6\xe7^y\xceR\xe6F\xaa\xf5*\xf7yhXU3\xd4\xd9qe\x1c^\xb4\"H\x96\xb1\xbd\xa0\x9dy\xd8P\x1c\xd3\xeb\x93\x1c\xf2 \"Xz\xe0\xc43\x8e\xbcv\xde3o0\x13\x83\x13X\x9c\x88\xc5B\f\xfa\x98\xfaN\x00\xf5\x80\x9d9p\xa0\xe1\xb1S\x88a$ \xaf\n4\xa3\x04I \xb1q\xaa\xd0\v\x00\xa6\xf3\xb0\x01n)\x9fX*\xf1w\u0526\u00e8\x1b\xd5\x02\u007f\x1e\xf9;\xaf\x83\xe2&\f=\xd9\xcb*l5J0\xad7\xd2*z/\x15\xf4\xf0\xf3\x85M\x89\xe7>o*\xde\x01\xbe\x0f\xf7\x1a\x06\xe1\x1b\xf9\x9e\xa1\b\xd5t\x92\xba}v\x8a\x93s\xc4\xca#\x93%\x06G\xa7$\xa2\xb0\x891U7\xa3w\x1d\xce]x\x0f6vnG\xdf-\xf2s\x02\xed\xe2_~\xf95\xbc\xfe\xfa\x9b\xffa|-\x8f>\xfaW\xebb\xbe^\xb7~=\xfc\xf0w>\xb2\xb3\xb3\xdd5\x8dzP\x88wZ<\xbd\xc7\xc1+W\xb0\u007f\xf5\xa9\x9c\xb2\x1e\x8bvx\x13K\xe8\xa7\f\xc7\x01;\x17U.\xf6^\xd09\xa0?\xec\xd1\xdd\xe80_(7\xf9h\xeep8s8\x98\xf58\x98{\x9c,\x04\xb3\xdec\xde\v\x96}\x80%\x9c\xaa4]`\u007f\xf4^\xa1\x94y'8Xx\xbc9s\u061f;\xccc\x11G\x9d\x0fMPf\x04\xfb1,gP.\x05+JL\xf2\xc1\x9b;*\x06\x97\n\xb7p\xef\x87_\xa4*\x86\x19\x13g,`\xb0\x80A/\x9c\xb4\xb2E\x9e\x05\x9a\xb9S\b\xc0\xe5b\x13\x8f.C\xf8\xa3*\x95\x9c\x95\xba\xe4\x03-\xb0\x1c.\x04\xaby;\xf7h\xe6\xaeH\x10\x02\x06\xc9\u007f(\xffKE7N\xa5\x9d/e+\x83\xf2E\u0645v\xe5\n\xf5\x10\x86{\xf2*\u06fe\x18SS\u044dK6k\u031b\x8dRE\xcd\u00a39r\xaa\u0685\x069\x8b\xf39\x1c\xa3\f\x9e.6\xaaf\xe1`\x17>\f\x8cI-\x1bb\x17\x1d\xe9N\xd5\xc9 \x8cU}Q\xd0\xc3&\xedS5\x17l\x9d9\x8f3w\xdc\x03b\x0e\xf3\xa5\xc0.\x12\xc1\xa2\xebppp\xf0\x9f\x1f\x1d\xbcy)\xc0\x97\xebb\xbe^\xb7~}\xfa\xd3\u007f\xf3_\xbd\xfb\xdd\xef\uedb7\xb6 \xe2\xc5\xfb\xc8Y&\xcc\xf7\xaf\xe3\u0367\xfe2t\ub738\xd2U\xe0/\x03\xdex\x1d\x86\x86\xa1gT\b.\x17\x1e\xcb\xeb\x1d\xe6\xd7:\x1c\xcd\x1c\xf6\xe7\x1e\xfb3\x87\x1b3\x87\xfd\x93\x1e\u05ce{\\\x9b9\\\x9f\xe9\xe0\xf2\xc6\xc2c?\f\xe1\x0e\x97\xf5\xdb~\xf8\xb3\xa5\x93\"\x86\xae\x1e\x98\"tb*`\x89\xbe\x1b8\u0144\xe4\x94a\x9dW\x00\x99|\xaeLv\xee\u041c\xf8\"<a\u012e\x97\xea\xa24\x9c\a\xc4a\xa5Y*\xd6\x1c\x8b\n\x87\x82N#\x18D\xad1\x1d|\x14:d\xf8\"\xb0)\xc2\xee\x0e\xb0'\x1e\x1c\xad\f\x06\xa7\x90\x95\x92\x9b\xe6\x19y\xa0(E\x98\xb1 \x17M\xee\x05\xf6D\x19,\xec\xe4T\x81\x00U\x00}\x9d\x105\xac\xef\xd5\xec\x02\xb9C7'\x0e<s)d\xda\a\x88%u\u5147<\xc5k{\xe2\u00c6L\xba\u0641T\xd9K\x9c\x99Ta\xf8\xc9E89\x0f\x9d@\xa9T\x88\xea/\xf0\xf6\xcb\x0fa\xbay\x06\xe2\xfbt\n%f\xf4]\x8fG\x1f}|\xeb\x9f\xff\xce\xef>\xfc\xad\xf2\u072f\x8b\xf9\xb7\xd1\xfa\xdc\xe7\xfeM|\xf7\xf0\x81\a\x1ex,\x18\xec\x93\x1e=\x1d@\x8c~~\x8ck\xcf?\x0e\xd7-S\u01e2\x1d\xa0\xd3\xf3\xb6\x0f\x82\v\x118xx\x1e\x1c\xa8\xc3d\u035c8\x98\x13\x95\xd5\xfb\x88\u007fz\x95a\xf7=\xb0\U001024e0Z<(D.\x87\v\x8f\x93\xa5`\xdey5\x99Bf<\x8e\xc0\xb3\t\x1aQ\x88%;\xf5\r\xd9\x18\xc3\x0e\xbb\xa2\u03608\xf2\x17\x1bDs\xa2\xdd4R\x105a5e3\xe3\xd442\xc5$\xafE\xd6tR\r.\xc9I\x8d\x8d\xaf\xbc\xea1P\x04`\xef\xf3\t\u0123\xb0\x8b\x11e\x9a\u031c\xfa\xe9\f.\x97/f\x18\xe3_y\x14\x91\x82\b`\x16N!\x90N\xf2fYz,\xa4\r`0\x9f(\xbd\x04\u02bc\xcd\xc4%\xaf\xbfQ\xb4\xca5'\x0eX\xf4\xa1y\xf0\xa9\xa1\xc8\n\xd6p\xedz\x0f;\xeb\x95YS\x0e\xbf\u3f03\xa0\x05\x9d9\xf1\xc4KK\xa2tm\x12S\x86R\x8c\\\x9c%\x9d\xbb\xf8^L\xb7\xf7\u0f7e\x9eHGt\xce\xe3\xf5\xd7\xdf\u011f\xfd\xf9_\xfcd\xfc:\u007f\xf0\x87\x9f_\x17\xf3\xf5\xba5\xeb\xe3\x1f\xff\xe1\u0521L\xa7\x93\xff}cc\n\xdb4\x19\x13\x06\xe0]\x8f\xa3W_\xc4\xec\xcd\xd7@\xe0\u0288\x88\xc4\xeb0\xc8\xf5\xc1|\xcb\xc3G\xbb\\\x1e\x84\xe0:\xf5\u07d8\u0303\xcf4\xe5#u\nA\xa6\xfa-w\x87\x92\xa9u\xc3r\xb7\x12@\xa4t9v\x12X,\x85\xb0u\xd8\x05\x16\xb62\u547f\xac\xca$\xb9`q\x1f\x86\x8b\x9dO\xf4MB\xa6>&D\xa7t(\x1c\x90<x\xe9\xc1\x91S\x1ep\x17\xf2\xc3fYN\xd9t\n\xbb\xe2\x02xfW\x84\x82\x94?c\xe4\x80/|\xbd\xd9\r\xdeV6\xa3\xd3pn\xa2\x84e\x9b\xa5W\n\uaa7b\xc0\x88)\u02d8A\v\x06\x9e\xf6\xc3Y\x86\bx\u0583\x8f\x97jX\xe3\xa5\xe6\xa6\"_|;\xd7\xd9F\xb2\n\xf0\xf5\x04=\xbf+\x03S\xb6\xda\xf63R\x16c2T\x9a\xb9x\x87\u035d\xdbq\xdb\xf9\xfbA\xa4\xac\x96b\xfe*'\xb39\x9e\xbfr\xe5'\xfb~\xf6>\x00\xf8\xc1\x1f\xf8\x18~\xff\xf7?\xb3.\xe6\xebuk\u05f9s\xb7\xff\xf1\xddw_F\xdb4U\xcc\x16\xb1\xc1\xec\xfa\x1b\xd8\u007f\xe9Y\x95H\u01ee(\x98ei\u01e2]\xbab\x8c^9\xce<pA\x14e*\xb4s\x8fvQ\xc3\x15\xe2W\x9fu*\xe0\xe1*\xc3\x19\x18\t!\xce\x0f&y\x81\xe9}`X\xc8\xc8\xe0M2\x1c1L\xd5I\u07ae\x19;\xa7\x00x\xc7\xee\x91{\x8ff\xa6Xw\x15\xcb&\xa8L\x14\u01ecz\xc9\x03\xcdB\xd2\xd03\x16\u0728F,q\x109-\"o\xe4''\x0fp\x1f\xe1\x1a)\x95\xf3Z|g\x1e\xa6\xaf;\xechx,2\u019a\xa9\xf7\x8f2T\xa3Yz4s\t\xbcr\x19\x84\x84\f\u01b6oa\xaf\xbb\xe2r\x9c\\\x12\xa8\xb6F\xee:-\xe6K\x97\xe62)\x05%|W\xc5\xf0%\xcd;8x\xac\fO\x00\xf1\xd4\"\x85\x1d/E\xf8%n'Q=*R\u51ca\xf7\xb0\xb6\xc5\xc5\xfb\xbe\v\xcdd#\xe5\x8c\n\x00f\xa6\xa3\xe3\x13<\xf5\xd43g~\xe37\xfe\x97\xbf\x15\xbf\u0697\xbe\xf4\xa5u1_\xaf[\xb3\x9e|\xf2q\x00\xc0\xe5\u02d7\xaf\xdeq\xc7\x1d/\xd8@A\x8c\x8f%\x1b\x83\xf9\xc1\x9b8\xb8\xfa\x14L\xc8pL\xc3\xcfp\xa3\xfbP\xd0)\xc0.>(>c\xaaL\x1c\xf2A\xa0\x05}\xe6\xd1,\xfch\x81\x1a\x0e\xff\x860\x80\xc88\x04\x90:{\xa7\xf0\x822=h\xe0'^\x1f\xab\x85\xea\xae0\x16\xfe\u0215O\xf3\xba\x82sL.\f\x17\x17\xbeN\xfe\x19r\x97\x87\xb5+\xe2\xb9K\x9f\xd9\x1f\x82\x84\xeb\x0f\x85Q\xd5\xd7\xe128{\xac\xf4\xea\x06\xc1\x9d\xcf\xd0G\xb1\x91f\xbeuY\u0425F>\n\xf8E\x06\u05d7\n\xf8J7\x86|Z\xa1*\xf7\x94\xea\xd3C\x89\xa6\xac\b\b\xa4\xee\u02a5t\x12\x0e\xb4J\x0eZO\xe7\x82e\x80\x0e@3\"\x13\xe4\xfa\x9d\xc0\xc4al\u0090d\xe5^-\x19WD\xa5NW*x'\xb2[\x12\x9b\xcaG\u058a\xee\xe0g/\xbe\a;{\x17\xe1\xbd\xcb\x1b\x1a\x11\x8c\xb1x\xf3\xcdk\xf8\xccg>\xfb)\x11\xd9\x01\x80\x8f}\xec\xe3\xebb\xbe^\xb7f=\xf8\xe0C\x00\x80\x9f\xfa\xa9\x9f>\xd8\xdb\xdb\xfbB\u06f6`\u00c1k\xabC\xcf\xe5\xc91n\\}\x1a\u02d3\xa3\x18\xe1[\r\xb1H\x04pN\x99\x06\xe2A\x81s.&c\xc2%\x83\x80{\xc1$\x14\xf4lBU?\xectJSJ\xa7\xb4\x91\x11\xdePJ\xe2\x00.\x19<\xb0\x82\xa2\xed_\xc9o\x96\u02ba5}\xdf\xc2@\x8a\x05hO<\xec\u04af4\xa1c\xf0/8\f\xf3\xe6n\u0415\a\x16\x8b\x8c`\xf9E\x9b\x1f\v\u06eaD\xb6\xa8\x86\xc1k\x9e\x9d\xa8|\xdd\xe7\x93\a\xf9P\xec:\x19\ud307\x8c\x9c\xea4Q|\xbe]ze\x89\xf8b\x17\x93\x9b\x1f \xe8\xb4\x1dz\xe0\x9f^}\xef\xb8\x11\x10\xb41\b\xc3\x15\x9e;U\xbe\x96\xa6\x95\xc1K\xc7v\x92\xbfF0\u02eaY/>\xe1\xec\xbe4v\v\xc5\xdb\xc7\x01k\xf8\xfb\xf8\n\x15\xf2\xe3tO8\xd7a\xb2q\x06\xe7\xef\xf9\x0e\xdd\xfc\xbcK/\x9c\x8d\xc1\xc1\xe11^}\xed\xb5\xef\xfbG\xff\xe8\u007f\xfay\x00\xf8\xc1\x1f\xfc\u063a\x98\xaf\u05ed]Dtt\xd7]w}\xfe\xe2\xc5\v`f0s1\xe8\x13\x1c\xbc\xf4,\xf6_z\x16\xc6\xd8,n\xf1>)\xe7\xbc\xf8<\x14r\x1e\xe4\x94\xc6\xe7M\xb0\xc1\x1d\u0d26WS\xa5f.\xc1r\x14+\x18\xab\xa1\x12\xd7\x1cU\xe1\xd7E \xc8\xf0\xb9\xf7\xa7\x88XV\xc1\xe1\xb2\x1e\x92\x14\x03O\xc9\xe3M\n\x02\"vY\xb0c\x9c`\x12\x06\x99+\xf3\xbf\x016-\b\xdd\xf1R\x8a\x84\xc8\xe0\a\xe3d\xe5\x1fJ\xb1C\b#\xa5<\xd5s\x88\x81\xca*b\xfeN\u007f~^\xe6\x0e\x9dD\xa9\x9a6\xc0C\xa3\x90\aF\xfc\xb1\n\t)\a\xbe\xbdv\xe5\xa7\u007f\x8dr\x97X1\x1b\x1b\xd1l\r\u060ai\x86\x13?\xe2 J\x10\xa7\xb3\x06\xbb\x90L\xf1\x14\x9d\t\u0639\xb2W\xf4z\x06\x8f\x98\xd8]\x87\xc1e\xa4\x12\"D\xcb\xd5\xd9\xdbTZx\xe6{\xae\xb8\xc1\xa2\xea\xd3\xf5\x1d\x8cmq\xe9\x81\x0fcs\xe7|\x8a\xac\x8b\u0670ll\xf7\xd2K/\xe3\v_\xf8\xd2/\x8a\u021d\x00p\xe5\u02b3\xb4.\xe6\xebuK\u05af\xfc\xca\u007fE\x00p\xe9\xd2\u017f\xb8\xef\xbew)\x9c\x12&\xfeD\x00\x1b\x8b\xa3\xd7^\xc4\xfe\x95'\xc0\xd6Vj\xf8H\x0f\x13x\x888\x888\xc0\xb9\x14\xa8\x9b\n:\x90\x94\x82\x1c\xe0\x05\xd3\t&'\x0ev\xa6V\xba\x15d1H\xa5\xf7#X\xb9\f\xba9v\x81\xc5\xe2O\xa3\xcb\xe5b(R\b\x8aV*L\xd1*\xa6D\x8b\x88o\xe7\x0e\xddvZ\xe0J\xd1\f\r\u00c4(C3\xe4jy+W\xcaI\xaa\x8bz\xd9\xd9S\xd1\b\x13%\u07d5\x15nf\x81\xfbG\x0f\x97\xf8\u0112\x00v\xe1t\x18:\xac_\x18H\xff\xa9 \xf6\x84\x8d\xc4v\x82&Z\xc8V\xf0\x04\x9d\x82w\xadZ\x13\x97\x11v\xe9\xe4B\x83kV~\xc5\xe81\xe0\xf3\xa0\xd8,\u00bc\x82\tv\xa9\xe2 .6\xad\xfc&\x89:\x1b\xa3\xb2\xa9\xb0\u01e5p\x8f\xb31\xfa1(\xdd\xf3\\\xec\xf4\xe2k\x1e;\xe0\xe1\\\x87\x9dsw\xe3\xc2\xfd\x0f\u00c7\xe1\u007f<\x1a6\xed\xc4^\xbb\xbe\x8f\x17^\xb8\xfa\xf0o\xfd\xe6?\xfeY\x00\xf8\xd1\x1f\xfd\xeb$\"\xebb\xbe^o\xff\xfa\xb9\x9f\xfb9\x01\x80_\xfa\xa5_|\xf5\xce;\xef|\xacm\xda\xec\xdd\x1dD\x14\x8b\xc3\x1b\xb8\xf1\xc2Sp\xf3\xe3\f\xc1 \xe3\xe6)\xbd\u073b\u0421\xbb\xc0\xb4\x90\xba\xdb\xf5\n\x85\x18'\xb0\xbd\xa0YxL\x0e\x9dJ\xe6\xe3\xb16\u05e4\xf1\x8e\xfa\x94\x8e\x90]\xe0\x97\xdf\xec\xe8_\xbd\x16I^\x1d2\u0491&\xaf\x16\xd4dA*\xbeV\xbb\xd0P\x05\x19qy\x8c\u03efYFW\xc1\xac\xd8DE\x9b\x1b\xd4\xf3\"T5\x16:\x1e\xebn\xe9\xf4\x83\a\x05\xf6\x8dv\xe2\xc1\xaa\xa0W/\x18ve\x98\xf7MN<\xc5&\xd9,|R\u050e\xa4\x88\xdc\xf4ZW\xaf\xad \xa3\xa4\x82=\x8c\x8d\xca^\x0f\xd9K\x05ZX\xa9Sv\x8e]\n\x9a\xc0\xa3\x1fK,\x11`esY\xb9FD\xf5\xe6\x01\xaa\xff,J\xfb\xe3\xe6D:\x87\xe9\xbb\x05\xect\x13\x17\xde\xf5\x1f\xa0\x9dn%\u07bb\x04\xdeU\xd3N\xdd\xd3O?\x83/\u007f\xe5+\u007f[D.>\xfa\xe8c\xfes\x9f\xfb\xb7f]\xcc\xd7\xebm_\x1f\xfa\xd0w)&j'\xcf\x186\xff\xfc\xfc\x9d\xe7\x035\x91**\xd7\xf5\xe7\x1e\xc3\xd1+/\xc04\xadv\xe0\xa5\xf1G\x14S{e\x1c\b<\xc8{eYt\xf1\x18\x1ce\xeb\xc1;\xc5i\x81\xb1\v\x8f\xc9A\x8f&J\xb7\x8b\a\xcf\xd7\xf4o\fO\xc7\xe5\u0411:\r:&\x8f\x82_>\xa80\x05$\x91\xeb\xd6\bH?\x14\xfc\f\xb1\xf3\xa285sI\xf8\xb9\x14\xafKB!\xb4\xa1\x9b,a\x1d\xf6\xa5(iX\x1d\xa9\xda`(%\xe2\x14\xf0\x0f\x8d\xb4\xd3\xe5F\x83<\f.C\xb2\xb9\xd3\xd7s\x1a#h\xac\xb8\x9b\x0e\x85@\xe8\x94O:\xfd\x0fW\x85U\x18\u02cf\x1a\x9c\xc8(\xc7\xe3\xb1\xcfg\x01\b\xd0\xcc=\xda\xc3\xc0s\x97b\xd0.\x999U~C\xa9\xe1\xc4B\xec\x15\xa1\xc2\xc8]\x8f\x12\xfd \"\x8a'T\xa6,\xff\x0f\xafD|\x8f3\xe7\xef\xc5\xd9K\x0f\xc2\xfb\x0e\xd9\x17\x18h\xda\xd6\\\xbb~\x03O<\xf1\xe4w\xfd\x93\u007f\xf2\xbf\xfe4\x00|\xe2\x13?,\xebb\xbe^\xb7d\xfd\xea\xaf\xfe\xb7-\x00\\\xbct\xf1s\x97.]\xd2b\xce\xf9\xcc\xcd\xc6`\xff\xc5gp\xe3\xca\x13`c\xab\x87(S\f3\xd4\"\u0781\x9c\x16\x00vJ\xe9\xe3h\x0eUL\xdd(\u061e\u0699C\xbb\xdf\x05N\xf4\xc0s|\x80qc\x88\x84\x14\xf6\xb0\xb4\xc2C\xce_\x84N\x03\x89cM-\xe3L\xab\xe8\xa2\"^,\xbc\xde\xe4\xa5\x12\xbeo{\x1c\u0516\xa8\x94\xf0\u02b4XH\xe5\xd8WBN\xe3\xfc\xbcS^\xa3d:\xa0\x10\xad\x0e\x15\x8bo\x1e1e\xd3\xf9`C\x90\xa9\x8a6\rC\x05r\x93\x1a\f\xd2Aj\xb3Tj#\xc9i\a#y\v\x10\xfe\x94\xfa\x9e\xb8\xfa%\xf5\x05\x95\x8fx,\xd2\x19W'\x95\xf9\u03dc\xdeO4vB\xc9>\xe8\xc4\x14f@Y\xe1)\xc3\x13\xc9\u0419\x11\x12\xfc\x85\xa2w\x8b\xcfP\x1f\t\xd80\x9c\xeb\xb0y\xe6<\xce\xdd\xf5\xbej`\"\x04\x80\x19M;\x91\xc7\x1f\u007f\x02\x9f\xff\xfc\xe7?%\"[\x00\xfco\xff\xf6\xffA\xebb\xbe^o\xfbz\xf7\xbb\x1fp\x00\xf0\xf7\xfe\xde\u007f\xf3\xdc\xed\xb7\x9f\xfb\xea\xe6\xe6\x14q,\x19\x8d\xb7\xba\x93C\xbc\xf9\xd4_\xa2;\xba\x0e\xd3\xd8|T%$\xc3\"}\x8b\xb8\xb9\xcb<j\x8f\x94\x0f\x19\xbdT*\x8c\xd3++\xa1=\xec\x83(\x87\u01a1\x84A\x8dM\xf4\xe4^]\x12\xcb\xd82:\xad8W6\xae\xa7\x15\x1fZ\xc1{\xd3\xd7\r\x83\xcbx\u02a0\xe4\xdf\xe2\x03\x8b$p\x8f\x03V\xae\x94\xb9\\\xa9J\x19~]~j*\xcf\n\xa4!c\u00c2\x12`\xc6`\xf8JI\xec\xa4\xddm\xf8\xb8\xd3\xc1\xa1HM\r\x94\x91Q\x83B\x1a\xc5F\fy\x8b\xe6{`t\x83\xdaO^\x863\x89\x91\xe6>\x0eX)am\xd10Ka\x16\xea}\xba\xa7J\xe8>\x9d\x98R\xfa7W \x8a\x14\x9b\\:qb\x10[\x18\xed\f\xbc\x87w^\a\x9f(\xf3R)\xcd\f\xc8Z\x9c9\u007f\x0f\xa6\xdbg\x83\xcd@\xaa\xf80\xb6\xa5\xfd\xfd\x03<\xfb\xecs\x9f\xfe\xb5_\xfb\x1f\x1f\x06\x80\x9f\xf9\x99\x9f\x95u1_\xaf\xb7}\xfd\xc2/\xfc\xa2\xbb\xed\xb6\xdb\xccd\xb2\xf9\xc4m\xb7\xdd\xf6\xff\xdd}\xf9.\xf4}/\u027c?@\x02\xaf=\xf6e\x1c\xbe\xf4,l3\t\xceq>\x1dU\xe3\xfb\xd2;\xf8\xbeW[\\\xef\x8b\xe3o-\x91/\xebP,\x90f.h\x0e\x1dh\u9aeeQd\xbcq\x8d\x0f\xbd\xe9$u\xbb4Zf\x8ax\f\xa1\x9a7\"7\xb9\xbb\xa5\xc6\xc8++\\\xaf\x05\x9b}\x10\x13\x85anb\xb0,\x94}\x81\"H\x19\xa1;&?\x024\xc8\xc8&\x82z H2\b`\xae\x03W\a\xf0svd\xa4\xbe\xe0\xca{\xa8\x0f\xf9|\xd5\xd9+A\xc4\x1c<X\x82\xfb\xe0\u0403e\xf8\x9e\x8c\xee\x9bTAF\x84\xa1\x9fM.\x8c\xa3\x9e4)\u03142S\u01c5T\xab\x18\x80R$\x03Qq\x02+1p\xfd\xbfY1\xd3)\x83)\xca\xc6$\xaa>\xa9\xf8s\x1a\x9c\x16\xc5\x10z\xe9\xb1{\xe7\xdd\xd8;\u007f\xafr\xe1#t\x13\u00a4A\x8cg\x9e}\x16\xaf\xbc\xf2\xea/\x8b\xc8\xc6\x1afY\xaf[\xb2\x88\b\xbf\xfe\xeb\xbfF\x00\xf0S?\xf5\x93\u007fy\xf1\u2965xob\"\x9a@\r\x85N^\xff:\xae_y\x02\xd2/a-g\xe7\xba8\f\r\xc5\\\\\x0f\xdfw\u06a5\x8fv\xd6T\b\x8f\x8a\x01\x97\x17\x98\xb9\xba\u5c53\x95;N\x06tD\t\x1d\x99\x06<\f9\x8c\x94\xc3\x1bP\x93\xffd\xb5\x8dEYOO\xeb\u042b\xfa\x19\x1c\v9t\xda\xe4\x94niB\xb0\xb1Y\x043\xad\xd8\xfe\x06j!\xb9\x01\xbd\xaf\x1c\x02\f`\xa5\xea\xba\xd1\xc8\xfb\xe5\xbf\x1b\x1c[\xa4\xd8\x00L\x1f\x95\xa7\x92\xb1\xf3Y\x98e\f\xf6\x85\xb8!\xc488._\xaf\xe4L%\f\x83\xa7Qdq\xa6\x8f\a\x89Ho\xb9Jf\x8aT\xc1\xcf)t[\n3.h\xf0\a\xa1\xf0\x81\b\xefk\x016 6A\xe5i*A\x9c\xe6\xd4r\x1a\xf6S\xe1\u0260\x11\x82yc(\xad\x99=A\x83\xa2\x8d\u00d9\xf3wb\xef\xfc]yK\x8a\xd0\f1\x88\rn\\\xbf\x81\xabW\xaf\xfe\xf0\xff\xfd\u007f\xfd\x9f\xbb\x00\xf0\xc5/\xfe\x11\xad\x8b\xf9z\xbd\xed\xeb\xe3\x1f\xff\x98\x03\x80\x1f\xf9\x91\x1f\xfb\xfc\xe5\xcbw}\xf5\xfc\xf9\xdb\xd1w\u02fe\xa4Uy\xd7\xe3\xfa\xb3\x8f\xa2?\xdeGc\x1b\x18\x04\xaaWY\f\xc5C|_\x99\x11U\x02\xf5\xd8\xed\xc6G\xa5\xe8\xdac\x92\x85\x9diA''\x95\xd7I*(\xa5wy\x18\xf4\xd1P\xc82R@\u0190\x02\x19\x83~G\xf3\u03a4\x1a\x8cR\xb4\x9c\xf5\x99\u007f\x1e\xfd[\x9a\x90\xc0\x13\xa7\xadTl<\xa3\x85\xfc\x1b\x01\x9aO\x83(d\x84:B\xb5\x1b\x19I\x18\x86\x16p\x8bYJ\xa2*\u018d11X:\x81\r\xb2\xfd\xa4\xac\x8c[aAK\xadh\x84+\u01bac\x9b#N\t|\xaeOP+\xe8Q\x18jK\xf5\xb3\x87\u035b\t\x9eB\x91M]=\x03\xc4\xca\xfd\xe6P\xd0\xd9d?\x18\xc4 \xf1\u0708\x94\xe6]\x12/\br\xb7\x1d\x87\xd7>\xd0:M#\x98\x9e\xd9\xc6\u0785{a'\x1bJS,n\"6\x16\xce{\xbc\xf0\xc2\vw\xbd\xf0\xc2\xd5\xef\x02\x80\xef\xfd\xde\xef\x97u1_\xaf\xb7}\xbd\xe7=\x0f\xc9#\x8f\xfc1\x11\xd1\xebw\xdcq\xfb\xffs\xee\xecYx\xef\vb\xb9>\xba7\xae<\x89\u064d7u\x10JE@3\xe7\aL1G\xa5(\xd2\xe0!\x1d\x01Z\x93qU\xc4H\xe1\xb5sl\x8e\xbd\x8a\x8a\x06Tp):6re\xfaN\x81O\xd3\xd8\xd9\xff4\xcd=U\x8a\xc8\xf4\xc5eDj*\x03)}`\xe7D\xb7\xc6f\xe6\xd0\x1e\xf5)\r\x88J\u067f\x94\xd4I\x19\x96\xbd\u0453\xc0J!<EQ\x996G*`\x87\xf8y^\x031RwN\x12R\x89\x94r\x88\xc0\xaf&\u039ctv\x01+\xf7\xab\xbf4\x19\xa3\xff!\xe7r\x96\x19Cc\xe3QZ\xf9\x93!\xec\x94\u007fPR\xe5T\xa1)\x18H\xd0\b\x10\xcb\x19R\x01g\u061c\b \x03b\xabPKpWK\x97\x8fJ\x1b\xe0\x9c\xf4\x14\xedv}\x14\xc6E\n\x14k\xf3b\f\xc1\xb4\f\xd7\b\xce\\\xb8\x17[g\xce\u00c5\x04\xa2\xf8:\x8cm\xe0\x9d\xe0\xd5W_C\xd7/\u007f>\xfel\xff\xee\xdf\xfd\x9bu1_\xaf\xb7\u007f}\xe4#\xdf#\x00\xf03\u007f\xeb?\xf9\u02fd\xbd\xbd\xeb\x8d1\x94\xe4\x99\xe1f\x9f\xddx\x1dG\xaf^\x85\x13\x0f\xb0Y\xc1\xbf\xd3\xc3\xe9\x1d\xc4wY}W\xe9\xb0\v\x81J\x10\xc2\xe815\x14\x1e\"\x8dm;V\xbbU\xc8\xd0]\xaf@/\xfa<8,\x83\x12\x12\xaf\xbb\xc4%\xe4\x14\xfa\x9fHE7\x8c\x83\xac\x92Yq\xca>TI\xe9\xb9W\xf6\x8aI\x83\xcf\xfc\x99\x9c\xe4\xfbC\xea\xdc\x18LR\x1f\bV>(m$\xcbPb\xbay\u05df\xbas\n\x19\x9e\u02c0\x9d\x17\x97\xc8\u012e<0\x91\xeaW*\x83\x83K,\xd82\u0632CQ\xa7!\x14\xb4B\x16\\9\xfdH\xc1<\x1a\v\xd2(0\x12\x80\t$\xa1|3\a\xf8#\x8a\u07b8\xf0K\xa6*\xfb4m\xac\x83\x81\x01\x11'\xd4=J\xffS\\!)\xa4c\r\xc1X\x02Y\x82\xa0\u01d9K\x97\xb1s\xf6N\xf5t\x8e\u015c\t\xcc\x16\xc4\x16\xf3\xf9\x1cO>\xf9\xd4GD\xa4\x01\x80\xcf}\xees\xebb\xbe^\xb7n}\xe8\xe1\x0f\xff\xdb\v\x17\xee|~oo\x17}\xdfgO\v\x01\xdcb\x86\xebW\x9e\x84[\xccA\u01aaA\u007f\xc1Z\xa0\xf4Pz8\xd7\u00fb\x1e+\x86\xaaT\f\xa0\xca\xce8P\xfe\xa2\xb8\x86\x9d\xa0=\u0283\xc5\x15\xf1G\x90\xf0\x93_\xc5P\xa4\xea\xe2\x8a?/\xa8\x8bc\xf6-\xabX\xb6\xa4\x0en\x98@A\x85\x95\x00\xb9\x82\xe5\xe2\xf2\xd00\xc1+.G\xd9}ckL55\xb0\x88\x1d\u0242\xab\x93DkH&BA\u9be3Mnd\x10\x85\x84\x9f(\xf6\xa2\xb7\u45a7\xe9\x04\xc6\u0520r\xcaah\xe8\xb78\xfc)e\xa4\x87\x8fT\xd9\xf45\x83M\x04\a\xa8\xab<\x19\u0122\x9aN\f\x12\x06\xf4Qz_@Q\xb2\x12*\x121s\xb5\xb5\xe0\xb09H\xe8V\x98\x04l\td\b\x04\x87\x8d\xdb\xce\xe0\u031d\x97al\x1b\xf2I\xf3\xa9\xc2X\x8b\xc5b\x81'\x9ex\xe2\xd2\x17\xbe\xf0\a\xef\x06\x80\xbf\xff\xf7\xff\xfbu1_\xaf\xb7\u007f=\xf3\u0313\xf1A8\xb9x\xf1\xe2\x1fmnm\xa6@\x80\xe8$\xd7-\x17\xb8\xf6\xdcc\xe8f'*\x87\xa6\u0324\b'\xe2\x80C\u01a3jW\u3728\xbb\xd3\xe8R\u0211\xd5\u0485\xa2\xe7\xb2_\xf7\xe4XM\x9ed\xa5\x98K\xea\x80\xe9\x14%\f\x8d1%\x86\xe1\b7+-C*x]*\xb3\xfa\xb5\x80Q\u022b\xcb`\x12\xfb\xf8\x82\xc5\"\xa7\x15lz\x8b\xc2.\xab\x88\xd1h\\\xdb\x10Y\xa2jf\xaa8x\xac\x87\x14:q\x1fT\xa2\x8a\xa3\x93\u01ea-\x02\x9dVvW\xdfJ\xa0\xa5R8\t\xaa\u079ep\x9a\xccW/:G\u0604\u00b9\xa0l\x00L\xf8\t\x9cT4\xcfT\u04a5(\xe2\x12\xc2\u028bBO#\x9d\u007f\x86Z\x8a\xcf#\x82p\b\xb1f=\x8cRCZ\xe4\x01\x90e\x9c\xbb|?\xa6[g\xe0\\\x9f\x8b9\x11\xd86Xv=^~\xf9\xe5\xc9g>\xf3\xd9O\xc5\xd7\xf8\x87\xdf\x04\xc1\x15\xebb\xfem\xbe\x1ex\xe0\xc1\xf4\xfeG?\xfa\xe1?\x9aN\xa7G\xc6\xc4\xcc\xc30\xc8\xf3\x1eG\xaf\\\xc5\xc9\x1b/\xa9\x10\x83\xb9\xa8)\x02\x1f\x06R\xca=\xef\xe1]\a\x1fb\xb6\xea\xb0\xc7\xfc\x9c\x97\xf1g\xe4<\xa8\xf3\u025f\x1b\"\xe0e\xe8\xd0\x175\xf7\x90\x12\xcd/\x17M\x00xK5\xcc\x18>]\xb8\x14\xd6|\xc4\u0560\x86R\x0e\x9e\xads\v\nf\x18\x8e\xa6H\xa8M\xd9\xc2\x00\x00 \x00IDAT8)\xbcXV\xeclO\u00cf\xdf\u00bf@\xc6\xe1\u007f9\xa5\xa9\x97B\xa8e\x96>\xd1\x15c\x88E3\xd37\x13\x95\x95n\x84\xbb=\x80\x97\x86.\x8b\xa5W\xfa)\xad\xf6MN\x1e#\u007f\x9d\xfcS\f\xc8\x18u\xe2\fp\x8a0\xa7\xcf\xe3\xa1\xe3b8\x19\x96\xfe\xfbz\x8f\xac\xc2\\\x91\xd1R\xe3\xff\xf9\xdeT>>\xc1\x18F\xc3\x046\x04j(\xcfTHp\xf6]\xef\xc6\xd6\xde\xed\x99\tC\xf1k[x\x0f,\x16K\xbc\xf0\xc2\xd5_\x14\xd1v\xe4\a~\xe0c\xebb\xbe^\xb7n\xfd\xec\xcf\xfe\xed/\xdc\u007f\xdf\xfd\xf3\xad\xadM\xb8\xbe\x17}(\x14W\\\x9e\x1c\xe0\xe0\x85'\x01\xef\xc0\x86S\"\x83\x88\x04?\xf3l\xbe\xe5]\x97\xa0\x96\x88G\xe6\u0388*\x0eu6\xb6\xaa\x8b#\xa0I\xf0\xed\xb1\x03\xf7z\x02PEcY@G\xf0\xd813\xaa\xb1z)\x03\u0339\xc4\ah\x1c+\x18\xfa\x81Se\xbaNE\x96\xe5[\xe1*CE\x10\x9d\xf2\xe7\xc5\xffK|\xa8\xe2\xce\xe5\x01\xb0\x9c\xe67&\x85\xd4\xdf\x15)J\xc9#\aI\xf1\xba\xf2\xbdG\xf6\x17\x19\x19gF\xd8'\xceB\xeaKwsc\x97\x15\x9az(\xe4\xc4\f\x84\x82\xee\r%\xd8#\xcd-\x80\"\x14e\xe0\u0452\x9c\xc5hpU\a\x11t\x81\x01C\x05\xc5QBRJct\xf0\u026d\x01Y*\xf2p\x1d\xb6\u039d\xc7\xf6\xd9\xf3`b\xddD\x8a\xae\xde6\xad\x1c\x1e\x1e\xe2\xd1G\x1f\xbd\xf8\xf9\xcf\u007f\xee\xaf}\xb3<\xdf\xebb\xfe\xef\xc1\xfa\xa7\xff\xf4\u007f\x8b7\xe3\v\x17/\xdc\xf9\xc5\xe9\xa4\xd5\xea\x1a\x8f\xad\x04t\xb3c\xbc\xf9\xcc\xd7\xe0\xbb\x05\x989\xfb\x92\xc7F,>\x04\u041b\xdd\xf7\u02d0+\xba\xda1V&V\xa5\xb1\xb6HV]\u01b4\xa2\x85Gs\uc495k\xeavob\xfe$\x83.x\xd4\xc1\xae<\xdd\xcbi8\xb5TM5%\xbea\u0465\xcap\xa3\xa8\xf39+S(\x19:\x0f\u07ac\x1b\x1f\xf9{\x1a\x9e.$'n\x14\x19\x11\xa7\xa62E\x05k\xf8\xa7v\xa16\xb3\x11\xb2\xc8\tH\xa74\xd3R\xf2UJ)S1\xad\xa6\xb7\x8a\x1b\x1a\xf9\xf3\xe1) u\xe2\x041\f\xd70<S\x1d\x18]\\\xeaZ\x80\x95\u00da\xa9\bo\x8e\xf7A\u2167\xaf%\xa9\xc9\xd0_\x9b\xde\xef&2X\x1a\x03\x9ed{\x00\x0e\xa6sv\xd2\u2d8b\xf7\xc0\xb6Suk\xa4lf\u01a6\xa1\x93\xd9\x1cG\xc7Gw\xfc\u025f|\xe5\xa7\xd7\xc5|\xbdn\xd9\xfa\u0407>\x94\xde\u007f\xf0\xbd\xef\xfd\xe2d2\u0566&\xa5\xa2\x13|\xdf\xe3\xe0\xe5+\x98_\u007f]\x99\x03\xc9\xf1\x90\xaa\"\x1d\xe5\xfd\xcew5v>\f/(\x9eB*\xe2\xdbb\x06g\xe4g\xb3\x13\xb43\x87\xf6\u0105\xa1\xe7M\x9c\xb8V\xa8\u007fr\xd3#\xbe\xd0\xcd\x0e\xffR8\rR\r\xe5$\xde{\x194\x11_\x17UFP\xdf\x10\xbcpj\xbd\xa3\x9b\xd7\u0081mp\xec\x8c\xc7\x10\x9c$\xc4q\x92=r\"\xa7<\u57be\xb5\xbeE\xc6@\x962TCd\x10\xed<\xdc\xedFH\xe8%\xbd4P\x01\xc50\x84\b\xce0\x96\x1b\x16\x8e\xf5c\x98\x907;2'\xc8E\x9c\x03\xee\x1e\xf9\xe6\x8cA\xe0g\xb5\x1b\xe4\xc6D\u007f\u007f\u0094\xbd]\x02\x8b\xa5\x1a\xf6\x8aR\x16\xcf\xdcu/\u068d\xcd\xc4j\x89\xd1sD\x1c\u063a\x1eW\xaf^\xfd\u8e98\xaf\xd7-[\xef\u007f\u007f>\t>\xf4\xde\xf7\xfe\xebK\x97.\xa1i,\xbc\xb8P\xd4\xf4A\x99\xdfx\x037^x*Q\xc4R\u0716\xe4\x87\"&O\x88\xef\xe1\xfb%\x10x\xe7\t\x8fL\x83\xa6\xa2\xc8\xfb\x12w\x8e\f\x11\x9f\x86\xa2\x14\x859\xc7N\xa9s\x812&\xb4*\nO\x1e\xd6TW{Yq\u05ebq\xf3\x9a\x98!#\x85\x8b\xc6an)7\x10\x1a\xc0\xf7rJm\x96Uo\x93\xb1#\xc3J=\x1c\x16w\x1a$,\x15\xc5\xea4\xdb\xdc\xc0\x8fO\xc3\xda\x12\x16\"T\xac\xf1\x15\xcf\xf2JDu\x9a\x1b\x1a\xa1\xb6>\xa7Qx\xa9d\xe0\xd0\xe0\x8ax\xd6\xee\xdc4\x16\xbce\x80-\x86oY?\x87\x00c\x18&\f\xe2I2\u03fb\xf6\uf29b\x8c:\x03Q\u0237\x8dM\xc8*\xbd_\xcd}\xa2\x10\x89\u00d1\xcd\x1b\xf5\x04\x8d3\xa4\b\xd1\x10\x11v\xee\xb8\x18\xc4C9\x10#\xfeLM;\xa57^\u007f\x13\x8f?\xfe\u0136\x88l\x03\x99l\xb0.\xe6\xeb\xf5\xb6\xae/\u007f\xf9K\x04\x00?\xf6\xd7?\xfdg\xf7\xde{\xf7\xd3M\xd3hDVR\xdd1\xe6\x87\xfb\xb8\xf6\xdcc qJ\x1b\xa3\x82\xf6\a\xa9\xfe\vx\x88\xef\xe0]W\xe0+EWKuzs\xec\xce5\xe5G\x87q\xa5l\x9ez\u581bE\xf0p\x19\xaad\xe8f\x92\x14Z%\x91\x8c\u06a7\x8eu\u01b2\x8aU\x8f\xd6\u027a\xf3\x8b\xf8\xf9P\x12Ie'\xff\x8d,\xba9\x12S\xa6\xf7\xe4\x14zI\x85o\x05\xe2\"*\x04WaP\xeb\xc7\xe0\xa2\xe1\xf5\x93\xe2@D\xd5\ub5caX(\xab\xf6\x03\xdf\u0211$\x16X\xd6n\xb8\x9dXl\xec\xb6\xd8>\xdb\xe2\xccn\x8b\xad-\x8b\xa6a\xb4\x86\u0476\x8c\xa6UL\xdb2\xea\xc0\x0fP\x96\xebG\x8f\x97\xb2s\xe7\x12#\xaf\xef\x17\x1f\u031c\r\v\x98\x00\t,\x96\xd8\x1c$\xff\xf3P\u0427{g\xd1n\x9d\xd1\xe6\"]\x1f=c\x18cp2\x9b\xe1\xf0\xf0\xf0\xae\xbf\xfa\xab\xbf\xf8\x00\x00\xfc\xf1\x1f\u007fi]\xcc\xd7\xeb\xed_\x1f\xfe\xf0G\x03M\x9b\xbaK\x97\xee\xfa\xbd\xe9t\xa2\x8f\xacw\xc1\x82\x9f\xd1\xcd\xe7\u063f\xfa4\x96\xfb\xaf\x83\xad\xc9).T\x14\xe4\xf8p\x06E\xa8s]H5/\xf3\u054ad\x9a\x92\xba\xe8\x8a.\xdd\xe70\t\ntE\xd3\x05VF\x80\n\x84Vs2\xa5\xd0\x12\xae\x18\xb7~\x03\xfc\xe9(u\x8c\xc3\u0541\x04i\xd0c\x16'\x848\x00\x1cD\xbfU\x1c\xc7R\x19:\xdc0d\x9c\xaaBc/\xa0\x1c\xfcRNQ\x1a\xe2\xff2,\xfcaX\xb8\xaa\x16- \xa14\a\xac\x8d\u0525\fz\xc6MR\xb6o\x86\xb9W\x1ba\xcd!\x8f~\xf0d\bm\xcb\xd8\xd80\xd887\xc5\xd6\xde\x14\xbb\x9b\rv\xce4\xd8\u07b6\xd8h\x19\x8d\x01\x8c%\xd8\t\xc3\x1a\x0e\x8c\u0161\xd9\x0eU\xb9\xaf\\\x9aq\x85W\x10\xa3\x12\x95\x88$A\xb6Oh,\x83\x1b\x02O,LksFn\x81\xbd{\xefa\xa6\x1b\xd8>\u007fIgH\xdeW\x87\x18f\x03\xef\x81\xfd\xfd\xfd3\xbf\xf3;\xff\xe2\x12\x00\xfc\xb3\u007f\xf6\xdbx\xea\xa9\xc7\xd7\xc5|\xbdn\xddz\xcf{\xde\xf3G\x0f<\xf0\x80>`\xde\x05\x99\xa6b\xc8'o\xbe\x82\x83\xaf?\acl\xa8\u0252\x1f\xcb\x02\x1fW\xec\\\xe1\x96\xca|\xab:\xfe\x8f\xb4\x99\x1e\x95?y\xea\"]\xc6\xd1c.e,\x9c\xe9\x9fS\u0462\x0f3\xccV \x90\\\xfch%\x03\x13\x99\xdf|\x1a\xb6]R\xe2X\v\x87p\x8ex\x13*,\vF\v\x1c\u0764 \xca*\x82Q\xc09\xaa\xf5\xa1J\x81+\x18\fr\x19\xc5F\xa3\xd7\xc93\xe0\rF\xc83\xc5FC#\xdb\x16\x8d\x1b\x98\xd1\n5h\xc0R\x1a3\xa3/60\x1a\x02YD\xb0\x960i\x18v\x8baw,l\xdb\xc0\xb0\x81\x99\x18\xd8M\x06\x1bE\xc4\r+\xef\x9b-\x83,\xaf\xe0\xe1U\xe6I:\xb5D\x99~hB0 \x06\x91\x84\xd9+\x81\f\x83\xdb\u0415\x97A\xd8\xe1g\xf0\xe2\xc1\xd6b\xe7\xceK*\xa4\xf3!\xa0%\u078fLX.\xbbn:\xdd\u061b\xcdN>\x01\x00?\xf1\x13?\xce\xefy\xcfC\xebb\xbe^o\xff\xfa\x87\xff\xf0\xd7\t\x00\xee\xbd\xf7\xdeG.^\xbc\xf8jc\x9bp\xe3\xea\x83\xc0\xd6\xe0\xe4\xfa\x1b\xd8\u007f\xf1Y\x18\u02e3]nv\xec\b\x959\xf0\xce!\xbe\xa0r\xdf\xe4\x1c.\xb5\xba3\xc2.)\xa9'\f\xeb\xa2\x19\x94\x84\x81\x95\u0400!B\xb4\xc2\x1d\xaf_\xaf\u0704\x0f]\xff\xd5X0\x03\r`\tg\x19\xae\x89\xafC\x8a\r\x81\x060\r\x12\x1bf\x15\xe2\x19\xf1\xfd\xa5\xba\x97EQP\xca\x19\xc4\xf0\u02d4\xbe&\xf1\xfa(;$~\\\x0fM\x85W7\x9b\xb1\xba^l)+\x93\x05\x1a\x83\u042bv\u007f\x15\x98!\xca\xef3k17\x13\x02\xefZ\xa0\t\x12\"c\xd5\x17h\xa3\x05\xb5\x16`\x1dp2\xb4\x93G\xfc\xfd\x13\xaa\xe3I9\xbbH\x94\xc4D\x81)\xb4\xa3\xe1\xda\x10)\x83\x85\x98@\r\ubc35\x8a\r,\f\xf5E@\xc6`\xe7\xf6\xf3`k\x03\xa6N\x15m\xd36\x8d\\\xbf~\x1dO=\xf5\xcc\x1e\x00\xfc\xf2/\xff\x1d\u007fxx}]\xcc\xd7\xeb\xed_?\xfe\xe3\x9f\x16\x00\xf8\u0527\xfe\xa3\x97\u03de\xbd\xed\xf7766re\x10\xbd\x1d\x96\xb3\x13\x1c\xbc|\x05\xdd\xecP]\xe9Rvc=K\f\xed9\xe0{H\x10\x11\x95\xb2\xe7\x8a\xee7r&\u03f0\x01\xe5\x80\xe5\x10t\x11y\xd3\xe4D\v\x91\x89l\x8a\xd3<ds\x95!\x19)\xd1R\xcf\xeah\x80\x03\x97\x90,\xa1\x86O\x84\xf5\xe5\xf5\ra\xb9\xc1\x81\x0f]\f\xe3\x88\u01bboz+\\y\x05W)\xe0eZ\x05\xb9K\u0590\xe4\xa1f\xec\xca]\xa3\\m\xcf\x04grO,\x85h\xaaJB*\x06\x895\a\xb5\xf4\xbf\xc1*]\x92\x86\x8a\xdb\u04f0\xad\xfaZ\x90!\xd8F=Px\u04c2\xa6&\x83Y\xcc0M\x03\xbb5\x85\u0759\xc2\xd8&\xd9\u0732!\x98\x86\xd57%\xe5\xf7\x15\x82\xa9(\xe5/\xb3EC\xb2\x90@\n\xd1\x18\x85\u0344U\xb6\xdfj\xd7\u03e7x\xb0\x8b\b\x8c\xb1\u063e\xedv\xd8v2\xfa\xb3\xc589\x10\xbdWD\xee\x05\x80\xa7\x9f~\x86\xd7\xc5|\xbd\xde\xf6\xf5\xe0\x83\xef\v\xdd\x12\xcd.^\xbc\xf4\xe5\xbb\xef\xbe\f\xef\x1c(\x8e\x86\xc2\xc4~\xff\xe5+8|\xed%\u0626\xa9\x8e\xcb5\xc3!\n3\x02\xd4\xe2\xba\x11\xbe7\xad\x1e\xc1\xa5\xd8\x1cbw\x9e\xe8\x8bH\x1e`\xc9\xe40\xc0\ae\x9ed\xecH\x890\xda[\x8f\xa5\xe3\xe4\xd7P3+F\x91!\xa9s%\x84\x00\xdad\u041e\x05My\x80\x9d\xbfU\xd1\x1e\xc3$V\xedmW\x93\xe6J\x04x\x10\xedS\x86'\x93@,\xc1[\x86\xb7\xe1\xf5\x18(\\a\x8aN\x1d\x94\x8a\x9a0\xadz\xbc\x9fB6\x1c\x8d\x8e\x1b\xaa\x80\xb0z]\x87\u078a\xb1(\x9b\xa9\x01m\x1b\xc0\xd2\xc0g\x8c`\xda\x06vw\x03vk\x03\xb6\x9d\x80m\x03f\x03\xd3\x18\xd8\u05a4\xa1<\x06\x8c\xa6\x12\xaa\x12\x91\xaa[\x8fX\xb9@\x87\x9e\x86\xb5\x90SC\x85\xdch\xe8p\xa9\x1b\x9ca\xc2\xc6\xd6.\x9av\xa3\n\xc8\u0223!\xa2\xae\xebqrt|\xf6\xd5W_>\a\x00\x8f=\xfe\u013a3_\xaf[\xb3\xbe\xf6\xb5\xaf\x12\x00\xdcs\xcf\xe5G\xee\xba\ubbbe,\xb0\"\x0eD\x8c\xfdW_\xc0\xe1+\u03e3i\x9a\xe0c\x11\x1e\x8eA\u01d6\xb2\x15\xc5\xc3E\x9f\x8c\x02\xfa .`\x05\xa2\xd5\xe3\xfc \u0693nR\xefF}\xb4G>Y\x04+_|\xb5\u03d5S\x99}eU\x8f\xe2\x92\u0254\xb1\xb9\xa7C\xba\u026e\x85m\xb9:\xfa\v\x8d\x9f\x16\b7{\xd1R\xc3\xff\x03\xa3/*w4:E\x84\x946\xa7|\r\x1a&lX\xc6dj\xd0L\x18\xb6\xe54|\xf4\xa1\xb8\xa7\xa4 \x1a\xbb\xe8\xe3q|+\xe3\x8f\x11\xf5\xd1\xc0\xb9<'%\x05\xac\xdcX\x02m\x19\xd0\x06\xe3\x14!+hja6\x1b\x98I\x8bf2E\xd3Na\xdb\x16Mka\f\xd73\x0f*7\xf6\xe8\x8cX\x9b~I\xc8\x00U\u02a3r\u02a9\xd5\u007f[Z\xd5P\xe0\x9e\x97,\x18\x81\xc0\xb4-\xectR\x04GSv\xdc\x14\b\x11\xe1\xe5W^Y\xfe\xee\xef\xfe\xee\x12\x00\xbe\xf4\x0e2Z\xd6\xc5\xfc\u07f3\xf5\x81\x0f|\x87\x00\xc0/\xfc\xc2/]\xdd\xdd\xdd\xfd\xc3\xcd\xcd)\xbc\x12\xcaCm ,\x0e\xf7\xb1\xff\xea\x15\xf8\xff\x9f\xbd7\x8f\xb7\xac*\xafE\xc7\xf7\u0379\xd6\xde\xfbtTA!\x14\x05\x04J\x1a\xa1 \x16\x02\u04bdkh\x05\x1b\x82\x1a\x8d\xb9\x11\x8d\x06\tQ\xaf\xbe\xe73\xd7\x17\xafz\x93\x97\u0118\xe4&\xf16&1/O\xa3\x98\\rc\xf3\x8c\x82J\xb0\uf0c8 J+}[\rU\xa7\xdf\u035as~\xf7\x8f9\xe7Zs\xad\xbdNU\xde\xfd\x03\xcab\xcf\xdf\xef\xfc\xaa\xea\xd49\xbb_c~s|\xe3\x1b\xc3\f\xfdh\u007f\xfa\xf1\x16$\xa6G>$\xd7\x06\xaf\xe8\xaa\x11Jc\xd2i\x8c\x01\xb6\xd4*\xd3v\xb8\x93\x8a\x86AK\x15\x9c\x0e5\xa1\xc9SW\x8d2i&a\xb4\x13\x1d5\xb9_\xa5\xbc \xe4\aht\xa6\x15\xba\x8a\u0419Q\xe8L+hE\x15\x17\xbd\x06\xc3R\u056bM\x93\u0644\x8a\x925\x0e\x13\xf1\xb5^\x83\u0268\xf9e\x85\xc6q\x87\x19\u074c\x90+\x82\xee1\xb29\x8d\xbc\xc3\xc8\x14\x95\x94QJ\xbbT\x81\r\xb2\x16\x19^\xc7\xf5V\xdf\xf5&]EcL\x98\xe2\x00\xa4=\x06\xcd(\u007f2h\v\xe0p\xfeT\x81N\xa0BXC\xe9\x0e\xb2\xbc\x87\xac\xd3\x01)\xed\xfd[h\xfc\xb5\xabT3TV\xfa\xc4\xec\x11Ny\x1f\x16\xad\x19\x943\x90q\u057f\x89\xb2D\xa1\xaa\x91\x1a\xab|\xeb\x13\x86T\xd6I\x9e\x8e\x84&h\xa5\xdc\x1a\x8eF|\xff\xfd\xf73\x00\xec\u0639s\x02\xe6\x93\xf5\u052d#\x8e8\\\x13\xd1#\xb3\xb3\xb37\x1cu\xd4Q0\xa3\x91Kydk\n\xcc?z/\x06\v;\xa1X\xfb\x12&U\x12D\x02@\xa8\x94qE\x99b*\xe1\x1a\xaf\x9c\x93d\x99\x9a\xe2\xa4}n?\x1eiym\x81t{\xc4|\"ii\x83\xee\xb5\x15\x8c\x95\xcf5sP]Li\xe8\x0342M\xd0\x04\xe4\x1d\x86\x9e\xd3P\x1d\xdf@s%\x8d\x91p4h\xabZe\x1c\xf2ZmZb\xe5\xdc\xd0\xc0K\x8b\x9fJ\xc2\xd9g\x8a\xd0U\x80V\x04\xd6\x04\x9e\u0460\xf5\x19t\x87\x91k\x86V\xa1R\xe6\u051aaO\xc6_\xe3\xc8\x1d\x1d\"\xd3MZ\xf6\xd0\x12\x90\xa0*\xc92\xf2\u0291i\x05\xear\xcdc\xb1^\xea\x87[\xeb\x06\xc0-\x15\x88\nJu\xc1y\xc7\xef\f@K\xb0E\x9d\xb6\x8a\xcf\u0445MKE3\xad\x9c|`\aPV\xe2>\x1d\xd1\x17&\"\x95\x04\xd79\x81\x90\x82\xeat\xab\t\xe0z\xe6(\x0f\x87#\x1cr\u0221\xcf9\xe6\xd8cO\x04\x80\x13O<\x91&`>YO\xd9z\xc3\x1b\xde@\x00p\xcc1\xc7\u0733n\xdd:\x80\xa0D\x9cP\x19@\xc0\xd8\xf5\xf8\xfdX\x99\u07c6,\xefTC\x87\x89\xee<\x1e\u0465\xf4\x96\xb65\x99\"\xb5\x8d\xaaK+\xea\xd6\x12\u071b\xd3\xf5.\x02\x9b\x932\x84\xbaV\xd1\u05fa\x9aT\x13z\x8f_\xe6k_g5W\x12\x02\xb2,4\xecf\x14\xb8\xabj\xb9\x11\xaa\xa7\xa0\xa6T5\x04\xd3\x12j\xbc7xlS+J\xed\x90\"\xf5\x83K\xa4\v$\xc5s\xffxY\x13::\xd2\x04\xf0^#\xd3\njZA\xf5\x14t\x06t\xb4\x0fapLp\u068f\xd3\xd7\x01y\xcf\xc3U\xe5\xc9E*\x8bc\xec\u1d4d\xbd\x06\xa5\x00\xce\x18\xd4U~\u0693\x9atT\x83\xaa\x12\x01r\xf2\xd59\xa7\x16\x02\x04h\rR:\x8c\xf3S\x12#\xc7`j\x8e\xf4'v2\x01\xb8UG\x81s5v\x1ap\xceS\x85Rg\t}\xea\x96R\xe0\xbc\xeb\x1fZ\f\x9c\x8e\xb1\x89DT\x14\x85\x9d\x9d\x9b\xa5^o\xea(\x00\x98\x9b\x9b\x9b\x80\xf9d=uk\u077a\x03\x1c\x00\x9cw\xde/\xdc\xc1\u0337\xe7y\x0eg\v\x11g|\xe3Gi,\xee|\f\xbb\xb7=\xe8\xa5cTM\xdd\u0544\x89R\xc9\x1a\x05\xce'\x11\x89\tm\xa7\x14\x8c\u0491Lj\x16\xeb5\xee 6\x16IU\xe0\xe4\x1d\x15\xa9\x15\x1c\xc7i\x1a)9\x01\x19s\u05d6\xf6\f\xe5\u06a4\x8f\xe7vuF\xe0\x0e\x81g\x94\x97\u01e5A\u011a\xa0\xa6\x95\a\xa9\xb4\xa1\xb8FSQ\u068e\x0e-\x9e\\5\xb9dT\x0fQ5\x06Z{\x96\xe4\x1b\u00e4\x80NF\xd0\u0468J3h\x86A9\x81\t\xe0\xf085\xc3\xd3/Lp\n\xb0:q*DK?\xa39\xfa\xb4\xc6ND\x8d\u05d6\x92\x97\x93\x13\x9e\x9a\xa7\x14\xa8\xc3\xe1\x90Q\x97s\x96\xbe)\xb1\x9bA\x04\xea2D\xa1\x8cx\xf3AQ\x1cr\xf0\xc2\u0670T\x1f\xb5\xbc\xfe\x8cr\xa8\x88\x99\xa02\xdf\xf8\x14\xaa\xac\x1f\xc4:\xd8d\xb2\x13hX\x00K\xa8\xcc\xf3^5\x80\x84T\xc1\xe4\xc7\xfe\xad1\x18\r}\xf8\xeah4\x9a\xd0,\x93\xf5\u052ds\xce9\xc7\x01\xc0)\xa7\x9cv\xffA\a\x1d\xf4\xc0\xec\xcc\f\x8c1\u039a\"8!\x12\xcch\x88\xed\x0f\u078ea\u007f\x1e*\xcb\u00a7%H\xbe\\\xe5']\x06\x068\a\x17\xc2+\x90D\xb6\x8d\x83\x9c\xd4t\xd4\xf5\u03a4\xd7\x00\xebL!\xef0\xf2\x8e\x1f\u91a2\xbaN\xba\xea?%\xf2f\xaa\xf3\xc92>u\xb9&\xa9\x90L\x161\x03:\xf3\x928\x9a\u0460\x9e*G\xe3\xd3\xdf\xd5S\x8c\u038cB\x96yN\u0595\xd4\u0178;\xc9\xda>-\rZ\x02i;\x93jz\u9997U\u0515g\x9a\xa1\x94\x97\xfa0\x13\xb8\xcb@O\x87\x14\x1f@r\x0f\x8c\xc4\x04M\x9e*\"\xf2rO\xa7\xa9\xee\xf9\xbe'=\xa5\x94n\r{5*\x88'\x18\xad\xbd\x82\x85\xbb\xcaS,\x94\x06\x83H\t\x98\xfe-Hc\x00}u\x9e\u6002Q\x99c\xc5Sa\u0512K\xf2z5\xe8'o\xc9\xe2)\x16Q(\xbb\x9eN\x04\xce\xf9l\xd0\xd42\xb7\xa2p<\xedb\x84\xa0:\xbd$\x90C\xea\xc2\"\x11\xb2\u01a2(F\x02\x00\xa6(&`>YO\xdd:\u3333\u4aab~C\x11\xd1\u02a6M\x9b\xee:l\xd3a0\u0188\xb3\x06\u058c\xe0\x9c\x05k\x8d\xc7\xef\xfd!\x16w=\x06\xdd\xe9\xc2\x11`\x93(\xb3\x94\xe2\xf0 \xee\x12C\xa2\xb5\x9cI\xea\xf6\xb3c\xf0!^>\x96k\xdf\xc0\u02fb\xaa\xa4\x0f\\\x18\x84\x19\x93\xc6Q\u04c0\xbc\xc1?\xb7\xc8\xeeh\rV_\xe0\xc7\xc8YyP\xe4Y\xed'-\x93\xe9\u0258\x80\u011a\x90\xcfhtr\x86\xd6\x04\xd1T\x8d\x9c'\r`\u0683\x9aE\x92>\xa04\x8d\xbbHZ&\xe5+\xc0\x12\xf6\x01\xc4Y^\xa90\xa8C\xa0\x19\xe5\xb9\xe18\x80\xc5\x04\xf4\xfc\xf7\x88\xc3\xfb\xe7\x82\x12I\x13D\xb7\xbc\n\xb2vw\x81\xd0\xf4\xea\x19\u007fK\\\xb2)\xaa\x0eC\xcdy\xae\u0725\x80\x99(^\xa89\xd1+~\x03\xa7\x8c\xc0\x91\xdf&\xa0\xa3\b:\xba+&'\x99\xca\x06(\xbc\a\xa1\x8a\x8e\xbf\xc8\xda\xd36\xfe\xbd\x8c6\xb9\xa86\x11\xa9'\xa1\xc6\x13\xd1\xc8:Xbd\u0769R\xcbN\xa9\xe82\xa6T\x85\xd4#\xff#n\x02\xe6\x93\xf5\u052e#\x8f<\xd2\x01\xc0\xb1\xc7\x1e\xfb/Z\xeb\x95,\x8c\x83\xfa\xe1\x1f\vf\x85\u015d\x8fa\xfb\x83\xb7\xc3:\x03\xce\x14(\v\xfa\xdc\xd8LK\xa4g^\xa2hC\xac\x9c\xad\u05ffM[\xd2\x16 M\x1cR\xbdicO\x81\xa7\xb5\a\x84\x10\xc2.k\x17\x8d\xb5\xbf\x8c+\xa6\xa5\xb5:o\xdeH\xa4\x05\xb4\"\xcf\xefv\xa8.QoT\xa6\xdcc\xa8)\xafl\x81\"X\x15\u031b\x90\xd2Bk\xf9\x9cP\xc3tP\xd6d\x9fR\x18\r4/\x90\x11\xb2\x8e\xaf\xca\x19!\xfalZ\x81z\xaa\x9a\xf0\f\x0f\x96;\f\xee( $F\x95U\xb6\n\x03F\rm\xfd\xf8\xabKk\x9cp$y.u\u0263V\xecO8=\x06\x05\xae\x9c\x92\xa8\xc2\xf4P4.\xa4\xf1o\x06u\xd9[\xd4R\x10\xa5\xc0\xf7?)\x9eLP\xc9\x05k\x9f5\xf6\xef\a8|V\u00c0\x10$-\"\xa8Rf\xc1\xf7c$\xfa\xbd\x130\xb4\x82\u0551\x83\x03\x81\xb3,\xf9]i|\xac\xbc1\x1c1=\xed\xd7\xf4\x04\u031f\xa1\xeb\xcc3\xbd\r\xf3\x9b\xdf\xfc\x96\x1f\xaf_\u007f\xe0\x83\xddn7T-\x0eb\x8d\u709d\xc5\x03\xb7}\r\xfd\xe5\x1d\xc8z=pF\x90\x8e\x82\xcb\bN{\x1f\x10\x1f\xfb\x85r\x04\xdaK\x14M9\xfe\\\xa75\xc6\xf5\xe0m\x93\x98\x1c\x9ax4\xa7\xc0=\x0e2\xc0\xa4:o\xd8\xd9\x12\u045e ~\x8f\xf5qZ\u1ae0\x85\xe6\x1e\x033\x1e\xfc\xaa\r\"5\x8f\rn}\x99o4R\xee\x83\x16\x9c\n\x13\xa2\xdc\u019f7\x06i\xa8v\xa6G\xb3!\u062c<\u02de\x02\x00\xc9\b\xbaC\u0202\xf2\x8e\x18\xa0@e\xa0\x1e\xcf\xe9\xa1J\x11\\\x8fa\b\xb0\xae\xbe)\xfaf\xe8\xb8\u007f\xb8\xb4\x9e[\xc6#D\x9b\x9a\x17\x9f\f\xe77\x1a\xee\x12hN\x039\x97\xbf(\x8d\x91\u007f\xa1\x9a\x8d|\xf5w\x11\xff{\xe1I\x92?\x02\xfa\xd70\xce>p\xdd\n\xd8KE\xeb\xd6\xc9\x16\xe13\x8a\xd8@\x0f\x95x\x00\xef\xf8\x15\xef\xdcY\xa0?tX\xec[\x14\xd6\u007f\x86I\xe9F\x82\x91\xff\U000948cf]t\xd6w\u775bT\xe6\x93\xf5\x14\xaf\xf3\u03ff0\xba(\xfed\xfd\xfau?\u0770a\x03\xac\xb5\xe2]\xf7\x83\"\x85\x15\x9e|\xf8n,l\xbf\xcf\x1b\xf8+\xf6\x13\x99D\xb0\f\xd8\f\xb0\xba2w\x92\xc0=W\x9as\xc1\xda\x01\fI\x85\x9aL\xceHP\x88P\xa0:hFA\xe5\fR\xf1\x18\xbd7\xb6\xb6\x9c)\u017f\u05a7U\x82\xdeXi\x02g\x04L)\xa0\xa3\xead1\u01951\x04?\x19*]*9WI\xc3\x15\x924\xf9\xba\xfa\x1c5\x0f\x1bYc\u04d1f\x0f\x00\xa1\x19\x9c\xb3w\xfe\x8bw\xa1}\xf5K\x19\x97A\x105\x13*\b\\N0\x8a`\u04f4\xa1\x14`\xa9\xfd\xddi\xdb\xf4Z\u01cc\u04aa<#p\x87\x81\x19\r\x9aR\xa8\xfb\xa47NQ2~\x88+\x1fZ9\xe0\x13\xdfW\xff\x99\x92`(\u6e1a\ue31f\x1b\x0f\u8f8fc\xadC!\x82\u00b9Z\xfb\xa44\xe5\x92J\x8e(\"0\xd6ayP`a\xa5\xc0\xa8\xf0\tMD\xde;\xc67\xfc%\t\x88\x0e-Sq\x12\xa8\x97,\\O\x135\xcbd=\xb5+\xfd\u031dx\xe2\t;gg\xe7 \"\xe4-C\xfd\\=+\x85a\u007f\x05\x0f\xff\u4ef0v\b\xd6Y\xa9.\x88\xb2A\x973\xac\xe6Ro]F\xd1%\xdaqjuR\x94\x1aE[ce\xb9\xb27\xa5)\x055\xa3\xa03\xafn\x88\x95W\x1d`\xd0N\u153a\xe0\xbd\x1bps\x1cl\xe9\x84\nW%\xb7/T\x9bq\xa9\xf5\"\x15\x83\xa75(\xe3j\u011f\xd1Ry\x03k\xfd\x8d\xd6<Q\x8c\xd3,\xa2}\u056b9\xe8\xa5\x15\x01S\f\xf4T\xf3e\xadQX\x96\t\x83\x8c`y\x8di\u073d`P\x05a\x82f\xeef\xfa\xa8\x15\x03y\xe6O74\xa7\xab\xb1\xfd\xe6)\x85ZB\xa4\xa4\xa5E\xd0\t\x05D\xb0\x9appp\xe4`\x15\xc1\xe4T:E:\x9d\x18\x8cY\x813\x0e\x16\x80!\xc0\xd80\xa1\x1cA\xbc\xe4\xcc\xfdk\xe1\x9c`0tXZ\xb5XZ\xb506\x1e4%\xc4\xd4E)dj\x17\xe0\xdf\\fR\xd6YdZ\xef\xda\xcb\x1b8\x01\xf3\xc9z*\xf8\xf3#~\xa0\x14\x17Z\xab\xa4j\xb1 Rp\xce\xe2\xf1{\u007f\x84\xfe\xfc\x0e0\x14\xb8\x1c\xfc\xf1U\x95U\x84\xd14\xc3LQHm\tIDh6\xbb\x1aR@\u051b~5\xea3p\xcf>4\x83\xa0f4\xf2)\x15T#\t\xf0T\xa7\xe6\x86lN*\x15H\v\xc5\xd36d\u009a\xc19{\xf9\\\x87\xf7\x98\x19\x9dn\x16\x04\x01\xf7\x18\u064c\xaa,\xb7\xa3\xbae\xec\xfe\xd6\xf2i,\xdd\xd3\xf7t|\x800Aw\x152\xe5\a\xa9\x88\b\x92\x93\xaf\x80U5);v\x86\x10\u007f\xa5\xbb.\xc3i\xef\xc4S\x029\xd57\u023dGUS\xe3tB5\x1f.\xad\x83zeV\x83:\x9c\xf0\u048d\xd3F\xe3\xce\xd6\x1cD\u04be\xdav\xe2`\xc5\xc1\x88\xf8\u07ae\x02l\x87\xe1\xf2\xe0K\u00fe\xf9\u8303-\x1c\x9c\x15\x18\b\xac\b\xcc\xc8a4t\xb0&j\xca\x05\xd6\n\x8c\x15\f\x06\x0e\x8b+\x06\v+\x06\xab\x03\v\x91@\xb7\u01e0\n88[T\xba$\xaaNP\xd69\xe9t;\u0635{\xd7\x03\xfdA\xff\x16\x00\u063e}\xbb\x9b\x80\xf9d=m\xeb\xdcs\u03fbijjzg\xa7\xd3A)7ta\u0519\xbd-\xee\x8e{o\v\x12=F\x9a,L\xd6\a\u43ba\x04\xa3\xf7\xe0\xf3]K\x9f\x97\x96\x01\xa2\xca\xdd\xce{\x88$\x84j\x87\x90\xcd)\xe8\x8e\x0f\xfeu\x8d\xea\x97\x1a\xb7O\x89- \x89\xec\x85b\t:\xe4\xdc7\u0290S\xf0\xcfn\x199\x97\xf6\x84#\u0584,\xd0Aec\x98\x81\xf1\x12to\xdc\xfe\x9e+c\u02bd\u05ca\x8a\xaf?\u00df\nr\xaei\xb5\xdb6\r\x06!\xeby\x8e?\xea\xf7K\x1fo^\xab\x12o\xb6\x8eS9\u0478\u007f\xbc\x1f\xcca\u042c\x02\xa6\xfdI!\x86HS\xb2\x11Hi\xd4YW\xec\xa4\xf3_\xf1?\x1c\x93?\xf9\xc1\xf7s\xac8\xff\xfeG\x13\xb1\x98\x02d\x1c\xc4\xf8\x11|'\x9e\x901\x01\xe0\v#\x18\x0e-F\x85Aa\x1c\x8c\x15\x14F\xb0\xb2j\xb0\xb4R\xa0?p0\xae\x1a\xed\x0f\x01\xb9\xfe\xb1:\a[\x8cJs8F\x95\xc0%\u21080\u055b\xeao\u077a\xb5\x0f\x00\x87\x1f~\xf8\x843\x9f\xac\xa7o\x1du\xd4\xe6\x1b7m:l\xc7\xdc\xdc\x01\xb0\xd6W\xd5\xe2,\x98\x00\x9de\x18\xae.\xe1\xf1\x9f\xde\x02\x16\v\u036aVF\xb1\x03\xf4\u04075\xdb\xcc\xf3\u65774j\xe0\"\u0524F\xa4V\xa9I\xfcD\xaa\xc47<\xdc\x17Okd3\n\u0228\xd6\xe8\xaa\xe1\f\x8d\xfbp\xcbX5\x99\xd6\xc2\xe1\xd7\x19\xe0\x8c@\n\xbe2\xe7\x16\xf0M\x1c\x1e\x9b@\xcc\xe4\x95-\xd2\xe3\xaa\u008d\x8a\x8a1\u007f,\u00b8\xba\x86Z\xcf\x0f)\xb0\x11\x13\xb2\x0eA\xa9\xa0\x9bf\x02z\xec\xf9}\x1e\xefC\xa4\xb5\xb3\xf8\x97\x14\xbd\x8c\u045b\xd3\xe8L)t\x82>=\x866H\x8b\x87\x0e\xed\xf5TQ\x1f\xb5\xcd4AM\xb1\xaf\xca3\xaeM\xbf\x8fW\xf4\xf5\x9eE\x9bs\xbb\x930\x18\x95y\xf9eMJn\x05\u0537\x90\x91\x85\x14\x0e\x12\xaa\xf1H\xa1\x18\xe5i@\n!\xcc\u00d1\xc3p\xe80\x1a9\x8c\n\x87\xfe\xd0a0r\xb0\x12\xde\u007f\x92@[\x95.\xbb~\xc3q\x16f\xb0Z\xf5\x17\xe2)\t\x028G\x04\xe0\xd0C\x0f\xd5\xcf\u007f\xfeY\x1a\x00\xb6n\xdd:\x01\xf3\xc9z\xea\xd7\xea\xeaR\u025fo\xde|\xf4\xad\xddn\xb7\xf4\x87vA/\xabX\xc1Z\x83\xf9m\x0fb\xb0\xb2\ryG#SA\xcd!\x04v\x04e\x04\xd9\xd0_\xfdN\xc5\xea\u070d\xe5\x97Q\xb3\t\x88(WKR\x85\xd8?\x1e!\n\xd3\xfbA/\xad\b\xd9\\\x06\xdd\xf5\xd5y=\x88\x81\u01a3gbe-\xcd\u071b\x06\xe5C\x04\xce\xd9Osj?%X\xaf\x14\xc7C\x96K\x15\rR\xab^\x82\xe91l\x90\xff\xf9F\xf1\u06aa\x90\xf4d\x10gU\x05kW\xc8\x1c\x1c\x10uTph\x02M\xd79\xe9\xb5n!\x16\x9b\x19y\a\xc8|Z!\xd7\u0790K\x01Aw\x1e\xbcT\x80\x16E\v\xb5l\x9a\t\x81\x15$\x9dY7\xf0\xe4=\xae\x01\xb4\xb4%\x12\u057a\xa6\x91\xb0\xa1\xb1S\x013Aw\xbd<\x95\t\xd0\xca\a\x99\xa0o\x81\xbe\x81+\x9c7\u010a\x95\xbe\x00\x8e\x04\x96\x91\x18i\x01\xd6\t\x06#A\u007f\xe8\xd0\x1f8\x8c\x8c\v\x1e\xe7^\xd6\xc9YH5\xe2\xaa\x1f\xa0\x14C\xac\xc1p1\x04N4\xe4\x87\xceY\xd7\xebu\xe1\x9c\xdc\x0f\xe0^_\x99o\x9ap\xe6\x93\xf5\u052f\xa9\xa9\xd9\xf2\xef[\xb7n\xfd\xfa\xd4TO\xb4\u05be\xba\x15\a'\x16\xcc\f\xa52,=\xf9\x04\xb6=r;\xf4\\\x86,gh\x95\x00\xad\x13pa\xc1C\vK\x0eF\t\f\xd5u\xbbcQo\xa9~;\xb5\xa4\xd5\x04d\x1e\x10kV,\"\xd0S\f\xbd>\x87\xed\xb2\xd7GG:\xa6v\x1f\xd2N\x8d\a\xb4\x8a\xfe&e\x85\xa7\x025\xa0B\xfa\x8c\xa65=KJxJ\x9bg%\xb5OP=\x05\x9aU\xde\xe8JUz\xfc\xb6\a\xd4v\u016fE\xc2pF\xd0S\f\xa5\xa9<I\xc4\xe9\xd4:\xf7\xd3\xc2S40Xk\x82\x9e\t\x92O\xae\u0326\xa2\x13\xa1$\x00M\x8d\x8a|L\x16\x9aL\x8ef\x9a\xc0\xb3\x9e+\aS\xddL\xad\xa5\xd3Y\u007f\xc8U\xd5-\x8d\x82_qP\x181\x81!\xc8\bPp\x80q\x80\xf1\x9f\xb1\xd2R\"d}\x9a\xa8\xa7\a\u00a0\x92\u007f\x8e\xd6\t\xac\x00.9m(\xe5\x03+\x94R\x89j&l\x80Z\xc3\x16C\xf4wm\xf3\xb2HR5%\x901F\xe6\xe6\x0e\xc0`0\xb8\x8b\x88\x16^\xf4\xa2\x17\xd1s\x9e\xb3e\x02\xe6\x93\xf5\xf4\xae\x8b.\xba\xf0[\x9b6m\xea\xfbJ#L\xb39\u007f\x06\xd5Y\x86\xc1\xd2\x02\xb6?t7\xa8\xe7)\x0f\xa5\xfdD\xa1\x8b`\x12\x02\x9a\xc5:X\x16/\x0f#i\xaf\xeej\xff\xac\x1aK^\x06\x16\xbc<\x12<\x90\u0130.\x9bUP3\x1aN\a\u007f\x91\xf0\xe5\xe5\x80M0\xa3J\xcb^\xa59\u051a}\xde\x03&\\\xc7\x1d.\xb5\xe5cvYM\xb5E\x02\x98\x04@30\xdde\u032e\xcb05\xad\xd0\xcd\u067b.\xeax\x8c\a@\xe3\x94\n\xed\x85='\x00\xaa\xe3\apJ\xaf\xf3\x0e{\xc9\x1f\xb7\x19\x8b7\xa7\u007f\xa8\xc6r\xabP\xe5\xa3\xe7\x03\"4\xc3\xdb\xfc\xe6\xec\x83\x1bH\xb0\xc6\x1b5\xf6\xc0\xe2T\xacb@\a\xf5\n\xe5\\\xdf\\\xa4\nInk_4\x03M< \xfb\xa5\x90\xf4<4\x95\xbe\xe5*L\x8dJ#W\x02\xc1\xb3\u0769\xd4\u0650\xab\x93\x14\xa3\xac\u0115\"?\x90\xa6\x19\x14\x9c\x18\xabM-(X\x98P\xf4\x971\\\x9a\xf7\x8d\xfd\x84\xa4\x13q0\xa6\x90\x03\x0e\x98\u00f1\xc7\x1e\x93\x01\xc0\x05\x17\x9c\xff\xb4N\x0eM\xc0\xfc\x19\xbe>\xf5\xa9O\x00\x00\x0e<\xf0\u0ece>\xfa\xe8Gz\xbd\xa9\xd2\x19.\uabd9\x15\x9c5Xx\xfca\xac.<\t5\x97\x83\xa7\x95\xf7\x87\xa6DE\x1d\x91\xd7Yx]\x8b\v\u0375Jw\u0754\xe1J\xf4\x91F\"\xb5K.\xc6t\x06C\xc4k\xc1;s\x1a\bt\x8bS\x1c\x86\x98\x1aa\xcb\xdc\xc6\xf3\xd63D}\xfed\x18\xf7\u05be\xf9\u064c\x19m\xe3\xaf\u04d4\xe5X\xf12\x019\x13z3\n\x9dYo\x99\x9be\xdeF\u05c3\xba\xf7\x15'\xaa\xb3\xe5BX\xdb\xfa@<_\xac:\\\x05\xd1k\xf2~1\x19\xd5\xf8\vj4&\xdb\xe8\xee\x9a\xe8\xa7\xe7\xd5;y\xc6\xe8jF\xce~\xf2U1\x1a\xfd\x8c\xb4\xdf\xd14m\xa7\xd2aR\xcdi\u040c\xae\xc8\xee\xb4\x0e\x97\xba\x15\xa646E\x8a:\xf1\x04\x94H\xbc\xc4\x10\x85\xf3\x95wF\x1e\xd0\u00f0\x90K\xbd\xde\xc5\x1bq9\x82\x9f\u007f\xa8\x92\x80|SVy\xb5\x92\xf7\xdca\xbfy+x\xbf\xf3P\xb4H\xd2]\xf1\xc6\\\f\x81C\u007f\xd7v\x14\xfd\x95\x00\xf0\x15\xc5\x146!\xdd\xe9\xe48\xfc\xf0\u00df\x04\x80\xa5\xa5%\x99\x80\xf9d=m\xeb\xe7\u007f\xfe\xe4\xf2\x83\xbfn\u077aok\xad\xc1\xcc\xe5 \x85\x04/sR\nK;\xb6a\xf7#\xf7C\xe7\x19x\x9a\xa1\xa7uh\xa2I5=\n\xf1\xde\xe6\xc5\b\u018d\xe0\xc4V\xa1\x01\x81\u05e6\xf4\x9c\x9e$u1{p\x85\xae\x18\xe4\x88\xe5Qz\xae\x00t\xa7\x14\xbas\xba\xfc\xf4\nU\xea\x86X\x02S\x8b\xdab\xac\xc2$T\x94H\x1e\x00=\xa1d\x9aus\x9b7z\xfc\xa9r \x91\xbd\x15\x80\uacbf\xed\x8c=8\x04\x1d\xbbR\xe3qw\xed.\xe8~\u0491;\xde\xf5\u0417\xaaa\xc4}\x8a\x13\xfb\xef\u0118\xab1\x8b\x19\x03\x8e\x13\u0303u\x1ek9\xf7\xb6\xb4J\x91\xa72\xbc=;\x14q\u02f3\x8b\xef\x1fjS\x9b\x9e\xd3\x06\xf4\x8c\x02\xcde\x1el\x91p\xea\xd4n\xddPg\x81\xa8\xfcQ\n\x95\xb8\x98\xa0N\xb1\x0e\u0384\x93F\xb0\xc5\x15M\x18\x8a\xc3\x10\xe2O\x80\xe1\xb9\xfbA6\x82\xe1\xc4m\x92P\x16\b:X\x003W\x95\x81\x88\vCC\u0276\x15\x1d;\xb5\x82X\x83\xc5\xc7\x1f\xf4j\x16nD&:\x8b,\u02f8(\xcc\xee\xf5\xeb\xd7\u007f\x1f\x00\xf2<\xc7\x04\xcc'\xebi[\xc7\x1e\xfb\x9c\xf2\xef\x1b6l\xf8\xce\xd4T/\xc4sUm(\"\x06\xeb\f+\xbb\xb7c\xd7#\xf7\x81Y\x815C\xcdh\xa8\x9e\xaa\x86)\"\u8245\xb5#\x98\xa2\x8fQ\xb1\nk\x87\xde\xf3\xc5\xd9\xd2]Q\u0106/W~1I\x98\xf4\fc\xd2\xc1x\xabn\x80\xe4\x03\x18z\xebrds\xda\xda\x15\xcc\xfa\x00\x00 \x00IDAT\xcb\xd4J>\xc2S(\xd4\xdauk\xe14\x94\xaf|\xbd\x8b\x93o\x80\xd6+Q\x19\x9bYoU:\x96\x9bT\x00\xa5.{\xfe8\xfa\x83D\x80\xe1\x90A\u065a\xd8\xd3(\xa6\xa3\xe41\xe7\xb0?y\xfa\tS\u028f\xb8'`\xd8\u6552\x9ef\xcaSN\xf2\xbf\xc2~z\x95\xf3\x8af\xa2\xb1I\x9e\xda!\xa1\u04a4'3\xa6Y\x97\xc1\xeb3`\xca\xd3@\x92h\xddkV\xb7\x90*\x90[\u0195;\x12\x93}\x8ad\u01f1\x01t\x9d\xaf\xdc]NXq\x82%\xe30\xd4@\x911LF0\xda\xcbb\x8d\x06\xa0\xb8z\xadu\bmV\xfe\xab\fW\xa1\u051f?$\r\x05\xc7N\xef\xb2K \xa5`\x8b\x11\xe6\x1f\xbc\x1b\xb6\x18\xfa\x1f\f\xca\x18\t\xee\xa0J)\x1c|\xf0\xc1\xc3\xcb.\xbbt\x01\x00\xce>\xfb\u0327\xf5Z\xd6\x138\x9b\xac\xb8\xba\xdd\xfc\u01a3\x8e:\n\x8f>\xfa\x18\x86\xc3a\xe9\x12GD\x10(\x8c\xfa\xab\x98\u007f\xe2\x11\x8c\xfa\xab\xfe\xfb\xcaA\xcd*P\xe1\xe0\xfa6\xc4o\x95\x1a\\X;\x80X\x03 \x03\x93\x06\xb3\x06\x83\xcb\v>\xba\x06z\x05\x01Cu\x14\x9c\x10`\x05\xb0\x9e\x1b\x85b\by\xef\x13J<NX\x13:\xeb2\xb8\xa1\x83]\xb1~4\x1b\x16\x10Sj\x9b\x01\n\xa1\x05\xc1\a\xbb\x96_\x1a+0\nc\xe3\\\vs\x1e\xab\x96\x13\xa3\xf1T&\x1f\xd5?\"\x94\u0394xN\xba\xe3\x00c=\b\a\u037bP\xe5\xe1\x1d\xd52c\x03VqX*4f\x81@\v\x84\xfc\xcc\xd2\xc6\x11I\x0f I)\x82T\xf1~M\xd59%\x9b\x12\xe5\ft\x150t\xe5k\xe2'|\x1but\x12^MU+\x02Z\x13\xb2\x034x.4=\x1dJ\xce]Z\xec\u039c\xf3\x14\f\xd9T@*\x89s\xa4\x84\xea2q5\f7\xe0\x9c\xc3\xca\xc0`\xbeo0$\xff\x9e9\xe7sf\xbd\xef\xb9\xe7\xe7)\xe1\xc9#`G\xc6Mhl\u05b8z)\xc3\xc90\xca>Ii\xac\xee|\x02\xcb\xdb\x1e\x868\a\xa5\xf32\x95HB\xf3s\xe3!\u03e2\xf5\xeb\xd7\xfft\xfd\xfa\r_\a\x80\x13O<Q&`>YO\xeb\x1a\x0eW\xd1\xe9La\u02d6\x93\x16n\xbd\xf5G\xcb\xff\xf2/zf8\x18\x02\xf0\u04c2\x95\xf3-ai\xc7\xe3X~r\x1bf7l\x84+\x86\xd0\x19!\x9f\xd3\xe8;\x81\x1b\xc5\x04\x1a\x02\xc8\xf9\v\xac\x18\xa1pE\x88\xfe\xd2 \xe8\xb2\u05258%)\xe4+'\u0470\x8e\x80\x91\x03\x93?:\xc3V\x01\x10D\xf5 `\u0342l\xda\u00ac\x8e`\n\a\xb2\x0e,>$\x83\\\xa4\x05\x18\f\x05\"\r0WG|\xf6\xea\x15\"\x04\x8a\x85\x92\xe0\x04)C\x12j2:T\x91b\u0360\x8bT\x16)B@\xc6\x1ex\x87\xce\xff\xdb\x010\x91\xa2\xf0\x95\xa6OWJ#\x86\xaa\xe3>\xa9\xb0\xc1\xc4\u02b1\xc3\xc0\xb4\xcf\u03ecB\x9fi\x9cFj\x99\xb8tR\x0fe\x02\x00\x0e\xaf\x81\xf4\x18n\xd9\u07ce\x15\x81qRZ\xfd\x8e7=\x93\xb4#&d\xd3\nj]\xe6O\n\xae\x9e\xa3]\xca\x12]\xe0\xb3\x1dPX\x815\x0e\x19\x01\xb9B\xeb\b\u007f\x93\x11s\x02\x14\x85\xc5\xe2b\x81]\xf3#\f\v\a\x8aJ'?\xc5S\xc6\xd3\xc1Q2\x1c\xdc4O\xf0^+U?\xb7\xaa\u04a3\xcbbT\x1f)\xad\x01g\xb0\xed\xf6\x1b1X\xd8\x05V\xc1\x1f>\xbc\x98\xe2,\x9c\xb54w\xc0\x1c6o\xde\xfc#\n;\xd8!\x87\x1c6\xa1Y&\xeb\xe9]\x9d\xce\x14\x00\xe0\xbc\xf3.\xd8\x01\xd0u\x9dN\x0eV\\\x82m\xe4]\x89\x19K\xdb\x1f\xc3\xf2\xce'\xa0t\x06\x12\x81\"\xa0\xdbc\xe8\x19U\x1an\x95Lp9@d\xe1l\x01S\f`\x8aU\x14\xa3U\x183\x805C\x883\xc12\xd7\x02\xec d\x01k \xa6\x803\x05l\xe1\xfft\xa3\x11\xccp\b;\x1c\xc2\x0e\a\xb0\xc3\x01\xa4\x18B\xe5\x06\xbak\xe0\xdc\b\xd6\x19X\xb1!\xe9\xc8\x02A^im\x01kG~,[lI\u00b3\xf6\xc7j\u0242\u0331\u64ce\xb2I\n\x12P\xcd\xc3[\xea\xfcC\xb3\x92\x95\xa4:\xcf\"7\x14\x9c\x1f\x11}\xd1\xdb\u87e4Q\x9c1\xc2\xfe\xe3m\x87g5\x90Q-\x10[Z\x06\x98\x9a \xe6d<r\"\xd5\xd3HF>s\x93+\x93\xb3fFv(\xa8=\r\xe2\x04&4b\xb3\u0408\xf6z?\a8\xaf\xf9v\xf1\xcb\xf8?\xad\x11\xd80\u0623\x9c\x94\xdav\xa4^\xee)\xb1\x1f\xfen\x1d0\x18Y<\xb9{\x88\x1dO\xf61\x18\xda\xd0@\x0e\x8d\xf9\xa8|R\x04V\x9e6\x8b7A\xd2\xd0\rIr4\t\x96\xe7\xcc\xc1\xc6!\x86\x91\x10\xcaD\xa1\x9dw\u0782\xc7n\xfa\x1a\xec\xa8\xef]\x13\x9d\xc0Yo\xeflM\x81<\xd7\xc8\xf3|\xfe\xf4\xd3O\xbda_\xb9\x8e'`>YI\xe1E\xab\x87\x1f~\xf8]\xd3\xd3\xd3 f\xdf\xedOB\x1eH+\xac\xcc\xef\xc4\xf2\xce\xed\xbe\x92\x0e\x95\x17A\xd0\xe9)`JyYXc\xea\xaf\xdc\x10\x9c\x83u\x06\xce\x198[\xc0Y\x03gM\x00\xd8 gd$\xf3\xde!\x8a.\x06_XS\xe6\x8c:k\xe1\x8c\x05\x91 \x9fV\xc8z\xbe\x8a-\x9d\xf4\xa2\xe1W\xb0*ub\xe1\u0107N\x1bW\xc0\xb1\v`\xc0a|\xbf\x81\xadR\x85mV\xb4\n\xd5hri\xce*\xa5\xb1\xcde\xd3NU\u0464\n\xa5\xea\xa2\rZkWe\x96D\xf5M)\xcf\xe9\x97\x1cxJ\xe4\x8f;\x9f\x8c\xa9E\x9a\x8e\x84)\xf5\x92\xfa\x86k\xaf\xba\xa9\xf4\xf3!\xb2-\xe8\xb7]\xf4-d@\xf5\x18\x1cl\x82\xe3\xd4e\xf0g\xf3TMi-+\xc1\x06B\x90\xb1\x0f\x98P\x8d\x87]\xfe3\xa1\x87\x00`4\xb2\xd8=?\xc2\xee\xf9\x01\x8a\xc2\xf8p\x8d\x92\xd3\xf6@L*\x84N$\xc0]?\x85T\xb6\xb9\x84DvJ\x95\x92\x89\x98\xfdO\t@J\xa3?\xff$\x1e\xb9\xe9+Xy\xf2q\xb0\xca\xfc\t\xc1\x85\xfe\x8e\xb30\xa6\xc0\xf4\xf44\x0e;\xec\xb0]\x97]\xf6\x8aoN\xc0|\xb2\xf6\xc9U\x14\u0177\x0e\xdbxX\xe0\x1d\x15\xca(s\x02\x88\x14\xcc`\x80\x95\xf9\x9d0\xa3Q\xc0Z_\x8du\x14\u041bQp\xb9\x97\f6r\x18*\u03d6\b\x80%\xd8\xfa\xafJ\x96X\xaf\n\u02f3vR\xc6E\xa0\xf6Gg\x01\xe7\x84\u03acB7\x8c\xbb;F\xd9\x18\x15\b\x1c\v\xac\n\x80$\x0eV\f\x80\x02\x8a\x02\x9f\x9dq\xbdQW\x17\x0f\x06k\x01jt\x12\xa9\xe5\xe2I\xa3\xd0B\xdaE\x8f*_t\xf6\x80.M\fo\x94\xcd\xd1+>\x86P\xd0\x14\x97\x89GT\x93\x82\xd4E\xd6\u037e/5.\xf0Z*O\xec\xefR\xe0\xcecfh\xfa:\xa3\xca\x1a\x05|\xd5\xee\u008d\xaaYU\xf3)\x8f\xef\x8b\rU\xb9\xb5\xc1'\xc5\xfa\u05eb\x9e(\xd5|\xd5\xc2\xf0\x8f\x8b\x01I\x82\xfe\xd0b\xe7\xee\x02\xf3\v#8k\xc1\\\x8d\u06f3\xf6\xcd\xe1\xb2\xe3*\xf5\xbc\xd9J8%\x89\u02c3\x84\x8a\u07a7/)\xed7\x030\u0574\xee\xc4\n\xfd\xdd\u06f1\xf8\xe8}\x10k\x03\xd0Gu\x90\x83\xb5\x06\xa6(033\x83\xe3\x8e;\xf6>\"z\x12\x00>\xf9\xc9\u007f\x9c\x80\xf9d\xed\x1b\xeb\xee\xbb\xef\x04\x00l9\xe9\xa4\xe5\u00cf8\xbc\xac\u0309\t\x04\xf6U\"\xfcD\xdd\xf2\xae\x1d\x18,-&\x86C\xfeb\xec*`jF\x01yt\xe6\xab<?\xa2b\xa0\xf4\u00e6\x84\x91 \xef\x8bR\x02y\xb8@\tU2M\xac\xb0\xa5q4'\x110\t\xb2)Fg\x9a\xd1\xc9}x\xafP\xb0\xe8%\t_\u0443\u011f\x00\x98\x05\x10\x03a\x1b\xae\x82\xc6\xd0\xd1\xd8\xe8z;\x10U\x99\xa6\x8dvc\xfc\xb9h\xde\x15\xa3\xdd\xc2\xc8x\xaa3O\xd17\x86\\\x18'>\xa6o\xba\nBn\xe1e\xb0v\nP\xb2WP\xeb\xccO\xf5=E !P\x18\x8d'\x178\xf51J\u0217\xb5\xa6\xcb0]\x8eA;\xfe\xfdp\x80\xb5a\u042c\x11\xfc \xe2*Z\x05\rN[\x00q\x95n\xdc9\xc1j\xdfb\xe7\xae\x02\x8b+\x05\x00\a\u05be\xe1\xcd\x01\x80)\x18a\x89s\x15\x98\xa7\x13f\xd13\x87\xabH=\x0e\x95\xb8\xd2\x04\x9d{\xa9\"\x10\xc2:\x90\xc8E\x89`G\x03\x98a?)*$4a\r\x8ab\x88\xa8\xf0\u06bcy\xf3W\xe2\xcf\x1c}\xf4\xd1\x130\x9f\xac}c\x1dw\x9c\x97(\x9eq\u05bfy\xa8?\x18}+\xeftA\xac\xaa\xd4rT\x8d\xa3\x95\x85y\fVV\xc2\xff\a\b\vWc'gt\xa7\x19\xc4\xf0\xa6]e\xe8n\xc2\xf1\x12jy\x90e&X);\b\x00 \x15\x18\x94\x19\x8b\xb1rNS_\xc4\x1f\xbfy\x8a\xa1;\x04\x1d\x8e\xe3.1\xfdB\xd4\xc0\x97G\xec\b\x9e\x1e\xe0\xd3@\aB\xaa\x85oC\xf1*\xe0Y\x1az\xe9f\xba{\xa9\r\x0f\xf7\xe7\x0f\x02T\x02e\x19\x87GU\x15lC\x83\x8f;>v\r-@\xdc\x04ri\x99\x19\xaa\x019\xd5O;)xQ!\xa0U\vg=\xfd\x9d\bw\x12\x1e\xdd\u007f\x99\x0ec\xd0e\xac\x1a\x81)B\x90w\xe0\u015dqej\x0f\xa5\xe1\x13\xc9\xccB\xd4us\xa05\xac\x93\xe05\xee?\x13\xab}\x8b\x9d\v\x05\x96\x87\x16\xc4\x02V\xe2#\xf1\xcaCb\xf2\x9eK#\xfd\x87jm\x87\xca)\x80\t\xa4\x18:S\xc8r\r\x1d\xfcW\x1cE\xe9hu2c\xa51Z^\x84\u9bc2X%/\x9a\x835~~\"\x16'\x87\x1ez\xc8\xee\xf8:\x9fz\xea\xe9O\xfb5<Q\xb3LVmm\xfe\xb9\xc3W\x0e\u07b8iG\xa7\xd3\xf3i+H\xfdH\xfc\xc51Z]\xc1h8,\xb5\x85\x92\x14\xd4 \x87\xbcC\xa0\x19F\u007f\xb7\xf7\x97\xa6\x94d\x0e\x90\xcee\xc3-H\xc2\xe2\xd0OHN\xe7`mZ\x8dL\xfaQs!\xaa\x0f\u02e4\u0702\x06\xa8KP}\x81\x90\x85%\x01\a~\x87\xc4_\xbcB\x14\xb4\xeb\f\xb0@THV\xa2\n\u009bp9V\xf3\x8e\xd1\x04\x95d\xb2\xa6\U0010ea99)\x05\xacz\xa4d\x10\x14\x8b\al\xa0\x94\xf4\xc5M\xcdi\xff\x18\xb3\x9c\xa0\xa6\xb5\x0f\x8a\x96=m+U\x00H\xfaR4\a6\xd3\xef\x8d%\r\x15\x0en`1\n\xde%\x14\xbd\x14\xd2<M\x006#\x8c\xa6\x18\x05\x03\xcb\xcb\x06d\x1cf\xba\nq,\x81bt \xca~h\x99\xe6\xe3l\u0578TD\xe5\xb0\x12B\x0fAD0\x18Z\xcc/\x1b\f\v_\x89\x97\xea\x11\x9bn\xf0\xae|l\xc4\xfe\x9f\xd4\xf0\xe3\xe1Pi\xc7\x0e\x06\xeb0\x05J\xd1&\x82|\xcf6\u0775\xa2\xd6\\,V\xb6=\x84\xe1\xf2<tw\xaa\fhv\xc1\n7\xfe\xbb\xd7\xebbyy\xe5\xdf\x02\xf8\u043er\xedN\xc0|\xb2\x9ak\xe3\x99\xe7\xfc\xc2\xf3n\xb8\xe1+\x18\xec^\x00)\xa9\x1d\xca\t\xf0\xc7\xd0b\x94\x9cj\xa5\xacV$\\h\xd9\x14C\x8c\xc2p\xd1\xc0\x19_\x98\nU\xb3\x99\x02\x0f\x12\xc2\xe4A\x98\xbd\u04e2D\xb3\xc5p\x81ID\xbaRYR\xcf\u03d4\xb2J\x0e\ua64e\x80r\x01\x0f,\n\xe3e\x82\x04\x80\x8c\x1fqt\x01y\x9c\x05\x9c\xb0\xd7);\v\x04\xa3\xa5\xb4\xc8\x1b\xeb3\"\xd5i7\"\xdfd<yG\x82|\x8e\u00b4%\x86\x9e\xdea\x0e\x15\xb9\x15\x90P\xd5\xf4e\xae\xd4$]\x86\xebr\xb2\xc1\xb4\xa5%U\xe1\xc4\xcdnb\x9b\x86\x9d\x1a*\x15q\x80\x85\xa0\x00`\x00X+!1\u067fY\x92\xb8?\x1aM\x18t\t\x05\t\xa4\xf02\xc3\xdd#\x87~\xdfzj\x8b\x83\xb5C\xf0\n7\xc6\xc1F\xee\\\xa4\xb4e\x88\xaf\x9bV\xc0TW!\xcb\x18B\x04c\x1dV\x87\x0e\x85\x15\xef`\tW\xa3i<\x90W\x80\x1d\x9fY\xe9?\x1e\x92\xaa#`\xab`M\x10?\x1a*I\x1ct`8\xe7B#\x9a\u02a2Du:X\xd9\xf18\xe6\x1f\xba\xa7\xe6\xea(\xe2\x81\u071aQ\xad\xfd\xb0c\xc7\xceC\xf7\xa5\vw\x02\xe6\x93\xd5\\\x1b6\x1cr\xd8\xcfM\u036e\u00ee]\xbb\xa1\xda2\x1a\x9d\xf5\x04)b~f:\x89R\x01A6\xad\x00\x03\x8cV-\xac\xf5\u0382`\x02\xd9\x18\xca\xeb]\x0f\x89\x01\xe1\xd0\u0434\xfeZq\x89\x93_\xd9\x04%jL\xbe \t\x85\x0e\xa7\b\x06\xa8G\xc8\xfa\x04[8\x18\xe7*P3\x14\x1a\xae\xec+G\xb2Pb!VA\x94\xf3\xda\xeeXR\v%\xc0\x910\xe2\xd2R\xea\n\x1aTTR\xf5\x89\xa7\b\xa4\xab +6\u8ba5\xa2XJ<\xa1\x10\x8b\u7a56aF\xe8\xaad\x8a\x9ch\xccy2Up\xb4\r\x06\xc5\x1d\x86\x1a^\ua945\x8e\xf8\xb8\xb4\xc5\xc2\xc1u\t\xd3C\x82\xf2\u0495\xe0sB\xb0\x00\n%\x18d\x84B\x04(\xbc:%\u01ad\xad\x1a\xa0\xdf\x0f\xef\xa1\x04\x9f\x13\x97l\xb6\x89\xc1\x16Q\x95\x02e\x1d0\xb2\x02\xa5<%\xe7\xc2\v\xad\xf2 \x89uRN\bc\xcc\xf5>(e\x92\xa0\x13\nt\x8ab\nn\x90\u0546\x16\xdd\x12\xe3g\u060a\xef\xa1P\xa2,\x15'Py\x17\x8b\x8f?\x80\xc5G\xef\x87\xeet\xcaW\xcb9\vS\f\x83\x84\xd6?\x15c\f\xb2,\xfb\xf1\x04\xcc'k_^J\xb2\x1e\xf4\xd4\\\xf0Z\xf1\x17%S\f\x8c@p\x94c8i\xda\xc4V\xc0\xc6 \x9fW9\xa3\x00\x11\x8c\xfa^\xe2\xc6\xde\xc9\xc8W\xa4H\x94-\x11rC\x05fC,\x1a\xc7H\x9c\xb8W\u0109\xcbF\x8ee\x82 \x90\fP=B\xc7\x10\xdcH\xbc\xe2F\x82\x1b^\x11\xa7\x0e\t\x02\vk\rP\x10lt\xe3\x8b<)5\xaba\x97T\xb8\xf5\xd9~\u007f\"\xa9\xa8\x89V+\x98N\xb0\xf6-<\xdd\xe38\x86\x02KH&\"HHj\xb2\x9a\xd1\xe9*d\xaa\xf1\x1c\xc7\xfeY\xa5\xdeT\xed\aJz\vi\xd6k\xe5\xd3\x02\x01\x8a\xc2ai\xd5`q\xd5\xfaA\x9c.\xc1A\xa13p`\xeb\xfb\rF\x01\x85\x16\x8c2\xf2\xcd\xd88^\x1fv\xb5\xb8\xb9\t\x04b\x1a\x94\x17*0\xadK#C\x89\x1c6'\x87\xca\x10\x8b\x94o\x14W\x95\xb8\x94\xf7S}\xb4\xd2\xcd\x01e3\x99\x88\xa1\u0201(\x9c|\xe2\xe7\x96c\x8f&\xc8B\x9d\x8f\x92\xab\x1c\xd4}\xf3\x95\x15\u00cd\x06\xd8\xfd\xc0\x9d\x18,\xecB\x16\f\xe7\x9c\bL1\x825E\xf9\xd2+\xa5h\u00c6\r\u0634\u9c3f\u0717.\xdcI\x03t\xb2\x00\x00\x8f\xce\xfb\xa0\x8a!\xe0\n\x95Cwz(5\xbb\x9cp\xc2\"P\x9d\x1eDe(\x8c\r\x9ab\x8c!\x98\xa7?\x04\xdc!\xe8\x1eC+\u007f\xaeO\x8d\x8d\xa8\x9cLta`\xc8+\x14\x9c\xf3\x15\xb5\xb1\x16\xc6\x1aX\xe3\x1bm\xb1\x11\uab03\x18\xaf?/\xbf\x1f\xa5\x8e6<\x98\x1eCu\t\xb9\xf6r;\u07d8\x8b\x9e\x1fA\x12I^\u007fn\x8d\x81\x1d\x8d<'Z\xb3i$\xa4\u0588\xd1\xcc*V\xa0\xf1\xc9:\x91\x96\x89\xc3\xe6\x16\xe9\xf3:\x05\x04\xcb>\x0e-R$\xc2\xc1\xf5\x11\xde,J\xcf0f\xa78\xf8VI\xbd\xa1\x97\x10+^bQM\xf5P\xc9Q\a\xba*\xe9@F\xc1\x87\xb3\x82\xe5\x15\x83\xed\xbbGxr~\x84A\xdfxgB+X\xe9\x10\xe6g\x15\x16f\x18\xcb=\xc6j\a\xe8\xe7\x04\x13\x8f\x1aQ1b\x05b\x91\f\v\xc5\x10\x90\xcay\x90\x12\xba\xa7\xd4}\x97\xaed\x95N\\\xe5\f\x95\xb1\xf7\x92WTy\xe2\xc4Ppjf\xc4&\xee\x9a\xd1KE\a5\x14\xa5\xf9\x11\x1e\xe0]\x98\x9aR\xe1\xe4`k\x01\u05b1\xa0\x10\xe8N\x17\u02cf=\x80\x9dw\xdc\f\x8aY\xb8\x10?\xec6\x1a\u052a\xf2\x03\x0e8\x00'\x9d\xb4\xe5;\xaf~\xf5\xbf\xfd2\x00|\xf4\xa3\x1f\xa1}\xe1\x1a\x9eT\xe6\x93\x05\x00\xd8=\xf4\u0235\x00\x18C\x1aJg\xb59\xf5*\xf4\u01c2\xbb\xb3(\xa8\x8b\xa20\xc8\x1a\xc1\x03\xa9C\xac\x8b\x00\x94\x937\xb4\n\xea\x87\xf2\x8c\v\x01\xbb\xc8!W\xbc\xafq\xc0\xd0z\x10\u0395 S\xec+kix\x17Z4-I\x93O\xb6\x1fUW#\x87\xac\xf0\xaa\x898\xaa\x0f\x17=Fbc\xcd\xc2Y\x02F#@\x00\x95\xe5\xe5 \t\t\xb5\xba\xff\x95`\xbf\xb6J\xb0^F3\xbc\x9e]Y\xb8\"!=\x92\xecN\xc7\x00:\x8c\xee\xb4*#\xdd(\x95\xcd5n[\x12\xa9f\xa4\x87\xbc\x9dkh\x16\xbb\n\x83\xe1\x04##X\\5X^\xb1\x18\x8dl\t\xc2\x12\x1f_\xdc\x178\xfe\x9b*\xf0\x1e\ubf3a\xc6\xf7\xa4\xb2\u0265\x1a\xabS*JJ O\xaa\xf0*\x14\xa3\xf1\xbc\x82&<Rg\xf1=#\xe6d\xca\u022b`\xc8\x01J\x04~~\x88\u02ea\xbctc\x88\x9f\xc7t\x82@\xa4\xf2M\xcfr\xb8\xa2\xc0\x8e\xbbo\xc1\xf2\xceGAJ\xfb\xcd\xdb\x1a\x14\xa3\x01\x9c-jo\xe7\xf1\xc7\x1f\x87\xe3\x8f?\xfe\x0f\xe2\xbf_\xff\xfa_\x97}\xe1\x1a\x9eT\xe6\x93\x05\x008\xe9\x909\x01\x80\xc5\x02\x87\xac\x16\u05bb\x1c&\\e\xea\xe67}\xe0\xc1\x98=`\x0e\x19,\xfc\xc0\xa0\x8cM\x19\xa6L\x05g\xec\xdd\x15\x93\x19\x13?\xfa\xed\xbc$\x90\xaba!\x02\xa1p\xc0ja\xe1D\xbc\xcb P\x1b\xa7\xafM2\xa6G\xf2x\xec\x0f\x15\x1du\xfc`\x8b\n\x16\xb4%\xe7\xea\x82Wv\x1c,r\x02\t\x13\xa5\xa5\x85@\xac\xd0\x13\x80\xaa\xf5^\x81Z\xd5Xk,\xb6`\xb9 \x04EG\xdb\x00[\xe9\xa2)\x9c\x1a\x1c\x00\x97\x05\xcfsS9GJS\xb7m-\x9c\xf5\xd5t)\xd1s\xbe\x81\xe9\xa5\u05c2\xe1\xd0ai\xb1\xc0\xd2B\x81\u0565\x02\xf3\xf3#<\xb1\xad\x8f]\xbbF\x18\rmU\xd1\xd7N\x15\xb1\xd2\x0fQl\xc6A\x8c++\u007f\x89\xa7\xaa8\xa9\x19\x95LI\xf0\x06q\xeap\x15@[3(S\xe0\x8e\x86\xca\x158S\xde.\x82\x93\xbe\x04\xd59\xff\xb2\xefL\xd5f@\x9a\xbd\\3\xe3\u048a\x81\xe0\u9c4c\t\x8a\xbc\xdd0\az\xceIU\xad\xdb`\x0f \xa9\x1ds\xb8/\x95eX}\xf2\tl\xbf\xe3\ap\xd6\xfaq0\x11\x98b\b3\x1cT\x9f\x05\x00sss\u0632e\u02d7\xdf\xf1\x8e\u007f\xff\x15\x00\xb8\xfa\uafe5}\xe5\x1a\x9eT\xe6\x93U\x81\xce5o\xd6w\x14\xb8\xa0\xdf_\xc1\xea\xe2n?\x05\x1a\xab\xbf0m\xa7\xf3\x1e\xd6\x1f\xba\t\xb33S(V\x17\x92z\x80\x12n\x93\xc6\f\x94J3\xab\x91\xe7\xaf-\x00\x16\xef\xa0'\xd6gF\x92\xf8\xe6\u0520\xb0`\x89\xa3\xdfA\xd9\x10\xaap\"\x19\xdb5$\xb8;\x96\xd1a.\xc9\x15\xed2\xa8O`\xeb\xedt]\u0d8dq\u022c@9\a\x10{G=\xb1p\x96\x01\xb6 \x1b\xf8[\xa8\xa0\xac\xa9\xaah\x91\u0532@jsBi\xa5]\x9d\x16\xa8\fM\xb6\x10\x14Q\xb7\xe7*\xab\x00G\x80Q\x02\xeb\x04f\xe4\xfc\xeb\x03\x86\x0e\xbc\xb9+cM\xbd\xd4/N:FS\xb0\xa8\xe9\x87\x00\u00e1\xc3\xc2r\x81\xc1\xc0y\x8d6\x80Q\xe1S\xe9Yy~:r\xdd\xe5\xfb\x14\xabo\x97\x94\xdb\x12o]\xaa\xed<\xf5\x1dK\xacl\xab\xc9\u02eaI\u0351\x0e\xa1h\x18V\xff\xfd\xaa\xf9\x9b\xf4\"\xa8R3\tG\x99d\x90\r\x86\u03e0\xd8\U0001e2c0\xe1\x87\xc6R\x99\xb9$\x9b0\xc37zm\x943\x82\xe0\xca\u03ea\x80\x94\x02\xc4a\xf9\x89\x870\u063d\xdd\u007f\x9f\t\xd6\x14(\x86}\xdfSI\xda\x14\xa7\x9f~\x1a\xce<\xf3\xcc?'\xa2!\x00\xbc\xeeuo\x90}\xe5\xfa\x9dT\xe6\x93U\xae\xdbN\xfe\x8d\r\xfd\x81}\xc1\x13\x8f>\x8c\x9d\x8f<\bV\xaa\xac\u0108\x00g\v\xcc\x1e|\x18\xd6o\xfc9\xc0\x19T\u04cf\xa8\x94\vc\x9d\xbf0,B\xe2\xa9\x16\xf2a\xd1\xcez\xa7\x0f\x82\x83\x83\xf5^\xe7#\x83bP\xc0\x0e-z$\u0202\u06d2\x18T\u0561\x95\xd2B \xe9\xc0\u008a\x84\x01\x14\t\xb4\xb8\xaf8U\xc7Ws*\x84/\u0107U\x14\x82\xd1\xc0\x86\xa9R\xcf\u04fbh\xa6dm\xe0\x9d]\b\x9c\x96\x92r(\x9b|\x0ep&\x04)X?0\xe3\x8aP\xc9Z\x01Y\a\x8a\xa3\xed\x85\xff?\a\xc1\xd0\tL\x11\x86glh\u0205\xaf\x11\x01\u00d1\xc3\xea\x8a\xc5j\xdfb0\xb0\xe8\x0f\xfc\u07d7W\r\x16\x96\r\x96V\f\x86#\ak|pC\u007f\xe00\xbf\xe0\xc7\xde\x17\x17\v\xec\xda=\u0136\x9d}\xcc/\x8e0\x18\x85\xdb\xe8[X#\xd5LVt\u038a\xbd\x8exR\xb1\xc90VR\xb1\x8f\xd9\xc1DM6\x85\xca[q\u5552\xf9\x04#\xd5ap\xaeJ\xc30j&8\xa5\xf9$TQ/\xacB\xb0v\xbc\xad\xf0E\x89\xe3B\x9c\xfcd\x12d\x1c>'\xe1q\u0692\xc2\x0f\x15:3\x1c\bB\x89q\\\xe2\xb5C\xe4C\x9b\x8b\x95\x05 \x19\xdb/\x86\xab0\u5d27\xff\xe1\r\x1b6\xe0\x94S\xb6\xfe\xc3\x15W\xbc\xf1\x06\x00\xf8\x9b\xbf\xf9k\u0697\xae\xdfIe>Y\xf8\xf1\xbd\xf7\xe1\xa4go\xc6\t[\x9e;\xf5\xd5;\x1f;\xf9\x87\u07f8\x01K\xf3\xbb\u041b\x9e\t@\xee\xa7\xe4\x9c1\x98y\xd6&\xcc\x1c\xbc1|\u0429\x96r\x1f\xf4n\x151\x13)W\xeb`\x04@\x16\xe4w\u05abc\x14\xfb\xc6\x15\xe9\xc0\x8f:\a%\xc0\xb4\x06:\xca\x03\x8e\x8bMX\xc4\x01%*/0W\xfaySP(\x04\xa1DPL0\x93o\n\xc6)?[m>\xce:\xf4\x97-h\xca!\xcb\x18D\xde\x1f\x06\xca\xfbs\b\xf9\xd8;(\x8b(\x87p\x0e\x15\x9d\x13\x1eC\xc3\b\xb7\xa4\xfeK\xccw(\u04da\n#X\xb5\xbeJ\xcc\xc3\tB\xc2\xc4g\xa1\x01\x03\a3\x12\xac\x8c\b\xc5\u040f\x9cKR\x95\x1b'\xc8\x14a\xaa\xab\xa0\xd97\xf7V\xfa\x06\xab}\x13\xaa2\x82\xb1\xe15\xabq\xedR6n)\xf5+\x97\xd4i\x91\u01a8\xa1\xb4/PU\xbc\xd4H\xf2\xa9\xf2[\x91\x84;\x8c\xadTb\x99~+n\b\xd1@\x8b\t\u03b0\xaf\xa9-\xfb\r\xbcv\x18\xf3\x958\x91@IP\xef\xc4SS\xb0\xe8\x15\x00:X\x05\x1b'\xb0\xb1!\x90\x98l\xc5S\x8dw_\xd4\xe8\xce\xccy\x96\xc9\x1aXk1\x1a\xf6\u02e6\xa7\b\x9c\u059aO=\xf5y\x0f\\y\xe5\x15\xff7\x11\x15\xef~\xf7\xbb\xb2+\xaf\xbc\xaa\x98\x80\xf9d\xed\x13\xeb\xd1G\x1f\xc6\xf7\xbf\u007f#Nz\xf6f\x00\xc0*0\xfd\xc5O\xfcw|\xff+_\xf4MIq\xa5`W\x9c\x03\xeb\x1c3\x87\x1e\r5\xb5\x0e\xa6X\xf5\x95.\xb3?\xaa\x82@*\x83\x90*\xe9\x8e\xc28X\xe7+(f@2\a\xd1\x02\t\xc1\x13\xba\xa3||\x99V\x81&A\xa0W\xa2\x02\xa5\xba\xea\xa5\xd4LK\xe2\xc2\xe8\xabp0\u01d3|#\xc5GJn\xdc)@\x8a\xe8#\x13\xaa\xf3U\x83\xfe\x12\x83f3?5\xc8\x02h\x80\x11\x142\xe45\xe9\xe5m\x05`)\u00c4\xb9\n\xa2\xaf5\xfc\xaa\xb6\x00\xacu\x81\x86\x16,\xf7-V\xad\x03\xeb\u0434c\x81c`\xa4\b#\x0eC;V`D`FR#\x92)\xf8\xc7\x14\x00\x86}\vf\x82\xb5\x02\x13\xaai\x8bJy\xc3A@\xed\x15-R\u6596`\x1cOP\"\xed\xa3\xa2\x8d\xac\xcfR\xcd\x14\xab\xe8\x98e\x1a\xfdf\x12\x95J\xbd\xf1\xd9\xcc\fM\xfe+\x00\xb7\x8fi\xe3\xa4Z'\x90\xadxwo\xf8\xe6\xca\x17=>\xe4\xe8\x89n\x1d\xe0\x1cC3\x82\xaa(<\u007f\xf8M\xb3\x90\xa81\x0f\x1e-I\xaf\x85\xc2\xedw\xbb=(\x02\x06K\xf30E\x81b\u0507\rMO\xa5\x94Xk\xe9\xb4\xd3N\u0165\x97\xbe\xf4\x1f\x8f?~\u02dd\x00\xf0\xf6\xb7\xbf\u077c\xef}\xef\xc7\x04\xcc'k\x9fXw\xdf}\x17^\xf6\xb2_\x8a\xbcs\xefw\xff\xe0\x0f\xfe\xe8S\x1f\xbb\x1a;\x9fx\x04\xdd\xee4\x9cu\xa5\xc1\x96\x19\r0u\u0421\x98;j\v\x06#\v\xed\x1c\xf2L\xc3\x19\x83\x1dw\u07c6'\xee\xba\x15B\x8cl\xfa\x00p\xd6\x05\xe7=@\xf5\xa0\xf2.\xa6f\xa6\u041b\x9a\x02e30N\x83\x95F\xa65\xa8\x97Au\xbd\x91\x94\xb0@LQU\xf6\xc2U\x05(\x95\x9a\xa2\xe4Y\xa3a\x1e\x04\xcc\x0eJ\x92\xb0\x8b8`\x14mX\x15 \xdaW\xf2\xb6\xec\x82\xf91\xf6\xc1R\x01\xce\b\xdd\\{<J\a\r\x85R\x19O\xbd\x0f@\xf5\xc0\xb6\xd8\x0f\x95\xe4\x90\x12' G\x85\xc3J\xdfb\xb5\x1fxk\x06\xac\xf6)\xf1\x8e\bV\xf9\x1eBt\x87\xa4\xc4G7N\xcdzJ\xc4\xd7\xdc\u01a1\xee\xec\x18'o\xe3\x10U[\xa8(Qb\\F\xe3\x1e\xea\xad\xddk*\xc3\x1b*Y`RE\x03\xa9\x16\xb0\u04b4S\xfdu\x92\xf4wc3\x93\x13\x85Kz\x14\x88\xe1\xde\xd5\xee\x01\"\x15n\xc3\xc5t@\xa8\xe8\tI\x95\xd7\x01\x13\xc0Ba\u0407`d\u071f,nbD\x04\xa5\x14\xa6\xa6\xa71\u06bd\r\xf7}\xeb\v\x18\xae,\x95\xde\xf7~\xa2\x95`\xad5G\x1f}Tv\xfa\xe9\xa7\xdf\U00096dfc\xf5\xfd\xff\xee\u07fd\r_\xfa\xd2\xf5\xbca\u00f3\u073ev=O\xc0\xfc\x19\xba\xe6\xe7wa\u077a\x03\xcb\u007f\xff\xe1\x1f\xfe\xc1\xef}\xfes\x9f}\xf1C\xf7\xdf\v8'\xce\x19\xf2>\xe3\x1a.\x94\xb2\a>\xfb\xe71w\xe4\xf1\x18\fG`\x00\x86\x18\xa3\x95%\xdc\xf7\xc3\xef\xe1\x81\xef]\x0f;\x1a@uz`\xa5\xc1:\x87\xd29T\x96C\xe5\x1ddy\a\u0719\x02\xeb.\x14w\xa0\x14\xa3;5\x8d\x83\x8f<\x06S\a\x1d\x84\xa9u\ab\xf6Y\x9b\xc0*\x83s~\xf8\xc3k\x83\xebcIH\xc0:\x0e\x15Q\xe0\xb4]\x90\x1aV\xc7\xe8\xf0\xe3\x04\f\x01\x8c\"\xd7\x1e\xa4n\x10A\xd1\x17,,\x10d\x16\xe8u3Tv\xb2\x94\x1a\xf0y^\xb9\xdcT\xa8\x1a)\x97d\x80'}\x88V0\x18X\xac\x0e,V\xfb\x06\xc3\xc2\x05\xef\x9a \xcfVH@U\xca\u01a3$I=\xf5\xb1\xdb\x16\x8a\"\xfdy\xa97.\xd3a\x9dd\x8c\x145\xa3\x17Jm\x1a\xc2Tn\xec_\x06\xb9h\t\xe2T}\x0f\x8d\xe9Ij\xd9\x10R)\"S\xa5\vo\xfen\x93\xdbI\xbd\xc8%U\u0144!3\xcd@\x0e?\xbe/\xce\xc7\rJ\xe8$\xc7\u03c2\t\x83\xc4q\xaa\x94\x90\xbew\u1e72Bwj\x06\x18\xae\xe2'\xd7\xfd\x1d\x1e\xfd\xc9\xf7a\xc5\xc1\x98Q\b\xe6Vp\xceI\xaf\xd7\u034e;\xeeXw\xc9%\x17\xbf\x9b\x88\u6bff\xfe\xf3|\xe1\x85\x17\xbbo|\xe3kx\xc1\v\u039d\x80\xf9d=\xbd+\x1d\xc1\x17\x91\xb9\xff\xf2_>\xf0\xc1\xab\xaf\xfe\xf8ko\xbe\xf9\x87%\x8a8k\xabtwkp\xe0\xe6\x13q\xc4\xff\xf6R\xe8N\xd7\u02f5\x04\xb0C\x03\xeb4\xa66\x1e\x8b\r\xc7<\x01[\f\xfdE`\n\xd8b\b;\x1a\u0099\x11\xccR\x1f\xabf\x04gF\xb0\xa3\x01l1\x808\x03\xd69\ue7daC65\x8dC\x8f?\x15']r9\xd6\x1fz\x04\u020c\x82\xe1V<27\x89\x00jbQ\xa2\u0328\xa0E\x82\xb6:\x82*;It\U00061135\x9e\xb6\x98\x17\x82\x13\x8d\x19E\xd0\xe29[AU\u0757\x8d3\xa9\xc2\x0flc0&V\xd1\xe2\x04\x8b\xcb\x06\xbb\x17F\x18\x1a\a\xe7\u01bd\xbcSO\x97\x1a\xb2\xb9:pS\x92\xe99\xaec\x97\xba\x87n\x82\x92\xb4\x86\x85oi\xbf[\xfe\x117\x14.i#/1\x8c\x19\xc6u\x1bB*\xa5\x9a\x91\xf6\xa2\x1a\xad\x12\x03\x92c\x88D\u4f9aN\xc2$)\x9b\xdf\xe24\x1c\xf2d\xcb'\xc2\xfe}\xd1\u481c\x9f\xd8u\x89\xad\xb2\n\xdd\v#\xfe\xcb5\xf9\u007f\xaa\x14,L\nyo\x1an\xb0\x8c\x9f|\xe1\xefq\xdfw\xbf\xe4\xfb.\xc5\b\x90\xe0a.\"D\xa0\x13N8\x01/x\xc1\v\xfe\xf2%/\xb9\xf4\xf3\x00p\xf1\xc5/v\x1f\xff\xf8\xc7\xf69 \x9f\x80\xf93\x14\xc8/\xbd\xf4\xa5\n\x80\x15\x91\r\xef\u007f\xff\xfb\xae\xf9\xdc\u7bbd\xf0\x96[n\xad\x95\x83Q\x1b\xedL\x81\xe9\x83\x0e\xc5\xe6\v_\x8d\xf5\u01dc\x8cbe9\xd0\b\x04\xe7,Hi\x1cr\xd2\xd9\xd8p\xfc\xa9!\xb1\xd7W\xb4\xce\x14a\xaar\x003XE\xb1\xba\x8cbe\x01\xab\xbb\x1e\xc7\xf2\xf6\a1\xd8\xfd\x04\x9c-\xc0Y\x17*\x9f\x82\xcb7`q\x95\xd1\xe9\x17\xc8U2\xf5\u01c9\xa7GI\xe2F\xf8\x960\xd4S9,R\xe0\xaaG\xc6b8r\xe8\x0f\x9cW\u007f8\xa0\x13=;J\v\x19\x1f\xf8\u03240\xb2\n\xf3K\x0e#kq\x00\x1cz\x04\xe8tz\xb2\u00b32->r\xe5\xde\x0124e\x9d`a\xa9\xc0\xce\xf9\x11\x8cI%\x84\x187xI\xfe#\x86\x05c\xac9\xd9tkL\x01\xbce\x8b\x8b\xc8\xc8\xd5w\xa9\xa1\x1cI\xff,\xe9\x91D\xcf]\x01\xb8\x00\xcd,\xd0D\x96\x12y\xfc\x12\xf4\x03\x80\x97Fb\xb5\xe7<Nw\xa4\x93\xa2Mn=}\xec\x04\x82V\x04\xc5\xca+\x84b\xa5MH\x147\xfe3`\\\x90\x9f&\x8f\x9f\x13Y)\t\x90\xe7]\x14\vO\xe2\x8e\xcf_\x8d\xfb\xbe\xfdE8\xeb\xa3\xe0\u011a\xca\n\x00\xa0#\x8f\xfc9\x9cw\xdey\xdf\u007f\xf7\xbb\xdf\xfbV\x00\u0638q#\xfe\xfa\xaf\xff\n\xbf\xf8\x8b/\xdb'\xaf\xed\t\x98?3+r+\"\a\xff\xee\xef\xfe\xce5\xd7]\xf7\xf9\vn\xba\xe9\aHr}|S\x93\x18\xce\x14\xe8\u032d\xc7Q\xe7\xbe\x02\x1b\x9f\xf7\v~\xa0\x82\x01\xa8\x90\xbe\"\x1c\xf8G\x85<\xef\x95\xf7\x91V\x9b\x02\xf2#\xf6\xae2]rE\x81\xfe\xee'P\xac.\xa2;\xb7\x01zj\x16*\xebB\x84\xb0{\xd7*fg4\xa6z:p\xe0M\u08925\xa8\x02\xe9C \xb1\x01\x86\x85\xc5`\xe80\x18Y\x18\xe3\x81=R\x046\xfapH\xd4>\x13D)\xb0V\x10\xad!PX\x19\x12F\xf3\x0e\xbd\x91A\xb7\xe3\xd0\xc9\x14\xb4\x0e\xca\x18\xaa&2]h\x86:\v\x14\xd6z9\x9c\x13\fG\x0eK+\x06\xc6:\x9f'\xd9B%4\xc1\xb9\x1ct\xaaH\xe3\xf2\xb9q\x02\x88\x92\x00\x9c$X\x8b\x14\xc4S~\xba\xd9xL\x8c\xcb\xca\xe9Kj\xfcn\xba\xebPL\xbb\xa7\x8a\x87\xa7j\xac\x1e\xa1\x01Zj\xc8\xc3mH\xc2\x1e\x11\xd5\x19\xa3\x9a+e\x83M\x8a\x8b\u00ddH\xf0\xed\xc9\x19\xd0a\x1a\u05c9\xf2Q\x85\xc4\xc1\x06\x82\xc0`\xaf%\x0f\x12X\xa1\xf43\x18g\x13|\xb9\x9eu{\x18-\xed\xc6\x1d\xd7\x06 \x17\x17r=\x87\xe5\xfb+\x00fgg\xf1\xdc\xe7\xfe\xfc\xb6\xb7\xbd\xed\xadW\xfe\xe9\x9f\xfe\x19\x00\xe0?\xfc\x87\xdf\xdeg\x81|\x02\xe6\u03e0u\xcd5\u007f\x9fR+\a\xbf\xef}\u007f\x10\x80\xfc\xa6\xda%\xc5\xca'\x01\x883\u041d9l\xbe\xf0U8\xfa\xfc_\x02)\r7\x1a\x822\x9f2\xcc\xc6V\xa92\xd6\xc1\x9aA96^\xbb>%\x89\x14\x82\x97\b\xeaN\x17\xb3\x87\x1c\xdd\x18\xfcqp\u01a0\xb0\x84\xa5\x15\u03c1\xf6z\xde\xcat\fD\x02\xc00\x01\xc6x\xe0\x1e\x15\x1eH\x87A\xc3\x1dKiV\b\x8a\b\xcf\xdfZ\xf2&_D\xec7%\xad\x81\\A\xb1\x0e\t\b\x04\xe3\bK\xcb\x05VV\t\x99fd\xc1\x0f[\xc5\xd0\xdf \xf4\xb1\xce\u06fd\x8e\x02\x95\x02\x04\x90\x0f\xd5z\x15\xa9\xd6R\x9d\x02\x15\xe8\xb4\xf0\r\xcdP\x89FQ\x9c\x84f\xa3jDRjnE\xf5)\x9a`f\x85\x10\x9b\u059cl\xad)rP\x81x\xadzO\xe4\x83\u008d\xff\x8f\x15\xb74@\xbf\xad!\xba\xb71\x9b\x90p\xa5D\xa0)\x80\x94\x00V\xa8|n\x12U;>\x85\xd9\x039G\u03df\xf0\xb9\f=\x14'\x9e\x03\xcf;9\x06\xbb\x9e\xc0\x1d\xd7}\x1c\x0f~\xefKeM_\fW}\x97\x84|\x91\xd2\xe9tp\xdey\xe7\xe25\xafy\xcd;\x8e<\xf2\xa8[\x01\xe0w~\xe7\xbd\xf4\u05b7\xfe\xef\xb2/_\xe3\x130\u007f\x06\xac[n\xb9\x19\xd7^{m\t'\u007f\xfc\xc7\u007f\xf4;_\xfc\xe2\xf5\x17\xdc|\xf3\xcd5 \xf71q\n\xceZ\xa8n\x0f\xc7\\\xfcj\x1c\u007f\xe9\x1b@*\x87\x19\xae\x86\x80g\xf1A\x12\x91\xdaH\x06y\xe0\xc4\xfb\x83K\x92\xba#i\x9b,\xd2\x16\xa6\xfcW\x1c\xc0\x11x\x89\x1a\x93\xaf\xa0\x17\xfb\x0eC\v\xf4:\x82,\xf3#\xf9)7\xed\xac\xa00\x0e\xfd\x81E\u007f\xe8\x82D/\x02\x0e'r\xba`\x9b\u01de\x04\x96\x8c\xe1Dy)\x89\xd6\x10\xc5A\x1aWy~H\xa2\xcb\x1e\x8e\x1c\x86C_\x067)\x13)'$i\\\x14\xd2L\x91\x93:s\xd1`\x90\xbd,\x12\xa8\x95\xb2\x8d\x1c\x8e\x9a\u0084\x12:%275uH\xa3\ua9b1\f\xb9\x06E\x938coU\xd1\xd4\x00\x00 \x00IDAT\xa5\x96\xb9\x14'\xad8\xa1`\xa8QqK\xbd\xc0\xde\xdb$\rS\xbb;{\xb5\xefx\u007ft\x8d`\xc9+1\t\xaa2H#\n\ue4e0\xd0\xecDp\\\f'P.Mi\xa0\xb3\fYg\n\x8b\x0f\u0789\xdb?\xf71<z\u02f7\xfc\xe7\x01@1\xec\x03b\xbd+(D:\x9d\x9c\xce?\xff<\xbc\xf6\xb5\x97\xff\xd1\xcb^\xf6\x8a\xbf\a\x80\xf7\xbe\xf7=t\xd6Yg\u027e~\x9dO\xc0\xfc\x19\xb0\xbe\xf8\xc5\xeb\xf1\x9e\xf7\xfcG\x01\x80\u007f\xfa\xa7\xff\xef\r\u007f\xf6g\x1fx\u04cd7\xde\x18='|a\xc7\xec\xaboc\x90\xf5z\xd8\xf2\xf27\xe2\xe4W\xbf\r\xc49\x86K\v\x95\xec\xaf\x04\x8az\xa3\x91\x9c\xc0\x99p\xe1\tU\xae}\"\xa5\xc3\xe0\xd8\xc5\x1e\xffO\u01fcFU\xeb&\x8e \xb0\x05\xa0\xac\x83\x8ay\x8ep\xe5\x84gQ\b\xac\xf5\u0b78~\xeb\x14\a\x85XW\xe1\xd4J\x83Y\xf9\n,|\xf9\xdeZ](\x1e\x95\x13\x10\x80XB\xaf\x8fj4R\xd9L+u\x92\xb4VvD\xfd9\xa7\xfcu#{\xb2\xfa\x15N\ng\xa9R\x908\xe5\xbe\xeb\xbcu9\xdcEU\x95^\a\xefz\a\xb5\n\xaf\xa8\xd34\x14o\x93\xa9\x1d\xc0\xd1\u061c\x1a\x159\xd7\u0741\xeb'\x92=\xa8 \x83\x190\x14\xfc`\x94\"\x05)\xd8O\xe1\u0081\xc5A\xc4\u008a\xb7B`\x00\x16\x15u\x16K~?\x04D\xa5\xcc3\u02fb\u0419\xc6\xf6;~\x80\xdb?\xfb\xb7\xd8~\xd7\x0f\xc1\u02a7\xa1\x14\xc3\x158k\xbc7\xbbx \xbf\xf8\xe2\x17\xe2\rox\xc3\x1f\xbd\xe4%\x97\xbe\v\x00\xae\xbb\xees\xfc\x92\x97\\\xea~\x16\xae\xf3\t\x98\xef\xe7\xeb\x9e{\xee\u0131\xc7>'\x02\xd1\xcf]u\xd5o\xfc\xee]w\xdd\u0163\xd1(9\xd52\x98\x19\xd6\x14\u0226f\xb0\xf5\x97\u07c2\xe7\xbd\xee\xdf\xc3\xe9\x0eVvn\ai\r\xb1\xa6\xd2A\xd7xP\n\u0578\xb7\"\x8d\xbe\xe1\xa5\xc4\u03c6\xe0]\x17\xc2'B\xb5\x1e\xbd5@R\x1e\xab)N\xc7P\xacJ=\xd8G\x85\x82\xef|Q\xa5E\xce\x00\x9d\xc7n-%\x13\x8a*T\xe7\x1a\xa45\xa0\x14\x88t\u0253\x97\x0f?\x04B\xb7Z\x1f\xc6\xd9\xf0HE\u0101\x95\xb4c\x17\xef3\r\x9b\x93\xf1&\x1e\xa8\x85zH&dk\u07d2jH\xa7tMlJ\xfa\x12\x15\n% \\\x01r\xcd\xf0\xb2\xe2\xcb+\xcd_Y\xd1S\xfaC\t\x8dRo\xa26\x00\xbaI\xfb\xb4\x1f6\xea\x89MkT\xe1\u9a40\xe1\a\xc6r\xf6T\xcb\xc8*82\u07bf'd\x8d\x86\xb8W\x18\x01\n\xe7S\x83@\xc1\xfe\x98*k`!B\xd6\xed\x82l\x81\a\xbeu\x1d\xee\xba\xe1\x1f\xb1\xf0\xf0O\xa1t\x0e\x10\xf9\xc1 3\x02AP\x14\x85LMO\xd1\xc5\x17\xbf\x10o|\xe3\x15\xef\xbf\u4497\xfc\a\x00\xb8\xf5\u059b\xf5s\x9f\xfb<\xf3\xb3r\xadO\xc0|?_\x11\xc8\x01\xe0\x83\x1f\xfc\xafo\xbe\xf1\xc6\x1b\x8f\u0736m[\xedx\u034aa\n\x83\xee\xf4\x1cN\xbf\xfcmx\xdek\xdf\t\u05dd\xc2\xca\xe2\"T\xa7\x9b\\t\xae2\x92J\bQ\x0f\x8c\x14\x9aR\f\xd2\x1a\u0119\xbf<\x03\x90;k \xc6@|\x84{Y\x95\xc7x\xb8\xb1+>\x98g\x11\xd7K\xdd\nP\x13\xb8\xf3\x1dN_\x89+\rRY\xa0U\x94\x0f\x9d\xe6\xa4\u04a6\u0291\xd6S%\xd4Z5\x8a\xf8\tG)\xfd_\xfc\xe9\xc3k\xc5)\x89\xabk\x80T\x1aRD\ud217\x0e\u05d0\xb4P!@5\x0f\x934\x16+\x86\x84\xc6h\x97&\xa5CM\xfe=\x8d\x91\xe3\xea1D.\x1c\xc9@O\xeb\xd0\xd1^\x165\x03\xa3k\xdc~rPI\x8b\x80\x12\xed=\x90k\x06r\xae\xf8z\xa5\x18\xb0\n \xeb\xa78\x01(\x12\x14\x16\x18\x05-y\x15:\xc5a(\u0281\x88\x91O\xf50X\u0609\xfb\xbe\xfci\xdc\xf7\x8d\xcfa\xb0\xb8\v*\xcb\x01\x10\x8c\x19z;\nq(\x8aBz\xdd.\xbd\xf0\xa2\x8bp\xe5\x95W\xfe\xe1%\x97\xbc\xf8\u075e\x9a\xfc\xc1\xcf\x14\x90O\xc0\xfc\x19\xb4D\xe4\xc8_\xfe\xe5W\xbd\xe2\xe1\x87\x1fFEM\xfa)8\xb1\x16Y\xa7\x8b3.\u007f\x1b\u03ba\xf2=pY\a\v\x8bK`\xad\xa1:]\x00\x80eU\x19O\xa5\xf3\x87AM\xe0\x91@\x81\x02\x9dA\xac|\xe5\x1a~\x9e\xadop\x8a5\xc1\xf9\xceU!\xbd\xa0\xe4v\x83\xe1S\n\xe0\xc9\xc6C\xb5\xe0\xe5\x00\x82\xac\x00f(\xed\x81\\\x02\x1fZ\xa3\x02D\xea`\x1a\x13s\x9a\x00\x19\x833b\xa3/\xda\xc1J\x04?)O\x02\xe0\u069eR\x8b\xe1\xack\xb2\u06c1\x1c\xa8\xf3\xdbq\u04e1\x96\xffo%\xa2\x1b\x8d\u0438\v\xd5F\xec\x13\n\x05i\xa5\x9fV\xe1\xa8O\xdf\xd3\xf8Sjm\\\x8a\x8c?4\xda\x03aN\xb5\x83Ju\xa6\xd1\xe4e\xa3\xa9\xab\xa2\x04? &\x86\x8d\xea*r(,P\u0113Yz\xe2\x01\x00\xe7@\xa4\xa0\xf2\x0e\x96\x1e\xbb\x1fw|\xeecx\xf8\xc6/\xc1\x16#\xb0\u03bd\xe3\xa45\xb0\xa3!\b\x0e\xc6Z\xbbn\xdd:u\xfe\xf9\xe7\xe2\xcdo~\xf3\u007f\xbc\xe0\x82\x8b~\x1f\x00>\xfc\xe1\xff\x87\xb6n=\xd5\xfc\xac]\xe3\x130\u07cf\xd7O~r\x1b\xb6l9\x19\x00\xf0\xf1\x8f\u007f\uc94f>\xfa\xe81\xbbv\xed\xae\xae+\xf2\xb2\fq\x82\x93.|9\xcex\xfd;\xa0z\x1d\xac\xce/\x87\xa9z\x02\xe9\xcc\xdb\xd0f\xc6s\xec\xc1\xb1NP)\t(P#>E\xa0\xb2\xc0\xa52b\xcc\x03.g\f\xe8,T\xe5.\x96\xc6U\xa3TB|@\xb4aM\xa5\xe5TQ0\x15~q\x05\x86\xf0\xee}~s\t\u0380$e\x80p\x1a6\r4|\xc8\xd3L\xd14\xe6>z\u03d0\x94\xdar\x92\xca\u0696\xc6\xd0+\x81?\xaeZ\xbe\xf5*=\x89\x9a\xab=\xb7\xc8l$\xf6\xba2N\xba\x97\xda\xf6Fu^r\xe1L\x15\xa5B\x95\u007fw)gL\xbcV\x9ae\xb34\xec\n\xdah\x95t\x83l&\xf7\xd5\x1fI\xf3w\tQh\x1a7\x81\xb4\x1a\x8f\xcb\x06\x0f{\x0e`\x0e\u0170\x81n1\x8e`\x85\u0293\x0f#\x89\x00\x14\a\xad3\xa8N\x0f;\xee\xfc\x01\xee\xf8\xa7\x0f\xe3\xb1\x1f}\xcfG\xc7\xe9\f\x80O`\xb2f\b\x88\x85)\n\u0670\xe1 u\xdai\xa7\xed~\xfd\xeb_\xff\x9f.\xb8\xe0\xa2\xf7\x03\xc0\xaf\xfd\xdaki\xe3\xc6\xc3\xe4g\xf1z\x9f\x80\xf9~\xbc\xb6l9\x19\"BD$\xb7\xdd\xf6\xe33\a\x83!Dd\b\xa0\x13/0g-\x0e:\xfc(<\xffW\u078c\xe9\x03\xd7aq\xf7\nD$\xf0\x92\xc1\xc7C)\x90RP\xd1\x1e\x15i2=UG\xf8hO\x1a*\xeb\x18\xd2SzgST\x97\x04g\xc2\x04\x14\xa9\xbc0\xd3[n\f\xcd4\xea\xbc4\u0772TT8)\x15\x18\xe2Z@\xb1\x95\x17\xa8\xa8\x8d\x84d\xaem$^)!-\xdcx\x12\xa9W\xbb\x91\xe4n\xa4Q\xa9\u05ea\xda \x17\x045~!\xcd\x1aM<N\xa4\xbe\x89\x107\a\x81\xe2\xc0\x0e\x05\xd0G\u0660\xac\x85k\xc88\xeaR\x83\xc3/\xc1\x9b\u05ae\xc4\xeb\xa5:\xc6\x1d\b\xcawQjD\xba\xa2\x10\xbc\x14\x9a\xbc.\f\xfd\x94\x8aN\xf2\xa0.*\x00\xbaQpdC\x11\xe1\xfb-\xe9+\xaf;=h\xad\xf1\xd8\x0f\xbf\x81\x1f}\xe2/\xb1\xeb\u079f\x80\x94\xf2\xfd\x9e\xf0\xe1p\xae\x80\xb3\x05\xac)p\xfc\xf1\xc7\u0469\xa7\x9e\xfa\xf0\xa5\x97^\xfa\xab\x97^z\u0677\x00\u0aab\xae\xa4\x97\xbe\xf4\xa5\xf2\xe2\x17\xbf\x14\x130\x9f\xac}j\xdd{\xef=DD\"\"\xeb^\xf3\x9a_=\xe2\xb1\xc7\x1e\x03\x11\xa9\u0503\x9c\x00\x1c\u007f\u03858\xe4\u06130\x1ax\xcfVW\x83LI\xf0\x8eB\xb03j\xa0[N\xd7ES\xa60\xd2.\xae\xaa\x16K>9!\x97\xe3,G\u0273\n%tJC\u007f!\x82:\x1e\xc8\x18\xef\\\u06948\xa9\xe7\x12Q\v\u04a4\\uc*\xbe\xf93\xe5\x8f&\xf4\xcd8\xcd\xef\r\x00\xd2\xe0\x8a\xa6\x99\x155\xeaWj\xdb\\J\x95K]iRe`V\u07c8\x13\xb2\xad1O{\x00\xdbT&)mO{|@u\xaf\x8bQ\x97\xccsz\xa7\xe5f\xe2\xc1Z%_\x12(\x13\xc5\xde\xf91>O'\x02#\x04\x03\x05C\x0e\xa2t\xb0V\xf0fl\xf1\xa4E\x8a\x91\xe7=\xd8a\x1f\xf7}\xf5\xb3\xb8\xe3\u068fa\xf7\xc3?\x85\xd2\x1d\u007f\xaa\n[\x89\xb5\x8583 \u034c\x13O>\t\xe7\x9cs\xceg\xde\xf3\x9ew\xfd\x9f\x87\x1ez\xf8\xfd\x00\xf0\xaew\xfd6\x9d}\xf6Yr\u9957\xfd\xcc^\xef\x130\u07cf\xd7\xfd\xf7\xdfO\x00\xa4(\x86'\x0e\x87\u00e3\x17\x17\x17\xc1\xccdm\xf4iv\xc8r\x8d\u00f7\x9c\x8a|z\x16\v\xfd\x81?\xc6\x06\xdbP'M\x1e\xb5\x91\x05\xb9V\xc1\xab\x02\xa8\xc7\b\x1d\x97\xfc\xbcC9\x86/\xa5\xa7t%\xed\xa3\xa6\xa1\x9f\xa4\xbc\xb0\x946\"13\xa2\x04\xb4\xf8\bc\xd2\x10\u0575t\x94\xec\x0e\u049c%O\xe2\u07e9^V\xd6\xce H(\x8b\xb5\baj\x96\xe4q\x8a\xb2\xac\xdd\u00c8y\xed%L\x9e{\x94(rL\u04a31\xfb\x15\xa0\x95%\xa9\xfb\xbd4\x8a\u548f\x96\xd6B\x1a\x82q\x9a\xa5\xc9\xe2\xd4\xda\xce\r\x11P\xaa\x9e\xe1&M\x13$\x8b\xb1\x1a\xd7T\x9d\xb9\x84\xaasX\xf4\xcd1a,_H`\x89!*\x03\x87f5\xfb@(\x88#\xa8L#\u02fbXz\xe2A\xdc\xfd\x85kp\xff7\xfe\t\x83\xa5y\xa8\xac[\xebS8;\x92b\u0627^7\xc7\x19g\x9c\x81\xcb.\xbb\xec#o}\xeb\u06ee\xf8\xe0\a\xff\x02\x17]t\xa1\xfa\xe7\u007f\xbe\xc1\x12\x91\xfc\xac_\xef\x130\u07cf\u05ed\xb7\xde\n\x00\xf8\xeew\xbf3\u06b6\xed\t\x93$\x8c\xfb\x0f\xb9\x00y\xa7\x87\xf5\a\x1f\x8a,Wp\xcb\x16\xb6\xf4\x84\x1e\xbf\xc0\xc7*\xb8\xe4\xf8.i\u015ap\xc4\x12\xc0\x9dR\x1e=\x863KmN\xbd<-\xd4F\xbe\xa9Fs\x8f\xc9\xff\"\xf7\x1a\xdd\fE\xa4\x1a3O\xe5\x13\x89\x96\xbc\xae\x90\xa9N\r\t\x05_\xca\xdc\u028a|\u0301\xb0\xd1\xe0ly\x81\xa8\x85\x18\x1a\x93\"6\x90\xb9m\xf0\xa6\xed\xd0@\x18\xf7\xe8*\xc1vM\xe2\x1a5#.iT\xeac-\x80\x96;\u07db\u0405\x92\xcfN\t2\x04d\u0283\xb9&oM\x1b\u007fVQ\xf5Yt$p\xe2-~c\x18\x92\x84\xe9Md\xfe\xf5c&(\ue049Q\xf4W\xf0\xc8M_\u015d\xd7^\x8dm\xb7\xdf\bg\r\x98\xb3*l\x1a\x04g\x8d\f\a\xabt\xf0\x86\x83p\xca)[\x17.\xbb\xec\x17\xdf\xfe\xa67\xbd\xe5o\x01\xe0O\xfe\u43f2w\xbe\xf3\xb7\v\"\xda/\xae\xf7\t\x98\xef\xc7\ub847\x1e\x02\x00\xdc~\xfb\x1d\xb4\xb8\xb8T\xe5D\xa6\xd8\xe9\x04\x9c\xe5\xc1\a\xc4WK.a4\xdcZ\xc7p\xaa\x83\xebZ\xd7\xc3x\x85G5>\xb5f\xce\x1a\x80\xbef\xe9\x9aR\xda\x01\xa0]\xf2\u007fI<\xa9\xaf\xf6\x1458\xe0D_\xbd'\xc1s\x8b\x11UIaP;\xbe\xd5\x14\x1fi\x1a\x8fT\xd5p\n\xacT/\xe21\xa6\xf2\xdc\xd3\xc6\u0664J\xd2j\xba\xe1\u007f\x024\x95#\xed\xef\u025e\xda\a\xa9\x8c\xb3mci\xda\xd8R\vM\xa3\u0607HDJ\xc5\x03\xbd #\xf2\xa1\x12a\xe0G U\xe0r\x8d\u0593\xe4\xc4%Py\a:?\x00\xa6\xbf\x84\x9dw\u078c{\xbe\xf4I<\xf0\x8d\xcfbe\xc7c`\x95y\u014a\xf3\u039d\x9e\xa6rR\x8c\x86t\xc4\xe1\x9bp\xce9g\xcf_y\xe5\x1b_~\xdey\x17|\r\x00~\xef\xf7~\x976o\xde\\\xecO\xd7\xfb\x04\xcc\xf7\xe3\xd5\xef\x0f\x00\x00\xcb\xcb+0\u01b46\x02\xfb\xab\xcb\u8bec\x04\x06\x84a}\xf9\\S-H;\x05\\\a\xa5\xe4\xfbM\u0395\u05a8\xea\xeb\xf5jThP\"[\xab\x98XnX\x8dx'\x01\x81\xb3\u0271\xdf5C\u0712Z85\x9cj\xf3\v\xa7\x96J;\xf9y\xda\x03\r\x9d\xe2\x0e\xed\x85\xf2Hi\x8a6\x00LM\xb6\xa8\xc1\xd9K\xe3\xf5\x96\xc6\f}\xbbU@\x02\xcc2\x0e\xf6m?\x9b\x9a;\x8eY\xd36\xfe\xa4\x96\u075b \xa1\n\xf7i@\xf1\xb8@\x94\xf8\xd3\v` q\x0e\xcd[\n\xa3jZ\xa7Q\x81\x00!\x9f\x9e\x83\xd8\x02\xbb\xef\xbd\r\xf7~\xed3\xb8\xe7\x8b\xd7`\xe1\xe1{\x82\x0f\x0e{7c[\xf8\x01\xb8\x80\xff\xa6\x18\xd2a\x87m\xc4%\x97\\\xfc\xf0o\xfe\xe6Uo8\xe5\x94S\xbf\x06\x00g\x9f}6\x1dq\xc4\x11\xf2\xcaW\xfe2&`>Y?\x13kzz\x1a\x00\xb0n\xdd\x01\xd0ZS\x1b\x98[\x01\x1e\xbb\xfb6<\xfb\xdc_\x84\xca2\xc0\x8cJ\xa0qu\xf4+\x8bT'-<\xed\x1eh\x81\xf1\n\x93BVg2Q\x1a\xf9\xe1d#`\xd4\xef75dt\xf0\xde\x1c\xa4\xe3\b~x\xbc\x8d*\xb8\x1cj\xa2\x06O,\xb4f\xb5\xdd\x046\xa0\xddKdOTC:H\xda\xe4\xe2\xa9\xed\xa4\u04e0^\xa2\x94\x9dZ\xee\xdf5*zY\u3157f%\x1e\x01\xbf\xa5\xe1K\r\u03bby;{z\xee\x94\xd0m\x8a\xbc\u02e1\xa2\x94/\xf7wl\xc5W\xe2&\x98_9T\x8a\x14'\xa8\xbd\xd7\x11\u01f9\xd3\x05\xeb\f\xbb\xef\xbf\x13\x0f~\xe3\x9f\xf0\xd3/}\x02;\xef\xb9\r\xce\x14\t3\xe7\x1b\xf7\xc4\u02a3\xb83B\x04\u06b2\xe5D\x9c~\xfa\xe9\x9f\xfd\u0407>\xf8V\xa2\xfc!\x00x\xc5+^N\xaf}\xed\xe5\xf2\xf2\x97\xff\xd2~w\xbdO\xc0|?^g\x9du&>\xf0\x01\xe0E/\xba\xa4\xf3\x0f\xff\xf0?r\x9f\xe03~Y\xfe\xf0\xf3\xff\x88\xe7\\\xfcj\xac?~\v\x86\xa3\x11\x8ci\ua269\x9e\u053e\x06pK\xa3Z\x17\xa9Q\xcc\xe9\xa19\x0e\x1d6\xc2\x1a*q\x9f\xa2\xa4\"\x0e\xa0j\xa5Q\x95\xa2r\x1etX\xeb\u030f\xb1\xc6\xdfZ\x15\xf6\xbfF\f\xb2'*\xa9\xf5\xb4\xd22h\x93\x16\xff\xae\x85\xedi\xbb\u03f1\xf9$Z\x9b\xc7\x1es\x14h>\xb6\xa4\x1a\x8eT\x9a\uc067\xdf\x13\x8d\x1e\xe9\x16\u037e\x12\xd7\fd\u025b\xee\xc4\x0f\xf9\xd8p\x92\xaa;$W\x03K.\x8c\xe9#PcJi\xa8\xbc\x8b\xfe\xeem\xb8\xef\xeb\x9f\xc5=_\xbc\x06\xdbn\xfb\x1e\u0330\xdf\xf2\xfa\x87\xe9_\x888k\xe9\xc0\x03\xd7\xd3\xc9'\x9f\x8c\xf3\xcf?\xef\xaf\xdf\xfd\xee\xf7\xbe\x93\x88\x16\x01\xe0\x8f\xff\xf8\xfdt\xee\xb9\xe7\xca\x19g\x9c\xb5_^\xef4\x81\xbc\xfdw\xddz\xeb\x0f\xe9\xb9\xcf=EDD\xbf\xf0\x85\x17}\xfe\xc6\x1b\xbf\u007f\xd1\xc2\xc2B\x01 k\xfe\xec9\xaf\xfb?\xf0\xa2w}\x00\xab\x05\xb0k~\x11&\t@p\xad\xf9\xedT\x06\u8d8dl73}\u04df+\x81<\x06\u0460R\u03a4\xe0\xe5\x12\x00\x8c`\xdeVIK\x8b\xeap\f@\xf1\xaf\xb4_\xc5\xda \xb9\xa7\n\xbc\xf5\xff\xa5\xae\xf2\xc0\x1a\x8f3\xd5~G\xa0N)\x13Yc\xd3L)\x1d\xfaW<^iy({\xba\x8dh\xa6\x16\u04d5\xe2\xfb\xc0I\u04d9#\x80\aKZ\x1d\xee\xa0pQ^X\xf5`\u01ac\u0723\xc6'\xdc9\xb3\x02k\r\xcer\x10\x80\xc7~\xf0u\xfc\xe4\xd3\u007f\x8d\a\xbf\xfd\x05\f\x97\xe6[@<L\xff\x86\xb8\xb8<\xd7x\u05b3\x0e\xc1\xf3\x9ew\xca]\xafz\xd5+\xff\xecW\u007f\xf5\xf2\xbf\x01\x80\x0f\u007f\xf8o\xf8\x8a+\xaet\xfb\xfb\xf5\xce\x13\xc8\xdb\u007fW\x00r\"\"\xb3i\u04e6o\xcf\xce\xce\x02Q\xf1\xd6X\xffr\xcd_\xe0\xe6O\u007f\x14\x9c\x01y\xb7\x97\\\xe4\xf5\x9c\xb3(\t\x94\xa0Q\xe7\x16\x8a\xa2\xa6hi)\x96#?\xad\xaa\x10\xf6\x9a\"\x06 \x98p$w\x12+G\x1a?U\x10\xad\xcd\xddJ{UL{\xa1\x81\xfeU\u07e3J\xf3\x9d\x98 \x8e}I\xe3g\xc6B{\x9a\xaf\x1daL\u07ce\x96\xd7xlR\u007f/\x95\xf4\xda\xd56\x95\x1b\xa6j\xde>\xb5\xbcv\x84\x92\v\xcf\u060f\xe0w\x15\xa1\u02c4\x9c\xa3\xb4P0r\xc0\xc8\tFNj@.\xd2\xdc\xc0\xc2ILk\xe4\xbd\x19\xe8\xde\x14\x88\b\x8b\x0f\u078d\x9b>\xfc>|\xed}W\xe1\xee/^\xe3\x81|,\xfeNy\x97O\u71cb\x0e<p\x1dN;\xed4\xbc\xfa\u056f\xba\xfe\x03\x1f\xf8\xc0\v#\x90_}\xf5G\xf9\x8a+\xaet\u007f\xfe\xe7\u007f\xba\xdf_\xef\x13\x9ae?^_\xff\xfaW\x11\xf5\xb3o|\xe3\x15\u07f9\xe5\x96[\x17\x1fy\u4479\xb6\x13\xbd)\n\\\xff\xa7\xef\x84\xd1]\x1cs\xf1\xaf@\x15\x05`m\xb8\b+\rw\x8a\x95\xc1\x03z\xbc\xb1\xd6\xc2\x17\x8c\xe5\x05Q\x18\xb2I6\b\x97T\xf6q\xb3\x18\xcf\xf8LO\x002V\x1d\xcbZ\x1c\xc3^\xa8\x92\xbd\xfdl:\xc9\u0674\u007fM+\u0174\xc1\xd7\x06\xde\xd8\x03\x95\xd2<\xcdP\v\xfdB{\xa4=R\u05d3::Sku^\xd1]%\x9d\x95\x9a`\x05N;>\x1e\x9dLmj\xf6\xd6\x03L\xf5\n\xdc\x04N\xdco\x9cT\xdbW\x89\xc2\xe8=|'Ck\r\u055d\x02 \x18,\xec\u0093w\u0742\a\xbe\xfd\x05<v\xf3\u05f1\ubf9f\xc0\f\xfa\xcd]\xbe\xf2\xdcwN\xc4\x14t\xd0A\a\xe1\xb0\xc36\xe2\xc4\x13O\xfc\xf1\x05\x17\x9c\xff\x97W^y\xd5\xdfGZ\xe5\xdak?\xcb/}\xe9/\xba\xcf|\xe6\xd3x\xd9\xcb^\xb1\xdf_\xef\x13\x9a\xe5\x19\xb4~\xe37\xde\xf8\xd9\xcf|\u6cd7n\u07fe}\xbc\xd4\f\x17\xcb\xdc\u01a3p\xc9\xef}\x14\x1bO\xf97\x18\xf4W\x82\xd6\xd7\a\x00\xa4:\xb8V}s\vHq\nz\x01\x8f\xe3\xf0\b\xc1{q\xb8\xd0\xf8L\xaf[\xb7\x87\x90\x9eVj\xa7\r\x19[\xa8\x96\xff\x15:\x05-\x95jM~\xb8\x87\x8d\x80\xf6r\u007f\xf2\xff\xe3B\xac\x816%\x96\a\xb2\x97'\xd6\u0417\xb7\x9d\n\u048d$\xddDJks\xf2R\xc3\x0e\xd7\xe7W\xad\bF\xb6\xeag\xc4|\xcex/5\x9aM\x00\u07f7ad\xd3\xd3`\x01V\x9f|\x02\x8f\xfc\xe0k\xf8\xe9\x97>\x89\xc7\u007f\xf4]\xac>\xf9\xc4\x1a\xaf?\x05\x1fr\xb2\xb6\x18\xb9u\xeb\xd7g\xc7<\xfb\xd9\u063c\xf9\u87dey\xe6\xf3\xff\xdb\xdb\xdf\xfe[\u007fCD%\xa1\xfew\u007fw5]~\xf9\xeb\xe4\x99t}O\xc0|?_\x9f\xfc\xe4?\"J\xb0\xbe\xfa\xd5/\x9fu\xd5Uo\xfa\xd4\xddw\u07fdqO\xbf\xb3\xf5\x97\u07ccs\xff\xaf\xff\x86a\xbf\x0f\xe3\xbc\xe6\xc0%~\xdee\xf5\x89\xb4\x8a[;\x977\xf5\aA\x00s\xae\x02\xdek\r\xb8\xe8\u0451zm\xd5\xf4\xdb4\x0e\xf0.i\u6943N\xa9\x94\xb2)\xb3k\x1bY\x97\x96*|\xefT\xc5\u068a\x17\x91q*\xa6mx\x96\x1a\xb4Jk\xc61\xa1\xe6E\xd2\xd4\xfbKc6\xaa\x91i\f\x91\x8a\u058a?\xdf\u059c\xe6@\xa3\xa8D\xaa\x19u\xe21\xc4\u00c6\xc1\x1e\x13\xbc\xe6\u0460Q$\xd26Toz\x131\xf2\xde\f\xc4\x19,<r\x1f\x1e\xbf\xf5;\xb8\xff\x1b\xd7\xe2\xe1\x1b\xbf\x8c\xe1\xf2\xfc\x9a N\xac\xbc\u07fe\xb5\xae\xd3\xe9\xf0q\xc7>\x1b'\x9cp\xc2\xe3\x17\\p\xde'\xae\xbc\xf27\xff\x84\x88\x1eM\u007f\xe7\xc6\x1b\xbf\x87\xe7?\xff\xccg\u0735>\x01\xf3g\xc0\xfa\xad\xdfz\ab(\xed\u55ff\xe6=\xd7_\xff\u03ff\xbfc\u01ce1\xfca\xe5#\xe3\x9e\xf5\x9c\xad\xb8\xf4?}\n\a\x1c\xbd\x19+\xbb\x97|\xb2O\xe9\xa1RM\xbb\xa4\x81p\xcdj=\x8eo\xa3\x99l\x8fj\x80\xa4\xe4\xe0\x13\x80\xb1\rpX\x8b\n\xdf\xe3\x9c\u0358\u007f\xeb\xdaMPZk\x8eH\xc6\xf9c\xd9\x03\xc5Q\xbb\xfd\xda\xff'\u05b9\te\xd5\u49db\xf7W5\x1b\x83\xdb\xe0\x1a\x9b\x95\xb4\x9cL\u0186_\xd1p/\xa0\xea=\xe2F\u056e\x89\x02\x95\xe2}T\xac\xab\xde,\x17\x14)#G\xb0.\xd17%Fk\xe9f\xcb\xf1\x15P\ny\xb7\v3\x1aa\xe7Oo\xc3#?\xf8:\x1e\xf8\xe6ux\xe2\xf6\x9bP\xac.\xb5\x03Sp\xe3,CK 8\xe4Y\a\xe3\xb8\xe3\x8e\xdbu\xce9g\xff\x8f\xdf\xff\xfd\xf7\xfd\x15\x11\u0756\xfe\u03bd\xf7\u0783g?\xfb\xd8g\xecu>\x01\xf3g\xc0\xfa\xc8G\xfe_\xfc\xfa\xaf\xbf1\x80\x89\x1cp\uee7f\xf0\xc9o~\xf3\x9b\x17:\xd7\xeeq\xca\xcc8\xe1\u016f\xc1\x19W\xbe\x17sG\x1e\x8b\xe1\xb0@Q\x14\x10g\xe1b-^*Y\xa8\x01\xec\xd5m\xa9\x84\x03\x88\xba\xe3X\u5e44#\x8f|y\xe4mC&\xd1X\xc5\f\xaa\uedd5{nC\xf9\xa6\xcdk\v\xa72\xc6}\xaf\xc5\xe7\xa0e\x02\xb26\xfe)\xf5\r\xa3\xb4}\xa5\xca\u05e6e\a\x8aI;M@\x8f\x9e.\xdeU\xb0\xb1\xb1H;\xdf.\xc9k]\xbe\xee\u912bT NR\u077f\"Ow\xe9\xa0\x15\x8f\x16\u0205\x93\x92B\x89\u0787\"\xd28uH\xa9\x18W\xd1\x06\x81\t\xac2\xe8N\x0ek,\x1e\xbf\xed{\xb8\xef\x9b\xd7\xe1\xc1\xef\u0740\x9d\xf7\xfc\b\xb6\x18\xb5\xa2Q\x19p\x12\xde\xebLkt{\x1d\x9cx\xc2\t\u063a\xf5\xb9?\xb8\xe0\xfc\xf3\xdf\xf1\xcaW\xbd\xfa\xeb\xe9\xaf\xfd\xdd\xdf]\x8d\xcb/\u007f\xdd3\xfe:\x9f\x80\xf93d\xfd\xf8\u01f7\u2913\x9e\v\x00\xf8\xda\u05fe\xb2\xf5\xfd\xef\u007f\xff\u05ef\xbf\xfe\x86\xb9Vr84\x167m=\a[^q%\x8e8\xfbE\xe8\x1e\xf8,\x18\xe3P\f\ap\u0468\xab\x85w\x8d\x00V\xafM\xa5\xf4\xb3\x8eG\xf8\xf8;&\xe1\xc7K\x87\xbb\xa4t\x96\x96t\x84\xb1\xea\xbcm\x80\xa6\r\xe9Z\x88\xf65\xbdRP\xc9\xef\x9a\x00\xdb\x1c\xe0\u065b>[jS\xad\xd5\xf3\xad\x03yx\xf6m\x8a\x97\xb0\xf9U:q\n\x95\xaf\xff\xa6kP[Q:\x18+\xed\u0606\xb4e\xe5N5\xc0\x8f\xf7i\xa4\xd2RF\x8f\x1eA4.KN\x19\xa8d\x89\x11\xc0\xe3\xaeKD\xe0,\x83\xce;p\xd6b\xdb\x1d?\xc4\xdd_\xfe\x14\xee\xff\xe6ux\xf2\xbe\xdb\xd1\xf4\a\x8a\x0f\xa0\x04p?\xf5C\x9dN\x8e\x99\xe9\x19l\u06b4\t\xcf{\xde)\v'\x9ex\u009f\xfc\xd6o\xbd\xf3CD\xb4\v\x00>\xfe\xf1\x8f\xf1k_\xfbknreO\xc0\xfc\x19\xb9n\xba\xe9\xfbt\xdai\xa7\v\x00|\xe2\x13\xff\xf0\x92\xff\xfc\x9f\xff\xeb5\xdf\xfe\xf6wf\xdb>\x16\x14F\xfa\xa7\xd6o\xc0\xc6\u7783\x13^\xf6F\x1cv\xea\xb9PS3\xb0\xc6\xc1\fVk\x8d\xae\x8a\xb2\x90\x06\x8f^\x81I\x12(_\x1bXI\xc1\xbcf\xb0DU\xd5O\r\xad\xf2ZMKip\xed\xcd\x1fn\xca'E\x9a\xbe1\x82*\xbb\xa1\x82cn\xb1*H=l\u02a1\x9e\xf0}\xa6\xf1\xfec\x94]J\xa8d\u02f0\x86\xe4a\xa6\x1b\x9dC}X\b\t\xe0\u01e0\xb44\x06/Fw\xc6\u7903\xc3c\xf4\b72\xde\xe5\xb0R\xdf\\R\x8a\xab&-M^\u0426\xb5B\xa9Ya\x8dNo\n\"\x82'\xef\xbb\x1dw|\xe1\xbf\xe3\xbeo~\x1e\xbb\x1f\xb8\x13f4h1\x81!\x1f$\xce\f\x11'\xe2\x84\xe6\xe6f1;;\x83C\x0e9\x14g\x9f}\xe6\u03a3\x8f>\xfa/^x\xf1%\u007f{\u0496\x93\x1e\x04\x80W\xbe\xf2\x15\xea\x13\x9f\xf8\x94\xdd_\u0331&`>Y\xff\xcb\xeb\xaf\xfe\xea/\xf8Moz\x8b\x03\x80\xab\xaf\xfe\u0605\x1f\xf9\xc8G\xae\xf9\xcew\xbe\xbba4\x1a\x19\xa5\x94\xb2\u05b5\xea3f\x0f9\x1c\x87\x9dz.\x8e\xb9\xe8Wp\xe8\xd6s\xa0{3(\x06\xabp\x81\x96i~\xa4\u04a37S\x134+d\xa5\x06\xa7]w\xf1\xab6\x95\xa6\xba\xa5\xc9\x17\xd7\x14\x19M\xab\xdb=\xad\x06h1\x118\xdcge\x0e\x15\x81\x8f\xcaA\xa7X\xc4:Y;u'\x1d\xb2\xa9\x01:\u0579\xf9\x96t\xb9\xbd6C}\x90\x03\x95\x9bb\xc6\x04\x15\xc0\x19\xa5\xc5,\xd5\f\u04cc\xabd\xa4\xae\xda&\x91\xe4\x8d\xd4\r\xba\xa4\x02o$\x9b\x05\xa5\xd2$q0\u0387C\xe4\xdd\x0e\x96\x1f\u007f\bw\xdf\xf0I\xfc\xf8\u068fc\xe7Oo\x83\xb4T\xe2Q\x99\xa2\x98\xc59g\x8c)p\xd0A\af\x1b6\x1c\x8c\x83\x0f>x\xfb\x19g<\xff\xd6\xd3N;\xf5\u00ff\xf2+\xbfz\x1d\x11-\xc7\u07fb\xfa\xea\x8f\xd2\xeb^\xf7z\x99\\\xc5\x130\x9f\xac\xb0>\xf4\xa1\xbf\xe4\xdf\xfc\xcd7;\x00\xf8\xf4\xa7?y\xc1\xd5W_\xfd\x0f\xdf\xfa\u05b77\xec\xdc\xf9$\x98\u0649\b\xa7>.13\x93\x94\xc6\xf4\xc1\x1b\xb1\xf9\u0717\xe1\x84\u02ee\xc0\xfag\x9f\xfc?\u06fb\xd2\xe0:\xaa3{\uef7d\xbcE\xfb\xd3\xf2$\u02ca6\x8c\x1d/x\x19\xc6\xc66v\x8c\x1c\x98\x94\xcb\xd4\f\x1e\x02\xe4G25E\xe18\x19\xa8\u0250T`H\x82a\x96\x84Jf\x8a\u0270XP@\xa8\n!\xc9\x04\xc8x\x02\x89\a\x13\x1b\xe3\x15\x19[\xc6c\xcb\xd6bY\x8b%\xdbOz\u04b3\x96\xb7t\xf7\xbd\xf3\xa3\xbb_w?=9T*\x10\x92\xf4\xf7G\xb6\xf4\xd4oQ\xdfs\xbf{\xbe\xf3\x9d\x0f\x86a@OM[G\xe5\x1c\x8b\u05dc\xbc\xd3[\xfa\x83\x8bs\xc7\fj\x06\xee6}\v\xa0DNES\xe4aT\x80Y\\\r?\xc0\x02p\x80\xdb\x04t\x99\xda\xca\x0e\x93;\xd6sN\x10N\x929s\xd7\x103\xb2~GuCs\x00\xde\xfe\xbf\xbd9\xe4\xb6\xf0\xbb\xaf\u0248\xa3\xef'\xd9l\xdetx\xb4\u01aaZ\x80k\xda\xc9\xdat\x89\xbb{\x96{\x8a\xc1b\xc6)\x04\x96~\xdcV\xbc\x10\x97g\x8b\xd3[`\x1e\x9f$E\x85\x12\n \x99\xb8\x82\xc1\xf7\xf6\xa1\xfd\xa7O\xa1\xef\u0777\xf2r\xe2\x84R\xd3\b\x8b1\b\xce\rn\x18,\x12)CCC\x03\xaa\xab\xab/,\\\xb8\xf0\xf5\x96\x96\x96\xc7ZZ6\x9es\xff\xde7\xbf\xf9\x10Y\xb6l\x99\xb8\xed\xb6\xbf\xf6\x17\xaf\x0f\xe6~\xe4\u01b7\xbe\xf5M\xf2\xe8\xa3\xff$\x00\xa0\xad\xed\xf0\x86\x1d;\x9e\xf9\xcf\xfd\xfb\x0f,<w\xee\x1c\f\xc3\xcc\xef8w(Ij\xb5M\v\xc1A)Ct\xc9\r\xf8\xe4_\u074dO\u0738\x19\x81\x92\x12h)\x1dF&\x85\\\x8d_>s/\x01\xaf\u0305\xe4\xe4\xa2\xc45\x9d&\x97\x1b\xcf*'0\xcb88\u0324C\x90\x876\u0235\x17\x80\u02e6\xd56\xf9\xa2\xc4\xecj\x14 \x969\x94p\x81\"q\x15\x05\x85\xd7\u070b\xe4Q\x9c\xe4\xd0\x14\x94\x98\xf2=B\xecY\x1e\xf6\xa7 r\xe4\x92\xe6\xc6B@\xac\xe767\x99,\x03\xef\xca\xfeM\x1bY1\xc3\u01c6\xbbOA\xc8\xd5\xec\v\xab\x17\x80xL\xbe$\xd7H;\xee\xca\u0429e\x8aEe\x19JH\x8561\x81\xe1\xe3\xef\xa0\xe3\xcdW\xd0\xf5\xf6N$\xc7F\xf2\x838\xa1`\x8cZ\x9b\tGEy\x05\xe6\u039d\x8b\x9a\x9a\xe8\xb1\r\x1b6\xec\u07f6\ud2ed\x8a\x12<\xed\xfe\xbd\x05\v\xaeEkk+\u05ad\xfb\x94\xbf`}0\xf7\xe3j\xf1\xe0\x83_'\xdf\xfe\xf6cV3\xa5\xa8\u07fe\xfd\xe1\xc7\xf6\xed\xdb\xf7\u064e\x8e3\x88\xc7\u3982\u015d\xa1Sj6n\x00\xe0Z\x06\xc1\xb2J\xd4\u07f8\tsW\u0742\xaa\xeb\u05a00Z\vC3`his\xaa}\x0e\xaa\x12\xe1:\xda\xc3\xd5\xe5\txG\xb3[w\xa6c\x80\ubc3c\xd9\xd6~kX\xb4\x10\xb3\xdf\xd0\xf9\x9a\x98\xf2\xb5\xc5S7\x9dB\\\xad\xed\"\xbf.<WS\x9d\xcb\xc7\xcf\xec\xd1\x17N\x03\x15q\x0f7\x16\x96\xae\x9bf\xb5\xf7\x0e\x95\"r\x9as\x9cL\x99\xbb^\x89)\x17$Y\xe9\xa3]o0\\j#\x87\xa3\x9f\xf99\x10\x17}\"\x11\xa7\x11\x88YER\xcd\xdap\x19e`\x8c\x81\xc92\x88\x04L\x8f\xc6\xd1wd7z\xf6\xfe7z\x0f\xfc\n\xc9D|\x16\x10g \x14`\xcc\x04\xf4\xca\xca\n,\\\xb8\x10\x8d\x8d\r\a\x1b\x1b\x9b^\xfa\xcaW\xfe\xe1g\x84\x10O\x17\xdb\xf7\xbe\xf7]|\xf5\xab_\xf3\x17\xa8\x0f\xe6~|\xd0x\xe2\x89\uf8e6\xa6\x06\xf6\xf1U\b\xa1\xb4\xb6\xee\xd8x\xf0\xe0\xc1\xad\xdd\xdd\u0777\xf6\xf4\xf4`ll\f\x99\x8c\xe6\x90\x1d\xd61\x99\x10\nn\x18\x10\x82#TZ\x89\xaa%\xabP\xbfv\x13\xeaV\u007f\x06\xe1\xca90t\x1d\x86\x96\xb1\x94/\x0e\x0fmg\xb2\x1e\x05\x8a\x98Y,txh/Q\x92\xab84D~je6\xfa%\x17\xe0i\xce\x00\a\x96\xd3\xfa\xee\x06k7\xbfo\xcb\xf4`78!\xff\t\xc0\xcb\x15;\x198\xb1\u032b\x18\x00j\x8d\x88c\u01247l\x8bX\xf7k3r(\x9e\xecf\x92;\xddIx\u007f\x0e\xe4\x11\xf3\xb8d\x89\xcc\xea\xec\x94\b\xc9\xcaE\t1\xff\xc6L\x92!@\xc1\x98\xf9\x1a3\x93\x93\xb8|\xf6\x04\xfa\xda~\x8d\xc1\xe3\xfb1\xf4\xfeAd\xa6&g\xbcIJ((%\xd6\xf0n\x01\xc6\x18\xa2\xd1*\u031f?\x1fUU\x95\xad\x1b6\xdc\xf4\xcb/|\xe1o\x8e\x10B.\x02@M\xcd\x1c\xf2\xdcs\u03c8\x13'N\xa0\xa9\xa9\t\xb7\xdf~\x87\xbf8}0\xf7\xe3\xb7\t!\x04\x1eyd;\u06fe\xfd\x11\xc3\x06\xf5g\x9fm\xdd\xd2\xd9\xd9\xf5\x8d\x83\a\x0f~\xb2\xa3\xa3\x03cc\xe3\x0e\xa0[]y\x84I\x96\x1e\u06000\f\x04\x8a\xcbQ\xb5\xf0\xcf\xd0t\xd3\x16\u052d\u0744\x82\xf2J\xe8\x9a\x01-\x9d\x02\x11\xe6du\xe1\xd2,{\x00\xd0C18\x15S\xe2\xa1f\xe0\x9a\xa4\xe9\xe2\xcdg\u0443_ej\x9a\xa7\x89\xc6]\x90\xa49\x13\x1e\xf290\xba\x1bw\x04f\x1f\xe2\x90\u07e4\xcb\xedDh\xfa\u04d8\u067a\xc8z\x9d\x88<\x83\x96!r\xadj\x9d\x13\x8e]\xa0%\xae\xcf\x17\x989\xc9\xc8\xdd4\xc4\\\x9b\vc\x14\x8c\u0240$;:|\xc1a\xa4\xa61y\xf9\x02FzN\xe1\xe2\u98f8\xd4\xf1\x1e\xe2\xe7;\x91\x18:\xef\u027e\x9d\x8d\xd1j\xf2!\x14\xdc\xd0\x05c\x8cD\"\x11,X0\x1f\xcd\xcd\xcd\u03efX\xb1\xe2\u026d[\xbfx\xcc~\xfc\xe2\xc5K\xd8\xf3\xcf?+\xae\xbf~\xa5/1\xf4\xc1\u070f\xdfetu\x9d\xa1\x0f?\xfc\b\xfb\u044f^\xd6,P/|\xee\xb9g?\xbfw\xef\xde\a\xde}\xb7\xad\xb6\xbb\xbb\an\x1e\xdd\xee\u04a3\x8c\x81P\t\x9c\xeb\x10\x86\x8e`I\x05j\x96\xaf\xc3\xe2-\xf7 z\xddZHA\x15\\\x17\xa6\xfa\x85s\xcblIx@z&\x14z=@\xdc\xee\x8d\"\xcb\xc5\u007f\x10W\x15x\xb8x\xb7$\u041d1\x13\xfb\xf4\x007_\rOJ\xce-\x030\x8f\xdcp\x96\x85$f1~'y\xb6\x1a\x87~q\xb6,\xe2\xfa\xbe\xbbx)\xf2\x8c\x87\x13\xaeS\xc6\f\v\x84\xec{4\x87hg\a+\xcb2$E\x81\xa4\x98\x04|r:\x03#=\x8d\xd4\xf8(\xc6\xfa\xbbp\xf9l;\x86\xde?\x84\xf1\x81nL\x8e\\B21\xea=\xd1\xc8\n`\xdd\v\xc4*~\x9b\x1b\x91\x10\x86\xa1\x93\xf2\xf2\b\xaa\xaa\xa2X\xb2\xe4\xba\xf77o\xde\xf4\xe4\x1dw\xdc\xf5\x8c-'\xbc\uffbfS\xee\xbc\xf3\x0e\xe3\x86\x1b\xd6\xfa\x12C\x1f\xcc\xfd\xf80\xe3\xc0\x81w\u021a57\nW\xe6\x1e~\xec\xb1\xef<\xbeg\u03de\u03df8qB\xb9x\xf1\x12\x00h\x84\x10\xd9\x01\v3+\x03Hv\nLQM=\xea\xd7nBs\xcb\x16\x945.\x84ZRf\x0e+H&\xad\xe6\x11\xc7\xff\xd6\xe3o\r/\xf0e\x8b\x9d9z\xf1\xfc\xae0y\xb2qO\xf1q\xc6EL\xbe\x1a\x8e\xb6\xdb),\nO\xd3\x0e\x17\xf0(\xc3E\xbe4|Vt\x9f\xc9\x059\xde5N\x11\xd8,\u019a[\x8e\x9d\xf3r\x97\xcc3\xf7rY\xeb\x81\xec\fW/\xc9D ,\x9bXU\x91A\xe4\x00d\x85\x82gt\xa4'\xc60=2\x8c\xf1\xc1s\x18>\xfb>bg\x8ea\xe4\\\a\x92c1\xa4\xa7&\xc0\r}\xc6\xe7I)\u02f6\u061b_\xad\xb1mB\x18\xe9t\x9a\x97\x95\x95\xc9s\xe6\xd4`\xfe\xfc\xf9\xf1\x95+\xff\xfc\xdf\xee\xbf\xffk\xdf%\x84d\xc7\x02}\xe7;\xffJ\x1ex\xe0\x1f}y\xa1\x0f\xe6~|T\xf1\xea\xab?\xc3\xee\u077b\xc9SO\xed\xc8.\xbcw\xde\u0673\xf1\xe5\x97\u007f\xf2\x8d\xe3\xc7O\xac\xef\xec\xec\xc4\xe8\u8a1d\x9cR\x0f\x8d`\x1d\xbd\x05\u7812\x84py\r\xe6,[\x83\xba\x1bnA\xe5\u0095(\xaem\x02Q$\xe8i\u00f4\b\xe0\x1c\x82\xebV\xd6\xeed\xa6\xe6\x14!W\xcb\xd1U-\x02\xf3\xd3(\xf4K\xd5\x00\x00\v\xecIDAT\"\x1e\x83+K\x8d\xe1\xc9\xfc\x89\xfd\xe2g\aL\x9b\xe20r&M\xb8\x9b\x99f{\x1dW\x1b\xd6\xecH\xfeL\x12;+[\xb4~n\xb8\xb2\xf3|\x1c8r\b!\x02\xaf2\x881\x06EU!)\x12\x88\x00\u04898&c\x171q\xb1\x0f\xa3\xbd\x1d\x88u\x9d\xc4\u04296\\\x19\ua0de\x9a\xc2\f{\a\xf7\xe9\x8b8~\xf2\x0emC@\b\x11\x9a\x96\xe1\xa1P\x98\u035bw\r\xea\xea\xea\x06\x97-[\xba\xf3\xcb_\xfe\u04bfG\"\x95=\xeekuvv`\u07bc\x05\xfe\xe2\xf2\xc1\u070f\xdfG\xb4\xb6>\x8d\xad[\xb7\xb9\x8e\xf9B~\xe9\xa5\x1f\u07bdk\u05ee;\xfb\xfb\a\u059d<y\x12\xf1x<\xef\x8deN\x81qRc\xb5\xa0\bU\v\xaf\xc7\u0715-\xa8\xb8v\x05\xd4\xd2J\xc8\xc1\x10\x94P!\xd4\xc2\x12PU13`\x0e@p\b\xce\xc1\r\x03\xdc\xe0\x10\xe0\x10\xdcQ\xc3\x10\xf7\xf4#\x97b&+\x13$n\xda\xc1\u017c\x13g$\x9d\xb3\v\x89lA\x93\xc2\xfd\x1cv\xb6nj\xb6s\xfdP\xdc\xe1\xf6\x96\x99\x15q\xb3\x19\xb4w\xba\x12\x85\xd7\xfc*\x97\x02\x82epe\x16\x17-\x1fx\xe1\x14\x88\x89\xdd\xe9I\x19\x98$\x81\xc9\x12\b\x03\xb4\xc9$\xa6.\xf5c|\xf0\x1c\u2f671\xda{\x06\xa3\xe7N#~\xfe,\x92\x89\xf8\fi\xa7\xbd\x11\x13J\u034d\x81x\x89\xae\xdc\xc7\xc2\xf2$onnBCC\xe3\xe8\x92%\x8b\u007f\xfc\xd0C\x0f=\xa5\xaa^\x89\xe1\xe0`?jk\xeb\xfc\xc5\u40f9\x1f\xbf\xefx\xeb\xad\xffEK\xcb\xcd\xde\xccS\x88\xf0\v/<\xff\u0653'On\xeb\xec\xec\xbc\xfe\xddw\xdb0\x1a\x8fg}[\xbc\xa0nfw \x04\x82\xeb\xa0LFaU-\u0095\xb5P\nK\x10(*C\xa8<\x8aP$\x8a`Y\x15\xe4\x82b(\x05\xc5\b\x14\x96B-\x8e@-,\x01\x91\x95,gM\x00\x10av\xc8\b\xc1\u0351\xee\x9c[|\xbe\x93u\xe7v^\x12\xe25\xa1r+b\xf2\xa9i\xa8\x95\x1d\xdb*\x0f\x9bj!y\x8a\x94\xf9T3\xe6>F\xb2lR.yd\x83e\xd6\xf8\xca*\x80:\x1b\x94p\x9aw(1\x1b\xf8)\x01\xb3(-JMb\x852\n\x89\x02Z:\x83\x89K\x03&h\xf7\x9e\xc1X\xdfY\x8c\xf5wc|\xa0\v\x13\x17\a\xbd\xe3\xe0(3yn\xeb\x14E\x89W\xc6#\xf2\fN\x15B\x80R\x02YV \xcb\x12\x1a\x1b\x1b\xd1\xdc\xdc<\x19\x8dV\xb5\xdeu\u05dd?Y\xb5jM\x9b\xfb\xf1\xdf\xff\xfe\xe3\xb8\uffbf\xf7\x17\x90\x0f\xe6~|\x9c\xe2\x95W\xfe\v[\xb6\u070e#G\x0ea\u03de\xbd\xec\x81\a\x1e\xb4\x95/U/\xbf\xfc\u04a7\xdb\xdbO\xdc\xdd\xd6vt\xfd\xa9S\xa7\x90H$\x90N\xa7=L\xc5L\n\xc603hj\x82<\xa1\x14L\x0e@\t\x17A\n\x85!\a\v\xa0\x84\x8b\xa0\x16\x95!\x14\xa9B\xb8j.\xc2\x15s\x10(.\x85\x1a.B\xb0\xb8\x14\xc1\x92\n\xa8\x85%\x90\x14\x15\xce \x04\x0eC7\xcci\xedB\x98\x13L\x05\xcf\x0e\f\xa6\xd4\xd2e\u00dc\x96\x03 ;\xcb\xd46\xb0\u02b5\x9b\xe5B@\xe3f\xe1\xd5\x00\xcd\u038c\x13v\xeaL\b\xe0\x18E\xb9\xc6\u06f9\xac\xa9,>\u01b1\x036\v\xae\xb6\xbe\x9c\x11\xc7!\xd11,s\rL&f\x87'\x17\x02\f\xe6&\x96\x99\x9e\u0115\xe1~\x8c\xf5w\"~\xfe,F{;\x90\x18\xee\xc3\xc4\xf0\x00&b\x17\xc0u\x87\xf3\xa6L\u029aY\x99\xb2A\xe21\xf52\x9d\x10E\xdeF,\xc30\x04!\x84\x84B!\x14\x15\x15\xa2\xb1\xb1\x11\x8b\x17/>\xdd\xd4\xd4\xf8\x8b\xa5K\x97\xbd|\xd3M-\xed\x00\xb0~\xfd\x8dt\u06f6m\xb8\xf3\xce\xcf\xf9\xea\x14\x1f\xcc\xfd\xf8C\x897\xde\xf8\x05}\xe3\x8d_\xb2'\x9ex\xd2V\xbe\x84\u007f\xf0\x83\x176\x1f=\xfa\u07b7\x8e\x1e=\xba``\xa0\x1f\xb1X\f\x9a\xa6\xe7-\t\xba\xb3C\x13\xe0I6\xd3\x16\x82Cp\xd7\fw\xca \xa9A\u0221\x02H\x81\x10\xa4@\x10J\xa8\x00\x81\xc2R\x84\"QD\x1a\xe6#\u04bc\b\xa5\x9f\xb8\x16\xe1\xb2JH\x8a\nI\x92 I\xd41\xc4\x12\x00\xb8\x01\xce-B\xc5eW\xeb1\x8d\xb2\xa8\ffm2 \x04\x19\x01d,x\xd2\f\xb3\u02d2[\x1bD\x96\n\xe2\xe6\tA\b\x93'2\xbf\xaf\xc3\xd0\xd20\xd2Ip]\x03\xd7M\x1baSJHA\xc0A\xb9n\x82\xb3\xaeA\x18:\fC\x03\x84\x80\x1a.\x82\x1c\f\x9b\x12A-\x8d\xf4D\x02S\xf1\u02d8\x8c\rc\xf2R?\x12\xc3\x03\x98\x8a_F*1\x8aTb\x14\xc9\u0118\xc7\a\x85P\x06\xcaXv\x93\xb3\v\x964\x0f\x80C\x88\x19\x12Ka\x06!\x04(**FYY\x19\xae\xb9\xa6\x19MM\x8d?^\xb0`\xc1\xd3\u06f6}\xb9\x8b\x102l?]k\xeb\xd3l\xeb\xd6m\xba\xbf2|0\xf7\xe3\x0f4\xac\x81\xd1n\xe5\vmm\xdd\xf1\xb9\u00c7\x0f\xdf\u007f\xf2\xe4\xff-\x1d\x19\x19\xc1\xf0\xf002\x99\x8cA\b\x04\xa5L2\v\x9d9\x1c\xac\xc5E\x10\xf7\x84g\xe7\xa2\x10\xdc\u021b5RJ!\a\xc3P\vK\x10,-G\u025cFT^\xb3\b%\xd1Z\x14\x94U \\Z\t\xb5\xa8\x14r(\fI\t\x80I2d\x89\x81P\xe6iN2\v\x8f\xe6f\xc2u\x03\x84\xeb\xa0\xe0\xa000\x9d\xd611\x95\xc4\u0115+\x98\x1a\x1f\xc5\xd4D\x02\xa9\xa9\t\xe8\xa9$2\xa9\x142\x13cHO\x8cCKN\xc2H\xa7`d\xd204\xf3\xab\x9eJ\x82k\xa6m\xb00\f\ap\xadZ\x02\xb1\xc0\x94s\xdd,\x06\x1b\xe6\xfb\xb4\x1b\xb3\f-\x03\xaee\xa0k\x19\xf3\u07fav\xf5\x05\x9d\x95\x8bRPP\xc7\xc7\x1c\x8e\u01ce;\x03'\xc43(\x9b\xeb\xban\b\xc1%UUI$\x12AYY\x19\xae\xbdv~b\xf9\xf2\xa5\xaf/_\xbe\xec_n\xbe\xf93\x1e>\xfc\xb5\xd7^!\xd5\xd5Q\xb1j\xd5\x1a\u007f1\xf8`\xee\xc7\x1fC\xec\xdc\xf9sr\xeb\xad\u007f)r8\u04ed\x1d\x1d\x1d[\xbb\xba\xba\xe7\x0f\r\r\x05c\xb1\x18\xe2\xf11!\x047\b\xa1T\bn+(H.\x1d\xe3\xf6Gw\xeeT\xe2\x92\xf59\xed\xa3B\x18.\x87?\x02I\r\x80\xc92\x98\xacB\t\x86\xa0\x14\x14\xa1\xb8\xbc\x1a\x85\x955\b\x16\x95@\r\x86!)*\x18\xa5Y!$#\x020t\x18\x99\f2\xa9i\xd3\xe27\x93\x82\xd0\u04d8\xba2\x8exl\x04\x93\x93W\x90\x9e\x9e\x86\xaee\xc0\r\xdd\x04^+\x03\xe7\u0724v\x84\xf8\xe8Tw6\x10\x13W\xb3\x8eS\xb4\x9c\xc9w\xbb5\xdd\xc4\xf1+\x067\x83J\x12#\x95\x95\x95(++CAA\xc1x]]]\xfb\xfa\xf5\xeb\xf7\xdes\xcf\u059f\x12B:\xdc\xd7{\xf0\xc1\xaf\x93{\xef\xbdW\xd4\xd4\xd4\xfa7\xbf\x0f\xe6~\xfc\xb1\xc5\xd3O?\x85S\xa7N\x91'\x9ex\u049d\xa9\xb3C\x87\x0e\u073as\xe7\xceM\xdd\xdd=\xab\x93\xc9\xe4\x82X,\x86\x81\x81ALOOA\xd7uLO'!\xc4\a2\xac\xbd\u028dLr\xe7\xd2\xffN\x16E\xde\xd1l\x1f\xe5\x02\xf5H\x02]_\xad\xb6y\u4302\xcb97y\u078d\xfd\x18\xce\x058\xe7\x90$\t\xaa\xaa\xa2\xa8\xa8\b\xd1h\x14\x91H\xd9XII\xc9\u19a6\xa6C\x1b7\xb6\x1c\xfd\u0527n\xdaE\b\xf1\xf0\xdfo\xbe\xf9+|\xfa\xd3\u007f\xe1\xdf\xec>\x98\xfb\xf1\xa7\x10\xb3\xa9\x18\x84\xe0\u05fe\xf8\xe2\x8b\x1b\xdb\xdb\xdb\xe7\uaeb6\xe2\u0295\x89\xf2s\xe7\xceM\x85\xc3\xe1\u56a6\x05{{\xcf#\x91H \x93I\xc308\f\xc3@&\x93\xf9\xe3\\x\xb9Y2!\x1e\xd31\xea\xea\xc1w\x17D\xed,;\xdf\x06f\u007f\x9fR{\xec\x9a\xd5\xf9i\xa9V\x18cP\xd5\x00**\xcaQ[[\v\u01a4\xe9`0\xd0Y]]=\xac\xaa\u02ae\xdbn\xdbrv\u035a\xb5\xbf\xca\u05d5\xb9k\xd7/q\xcb-\x9f\xf1on\x1f\xcc\xfd\xf8S\x8c\x8b\x17\x87\x10\x8d\xd6`p\xb0\x8f\xd4\xd6~B\xe4\x1c\xfb\xab\x00\x94\xec\u077b7\xd9\xdb\xdb3\xaf\xaf\xaf\xaf\xee\u0295\xc9\x06Bp\xfd\xc8H\x8c\xc6b#)JiEQQ\u046a\u02d7/c``\x10\x13\x13\x13\u0434\f4M\x83\xae\x1b0\f\x03\x9a\xa6\x9b<\xb7\xdbf\xe0Cj\x11\xbf\x9a\x89\x96\x99\a\x93\x19\xf3D\xb3\t\xf2l\xb9\xbdG\xcbm6\xfa0f\xd3%\xc4j\xa42m\x87\u0740\x9d}FB!\u02e6R\xc56\xb7b\x8cZ\xe0mf\u07a5\xa5%\xa8\xad\xadE0\x18\ucaac\xac\xec\f\x04\x82GC\xa1`\xe7\xbcy\u05cc\xb6\xb4l8\x1f\x8d\xd6^$\x84$\xec\xabvv\x9e!\xf3\xe6\xcd\x17\x00\xb0c\xc7S\u063cy3\xe6\u0319\xeb\xdf\xd0>\x98\xfb\xf1\xa7\x1c==\x9dhj\x9a\a!\x04v\xee\xfc9\x89\xc5b\xec\xc9'\x9f\x16\u01cf\x1f7\xf2\x03\xa6\xa8\xb0\xeeK\xbd\xa3\xe3T\xb8\xaf\xaf\u007fN[[[@Q\xe4\u0159Lf\xe5\u0673g\xb5\xe9\xe9diII\xf1\xba\x8b\x17/\x97\x0e\f\f`||\x1c\xba\xae!\x93\u0450L\x9a\xfe/6\xd8Sk B~\x80\xcf\xef\xed2\xc3\xfb\u071e=*\x04r\x8b\xb7\xb2,\x83Rj6\xf5\xe4\\U\x96e(\xb2\x9c\x05W\x0f\xc7M\x88w,\x9b\x95=\xeb\xba\x0e\u039d\xe9M\xf6\xefp\xceA)\x81$IP\x14\x15\xb2,\x811\x06J\x19dYBqq\xb1\x88F\xa3\xa4\xa4\xa48\x1d\x8f\xc7\x0f\x8c\x8d\x8d\x9fkhh\b44\u05031\xe98\xe7\xbcm\xf5\xea\x1b\x86W\xaf^;F\b\x19\xcd}\xcf\xf7\xddw\xaf\xb4b\xc5r\xb1l\xd92\xbed\xc9R\xf1\xfa\xeb\xff\x83M\x9b6\xfb7\xb0\x0f\xe6~\xf8q\xb5\fW\xe0\u0529\xf7iOO/y\xfb\xed}(,\f\xd3D\xe2\n}\xfc\xf1\xffH\xff\x86\xdf\v\x02\x10\xa9\u0514|\xe4\xc8\xe1\u04b6\xb6cJEE\xf9\xc6\u04e7;\x16\x8d\x8c\u0116\x8d\x8c\x8c.\x1e\x1a\x1a\n'\x12\xe3\xd40\xcc\x06\x97t:\x8d\xa9\xa9)LM%a\xaa\r\t4M\x87\xa6i\xf9\x93k\x80\u0232\fIbfc\x90p\x18sU\r\xa0\xa8\xa8\x10\x81@\x00\x92$\x81s\x81d2\t\x00P\x14\x05\xc1`\x00\x92$\x9bEXY\x02!$I\b\x9d,..DAA\x01\x14EE \xa0BUU(\x8a\n\xc6X\xf6\x89UU\xa5\xc1`\x80^\xb80\xb4\xef\xfc\xf9\xf3\xa7\xc2\xe10K\xa7\u04d9\xea\xea\xea\xe6`0T\xdc\xdf\xdfwZQ\x14c\xee\u0739\xa4\xbe\xbe\x1e\xc5\xc5E\b\x85B2\x80\xc1c\xc7\xda\xf7LNN&\u05acY%777\x19\r\r\r\xf1H\xa4j\n\xf6\xdc\rB\x92\xb9\xeb~\xfb\xf6\x87\x95T*i\xd4\xd7\u05cbU\xabV\x8a\xa5KW\xf8\xdap\x1f\xcc\xfd\xf0\xe3\u00cb\xae\xae3\u063d\xfb\xd7\u0636\xedK\xbf\xf1\xb1\xd3\xd3\x13U\x87\x0e\x1d^\xb7\u007f\xff\x81\x8at:\xb5H\b\\\xdf\xdd\u074dX,\x16\xe6\\\xd4%\x12\t#\x95JrEQKB\xa1\x90\xb5A8\x14\rc\fB\b$\x12\t=\x9dNO\xaa\xaaJM\xe0UH(\x14\x82,+\xa3\x15\x15\x91X}}=\"\x91\x88$\x84\xb8\xd4\xd9\xd9\xf9v:\x9d\x89777K\xcd\xcdM(..\x86$1Z\\\\\"\x15\x16\x16\xbe;22z|\xf5\xea\x1b\fYV\xd9\aY\x8b\x84\x90\xe9<\x9b\x98G\xfe\xf9\xdb\xc4\x0f\u007f\xf8\"\u05ad[\x87\xba\xba\x06\xff\xa6\xf2\xc1\xdc\x0f?>>\xd1\xde\xfe\x1e\x1e}\xf4\x9f\xf1\uaaef}\x90\x93@\xf9\x89\x13\xc7n>r\xa4-}\xe6\xcc\x19\xbd\xb2\xb2\xe2\x9a\xf2\xf2\xc8\n\xceE\x88s\xce3\x99\x8c\xb0\xb2c\x99\x10\x92\xec\xed=\xff\xf6\x85\v\x17\x06\xeb\xea\xeaX]]\x1d\xa9\xae\x8e\xb2\x86\x86\x06\xb2h\u0452vB\xc8\u064f\xe3\xe71<<\x88\xeaj_&\xe8\x87\x1f~\xfc\x81\xc7\xd0\xd0\xc0\xc7\xf2u\xdd\u007f\xff\xdfbp\xb0\xdf\xff\x03\xf9\xf1\xa1\xc7\xff\x03 \x99\rH\xa6\x0e6\x92\x00\x00\x00\x00IEND\xaeB`\x82") - -// dashboardDockerfile is the Dockerfile required to build an dashboard container -// to aggregate various private network services under one easily accessible page. -var dashboardDockerfile = ` -FROM mhart/alpine-node:latest - -WORKDIR /usr/app - -RUN \ - npm install connect serve-static && \ - \ - echo 'var connect = require("connect");' > server.js && \ - echo 'var serveStatic = require("serve-static");' >> server.js && \ - echo 'connect().use(serveStatic("/dashboard")).listen(80, function(){' >> server.js && \ - echo ' console.log("Server running on 80...");' >> server.js && \ - echo '});' >> server.js - -ADD {{.Network}}.json /dashboard/{{.Network}}.json -ADD {{.Network}}-cpp.json /dashboard/{{.Network}}-cpp.json -ADD {{.Network}}-harmony.json /dashboard/{{.Network}}-harmony.json -ADD {{.Network}}-parity.json /dashboard/{{.Network}}-parity.json -ADD {{.Network}}-python.json /dashboard/{{.Network}}-python.json -ADD index.html /dashboard/index.html -ADD puppeth.png /dashboard/puppeth.png - -EXPOSE 80 - -CMD ["node", "./server.js"] -` - -// dashboardComposefile is the docker-compose.yml file required to deploy and -// maintain an service aggregating dashboard. -var dashboardComposefile = ` -version: '2' -services: - dashboard: - build: . - image: {{.Network}}/dashboard{{if not .VHost}} - ports: - - "{{.Port}}:80"{{end}} - environment: - - ETHSTATS_PAGE={{.EthstatsPage}} - - EXPLORER_PAGE={{.ExplorerPage}} - - WALLET_PAGE={{.WalletPage}} - - FAUCET_PAGE={{.FaucetPage}}{{if .VHost}} - - VIRTUAL_HOST={{.VHost}}{{end}} - logging: - driver: "json-file" - options: - max-size: "1m" - max-file: "10" - restart: always -` - -// deployDashboard deploys a new dashboard container to a remote machine via SSH, -// docker and docker-compose. If an instance with the specified network name -// already exists there, it will be overwritten! -func deployDashboard(client *sshClient, network string, conf *config, config *dashboardInfos, nocache bool) ([]byte, error) { - // Generate the content to upload to the server - workdir := fmt.Sprintf("%d", rand.Int63()) - files := make(map[string][]byte) - - dockerfile := new(bytes.Buffer) - template.Must(template.New("").Parse(dashboardDockerfile)).Execute(dockerfile, map[string]interface{}{ - "Network": network, - }) - files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() - - composefile := new(bytes.Buffer) - template.Must(template.New("").Parse(dashboardComposefile)).Execute(composefile, map[string]interface{}{ - "Network": network, - "Port": config.port, - "VHost": config.host, - "EthstatsPage": config.ethstats, - "ExplorerPage": config.explorer, - "FaucetPage": config.faucet, - }) - files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() - - statsLogin := fmt.Sprintf("yournode:%s", conf.ethstats) - if !config.trusted { - statsLogin = "" - } - indexfile := new(bytes.Buffer) - bootCpp := make([]string, len(conf.bootnodes)) - for i, boot := range conf.bootnodes { - bootCpp[i] = "required:" + strings.TrimPrefix(boot, "enode://") - } - bootHarmony := make([]string, len(conf.bootnodes)) - for i, boot := range conf.bootnodes { - bootHarmony[i] = fmt.Sprintf("-Dpeer.active.%d.url=%s", i, boot) - } - bootPython := make([]string, len(conf.bootnodes)) - for i, boot := range conf.bootnodes { - bootPython[i] = "'" + boot + "'" - } - template.Must(template.New("").Parse(dashboardContent)).Execute(indexfile, map[string]interface{}{ - "Network": network, - "NetworkID": conf.Genesis.Config.ChainID, - "NetworkTitle": strings.Title(network), - "EthstatsPage": config.ethstats, - "ExplorerPage": config.explorer, - "FaucetPage": config.faucet, - "GethGenesis": network + ".json", - "Bootnodes": conf.bootnodes, - "BootnodesFlat": strings.Join(conf.bootnodes, ","), - "Ethstats": statsLogin, - "Ethash": conf.Genesis.Config.Ethash != nil, - "CppGenesis": network + "-cpp.json", - "CppBootnodes": strings.Join(bootCpp, " "), - "HarmonyGenesis": network + "-harmony.json", - "HarmonyBootnodes": strings.Join(bootHarmony, " "), - "ParityGenesis": network + "-parity.json", - "PythonGenesis": network + "-python.json", - "PythonBootnodes": strings.Join(bootPython, ","), - "Homestead": conf.Genesis.Config.HomesteadBlock, - "Tangerine": conf.Genesis.Config.EIP150Block, - "Spurious": conf.Genesis.Config.EIP155Block, - "Byzantium": conf.Genesis.Config.ByzantiumBlock, - "Constantinople": conf.Genesis.Config.ConstantinopleBlock, - "ConstantinopleFix": conf.Genesis.Config.PetersburgBlock, - }) - files[filepath.Join(workdir, "index.html")] = indexfile.Bytes() - - // Marshal the genesis spec files for go-ethereum and all the other clients - genesis, _ := conf.Genesis.MarshalJSON() - files[filepath.Join(workdir, network+".json")] = genesis - - if conf.Genesis.Config.Ethash != nil { - cppSpec, err := newAlethGenesisSpec(network, conf.Genesis) - if err != nil { - return nil, err - } - cppSpecJSON, _ := json.Marshal(cppSpec) - files[filepath.Join(workdir, network+"-cpp.json")] = cppSpecJSON - - harmonySpecJSON, _ := conf.Genesis.MarshalJSON() - files[filepath.Join(workdir, network+"-harmony.json")] = harmonySpecJSON - - paritySpec, err := newParityChainSpec(network, conf.Genesis, conf.bootnodes) - if err != nil { - return nil, err - } - paritySpecJSON, _ := json.Marshal(paritySpec) - files[filepath.Join(workdir, network+"-parity.json")] = paritySpecJSON - - pyethSpec, err := newPyEthereumGenesisSpec(network, conf.Genesis) - if err != nil { - return nil, err - } - pyethSpecJSON, _ := json.Marshal(pyethSpec) - files[filepath.Join(workdir, network+"-python.json")] = pyethSpecJSON - } else { - for _, client := range []string{"cpp", "harmony", "parity", "python"} { - files[filepath.Join(workdir, network+"-"+client+".json")] = []byte{} - } - } - files[filepath.Join(workdir, "puppeth.png")] = dashboardMascot - - // Upload the deployment files to the remote server (and clean up afterwards) - if out, err := client.Upload(files); err != nil { - return out, err - } - defer client.Run("rm -rf " + workdir) - - // Build and deploy the dashboard service - if nocache { - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s build --pull --no-cache && docker-compose -p %s up -d --force-recreate --timeout 60", workdir, network, network)) - } - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network)) -} - -// dashboardInfos is returned from a dashboard status check to allow reporting -// various configuration parameters. -type dashboardInfos struct { - host string - port int - trusted bool - - ethstats string - explorer string - faucet string -} - -// Report converts the typed struct into a plain string->string map, containing -// most - but not all - fields for reporting to the user. -func (info *dashboardInfos) Report() map[string]string { - return map[string]string{ - "Website address": info.host, - "Website listener port": strconv.Itoa(info.port), - "Ethstats service": info.ethstats, - "Explorer service": info.explorer, - "Faucet service": info.faucet, - } -} - -// checkDashboard does a health-check against a dashboard container to verify if -// it's running, and if yes, gathering a collection of useful infos about it. -func checkDashboard(client *sshClient, network string) (*dashboardInfos, error) { - // Inspect a possible ethstats container on the host - infos, err := inspectContainer(client, fmt.Sprintf("%s_dashboard_1", network)) - if err != nil { - return nil, err - } - if !infos.running { - return nil, ErrServiceOffline - } - // Resolve the port from the host, or the reverse proxy - port := infos.portmap["80/tcp"] - if port == 0 { - if proxy, _ := checkNginx(client, network); proxy != nil { - port = proxy.port - } - } - if port == 0 { - return nil, ErrNotExposed - } - // Resolve the host from the reverse-proxy and configure the connection string - host := infos.envvars["VIRTUAL_HOST"] - if host == "" { - host = client.server - } - // Run a sanity check to see if the port is reachable - if err = checkPort(host, port); err != nil { - log.Warn("Dashboard service seems unreachable", "server", host, "port", port, "err", err) - } - // Container available, assemble and return the useful infos - return &dashboardInfos{ - host: host, - port: port, - ethstats: infos.envvars["ETHSTATS_PAGE"], - explorer: infos.envvars["EXPLORER_PAGE"], - faucet: infos.envvars["FAUCET_PAGE"], - }, nil -}
diff --git go-ethereum/cmd/puppeth/module_nginx.go celo/cmd/puppeth/module_nginx.go deleted file mode 100644 index 77a066245cafdb758018e3d908dffb722b243cc8..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/module_nginx.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bytes" - "fmt" - "html/template" - "math/rand" - "path/filepath" - "strconv" - - "github.com/ethereum/go-ethereum/log" -) - -// nginxDockerfile is theis the Dockerfile required to build an nginx reverse- -// proxy. -var nginxDockerfile = `FROM jwilder/nginx-proxy` - -// nginxComposefile is the docker-compose.yml file required to deploy and maintain -// an nginx reverse-proxy. The proxy is responsible for exposing one or more HTTP -// services running on a single host. -var nginxComposefile = ` -version: '2' -services: - nginx: - build: . - image: {{.Network}}/nginx - container_name: {{.Network}}_nginx_1 - ports: - - "{{.Port}}:80" - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - logging: - driver: "json-file" - options: - max-size: "1m" - max-file: "10" - restart: always -` - -// deployNginx deploys a new nginx reverse-proxy container to expose one or more -// HTTP services running on a single host. If an instance with the specified -// network name already exists there, it will be overwritten! -func deployNginx(client *sshClient, network string, port int, nocache bool) ([]byte, error) { - log.Info("Deploying nginx reverse-proxy", "server", client.server, "port", port) - - // Generate the content to upload to the server - workdir := fmt.Sprintf("%d", rand.Int63()) - files := make(map[string][]byte) - - dockerfile := new(bytes.Buffer) - template.Must(template.New("").Parse(nginxDockerfile)).Execute(dockerfile, nil) - files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() - - composefile := new(bytes.Buffer) - template.Must(template.New("").Parse(nginxComposefile)).Execute(composefile, map[string]interface{}{ - "Network": network, - "Port": port, - }) - files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() - - // Upload the deployment files to the remote server (and clean up afterwards) - if out, err := client.Upload(files); err != nil { - return out, err - } - defer client.Run("rm -rf " + workdir) - - // Build and deploy the reverse-proxy service - if nocache { - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s build --pull --no-cache && docker-compose -p %s up -d --force-recreate --timeout 60", workdir, network, network)) - } - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network)) -} - -// nginxInfos is returned from an nginx reverse-proxy status check to allow -// reporting various configuration parameters. -type nginxInfos struct { - port int -} - -// Report converts the typed struct into a plain string->string map, containing -// most - but not all - fields for reporting to the user. -func (info *nginxInfos) Report() map[string]string { - return map[string]string{ - "Shared listener port": strconv.Itoa(info.port), - } -} - -// checkNginx does a health-check against an nginx reverse-proxy to verify whether -// it's running, and if yes, gathering a collection of useful infos about it. -func checkNginx(client *sshClient, network string) (*nginxInfos, error) { - // Inspect a possible nginx container on the host - infos, err := inspectContainer(client, fmt.Sprintf("%s_nginx_1", network)) - if err != nil { - return nil, err - } - if !infos.running { - return nil, ErrServiceOffline - } - // Container available, assemble and return the useful infos - return &nginxInfos{ - port: infos.portmap["80/tcp"], - }, nil -}
diff --git go-ethereum/cmd/puppeth/module.go celo/cmd/puppeth/module.go deleted file mode 100644 index 24f73dfaae4f76c07dff1d3c93161a853e8f9283..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/module.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "encoding/json" - "errors" - "fmt" - "net" - "strconv" - "strings" - "time" - - "github.com/ethereum/go-ethereum/log" -) - -var ( - // ErrServiceUnknown is returned when a service container doesn't exist. - ErrServiceUnknown = errors.New("service unknown") - - // ErrServiceOffline is returned when a service container exists, but it is not - // running. - ErrServiceOffline = errors.New("service offline") - - // ErrServiceUnreachable is returned when a service container is running, but - // seems to not respond to communication attempts. - ErrServiceUnreachable = errors.New("service unreachable") - - // ErrNotExposed is returned if a web-service doesn't have an exposed port, nor - // a reverse-proxy in front of it to forward requests. - ErrNotExposed = errors.New("service not exposed, nor proxied") -) - -// containerInfos is a heavily reduced version of the huge inspection dataset -// returned from docker inspect, parsed into a form easily usable by puppeth. -type containerInfos struct { - running bool // Flag whether the container is running currently - envvars map[string]string // Collection of environmental variables set on the container - portmap map[string]int // Port mapping from internal port/proto combos to host binds - volumes map[string]string // Volume mount points from container to host directories -} - -// inspectContainer runs docker inspect against a running container -func inspectContainer(client *sshClient, container string) (*containerInfos, error) { - // Check whether there's a container running for the service - out, err := client.Run(fmt.Sprintf("docker inspect %s", container)) - if err != nil { - return nil, ErrServiceUnknown - } - // If yes, extract various configuration options - type inspection struct { - State struct { - Running bool - } - Mounts []struct { - Source string - Destination string - } - Config struct { - Env []string - } - HostConfig struct { - PortBindings map[string][]map[string]string - } - } - var inspects []inspection - if err = json.Unmarshal(out, &inspects); err != nil { - return nil, err - } - inspect := inspects[0] - - // Infos retrieved, parse the above into something meaningful - infos := &containerInfos{ - running: inspect.State.Running, - envvars: make(map[string]string), - portmap: make(map[string]int), - volumes: make(map[string]string), - } - for _, envvar := range inspect.Config.Env { - if parts := strings.Split(envvar, "="); len(parts) == 2 { - infos.envvars[parts[0]] = parts[1] - } - } - for portname, details := range inspect.HostConfig.PortBindings { - if len(details) > 0 { - port, _ := strconv.Atoi(details[0]["HostPort"]) - infos.portmap[portname] = port - } - } - for _, mount := range inspect.Mounts { - infos.volumes[mount.Destination] = mount.Source - } - return infos, err -} - -// tearDown connects to a remote machine via SSH and terminates docker containers -// running with the specified name in the specified network. -func tearDown(client *sshClient, network string, service string, purge bool) ([]byte, error) { - // Tear down the running (or paused) container - out, err := client.Run(fmt.Sprintf("docker rm -f %s_%s_1", network, service)) - if err != nil { - return out, err - } - // If requested, purge the associated docker image too - if purge { - return client.Run(fmt.Sprintf("docker rmi %s/%s", network, service)) - } - return nil, nil -} - -// resolve retrieves the hostname a service is running on either by returning the -// actual server name and port, or preferably an nginx virtual host if available. -func resolve(client *sshClient, network string, service string, port int) (string, error) { - // Inspect the service to get various configurations from it - infos, err := inspectContainer(client, fmt.Sprintf("%s_%s_1", network, service)) - if err != nil { - return "", err - } - if !infos.running { - return "", ErrServiceOffline - } - // Container online, extract any environmental variables - if vhost := infos.envvars["VIRTUAL_HOST"]; vhost != "" { - return vhost, nil - } - return fmt.Sprintf("%s:%d", client.server, port), nil -} - -// checkPort tries to connect to a remote host on a given -func checkPort(host string, port int) error { - log.Trace("Verifying remote TCP connectivity", "server", host, "port", port) - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), time.Second) - if err != nil { - return err - } - conn.Close() - return nil -}
diff --git go-ethereum/cmd/puppeth/module_explorer.go celo/cmd/puppeth/module_explorer.go deleted file mode 100644 index 1e298609f5ba890ace2c8d53819c2d60a159a136..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/module_explorer.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bytes" - "fmt" - "html/template" - "math/rand" - "path/filepath" - "strconv" - "strings" - - "github.com/ethereum/go-ethereum/log" -) - -// explorerDockerfile is the Dockerfile required to run a block explorer. -var explorerDockerfile = ` -FROM puppeth/blockscout:latest - -ADD genesis.json /genesis.json -RUN \ - echo 'geth --cache 512 init /genesis.json' > explorer.sh && \ - echo $'geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,debug" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" --exitwhensynced' >> explorer.sh && \ - echo $'exec geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,debug" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" &' >> explorer.sh && \ - echo '/usr/local/bin/docker-entrypoint.sh postgres &' >> explorer.sh && \ - echo 'sleep 5' >> explorer.sh && \ - echo 'mix do ecto.drop --force, ecto.create, ecto.migrate' >> explorer.sh && \ - echo 'mix phx.server' >> explorer.sh - -ENTRYPOINT ["/bin/sh", "explorer.sh"] -` - -// explorerComposefile is the docker-compose.yml file required to deploy and -// maintain a block explorer. -var explorerComposefile = ` -version: '2' -services: - explorer: - build: . - image: {{.Network}}/explorer - container_name: {{.Network}}_explorer_1 - ports: - - "{{.EthPort}}:{{.EthPort}}" - - "{{.EthPort}}:{{.EthPort}}/udp"{{if not .VHost}} - - "{{.WebPort}}:4000"{{end}} - environment: - - ETH_PORT={{.EthPort}} - - ETH_NAME={{.EthName}} - - BLOCK_TRANSFORMER={{.Transformer}}{{if .VHost}} - - VIRTUAL_HOST={{.VHost}} - - VIRTUAL_PORT=4000{{end}} - volumes: - - {{.Datadir}}:/opt/app/.ethereum - - {{.DBDir}}:/var/lib/postgresql/data - logging: - driver: "json-file" - options: - max-size: "1m" - max-file: "10" - restart: always -` - -// deployExplorer deploys a new block explorer container to a remote machine via -// SSH, docker and docker-compose. If an instance with the specified network name -// already exists there, it will be overwritten! -func deployExplorer(client *sshClient, network string, bootnodes []string, config *explorerInfos, nocache bool, isClique bool) ([]byte, error) { - // Generate the content to upload to the server - workdir := fmt.Sprintf("%d", rand.Int63()) - files := make(map[string][]byte) - - dockerfile := new(bytes.Buffer) - template.Must(template.New("").Parse(explorerDockerfile)).Execute(dockerfile, map[string]interface{}{ - "NetworkID": config.node.network, - "Bootnodes": strings.Join(bootnodes, ","), - "Ethstats": config.node.ethstats, - "EthPort": config.node.port, - }) - files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() - - transformer := "base" - if isClique { - transformer = "clique" - } - composefile := new(bytes.Buffer) - template.Must(template.New("").Parse(explorerComposefile)).Execute(composefile, map[string]interface{}{ - "Network": network, - "VHost": config.host, - "Ethstats": config.node.ethstats, - "Datadir": config.node.datadir, - "DBDir": config.dbdir, - "EthPort": config.node.port, - "EthName": config.node.ethstats[:strings.Index(config.node.ethstats, ":")], - "WebPort": config.port, - "Transformer": transformer, - }) - files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() - files[filepath.Join(workdir, "genesis.json")] = config.node.genesis - - // Upload the deployment files to the remote server (and clean up afterwards) - if out, err := client.Upload(files); err != nil { - return out, err - } - defer client.Run("rm -rf " + workdir) - - // Build and deploy the boot or seal node service - if nocache { - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s build --pull --no-cache && docker-compose -p %s up -d --force-recreate --timeout 60", workdir, network, network)) - } - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network)) -} - -// explorerInfos is returned from a block explorer status check to allow reporting -// various configuration parameters. -type explorerInfos struct { - node *nodeInfos - dbdir string - host string - port int -} - -// Report converts the typed struct into a plain string->string map, containing -// most - but not all - fields for reporting to the user. -func (info *explorerInfos) Report() map[string]string { - report := map[string]string{ - "Website address ": info.host, - "Website listener port ": strconv.Itoa(info.port), - "Ethereum listener port ": strconv.Itoa(info.node.port), - "Ethstats username": info.node.ethstats, - } - return report -} - -// checkExplorer does a health-check against a block explorer server to verify -// whether it's running, and if yes, whether it's responsive. -func checkExplorer(client *sshClient, network string) (*explorerInfos, error) { - // Inspect a possible explorer container on the host - infos, err := inspectContainer(client, fmt.Sprintf("%s_explorer_1", network)) - if err != nil { - return nil, err - } - if !infos.running { - return nil, ErrServiceOffline - } - // Resolve the port from the host, or the reverse proxy - port := infos.portmap["4000/tcp"] - if port == 0 { - if proxy, _ := checkNginx(client, network); proxy != nil { - port = proxy.port - } - } - if port == 0 { - return nil, ErrNotExposed - } - // Resolve the host from the reverse-proxy and the config values - host := infos.envvars["VIRTUAL_HOST"] - if host == "" { - host = client.server - } - // Run a sanity check to see if the devp2p is reachable - p2pPort := infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"] - if err = checkPort(host, p2pPort); err != nil { - log.Warn("Explorer node seems unreachable", "server", host, "port", p2pPort, "err", err) - } - if err = checkPort(host, port); err != nil { - log.Warn("Explorer service seems unreachable", "server", host, "port", port, "err", err) - } - // Assemble and return the useful infos - stats := &explorerInfos{ - node: &nodeInfos{ - datadir: infos.volumes["/opt/app/.ethereum"], - port: infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"], - ethstats: infos.envvars["ETH_NAME"], - }, - dbdir: infos.volumes["/var/lib/postgresql/data"], - host: host, - port: port, - } - return stats, nil -}
diff --git go-ethereum/cmd/puppeth/genesis_test.go celo/cmd/puppeth/genesis_test.go deleted file mode 100644 index d2cf3bbb9c3bd8d2cd7033005fa0319c0c301f13..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/genesis_test.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bytes" - "encoding/json" - "io/ioutil" - "reflect" - "strings" - "testing" - - "github.com/davecgh/go-spew/spew" - "github.com/ethereum/go-ethereum/core" -) - -// Tests the go-ethereum to Aleth chainspec conversion for the Stureby testnet. -func TestAlethSturebyConverter(t *testing.T) { - blob, err := ioutil.ReadFile("testdata/stureby_geth.json") - if err != nil { - t.Fatalf("could not read file: %v", err) - } - var genesis core.Genesis - if err := json.Unmarshal(blob, &genesis); err != nil { - t.Fatalf("failed parsing genesis: %v", err) - } - spec, err := newAlethGenesisSpec("stureby", &genesis) - if err != nil { - t.Fatalf("failed creating chainspec: %v", err) - } - - expBlob, err := ioutil.ReadFile("testdata/stureby_aleth.json") - if err != nil { - t.Fatalf("could not read file: %v", err) - } - expspec := &alethGenesisSpec{} - if err := json.Unmarshal(expBlob, expspec); err != nil { - t.Fatalf("failed parsing genesis: %v", err) - } - if !reflect.DeepEqual(expspec, spec) { - t.Errorf("chainspec mismatch") - c := spew.ConfigState{ - DisablePointerAddresses: true, - SortKeys: true, - } - exp := strings.Split(c.Sdump(expspec), "\n") - got := strings.Split(c.Sdump(spec), "\n") - for i := 0; i < len(exp) && i < len(got); i++ { - if exp[i] != got[i] { - t.Logf("got: %v\nexp: %v\n", exp[i], got[i]) - } - } - } -} - -// Tests the go-ethereum to Parity chainspec conversion for the Stureby testnet. -func TestParitySturebyConverter(t *testing.T) { - blob, err := ioutil.ReadFile("testdata/stureby_geth.json") - if err != nil { - t.Fatalf("could not read file: %v", err) - } - var genesis core.Genesis - if err := json.Unmarshal(blob, &genesis); err != nil { - t.Fatalf("failed parsing genesis: %v", err) - } - spec, err := newParityChainSpec("stureby", &genesis, []string{}) - if err != nil { - t.Fatalf("failed creating chainspec: %v", err) - } - enc, err := json.MarshalIndent(spec, "", " ") - if err != nil { - t.Fatalf("failed encoding chainspec: %v", err) - } - expBlob, err := ioutil.ReadFile("testdata/stureby_parity.json") - if err != nil { - t.Fatalf("could not read file: %v", err) - } - if !bytes.Equal(expBlob, enc) { - t.Fatalf("chainspec mismatch") - } -}
diff --git go-ethereum/cmd/puppeth/module_ethstats.go celo/cmd/puppeth/module_ethstats.go deleted file mode 100644 index ee835cc188d64b24967d16fe5eaccd9b5a77959c..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/module_ethstats.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bytes" - "fmt" - "math/rand" - "path/filepath" - "strconv" - "strings" - "text/template" - - "github.com/ethereum/go-ethereum/log" -) - -// ethstatsDockerfile is the Dockerfile required to build an ethstats backend -// and associated monitoring site. -var ethstatsDockerfile = ` -FROM puppeth/ethstats:latest - -RUN echo 'module.exports = {trusted: [{{.Trusted}}], banned: [{{.Banned}}], reserved: ["yournode"]};' > lib/utils/config.js -` - -// ethstatsComposefile is the docker-compose.yml file required to deploy and -// maintain an ethstats monitoring site. -var ethstatsComposefile = ` -version: '2' -services: - ethstats: - build: . - image: {{.Network}}/ethstats - container_name: {{.Network}}_ethstats_1{{if not .VHost}} - ports: - - "{{.Port}}:3000"{{end}} - environment: - - WS_SECRET={{.Secret}}{{if .VHost}} - - VIRTUAL_HOST={{.VHost}}{{end}}{{if .Banned}} - - BANNED={{.Banned}}{{end}} - logging: - driver: "json-file" - options: - max-size: "1m" - max-file: "10" - restart: always -` - -// deployEthstats deploys a new ethstats container to a remote machine via SSH, -// docker and docker-compose. If an instance with the specified network name -// already exists there, it will be overwritten! -func deployEthstats(client *sshClient, network string, port int, secret string, vhost string, trusted []string, banned []string, nocache bool) ([]byte, error) { - // Generate the content to upload to the server - workdir := fmt.Sprintf("%d", rand.Int63()) - files := make(map[string][]byte) - - trustedLabels := make([]string, len(trusted)) - for i, address := range trusted { - trustedLabels[i] = fmt.Sprintf("\"%s\"", address) - } - bannedLabels := make([]string, len(banned)) - for i, address := range banned { - bannedLabels[i] = fmt.Sprintf("\"%s\"", address) - } - - dockerfile := new(bytes.Buffer) - template.Must(template.New("").Parse(ethstatsDockerfile)).Execute(dockerfile, map[string]interface{}{ - "Trusted": strings.Join(trustedLabels, ", "), - "Banned": strings.Join(bannedLabels, ", "), - }) - files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() - - composefile := new(bytes.Buffer) - template.Must(template.New("").Parse(ethstatsComposefile)).Execute(composefile, map[string]interface{}{ - "Network": network, - "Port": port, - "Secret": secret, - "VHost": vhost, - "Banned": strings.Join(banned, ","), - }) - files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() - - // Upload the deployment files to the remote server (and clean up afterwards) - if out, err := client.Upload(files); err != nil { - return out, err - } - defer client.Run("rm -rf " + workdir) - - // Build and deploy the ethstats service - if nocache { - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s build --pull --no-cache && docker-compose -p %s up -d --force-recreate --timeout 60", workdir, network, network)) - } - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network)) -} - -// ethstatsInfos is returned from an ethstats status check to allow reporting -// various configuration parameters. -type ethstatsInfos struct { - host string - port int - secret string - config string - banned []string -} - -// Report converts the typed struct into a plain string->string map, containing -// most - but not all - fields for reporting to the user. -func (info *ethstatsInfos) Report() map[string]string { - return map[string]string{ - "Website address": info.host, - "Website listener port": strconv.Itoa(info.port), - "Login secret": info.secret, - "Banned addresses": strings.Join(info.banned, "\n"), - } -} - -// checkEthstats does a health-check against an ethstats server to verify whether -// it's running, and if yes, gathering a collection of useful infos about it. -func checkEthstats(client *sshClient, network string) (*ethstatsInfos, error) { - // Inspect a possible ethstats container on the host - infos, err := inspectContainer(client, fmt.Sprintf("%s_ethstats_1", network)) - if err != nil { - return nil, err - } - if !infos.running { - return nil, ErrServiceOffline - } - // Resolve the port from the host, or the reverse proxy - port := infos.portmap["3000/tcp"] - if port == 0 { - if proxy, _ := checkNginx(client, network); proxy != nil { - port = proxy.port - } - } - if port == 0 { - return nil, ErrNotExposed - } - // Resolve the host from the reverse-proxy and configure the connection string - host := infos.envvars["VIRTUAL_HOST"] - if host == "" { - host = client.server - } - secret := infos.envvars["WS_SECRET"] - config := fmt.Sprintf("%s@%s", secret, host) - if port != 80 && port != 443 { - config += fmt.Sprintf(":%d", port) - } - // Retrieve the IP banned list - banned := strings.Split(infos.envvars["BANNED"], ",") - - // Run a sanity check to see if the port is reachable - if err = checkPort(host, port); err != nil { - log.Warn("Ethstats service seems unreachable", "server", host, "port", port, "err", err) - } - // Container available, assemble and return the useful infos - return &ethstatsInfos{ - host: host, - port: port, - secret: secret, - config: config, - banned: banned, - }, nil -}
diff --git go-ethereum/cmd/puppeth/wizard.go celo/cmd/puppeth/wizard.go deleted file mode 100644 index 34b7e26d4bd7f9b1ea7d622062ef5303669bcead..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/wizard.go +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "io/ioutil" - "math/big" - "net" - "net/url" - "os" - "path/filepath" - "sort" - "strconv" - "strings" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/log" - "golang.org/x/crypto/ssh/terminal" -) - -// config contains all the configurations needed by puppeth that should be saved -// between sessions. -type config struct { - path string // File containing the configuration values - bootnodes []string // Bootnodes to always connect to by all nodes - ethstats string // Ethstats settings to cache for node deploys - - Genesis *core.Genesis `json:"genesis,omitempty"` // Genesis block to cache for node deploys - Servers map[string][]byte `json:"servers,omitempty"` -} - -// servers retrieves an alphabetically sorted list of servers. -func (c config) servers() []string { - servers := make([]string, 0, len(c.Servers)) - for server := range c.Servers { - servers = append(servers, server) - } - sort.Strings(servers) - - return servers -} - -// flush dumps the contents of config to disk. -func (c config) flush() { - os.MkdirAll(filepath.Dir(c.path), 0755) - - out, _ := json.MarshalIndent(c, "", " ") - if err := ioutil.WriteFile(c.path, out, 0644); err != nil { - log.Warn("Failed to save puppeth configs", "file", c.path, "err", err) - } -} - -type wizard struct { - network string // Network name to manage - conf config // Configurations from previous runs - - servers map[string]*sshClient // SSH connections to servers to administer - services map[string][]string // Ethereum services known to be running on servers - - in *bufio.Reader // Wrapper around stdin to allow reading user input - lock sync.Mutex // Lock to protect configs during concurrent service discovery -} - -// read reads a single line from stdin, trimming if from spaces. -func (w *wizard) read() string { - fmt.Printf("> ") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - return strings.TrimSpace(text) -} - -// readString reads a single line from stdin, trimming if from spaces, enforcing -// non-emptyness. -func (w *wizard) readString() string { - for { - fmt.Printf("> ") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.TrimSpace(text); text != "" { - return text - } - } -} - -// readDefaultString reads a single line from stdin, trimming if from spaces. If -// an empty line is entered, the default value is returned. -func (w *wizard) readDefaultString(def string) string { - fmt.Printf("> ") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.TrimSpace(text); text != "" { - return text - } - return def -} - -// readDefaultYesNo reads a single line from stdin, trimming if from spaces and -// interpreting it as a 'yes' or a 'no'. If an empty line is entered, the default -// value is returned. -func (w *wizard) readDefaultYesNo(def bool) bool { - for { - fmt.Printf("> ") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.ToLower(strings.TrimSpace(text)); text == "" { - return def - } - if text == "y" || text == "yes" { - return true - } - if text == "n" || text == "no" { - return false - } - log.Error("Invalid input, expected 'y', 'yes', 'n', 'no' or empty") - } -} - -// readURL reads a single line from stdin, trimming if from spaces and trying to -// interpret it as a URL (http, https or file). -func (w *wizard) readURL() *url.URL { - for { - fmt.Printf("> ") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - uri, err := url.Parse(strings.TrimSpace(text)) - if err != nil { - log.Error("Invalid input, expected URL", "err", err) - continue - } - return uri - } -} - -// readInt reads a single line from stdin, trimming if from spaces, enforcing it -// to parse into an integer. -func (w *wizard) readInt() int { - for { - fmt.Printf("> ") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.TrimSpace(text); text == "" { - continue - } - val, err := strconv.Atoi(strings.TrimSpace(text)) - if err != nil { - log.Error("Invalid input, expected integer", "err", err) - continue - } - return val - } -} - -// readDefaultInt reads a single line from stdin, trimming if from spaces, enforcing -// it to parse into an integer. If an empty line is entered, the default value is -// returned. -func (w *wizard) readDefaultInt(def int) int { - for { - fmt.Printf("> ") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.TrimSpace(text); text == "" { - return def - } - val, err := strconv.Atoi(strings.TrimSpace(text)) - if err != nil { - log.Error("Invalid input, expected integer", "err", err) - continue - } - return val - } -} - -// readDefaultBigInt reads a single line from stdin, trimming if from spaces, -// enforcing it to parse into a big integer. If an empty line is entered, the -// default value is returned. -func (w *wizard) readDefaultBigInt(def *big.Int) *big.Int { - for { - fmt.Printf("> ") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.TrimSpace(text); text == "" { - return def - } - val, ok := new(big.Int).SetString(text, 0) - if !ok { - log.Error("Invalid input, expected big integer") - continue - } - return val - } -} - -/* -// readFloat reads a single line from stdin, trimming if from spaces, enforcing it -// to parse into a float. -func (w *wizard) readFloat() float64 { - for { - fmt.Printf("> ") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.TrimSpace(text); text == "" { - continue - } - val, err := strconv.ParseFloat(strings.TrimSpace(text), 64) - if err != nil { - log.Error("Invalid input, expected float", "err", err) - continue - } - return val - } -} -*/ - -// readDefaultFloat reads a single line from stdin, trimming if from spaces, enforcing -// it to parse into a float. If an empty line is entered, the default value is returned. -func (w *wizard) readDefaultFloat(def float64) float64 { - for { - fmt.Printf("> ") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.TrimSpace(text); text == "" { - return def - } - val, err := strconv.ParseFloat(strings.TrimSpace(text), 64) - if err != nil { - log.Error("Invalid input, expected float", "err", err) - continue - } - return val - } -} - -// readPassword reads a single line from stdin, trimming it from the trailing new -// line and returns it. The input will not be echoed. -func (w *wizard) readPassword() string { - fmt.Printf("> ") - text, err := terminal.ReadPassword(int(os.Stdin.Fd())) - if err != nil { - log.Crit("Failed to read password", "err", err) - } - fmt.Println() - return string(text) -} - -// readAddress reads a single line from stdin, trimming if from spaces and converts -// it to an Ethereum address. -func (w *wizard) readAddress() *common.Address { - for { - // Read the address from the user - fmt.Printf("> 0x") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.TrimSpace(text); text == "" { - return nil - } - // Make sure it looks ok and return it if so - if len(text) != 40 { - log.Error("Invalid address length, please retry") - continue - } - bigaddr, _ := new(big.Int).SetString(text, 16) - address := common.BigToAddress(bigaddr) - return &address - } -} - -// readDefaultAddress reads a single line from stdin, trimming if from spaces and -// converts it to an Ethereum address. If an empty line is entered, the default -// value is returned. -func (w *wizard) readDefaultAddress(def common.Address) common.Address { - for { - // Read the address from the user - fmt.Printf("> 0x") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.TrimSpace(text); text == "" { - return def - } - // Make sure it looks ok and return it if so - if len(text) != 40 { - log.Error("Invalid address length, please retry") - continue - } - bigaddr, _ := new(big.Int).SetString(text, 16) - return common.BigToAddress(bigaddr) - } -} - -// readJSON reads a raw JSON message and returns it. -func (w *wizard) readJSON() string { - var blob json.RawMessage - - for { - fmt.Printf("> ") - if err := json.NewDecoder(w.in).Decode(&blob); err != nil { - log.Error("Invalid JSON, please try again", "err", err) - continue - } - return string(blob) - } -} - -// readIPAddress reads a single line from stdin, trimming if from spaces and -// returning it if it's convertible to an IP address. The reason for keeping -// the user input format instead of returning a Go net.IP is to match with -// weird formats used by ethstats, which compares IPs textually, not by value. -func (w *wizard) readIPAddress() string { - for { - // Read the IP address from the user - fmt.Printf("> ") - text, err := w.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.TrimSpace(text); text == "" { - return "" - } - // Make sure it looks ok and return it if so - if ip := net.ParseIP(text); ip == nil { - log.Error("Invalid IP address, please retry") - continue - } - return text - } -}
diff --git go-ethereum/cmd/puppeth/ssh.go celo/cmd/puppeth/ssh.go deleted file mode 100644 index 8a8df3d79f644fe613a36adae004a12358595d51..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/ssh.go +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bufio" - "bytes" - "errors" - "fmt" - "io/ioutil" - "net" - "os" - "os/user" - "path/filepath" - "strings" - - "github.com/ethereum/go-ethereum/log" - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/agent" - "golang.org/x/crypto/ssh/terminal" -) - -// sshClient is a small wrapper around Go's SSH client with a few utility methods -// implemented on top. -type sshClient struct { - server string // Server name or IP without port number - address string // IP address of the remote server - pubkey []byte // RSA public key to authenticate the server - client *ssh.Client - logger log.Logger -} - -const EnvSSHAuthSock = "SSH_AUTH_SOCK" - -// dial establishes an SSH connection to a remote node using the current user and -// the user's configured private RSA key. If that fails, password authentication -// is fallen back to. server can be a string like user:identity@server:port. -func dial(server string, pubkey []byte) (*sshClient, error) { - // Figure out username, identity, hostname and port - hostname := "" - hostport := server - username := "" - identity := "id_rsa" // default - - if strings.Contains(server, "@") { - prefix := server[:strings.Index(server, "@")] - if strings.Contains(prefix, ":") { - username = prefix[:strings.Index(prefix, ":")] - identity = prefix[strings.Index(prefix, ":")+1:] - } else { - username = prefix - } - hostport = server[strings.Index(server, "@")+1:] - } - if strings.Contains(hostport, ":") { - hostname = hostport[:strings.Index(hostport, ":")] - } else { - hostname = hostport - hostport += ":22" - } - logger := log.New("server", server) - logger.Debug("Attempting to establish SSH connection") - - user, err := user.Current() - if err != nil { - return nil, err - } - if username == "" { - username = user.Username - } - - // Configure the supported authentication methods (ssh agent, private key and password) - var ( - auths []ssh.AuthMethod - conn net.Conn - ) - if conn, err = net.Dial("unix", os.Getenv(EnvSSHAuthSock)); err != nil { - log.Warn("Unable to dial SSH agent, falling back to private keys", "err", err) - } else { - client := agent.NewClient(conn) - auths = append(auths, ssh.PublicKeysCallback(client.Signers)) - } - if err != nil { - path := filepath.Join(user.HomeDir, ".ssh", identity) - if buf, err := ioutil.ReadFile(path); err != nil { - log.Warn("No SSH key, falling back to passwords", "path", path, "err", err) - } else { - key, err := ssh.ParsePrivateKey(buf) - if err != nil { - fmt.Printf("What's the decryption password for %s? (won't be echoed)\n>", path) - blob, err := terminal.ReadPassword(int(os.Stdin.Fd())) - fmt.Println() - if err != nil { - log.Warn("Couldn't read password", "err", err) - } - key, err := ssh.ParsePrivateKeyWithPassphrase(buf, blob) - if err != nil { - log.Warn("Failed to decrypt SSH key, falling back to passwords", "path", path, "err", err) - } else { - auths = append(auths, ssh.PublicKeys(key)) - } - } else { - auths = append(auths, ssh.PublicKeys(key)) - } - } - auths = append(auths, ssh.PasswordCallback(func() (string, error) { - fmt.Printf("What's the login password for %s at %s? (won't be echoed)\n> ", username, server) - blob, err := terminal.ReadPassword(int(os.Stdin.Fd())) - - fmt.Println() - return string(blob), err - })) - } - // Resolve the IP address of the remote server - addr, err := net.LookupHost(hostname) - if err != nil { - return nil, err - } - if len(addr) == 0 { - return nil, errors.New("no IPs associated with domain") - } - // Try to dial in to the remote server - logger.Trace("Dialing remote SSH server", "user", username) - keycheck := func(hostname string, remote net.Addr, key ssh.PublicKey) error { - // If no public key is known for SSH, ask the user to confirm - if pubkey == nil { - fmt.Println() - fmt.Printf("The authenticity of host '%s (%s)' can't be established.\n", hostname, remote) - fmt.Printf("SSH key fingerprint is %s [MD5]\n", ssh.FingerprintLegacyMD5(key)) - fmt.Printf("Are you sure you want to continue connecting (yes/no)? ") - - for { - text, err := bufio.NewReader(os.Stdin).ReadString('\n') - switch { - case err != nil: - return err - case strings.TrimSpace(text) == "yes": - pubkey = key.Marshal() - return nil - case strings.TrimSpace(text) == "no": - return errors.New("users says no") - default: - fmt.Println("Please answer 'yes' or 'no'") - continue - } - } - } - // If a public key exists for this SSH server, check that it matches - if bytes.Equal(pubkey, key.Marshal()) { - return nil - } - // We have a mismatch, forbid connecting - return errors.New("ssh key mismatch, readd the machine to update") - } - client, err := ssh.Dial("tcp", hostport, &ssh.ClientConfig{User: username, Auth: auths, HostKeyCallback: keycheck}) - if err != nil { - return nil, err - } - // Connection established, return our utility wrapper - c := &sshClient{ - server: hostname, - address: addr[0], - pubkey: pubkey, - client: client, - logger: logger, - } - if err := c.init(); err != nil { - client.Close() - return nil, err - } - return c, nil -} - -// init runs some initialization commands on the remote server to ensure it's -// capable of acting as puppeth target. -func (client *sshClient) init() error { - client.logger.Debug("Verifying if docker is available") - if out, err := client.Run("docker version"); err != nil { - if len(out) == 0 { - return err - } - return fmt.Errorf("docker configured incorrectly: %s", out) - } - client.logger.Debug("Verifying if docker-compose is available") - if out, err := client.Run("docker-compose version"); err != nil { - if len(out) == 0 { - return err - } - return fmt.Errorf("docker-compose configured incorrectly: %s", out) - } - return nil -} - -// Close terminates the connection to an SSH server. -func (client *sshClient) Close() error { - return client.client.Close() -} - -// Run executes a command on the remote server and returns the combined output -// along with any error status. -func (client *sshClient) Run(cmd string) ([]byte, error) { - // Establish a single command session - session, err := client.client.NewSession() - if err != nil { - return nil, err - } - defer session.Close() - - // Execute the command and return any output - client.logger.Trace("Running command on remote server", "cmd", cmd) - return session.CombinedOutput(cmd) -} - -// Stream executes a command on the remote server and streams all outputs into -// the local stdout and stderr streams. -func (client *sshClient) Stream(cmd string) error { - // Establish a single command session - session, err := client.client.NewSession() - if err != nil { - return err - } - defer session.Close() - - session.Stdout = os.Stdout - session.Stderr = os.Stderr - - // Execute the command and return any output - client.logger.Trace("Streaming command on remote server", "cmd", cmd) - return session.Run(cmd) -} - -// Upload copies the set of files to a remote server via SCP, creating any non- -// existing folders in the mean time. -func (client *sshClient) Upload(files map[string][]byte) ([]byte, error) { - // Establish a single command session - session, err := client.client.NewSession() - if err != nil { - return nil, err - } - defer session.Close() - - // Create a goroutine that streams the SCP content - go func() { - out, _ := session.StdinPipe() - defer out.Close() - - for file, content := range files { - client.logger.Trace("Uploading file to server", "file", file, "bytes", len(content)) - - fmt.Fprintln(out, "D0755", 0, filepath.Dir(file)) // Ensure the folder exists - fmt.Fprintln(out, "C0644", len(content), filepath.Base(file)) // Create the actual file - out.Write(content) // Stream the data content - fmt.Fprint(out, "\x00") // Transfer end with \x00 - fmt.Fprintln(out, "E") // Leave directory (simpler) - } - }() - return session.CombinedOutput("/usr/bin/scp -v -tr ./") -}
diff --git go-ethereum/cmd/puppeth/module_faucet.go celo/cmd/puppeth/module_faucet.go deleted file mode 100644 index 7ed6b0029c8a5382a65fede9a5ed26836cb52547..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/module_faucet.go +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "html/template" - "math/rand" - "path/filepath" - "strconv" - "strings" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" -) - -// faucetDockerfile is the Dockerfile required to build a faucet container to -// grant crypto tokens based on GitHub authentications. -var faucetDockerfile = ` -FROM ethereum/client-go:alltools-latest - -ADD genesis.json /genesis.json -ADD account.json /account.json -ADD account.pass /account.pass - -EXPOSE 8080 30303 30303/udp - -ENTRYPOINT [ \ - "faucet", "--genesis", "/genesis.json", "--network", "{{.NetworkID}}", "--bootnodes", "{{.Bootnodes}}", "--ethstats", "{{.Ethstats}}", "--ethport", "{{.EthPort}}", \ - "--faucet.name", "{{.FaucetName}}", "--faucet.amount", "{{.FaucetAmount}}", "--faucet.minutes", "{{.FaucetMinutes}}", "--faucet.tiers", "{{.FaucetTiers}}", \ - "--account.json", "/account.json", "--account.pass", "/account.pass" \ - {{if .CaptchaToken}}, "--captcha.token", "{{.CaptchaToken}}", "--captcha.secret", "{{.CaptchaSecret}}"{{end}}{{if .NoAuth}}, "--noauth"{{end}} \ - {{if .TwitterToken}}, "--twitter.token.v1", "{{.TwitterToken}}"{{end}} \ -]` - -// faucetComposefile is the docker-compose.yml file required to deploy and maintain -// a crypto faucet. -var faucetComposefile = ` -version: '2' -services: - faucet: - build: . - image: {{.Network}}/faucet - container_name: {{.Network}}_faucet_1 - ports: - - "{{.EthPort}}:{{.EthPort}}" - - "{{.EthPort}}:{{.EthPort}}/udp"{{if not .VHost}} - - "{{.ApiPort}}:8080"{{end}} - volumes: - - {{.Datadir}}:/root/.faucet - environment: - - ETH_PORT={{.EthPort}} - - ETH_NAME={{.EthName}} - - FAUCET_AMOUNT={{.FaucetAmount}} - - FAUCET_MINUTES={{.FaucetMinutes}} - - FAUCET_TIERS={{.FaucetTiers}} - - CAPTCHA_TOKEN={{.CaptchaToken}} - - CAPTCHA_SECRET={{.CaptchaSecret}} - - TWITTER_TOKEN={{.TwitterToken}} - - NO_AUTH={{.NoAuth}}{{if .VHost}} - - VIRTUAL_HOST={{.VHost}} - - VIRTUAL_PORT=8080{{end}} - logging: - driver: "json-file" - options: - max-size: "1m" - max-file: "10" - restart: always -` - -// deployFaucet deploys a new faucet container to a remote machine via SSH, -// docker and docker-compose. If an instance with the specified network name -// already exists there, it will be overwritten! -func deployFaucet(client *sshClient, network string, bootnodes []string, config *faucetInfos, nocache bool) ([]byte, error) { - // Generate the content to upload to the server - workdir := fmt.Sprintf("%d", rand.Int63()) - files := make(map[string][]byte) - - dockerfile := new(bytes.Buffer) - template.Must(template.New("").Parse(faucetDockerfile)).Execute(dockerfile, map[string]interface{}{ - "NetworkID": config.node.network, - "Bootnodes": strings.Join(bootnodes, ","), - "Ethstats": config.node.ethstats, - "EthPort": config.node.port, - "CaptchaToken": config.captchaToken, - "CaptchaSecret": config.captchaSecret, - "FaucetName": strings.Title(network), - "FaucetAmount": config.amount, - "FaucetMinutes": config.minutes, - "FaucetTiers": config.tiers, - "NoAuth": config.noauth, - "TwitterToken": config.twitterToken, - }) - files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() - - composefile := new(bytes.Buffer) - template.Must(template.New("").Parse(faucetComposefile)).Execute(composefile, map[string]interface{}{ - "Network": network, - "Datadir": config.node.datadir, - "VHost": config.host, - "ApiPort": config.port, - "EthPort": config.node.port, - "EthName": config.node.ethstats[:strings.Index(config.node.ethstats, ":")], - "CaptchaToken": config.captchaToken, - "CaptchaSecret": config.captchaSecret, - "FaucetAmount": config.amount, - "FaucetMinutes": config.minutes, - "FaucetTiers": config.tiers, - "NoAuth": config.noauth, - "TwitterToken": config.twitterToken, - }) - files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() - - files[filepath.Join(workdir, "genesis.json")] = config.node.genesis - files[filepath.Join(workdir, "account.json")] = []byte(config.node.keyJSON) - files[filepath.Join(workdir, "account.pass")] = []byte(config.node.keyPass) - - // Upload the deployment files to the remote server (and clean up afterwards) - if out, err := client.Upload(files); err != nil { - return out, err - } - defer client.Run("rm -rf " + workdir) - - // Build and deploy the faucet service - if nocache { - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s build --pull --no-cache && docker-compose -p %s up -d --force-recreate --timeout 60", workdir, network, network)) - } - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network)) -} - -// faucetInfos is returned from a faucet status check to allow reporting various -// configuration parameters. -type faucetInfos struct { - node *nodeInfos - host string - port int - amount int - minutes int - tiers int - noauth bool - captchaToken string - captchaSecret string - twitterToken string -} - -// Report converts the typed struct into a plain string->string map, containing -// most - but not all - fields for reporting to the user. -func (info *faucetInfos) Report() map[string]string { - report := map[string]string{ - "Website address": info.host, - "Website listener port": strconv.Itoa(info.port), - "Ethereum listener port": strconv.Itoa(info.node.port), - "Funding amount (base tier)": fmt.Sprintf("%d Ethers", info.amount), - "Funding cooldown (base tier)": fmt.Sprintf("%d mins", info.minutes), - "Funding tiers": strconv.Itoa(info.tiers), - "Captha protection": fmt.Sprintf("%v", info.captchaToken != ""), - "Using Twitter API": fmt.Sprintf("%v", info.twitterToken != ""), - "Ethstats username": info.node.ethstats, - } - if info.noauth { - report["Debug mode (no auth)"] = "enabled" - } - if info.node.keyJSON != "" { - var key struct { - Address string `json:"address"` - } - if err := json.Unmarshal([]byte(info.node.keyJSON), &key); err == nil { - report["Funding account"] = common.HexToAddress(key.Address).Hex() - } else { - log.Error("Failed to retrieve signer address", "err", err) - } - } - return report -} - -// checkFaucet does a health-check against a faucet server to verify whether -// it's running, and if yes, gathering a collection of useful infos about it. -func checkFaucet(client *sshClient, network string) (*faucetInfos, error) { - // Inspect a possible faucet container on the host - infos, err := inspectContainer(client, fmt.Sprintf("%s_faucet_1", network)) - if err != nil { - return nil, err - } - if !infos.running { - return nil, ErrServiceOffline - } - // Resolve the port from the host, or the reverse proxy - port := infos.portmap["8080/tcp"] - if port == 0 { - if proxy, _ := checkNginx(client, network); proxy != nil { - port = proxy.port - } - } - if port == 0 { - return nil, ErrNotExposed - } - // Resolve the host from the reverse-proxy and the config values - host := infos.envvars["VIRTUAL_HOST"] - if host == "" { - host = client.server - } - amount, _ := strconv.Atoi(infos.envvars["FAUCET_AMOUNT"]) - minutes, _ := strconv.Atoi(infos.envvars["FAUCET_MINUTES"]) - tiers, _ := strconv.Atoi(infos.envvars["FAUCET_TIERS"]) - - // Retrieve the funding account information - var out []byte - keyJSON, keyPass := "", "" - if out, err = client.Run(fmt.Sprintf("docker exec %s_faucet_1 cat /account.json", network)); err == nil { - keyJSON = string(bytes.TrimSpace(out)) - } - if out, err = client.Run(fmt.Sprintf("docker exec %s_faucet_1 cat /account.pass", network)); err == nil { - keyPass = string(bytes.TrimSpace(out)) - } - // Run a sanity check to see if the port is reachable - if err = checkPort(host, port); err != nil { - log.Warn("Faucet service seems unreachable", "server", host, "port", port, "err", err) - } - // Container available, assemble and return the useful infos - return &faucetInfos{ - node: &nodeInfos{ - datadir: infos.volumes["/root/.faucet"], - port: infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"], - ethstats: infos.envvars["ETH_NAME"], - keyJSON: keyJSON, - keyPass: keyPass, - }, - host: host, - port: port, - amount: amount, - minutes: minutes, - tiers: tiers, - captchaToken: infos.envvars["CAPTCHA_TOKEN"], - captchaSecret: infos.envvars["CAPTCHA_SECRET"], - noauth: infos.envvars["NO_AUTH"] == "true", - twitterToken: infos.envvars["TWITTER_TOKEN"], - }, nil -}
diff --git go-ethereum/cmd/puppeth/wizard_explorer.go celo/cmd/puppeth/wizard_explorer.go deleted file mode 100644 index 4a0ccd51df08c189074bc321041675c39a63b325..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/wizard_explorer.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/ethereum/go-ethereum/log" -) - -// deployExplorer creates a new block explorer based on some user input. -func (w *wizard) deployExplorer() { - // Do some sanity check before the user wastes time on input - if w.conf.Genesis == nil { - log.Error("No genesis block configured") - return - } - if w.conf.ethstats == "" { - log.Error("No ethstats server configured") - return - } - // Select the server to interact with - server := w.selectServer() - if server == "" { - return - } - client := w.servers[server] - - // Retrieve any active node configurations from the server - infos, err := checkExplorer(client, w.network) - if err != nil { - infos = &explorerInfos{ - node: &nodeInfos{port: 30303}, - port: 80, - host: client.server, - } - } - existed := err == nil - - infos.node.genesis, _ = json.MarshalIndent(w.conf.Genesis, "", " ") - infos.node.network = w.conf.Genesis.Config.ChainID.Int64() - - // Figure out which port to listen on - fmt.Println() - fmt.Printf("Which port should the explorer listen on? (default = %d)\n", infos.port) - infos.port = w.readDefaultInt(infos.port) - - // Figure which virtual-host to deploy ethstats on - if infos.host, err = w.ensureVirtualHost(client, infos.port, infos.host); err != nil { - log.Error("Failed to decide on explorer host", "err", err) - return - } - // Figure out where the user wants to store the persistent data - fmt.Println() - if infos.node.datadir == "" { - fmt.Printf("Where should node data be stored on the remote machine?\n") - infos.node.datadir = w.readString() - } else { - fmt.Printf("Where should node data be stored on the remote machine? (default = %s)\n", infos.node.datadir) - infos.node.datadir = w.readDefaultString(infos.node.datadir) - } - // Figure out where the user wants to store the persistent data for backend database - fmt.Println() - if infos.dbdir == "" { - fmt.Printf("Where should postgres data be stored on the remote machine?\n") - infos.dbdir = w.readString() - } else { - fmt.Printf("Where should postgres data be stored on the remote machine? (default = %s)\n", infos.dbdir) - infos.dbdir = w.readDefaultString(infos.dbdir) - } - // Figure out which port to listen on - fmt.Println() - fmt.Printf("Which TCP/UDP port should the archive node listen on? (default = %d)\n", infos.node.port) - infos.node.port = w.readDefaultInt(infos.node.port) - - // Set a proper name to report on the stats page - fmt.Println() - if infos.node.ethstats == "" { - fmt.Printf("What should the explorer be called on the stats page?\n") - infos.node.ethstats = w.readString() + ":" + w.conf.ethstats - } else { - fmt.Printf("What should the explorer be called on the stats page? (default = %s)\n", infos.node.ethstats) - infos.node.ethstats = w.readDefaultString(infos.node.ethstats) + ":" + w.conf.ethstats - } - // Try to deploy the explorer on the host - nocache := false - if existed { - fmt.Println() - fmt.Printf("Should the explorer be built from scratch (y/n)? (default = no)\n") - nocache = w.readDefaultYesNo(false) - } - if out, err := deployExplorer(client, w.network, w.conf.bootnodes, infos, nocache, w.conf.Genesis.Config.Clique != nil); err != nil { - log.Error("Failed to deploy explorer container", "err", err) - if len(out) > 0 { - fmt.Printf("%s\n", out) - } - return - } - // All ok, run a network scan to pick any changes up - log.Info("Waiting for node to finish booting") - time.Sleep(3 * time.Second) - - w.networkStats() -}
diff --git go-ethereum/cmd/puppeth/module_node.go celo/cmd/puppeth/module_node.go deleted file mode 100644 index 9fa0f2cfbbbc0077f3a974cb3c3b833e7caf3342..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/module_node.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "math/rand" - "path/filepath" - "strconv" - "strings" - "text/template" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" -) - -// nodeDockerfile is the Dockerfile required to run an Ethereum node. -var nodeDockerfile = ` -FROM ethereum/client-go:latest - -ADD genesis.json /genesis.json -{{if .Unlock}} - ADD signer.json /signer.json - ADD signer.pass /signer.pass -{{end}} -RUN \ - echo 'geth --cache 512 init /genesis.json' > geth.sh && \{{if .Unlock}} - echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}} - echo $'exec geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --nat extip:{{.IP}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--miner.etherbase {{.Etherbase}} --mine --miner.threads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --miner.gastarget {{.GasTarget}} --miner.gaslimit {{.GasLimit}} --miner.gasprice {{.GasPrice}}' >> geth.sh - -ENTRYPOINT ["/bin/sh", "geth.sh"] -` - -// nodeComposefile is the docker-compose.yml file required to deploy and maintain -// an Ethereum node (bootnode or miner for now). -var nodeComposefile = ` -version: '2' -services: - {{.Type}}: - build: . - image: {{.Network}}/{{.Type}} - container_name: {{.Network}}_{{.Type}}_1 - ports: - - "{{.Port}}:{{.Port}}" - - "{{.Port}}:{{.Port}}/udp" - volumes: - - {{.Datadir}}:/root/.ethereum{{if .Ethashdir}} - - {{.Ethashdir}}:/root/.ethash{{end}} - environment: - - PORT={{.Port}}/tcp - - TOTAL_PEERS={{.TotalPeers}} - - LIGHT_PEERS={{.LightPeers}} - - STATS_NAME={{.Ethstats}} - - MINER_NAME={{.Etherbase}} - - GAS_TARGET={{.GasTarget}} - - GAS_LIMIT={{.GasLimit}} - - GAS_PRICE={{.GasPrice}} - logging: - driver: "json-file" - options: - max-size: "1m" - max-file: "10" - restart: always -` - -// deployNode deploys a new Ethereum node container to a remote machine via SSH, -// docker and docker-compose. If an instance with the specified network name -// already exists there, it will be overwritten! -func deployNode(client *sshClient, network string, bootnodes []string, config *nodeInfos, nocache bool) ([]byte, error) { - kind := "sealnode" - if config.keyJSON == "" && config.etherbase == "" { - kind = "bootnode" - bootnodes = make([]string, 0) - } - // Generate the content to upload to the server - workdir := fmt.Sprintf("%d", rand.Int63()) - files := make(map[string][]byte) - - lightFlag := "" - if config.peersLight > 0 { - lightFlag = fmt.Sprintf("--light.maxpeers=%d --light.serve=50", config.peersLight) - } - dockerfile := new(bytes.Buffer) - template.Must(template.New("").Parse(nodeDockerfile)).Execute(dockerfile, map[string]interface{}{ - "NetworkID": config.network, - "Port": config.port, - "IP": client.address, - "Peers": config.peersTotal, - "LightFlag": lightFlag, - "Bootnodes": strings.Join(bootnodes, ","), - "Ethstats": config.ethstats, - "Etherbase": config.etherbase, - "GasTarget": uint64(1000000 * config.gasTarget), - "GasLimit": uint64(1000000 * config.gasLimit), - "GasPrice": uint64(1000000000 * config.gasPrice), - "Unlock": config.keyJSON != "", - }) - files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() - - composefile := new(bytes.Buffer) - template.Must(template.New("").Parse(nodeComposefile)).Execute(composefile, map[string]interface{}{ - "Type": kind, - "Datadir": config.datadir, - "Ethashdir": config.ethashdir, - "Network": network, - "Port": config.port, - "TotalPeers": config.peersTotal, - "Light": config.peersLight > 0, - "LightPeers": config.peersLight, - "Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")], - "Etherbase": config.etherbase, - "GasTarget": config.gasTarget, - "GasLimit": config.gasLimit, - "GasPrice": config.gasPrice, - }) - files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() - - files[filepath.Join(workdir, "genesis.json")] = config.genesis - if config.keyJSON != "" { - files[filepath.Join(workdir, "signer.json")] = []byte(config.keyJSON) - files[filepath.Join(workdir, "signer.pass")] = []byte(config.keyPass) - } - // Upload the deployment files to the remote server (and clean up afterwards) - if out, err := client.Upload(files); err != nil { - return out, err - } - defer client.Run("rm -rf " + workdir) - - // Build and deploy the boot or seal node service - if nocache { - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s build --pull --no-cache && docker-compose -p %s up -d --force-recreate --timeout 60", workdir, network, network)) - } - return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network)) -} - -// nodeInfos is returned from a boot or seal node status check to allow reporting -// various configuration parameters. -type nodeInfos struct { - genesis []byte - network int64 - datadir string - ethashdir string - ethstats string - port int - enode string - peersTotal int - peersLight int - etherbase string - keyJSON string - keyPass string - gasTarget float64 - gasLimit float64 - gasPrice float64 -} - -// Report converts the typed struct into a plain string->string map, containing -// most - but not all - fields for reporting to the user. -func (info *nodeInfos) Report() map[string]string { - report := map[string]string{ - "Data directory": info.datadir, - "Listener port": strconv.Itoa(info.port), - "Peer count (all total)": strconv.Itoa(info.peersTotal), - "Peer count (light nodes)": strconv.Itoa(info.peersLight), - "Ethstats username": info.ethstats, - } - if info.gasTarget > 0 { - // Miner or signer node - report["Gas price (minimum accepted)"] = fmt.Sprintf("%0.3f GWei", info.gasPrice) - report["Gas floor (baseline target)"] = fmt.Sprintf("%0.3f MGas", info.gasTarget) - report["Gas ceil (target maximum)"] = fmt.Sprintf("%0.3f MGas", info.gasLimit) - - if info.etherbase != "" { - // Ethash proof-of-work miner - report["Ethash directory"] = info.ethashdir - report["Miner account"] = info.etherbase - } - if info.keyJSON != "" { - // Clique proof-of-authority signer - var key struct { - Address string `json:"address"` - } - if err := json.Unmarshal([]byte(info.keyJSON), &key); err == nil { - report["Signer account"] = common.HexToAddress(key.Address).Hex() - } else { - log.Error("Failed to retrieve signer address", "err", err) - } - } - } - return report -} - -// checkNode does a health-check against a boot or seal node server to verify -// whether it's running, and if yes, whether it's responsive. -func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error) { - kind := "bootnode" - if !boot { - kind = "sealnode" - } - // Inspect a possible bootnode container on the host - infos, err := inspectContainer(client, fmt.Sprintf("%s_%s_1", network, kind)) - if err != nil { - return nil, err - } - if !infos.running { - return nil, ErrServiceOffline - } - // Resolve a few types from the environmental variables - totalPeers, _ := strconv.Atoi(infos.envvars["TOTAL_PEERS"]) - lightPeers, _ := strconv.Atoi(infos.envvars["LIGHT_PEERS"]) - gasTarget, _ := strconv.ParseFloat(infos.envvars["GAS_TARGET"], 64) - gasLimit, _ := strconv.ParseFloat(infos.envvars["GAS_LIMIT"], 64) - gasPrice, _ := strconv.ParseFloat(infos.envvars["GAS_PRICE"], 64) - - // Container available, retrieve its node ID and its genesis json - var out []byte - if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 geth --exec admin.nodeInfo.enode --cache=16 attach", network, kind)); err != nil { - return nil, ErrServiceUnreachable - } - enode := bytes.Trim(bytes.TrimSpace(out), "\"") - - if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /genesis.json", network, kind)); err != nil { - return nil, ErrServiceUnreachable - } - genesis := bytes.TrimSpace(out) - - keyJSON, keyPass := "", "" - if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /signer.json", network, kind)); err == nil { - keyJSON = string(bytes.TrimSpace(out)) - } - if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /signer.pass", network, kind)); err == nil { - keyPass = string(bytes.TrimSpace(out)) - } - // Run a sanity check to see if the devp2p is reachable - port := infos.portmap[infos.envvars["PORT"]] - if err = checkPort(client.server, port); err != nil { - log.Warn(fmt.Sprintf("%s devp2p port seems unreachable", strings.Title(kind)), "server", client.server, "port", port, "err", err) - } - // Assemble and return the useful infos - stats := &nodeInfos{ - genesis: genesis, - datadir: infos.volumes["/root/.ethereum"], - ethashdir: infos.volumes["/root/.ethash"], - port: port, - peersTotal: totalPeers, - peersLight: lightPeers, - ethstats: infos.envvars["STATS_NAME"], - etherbase: infos.envvars["MINER_NAME"], - keyJSON: keyJSON, - keyPass: keyPass, - gasTarget: gasTarget, - gasLimit: gasLimit, - gasPrice: gasPrice, - } - stats.enode = string(enode) - - return stats, nil -}
diff --git go-ethereum/cmd/puppeth/puppeth.go celo/cmd/puppeth/puppeth.go deleted file mode 100644 index e57b6588516edc2645ce4896523a0611d9d4d8be..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/puppeth.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -// puppeth is a command to assemble and maintain private networks. -package main - -import ( - "math/rand" - "os" - "strings" - "time" - - "github.com/ethereum/go-ethereum/log" - "gopkg.in/urfave/cli.v1" -) - -// main is just a boring entry point to set up the CLI app. -func main() { - app := cli.NewApp() - app.Name = "puppeth" - app.Usage = "assemble and maintain private Ethereum networks" - app.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "network", - Usage: "name of the network to administer (no spaces or hyphens, please)", - }, - cli.IntFlag{ - Name: "loglevel", - Value: 3, - Usage: "log level to emit to the screen", - }, - } - app.Before = func(c *cli.Context) error { - // Set up the logger to print everything and the random generator - log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true)))) - rand.Seed(time.Now().UnixNano()) - - return nil - } - app.Action = runWizard - app.Run(os.Args) -} - -// runWizard start the wizard and relinquish control to it. -func runWizard(c *cli.Context) error { - network := c.String("network") - if strings.Contains(network, " ") || strings.Contains(network, "-") || strings.ToLower(network) != network { - log.Crit("No spaces, hyphens or capital letters allowed in network name") - } - makeWizard(c.String("network")).run() - return nil -}
diff --git go-ethereum/cmd/puppeth/wizard_netstats.go celo/cmd/puppeth/wizard_netstats.go deleted file mode 100644 index 680104167b295fa8b0d7379fd6f63d42e9c62a6f..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/wizard_netstats.go +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "encoding/json" - "os" - "sort" - "strings" - "sync" - - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/log" - "github.com/olekukonko/tablewriter" -) - -// networkStats verifies the status of network components and generates a protip -// configuration set to give users hints on how to do various tasks. -func (w *wizard) networkStats() { - if len(w.servers) == 0 { - log.Info("No remote machines to gather stats from") - return - } - // Clear out some previous configs to refill from current scan - w.conf.ethstats = "" - w.conf.bootnodes = w.conf.bootnodes[:0] - - // Iterate over all the specified hosts and check their status - var pend sync.WaitGroup - - stats := make(serverStats) - for server, pubkey := range w.conf.Servers { - pend.Add(1) - - // Gather the service stats for each server concurrently - go func(server string, pubkey []byte) { - defer pend.Done() - - stat := w.gatherStats(server, pubkey, w.servers[server]) - - // All status checks complete, report and check next server - w.lock.Lock() - defer w.lock.Unlock() - - delete(w.services, server) - for service := range stat.services { - w.services[server] = append(w.services[server], service) - } - stats[server] = stat - }(server, pubkey) - } - pend.Wait() - - // Print any collected stats and return - stats.render() -} - -// gatherStats gathers service statistics for a particular remote server. -func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *serverStat { - // Gather some global stats to feed into the wizard - var ( - genesis string - ethstats string - bootnodes []string - ) - // Ensure a valid SSH connection to the remote server - logger := log.New("server", server) - logger.Info("Starting remote server health-check") - - stat := &serverStat{ - services: make(map[string]map[string]string), - } - if client == nil { - conn, err := dial(server, pubkey) - if err != nil { - logger.Error("Failed to establish remote connection", "err", err) - stat.failure = err.Error() - return stat - } - client = conn - } - stat.address = client.address - - // Client connected one way or another, run health-checks - logger.Debug("Checking for nginx availability") - if infos, err := checkNginx(client, w.network); err != nil { - if err != ErrServiceUnknown { - stat.services["nginx"] = map[string]string{"offline": err.Error()} - } - } else { - stat.services["nginx"] = infos.Report() - } - logger.Debug("Checking for ethstats availability") - if infos, err := checkEthstats(client, w.network); err != nil { - if err != ErrServiceUnknown { - stat.services["ethstats"] = map[string]string{"offline": err.Error()} - } - } else { - stat.services["ethstats"] = infos.Report() - ethstats = infos.config - } - logger.Debug("Checking for bootnode availability") - if infos, err := checkNode(client, w.network, true); err != nil { - if err != ErrServiceUnknown { - stat.services["bootnode"] = map[string]string{"offline": err.Error()} - } - } else { - stat.services["bootnode"] = infos.Report() - - genesis = string(infos.genesis) - bootnodes = append(bootnodes, infos.enode) - } - logger.Debug("Checking for sealnode availability") - if infos, err := checkNode(client, w.network, false); err != nil { - if err != ErrServiceUnknown { - stat.services["sealnode"] = map[string]string{"offline": err.Error()} - } - } else { - stat.services["sealnode"] = infos.Report() - genesis = string(infos.genesis) - } - logger.Debug("Checking for explorer availability") - if infos, err := checkExplorer(client, w.network); err != nil { - if err != ErrServiceUnknown { - stat.services["explorer"] = map[string]string{"offline": err.Error()} - } - } else { - stat.services["explorer"] = infos.Report() - } - logger.Debug("Checking for faucet availability") - if infos, err := checkFaucet(client, w.network); err != nil { - if err != ErrServiceUnknown { - stat.services["faucet"] = map[string]string{"offline": err.Error()} - } - } else { - stat.services["faucet"] = infos.Report() - } - logger.Debug("Checking for dashboard availability") - if infos, err := checkDashboard(client, w.network); err != nil { - if err != ErrServiceUnknown { - stat.services["dashboard"] = map[string]string{"offline": err.Error()} - } - } else { - stat.services["dashboard"] = infos.Report() - } - // Feed and newly discovered information into the wizard - w.lock.Lock() - defer w.lock.Unlock() - - if genesis != "" && w.conf.Genesis == nil { - g := new(core.Genesis) - if err := json.Unmarshal([]byte(genesis), g); err != nil { - log.Error("Failed to parse remote genesis", "err", err) - } else { - w.conf.Genesis = g - } - } - if ethstats != "" { - w.conf.ethstats = ethstats - } - w.conf.bootnodes = append(w.conf.bootnodes, bootnodes...) - - return stat -} - -// serverStat is a collection of service configuration parameters and health -// check reports to print to the user. -type serverStat struct { - address string - failure string - services map[string]map[string]string -} - -// serverStats is a collection of server stats for multiple hosts. -type serverStats map[string]*serverStat - -// render converts the gathered statistics into a user friendly tabular report -// and prints it to the standard output. -func (stats serverStats) render() { - // Start gathering service statistics and config parameters - table := tablewriter.NewWriter(os.Stdout) - - table.SetHeader([]string{"Server", "Address", "Service", "Config", "Value"}) - table.SetAlignment(tablewriter.ALIGN_LEFT) - table.SetColWidth(40) - - // Find the longest lines for all columns for the hacked separator - separator := make([]string, 5) - for server, stat := range stats { - if len(server) > len(separator[0]) { - separator[0] = strings.Repeat("-", len(server)) - } - if len(stat.address) > len(separator[1]) { - separator[1] = strings.Repeat("-", len(stat.address)) - } - if len(stat.failure) > len(separator[1]) { - separator[1] = strings.Repeat("-", len(stat.failure)) - } - for service, configs := range stat.services { - if len(service) > len(separator[2]) { - separator[2] = strings.Repeat("-", len(service)) - } - for config, value := range configs { - if len(config) > len(separator[3]) { - separator[3] = strings.Repeat("-", len(config)) - } - for _, val := range strings.Split(value, "\n") { - if len(val) > len(separator[4]) { - separator[4] = strings.Repeat("-", len(val)) - } - } - } - } - } - // Fill up the server report in alphabetical order - servers := make([]string, 0, len(stats)) - for server := range stats { - servers = append(servers, server) - } - sort.Strings(servers) - - for i, server := range servers { - // Add a separator between all servers - if i > 0 { - table.Append(separator) - } - // Fill up the service report in alphabetical order - services := make([]string, 0, len(stats[server].services)) - for service := range stats[server].services { - services = append(services, service) - } - sort.Strings(services) - - if len(services) == 0 { - if stats[server].failure != "" { - table.Append([]string{server, stats[server].failure, "", "", ""}) - } else { - table.Append([]string{server, stats[server].address, "", "", ""}) - } - } - for j, service := range services { - // Add an empty line between all services - if j > 0 { - table.Append([]string{"", "", "", separator[3], separator[4]}) - } - // Fill up the config report in alphabetical order - configs := make([]string, 0, len(stats[server].services[service])) - for service := range stats[server].services[service] { - configs = append(configs, service) - } - sort.Strings(configs) - - for k, config := range configs { - for l, value := range strings.Split(stats[server].services[service][config], "\n") { - switch { - case j == 0 && k == 0 && l == 0: - table.Append([]string{server, stats[server].address, service, config, value}) - case k == 0 && l == 0: - table.Append([]string{"", "", service, config, value}) - case l == 0: - table.Append([]string{"", "", "", config, value}) - default: - table.Append([]string{"", "", "", "", value}) - } - } - } - } - } - table.Render() -}
diff --git go-ethereum/cmd/puppeth/wizard_network.go celo/cmd/puppeth/wizard_network.go deleted file mode 100644 index 2348f03d2aa45897fcee51726c21024a5c441f7f..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/wizard_network.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "fmt" - "strings" - - "github.com/ethereum/go-ethereum/log" -) - -// manageServers displays a list of servers the user can disconnect from, and an -// option to connect to new servers. -func (w *wizard) manageServers() { - // List all the servers we can disconnect, along with an entry to connect a new one - fmt.Println() - - servers := w.conf.servers() - for i, server := range servers { - fmt.Printf(" %d. Disconnect %s\n", i+1, server) - } - fmt.Printf(" %d. Connect another server\n", len(w.conf.Servers)+1) - - choice := w.readInt() - if choice < 0 || choice > len(w.conf.Servers)+1 { - log.Error("Invalid server choice, aborting") - return - } - // If the user selected an existing server, drop it - if choice <= len(w.conf.Servers) { - server := servers[choice-1] - client := w.servers[server] - - delete(w.servers, server) - if client != nil { - client.Close() - } - delete(w.conf.Servers, server) - w.conf.flush() - - log.Info("Disconnected existing server", "server", server) - w.networkStats() - return - } - // If the user requested connecting a new server, do it - if w.makeServer() != "" { - w.networkStats() - } -} - -// makeServer reads a single line from stdin and interprets it as -// username:identity@hostname to connect to. It tries to establish a -// new SSH session and also executing some baseline validations. -// -// If connection succeeds, the server is added to the wizards configs! -func (w *wizard) makeServer() string { - fmt.Println() - fmt.Println("What is the remote server's address ([username[:identity]@]hostname[:port])?") - - // Read and dial the server to ensure docker is present - input := w.readString() - - client, err := dial(input, nil) - if err != nil { - log.Error("Server not ready for puppeth", "err", err) - return "" - } - // All checks passed, start tracking the server - w.servers[input] = client - w.conf.Servers[input] = client.pubkey - w.conf.flush() - - return input -} - -// selectServer lists the user all the currently known servers to choose from, -// also granting the option to add a new one. -func (w *wizard) selectServer() string { - // List the available server to the user and wait for a choice - fmt.Println() - fmt.Println("Which server do you want to interact with?") - - servers := w.conf.servers() - for i, server := range servers { - fmt.Printf(" %d. %s\n", i+1, server) - } - fmt.Printf(" %d. Connect another server\n", len(w.conf.Servers)+1) - - choice := w.readInt() - if choice < 0 || choice > len(w.conf.Servers)+1 { - log.Error("Invalid server choice, aborting") - return "" - } - // If the user requested connecting to a new server, go for it - if choice <= len(w.conf.Servers) { - return servers[choice-1] - } - return w.makeServer() -} - -// manageComponents displays a list of network components the user can tear down -// and an option -func (w *wizard) manageComponents() { - // List all the components we can tear down, along with an entry to deploy a new one - fmt.Println() - - var serviceHosts, serviceNames []string - for server, services := range w.services { - for _, service := range services { - serviceHosts = append(serviceHosts, server) - serviceNames = append(serviceNames, service) - - fmt.Printf(" %d. Tear down %s on %s\n", len(serviceHosts), strings.Title(service), server) - } - } - fmt.Printf(" %d. Deploy new network component\n", len(serviceHosts)+1) - - choice := w.readInt() - if choice < 0 || choice > len(serviceHosts)+1 { - log.Error("Invalid component choice, aborting") - return - } - // If the user selected an existing service, destroy it - if choice <= len(serviceHosts) { - // Figure out the service to destroy and execute it - service := serviceNames[choice-1] - server := serviceHosts[choice-1] - client := w.servers[server] - - if out, err := tearDown(client, w.network, service, true); err != nil { - log.Error("Failed to tear down component", "err", err) - if len(out) > 0 { - fmt.Printf("%s\n", out) - } - return - } - // Clean up any references to it from out state - services := w.services[server] - for i, name := range services { - if name == service { - w.services[server] = append(services[:i], services[i+1:]...) - if len(w.services[server]) == 0 { - delete(w.services, server) - } - } - } - log.Info("Torn down existing component", "server", server, "service", service) - return - } - // If the user requested deploying a new component, do it - w.deployComponent() -} - -// deployComponent displays a list of network components the user can deploy and -// guides through the process. -func (w *wizard) deployComponent() { - // Print all the things we can deploy and wait or user choice - fmt.Println() - fmt.Println("What would you like to deploy? (recommended order)") - fmt.Println(" 1. Ethstats - Network monitoring tool") - fmt.Println(" 2. Bootnode - Entry point of the network") - fmt.Println(" 3. Sealer - Full node minting new blocks") - fmt.Println(" 4. Explorer - Chain analysis webservice") - fmt.Println(" 5. Faucet - Crypto faucet to give away funds") - fmt.Println(" 6. Dashboard - Website listing above web-services") - - switch w.read() { - case "1": - w.deployEthstats() - case "2": - w.deployNode(true) - case "3": - w.deployNode(false) - case "4": - w.deployExplorer() - case "5": - w.deployFaucet() - case "6": - w.deployDashboard() - default: - log.Error("That's not something I can do") - } -}
diff --git go-ethereum/cmd/puppeth/wizard_intro.go celo/cmd/puppeth/wizard_intro.go deleted file mode 100644 index d3902507bee50770f1b6892b6ae6d32bf24293d2..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/wizard_intro.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - "sync" - - "github.com/ethereum/go-ethereum/log" -) - -// makeWizard creates and returns a new puppeth wizard. -func makeWizard(network string) *wizard { - return &wizard{ - network: network, - conf: config{ - Servers: make(map[string][]byte), - }, - servers: make(map[string]*sshClient), - services: make(map[string][]string), - in: bufio.NewReader(os.Stdin), - } -} - -// run displays some useful infos to the user, starting on the journey of -// setting up a new or managing an existing Ethereum private network. -func (w *wizard) run() { - fmt.Println("+-----------------------------------------------------------+") - fmt.Println("| Welcome to puppeth, your Ethereum private network manager |") - fmt.Println("| |") - fmt.Println("| This tool lets you create a new Ethereum network down to |") - fmt.Println("| the genesis block, bootnodes, miners and ethstats servers |") - fmt.Println("| without the hassle that it would normally entail. |") - fmt.Println("| |") - fmt.Println("| Puppeth uses SSH to dial in to remote servers, and builds |") - fmt.Println("| its network components out of Docker containers using the |") - fmt.Println("| docker-compose toolset. |") - fmt.Println("+-----------------------------------------------------------+") - fmt.Println() - - // Make sure we have a good network name to work with fmt.Println() - // Docker accepts hyphens in image names, but doesn't like it for container names - if w.network == "" { - fmt.Println("Please specify a network name to administer (no spaces, hyphens or capital letters please)") - for { - w.network = w.readString() - if !strings.Contains(w.network, " ") && !strings.Contains(w.network, "-") && strings.ToLower(w.network) == w.network { - fmt.Printf("\nSweet, you can set this via --network=%s next time!\n\n", w.network) - break - } - log.Error("I also like to live dangerously, still no spaces, hyphens or capital letters") - } - } - log.Info("Administering Ethereum network", "name", w.network) - - // Load initial configurations and connect to all live servers - w.conf.path = filepath.Join(os.Getenv("HOME"), ".puppeth", w.network) - - blob, err := ioutil.ReadFile(w.conf.path) - if err != nil { - log.Warn("No previous configurations found", "path", w.conf.path) - } else if err := json.Unmarshal(blob, &w.conf); err != nil { - log.Crit("Previous configuration corrupted", "path", w.conf.path, "err", err) - } else { - // Dial all previously known servers concurrently - var pend sync.WaitGroup - for server, pubkey := range w.conf.Servers { - pend.Add(1) - - go func(server string, pubkey []byte) { - defer pend.Done() - - log.Info("Dialing previously configured server", "server", server) - client, err := dial(server, pubkey) - if err != nil { - log.Error("Previous server unreachable", "server", server, "err", err) - } - w.lock.Lock() - w.servers[server] = client - w.lock.Unlock() - }(server, pubkey) - } - pend.Wait() - w.networkStats() - } - // Basics done, loop ad infinitum about what to do - for { - fmt.Println() - fmt.Println("What would you like to do? (default = stats)") - fmt.Println(" 1. Show network stats") - if w.conf.Genesis == nil { - fmt.Println(" 2. Configure new genesis") - } else { - fmt.Println(" 2. Manage existing genesis") - } - if len(w.servers) == 0 { - fmt.Println(" 3. Track new remote server") - } else { - fmt.Println(" 3. Manage tracked machines") - } - if len(w.services) == 0 { - fmt.Println(" 4. Deploy network components") - } else { - fmt.Println(" 4. Manage network components") - } - - choice := w.read() - switch { - case choice == "" || choice == "1": - w.networkStats() - - case choice == "2": - if w.conf.Genesis == nil { - fmt.Println() - fmt.Println("What would you like to do? (default = create)") - fmt.Println(" 1. Create new genesis from scratch") - fmt.Println(" 2. Import already existing genesis") - - choice := w.read() - switch { - case choice == "" || choice == "1": - w.makeGenesis() - case choice == "2": - w.importGenesis() - default: - log.Error("That's not something I can do") - } - } else { - w.manageGenesis() - } - case choice == "3": - if len(w.servers) == 0 { - if w.makeServer() != "" { - w.networkStats() - } - } else { - w.manageServers() - } - case choice == "4": - if len(w.services) == 0 { - w.deployComponent() - } else { - w.manageComponents() - } - default: - log.Error("That's not something I can do") - } - } -}
diff --git go-ethereum/cmd/puppeth/wizard_genesis.go celo/cmd/puppeth/wizard_genesis.go deleted file mode 100644 index e28ace08924c78e1e52bb66f6019f8f0947ea194..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/wizard_genesis.go +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "math/big" - "math/rand" - "net/http" - "os" - "path/filepath" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" -) - -// makeGenesis creates a new genesis struct based on some user input. -func (w *wizard) makeGenesis() { - // Construct a default genesis block - genesis := &core.Genesis{ - Timestamp: uint64(time.Now().Unix()), - GasLimit: 4700000, - Difficulty: big.NewInt(524288), - Alloc: make(core.GenesisAlloc), - Config: &params.ChainConfig{ - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - }, - } - // Figure out which consensus engine to choose - fmt.Println() - fmt.Println("Which consensus engine to use? (default = clique)") - fmt.Println(" 1. Ethash - proof-of-work") - fmt.Println(" 2. Clique - proof-of-authority") - - choice := w.read() - switch { - case choice == "1": - // In case of ethash, we're pretty much done - genesis.Config.Ethash = new(params.EthashConfig) - genesis.ExtraData = make([]byte, 32) - - case choice == "" || choice == "2": - // In the case of clique, configure the consensus parameters - genesis.Difficulty = big.NewInt(1) - genesis.Config.Clique = &params.CliqueConfig{ - Period: 15, - Epoch: 30000, - } - fmt.Println() - fmt.Println("How many seconds should blocks take? (default = 15)") - genesis.Config.Clique.Period = uint64(w.readDefaultInt(15)) - - // We also need the initial list of signers - fmt.Println() - fmt.Println("Which accounts are allowed to seal? (mandatory at least one)") - - var signers []common.Address - for { - if address := w.readAddress(); address != nil { - signers = append(signers, *address) - continue - } - if len(signers) > 0 { - break - } - } - // Sort the signers and embed into the extra-data section - for i := 0; i < len(signers); i++ { - for j := i + 1; j < len(signers); j++ { - if bytes.Compare(signers[i][:], signers[j][:]) > 0 { - signers[i], signers[j] = signers[j], signers[i] - } - } - } - genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65) - for i, signer := range signers { - copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:]) - } - - default: - log.Crit("Invalid consensus engine choice", "choice", choice) - } - // Consensus all set, just ask for initial funds and go - fmt.Println() - fmt.Println("Which accounts should be pre-funded? (advisable at least one)") - for { - // Read the address of the account to fund - if address := w.readAddress(); address != nil { - genesis.Alloc[*address] = core.GenesisAccount{ - Balance: new(big.Int).Lsh(big.NewInt(1), 256-7), // 2^256 / 128 (allow many pre-funds without balance overflows) - } - continue - } - break - } - fmt.Println() - fmt.Println("Should the precompile-addresses (0x1 .. 0xff) be pre-funded with 1 wei? (advisable yes)") - if w.readDefaultYesNo(true) { - // Add a batch of precompile balances to avoid them getting deleted - for i := int64(0); i < 256; i++ { - genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)} - } - } - // Query the user for some custom extras - fmt.Println() - fmt.Println("Specify your chain/network ID if you want an explicit one (default = random)") - genesis.Config.ChainID = new(big.Int).SetUint64(uint64(w.readDefaultInt(rand.Intn(65536)))) - - // All done, store the genesis and flush to disk - log.Info("Configured new genesis block") - - w.conf.Genesis = genesis - w.conf.flush() -} - -// importGenesis imports a Geth genesis spec into puppeth. -func (w *wizard) importGenesis() { - // Request the genesis JSON spec URL from the user - fmt.Println() - fmt.Println("Where's the genesis file? (local file or http/https url)") - url := w.readURL() - - // Convert the various allowed URLs to a reader stream - var reader io.Reader - - switch url.Scheme { - case "http", "https": - // Remote web URL, retrieve it via an HTTP client - res, err := http.Get(url.String()) - if err != nil { - log.Error("Failed to retrieve remote genesis", "err", err) - return - } - defer res.Body.Close() - reader = res.Body - - case "": - // Schemaless URL, interpret as a local file - file, err := os.Open(url.String()) - if err != nil { - log.Error("Failed to open local genesis", "err", err) - return - } - defer file.Close() - reader = file - - default: - log.Error("Unsupported genesis URL scheme", "scheme", url.Scheme) - return - } - // Parse the genesis file and inject it successful - var genesis core.Genesis - if err := json.NewDecoder(reader).Decode(&genesis); err != nil { - log.Error("Invalid genesis spec", "err", err) - return - } - log.Info("Imported genesis block") - - w.conf.Genesis = &genesis - w.conf.flush() -} - -// manageGenesis permits the modification of chain configuration parameters in -// a genesis config and the export of the entire genesis spec. -func (w *wizard) manageGenesis() { - // Figure out whether to modify or export the genesis - fmt.Println() - fmt.Println(" 1. Modify existing configurations") - fmt.Println(" 2. Export genesis configurations") - fmt.Println(" 3. Remove genesis configuration") - - choice := w.read() - switch choice { - case "1": - // Fork rule updating requested, iterate over each fork - fmt.Println() - fmt.Printf("Which block should Homestead come into effect? (default = %v)\n", w.conf.Genesis.Config.HomesteadBlock) - w.conf.Genesis.Config.HomesteadBlock = w.readDefaultBigInt(w.conf.Genesis.Config.HomesteadBlock) - - fmt.Println() - fmt.Printf("Which block should EIP150 (Tangerine Whistle) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP150Block) - w.conf.Genesis.Config.EIP150Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP150Block) - - fmt.Println() - fmt.Printf("Which block should EIP155 (Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP155Block) - w.conf.Genesis.Config.EIP155Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP155Block) - - fmt.Println() - fmt.Printf("Which block should EIP158/161 (also Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP158Block) - w.conf.Genesis.Config.EIP158Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP158Block) - - fmt.Println() - fmt.Printf("Which block should Byzantium come into effect? (default = %v)\n", w.conf.Genesis.Config.ByzantiumBlock) - w.conf.Genesis.Config.ByzantiumBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ByzantiumBlock) - - fmt.Println() - fmt.Printf("Which block should Constantinople come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock) - w.conf.Genesis.Config.ConstantinopleBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock) - if w.conf.Genesis.Config.PetersburgBlock == nil { - w.conf.Genesis.Config.PetersburgBlock = w.conf.Genesis.Config.ConstantinopleBlock - } - fmt.Println() - fmt.Printf("Which block should Petersburg come into effect? (default = %v)\n", w.conf.Genesis.Config.PetersburgBlock) - w.conf.Genesis.Config.PetersburgBlock = w.readDefaultBigInt(w.conf.Genesis.Config.PetersburgBlock) - - fmt.Println() - fmt.Printf("Which block should Istanbul come into effect? (default = %v)\n", w.conf.Genesis.Config.IstanbulBlock) - w.conf.Genesis.Config.IstanbulBlock = w.readDefaultBigInt(w.conf.Genesis.Config.IstanbulBlock) - - fmt.Println() - fmt.Printf("Which block should Berlin come into effect? (default = %v)\n", w.conf.Genesis.Config.BerlinBlock) - w.conf.Genesis.Config.BerlinBlock = w.readDefaultBigInt(w.conf.Genesis.Config.BerlinBlock) - - fmt.Println() - fmt.Printf("Which block should London come into effect? (default = %v)\n", w.conf.Genesis.Config.LondonBlock) - w.conf.Genesis.Config.LondonBlock = w.readDefaultBigInt(w.conf.Genesis.Config.LondonBlock) - - out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ") - fmt.Printf("Chain configuration updated:\n\n%s\n", out) - - w.conf.flush() - - case "2": - // Save whatever genesis configuration we currently have - fmt.Println() - fmt.Printf("Which folder to save the genesis specs into? (default = current)\n") - fmt.Printf(" Will create %s.json, %s-aleth.json, %s-harmony.json, %s-parity.json\n", w.network, w.network, w.network, w.network) - - folder := w.readDefaultString(".") - if err := os.MkdirAll(folder, 0755); err != nil { - log.Error("Failed to create spec folder", "folder", folder, "err", err) - return - } - out, _ := json.MarshalIndent(w.conf.Genesis, "", " ") - - // Export the native genesis spec used by puppeth and Geth - gethJson := filepath.Join(folder, fmt.Sprintf("%s.json", w.network)) - if err := ioutil.WriteFile(gethJson, out, 0644); err != nil { - log.Error("Failed to save genesis file", "err", err) - return - } - log.Info("Saved native genesis chain spec", "path", gethJson) - - // Export the genesis spec used by Aleth (formerly C++ Ethereum) - if spec, err := newAlethGenesisSpec(w.network, w.conf.Genesis); err != nil { - log.Error("Failed to create Aleth chain spec", "err", err) - } else { - saveGenesis(folder, w.network, "aleth", spec) - } - // Export the genesis spec used by Parity - if spec, err := newParityChainSpec(w.network, w.conf.Genesis, []string{}); err != nil { - log.Error("Failed to create Parity chain spec", "err", err) - } else { - saveGenesis(folder, w.network, "parity", spec) - } - // Export the genesis spec used by Harmony (formerly EthereumJ) - saveGenesis(folder, w.network, "harmony", w.conf.Genesis) - - case "3": - // Make sure we don't have any services running - if len(w.conf.servers()) > 0 { - log.Error("Genesis reset requires all services and servers torn down") - return - } - log.Info("Genesis block destroyed") - - w.conf.Genesis = nil - w.conf.flush() - default: - log.Error("That's not something I can do") - return - } -} - -// saveGenesis JSON encodes an arbitrary genesis spec into a pre-defined file. -func saveGenesis(folder, network, client string, spec interface{}) { - path := filepath.Join(folder, fmt.Sprintf("%s-%s.json", network, client)) - - out, _ := json.MarshalIndent(spec, "", " ") - if err := ioutil.WriteFile(path, out, 0644); err != nil { - log.Error("Failed to save genesis file", "client", client, "err", err) - return - } - log.Info("Saved genesis chain spec", "client", client, "path", path) -}
diff --git go-ethereum/cmd/puppeth/wizard_nginx.go celo/cmd/puppeth/wizard_nginx.go deleted file mode 100644 index 8397b7fd57ff3edc3b142627ef402e5201c40bcc..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/wizard_nginx.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/log" -) - -// ensureVirtualHost checks whether a reverse-proxy is running on the specified -// host machine, and if yes requests a virtual host from the user to host a -// specific web service on. If no proxy exists, the method will offer to deploy -// one. -// -// If the user elects not to use a reverse proxy, an empty hostname is returned! -func (w *wizard) ensureVirtualHost(client *sshClient, port int, def string) (string, error) { - proxy, _ := checkNginx(client, w.network) - if proxy != nil { - // Reverse proxy is running, if ports match, we need a virtual host - if proxy.port == port { - fmt.Println() - fmt.Printf("Shared port, which domain to assign? (default = %s)\n", def) - return w.readDefaultString(def), nil - } - } - // Reverse proxy is not running, offer to deploy a new one - fmt.Println() - fmt.Println("Allow sharing the port with other services (y/n)? (default = yes)") - if w.readDefaultYesNo(true) { - nocache := false - if proxy != nil { - fmt.Println() - fmt.Printf("Should the reverse-proxy be rebuilt from scratch (y/n)? (default = no)\n") - nocache = w.readDefaultYesNo(false) - } - if out, err := deployNginx(client, w.network, port, nocache); err != nil { - log.Error("Failed to deploy reverse-proxy", "err", err) - if len(out) > 0 { - fmt.Printf("%s\n", out) - } - return "", err - } - // Reverse proxy deployed, ask again for the virtual-host - fmt.Println() - fmt.Printf("Proxy deployed, which domain to assign? (default = %s)\n", def) - return w.readDefaultString(def), nil - } - // Reverse proxy not requested, deploy as a standalone service - return "", nil -}
diff --git go-ethereum/cmd/puppeth/wizard_node.go celo/cmd/puppeth/wizard_node.go deleted file mode 100644 index d20f16fb44b7a15abf6a3b619927227cde9a2634..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/wizard_node.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" -) - -// deployNode creates a new node configuration based on some user input. -func (w *wizard) deployNode(boot bool) { - // Do some sanity check before the user wastes time on input - if w.conf.Genesis == nil { - log.Error("No genesis block configured") - return - } - if w.conf.ethstats == "" { - log.Error("No ethstats server configured") - return - } - // Select the server to interact with - server := w.selectServer() - if server == "" { - return - } - client := w.servers[server] - - // Retrieve any active node configurations from the server - infos, err := checkNode(client, w.network, boot) - if err != nil { - if boot { - infos = &nodeInfos{port: 30303, peersTotal: 512, peersLight: 256} - } else { - infos = &nodeInfos{port: 30303, peersTotal: 50, peersLight: 0, gasTarget: 7.5, gasLimit: 10, gasPrice: 1} - } - } - existed := err == nil - - infos.genesis, _ = json.MarshalIndent(w.conf.Genesis, "", " ") - infos.network = w.conf.Genesis.Config.ChainID.Int64() - - // Figure out where the user wants to store the persistent data - fmt.Println() - if infos.datadir == "" { - fmt.Printf("Where should data be stored on the remote machine?\n") - infos.datadir = w.readString() - } else { - fmt.Printf("Where should data be stored on the remote machine? (default = %s)\n", infos.datadir) - infos.datadir = w.readDefaultString(infos.datadir) - } - if w.conf.Genesis.Config.Ethash != nil && !boot { - fmt.Println() - if infos.ethashdir == "" { - fmt.Printf("Where should the ethash mining DAGs be stored on the remote machine?\n") - infos.ethashdir = w.readString() - } else { - fmt.Printf("Where should the ethash mining DAGs be stored on the remote machine? (default = %s)\n", infos.ethashdir) - infos.ethashdir = w.readDefaultString(infos.ethashdir) - } - } - // Figure out which port to listen on - fmt.Println() - fmt.Printf("Which TCP/UDP port to listen on? (default = %d)\n", infos.port) - infos.port = w.readDefaultInt(infos.port) - - // Figure out how many peers to allow (different based on node type) - fmt.Println() - fmt.Printf("How many peers to allow connecting? (default = %d)\n", infos.peersTotal) - infos.peersTotal = w.readDefaultInt(infos.peersTotal) - - // Figure out how many light peers to allow (different based on node type) - fmt.Println() - fmt.Printf("How many light peers to allow connecting? (default = %d)\n", infos.peersLight) - infos.peersLight = w.readDefaultInt(infos.peersLight) - - // Set a proper name to report on the stats page - fmt.Println() - if infos.ethstats == "" { - fmt.Printf("What should the node be called on the stats page?\n") - infos.ethstats = w.readString() + ":" + w.conf.ethstats - } else { - fmt.Printf("What should the node be called on the stats page? (default = %s)\n", infos.ethstats) - infos.ethstats = w.readDefaultString(infos.ethstats) + ":" + w.conf.ethstats - } - // If the node is a miner/signer, load up needed credentials - if !boot { - if w.conf.Genesis.Config.Ethash != nil { - // Ethash based miners only need an etherbase to mine against - fmt.Println() - if infos.etherbase == "" { - fmt.Printf("What address should the miner use?\n") - for { - if address := w.readAddress(); address != nil { - infos.etherbase = address.Hex() - break - } - } - } else { - fmt.Printf("What address should the miner use? (default = %s)\n", infos.etherbase) - infos.etherbase = w.readDefaultAddress(common.HexToAddress(infos.etherbase)).Hex() - } - } else if w.conf.Genesis.Config.Clique != nil { - // If a previous signer was already set, offer to reuse it - if infos.keyJSON != "" { - if key, err := keystore.DecryptKey([]byte(infos.keyJSON), infos.keyPass); err != nil { - infos.keyJSON, infos.keyPass = "", "" - } else { - fmt.Println() - fmt.Printf("Reuse previous (%s) signing account (y/n)? (default = yes)\n", key.Address.Hex()) - if !w.readDefaultYesNo(true) { - infos.keyJSON, infos.keyPass = "", "" - } - } - } - // Clique based signers need a keyfile and unlock password, ask if unavailable - if infos.keyJSON == "" { - fmt.Println() - fmt.Println("Please paste the signer's key JSON:") - infos.keyJSON = w.readJSON() - - fmt.Println() - fmt.Println("What's the unlock password for the account? (won't be echoed)") - infos.keyPass = w.readPassword() - - if _, err := keystore.DecryptKey([]byte(infos.keyJSON), infos.keyPass); err != nil { - log.Error("Failed to decrypt key with given password") - return - } - } - } - // Establish the gas dynamics to be enforced by the signer - fmt.Println() - fmt.Printf("What gas limit should empty blocks target (MGas)? (default = %0.3f)\n", infos.gasTarget) - infos.gasTarget = w.readDefaultFloat(infos.gasTarget) - - fmt.Println() - fmt.Printf("What gas limit should full blocks target (MGas)? (default = %0.3f)\n", infos.gasLimit) - infos.gasLimit = w.readDefaultFloat(infos.gasLimit) - - fmt.Println() - fmt.Printf("What gas price should the signer require (GWei)? (default = %0.3f)\n", infos.gasPrice) - infos.gasPrice = w.readDefaultFloat(infos.gasPrice) - } - // Try to deploy the full node on the host - nocache := false - if existed { - fmt.Println() - fmt.Printf("Should the node be built from scratch (y/n)? (default = no)\n") - nocache = w.readDefaultYesNo(false) - } - if out, err := deployNode(client, w.network, w.conf.bootnodes, infos, nocache); err != nil { - log.Error("Failed to deploy Ethereum node container", "err", err) - if len(out) > 0 { - fmt.Printf("%s\n", out) - } - return - } - // All ok, run a network scan to pick any changes up - log.Info("Waiting for node to finish booting") - time.Sleep(3 * time.Second) - - w.networkStats() -}
diff --git go-ethereum/cmd/puppeth/genesis.go celo/cmd/puppeth/genesis.go deleted file mode 100644 index ee0aea9b31735e4b39930268a8ecb8b0ba53823d..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/genesis.go +++ /dev/null @@ -1,626 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "errors" - "math" - "math/big" - "strings" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - math2 "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" -) - -// alethGenesisSpec represents the genesis specification format used by the -// C++ Ethereum implementation. -type alethGenesisSpec struct { - SealEngine string `json:"sealEngine"` - Params struct { - AccountStartNonce math2.HexOrDecimal64 `json:"accountStartNonce"` - MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"` - HomesteadForkBlock *hexutil.Big `json:"homesteadForkBlock,omitempty"` - DaoHardforkBlock math2.HexOrDecimal64 `json:"daoHardforkBlock"` - EIP150ForkBlock *hexutil.Big `json:"EIP150ForkBlock,omitempty"` - EIP158ForkBlock *hexutil.Big `json:"EIP158ForkBlock,omitempty"` - ByzantiumForkBlock *hexutil.Big `json:"byzantiumForkBlock,omitempty"` - ConstantinopleForkBlock *hexutil.Big `json:"constantinopleForkBlock,omitempty"` - ConstantinopleFixForkBlock *hexutil.Big `json:"constantinopleFixForkBlock,omitempty"` - IstanbulForkBlock *hexutil.Big `json:"istanbulForkBlock,omitempty"` - MinGasLimit hexutil.Uint64 `json:"minGasLimit"` - MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"` - TieBreakingGas bool `json:"tieBreakingGas"` - GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"` - MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"` - DifficultyBoundDivisor *math2.HexOrDecimal256 `json:"difficultyBoundDivisor"` - DurationLimit *math2.HexOrDecimal256 `json:"durationLimit"` - BlockReward *hexutil.Big `json:"blockReward"` - NetworkID hexutil.Uint64 `json:"networkID"` - ChainID hexutil.Uint64 `json:"chainID"` - AllowFutureBlocks bool `json:"allowFutureBlocks"` - } `json:"params"` - - Genesis struct { - Nonce types.BlockNonce `json:"nonce"` - Difficulty *hexutil.Big `json:"difficulty"` - MixHash common.Hash `json:"mixHash"` - Author common.Address `json:"author"` - Timestamp hexutil.Uint64 `json:"timestamp"` - ParentHash common.Hash `json:"parentHash"` - ExtraData hexutil.Bytes `json:"extraData"` - GasLimit hexutil.Uint64 `json:"gasLimit"` - } `json:"genesis"` - - Accounts map[common.UnprefixedAddress]*alethGenesisSpecAccount `json:"accounts"` -} - -// alethGenesisSpecAccount is the prefunded genesis account and/or precompiled -// contract definition. -type alethGenesisSpecAccount struct { - Balance *math2.HexOrDecimal256 `json:"balance,omitempty"` - Nonce uint64 `json:"nonce,omitempty"` - Precompiled *alethGenesisSpecBuiltin `json:"precompiled,omitempty"` -} - -// alethGenesisSpecBuiltin is the precompiled contract definition. -type alethGenesisSpecBuiltin struct { - Name string `json:"name,omitempty"` - StartingBlock *hexutil.Big `json:"startingBlock,omitempty"` - Linear *alethGenesisSpecLinearPricing `json:"linear,omitempty"` -} - -type alethGenesisSpecLinearPricing struct { - Base uint64 `json:"base"` - Word uint64 `json:"word"` -} - -// newAlethGenesisSpec converts a go-ethereum genesis block into a Aleth-specific -// chain specification format. -func newAlethGenesisSpec(network string, genesis *core.Genesis) (*alethGenesisSpec, error) { - // Only ethash is currently supported between go-ethereum and aleth - if genesis.Config.Ethash == nil { - return nil, errors.New("unsupported consensus engine") - } - // Reconstruct the chain spec in Aleth format - spec := &alethGenesisSpec{ - SealEngine: "Ethash", - } - // Some defaults - spec.Params.AccountStartNonce = 0 - spec.Params.TieBreakingGas = false - spec.Params.AllowFutureBlocks = false - - // Dao hardfork block is a special one. The fork block is listed as 0 in the - // config but aleth will sync with ETC clients up until the actual dao hard - // fork block. - spec.Params.DaoHardforkBlock = 0 - - if num := genesis.Config.HomesteadBlock; num != nil { - spec.Params.HomesteadForkBlock = (*hexutil.Big)(num) - } - if num := genesis.Config.EIP150Block; num != nil { - spec.Params.EIP150ForkBlock = (*hexutil.Big)(num) - } - if num := genesis.Config.EIP158Block; num != nil { - spec.Params.EIP158ForkBlock = (*hexutil.Big)(num) - } - if num := genesis.Config.ByzantiumBlock; num != nil { - spec.Params.ByzantiumForkBlock = (*hexutil.Big)(num) - } - if num := genesis.Config.ConstantinopleBlock; num != nil { - spec.Params.ConstantinopleForkBlock = (*hexutil.Big)(num) - } - if num := genesis.Config.PetersburgBlock; num != nil { - spec.Params.ConstantinopleFixForkBlock = (*hexutil.Big)(num) - } - if num := genesis.Config.IstanbulBlock; num != nil { - spec.Params.IstanbulForkBlock = (*hexutil.Big)(num) - } - spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64()) - spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64()) - spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize) - spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit) - spec.Params.MaxGasLimit = (hexutil.Uint64)(math.MaxInt64) - spec.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty) - spec.Params.DifficultyBoundDivisor = (*math2.HexOrDecimal256)(params.DifficultyBoundDivisor) - spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor) - spec.Params.DurationLimit = (*math2.HexOrDecimal256)(params.DurationLimit) - spec.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward) - - spec.Genesis.Nonce = types.EncodeNonce(genesis.Nonce) - spec.Genesis.MixHash = genesis.Mixhash - spec.Genesis.Difficulty = (*hexutil.Big)(genesis.Difficulty) - spec.Genesis.Author = genesis.Coinbase - spec.Genesis.Timestamp = (hexutil.Uint64)(genesis.Timestamp) - spec.Genesis.ParentHash = genesis.ParentHash - spec.Genesis.ExtraData = genesis.ExtraData - spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit) - - for address, account := range genesis.Alloc { - spec.setAccount(address, account) - } - - spec.setPrecompile(1, &alethGenesisSpecBuiltin{Name: "ecrecover", - Linear: &alethGenesisSpecLinearPricing{Base: 3000}}) - spec.setPrecompile(2, &alethGenesisSpecBuiltin{Name: "sha256", - Linear: &alethGenesisSpecLinearPricing{Base: 60, Word: 12}}) - spec.setPrecompile(3, &alethGenesisSpecBuiltin{Name: "ripemd160", - Linear: &alethGenesisSpecLinearPricing{Base: 600, Word: 120}}) - spec.setPrecompile(4, &alethGenesisSpecBuiltin{Name: "identity", - Linear: &alethGenesisSpecLinearPricing{Base: 15, Word: 3}}) - if genesis.Config.ByzantiumBlock != nil { - spec.setPrecompile(5, &alethGenesisSpecBuiltin{Name: "modexp", - StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock)}) - spec.setPrecompile(6, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_add", - StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock), - Linear: &alethGenesisSpecLinearPricing{Base: 500}}) - spec.setPrecompile(7, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_mul", - StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock), - Linear: &alethGenesisSpecLinearPricing{Base: 40000}}) - spec.setPrecompile(8, &alethGenesisSpecBuiltin{Name: "alt_bn128_pairing_product", - StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock)}) - } - if genesis.Config.IstanbulBlock != nil { - if genesis.Config.ByzantiumBlock == nil { - return nil, errors.New("invalid genesis, istanbul fork is enabled while byzantium is not") - } - spec.setPrecompile(6, &alethGenesisSpecBuiltin{ - Name: "alt_bn128_G1_add", - StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock), - }) // Aleth hardcoded the gas policy - spec.setPrecompile(7, &alethGenesisSpecBuiltin{ - Name: "alt_bn128_G1_mul", - StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock), - }) // Aleth hardcoded the gas policy - spec.setPrecompile(9, &alethGenesisSpecBuiltin{ - Name: "blake2_compression", - StartingBlock: (*hexutil.Big)(genesis.Config.IstanbulBlock), - }) - } - return spec, nil -} - -func (spec *alethGenesisSpec) setPrecompile(address byte, data *alethGenesisSpecBuiltin) { - if spec.Accounts == nil { - spec.Accounts = make(map[common.UnprefixedAddress]*alethGenesisSpecAccount) - } - addr := common.UnprefixedAddress(common.BytesToAddress([]byte{address})) - if _, exist := spec.Accounts[addr]; !exist { - spec.Accounts[addr] = &alethGenesisSpecAccount{} - } - spec.Accounts[addr].Precompiled = data -} - -func (spec *alethGenesisSpec) setAccount(address common.Address, account core.GenesisAccount) { - if spec.Accounts == nil { - spec.Accounts = make(map[common.UnprefixedAddress]*alethGenesisSpecAccount) - } - - a, exist := spec.Accounts[common.UnprefixedAddress(address)] - if !exist { - a = &alethGenesisSpecAccount{} - spec.Accounts[common.UnprefixedAddress(address)] = a - } - a.Balance = (*math2.HexOrDecimal256)(account.Balance) - a.Nonce = account.Nonce - -} - -// parityChainSpec is the chain specification format used by Parity. -type parityChainSpec struct { - Name string `json:"name"` - Datadir string `json:"dataDir"` - Engine struct { - Ethash struct { - Params struct { - MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"` - DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"` - DurationLimit *hexutil.Big `json:"durationLimit"` - BlockReward map[string]string `json:"blockReward"` - DifficultyBombDelays map[string]string `json:"difficultyBombDelays"` - HomesteadTransition hexutil.Uint64 `json:"homesteadTransition"` - EIP100bTransition hexutil.Uint64 `json:"eip100bTransition"` - } `json:"params"` - } `json:"Ethash"` - } `json:"engine"` - - Params struct { - AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"` - MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"` - MinGasLimit hexutil.Uint64 `json:"minGasLimit"` - GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"` - NetworkID hexutil.Uint64 `json:"networkID"` - ChainID hexutil.Uint64 `json:"chainID"` - MaxCodeSize hexutil.Uint64 `json:"maxCodeSize"` - MaxCodeSizeTransition hexutil.Uint64 `json:"maxCodeSizeTransition"` - EIP98Transition hexutil.Uint64 `json:"eip98Transition"` - EIP150Transition hexutil.Uint64 `json:"eip150Transition"` - EIP160Transition hexutil.Uint64 `json:"eip160Transition"` - EIP161abcTransition hexutil.Uint64 `json:"eip161abcTransition"` - EIP161dTransition hexutil.Uint64 `json:"eip161dTransition"` - EIP155Transition hexutil.Uint64 `json:"eip155Transition"` - EIP140Transition hexutil.Uint64 `json:"eip140Transition"` - EIP211Transition hexutil.Uint64 `json:"eip211Transition"` - EIP214Transition hexutil.Uint64 `json:"eip214Transition"` - EIP658Transition hexutil.Uint64 `json:"eip658Transition"` - EIP145Transition hexutil.Uint64 `json:"eip145Transition"` - EIP1014Transition hexutil.Uint64 `json:"eip1014Transition"` - EIP1052Transition hexutil.Uint64 `json:"eip1052Transition"` - EIP1283Transition hexutil.Uint64 `json:"eip1283Transition"` - EIP1283DisableTransition hexutil.Uint64 `json:"eip1283DisableTransition"` - EIP1283ReenableTransition hexutil.Uint64 `json:"eip1283ReenableTransition"` - EIP1344Transition hexutil.Uint64 `json:"eip1344Transition"` - EIP1884Transition hexutil.Uint64 `json:"eip1884Transition"` - EIP2028Transition hexutil.Uint64 `json:"eip2028Transition"` - } `json:"params"` - - Genesis struct { - Seal struct { - Ethereum struct { - Nonce types.BlockNonce `json:"nonce"` - MixHash hexutil.Bytes `json:"mixHash"` - } `json:"ethereum"` - } `json:"seal"` - - Difficulty *hexutil.Big `json:"difficulty"` - Author common.Address `json:"author"` - Timestamp hexutil.Uint64 `json:"timestamp"` - ParentHash common.Hash `json:"parentHash"` - ExtraData hexutil.Bytes `json:"extraData"` - GasLimit hexutil.Uint64 `json:"gasLimit"` - } `json:"genesis"` - - Nodes []string `json:"nodes"` - Accounts map[common.UnprefixedAddress]*parityChainSpecAccount `json:"accounts"` -} - -// parityChainSpecAccount is the prefunded genesis account and/or precompiled -// contract definition. -type parityChainSpecAccount struct { - Balance math2.HexOrDecimal256 `json:"balance"` - Nonce math2.HexOrDecimal64 `json:"nonce,omitempty"` - Builtin *parityChainSpecBuiltin `json:"builtin,omitempty"` -} - -// parityChainSpecBuiltin is the precompiled contract definition. -type parityChainSpecBuiltin struct { - Name string `json:"name"` // Each builtin should has it own name - Pricing interface{} `json:"pricing"` // Each builtin should has it own price strategy - ActivateAt *hexutil.Big `json:"activate_at,omitempty"` // ActivateAt can't be omitted if empty, default means no fork -} - -// parityChainSpecPricing represents the different pricing models that builtin -// contracts might advertise using. -type parityChainSpecPricing struct { - Linear *parityChainSpecLinearPricing `json:"linear,omitempty"` - ModExp *parityChainSpecModExpPricing `json:"modexp,omitempty"` - - // Before the https://github.com/paritytech/parity-ethereum/pull/11039, - // Parity uses this format to config bn pairing price policy. - AltBnPairing *parityChainSepcAltBnPairingPricing `json:"alt_bn128_pairing,omitempty"` - - // Blake2F is the price per round of Blake2 compression - Blake2F *parityChainSpecBlakePricing `json:"blake2_f,omitempty"` -} - -type parityChainSpecLinearPricing struct { - Base uint64 `json:"base"` - Word uint64 `json:"word"` -} - -type parityChainSpecModExpPricing struct { - Divisor uint64 `json:"divisor"` -} - -// parityChainSpecAltBnConstOperationPricing defines the price -// policy for bn const operation(used after istanbul) -type parityChainSpecAltBnConstOperationPricing struct { - Price uint64 `json:"price"` -} - -// parityChainSepcAltBnPairingPricing defines the price policy -// for bn pairing. -type parityChainSepcAltBnPairingPricing struct { - Base uint64 `json:"base"` - Pair uint64 `json:"pair"` -} - -// parityChainSpecBlakePricing defines the price policy for blake2 f -// compression. -type parityChainSpecBlakePricing struct { - GasPerRound uint64 `json:"gas_per_round"` -} - -type parityChainSpecAlternativePrice struct { - AltBnConstOperationPrice *parityChainSpecAltBnConstOperationPricing `json:"alt_bn128_const_operations,omitempty"` - AltBnPairingPrice *parityChainSepcAltBnPairingPricing `json:"alt_bn128_pairing,omitempty"` -} - -// parityChainSpecVersionedPricing represents a single version price policy. -type parityChainSpecVersionedPricing struct { - Price *parityChainSpecAlternativePrice `json:"price,omitempty"` - Info string `json:"info,omitempty"` -} - -// newParityChainSpec converts a go-ethereum genesis block into a Parity specific -// chain specification format. -func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []string) (*parityChainSpec, error) { - // Only ethash is currently supported between go-ethereum and Parity - if genesis.Config.Ethash == nil { - return nil, errors.New("unsupported consensus engine") - } - // Reconstruct the chain spec in Parity's format - spec := &parityChainSpec{ - Name: network, - Nodes: bootnodes, - Datadir: strings.ToLower(network), - } - spec.Engine.Ethash.Params.BlockReward = make(map[string]string) - spec.Engine.Ethash.Params.DifficultyBombDelays = make(map[string]string) - // Frontier - spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty) - spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor) - spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit) - spec.Engine.Ethash.Params.BlockReward["0x0"] = hexutil.EncodeBig(ethash.FrontierBlockReward) - - // Homestead - spec.Engine.Ethash.Params.HomesteadTransition = hexutil.Uint64(genesis.Config.HomesteadBlock.Uint64()) - - // Tangerine Whistle : 150 - // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-608.md - spec.Params.EIP150Transition = hexutil.Uint64(genesis.Config.EIP150Block.Uint64()) - - // Spurious Dragon: 155, 160, 161, 170 - // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-607.md - spec.Params.EIP155Transition = hexutil.Uint64(genesis.Config.EIP155Block.Uint64()) - spec.Params.EIP160Transition = hexutil.Uint64(genesis.Config.EIP155Block.Uint64()) - spec.Params.EIP161abcTransition = hexutil.Uint64(genesis.Config.EIP158Block.Uint64()) - spec.Params.EIP161dTransition = hexutil.Uint64(genesis.Config.EIP158Block.Uint64()) - - // Byzantium - if num := genesis.Config.ByzantiumBlock; num != nil { - spec.setByzantium(num) - } - // Constantinople - if num := genesis.Config.ConstantinopleBlock; num != nil { - spec.setConstantinople(num) - } - // ConstantinopleFix (remove eip-1283) - if num := genesis.Config.PetersburgBlock; num != nil { - spec.setConstantinopleFix(num) - } - // Istanbul - if num := genesis.Config.IstanbulBlock; num != nil { - spec.setIstanbul(num) - } - spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize) - spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit) - spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor) - spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64()) - spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64()) - spec.Params.MaxCodeSize = params.MaxCodeSize - // geth has it set from zero - spec.Params.MaxCodeSizeTransition = 0 - - // Disable this one - spec.Params.EIP98Transition = math.MaxInt64 - - spec.Genesis.Seal.Ethereum.Nonce = types.EncodeNonce(genesis.Nonce) - spec.Genesis.Seal.Ethereum.MixHash = genesis.Mixhash[:] - spec.Genesis.Difficulty = (*hexutil.Big)(genesis.Difficulty) - spec.Genesis.Author = genesis.Coinbase - spec.Genesis.Timestamp = (hexutil.Uint64)(genesis.Timestamp) - spec.Genesis.ParentHash = genesis.ParentHash - spec.Genesis.ExtraData = genesis.ExtraData - spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit) - - spec.Accounts = make(map[common.UnprefixedAddress]*parityChainSpecAccount) - for address, account := range genesis.Alloc { - bal := math2.HexOrDecimal256(*account.Balance) - - spec.Accounts[common.UnprefixedAddress(address)] = &parityChainSpecAccount{ - Balance: bal, - Nonce: math2.HexOrDecimal64(account.Nonce), - } - } - spec.setPrecompile(1, &parityChainSpecBuiltin{Name: "ecrecover", - Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 3000}}}) - - spec.setPrecompile(2, &parityChainSpecBuiltin{ - Name: "sha256", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 60, Word: 12}}, - }) - spec.setPrecompile(3, &parityChainSpecBuiltin{ - Name: "ripemd160", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 600, Word: 120}}, - }) - spec.setPrecompile(4, &parityChainSpecBuiltin{ - Name: "identity", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 15, Word: 3}}, - }) - if genesis.Config.ByzantiumBlock != nil { - spec.setPrecompile(5, &parityChainSpecBuiltin{ - Name: "modexp", - ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), - Pricing: &parityChainSpecPricing{ - ModExp: &parityChainSpecModExpPricing{Divisor: 20}, - }, - }) - spec.setPrecompile(6, &parityChainSpecBuiltin{ - Name: "alt_bn128_add", - ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), - Pricing: &parityChainSpecPricing{ - Linear: &parityChainSpecLinearPricing{Base: 500, Word: 0}, - }, - }) - spec.setPrecompile(7, &parityChainSpecBuiltin{ - Name: "alt_bn128_mul", - ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), - Pricing: &parityChainSpecPricing{ - Linear: &parityChainSpecLinearPricing{Base: 40000, Word: 0}, - }, - }) - spec.setPrecompile(8, &parityChainSpecBuiltin{ - Name: "alt_bn128_pairing", - ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), - Pricing: &parityChainSpecPricing{ - AltBnPairing: &parityChainSepcAltBnPairingPricing{Base: 100000, Pair: 80000}, - }, - }) - } - if genesis.Config.IstanbulBlock != nil { - if genesis.Config.ByzantiumBlock == nil { - return nil, errors.New("invalid genesis, istanbul fork is enabled while byzantium is not") - } - spec.setPrecompile(6, &parityChainSpecBuiltin{ - Name: "alt_bn128_add", - ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), - Pricing: map[*hexutil.Big]*parityChainSpecVersionedPricing{ - (*hexutil.Big)(big.NewInt(0)): { - Price: &parityChainSpecAlternativePrice{ - AltBnConstOperationPrice: &parityChainSpecAltBnConstOperationPricing{Price: 500}, - }, - }, - (*hexutil.Big)(genesis.Config.IstanbulBlock): { - Price: &parityChainSpecAlternativePrice{ - AltBnConstOperationPrice: &parityChainSpecAltBnConstOperationPricing{Price: 150}, - }, - }, - }, - }) - spec.setPrecompile(7, &parityChainSpecBuiltin{ - Name: "alt_bn128_mul", - ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), - Pricing: map[*hexutil.Big]*parityChainSpecVersionedPricing{ - (*hexutil.Big)(big.NewInt(0)): { - Price: &parityChainSpecAlternativePrice{ - AltBnConstOperationPrice: &parityChainSpecAltBnConstOperationPricing{Price: 40000}, - }, - }, - (*hexutil.Big)(genesis.Config.IstanbulBlock): { - Price: &parityChainSpecAlternativePrice{ - AltBnConstOperationPrice: &parityChainSpecAltBnConstOperationPricing{Price: 6000}, - }, - }, - }, - }) - spec.setPrecompile(8, &parityChainSpecBuiltin{ - Name: "alt_bn128_pairing", - ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), - Pricing: map[*hexutil.Big]*parityChainSpecVersionedPricing{ - (*hexutil.Big)(big.NewInt(0)): { - Price: &parityChainSpecAlternativePrice{ - AltBnPairingPrice: &parityChainSepcAltBnPairingPricing{Base: 100000, Pair: 80000}, - }, - }, - (*hexutil.Big)(genesis.Config.IstanbulBlock): { - Price: &parityChainSpecAlternativePrice{ - AltBnPairingPrice: &parityChainSepcAltBnPairingPricing{Base: 45000, Pair: 34000}, - }, - }, - }, - }) - spec.setPrecompile(9, &parityChainSpecBuiltin{ - Name: "blake2_f", - ActivateAt: (*hexutil.Big)(genesis.Config.IstanbulBlock), - Pricing: &parityChainSpecPricing{ - Blake2F: &parityChainSpecBlakePricing{GasPerRound: 1}, - }, - }) - } - return spec, nil -} - -func (spec *parityChainSpec) setPrecompile(address byte, data *parityChainSpecBuiltin) { - if spec.Accounts == nil { - spec.Accounts = make(map[common.UnprefixedAddress]*parityChainSpecAccount) - } - a := common.UnprefixedAddress(common.BytesToAddress([]byte{address})) - if _, exist := spec.Accounts[a]; !exist { - spec.Accounts[a] = &parityChainSpecAccount{} - } - spec.Accounts[a].Builtin = data -} - -func (spec *parityChainSpec) setByzantium(num *big.Int) { - spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ByzantiumBlockReward) - spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(3000000) - n := hexutil.Uint64(num.Uint64()) - spec.Engine.Ethash.Params.EIP100bTransition = n - spec.Params.EIP140Transition = n - spec.Params.EIP211Transition = n - spec.Params.EIP214Transition = n - spec.Params.EIP658Transition = n -} - -func (spec *parityChainSpec) setConstantinople(num *big.Int) { - spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ConstantinopleBlockReward) - spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(2000000) - n := hexutil.Uint64(num.Uint64()) - spec.Params.EIP145Transition = n - spec.Params.EIP1014Transition = n - spec.Params.EIP1052Transition = n - spec.Params.EIP1283Transition = n -} - -func (spec *parityChainSpec) setConstantinopleFix(num *big.Int) { - spec.Params.EIP1283DisableTransition = hexutil.Uint64(num.Uint64()) -} - -func (spec *parityChainSpec) setIstanbul(num *big.Int) { - spec.Params.EIP1344Transition = hexutil.Uint64(num.Uint64()) - spec.Params.EIP1884Transition = hexutil.Uint64(num.Uint64()) - spec.Params.EIP2028Transition = hexutil.Uint64(num.Uint64()) - spec.Params.EIP1283ReenableTransition = hexutil.Uint64(num.Uint64()) -} - -// pyEthereumGenesisSpec represents the genesis specification format used by the -// Python Ethereum implementation. -type pyEthereumGenesisSpec struct { - Nonce types.BlockNonce `json:"nonce"` - Timestamp hexutil.Uint64 `json:"timestamp"` - ExtraData hexutil.Bytes `json:"extraData"` - GasLimit hexutil.Uint64 `json:"gasLimit"` - Difficulty *hexutil.Big `json:"difficulty"` - Mixhash common.Hash `json:"mixhash"` - Coinbase common.Address `json:"coinbase"` - Alloc core.GenesisAlloc `json:"alloc"` - ParentHash common.Hash `json:"parentHash"` -} - -// newPyEthereumGenesisSpec converts a go-ethereum genesis block into a Parity specific -// chain specification format. -func newPyEthereumGenesisSpec(network string, genesis *core.Genesis) (*pyEthereumGenesisSpec, error) { - // Only ethash is currently supported between go-ethereum and pyethereum - if genesis.Config.Ethash == nil { - return nil, errors.New("unsupported consensus engine") - } - spec := &pyEthereumGenesisSpec{ - Nonce: types.EncodeNonce(genesis.Nonce), - Timestamp: (hexutil.Uint64)(genesis.Timestamp), - ExtraData: genesis.ExtraData, - GasLimit: (hexutil.Uint64)(genesis.GasLimit), - Difficulty: (*hexutil.Big)(genesis.Difficulty), - Mixhash: genesis.Mixhash, - Coinbase: genesis.Coinbase, - Alloc: genesis.Alloc, - ParentHash: genesis.ParentHash, - } - return spec, nil -}
diff --git go-ethereum/cmd/puppeth/testdata/stureby_aleth.json celo/cmd/puppeth/testdata/stureby_aleth.json deleted file mode 100644 index d18ba3854aa5513d26fb886b5791bc93040f51dd..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/testdata/stureby_aleth.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "sealEngine": "Ethash", - "params": { - "accountStartNonce": "0x0", - "maximumExtraDataSize": "0x20", - "homesteadForkBlock": "0x2710", - "daoHardforkBlock": "0x0", - "EIP150ForkBlock": "0x3a98", - "EIP158ForkBlock": "0x59d8", - "byzantiumForkBlock": "0x7530", - "constantinopleForkBlock": "0x9c40", - "constantinopleFixForkBlock": "0x9c40", - "istanbulForkBlock": "0xc350", - "minGasLimit": "0x1388", - "maxGasLimit": "0x7fffffffffffffff", - "tieBreakingGas": false, - "gasLimitBoundDivisor": "0x400", - "minimumDifficulty": "0x20000", - "difficultyBoundDivisor": "0x800", - "durationLimit": "0xd", - "blockReward": "0x4563918244f40000", - "networkID": "0x4cb2e", - "chainID": "0x4cb2e", - "allowFutureBlocks": false - }, - "genesis": { - "nonce": "0x0000000000000000", - "difficulty": "0x20000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x59a4e76d", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x0000000000000000000000000000000000000000000000000000000b4dc0ffee", - "gasLimit": "0x47b760" - }, - "accounts": { - "0000000000000000000000000000000000000001": { - "balance": "0x1", - "precompiled": { - "name": "ecrecover", - "linear": { - "base": 3000, - "word": 0 - } - } - }, - "0000000000000000000000000000000000000002": { - "balance": "0x1", - "precompiled": { - "name": "sha256", - "linear": { - "base": 60, - "word": 12 - } - } - }, - "0000000000000000000000000000000000000003": { - "balance": "0x1", - "precompiled": { - "name": "ripemd160", - "linear": { - "base": 600, - "word": 120 - } - } - }, - "0000000000000000000000000000000000000004": { - "balance": "0x1", - "precompiled": { - "name": "identity", - "linear": { - "base": 15, - "word": 3 - } - } - }, - "0000000000000000000000000000000000000005": { - "balance": "0x1", - "precompiled": { - "name": "modexp", - "startingBlock": "0x7530" - } - }, - "0000000000000000000000000000000000000006": { - "balance": "0x1", - "precompiled": { - "name": "alt_bn128_G1_add", - "startingBlock": "0x7530" - } - }, - "0000000000000000000000000000000000000007": { - "balance": "0x1", - "precompiled": { - "name": "alt_bn128_G1_mul", - "startingBlock": "0x7530" - } - }, - "0000000000000000000000000000000000000008": { - "balance": "0x1", - "precompiled": { - "name": "alt_bn128_pairing_product", - "startingBlock": "0x7530" - } - }, - "0000000000000000000000000000000000000009": { - "balance": "0x1", - "precompiled": { - "name": "blake2_compression", - "startingBlock": "0xc350" - } - } - } -} \ No newline at end of file
diff --git go-ethereum/cmd/puppeth/testdata/stureby_parity.json celo/cmd/puppeth/testdata/stureby_parity.json deleted file mode 100644 index e9229f99b7eabf4267cbb63a55d67ae6cbdc49a3..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/testdata/stureby_parity.json +++ /dev/null @@ -1,213 +0,0 @@ -{ - "name": "stureby", - "dataDir": "stureby", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x20000", - "difficultyBoundDivisor": "0x800", - "durationLimit": "0xd", - "blockReward": { - "0x0": "0x4563918244f40000", - "0x7530": "0x29a2241af62c0000", - "0x9c40": "0x1bc16d674ec80000" - }, - "difficultyBombDelays": { - "0x7530": "0x2dc6c0", - "0x9c40": "0x1e8480" - }, - "homesteadTransition": "0x2710", - "eip100bTransition": "0x7530" - } - } - }, - "params": { - "accountStartNonce": "0x0", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "gasLimitBoundDivisor": "0x400", - "networkID": "0x4cb2e", - "chainID": "0x4cb2e", - "maxCodeSize": "0x6000", - "maxCodeSizeTransition": "0x0", - "eip98Transition": "0x7fffffffffffffff", - "eip150Transition": "0x3a98", - "eip160Transition": "0x59d8", - "eip161abcTransition": "0x59d8", - "eip161dTransition": "0x59d8", - "eip155Transition": "0x59d8", - "eip140Transition": "0x7530", - "eip211Transition": "0x7530", - "eip214Transition": "0x7530", - "eip658Transition": "0x7530", - "eip145Transition": "0x9c40", - "eip1014Transition": "0x9c40", - "eip1052Transition": "0x9c40", - "eip1283Transition": "0x9c40", - "eip1283DisableTransition": "0x9c40", - "eip1283ReenableTransition": "0xc350", - "eip1344Transition": "0xc350", - "eip1884Transition": "0xc350", - "eip2028Transition": "0xc350" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x20000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x59a4e76d", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x0000000000000000000000000000000000000000000000000000000b4dc0ffee", - "gasLimit": "0x47b760" - }, - "nodes": [], - "accounts": { - "0000000000000000000000000000000000000001": { - "balance": "0x1", - "builtin": { - "name": "ecrecover", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }, - "0000000000000000000000000000000000000002": { - "balance": "0x1", - "builtin": { - "name": "sha256", - "pricing": { - "linear": { - "base": 60, - "word": 12 - } - } - } - }, - "0000000000000000000000000000000000000003": { - "balance": "0x1", - "builtin": { - "name": "ripemd160", - "pricing": { - "linear": { - "base": 600, - "word": 120 - } - } - } - }, - "0000000000000000000000000000000000000004": { - "balance": "0x1", - "builtin": { - "name": "identity", - "pricing": { - "linear": { - "base": 15, - "word": 3 - } - } - } - }, - "0000000000000000000000000000000000000005": { - "balance": "0x1", - "builtin": { - "name": "modexp", - "pricing": { - "modexp": { - "divisor": 20 - } - }, - "activate_at": "0x7530" - } - }, - "0000000000000000000000000000000000000006": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "0x0": { - "price": { - "alt_bn128_const_operations": { - "price": 500 - } - } - }, - "0xc350": { - "price": { - "alt_bn128_const_operations": { - "price": 150 - } - } - } - }, - "activate_at": "0x7530" - } - }, - "0000000000000000000000000000000000000007": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "0x0": { - "price": { - "alt_bn128_const_operations": { - "price": 40000 - } - } - }, - "0xc350": { - "price": { - "alt_bn128_const_operations": { - "price": 6000 - } - } - } - }, - "activate_at": "0x7530" - } - }, - "0000000000000000000000000000000000000008": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "0x0": { - "price": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000 - } - } - }, - "0xc350": { - "price": { - "alt_bn128_pairing": { - "base": 45000, - "pair": 34000 - } - } - } - }, - "activate_at": "0x7530" - } - }, - "0000000000000000000000000000000000000009": { - "balance": "0x1", - "builtin": { - "name": "blake2_f", - "pricing": { - "blake2_f": { - "gas_per_round": 1 - } - }, - "activate_at": "0xc350" - } - } - } -} \ No newline at end of file
diff --git go-ethereum/cmd/puppeth/testdata/stureby_geth.json celo/cmd/puppeth/testdata/stureby_geth.json deleted file mode 100644 index 79f03469af8c328b7884f939177697755534ef00..0000000000000000000000000000000000000000 --- go-ethereum/cmd/puppeth/testdata/stureby_geth.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "config": { - "chainId": 314158, - "homesteadBlock": 10000, - "eip150Block": 15000, - "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "eip155Block": 23000, - "eip158Block": 23000, - "byzantiumBlock": 30000, - "constantinopleBlock": 40000, - "petersburgBlock": 40000, - "istanbulBlock": 50000, - "ethash": {} - }, - "nonce": "0x0", - "timestamp": "0x59a4e76d", - "extraData": "0x0000000000000000000000000000000000000000000000000000000b4dc0ffee", - "gasLimit": "0x47b760", - "difficulty": "0x20000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "coinbase": "0x0000000000000000000000000000000000000000", - "alloc": { - "0000000000000000000000000000000000000001": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000002": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000003": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000004": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000005": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000006": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000007": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000008": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000009": { - "balance": "0x1" - } - }, - "number": "0x0", - "gasUsed": "0x0", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" -} \ No newline at end of file
diff --git go-ethereum/common/big.go celo/common/big.go index aa2dfadbd36050711a6690279c83b84a0b1277d8..32a489bf0f070395eb6d25a0ff36c01c91b34be5 100644 --- go-ethereum/common/big.go +++ celo/common/big.go @@ -16,7 +16,10 @@ // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.   package common   -import "math/big" +import ( + "fmt" + "math/big" +)   // Common big integers often used var ( @@ -28,3 +31,13 @@ Big32 = big.NewInt(32) Big256 = big.NewInt(256) Big257 = big.NewInt(257) ) + +// MustBigInt creates a big.Int from a string +// panics on error +func MustBigInt(str string) *big.Int { + i, ok := new(big.Int).SetString(str, 10) + if !ok { + panic(fmt.Errorf("Invalid string for big.Int: %s", str)) + } + return i +}
diff --git go-ethereum/common/abi.go celo/common/abi.go new file mode 100644 index 0000000000000000000000000000000000000000..5a99123ece8141e1a50ec1cb6818da65d92f798d --- /dev/null +++ celo/common/abi.go @@ -0,0 +1,48 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package common + +import ( + "math/big" +) + +func AddressToAbi(address Address) []byte { + return LeftPadBytes(address.Bytes(), 32) +} + +func AmountToAbi(amount *big.Int) []byte { + return LeftPadBytes(amount.Bytes(), 32) +} + +// Generates ABI for a given method and its arguments. +func GetEncodedAbi(methodSelector []byte, varAbis [][]byte) []byte { + encodedVarsAbiByteSize := 0 + for _, varAbi := range varAbis { + encodedVarsAbiByteSize += len(varAbi) + } + encodedAbi := make([]byte, len(methodSelector)+encodedVarsAbiByteSize) + + copy(encodedAbi[0:len(methodSelector)], methodSelector[:]) + + copyCursor := len(methodSelector) + for _, varAbi := range varAbis { + copy(encodedAbi[copyCursor:copyCursor+len(varAbi)], varAbi[:]) + copyCursor += len(varAbi) + } + + return encodedAbi +}
diff --git go-ethereum/common/types.go celo/common/types.go index 20ad76ac4f8ad5634dcf02e239bd7f5af0097065..9f5c508583f2aa2032556898aca0c7407d2f600c 100644 --- go-ethereum/common/types.go +++ celo/common/types.go @@ -43,6 +43,7 @@ var ( hashT = reflect.TypeOf(Hash{}) addressT = reflect.TypeOf(Address{}) + ZeroAddress = BytesToAddress([]byte{}) )   // Hash represents the 32 byte Keccak256 hash of arbitrary data. @@ -337,6 +338,17 @@ // Value implements valuer for database/sql. func (a Address) Value() (driver.Value, error) { return a[:], nil +} + +// Converts an array of addresses to an array of their hex strings. Used for printing out an array of addresses +func ConvertToStringSlice(addresses []Address) []string { + returnList := make([]string, len(addresses)) + + for i, address := range addresses { + returnList[i] = address.Hex() + } + + return returnList }   // ImplementsGraphQLType returns true if Hash implements the specified GraphQL type.
diff --git go-ethereum/common/decimal/decimal.go celo/common/decimal/decimal.go new file mode 100644 index 0000000000000000000000000000000000000000..6112d07af559091658bb70e1c40fbc979f488d51 --- /dev/null +++ celo/common/decimal/decimal.go @@ -0,0 +1,70 @@ +package decimal + +import ( + "encoding/json" + "math/big" + + dec "github.com/shopspring/decimal" +) + +// Precision creates precision constant required for decimal type +func Precision(digits int64) dec.Decimal { + return dec.NewFromInt(10).Pow(dec.NewFromInt(digits)) +} + +// String returns big.Int string representation as a number with fixed decimals +func String(value *big.Int, precision dec.Decimal) string { + amount, _ := dec.NewFromString(value.String()) + return amount.Div(precision).String() +} + +// FromJSON unnmarshal JSON string a big.Int with fixed precision +func FromJSON(b []byte, precision dec.Decimal) (*big.Int, error) { + var data string + if err := json.Unmarshal(b, &data); err != nil { + return nil, err + } + return New(data, precision) +} + +// ToJSON returns json marshalling as decimal number +func ToJSON(value *big.Int, precision dec.Decimal) ([]byte, error) { + return json.Marshal(String(value, precision)) +} + +// New creates a new Decimal from a string|float|int|decimal +func New(iamount interface{}, precision dec.Decimal) (*big.Int, error) { + var err error + amount := dec.NewFromFloat(0) + switch v := iamount.(type) { + case string: + amount, err = dec.NewFromString(v) + if err != nil { + return nil, err + } + case float64: + amount = dec.NewFromFloat(v) + case int64: + amount = dec.NewFromFloat(float64(v)) + case dec.Decimal: + amount = v + case *dec.Decimal: + amount = *v + } + + result := amount.Mul(precision) + + value := new(big.Int) + value.SetString(result.String(), 10) + + return value, nil +} + +// MustNew New variant that panics on error +func MustNew(iamount interface{}, precision dec.Decimal) *big.Int { + value, err := New(iamount, precision) + if err != nil { + panic(err) + } + return value +}
diff --git go-ethereum/common/compiler/vyper.go celo/common/compiler/vyper.go index 517ebf55fd91dd244dd2221a7474ff2173edf3fa..15a5ce0937a971f5b816f55c19a8c84ff54f4f8f 100644 --- go-ethereum/common/compiler/vyper.go +++ celo/common/compiler/vyper.go @@ -83,6 +83,7 @@ if err != nil { return nil, err } args := s.makeArgs() + // #nosec (only used in abigen locally) cmd := exec.Command(s.Path, append(args, sourcefiles...)...) return s.run(cmd, source) }
diff --git go-ethereum/common/task/task.go celo/common/task/task.go new file mode 100644 index 0000000000000000000000000000000000000000..cbcbb34b6d484402fdb4cb638d952bc0c0186bd0 --- /dev/null +++ celo/common/task/task.go @@ -0,0 +1,52 @@ +package task + +import "time" + +type StopFn func() + +// ticker is an interface which allows us to test RunTaskRepeateadly without +// needing to rely on actual timing which is not perfectly accurate and thus +// makes tests flakey. +type ticker interface { + stop() + tickChan() <-chan time.Time +} + +func NewDefaultTicker(period time.Duration) *DefaultTicker { + return &DefaultTicker{*time.NewTicker(period)} +} + +// DefaultTicker is an implementation of ticker which simply delegates to +// time.Ticker. +type DefaultTicker struct { + t time.Ticker +} + +func (d *DefaultTicker) tickChan() <-chan time.Time { + return d.t.C +} +func (d *DefaultTicker) stop() { + d.t.Stop() +} + +func RunTaskRepeateadly(task func(), t ticker) StopFn { + // Setup the ticker and the channel to signal + // the ending of the interval + stop := make(chan struct{}) + + go func() { + for { + select { + case <-t.tickChan(): + task() + case <-stop: + t.stop() + return + } + } + }() + + return func() { + stop <- struct{}{} + } +}
diff --git go-ethereum/common/task/task_test.go celo/common/task/task_test.go new file mode 100644 index 0000000000000000000000000000000000000000..82b715a8b8d30c2e3ecdb7e270d9700e931bc8e5 --- /dev/null +++ celo/common/task/task_test.go @@ -0,0 +1,40 @@ +package task + +import ( + "testing" + "time" +) + +type testTicker struct { + tc chan time.Time +} + +func (t *testTicker) stop() {} + +func (t *testTicker) tickChan() <-chan time.Time { + return t.tc +} +func (t *testTicker) sendTick() { + t.tc <- time.Now() +} + +func TestRunTaskRepeateadly(t *testing.T) { + t.Parallel() + + counter := 0 + ping := func() { counter++ } + + tt := &testTicker{ + tc: make(chan time.Time), + } + + stopTask := RunTaskRepeateadly(ping, tt) + tt.sendTick() + tt.sendTick() + tt.sendTick() + stopTask() + + if counter != 3 { + t.Errorf("Expect task to run 3 times but got %d", counter) + } +}
diff --git go-ethereum/common/decimal/decimal_test.go celo/common/decimal/decimal_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bc072208acea56771086c288b5d48996dcf6bf1a --- /dev/null +++ celo/common/decimal/decimal_test.go @@ -0,0 +1,36 @@ +package decimal + +import ( + "fmt" + "math/big" + "testing" + + . "github.com/onsi/gomega" +) + +var precision = Precision(24) + +func TestToString(t *testing.T) { + RegisterTestingT(t) + Ω(String(MustNew("1.135", precision), precision)).To(Equal("1.135")) +} + +func TestNumberOfDecimales(t *testing.T) { + RegisterTestingT(t) + Ω(MustNew("1.335", Precision(6))).To(Equal(big.NewInt(1335000))) +} + +func TestJsonMarshalling(t *testing.T) { + RegisterTestingT(t) + + value := MustNew("1.135", precision) + + bt, err := ToJSON(value, precision) + Ω(err).ShouldNot(HaveOccurred()) + + fmt.Println((string)(bt)) + otherValue, err := FromJSON(bt, precision) + Ω(err).Should(Succeed()) + Ω(String(otherValue, precision)).To(Equal(String(value, precision))) + +}
diff --git go-ethereum/common/fdlimit/fdlimit_test.go celo/common/fdlimit/fdlimit_test.go index 21362b8463a3fc63d44c83450bc74077e67df513..9fd5e9fc3cbd6f1264cf691bfb5f49aa1472e32f 100644 --- go-ethereum/common/fdlimit/fdlimit_test.go +++ celo/common/fdlimit/fdlimit_test.go @@ -17,7 +17,6 @@ package fdlimit   import ( - "fmt" "testing" )   @@ -30,7 +29,7 @@ if err != nil { t.Fatal(err) } if hardlimit < target { - t.Skip(fmt.Sprintf("system limit is less than desired test target: %d < %d", hardlimit, target)) + t.Skipf("system limit is less than desired test target: %d < %d", hardlimit, target) }   if limit, err := Current(); err != nil || limit <= 0 {
diff --git go-ethereum/common/compiler/solidity.go celo/common/compiler/solidity.go index 90052dd626a3d2cb6945bcb0c7bdc1c84799cd28..96a144801411e7fed384aeeaeba4af07acd22bf0 100644 --- go-ethereum/common/compiler/solidity.go +++ celo/common/compiler/solidity.go @@ -109,6 +109,7 @@ if err != nil { return nil, err } args := append(s.makeArgs(), "--") + // #nosec (only used in testing) cmd := exec.Command(s.Path, append(args, "-")...) cmd.Stdin = strings.NewReader(source) return s.run(cmd, source) @@ -128,6 +129,7 @@ if err != nil { return nil, err } args := append(s.makeArgs(), "--") + // #nosec (used for abigen) cmd := exec.Command(s.Path, append(args, sourcefiles...)...) return s.run(cmd, source) }
diff --git go-ethereum/common/decimal/bigintstr/big_int_str.go celo/common/decimal/bigintstr/big_int_str.go new file mode 100644 index 0000000000000000000000000000000000000000..654b0c523481c3dd16d957b68dcef2e8e1ee1560 --- /dev/null +++ celo/common/decimal/bigintstr/big_int_str.go @@ -0,0 +1,35 @@ +package bigintstr + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common/decimal" +) + +// Since this is just a wrapper for big.Int, it's precision is 1 (10^0) +// We could created a wrapper without using `decimal`, but this lets us reuse some code. +var precision = decimal.Precision(0) + +// BigIntStr is a wrapper for big.Int which is marshaled to a string +type BigIntStr big.Int + +// MustNew creates an instance of Fixed from a string +func MustNew(str string) *BigIntStr { return (*BigIntStr)(decimal.MustNew(str, precision)) } + +// String implements fmt.Stringer +func (v *BigIntStr) String() string { return decimal.String(v.BigInt(), precision) } + +// BigInt returns big.Int representation +func (v *BigIntStr) BigInt() *big.Int { return (*big.Int)(v) } + +// MarshalJSON implements json.Marshaller +func (v BigIntStr) MarshalJSON() ([]byte, error) { return decimal.ToJSON(v.BigInt(), precision) } + +// UnmarshalJSON implements json.Unmarshaller +func (v *BigIntStr) UnmarshalJSON(b []byte) error { + value, err := decimal.FromJSON(b, precision) + if err == nil { + *v = (BigIntStr)(*value) + } + return err +}
diff --git go-ethereum/common/decimal/token/token.go celo/common/decimal/token/token.go new file mode 100644 index 0000000000000000000000000000000000000000..6bcd60d038bb9322dc94e39cfbb1fc8f4ff49f4b --- /dev/null +++ celo/common/decimal/token/token.go @@ -0,0 +1,33 @@ +package token + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common/decimal" +) + +var precision = decimal.Precision(18) + +// Token is a decimal number with 18 decimal digits +type Token big.Int + +// MustNew creates an instance of Token from a string +func MustNew(str string) *Token { return (*Token)(decimal.MustNew(str, precision)) } + +// String implements fmt.Stringer +func (t *Token) String() string { return decimal.String(t.BigInt(), precision) } + +// BigInt returns big.Int representation +func (t *Token) BigInt() *big.Int { return (*big.Int)(t) } + +// MarshalJSON implements json.Marshaller +func (t Token) MarshalJSON() ([]byte, error) { return decimal.ToJSON(t.BigInt(), precision) } + +// UnmarshalJSON implements json.Unmarshaller +func (t *Token) UnmarshalJSON(b []byte) error { + value, err := decimal.FromJSON(b, precision) + if err == nil { + *t = (Token)(*value) + } + return err +}
diff --git go-ethereum/common/decimal/fixed/fixed.go celo/common/decimal/fixed/fixed.go new file mode 100644 index 0000000000000000000000000000000000000000..fd8a6a75697b7032ac8bd66a0d049a00b5074b71 --- /dev/null +++ celo/common/decimal/fixed/fixed.go @@ -0,0 +1,33 @@ +package fixed + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common/decimal" +) + +var precision = decimal.Precision(24) + +// Fixed is a decimal number with 24 decimal digits +type Fixed big.Int + +// MustNew creates an instance of Fixed from a string +func MustNew(str string) *Fixed { return (*Fixed)(decimal.MustNew(str, precision)) } + +// String implements fmt.Stringer +func (v *Fixed) String() string { return decimal.String(v.BigInt(), precision) } + +// BigInt returns big.Int representation +func (v *Fixed) BigInt() *big.Int { return (*big.Int)(v) } + +// MarshalJSON implements json.Marshaller +func (v Fixed) MarshalJSON() ([]byte, error) { return decimal.ToJSON(v.BigInt(), precision) } + +// UnmarshalJSON implements json.Unmarshaller +func (v *Fixed) UnmarshalJSON(b []byte) error { + value, err := decimal.FromJSON(b, precision) + if err == nil { + *v = (Fixed)(*value) + } + return err +}
diff --git go-ethereum/consensus/protocol.go celo/consensus/protocol.go new file mode 100644 index 0000000000000000000000000000000000000000..ac5a964c4e328cef34e418996e50a20d29fa64be --- /dev/null +++ celo/consensus/protocol.go @@ -0,0 +1,60 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// Package consensus implements different Ethereum consensus engines. +package consensus + +import ( + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// Broadcaster defines the interface to enqueue blocks to fetcher, find peer +type Broadcaster interface { + // FindPeers retrieves peers by addresses + FindPeers(targets map[enode.ID]bool, purpose p2p.PurposeFlag) map[enode.ID]Peer +} + +// P2PServer defines the interface for a p2p.server to get the local node's enode and to add/remove for static/trusted peers +type P2PServer interface { + // Gets this node's enode + Self() *enode.Node + // AddPeer will add a peer to the p2p server instance + AddPeer(node *enode.Node, purpose p2p.PurposeFlag) + // RemovePeer will remove a peer from the p2p server instance + RemovePeer(node *enode.Node, purpose p2p.PurposeFlag) + // AddTrustedPeer will add a trusted peer to the p2p server instance + AddTrustedPeer(node *enode.Node, purpose p2p.PurposeFlag) + // RemoveTrustedPeer will remove a trusted peer from the p2p server instance + RemoveTrustedPeer(node *enode.Node, purpose p2p.PurposeFlag) +} + +// Peer defines the interface for a p2p.peer +type Peer interface { + // Send sends the message to this peer + Send(msgcode uint64, data interface{}) error + // Node returns the peer's enode + Node() *enode.Node + // Version returns the peer's version + Version() uint + // Blocks until a message is read directly from the peer. + // This should only be used during a handshake. + ReadMsg() (p2p.Msg, error) + // Inbound returns if the peer connection is inbound + Inbound() bool + // PurposeIsSet returns if the peer has a purpose set + PurposeIsSet(purpose p2p.PurposeFlag) bool +}
diff --git go-ethereum/consensus/consensus.go celo/consensus/consensus.go index 2a5aac945d9ddb18ab797dbc3c25168dd742f229..8461fd3139394a3f6d7ec295d37bc4dc79e9d616 100644 --- go-ethereum/consensus/consensus.go +++ celo/consensus/consensus.go @@ -21,8 +21,12 @@ import ( "math/big"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -46,13 +50,16 @@ // GetHeaderByHash retrieves a block header from the database by its hash. GetHeaderByHash(hash common.Hash) *types.Header }   -// ChainReader defines a small collection of methods needed to access the local -// blockchain during header and/or uncle verification. -type ChainReader interface { +// ChainContext defines a small collection of methods needed to access the local +// blockchain +type ChainContext interface { ChainHeaderReader   - // GetBlock retrieves a block from the database by hash and number. - GetBlock(hash common.Hash, number uint64) *types.Block + // NewEVMRunnerForCurrentBlock creates the System's EVMRunner for current block & state + NewEVMRunnerForCurrentBlock() (vm.EVMRunner, error) + + // NewEVMRunner creates the System's EVMRunner for given header & sttate + NewEVMRunner(header *types.Header, state vm.StateDB) vm.EVMRunner }   // Engine is an algorithm agnostic consensus engine. @@ -73,9 +80,9 @@ // a results channel to retrieve the async verifications (the order is that of // the input slice). VerifyHeaders(chain ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)   - // VerifyUncles verifies that the given block's uncles conform to the consensus - // rules of a given engine. - VerifyUncles(chain ChainReader, block *types.Block) error + // VerifySeal checks whether the crypto seal on a header is valid according to + // the consensus rules of the given engine. + VerifySeal(header *types.Header) error   // Prepare initializes the consensus fields of a block header according to the // rules of a particular engine. The changes are executed inline. @@ -86,30 +93,24 @@ // but does not assemble the block. // // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). - Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, - uncles []*types.Header) + Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction)   // FinalizeAndAssemble runs any post-transaction state modifications (e.g. block // rewards) and assembles the final block. // // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). - FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, - uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) + FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, receipts []*types.Receipt, randomness *types.Randomness) (*types.Block, error)   - // Seal generates a new sealing request for the given input block and pushes - // the result into the given channel. + // Seal generates a new sealing request for the given input block. // - // Note, the method returns immediately and will send the result async. More - // than one result may also be returned depending on the consensus algorithm. - Seal(chain ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error + // Note: The engine will insert the resulting block. + Seal(chain ChainHeaderReader, block *types.Block) error   - // SealHash returns the hash of a block prior to it being sealed. - SealHash(header *types.Header) common.Hash + // GetValidators returns the list of current validators. + GetValidators(blockNumber *big.Int, headerHash common.Hash) []istanbul.Validator   - // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty - // that a new block should have. - CalcDifficulty(chain ChainHeaderReader, time uint64, parent *types.Header) *big.Int + EpochSize() uint64   // APIs returns the RPC APIs this consensus engine provides. APIs(chain ChainHeaderReader) []rpc.API @@ -118,6 +119,43 @@ // Close terminates any background threads maintained by the consensus engine. Close() error }   +type Genesis interface { + GetAlloc() GenesisAlloc + + UnmarshalFromDB(db ethdb.Database) error +} + +type GenesisAlloc map[common.Address]GenesisAccount + +type GenesisAccount interface { + GetPublicKey() []byte +} + +// Handler should be implemented if the consensus needs to handle and send peer messages +type Handler interface { + // NewWork handles a new work event from the miner + NewWork() error + + // HandleMsg handles a message from peer + HandleMsg(address common.Address, data p2p.Msg, peer Peer) (bool, error) + + // SetBroadcaster sets the broadcaster to send message to peers + SetBroadcaster(Broadcaster) + + // SetP2PServer sets the p2p server to connect/disconnect to/from peers + SetP2PServer(P2PServer) + + // RegisterPeer will notify the consensus engine that a new peer has been added + RegisterPeer(peer Peer, fromProxiedNode bool) error + + // UnregisterPeer will notify the consensus engine that a new peer has been removed + UnregisterPeer(peer Peer, fromProxiedNode bool) + + // Handshake will begin a handshake with a new peer. It returns if the peer + // has identified itself as a validator and should bypass any max peer checks. + Handshake(peer Peer) (bool, error) +} + // PoW is a consensus engine based on proof-of-work. type PoW interface { Engine @@ -125,3 +163,60 @@ // Hashrate returns the current mining hashrate of a PoW consensus engine. Hashrate() float64 } + +// Istanbul is a consensus engine to avoid byzantine failure +type Istanbul interface { + Engine + + // IsProxiedValidator returns true if this node is a proxied validator + IsProxiedValidator() bool + + // IsProxy returns true if this node is a proxy + IsProxy() bool + + // IsPrimary returns true if this node is the primary validator + IsPrimary() bool + + // IsPrimaryForSeq returns true if this node is the primary validator for the sequence + IsPrimaryForSeq(seq *big.Int) bool + + // SetChain injects the blockchain and related functions to the istanbul consensus engine + SetChain(chain ChainContext, currentBlock func() *types.Block, stateAt func(common.Hash) (*state.StateDB, error)) + + // SetCallBacks sets call back functions + SetCallBacks(hasBadBlock func(common.Hash) bool, + processBlock func(*types.Block, *state.StateDB) (types.Receipts, []*types.Log, uint64, error), + validateState func(*types.Block, *state.StateDB, types.Receipts, uint64) error, + onNewConsensusBlock func(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB)) error + + // StartValidating starts the validating engine + StartValidating() error + + // StopValidating stops the validating engine + StopValidating() error + + // StartAnnouncing starts the announcing + StartAnnouncing() error + + // StopAnnouncing stops the announcing + StopAnnouncing() error + + // StartProxiedValidatorEngine starts the proxied validator engine + StartProxiedValidatorEngine() error + + // StopProxiedValidatorEngine stops the proxied validator engine + StopProxiedValidatorEngine() error + + // UpdateValSetDiff will update the validator set diff in the header, if the mined header is the last block of the epoch. + // The changes are executed inline. + UpdateValSetDiff(chain ChainHeaderReader, header *types.Header, state *state.StateDB) error + + // IsLastBlockOfEpoch will check to see if the header is from the last block of an epoch + IsLastBlockOfEpoch(header *types.Header) bool + + // ValidatorAddress will return the istanbul engine's validator address + ValidatorAddress() common.Address + + // GenerateRandomness will generate the random beacon randomness + GenerateRandomness(parentHash common.Hash) (common.Hash, common.Hash, error) +}
diff --git go-ethereum/consensus/consensustest/mockprotocol.go celo/consensus/consensustest/mockprotocol.go new file mode 100644 index 0000000000000000000000000000000000000000..9baf733581a6b6601fc5aebb05264da3a8251d7d --- /dev/null +++ celo/consensus/consensustest/mockprotocol.go @@ -0,0 +1,415 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package consensustest + +import ( + "crypto/ecdsa" + "errors" + "math/big" + "net" + "runtime" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/trie" +) + +var ( + errFakeFail = errors.New("mockEngine fake fail") +) + +type MockBroadcaster struct{} + +func (b *MockBroadcaster) Enqueue(id string, block *types.Block) { +} + +func (b *MockBroadcaster) FindPeers(targets map[enode.ID]bool, purpose p2p.PurposeFlag) map[enode.ID]consensus.Peer { + return make(map[enode.ID]consensus.Peer) +} + +type MockP2PServer struct { + Node *enode.Node +} + +var defaultPubKey *ecdsa.PublicKey = &ecdsa.PublicKey{ + Curve: crypto.S256(), + X: hexutil.MustDecodeBig("0x760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1"), + Y: hexutil.MustDecodeBig("0xb01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d")} + +func NewMockP2PServer(pubKey *ecdsa.PublicKey) *MockP2PServer { + if pubKey == nil { + pubKey = defaultPubKey + } + + mockNode := enode.NewV4( + pubKey, + net.IP{192, 168, 0, 1}, + 30303, + 30303) + + return &MockP2PServer{Node: mockNode} +} + +func (serv *MockP2PServer) Self() *enode.Node { + return serv.Node +} + +func (serv *MockP2PServer) AddPeer(node *enode.Node, purpose p2p.PurposeFlag) {} + +func (serv *MockP2PServer) RemovePeer(node *enode.Node, purpose p2p.PurposeFlag) {} + +func (serv *MockP2PServer) AddTrustedPeer(node *enode.Node, purpose p2p.PurposeFlag) {} + +func (serv *MockP2PServer) RemoveTrustedPeer(node *enode.Node, purpose p2p.PurposeFlag) {} + +type MockPeer struct { + node *enode.Node + purposes p2p.PurposeFlag +} + +func NewMockPeer(node *enode.Node, purposes p2p.PurposeFlag) *MockPeer { + mockPeer := &MockPeer{node: node, purposes: purposes} + + return mockPeer +} + +func (mp *MockPeer) Send(msgCode uint64, data interface{}) error { + return nil +} + +func (mp *MockPeer) Node() *enode.Node { + return mp.node +} + +func (mp *MockPeer) Version() uint { + return 0 +} + +func (mp *MockPeer) ReadMsg() (p2p.Msg, error) { + return p2p.Msg{}, nil +} + +func (mp *MockPeer) Inbound() bool { + return false +} + +func (mp *MockPeer) PurposeIsSet(purpose p2p.PurposeFlag) bool { + return true +} + +type Mode uint + +// MockEngine provides a minimal fake implementation of a consensus engine for use in blockchain tests. +type MockEngine struct { + consensus.Engine + + mode Mode + + fakeFail uint64 // Block number which fails consensus even in fake mode + fakeDelay time.Duration // Time delay to sleep for before returning from verify + + processBlock func(block *types.Block, statedb *state.StateDB) (types.Receipts, []*types.Log, uint64, error) + validateState func(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error + onNewConsensusBlock func(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB) +} + +const ( + Fake Mode = iota + FullFake +) + +var ( + // Max time from current time allowed for blocks, before they're considered future blocks + allowedFutureBlockTime = 15 * time.Second + + errZeroBlockTime = errors.New("timestamp equals parent's") +) + +// NewFaker creates a MockEngine consensus engine that accepts +// all blocks' seal as valid, though they still have to conform to the Ethereum +// consensus rules. +func NewFaker() *MockEngine { + return &MockEngine{ + mode: Fake, + } +} + +// NewFakeFailer creates a MockEngine consensus engine that +// accepts all blocks as valid apart from the single one specified, though they +// still have to conform to the Ethereum consensus rules. +func NewFakeFailer(blockNumber uint64) *MockEngine { + return &MockEngine{ + mode: Fake, + fakeFail: blockNumber, + } +} + +// NewFakeDelayer creates a MockEngine consensus engine that +// accepts all blocks as valid, but delays verifications by some time, though +// they still have to conform to the Ethereum consensus rules. +func NewFakeDelayer(delay time.Duration) *MockEngine { + return &MockEngine{ + mode: Fake, + fakeDelay: delay, + } +} + +// NewFullFaker creates an MockEngine consensus engine with a full fake scheme that +// accepts all blocks as valid, without checking any consensus rules whatsoever. +func NewFullFaker() *MockEngine { + return &MockEngine{ + mode: FullFake, + } +} + +func (e *MockEngine) accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header) { + // Simply touch coinbase account + reward := big.NewInt(1) + state.AddBalance(header.Coinbase, reward) +} + +func (e *MockEngine) Finalize(chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction) { + e.accumulateRewards(chain.Config(), statedb, header) + header.Root = statedb.IntermediateRoot(chain.Config().IsEIP158(header.Number)) +} + +func (e *MockEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction, receipts []*types.Receipt, randomness *types.Randomness) (*types.Block, error) { + e.accumulateRewards(chain.Config(), statedb, header) + header.Root = statedb.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + + // Header seems complete, assemble into a block and return + return types.NewBlock(header, txs, receipts, randomness, new(trie.Trie)), nil +} + +func (e *MockEngine) Author(header *types.Header) (common.Address, error) { + return header.Coinbase, nil +} + +func (e *MockEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { + if e.mode == FullFake { + return nil + } + // Short circuit if the header is known, or if its parent is unknown + number := header.Number.Uint64() + if chain.GetHeader(header.Hash(), number) != nil { + return nil + } + parent := chain.GetHeader(header.ParentHash, number-1) + if parent == nil && chain.Config().FullHeaderChainAvailable { + return consensus.ErrUnknownAncestor + } + // Sanity checks passed, do a proper verification + return e.verifyHeader(chain, header, parent, seal) +} + +// verifyHeader checks whether a header conforms to the consensus rules +func (e *MockEngine) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, seal bool) error { + // Ensure that the extra data format is satisfied + if _, err := header.IstanbulExtra(); err != nil { + return errors.New("invalid extra data format") + } + // Verify the header's timestamp + if header.Time > uint64(time.Now().Add(allowedFutureBlockTime).Unix()) { + return consensus.ErrFutureBlock + } + if parent != nil { + if header.Time <= parent.Time { + return errZeroBlockTime + } + // Verify that the block number is parent's +1 + if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 { + return consensus.ErrInvalidNumber + } + } + // Verify the engine specific seal securing the block + if seal { + if err := e.VerifySeal(header); err != nil { + return err + } + } + return nil +} + +func (e *MockEngine) VerifySeal(header *types.Header) error { + return e.verifySeal(header) +} + +func (e *MockEngine) verifySeal(header *types.Header) error { + time.Sleep(e.fakeDelay) + if e.fakeFail == header.Number.Uint64() { + return errFakeFail + } + return nil +} + +// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers +// concurrently. The method returns a quit channel to abort the operations and +// a results channel to retrieve the async verifications. +func (e *MockEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { + if e.mode == FullFake || len(headers) == 0 { + abort, results := make(chan struct{}), make(chan error, len(headers)) + for i := 0; i < len(headers); i++ { + results <- nil + } + return abort, results + } + + // Spawn as many workers as allowed threads + workers := runtime.GOMAXPROCS(0) + if len(headers) < workers { + workers = len(headers) + } + + // Create a task channel and spawn the verifiers + var ( + inputs = make(chan int) + done = make(chan int, workers) + errors = make([]error, len(headers)) + abort = make(chan struct{}) + ) + for i := 0; i < workers; i++ { + go func() { + for index := range inputs { + errors[index] = e.verifyHeaderWorker(chain, headers, seals, index) + done <- index + } + }() + } + + errorsOut := make(chan error, len(headers)) + go func() { + defer close(inputs) + var ( + in, out = 0, 0 + checked = make([]bool, len(headers)) + inputs = inputs + ) + for { + select { + case inputs <- in: + if in++; in == len(headers) { + // Reached end of headers. Stop sending to workers. + inputs = nil + } + case index := <-done: + for checked[index] = true; checked[out]; out++ { + errorsOut <- errors[out] + if out == len(headers)-1 { + return + } + } + case <-abort: + return + } + } + }() + return abort, errorsOut +} + +func (e *MockEngine) verifyHeaderWorker(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, index int) error { + var parent *types.Header + if index == 0 { + parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1) + } else if headers[index-1].Hash() == headers[index].ParentHash { + parent = headers[index-1] + } + if parent == nil && chain.Config().FullHeaderChainAvailable { + return consensus.ErrUnknownAncestor + } + if chain.GetHeader(headers[index].Hash(), headers[index].Number.Uint64()) != nil { + return nil // known block + } + return e.verifyHeader(chain, headers[index], parent, seals[index]) +} + +func (e *MockEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { + parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) + if parent == nil { + return consensus.ErrUnknownAncestor + } + + // Matches delay in consensus/istanbul/backend/engine.go:386 in (*Backend).Prepare + delay := time.Until(time.Unix(int64(header.Time), 0)) + if delay > 0 { + time.Sleep(delay) + } + + return nil +} + +type fullChain interface { + CurrentBlock() *types.Block + StateAt(common.Hash) (*state.StateDB, error) +} + +func (e *MockEngine) Seal(chain consensus.ChainHeaderReader, block *types.Block) error { + header := block.Header() + finalBlock := block.WithHeader(header) + c := chain.(fullChain) + + parent := c.CurrentBlock() + + state, err := c.StateAt(parent.Root()) + if err != nil { + return err + } + + receipts, logs, _, err := e.processBlock(finalBlock, state) + if err != nil { + return err + } + e.onNewConsensusBlock(block, receipts, logs, state) + + return nil +} + +// APIs implements consensus.Engine, returning the user facing RPC APIs. +func (e *MockEngine) APIs(chain consensus.ChainHeaderReader) []rpc.API { + return []rpc.API{} +} + +// Close closes the exit channel to notify all backend threads exiting. +func (e *MockEngine) Close() error { + return nil +} + +// EpochSize size of the epoch +func (e *MockEngine) EpochSize() uint64 { + return 100 +} + +// SetCallBacks sets call back functions +func (e *MockEngine) SetCallBacks(hasBadBlock func(common.Hash) bool, + processBlock func(*types.Block, *state.StateDB) (types.Receipts, []*types.Log, uint64, error), + validateState func(*types.Block, *state.StateDB, types.Receipts, uint64) error, + onNewConsensusBlock func(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB)) error { + e.processBlock = processBlock + e.validateState = validateState + e.onNewConsensusBlock = onNewConsensusBlock + + return nil + +}

Celo implements Istanbul BFT, specified in the Celo IBFT Spec, and loosely based off of EIP 650. This section is almost entirely Celo-specific.

diff --git go-ethereum/consensus/istanbul/utils.go celo/consensus/istanbul/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..788c5cd091c61d6d2ad0bdd2b105a2adb423dec4 --- /dev/null +++ celo/consensus/istanbul/utils.go @@ -0,0 +1,227 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package istanbul + +import ( + "encoding/hex" + "errors" + "fmt" + "math/big" + "time" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/crypto/sha3" +) + +func RLPHash(v interface{}) (h common.Hash) { + hw := sha3.NewLegacyKeccak256() + rlp.Encode(hw, v) + hw.Sum(h[:0]) + return h +} + +// GetSignatureAddress gets the signer address from the signature +func GetSignatureAddress(data []byte, sig []byte) (common.Address, error) { + // 1. Keccak data + hashData := crypto.Keccak256(data) + // 2. Recover public key + pubkey, err := crypto.SigToPub(hashData, sig) + if err != nil { + return common.Address{}, err + } + return crypto.PubkeyToAddress(*pubkey), nil +} + +func CheckValidatorSignature(valSet ValidatorSet, data []byte, sig []byte) (common.Address, error) { + // 1. Get signature address + signer, err := GetSignatureAddress(data, sig) + if err != nil { + log.Error("Failed to get signer address", "err", err) + return common.Address{}, err + } + + // 2. Check validator + if _, val := valSet.GetByAddress(signer); val != nil { + return val.Address(), nil + } + + return common.Address{}, fmt.Errorf("not an elected validator %s", signer.Hex()) +} + +// GetNumberWithinEpoch retrieves the block number within an epoch. +// The return value will be 1-based; thus first block of epoch is 1, and last block of epoch is `epochSize` +// There is a special case if the number == 0. It is basically the last block of the 0th epoch, and should have a value of epochSize +func GetNumberWithinEpoch(number uint64, epochSize uint64) uint64 { + number = number % epochSize + if number == 0 { + return epochSize + } + return number +} + +// IsLastBlockOfEpoch indicates if block number is the last block of its epoch +func IsLastBlockOfEpoch(number uint64, epochSize uint64) bool { + return GetNumberWithinEpoch(number, epochSize) == epochSize +} + +// IsFirstBlockOfEpoch indicates if block number is the first block of its epoch +func IsFirstBlockOfEpoch(number uint64, epochSize uint64) bool { + return GetNumberWithinEpoch(number, epochSize) == 1 +} + +// GetEpochNumber retrieves the epoch number given the block number. +// Epoch 0 is a special block that only contains the genesis block (block 0), epoch 1 +// starts at block 1 +func GetEpochNumber(number uint64, epochSize uint64) uint64 { + if IsLastBlockOfEpoch(number, epochSize) { + return number / epochSize + } else { + return number/epochSize + 1 + } +} + +// MustGetEpochFirstBlockGivenBlockNumber is a variant of GetEpochFirstBlockGivenBlockNumber +// that panics if called for epoch 0 (genesis) +func MustGetEpochFirstBlockGivenBlockNumber(blockNumber uint64, epochSize uint64) uint64 { + firstBlock, err := GetEpochFirstBlockGivenBlockNumber(blockNumber, epochSize) + if err != nil { + panic(err) + } + return firstBlock +} + +// GetEpochFirstBlockGivenBlockNumber retrieves first block of a given block's epoch +// Fails when try to obtain first block of epoch 0 (genesis) +func GetEpochFirstBlockGivenBlockNumber(blockNumber uint64, epochSize uint64) (uint64, error) { + epochNumber := GetEpochNumber(blockNumber, epochSize) + return GetEpochFirstBlockNumber(epochNumber, epochSize) +} + +// GetEpochFirstBlockNumber retrieves first block of epoch. +func GetEpochFirstBlockNumber(epochNumber uint64, epochSize uint64) (uint64, error) { + // Epoch 0 is just the genesis block, it doesn't have a first block (only last) + if epochNumber == 0 { + return 0, errors.New("No first block for epoch 0") + } + + return ((epochNumber - 1) * epochSize) + 1, nil +} + +// GetEpochLastBlockNumber retrieves last block of epoch +func GetEpochLastBlockNumber(epochNumber uint64, epochSize uint64) uint64 { + if epochNumber == 0 { + return 0 + } + // Epoch 0 is just the genesis bock, so epoch 1 starts at block 1 and ends at block epochSize + // And from then on, it's epochSize more for each epoch + return epochNumber * epochSize +} + +func ValidatorSetDiff(oldValSet []ValidatorData, newValSet []ValidatorData) ([]ValidatorData, *big.Int) { + valSetMap := make(map[common.Address]bool) + oldValSetIndices := make(map[common.Address]int) + + for i, oldVal := range oldValSet { + if (oldVal.Address != common.Address{}) { + valSetMap[oldVal.Address] = true + oldValSetIndices[oldValSet[i].Address] = i + } + } + + var addedValidators []ValidatorData + for _, newVal := range newValSet { + index, ok := oldValSetIndices[newVal.Address] + if ok && (oldValSet[index].BLSPublicKey == newVal.BLSPublicKey) { + // We found a common validator. Pop from the map + delete(valSetMap, newVal.Address) + } else { + // We found a new validator that is not in the old validator set + addedValidators = append(addedValidators, ValidatorData{ + Address: newVal.Address, + BLSPublicKey: newVal.BLSPublicKey, + }) + } + } + + removedValidatorsBitmap := big.NewInt(0) + for rmVal := range valSetMap { + removedValidatorsBitmap = removedValidatorsBitmap.SetBit(removedValidatorsBitmap, oldValSetIndices[rmVal], 1) + } + + return addedValidators, removedValidatorsBitmap +} + +// CompareValidatorSlices compares 2 validator slices and indicate if they are equal. +// Equality is defined as: valseSet1[i] must be equal to valSet2[i] for every i. +// (aka. order matters) +func CompareValidatorSlices(valSet1 []common.Address, valSet2 []common.Address) bool { + if len(valSet1) != len(valSet2) { + return false + } + + for i := 0; i < len(valSet1); i++ { + if valSet1[i] != valSet2[i] { + return false + } + } + + return true +} + +func CompareValidatorPublicKeySlices(valSet1 []blscrypto.SerializedPublicKey, valSet2 []blscrypto.SerializedPublicKey) bool { + if len(valSet1) != len(valSet2) { + return false + } + + for i := 0; i < len(valSet1); i++ { + if valSet1[i] != valSet2[i] { + return false + } + } + + return true +} + +func ConvertPublicKeysToStringSlice(publicKeys []blscrypto.SerializedPublicKey) []string { + publicKeyStrs := []string{} + for i := 0; i < len(publicKeys); i++ { + publicKeyStrs = append(publicKeyStrs, hex.EncodeToString(publicKeys[i][:])) + } + + return publicKeyStrs +} + +func GetNodeID(enodeURL string) (*enode.ID, error) { + node, err := enode.ParseV4(enodeURL) + if err != nil { + return nil, err + } + + id := node.ID() + return &id, nil +} + +func GetTimestamp() uint { + // Unix() returns a int64, but we need a uint for the golang rlp encoding implmentation. Warning: This timestamp value will be truncated in 2106. + return uint(time.Now().Unix()) +}
diff --git go-ethereum/consensus/istanbul/types_v2_test.go celo/consensus/istanbul/types_v2_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9dadaa02bd7755b6c2b8155a979474f731b354a6 --- /dev/null +++ celo/consensus/istanbul/types_v2_test.go @@ -0,0 +1,377 @@ +package istanbul + +import ( + "bytes" + "math" + "math/big" + "reflect" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/stretchr/testify/assert" +) + +func dummyRoundChangeRequest() *RoundChangeRequest { + req := RoundChangeRequest{ + View: View{ + Round: common.Big1, + Sequence: common.Big2, + }, + PreparedCertificateV2: PreparedCertificateV2{ + PrepareOrCommitMessages: []Message{}, + ProposalHash: dummyBlock(2).Hash(), + }, + } + // Set empty rather than nil signature since this is how rlp decodes non + // existent slices. + req.Signature = []byte{} + return &req +} + +func dummyRoundChangeCertificateV2() *RoundChangeCertificateV2 { + return &RoundChangeCertificateV2{ + Requests: []RoundChangeRequest{*dummyRoundChangeRequest(), *dummyRoundChangeRequest(), *dummyRoundChangeRequest()}, + } +} + +func TestRoundChangeCertificateMaxPCNil(t *testing.T) { + rcc := &RoundChangeCertificateV2{ + Requests: []RoundChangeRequest{*dummyRoundChangeRequest(), *dummyRoundChangeRequest(), *dummyRoundChangeRequest()}, + } + rcc.Requests[0].View.Round = big.NewInt(7) + rcc.Requests[1].View.Round = big.NewInt(3) + rcc.Requests[2].View.Round = big.NewInt(4) + r := rcc.HighestRoundWithPreparedCertificate() + assert.Nil(t, r) +} + +func TestRoundChangeCertificateMaxPCNotNil(t *testing.T) { + rcc := &RoundChangeCertificateV2{ + Requests: []RoundChangeRequest{*dummyRoundChangeRequest(), *dummyRoundChangeRequest(), + *dummyRoundChangeRequest(), *dummyRoundChangeRequest(), *dummyRoundChangeRequest()}, + } + rcc.Requests[0].View.Round = big.NewInt(6) + rcc.Requests[0].PreparedCertificateV2.PrepareOrCommitMessages = make([]Message, 1) + rcc.Requests[1].View.Round = big.NewInt(3) + rcc.Requests[1].PreparedCertificateV2.PrepareOrCommitMessages = make([]Message, 1) + rcc.Requests[2].View.Round = big.NewInt(7) + rcc.Requests[2].PreparedCertificateV2.PrepareOrCommitMessages = make([]Message, 1) + rcc.Requests[3].View.Round = big.NewInt(10) // doesn't count, Empty PC + rcc.Requests[3].PreparedCertificateV2.PrepareOrCommitMessages = make([]Message, 0) + rcc.Requests[3].View.Round = big.NewInt(4) + rcc.Requests[3].PreparedCertificateV2.PrepareOrCommitMessages = make([]Message, 1) + r := rcc.HighestRoundWithPreparedCertificate() + assert.Same(t, r, rcc.Requests[2].View.Round) + maxPC := rcc.AnyHighestPreparedCertificate() + assert.NotNil(t, maxPC) + assert.Same(t, &rcc.Requests[2].PreparedCertificateV2, maxPC) +} + +func TestRoundChangeCertificateV2RLPEncoding(t *testing.T) { + var result, original *RoundChangeCertificateV2 + original = dummyRoundChangeCertificateV2() + + rawVal, err := rlp.EncodeToBytes(original) + if err != nil { + t.Fatalf("Error %v", err) + } + + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Fatalf("Error %v", err) + } + + assert.Equal(t, len(original.Requests), len(original.Requests)) + o1 := original.Requests[0] + r1 := result.Requests[0] + + if !reflect.DeepEqual(o1.Address, r1.Address) { + t.Fatalf("RLP Encode/Decode mismatch at first Address") + } + + if !reflect.DeepEqual(o1.Signature, r1.Signature) { + t.Fatalf("RLP Encode/Decode mismatch at first Signature") + } + + if !reflect.DeepEqual(o1.View, r1.View) { + t.Fatalf("RLP Encode/Decode mismatch at first View") + } + + if !reflect.DeepEqual(o1.PreparedCertificateV2, r1.PreparedCertificateV2) { + t.Fatalf("RLP Encode/Decode mismatch at first PreparedCertificateV2") + } + + if !reflect.DeepEqual(original, result) { + t.Fatalf("RLP Encode/Decode mismatch. Got %v, expected %v", result, original) + } +} + +func TestRoundChangeRequestRLPEncoding(t *testing.T) { + var result, original *RoundChangeRequest + original = &RoundChangeRequest{ + Address: common.BigToAddress(big.NewInt(3)), + View: View{ + Round: common.Big1, + Sequence: common.Big256, + }, + PreparedCertificateV2: PreparedCertificateV2{ + PrepareOrCommitMessages: []Message{}, + }, + Signature: []byte{3, 2}, + } + + rawVal, err := rlp.EncodeToBytes(original) + if err != nil { + t.Fatalf("Error %v", err) + } + + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Fatalf("Error %v", err) + } + o1 := original + r1 := result + if !reflect.DeepEqual(o1.Address, r1.Address) { + t.Fatalf("RLP Encode/Decode mismatch at first Address") + } + + if !reflect.DeepEqual(o1.Signature, r1.Signature) { + t.Fatalf("RLP Encode/Decode mismatch at first Signature") + } + + if !reflect.DeepEqual(o1.View, r1.View) { + t.Fatalf("RLP Encode/Decode mismatch at first View") + } + + if !reflect.DeepEqual(o1.PreparedCertificateV2, r1.PreparedCertificateV2) { + t.Fatalf("RLP Encode/Decode mismatch at first PreparedCertificateV2. Got %v, expected %v", result, original) + } + + if !reflect.DeepEqual(original, result) { + t.Fatalf("RLP Encode/Decode mismatch. Got %v, expected %v", result, original) + } +} + +func TestRoundChangeV2RLPEncoding(t *testing.T) { + var result, original *RoundChangeV2 + pc := EmptyPreparedCertificate() + request := RoundChangeRequest{ + Address: common.BigToAddress(big.NewInt(3)), + View: View{ + Round: common.Big1, + Sequence: common.Big256, + }, + PreparedCertificateV2: PCV2FromPCV1(pc), + Signature: []byte{3, 2}, + } + original = &RoundChangeV2{ + Request: request, + PreparedProposal: pc.Proposal, + } + + rawVal, err := rlp.EncodeToBytes(original) + if err != nil { + t.Fatalf("Error %v", err) + } + + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Fatalf("Error %v", err) + } + + if !reflect.DeepEqual(original.PreparedProposal.Number(), result.PreparedProposal.Number()) { + t.Fatalf("RLP Encode/Decode mismatch. Got %v, expected %v", result.PreparedProposal.Number(), original.PreparedProposal.Number()) + } + + if !reflect.DeepEqual(original.Request, result.Request) { + t.Fatalf("RLP Encode/Decode mismatch. Got %v, expected %v", result.Request, original.Request) + } +} + +// **** Big size prepreparev2 test + +func fillF(slice []byte, length int) { + for i := 0; i < length && i < len(slice); i++ { + slice[i] = 0xFF + } +} + +func getF(length int) []byte { + f := make([]byte, length) + fillF(f, length) + return f +} + +func bigF(length int) *big.Int { + even := new(big.Int).Lsh(common.Big1, uint(length)) + return new(big.Int).Sub(even, common.Big1) +} + +func writeExtra(header *types.Header, validators int) error { + extra := types.IstanbulExtra{ + AddedValidators: make([]common.Address, validators), + AddedValidatorsPublicKeys: make([]blscrypto.SerializedPublicKey, validators), + RemovedValidators: big.NewInt(int64(validators)), + Seal: getF(64), + AggregatedSeal: types.IstanbulAggregatedSeal{ + Bitmap: bigF(validators), + Signature: getF(48), + Round: common.Big32, + }, + ParentAggregatedSeal: types.IstanbulAggregatedSeal{ + Bitmap: bigF(validators), + Signature: getF(48), + Round: common.Big32, + }, + } + for i := 0; i < validators; i++ { + extra.AddedValidators[i] = common.BytesToAddress(getF(20)) + fillF(extra.AddedValidatorsPublicKeys[i][:], 96) + } + payload, err := rlp.EncodeToBytes(&extra) + if err != nil { + return err + } + + if len(header.Extra) < types.IstanbulExtraVanity { + header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity-len(header.Extra))...) + } + header.Extra = append(header.Extra[:types.IstanbulExtraVanity], payload...) + return nil +} + +func bigHeader(validators int) *types.Header { + header := &types.Header{ + ParentHash: common.BigToHash(common.Big257), + Coinbase: common.BigToAddress(common.Big32), + Root: common.BigToHash(common.Big1), + TxHash: common.BigToHash(common.Big256), + ReceiptHash: common.BigToHash(common.Big3), + Bloom: [types.BloomByteLength]byte{}, + Number: big.NewInt(37), + GasUsed: 123213, + Time: 100, + } + fillF(header.Bloom[:], types.BloomByteLength) + writeExtra(header, validators) + return header +} + +func bigTxs(gasLimit int, gasPerByte int) []*types.Transaction { + curr := common.BytesToAddress(getF(20)) + gateFeeRec := common.BytesToAddress(getF(20)) + tx := types.NewTransaction(999, common.BytesToAddress(getF(20)), + big.NewInt(329274), 2942729, big.NewInt(294279), + &curr, + &gateFeeRec, big.NewInt(32027), getF(gasLimit/gasPerByte)) + return []*types.Transaction{tx} +} + +func bigBlock(gasLimit int, validators int, gasPerByte int) *types.Block { + randomness := &types.Randomness{ + Revealed: common.BigToHash(common.Big3), + Committed: common.BigToHash(common.Big2), + } + // Full header + header := bigHeader(validators) + txs := bigTxs(gasLimit, gasPerByte) + receipts := []*types.Receipt{{ + Type: 9, + PostState: getF(32), + Status: 9, + CumulativeGasUsed: 1_729_919, + Bloom: [types.BloomByteLength]byte{}, + Logs: []*types.Log{{ + Address: common.BytesToAddress(getF(20)), + Topics: []common.Hash{common.BytesToHash(getF(32)), common.BytesToHash(getF(32))}, + Data: getF(53), + }}, + }} + fillF(receipts[0].Bloom[:], types.BloomByteLength) + block := types.NewBlock(header, txs, receipts, randomness, newHasher()) + return block +} + +func bigPC(quorum int) *PreparedCertificateV2 { + // while quorum amount of commits is imposible by consensus design, + // and the max should be (quorum - 1), for simplification + // we'll just use quorum commit messages + pc := &PreparedCertificateV2{} + pc.ProposalHash = common.BytesToHash(getF(32)) + pc.PrepareOrCommitMessages = make([]Message, quorum) + for i := 0; i < quorum; i++ { + msg := NewCommitMessage(&CommittedSubject{ + Subject: &Subject{ + View: &View{Round: big.NewInt(21), Sequence: big.NewInt(3_192_191_242)}, + Digest: common.BytesToHash(getF(32)), + }, + EpochValidatorSetSeal: getF(48), + CommittedSeal: getF(48), + }, common.BytesToAddress(getF(20))) + msg.Signature = getF(64) + pc.PrepareOrCommitMessages[i] = *msg + } + return pc +} + +func bigRequest(quorum int) *RoundChangeRequest { + r := &RoundChangeRequest{ + Address: common.BytesToAddress(getF(20)), + View: View{ + Round: big.NewInt(23), + Sequence: big.NewInt(2_912_119_182), + }, + PreparedCertificateV2: *bigPC(quorum), + Signature: getF(64), + } + return r +} + +func bigRCC(validators int) *RoundChangeCertificateV2 { + rcc := &RoundChangeCertificateV2{} + quorum := int(math.Ceil(float64(2*validators) / 3)) + rcc.Requests = make([]RoundChangeRequest, quorum) + for i := 0; i < quorum; i++ { + rcc.Requests[i] = *bigRequest(quorum) + } + return rcc +} + +// TestBigPreprepareV2Size constructs the largest possible consensus message in +// order to ensure that it does not exceed the built in size limits. +func TestBigPreprepareV2Size(t *testing.T) { + gasLimit := 34_600_000 + validators := 110 + gasPerByte := params.TxDataZeroGas + sizeLimit := int(9.5 * 1024 * 1024) + block := bigBlock(gasLimit, validators, int(gasPerByte)) + assert.NotNil(t, block) + bigRCC := bigRCC(validators) + assert.NotNil(t, bigRCC) + message := NewPreprepareV2Message(&PreprepareV2{ + View: &View{ + Round: big.NewInt(15), + Sequence: big.NewInt(2_170_123_456), + }, + Proposal: block, + RoundChangeCertificateV2: *bigRCC, + }, common.BytesToAddress(getF(20))) + + rawVal, err := rlp.EncodeToBytes(message) + if err != nil { + t.Fatalf("Error %v", err) + } + assert.LessOrEqual(t, len(rawVal), sizeLimit, "PreprepareV2 message size is too big") + result := &Message{} + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Fatalf("Error %v", err) + } + rawVal2, err := rlp.EncodeToBytes(result) + if err != nil { + t.Fatalf("Error %v", err) + } + if !reflect.DeepEqual(rawVal, rawVal2) { + t.Fatalf("Serialization/Deserialization failed for PreprepareV2") + } +}
diff --git go-ethereum/consensus/istanbul/protocol.go celo/consensus/istanbul/protocol.go new file mode 100644 index 0000000000000000000000000000000000000000..91f980c4ee5470d2a0a78539955fe2f7a71f7565 --- /dev/null +++ celo/consensus/istanbul/protocol.go @@ -0,0 +1,56 @@ +// Copyright 2017 The celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package istanbul + +import ( + "github.com/ethereum/go-ethereum/p2p" +) + +// Constants to match up protocol versions and messages +const ( + // Supported versions + Celo67 = 67 // incorporates changes from eth/66 (EIP-2481) +) + +// protocolName is the official short name of the protocol used during capability negotiation. +const ProtocolName = "istanbul" + +// ProtocolVersions are the supported versions of the istanbul protocol (first is primary). +// (First is primary in the sense that it's the most current one supported) +var ProtocolVersions = []uint{Celo67} + +// protocolLengths are the number of implemented message corresponding to different protocol versions. +// celo/67, uses as the last message the 0x18, so it has 25 messages (including the 0x00) +var ProtocolLengths = map[uint]uint64{Celo67: 25} + +// Message codes for istanbul related messages +// If you want to add a code, you need to increment the protocolLengths Array size +// and update the IsIstanbulMsg function below! +const ( + ConsensusMsg = 0x11 + QueryEnodeMsg = 0x12 + ValEnodesShareMsg = 0x13 + FwdMsg = 0x14 + DelegateSignMsg = 0x15 + VersionCertificatesMsg = 0x16 + EnodeCertificateMsg = 0x17 + ValidatorHandshakeMsg = 0x18 +) + +func IsIstanbulMsg(msg p2p.Msg) bool { + return msg.Code >= ConsensusMsg && msg.Code <= ValidatorHandshakeMsg +}
diff --git go-ethereum/consensus/istanbul/types_v2.go celo/consensus/istanbul/types_v2.go new file mode 100644 index 0000000000000000000000000000000000000000..eb5ac27d96c162480611eb65c7946a845a18ecbd --- /dev/null +++ celo/consensus/istanbul/types_v2.go @@ -0,0 +1,329 @@ +package istanbul + +import ( + "io" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +type PayloadNoSig interface { + PayloadNoSig() ([]byte, error) +} + +type ValidateFn func([]byte, []byte) (common.Address, error) + +func GetSignerFromSignature(p PayloadNoSig, signature []byte, validateFn ValidateFn) (common.Address, error) { + data, err := p.PayloadNoSig() + if err != nil { + return common.Address{}, err + } + + signer, err := validateFn(data, signature) + if err != nil { + return common.Address{}, err + } + return signer, nil +} + +func CheckSignedBy(p PayloadNoSig, signature []byte, signer common.Address, wrongSignatureError error, validateFn ValidateFn) error { + extractedSigner, err := GetSignerFromSignature(p, signature, validateFn) + if err != nil { + return err + } + + if extractedSigner != signer { + return wrongSignatureError + } + return nil +} + +// ## PreprepareV2 ############################################################## + +// NewPreprepareV2Message constructs a Message instance with the given sender and +// prePrepare. Both the prePrepare instance and the serialized bytes of +// prePrepare are part of the returned Message. +func NewPreprepareV2Message(prePrepareV2 *PreprepareV2, sender common.Address) *Message { + message := &Message{ + Address: sender, + Code: MsgPreprepareV2, + prePrepareV2: prePrepareV2, + } + setMessageBytes(message, prePrepareV2) + return message +} + +type PreprepareV2 struct { + View *View + Proposal Proposal + RoundChangeCertificateV2 RoundChangeCertificateV2 +} + +// EncodeRLP serializes pp into the Ethereum RLP format. +func (pp *PreprepareV2) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{pp.View, pp.Proposal, &pp.RoundChangeCertificateV2}) +} + +// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream. +func (pp *PreprepareV2) DecodeRLP(s *rlp.Stream) error { + var prep2 struct { + View *View + Proposal *types.Block + RoundChangeCertificateV2 RoundChangeCertificateV2 + } + + if err := s.Decode(&prep2); err != nil { + return err + } + pp.View = prep2.View + pp.RoundChangeCertificateV2 = prep2.RoundChangeCertificateV2 + pp.Proposal = prep2.Proposal + return nil +} + +func (pp *PreprepareV2) HasRoundChangeCertificateV2() bool { + return !pp.RoundChangeCertificateV2.IsEmpty() +} + +func (pp *PreprepareV2) Summary() *PreprepareSummary { + return &PreprepareSummary{ + View: pp.View, + ProposalHash: pp.Proposal.Hash(), + RoundChangeCertificateSenders: MapRoundChangeRequestsToSenders(pp.RoundChangeCertificateV2.Requests), + } +} + +// MapRoundChangeRequestsToSenders map a list of RoundChangeRequest to the list of the sender addresses +func MapRoundChangeRequestsToSenders(requests []RoundChangeRequest) []common.Address { + returnList := make([]common.Address, len(requests)) + + for i, r := range requests { + returnList[i] = r.Address + } + + return returnList +} + +type PreparedCertificateV2 struct { + ProposalHash common.Hash + PrepareOrCommitMessages []Message +} + +func PCV2FromPCV1(pc PreparedCertificate) PreparedCertificateV2 { + return PreparedCertificateV2{ + ProposalHash: pc.Proposal.Hash(), + PrepareOrCommitMessages: pc.PrepareOrCommitMessages, + } +} + +func (pc *PreparedCertificateV2) IsEmpty() bool { + return len(pc.PrepareOrCommitMessages) == 0 +} + +// EncodeRLP serializes pc into the Ethereum RLP format. +func (pc *PreparedCertificateV2) EncodeRLP(w io.Writer) error { + var messages []Message = pc.PrepareOrCommitMessages + if messages == nil { + messages = []Message{} + } + return rlp.Encode(w, []interface{}{pc.ProposalHash, messages}) +} + +// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream. +func (pc *PreparedCertificateV2) DecodeRLP(s *rlp.Stream) error { + type decodable PreparedCertificateV2 + var d decodable + if err := s.Decode(&d); err != nil { + return err + } + *pc = PreparedCertificateV2(d) + if pc.PrepareOrCommitMessages == nil { + pc.PrepareOrCommitMessages = []Message{} + } + return nil +} + +type RoundChangeRequest struct { + Address common.Address + View View + PreparedCertificateV2 PreparedCertificateV2 + Signature []byte +} + +func (rcr *RoundChangeRequest) HasPreparedCertificate() bool { + return !rcr.PreparedCertificateV2.IsEmpty() +} + +// EncodeRLP serializes rcr into the Ethereum RLP format. +func (rcr *RoundChangeRequest) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{rcr.Address, &rcr.View, &rcr.PreparedCertificateV2, rcr.Signature}) +} + +// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream. +func (rcr *RoundChangeRequest) DecodeRLP(s *rlp.Stream) error { + type decodable RoundChangeRequest + var d decodable + if err := s.Decode(&d); err != nil { + return err + } + *rcr = RoundChangeRequest(d) + return nil +} + +func (rcr *RoundChangeRequest) PayloadNoSig() ([]byte, error) { + return rlp.EncodeToBytes(&RoundChangeRequest{ + Address: rcr.Address, + View: rcr.View, + PreparedCertificateV2: rcr.PreparedCertificateV2, + Signature: []byte{}, + }) +} + +func (rcr *RoundChangeRequest) Sign(signingFn func(data []byte) ([]byte, error)) error { + // Construct and encode a round change request with no signature + payloadNoSig, err := rcr.PayloadNoSig() + if err != nil { + return err + } + rcr.Signature, err = signingFn(payloadNoSig) + return err +} + +// NewRoundChangeV2Message constructs a Message instance with the given sender and +// roundChangeV2. Both the roundChangeV2 instance and the serialized bytes of +// roundChange are part of the returned Message. +func NewRoundChangeV2Message(roundChangeV2 *RoundChangeV2, sender common.Address) *Message { + message := &Message{ + Address: sender, + Code: MsgRoundChangeV2, + roundChangeV2: roundChangeV2, + } + setMessageBytes(message, roundChangeV2) + return message +} + +type RoundChangeV2 struct { + Request RoundChangeRequest + PreparedProposal Proposal +} + +func (rc *RoundChangeV2) HasPreparedCertificate() bool { + return rc.Request.HasPreparedCertificate() +} + +func (rc *RoundChangeV2) ProposalMatch() bool { + return rc.PreparedProposal.Hash() == rc.Request.PreparedCertificateV2.ProposalHash +} + +// EncodeRLP serializes rc into the Ethereum RLP format. +func (rc *RoundChangeV2) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{&rc.Request, rc.PreparedProposal}) +} + +// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream. +func (rc *RoundChangeV2) DecodeRLP(s *rlp.Stream) error { + var rc_decoded struct { + Request RoundChangeRequest + PreparedProposal *types.Block + } + + if err := s.Decode(&rc_decoded); err != nil { + return err + } + rc.PreparedProposal = rc_decoded.PreparedProposal + rc.Request = rc_decoded.Request + return nil +} + +type RoundChangeCertificateV2 struct { + Requests []RoundChangeRequest +} + +func (rcc *RoundChangeCertificateV2) IsEmpty() bool { + return len(rcc.Requests) == 0 +} + +func (rcc *RoundChangeCertificateV2) AnyHighestPreparedCertificate() *PreparedCertificateV2 { + pcRound := rcc.HighestRoundWithPreparedCertificate() + if pcRound == nil { + return nil + } + return rcc.getAnyPreparedCertificateForRound(pcRound) +} + +// HighestRoundWithPreparedCertificate returns the highest round request with a PC. Be mindful +// that there could be many different PCs with the same highest round. +func (rcc *RoundChangeCertificateV2) HighestRoundWithPreparedCertificate() *big.Int { + var maxRound *big.Int + for _, req := range rcc.Requests { + if !req.HasPreparedCertificate() { + continue + } + if req.View.Round == nil { + continue + } + round := req.View.Round + if maxRound == nil || round.Cmp(maxRound) > 0 { + maxRound = round + } + } + return maxRound +} + +func (rcc *RoundChangeCertificateV2) getAnyPreparedCertificateForRound(round *big.Int) *PreparedCertificateV2 { + for i, req := range rcc.Requests { + if req.View.Round == nil { + continue + } + if req.View.Round.Cmp(round) != 0 { + continue + } + if !req.HasPreparedCertificate() { + continue + } + // Use the index in Requests to return the exact same pointer + // otherwise it may get overwritten by the reuse of the req variable + // in the loop + return &rcc.Requests[i].PreparedCertificateV2 + } + return nil +} + +func (rcc *RoundChangeCertificateV2) GetPreparedCertificateFor(round *big.Int, proposalHash common.Hash) *PreparedCertificateV2 { + for i, req := range rcc.Requests { + if req.View.Round == nil { + continue + } + if req.View.Round.Cmp(round) != 0 { + continue + } + if !req.HasPreparedCertificate() { + continue + } + if req.PreparedCertificateV2.ProposalHash == proposalHash { + // Use the index in Requests to return the exact same pointer + // otherwise it may get overwritten by the reuse of the req variable + // in the loop + return &rcc.Requests[i].PreparedCertificateV2 + } + } + return nil +} + +// EncodeRLP serializes rcc into the Ethereum RLP format. +func (rcc *RoundChangeCertificateV2) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{rcc.Requests}) +} + +// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream. +func (rcc *RoundChangeCertificateV2) DecodeRLP(s *rlp.Stream) error { + type decodable RoundChangeCertificateV2 + var d decodable + if err := s.Decode(&d); err != nil { + return err + } + *rcc = RoundChangeCertificateV2(d) + return nil +}
diff --git go-ethereum/consensus/istanbul/celo-ibft-spec.md celo/consensus/istanbul/celo-ibft-spec.md new file mode 100644 index 0000000000000000000000000000000000000000..75b67fcaf4ef8ae573582b9eed751c0a4cd7fc76 --- /dev/null +++ celo/consensus/istanbul/celo-ibft-spec.md @@ -0,0 +1,476 @@ +# Specification for Celo IBFT protocol + +## Celo IBFT specification + +This document is an attempt to specify at a conceptual level the consensus +algorithm currently used by the Celo blockchain. It attempts to distill +everything that is pertinent to consensus and eschew all that is not. Where +consensus is the problem of agreeing on a sequence of values in a decentralized +manner. + +It is not trying to specify everything that lives under `consensus/istanbul`. + +By doing this we allow for a clean and clear specification of the consensus, +as a consequence this specification does not closely resemble the code and +mapping this to the code and vice versa, requires considerable effort. + +### High level overview + +The Celo IBFT protocol is a BFT (Byzantine Fault Tolerant) protocol that allows +a group of participants (commonly referred to as validators in the Celo +ecosystem) to agree on the ordering of values by exchanging voting messages +across a network. The voting proceeds in rounds and for each round a specific +participant is able to propose the value to be agreed upon, they are known as +the proposer. As long as less than 1/3rd of participants deviate from the +protocol then the protocol should ensure that there is only one ordering of +values that participants agree on and also that they can continue to agree on +new values, i.e. they don't get stuck. + +Ensuring that only one ordering is agreed upon is referred to as 'safety' and +ensuring that the participants can continue to agree on new values is referred +to as 'liveness'. + +### System model + +We consider a group of `3f+1` participating computers (participants) that are +able to communicate across a network. The group should be able to tolerate `f` +failures without losing liveness or safety. + +All participants know the other participants in the system and it is assumed +that all messages are signed in some way such that participants in the protocol +know which participant a message came from and that messages cannot be forged. + +Only messages from participants are considered. + +The Msgs variable holds all the messages ever received by a participant except +for messages explicitly removed by the consensus algorithm. + +When considering sets of messages denoted by either `M` or `N` (this doesn't +apply to Msgs) only messages from distinct participants are considered, I.E +sets denoted by `M` or `N` cannot contain more than one message from any +participant. + +For messages containing a value, only messages with valid values are considered. + +Participants are able to broadcast messages to all other participants. In the +case that a participant is off-line or somehow inaccessible they will not +receive broadcast messages and except for round change messages there is no +mechanism for these messages to be re-sent. + +In the case of round change messages they are periodically re-broadcast. + +We refer to a consensus instance to mean consensus for a specific height. + +We assume the existence of an application that feeds values to be agreed upon +to the consensus instance and also receives agreed upon values from the +consensus. The application also provides application specific implementations +for certain functions that the consensus algorithm relies upon, such as a +function that determines the validity of a value. + +### Algorithm + +See supporting [functions](#Functions) and [notation](#Appendix-1-Notation). + +Note all state modifications are contained in the following code block, +supporting functions do not modify state, they all operate without side +effects. + +Variable names |Instance state +--------------------------------|---------------------------------------- +`H - height` |`Hc - current height` +`R - round` |`Rc - current round` +`V - value` |`Rd - desired round` +`T - Message type` |`Vc - currently proposed value` +`RCC - round change certificate`|`PCc - current prepared certificate` +`PC - prepared certificate` |`Sc - current participant state` +`S - participant state` |`Msgs - set of all sent and received messages` +`M or N - message sets` |`PendingEvent - the most recent event sent from the application` + +``` +// Upon receiving this event the algorithm transitions to the next height the +// final committed event signifies that the application has accepted the agreed +// upon value for the current height. +upon: <FinalCommittedEvent> = PendingEvent + PendingEvent ← nil + Hc ← Hc+1 + Rc ← 0 + Rd ← 0 + PCc ← nil + Sc ← AcceptRequest + Vc ← nil + schedule onRoundChangeTimeout(Hc, 0) after roundChangeTimeout(0) + scheduleResendRoundChange(0) + +// A request event is a request to reach agreement on the provided value, the +// request event is sent by the application, if this participant is the proposer +// it will propose that value by sending a preprepared message. +upon: <RequestEvent, Hc, V> = PendingEvent && Sc = AcceptRequest + PendingEvent ← nil + if Rc = 0 && isProposer(Hc, Rd) { + broadcast(<Preprepare, Hc, 0, V, nil>) + } + +// When we see a preprepare from a proposer participants will vote for the +// value (if valid) by sending a prepare message. +upon: m ← <Preprepare, Hc, Rd, V, RCC> ∈ Msgs && m from proposer(Hc, Rd) && Sc = AcceptRequest + if (Rd > 0 && validRCC(V, RCC)) || (Rd = 0 && RCC = nil) { + Rc ← Rd + Vc ← V + Sc ← Preprepared + broadcast(<Prepare, Hc, Rd, Vc>) + } + +// When a participant sees at least 2f+1 prepare or commit messages for a value +// it will send a commit message for that value. +upon: M ← { <T, Hc, Rd, Vc> : T ∈ {Prepare, Commit} } ∈ Msgs && |M| >= 2f+1 && Sc = Preprepared + Sc ← Prepared + PCc ← <PreparedCertificate, M, Vc> + broadcast(<Commit, Hc, Rd, Vc>) + +// When a participant sees at least 2f+1 commit messages for a value, they +// consider that value committed (agreed) and pass the value to the application, +// which will in turn issue a final committed event if the value is considered +// valid by the application. +upon: M ← { <Commit, Hc, Rd, Vc> } ∈ Msgs && |M| >= 2f+1 && Sc ∈ {Preprepared, Prepared} + Sc ← Committed + deliverValue(Vc) + +// Upon receipt of a round change if that round change is old, send the participant's round +// change back to the sender to help them catch up, in order to avoid this condition triggering +// repeatedly the received round change message is then removed from Msgs. Otherwise if there are +// at least 2f+1 round change messages sharing the same round then switch to it. Otherwise if +// there are at least f+1 round change messages switch to the highest round that is less than or +// equal to the top f+1 rounds. +upon: m<RoundChange, Hc , R, PC> ∈ Msgs && (PC = nil || validPC(PC)) + if R < Rd { + Msgs ← Msgs/{m} + send(<RoundChange, Hc, Rd, PCc>, sender(m)) + } else if quorumRound() > Rd { + Rd ← quorumRound() + Rc ← quorumRound() + Sc ← AcceptRequest + schedule onRoundChangeTimeout(Hc, Rd) after roundChangeTimeout(Rd) + if Vc != nil && isProposer(Hc, Rc) { + broadcast(<Preprepare, Hc, Rc, Vc, PCc>) + } + } else if f1Round() > Rd { + Rd ← f1Round() + Sc ← WaitingForNewRound + schedule onRoundChangeTimeout(Hc, Rd) after roundChangeTimeout(Rd) + broadcast(<RoundChange, Hc, Rd, PCc>) + } + +// Functions that modify instance state. + +// As long as the round and height have not changed since it was scheduled +// onRoundChangeTimeout increments desired round, sets the current state to be +// WaitingForNewRound and broadcasts a round change message. +// +// Note: This function is referred to in the code as +// `handleTimeoutAndMoveToNextRound`, which is misleading because it does not move +// to the next round, it only updates the desired round and sends a round change +// message. Hence why it has been renamed here to avoid confusion. +onRoundChangeTimeout(H, R) { + if H = Hc && R = Rd { + Rd ← Rd+1 + Sc ← WaitingForNewRound + schedule onRoundChangeTimeout(Hc, Rd) after roundChangeTimeout(Rd) + broadcast(<RoundChange, Hc, Rd, PCc>) + } +} + +// As long as the round and height have not changed since it was scheduled +// onResendRoundChangeTimeout broadcasts a round change message and re-schedules +// the resend round change timeout. +onResendRoundChangeTimeout(H, R) { + if H = Hc && R = Rd { + if Sc = WaitingForNewRound { + broadcast(<RoundChange, Hc, Rd, PCc>) + } + scheduleResendRoundChange(Rd) + } +} + +// Schedules a resend of the round change message unless the round change +// timeout is sufficiently small. +scheduleResendRoundChange(R) { + t ← roundChangeTimeout(R) / 2 + if Sc = WaitingForNewRound && t >= MIN_RESEND_ROUNDCHANGE_TIMEOUT { + if t > MAX_RESEND_ROUNDCHANGE_TIMEOUT { + t ← MAX_RESEND_ROUNDCHANGE_TIMEOUT + } + schedule onResendRoundChangeTimeout(Hc, R) after t + } +} + +``` + +### Supporting Functions + +These functions can read global state but cannot modify it. + +#### Application provided functions +No pseudocode is provided for these functions since their implementation is +application specific. + +`proposer(H,R)`\ +Returns the proposer for the given height and round. + +`isProposer(H, R)`\ +Returns true if the current participant is the proposer for the given height +and round. + +`deliverValue(V)`\ +Delivers the given value to the application. + +`roundChangeTimeout(R)`\ +Returns the timeout for the given round + +`broadcast(<PP, H, R, V>)`\ +Broadcasts the given message to all connected participants. + +`send(<Commit, H, R, V>, sender(m))`\ +Sends the given message to to the sender of another message. + +#### PCRound +Asserts that all messages in the given prepared certificate share the same round and returns that round. +``` +PCRound(<PreparedCertificate, M, *>) { + ∃ R : ∀ m<*, *, Rm, *> ∈ M : R = Rm + return R +} +``` + +#### PCValue +Return the value associated with a prepared certificate. +``` +PCValue(<PreparedCertificate, *, V>) { + return V +} +``` + +#### validPC + +Returns true if the message set contains prepare or commit messages from at +least 2f+1 and no more than 3f+1 participants for current height, matching the +prepared certificate value and all sharing the same height and round. +``` +validPC(<PreparedCertificate, M, V>) { + N ← { m<T, Hc, *, Vm> ∈ M : (T = Prepare || T = Commit) && Vm = V } + return 2f+1 <= |N| <= 3f+1 && + ∀ m<*, Hm, Rm, *>, n<*, Hn, Rn, *> ∈ N : Hm = Hn && Rm = Rn +} +``` + +#### validRCC + +Returns true if the round change contains at least 2f+1 and no more than 3f+1 +round changes for the current height, with a round greater or equal to the +desired round and either have a valid prepared certificate or no prepared +certificate. If any round change certificates have a prepared certificate, then +there must exist one with round greater than or equal to all the others and +with a value of V. + +``` +validRCC(V, RCC) { + M ← { m<RoundChange, Hm , Rm, PC> ∈ RCC : Hm = Hc && Rm >= Rd && (PC = nil || validPC(PC)) } + N ← { m<RoundChange, * , *, PC> ∈ M : PC != nil } + if |N| > 0 { + return 2f+1 <= |M| <= 3f+1 && + ∃ m<RoundChange, *, *, Pcm> ∈ N : validPC(PCm) && PCValue(PCm) = V && ∀ n<RoundChange, * , *, PCn> ∈ N != m : PCRound(PCm) >= PCRound(PCn) + } + return 2f+1 <= |M| <= 3f+1 +} +``` + +#### quorumRound + +Asserts that at least 2f+1 round change messages share the same round and +returns that round. +``` +quorumRound() { + M ← { m<RoundChange, Hc, Rm, *>, n<RoundChange, Hc, Rn, *> ∈ Msgs : Rm = Rn } && + |M| >= 2f+1 && + ∃ R : ∀ m<*, *, Rm, *> ∈ M : R = Rm + return R +} +``` + +#### f1Round + +Asserts that there are at least f+1 round change messages and returns the +lowest round from the top f+1 rounds. +``` +f1Round() { + // This is saying that for any Rm there cannot be >= f+1 elements set with a + // larger R, since if there were that would mean that Rm is not in the top + // f+1 rounds. + M ← { m<RoundChange, Hc, Rm, *> ∈ Msgs : |{ n<RoundChange, Hc, Rn, *> ∈ Msgs : Rm < Rn }| < f+1 } && + |M| >= f+1 && + ∃ R : ∀ m<*, *, Rm, *> ∈ M : R <= Rm + return R +} +``` + +## Appendix 1: Notation +Message sets are represented as `M` if there is need to represent more than one +message set within a single scope then `N` is used for the other message set. +Elements of sets are represented with lower case letters (e.g. `m ∈ M`). +Because all sets are messages we use `m` to represent an element and if we need +to denote 2 messages from the same set we use `n` to denote the second message. +Other variables are represented with Upper case letters (e.g. `H` for height). + +Composite objects are defined by a set of comma separated variables enclosed in +angled brackets.\ +E.g. `<A, B, C>` + +If we need to refer to the composite object we prefix the angled brackets with +an identifier that is a lower case letter.\ +E.g. `m<A, B, C>` + +An identifier can also be provided in order to distinguish variables belonging +to a composite object from other similarly named variables in the same scope. +In this case the variables are annotated with the identifier by appending the +identifier to the variable name.\ +E.g. `m<Am, Bm, Cm>` + +If all instances of a composite object element share the same value for one +of its variables then that value can be used in the definition. E.g. `<A, B, Hc>` +represents a composite element with variables `A` `B` and current height. + +If a composite object element has variables for which the value is not +important then `*` is used in the place of that variable. + +### Numbers of participants +`3f+1 - the total number of participants`\ +`2f+1 - a quorum of participants` +`f - the number of failed or malicious participants that the system can tolerate` + +### Participant states +`AcceptRequest`\ +`Preprepared`\ +`Prepared`\ +`Committed`\ +`WaitingForNewRound` + +### Application defined values +`MIN_RESEND_ROUNDCHANGE_TIMEOUT`\ +`MAX_RESEND_ROUNDCHANGE_TIMEOUT` + +### Variable names +`S - participant state`\ +`H - height`\ +`R - round`\ +`V - value`\ +`T - Message type`\ +`RCC - round change certificate`\ +`PC - prepared certificate`\ +`M or N - message sets`\ +`nil - indicates that the relevant variable is not set` + +### Message composite object structures +`<Preprepare, H, R, V, RCC>`\ +`<Prepare, H, R, V>`\ +`<Commit, H, R, V>`\ +`<RoundChange, H, R, PC>`\ +`<PreparedCertificate, M, V>` + +### Event composite object structures +Events are a means for the application to communicate with the consensus +instance, they are never sent across the network. + +`<RequestEvent, H, V> - request event, provides the value for the proposer to propose`\ +`<FinalCommittedEvent> - final committed event, sent by the application to initiate a new consensus instance` + +### Pseudocode notation +``` +Function definitions are represented as follows where the name of the function is foo, its +parameters are X and Y, functions can optionally return a value. + +foo(X, Y) { + ... + return X +} +``` +``` +Conditional statements are represented as follows where statements inside only +one set of the curly braces will be executed if the corresponding condition C +evaluates to true or in the case of the final else those statements will be +executed if none of the previous conditions evaluated to true. + +if C { + ... +} else if C { + ... +} else { + ... +} +``` +``` +upon: UponCondition - Pseudocode directly following upon statements is executed +when the associated UponCondition evaluates to true + +Upon conditions are structured thus: + +<composite object, set of objects or event to match against> <additional qualifications> + +E.G: +// 2f+1 commit messages for the current round and height with a non nil value. +M ← <Commit, Hc, Rc, V> && |M| = 2f+1 && V != nil +``` +``` +schedule <function call> after <duration> - This notation schedules the given +function call to occur after the given duration. + +``` + +### Math notation +`← - assignment`\ +`= - is equal`\ +`!= - is not equal`\ +`&& - logical and`\ +`|| - logical or`\ +`{X, Y} - the set containing X and Y`\ +`|M| - the cardinality of M`\ +`m ∈ M - m is an element of the set M`\ +`{ m : C(m) } - set builder notation, the set of messages m such that they satisfy condition C`\ +`∃ m : C(m) - there exists m that satisfies condition C`\ +`∀ m : C(m) - all m satisfy condition C`\ +`M/N - Set difference, the set containing all elements of M and no elements of N` + +### Math Notation examples +``` +// There exists a commit message m in M such that m's height (Hm) is +// less than m's round (Rm) and m's value (V) is not important. +∃ m<Commit, H, R, *> ∈ M : Hm < Rm + +// The cardinality of prepare messages in M with height and round equal +// to Hc and value equal to V is greater than 1 and less than 10. +1 < |{ m<Prepare, Hc, Rm, Vm> ∈ M : Rm = Hc && Vm = V }| < 10 +``` + +## Appendix 2: Notes + +### What do the prepared certificate and round change certificate do? + +They enforce that if it’s possible that a node could have confirmed a block in +a round, then all remaining nodes in future rounds can only confirm the same +block. + +- A node could have confirmed a block if 2f+1 nodes send commit messages for a + block. +- If a node sends a commit message for a block then it must have seen 2f+1 + prepares or commits for that block and therefore must have a prepared + certificate. +- Therefore there must be at least 2f+1 nodes with prepared certificates for a + block if 2f+1 nodes have sent commit messages for a block. +- When the proposer proposes in the next round they must provide a round change + certificate with 2f+1 round change messages. +- When nodes send round change messages they should include the prepared + certificate if they have one. +- So if 2f+1 nodes have prepared certificates then f nodes do not. +- If the proposer of the next round includes the f round change messages from + the nodes that do not have prepared certificates, and if there are a further + f byzantine nodes that do not include their prepared certificate even if they + had one, there still remains one round change message that must come from a + non byzantine node and must include a prepared certificate.
diff --git go-ethereum/consensus/istanbul/utils_test.go celo/consensus/istanbul/utils_test.go new file mode 100644 index 0000000000000000000000000000000000000000..78d32bfc2b1b77a564cf8630f847d5f58a9480e2 --- /dev/null +++ celo/consensus/istanbul/utils_test.go @@ -0,0 +1,285 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package istanbul + +import ( + "math/big" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" +) + +func TestValidatorSetDiff(t *testing.T) { + tests := []struct { + inputOldValset []common.Address + inputNewValset []common.Address + expectedAddedVals []common.Address + expectedRemovedVals *big.Int + }{ + { + // Test validator sets that are both empty + inputOldValset: []common.Address{}, + inputNewValset: []common.Address{}, + expectedAddedVals: []common.Address{}, + expectedRemovedVals: big.NewInt(0), + }, + + { + // Test validator sets that are the same + inputOldValset: []common.Address{common.HexToAddress("0x64DB1B94A0304E4c27De2E758B2f962d09dFE503"), + common.HexToAddress("0xC257274276a4E539741Ca11b590B9447B26A8051"), + common.HexToAddress("0x2140eFD7Ba31169c69dfff6CDC66C542f0211825")}, + inputNewValset: []common.Address{common.HexToAddress("0x64DB1B94A0304E4c27De2E758B2f962d09dFE503"), + common.HexToAddress("0xC257274276a4E539741Ca11b590B9447B26A8051"), + common.HexToAddress("0x2140eFD7Ba31169c69dfff6CDC66C542f0211825")}, + expectedAddedVals: []common.Address{}, + expectedRemovedVals: big.NewInt(0), + }, + + { + // Test validator sets where one is empty + inputOldValset: []common.Address{}, + inputNewValset: []common.Address{common.HexToAddress("0x64DB1B94A0304E4c27De2E758B2f962d09dFE503"), + common.HexToAddress("0xC257274276a4E539741Ca11b590B9447B26A8051"), + common.HexToAddress("0x2140eFD7Ba31169c69dfff6CDC66C542f0211825")}, + expectedAddedVals: []common.Address{common.HexToAddress("0x64DB1B94A0304E4c27De2E758B2f962d09dFE503"), + common.HexToAddress("0xC257274276a4E539741Ca11b590B9447B26A8051"), + common.HexToAddress("0x2140eFD7Ba31169c69dfff6CDC66C542f0211825")}, + expectedRemovedVals: big.NewInt(0), + }, + + { + // Test validator sets where other is empty + inputOldValset: []common.Address{common.HexToAddress("0x64DB1B94A0304E4c27De2E758B2f962d09dFE503"), + common.HexToAddress("0xC257274276a4E539741Ca11b590B9447B26A8051"), + common.HexToAddress("0x2140eFD7Ba31169c69dfff6CDC66C542f0211825")}, + inputNewValset: []common.Address{}, + expectedAddedVals: []common.Address{}, + expectedRemovedVals: big.NewInt(7), // 111, all were removed + }, + + { + // Test validator sets that have some common elements + inputOldValset: []common.Address{common.HexToAddress("0x64DB1B94A0304E4c27De2E758B2f962d09dFE503"), + common.HexToAddress("0xC257274276a4E539741Ca11b590B9447B26A8051"), + common.HexToAddress("0x2140eFD7Ba31169c69dfff6CDC66C542f0211825"), + common.HexToAddress("0x18a00A3b357F7c309f0025dAe883170140527F76"), + common.HexToAddress("0xaF6532a62c7c7c951129cd55078B19216E81Dad9"), + common.HexToAddress("0x48Fa44872054C1426bdAB29834972c45D207D9DE")}, + inputNewValset: []common.Address{common.HexToAddress("0x64DB1B94A0304E4c27De2E758B2f962d09dFE503"), + common.HexToAddress("0xC257274276a4E539741Ca11b590B9447B26A8051"), + common.HexToAddress("0x2140eFD7Ba31169c69dfff6CDC66C542f0211825"), + common.HexToAddress("0x31722d8C03e18a84891f45A4ECDe4444C8bE0907"), + common.HexToAddress("0xB55A183bF5db01665f9fC5DfbA71Fc6f8b5e42e6"), + common.HexToAddress("0x5B570EA42eBE010df95670389b93fd17d9Db9F23")}, + expectedAddedVals: []common.Address{common.HexToAddress("0x31722d8C03e18a84891f45A4ECDe4444C8bE0907"), + common.HexToAddress("0xB55A183bF5db01665f9fC5DfbA71Fc6f8b5e42e6"), + common.HexToAddress("0x5B570EA42eBE010df95670389b93fd17d9Db9F23")}, + expectedRemovedVals: big.NewInt(56), + }, + + { + // Test validator sets that have no common elements + inputOldValset: []common.Address{common.HexToAddress("0x18a00A3b357F7c309f0025dAe883170140527F76"), + common.HexToAddress("0xaF6532a62c7c7c951129cd55078B19216E81Dad9"), + common.HexToAddress("0x48Fa44872054C1426bdAB29834972c45D207D9DE")}, + inputNewValset: []common.Address{common.HexToAddress("0x31722d8C03e18a84891f45A4ECDe4444C8bE0907"), + common.HexToAddress("0xB55A183bF5db01665f9fC5DfbA71Fc6f8b5e42e6"), + common.HexToAddress("0x5B570EA42eBE010df95670389b93fd17d9Db9F23")}, + expectedAddedVals: []common.Address{common.HexToAddress("0x31722d8C03e18a84891f45A4ECDe4444C8bE0907"), + common.HexToAddress("0xB55A183bF5db01665f9fC5DfbA71Fc6f8b5e42e6"), + common.HexToAddress("0x5B570EA42eBE010df95670389b93fd17d9Db9F23")}, + expectedRemovedVals: big.NewInt(7), // 111, all were removed + }, + } + + for i, tt := range tests { + convertedInputOldValSet := []ValidatorData{} + for _, addr := range tt.inputOldValset { + convertedInputOldValSet = append(convertedInputOldValSet, ValidatorData{ + Address: addr, + BLSPublicKey: blscrypto.SerializedPublicKey{}, + }) + } + convertedInputNewValSet := []ValidatorData{} + for _, addr := range tt.inputNewValset { + convertedInputNewValSet = append(convertedInputNewValSet, ValidatorData{ + Address: addr, + BLSPublicKey: blscrypto.SerializedPublicKey{}, + }) + } + addedVals, removedVals := ValidatorSetDiff(convertedInputOldValSet, convertedInputNewValSet) + addedValsAddresses, _ := SeparateValidatorDataIntoIstanbulExtra(addedVals) + + if !CompareValidatorSlices(addedValsAddresses, tt.expectedAddedVals) || removedVals.Cmp(tt.expectedRemovedVals) != 0 { + t.Errorf("test %d failed - have: addedVals %v, removedVals %v; want: addedVals %v, removedVals %v", i, addedValsAddresses, removedVals, tt.expectedAddedVals, tt.expectedRemovedVals) + } + } + +} + +func TestGetEpochFirstBlockNumber(t *testing.T) { + type args struct { + epochNumber uint64 + epochSize uint64 + } + tests := []struct { + name string + args args + want uint64 + wantErr bool + }{ + {"No epoch 0", args{0, 10}, 0, true}, + {"epoch1", args{1, 10}, 1, false}, + {"epoch2", args{2, 10}, 11, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetEpochFirstBlockNumber(tt.args.epochNumber, tt.args.epochSize) + if (err != nil) != tt.wantErr { + t.Errorf("GetEpochFirstBlockNumber() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("GetEpochFirstBlockNumber() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetEpochLastBlockNumber(t *testing.T) { + type args struct { + epochNumber uint64 + epochSize uint64 + } + tests := []struct { + name string + args args + want uint64 + }{ + {"epoch 0", args{0, 10}, 0}, + {"epoch 1", args{1, 10}, 10}, + {"epoch 2", args{2, 10}, 20}, + {"epoch size 1", args{1, 1}, 1}, + {"epoch size 2", args{1, 2}, 2}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetEpochLastBlockNumber(tt.args.epochNumber, tt.args.epochSize); got != tt.want { + t.Errorf("GetEpochLastBlockNumber() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetNumberWithinEpoch(t *testing.T) { + type args struct { + number uint64 + epochSize uint64 + } + tests := []struct { + name string + args args + want uint64 + }{ + {"block 0", args{0, 10}, 10}, + {"block 0 other size", args{0, 15}, 15}, + {"block 1", args{1, 10}, 1}, + {"block 1 epoch 2", args{11, 10}, 1}, + {"block 5 epoch 2", args{15, 10}, 5}, + {"last block epoch 2", args{20, 10}, 10}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetNumberWithinEpoch(tt.args.number, tt.args.epochSize); got != tt.want { + t.Errorf("GetNumberWithinEpoch() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestIsLastBlockOfEpoch(t *testing.T) { + type args struct { + number uint64 + epochSize uint64 + } + tests := []struct { + name string + args args + want bool + }{ + {"genesis block", args{0, 10}, true}, + {"epoch 1, block 1", args{1, 10}, false}, + {"epoch 2, block 3", args{13, 10}, false}, + {"epoch 2, block 20", args{20, 10}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsLastBlockOfEpoch(tt.args.number, tt.args.epochSize); got != tt.want { + t.Errorf("IsLastBlockOfEpoch() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestIsFirstBlockOfEpoch(t *testing.T) { + type args struct { + number uint64 + epochSize uint64 + } + tests := []struct { + name string + args args + want bool + }{ + {"genesis block", args{0, 10}, false}, + {"epoch 1, block 1", args{1, 10}, true}, + {"epoch 2, block 3", args{13, 10}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsFirstBlockOfEpoch(tt.args.number, tt.args.epochSize); got != tt.want { + t.Errorf("IsFirstBlockOfEpoch() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetEpochNumber(t *testing.T) { + type args struct { + number uint64 + epochSize uint64 + } + tests := []struct { + name string + args args + want uint64 + }{ + {"genesis block", args{0, 10}, 0}, + {"epoch 1 first block", args{1, 10}, 1}, + {"epoch 1 last block", args{10, 10}, 1}, + {"epoch 2 first lbock", args{11, 10}, 2}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetEpochNumber(tt.args.number, tt.args.epochSize); got != tt.want { + t.Errorf("GetEpochNumber() = %v, want %v", got, tt.want) + } + }) + } +}
diff --git go-ethereum/consensus/istanbul/types_test.go celo/consensus/istanbul/types_test.go new file mode 100644 index 0000000000000000000000000000000000000000..dd6593266b8b3ae1c9faf473734ad9194b1f45b3 --- /dev/null +++ celo/consensus/istanbul/types_test.go @@ -0,0 +1,264 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package istanbul + +import ( + "hash" + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/crypto/sha3" +) + +// testHasher is the helper tool for transaction/receipt list hashing. +// The original hasher is trie, in order to get rid of import cycle, +// use the testing hasher instead. +type testHasher struct { + hasher hash.Hash +} + +func newHasher() *testHasher { + return &testHasher{hasher: sha3.NewLegacyKeccak256()} +} + +func (h *testHasher) Reset() { + h.hasher.Reset() +} + +func (h *testHasher) Update(key, val []byte) { + h.hasher.Write(key) + h.hasher.Write(val) +} + +func (h *testHasher) Hash() common.Hash { + return common.BytesToHash(h.hasher.Sum(nil)) +} + +func TestViewCompare(t *testing.T) { + // test equality + srvView := &View{ + Sequence: big.NewInt(2), + Round: big.NewInt(1), + } + tarView := &View{ + Sequence: big.NewInt(2), + Round: big.NewInt(1), + } + if r := srvView.Cmp(tarView); r != 0 { + t.Errorf("source(%v) should be equal to target(%v): have %v, want %v", srvView, tarView, r, 0) + } + + // test larger Sequence + tarView = &View{ + Sequence: big.NewInt(1), + Round: big.NewInt(1), + } + if r := srvView.Cmp(tarView); r != 1 { + t.Errorf("source(%v) should be larger than target(%v): have %v, want %v", srvView, tarView, r, 1) + } + + // test larger Round + tarView = &View{ + Sequence: big.NewInt(2), + Round: big.NewInt(0), + } + if r := srvView.Cmp(tarView); r != 1 { + t.Errorf("source(%v) should be larger than target(%v): have %v, want %v", srvView, tarView, r, 1) + } + + // test smaller Sequence + tarView = &View{ + Sequence: big.NewInt(3), + Round: big.NewInt(1), + } + if r := srvView.Cmp(tarView); r != -1 { + t.Errorf("source(%v) should be smaller than target(%v): have %v, want %v", srvView, tarView, r, -1) + } + tarView = &View{ + Sequence: big.NewInt(2), + Round: big.NewInt(2), + } + if r := srvView.Cmp(tarView); r != -1 { + t.Errorf("source(%v) should be smaller than target(%v): have %v, want %v", srvView, tarView, r, -1) + } +} + +func dummyView() *View { + return &View{ + Round: big.NewInt(15), + Sequence: big.NewInt(42), + } +} +func dummySubject() *Subject { + return &Subject{ + View: dummyView(), + Digest: common.HexToHash("1234567890"), + } +} + +func dummyBlock(number int64) *types.Block { + header := &types.Header{ + Number: big.NewInt(number), + GasUsed: 123213, + Time: 100, + Extra: []byte{01, 02}, + } + feeCurrencyAddr := common.HexToAddress("02") + gatewayFeeRecipientAddr := common.HexToAddress("03") + tx := types.NewTransaction(1, common.HexToAddress("01"), big.NewInt(1), 10000, big.NewInt(10), &feeCurrencyAddr, &gatewayFeeRecipientAddr, big.NewInt(34), []byte{04}) + return types.NewBlock(header, []*types.Transaction{tx}, nil, nil, newHasher()) +} +func dummyMessage(code uint64) *Message { + msg := NewPrepareMessage(dummySubject(), common.HexToAddress("AABB")) + // Set empty rather than nil signature since this is how rlp decodes non + // existent slices. + msg.Signature = []byte{} + return msg +} + +func dummyPreparedCertificate() *PreparedCertificate { + return &PreparedCertificate{ + PrepareOrCommitMessages: []Message{*dummyMessage(42), *dummyMessage(32), *dummyMessage(15)}, + Proposal: dummyBlock(1), + } +} + +func assertEqual(t *testing.T, prefix string, o, r interface{}) { + if !reflect.DeepEqual(o, r) { + t.Errorf("%s: Got %#v, expected %#v", prefix, r, o) + } +} + +func TestViewRLPEncoding(t *testing.T) { + var result, original *View + original = dummyView() + + rawVal, err := rlp.EncodeToBytes(original) + if err != nil { + t.Fatalf("Error %v", err) + } + + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Fatalf("Error %v", err) + } + + if !reflect.DeepEqual(original, result) { + t.Fatalf("RLP Encode/Decode mismatch. Got %v, expected %v", result, original) + } +} + +func TestMessageRLPEncoding(t *testing.T) { + var result, original *Message + original = dummyMessage(42) + + rawVal, err := rlp.EncodeToBytes(original) + if err != nil { + t.Fatalf("Error %v", err) + } + + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Fatalf("Error %v", err) + } + + if !reflect.DeepEqual(original, result) { + t.Fatalf("RLP Encode/Decode mismatch. Got %v, expected %v", result, original) + } +} + +func TestPreparedCertificateRLPEncoding(t *testing.T) { + var result, original *PreparedCertificate + original = dummyPreparedCertificate() + + rawVal, err := rlp.EncodeToBytes(original) + if err != nil { + t.Fatalf("Error %v", err) + } + + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Fatalf("Error %v", err) + } + + // decoded Blocks don't equal Original ones so we need to check equality differently + assertEqual(t, "RLP Encode/Decode mismatch: PrepareOrCommitMessages", result.PrepareOrCommitMessages, original.PrepareOrCommitMessages) + assertEqual(t, "RLP Encode/Decode mismatch: BlockHash", result.Proposal.Hash(), original.Proposal.Hash()) +} + +func TestSubjectRLPEncoding(t *testing.T) { + var result, original *Subject + original = dummySubject() + + rawVal, err := rlp.EncodeToBytes(original) + if err != nil { + t.Fatalf("Error %v", err) + } + + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Fatalf("Error %v", err) + } + + if !reflect.DeepEqual(original, result) { + t.Fatalf("RLP Encode/Decode mismatch. Got %v, expected %v", result, original) + } +} + +func TestCommittedSubjectRLPEncoding(t *testing.T) { + var result, original *CommittedSubject + original = &CommittedSubject{ + Subject: dummySubject(), + CommittedSeal: []byte{12, 13, 23}, + EpochValidatorSetSeal: []byte{1, 5, 50}, + } + + rawVal, err := rlp.EncodeToBytes(original) + if err != nil { + t.Fatalf("Error %v", err) + } + + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Fatalf("Error %v", err) + } + + if !reflect.DeepEqual(original, result) { + t.Fatalf("RLP Encode/Decode mismatch. Got %v, expected %v", result, original) + } +} + +func TestForwardMessageRLPEncoding(t *testing.T) { + var result, original *ForwardMessage + original = &ForwardMessage{ + Code: 0x11, // istanbulConsensusMsg, but doesn't matter what it is + DestAddresses: []common.Address{common.HexToAddress("123123")}, + Msg: []byte{23, 23, 12, 3}, + } + + rawVal, err := rlp.EncodeToBytes(original) + if err != nil { + t.Fatalf("Error %v", err) + } + + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Fatalf("Error %v", err) + } + + if !reflect.DeepEqual(original, result) { + t.Fatalf("RLP Encode/Decode mismatch. Got %v, expected %v", result, original) + } +}
diff --git go-ethereum/consensus/istanbul/gossip_cache.go celo/consensus/istanbul/gossip_cache.go new file mode 100644 index 0000000000000000000000000000000000000000..7cf76260da6034ab9cbb82c7bd2733ef2324a7fd --- /dev/null +++ celo/consensus/istanbul/gossip_cache.go @@ -0,0 +1,92 @@ +// Copyright 2017 The celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package istanbul + +import ( + lru "github.com/hashicorp/golang-lru" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" +) + +type GossipCache interface { + MarkMessageProcessedByPeer(peerNodeAddr common.Address, payload []byte) + CheckIfMessageProcessedByPeer(peerNodeAddr common.Address, payload []byte) bool + + MarkMessageProcessedBySelf(payload []byte) + CheckIfMessageProcessedBySelf(payload []byte) bool +} + +type LRUGossipCache struct { + messageCacheSize int + peerRecentMessages *lru.ARCCache // the cache of peer's recent messages + selfRecentMessages *lru.ARCCache // the cache of self recent messages +} + +func NewLRUGossipCache(peerCacheSize, messageCacheSize int) *LRUGossipCache { + logger := log.New() + peerRecentMessages, err := lru.NewARC(peerCacheSize) + if err != nil { + logger.Crit("Failed to create recent messages cache", "err", err) + } + selfRecentMessages, err := lru.NewARC(messageCacheSize) + if err != nil { + logger.Crit("Failed to create known messages cache", "err", err) + } + return &LRUGossipCache{ + messageCacheSize: messageCacheSize, + peerRecentMessages: peerRecentMessages, + selfRecentMessages: selfRecentMessages, + } +} + +func (gc *LRUGossipCache) MarkMessageProcessedByPeer(peerNodeAddr common.Address, payload []byte) { + ms, ok := gc.peerRecentMessages.Get(peerNodeAddr) + var m *lru.ARCCache + if ok { + m, _ = ms.(*lru.ARCCache) + } else { + m, _ = lru.NewARC(gc.messageCacheSize) + gc.peerRecentMessages.Add(peerNodeAddr, m) + } + payloadHash := RLPHash(payload) + m.Add(payloadHash, true) +} + +func (gc *LRUGossipCache) CheckIfMessageProcessedByPeer(peerNodeAddr common.Address, payload []byte) bool { + ms, ok := gc.peerRecentMessages.Get(peerNodeAddr) + var m *lru.ARCCache + if ok { + m, _ = ms.(*lru.ARCCache) + payloadHash := RLPHash(payload) + _, ok := m.Get(payloadHash) + return ok + } + + return false +} + +func (gc *LRUGossipCache) MarkMessageProcessedBySelf(payload []byte) { + payloadHash := RLPHash(payload) + gc.selfRecentMessages.Add(payloadHash, true) +} + +func (gc *LRUGossipCache) CheckIfMessageProcessedBySelf(payload []byte) bool { + payloadHash := RLPHash(payload) + _, ok := gc.selfRecentMessages.Get(payloadHash) + return ok +}
diff --git go-ethereum/consensus/istanbul/validator.go celo/consensus/istanbul/validator.go new file mode 100644 index 0000000000000000000000000000000000000000..758513555c6fc0019ba4f06cd7c6311f2de57ea6 --- /dev/null +++ celo/consensus/istanbul/validator.go @@ -0,0 +1,194 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package istanbul + +import ( + "bytes" + "errors" + "fmt" + "math/big" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + + "github.com/ethereum/go-ethereum/common" +) + +var ( + errInvalidValidatorSetDiffSize = errors.New("istanbul extra validator set data has different size") +) + +type ValidatorData struct { + Address common.Address + BLSPublicKey blscrypto.SerializedPublicKey +} + +type ValidatorDataWithBLSKeyCache struct { + Address common.Address + BLSPublicKey blscrypto.SerializedPublicKey + UncompressedBLSPublicKey []byte +} + +type Validator interface { + fmt.Stringer + + // Address returns address + Address() common.Address + + // BLSPublicKey returns the BLS public key (compressed format) + BLSPublicKey() blscrypto.SerializedPublicKey + + // BLSPublicKeyUncompressed returns the BLS public key (uncompressed format) + BLSPublicKeyUncompressed() []byte + + // Serialize returns binary reprenstation of the Validator + // can be use used to instantiate a validator with DeserializeValidator() + Serialize() ([]byte, error) + + // AsData returns Validator representation as ValidatorData + AsData() *ValidatorData + + // AsData returns Validator representation as ValidatorData + AsDataWithBLSKeyCache() *ValidatorDataWithBLSKeyCache + + // CacheUncompressedBLSKey stores the uncompressed BLS public key to cache + CacheUncompressedBLSKey() + + // Copy validator + Copy() Validator +} + +// MapValidatorsToAddresses maps a slice of validator to a slice of addresses +func MapValidatorsToAddresses(validators []Validator) []common.Address { + returnList := make([]common.Address, len(validators)) + + for i, val := range validators { + returnList[i] = val.Address() + } + + return returnList +} + +// MapValidatorsToPublicKeys maps a slice of validator to a slice of public keys +func MapValidatorsToPublicKeys(validators []Validator) []blscrypto.SerializedPublicKey { + returnList := make([]blscrypto.SerializedPublicKey, len(validators)) + + for i, val := range validators { + returnList[i] = val.BLSPublicKey() + } + + return returnList +} + +// ---------------------------------------------------------------------------- + +type ValidatorsDataByAddress []ValidatorData + +func (a ValidatorsDataByAddress) Len() int { return len(a) } +func (a ValidatorsDataByAddress) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ValidatorsDataByAddress) Less(i, j int) bool { + return bytes.Compare(a[i].Address[:], a[j].Address[:]) < 0 +} + +// ---------------------------------------------------------------------------- + +type ValidatorSet interface { + fmt.Stringer + + // Sets the randomness for use in the proposer policy. + // This is injected into the ValidatorSet when we call `getOrderedValidators` + SetRandomness(seed common.Hash) + // Sets the randomness for use in the proposer policy + GetRandomness() common.Hash + + // Return the validator size + Size() int + // Get the maximum number of faulty nodes + F() int + // Get the minimum quorum size + MinQuorumSize() int + + // List returns all the validators + List() []Validator + // Return the validator index + GetIndex(addr common.Address) int + // Get validator by index + GetByIndex(i uint64) Validator + // Get validator by given address + GetByAddress(addr common.Address) (int, Validator) + // CointainByAddress indicates if a validator with the given address is present + ContainsByAddress(add common.Address) bool + + // Add validators + AddValidators(validators []ValidatorData) bool + // Remove validators + RemoveValidators(removedValidators *big.Int) bool + // Copy validator set + Copy() ValidatorSet + + // CacheUncompressedBLSKey stores the uncompressed BLS public key to cache for each validator in the valset + CacheUncompressedBLSKey() + + // HasBLSKeyCache tests that all uncompressed BLS public keys are in the cache, otherwise returns false + HasBLSKeyCache() bool + + // Serialize returns binary reprentation of the ValidatorSet + // can be use used to instantiate a validator with DeserializeValidatorSet() + Serialize() ([]byte, error) +} + +type ValidatorSetData struct { + Validators []ValidatorData + Randomness common.Hash +} + +type ValidatorSetDataWithBLSKeyCache struct { + Validators []ValidatorDataWithBLSKeyCache + Randomness common.Hash +} + +// ---------------------------------------------------------------------------- + +// ProposerSelector returns the block proposer for a round given the last proposer, round number, and randomness. +type ProposerSelector func(validatorSet ValidatorSet, lastBlockProposer common.Address, currentRound uint64) Validator + +// ---------------------------------------------------------------------------- + +func CombineIstanbulExtraToValidatorData(addrs []common.Address, blsPublicKeys []blscrypto.SerializedPublicKey) ([]ValidatorData, error) { + if len(addrs) != len(blsPublicKeys) { + return nil, errInvalidValidatorSetDiffSize + } + validators := []ValidatorData{} + for i := range addrs { + validators = append(validators, ValidatorData{ + Address: addrs[i], + BLSPublicKey: blsPublicKeys[i], + }) + } + + return validators, nil +} + +func SeparateValidatorDataIntoIstanbulExtra(validators []ValidatorData) ([]common.Address, []blscrypto.SerializedPublicKey) { + addrs := []common.Address{} + pubKeys := []blscrypto.SerializedPublicKey{} + for i := range validators { + addrs = append(addrs, validators[i].Address) + pubKeys = append(pubKeys, validators[i].BLSPublicKey) + } + + return addrs, pubKeys +}
diff --git go-ethereum/consensus/istanbul/types.go celo/consensus/istanbul/types.go new file mode 100644 index 0000000000000000000000000000000000000000..18fafd8838275ed23645d80d993c94f08ff851e1 --- /dev/null +++ celo/consensus/istanbul/types.go @@ -0,0 +1,1021 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package istanbul + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "io" + "math/big" + "time" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" +) + +// Decrypt is a decrypt callback function to request an ECIES ciphertext to be +// decrypted +type DecryptFn func(accounts.Account, []byte, []byte, []byte) ([]byte, error) + +// SignerFn is a signer callback function to request a header to be signed by a +// backing account. +type SignerFn func(accounts.Account, string, []byte) ([]byte, error) + +// BLSSignerFn is a signer callback function to request a message and extra data to be signed by a +// backing account using BLS with a direct or composite hasher +type BLSSignerFn func(accounts.Account, []byte, []byte, bool, bool) (blscrypto.SerializedSignature, error) + +// HashSignerFn is a signer callback function to request a hash to be signed by a +// backing account. +type HashSignerFn func(accounts.Account, []byte) ([]byte, error) + +// Proposal supports retrieving height and serialized block to be used during Istanbul consensus. +type Proposal interface { + // Number retrieves the sequence number of this proposal. + Number() *big.Int + + Header() *types.Header + + // Hash retrieves the hash of this block + Hash() common.Hash + + // ParentHash retrieves the hash of this block's parent + ParentHash() common.Hash + + EncodeRLP(w io.Writer) error + + DecodeRLP(s *rlp.Stream) error +} + +// ## Request ############################################################## + +type Request struct { + Proposal Proposal +} + +// EncodeRLP serializes b into the Ethereum RLP format. +func (b *Request) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{b.Proposal}) +} + +// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream. +func (b *Request) DecodeRLP(s *rlp.Stream) error { + var request struct { + Proposal *types.Block + } + + if err := s.Decode(&request); err != nil { + return err + } + + b.Proposal = request.Proposal + return nil +} + +// ## View ############################################################## + +// View includes a round number and a sequence number. +// Sequence is the block number we'd like to commit. +// Each round has a number and is composed by 3 steps: preprepare, prepare and commit. +// +// If the given block is not accepted by validators, a round change will occur +// and the validators start a new round with round+1. +type View struct { + Round *big.Int + Sequence *big.Int +} + +func (v *View) String() string { + if v.Round == nil || v.Sequence == nil { + return "Invalid" + } + return fmt.Sprintf("{Round: %d, Sequence: %d}", v.Round.Uint64(), v.Sequence.Uint64()) +} + +// Cmp compares v and y and returns: +// -1 if v < y +// 0 if v == y +// +1 if v > y +func (v *View) Cmp(y *View) int { + if v.Sequence.Cmp(y.Sequence) != 0 { + return v.Sequence.Cmp(y.Sequence) + } + if v.Round.Cmp(y.Round) != 0 { + return v.Round.Cmp(y.Round) + } + return 0 +} + +type PreprepareSummary struct { + View *View `json:"view"` + ProposalHash common.Hash `json:"proposalHash"` + RoundChangeCertificateSenders []common.Address `json:"roundChangeCertificateSenders"` +} + +// ## PreparedCertificate ##################################################### + +type PreparedCertificate struct { + Proposal Proposal + PrepareOrCommitMessages []Message +} + +type PreparedCertificateData struct { + Proposal *types.Block + PrepareOrCommitMessages []Message +} + +type PreparedCertificateSummary struct { + ProposalHash common.Hash `json:"proposalHash"` + PrepareSenders []common.Address `json:"prepareSenders"` + CommitSenders []common.Address `json:"commitSenders"` +} + +func EmptyPreparedCertificate() PreparedCertificate { + emptyHeader := &types.Header{ + Number: big.NewInt(0), + GasUsed: 0, + Time: 0, + } + block := &types.Block{} + block = block.WithRandomness(&types.EmptyRandomness) + block = block.WithEpochSnarkData(&types.EmptyEpochSnarkData) + + return PreparedCertificate{ + Proposal: block.WithHeader(emptyHeader), + PrepareOrCommitMessages: []Message{}, + } +} + +func EmptyPreparedCertificateV2() (PreparedCertificateV2, Proposal) { + pc := EmptyPreparedCertificate() + return PCV2FromPCV1(pc), pc.Proposal +} + +func (pc *PreparedCertificate) IsEmpty() bool { + return len(pc.PrepareOrCommitMessages) == 0 +} + +func (pc *PreparedCertificate) AsData() *PreparedCertificateData { + return &PreparedCertificateData{ + Proposal: pc.Proposal.(*types.Block), + PrepareOrCommitMessages: pc.PrepareOrCommitMessages, + } +} + +func (pc *PreparedCertificate) Summary() *PreparedCertificateSummary { + var prepareSenders, commitSenders []common.Address + for _, msg := range pc.PrepareOrCommitMessages { + if msg.Code == MsgPrepare { + prepareSenders = append(prepareSenders, msg.Address) + } else { + commitSenders = append(commitSenders, msg.Address) + } + } + + return &PreparedCertificateSummary{ + ProposalHash: pc.Proposal.Hash(), + PrepareSenders: prepareSenders, + CommitSenders: commitSenders, + } +} + +// RLP Encoding --------------------------------------------------------------- + +// EncodeRLP serializes b into the Ethereum RLP format. +func (pc *PreparedCertificate) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, pc.AsData()) +} + +// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream. +func (pc *PreparedCertificate) DecodeRLP(s *rlp.Stream) error { + var data PreparedCertificateData + if err := s.Decode(&data); err != nil { + return err + } + pc.PrepareOrCommitMessages, pc.Proposal = data.PrepareOrCommitMessages, data.Proposal + return nil + +} + +// ## Subject ################################################################# + +// NewPrepareMessage constructs a Message instance with the given sender and +// subject. Both the subject instance and the serialized bytes of subject are +// part of the returned Message. +func NewPrepareMessage(subject *Subject, sender common.Address) *Message { + message := &Message{ + Address: sender, + Code: MsgPrepare, + prepare: subject, + } + setMessageBytes(message, subject) + return message +} + +type Subject struct { + View *View + Digest common.Hash +} + +func (s *Subject) String() string { + return fmt.Sprintf("{View: %v, Digest: %v}", s.View, s.Digest.String()) +} + +// ## CommittedSubject ################################################################# + +// NewCommitMessage constructs a Message instance with the given sender and +// commit. Both the commit instance and the serialized bytes of commit are +// part of the returned Message. +func NewCommitMessage(commit *CommittedSubject, sender common.Address) *Message { + message := &Message{ + Address: sender, + Code: MsgCommit, + committedSubject: commit, + } + setMessageBytes(message, commit) + return message +} + +type CommittedSubject struct { + Subject *Subject + CommittedSeal []byte + EpochValidatorSetSeal []byte +} + +// ## ForwardMessage ################################################################# + +// NewForwardMessage constructs a Message instance with the given sender and +// forwardMessage. Both the forwardMessage instance and the serialized bytes of +// fowardMessage are part of the returned Message. +func NewForwardMessage(fowardMessage *ForwardMessage, sender common.Address) *Message { + message := &Message{ + Address: sender, + Code: FwdMsg, + forwardMessage: fowardMessage, + } + setMessageBytes(message, fowardMessage) + return message +} + +type ForwardMessage struct { + Code uint64 + Msg []byte + DestAddresses []common.Address +} + +// =============================================================== +// +// define the IstanbulQueryEnode message format, the QueryEnodeMsgCache entries, the queryEnode send function (both the gossip version and the "retrieve from cache" version), and the announce get function + +// NewQueryEnodeMessage constructs a Message instance with the given sender and +// queryEnode. Both the queryEnode instance and the serialized bytes of +// queryEnode are part of the returned Message. +func NewQueryEnodeMessage(queryEnode *QueryEnodeData, sender common.Address) *Message { + message := &Message{ + Address: sender, + Code: QueryEnodeMsg, + queryEnode: queryEnode, + } + setMessageBytes(message, queryEnode) + return message +} + +type EncryptedEnodeURL struct { + DestAddress common.Address + EncryptedEnodeURL []byte +} + +func (ee *EncryptedEnodeURL) String() string { + return fmt.Sprintf("{DestAddress: %s, EncryptedEnodeURL length: %d}", ee.DestAddress.String(), len(ee.EncryptedEnodeURL)) +} + +type QueryEnodeData struct { + EncryptedEnodeURLs []*EncryptedEnodeURL + Version uint + // The timestamp of the node when the message is generated. + // This results in a new hash for a newly generated message so it gets regossiped by other nodes + Timestamp uint +} + +func (qed *QueryEnodeData) String() string { + return fmt.Sprintf("{Version: %v, Timestamp: %v, EncryptedEnodeURLs: %v}", qed.Version, qed.Timestamp, qed.EncryptedEnodeURLs) +} + +// HasDuplicates returns true if there are duplicate destination addresses in the query, and the first +// duplicate's destination address. +func (qed *QueryEnodeData) HasDuplicates() (bool, common.Address) { + var encounteredAddresses = make(map[common.Address]bool) + for _, encEnodeURL := range qed.EncryptedEnodeURLs { + if encounteredAddresses[encEnodeURL.DestAddress] { + return true, encEnodeURL.DestAddress + } + + encounteredAddresses[encEnodeURL.DestAddress] = true + } + return false, common.Address{} +} + +// ============================================== +// +// define the functions that needs to be provided for rlp Encoder/Decoder. + +// EncodeRLP serializes ar into the Ethereum RLP format. +func (ee *EncryptedEnodeURL) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{ee.DestAddress, ee.EncryptedEnodeURL}) +} + +// DecodeRLP implements rlp.Decoder, and load the ar fields from a RLP stream. +func (ee *EncryptedEnodeURL) DecodeRLP(s *rlp.Stream) error { + var msg struct { + DestAddress common.Address + EncryptedEnodeURL []byte + } + + if err := s.Decode(&msg); err != nil { + return err + } + ee.DestAddress, ee.EncryptedEnodeURL = msg.DestAddress, msg.EncryptedEnodeURL + return nil +} + +// EncodeRLP serializes ad into the Ethereum RLP format. +func (qed *QueryEnodeData) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{qed.EncryptedEnodeURLs, qed.Version, qed.Timestamp}) +} + +// DecodeRLP implements rlp.Decoder, and load the ad fields from a RLP stream. +func (qed *QueryEnodeData) DecodeRLP(s *rlp.Stream) error { + var msg struct { + EncryptedEnodeURLs []*EncryptedEnodeURL + Version uint + Timestamp uint + } + + if err := s.Decode(&msg); err != nil { + return err + } + qed.EncryptedEnodeURLs, qed.Version, qed.Timestamp = msg.EncryptedEnodeURLs, msg.Version, msg.Timestamp + return nil +} + +// ## Consensus Message codes ########################################################## + +const ( + DEPRECATED_MsgPreprepare uint64 = iota // Moved to V2 + MsgPrepare + MsgCommit + DEPRECATED_MsgRoundChange // Moved to V2 + MsgRoundChangeV2 + MsgPreprepareV2 +) + +// IsRoundChangeCode returns true if and only if the message code equals MsgRoundChangeV2 +func IsRoundChangeCode(istanbulMsgCode uint64) bool { + return istanbulMsgCode == MsgRoundChangeV2 +} + +// IsPreprepareCode returns true if and only if the message code equals MsgPreprepareV2 +func IsPreprepareCode(istanbulMsgCode uint64) bool { + return istanbulMsgCode == MsgPreprepareV2 +} + +// Message is a wrapper used for all istanbul communication. It encapsulates +// the sender's address, a code that indicates the type of the wrapped message +// and a signature. Message instances also hold a deserialised instance of the +// inner message which can be retrieved by calling the corresponding function +// (Commit(), Preprepare() ... etc). +// +// Messages should be initialised either through the use of one of the +// NewXXXMessage constructors or by calling FromPayload on an empty Message +// instance, these mechanisms ensure that the produced Message instances will +// contain the deserialised inner message instance and the serialised bytes of +// the inner message. +type Message struct { + Code uint64 + Msg []byte // The serialised bytes of the innner message. + Address common.Address // The sender address + Signature []byte // Signature of the Message using the private key associated with the "Address" field + + // The below fields are the potential inner message instances only one + // should be set for a message instance. These fields are not rlp + // serializable since they are private. They are set when calling + // Message.FromPayload, or at message construction time. + committedSubject *CommittedSubject + prePrepareV2 *PreprepareV2 + prepare *Subject + roundChangeV2 *RoundChangeV2 + queryEnode *QueryEnodeData + forwardMessage *ForwardMessage + enodeCertificate *EnodeCertificate + versionCertificates []*VersionCertificate + valEnodeShareData *ValEnodesShareData +} + +// setMessageBytes sets the Msg field of msg to the rlp serialised bytes of +// innerMessage. If innerMessage fails serialisation then this function +// panics. This is intended for use by NewXXXMessage constructors only. +func setMessageBytes(msg *Message, innerMessage interface{}) { + bytes, err := rlp.EncodeToBytes(innerMessage) + if err != nil { + panic(fmt.Sprintf("attempt to serialise inner message of type %T failed. %s", innerMessage, err)) + } + msg.Msg = bytes +} + +func (m *Message) Sign(signingFn func(data []byte) ([]byte, error)) error { + // Construct and encode a message with no signature + payloadNoSig, err := m.PayloadNoSig() + if err != nil { + return err + } + m.Signature, err = signingFn(payloadNoSig) + return err +} + +func (m *Message) DecodeMessage() error { + var err error + switch m.Code { + case MsgPreprepareV2: + var p *PreprepareV2 + err = m.decode(&p) + if err != nil { + return err + } + m.prePrepareV2 = p + case MsgPrepare: + var p *Subject + err = m.decode(&p) + m.prepare = p + case MsgCommit: + var cs *CommittedSubject + err = m.decode(&cs) + m.committedSubject = cs + case MsgRoundChangeV2: + var p *RoundChangeV2 + err = m.decode(&p) + if err != nil { + return err + } + m.roundChangeV2 = p + case QueryEnodeMsg: + var q *QueryEnodeData + err = m.decode(&q) + m.queryEnode = q + case FwdMsg: + var f *ForwardMessage + err = m.decode(&f) + m.forwardMessage = f + case EnodeCertificateMsg: + var e *EnodeCertificate + err = m.decode(&e) + m.enodeCertificate = e + case VersionCertificatesMsg: + var v []*VersionCertificate + err = m.decode(&v) + m.versionCertificates = v + case ValEnodesShareMsg: + var v *ValEnodesShareData + err = m.decode(&v) + m.valEnodeShareData = v + default: + err = fmt.Errorf("unrecognised message code %d", m.Code) + } + return err +} + +func (m *Message) DecodeRLP(stream *rlp.Stream) error { + type decodable Message + var d decodable + err := stream.Decode(&d) + if err != nil { + return err + } + *m = Message(d) + + if len(m.Msg) == 0 && len(m.Signature) == 0 { + // Empty validator handshake message + return nil + } + + return m.DecodeMessage() +} + +// FromPayload decodes b into a Message instance it will set one of the private +// fields committedSubject, prePrepare, prepare or roundChange depending on the +// type of the message. +func (m *Message) FromPayload(b []byte, validateFn ValidateFn) error { + // Decode Message + err := rlp.DecodeBytes(b, &m) + if err != nil { + return err + } + + // Validate message (on a message without Signature) + if validateFn != nil { + if err := CheckSignedBy(m, m.Signature, m.Address, ErrInvalidSigner, validateFn); err != nil { + return err + } + } + return nil +} + +func (m *Message) Payload() ([]byte, error) { + return rlp.EncodeToBytes(m) +} + +func (m *Message) PayloadNoSig() ([]byte, error) { + return rlp.EncodeToBytes(&Message{ + Code: m.Code, + Msg: m.Msg, + Address: m.Address, + Signature: []byte{}, + }) +} + +func (m *Message) decode(val interface{}) error { + return rlp.DecodeBytes(m.Msg, val) +} + +func (m *Message) String() string { + return fmt.Sprintf("{Code: %v, Address: %v}", m.Code, m.Address.String()) +} + +// Commit returns the committed subject if this is a commit message. +func (m *Message) Commit() *CommittedSubject { + return m.committedSubject +} + +// PreprepareV2 returns preprepare if this is a preprepare message. +func (m *Message) PreprepareV2() *PreprepareV2 { + return m.prePrepareV2 +} + +// Prepare returns prepare if this is a prepare message. +func (m *Message) Prepare() *Subject { + return m.prepare +} + +// RoundChangeV2 returns a round change v2 if this is a round change v2 message. +func (m *Message) RoundChangeV2() *RoundChangeV2 { + return m.roundChangeV2 +} + +// QueryEnode returns query enode data if this is a query enode message. +func (m *Message) QueryEnodeMsg() *QueryEnodeData { + return m.queryEnode +} + +// ForwardMessage returns forward message if this is a forward message. +func (m *Message) ForwardMessage() *ForwardMessage { + return m.forwardMessage +} + +// EnodeCertificate returns the enode certificate if this is an enode +// certificate message +func (m *Message) EnodeCertificate() *EnodeCertificate { + return m.enodeCertificate +} + +// VersionCertificates returns the version certificate entries if this is a +// version certificates message. +func (m *Message) VersionCertificates() []*VersionCertificate { + return m.versionCertificates +} + +// ValEnodesShareData returns val enode share data if this is a val enodes share message. +func (m *Message) ValEnodesShareData() *ValEnodesShareData { + return m.valEnodeShareData +} + +func (m *Message) Copy() *Message { + return &Message{ + Code: m.Code, + Msg: append(m.Msg[:0:0], m.Msg...), + Address: m.Address, + Signature: append(m.Signature[:0:0], m.Signature...), + } +} + +// MapMessagesToSenders map a list of Messages to the list of the sender addresses +func MapMessagesToSenders(messages []Message) []common.Address { + returnList := make([]common.Address, len(messages)) + + for i, ms := range messages { + returnList[i] = ms.Address + } + + return returnList +} + +// ## EnodeCertificate ###################################################################### + +// NewValEnodesShareMessage constructs a Message instance with the given sender +// and enodeCertificate. Both the enodeCertificate instance and the serialized +// bytes of enodeCertificate are part of the returned Message. +func NewEnodeCeritifcateMessage(enodeCertificate *EnodeCertificate, sender common.Address) *Message { + message := &Message{ + Address: sender, + Code: EnodeCertificateMsg, + enodeCertificate: enodeCertificate, + } + setMessageBytes(message, enodeCertificate) + return message +} + +type EnodeCertificate struct { + EnodeURL string + Version uint +} + +// EncodeRLP serializes ec into the Ethereum RLP format. +func (ec *EnodeCertificate) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{ec.EnodeURL, ec.Version}) +} + +// DecodeRLP implements rlp.Decoder, and load the ec fields from a RLP stream. +func (ec *EnodeCertificate) DecodeRLP(s *rlp.Stream) error { + var msg struct { + EnodeURL string + Version uint + } + + if err := s.Decode(&msg); err != nil { + return err + } + ec.EnodeURL, ec.Version = msg.EnodeURL, msg.Version + return nil +} + +// ## EnodeCertMsg ###################################################################### +type EnodeCertMsg struct { + Msg *Message + DestAddresses []common.Address +} + +// ## AddressEntry ###################################################################### +// AddressEntry is an entry for the valEnodeTable. +type AddressEntry struct { + Address common.Address + PublicKey *ecdsa.PublicKey + Node *enode.Node + Version uint + HighestKnownVersion uint + NumQueryAttemptsForHKVersion uint + LastQueryTimestamp *time.Time +} + +func (ae *AddressEntry) String() string { + var nodeString string + if ae.Node != nil { + nodeString = ae.Node.String() + } + return fmt.Sprintf("{address: %v, enodeURL: %v, version: %v, highestKnownVersion: %v, numQueryAttempsForHKVersion: %v, LastQueryTimestamp: %v}", ae.Address.String(), nodeString, ae.Version, ae.HighestKnownVersion, ae.NumQueryAttemptsForHKVersion, ae.LastQueryTimestamp) +} + +// Implement RLP Encode/Decode interface +type AddressEntryRLP struct { + Address common.Address + CompressedPublicKey []byte + EnodeURL string + Version uint + HighestKnownVersion uint + NumQueryAttemptsForHKVersion uint + LastQueryTimestamp []byte +} + +// EncodeRLP serializes AddressEntry into the Ethereum RLP format. +func (ae *AddressEntry) EncodeRLP(w io.Writer) error { + var nodeString string + if ae.Node != nil { + nodeString = ae.Node.String() + } + var publicKeyBytes []byte + if ae.PublicKey != nil { + publicKeyBytes = crypto.CompressPubkey(ae.PublicKey) + } + var lastQueryTimestampBytes []byte + if ae.LastQueryTimestamp != nil { + var err error + lastQueryTimestampBytes, err = ae.LastQueryTimestamp.MarshalBinary() + if err != nil { + return err + } + } + + return rlp.Encode(w, AddressEntryRLP{Address: ae.Address, + CompressedPublicKey: publicKeyBytes, + EnodeURL: nodeString, + Version: ae.Version, + HighestKnownVersion: ae.HighestKnownVersion, + NumQueryAttemptsForHKVersion: ae.NumQueryAttemptsForHKVersion, + LastQueryTimestamp: lastQueryTimestampBytes}) +} + +// DecodeRLP implements rlp.Decoder, and load the AddressEntry fields from a RLP stream. +func (ae *AddressEntry) DecodeRLP(s *rlp.Stream) error { + var entry AddressEntryRLP + var err error + if err := s.Decode(&entry); err != nil { + return err + } + var node *enode.Node + if len(entry.EnodeURL) > 0 { + node, err = enode.ParseV4(entry.EnodeURL) + if err != nil { + return err + } + } + var publicKey *ecdsa.PublicKey + if len(entry.CompressedPublicKey) > 0 { + publicKey, err = crypto.DecompressPubkey(entry.CompressedPublicKey) + if err != nil { + return err + } + } + lastQueryTimestamp := &time.Time{} + if len(entry.LastQueryTimestamp) > 0 { + err := lastQueryTimestamp.UnmarshalBinary(entry.LastQueryTimestamp) + if err != nil { + return err + } + } + + *ae = AddressEntry{Address: entry.Address, + PublicKey: publicKey, + Node: node, + Version: entry.Version, + HighestKnownVersion: entry.HighestKnownVersion, + NumQueryAttemptsForHKVersion: entry.NumQueryAttemptsForHKVersion, + LastQueryTimestamp: lastQueryTimestamp} + return nil +} + +// GetNode returns the address entry's node +func (ae *AddressEntry) GetNode() *enode.Node { + return ae.Node +} + +// GetVersion returns the addess entry's version +func (ae *AddressEntry) GetVersion() uint { + return ae.Version +} + +// GetAddess returns the addess entry's address +func (ae *AddressEntry) GetAddress() common.Address { + return ae.Address +} + +// ## VersionCertificate ###################################################################### + +// NewVersionCeritifcatesMessage constructs a Message instance with the given sender +// and versionCertificates. Both the versionCertificates instance and the serialized +// bytes of versionCertificates are part of the returned Message. +func NewVersionCeritifcatesMessage(versionCertificates []*VersionCertificate, sender common.Address) *Message { + message := &Message{ + Address: sender, + Code: VersionCertificatesMsg, + versionCertificates: versionCertificates, + } + setMessageBytes(message, versionCertificates) + return message +} + +// VersionCertificate is an entry in the VersionCertificateDB. +// It's a signed message from a registered or active validator indicating +// the most recent version of its enode. +type VersionCertificate struct { + Version uint + Signature []byte + address common.Address + pubKey *ecdsa.PublicKey +} + +// NewVersionCeritifcate constructs a VersionCertificate instance with the +// given version. It uses the signingFn to generate a version signature and +// then builds a version certificate from the version and its signature. +func NewVersionCertificate(version uint, signingFn func([]byte) ([]byte, error)) (*VersionCertificate, error) { + vc := &VersionCertificate{Version: version} + payloadToSign, err := vc.signaturePayload() + if err != nil { + return nil, err + } + vc.Signature, err = signingFn(payloadToSign) + if err != nil { + return nil, err + } + err = vc.recoverAddressAndPubKey() + if err != nil { + return nil, err + } + + return vc, nil +} + +// NewVersionCeritifcateFrom fields constructs a VersionCertificate instance +// with the given fields, using them to build a VersionCertificate instance. +// No validation is done on the provided fields. It +// is assumed that the fields are valid, meaning that the signature was +// generated using the given version and the private part of the given public +// key, and also that the address corresponds to the given public key. +func NewVersionCertificateFromFields(version uint, signature []byte, address common.Address, key *ecdsa.PublicKey) *VersionCertificate { + return &VersionCertificate{ + Version: version, + Signature: signature, + address: address, + pubKey: key, + } +} + +// Used as a salt when signing versionCertificate. This is to account for +// the unlikely case where a different signed struct with the same field types +// is used elsewhere and shared with other nodes. If that were to happen, a +// malicious node could try sending the other struct where this struct is used, +// or vice versa. This ensures that the signature is only valid for this struct. +var versionCertificateSalt = []byte("versionCertificate") + +func (vc *VersionCertificate) signaturePayload() ([]byte, error) { + return rlp.EncodeToBytes([]interface{}{versionCertificateSalt, vc.Version}) +} + +func (vc *VersionCertificate) Address() common.Address { + return vc.address +} + +func (vc *VersionCertificate) PublicKey() *ecdsa.PublicKey { + return vc.pubKey +} + +func (vc *VersionCertificate) String() string { + return fmt.Sprintf("%d", vc.Version) +} + +func (vc *VersionCertificate) DecodeRLP(s *rlp.Stream) error { + // Create separate type to avoid stack overflow when calling Decode + type decodable VersionCertificate + var d decodable + if err := s.Decode(&d); err != nil { + return err + } + // copy struct data + *vc = VersionCertificate(d) + + return vc.recoverAddressAndPubKey() +} + +func (vc *VersionCertificate) recoverAddressAndPubKey() error { + payloadToSign, err := vc.signaturePayload() + if err != nil { + return err + } + payloadHash := crypto.Keccak256(payloadToSign) + vc.pubKey, err = crypto.SigToPub(payloadHash, vc.Signature) + if err != nil { + return err + } + vc.address = crypto.PubkeyToAddress(*vc.pubKey) + return nil +} + +// ## SharedValidatorEnode ###################################################################### + +// NewValEnodesShareMessage constructs a Message instance with the given sender +// and valEnodeShareData. Both the valEnodeShareData instance and the +// serialized bytes of valEnodeShareData are part of the returned Message. +func NewValEnodesShareMessage(valEnodeShareData *ValEnodesShareData, sender common.Address) *Message { + message := &Message{ + Address: sender, + Code: ValEnodesShareMsg, + valEnodeShareData: valEnodeShareData, + } + setMessageBytes(message, valEnodeShareData) + return message +} + +type SharedValidatorEnode struct { + Address common.Address + EnodeURL string + Version uint +} + +type ValEnodesShareData struct { + ValEnodes []SharedValidatorEnode +} + +func (sve *SharedValidatorEnode) String() string { + return fmt.Sprintf("{Address: %s, EnodeURL: %v, Version: %v}", sve.Address.Hex(), sve.EnodeURL, sve.Version) +} + +func (sd *ValEnodesShareData) String() string { + outputStr := "{ValEnodes:" + for _, valEnode := range sd.ValEnodes { + outputStr = fmt.Sprintf("%s %s", outputStr, valEnode.String()) + } + return fmt.Sprintf("%s}", outputStr) +} + +// EncodeRLP serializes sd into the Ethereum RLP format. +func (sd *ValEnodesShareData) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{sd.ValEnodes}) +} + +// DecodeRLP implements rlp.Decoder, and load the sd fields from a RLP stream. +func (sd *ValEnodesShareData) DecodeRLP(s *rlp.Stream) error { + var msg struct { + ValEnodes []SharedValidatorEnode + } + + if err := s.Decode(&msg); err != nil { + return err + } + sd.ValEnodes = msg.ValEnodes + return nil +} + +var ( + // errInvalidSigningFn is returned when the consensus signing function is invalid. + errInvalidSigningFn = errors.New("invalid signing function for istanbul messages") +) + +type EcdsaInfo struct { + Address common.Address // Ethereum address of the ECDSA signing key + PublicKey *ecdsa.PublicKey // The signer public key + + decrypt DecryptFn // Decrypt function to decrypt ECIES ciphertext + sign SignerFn // Signer function to authorize hashes with + signHash HashSignerFn // Signer function to create random seed +} + +func NewEcdsaInfo(ecdsaAddress common.Address, publicKey *ecdsa.PublicKey, + decryptFn DecryptFn, signFn SignerFn, signHashFn HashSignerFn) *EcdsaInfo { + return &EcdsaInfo{ + Address: ecdsaAddress, + PublicKey: publicKey, + decrypt: decryptFn, + sign: signFn, + signHash: signHashFn, + } +} + +// Sign hashes and signs the data with the ecdsa account +func (ei EcdsaInfo) Sign(data []byte) ([]byte, error) { + if ei.sign == nil { + return nil, errInvalidSigningFn + } + return ei.sign(accounts.Account{Address: ei.Address}, accounts.MimetypeIstanbul, data) +} + +// SignHash signs the given hash with the ecdsa account +func (ei EcdsaInfo) SignHash(hash common.Hash) ([]byte, error) { + return ei.signHash(accounts.Account{Address: ei.Address}, hash.Bytes()) +} + +// Decrypt is a decrypt callback function to request an ECIES ciphertext to be +// decrypted +func (ei EcdsaInfo) Decrypt(payload []byte) ([]byte, error) { + return ei.decrypt(accounts.Account{Address: ei.Address}, payload, nil, nil) +} + +type BlsInfo struct { + Address common.Address // Ethereum address of the BLS signing key + sign BLSSignerFn // Signer function to authorize BLS messages +} + +func NewBlsInfo(blsAddress common.Address, signBLSFn BLSSignerFn) *BlsInfo { + return &BlsInfo{ + Address: blsAddress, + sign: signBLSFn, + } +} + +// Sign signs with the bls account +func (bi *BlsInfo) Sign(data []byte, extra []byte, useComposite, cip22 bool) (blscrypto.SerializedSignature, error) { + if bi.sign == nil { + return blscrypto.SerializedSignature{}, errInvalidSigningFn + } + return bi.sign(accounts.Account{Address: bi.Address}, data, extra, useComposite, cip22) +} + +type Wallets struct { + Ecdsa EcdsaInfo + Bls BlsInfo +}
diff --git go-ethereum/consensus/istanbul/config.go celo/consensus/istanbul/config.go new file mode 100644 index 0000000000000000000000000000000000000000..e7c4d28bd32e7ad4268543cd50549712e37cb035 --- /dev/null +++ celo/consensus/istanbul/config.go @@ -0,0 +1,122 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package istanbul + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" +) + +const ( + //MinEpochSize represents the minimum permissible epoch size + MinEpochSize = 3 +) + +// ProposerPolicy represents the policy used to order elected validators within an epoch +type ProposerPolicy uint64 + +const ( + RoundRobin ProposerPolicy = iota + Sticky + ShuffledRoundRobin +) + +// Config represents the istanbul consensus engine +type Config struct { + RequestTimeout uint64 `toml:",omitempty"` // The timeout for each Istanbul round in milliseconds. + TimeoutBackoffFactor uint64 `toml:",omitempty"` // Timeout at subsequent rounds is: RequestTimeout + 2**round * TimeoutBackoffFactor (in milliseconds) + MinResendRoundChangeTimeout uint64 `toml:",omitempty"` // Minimum interval with which to resend RoundChange messages for same round + MaxResendRoundChangeTimeout uint64 `toml:",omitempty"` // Maximum interval with which to resend RoundChange messages for same round + BlockPeriod uint64 `toml:",omitempty"` // Default minimum difference between two consecutive block's timestamps in second + ProposerPolicy ProposerPolicy `toml:",omitempty"` // The policy for proposer selection + Epoch uint64 `toml:",omitempty"` // The number of blocks after which to checkpoint and reset the pending votes + + ReplicaStateDBPath string `toml:",omitempty"` // The location for the validator replica state DB + ValidatorEnodeDBPath string `toml:",omitempty"` // The location for the validator enodes DB + VersionCertificateDBPath string `toml:",omitempty"` // The location for the signed announce version DB + RoundStateDBPath string `toml:",omitempty"` // The location for the round states DB + Validator bool `toml:",omitempty"` // Specified if this node is configured to validate (specifically if --mine command line is set) + Replica bool `toml:",omitempty"` // Specified if this node is configured to be a replica + + // Proxy Configs + Proxy bool `toml:",omitempty"` // Specifies if this node is a proxy + ProxiedValidatorAddress common.Address `toml:",omitempty"` // The address of the proxied validator + + // Proxied Validator Configs + Proxied bool `toml:",omitempty"` // Specifies if this node is proxied + ProxyConfigs []*ProxyConfig `toml:",omitempty"` // The set of proxy configs for this proxied validator at startup + + // Announce Configs + AnnounceQueryEnodeGossipPeriod uint64 `toml:",omitempty"` // Time duration (in seconds) between gossiped query enode messages + AnnounceAggressiveQueryEnodeGossipOnEnablement bool `toml:",omitempty"` // Specifies if this node should aggressively query enodes on announce enablement + AnnounceAdditionalValidatorsToGossip int64 `toml:",omitempty"` // Specifies the number of additional non-elected validators to gossip an announce + + // Load test config + LoadTestCSVFile string `toml:",omitempty"` // If non-empty, specifies the file to write out csv metrics about the block production cycle to. +} + +// ProxyConfig represents the configuration for validator's proxies +type ProxyConfig struct { + InternalNode *enode.Node `toml:",omitempty"` // The internal facing node of the proxy that this proxied validator will peer with + ExternalNode *enode.Node `toml:",omitempty"` // The external facing node of the proxy that the proxied validator will broadcast via the announce message +} + +// DefaultConfig for istanbul consensus engine +var DefaultConfig = &Config{ + RequestTimeout: 3000, + TimeoutBackoffFactor: 1000, + MinResendRoundChangeTimeout: 15 * 1000, + MaxResendRoundChangeTimeout: 2 * 60 * 1000, + BlockPeriod: 5, + ProposerPolicy: ShuffledRoundRobin, + Epoch: 30000, + ReplicaStateDBPath: "replicastate", + ValidatorEnodeDBPath: "validatorenodes", + VersionCertificateDBPath: "versioncertificates", + RoundStateDBPath: "roundstates", + Validator: false, + Replica: false, + Proxy: false, + Proxied: false, + AnnounceQueryEnodeGossipPeriod: 300, // 5 minutes + AnnounceAggressiveQueryEnodeGossipOnEnablement: true, + AnnounceAdditionalValidatorsToGossip: 10, + LoadTestCSVFile: "", // disable by default +} + +//ApplyParamsChainConfigToConfig applies the istanbul config values from params.chainConfig to the istanbul.Config config +func ApplyParamsChainConfigToConfig(chainConfig *params.ChainConfig, config *Config) error { + if chainConfig.Istanbul.Epoch != 0 { + if chainConfig.Istanbul.Epoch < MinEpochSize { + return fmt.Errorf("istanbul.Epoch must be greater than %d", MinEpochSize-1) + } + + config.Epoch = chainConfig.Istanbul.Epoch + } + if chainConfig.Istanbul.RequestTimeout != 0 { + config.RequestTimeout = chainConfig.Istanbul.RequestTimeout + } + if chainConfig.Istanbul.BlockPeriod != 0 { + config.BlockPeriod = chainConfig.Istanbul.BlockPeriod + } + config.ProposerPolicy = ProposerPolicy(chainConfig.Istanbul.ProposerPolicy) + + return nil +}
diff --git go-ethereum/consensus/istanbul/errors.go celo/consensus/istanbul/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..eaf81f67ca411588cc0ef65fbe14c97b65060394 --- /dev/null +++ celo/consensus/istanbul/errors.go @@ -0,0 +1,47 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package istanbul + +import "errors" + +var ( + // ErrUnauthorizedAddress is returned when given address cannot be found in + // current validator set. + ErrUnauthorizedAddress = errors.New("not an elected validator") + // ErrInvalidSigner is returned if a message's signature does not correspond to the address in msg.Address + ErrInvalidSigner = errors.New("signed by incorrect validator") + // ErrStoppedEngine is returned if the engine is stopped + ErrStoppedEngine = errors.New("stopped engine") + // ErrStartedEngine is returned if the engine is already started + ErrStartedEngine = errors.New("started engine") + // ErrStoppedAnnounce is returned if announce is stopped + ErrStoppedAnnounce = errors.New("stopped announce") + // ErrStartedAnnounce is returned if announce is already started + ErrStartedAnnounce = errors.New("started announce") + // ErrStoppedProxiedValidatorEngine is returned if proxied validator engine is stopped + ErrStoppedProxiedValidatorEngine = errors.New("stopped proxied validator engine") + // ErrStartedProxiedValidatorEngine is returned if proxied validator engine is already started + ErrStartedProxiedValidatorEngine = errors.New("started proxied validator engine") + // ErrStoppedVPHThread is returned if validator peer handler thread is stopped + ErrStoppedVPHThread = errors.New("stopped validator peer handler thread") + // ErrStartedVPHThread is returned if validator peer handler thread is already started + ErrStartedVPHThread = errors.New("started validator peer handler thread") + // ErrValidatorNotProxied is returned if the validator is not configured to be proxied + ErrValidatorNotProxied = errors.New("validator not proxied") + // ErrInvalidEnodeCertMsgMapOldVersion is returned if a validator sends old enode certificate message + ErrInvalidEnodeCertMsgMapOldVersion = errors.New("invalid enode certificate message map because of old version") +)
diff --git go-ethereum/consensus/istanbul/events.go celo/consensus/istanbul/events.go new file mode 100644 index 0000000000000000000000000000000000000000..b81385f2c017125282b56280aecb1297601fa0b4 --- /dev/null +++ celo/consensus/istanbul/events.go @@ -0,0 +1,39 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package istanbul + +import "github.com/ethereum/go-ethereum/p2p/enode" + +// RequestEvent is posted to propose a proposal +type RequestEvent struct { + Proposal Proposal +} + +// MessageEvent is posted for Istanbul engine communication +type MessageEvent struct { + Payload []byte +} + +// MessageWithPeerIDEvent is a MessageEvent with the peerID that sent the message +type MessageWithPeerIDEvent struct { + PeerID enode.ID + Payload []byte +} + +// FinalCommittedEvent is posted when a proposal is committed +type FinalCommittedEvent struct { +}
diff --git go-ethereum/consensus/istanbul/backend/backend_test.go celo/consensus/istanbul/backend/backend_test.go new file mode 100644 index 0000000000000000000000000000000000000000..afcac3e8f56d96de4264a8984dce098a64dea2e7 --- /dev/null +++ celo/consensus/istanbul/backend/backend_test.go @@ -0,0 +1,174 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "fmt" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +func TestSign(t *testing.T) { + b := newBackend() + data := []byte("Here is a string....") + sig, err := b.Sign(data) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + //Check signature recover + hashData := crypto.Keccak256(data) + pubkey, _ := crypto.Ecrecover(hashData, sig) + var signer common.Address + copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) + if signer != getAddress() { + t.Errorf("address mismatch: have %v, want %s", signer.Hex(), getAddress().Hex()) + } +} + +func TestCheckSignature(t *testing.T) { + key, _ := generatePrivateKey() + data := []byte("Here is a string....") + hashData := crypto.Keccak256(data) + sig, _ := crypto.Sign(hashData, key) + b := newBackend() + a := getAddress() + err := b.CheckSignature(data, a, sig) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + a = getInvalidAddress() + err = b.CheckSignature(data, a, sig) + if err != errInvalidSignature { + t.Errorf("error mismatch: have %v, want %v", err, errInvalidSignature) + } +} + +func TestCheckValidatorSignature(t *testing.T) { + + vset, keys := newTestValidatorSet(5) + + // 1. Positive test: sign with validator's key should succeed + data := []byte("dummy data") + hashData := crypto.Keccak256(data) + for i, k := range keys { + // Sign + sig, err := crypto.Sign(hashData, k) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + // CheckValidatorSignature should succeed + addr, err := istanbul.CheckValidatorSignature(vset, data, sig) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + validator := vset.GetByIndex(uint64(i)) + if addr != validator.Address() { + t.Errorf("validator address mismatch: have %v, want %v", addr, validator.Address()) + } + } + + // 2. Negative test: sign with any key other than validator's key should return error + key, err := crypto.GenerateKey() + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + // Sign + sig, err := crypto.Sign(hashData, key) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + // CheckValidatorSignature should return ErrUnauthorizedAddress + addr, err := istanbul.CheckValidatorSignature(vset, data, sig) + expectedErr := fmt.Errorf("not an elected validator %s", crypto.PubkeyToAddress(key.PublicKey).Hex()) + if err.Error() != expectedErr.Error() { + t.Errorf("error mismatch: have %v, want %v", err, expectedErr) + } + emptyAddr := common.Address{} + if addr != emptyAddr { + t.Errorf("address mismatch: have %v, want %v", addr, emptyAddr) + } +} + +func TestNormalCommit(t *testing.T) { + + chain, backend := newBlockChain(1, true) + defer chain.Stop() + block := makeBlockWithoutSeal(chain, backend, chain.Genesis()) + expBlock, _ := backend.signBlock(block) + expectedSignature := make([]byte, types.IstanbulExtraBlsSignature) + + newHeadCh := make(chan core.ChainHeadEvent, 10) + sub := chain.SubscribeChainHeadEvent(newHeadCh) + defer sub.Unsubscribe() + + if err := backend.Commit(expBlock, types.IstanbulAggregatedSeal{Round: big.NewInt(0), Bitmap: big.NewInt(0), Signature: expectedSignature}, types.IstanbulEpochValidatorSetSeal{Bitmap: big.NewInt(0), Signature: nil}, nil); err != nil { + if err != nil { + t.Errorf("error mismatch: have %v, want %v", err, nil) + } + } + + // to avoid race condition is occurred by goroutine + select { + case result := <-newHeadCh: + if result.Block.Hash() != expBlock.Hash() { + t.Errorf("hash mismatch: have %v, want %v", result.Block.Hash(), expBlock.Hash()) + } + case <-time.After(10 * time.Second): + t.Fatal("timeout") + } + +} + +func TestInvalidCommit(t *testing.T) { + + chain, backend := newBlockChain(1, true) + defer chain.Stop() + block := makeBlockWithoutSeal(chain, backend, chain.Genesis()) + expBlock, _ := backend.signBlock(block) + + if err := backend.Commit(expBlock, types.IstanbulAggregatedSeal{Round: big.NewInt(0), Bitmap: big.NewInt(0), Signature: nil}, types.IstanbulEpochValidatorSetSeal{Bitmap: big.NewInt(0), Signature: nil}, nil); err != nil { + if err != errInvalidAggregatedSeal { + t.Errorf("error mismatch: have %v, want %v", err, errInvalidAggregatedSeal) + } + + } +} + +func TestGetProposer(t *testing.T) { + numValidators := 1 + genesisCfg, nodeKeys := getGenesisAndKeys(numValidators, true) + chain, engine, _ := newBlockChainWithKeys(false, common.Address{}, false, genesisCfg, nodeKeys[0]) + defer chain.Stop() + if _, err := makeBlock(nodeKeys, chain, engine, chain.Genesis()); err != nil { + t.Errorf("Failed to make a block: %v", err) + } + + expected := engine.AuthorForBlock(1) + actual := engine.Address() + if actual != expected { + t.Errorf("proposer mismatch: have %v, want %v, currentblock: %v", actual.Hex(), expected.Hex(), chain.CurrentBlock().Number()) + } + +}
diff --git go-ethereum/consensus/istanbul/backend/backend.go celo/consensus/istanbul/backend/backend.go new file mode 100644 index 0000000000000000000000000000000000000000..32c113c6fdce2a63bf7a5267e1c11acc8b9d5a25 --- /dev/null +++ celo/consensus/istanbul/backend/backend.go @@ -0,0 +1,1132 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + "os" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/announce" + "github.com/ethereum/go-ethereum/consensus/istanbul/backend/internal/replica" + istanbulCore "github.com/ethereum/go-ethereum/consensus/istanbul/core" + "github.com/ethereum/go-ethereum/consensus/istanbul/proxy" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/contracts" + "github.com/ethereum/go-ethereum/contracts/election" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/trie" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/contracts/random" + "github.com/ethereum/go-ethereum/contracts/validators" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" + lru "github.com/hashicorp/golang-lru" +) + +// New creates an Ethereum backend for Istanbul core engine. +func New(config *istanbul.Config, db ethdb.Database) consensus.Istanbul { + // Allocate the snapshot caches and create the engine + logger := log.New() + recentSnapshots, err := lru.NewARC(inmemorySnapshots) + if err != nil { + logger.Crit("Failed to create recent snapshots cache", "err", err) + } + + coreStarted := atomic.Value{} + coreStarted.Store(false) + backend := &Backend{ + config: config, + istanbulEventMux: new(event.TypeMux), + logger: logger, + db: db, + recentSnapshots: recentSnapshots, + coreStarted: coreStarted, + gossipCache: istanbul.NewLRUGossipCache(inmemoryPeers, inmemoryMessages), + updatingCachedValidatorConnSetCond: sync.NewCond(&sync.Mutex{}), + finalizationTimer: metrics.NewRegisteredTimer("consensus/istanbul/backend/finalize", nil), + blocksElectedMeter: metrics.NewRegisteredMeter("consensus/istanbul/blocks/elected", nil), + blocksElectedAndSignedMeter: metrics.NewRegisteredMeter("consensus/istanbul/blocks/signedbyus", nil), + blocksElectedButNotSignedMeter: metrics.NewRegisteredMeter("consensus/istanbul/blocks/missedbyus", nil), + blocksElectedAndProposedMeter: metrics.NewRegisteredMeter("consensus/istanbul/blocks/proposedbyus", nil), + blocksTotalSigsGauge: metrics.NewRegisteredGauge("consensus/istanbul/blocks/totalsigs", nil), + blocksValSetSizeGauge: metrics.NewRegisteredGauge("consensus/istanbul/blocks/validators", nil), + blocksTotalMissedRoundsMeter: metrics.NewRegisteredMeter("consensus/istanbul/blocks/missedrounds", nil), + blocksMissedRoundsAsProposerMeter: metrics.NewRegisteredMeter("consensus/istanbul/blocks/missedroundsasproposer", nil), + blocksElectedButNotSignedGauge: metrics.NewRegisteredGauge("consensus/istanbul/blocks/missedbyusinarow", nil), + blocksFinalizedTransactionsGauge: metrics.NewRegisteredGauge("consensus/istanbul/blocks/transactions", nil), + blocksFinalizedGasUsedGauge: metrics.NewRegisteredGauge("consensus/istanbul/blocks/gasused", nil), + sleepGauge: metrics.NewRegisteredGauge("consensus/istanbul/backend/sleep", nil), + } + backend.aWallets.Store(&istanbul.Wallets{}) + if config.LoadTestCSVFile != "" { + if f, err := os.Create(config.LoadTestCSVFile); err == nil { + backend.csvRecorder = metrics.NewCSVRecorder(f, "blockNumber", "txCount", "gasUsed", "round", + "cycle", "sleep", "consensus", "block_verify", "block_construct", + "sysload", "syswait", "procload") + } + } + + backend.core = istanbulCore.New(backend, backend.config) + + if config.Validator { + rs, err := replica.NewState(config.Replica, config.ReplicaStateDBPath, backend.StartValidating, backend.StopValidating) + if err != nil { + logger.Crit("Can't open ReplicaStateDB", "err", err, "dbpath", config.ReplicaStateDBPath) + } + backend.replicaState = rs + } else { + backend.replicaState = nil + } + + backend.vph = newVPH(backend) + valEnodeTable, err := announce.OpenValidatorEnodeDB(config.ValidatorEnodeDBPath, backend.vph) + if err != nil { + logger.Crit("Can't open ValidatorEnodeDB", "err", err, "dbpath", config.ValidatorEnodeDBPath) + } + backend.valEnodeTable = valEnodeTable + + // If this node is a proxy or is a proxied validator, then create the appropriate proxy engine object + if backend.IsProxy() { + backend.proxyEngine, err = proxy.NewProxyEngine(backend, backend.config) + if err != nil { + logger.Crit("Can't create a new proxy engine", "err", err) + } + } else if backend.IsProxiedValidator() { + backend.proxiedValidatorEngine, err = proxy.NewProxiedValidatorEngine(backend, backend.config) + if err != nil { + logger.Crit("Can't create a new proxied validator engine", "err", err) + } + } + + backend.announceManager = createAnnounceManager(backend) + + return backend +} + +func createAnnounceManager(backend *Backend) *announce.Manager { + versionCertificateTable, err := announce.OpenVersionCertificateDB(backend.config.VersionCertificateDBPath) + if err != nil { + backend.logger.Crit("Can't open VersionCertificateDB", "err", err, "dbpath", backend.config.VersionCertificateDBPath) + } + vcGossiper := announce.NewVcGossiper(func(payload []byte) error { + return backend.Gossip(payload, istanbul.VersionCertificatesMsg) + }) + + state := announce.NewAnnounceState(backend.valEnodeTable, versionCertificateTable) + checker := announce.NewValidatorChecker(&backend.aWallets, backend.RetrieveValidatorConnSet, backend.IsValidating) + ovcp := announce.NewOutboundVCProcessor(checker, backend, vcGossiper) + ecertHolder := announce.NewLockedHolder() + pruner := announce.NewAnnounceStatePruner(backend.RetrieveValidatorConnSet) + + var vpap announce.ValProxyAssigmnentProvider + var ecertGenerator announce.EnodeCertificateMsgGenerator + var onNewEnodeMsgs announce.OnNewEnodeCertsMsgSentFn + if backend.IsProxiedValidator() { + ecertGenerator = announce.NewEnodeCertificateMsgGenerator( + announce.NewProxiedExternalFacingEnodeGetter(backend.proxiedValidatorEngine.GetProxiesAndValAssignments), + ) + vpap = announce.NewProxiedValProxyAssigmentProvider(backend.proxiedValidatorEngine.GetValidatorProxyAssignments) + onNewEnodeMsgs = backend.proxiedValidatorEngine.SendEnodeCertsToAllProxies + } else { + ecertGenerator = announce.NewEnodeCertificateMsgGenerator(announce.NewSelfExternalFacingEnodeGetter(backend.SelfNode)) + vpap = announce.NewSelfValProxyAssigmentProvider(backend.SelfNode) + onNewEnodeMsgs = nil + } + + avs := announce.NewVersionSharer(&backend.aWallets, backend, state, ovcp, ecertGenerator, ecertHolder, onNewEnodeMsgs) + worker := createAnnounceWorker(backend, state, ovcp, vcGossiper, checker, pruner, vpap, avs) + return announce.NewManager( + backend.config, + &backend.aWallets, + backend, + backend, + backend, + state, + backend.gossipCache, + checker, + ovcp, + ecertHolder, + vcGossiper, + vpap, + worker) +} + +func createAnnounceWorker(backend *Backend, state *announce.AnnounceState, ovcp announce.OutboundVersionCertificateProcessor, + vcGossiper announce.VersionCertificateGossiper, + checker announce.ValidatorChecker, pruner announce.AnnounceStatePruner, + vpap announce.ValProxyAssigmnentProvider, avs announce.VersionSharer) announce.Worker { + announceVersion := announce.NewAtomicVersion() + peerCounter := func(purpose p2p.PurposeFlag) int { + return len(backend.broadcaster.FindPeers(nil, p2p.AnyPurpose)) + } + + enodeGossiper := announce.NewEnodeQueryGossiper(announceVersion, func(payload []byte) error { + return backend.Gossip(payload, istanbul.QueryEnodeMsg) + }) + // Gossip the announce after a minute. + // The delay allows for all receivers of the announce message to + // have a more up-to-date cached registered/elected valset, and + // hence more likely that they will be aware that this node is + // within that set. + waitPeriod := 1 * time.Minute + if backend.config.Epoch <= 10 { + waitPeriod = 5 * time.Second + } + return announce.NewWorker( + waitPeriod, + &backend.aWallets, + announceVersion, + state, + checker, + pruner, + vcGossiper, + enodeGossiper, + backend.config, + peerCounter, + vpap, + avs, + ) +} + +// ---------------------------------------------------------------------------- + +type Backend struct { + config *istanbul.Config + istanbulEventMux *event.TypeMux + + aWallets atomic.Value + + core istanbulCore.Engine + logger log.Logger + db ethdb.Database + chain consensus.ChainContext + currentBlock func() *types.Block + hasBadBlock func(hash common.Hash) bool + stateAt func(hash common.Hash) (*state.StateDB, error) + replicaState replica.State + + processBlock func(block *types.Block, statedb *state.StateDB) (types.Receipts, []*types.Log, uint64, error) + validateState func(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error + onNewConsensusBlock func(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB) + + // We need this to be an atomic value so that we can access it in a lock + // free way from IsValidating. This is required because StartValidating + // makes a call to RefreshValPeers while holding coreMu and RefreshValPeers + // waits for all validator peers to be deleted and then reconnects to known + // validators. If any of those peers has called IsValidating before + // RefreshValPeers tries to delete them the system gets stuck in a + // deadlock, the peer will never acquire coreMu because it is held by + // StartValidating, and StartValidating will never return because it is + // waiting for all peers to disconnect. + coreStarted atomic.Value + coreMu sync.RWMutex + + // Snapshots for recent blocks to speed up reorgs + recentSnapshots *lru.ARCCache + + // event subscription for ChainHeadEvent event + broadcaster consensus.Broadcaster + + // interface to the p2p server + p2pserver consensus.P2PServer + + gossipCache istanbul.GossipCache + + valEnodeTable *announce.ValidatorEnodeDB + + announceManager *announce.Manager + + delegateSignFeed event.Feed + delegateSignScope event.SubscriptionScope + + // Metric timer used to record block finalization times. + finalizationTimer metrics.Timer + + // Meters for number of blocks seen for which the current validator signer has been elected, + // for which it was elected and has signed, elected but not signed, and both elected and proposed. + blocksElectedMeter metrics.Meter + blocksElectedAndSignedMeter metrics.Meter + blocksElectedButNotSignedMeter metrics.Meter + blocksElectedAndProposedMeter metrics.Meter + + // Gauge for how many blocks that we missed while elected in a row. + blocksElectedButNotSignedGauge metrics.Gauge + + // Gauge for total signatures in parentSeal of last received block (how much better than quorum are we doing) + blocksTotalSigsGauge metrics.Gauge + + // Gauge for validator set size of grandparent of last received block (maximum value for blocksTotalSigsGauge) + blocksValSetSizeGauge metrics.Gauge + + // Meter counting cumulative number of round changes that had to happen to get blocks agreed + // for all blocks & when are the proposer. + blocksTotalMissedRoundsMeter metrics.Meter + blocksMissedRoundsAsProposerMeter metrics.Meter + + // Gauge counting the transactions in the last block + blocksFinalizedTransactionsGauge metrics.Gauge + + // Gauge counting the gas used in the last block + blocksFinalizedGasUsedGauge metrics.Gauge + + // Gauge reporting how many nanoseconds were spent sleeping + sleepGauge metrics.Gauge + // Start of the previous block cycle. + cycleStart time.Time + + // Consensus csv recorded for load testing + csvRecorder *metrics.CSVRecorder + + // Cache for the return values of the method RetrieveValidatorConnSet + cachedValidatorConnSet map[common.Address]bool + cachedValidatorConnSetBlockNum uint64 + cachedValidatorConnSetTS time.Time + cachedValidatorConnSetMu sync.RWMutex + + // Used for ensuring that only one goroutine is doing the work of updating + // the validator conn set cache at a time. + updatingCachedValidatorConnSet bool + updatingCachedValidatorConnSetErr error + updatingCachedValidatorConnSetCond *sync.Cond + + // Handler to manage and maintain validator peer connections + vph *validatorPeerHandler + + // Handler for proxy related functionality + proxyEngine proxy.ProxyEngine + + // Handler for proxied validator related functionality + proxiedValidatorEngine proxy.ProxiedValidatorEngine + proxiedValidatorEngineRunning bool + proxiedValidatorEngineMu sync.RWMutex + + // RandomSeed (and it's mutex) used to generate the random beacon randomness + randomSeed []byte + randomSeedMu sync.Mutex + + // Test hooks + abortCommitHook func(result *istanbulCore.StateProcessResult) bool // Method to call upon committing a proposal +} + +func (sb *Backend) isCoreStarted() bool { + return sb.coreStarted.Load().(bool) +} + +// IsProxy returns true if instance has proxy flag +func (sb *Backend) IsProxy() bool { + return sb.config.Proxy +} + +// GetProxyEngine returns the proxy engine object +func (sb *Backend) GetProxyEngine() proxy.ProxyEngine { + return sb.proxyEngine +} + +// IsProxiedValidator returns true if instance has proxied validator flag +func (sb *Backend) IsProxiedValidator() bool { + return sb.config.Proxied && sb.IsValidator() +} + +// GetProxiedValidatorEngine returns the proxied validator engine object +func (sb *Backend) GetProxiedValidatorEngine() proxy.ProxiedValidatorEngine { + sb.proxiedValidatorEngineMu.RLock() + defer sb.proxiedValidatorEngineMu.RUnlock() + return sb.proxiedValidatorEngine +} + +// IsValidating return true if instance is validating +func (sb *Backend) IsValidating() bool { + // TODO: Maybe a little laggy, but primary / replica should track the core + return sb.isCoreStarted() +} + +// IsValidator return if instance is a validator (either proxied or standalone) +func (sb *Backend) IsValidator() bool { + return sb.config.Validator +} + +// ChainConfig returns the configuration from the embedded blockchain reader. +func (sb *Backend) ChainConfig() *params.ChainConfig { + return sb.chain.Config() +} + +// SendDelegateSignMsgToProxy sends an istanbulDelegateSign message to a proxy +// if one exists +func (sb *Backend) SendDelegateSignMsgToProxy(msg []byte, peerID enode.ID) error { + if sb.IsProxiedValidator() { + return sb.proxiedValidatorEngine.SendDelegateSignMsgToProxy(msg, peerID) + } else { + return errors.New("No Proxy found") + } +} + +// SendDelegateSignMsgToProxiedValidator sends an istanbulDelegateSign message to a +// proxied validator if one exists +func (sb *Backend) SendDelegateSignMsgToProxiedValidator(msg []byte) error { + if sb.IsProxy() { + return sb.proxyEngine.SendDelegateSignMsgToProxiedValidator(msg) + } else { + return errors.New("No Proxied Validator found") + } +} + +// Authorize implements istanbul.Backend.Authorize +func (sb *Backend) Authorize(ecdsaAddress, blsAddress common.Address, publicKey *ecdsa.PublicKey, decryptFn istanbul.DecryptFn, signFn istanbul.SignerFn, signBLSFn istanbul.BLSSignerFn, signHashFn istanbul.HashSignerFn) { + bls := istanbul.NewBlsInfo(blsAddress, signBLSFn) + ecdsa := istanbul.NewEcdsaInfo(ecdsaAddress, publicKey, decryptFn, signFn, signHashFn) + w := &istanbul.Wallets{ + Ecdsa: *ecdsa, + Bls: *bls, + } + sb.aWallets.Store(w) + sb.core.SetAddress(w.Ecdsa.Address) +} + +func (sb *Backend) wallets() *istanbul.Wallets { + return sb.aWallets.Load().(*istanbul.Wallets) +} + +// Address implements istanbul.Backend.Address +func (sb *Backend) Address() common.Address { + return sb.wallets().Ecdsa.Address +} + +// SelfNode returns the owner's node (if this is a proxy, it will return the external node) +func (sb *Backend) SelfNode() *enode.Node { + return sb.p2pserver.Self() +} + +// Close the backend +func (sb *Backend) Close() error { + sb.delegateSignScope.Close() + var errs []error + if err := sb.valEnodeTable.Close(); err != nil { + errs = append(errs, err) + } + if err := sb.announceManager.Close(); err != nil { + errs = append(errs, err) + } + if sb.replicaState != nil { + if err := sb.replicaState.Close(); err != nil { + errs = append(errs, err) + } + } + if err := sb.csvRecorder.Close(); err != nil { + errs = append(errs, err) + } + var concatenatedErrs error + for i, err := range errs { + if i == 0 { + concatenatedErrs = err + } else { + concatenatedErrs = fmt.Errorf("%v; %v", concatenatedErrs, err) + } + } + return concatenatedErrs +} + +// Validators implements istanbul.Backend.Validators +func (sb *Backend) Validators(proposal istanbul.Proposal) istanbul.ValidatorSet { + return sb.getOrderedValidators(proposal.Number().Uint64(), proposal.Hash()) +} + +// ParentBlockValidators implements istanbul.Backend.ParentBlockValidators +func (sb *Backend) ParentBlockValidators(proposal istanbul.Proposal) istanbul.ValidatorSet { + return sb.getOrderedValidators(proposal.Number().Uint64()-1, proposal.ParentHash()) +} + +func (sb *Backend) NextBlockValidators(proposal istanbul.Proposal) (istanbul.ValidatorSet, error) { + istExtra, err := proposal.Header().IstanbulExtra() + if err != nil { + return nil, err + } + + // There was no change + if len(istExtra.AddedValidators) == 0 && istExtra.RemovedValidators.BitLen() == 0 { + return sb.ParentBlockValidators(proposal), nil + } + + snap, err := sb.snapshot(sb.chain, proposal.Number().Uint64()-1, common.Hash{}, nil) + if err != nil { + return nil, err + } + snap = snap.copy() + + addedValidators, err := istanbul.CombineIstanbulExtraToValidatorData(istExtra.AddedValidators, istExtra.AddedValidatorsPublicKeys) + if err != nil { + return nil, err + } + + if !snap.ValSet.RemoveValidators(istExtra.RemovedValidators) { + return nil, fmt.Errorf("could not obtain next block validators: failed at remove validators") + } + if !snap.ValSet.AddValidators(addedValidators) { + return nil, fmt.Errorf("could not obtain next block validators: failed at add validators") + } + + return snap.ValSet, nil +} + +func (sb *Backend) GetValidators(blockNumber *big.Int, headerHash common.Hash) []istanbul.Validator { + validatorSet := sb.getValidators(blockNumber.Uint64(), headerHash) + return validatorSet.List() +} + +// Commit implements istanbul.Backend.Commit +func (sb *Backend) Commit(proposal istanbul.Proposal, aggregatedSeal types.IstanbulAggregatedSeal, aggregatedEpochValidatorSetSeal types.IstanbulEpochValidatorSetSeal, result *istanbulCore.StateProcessResult) error { + // Check if the proposal is a valid block + block, ok := proposal.(*types.Block) + if !ok { + sb.logger.Error("Invalid proposal, %v", proposal) + return errInvalidProposal + } + + h := block.Header() + // Append seals into extra-data + err := writeAggregatedSeal(h, aggregatedSeal, false) + if err != nil { + return err + } + // update block's header + block = block.WithHeader(h) + block = block.WithEpochSnarkData(&types.EpochSnarkData{ + Bitmap: aggregatedEpochValidatorSetSeal.Bitmap, + Signature: aggregatedEpochValidatorSetSeal.Signature, + }) + if sb.csvRecorder != nil { + sb.recordBlockProductionTimes(block.Header().Number.Uint64(), len(block.Transactions()), block.GasUsed(), aggregatedSeal.Round.Uint64()) + } + + if sb.abortCommitHook != nil && sb.abortCommitHook(result) { + return errors.New("nil StateProcessResult") + } + + sb.logger.Info("Committed", "address", sb.Address(), "round", aggregatedSeal.Round.Uint64(), "hash", proposal.Hash(), "number", proposal.Number().Uint64()) + + // If caller didn't provide a result, try verifying the block to produce one + if result == nil { + // This is a suboptimal path, since caller is expected to already have a result available + // and thus to avoid doing the block processing again + sb.logger.Warn("Potentially duplicated processing for block", "number", block.Number(), "hash", block.Hash()) + if result, _, err = sb.Verify(proposal); err != nil { + return err + } + } + sb.onNewConsensusBlock(block, result.Receipts, result.Logs, result.State) + return nil +} + +// EventMux implements istanbul.Backend.EventMux +func (sb *Backend) EventMux() *event.TypeMux { + return sb.istanbulEventMux +} + +// Verify implements istanbul.Backend.Verify +func (sb *Backend) Verify(proposal istanbul.Proposal) (*istanbulCore.StateProcessResult, time.Duration, error) { + // Check if the proposal is a valid block + block, ok := proposal.(*types.Block) + if !ok { + sb.logger.Error("Invalid proposal, %v", proposal) + return nil, 0, errInvalidProposal + } + + // check bad block + if sb.hasBadProposal(block.Hash()) { + return nil, 0, core.ErrBannedHash + } + + // check block body + txnHash := types.DeriveSha(block.Transactions(), new(trie.Trie)) + if txnHash != block.Header().TxHash { + return nil, 0, errMismatchTxhashes + } + + // If the current block occurred before the Donut hard fork, check that the author and coinbase are equal. + if !sb.chain.Config().IsDonut(block.Number()) { + addr, err := sb.Author(block.Header()) + if err != nil { + sb.logger.Error("Could not recover original author of the block to verify against the header's coinbase", "err", err, "func", "Verify") + return nil, 0, errInvalidProposal + } else if addr != block.Header().Coinbase { + sb.logger.Error("Block proposal author and coinbase must be the same when Donut hard fork is active") + return nil, 0, errInvalidCoinbase + } + } + + err := sb.verifyHeaderFromProposal(sb.chain, block.Header()) + + if err != nil { + if err == consensus.ErrFutureBlock { + return nil, time.Unix(int64(block.Header().Time), 0).Sub(now()), consensus.ErrFutureBlock + } else { + return nil, 0, err + } + } + + // Process the block to verify that the transactions are valid and to retrieve the resulting state and receipts + // Get the state from this block's parent. + state, err := sb.stateAt(block.Header().ParentHash) + if err != nil { + sb.logger.Error("verify - Error in getting the block's parent's state", "parentHash", block.Header().ParentHash.Hex(), "err", err) + return nil, 0, err + } + + // Apply this block's transactions to update the state + receipts, logs, usedGas, err := sb.processBlock(block, state) + if err != nil { + sb.logger.Error("verify - Error in processing the block", "err", err) + return nil, 0, err + } + + // Validate the block + if err := sb.validateState(block, state, receipts, usedGas); err != nil { + sb.logger.Error("verify - Error in validating the block", "err", err) + return nil, 0, err + } + + // verify the validator set diff if this is the last block of the epoch + if istanbul.IsLastBlockOfEpoch(block.Header().Number.Uint64(), sb.config.Epoch) { + if err := sb.verifyValSetDiff(proposal, block, state); err != nil { + sb.logger.Error("verify - Error in verifying the val set diff", "err", err) + return nil, 0, err + } + } + + result := &istanbulCore.StateProcessResult{Receipts: receipts, Logs: logs, State: state} + return result, 0, nil +} + +func (sb *Backend) getNewValidatorSet(header *types.Header, state *state.StateDB) ([]istanbul.ValidatorData, error) { + vmRunner := sb.chain.NewEVMRunner(header, state) + newValSetAddresses, err := election.GetElectedValidators(vmRunner) + if err != nil { + return nil, err + } + newValSet, err := validators.GetValidatorData(vmRunner, newValSetAddresses) + return newValSet, err +} + +func (sb *Backend) verifyValSetDiff(proposal istanbul.Proposal, block *types.Block, state *state.StateDB) error { + header := block.Header() + + // Ensure that the extra data format is satisfied + istExtra, err := header.IstanbulExtra() + if err != nil { + return err + } + + newValSet, err := sb.getNewValidatorSet(block.Header(), state) + if err != nil { + if len(istExtra.AddedValidators) != 0 || istExtra.RemovedValidators.BitLen() != 0 { + sb.logger.Error("verifyValSetDiff - Invalid val set diff. Non empty diff when it should be empty.", "addedValidators", common.ConvertToStringSlice(istExtra.AddedValidators), "removedValidators", istExtra.RemovedValidators.Text(16)) + return errInvalidValidatorSetDiff + } + } else { + parentValidators := sb.ParentBlockValidators(proposal) + oldValSet := make([]istanbul.ValidatorData, 0, parentValidators.Size()) + + for _, val := range parentValidators.List() { + oldValSet = append(oldValSet, istanbul.ValidatorData{ + Address: val.Address(), + BLSPublicKey: val.BLSPublicKey(), + }) + } + + addedValidators, removedValidators := istanbul.ValidatorSetDiff(oldValSet, newValSet) + + addedValidatorsAddresses := make([]common.Address, 0, len(addedValidators)) + addedValidatorsPublicKeys := make([]blscrypto.SerializedPublicKey, 0, len(addedValidators)) + for _, val := range addedValidators { + addedValidatorsAddresses = append(addedValidatorsAddresses, val.Address) + addedValidatorsPublicKeys = append(addedValidatorsPublicKeys, val.BLSPublicKey) + } + + if !istanbul.CompareValidatorSlices(addedValidatorsAddresses, istExtra.AddedValidators) || removedValidators.Cmp(istExtra.RemovedValidators) != 0 || !istanbul.CompareValidatorPublicKeySlices(addedValidatorsPublicKeys, istExtra.AddedValidatorsPublicKeys) { + sb.logger.Error("verifyValSetDiff - Invalid val set diff. Comparison failed. ", "got addedValidators", common.ConvertToStringSlice(istExtra.AddedValidators), "got removedValidators", istExtra.RemovedValidators.Text(16), "got addedValidatorsPublicKeys", istanbul.ConvertPublicKeysToStringSlice(istExtra.AddedValidatorsPublicKeys), "expected addedValidators", common.ConvertToStringSlice(addedValidatorsAddresses), "expected removedValidators", removedValidators.Text(16), "expected addedValidatorsPublicKeys", istanbul.ConvertPublicKeysToStringSlice(addedValidatorsPublicKeys)) + return errInvalidValidatorSetDiff + } + } + + return nil +} + +// Sign implements istanbul.Backend.Sign +func (sb *Backend) Sign(data []byte) ([]byte, error) { + return sb.wallets().Ecdsa.Sign(data) +} + +// Sign implements istanbul.Backend.SignBLS +func (sb *Backend) SignBLS(data []byte, extra []byte, useComposite, cip22 bool) (blscrypto.SerializedSignature, error) { + w := sb.wallets() + return w.Bls.Sign(data, extra, useComposite, cip22) +} + +// CheckSignature implements istanbul.Backend.CheckSignature +func (sb *Backend) CheckSignature(data []byte, address common.Address, sig []byte) error { + signer, err := istanbul.GetSignatureAddress(data, sig) + if err != nil { + sb.logger.Error("Failed to get signer address", "err", err) + return err + } + // Compare derived addresses + if signer != address { + return errInvalidSignature + } + return nil +} + +// HasBlock implements istanbul.Backend.HasBlock +func (sb *Backend) HasBlock(hash common.Hash, number *big.Int) bool { + return sb.chain.GetHeader(hash, number.Uint64()) != nil +} + +// AuthorForBlock returns the address of the block offer from a given number. +func (sb *Backend) AuthorForBlock(number uint64) common.Address { + if h := sb.chain.GetHeaderByNumber(number); h != nil { + a, _ := sb.Author(h) + return a + } + return common.ZeroAddress +} + +// HashForBlock returns the block hash from the canonical chain for the given number. +func (sb *Backend) HashForBlock(number uint64) common.Hash { + if h := sb.chain.GetHeaderByNumber(number); h != nil { + return h.Hash() + } + return common.Hash{} +} + +func (sb *Backend) getValidators(number uint64, hash common.Hash) istanbul.ValidatorSet { + snap, err := sb.snapshot(sb.chain, number, hash, nil) + if err != nil { + sb.logger.Warn("Error getting snapshot", "number", number, "hash", hash, "err", err) + return validator.NewSet(nil) + } + return snap.ValSet +} + +// validatorRandomnessAtBlockNumber calls into the EVM to get the randomness to use in proposer ordering at a given block. +func (sb *Backend) validatorRandomnessAtBlockNumber(number uint64, hash common.Hash) (common.Hash, error) { + lastBlockInPreviousEpoch := number + if number > 0 { + lastBlockInPreviousEpoch = number - istanbul.GetNumberWithinEpoch(number, sb.config.Epoch) + } + vmRunner, err := sb.chain.NewEVMRunnerForCurrentBlock() + if err != nil { + return common.Hash{}, err + } + return random.BlockRandomness(vmRunner, lastBlockInPreviousEpoch) +} + +func (sb *Backend) getOrderedValidators(number uint64, hash common.Hash) istanbul.ValidatorSet { + valSet := sb.getValidators(number, hash) + if valSet.Size() == 0 { + return valSet + } + + if sb.config.ProposerPolicy == istanbul.ShuffledRoundRobin { + seed, err := sb.validatorRandomnessAtBlockNumber(number, hash) + if err != nil { + if err == contracts.ErrRegistryContractNotDeployed { + sb.logger.Debug("Failed to set randomness for proposer selection", "block_number", number, "hash", hash, "error", err) + } else { + sb.logger.Warn("Failed to set randomness for proposer selection", "block_number", number, "hash", hash, "error", err) + } + } + valSet.SetRandomness(seed) + } + + return valSet +} + +// GetCurrentHeadBlock retrieves the last block +func (sb *Backend) GetCurrentHeadBlock() istanbul.Proposal { + return sb.currentBlock() +} + +// GetCurrentHeadBlockAndAuthor retrieves the last block alongside the coinbase address for it +func (sb *Backend) GetCurrentHeadBlockAndAuthor() (istanbul.Proposal, common.Address) { + block := sb.currentBlock() + + if block.Number().Cmp(common.Big0) == 0 { + return block, common.ZeroAddress + } + + proposer, err := sb.Author(block.Header()) + + if err != nil { + sb.logger.Error("Failed to get block proposer", "err", err) + return nil, common.ZeroAddress + } + + // Return header only block here since we don't need block body + return block, proposer +} + +func (sb *Backend) LastSubject() (istanbul.Subject, error) { + lastProposal, _ := sb.GetCurrentHeadBlockAndAuthor() + istExtra, err := lastProposal.Header().IstanbulExtra() + if err != nil { + return istanbul.Subject{}, err + } + lastView := &istanbul.View{Sequence: lastProposal.Number(), Round: istExtra.AggregatedSeal.Round} + return istanbul.Subject{View: lastView, Digest: lastProposal.Hash()}, nil +} + +func (sb *Backend) hasBadProposal(hash common.Hash) bool { + if sb.hasBadBlock == nil { + return false + } + return sb.hasBadBlock(hash) +} + +// RefreshValPeers will create 'validator' type peers to all the valset validators, and disconnect from the +// peers that are not part of the valset. +// It will also disconnect all validator connections if this node is not a validator. +// Note that adding and removing validators are idempotent operations. If the validator +// being added or removed is already added or removed, then a no-op will be done. +func (sb *Backend) RefreshValPeers() error { + logger := sb.logger.New("func", "RefreshValPeers") + logger.Trace("Called RefreshValPeers") + + if sb.broadcaster == nil { + return errors.New("Broadcaster is not set") + } + + valConnSet, err := sb.RetrieveValidatorConnSet() + if err != nil { + return err + } + sb.valEnodeTable.RefreshValPeers(valConnSet, sb.ValidatorAddress()) + return nil +} + +func (sb *Backend) ValidatorAddress() common.Address { + if sb.IsProxy() { + return sb.config.ProxiedValidatorAddress + } + return sb.Address() +} + +// RetrieveValidatorConnSet returns the cached validator conn set if the cache +// is younger than 20 blocks, younger than 1 minute, or if an epoch transition didn't occur since the last +// cached entry. In the event of a cache miss, this may block for a +// couple seconds while retrieving the uncached set. +func (sb *Backend) RetrieveValidatorConnSet() (map[common.Address]bool, error) { + var valConnSetToReturn map[common.Address]bool = nil + + sb.cachedValidatorConnSetMu.RLock() + + // wait period in blocks + waitPeriod := uint64(20) + + // wait period in seconds + waitPeriodSec := 60 * time.Second + + // Check to see if there is a cached validator conn set + if sb.cachedValidatorConnSet != nil { + currentBlockNum := sb.currentBlock().Number().Uint64() + pendingBlockNum := currentBlockNum + 1 + + // We want to get the val conn set that is meant to validate the pending block + desiredValSetEpochNum := istanbul.GetEpochNumber(pendingBlockNum, sb.config.Epoch) + + // Note that the cached validator conn set is applicable for the block right after the cached block num + cachedEntryEpochNum := istanbul.GetEpochNumber(sb.cachedValidatorConnSetBlockNum+1, sb.config.Epoch) + + // Returned the cached entry if it's within the same current epoch and that it's within waitPeriod + // blocks of the pending block. + if cachedEntryEpochNum == desiredValSetEpochNum && (sb.cachedValidatorConnSetBlockNum+waitPeriod) > currentBlockNum && time.Since(sb.cachedValidatorConnSetTS) <= waitPeriodSec { + valConnSetToReturn = sb.cachedValidatorConnSet + } + } + + if valConnSetToReturn == nil { + sb.cachedValidatorConnSetMu.RUnlock() + + if err := sb.updateCachedValidatorConnSet(); err != nil { + return nil, err + } + + sb.cachedValidatorConnSetMu.RLock() + valConnSetToReturn = sb.cachedValidatorConnSet + } + + valConnSetCopy := make(map[common.Address]bool) + for address, inSet := range valConnSetToReturn { + valConnSetCopy[address] = inSet + } + defer sb.cachedValidatorConnSetMu.RUnlock() + return valConnSetCopy, nil +} + +// retrieveCachedValidatorConnSet returns the most recently cached validator conn set. +// If no set has ever been cached, nil is returned. +func (sb *Backend) retrieveCachedValidatorConnSet() map[common.Address]bool { + sb.cachedValidatorConnSetMu.RLock() + defer sb.cachedValidatorConnSetMu.RUnlock() + return sb.cachedValidatorConnSet +} + +// updateCachedValidatorConnSet updates the cached validator conn set. If another +// goroutine is simultaneously updating the cached set, this goroutine will wait +// for the update to be finished to prevent the update work from occurring +// simultaneously. +func (sb *Backend) updateCachedValidatorConnSet() (err error) { + logger := sb.logger.New("func", "updateCachedValidatorConnSet") + var waited bool + sb.updatingCachedValidatorConnSetCond.L.Lock() + // Checking the condition in a for loop as recommended to prevent a race + for sb.updatingCachedValidatorConnSet { + waited = true + logger.Trace("Waiting for another goroutine to update the set") + // If another goroutine is updating, wait for it to finish + sb.updatingCachedValidatorConnSetCond.Wait() + } + if waited { + defer sb.updatingCachedValidatorConnSetCond.L.Unlock() + return sb.updatingCachedValidatorConnSetErr + } + // If we didn't wait, we show that we will start updating the cache + sb.updatingCachedValidatorConnSet = true + sb.updatingCachedValidatorConnSetCond.L.Unlock() + + defer func() { + sb.updatingCachedValidatorConnSetCond.L.Lock() + sb.updatingCachedValidatorConnSet = false + // Share the error with other goroutines that are waiting on this one + sb.updatingCachedValidatorConnSetErr = err + // Broadcast to any waiting goroutines that the update is complete + sb.updatingCachedValidatorConnSetCond.Broadcast() + sb.updatingCachedValidatorConnSetCond.L.Unlock() + }() + + validatorConnSet, blockNum, connSetTS, err := sb.retrieveUncachedValidatorConnSet() + if err != nil { + return err + } + sb.cachedValidatorConnSetMu.Lock() + sb.cachedValidatorConnSet = validatorConnSet + sb.cachedValidatorConnSetBlockNum = blockNum + sb.cachedValidatorConnSetTS = connSetTS + sb.cachedValidatorConnSetMu.Unlock() + return nil +} + +func (sb *Backend) retrieveUncachedValidatorConnSet() (map[common.Address]bool, uint64, time.Time, error) { + logger := sb.logger.New("func", "retrieveUncachedValidatorConnSet") + // Retrieve the validator conn set from the election smart contract + validatorsSet := make(map[common.Address]bool) + + currentBlock := sb.currentBlock() + currentState, err := sb.stateAt(currentBlock.Hash()) + if err != nil { + return nil, 0, time.Time{}, err + } + vmRunner := sb.chain.NewEVMRunner(currentBlock.Header(), currentState) + electNValidators, err := election.ElectNValidatorSigners(vmRunner, sb.config.AnnounceAdditionalValidatorsToGossip) + + // The validator contract may not be deployed yet. + // Even if it is deployed, it may not have any registered validators yet. + if err == contracts.ErrSmartContractNotDeployed || err == contracts.ErrRegistryContractNotDeployed { + logger.Trace("Can't elect N validators because smart contract not deployed. Setting validator conn set to current elected validators.", "err", err) + } else if err != nil { + logger.Error("Error in electing N validators. Setting validator conn set to current elected validators", "err", err) + } + + for _, address := range electNValidators { + validatorsSet[address] = true + } + + // Add active validators regardless + valSet := sb.getValidators(currentBlock.Number().Uint64(), currentBlock.Hash()) + for _, val := range valSet.List() { + validatorsSet[val.Address()] = true + } + + connSetTS := time.Now() + + logger.Trace("Returning validator conn set", "validatorsSet", validatorsSet) + return validatorsSet, currentBlock.Number().Uint64(), connSetTS, nil +} + +func (sb *Backend) AddProxy(node, externalNode *enode.Node) error { + if sb.IsProxiedValidator() { + return sb.proxiedValidatorEngine.AddProxy(node, externalNode) + } else { + return proxy.ErrNodeNotProxiedValidator + } +} + +func (sb *Backend) RemoveProxy(node *enode.Node) error { + if sb.IsProxiedValidator() { + return sb.proxiedValidatorEngine.RemoveProxy(node) + } else { + return proxy.ErrNodeNotProxiedValidator + } +} + +// VerifyPendingBlockValidatorSignature will verify that the message sender is a validator that is responsible +// for the current pending block (the next block right after the head block). +func (sb *Backend) VerifyPendingBlockValidatorSignature(data []byte, sig []byte) (common.Address, error) { + block := sb.currentBlock() + valSet := sb.getValidators(block.Number().Uint64(), block.Hash()) + return istanbul.CheckValidatorSignature(valSet, data, sig) +} + +// VerifyValidatorConnectionSetSignature will verify that the message sender is a validator that is responsible +// for the current pending block (the next block right after the head block). +func (sb *Backend) VerifyValidatorConnectionSetSignature(data []byte, sig []byte) (common.Address, error) { + if valConnSet, err := sb.RetrieveValidatorConnSet(); err != nil { + return common.Address{}, err + } else { + validators := make([]istanbul.ValidatorData, len(valConnSet)) + i := 0 + for address := range valConnSet { + validators[i].Address = address + i++ + } + + return istanbul.CheckValidatorSignature(validator.NewSet(validators), data, sig) + } +} + +func (sb *Backend) IsPrimaryForSeq(seq *big.Int) bool { + if sb.replicaState != nil { + return sb.replicaState.IsPrimaryForSeq(seq) + } + return false +} + +func (sb *Backend) IsPrimary() bool { + if sb.replicaState != nil { + return sb.replicaState.IsPrimary() + } + return false +} + +// UpdateReplicaState updates the replica state with the latest seq. +func (sb *Backend) UpdateReplicaState(seq *big.Int) { + if sb.replicaState != nil { + sb.replicaState.NewChainHead(seq) + } +} + +// recordBlockProductionTimes records information about the block production cycle and reports it through the CSVRecorder +func (sb *Backend) recordBlockProductionTimes(blockNumber uint64, txCount int, gasUsed, round uint64) { + cycle := time.Since(sb.cycleStart) + sb.cycleStart = time.Now() + sleepGauge := sb.sleepGauge + consensusGauge := metrics.Get("consensus/istanbul/core/consensus_commit").(metrics.Gauge) + verifyGauge := metrics.Get("consensus/istanbul/core/verify").(metrics.Gauge) + blockConstructGauge := metrics.Get("miner/worker/block_construct").(metrics.Gauge) + cpuSysLoadGauge := metrics.Get("system/cpu/sysload").(metrics.Gauge) + cpuSysWaitGauge := metrics.Get("system/cpu/syswait").(metrics.Gauge) + cpuProcLoadGauge := metrics.Get("system/cpu/procload").(metrics.Gauge) + + sb.csvRecorder.Write(blockNumber, txCount, gasUsed, round, + cycle.Nanoseconds(), sleepGauge.Value(), consensusGauge.Value(), verifyGauge.Value(), blockConstructGauge.Value(), + cpuSysLoadGauge.Value(), cpuSysWaitGauge.Value(), cpuProcLoadGauge.Value()) + +} + +func (sb *Backend) GetValEnodeTableEntries(valAddresses []common.Address) (map[common.Address]*istanbul.AddressEntry, error) { + addressEntries, err := sb.valEnodeTable.GetValEnodes(valAddresses) + + if err != nil { + return nil, err + } + + returnMap := make(map[common.Address]*istanbul.AddressEntry) + + for address, addressEntry := range addressEntries { + returnMap[address] = addressEntry + } + + return returnMap, nil +} + +func (sb *Backend) RewriteValEnodeTableEntries(entries map[common.Address]*istanbul.AddressEntry) error { + addressesToKeep := make(map[common.Address]bool) + entriesToUpsert := make([]*istanbul.AddressEntry, 0, len(entries)) + + for _, entry := range entries { + addressesToKeep[entry.GetAddress()] = true + entriesToUpsert = append(entriesToUpsert, entry) + } + + sb.valEnodeTable.PruneEntries(addressesToKeep) + sb.valEnodeTable.UpsertVersionAndEnode(entriesToUpsert) + + return nil +} + +// AnnounceManager wrapping functions + +// GetAnnounceVersion will retrieve the current announce version. +func (sb *Backend) GetAnnounceVersion() uint { + return sb.announceManager.GetAnnounceVersion() +} + +// UpdateAnnounceVersion will asynchronously update the announce version. +func (sb *Backend) UpdateAnnounceVersion() { + sb.announceManager.UpdateAnnounceVersion() +} + +// SetEnodeCertificateMsgMap will verify the given enode certificate message map, then update it on this struct. +func (sb *Backend) SetEnodeCertificateMsgMap(enodeCertMsgMap map[enode.ID]*istanbul.EnodeCertMsg) error { + return sb.announceManager.SetEnodeCertificateMsgMap(enodeCertMsgMap) +} + +// RetrieveEnodeCertificateMsgMap gets the most recent enode certificate messages. +// May be nil if no message was generated as a result of the core not being +// started, or if a proxy has not received a message from its proxied validator +func (sb *Backend) RetrieveEnodeCertificateMsgMap() map[enode.ID]*istanbul.EnodeCertMsg { + return sb.announceManager.RetrieveEnodeCertificateMsgMap() +} + +// StartAnnouncing implements consensus.Istanbul.StartAnnouncing +func (sb *Backend) StartAnnouncing() error { + return sb.announceManager.StartAnnouncing(func() error { + return sb.vph.startThread() + }) +} + +// StopAnnouncing implements consensus.Istanbul.StopAnnouncing +func (sb *Backend) StopAnnouncing() error { + return sb.announceManager.StopAnnouncing(func() error { + return sb.vph.stopThread() + }) +}
diff --git go-ethereum/consensus/istanbul/validator/selectors_test.go celo/consensus/istanbul/validator/selectors_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1e618aef4e2a83596f490f67899b9320634489b8 --- /dev/null +++ celo/consensus/istanbul/validator/selectors_test.go @@ -0,0 +1,270 @@ +// Copyright 2019 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package validator + +import ( + "fmt" + "reflect" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +var testAddresses = []string{ + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000003", + "0000000000000000000000000000000000000004", + "0000000000000000000000000000000000000005", +} + +func TestStickyProposer(t *testing.T) { + var addrs []common.Address + var validators []istanbul.Validator + for _, strAddr := range testAddresses { + addr := common.HexToAddress(strAddr) + addrs = append(addrs, addr) + validators = append(validators, New(addr, blscrypto.SerializedPublicKey{})) + } + + v, err := istanbul.CombineIstanbulExtraToValidatorData(addrs, make([]blscrypto.SerializedPublicKey, len(addrs))) + if err != nil { + t.Fatalf("CombineIstanbulExtraToValidatorData(...): %v", err) + } + valSet := newDefaultSet(v) + selector := GetProposerSelector(istanbul.Sticky) + + cases := []struct { + lastProposer common.Address + round uint64 + want istanbul.Validator + }{{ + lastProposer: addrs[0], + round: 0, + want: validators[0], + }, { + lastProposer: addrs[0], + round: 1, + want: validators[1], + }, { + lastProposer: addrs[0], + round: 2, + want: validators[2], + }, { + lastProposer: addrs[2], + round: 2, + want: validators[4], + }, { + lastProposer: addrs[2], + round: 3, + want: validators[0], + }, { + lastProposer: common.Address{}, + round: 3, + want: validators[3], + }} + + for i, c := range cases { + t.Run(fmt.Sprintf("case:%d", i), func(t *testing.T) { + t.Logf("selectProposer(%s, %d)", c.lastProposer.String(), c.round) + proposer := selector(valSet, c.lastProposer, c.round) + if val := proposer; !reflect.DeepEqual(val, c.want) { + t.Errorf("proposer mismatch: have %v, want %v", val, c.want) + } + }) + } +} + +func TestRoundRobinProposer(t *testing.T) { + var addrs []common.Address + var validators []istanbul.Validator + for _, strAddr := range testAddresses { + addr := common.HexToAddress(strAddr) + addrs = append(addrs, addr) + validators = append(validators, New(addr, blscrypto.SerializedPublicKey{})) + } + + v, err := istanbul.CombineIstanbulExtraToValidatorData(addrs, make([]blscrypto.SerializedPublicKey, len(addrs))) + if err != nil { + t.Fatalf("CombineIstanbulExtraToValidatorData(...): %v", err) + } + valSet := newDefaultSet(v) + selector := GetProposerSelector(istanbul.RoundRobin) + + cases := []struct { + lastProposer common.Address + round uint64 + want istanbul.Validator + }{{ + lastProposer: addrs[0], + round: 0, + want: validators[1], + }, { + lastProposer: addrs[0], + round: 1, + want: validators[2], + }, { + lastProposer: addrs[0], + round: 2, + want: validators[3], + }, { + lastProposer: addrs[2], + round: 2, + want: validators[0], + }, { + lastProposer: addrs[2], + round: 3, + want: validators[1], + }, { + lastProposer: common.Address{}, + round: 3, + want: validators[3], + }} + + for i, c := range cases { + t.Run(fmt.Sprintf("case:%d", i), func(t *testing.T) { + t.Logf("selectProposer(%s, %d)", c.lastProposer.String(), c.round) + proposer := selector(valSet, c.lastProposer, c.round) + if val := proposer; !reflect.DeepEqual(val, c.want) { + t.Errorf("proposer mismatch: have %v, want %v", val, c.want) + } + }) + } +} + +func TestShuffledRoundRobinProposer(t *testing.T) { + var addrs []common.Address + var validators []istanbul.Validator + for _, strAddr := range testAddresses { + addr := common.HexToAddress(strAddr) + addrs = append(addrs, addr) + validators = append(validators, New(addr, blscrypto.SerializedPublicKey{})) + } + + v, err := istanbul.CombineIstanbulExtraToValidatorData(addrs, make([]blscrypto.SerializedPublicKey, len(addrs))) + if err != nil { + t.Fatalf("CombineIstanbulExtraToValidatorData(...): %v", err) + } + valSet := newDefaultSet(v) + selector := GetProposerSelector(istanbul.ShuffledRoundRobin) + + // Verify a number of explicit cases with expected output. + testSeed := common.HexToHash("f36aa9716b892ec8") + cases := []struct { + lastProposer common.Address + round uint64 + seed common.Hash + want istanbul.Validator + }{{ + lastProposer: common.Address{}, + round: 0, + want: validators[0], + }, { + lastProposer: common.Address{}, + round: 3, + want: validators[2], + }, { + lastProposer: addrs[0], + round: 0, + want: validators[4], + }, { + lastProposer: addrs[0], + round: 1, + want: validators[3], + }, { + lastProposer: addrs[0], + round: 2, + want: validators[2], + }, { + lastProposer: addrs[2], + round: 2, + want: validators[4], + }, { + lastProposer: addrs[2], + round: 3, + want: validators[3], + }, { + lastProposer: common.Address{}, + round: 0, + seed: testSeed, + want: validators[1], + }, { + lastProposer: addrs[0], + round: 0, + seed: testSeed, + want: validators[2], + }, { + lastProposer: addrs[0], + round: 1, + seed: testSeed, + want: validators[1], + }, { + lastProposer: addrs[0], + round: 2, + seed: testSeed, + want: validators[4], + }} + + for i, c := range cases { + t.Run(fmt.Sprintf("case:%d", i), func(t *testing.T) { + t.Logf("SetRandomness(%s)", c.seed.String()) + valSet.SetRandomness(c.seed) + t.Logf("selectProposer(%s, %d)", c.lastProposer.String(), c.round) + proposer := selector(valSet, c.lastProposer, c.round) + if val := proposer; !reflect.DeepEqual(val, c.want) { + t.Errorf("proposer mismatch: have %v, want %v", val, c.want) + } + }) + } + + // Verify that the ordering is a stable round robin during round changes. + valSet.SetRandomness(testSeed) + t.Run("round changes", func(t *testing.T) { + var lastProposer common.Address + order := make([]common.Address, len(validators)) + for round := uint64(0); round < 100; round++ { + proposer := selector(valSet, lastProposer, round) + index := round % uint64(len(validators)) + if want := order[index]; want != (common.Address{}) { + if proposer.Address() != want { + t.Errorf("proposer mismatch on round %d: have %v, want %v", round, proposer.Address(), want) + } + } else { + order[index] = proposer.Address() + } + } + }) + + // Verify that the ordering is a stable round robin during sequence advancement. + t.Run("sequence advancement", func(t *testing.T) { + var lastProposer common.Address + order := make([]common.Address, len(validators)) + for seq := 0; seq < 100; seq++ { + proposer := selector(valSet, lastProposer, 0) + index := seq % len(validators) + if want := order[index]; want != (common.Address{}) { + if proposer.Address() != want { + t.Errorf("proposer mismatch on sequence %d: have %v, want %v", seq, proposer.Address(), want) + } + } else { + order[index] = proposer.Address() + } + } + }) +}
diff --git go-ethereum/consensus/istanbul/announce/validator_checker.go celo/consensus/istanbul/announce/validator_checker.go new file mode 100644 index 0000000000000000000000000000000000000000..33223b24ba64cf19a63b1895f20b5bd46a22b862 --- /dev/null +++ celo/consensus/istanbul/announce/validator_checker.go @@ -0,0 +1,46 @@ +package announce + +import ( + "sync/atomic" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +type ValidatorChecker interface { + // IsElectedOrNearValidator returns true iff this node is currently a NearlyElectedValidator. + IsElectedOrNearValidator() (bool, error) + // IsValidating returns true iff the node is started and running as a Validator. + IsValidating() bool +} + +type checker struct { + aWallets *atomic.Value + retrieveValidatorConnSet func() (map[common.Address]bool, error) + isValidating func() bool +} + +func NewValidatorChecker( + wallets *atomic.Value, + retrieveValidatorConnSetFn func() (map[common.Address]bool, error), + isValidatingFn func() bool) ValidatorChecker { + return &checker{ + aWallets: wallets, + retrieveValidatorConnSet: retrieveValidatorConnSetFn, + isValidating: isValidatingFn, + } +} + +func (c *checker) IsElectedOrNearValidator() (bool, error) { + // Check if this node is in the validator connection set + validatorConnSet, err := c.retrieveValidatorConnSet() + if err != nil { + return false, err + } + w := c.aWallets.Load().(*istanbul.Wallets) + return validatorConnSet[w.Ecdsa.Address], nil +} + +func (c *checker) IsValidating() bool { + return c.isValidating() +}
diff --git go-ethereum/consensus/istanbul/announce/version_certificate_db_test.go celo/consensus/istanbul/announce/version_certificate_db_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1ce7556be096da5b57c797c2ab0f54c676ba5fca --- /dev/null +++ celo/consensus/istanbul/announce/version_certificate_db_test.go @@ -0,0 +1,196 @@ +package announce + +import ( + "bytes" + "crypto/ecdsa" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" + "github.com/stretchr/testify/require" + "github.com/syndtr/goleveldb/leveldb" +) + +var keyA *ecdsa.PrivateKey +var keyB *ecdsa.PrivateKey + +func init() { + var err error + keyA, err = crypto.GenerateKey() + if err != nil { + panic(err.Error()) + } + keyB, err = crypto.GenerateKey() + if err != nil { + panic(err.Error()) + } +} + +func signA(data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), keyA) +} + +func signB(data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), keyB) +} + +func TestVersionCertificateDBUpsert(t *testing.T) { + table, err := OpenVersionCertificateDB("") + if err != nil { + t.Fatal("Failed to open DB") + } + entryA, err := istanbul.NewVersionCertificate(1, signA) + require.NoError(t, err) + entriesToUpsert := []*istanbul.VersionCertificate{entryA} + newEntries, err := table.Upsert(entriesToUpsert) + if err != nil { + t.Fatal("Failed to upsert entry") + } + if !reflect.DeepEqual(newEntries, entriesToUpsert) { + t.Errorf("Upsert did not return the expected new entries %v != %v", newEntries, entriesToUpsert) + } + + entry, err := table.Get(entryA.Address()) + if err != nil { + t.Errorf("got %v", err) + } + if !versionCertificateEntriesEqual(entry, entryA) { + t.Error("The upserted entry is not deep equal to the original") + } + + entryAOld, err := istanbul.NewVersionCertificate(0, signA) + require.NoError(t, err) + entriesToUpsert = []*istanbul.VersionCertificate{entryAOld} + newEntries, err = table.Upsert(entriesToUpsert) + if err != nil { + t.Fatal("Failed to upsert old entry") + } + if len(newEntries) != 0 { + t.Errorf("Expected no new entries to be returned by Upsert with old version, got %v", newEntries) + } + + entry, err = table.Get(entryA.Address()) + if err != nil { + t.Errorf("got %v", err) + } + if !versionCertificateEntriesEqual(entry, entryA) { + t.Error("Upserting an old version gave a new entry") + } + + entryANew, err := istanbul.NewVersionCertificate(2, signA) + require.NoError(t, err) + entriesToUpsert = []*istanbul.VersionCertificate{entryANew} + newEntries, err = table.Upsert(entriesToUpsert) + if err != nil { + t.Fatal("Failed to upsert old entry") + } + if !reflect.DeepEqual(newEntries, entriesToUpsert) { + t.Errorf("Expected new entries to be returned by Upsert with new version, got %v", newEntries) + } + + entry, err = table.Get(entryA.Address()) + if err != nil { + t.Errorf("got %v", err) + } + if !reflect.DeepEqual(entry, entryANew) { + t.Error("Upserting a new version did not give a new entry") + } +} + +func TestVersionCertificateDBRemove(t *testing.T) { + table, err := OpenVersionCertificateDB("") + if err != nil { + t.Fatal("Failed to open DB") + } + + entryA, err := istanbul.NewVersionCertificate(1, signA) + require.NoError(t, err) + entriesToUpsert := []*istanbul.VersionCertificate{entryA} + _, err = table.Upsert(entriesToUpsert) + if err != nil { + t.Fatal("Failed to upsert entry") + } + + err = table.Remove(entryA.Address()) + if err != nil { + t.Fatal("Failed to delete") + } + + if _, err := table.Get(entryA.Address()); err != nil { + if err != leveldb.ErrNotFound { + t.Fatalf("Can't get, different error: %v", err) + } + } else { + t.Fatalf("Delete didn't work") + } +} + +func TestVersionCertificateDBPrune(t *testing.T) { + table, err := OpenVersionCertificateDB("") + if err != nil { + t.Fatal("Failed to open DB") + } + + entryA, err := istanbul.NewVersionCertificate(1, signA) + require.NoError(t, err) + entryB, err := istanbul.NewVersionCertificate(1, signB) + require.NoError(t, err) + batch := []*istanbul.VersionCertificate{entryA, entryB} + + _, err = table.Upsert(batch) + if err != nil { + t.Fatal("Failed to upsert entry") + } + + addressesToKeep := make(map[common.Address]bool) + addressesToKeep[entryB.Address()] = true + + table.Prune(addressesToKeep) + + _, err = table.Get(entryB.Address()) + if err != nil { + t.Errorf("It should have found %s after prune", entryB.Address().Hex()) + } + _, err = table.Get(entryA.Address()) + if err == nil { + t.Errorf("It should have NOT found %s after prune", entryA.Address().Hex()) + } + +} + +func TestVersionCertificateEntryRLP(t *testing.T) { + + original, err := istanbul.NewVersionCertificate(1, signA) + require.NoError(t, err) + + rawEntry, err := rlp.EncodeToBytes(original) + if err != nil { + t.Errorf("Error %v", err) + } + + var result istanbul.VersionCertificate + if err = rlp.DecodeBytes(rawEntry, &result); err != nil { + t.Errorf("Error %v", err) + } + + if result.Address().String() != original.Address().String() { + t.Errorf("node doesn't match: got: %s expected: %s", result.Address().String(), original.Address().String()) + } + if result.Version != original.Version { + t.Errorf("version doesn't match: got: %v expected: %v", result.Version, original.Version) + } + if !bytes.Equal(result.Signature, original.Signature) { + t.Errorf("version doesn't match: got: %v expected: %v", result.Signature, original.Signature) + } +} + +// Compares the field values of two VersionCertificateEntrys +func versionCertificateEntriesEqual(a, b *istanbul.VersionCertificate) bool { + return a.Address() == b.Address() && + bytes.Equal(crypto.FromECDSAPub(a.PublicKey()), crypto.FromECDSAPub(b.PublicKey())) && + a.Version == b.Version && + bytes.Equal(a.Signature, b.Signature) +}
diff --git go-ethereum/consensus/istanbul/announce/task_state.go celo/consensus/istanbul/announce/task_state.go new file mode 100644 index 0000000000000000000000000000000000000000..e6933c2baf5c5345fcce6ce7fd1449a9df1474d3 --- /dev/null +++ celo/consensus/istanbul/announce/task_state.go @@ -0,0 +1,201 @@ +package announce + +import ( + "time" + + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" +) + +// QueryEnodeGossipFrequencyState specifies how frequently to gossip query enode messages +type QueryEnodeGossipFrequencyState int + +const ( + // HighFreqBeforeFirstPeerState will send out a query enode message every 1 minute until the first peer is established + HighFreqBeforeFirstPeerState QueryEnodeGossipFrequencyState = iota + + // HighFreqAfterFirstPeerState will send out an query enode message every 1 minute for the first 10 query enode messages after the first peer is established. + // This is on the assumption that when this node first establishes a peer, the p2p network that this node is in may + // be partitioned with the broader p2p network. We want to give that p2p network some time to connect to the broader p2p network. + HighFreqAfterFirstPeerState + + // LowFreqState will send out an query every config.AnnounceQueryEnodeGossipPeriod seconds + LowFreqState +) + +// announceTaskState encapsulates the state needed to guide the behavior of the announce protocol +// thread. This type is designed to be used from A SINGLE THREAD only. +type announceTaskState struct { + config *istanbul.Config + // Create a ticker to poll if istanbul core is running and if this node is in + // the validator conn set. If both conditions are true, then this node should announce. + checkIfShouldAnnounceTicker *time.Ticker + // Occasionally share the entire version certificate table with all peers + shareVersionCertificatesTicker *time.Ticker + pruneAnnounceDataStructuresTicker *time.Ticker + + queryEnodeTicker *time.Ticker + queryEnodeTickerCh <-chan time.Time + queryEnodeFrequencyState QueryEnodeGossipFrequencyState + currentQueryEnodeTickerDuration time.Duration + numQueryEnodesInHighFreqAfterFirstPeerState int + // TODO: this can be removed once we have more faith in this protocol + updateAnnounceVersionTicker *time.Ticker + updateAnnounceVersionTickerCh <-chan time.Time + + generateAndGossipQueryEnodeCh chan struct{} + + // Replica validators listen & query for enodes (query true, announce false) + // Primary validators annouce (updateAnnounceVersion) (query true, announce true) + // Replicas need to query to populate their validator enode table, but don't want to + // update the proxie's validator assignments at the same time as the primary. + shouldQuery, shouldAnnounce bool + querying, announcing bool +} + +func NewAnnounceTaskState(config *istanbul.Config) *announceTaskState { + return &announceTaskState{ + config: config, + checkIfShouldAnnounceTicker: time.NewTicker(5 * time.Second), + shareVersionCertificatesTicker: time.NewTicker(5 * time.Minute), + pruneAnnounceDataStructuresTicker: time.NewTicker(10 * time.Minute), + generateAndGossipQueryEnodeCh: make(chan struct{}, 1), + } +} + +func (st *announceTaskState) ShouldStartQuerying() bool { + return st.shouldQuery && !st.querying +} + +func (st *announceTaskState) OnStartQuerying() { + if st.config.AnnounceAggressiveQueryEnodeGossipOnEnablement { + st.queryEnodeFrequencyState = HighFreqBeforeFirstPeerState + // Send an query enode message once a minute + st.currentQueryEnodeTickerDuration = 1 * time.Minute + st.numQueryEnodesInHighFreqAfterFirstPeerState = 0 + } else { + st.queryEnodeFrequencyState = LowFreqState + st.currentQueryEnodeTickerDuration = time.Duration(st.config.AnnounceQueryEnodeGossipPeriod) * time.Second + } + + // Enable periodic gossiping by setting announceGossipTickerCh to non nil value + st.queryEnodeTicker = time.NewTicker(st.currentQueryEnodeTickerDuration) + st.queryEnodeTickerCh = st.queryEnodeTicker.C + + st.querying = true +} + +func (st *announceTaskState) ShouldStopQuerying() bool { + return !st.shouldQuery && st.querying +} + +func (st *announceTaskState) OnStopQuerying() { + // Disable periodic queryEnode msgs by setting queryEnodeTickerCh to nil + st.queryEnodeTicker.Stop() + st.queryEnodeTickerCh = nil + st.querying = false +} + +func (st *announceTaskState) ShouldStartAnnouncing() bool { + return st.shouldAnnounce && !st.announcing +} + +func (st *announceTaskState) OnStartAnnouncing() { + st.updateAnnounceVersionTicker = time.NewTicker(5 * time.Minute) + st.updateAnnounceVersionTickerCh = st.updateAnnounceVersionTicker.C + + st.announcing = true +} + +func (st *announceTaskState) ShouldStopAnnouncing() bool { + return !st.shouldAnnounce && st.announcing +} + +func (st *announceTaskState) OnStopAnnouncing() { + // Disable periodic updating of announce version + st.updateAnnounceVersionTicker.Stop() + st.updateAnnounceVersionTickerCh = nil + + st.announcing = false +} + +func (st *announceTaskState) UpdateFrequencyOnGenerate(peers int) { + switch st.queryEnodeFrequencyState { + case HighFreqBeforeFirstPeerState: + if peers > 0 { + st.queryEnodeFrequencyState = HighFreqAfterFirstPeerState + } + + case HighFreqAfterFirstPeerState: + if st.numQueryEnodesInHighFreqAfterFirstPeerState >= 10 { + st.queryEnodeFrequencyState = LowFreqState + } + st.numQueryEnodesInHighFreqAfterFirstPeerState++ + + case LowFreqState: + if st.currentQueryEnodeTickerDuration != time.Duration(st.config.AnnounceQueryEnodeGossipPeriod)*time.Second { + // Reset the ticker + st.currentQueryEnodeTickerDuration = time.Duration(st.config.AnnounceQueryEnodeGossipPeriod) * time.Second + st.queryEnodeTicker.Stop() + st.queryEnodeTicker = time.NewTicker(st.currentQueryEnodeTickerDuration) + st.queryEnodeTickerCh = st.queryEnodeTicker.C + } + } +} + +func (st *announceTaskState) OnAnnounceThreadQuitting() { + st.checkIfShouldAnnounceTicker.Stop() + st.pruneAnnounceDataStructuresTicker.Stop() + st.shareVersionCertificatesTicker.Stop() + if st.querying { + st.queryEnodeTicker.Stop() + } + if st.announcing { + st.updateAnnounceVersionTicker.Stop() + } +} + +// startGossipQueryEnodeTask will schedule a task for the announceThread to +// generate and gossip a queryEnode message +func (st *announceTaskState) startGossipQueryEnodeTask() { + // sb.generateAndGossipQueryEnodeCh has a buffer of 1. If there is a value + // already sent to the channel that has not been read from, don't block. + select { + case st.generateAndGossipQueryEnodeCh <- struct{}{}: + default: + } +} + +func (st *announceTaskState) updateAnnounceThreadStatus(logger log.Logger, startWaitPeriod time.Duration, updateAnnounceVersion func()) { + if st.ShouldStartQuerying() { + logger.Info("Starting to query") + + time.AfterFunc(startWaitPeriod, func() { + st.startGossipQueryEnodeTask() + }) + + st.OnStartQuerying() + + logger.Trace("Enabled periodic gossiping of announce message (query mode)") + + } else if st.ShouldStopQuerying() { + logger.Info("Stopping querying") + + st.OnStopQuerying() + logger.Trace("Disabled periodic gossiping of announce message (query mode)") + } + + if st.ShouldStartAnnouncing() { + logger.Info("Starting to announce") + + updateAnnounceVersion() + + st.OnStartAnnouncing() + logger.Trace("Enabled periodic gossiping of announce message") + } else if st.ShouldStopAnnouncing() { + logger.Info("Stopping announcing") + + st.OnStopAnnouncing() + logger.Trace("Disabled periodic gossiping of announce message") + } +}
diff --git go-ethereum/consensus/istanbul/proxy/consensus_message.go celo/consensus/istanbul/proxy/consensus_message.go new file mode 100644 index 0000000000000000000000000000000000000000..67b20e77e5c9eae01b2f875129c2ac9050452009 --- /dev/null +++ celo/consensus/istanbul/proxy/consensus_message.go @@ -0,0 +1,52 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +// handleConsensusMsg is invoked by the proxy to forward valid consensus messages to +// it's proxied validator +func (p *proxyEngine) handleConsensusMsg(peer consensus.Peer, payload []byte) (bool, error) { + logger := p.logger.New("func", "handleConsensusMsg") + + // Verify that this message is not from the proxied validator + p.proxiedValidatorsMu.RLock() + defer p.proxiedValidatorsMu.RUnlock() + if ok := p.proxiedValidatorIDs[peer.Node().ID()]; ok { + logger.Warn("Got a consensus message from the proxied validator. Ignoring it", "from", peer.Node().ID()) + return false, nil + } + + msg := new(istanbul.Message) + + // Verify that this message is created by a legitimate validator before forwarding to the proxied validator. + if err := msg.FromPayload(payload, p.backend.VerifyPendingBlockValidatorSignature); err != nil { + logger.Error("Got a consensus message signed by a validator not within the pending block validator set.", "err", err) + return true, istanbul.ErrUnauthorizedAddress + } + + // Need to forward the message to the proxied validators + logger.Trace("Forwarding consensus message to proxied validators", "from", peer.Node().ID()) + for proxiedValidator := range p.proxiedValidators { + p.backend.Unicast(proxiedValidator, payload, istanbul.ConsensusMsg) + } + + return true, nil +}
diff --git go-ethereum/consensus/istanbul/proxy/delegate_sign_message.go celo/consensus/istanbul/proxy/delegate_sign_message.go new file mode 100644 index 0000000000000000000000000000000000000000..49ce8223d53e4a354b98260b6dc6fe22bf270697 --- /dev/null +++ celo/consensus/istanbul/proxy/delegate_sign_message.go @@ -0,0 +1,54 @@ +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// SendDelegateSignMsgToProxy sends an istanbulDelegateSign message to a proxy +// if one exists +func (pv *proxiedValidatorEngine) SendDelegateSignMsgToProxy(msg []byte, peerID enode.ID) error { + proxy, err := pv.getProxy(peerID) + if err != nil { + return err + } + + if proxy == nil { + // If we got here, then the proxy that sent the message to be signed is not up anymore + return ErrNoCelostatsProxy + } + + pv.backend.Unicast(proxy.peer, msg, istanbul.DelegateSignMsg) + return nil +} + +// SendDelegateSignMsgToProxiedValidator sends an istanbulDelegateSign message to a +// proxied validator if one exists +func (p *proxyEngine) SendDelegateSignMsgToProxiedValidator(msg []byte) error { + p.proxiedValidatorsMu.RLock() + defer p.proxiedValidatorsMu.RUnlock() + + if len(p.proxiedValidators) != 0 { + for proxiedValidator := range p.proxiedValidators { + p.backend.Unicast(proxiedValidator, msg, istanbul.DelegateSignMsg) + } + return nil + } + + return ErrNoProxiedValidator +}
diff --git go-ethereum/consensus/istanbul/announce/val_enode_db_test.go celo/consensus/istanbul/announce/val_enode_db_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ad6463c10addff7e545a2bf9aa6a8d6977809fc7 --- /dev/null +++ celo/consensus/istanbul/announce/val_enode_db_test.go @@ -0,0 +1,155 @@ +package announce + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" + "github.com/syndtr/goleveldb/leveldb" +) + +var ( + addressA = common.HexToAddress("0x00Ce0d46d924CC8437c806721496599FC3FFA268") + addressB = common.HexToAddress("0xFFFFFF46d924CCFFFFc806721496599FC3FFFFFF") + enodeURLA = "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150" + enodeURLB = "enode://38b219b54ed49cf7d802e8add586fc75b531ed2c31e43b5da71c35982b2e6f5c56fa9cfbe39606fe71fbee2566b94c2874e950b1ec88323103c835246e3d0023@127.0.0.1:37303" + nodeA, _ = enode.ParseV4(enodeURLA) + nodeB, _ = enode.ParseV4(enodeURLB) +) + +type mockListener struct{} + +func (ml *mockListener) AddValidatorPeer(node *enode.Node, address common.Address) {} +func (ml *mockListener) RemoveValidatorPeer(node *enode.Node) {} +func (ml *mockListener) ReplaceValidatorPeers(nodeNodes []*enode.Node) {} +func (ml *mockListener) ClearValidatorPeers() {} + +func TestSimpleCase(t *testing.T) { + vet, err := OpenValidatorEnodeDB("", &mockListener{}) + if err != nil { + t.Fatal("Failed to open DB") + } + + addressEntry := &istanbul.AddressEntry{Address: addressA, Node: nodeA, Version: 1} + + err = vet.UpsertVersionAndEnode([]*istanbul.AddressEntry{addressEntry}) + if err != nil { + t.Fatal("Failed to upsert") + } + + addr, err := vet.GetAddressFromNodeID(nodeA.ID()) + if err != nil { + t.Errorf("got %v", err) + } + if addr != addressA { + t.Error("Invalid address saved") + } + + node, err := vet.GetNodeFromAddress(addressA) + if err != nil { + t.Errorf("got %v", err) + } + if node.String() != enodeURLA { + t.Error("Invalid enode saved") + } +} + +func TestDeleteEntry(t *testing.T) { + vet, err := OpenValidatorEnodeDB("", &mockListener{}) + if err != nil { + t.Fatal("Failed to open DB") + } + + addressEntry := &istanbul.AddressEntry{Address: addressA, Node: nodeA, Version: 2} + + err = vet.UpsertVersionAndEnode([]*istanbul.AddressEntry{addressEntry}) + if err != nil { + t.Fatal("Failed to upsert") + } + + err = vet.RemoveEntry(addressA) + if err != nil { + t.Fatal("Failed to delete") + } + + if _, err := vet.GetNodeFromAddress(addressA); err != nil { + if err != leveldb.ErrNotFound { + t.Fatalf("Can't get, different error: %v", err) + } + } else { + t.Fatalf("Delete didn't work") + } + +} + +func TestPruneEntries(t *testing.T) { + vet, err := OpenValidatorEnodeDB("", &mockListener{}) + if err != nil { + t.Fatal("Failed to open DB") + } + + batch := []*istanbul.AddressEntry{ + {Address: addressA, Node: nodeA, Version: 2}, + {Address: addressB, Node: nodeB, Version: 2}, + } + + vet.UpsertVersionAndEnode(batch) + + addressesToKeep := make(map[common.Address]bool) + addressesToKeep[addressB] = true + + vet.PruneEntries(addressesToKeep) + + _, err = vet.GetNodeFromAddress(addressB) + if err != nil { + t.Errorf("It should have found %s after prune", addressB.Hex()) + } + _, err = vet.GetNodeFromAddress(addressA) + if err == nil { + t.Errorf("It should have NOT found %s after prune", addressA.Hex()) + } + +} + +func TestRLPEntries(t *testing.T) { + original := istanbul.AddressEntry{Address: addressA, Node: nodeA, Version: 1} + + rawEntry, err := rlp.EncodeToBytes(&original) + if err != nil { + t.Errorf("Error %v", err) + } + + var result istanbul.AddressEntry + if err = rlp.DecodeBytes(rawEntry, &result); err != nil { + t.Errorf("Error %v", err) + } + + if result.Node.String() != original.Node.String() { + t.Errorf("node doesn't match: got: %s expected: %s", result.Node.String(), original.Node.String()) + } + if result.Version != original.Version { + t.Errorf("version doesn't match: got: %v expected: %v", result.Version, original.Version) + } +} + +func TestTableToString(t *testing.T) { + vet, err := OpenValidatorEnodeDB("", &mockListener{}) + if err != nil { + t.Fatal("Failed to open DB") + } + + batch := []*istanbul.AddressEntry{ + {Address: addressA, Node: nodeA, Version: 2}, + {Address: addressB, Node: nodeB, Version: 2}, + } + + vet.UpsertVersionAndEnode(batch) + + expected := "ValEnodeTable: [0x00Ce0d46d924CC8437c806721496599FC3FFA268 => {address: 0x00Ce0d46d924CC8437c806721496599FC3FFA268, enodeURL: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150, version: 2, highestKnownVersion: 0, numQueryAttempsForHKVersion: 0, LastQueryTimestamp: 0001-01-01 00:00:00 +0000 UTC}] [0xfFFFff46D924CCfffFc806721496599fC3FFffff => {address: 0xfFFFff46D924CCfffFc806721496599fC3FFffff, enodeURL: enode://38b219b54ed49cf7d802e8add586fc75b531ed2c31e43b5da71c35982b2e6f5c56fa9cfbe39606fe71fbee2566b94c2874e950b1ec88323103c835246e3d0023@127.0.0.1:37303, version: 2, highestKnownVersion: 0, numQueryAttempsForHKVersion: 0, LastQueryTimestamp: 0001-01-01 00:00:00 +0000 UTC}]" + + if vet.String() != expected { + t.Errorf("String() error: got: %s", vet.String()) + } +}
diff --git go-ethereum/consensus/istanbul/backend/test_utils.go celo/consensus/istanbul/backend/test_utils.go new file mode 100644 index 0000000000000000000000000000000000000000..7f5e77ed3316299802e0bfc4bd9c210b0a173d41 --- /dev/null +++ celo/consensus/istanbul/backend/test_utils.go @@ -0,0 +1,373 @@ +package backend + +import ( + "bytes" + "crypto/ecdsa" + "errors" + "fmt" + "strings" + "time" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/celo-org/celo-bls-go/bls" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/backend/backendtest" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +// in this test, we can set n to 1, and it means we can process Istanbul and commit a +// block by one node. Otherwise, if n is larger than 1, we have to generate +// other fake events to process Istanbul. +func newBlockChain(n int, isFullChain bool) (*core.BlockChain, *Backend) { + genesis, nodeKeys := getGenesisAndKeys(n, isFullChain) + + bc, be, _ := newBlockChainWithKeys(false, common.Address{}, false, genesis, nodeKeys[0]) + return bc, be +} + +func newBlockChainWithKeys(isProxy bool, proxiedValAddress common.Address, isProxied bool, genesis *core.Genesis, privateKey *ecdsa.PrivateKey) (*core.BlockChain, *Backend, *istanbul.Config) { + memDB := rawdb.NewMemoryDatabase() + config := *istanbul.DefaultConfig + config.ReplicaStateDBPath = "" + config.ValidatorEnodeDBPath = "" + config.VersionCertificateDBPath = "" + config.RoundStateDBPath = "" + config.Proxy = isProxy + config.ProxiedValidatorAddress = proxiedValAddress + config.Proxied = isProxied + config.Validator = !isProxy + istanbul.ApplyParamsChainConfigToConfig(genesis.Config, &config) + + b, _ := New(&config, memDB).(*Backend) + + var publicKey ecdsa.PublicKey + if !isProxy { + publicKey = privateKey.PublicKey + address := crypto.PubkeyToAddress(publicKey) + decryptFn := DecryptFn(privateKey) + signerFn := SignFn(privateKey) + signerBLSFn := SignBLSFn(privateKey) + signerHashFn := SignHashFn(privateKey) + b.Authorize(address, address, &publicKey, decryptFn, signerFn, signerBLSFn, signerHashFn) + } else { + proxyNodeKey, _ := crypto.GenerateKey() + publicKey = proxyNodeKey.PublicKey + } + + genesis.MustCommit(memDB) + + blockchain, err := core.NewBlockChain(memDB, nil, genesis.Config, b, vm.Config{}, nil, nil) + if err != nil { + panic(err) + } + + b.SetChain( + blockchain, + blockchain.CurrentBlock, + func(hash common.Hash) (*state.StateDB, error) { + stateRoot := blockchain.GetHeaderByHash(hash).Root + return blockchain.StateAt(stateRoot) + }, + ) + b.SetBroadcaster(&consensustest.MockBroadcaster{}) + b.SetP2PServer(consensustest.NewMockP2PServer(&publicKey)) + b.StartAnnouncing() + + if !isProxy { + b.SetCallBacks(blockchain.HasBadBlock, + func(block *types.Block, state *state.StateDB) (types.Receipts, []*types.Log, uint64, error) { + return blockchain.Processor().Process(block, state, *blockchain.GetVMConfig()) + }, + blockchain.Validator().ValidateState, + func(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB) { + if err := blockchain.InsertPreprocessedBlock(block, receipts, logs, state); err != nil { + panic(fmt.Sprintf("could not InsertPreprocessedBlock: %v", err)) + } + }) + if isProxied { + b.StartProxiedValidatorEngine() + } + b.StartValidating() + } + + return blockchain, b, &config +} + +func getGenesisAndKeys(n int, isFullChain bool) (*core.Genesis, []*ecdsa.PrivateKey) { + // Setup validators + var nodeKeys = make([]*ecdsa.PrivateKey, n) + validators := make([]istanbul.ValidatorData, n) + for i := 0; i < n; i++ { + var addr common.Address + if i == 0 { + nodeKeys[i], _ = generatePrivateKey() + addr = getAddress() + } else { + nodeKeys[i], _ = crypto.GenerateKey() + addr = crypto.PubkeyToAddress(nodeKeys[i].PublicKey) + } + blsPrivateKey, _ := blscrypto.ECDSAToBLS(nodeKeys[i]) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + validators[i] = istanbul.ValidatorData{ + Address: addr, + BLSPublicKey: blsPublicKey, + } + + } + + // generate genesis block + genesis := core.MainnetGenesisBlock() + genesis.Config = params.IstanbulTestChainConfig + if !isFullChain { + genesis.Config.FullHeaderChainAvailable = false + } + // force enable Istanbul engine + genesis.Config.Istanbul = &params.IstanbulConfig{ + Epoch: 10, + } + + AppendValidatorsToGenesisBlock(genesis, validators) + return genesis, nodeKeys +} + +func AppendValidatorsToGenesisBlock(genesis *core.Genesis, validators []istanbul.ValidatorData) { + if len(genesis.ExtraData) < types.IstanbulExtraVanity { + genesis.ExtraData = append(genesis.ExtraData, bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)...) + } + genesis.ExtraData = genesis.ExtraData[:types.IstanbulExtraVanity] + + var addrs []common.Address + var publicKeys []blscrypto.SerializedPublicKey + + for i := range validators { + if (validators[i].BLSPublicKey == blscrypto.SerializedPublicKey{}) { + panic("BLSPublicKey is nil") + } + addrs = append(addrs, validators[i].Address) + publicKeys = append(publicKeys, validators[i].BLSPublicKey) + } + + ist := &types.IstanbulExtra{ + AddedValidators: addrs, + AddedValidatorsPublicKeys: publicKeys, + Seal: []byte{}, + AggregatedSeal: types.IstanbulAggregatedSeal{}, + ParentAggregatedSeal: types.IstanbulAggregatedSeal{}, + } + + istPayload, err := rlp.EncodeToBytes(&ist) + if err != nil { + panic("failed to encode istanbul extra") + } + genesis.ExtraData = append(genesis.ExtraData, istPayload...) +} + +func makeHeader(parent *types.Block, config *istanbul.Config) *types.Header { + header := &types.Header{ + ParentHash: parent.Hash(), + Number: parent.Number().Add(parent.Number(), common.Big1), + GasUsed: 0, + Extra: parent.Extra(), + Time: parent.Time() + config.BlockPeriod, + } + return header +} + +func makeBlock(keys []*ecdsa.PrivateKey, chain *core.BlockChain, engine *Backend, parent *types.Block) (*types.Block, error) { + block := makeBlockWithoutSeal(chain, engine, parent) + + // Set up block subscription + chainHeadCh := make(chan core.ChainHeadEvent, 10) + sub := chain.SubscribeChainHeadEvent(chainHeadCh) + defer sub.Unsubscribe() + + // start seal request (this is non-blocking) + err := engine.Seal(chain, block) + if err != nil { + return nil, err + } + + // Wait for and then save the mined block. + select { + case ev := <-chainHeadCh: + block = ev.Block + case <-time.After(6 * time.Second): + return nil, errors.New("Timed out when making a block") + } + + // Notify the core engine to stop working on current Seal. + go engine.istanbulEventMux.Post(istanbul.FinalCommittedEvent{}) + + return block, nil +} + +func makeBlockWithoutSeal(chain *core.BlockChain, engine *Backend, parent *types.Block) *types.Block { + header := makeHeader(parent, engine.config) + // The worker that calls Prepare is the one filling the Coinbase + header.Coinbase = engine.wallets().Ecdsa.Address + engine.Prepare(chain, header) + time.Sleep(time.Until(time.Unix(int64(header.Time), 0))) + + state, err := chain.StateAt(parent.Root()) + if err != nil { + fmt.Printf("Error!! %v\n", err) + } + engine.Finalize(chain, header, state, nil) + + block, err := engine.FinalizeAndAssemble(chain, header, state, nil, nil, nil) + if err != nil { + fmt.Printf("Error!! %v\n", err) + } + + return block +} + +/** + * SimpleBackend + * Private key: bb047e5940b6d83354d9432db7c449ac8fca2248008aaa7271369880f9f11cc1 + * Public key: 04a2bfb0f7da9e1b9c0c64e14f87e8fb82eb0144e97c25fe3a977a921041a50976984d18257d2495e7bfd3d4b280220217f429287d25ecdf2b0d7c0f7aae9aa624 + * Address: 0x70524d664ffe731100208a0154e556f9bb679ae6 + */ +func getAddress() common.Address { + return common.HexToAddress("0x70524d664ffe731100208a0154e556f9bb679ae6") +} + +func getInvalidAddress() common.Address { + return common.HexToAddress("0xc63597005f0da07a9ea85b5052a77c3b0261bdca") +} + +func generatePrivateKey() (*ecdsa.PrivateKey, error) { + key := "bb047e5940b6d83354d9432db7c449ac8fca2248008aaa7271369880f9f11cc1" + return crypto.HexToECDSA(key) +} + +func newTestValidatorSet(n int) (istanbul.ValidatorSet, []*ecdsa.PrivateKey) { + // generate validators + keys := make(Keys, n) + validators := make([]istanbul.ValidatorData, n) + for i := 0; i < n; i++ { + privateKey, _ := crypto.GenerateKey() + blsPrivateKey, _ := blscrypto.ECDSAToBLS(privateKey) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + keys[i] = privateKey + validators[i] = istanbul.ValidatorData{ + Address: crypto.PubkeyToAddress(privateKey.PublicKey), + BLSPublicKey: blsPublicKey, + } + } + vset := validator.NewSet(validators) + return vset, keys +} + +type Keys []*ecdsa.PrivateKey + +func (slice Keys) Len() int { + return len(slice) +} + +func (slice Keys) Less(i, j int) bool { + return strings.Compare(crypto.PubkeyToAddress(slice[i].PublicKey).String(), crypto.PubkeyToAddress(slice[j].PublicKey).String()) < 0 +} + +func (slice Keys) Swap(i, j int) { + slice[i], slice[j] = slice[j], slice[i] +} + +func DecryptFn(key *ecdsa.PrivateKey) istanbul.DecryptFn { + if key == nil { + key, _ = generatePrivateKey() + } + + return func(_ accounts.Account, c, s1, s2 []byte) ([]byte, error) { + eciesKey := ecies.ImportECDSA(key) + return eciesKey.Decrypt(c, s1, s2) + } +} + +func SignFn(key *ecdsa.PrivateKey) istanbul.SignerFn { + if key == nil { + key, _ = generatePrivateKey() + } + + return func(_ accounts.Account, mimeType string, data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), key) + } +} + +func SignBLSFn(key *ecdsa.PrivateKey) istanbul.BLSSignerFn { + if key == nil { + key, _ = generatePrivateKey() + } + + return func(_ accounts.Account, data []byte, extraData []byte, useComposite, cip22 bool) (blscrypto.SerializedSignature, error) { + privateKeyBytes, err := blscrypto.ECDSAToBLS(key) + if err != nil { + return blscrypto.SerializedSignature{}, err + } + + privateKey, err := bls.DeserializePrivateKey(privateKeyBytes) + if err != nil { + return blscrypto.SerializedSignature{}, err + } + defer privateKey.Destroy() + + signature, err := privateKey.SignMessage(data, extraData, useComposite, cip22) + if err != nil { + return blscrypto.SerializedSignature{}, err + } + defer signature.Destroy() + signatureBytes, err := signature.Serialize() + if err != nil { + return blscrypto.SerializedSignature{}, err + } + + return blscrypto.SerializedSignatureFromBytes(signatureBytes) + } +} + +func SignHashFn(key *ecdsa.PrivateKey) istanbul.HashSignerFn { + if key == nil { + key, _ = generatePrivateKey() + } + + return func(_ accounts.Account, data []byte) ([]byte, error) { + return crypto.Sign(data, key) + } +} + +func newBackend() (b *Backend) { + _, b = newBlockChain(4, true) + + key, _ := generatePrivateKey() + address := crypto.PubkeyToAddress(key.PublicKey) + b.Authorize(address, address, &key.PublicKey, DecryptFn(key), SignFn(key), SignBLSFn(key), SignHashFn(key)) + return +} + +type testBackendFactoryImpl struct{} + +// TestBackendFactory can be passed to backendtest.InitTestBackendFactory +var TestBackendFactory backendtest.TestBackendFactory = testBackendFactoryImpl{} + +// New is part of TestBackendInterface. +func (testBackendFactoryImpl) New(isProxy bool, proxiedValAddress common.Address, isProxied bool, genesisCfg *core.Genesis, privateKey *ecdsa.PrivateKey) (backendtest.TestBackendInterface, *istanbul.Config) { + _, be, config := newBlockChainWithKeys(isProxy, proxiedValAddress, isProxied, genesisCfg, privateKey) + return be, config +} + +// GetGenesisAndKeys is part of TestBackendInterface +func (testBackendFactoryImpl) GetGenesisAndKeys(numValidators int, isFullChain bool) (*core.Genesis, []*ecdsa.PrivateKey) { + return getGenesisAndKeys(numValidators, isFullChain) +}
diff --git go-ethereum/consensus/istanbul/core/preprepare_v2.go celo/consensus/istanbul/core/preprepare_v2.go new file mode 100644 index 0000000000000000000000000000000000000000..076a7342bafef0bbace640f2066278226a6b6a6e --- /dev/null +++ celo/consensus/istanbul/core/preprepare_v2.go @@ -0,0 +1,139 @@ +package core + +import ( + "errors" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +func (c *core) sendPreprepareV2(request *istanbul.Request, roundChangeCertificateV2 istanbul.RoundChangeCertificateV2) { + logger := c.newLogger("func", "sendPreprepareV2") + + // If I'm the proposer and I have the same sequence with the proposal + if c.current.Sequence().Cmp(request.Proposal.Number()) == 0 && c.isProposer() { + m := istanbul.NewPreprepareV2Message(&istanbul.PreprepareV2{ + View: c.current.View(), + Proposal: request.Proposal, + RoundChangeCertificateV2: roundChangeCertificateV2, + }, c.address) + logger.Debug("Sending preprepareV2", "m", m) + c.broadcast(m) + } +} + +// ResendPreprepare sends again the preprepare message. +func (c *core) ResendPreprepare() error { + logger := c.newLogger("func", "resendPreprepare") + if !c.isProposer() { + return errors.New("Cant resend preprepare if not proposer") + } + st := c.current.State() + if st != StatePreprepared && st != StatePrepared && st != StateCommitted { + return errors.New("Cant resend preprepare if not in preprepared, prepared, or committed state") + } + m := istanbul.NewPreprepareV2Message(c.current.PreprepareV2(), c.address) + logger.Debug("Re-Sending preprepare v2", "m", m) + c.broadcast(m) + return nil +} + +func (c *core) handlePreprepareV2(msg *istanbul.Message) error { + defer c.handlePrePrepareTimer.UpdateSince(time.Now()) + + logger := c.newLogger("func", "handlePreprepareV2", "tag", "handleMsg", "from", msg.Address) + logger.Trace("Got preprepareV2 message", "m", msg) + + preprepareV2 := msg.PreprepareV2() + + logger = logger.New("msg_num", preprepareV2.Proposal.Number(), "msg_hash", + preprepareV2.Proposal.Hash(), "msg_seq", preprepareV2.View.Sequence, "msg_round", preprepareV2.View.Round) + + // Verify that the proposal is for the sequence number of the view we verified. + if preprepareV2.View.Sequence.Cmp(preprepareV2.Proposal.Number()) != 0 { + logger.Warn("Received preprepare with invalid block number") + return errInvalidProposal + } + + // Ensure we have the same view with the PREPREPARE message. + if err := c.checkMessage(istanbul.MsgPreprepareV2, preprepareV2.View); err != nil { + if err == errOldMessage { + // Get validator set for the given proposal + valSet := c.backend.ParentBlockValidators(preprepareV2.Proposal) + prevBlockAuthor := c.backend.AuthorForBlock(preprepareV2.Proposal.Number().Uint64() - 1) + proposer := c.selectProposer(valSet, prevBlockAuthor, preprepareV2.View.Round.Uint64()) + + // We no longer broadcast a COMMIT if this is a PREPREPARE from the correct proposer for an existing block. + // However, we log a WARN for potential future debugging value. + if proposer.Address() == msg.Address && c.backend.HasBlock(preprepareV2.Proposal.Hash(), preprepareV2.Proposal.Number()) { + logger.Warn("Would have sent a commit message for an old block") + return nil + } + } + // Probably shouldn't errFutureMessage as we should have moved to that round in handleRoundChangeCertificate + logger.Trace("Check preprepare failed", "err", err) + return err + } + + // Check proposer is valid for the message's view (this may be a subsequent round) + headBlock, headProposer := c.backend.GetCurrentHeadBlockAndAuthor() + if headBlock == nil { + logger.Error("Could not determine head proposer") + return errNotFromProposer + } + proposerForMsgRound := c.selectProposer(c.current.ValidatorSet(), headProposer, preprepareV2.View.Round.Uint64()) + if proposerForMsgRound.Address() != msg.Address { + logger.Warn("Ignore preprepare message from non-proposer", "actual_proposer", proposerForMsgRound.Address()) + return errNotFromProposer + } + + // If round > 0, handle the ROUND CHANGE certificate. If round = 0, it should not have a ROUND CHANGE certificate + if preprepareV2.View.Round.Cmp(common.Big0) > 0 { + if !preprepareV2.HasRoundChangeCertificateV2() { + logger.Error("Preprepare for non-zero round did not contain a round change certificate.") + return errMissingRoundChangeCertificate + } + // This also moves us to the next round if the certificate is valid. + err := c.handleRoundChangeCertificateV2(*preprepareV2.View, preprepareV2.RoundChangeCertificateV2, preprepareV2.Proposal) + if err != nil { + logger.Warn("Invalid round change certificate with preprepare.", "err", err) + return err + } + } else if preprepareV2.HasRoundChangeCertificateV2() { + logger.Error("Preprepare for round 0 has a round change certificate.") + return errInvalidProposal + } + + // Verify the proposal we received + if duration, err := c.verifyProposal(preprepareV2.Proposal); err != nil { + logger.Warn("Failed to verify proposal", "err", err, "duration", duration) + // if it's a future block, we will handle it again after the duration + if err == consensus.ErrFutureBlock { + c.stopFuturePreprepareTimer() + c.futurePreprepareTimer = time.AfterFunc(duration, func() { + c.sendEvent(backlogEvent{ + msg: msg, + }) + }) + } + return err + } + + if c.current.State() == StateAcceptRequest { + logger.Trace("Accepted preprepare v2", "tag", "stateTransition") + c.consensusTimestamp = time.Now() + + err := c.current.TransitionToPrepreparedV2(preprepareV2) + if err != nil { + return err + } + + // Process Backlog Messages + c.backlog.updateState(c.current.View(), c.current.State()) + c.sendPrepare() + } + + return nil +}
diff --git go-ethereum/consensus/istanbul/announce/network.go celo/consensus/istanbul/announce/network.go new file mode 100644 index 0000000000000000000000000000000000000000..1002bfe9666c27e1354d5756bef44fd8643fc40e --- /dev/null +++ celo/consensus/istanbul/announce/network.go @@ -0,0 +1,14 @@ +package announce + +import "github.com/ethereum/go-ethereum/common" + +// Network manages the communication needed for the announce protocol to work. +type Network interface { + // Gossip gossips protocol messages + Gossip(payload []byte, ethMsgCode uint64) error + // RetrieveValidatorConnSet returns the validator connection set + RetrieveValidatorConnSet() (map[common.Address]bool, error) + // Multicast will send the eth message (with the message's payload and msgCode field set to the params + // payload and ethMsgCode respectively) to the nodes with the signing address in the destAddresses param. + Multicast(destAddresses []common.Address, payload []byte, ethMsgCode uint64, sendToSelf bool) error +}
diff --git go-ethereum/consensus/istanbul/core/roundstate_test.go celo/consensus/istanbul/core/roundstate_test.go new file mode 100644 index 0000000000000000000000000000000000000000..47a1499d4bd93061aa8d3451a14add6de79a7a95 --- /dev/null +++ celo/consensus/istanbul/core/roundstate_test.go @@ -0,0 +1,280 @@ +package core + +import ( + "encoding/json" + "math/big" + "reflect" + "sort" + "strings" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/rlp" +) + +func TestRoundStateRLPEncoding(t *testing.T) { + dummyRoundState := func() RoundState { + valSet := validator.NewSet([]istanbul.ValidatorData{ + {Address: common.HexToAddress("2"), BLSPublicKey: blscrypto.SerializedPublicKey{1, 2, 3}}, + {Address: common.HexToAddress("4"), BLSPublicKey: blscrypto.SerializedPublicKey{3, 1, 4}}, + }) + view := &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(2)} + return newRoundState(view, valSet, valSet.GetByIndex(0)) + } + + t.Run("With nil fields", func(t *testing.T) { + rs := dummyRoundState() + + rawVal, err := rlp.EncodeToBytes(rs) + if err != nil { + t.Errorf("Error %v", err) + } + + var result *roundStateImpl + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Errorf("Error %v", err) + } + + assertEqualRoundState(t, rs, result) + }) + + t.Run("With a Pending Request", func(t *testing.T) { + rs := dummyRoundState() + rs.SetPendingRequest(&istanbul.Request{ + Proposal: makeBlock(1), + }) + + rawVal, err := rlp.EncodeToBytes(rs) + if err != nil { + t.Errorf("Error %v", err) + } + + var result *roundStateImpl + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Errorf("Error %v", err) + } + + assertEqualRoundState(t, rs, result) + }) + + t.Run("With a PreprepareV2", func(t *testing.T) { + rs := dummyRoundState() + + rs.TransitionToPrepreparedV2(&istanbul.PreprepareV2{ + Proposal: makeBlock(1), + View: rs.View(), + RoundChangeCertificateV2: istanbul.RoundChangeCertificateV2{}, + }) + + rawVal, err := rlp.EncodeToBytes(rs) + if err != nil { + t.Errorf("Error %v", err) + } + + var result *roundStateImpl + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Errorf("Error %v", err) + } + + assertEqualRoundState(t, rs, result) + }) + +} + +func TestRoundStateSummary(t *testing.T) { + view := &istanbul.View{Round: big.NewInt(2), Sequence: big.NewInt(2)} + + validatorAddresses := []common.Address{ + common.HexToAddress("1"), + common.HexToAddress("2"), + common.HexToAddress("3"), + common.HexToAddress("4"), + common.HexToAddress("5"), + common.HexToAddress("6"), + } + + dummyRoundState := func() RoundState { + + valData := make([]istanbul.ValidatorData, len(validatorAddresses)) + for i, addr := range validatorAddresses { + valData[i] = istanbul.ValidatorData{Address: addr, BLSPublicKey: blscrypto.SerializedPublicKey{1, 2, 3}} + } + valSet := validator.NewSet(valData) + + rs := newRoundState(view, valSet, valSet.GetByIndex(0)) + + // Add a few prepares + rs.AddPrepare(&istanbul.Message{ + Code: istanbul.MsgPrepare, + Address: validatorAddresses[1], + }) + rs.AddPrepare(&istanbul.Message{ + Code: istanbul.MsgPrepare, + Address: validatorAddresses[2], + }) + rs.AddPrepare(&istanbul.Message{ + Code: istanbul.MsgPrepare, + Address: validatorAddresses[3], + }) + rs.AddPrepare(&istanbul.Message{ + Code: istanbul.MsgPrepare, + Address: validatorAddresses[4], + }) + + // Add a few commits + rs.AddCommit(&istanbul.Message{ + Code: istanbul.MsgCommit, + Address: validatorAddresses[1], + }) + rs.AddCommit(&istanbul.Message{ + Code: istanbul.MsgCommit, + Address: validatorAddresses[2], + }) + rs.AddCommit(&istanbul.Message{ + Code: istanbul.MsgCommit, + Address: validatorAddresses[3], + }) + + // Add a few parent commits + rs.AddParentCommit(&istanbul.Message{ + Code: istanbul.MsgCommit, + Address: validatorAddresses[3], + }) + rs.AddParentCommit(&istanbul.Message{ + Code: istanbul.MsgCommit, + Address: validatorAddresses[4], + }) + rs.AddParentCommit(&istanbul.Message{ + Code: istanbul.MsgCommit, + Address: validatorAddresses[5], + }) + + return rs + } + + assertEqualAddressSet := func(t *testing.T, name string, got, expected []common.Address) { + gotStrings := make([]string, len(got)) + for i, addr := range got { + gotStrings[i] = addr.Hex() + } + + expectedStrings := make([]string, len(expected)) + for i, addr := range expected { + expectedStrings[i] = addr.Hex() + } + + sort.StringSlice(expectedStrings).Sort() + sort.StringSlice(gotStrings).Sort() + + if !reflect.DeepEqual(expectedStrings, gotStrings) { + t.Errorf("%s: Got %v expected %v", name, gotStrings, expectedStrings) + } + } + + t.Run("With nil fields", func(t *testing.T) { + rs := dummyRoundState() + rsSummary := rs.Summary() + + if strings.Compare(rsSummary.State, rs.State().String()) != 0 { + t.Errorf("State: Mismatch got %v expected %v", rsSummary.State, rs.State().String()) + } + + if rsSummary.Sequence.Cmp(rs.Sequence()) != 0 { + t.Errorf("Sequence: Mismatch got %v expected %v", rsSummary.Sequence, rs.Sequence()) + } + if rsSummary.Round.Cmp(rs.Round()) != 0 { + t.Errorf("Round: Mismatch got %v expected %v", rsSummary.Round, rs.Round()) + } + if rsSummary.DesiredRound.Cmp(rs.DesiredRound()) != 0 { + t.Errorf("DesiredRound: Mismatch got %v expected %v", rsSummary.DesiredRound, rs.DesiredRound()) + } + + if rsSummary.PendingRequestHash != nil { + t.Errorf("PendingRequestHash: Mismatch got %v expected %v", rsSummary.PendingRequestHash, nil) + } + + if !reflect.DeepEqual(rsSummary.ValidatorSet, validatorAddresses) { + t.Errorf("ValidatorSet: Mismatch got %v expected %v", rsSummary.ValidatorSet, validatorAddresses) + } + + if !reflect.DeepEqual(rsSummary.Proposer, validatorAddresses[0]) { + t.Errorf("Proposer: Mismatch got %v expected %v", rsSummary.Proposer, validatorAddresses[0]) + } + + assertEqualAddressSet(t, "Prepares", rsSummary.Prepares, validatorAddresses[1:5]) + assertEqualAddressSet(t, "Commits", rsSummary.Commits, validatorAddresses[1:4]) + assertEqualAddressSet(t, "ParentCommits", rsSummary.ParentCommits, validatorAddresses[3:6]) + + if rsSummary.Preprepare != nil { + t.Errorf("Preprepare: Mismatch got %v expected %v", rsSummary.Preprepare, nil) + } + + if rsSummary.PreparedCertificate != nil { + t.Errorf("PreparedCertificate: Mismatch got %v expected %v", rsSummary.PreparedCertificate, nil) + } + + _, err := json.Marshal(rsSummary) + if err != nil { + t.Errorf("Error %v", err) + } + }) + + t.Run("With a Pending Request", func(t *testing.T) { + rs := dummyRoundState() + block := makeBlock(1) + rs.SetPendingRequest(&istanbul.Request{ + Proposal: block, + }) + + rsSummary := rs.Summary() + + if rsSummary.PendingRequestHash == nil || !reflect.DeepEqual(*rsSummary.PendingRequestHash, block.Hash()) { + t.Errorf("PendingRequestHash: Mismatch got %v expected %v", rsSummary.PendingRequestHash, block.Hash()) + } + + _, err := json.Marshal(rsSummary) + if err != nil { + t.Errorf("Error %v", err) + } + }) + + t.Run("With a Preprepare", func(t *testing.T) { + rs := dummyRoundState() + block := makeBlock(1) + preprepare := &istanbul.PreprepareV2{ + Proposal: block, + View: rs.View(), + RoundChangeCertificateV2: istanbul.RoundChangeCertificateV2{ + Requests: []istanbul.RoundChangeRequest{ + {Address: validatorAddresses[3]}, + }, + }, + } + + rs.TransitionToPrepreparedV2(preprepare) + + rsSummary := rs.Summary() + + if rsSummary.Preprepare == nil { + t.Fatalf("Got nil Preprepare") + } + if !reflect.DeepEqual(rsSummary.Preprepare.View, rs.View()) { + t.Errorf("Preprepare.View: Mismatch got %v expected %v", rsSummary.Preprepare.View, rs.View()) + } + if !reflect.DeepEqual(rsSummary.Preprepare.ProposalHash, block.Hash()) { + t.Errorf("Preprepare.ProposalHash: Mismatch got %v expected %v", rsSummary.Preprepare.ProposalHash, block.Hash()) + } + + assertEqualAddressSet(t, "Preprepare.RoundChangeCertificateSenders", rsSummary.Preprepare.RoundChangeCertificateSenders, validatorAddresses[3:4]) + + _, err := json.Marshal(rsSummary) + if err != nil { + t.Errorf("Error %v", err) + } + }) + +}
diff --git go-ethereum/consensus/istanbul/proxy/proxied_validator_engine_test.go celo/consensus/istanbul/proxy/proxied_validator_engine_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4fb2ab41401c53bf8dad2c47c946534da5b50249 --- /dev/null +++ celo/consensus/istanbul/proxy/proxied_validator_engine_test.go @@ -0,0 +1,136 @@ +// Copyright 2017 The celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "reflect" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/consensus/istanbul/backend/backendtest" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p" +) + +func TestAddProxy(t *testing.T) { + numValidators := 2 + genesisCfg, nodeKeys := backendtest.GetGenesisAndKeys(numValidators, true) + + valBEi, _ := backendtest.NewTestBackend(false, common.Address{}, true, genesisCfg, nodeKeys[0]) + valBE := valBEi.(BackendForProxiedValidatorEngine) + + proxyBEi, _ := backendtest.NewTestBackend(true, valBE.Address(), false, genesisCfg, nil) + proxyBE := proxyBEi.(BackendForProxyEngine) + proxyPeer := consensustest.NewMockPeer(proxyBE.SelfNode(), p2p.ProxyPurpose) + + remoteValAddress := crypto.PubkeyToAddress(nodeKeys[1].PublicKey) + + // Get the proxied validator engine object + pvi := valBE.GetProxiedValidatorEngine() + pv := pvi.(*proxiedValidatorEngine) + + // Add the proxy to the proxied validator + pv.AddProxy(proxyBE.SelfNode(), proxyBE.SelfNode()) + + // Make sure the added proxy is within the proxy set but not assigned anything + proxies, assignments, err := pv.GetProxiesAndValAssignments() + + if err != nil { + t.Errorf("Error in retrieving proxies and val assignments. Error: %v", err) + } + + expectedProxy := Proxy{node: proxyBE.SelfNode(), + externalNode: proxyBE.SelfNode(), + peer: nil} + + if len(proxies) != 1 || proxies[0].node.URLv4() != expectedProxy.node.URLv4() || proxies[0].externalNode.URLv4() != expectedProxy.externalNode.URLv4() || proxies[0].peer != expectedProxy.peer { + t.Errorf("Unexpected proxies value. len(proxies): %d, proxy[0]: %v, expectedProxy: %v", len(proxies), proxies[0], expectedProxy) + } + + if len(assignments) != 1 || assignments[proxyBE.SelfNode().ID()] == nil || len(assignments[proxyBE.SelfNode().ID()]) != 0 { + t.Errorf("Unexpected validator assignments. assignments: %v", assignments) + } + + announceVersion := valBE.GetAnnounceVersion() + + // Connect the ProxyPeer + pv.RegisterProxyPeer(proxyPeer) + + // Sleep 6s since the registration of the proxy peer is asynchronous + time.Sleep(6 * time.Second) + + // Make sure the added proxy is within the proxy set and assigned to the remote validator + proxies, assignments, err = pv.GetProxiesAndValAssignments() + + if err != nil { + t.Errorf("Error in retrieving proxies and val assignments. Error: %v", err) + } + + expectedProxy = Proxy{node: proxyBE.SelfNode(), + externalNode: proxyBE.SelfNode(), + peer: proxyPeer} + + if len(proxies) != 1 || reflect.DeepEqual(proxies[0], expectedProxy.node.URLv4()) { + t.Errorf("Unexpected proxies value. len(proxies): %d, proxy[0]: %v, expectedProxy: %v", len(proxies), proxies[0], expectedProxy) + } + + if len(assignments) != 1 || assignments[proxyBE.SelfNode().ID()] == nil || len(assignments[proxyBE.SelfNode().ID()]) != 1 || assignments[proxyBE.SelfNode().ID()][0] != remoteValAddress { + t.Errorf("Unexpected validator assignments. assignments: %v", assignments) + } + + // Make sure that the announce version is incremented + if valBE.GetAnnounceVersion() <= announceVersion { + t.Errorf("Proxied validator announce version was not updated. announceVersion: %d, valBE.GetAnnounceVersion(): %d", announceVersion, valBE.GetAnnounceVersion()) + } + + // Make sure that the enode certificates are correctly created for the added proxy + ecMsgMap := valBE.RetrieveEnodeCertificateMsgMap() + if len(ecMsgMap) != 1 || ecMsgMap[proxyBE.SelfNode().ID()] == nil { + t.Errorf("Proxied validator enode cert message map incorrect. ecMsgMap: %v", ecMsgMap) + } + + announceVersion = valBE.GetAnnounceVersion() + + // Remove the proxy from the proxied validator + pv.RemoveProxy(proxyBE.SelfNode()) + + // Sleep for a sec since the proxy removal is asynchronous + time.Sleep(1 * time.Second) + + // Make sure the removed proxy's is not within the proxy set + proxies, assignments, err = pv.GetProxiesAndValAssignments() + + // Make sure that this proxy's enode certificate is removed from the enode certificate maps + if err != nil { + t.Errorf("Error in retrieving proxies and val assignments. Error: %v", err) + } + + if len(proxies) != 0 { + t.Errorf("Unexpected proxies. Proxies: %v", proxies) + } + + if len(assignments) != 0 { + t.Errorf("Unexpected validator assignments. assignments: %v", assignments) + } + + // Make sure that the announce version is incremented + if valBE.GetAnnounceVersion() <= announceVersion { + t.Errorf("Proxied validator announce version was not updated. announceVersion: %d, valBE.GetAnnounceVersion(): %d", announceVersion, valBE.GetAnnounceVersion()) + } +}
diff --git go-ethereum/consensus/istanbul/core/roundstate_save_decorator.go celo/consensus/istanbul/core/roundstate_save_decorator.go new file mode 100644 index 0000000000000000000000000000000000000000..8109e455204b0b96bc4d224247d19b30344c9ea5 --- /dev/null +++ celo/consensus/istanbul/core/roundstate_save_decorator.go @@ -0,0 +1,167 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +// createOrRestoreRoundState will obtain the last saved RoundState and use it if it's newer than the given Sequence, +// if not it will create a new one with the information given. +func withSavingDecorator(db RoundStateDB, rs RoundState) RoundState { + return &rsSaveDecorator{ + db: db, + rs: rs, + } +} + +type rsSaveDecorator struct { + rs RoundState + db RoundStateDB +} + +func (rsp *rsSaveDecorator) persistOnNoError(err error) error { + if err != nil { + return err + } + + return rsp.db.UpdateLastRoundState(rsp.rs) +} + +func (rsp *rsSaveDecorator) persistRcvdOnNoError(err error) error { + if err != nil { + return err + } + + return rsp.db.UpdateLastRcvd(rsp.rs) +} + +// mutation functions +func (rsp *rsSaveDecorator) StartNewRound(nextRound *big.Int, validatorSet istanbul.ValidatorSet, nextProposer istanbul.Validator) error { + return rsp.persistOnNoError(rsp.rs.StartNewRound(nextRound, validatorSet, nextProposer)) +} +func (rsp *rsSaveDecorator) StartNewSequence(nextSequence *big.Int, validatorSet istanbul.ValidatorSet, + nextProposer istanbul.Validator, parentCommits MessageSet) error { + return rsp.persistOnNoError(rsp.rs.StartNewSequence(nextSequence, validatorSet, nextProposer, parentCommits)) +} +func (rsp *rsSaveDecorator) TransitionToPrepreparedV2(preprepareV2 *istanbul.PreprepareV2) error { + return rsp.persistOnNoError(rsp.rs.TransitionToPrepreparedV2(preprepareV2)) +} +func (rsp *rsSaveDecorator) TransitionToWaitingForNewRound(r *big.Int, nextProposer istanbul.Validator) error { + return rsp.persistOnNoError(rsp.rs.TransitionToWaitingForNewRound(r, nextProposer)) +} +func (rsp *rsSaveDecorator) TransitionToCommitted() error { + return rsp.persistOnNoError(rsp.rs.TransitionToCommitted()) +} +func (rsp *rsSaveDecorator) TransitionToPrepared(quorumSize int) error { + return rsp.persistOnNoError(rsp.rs.TransitionToPrepared(quorumSize)) +} +func (rsp *rsSaveDecorator) AddCommit(msg *istanbul.Message) error { + return rsp.persistRcvdOnNoError(rsp.rs.AddCommit(msg)) +} +func (rsp *rsSaveDecorator) AddPrepare(msg *istanbul.Message) error { + return rsp.persistRcvdOnNoError(rsp.rs.AddPrepare(msg)) +} +func (rsp *rsSaveDecorator) AddParentCommit(msg *istanbul.Message) error { + return rsp.persistRcvdOnNoError(rsp.rs.AddParentCommit(msg)) +} +func (rsp *rsSaveDecorator) SetPendingRequest(pendingRequest *istanbul.Request) error { + return rsp.persistOnNoError(rsp.rs.SetPendingRequest(pendingRequest)) +} +func (rsp *rsSaveDecorator) SetProposalVerificationStatus(proposalHash common.Hash, verificationStatus error) { + // Don't persist on proposal verification status change, since it's just a cache + rsp.rs.SetProposalVerificationStatus(proposalHash, verificationStatus) +} +func (rsp *rsSaveDecorator) SetStateProcessResult(proposalHash common.Hash, result *StateProcessResult) { + rsp.rs.SetStateProcessResult(proposalHash, result) +} + +// DesiredRound implements RoundState.DesiredRound +func (rsp *rsSaveDecorator) DesiredRound() *big.Int { return rsp.rs.DesiredRound() } + +// State implements RoundState.State +func (rsp *rsSaveDecorator) State() State { return rsp.rs.State() } + +// Proposer implements RoundState.Proposer +func (rsp *rsSaveDecorator) Proposer() istanbul.Validator { return rsp.rs.Proposer() } + +// Subject implements RoundState.Subject +func (rsp *rsSaveDecorator) Subject() *istanbul.Subject { return rsp.rs.Subject() } + +// Proposal implements RoundState.Proposal +func (rsp *rsSaveDecorator) Proposal() istanbul.Proposal { return rsp.rs.Proposal() } + +// Round implements RoundState.Round +func (rsp *rsSaveDecorator) Round() *big.Int { return rsp.rs.Round() } + +// Commits implements RoundState.Commits +func (rsp *rsSaveDecorator) Commits() MessageSet { return rsp.rs.Commits() } + +// Prepares implements RoundState.Prepares +func (rsp *rsSaveDecorator) Prepares() MessageSet { return rsp.rs.Prepares() } + +// ParentCommits implements RoundState.ParentCommits +func (rsp *rsSaveDecorator) ParentCommits() MessageSet { return rsp.rs.ParentCommits() } + +// Sequence implements RoundState.Sequence +func (rsp *rsSaveDecorator) Sequence() *big.Int { return rsp.rs.Sequence() } + +// View implements RoundState.View +func (rsp *rsSaveDecorator) View() *istanbul.View { return rsp.rs.View() } + +// PreprepareV2 implements RoundState.PreprepareV2 +func (rsp *rsSaveDecorator) PreprepareV2() *istanbul.PreprepareV2 { return rsp.rs.PreprepareV2() } + +// PendingRequest implements RoundState.PendingRequest +func (rsp *rsSaveDecorator) PendingRequest() *istanbul.Request { return rsp.rs.PendingRequest() } + +// ValidatorSet implements RoundState.ValidatorSet +func (rsp *rsSaveDecorator) ValidatorSet() istanbul.ValidatorSet { return rsp.rs.ValidatorSet() } + +// GetPrepareOrCommitSize implements RoundState.GetPrepareOrCommitSize +func (rsp *rsSaveDecorator) GetPrepareOrCommitSize() int { return rsp.rs.GetPrepareOrCommitSize() } + +// GetValidatorByAddress implements RoundState.GetValidatorByAddress +func (rsp *rsSaveDecorator) GetValidatorByAddress(address common.Address) istanbul.Validator { + return rsp.rs.GetValidatorByAddress(address) +} + +// GetProposalVerificationStatus implements RoundState.GetProposalVerificationStatus +func (rsp *rsSaveDecorator) GetProposalVerificationStatus(proposalHash common.Hash) (verificationStatus error, isChecked bool) { + return rsp.rs.GetProposalVerificationStatus(proposalHash) +} + +// GetStateProcessResult implements RoundState.GetStateProcessResult +func (rsp *rsSaveDecorator) GetStateProcessResult(proposalHash common.Hash) (result *StateProcessResult) { + return rsp.rs.GetStateProcessResult(proposalHash) +} + +// IsProposer implements RoundState.IsProposer +func (rsp *rsSaveDecorator) IsProposer(address common.Address) bool { + return rsp.rs.IsProposer(address) +} + +// PreparedCertificate implements RoundState.PreparedCertificate +func (rsp *rsSaveDecorator) PreparedCertificate() istanbul.PreparedCertificate { + return rsp.rs.PreparedCertificate() +} + +// Summary implements RoundState.Summary +func (rsp *rsSaveDecorator) Summary() *RoundStateSummary { return rsp.rs.Summary() }
diff --git go-ethereum/consensus/istanbul/backend/message_senders.go celo/consensus/istanbul/backend/message_senders.go new file mode 100644 index 0000000000000000000000000000000000000000..fd57ffe004fc23c60d1437e8702cd5b053909ae3 --- /dev/null +++ celo/consensus/istanbul/backend/message_senders.go @@ -0,0 +1,130 @@ +// Copyright 2017 The celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// This function will return the peers with the addresses in the "destAddresses" parameter. +func (sb *Backend) getPeersFromDestAddresses(destAddresses []common.Address) map[enode.ID]consensus.Peer { + var targets map[enode.ID]bool + if destAddresses != nil { + targets = make(map[enode.ID]bool) + for _, addr := range destAddresses { + if valNode, err := sb.valEnodeTable.GetNodeFromAddress(addr); valNode != nil && err == nil { + targets[valNode.ID()] = true + } + } + } + return sb.broadcaster.FindPeers(targets, p2p.AnyPurpose) +} + +// Multicast implements istanbul.Backend.Multicast +// Multicast will send the eth message (with the message's payload and msgCode field set to the params +// payload and ethMsgCode respectively) to the nodes with the signing address in the destAddresses param. +// If this node is proxied and destAddresses is not nil, the message will be wrapped +// in an istanbul.ForwardMessage to ensure the proxy sends it to the correct +// destAddresses. +func (sb *Backend) Multicast(destAddresses []common.Address, payload []byte, ethMsgCode uint64, sendToSelf bool) error { + logger := sb.logger.New("func", "Multicast") + + var err error + + if sb.IsProxiedValidator() { + err = sb.proxiedValidatorEngine.SendForwardMsgToAllProxies(destAddresses, ethMsgCode, payload) + if err != nil { + logger.Warn("Error in sending forward message to the proxies", "err", err) + } + } else { + destPeers := sb.getPeersFromDestAddresses(destAddresses) + if len(destPeers) > 0 { + sb.asyncMulticast(destPeers, payload, ethMsgCode) + } + } + + if sendToSelf { + // Send to self. Note that it will never be a wrapped version of the consensus message. + msg := istanbul.MessageEvent{ + Payload: payload, + } + + go func() { + if err := sb.istanbulEventMux.Post(msg); err != nil { + logger.Warn("Error in posting message to self", "err", err) + } + }() + } + + return err +} + +// Gossip implements istanbul.Backend.Gossip +// Gossip will gossip the eth message to all connected peers +func (sb *Backend) Gossip(payload []byte, ethMsgCode uint64) error { + logger := sb.logger.New("func", "Gossip") + + // Get all connected peers + peersToSendMsg := sb.broadcaster.FindPeers(nil, p2p.AnyPurpose) + + // Mark that this node gossiped/processed this message, so that it will ignore it if + // one of it's peers sends the message to it. + sb.gossipCache.MarkMessageProcessedBySelf(payload) + + // Filter out peers that already sent us this gossip message + for nodeID, peer := range peersToSendMsg { + nodePubKey := peer.Node().Pubkey() + nodeAddr := crypto.PubkeyToAddress(*nodePubKey) + if sb.gossipCache.CheckIfMessageProcessedByPeer(nodeAddr, payload) { + delete(peersToSendMsg, nodeID) + logger.Trace("Peer already gossiped this message. Not sending message to it", "peer", peer) + continue + } else { + sb.gossipCache.MarkMessageProcessedByPeer(nodeAddr, payload) + } + } + + sb.asyncMulticast(peersToSendMsg, payload, ethMsgCode) + + return nil +} + +// sendMsg will asynchronously send the the Celo messages to all the peers in the destPeers param. +func (sb *Backend) asyncMulticast(destPeers map[enode.ID]consensus.Peer, payload []byte, ethMsgCode uint64) { + logger := sb.logger.New("func", "AsyncMulticastCeloMsg", "msgCode", ethMsgCode) + + for _, peer := range destPeers { + peer := peer // Create new instance of peer for the goroutine + go func() { + logger.Trace("Sending istanbul message(s) to peer", "peer", peer, "node", peer.Node()) + if err := peer.Send(ethMsgCode, payload); err != nil { + logger.Warn("Error in sending message", "peer", peer, "ethMsgCode", ethMsgCode, "err", err) + } + }() + } +} + +// Unicast asynchronously sends a message to a single peer. +func (sb *Backend) Unicast(peer consensus.Peer, payload []byte, ethMsgCode uint64) { + peerMap := map[enode.ID]consensus.Peer{peer.Node().ID(): peer} + sb.asyncMulticast(peerMap, payload, ethMsgCode) +}
diff --git go-ethereum/consensus/istanbul/core/prepare_test.go celo/consensus/istanbul/core/prepare_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a16c874c4cf6e3321ff34f87ddb4f6308ee006a3 --- /dev/null +++ celo/consensus/istanbul/core/prepare_test.go @@ -0,0 +1,580 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "reflect" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/crypto" +) + +func TestVerifyPreparedCertificate(t *testing.T) { + N := uint64(4) // replica 0 is the proposer, it will send messages to others + F := uint64(1) + sys := NewTestSystemWithBackend(N, F) + view := istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + } + proposal := makeBlock(0) + + for _, b := range sys.backends { + b.engine.Start() // start Istanbul core + } + + testCases := []struct { + name string + certificate istanbul.PreparedCertificate + expectedErr error + expectedView *istanbul.View + }{ + { + "Valid PREPARED certificate", + sys.getPreparedCertificate(t, []istanbul.View{view}, proposal), + nil, + &view, + }, + { + "Invalid PREPARED certificate, duplicate message", + func() istanbul.PreparedCertificate { + preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view}, proposal) + preparedCertificate.PrepareOrCommitMessages[1] = preparedCertificate.PrepareOrCommitMessages[0] + return preparedCertificate + }(), + errInvalidPreparedCertificateDuplicate, + nil, + }, + { + "Invalid PREPARED certificate, future message", + func() istanbul.PreparedCertificate { + futureView := istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(10), + } + preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{futureView}, proposal) + return preparedCertificate + }(), + errInvalidPreparedCertificateMsgView, + nil, + }, + { + "Invalid PREPARED certificate, includes preprepare message", + func() istanbul.PreparedCertificate { + preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view}, proposal) + testInvalidMsg, _ := sys.backends[0].getRoundChangeV2Message(view, sys.getPreparedCertificateV2(t, []istanbul.View{view}, proposal), proposal) + preparedCertificate.PrepareOrCommitMessages[0] = testInvalidMsg + return preparedCertificate + }(), + errInvalidPreparedCertificateMsgCode, + nil, + }, + { + "Invalid PREPARED certificate, hash mismatch", + func() istanbul.PreparedCertificate { + preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view}, proposal) + preparedCertificate.PrepareOrCommitMessages[1] = preparedCertificate.PrepareOrCommitMessages[0] + preparedCertificate.Proposal = makeBlock(1) + return preparedCertificate + }(), + errInvalidPreparedCertificateDigestMismatch, + nil, + }, + { + "Invalid PREPARED certificate, view inconsistencies", + func() istanbul.PreparedCertificate { + var view2 istanbul.View + view2.Sequence = big.NewInt(view.Sequence.Int64()) + view2.Round = big.NewInt(view.Round.Int64() + 1) + preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view, view2}, proposal) + return preparedCertificate + }(), + errInvalidPreparedCertificateInconsistentViews, + nil, + }, + { + "Empty certificate", + istanbul.EmptyPreparedCertificate(), + errInvalidPreparedCertificateNumMsgs, + nil, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + for _, backend := range sys.backends { + c := backend.engine.(*core) + view, err := c.verifyPreparedCertificate(test.certificate) + if err != test.expectedErr { + t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) + } + if err == nil { + if view.Cmp(test.expectedView) != 0 { + t.Errorf("view mismatch: have %v, want %v", view, test.expectedView) + } + view, err := c.getViewFromVerifiedPreparedCertificate(test.certificate) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + if view.Cmp(test.expectedView) != 0 { + t.Errorf("view mismatch: have %v, want %v", view, test.expectedView) + } + } + } + }) + } +} + +func TestHandlePrepare(t *testing.T) { + N := uint64(4) + F := uint64(1) + + proposal := newTestProposal() + expectedSubject := &istanbul.Subject{ + View: &istanbul.View{ + Round: big.NewInt(0), + Sequence: proposal.Number(), + }, + Digest: proposal.Hash(), + } + + testCases := []struct { + name string + system *testSystem + expectedErr error + }{ + { + "normal case", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + backend.peers, + ) + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePreprepared + } + } + return sys + }(), + nil, + }, + { + "normal case with prepared certificate", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + preparedCert := sys.getPreparedCertificate( + t, + []istanbul.View{ + { + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + }, + proposal) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + backend.peers, + ) + c.current.(*roundStateImpl).preparedCertificate = preparedCert + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePreprepared + } + } + return sys + }(), + nil, + }, + { + "Inconsistent subject due to prepared certificate", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + preparedCert := sys.getPreparedCertificate( + t, + []istanbul.View{ + { + Round: big.NewInt(0), + Sequence: big.NewInt(10), + }, + }, + proposal) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + backend.peers, + ) + c.current.(*roundStateImpl).preparedCertificate = preparedCert + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePreprepared + } + } + return sys + }(), + errInconsistentSubject, + }, + { + "future message", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePreprepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(2), + Sequence: big.NewInt(3), + }, + backend.peers, + ) + } + } + return sys + }(), + errFutureMessage, + }, + { + "subject not match", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePreprepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(0), + }, + backend.peers, + ) + } + } + return sys + }(), + errOldMessage, + }, + { + "subject not match", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePreprepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1)}, + backend.peers, + ) + } + } + return sys + }(), + errInconsistentSubject, + }, + { + "less than 2F+1", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + // save less than 2*F+1 replica + sys.backends = sys.backends[2*int(F)+1:] + + for i, backend := range sys.backends { + c := backend.engine.(*core) + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePreprepared + } + } + return sys + }(), + nil, + }, + // TODO: double send message + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + + test.system.Run(false) + + v0 := test.system.backends[0] + r0 := v0.engine.(*core) + + for i, v := range test.system.backends { + validator := r0.current.ValidatorSet().GetByIndex(uint64(i)) + msg := istanbul.NewPrepareMessage(v.engine.(*core).current.Subject(), validator.Address()) + err := r0.handlePrepare(msg) + if err != nil { + if err != test.expectedErr { + t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) + } + return + } + } + + // prepared is normal case + if r0.current.State() != StatePrepared { + // There are not enough PREPARE messages in core + if r0.current.State() != StatePreprepared { + t.Errorf("state mismatch: have %v, want %v", r0.current.State(), StatePreprepared) + } + if r0.current.Prepares().Size() >= r0.current.ValidatorSet().MinQuorumSize() { + t.Errorf("the size of PREPARE messages should be less than %v", 2*r0.current.ValidatorSet().MinQuorumSize()+1) + } + + return + } + + // core should have MinQuorumSize PREPARE messages + if r0.current.Prepares().Size() < r0.current.ValidatorSet().MinQuorumSize() { + t.Errorf("the size of PREPARE messages should be greater than or equal to MinQuorumSize: size %v", r0.current.Prepares().Size()) + } + + // a message will be delivered to backend if 2F+1 + if int64(len(v0.sentMsgs)) != 1 { + t.Errorf("the Send() should be called once: times %v", len(test.system.backends[0].sentMsgs)) + } + + // verify COMMIT messages + decodedMsg := new(istanbul.Message) + err := decodedMsg.FromPayload(v0.sentMsgs[0], nil) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + if decodedMsg.Code != istanbul.MsgCommit { + t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, istanbul.MsgCommit) + } + subject := decodedMsg.Commit().Subject + if !reflect.DeepEqual(subject, expectedSubject) { + t.Errorf("subject mismatch: have %v, want %v", subject, expectedSubject) + } + }) + } +} + +// round is not checked for now +func TestVerifyPrepare(t *testing.T) { + + // for log purpose + privateKey, _ := crypto.GenerateKey() + blsPrivateKey, _ := blscrypto.ECDSAToBLS(privateKey) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + peer := validator.New(getPublicKeyAddress(privateKey), blsPublicKey) + valSet := validator.NewSet([]istanbul.ValidatorData{ + { + Address: peer.Address(), + BLSPublicKey: blsPublicKey, + }, + }) + + sys := NewTestSystemWithBackend(uint64(1), uint64(0)) + + testCases := []struct { + expected error + + prepare *istanbul.Subject + roundState RoundState + }{ + { + // normal case + expected: nil, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + { + // old message + expected: errInconsistentSubject, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // different digest + expected: errInconsistentSubject, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: common.BytesToHash([]byte("1234567890")), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // malicious package(lack of sequence) + expected: errInconsistentSubject, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: nil}, + Digest: newTestProposal().Hash(), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // wrong PREPARE message with same sequence but different round + expected: errInconsistentSubject, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + { + // wrong PREPARE message with same round but different sequence + expected: errInconsistentSubject, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)}, + Digest: newTestProposal().Hash(), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + } + for i, test := range testCases { + c := sys.backends[0].engine.(*core) + c.current = test.roundState + + if err := c.verifyPrepare(test.prepare); err != nil { + if err != test.expected { + t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected) + } + } + } +} + +// benchMarkHandlePrepare benchmarks handling a prepare message +func BenchmarkHandlePrepare(b *testing.B) { + N := uint64(2) + F := uint64(1) // F does not affect tests + + sys := NewMutedTestSystemWithBackend(N, F) + // sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + backend.peers, + ) + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePreprepared + } + } + + sys.Run(false) + + v0 := sys.backends[0] + c := v0.engine.(*core) + m, _ := Encode(v0.engine.(*core).current.Subject()) + msg := istanbul.Message{ + Code: istanbul.MsgPrepare, + Msg: m, + Address: c.current.ValidatorSet().GetByIndex(uint64(1)).Address(), + } + + // benchmarked portion + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := c.handlePrepare(&msg) + if err != nil { + b.Errorf("Error handling the pre-prepare message. err: %v", err) + } + } +}
diff --git go-ethereum/consensus/istanbul/announce/queryenodeentries.go celo/consensus/istanbul/announce/queryenodeentries.go new file mode 100644 index 0000000000000000000000000000000000000000..244e0d9a39a58da022cd46961609103a234dcef5 --- /dev/null +++ celo/consensus/istanbul/announce/queryenodeentries.go @@ -0,0 +1,65 @@ +package announce + +import ( + "math" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" +) + +type QueryEnodeEntryProvider interface { + GetQueryEnodeValEnodeEntries(enforceRetryBackoff bool, exceptAddress common.Address) ([]*istanbul.AddressEntry, error) +} + +type qeep struct { + logger log.Logger + valEnodeTable *ValidatorEnodeDB +} + +func NewQueryEnodeEntryProvider(valEnodeTable *ValidatorEnodeDB) QueryEnodeEntryProvider { + return &qeep{ + logger: log.New("module", "announceQueryEnodeEntryProvider"), + valEnodeTable: valEnodeTable, + } +} + +func (q *qeep) GetQueryEnodeValEnodeEntries(enforceRetryBackoff bool, exceptEcdsaAddress common.Address) ([]*istanbul.AddressEntry, error) { + logger := q.logger.New("func", "getQueryEnodeValEnodeEntries") + valEnodeEntries, err := q.valEnodeTable.GetValEnodes(nil) + if err != nil { + return nil, err + } + + var queryEnodeValEnodeEntries []*istanbul.AddressEntry + for address, valEnodeEntry := range valEnodeEntries { + // Don't generate an announce record for ourselves + if address == exceptEcdsaAddress { + continue + } + + if valEnodeEntry.Version == valEnodeEntry.HighestKnownVersion { + continue + } + + if valEnodeEntry.PublicKey == nil { + logger.Warn("Cannot generate encrypted enode URL for a val enode entry without a PublicKey", "address", address) + continue + } + + if enforceRetryBackoff && valEnodeEntry.NumQueryAttemptsForHKVersion > 0 { + timeoutFactorPow := math.Min(float64(valEnodeEntry.NumQueryAttemptsForHKVersion-1), 5) + timeoutMinutes := int64(math.Pow(1.5, timeoutFactorPow) * 5) + timeoutForQuery := time.Duration(timeoutMinutes) * time.Minute + + if time.Since(*valEnodeEntry.LastQueryTimestamp) < timeoutForQuery { + continue + } + } + + queryEnodeValEnodeEntries = append(queryEnodeValEnodeEntries, valEnodeEntry) + } + + return queryEnodeValEnodeEntries, nil +}
diff --git go-ethereum/consensus/istanbul/core/backlog.go celo/consensus/istanbul/core/backlog.go new file mode 100644 index 0000000000000000000000000000000000000000..05425eb8ca9ed15c9129bb298fa6783e0d76a387 --- /dev/null +++ celo/consensus/istanbul/core/backlog.go @@ -0,0 +1,341 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "fmt" + "math/big" + "sort" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/prque" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" +) + +var ( + // msgPriority is defined for calculating processing priority to speedup consensus + // istanbul.MsgPreprepareV2 > istanbul.MsgCommit > istanbul.MsgPrepare + msgPriority = map[uint64]int{ + istanbul.MsgPreprepareV2: 1, + istanbul.MsgCommit: 2, + istanbul.MsgPrepare: 3, + } + + // Do not accept messages for views more than this many sequences in the future. + acceptMaxFutureSequence = big.NewInt(10) + acceptMaxFutureMsgsFromOneValidator = 1000 + acceptMaxFutureMessages = 10 * 1000 + acceptMaxFutureMessagesPruneBatch = 100 +) + +// checkMessage checks the message state +// return errInvalidMessage if the message is invalid +// return errFutureMessage if the message view is larger than current view +// return errOldMessage if the message view is smaller than current view +func (c *core) checkMessage(msgCode uint64, msgView *istanbul.View) error { + if msgView == nil || msgView.Sequence == nil || msgView.Round == nil { + return errInvalidMessage + } + + // First compare sequences. Prior seqs are always old. Future seqs are always future. + if msgView.Sequence.Cmp(c.current.Sequence()) < 0 { + return errOldMessage + } else if msgView.Sequence.Cmp(c.current.Sequence()) > 0 { + return errFutureMessage + } + + // We will never do consensus on any round less than desiredRound. + if c.current.Round().Cmp(c.current.DesiredRound()) > 0 { + panic(fmt.Errorf("Current and desired round mismatch! cur=%v des=%v", c.current.Round(), c.current.DesiredRound())) + } + + // Same sequence. Msgs for a round < desiredRound are always old. + if msgView.Round.Cmp(c.current.DesiredRound()) < 0 { + return errOldMessage + } + + // Msg is now correct sequence and >= desiredRound. + + // RoundChange messages are accepted in all states and for current or future rounds. + if istanbul.IsRoundChangeCode(msgCode) { + return nil + } + + // WaitingForNewRound and StateAcceptRequest: accepts Preprepare (including for rounds >= desiredRound), other messages are future. + if (c.current.State() == StateWaitingForNewRound || c.current.State() == StateAcceptRequest) && + !istanbul.IsPreprepareCode(msgCode) { + return errFutureMessage + } + + // For states(StatePreprepared, StatePrepared, StateCommitted): can accept all message types on same round. + if msgView.Round.Cmp(c.current.DesiredRound()) > 0 { + return errFutureMessage + } + + return nil +} + +// MsgBacklog represent a backlog of future messages +// It works by: +// - allowing storing messages with "store()" +// - call eventListener when a backlog message becomes "present" +// - updates its notion of time/state with updateState() +type MsgBacklog interface { + // store atttemps to store the message in the backlog + // it might not do so, if the message is too far in the future + store(msg *istanbul.Message) + + // updateState updates the notion of time/state of the backlog, + // as a side effect it will call the eventListener for all backlog + // messages that belong to the current "state" + updateState(view *istanbul.View, state State) +} + +type msgBacklogImpl struct { + backlogBySeq map[uint64]*prque.Prque + msgCountBySrc map[common.Address]int + msgCount int + + currentView *istanbul.View + currentState State + + backlogsMu *sync.Mutex + msgProcessor func(*istanbul.Message) + checkMessage func(msgCode uint64, msgView *istanbul.View) error + logger log.Logger +} + +func newMsgBacklog(msgProcessor func(*istanbul.Message), checkMessage func(msgCode uint64, msgView *istanbul.View) error) MsgBacklog { + initialView := &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + } + + return &msgBacklogImpl{ + backlogBySeq: make(map[uint64]*prque.Prque), + msgCountBySrc: make(map[common.Address]int), + msgCount: 0, + + currentView: initialView, + currentState: StateAcceptRequest, + + msgProcessor: msgProcessor, + checkMessage: checkMessage, + backlogsMu: new(sync.Mutex), + logger: log.New("type", "MsgBacklog"), + } +} + +func (c *msgBacklogImpl) store(msg *istanbul.Message) { + logger := c.logger.New("func", "store", "from", msg.Address, "cur_seq", c.currentView.Sequence, "cur_round", c.currentView.Round) + + view := extractMessageView(msg) + + c.backlogsMu.Lock() + defer c.backlogsMu.Unlock() + + // Never accept messages too far into the future + if view.Sequence.Cmp(new(big.Int).Add(c.currentView.Sequence, acceptMaxFutureSequence)) > 0 { + logger.Debug("Dropping message", "reason", "too far in the future", "m", msg) + return + } + + if view.Round.Cmp(maxRoundForPriorityQueue) >= 0 { + logger.Debug("Dropping message", "reason", "round exceeds PQ bounds check", "m", msg) + return + } + + // Check and inc per-validator future message limit + if c.msgCountBySrc[msg.Address] > acceptMaxFutureMsgsFromOneValidator { + logger.Debug("Dropping message", "reason", "exceeds per-address cap") + return + } + + logger.Trace("Store future message", "m", msg, "m_seq", view.Sequence, "m_round", view.Round) + c.msgCountBySrc[msg.Address]++ + c.msgCount++ + + // Add message to per-seq list + backlogForSeq := c.backlogBySeq[view.Sequence.Uint64()] + if backlogForSeq == nil { + backlogForSeq = prque.New(nil) + c.backlogBySeq[view.Sequence.Uint64()] = backlogForSeq + } + + backlogForSeq.Push(msg, toPriority(msg.Code, view)) + + // After insert, remove messages if we have more than "acceptMaxFutureMessages" + c.removeMessagesOverflow() +} + +// removeMessagesOverflow will remove messages if necessary to maintain the number of messages <= acceptMaxFutureMessages +// For that, it will remove messages that further on the future +func (c *msgBacklogImpl) removeMessagesOverflow() { + // Keep backlog below total max size by pruning future-most sequence first + // (we always leave one sequence's entire messages and rely on per-validator limits) + if c.msgCount > acceptMaxFutureMessages { + backlogSeqs := c.getSortedBacklogSeqs() + for i := len(backlogSeqs) - 1; i > 0; i-- { + seq := backlogSeqs[i] + if seq <= c.currentView.Sequence.Uint64() || + c.msgCount < (acceptMaxFutureMessages-acceptMaxFutureMessagesPruneBatch) { + break + } + c.clearBacklogForSeq(seq) + } + } +} + +// Return slice of sequences present in backlog sorted in ascending order +// Call with backlogsMu held. +func (c *msgBacklogImpl) getSortedBacklogSeqs() []uint64 { + backlogSeqs := make([]uint64, len(c.backlogBySeq)) + i := 0 + for k := range c.backlogBySeq { + backlogSeqs[i] = k + i++ + } + sort.Slice(backlogSeqs, func(i, j int) bool { + return backlogSeqs[i] < backlogSeqs[j] + }) + return backlogSeqs +} + +// clearBacklogForSeq will remove all entries in the backlog +// for the given seq +func (c *msgBacklogImpl) clearBacklogForSeq(seq uint64) { + c.processBacklogForSeq(seq, func(_ *istanbul.Message) bool { return false }) +} + +// processBacklogForSeq will call process() with each entry of the backlog +// for the given seq, until process returns "true". +// The entry on which process() returned false will remain in the backlog +func (c *msgBacklogImpl) processBacklogForSeq(seq uint64, process func(*istanbul.Message) bool) { + backlogForSeq := c.backlogBySeq[seq] + if backlogForSeq == nil { + return + } + + backlogSize := backlogForSeq.Size() + for i := 0; i < backlogSize; i++ { + m, priority := backlogForSeq.Pop() + msg := m.(*istanbul.Message) + + shouldStop := process(msg) + + if shouldStop { + backlogForSeq.Push(m, priority) + break + } + + c.msgCountBySrc[msg.Address]-- + if c.msgCountBySrc[msg.Address] == 0 { + delete(c.msgCountBySrc, msg.Address) + } + c.msgCount-- + } + + if backlogForSeq.Size() == 0 { + delete(c.backlogBySeq, seq) + } +} + +func (c *msgBacklogImpl) updateState(view *istanbul.View, state State) { + c.backlogsMu.Lock() + defer c.backlogsMu.Unlock() + + c.currentState = state + c.currentView = view + + c.processBacklog() +} + +func (c *msgBacklogImpl) processBacklog() { + + logger := c.logger.New("func", "processBacklog", "cur_seq", c.currentView.Sequence, "cur_round", c.currentView.Round) + processedMsgsConsidered, processedMsgsEnqueued, processedMsgsFuture := 0, 0, 0 + + for _, seq := range c.getSortedBacklogSeqs() { + + if seq < c.currentView.Sequence.Uint64() { + // Earlier sequence. Prune all messages. + c.clearBacklogForSeq(seq) + } else if seq == c.currentView.Sequence.Uint64() { + // Current sequence. Process all in order. + c.processBacklogForSeq(seq, func(msg *istanbul.Message) bool { + processedMsgsConsidered++ + + view := extractMessageView(msg) + + logger := logger.New("m", msg, "msg_view", view) + + err := c.checkMessage(msg.Code, view) + + if err == errFutureMessage { + logger.Debug("Future message in backlog for seq, pushing back to the backlog") + processedMsgsFuture++ + return true + } + + if err == nil { + logger.Trace("Post backlog event") + processedMsgsEnqueued++ + go c.msgProcessor(msg) + } else { + logger.Trace("Skip the backlog event", "err", err) + } + return false + }) + } + } + + if processedMsgsConsidered > 0 { + logger.Info("Processing istanbul backlog", "considered", processedMsgsConsidered, "future", processedMsgsFuture, "enqueued", processedMsgsEnqueued) + } +} + +// A safe maximum for round that prevents overflow +var ( + maxRoundForPriorityQueue = big.NewInt(1 << (63 - 5)) +) + +func toPriority(msgCode uint64, view *istanbul.View) int64 { + if istanbul.IsRoundChangeCode(msgCode) { + // msgRoundChange comes first + return 0 + } + // 10 * Round limits the range possible message codes to [0, 9] + // Caller must check for integer overflow. + return -int64(view.Round.Uint64()*10 + uint64(msgPriority[msgCode])) +} + +func extractMessageView(msg *istanbul.Message) *istanbul.View { + switch msg.Code { + case istanbul.MsgPreprepareV2: + return msg.PreprepareV2().View + case istanbul.MsgPrepare: + return msg.Prepare().View + case istanbul.MsgCommit: + return msg.Commit().Subject.View + case istanbul.MsgRoundChangeV2: + return &msg.RoundChangeV2().Request.View + default: + panic(fmt.Sprintf("unknown message code %q", msg.Code)) + } +}
diff --git go-ethereum/consensus/istanbul/announce/manager.go celo/consensus/istanbul/announce/manager.go new file mode 100644 index 0000000000000000000000000000000000000000..6bc29b6a57eeec2d8dcadbe1fa0c96718216a095 --- /dev/null +++ celo/consensus/istanbul/announce/manager.go @@ -0,0 +1,512 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package announce + +import ( + "encoding/hex" + "errors" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/proxy" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +var ( + errNodeMissingEnodeCertificate = errors.New("Node is missing enode certificate") + + // errUnauthorizedAnnounceMessage is returned when the received announce message is from + // an unregistered validator + errUnauthorizedAnnounceMessage = errors.New("unauthorized announce message") +) + +type ProxyContext interface { + GetProxiedValidatorEngine() proxy.ProxiedValidatorEngine +} + +// Manager is the facade and entry point for the implementation of the announce protocol. It exposes methods to +// start and stop the announce Worker, and to handle announce messages. +type Manager struct { + logger log.Logger + + config *istanbul.Config + + aWallets *atomic.Value + + addrProvider AddressProvider + proxyContext ProxyContext + network Network + + vcGossiper VersionCertificateGossiper + + gossipCache istanbul.GossipCache + + state *AnnounceState + + checker ValidatorChecker + + ovcp OutboundVersionCertificateProcessor + + worker Worker + + vpap ValProxyAssigmnentProvider + + announceRunning bool + announceMu sync.RWMutex + announceThreadWg *sync.WaitGroup + + ecertHolder EnodeCertificateMsgHolder +} + +// NewManager creates a new Manager using the valEnodeTable given. It is +// the responsibility of the caller to close the valEnodeTable, the Manager will +// not do it. +func NewManager( + config *istanbul.Config, + aWallets *atomic.Value, + network Network, proxyContext ProxyContext, + addrProvider AddressProvider, state *AnnounceState, + gossipCache istanbul.GossipCache, + checker ValidatorChecker, + ovcp OutboundVersionCertificateProcessor, + ecertHolder EnodeCertificateMsgHolder, + vcGossiper VersionCertificateGossiper, + vpap ValProxyAssigmnentProvider, + worker Worker) *Manager { + + am := &Manager{ + logger: log.New("module", "announceManager"), + aWallets: aWallets, + config: config, + network: network, + proxyContext: proxyContext, + addrProvider: addrProvider, + gossipCache: gossipCache, + vcGossiper: vcGossiper, + state: state, + checker: checker, + ovcp: ovcp, + ecertHolder: ecertHolder, + vpap: vpap, + worker: worker, + announceThreadWg: new(sync.WaitGroup), + announceRunning: false, + } + + return am +} + +func (m *Manager) isProxiedValidator() bool { + return m.config.Proxied && m.config.Validator +} + +func (m *Manager) Close() error { + // No need to close valEnodeTable since it's a reference, + // the creator of this announce manager is the responsible for + // closing it. + return m.state.VersionCertificateTable.Close() +} + +func (m *Manager) wallets() *istanbul.Wallets { + return m.aWallets.Load().(*istanbul.Wallets) +} + +// The announceThread will: +// 1) Periodically poll to see if this node should be announcing +// 2) Periodically share the entire version certificate table with all peers +// 3) Periodically prune announce-related data structures +// 4) Gossip announce messages periodically when requested +// 5) Update announce version when requested +func (m *Manager) announceThread() { + defer m.announceThreadWg.Done() + m.worker.Run() +} + +// HandleQueryEnodeMsg handles a queryEnodeMsg received by the p2p network, according to the announce protocol spec. +func (m *Manager) HandleQueryEnodeMsg(addr common.Address, peer consensus.Peer, payload []byte) error { + logger := m.logger.New("func", "HandleQueryEnodeMsg") + + msg := new(istanbul.Message) + + // Since this is a gossiped messaged, mark that the peer gossiped it (and presumably processed it) and check to see if this node already processed it + m.gossipCache.MarkMessageProcessedByPeer(addr, payload) + if m.gossipCache.CheckIfMessageProcessedBySelf(payload) { + return nil + } + defer m.gossipCache.MarkMessageProcessedBySelf(payload) + + // Decode message + err := msg.FromPayload(payload, istanbul.GetSignatureAddress) + if err != nil { + logger.Error("Error in decoding received Istanbul Announce message", "err", err, "payload", hex.EncodeToString(payload)) + return err + } + logger.Trace("Handling a queryEnode message", "from", msg.Address) + + // Check if the sender is within the validator connection set + validatorConnSet, err := m.network.RetrieveValidatorConnSet() + if err != nil { + logger.Trace("Error in retrieving validator connection set", "err", err) + return err + } + + if !validatorConnSet[msg.Address] { + logger.Debug("Received a message from a validator not within the validator connection set. Ignoring it.", "sender", msg.Address) + return errUnauthorizedAnnounceMessage + } + + qeData := msg.QueryEnodeMsg() + logger = logger.New("msgAddress", msg.Address, "msgVersion", qeData.Version) + + // Do some validation checks on the istanbul.QueryEnodeData + + // Check if the number of rows in the queryEnodePayload is at most 2 times the size of the current validator connection set. + // Note that this is a heuristic of the actual size of validator connection set at the time the validator constructed the announce message. + maxQueries := len(validatorConnSet) * 2 + if isValid, err := validateQueryEnode(m.logger, msg.Address, msg.QueryEnodeMsg(), maxQueries); !isValid || err != nil { + logger.Warn("Validation of queryEnode message failed", "isValid", isValid, "err", err) + return err + } + + // Only elected or nearly elected validators processes the queryEnode message + shouldProcess, err := m.checker.IsElectedOrNearValidator() + if err != nil { + logger.Warn("Error in checking if should process queryEnode", err) + } + + if shouldProcess { + logger.Trace("Processing an queryEnode message", "queryEnode records", qeData.EncryptedEnodeURLs) + w := m.wallets() + for _, encEnodeURL := range qeData.EncryptedEnodeURLs { + // Only process an encEnodURL intended for this node + if encEnodeURL.DestAddress != w.Ecdsa.Address { + continue + } + node, err := DecryptAndParseEnodeURL(&w.Ecdsa, encEnodeURL.EncryptedEnodeURL) + if err != nil { + logger.Error("Can't process encEnodeURL. err", err, "encEnodeURL.EncryptedEnodeURL", encEnodeURL.EncryptedEnodeURL) + return err + } + + // queryEnode messages should only be processed once because selfRecentMessages + // will cache seen queryEnode messages, so it's safe to answer without any throttling + if err := m.answerQueryEnodeMsg(msg.Address, node, qeData.Version); err != nil { + logger.Warn("Error answering an announce msg", "target node", node.URLv4(), "error", err) + return err + } + + break + } + } + + // Regossip this queryEnode message + return m.regossipQueryEnode(msg, qeData.Version, payload) +} + +// answerQueryEnodeMsg will answer a received queryEnode message from an origin +// node. If the origin node is already a peer of any kind, an enodeCertificate will be sent. +// Regardless, the origin node will be upserted into the val enode table +// to ensure this node designates the origin node as a ValidatorPurpose peer. +func (m *Manager) answerQueryEnodeMsg(address common.Address, node *enode.Node, version uint) error { + logger := m.logger.New("func", "answerQueryEnodeMsg", "address", address) + + // Get the external enode that this validator is assigned to + externalEnodeMap, err := m.vpap.GetValProxyAssignments([]common.Address{address}) + if err != nil { + logger.Warn("Error in retrieving assigned proxy for remote validator", "address", address, "err", err) + return err + } + + // Only answer query when validating + if externalEnode := externalEnodeMap[address]; externalEnode != nil && m.checker.IsValidating() { + enodeCertificateMsgs := m.RetrieveEnodeCertificateMsgMap() + + enodeCertMsg := enodeCertificateMsgs[externalEnode.ID()] + if enodeCertMsg == nil { + return errNodeMissingEnodeCertificate + } + + payload, err := enodeCertMsg.Msg.Payload() + if err != nil { + logger.Warn("Error getting payload of enode certificate message", "err", err) + return err + } + + if err := m.network.Multicast([]common.Address{address}, payload, istanbul.EnodeCertificateMsg, false); err != nil { + return err + } + } + + // Upsert regardless to account for the case that the target is a non-ValidatorPurpose + // peer but should be. + // If the target is not a peer and should be a ValidatorPurpose peer, this + // will designate the target as a ValidatorPurpose peer and send an enodeCertificate + // during the istanbul handshake. + if err := m.state.ValEnodeTable.UpsertVersionAndEnode([]*istanbul.AddressEntry{{Address: address, Node: node, Version: version}}); err != nil { + return err + } + return nil +} + +// validateQueryEnode will do some validation to check the contents of the queryEnode +// message. This is to force all validators that send a queryEnode message to +// create as succint message as possible, and prevent any possible network DOS attacks +// via extremely large queryEnode message. +func validateQueryEnode(lg log.Logger, msgAddress common.Address, qeData *istanbul.QueryEnodeData, maxQueries int) (bool, error) { + logger := lg.New("func", "validateQueryEnode", "msg address", msgAddress) + + // Check if there are any duplicates in the queryEnode message + if has, dupAddress := qeData.HasDuplicates(); has { + logger.Info("QueryEnode message has duplicate entries", "address", dupAddress) + return false, nil + } + if len(qeData.EncryptedEnodeURLs) > maxQueries { + logger.Info("Number of queryEnode message encrypted enodes is more max allowed", "num queryEnode enodes", len(qeData.EncryptedEnodeURLs), "max", maxQueries) + return false, nil + } + + return true, nil +} + +// regossipQueryEnode will regossip a received queryEnode message. +// If this node regossiped a queryEnode from the same source address within the last +// 5 minutes, then it won't regossip. This is to prevent a malicious validator from +// DOS'ing the network with very frequent announce messages. +// This opens an attack vector where any malicious node could continue to gossip +// a previously gossiped announce message from any validator, causing other nodes to regossip and +// enforce the cooldown period for future messages originating from the origin validator. +// This is circumvented by caching the hashes of messages that are regossiped +// with sb.selfRecentMessages to prevent future regossips. +func (m *Manager) regossipQueryEnode(msg *istanbul.Message, msgTimestamp uint, payload []byte) error { + logger := m.logger.New("func", "regossipQueryEnode", "queryEnodeSourceAddress", msg.Address, "msgTimestamp", msgTimestamp) + + // Don't throttle messages from our own address so that proxies always regossip + // query enode messages sent from the proxied validator + if msg.Address != m.addrProvider.ValidatorAddress() { + if lastGossiped, ok := m.state.LastQueryEnodeGossiped.Get(msg.Address); ok { + if time.Since(lastGossiped) < QueryEnodeGossipCooldownDuration { + logger.Trace("Already regossiped msg from this source address within the cooldown period, not regossiping.") + return nil + } + } + } + + logger.Trace("Regossiping the istanbul queryEnode message", "IstanbulMsg", msg.String()) + if err := m.network.Gossip(payload, istanbul.QueryEnodeMsg); err != nil { + return err + } + + m.state.LastQueryEnodeGossiped.Set(msg.Address, time.Now()) + + return nil +} + +// SendVersionCertificateTable sends all VersionCertificates this node +// has to a peer +func (m *Manager) SendVersionCertificateTable(peer consensus.Peer) error { + return m.vcGossiper.SendAllFrom(m.state.VersionCertificateTable, peer) +} + +// HandleVersionCertificatesMsg handles a versionCertificates received by the p2p network, according to the announce protocol spec. +func (m *Manager) HandleVersionCertificatesMsg(addr common.Address, peer consensus.Peer, payload []byte) error { + logger := m.logger.New("func", "HandleVersionCertificatesMsg") + logger.Trace("Handling version certificates msg") + + // Since this is a gossiped messaged, mark that the peer gossiped it (and presumably processed it) and check to see if this node already processed it + m.gossipCache.MarkMessageProcessedByPeer(addr, payload) + if m.gossipCache.CheckIfMessageProcessedBySelf(payload) { + return nil + } + defer m.gossipCache.MarkMessageProcessedBySelf(payload) + + msg := &istanbul.Message{} + if err := msg.FromPayload(payload, nil); err != nil { + logger.Error("Error in decoding version certificates message", "err", err, "payload", hex.EncodeToString(payload)) + return err + } + logger = logger.New("msg address", msg.Address) + + versionCertificates := msg.VersionCertificates() + + // If the announce's valAddress is not within the validator connection set, then ignore it + validatorConnSet, err := m.network.RetrieveValidatorConnSet() + if err != nil { + logger.Trace("Error in retrieving validator conn set", "err", err) + return err + } + + var validEntries []*istanbul.VersionCertificate + validAddresses := make(map[common.Address]bool) + // Verify all entries are valid and remove duplicates + for _, versionCertificate := range versionCertificates { + address := versionCertificate.Address() + if !validatorConnSet[address] { + logger.Debug("Found version certificate from an address not in the validator conn set", "address", address) + continue + } + if _, ok := validAddresses[address]; ok { + logger.Debug("Found duplicate version certificate in message", "address", address) + continue + } + validAddresses[address] = true + validEntries = append(validEntries, versionCertificate) + } + if err := m.ovcp.Process(m.state, validEntries, m.wallets().Ecdsa.Address); err != nil { + logger.Warn("Error upserting and gossiping entries", "err", err) + return err + } + return nil +} + +// UpdateAnnounceVersion will asynchronously update the announce version. +func (m *Manager) UpdateAnnounceVersion() { + m.worker.UpdateVersion() +} + +// GetAnnounceVersion will retrieve the current announce version. +func (m *Manager) GetAnnounceVersion() uint { + return m.worker.GetVersion() +} + +// RetrieveEnodeCertificateMsgMap gets the most recent enode certificate messages. +// May be nil if no message was generated as a result of the core not being +// started, or if a proxy has not received a message from its proxied validator +func (m *Manager) RetrieveEnodeCertificateMsgMap() map[enode.ID]*istanbul.EnodeCertMsg { + return m.ecertHolder.Get() +} + +// HandleEnodeCertificateMsg handles an enode certificate message for proxied and standalone validators. +func (m *Manager) HandleEnodeCertificateMsg(_ consensus.Peer, payload []byte) error { + logger := m.logger.New("func", "HandleEnodeCertificateMsg") + + var msg istanbul.Message + // Decode payload into msg + err := msg.FromPayload(payload, istanbul.GetSignatureAddress) + if err != nil { + logger.Error("Error in decoding received Istanbul Enode Certificate message", "err", err, "payload", hex.EncodeToString(payload)) + return err + } + logger = logger.New("msg address", msg.Address) + + enodeCertificate := msg.EnodeCertificate() + logger.Trace("Received Istanbul Enode Certificate message", "enodeCertificate", enodeCertificate) + + parsedNode, err := enode.ParseV4(enodeCertificate.EnodeURL) + if err != nil { + logger.Warn("Malformed v4 node in received Istanbul Enode Certificate message", "enodeCertificate", enodeCertificate, "err", err) + return err + } + + // Ensure this node is a validator in the validator conn set + shouldSave, err := m.checker.IsElectedOrNearValidator() + if err != nil { + logger.Error("Error checking if should save received validator enode url", "err", err) + return err + } + if !shouldSave { + logger.Debug("This node should not save validator enode urls, ignoring enodeCertificate") + return nil + } + + validatorConnSet, err := m.network.RetrieveValidatorConnSet() + if err != nil { + logger.Debug("Error in retrieving registered/elected valset", "err", err) + return err + } + + if !validatorConnSet[msg.Address] { + logger.Debug("Received Istanbul Enode Certificate message originating from a node not in the validator conn set") + return errUnauthorizedAnnounceMessage + } + + if err := m.state.ValEnodeTable.UpsertVersionAndEnode([]*istanbul.AddressEntry{{Address: msg.Address, Node: parsedNode, Version: enodeCertificate.Version}}); err != nil { + logger.Warn("Error in upserting a val enode table entry", "error", err) + return err + } + + // Send a valEnodesShare message to the proxy when it's the primary + if m.isProxiedValidator() && m.checker.IsValidating() { + m.proxyContext.GetProxiedValidatorEngine().SendValEnodesShareMsgToAllProxies() + } + + return nil +} + +func (m *Manager) SetEnodeCertificateMsgMap(enodeCertMsgMap map[enode.ID]*istanbul.EnodeCertMsg) error { + return m.ecertHolder.Set(enodeCertMsgMap) +} + +func (m *Manager) StartAnnouncing(onStart func() error) error { + m.announceMu.Lock() + defer m.announceMu.Unlock() + if m.announceRunning { + return istanbul.ErrStartedAnnounce + } + + m.announceRunning = true + m.announceThreadWg.Add(1) + go m.announceThread() + + if err := onStart(); err != nil { + m.unlockedStopAnnouncing(func() error { return nil }) + return err + } + + return nil +} + +func (m *Manager) StopAnnouncing(onStop func() error) error { + m.announceMu.Lock() + defer m.announceMu.Unlock() + + if !m.announceRunning { + return istanbul.ErrStoppedAnnounce + } + + return m.unlockedStopAnnouncing(onStop) +} + +func (m *Manager) unlockedStopAnnouncing(onStop func() error) error { + m.worker.Stop() + m.announceThreadWg.Wait() + + m.announceRunning = false + + return onStop() +} + +func (m *Manager) GetVersionCertificateTableInfo() (map[string]*VersionCertificateEntryInfo, error) { + return m.state.VersionCertificateTable.Info() +} + +// IsAnnounceRunning returns true iff the anounce Worker thread is running, without locking +// for access. +func (m *Manager) IsAnnounceRunning() bool { + m.announceMu.RLock() + defer m.announceMu.RUnlock() + return m.announceRunning +} + +// Worker returns the worker used by this manager. +func (m *Manager) Worker() Worker { + return m.worker +}
diff --git go-ethereum/consensus/istanbul/core/roundstate_db.go celo/consensus/istanbul/core/roundstate_db.go new file mode 100644 index 0000000000000000000000000000000000000000..17878de022fb140959c253f85bd272cff71dc65a --- /dev/null +++ celo/consensus/istanbul/core/roundstate_db.go @@ -0,0 +1,470 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "bytes" + "encoding/binary" + "io" + "math/big" + "os" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/task" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "github.com/syndtr/goleveldb/leveldb" + lvlerrors "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/syndtr/goleveldb/leveldb/util" +) + +const ( + dbVersion = 2 + dbVersionKey = "version" // Version of the database to flush if changes + lastViewKey = "lastView" // Last View that we know of + rsKey = "rs" // Database Key Pefix for RoundState + rcvdKey = "rcvd" // Database Key Prefix for rcvd messages from the RoundState (split saving) +) + +type RoundStateDB interface { + GetLastView() (*istanbul.View, error) + // GetOldestValidView returns the oldest valid view that can be stored on the db + // it might or might not be present on the db + GetOldestValidView() (*istanbul.View, error) + GetRoundStateFor(view *istanbul.View) (RoundState, error) + UpdateLastRoundState(rs RoundState) error + UpdateLastRcvd(rs RoundState) error + Close() error +} + +// RoundStateDBOptions are the options for a RoundStateDB instance +type RoundStateDBOptions struct { + withGarbageCollector bool + garbageCollectorPeriod time.Duration + sequencesToSave uint64 +} + +type roundStateDBImpl struct { + db *leveldb.DB + stopGarbageCollector task.StopFn + opts RoundStateDBOptions + logger log.Logger +} + +var defaultRoundStateDBOptions = RoundStateDBOptions{ + withGarbageCollector: true, + sequencesToSave: 100, + garbageCollectorPeriod: 2 * time.Minute, +} + +func coerceOptions(opts *RoundStateDBOptions) RoundStateDBOptions { + if opts == nil { + return defaultRoundStateDBOptions + } + + options := *opts + if options.sequencesToSave == 0 { + options.sequencesToSave = defaultRoundStateDBOptions.sequencesToSave + } + if options.withGarbageCollector && options.garbageCollectorPeriod == 0 { + options.garbageCollectorPeriod = defaultRoundStateDBOptions.garbageCollectorPeriod + } + return options +} + +func newRoundStateDB(path string, opts *RoundStateDBOptions) (RoundStateDB, error) { + logger := log.New("func", "newRoundStateDB", "type", "roundStateDB", "rsdb_path", path) + + logger.Info("Open roundstate db") + var db *leveldb.DB + var err error + if path == "" { + db, err = newMemoryDB() + } else { + db, err = newPersistentDB(path) + } + + if err != nil { + logger.Error("Failed to open roundstate db", "err", err) + return nil, err + } + + rsdb := &roundStateDBImpl{ + db: db, + opts: coerceOptions(opts), + logger: logger, + } + + if rsdb.opts.withGarbageCollector { + rsdb.stopGarbageCollector = task.RunTaskRepeateadly(rsdb.garbageCollectEntries, task.NewDefaultTicker(rsdb.opts.garbageCollectorPeriod)) + } + + return rsdb, nil +} + +// newMemoryDB creates a new in-memory node database without a persistent backend. +func newMemoryDB() (*leveldb.DB, error) { + db, err := leveldb.Open(storage.NewMemStorage(), nil) + if err != nil { + return nil, err + } + return db, nil +} + +// newPersistentNodeDB creates/opens a leveldb backed persistent node database, +// also flushing its contents in case of a version mismatch. +func newPersistentDB(path string) (*leveldb.DB, error) { + opts := &opt.Options{OpenFilesCacheCapacity: 5} + db, err := leveldb.OpenFile(path, opts) + if _, iscorrupted := err.(*lvlerrors.ErrCorrupted); iscorrupted { + db, err = leveldb.RecoverFile(path, nil) + } + if err != nil { + return nil, err + } + // The nodes contained in the cache correspond to a certain protocol version. + // Flush all nodes if the version doesn't match. + currentVer := make([]byte, binary.MaxVarintLen64) + currentVer = currentVer[:binary.PutVarint(currentVer, int64(dbVersion))] + + blob, err := db.Get([]byte(dbVersionKey), nil) + switch err { + case leveldb.ErrNotFound: + // Version not found (i.e. empty cache), insert it + if err := db.Put([]byte(dbVersionKey), currentVer, nil); err != nil { + db.Close() + return nil, err + } + + case nil: + // Version present, flush if different + if !bytes.Equal(blob, currentVer) { + db.Close() + if err = os.RemoveAll(path); err != nil { + return nil, err + } + return newPersistentDB(path) + } + } + return db, nil +} + +type rcvd struct { + Prepares MessageSet + Commits MessageSet + ParentCommits MessageSet +} + +type rcvdRLP struct { + Prep []byte + Comm []byte + Parc []byte +} + +func (r *rcvd) ToRLP() (*rcvdRLP, error) { + serializedParentCommits, err := r.ParentCommits.Serialize() + if err != nil { + return nil, err + } + serializedPrepares, err := r.Prepares.Serialize() + if err != nil { + return nil, err + } + serializedCommits, err := r.Commits.Serialize() + if err != nil { + return nil, err + } + return &rcvdRLP{ + Prep: serializedPrepares, + Comm: serializedCommits, + Parc: serializedParentCommits, + }, nil +} + +func (r *rcvd) FromRLP(r2 *rcvdRLP) error { + var err error + r.Prepares, err = deserializeMessageSet(r2.Prep) + if err != nil { + return err + } + r.ParentCommits, err = deserializeMessageSet(r2.Parc) + if err != nil { + return err + } + r.Commits, err = deserializeMessageSet(r2.Comm) + if err != nil { + return err + } + return nil +} + +func (r *rcvdRLP) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{r.Prep, r.Comm, r.Parc}) +} + +func (r *rcvdRLP) DecodeRLP(s *rlp.Stream) error { + var d struct { + Prepares []byte + Commits []byte + ParentCommits []byte + } + if err := s.Decode(&d); err != nil { + return err + } + r.Prep = d.Prepares + r.Comm = d.Commits + r.Parc = d.ParentCommits + return nil +} + +func rcvdFromRoundState(rs RoundState) *rcvd { + r := rcvd{} + r.Prepares = rs.Prepares() + r.Commits = rs.Commits() + r.ParentCommits = rs.ParentCommits() + return &r +} + +// UpdateLastRcvd will update to the db only the rcvd messages (prepares, commits, parentCommits). +// this is used to hold signed messages as proof for possible future slashes. +// While UpdateLastRoundState stores the messages as well, this is called from +// consensus when a message is received and approved, where we need fast updates +// to the db, and we want to avoid re-saving the block in these cases. +// +// Possible future improvements are to completely refactor the RoundState & Impl to +// include a fine-grained journaling system. +func (rsdb *roundStateDBImpl) UpdateLastRcvd(rs RoundState) error { + logger := rsdb.logger.New("func", "UpdateLastRcvd") + rcvdViewKey := rcvdView2Key(rs.View()) + + r := rcvdFromRoundState(rs) + rRLP, err := r.ToRLP() + if err != nil { + return err + } + entryBytes, err := rlp.EncodeToBytes(&rRLP) + if err != nil { + logger.Error("Failed to save rcvd messages from roundState", "reason", "rlp encoding", "err", err) + return err + } + batch := new(leveldb.Batch) + batch.Put(rcvdViewKey, entryBytes) + err = rsdb.db.Write(batch, nil) + if err != nil { + logger.Error("Failed to save rcvd messages from roundState", "reason", "levelDB write", "err", err, "func") + } + + return err +} + +// storeRoundState will store the currentRoundState in a Map<view, roundState> schema. +func (rsdb *roundStateDBImpl) UpdateLastRoundState(rs RoundState) error { + // We store the roundState for each view; since we'll need this + // information to allow the node to have evidence to show that + // a validator did a "valid" double signing + logger := rsdb.logger.New("func", "UpdateLastRoundState") + viewKey := view2Key(rs.View()) + + entryBytes, err := rlp.EncodeToBytes(rs) + if err != nil { + logger.Error("Failed to save roundState", "reason", "rlp encoding", "err", err) + return err + } + + batch := new(leveldb.Batch) + batch.Put([]byte(lastViewKey), viewKey) + batch.Put(viewKey, entryBytes) + + err = rsdb.db.Write(batch, nil) + if err != nil { + logger.Error("Failed to save roundState", "reason", "levelDB write", "err", err, "func") + } + + return err +} + +func (rsdb *roundStateDBImpl) GetLastView() (*istanbul.View, error) { + rawEntry, err := rsdb.db.Get([]byte(lastViewKey), nil) + if err != nil { + return nil, err + } + + return key2View(rawEntry), nil +} + +func (rsdb *roundStateDBImpl) GetOldestValidView() (*istanbul.View, error) { + lastView, err := rsdb.GetLastView() + // If nothing stored all views are valid + if err == leveldb.ErrNotFound { + return &istanbul.View{Sequence: common.Big0, Round: common.Big0}, nil + } else if err != nil { + return nil, err + } + + oldestValidSequence := new(big.Int).Sub(lastView.Sequence, new(big.Int).SetUint64(rsdb.opts.sequencesToSave)) + if oldestValidSequence.Cmp(common.Big0) < 0 { + oldestValidSequence = common.Big0 + } + + return &istanbul.View{Sequence: oldestValidSequence, Round: common.Big0}, nil +} + +func (rsdb *roundStateDBImpl) GetRoundStateFor(view *istanbul.View) (RoundState, error) { + viewKey := view2Key(view) + rawEntry, err := rsdb.db.Get(viewKey, nil) + if err != nil { + return nil, err + } + + var entry roundStateImpl + if err = rlp.DecodeBytes(rawEntry, &entry); err != nil { + return nil, err + } + // Check if rcvd is stored + rcvdViewKey := rcvdView2Key(view) + rawRcvd, err := rsdb.db.Get(rcvdViewKey, nil) + // No rcvd, return the roundstate as found + if err == leveldb.ErrNotFound { + return &entry, nil + } + // Unknown error. Return the roundstate as found, but log the err + if err != nil { + return nil, err + } + var r *rcvdRLP = &rcvdRLP{} + if err = rlp.DecodeBytes(rawRcvd, &r); err != nil { + return nil, err + } + // Transform into rcvd + var res *rcvd = &rcvd{} + err = res.FromRLP(r) + if err != nil { + return nil, err + } + return merged(&entry, res), nil +} + +func merged(rs *roundStateImpl, r *rcvd) *roundStateImpl { + rs.prepares.AddAll(r.Prepares.Values()) + rs.commits.AddAll(r.Commits.Values()) + rs.parentCommits.AddAll(r.ParentCommits.Values()) + return rs +} + +func (rsdb *roundStateDBImpl) Close() error { + if rsdb.opts.withGarbageCollector { + rsdb.stopGarbageCollector() + } + return rsdb.db.Close() +} + +func (rsdb *roundStateDBImpl) garbageCollectEntries() { + logger := rsdb.logger.New("func", "garbageCollectEntries") + + oldestValidView, err := rsdb.GetOldestValidView() + if err != nil { + logger.Error("Aborting RoundStateDB GarbageCollect: Failed to fetch oldestValidView", "err", err) + return + } + + logger.Debug("Pruning entries from old views", "oldestValidView", oldestValidView) + count, err := rsdb.deleteEntriesOlderThan(oldestValidView) + if err != nil { + logger.Error("Aborting RoundStateDB GarbageCollect: Failed to remove entries", "entries_removed", count, "err", err) + return + } + + logger.Debug("Finished RoundStateDB GarbageCollect", "removed_entries", count) +} + +func (rsdb *roundStateDBImpl) deleteEntriesOlderThan(lastView *istanbul.View) (int, error) { + fromViewKey := view2Key(&istanbul.View{Sequence: common.Big0, Round: common.Big0}) + toViewKey := view2Key(lastView) + + count, err := rsdb.deleteIteratorEntries(&util.Range{Start: fromViewKey, Limit: toViewKey}) + if err != nil { + return count, err + } + + fromRcvdKey := rcvdView2Key(&istanbul.View{Sequence: common.Big0, Round: common.Big0}) + toRcvdKey := rcvdView2Key(lastView) + rcvdCount, err := rsdb.deleteIteratorEntries(&util.Range{Start: fromRcvdKey, Limit: toRcvdKey}) + if err != nil { + return count + rcvdCount, err + } + return count + rcvdCount, nil +} + +func (rsdb *roundStateDBImpl) deleteIteratorEntries(rang *util.Range) (int, error) { + iter := rsdb.db.NewIterator(rang, nil) + defer iter.Release() + counter := 0 + for iter.Next() { + rawKey := iter.Key() + err := rsdb.db.Delete(rawKey, nil) + if err != nil { + return counter, err + } + counter++ + } + return counter, nil +} + +// view2Key will encode a view in binary format +// so that the binary format maintains the sort order for the view, +// using the rsKey prefix +func view2Key(view *istanbul.View) []byte { + return prefixView2Key(rsKey, view) +} + +// rcvdView2Key will encode a view in binary format +// so that the binary format maintains the sort order for the view, +// using the rcvdKey prefix +func rcvdView2Key(view *istanbul.View) []byte { + return prefixView2Key(rcvdKey, view) +} + +func prefixView2Key(prefix string, view *istanbul.View) []byte { + // leveldb sorts entries by key + // keys are sorted with their binary representation, so we need a binary representation + // that mantains the key order + // The key format is [ prefix . BigEndian(Sequence) . BigEndian(Round)] + // We use BigEndian so to maintain order in binary format + // And we want to sort by (seq, round); since seq had higher precedence than round + buff := make([]byte, len(prefix)+16) + + copy(buff, prefix) + // TODO (mcortesi) Support Seq/Round bigger than 64bits + binary.BigEndian.PutUint64(buff[len(prefix):], view.Sequence.Uint64()) + binary.BigEndian.PutUint64(buff[len(prefix)+8:], view.Round.Uint64()) + + return buff +} + +func key2View(key []byte) *istanbul.View { + prefixLen := len([]byte(rsKey)) + seq := binary.BigEndian.Uint64(key[prefixLen : prefixLen+8]) + round := binary.BigEndian.Uint64(key[prefixLen+8:]) + return &istanbul.View{ + Sequence: new(big.Int).SetUint64(seq), + Round: new(big.Int).SetUint64(round), + } +}
diff --git go-ethereum/consensus/istanbul/core/roundchange_v2.go celo/consensus/istanbul/core/roundchange_v2.go new file mode 100644 index 0000000000000000000000000000000000000000..74f16c3b3fd813051533e692f58374f36d1607f7 --- /dev/null +++ celo/consensus/istanbul/core/roundchange_v2.go @@ -0,0 +1,232 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +// sendRoundChange broadcasts a ROUND CHANGE message with the current desired round. +func (c *core) sendRoundChange() { + msg, err := c.buildSignedRoundChangeMsgV2(c.current.DesiredRound()) + if err != nil { + logger := c.newLogger("func", "sendRoundChange") + logger.Warn("Cannot build signed roundChangeV2 message", "error", err) + return + } + c.broadcast(msg) +} + +// sendRoundChange sends a ROUND CHANGE message for the current desired round back to a single address +func (c *core) sendRoundChangeAgain(addr common.Address) { + msg, err := c.buildSignedRoundChangeMsgV2(c.current.DesiredRound()) + if err != nil { + logger := c.newLogger("func", "sendRoundChangeAgain", "addr", addr) + logger.Warn("Cannot build signed roundChangeV2 message", "error", err) + return + } + c.unicast(msg, addr) +} + +// buildRoundChangeV2 builds a roundChangeV2 instance with an empty prepared certificate +func buildRoundChangeV2(addr common.Address, view *istanbul.View) *istanbul.RoundChangeV2 { + pc, proposal := istanbul.EmptyPreparedCertificateV2() + return &istanbul.RoundChangeV2{ + Request: istanbul.RoundChangeRequest{ + Address: addr, + View: *view, + PreparedCertificateV2: pc, + }, + PreparedProposal: proposal, + } +} + +// buildSignedRoundChangeMsgV2 builds a roundChangeV2 istanbul.Message, with the inner +// roundChangeRequest properly signed, ready to be broadcast/unicast. +func (c *core) buildSignedRoundChangeMsgV2(round *big.Int) (*istanbul.Message, error) { + nextView := &istanbul.View{ + Round: new(big.Int).Set(round), + Sequence: new(big.Int).Set(c.current.View().Sequence), + } + roundChangeV2 := buildRoundChangeV2(c.address, nextView) + pc := c.current.PreparedCertificate() + if !pc.IsEmpty() { + // Add prepare certificate proposal and votes + roundChangeV2.Request.PreparedCertificateV2 = istanbul.PCV2FromPCV1(pc) + roundChangeV2.PreparedProposal = pc.Proposal + } + // Sign the round change request + if err := roundChangeV2.Request.Sign(c.backend.Sign); err != nil { + return nil, err + } + return istanbul.NewRoundChangeV2Message(roundChangeV2, c.address), nil +} + +func (c *core) handleRoundChangeCertificateV2(view istanbul.View, roundChangeCertificateV2 istanbul.RoundChangeCertificateV2, proposal istanbul.Proposal) error { + subject := istanbul.Subject{ + View: &view, + Digest: proposal.Hash(), + } + + logger := c.newLogger("func", "handleRoundChangeCertificateV2", "proposal_round", subject.View.Round, "proposal_seq", subject.View.Sequence, "proposal_digest", subject.Digest.String()) + + if len(roundChangeCertificateV2.Requests) > c.current.ValidatorSet().Size() || len(roundChangeCertificateV2.Requests) < c.current.ValidatorSet().MinQuorumSize() { + return errInvalidRoundChangeCertificateNumMsgs + } + + seen := make(map[common.Address]bool) + for i := range roundChangeCertificateV2.Requests { + // use a different variable each time since we'll store a pointer to the variable + request := roundChangeCertificateV2.Requests[i] + + // Verify message signed by a validator + if err := istanbul.CheckSignedBy(&request, request.Signature, + request.Address, errInvalidRoundChangeCertificateMsgSignature, c.validateFn); err != nil { + return err + } + + // Check for duplicate ROUND CHANGE messages + if seen[request.Address] { + return errInvalidRoundChangeCertificateDuplicate + } + seen[request.Address] = true + + if request.View.Sequence == nil || request.View.Round == nil { + return errInvalidRoundChangeCertificateMsgView + } + + msgLogger := logger.New("msg_round", request.View.Round, "msg_seq", request.View.Sequence) + + // Verify ROUND CHANGE message is for the same sequence AND an equal or subsequent round as the proposal. + // We have already called checkMessage by this point and checked the proposal's and PREPREPARE's sequence match. + if request.View.Sequence.Cmp(subject.View.Sequence) != 0 || request.View.Round.Cmp(subject.View.Round) < 0 { + msgLogger.Error("Round change request in certificate for a different sequence or an earlier round") + return errInvalidRoundChangeCertificateMsgView + } + } + maxRound := roundChangeCertificateV2.HighestRoundWithPreparedCertificate() + if maxRound != nil { + pc := roundChangeCertificateV2.GetPreparedCertificateFor(maxRound, proposal.Hash()) + if pc == nil { + logger.Warn("Received proposal hash not found in available prepared certificates in the round change certificate", + "proposalHash", proposal.Hash()) + return errInvalidPreparedCertificateDigestMismatch + } + preparedView, err := c.verifyPCV2WithProposal(*pc, proposal) + if err != nil { + return err + } + // We must use the proposal in the prepared certificate with the highest round number. (See OSDI 99, Section 4.4) + // Older prepared certificates may be generated, but if no node committed, there is no guarantee that + // it will be the next pre-prepare. If one node committed, that block is guaranteed (by quorum intersection) + // to be the next pre-prepare. That (higher view) prepared cert should override older perpared certs for + // blocks that were not committed. + // Also reject round change messages where the prepared view is greater than the round change view. + if preparedView == nil || preparedView.Round.Cmp(subject.View.Round) > 0 { + return errInvalidRoundChangeViewMismatch + } + } + + // May have already moved to this round based on quorum round change messages. + logger.Trace("Trying to move to round change certificate's round", "target round", subject.View.Round) + + return c.startNewRound(subject.View.Round, false) +} + +func (c *core) handleRoundChangeV2(msg *istanbul.Message) error { + logger := c.newLogger("func", "handleRoundChangeV2", "tag", "handleMsg", "from", msg.Address) + + rc := msg.RoundChangeV2() + + // Check signature of the internal Request + if err := istanbul.CheckSignedBy(&rc.Request, rc.Request.Signature, + rc.Request.Address, errInvalidRoundChangeRequestSignature, c.validateFn); err != nil { + return err + } + // Check message address and request address is the same + if msg.Address != rc.Request.Address { + return errRoundChangeRequestAddressMismatch + } + logger = logger.New("msg_round", rc.Request.View.Round, "msg_seq", rc.Request.View.Sequence) + + // Must be same sequence and future round. + err := c.checkMessage(istanbul.MsgRoundChangeV2, &rc.Request.View) + + // If the RC message is for the current sequence but a prior round, help the sender fast forward + // by sending back to it (not broadcasting) a round change message for our desired round. + if err == errOldMessage && rc.Request.View.Sequence.Cmp(c.current.Sequence()) == 0 { + logger.Trace("Sending round change for desired round to node with a previous desired round", "msg_round", rc.Request.View.Round) + c.sendRoundChangeAgain(msg.Address) + return nil + } else if err != nil { + logger.Debug("Check round change message failed", "err", err) + return err + } + // Verify that it has a proposal only if and only if a prepared certificate is available + if !rc.ProposalMatch() { + return errRoundChangeProposalHashMismatch + } + // Verify the PREPARED certificate if present. + if rc.HasPreparedCertificate() { + preparedView, err := c.verifyPCV2WithProposal(rc.Request.PreparedCertificateV2, rc.PreparedProposal) + if err != nil { + return err + } else if preparedView == nil || preparedView.Round.Cmp(rc.Request.View.Round) > 0 { + return errInvalidRoundChangeViewMismatch + } + } + + roundView := rc.Request.View + + // Add the ROUND CHANGE message to its message set. + if err := c.roundChangeSetV2.Add(roundView.Round, msg); err != nil { + logger.Warn("Failed to add round change message", "roundView", roundView, "err", err) + return err + } + + // Skip to the highest round we know F+1 (one honest validator) is at, but + // don't start a round until we have a quorum who want to start a given round. + ffRound := c.roundChangeSetV2.MaxRound(c.current.ValidatorSet().F() + 1) + quorumRound := c.roundChangeSetV2.MaxOnOneRound(c.current.ValidatorSet().MinQuorumSize()) + logger = logger.New("ffRound", ffRound, "quorumRound", quorumRound) + logger.Trace("Got round change message", "rcs", c.roundChangeSetV2.String()) + // On f+1 round changes we send a round change and wait for the next round if we haven't done so already + // On quorum round change messages we go to the next round immediately. + if quorumRound != nil && quorumRound.Cmp(c.current.DesiredRound()) >= 0 { + logger.Debug("Got quorum round change messages, starting new round.") + return c.startNewRound(quorumRound, true) + } else if ffRound != nil { + logger.Debug("Got f+1 round change messages, sending own round change message and waiting for next round.") + c.waitForDesiredRound(ffRound) + } + + return nil +} + +// ---------------------------------------------------------------------------- + +// CurrentRoundChangeSet returns the current round change set summary. +func (c *core) CurrentRoundChangeSet() *RoundChangeSetSummary { + rcs := c.roundChangeSetV2 + if rcs != nil { + return rcs.Summary() + } + return nil +}
diff --git go-ethereum/consensus/istanbul/announce/vc_gossiper.go celo/consensus/istanbul/announce/vc_gossiper.go new file mode 100644 index 0000000000000000000000000000000000000000..45fa814d37c45e0ec6d1ca12aaad08c92001eec5 --- /dev/null +++ celo/consensus/istanbul/announce/vc_gossiper.go @@ -0,0 +1,68 @@ +package announce + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" +) + +type VersionCertificateGossiper interface { + // GossipAllFrom gossips all version certificates to every peer. Only the entries + // that are new to a node will end up being regossiped throughout the + // network. + GossipAllFrom(*VersionCertificateDB) error + // SendAllFrom sends all VersionCertificates this node + // has to a specific peer. + SendAllFrom(*VersionCertificateDB, consensus.Peer) error + // Gossip will send the given version certificates to all peers. + Gossip(versionCertificates []*istanbul.VersionCertificate) error +} + +func NewVcGossiper(gossipFunction func(payload []byte) error) VersionCertificateGossiper { + return &vcGossiper{ + logger: log.New("module", "versionCertificateGossiper"), + gossip: gossipFunction, + } +} + +type vcGossiper struct { + logger log.Logger + // gossip gossips protocol messages + gossip func(payload []byte) error +} + +func (vg *vcGossiper) GossipAllFrom(vcDb *VersionCertificateDB) error { + allVersionCertificates, err := vcDb.GetAll() + if err != nil { + vg.logger.Warn("Error getting all version certificates", "err", err) + return err + } + return vg.Gossip(allVersionCertificates) +} + +func (vg *vcGossiper) Gossip(versionCertificates []*istanbul.VersionCertificate) error { + logger := vg.logger.New("func", "Gossip") + payload, err := istanbul.NewVersionCeritifcatesMessage(versionCertificates, common.Address{}).Payload() + if err != nil { + logger.Warn("Error encoding version certificate msg", "err", err) + return err + } + return vg.gossip(payload) +} + +func (vg *vcGossiper) SendAllFrom(vcDb *VersionCertificateDB, peer consensus.Peer) error { + logger := vg.logger.New("func", "SendAllFrom") + allVersionCertificates, err := vcDb.GetAll() + if err != nil { + logger.Warn("Error getting all version certificates", "err", err) + return err + } + payload, err := istanbul.NewVersionCeritifcatesMessage(allVersionCertificates, common.Address{}).Payload() + if err != nil { + logger.Warn("Error encoding version certificate msg", "err", err) + return err + } + + return peer.Send(istanbul.VersionCertificatesMsg, payload) +}
diff --git go-ethereum/consensus/istanbul/backend/handler_test.go celo/consensus/istanbul/backend/handler_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a034ffb5fd9218ffbe615262118507756ece5e1f --- /dev/null +++ celo/consensus/istanbul/backend/handler_test.go @@ -0,0 +1,226 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" +) + +type MockPeer struct { + Messages chan p2p.Msg + NodeOverride *enode.Node +} + +func (p *MockPeer) Send(msgcode uint64, data interface{}) error { + return nil +} + +func (p *MockPeer) Node() *enode.Node { + if p.NodeOverride != nil { + return p.NodeOverride + } + return enode.MustParse("enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150") +} + +func (p *MockPeer) Version() uint { + return 0 +} + +func (p *MockPeer) ReadMsg() (p2p.Msg, error) { + select { + case msg := <-p.Messages: + return msg, nil + default: + return p2p.Msg{}, nil + } +} + +func (p *MockPeer) Inbound() bool { + return false +} + +func (p *MockPeer) PurposeIsSet(purpose p2p.PurposeFlag) bool { + return false +} + +func TestIstanbulMessage(t *testing.T) { + chain, backend := newBlockChain(1, true) + defer chain.Stop() + + // generate one msg + data := []byte("data1") + msg := makeMsg(istanbul.QueryEnodeMsg, data) + addr := common.BytesToAddress([]byte("address")) + + _, err := backend.HandleMsg(addr, msg, &MockPeer{}) + if err != nil { + t.Fatalf("handle message failed: %v", err) + } +} + +func TestRecentMessageCaches(t *testing.T) { + // Define the various voting scenarios to test + tests := []struct { + ethMsgCode uint64 + shouldCache bool + }{ + { + ethMsgCode: istanbul.ConsensusMsg, + shouldCache: false, + }, + { + ethMsgCode: istanbul.QueryEnodeMsg, + shouldCache: true, + }, + { + ethMsgCode: istanbul.ValEnodesShareMsg, + shouldCache: false, + }, + { + ethMsgCode: istanbul.FwdMsg, + shouldCache: false, + }, + { + ethMsgCode: istanbul.VersionCertificatesMsg, + shouldCache: true, + }, + { + ethMsgCode: istanbul.EnodeCertificateMsg, + shouldCache: false, + }, + { + ethMsgCode: istanbul.ValidatorHandshakeMsg, + shouldCache: false, + }, + } + + for _, tt := range tests { + chain, backend := newBlockChain(1, true) + + // generate a msg that is not an Announce + data := []byte("data1") + msg := makeMsg(tt.ethMsgCode, data) + addr := common.BytesToAddress([]byte("address")) + + // 1. this message should not be in cache + // for peers + if backend.gossipCache.CheckIfMessageProcessedByPeer(addr, data) { + t.Fatalf("the cache of messages for this peer should be nil") + } + + // for self + if backend.gossipCache.CheckIfMessageProcessedBySelf(data) { + t.Fatalf("the cache of messages should be nil") + } + + // 2. this message should be in cache only when ethMsgCode == istanbulQueryEnodeMsg || ethMsgCode == istanbulVersionCertificatesMsg + _, err := backend.HandleMsg(addr, msg, &MockPeer{}) + if err != nil { + t.Fatalf("handle message failed: %v", err) + } + + // Sleep for a bit, since some of the messages are handled in a different thread + time.Sleep(10 * time.Second) + + // for peers + if ok := backend.gossipCache.CheckIfMessageProcessedByPeer(addr, data); tt.shouldCache != ok { + t.Fatalf("the cache of messages for this peer should be nil") + } + // for self + if ok := backend.gossipCache.CheckIfMessageProcessedBySelf(data); tt.shouldCache != ok { + t.Fatalf("the cache of messages must be nil") + } + + chain.Stop() + } +} + +func TestReadValidatorHandshakeMessage(t *testing.T) { + chain, backend := newBlockChain(2, true) + defer chain.Stop() + + peer := &MockPeer{ + Messages: make(chan p2p.Msg, 1), + NodeOverride: backend.p2pserver.Self(), + } + + // Test an empty message being sent + emptyMsg := &istanbul.Message{} + emptyMsgPayload, err := emptyMsg.Payload() + if err != nil { + t.Errorf("Error getting payload of empty msg %v", err) + } + peer.Messages <- makeMsg(istanbul.ValidatorHandshakeMsg, emptyMsgPayload) + isValidator, err := backend.readValidatorHandshakeMessage(peer) + if err != nil { + t.Errorf("Error from readValidatorHandshakeMessage %v", err) + } + if isValidator { + t.Errorf("Expected isValidator to be false with empty istanbul message") + } + + var validMsg *istanbul.Message + // The enodeCertificate is not set synchronously. Wait until it's been set + for i := 0; i < 10; i++ { + // Test a legitimate message being sent + enodeCertMsg := backend.RetrieveEnodeCertificateMsgMap()[backend.SelfNode().ID()] + if enodeCertMsg != nil { + validMsg = enodeCertMsg.Msg + } + + if validMsg != nil { + break + } + time.Sleep(time.Duration(i) * time.Second) + } + if validMsg == nil { + t.Errorf("enodeCertificate is nil") + } + + validMsgPayload, err := validMsg.Payload() + if err != nil { + t.Errorf("Error getting payload of valid msg %v", err) + } + peer.Messages <- makeMsg(istanbul.ValidatorHandshakeMsg, validMsgPayload) + + block := backend.currentBlock() + valSet := backend.getValidators(block.Number().Uint64(), block.Hash()) + // set backend to a different validator + // use the atomic load & store to avoid race conditions + w := *backend.wallets() + w.Ecdsa.Address = valSet.GetByIndex(1).Address() + backend.aWallets.Store(&w) + isValidator, err = backend.readValidatorHandshakeMessage(peer) + if err != nil { + t.Errorf("Error from readValidatorHandshakeMessage with valid message %v", err) + } + if !isValidator { + t.Errorf("Expected isValidator to be true with valid message") + } +} + +func makeMsg(msgcode uint64, data interface{}) p2p.Msg { + size, r, _ := rlp.EncodeToReader(data) + return p2p.Msg{Code: msgcode, Size: uint32(size), Payload: r} +}
diff --git go-ethereum/consensus/istanbul/backend/announce_test.go celo/consensus/istanbul/backend/announce_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4bb8c964a61b7d614bf666aeeb64d8d4b154a04d --- /dev/null +++ celo/consensus/istanbul/backend/announce_test.go @@ -0,0 +1,269 @@ +package backend + +import ( + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/announce" + "github.com/ethereum/go-ethereum/rlp" +) + +// This file is kept in the backend package since, while actually testing the announce protocol code, it requires +// several dependencies from the istanbul code. + +// This test function will test the announce message generator and handler. +// It will also test the gossip query generator and handler. +func TestAnnounceGossipQueryMsg(t *testing.T) { + t.Skip() // Flaky + // Create three backends + numValidators := 3 + genesisCfg, nodeKeys := getGenesisAndKeys(numValidators, true) + + chain0, engine0, _ := newBlockChainWithKeys(false, common.Address{}, false, genesisCfg, nodeKeys[0]) + defer chain0.Stop() + chain1, engine1, _ := newBlockChainWithKeys(false, common.Address{}, false, genesisCfg, nodeKeys[1]) + defer chain1.Stop() + chain2, engine2, _ := newBlockChainWithKeys(false, common.Address{}, false, genesisCfg, nodeKeys[2]) + defer chain2.Stop() + + // Wait a bit so that the announce versions are generated for the engines + time.Sleep(6 * time.Second) + + engine0Address := engine0.Address() + engine1Address := engine1.Address() + engine2Address := engine2.Address() + + engine0AnnounceVersion := engine0.GetAnnounceVersion() + engine1AnnounceVersion := engine1.GetAnnounceVersion() + engine2AnnounceVersion := engine2.GetAnnounceVersion() + + engine0Enode := engine0.SelfNode() + + // Create version certificate messages for engine1 and engine2, so that engine0 will send a queryEnodeMessage to them + vCert1, err := istanbul.NewVersionCertificate(engine1AnnounceVersion, engine1.Sign) + if err != nil { + t.Errorf("Error in generating version certificate for engine1. Error: %v", err) + } + + vCert2, err := istanbul.NewVersionCertificate(engine1AnnounceVersion, engine2.Sign) + if err != nil { + t.Errorf("Error in generating version certificate for engine2. Error: %v", err) + } + + // Have engine0 handle vCert messages from engine1 and engine2 + + vCert1MsgPayload, err := istanbul.NewVersionCeritifcatesMessage([]*istanbul.VersionCertificate{vCert1}, engine1Address).Payload() + if err != nil { + t.Errorf("Error in encoding vCert1. Error: %v", err) + } + err = engine0.announceManager.HandleVersionCertificatesMsg(common.Address{}, nil, vCert1MsgPayload) + if err != nil { + t.Errorf("Error in handling vCert1. Error: %v", err) + } + + vCert2MsgPayload, err := istanbul.NewVersionCeritifcatesMessage([]*istanbul.VersionCertificate{vCert2}, engine2Address).Payload() + if err != nil { + t.Errorf("Error in encoding vCert2. Error: %v", err) + } + err = engine0.announceManager.HandleVersionCertificatesMsg(common.Address{}, nil, vCert2MsgPayload) + if err != nil { + t.Errorf("Error in handling vCert2. Error: %v", err) + } + + // Verify that engine0 will query for both engine1 and engine2's enodeURL + qeep := announce.NewQueryEnodeEntryProvider(engine0.valEnodeTable) + qeEntries, err := qeep.GetQueryEnodeValEnodeEntries(false, engine0.wallets().Ecdsa.Address) + if err != nil { + t.Errorf("Error in retrieving entries for queryEnode request") + } + + if len(qeEntries) != 2 { + t.Errorf("qeEntries size is incorrect. Have: %d, Want: 2", len(qeEntries)) + } + + for _, expectedEntry := range []*istanbul.AddressEntry{{Address: engine1Address, HighestKnownVersion: engine1AnnounceVersion}, + {Address: engine2Address, HighestKnownVersion: engine2AnnounceVersion}} { + found := false + for _, qeEntry := range qeEntries { + if qeEntry.Address == expectedEntry.Address && qeEntry.HighestKnownVersion == expectedEntry.HighestKnownVersion { + found = true + break + } + } + + if !found { + t.Errorf("Didn't find expected entry in qeEntries. Expected Entry: %v", expectedEntry) + } + } + + // Generate query enode message for engine0 + // TODO: refactor this test to remove the hard dependency on the generate&gossip fn + wk := engine0.announceManager.Worker() + qeMsg, err := wk.GenerateAndGossipQueryEnode(false) + if err != nil { + t.Errorf("Error in generating a query enode message. Error: %v", err) + } + + // Convert to payload + qePayload, err := qeMsg.Payload() + if err != nil { + t.Errorf("Error in converting QueryEnode Message to payload. Error: %v", err) + } + + // Handle the qeMsg for both engine1 and engine2 + err = engine1.announceManager.HandleQueryEnodeMsg(engine0.Address(), nil, qePayload) + if err != nil { + t.Errorf("Error in handling query enode message for engine1. Error: %v", err) + } + + err = engine2.announceManager.HandleQueryEnodeMsg(engine0.Address(), nil, qePayload) + if err != nil { + t.Errorf("Error in handling query enode message for engine2. Error: %v", err) + } + + // Verify that engine1 and engine2 has engine0's entry in their val enode table + expectedEntry := &istanbul.AddressEntry{Address: engine0Address, Node: engine0Enode, Version: engine0AnnounceVersion} + + entryMap, err := engine1.GetValEnodeTableEntries([]common.Address{engine0Address}) + if err != nil { + t.Errorf("Error in retrieving val enode table entry from engine1. Error: %v", err) + } + + if entry := entryMap[engine0Address]; entry == nil || entry.Address != expectedEntry.Address || entry.Node.URLv4() != expectedEntry.Node.URLv4() || entry.Version != expectedEntry.Version { + t.Errorf("Incorrect val enode table entry for engine0. Want: %v, Have: %v", expectedEntry, entry) + } + + entryMap, err = engine2.GetValEnodeTableEntries([]common.Address{engine0Address}) + if err != nil { + t.Errorf("Error in retrieving val enode table entry from engine2. Error: %v", err) + } + + if entry := entryMap[engine0Address]; entry == nil || entry.Address != expectedEntry.Address || entry.PublicKey != expectedEntry.PublicKey || entry.Node.URLv4() != expectedEntry.Node.URLv4() || entry.Version != expectedEntry.Version || entry.HighestKnownVersion != expectedEntry.HighestKnownVersion { + t.Errorf("Incorrect val enode table entry for engine0. Want: %v, Have: %v", expectedEntry, entry) + } + + engine0.StopAnnouncing() + engine1.StopAnnouncing() + engine2.StopAnnouncing() +} + +// Test enode certificate generation (via the announce thread), and the handling of an enode certificate msg. +func TestHandleEnodeCertificateMsg(t *testing.T) { + // Create two backends + numValidators := 2 + genesisCfg, nodeKeys := getGenesisAndKeys(numValidators, true) + + chain0, engine0, _ := newBlockChainWithKeys(false, common.Address{}, false, genesisCfg, nodeKeys[0]) + defer chain0.Stop() + chain1, engine1, _ := newBlockChainWithKeys(false, common.Address{}, false, genesisCfg, nodeKeys[1]) + defer chain1.Stop() + + engine0Node := engine0.SelfNode() + + // Wait for a bit for the enodeCert messages to be generated by engine0. The announce thread should create that on startup + time.Sleep(6 * time.Second) + + enodeCerts := engine0.RetrieveEnodeCertificateMsgMap() + if enodeCerts == nil || enodeCerts[engine0Node.ID()] == nil { + t.Errorf("No enode certificates generated for engine0") + } + enodeCertMsgPayload, _ := enodeCerts[engine0Node.ID()].Msg.Payload() + + // Handle the enodeCertMsg in engine1 + err := engine1.announceManager.HandleEnodeCertificateMsg(nil, enodeCertMsgPayload) + if err != nil { + t.Errorf("Error in handling an enode certificate message. Error: %v", err) + } + + // Verify that the enode certificate is saved in the val enode table via GetValEnodeTableEntries + vetEntryMap, err := engine1.GetValEnodeTableEntries([]common.Address{engine0.Address()}) + if err != nil { + t.Errorf("Error in retrieving val enode table entires. Error: %v", err) + } + + if vetEntryMap == nil || vetEntryMap[engine0.Address()] == nil { + t.Errorf("Missing val enode table entry for engine0") + } + + engine0VetEntry := vetEntryMap[engine0.Address()] + if engine0VetEntry.Address != engine0.Address() { + t.Errorf("Engine0's val enode table entry's address is incorrect. Want: %v, Have: %v", engine0.Address(), engine0VetEntry.Address) + } + + if engine0VetEntry.Version != engine0.GetAnnounceVersion() { + t.Errorf("Engine0's val enode table entry's version is incorrect. Want: %d, Have: %d", engine0.GetAnnounceVersion(), engine0VetEntry.Version) + } + + engine0.StopAnnouncing() + engine1.StopAnnouncing() +} + +// This function will test the setAndShareUpdatedAnnounceVersion function. +// It will verify that this function creates correct enode certificates, and that +// the engine's announce version is updated. +func TestSetAndShareUpdatedAnnounceVersion(t *testing.T) { + // Create one backend + numValidators := 1 + genesisCfg, nodeKeys := getGenesisAndKeys(numValidators, true) + + chain, engine, _ := newBlockChainWithKeys(false, common.Address{}, false, genesisCfg, nodeKeys[0]) + defer chain.Stop() + + // Wait a bit so that the announce versions are generated for the engines + time.Sleep(10 * time.Second) + + announceVersion := engine.GetAnnounceVersion() + 10000 + wrk := engine.announceManager.Worker() + if err := wrk.UpdateVersionTo(announceVersion); err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + // Verify that enode certificate map is set via RetrieveEnodeCertificateMsgMap + enodeCertMsgs := engine.RetrieveEnodeCertificateMsgMap() + + // For a standalone validator, there should be only one entry in the enodeCert Map. + // TODO. Add test case for when the engine has proxies + selfEnode := engine.SelfNode() + enodeCertMsg := enodeCertMsgs[selfEnode.ID()] + + if enodeCertMsg == nil { + t.Errorf("unassigned enode certificate") + } + + msgPayload, _ := enodeCertMsg.Msg.Payload() + + // Verify the actual message + var msg istanbul.Message + // Decode payload into msg + err := msg.FromPayload(msgPayload, istanbul.GetSignatureAddress) + if err != nil { + t.Errorf("Error in decoding received Istanbul Enode Certificate message. Error: %v", err) + } + + // Verify the msg sender + if msg.Address != engine.Address() { + t.Errorf("Incorrect msg sender for enode cert msg. Want: %v, Have: %v", engine.Address(), msg.Address) + } + + var enodeCertificate istanbul.EnodeCertificate + if err := rlp.DecodeBytes(msg.Msg, &enodeCertificate); err != nil { + t.Errorf("Error in decoding received Istanbul Enode Certificate message content. Error: %v", err) + } + + if enodeCertificate.EnodeURL != selfEnode.URLv4() { + t.Errorf("Incorrect enodeURL in the enode certificate. Want: %s, Have: %s", selfEnode.URLv4(), enodeCertificate.EnodeURL) + } + + if enodeCertificate.Version != announceVersion { + t.Errorf("Incorrect version in the enode certificate. Want: %d, Have %d", announceVersion, enodeCertificate.Version) + } + + // Verify that the dest address for the enode cert is nil. + if enodeCertMsg.DestAddresses != nil { + t.Errorf("Enode cert dest addresses is not nil") + } + + engine.StopAnnouncing() +}
diff --git go-ethereum/consensus/istanbul/announce/address_provider.go celo/consensus/istanbul/announce/address_provider.go new file mode 100644 index 0000000000000000000000000000000000000000..8063a4b8e4a8410cbde5b2d54fa7ff9f2604f181 --- /dev/null +++ celo/consensus/istanbul/announce/address_provider.go @@ -0,0 +1,12 @@ +package announce + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// AddressProvider provides the different addresses the announce manager needs +type AddressProvider interface { + SelfNode() *enode.Node + ValidatorAddress() common.Address +}
diff --git go-ethereum/consensus/istanbul/proxy/proxied_validator_engine.go celo/consensus/istanbul/proxy/proxied_validator_engine.go new file mode 100644 index 0000000000000000000000000000000000000000..76a180f7df2e27689984d448dfca4eb13243d5b2 --- /dev/null +++ celo/consensus/istanbul/proxy/proxied_validator_engine.go @@ -0,0 +1,653 @@ +// Copyright 2017 The celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// BackendForProxiedValidatorEngine provides the Istanbul backend application specific functions for Istanbul proxied validator engine +type BackendForProxiedValidatorEngine interface { + // Address returns the validator's signing address + Address() common.Address + + // IsValidating returns true if this node is currently validating + IsValidating() bool + + // IsProxiedValidator returns true if this node is a proxied validator + IsProxiedValidator() bool + + // SelfNode returns the owner's node (if this is a proxy, it will return the external node) + SelfNode() *enode.Node + + // Sign signs input data with the validator's ecdsa signing key + Sign([]byte) ([]byte, error) + + // Multicast sends a message to it's connected nodes filtered on the 'addresses' parameter (where each address + // is associated with those node's signing key) + // If sendToSelf is set to true, then the function will send an event to self via a message event + Multicast(addresses []common.Address, payload []byte, ethMsgCode uint64, sendToSelf bool) error + + // Unicast will asynchronously send a celo message to peer + Unicast(peer consensus.Peer, payload []byte, ethMsgCode uint64) + + // GetValEnodeTableEntries retrieves the entries in the valEnodeTable filtered on the "validators" parameter. + // If the parameter is nil, then no filter will be applied. + GetValEnodeTableEntries(validators []common.Address) (map[common.Address]*istanbul.AddressEntry, error) + + // UpdateAnnounceVersion will notify the announce protocol that this validator's valEnodeTable entry has been updated + UpdateAnnounceVersion() + + // GetAnnounceVersion will retrieve the current node's announce version + GetAnnounceVersion() uint + + // RetrieveEnodeCertificateMsgMap will retrieve this node's handshake enodeCertificate + RetrieveEnodeCertificateMsgMap() map[enode.ID]*istanbul.EnodeCertMsg + + // RetrieveValidatorConnSet will retrieve the validator connection set. + RetrieveValidatorConnSet() (map[common.Address]bool, error) + + // AddPeer will add a static peer + AddPeer(node *enode.Node, purpose p2p.PurposeFlag) + + // RemovePeer will remove a static peer + RemovePeer(node *enode.Node, purpose p2p.PurposeFlag) + + // GetProxiedValidatorEngine returns the proxied validator engine created for this Backend. This should only be used for the unit tests. + GetProxiedValidatorEngine() ProxiedValidatorEngine +} + +type fwdMsgInfo struct { + destAddresses []common.Address + ethMsgCode uint64 + payload []byte +} + +type proxiedValidatorEngine struct { + config *istanbul.Config + logger log.Logger + backend BackendForProxiedValidatorEngine + + isRunning bool + isRunningMu sync.RWMutex + + loopWG sync.WaitGroup // + + quit chan struct{} // Used to notify to the thread to quit + + addProxies chan []*istanbul.ProxyConfig // Used to notify to the thread that new proxies have been added via command line or rpc api + removeProxies chan []*enode.Node // Used to notify to the thread that proxies have been removed via rpc api + + addProxyPeer chan consensus.Peer // Used to notify to the thread of newly peered proxies + removeProxyPeer chan consensus.Peer // Used to notify to the thread of newly disconnected proxy peers + + proxiedValThreadOpCh chan proxiedValThreadOpFunc // Used to submit operations to be executed on the thread's local state + proxiedValThreadOpDoneCh chan struct{} + + sendValEnodeShareMsgsCh chan struct{} // Used to notify the thread to send a val enode share message to all of the proxies + + sendEnodeCertsCh chan map[enode.ID]*istanbul.EnodeCertMsg // Used to notify the thread to send the enode certs to the appropriate proxy. + + sendFwdMsgsCh chan *fwdMsgInfo // Used to send a forward message to all of the proxies + + newBlockchainEpoch chan struct{} // Used to notify to the thread that a new blockchain epoch has started +} + +// proxiedValThreadOpFunc is a function type to define operations executed with run's local state as parameters. +type proxiedValThreadOpFunc func(ps *proxySet) + +// New creates a new proxied validator engine. +func NewProxiedValidatorEngine(backend BackendForProxiedValidatorEngine, config *istanbul.Config) (ProxiedValidatorEngine, error) { + if !backend.IsProxiedValidator() { + return nil, ErrNodeNotProxiedValidator + } + + pv := &proxiedValidatorEngine{ + config: config, + logger: log.New(), + backend: backend, + + addProxies: make(chan []*istanbul.ProxyConfig), + removeProxies: make(chan []*enode.Node), + addProxyPeer: make(chan consensus.Peer, 10), + removeProxyPeer: make(chan consensus.Peer, 10), + + proxiedValThreadOpCh: make(chan proxiedValThreadOpFunc), + proxiedValThreadOpDoneCh: make(chan struct{}), + + sendValEnodeShareMsgsCh: make(chan struct{}), + sendEnodeCertsCh: make(chan map[enode.ID]*istanbul.EnodeCertMsg), + sendFwdMsgsCh: make(chan *fwdMsgInfo), + newBlockchainEpoch: make(chan struct{}), + } + + return pv, nil +} + +func (pv *proxiedValidatorEngine) Start() error { + pv.isRunningMu.Lock() + defer pv.isRunningMu.Unlock() + + if pv.isRunning { + return istanbul.ErrStartedProxiedValidatorEngine + } + + pv.loopWG.Add(1) + pv.quit = make(chan struct{}) + go pv.threadRun() + + if len(pv.config.ProxyConfigs) > 0 { + select { + case pv.addProxies <- pv.config.ProxyConfigs: + case <-pv.quit: + return istanbul.ErrStartedProxiedValidatorEngine + } + } + + pv.isRunning = true + pv.logger.Info("Proxied validator engine started") + return nil +} + +func (pv *proxiedValidatorEngine) Stop() error { + pv.isRunningMu.Lock() + defer pv.isRunningMu.Unlock() + + if !pv.isRunning { + return istanbul.ErrStoppedProxiedValidatorEngine + } + + close(pv.quit) + pv.loopWG.Wait() + + pv.isRunning = false + pv.logger.Info("Proxy engine stopped") + return nil +} + +// Running will return true if the proxied validator engine in runnning, and false otherwise. +func (pv *proxiedValidatorEngine) Running() bool { + pv.isRunningMu.RLock() + defer pv.isRunningMu.RUnlock() + + return pv.isRunning +} + +// AddProxy will add a proxy config, connect to it's internal enodeURL, and assign it remote validators. +func (pv *proxiedValidatorEngine) AddProxy(node, externalNode *enode.Node) error { + if !pv.Running() { + return istanbul.ErrStoppedProxiedValidatorEngine + } + + select { + case pv.addProxies <- []*istanbul.ProxyConfig{{InternalNode: node, ExternalNode: externalNode}}: + return nil + case <-pv.quit: + return istanbul.ErrStoppedProxiedValidatorEngine + } +} + +// RemoveProxy will remove a proxy, disconnect from it, and reassign remote validators that were originally assigned to them. +func (pv *proxiedValidatorEngine) RemoveProxy(node *enode.Node) error { + if !pv.Running() { + return istanbul.ErrStoppedProxiedValidatorEngine + } + + select { + case pv.removeProxies <- []*enode.Node{node}: + return nil + case <-pv.quit: + return istanbul.ErrStoppedProxiedValidatorEngine + } +} + +func (pv *proxiedValidatorEngine) RegisterProxyPeer(proxyPeer consensus.Peer) error { + if !pv.Running() { + return istanbul.ErrStoppedProxiedValidatorEngine + } + + logger := pv.logger.New("func", "RegisterProxyPeer") + if proxyPeer.PurposeIsSet(p2p.ProxyPurpose) { + logger.Info("Got new proxy peer", "proxyPeer", proxyPeer) + select { + case pv.addProxyPeer <- proxyPeer: + case <-pv.quit: + return istanbul.ErrStoppedProxiedValidatorEngine + } + } else { + logger.Error("Unauthorized connected peer to the proxied validator", "peerID", proxyPeer.Node().ID()) + return errUnauthorizedProxiedValidator + } + + return nil +} + +func (pv *proxiedValidatorEngine) UnregisterProxyPeer(proxyPeer consensus.Peer) error { + if !pv.Running() { + return istanbul.ErrStoppedProxiedValidatorEngine + } + + if proxyPeer.PurposeIsSet(p2p.ProxyPurpose) { + select { + case pv.removeProxyPeer <- proxyPeer: + case <-pv.quit: + return istanbul.ErrStoppedProxiedValidatorEngine + } + } + + return nil +} + +// This function will return the remote validator to proxy assignments for the given remote validators. +// If the "validators" parameter is nil, then this function will return all of the validator assignments. +func (pv *proxiedValidatorEngine) GetValidatorProxyAssignments(validators []common.Address) (map[common.Address]*Proxy, error) { + if !pv.Running() { + return nil, istanbul.ErrStoppedProxiedValidatorEngine + } + + valAssignments := make(map[common.Address]*Proxy) + + select { + case pv.proxiedValThreadOpCh <- func(ps *proxySet) { + for address, proxy := range ps.getValidatorAssignments(validators, nil) { + valAssignments[address] = proxy + } + }: + <-pv.proxiedValThreadOpDoneCh + + case <-pv.quit: + return nil, istanbul.ErrStoppedProxiedValidatorEngine + + } + + return valAssignments, nil +} + +// This function will return all of the proxies and all the proxy to validator assignments. +func (pv *proxiedValidatorEngine) GetProxiesAndValAssignments() ([]*Proxy, map[enode.ID][]common.Address, error) { + var proxies []*Proxy + var valAssignments map[enode.ID][]common.Address + + if !pv.Running() { + return nil, nil, istanbul.ErrStoppedProxiedValidatorEngine + } + + select { + case pv.proxiedValThreadOpCh <- func(ps *proxySet) { + proxies, valAssignments = ps.getProxyAndValAssignments() + }: + <-pv.proxiedValThreadOpDoneCh + + case <-pv.quit: + return nil, nil, istanbul.ErrStoppedProxiedValidatorEngine + + } + + return proxies, valAssignments, nil +} + +// SendValEnodeShareMsgs will signal to the running thread to send a val enode share message to all of the proxies +func (pv *proxiedValidatorEngine) SendValEnodesShareMsgToAllProxies() error { + if !pv.Running() { + return istanbul.ErrStoppedProxiedValidatorEngine + } + + select { + case pv.sendValEnodeShareMsgsCh <- struct{}{}: + + case <-pv.quit: + return istanbul.ErrStoppedProxiedValidatorEngine + } + + return nil +} + +// SendEnodeCertsToAllProxies will signal to the running thread to share the given enode certs to the appropriate proxy +func (pv *proxiedValidatorEngine) SendEnodeCertsToAllProxies(enodeCerts map[enode.ID]*istanbul.EnodeCertMsg) error { + if !pv.Running() { + return istanbul.ErrStoppedProxiedValidatorEngine + } + + select { + case pv.sendEnodeCertsCh <- enodeCerts: + + case <-pv.quit: + return istanbul.ErrStoppedProxiedValidatorEngine + } + + return nil +} + +// SendForwardMsgToAllProxies will signal to the running thread to send a forward message to all proxies. +func (pv *proxiedValidatorEngine) SendForwardMsgToAllProxies(finalDestAddresses []common.Address, ethMsgCode uint64, payload []byte) error { + if !pv.Running() { + return istanbul.ErrStoppedProxiedValidatorEngine + } + + select { + case pv.sendFwdMsgsCh <- &fwdMsgInfo{destAddresses: finalDestAddresses, ethMsgCode: ethMsgCode, payload: payload}: + + case <-pv.quit: + return istanbul.ErrStoppedProxiedValidatorEngine + } + + return nil +} + +// NewEpoch will notify the proxied validator's thread that a new epoch started +func (pv *proxiedValidatorEngine) NewEpoch() error { + if !pv.Running() { + return istanbul.ErrStoppedProxiedValidatorEngine + } + + select { + case pv.newBlockchainEpoch <- struct{}{}: + + case <-pv.quit: + return istanbul.ErrStoppedProxiedValidatorEngine + } + + return nil +} + +// run handles changes to proxies and validator assignments +func (pv *proxiedValidatorEngine) threadRun() { + var ( + // The minimum allowable time that a proxy can be disconnected from the proxied validator + // After this expires, the proxy handler will remove any validator assignments from the proxy. + minProxyDisconnectTime time.Duration = 30 * time.Second + + // The duration of time between thread update, which are occasional check-ins to ensure proxy/validator assignments are as intended + schedulerPeriod time.Duration = 30 * time.Second + + // Used to keep track of proxies & validators the proxies are associated with + ps *proxySet = newProxySet(newConsistentHashingPolicy()) + ) + + logger := pv.logger.New("func", "threadRun") + + defer pv.loopWG.Done() + + schedulerTicker := time.NewTicker(schedulerPeriod) + defer schedulerTicker.Stop() + + pv.updateValidatorAssignments(ps) + +loop: + for { + select { + case <-pv.quit: + // The proxied validator engine was stopped + break loop + + case addProxyNodes := <-pv.addProxies: + // Got command to add proxy nodes. + // Add any unseen proxies to the proxy set and add p2p static connections to them. + for _, proxyNode := range addProxyNodes { + proxyID := proxyNode.InternalNode.ID() + if ps.getProxy(proxyID) != nil { + logger.Debug("Proxy is already in the proxy set", "proxyNode", proxyNode, "proxyID", proxyID, "chan", "addProxies") + continue + } + log.Info("Adding proxy node", "proxyNode", proxyNode, "proxyID", proxyID) + ps.addProxy(proxyNode) + pv.backend.AddPeer(proxyNode.InternalNode, p2p.ProxyPurpose) + } + + case rmProxyNodes := <-pv.removeProxies: + // Got command to remove proxy nodes. + // Remove the proxy and remove the p2p static connection + for _, proxyNode := range rmProxyNodes { + proxyID := proxyNode.ID() + proxy := ps.getProxy(proxyID) + if proxy == nil { + logger.Warn("Proxy is not in the proxy set", "proxy", proxyNode, "proxyID", proxyID, "chan", "removeProxies") + continue + } + + logger.Info("Removing proxy node", "proxy", proxy.String(), "chan", "removeProxies") + + // If the removed proxy is connected, instruct it to disconnect any of it's validator connections + // by sending a val enode share message with an empty set. + if proxy.peer != nil { + pv.sendValEnodesShareMsg(proxy.peer, []common.Address{}) + } + + if valsReassigned := ps.removeProxy(proxyID); valsReassigned { + logger.Info("Remote validator to proxy assignment has changed. Sending val enode share messages and updating announce version") + pv.backend.UpdateAnnounceVersion() + pv.sendValEnodeShareMsgs(ps) + } + pv.backend.RemovePeer(proxy.node, p2p.ProxyPurpose) + } + + case connectedPeer := <-pv.addProxyPeer: + // Proxied peer just connected. + // Set the corresponding proxyInfo's peer + peerNode := connectedPeer.Node() + peerID := peerNode.ID() + proxy := ps.getProxy(peerID) + if proxy != nil { + logger.Debug("Connected proxy", "proxy", proxy.String(), "chan", "addProxyPeer") + if valsReassigned := ps.setProxyPeer(peerID, connectedPeer); valsReassigned { + logger.Info("Remote validator to proxy assignment has changed. Sending val enode share messages and updating announce version") + pv.backend.UpdateAnnounceVersion() + pv.sendValEnodeShareMsgs(ps) + } + } + + case disconnectedPeer := <-pv.removeProxyPeer: + // Proxied peer just disconnected. + peerID := disconnectedPeer.Node().ID() + if ps.getProxy(peerID) != nil { + logger.Debug("Disconnected proxy peer", "peerID", peerID, "chan", "removeProxyPeer") + ps.removeProxyPeer(peerID) + } + + case proxyHandlerOp := <-pv.proxiedValThreadOpCh: + proxyHandlerOp(ps) + pv.proxiedValThreadOpDoneCh <- struct{}{} + + case <-pv.newBlockchainEpoch: + // New blockchain epoch. Update the validators in the proxySet + valsReassigned, error := pv.updateValidatorAssignments(ps) + if error != nil { + logger.Warn("Error in updating validator assignments on new epoch", "error", error) + } + if valsReassigned { + pv.backend.UpdateAnnounceVersion() + pv.sendValEnodeShareMsgs(ps) + } + + case <-pv.sendValEnodeShareMsgsCh: + pv.sendValEnodeShareMsgs(ps) + + case enodeCerts := <-pv.sendEnodeCertsCh: + pv.sendEnodeCerts(ps, enodeCerts) + + case fwdMsg := <-pv.sendFwdMsgsCh: + pv.sendForwardMsg(ps, fwdMsg.destAddresses, fwdMsg.ethMsgCode, fwdMsg.payload) + + case <-schedulerTicker.C: + logger.Trace("schedulerTicker ticked") + + // Remove validator assignement for proxies that are disconnected for a minimum of `minProxyDisconnectTime` seconds. + // The reason for not immediately removing the validator asssignments is so that if there is a + // network disconnect then a quick reconnect, the validator assignments wouldn't be changed. + // If no reassignments were made, then resend all enode certificates and val enode share messages to the + // proxies, in case previous attempts failed. + if valsReassigned := ps.unassignDisconnectedProxies(minProxyDisconnectTime); valsReassigned { + pv.backend.UpdateAnnounceVersion() + pv.sendValEnodeShareMsgs(ps) + } else { + // Send out the val enode share message. We will resend the valenodeshare message here in case it was + // never successfully sent before. + pv.sendValEnodeShareMsgs(ps) + + // Also resend the enode certificates to the proxies (via a forward message), in case it was + // never successfully sent before. + + // Get all the enode certificate messages + proxyEnodeCertMsgs := pv.backend.RetrieveEnodeCertificateMsgMap() + + // Share the enode certs with the proxies + pv.sendEnodeCerts(ps, proxyEnodeCertMsgs) + } + } + } +} + +// sendValEnodeShareMsgs sends a ValEnodeShare Message to each proxy to update the proxie's validator enode table. +// This is a no-op for replica validators. +func (pv *proxiedValidatorEngine) sendValEnodeShareMsgs(ps *proxySet) { + logger := pv.logger.New("func", "sendValEnodeShareMsgs") + + for _, proxy := range ps.proxiesByID { + if proxy.peer != nil { + assignedValidators := ps.getValidatorAssignments(nil, []enode.ID{proxy.ID()}) + valAddresses := make([]common.Address, 0, len(assignedValidators)) + for valAddress := range assignedValidators { + valAddresses = append(valAddresses, valAddress) + } + logger.Info("Sending val enode share msg to proxy", "proxy peer", proxy.peer, "valAddresses length", len(valAddresses)) + logger.Trace("Sending val enode share msg to proxy with validator addresses", "valAddresses", common.ConvertToStringSlice(valAddresses)) + pv.sendValEnodesShareMsg(proxy.peer, valAddresses) + } + } +} + +// sendEnodeCerts will send the appropriate enode certificate to the proxies. +// This is a no-op for replica validators. +func (pv *proxiedValidatorEngine) sendEnodeCerts(ps *proxySet, enodeCerts map[enode.ID]*istanbul.EnodeCertMsg) { + logger := pv.logger.New("func", "sendEnodeCerts") + if !pv.backend.IsValidating() { + logger.Trace("Skipping sending EnodeCerts to proxies b/c not validating") + return + } + + for proxyID, proxy := range ps.proxiesByID { + if proxy.peer != nil && enodeCerts[proxyID] != nil { + // Generate message payload. Note that these enode certs are already signed by the validator + payload, err := enodeCerts[proxyID].Msg.Payload() + if err != nil { + logger.Error("Error getting payload of enode certificate message", "err", err, "proxyID", proxyID) + } + + logger.Info("Sharing enode certificate to proxy", "proxy peer", proxy.peer, "proxyID", proxyID) + pv.backend.Unicast(proxy.peer, payload, istanbul.EnodeCertificateMsg) + } + } + +} + +// updateValidatorAssignments will retrieve find the validator conn set diff between the current validator +// conn set and the proxy set's validator conn set, and apply any diff to the proxy set. +func (pv *proxiedValidatorEngine) updateValidatorAssignments(ps *proxySet) (bool, error) { + newVals, rmVals, err := pv.getValidatorConnSetDiff(ps.getValidators()) + log.Trace("Proxy Handler updating validators", "newVals", common.ConvertToStringSlice(newVals), "rmVals", common.ConvertToStringSlice(rmVals), "err", err, "func", "updateValiadtors") + if err != nil { + return false, err + } + + valsReassigned := false + if len(newVals) > 0 { + valsReassigned = valsReassigned || ps.addRemoteValidators(newVals) + } + + if len(rmVals) > 0 { + valsReassigned = valsReassigned || ps.removeRemoteValidators(rmVals) + } + + return valsReassigned, nil +} + +// This function will return a diff between the current Validator Connection set and the `validators` parameter. +func (pv *proxiedValidatorEngine) getValidatorConnSetDiff(validators []common.Address) (newVals []common.Address, rmVals []common.Address, err error) { + logger := pv.logger.New("func", "getValidatorConnSetDiff") + + logger.Trace("Proxied validator engine retrieving validator connection set diff", "validators", common.ConvertToStringSlice(validators)) + + // Get the set of active and registered validators + newValConnSet, err := pv.backend.RetrieveValidatorConnSet() + if err != nil { + logger.Warn("Proxy Handler couldn't get the validator connection set", "err", err) + return nil, nil, err + } + + // Don't add this validator's address to the returned new validator set + delete(newValConnSet, pv.backend.Address()) + + outputNewValConnSet := make([]common.Address, 0, len(newValConnSet)) + for newVal := range newValConnSet { + outputNewValConnSet = append(outputNewValConnSet, newVal) + } + logger.Trace("retrieved validator connset", "valConnSet", common.ConvertToStringSlice(outputNewValConnSet)) + + rmVals = make([]common.Address, 0) // There is a good chance that there will be no diff, so set size to 0 + + // First find all old val entries that are not in the newValConnSet (which will be the removed validator set), + // and find all the same val entries and remove them from the newValConnSet. + for _, oldVal := range validators { + if !newValConnSet[oldVal] { + rmVals = append(rmVals, oldVal) + } else { + delete(newValConnSet, oldVal) + } + } + + // Whatever is remaining in the newValConnSet is the new validator set. + newVals = make([]common.Address, 0, len(newValConnSet)) + for newVal := range newValConnSet { + newVals = append(newVals, newVal) + } + + logger.Trace("returned diff", "newVals", common.ConvertToStringSlice(newVals), "rmVals", common.ConvertToStringSlice(rmVals)) + + return newVals, rmVals, nil +} + +func (pv *proxiedValidatorEngine) IsProxyPeer(peerID enode.ID) (bool, error) { + proxy, err := pv.getProxy(peerID) + if err != nil { + return false, err + } + + return proxy != nil, nil +} + +func (pv *proxiedValidatorEngine) getProxy(peerID enode.ID) (*Proxy, error) { + proxies, _, err := pv.GetProxiesAndValAssignments() + if err != nil { + return nil, err + } + + for _, proxy := range proxies { + if proxy.peer != nil && proxy.peer.Node().ID() == peerID { + return proxy, nil + } + } + + return nil, nil +}
diff --git go-ethereum/consensus/istanbul/proxy/proxy_set.go celo/consensus/istanbul/proxy/proxy_set.go new file mode 100644 index 0000000000000000000000000000000000000000..8457a4c2e09ae4225c512e1cc04186f2a3ae870b --- /dev/null +++ celo/consensus/istanbul/proxy/proxy_set.go @@ -0,0 +1,222 @@ +// Copyright 2017 The celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// proxySet defines the set of proxies that the validator is aware of and +// validator/proxy assignments. +// WARNING: None of this object's functions are threadsafe, so it's +// the user's responsibility to ensure that. +type proxySet struct { + proxiesByID map[enode.ID]*Proxy // all proxies known by this node, whether or not they are peered + valAssignments *valAssignments // the mappings of proxy<->remote validators + valAssigner assignmentPolicy // used for assigning peered proxies with remote validators + logger log.Logger +} + +func newProxySet(assignmentPolicy assignmentPolicy) *proxySet { + return &proxySet{ + proxiesByID: make(map[enode.ID]*Proxy), + valAssignments: newValAssignments(), + valAssigner: assignmentPolicy, + logger: log.New(), + } +} + +// addProxy adds a proxy to the proxySet if it does not exist. +// The valAssigner is not made aware of the proxy until after the proxy +// is peered with. +func (ps *proxySet) addProxy(newProxy *istanbul.ProxyConfig) { + logger := ps.logger.New("func", "addProxy") + logger.Trace("Adding proxy to the proxy set", "new proxy internal ID", newProxy.InternalNode.String(), "new proxy external ID", newProxy.ExternalNode.String()) + internalID := newProxy.InternalNode.ID() + if ps.proxiesByID[internalID] == nil { + ps.proxiesByID[internalID] = &Proxy{ + node: newProxy.InternalNode, + externalNode: newProxy.ExternalNode, + peer: nil, + disconnectTS: time.Now(), + } + } else { + logger.Warn("Cannot add proxy, since a proxy with the same internal enode ID exists already") + } +} + +// getProxy returns the proxy in the proxySet with ID proxyID +func (ps *proxySet) getProxy(proxyID enode.ID) *Proxy { + proxy, ok := ps.proxiesByID[proxyID] + if ok { + return proxy + } + return nil +} + +// removeProxy removes a proxy with ID proxyID from the proxySet and valAssigner. +// Will return true if any of the validators got reassigned to a different proxy. +func (ps *proxySet) removeProxy(proxyID enode.ID) bool { + proxy := ps.getProxy(proxyID) + if proxy == nil { + return false + } + valsReassigned := ps.valAssigner.removeProxy(proxy, ps.valAssignments) + delete(ps.proxiesByID, proxyID) + return valsReassigned +} + +// setProxyPeer sets the peer for a proxy with enode ID proxyID. +// Since this proxy is now connected tto the proxied validator, it +// can now be assigned remote validators. +func (ps *proxySet) setProxyPeer(proxyID enode.ID, peer consensus.Peer) bool { + logger := ps.logger.New("func", "setProxyPeer") + logger.Trace("Setting proxy peer for proxy set", "proxyID", proxyID) + proxy := ps.proxiesByID[proxyID] + valsReassigned := false + if proxy != nil { + proxy.peer = peer + logger.Trace("Assigning validators to proxy", "proxyID", proxyID) + valsReassigned = ps.valAssigner.assignProxy(proxy, ps.valAssignments) + } + + return valsReassigned +} + +// removeProxyPeer sets the peer for a proxy with ID proxyID to nil. +func (ps *proxySet) removeProxyPeer(proxyID enode.ID) { + proxy := ps.proxiesByID[proxyID] + if proxy != nil { + proxy.peer = nil + proxy.disconnectTS = time.Now() + } +} + +// addRemoteValidators adds remote validators to be assigned by the valAssigner +func (ps *proxySet) addRemoteValidators(validators []common.Address) bool { + ps.logger.Trace("adding remote validators to the proxy set", "validators", common.ConvertToStringSlice(validators)) + return ps.valAssigner.assignRemoteValidators(validators, ps.valAssignments) +} + +// removeRemoteValidators removes remote validators from the validator assignments +func (ps *proxySet) removeRemoteValidators(validators []common.Address) bool { + ps.logger.Trace("removing remote validators from the proxy set", "validators", common.ConvertToStringSlice(validators)) + return ps.valAssigner.removeRemoteValidators(validators, ps.valAssignments) +} + +// getValidatorAssignments returns the validator assignments for the given set of validators filtered on +// the parameters `validators` AND `proxies`. If either or both of them or nil, then that means that there is no +// filter for that respective dimension. +func (ps *proxySet) getValidatorAssignments(validators []common.Address, proxyIDs []enode.ID) map[common.Address]*Proxy { + // First get temp set based on proxies filter + var tempValAssignmentsFromProxies map[common.Address]*enode.ID + + if proxyIDs != nil { + tempValAssignmentsFromProxies = make(map[common.Address]*enode.ID) + for _, proxyID := range proxyIDs { + if proxyValSet, ok := ps.valAssignments.proxyToVals[proxyID]; ok { + for valAddress := range proxyValSet { + tempValAssignmentsFromProxies[valAddress] = &proxyID + } + } + } + } else { + tempValAssignmentsFromProxies = ps.valAssignments.valToProxy + } + + // Now get temp set based on validators filter + var tempValAssignmentsFromValidators map[common.Address]*enode.ID + + if validators != nil { + tempValAssignmentsFromValidators = make(map[common.Address]*enode.ID) + for _, valAddress := range validators { + if enodeID, ok := ps.valAssignments.valToProxy[valAddress]; ok && enodeID != nil { + tempValAssignmentsFromValidators[valAddress] = enodeID + } + } + } else { + tempValAssignmentsFromValidators = ps.valAssignments.valToProxy + } + + // Now do an intersection between the two temporary maps. + // TODO: An optimization that can be done is to loop over the temporary map + // that is smaller. + valAssignments := make(map[common.Address]*Proxy) + + for outerValAddress := range tempValAssignmentsFromProxies { + if enodeID, ok := tempValAssignmentsFromValidators[outerValAddress]; ok { + if enodeID != nil { + proxy := ps.getProxy(*enodeID) + if proxy.peer != nil { + valAssignments[outerValAddress] = proxy + } + } else { + valAssignments[outerValAddress] = nil + } + } + } + + return valAssignments +} + +// unassignDisconnectedProxies unassigns proxies that have been disconnected for +// at least minAge ago +func (ps *proxySet) unassignDisconnectedProxies(minAge time.Duration) bool { + logger := ps.logger.New("func", "unassignDisconnectedProxies") + valsReassigned := false + for proxyID := range ps.valAssignments.proxyToVals { + proxy := ps.getProxy(proxyID) + if proxy != nil && proxy.peer == nil && time.Since(proxy.disconnectTS) >= minAge { + logger.Debug("Unassigning disconnected proxy", "proxy", proxy.String()) + valsReassigned = ps.valAssigner.removeProxy(proxy, ps.valAssignments) || valsReassigned + } + } + + return valsReassigned +} + +// getValidators returns all validators that are known by the proxy set +func (ps *proxySet) getValidators() []common.Address { + return ps.valAssignments.getValidators() +} + +// getProxyAndValAssignments returns the proxies that are added to the proxied validators +// and the remote validator assignments +func (ps *proxySet) getProxyAndValAssignments() ([]*Proxy, map[enode.ID][]common.Address) { + proxies := make([]*Proxy, 0, len(ps.proxiesByID)) + valAssignments := make(map[enode.ID][]common.Address) + + for proxyID, proxy := range ps.proxiesByID { + proxies = append(proxies, proxy) + assignedVals := ps.getValidatorAssignments(nil, []enode.ID{proxyID}) + assignedValsArray := make([]common.Address, 0, len(assignedVals)) + + for val := range assignedVals { + assignedValsArray = append(assignedValsArray, val) + } + + valAssignments[proxyID] = assignedValsArray + } + + return proxies, valAssignments +}
diff --git go-ethereum/consensus/istanbul/backend/engine_test.go celo/consensus/istanbul/backend/engine_test.go new file mode 100644 index 0000000000000000000000000000000000000000..cb6c715335a3943199307a73fac401fbed5699ef --- /dev/null +++ celo/consensus/istanbul/backend/engine_test.go @@ -0,0 +1,475 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "bytes" + "math/big" + "testing" + "time" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/core" + bccore "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + . "github.com/onsi/gomega" +) + +func stopEngine(engine *Backend) { + engine.StopValidating() + engine.StopAnnouncing() +} + +func TestPrepare(t *testing.T) { + g := NewGomegaWithT(t) + + chain, engine := newBlockChain(1, true) + defer stopEngine(engine) + defer chain.Stop() + header := makeHeader(chain.Genesis(), engine.config) + err := engine.Prepare(chain, header) + g.Expect(err).ToNot(HaveOccurred()) + + header.ParentHash = common.BytesToHash([]byte("1234567890")) + err = engine.Prepare(chain, header) + g.Expect(err).To(BeIdenticalTo(consensus.ErrUnknownAncestor)) +} + +func TestMakeBlockWithSignature(t *testing.T) { + g := NewGomegaWithT(t) + + numValidators := 1 + genesisCfg, nodeKeys := getGenesisAndKeys(numValidators, true) + chain, engine, _ := newBlockChainWithKeys(false, common.Address{}, false, genesisCfg, nodeKeys[0]) + + defer stopEngine(engine) + defer chain.Stop() + genesis := chain.Genesis() + + block, err := makeBlock(nodeKeys, chain, engine, genesis) + g.Expect(err).ToNot(HaveOccurred()) + + block2, err := makeBlock(nodeKeys, chain, engine, block) + g.Expect(err).ToNot(HaveOccurred()) + + _, err = makeBlock(nodeKeys, chain, engine, block2) + g.Expect(err).ToNot(HaveOccurred()) +} + +func TestSealCommitted(t *testing.T) { + chain, engine := newBlockChain(1, true) + defer stopEngine(engine) + defer chain.Stop() + // In normal case, the StateProcessResult should be passed into Commit + engine.abortCommitHook = func(result *core.StateProcessResult) bool { return result == nil } + + block := makeBlockWithoutSeal(chain, engine, chain.Genesis()) + expectedBlock, _ := engine.signBlock(block) + + go func() { + if err := engine.Seal(chain, block); err != nil { + t.Errorf("Failed to seal the block: %v", err) + } + }() + + newHeadCh := make(chan bccore.ChainHeadEvent, 10) + sub := chain.SubscribeChainHeadEvent(newHeadCh) + defer sub.Unsubscribe() + + select { + case newHead := <-newHeadCh: + if newHead.Block.Hash() != expectedBlock.Hash() { + t.Errorf("Expected result block hash of %v, but got %v", expectedBlock.Hash(), newHead.Block.Hash()) + } + case <-time.After(1 * time.Second): + t.Fatal("Timed out when waiting for a new block") + } +} + +func TestVerifyHeader(t *testing.T) { + g := NewGomegaWithT(t) + chain, engine := newBlockChain(1, true) + defer stopEngine(engine) + defer chain.Stop() + + // errEmptyAggregatedSeal case + block := makeBlockWithoutSeal(chain, engine, chain.Genesis()) + block, _ = engine.signBlock(block) + err := engine.VerifyHeader(chain, block.Header(), false) + g.Expect(err).Should(BeIdenticalTo(errEmptyAggregatedSeal)) + + // short extra data + header := block.Header() + header.Extra = []byte{} + err = engine.VerifyHeader(chain, header, false) + g.Expect(err).Should(BeIdenticalTo(errInvalidExtraDataFormat)) + + // incorrect extra format + header.Extra = []byte("0000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000000") + err = engine.VerifyHeader(chain, header, false) + g.Expect(err).Should(BeIdenticalTo(errInvalidExtraDataFormat)) + + // invalid timestamp + block = makeBlockWithoutSeal(chain, engine, chain.Genesis()) + header = block.Header() + header.Time = chain.Genesis().Time() + engine.config.BlockPeriod - 1 + err = engine.VerifyHeader(chain, header, false) + g.Expect(err).Should(BeIdenticalTo(errInvalidTimestamp)) + + // future block + block = makeBlockWithoutSeal(chain, engine, chain.Genesis()) + header = block.Header() + header.Time = uint64(now().Unix() + 10) + err = engine.VerifyHeader(chain, header, false) + g.Expect(err).Should(BeIdenticalTo(consensus.ErrFutureBlock)) +} + +func TestVerifySeal(t *testing.T) { + g := NewGomegaWithT(t) + numValidators := 1 + genesisCfg, nodeKeys := getGenesisAndKeys(numValidators, true) + chain, engine, _ := newBlockChainWithKeys(false, common.Address{}, false, genesisCfg, nodeKeys[0]) + defer stopEngine(engine) + defer chain.Stop() + + genesis := chain.Genesis() + + // cannot verify genesis + err := engine.VerifySeal(genesis.Header()) + g.Expect(err).Should(BeIdenticalTo(errUnknownBlock)) + + // should verify + block, err := makeBlock(nodeKeys, chain, engine, genesis) + g.Expect(err).ToNot(HaveOccurred()) + header := block.Header() + err = engine.VerifySeal(header) + g.Expect(err).ToNot(HaveOccurred()) + + // change header content and expect to invalidate signature + header.Number = big.NewInt(4) + err = engine.VerifySeal(header) + g.Expect(err).Should(BeIdenticalTo(errInvalidSignature)) + + // delete istanbul extra data and expect invalid extra data format + header = block.Header() + header.Extra = nil + err = engine.VerifySeal(header) + g.Expect(err).Should(BeIdenticalTo(errInvalidExtraDataFormat)) + + // modify seal bitmap and expect to fail the quorum check + header = block.Header() + extra, err := header.IstanbulExtra() + g.Expect(err).ToNot(HaveOccurred()) + extra.AggregatedSeal.Bitmap = big.NewInt(0) + encoded, err := rlp.EncodeToBytes(extra) + g.Expect(err).ToNot(HaveOccurred()) + header.Extra = append(header.Extra[:types.IstanbulExtraVanity], encoded...) + err = engine.VerifySeal(header) + g.Expect(err).Should(BeIdenticalTo(errInsufficientSeals)) + + // verifiy the seal on the unmodified block. + err = engine.VerifySeal(block.Header()) + g.Expect(err).ToNot(HaveOccurred()) +} + +func TestVerifyHeaders(t *testing.T) { + numValidators := 1 + genesisCfg, nodeKeys := getGenesisAndKeys(numValidators, true) + chain, engine, _ := newBlockChainWithKeys(false, common.Address{}, false, genesisCfg, nodeKeys[0]) + defer stopEngine(engine) + defer chain.Stop() + genesis := chain.Genesis() + + // success case + headers := []*types.Header{} + blocks := []*types.Block{} + size := 10 + + // generate blocks + for i := 0; i < size; i++ { + var b *types.Block + if i == 0 { + b, _ = makeBlock(nodeKeys, chain, engine, genesis) + } else { + b, _ = makeBlock(nodeKeys, chain, engine, blocks[i-1]) + } + + blocks = append(blocks, b) + headers = append(headers, blocks[i].Header()) + } + + // mock istanbul now() function + now = func() time.Time { + return time.Unix(int64(headers[size-1].Time), 0) + } + + t.Run("Success case", func(t *testing.T) { + _, results := engine.VerifyHeaders(chain, headers, nil) + + timeout := time.NewTimer(2 * time.Second) + index := 0 + OUT1: + for { + select { + case err := <-results: + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + break OUT1 + } + index++ + if index == size { + break OUT1 + } + case <-timeout.C: + break OUT1 + } + } + }) + + t.Run("Abort case", func(t *testing.T) { + // abort cases + abort, results := engine.VerifyHeaders(chain, headers, nil) + timeout := time.NewTimer(2 * time.Second) + + index := 0 + OUT: + for { + select { + case err := <-results: + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + break OUT + } + index++ + if index == 1 { + abort <- struct{}{} + } + if index >= size { + t.Errorf("verifyheaders should be aborted") + break OUT + } + case <-timeout.C: + break OUT + } + } + }) + + t.Run("Error Header cases", func(t *testing.T) { + // error header cases + headers[2].Number = big.NewInt(100) + _, results := engine.VerifyHeaders(chain, headers, nil) + timeout := time.NewTimer(2 * time.Second) + index := 0 + errors := 0 + expectedErrors := 8 + OUT3: + for { + select { + case err := <-results: + if err != nil { + errors++ + } + index++ + if index == size { + if errors != expectedErrors { + t.Errorf("error mismatch: have %v, want %v", errors, expectedErrors) + } + break OUT3 + } + case <-timeout.C: + break OUT3 + } + } + }) +} + +func TestVerifyHeaderWithoutFullChain(t *testing.T) { + chain, engine := newBlockChain(1, false) + defer stopEngine(engine) + defer chain.Stop() + + t.Run("should allow future block without full chain available", func(t *testing.T) { + g := NewGomegaWithT(t) + block := makeBlockWithoutSeal(chain, engine, chain.Genesis()) + header := block.Header() + header.Time = uint64(now().Unix() + 3) + err := engine.VerifyHeader(chain, header, false) + g.Expect(err).To(BeIdenticalTo(errEmptyAggregatedSeal)) + }) + + t.Run("should reject future block without full chain available", func(t *testing.T) { + g := NewGomegaWithT(t) + block := makeBlockWithoutSeal(chain, engine, chain.Genesis()) + header := block.Header() + header.Time = uint64(now().Unix() + 10) + err := engine.VerifyHeader(chain, header, false) + g.Expect(err).To(BeIdenticalTo(consensus.ErrFutureBlock)) + }) +} + +func TestPrepareExtra(t *testing.T) { + g := NewGomegaWithT(t) + + oldValidators := []istanbul.ValidatorData{ + {Address: common.HexToAddress("0x44add0ec310f115a0e603b2d7db9f067778eaf8a")}, + {Address: common.HexToAddress("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212")}, + } + + newValidators := []istanbul.ValidatorData{ + {Address: common.HexToAddress("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6")}, + {Address: common.HexToAddress("0x8be76812f765c24641ec63dc2852b378aba2b440")}, + } + + extra, err := rlp.EncodeToBytes(&types.IstanbulExtra{ + AddedValidators: []common.Address{}, + AddedValidatorsPublicKeys: []blscrypto.SerializedPublicKey{}, + RemovedValidators: big.NewInt(0), + Seal: []byte{}, + AggregatedSeal: types.IstanbulAggregatedSeal{}, + ParentAggregatedSeal: types.IstanbulAggregatedSeal{}, + }) + g.Expect(err).ToNot(HaveOccurred()) + + h := &types.Header{ + Extra: append(make([]byte, types.IstanbulExtraVanity), extra...), + } + + err = writeValidatorSetDiff(h, oldValidators, newValidators) + g.Expect(err).ToNot(HaveOccurred()) + + // the header must have the updated extra data + updatedExtra, err := h.IstanbulExtra() + g.Expect(err).ToNot(HaveOccurred()) + + var updatedExtraVals []istanbul.ValidatorData + for i := range updatedExtra.AddedValidators { + updatedExtraVals = append(updatedExtraVals, istanbul.ValidatorData{ + Address: updatedExtra.AddedValidators[i], + BLSPublicKey: updatedExtra.AddedValidatorsPublicKeys[i], + }) + } + + g.Expect(updatedExtraVals).To(Equal(newValidators), "validators were not properly updated") + + // the validators which were removed were 2, so the bitmap is 11, meaning it should be 3 + g.Expect(updatedExtra.RemovedValidators.Int64()).To(Equal(int64(3))) +} + +func TestWriteSeal(t *testing.T) { + g := NewGomegaWithT(t) + + vanity := bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity) + istExtra := &types.IstanbulExtra{ + AddedValidators: []common.Address{ + common.HexToAddress("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6"), + common.HexToAddress("0x8be76812f765c24641ec63dc2852b378aba2b440"), + }, + AddedValidatorsPublicKeys: []blscrypto.SerializedPublicKey{}, + RemovedValidators: big.NewInt(12), // 1100, remove third and fourth validators + Seal: []byte{}, + AggregatedSeal: types.IstanbulAggregatedSeal{Bitmap: big.NewInt(0), Signature: []byte{}, Round: big.NewInt(0)}, + ParentAggregatedSeal: types.IstanbulAggregatedSeal{Bitmap: big.NewInt(0), Signature: []byte{}, Round: big.NewInt(0)}, + } + istExtraRaw, err := rlp.EncodeToBytes(&istExtra) + g.Expect(err).ToNot(HaveOccurred()) + + expectedSeal := hexutil.MustDecode("0x29fe2612266a3965321c23a2e0382cd819e992f293d9a0032439728e41201d2c387cc9de5914a734873d79addb76c59ce73c1085a98b968384811b4ad050dddc56") + g.Expect(expectedSeal).To(HaveLen(types.IstanbulExtraSeal), "incorrect length for seal") + + expectedIstExtra := istExtra + expectedIstExtra.Seal = expectedSeal + + h := &types.Header{ + Extra: append(vanity, istExtraRaw...), + } + + // normal case + err = writeSeal(h, expectedSeal) + g.Expect(err).NotTo(HaveOccurred()) + + // verify istanbul extra-data + actualIstExtra, err := h.IstanbulExtra() + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(actualIstExtra).To(Equal(expectedIstExtra)) + + // invalid seal + unexpectedSeal := append(expectedSeal, make([]byte, 1)...) + err = writeSeal(h, unexpectedSeal) + g.Expect(err).To(BeIdenticalTo(errInvalidSignature)) +} + +func TestWriteAggregatedSeal(t *testing.T) { + g := NewGomegaWithT(t) + + vanity := bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity) + istExtra := &types.IstanbulExtra{ + AddedValidators: []common.Address{ + common.HexToAddress("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6"), + common.HexToAddress("0x8be76812f765c24641ec63dc2852b378aba2b440"), + }, + AddedValidatorsPublicKeys: []blscrypto.SerializedPublicKey{}, + RemovedValidators: big.NewInt(12), // 1100, remove third and fourth validators + Seal: []byte{}, + AggregatedSeal: types.IstanbulAggregatedSeal{}, + ParentAggregatedSeal: types.IstanbulAggregatedSeal{}, + } + istExtraRaw, err := rlp.EncodeToBytes(&istExtra) + g.Expect(err).NotTo(HaveOccurred()) + + aggregatedSeal := types.IstanbulAggregatedSeal{ + Round: big.NewInt(2), + Bitmap: big.NewInt(3), + Signature: append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraBlsSignature-3)...), + } + + expectedIstExtra := istExtra + expectedIstExtra.AggregatedSeal = aggregatedSeal + expectedIstExtra.ParentAggregatedSeal = aggregatedSeal + + h := &types.Header{ + Extra: append(vanity, istExtraRaw...), + } + + // normal case + err = writeAggregatedSeal(h, aggregatedSeal, false) + g.Expect(err).NotTo(HaveOccurred()) + + err = writeAggregatedSeal(h, aggregatedSeal, true) + g.Expect(err).NotTo(HaveOccurred()) + + // verify istanbul extra-data + actualIstExtra, err := h.IstanbulExtra() + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(actualIstExtra).To(Equal(expectedIstExtra)) + + // try to write an invalid length seal to the CommitedSeal or ParentCommit field + invalidAggregatedSeal := types.IstanbulAggregatedSeal{ + Round: big.NewInt(3), + Bitmap: big.NewInt(3), + Signature: append(aggregatedSeal.Signature, make([]byte, 1)...), + } + err = writeAggregatedSeal(h, invalidAggregatedSeal, false) + g.Expect(err).To(BeIdenticalTo(errInvalidAggregatedSeal)) + + err = writeAggregatedSeal(h, invalidAggregatedSeal, true) + g.Expect(err).To(BeIdenticalTo(errInvalidAggregatedSeal)) +}
diff --git go-ethereum/consensus/istanbul/announce/outvc_processor.go celo/consensus/istanbul/announce/outvc_processor.go new file mode 100644 index 0000000000000000000000000000000000000000..b7adcf6ca79814926cf0bdb6433e8ef5c7b9f8ac --- /dev/null +++ celo/consensus/istanbul/announce/outvc_processor.go @@ -0,0 +1,85 @@ +package announce + +import ( + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" +) + +type OutboundVersionCertificateProcessor interface { + Process(*AnnounceState, []*istanbul.VersionCertificate, common.Address) error +} + +func NewOutboundVCProcessor(checker ValidatorChecker, + addrProvider AddressProvider, + vcGossiper VersionCertificateGossiper) OutboundVersionCertificateProcessor { + return &ovcp{ + logger: log.New("module", "OutboundVersionCertificateProcessor"), + checker: checker, + addrProvider: addrProvider, + vcGossiper: vcGossiper, + } +} + +type ovcp struct { + logger log.Logger + checker ValidatorChecker + addrProvider AddressProvider + vcGossiper VersionCertificateGossiper +} + +func (o *ovcp) Process(state *AnnounceState, versionCertificates []*istanbul.VersionCertificate, selfEcdsaAddress common.Address) error { + shouldProcess, err := o.checker.IsElectedOrNearValidator() + if err != nil { + o.logger.Warn("Error in checking if should process queryEnode", err) + } + + if shouldProcess { + // Update entries in val enode db + var valEnodeEntries []*istanbul.AddressEntry + for _, entry := range versionCertificates { + // Don't add ourselves into the val enode table + if entry.Address() == selfEcdsaAddress { + continue + } + // Update the HighestKnownVersion for this address. Upsert will + // only update this entry if the HighestKnownVersion is greater + // than the existing one. + // Also store the PublicKey for future encryption in queryEnode msgs + valEnodeEntries = append(valEnodeEntries, &istanbul.AddressEntry{ + Address: entry.Address(), + PublicKey: entry.PublicKey(), + HighestKnownVersion: entry.Version, + }) + } + if err := state.ValEnodeTable.UpsertHighestKnownVersion(valEnodeEntries); err != nil { + o.logger.Warn("Error upserting val enode table entries", "err", err) + } + } + + newVCs, err := state.VersionCertificateTable.Upsert(versionCertificates) + if err != nil { + o.logger.Warn("Error upserting version certificate table entries", "err", err) + } + + // Only regossip entries that do not originate from an address that we have + // gossiped a version certificate for within the last 5 minutes, excluding + // our own address. + var versionCertificatesToRegossip []*istanbul.VersionCertificate + + for _, entry := range newVCs { + lastGossipTime, ok := state.LastVersionCertificatesGossiped.Get(entry.Address()) + if ok && time.Since(lastGossipTime) >= VersionCertificateGossipCooldownDuration && entry.Address() != o.addrProvider.ValidatorAddress() { + continue + } + versionCertificatesToRegossip = append(versionCertificatesToRegossip, entry) + state.LastVersionCertificatesGossiped.Set(entry.Address(), time.Now()) + } + + if len(versionCertificatesToRegossip) > 0 { + return o.vcGossiper.Gossip(versionCertificatesToRegossip) + } + return nil +}
diff --git go-ethereum/consensus/istanbul/core/prepare.go celo/consensus/istanbul/core/prepare.go new file mode 100644 index 0000000000000000000000000000000000000000..6b41dc7c13592b3f33abe8052cb4b59a47c97e09 --- /dev/null +++ celo/consensus/istanbul/core/prepare.go @@ -0,0 +1,241 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "errors" + "reflect" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +func (c *core) sendPrepare() { + logger := c.newLogger("func", "sendPrepare") + logger.Debug("Sending prepare") + c.broadcast(istanbul.NewPrepareMessage(c.current.Subject(), c.address)) +} + +func (c *core) verifySignedPrepareOrCommitMessage(message istanbul.Message, seen map[common.Address]bool) (*common.Address, error) { + // Verify message signed by a validator + if err := istanbul.CheckSignedBy(&message, message.Signature, + message.Address, errInvalidPreparedCertificateMsgSignature, c.validateFn); err != nil { + return nil, err + } + // Check for duplicate messages + if seen[message.Address] { + return nil, errInvalidPreparedCertificateDuplicate + } + seen[message.Address] = true + + // Check that the message is a PREPARE or COMMIT message + if message.Code != istanbul.MsgPrepare && message.Code != istanbul.MsgCommit { + return nil, errInvalidPreparedCertificateMsgCode + } + return &message.Address, nil +} + +func (c *core) verifyProposalPrepareOrCommitMessage(proposal istanbul.Proposal, message istanbul.Message, seen map[common.Address]bool) (*istanbul.View, error) { + logger := c.newLogger("func", "verifyProposalPrepareOrCommitMessage", "proposal_number", proposal.Number(), "proposal_hash", proposal.Hash().String()) + signer, err := c.verifySignedPrepareOrCommitMessage(message, seen) + if err != nil { + return nil, err + } + // Assume prepare but overwrite if commit + subject := message.Prepare() + if message.Code == istanbul.MsgCommit { + commit := message.Commit() + // Verify the committedSeal + src := c.current.GetValidatorByAddress(*signer) + err = c.verifyCommittedSeal(commit, src) + if err != nil { + logger.Error("Commit seal did not contain signature from message signer.", "err", err) + return nil, err + } + + newValSet, err := c.backend.NextBlockValidators(proposal) + if err != nil { + return nil, err + } + err = c.verifyEpochValidatorSetSeal(commit, proposal.Number().Uint64(), newValSet, src) + if err != nil { + logger.Error("Epoch validator set seal seal did not contain signature from message signer.", "err", err) + return nil, err + } + + subject = commit.Subject + } + + msgLogger := logger.New("msg_round", subject.View.Round, "msg_seq", subject.View.Sequence, "msg_digest", subject.Digest.String()) + msgLogger.Trace("Decoded message in prepared certificate", "code", message.Code) + + // Verify message for the proper sequence. + if subject.View.Sequence.Cmp(c.current.Sequence()) != 0 { + return nil, errInvalidPreparedCertificateMsgView + } + + // Verify message for the proper proposal. + if subject.Digest != proposal.Hash() { + return nil, errInvalidPreparedCertificateDigestMismatch + } + + return subject.View, nil +} + +func (c *core) verifyPCV2WithProposal(pcV2 istanbul.PreparedCertificateV2, proposal istanbul.Proposal) (*istanbul.View, error) { + return c.verifyProposalAndPCMessages(proposal, pcV2.PrepareOrCommitMessages) +} + +// Verify a prepared certificate and return the view that all of its messages pertain to. +func (c *core) verifyPreparedCertificate(preparedCertificate istanbul.PreparedCertificate) (*istanbul.View, error) { + return c.verifyProposalAndPCMessages(preparedCertificate.Proposal, preparedCertificate.PrepareOrCommitMessages) +} + +func (c *core) verifyProposalAndPCMessages(proposal istanbul.Proposal, pCMessages []istanbul.Message) (*istanbul.View, error) { + // Validate the attached proposal + if _, err := c.verifyProposal(proposal); err != nil { + return nil, errInvalidPreparedCertificateProposal + } + + if len(pCMessages) > c.current.ValidatorSet().Size() || len(pCMessages) < c.current.ValidatorSet().MinQuorumSize() { + return nil, errInvalidPreparedCertificateNumMsgs + } + + seen := make(map[common.Address]bool) + + var view *istanbul.View + for _, message := range pCMessages { + messageView, err := c.verifyProposalPrepareOrCommitMessage(proposal, message, seen) + if err != nil { + return nil, err + } + // Verify that the view is the same for all of the messages + if view == nil { + view = messageView + } else { + if view.Cmp(messageView) != 0 { + return nil, errInvalidPreparedCertificateInconsistentViews + } + } + } + return view, nil +} + +// Extract the view from a PreparedCertificate that has already been verified. +func (c *core) getViewFromVerifiedPreparedCertificate(preparedCertificate istanbul.PreparedCertificate) (*istanbul.View, error) { + return c.getViewFromVerifiedPreparedCertificateMessages(preparedCertificate.PrepareOrCommitMessages) +} + +func (c *core) getViewFromVerifiedPreparedCertificateV2(preparedCertificate istanbul.PreparedCertificateV2) (*istanbul.View, error) { + return c.getViewFromVerifiedPreparedCertificateMessages(preparedCertificate.PrepareOrCommitMessages) +} + +func (c *core) getViewFromVerifiedPreparedCertificateMessages(messages []istanbul.Message) (*istanbul.View, error) { + if len(messages) < c.current.ValidatorSet().MinQuorumSize() { + return nil, errInvalidPreparedCertificateNumMsgs + } + + message := messages[0] + + // Assume prepare but overwrite if commit + subject := message.Prepare() + if message.Code == istanbul.MsgCommit { + subject = message.Commit().Subject + } + return subject.View, nil +} + +func (c *core) handlePrepare(msg *istanbul.Message) error { + defer c.handlePrepareTimer.UpdateSince(time.Now()) + // Decode PREPARE message + prepare := msg.Prepare() + logger := c.newLogger("func", "handlePrepare", "tag", "handleMsg", "msg_round", prepare.View.Round, "msg_seq", prepare.View.Sequence, "msg_digest", prepare.Digest.String()) + + if err := c.checkMessage(istanbul.MsgPrepare, prepare.View); err != nil { + return err + } + + if err := c.verifyPrepare(prepare); err != nil { + return err + } + + // Add the PREPARE message to current round state + if err := c.current.AddPrepare(msg); err != nil { + logger.Error("Failed to add PREPARE message to round state", "err", err) + return err + } + + preparesAndCommits := c.current.GetPrepareOrCommitSize() + minQuorumSize := c.current.ValidatorSet().MinQuorumSize() + logger = logger.New("prepares_and_commits", preparesAndCommits, "commits", c.current.Commits().Size(), "prepares", c.current.Prepares().Size()) + logger.Trace("Accepted prepare") + + // Change to Prepared state if we've received enough PREPARE messages and we are in earlier state + // before Prepared state. + // TODO(joshua): Remove state comparisons (or change the cmp function) + if (preparesAndCommits >= minQuorumSize) && c.current.State().Cmp(StatePrepared) < 0 { + + err := c.current.TransitionToPrepared(minQuorumSize) + if err != nil { + logger.Error("Failed to create and set preprared certificate", "err", err) + return err + } + logger.Trace("Got quorum prepares or commits", "tag", "stateTransition") + // Update metrics. + if !c.consensusTimestamp.IsZero() { + c.consensusPrepareTimeGauge.Update(time.Since(c.consensusTimestamp).Nanoseconds()) + } + + // Process Backlog Messages + c.backlog.updateState(c.current.View(), c.current.State()) + + c.sendCommit() + } + + return nil +} + +// verifyPrepare verifies if the received PREPARE message is equivalent to our subject +func (c *core) verifyPrepare(prepare *istanbul.Subject) error { + logger := c.newLogger("func", "verifyPrepare", "prepare_round", prepare.View.Round, "prepare_seq", prepare.View.Sequence, "prepare_digest", prepare.Digest.String()) + + sub := c.current.Subject() + if !reflect.DeepEqual(prepare, sub) { + logger.Warn("Inconsistent subjects between PREPARE and proposal", "expected", sub, "got", prepare) + return errInconsistentSubject + } + + return nil +} + +// GossipPrepares gossips to other validators all the prepares received in the current round. +func (c *core) GossipPrepares() error { + logger := c.newLogger("func", "gossipPrepares") + st := c.current.State() + if st != StatePreprepared && st != StatePrepared && st != StateCommitted { + return errors.New("Cant gossip prepares if not in preprepared, prepared, or committed state") + } + prepares := c.current.Prepares().Values() + logger.Debug("Gossipping prepares", "len", len(prepares)) + for _, prepare := range prepares { + c.gossip(prepare) + // let the bandwidth breathe a little + time.Sleep(10 * time.Millisecond) + } + return nil +}
diff --git go-ethereum/consensus/istanbul/core/types.go celo/consensus/istanbul/core/types.go new file mode 100644 index 0000000000000000000000000000000000000000..f44cfe75470c79d0a9e4821f44523b96d94fe605 --- /dev/null +++ celo/consensus/istanbul/core/types.go @@ -0,0 +1,99 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/rlp" +) + +type Engine interface { + Start() error + Stop() error + // CurrentView returns the current view or nil if none + CurrentView() *istanbul.View + // CurrentRoundState returns the current roundState or nil if none + CurrentRoundState() RoundState + // CurrentRoundChangeSet returns the current round change set summary: + // a collection of the latest round change messages from all other + // validators. + CurrentRoundChangeSet() *RoundChangeSetSummary + + SetAddress(common.Address) + // Validator -> CommittedSeal from Parent Block + ParentCommits() MessageSet + // ForceRoundChange will force round change to the current desiredRound + 1 + ForceRoundChange() + + // ResendPreprepare sends again the preprepare message. + ResendPreprepare() error + // GossipPrepares gossips to other validators all the prepares received in the current round. + GossipPrepares() error + // GossipCommits gossips to other validators all the commits received in the current round. + GossipCommits() error +} + +// State represents the IBFT state +type State uint64 + +// Different IBFT Core States +const ( + StateAcceptRequest State = iota + StatePreprepared + StatePrepared + StateCommitted + StateWaitingForNewRound +) + +func (s State) String() string { + if s == StateAcceptRequest { + return "Accept request" + } else if s == StatePreprepared { + return "Preprepared" + } else if s == StatePrepared { + return "Prepared" + } else if s == StateCommitted { + return "Committed" + } else if s == StateWaitingForNewRound { + return "Waiting for new round" + } else { + return "Unknown" + } +} + +// Cmp compares s and y and returns: +// -1 if s is the previous state of y +// 0 if s and y are the same state +// +1 if s is the next state of y +func (s State) Cmp(y State) int { + if uint64(s) < uint64(y) { + return -1 + } + if uint64(s) > uint64(y) { + return 1 + } + return 0 +} + +// ============================================== +// +// helper functions + +func Encode(val interface{}) ([]byte, error) { + return rlp.EncodeToBytes(val) +}
diff --git go-ethereum/consensus/istanbul/announce/task_state_test.go celo/consensus/istanbul/announce/task_state_test.go new file mode 100644 index 0000000000000000000000000000000000000000..475ed8232eff7ad1858ffdbfdfc40cce0710b948 --- /dev/null +++ celo/consensus/istanbul/announce/task_state_test.go @@ -0,0 +1,38 @@ +package announce + +import ( + "testing" + + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/stretchr/testify/assert" +) + +func TestShouldStartStopAnnouncing(t *testing.T) { + st := &announceTaskState{} + assert.False(t, st.ShouldStartAnnouncing()) + st.shouldAnnounce = true + assert.True(t, st.ShouldStartAnnouncing()) + st.OnStartAnnouncing() + assert.False(t, st.ShouldStartAnnouncing()) + st.OnStopAnnouncing() + assert.True(t, st.ShouldStartAnnouncing()) + st.shouldAnnounce = false + assert.False(t, st.ShouldStartAnnouncing()) +} + +func TestShouldStartStopQuerying(t *testing.T) { + st := &announceTaskState{ + config: &istanbul.Config{ + AnnounceAggressiveQueryEnodeGossipOnEnablement: true, + }, + } + assert.False(t, st.ShouldStartQuerying()) + st.shouldQuery = true + assert.True(t, st.ShouldStartQuerying()) + st.OnStartQuerying() + assert.False(t, st.ShouldStartQuerying()) + st.OnStopQuerying() + assert.True(t, st.ShouldStartQuerying()) + st.shouldQuery = false + assert.False(t, st.ShouldStartQuerying()) +}
diff --git go-ethereum/consensus/istanbul/announce/db.go celo/consensus/istanbul/announce/db.go new file mode 100644 index 0000000000000000000000000000000000000000..9814ef7e5e44751e3c44cc557a29c870fa348c4f --- /dev/null +++ celo/consensus/istanbul/announce/db.go @@ -0,0 +1,41 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package announce + +import ( + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +var ( + errIncorrectEntryType = errors.New("Incorrect entry type") +) + +const ( + dbAddressPrefix = "address:" // Identifier to prefix node entries with + dbNodeIDPrefix = "nodeid:" // Identifier to prefix node entries with +) + +func addressKey(address common.Address) []byte { + return append([]byte(dbAddressPrefix), address.Bytes()...) +} + +func nodeIDKey(nodeID enode.ID) []byte { + return append([]byte(dbNodeIDPrefix), nodeID.Bytes()...) +}
diff --git go-ethereum/consensus/istanbul/core/preprepare_v2_test.go celo/consensus/istanbul/core/preprepare_v2_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f3fb9470bb2badacffd9f9450f29baff442945e1 --- /dev/null +++ celo/consensus/istanbul/core/preprepare_v2_test.go @@ -0,0 +1,579 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +func newTestPreprepareV2(v *istanbul.View) *istanbul.PreprepareV2 { + return &istanbul.PreprepareV2{ + View: v, + Proposal: newTestProposal(), + } +} + +func TestHandlePreprepareV2(t *testing.T) { + N := uint64(4) // replica 0 is the proposer, it will send messages to others + F := uint64(1) // F does not affect tests + + getRoundState := func(c *core) *roundStateImpl { + return c.current.(*rsSaveDecorator).rs.(*roundStateImpl) + } + + testCases := []struct { + name string + system func() *testSystem + getCert func(*testSystem) istanbul.RoundChangeCertificateV2 + expectedRequest istanbul.Proposal + expectedErr error + existingBlock bool + }{ + { + "normal case", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for _, backend := range sys.backends { + backend.engine.(*core).Start() + } + return sys + }, + func(_ *testSystem) istanbul.RoundChangeCertificateV2 { + return istanbul.RoundChangeCertificateV2{} + }, + newTestProposal(), + nil, + false, + }, + { + "proposal sequence doesn't match message sequence", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for _, backend := range sys.backends { + backend.engine.(*core).Start() + } + return sys + }, + func(_ *testSystem) istanbul.RoundChangeCertificateV2 { + return istanbul.RoundChangeCertificateV2{} + }, + makeBlock(3), + errInvalidProposal, + false, + }, + { + "non-proposer", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + // force remove replica 0, let replica 1 be the proposer + sys.backends = sys.backends[1:] + + for i, backend := range sys.backends { + c := backend.engine.(*core) + backend.engine.(*core).Start() + if i != 0 { + // replica 0 is the proposer + getRoundState(c).state = StatePreprepared + } + } + return sys + }, + func(_ *testSystem) istanbul.RoundChangeCertificateV2 { + return istanbul.RoundChangeCertificateV2{} + }, + makeBlock(1), + errNotFromProposer, + false, + }, + { + "errOldMessage", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + backend.engine.(*core).Start() + if i != 0 { + getRoundState(c).state = StatePreprepared + getRoundState(c).sequence = big.NewInt(10) + getRoundState(c).round = big.NewInt(10) + getRoundState(c).desiredRound = getRoundState(c).round + } + } + return sys + }, + func(_ *testSystem) istanbul.RoundChangeCertificateV2 { + return istanbul.RoundChangeCertificateV2{} + }, + makeBlock(1), + errOldMessage, + false, + }, + { + "test existing block", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for _, backend := range sys.backends { + c := backend.engine.(*core) + backend.engine.(*core).Start() + getRoundState(c).state = StatePreprepared + getRoundState(c).sequence = big.NewInt(10) + getRoundState(c).round = big.NewInt(10) + getRoundState(c).desiredRound = getRoundState(c).round + } + return sys + }, + func(_ *testSystem) istanbul.RoundChangeCertificateV2 { + return istanbul.RoundChangeCertificateV2{} + }, + // In the method testbackend_test.go:HasBlockMatching(), it will return true if the proposal's block number == 5 + makeBlock(5), + nil, + true, + }, + { + "ROUND CHANGE certificate missing", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for _, backend := range sys.backends { + c := backend.engine.(*core) + backend.engine.(*core).Start() + getRoundState(c).state = StatePreprepared + getRoundState(c).round = big.NewInt(int64(N)) + getRoundState(c).desiredRound = getRoundState(c).round + } + return sys + }, + func(_ *testSystem) istanbul.RoundChangeCertificateV2 { + return istanbul.RoundChangeCertificateV2{} + }, + makeBlock(1), + errMissingRoundChangeCertificate, + false, + }, + { + "ROUND CHANGE certificate invalid, duplicate messages.", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for _, backend := range sys.backends { + c := backend.engine.(*core) + backend.engine.(*core).Start() + getRoundState(c).state = StatePreprepared + getRoundState(c).round = big.NewInt(int64(N)) + getRoundState(c).desiredRound = getRoundState(c).round + } + return sys + }, + func(sys *testSystem) istanbul.RoundChangeCertificateV2 { + // Duplicate messages + pc, _ := istanbul.EmptyPreparedCertificateV2() + roundChangeCertificate := sys.getRoundChangeCertificateV2(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, pc) + roundChangeCertificate.Requests[1] = roundChangeCertificate.Requests[0] + return roundChangeCertificate + }, + makeBlock(1), + errInvalidRoundChangeCertificateDuplicate, + false, + }, + { + "ROUND CHANGE certificate contains PREPARED certificate with inconsistent views among the cert's messages", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for _, backend := range sys.backends { + c := backend.engine.(*core) + backend.engine.(*core).Start() + getRoundState(c).state = StatePreprepared + getRoundState(c).round = big.NewInt(int64(N)) + getRoundState(c).desiredRound = getRoundState(c).round + getRoundState(c).sequence = big.NewInt(1) + c.current.TransitionToPrepreparedV2(&istanbul.PreprepareV2{ + View: &istanbul.View{ + Round: big.NewInt(int64(N)), + Sequence: big.NewInt(1), + }, + Proposal: makeBlock(1), + RoundChangeCertificateV2: istanbul.RoundChangeCertificateV2{}, + }) + } + return sys + }, + func(sys *testSystem) istanbul.RoundChangeCertificateV2 { + view1 := *(sys.backends[0].engine.(*core).current.View()) + + var view2 istanbul.View + view2.Sequence = big.NewInt(view1.Sequence.Int64()) + view2.Round = big.NewInt(view1.Round.Int64() + 1) + + preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view1, view2}, makeBlock(1)) + pc := istanbul.PCV2FromPCV1(preparedCertificate) + roundChangeCertificate := sys.getRoundChangeCertificateV2(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, pc) + return roundChangeCertificate + }, + makeBlock(1), + errInvalidPreparedCertificateInconsistentViews, + false, + }, + { + "ROUND CHANGE certificate contains PREPARED certificate for a different block.", + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for _, backend := range sys.backends { + c := backend.engine.(*core) + backend.engine.(*core).Start() + getRoundState(c).state = StatePreprepared + getRoundState(c).round = big.NewInt(int64(N)) + getRoundState(c).desiredRound = getRoundState(c).round + c.current.TransitionToPrepreparedV2(&istanbul.PreprepareV2{ + View: &istanbul.View{ + Round: big.NewInt(int64(N)), + Sequence: big.NewInt(0), + }, + Proposal: makeBlock(2), + RoundChangeCertificateV2: istanbul.RoundChangeCertificateV2{}, + }) + } + return sys + }, + func(sys *testSystem) istanbul.RoundChangeCertificateV2 { + preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, makeBlock(2)) + pc := istanbul.PCV2FromPCV1(preparedCertificate) + roundChangeCertificate := sys.getRoundChangeCertificateV2(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, pc) + return roundChangeCertificate + }, + makeBlock(1), + errInvalidPreparedCertificateDigestMismatch, + false, + }, + { + "ROUND CHANGE certificate for N+1 round with valid PREPARED certificates", + // Round is N+1 to match the correct proposer. + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + backend.engine.(*core).Start() + getRoundState(c).round = big.NewInt(int64(N)) + getRoundState(c).desiredRound = getRoundState(c).round + if i != 0 { + getRoundState(c).state = StateAcceptRequest + } + } + return sys + }, + func(sys *testSystem) istanbul.RoundChangeCertificateV2 { + preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, makeBlock(1)) + pc := istanbul.PCV2FromPCV1(preparedCertificate) + roundChangeCertificate := sys.getRoundChangeCertificateV2(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, pc) + return roundChangeCertificate + }, + makeBlock(1), + nil, + false, + }, + { + "ROUND CHANGE certificate for N+1 round with empty PREPARED certificates", + // Round is N+1 to match the correct proposer. + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + backend.engine.(*core).Start() + getRoundState(c).round = big.NewInt(int64(N)) + getRoundState(c).desiredRound = getRoundState(c).round + if i != 0 { + getRoundState(c).state = StateAcceptRequest + } + } + return sys + }, + func(sys *testSystem) istanbul.RoundChangeCertificateV2 { + pc, _ := istanbul.EmptyPreparedCertificateV2() + roundChangeCertificate := sys.getRoundChangeCertificateV2(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, pc) + return roundChangeCertificate + }, + makeBlock(1), + nil, + false, + }, + { + "ROUND CHANGE certificate for N+1 or later rounds with empty PREPARED certificates", + // Round is N+1 to match the correct proposer. + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + backend.engine.(*core).Start() + getRoundState(c).round = big.NewInt(int64(N)) + getRoundState(c).desiredRound = getRoundState(c).round + if i != 0 { + getRoundState(c).state = StateAcceptRequest + } + } + return sys + }, + func(sys *testSystem) istanbul.RoundChangeCertificateV2 { + v1 := *(sys.backends[0].engine.(*core).current.View()) + v2 := istanbul.View{Sequence: v1.Sequence, Round: big.NewInt(v1.Round.Int64() + 1)} + v3 := istanbul.View{Sequence: v1.Sequence, Round: big.NewInt(v1.Round.Int64() + 2)} + pc, _ := istanbul.EmptyPreparedCertificateV2() + roundChangeCertificate := sys.getRoundChangeCertificateV2(t, []istanbul.View{v1, v2, v3}, pc) + return roundChangeCertificate + }, + makeBlock(1), + nil, + false, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + + t.Log("Running", "test", test.name) + sys := test.system() + + // Even those we pass core=false here, cores do get started and need stopping. + sys.Run(false) + defer sys.Stop(true) + + v0 := sys.backends[0] + r0 := v0.engine.(*core) + + curView := r0.current.View() + + preprepareView := curView + if test.existingBlock { + preprepareView = &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(5)} + } + + msg := istanbul.NewPreprepareV2Message( + &istanbul.PreprepareV2{ + View: preprepareView, + Proposal: test.expectedRequest, + RoundChangeCertificateV2: test.getCert(sys), + }, + v0.Address(), + ) + + for i, v := range sys.backends { + // i == 0 is primary backend, it is responsible for send PRE-PREPARE messages to others. + if i == 0 { + continue + } + + c := v.engine.(*core) + + // run each backends and verify handlePreprepare function. + if err := c.handlePreprepareV2(msg); err != nil { + if err != test.expectedErr { + t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) + } + return + } + + if c.current.State() != StatePreprepared { + t.Errorf("state mismatch: have %v, want %v", c.current.State(), StatePreprepared) + } + + if !test.existingBlock && !reflect.DeepEqual(c.current.Subject().View, curView) { + t.Errorf("view mismatch: have %v, want %v", c.current.Subject().View, curView) + } + + if test.existingBlock && len(v.sentMsgs) > 0 { + t.Errorf("expecting to ignore commits for old messages %v", v.sentMsgs) + } else { + continue + } + + // verify prepare messages + decodedMsg := new(istanbul.Message) + err := decodedMsg.FromPayload(v.sentMsgs[0], nil) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + expectedCode := istanbul.MsgPrepare + if test.existingBlock { + expectedCode = istanbul.MsgCommit + } + if decodedMsg.Code != expectedCode { + t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, expectedCode) + } + + subject := decodedMsg.Prepare() + if decodedMsg.Code == istanbul.MsgCommit { + subject = decodedMsg.Commit().Subject + } + + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + expectedSubject := c.current.Subject() + if test.existingBlock { + expectedSubject = &istanbul.Subject{View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(5)}, + Digest: test.expectedRequest.Hash()} + } + + if !reflect.DeepEqual(subject, expectedSubject) { + t.Errorf("subject mismatch: have %v, want %v", subject, expectedSubject) + } + + if expectedCode == istanbul.MsgCommit { + srcValidator := c.current.GetValidatorByAddress(v.address) + + if err := c.verifyCommittedSeal(decodedMsg.Commit(), srcValidator); err != nil { + t.Errorf("invalid seal. verify commmited seal error: %v, subject: %v, committedSeal: %v", err, expectedSubject, decodedMsg.Commit().CommittedSeal) + } + } + } + }) + } +} + +// benchMarkHandleRoundChangeV2 benchmarks handling a round change messages with n prepare or commit messages in the prepared certificate +func benchMarkHandleRoundChangeV2(n int, b *testing.B) { + // Setup + N := uint64(n) + F := uint64(1) // F does not affect tests + sys := NewMutedTestSystemWithBackend(N, F) + c := sys.backends[0].engine.(*core) + c.Start() + sys.backends[1].engine.(*core).Start() + // getPreparedCertificate defaults to 50% commits, 50% prepares. Modify the function to change the ratio. + preparedCertificate := sys.getPreparedCertificate(b, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, makeBlock(1)) + pc := istanbul.PCV2FromPCV1(preparedCertificate) + msg, err := sys.backends[1].getRoundChangeV2Message(istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, pc, preparedCertificate.Proposal) + if err != nil { + b.Errorf("Error creating a round change message. err: %v", err) + } + + // benchmarked portion + b.ResetTimer() + for i := 0; i < b.N; i++ { + err = c.handleRoundChangeV2(&msg) + if err != nil { + b.Errorf("Error handling the round change message. err: %v", err) + } + } +} + +func BenchmarkHandleRoundChange_10V2(b *testing.B) { benchMarkHandleRoundChangeV2(10, b) } +func BenchmarkHandleRoundChange_50V2(b *testing.B) { benchMarkHandleRoundChangeV2(50, b) } +func BenchmarkHandleRoundChange_90V2(b *testing.B) { + b.Skip("Skipping slow benchmark") + benchMarkHandleRoundChangeV2(90, b) +} +func BenchmarkHandleRoundChange_100V2(b *testing.B) { + b.Skip("Skipping slow benchmark") + benchMarkHandleRoundChangeV2(100, b) +} +func BenchmarkHandleRoundChange_120V2(b *testing.B) { + b.Skip("Skipping slow benchmark") + benchMarkHandleRoundChangeV2(120, b) +} +func BenchmarkHandleRoundChange_150V2(b *testing.B) { + b.Skip("Skipping slow benchmark") + benchMarkHandleRoundChangeV2(150, b) +} +func BenchmarkHandleRoundChange_200V2(b *testing.B) { + b.Skip("Skipping slow benchmark") + benchMarkHandleRoundChangeV2(200, b) +} + +// benchMarkHandlePreprepare benchmarks handling a preprepare with a round change certificate that has +// filled round change messages (i.e. the round change messages have prepared certificates that are not empty) +func benchMarkHandlePreprepareV2(n int, b *testing.B) { + // Setup + getRoundState := func(c *core) *roundStateImpl { + return c.current.(*rsSaveDecorator).rs.(*roundStateImpl) + } + N := uint64(n) + F := uint64(1) // F does not affect tests + sys := NewMutedTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + backend.engine.(*core).Start() + getRoundState(c).round = big.NewInt(int64(N)) + getRoundState(c).desiredRound = getRoundState(c).round + if i != 0 { + getRoundState(c).state = StateAcceptRequest + } + } + c := sys.backends[0].engine.(*core) + + // Create pre-prepare + block := makeBlock(1) + nextView := istanbul.View{Round: big.NewInt(int64(N)), Sequence: big.NewInt(1)} + // getPreparedCertificate defaults to 50% commits, 50% prepares. Modify the function to change the ratio. + preparedCertificate := sys.getPreparedCertificate(b, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, block) + roundChangeCertificate := sys.getRoundChangeCertificateV2(b, []istanbul.View{nextView}, istanbul.PCV2FromPCV1(preparedCertificate)) + msg, err := sys.backends[0].getPreprepareV2Message(nextView, roundChangeCertificate, block) + if err != nil { + b.Errorf("Error creating a pre-prepare message. err: %v", err) + } + + // benchmarked portion + b.ResetTimer() + for i := 0; i < b.N; i++ { + err = c.handlePreprepareV2(&msg) + if err != nil { + b.Errorf("Error handling the pre-prepare message. err: %v", err) + } + } +} + +func BenchmarkHandlePreprepare_10V2(b *testing.B) { benchMarkHandlePreprepareV2(10, b) } +func BenchmarkHandlePreprepare_50V2(b *testing.B) { + b.Skip("Skipping slow benchmark") + benchMarkHandlePreprepareV2(50, b) +} +func BenchmarkHandlePreprepare_90V2(b *testing.B) { + b.Skip("Skipping slow benchmark") + benchMarkHandlePreprepareV2(90, b) +} +func BenchmarkHandlePreprepare_100V2(b *testing.B) { + b.Skip("Skipping slow benchmark") + benchMarkHandlePreprepareV2(100, b) +} +func BenchmarkHandlePreprepare_120V2(b *testing.B) { + b.Skip("Skipping slow benchmark") + benchMarkHandlePreprepareV2(120, b) +} +func BenchmarkHandlePreprepare_150V2(b *testing.B) { + b.Skip("Skipping slow benchmark") + benchMarkHandlePreprepareV2(150, b) +} +func BenchmarkHandlePreprepare_200V2(b *testing.B) { + b.Skip("Skipping slow benchmark") + benchMarkHandlePreprepareV2(200, b) +}
diff --git go-ethereum/consensus/istanbul/proxy/proxy_test.go celo/consensus/istanbul/proxy/proxy_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4591e6bd8b94f41bce40df56f2afd2b4d20c5503 --- /dev/null +++ celo/consensus/istanbul/proxy/proxy_test.go @@ -0,0 +1,15 @@ +package proxy_test + +import ( + "os" + "testing" + + "github.com/ethereum/go-ethereum/consensus/istanbul/backend" + "github.com/ethereum/go-ethereum/consensus/istanbul/backend/backendtest" +) + +func TestMain(m *testing.M) { + backendtest.InitTestBackendFactory(backend.TestBackendFactory) + code := m.Run() + os.Exit(code) +}
diff --git go-ethereum/consensus/istanbul/announce/ecertholder.go celo/consensus/istanbul/announce/ecertholder.go new file mode 100644 index 0000000000000000000000000000000000000000..9c1dd95fa874314e650900ebdae4d763c40d2611 --- /dev/null +++ celo/consensus/istanbul/announce/ecertholder.go @@ -0,0 +1,90 @@ +package announce + +import ( + "errors" + "sync" + + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +var ( + errInvalidEnodeCertMsgMapInconsistentVersion = errors.New("invalid enode certificate message map because of inconsistent version") +) + +type EnodeCertificateMsgHolder interface { + // Get gets the most recent enode certificate messages. + // May be nil if no message was generated as a result of the core not being + // started, or if a proxy has not received a message from its proxied validator + Get() map[enode.ID]*istanbul.EnodeCertMsg + Set(enodeCertMsgMap map[enode.ID]*istanbul.EnodeCertMsg) error +} + +type lockedHolder struct { + logger log.Logger + // The enode certificate message map contains the most recently generated + // enode certificates for each external node ID (e.g. will have one entry per proxy + // for a proxied validator, or just one entry if it's a standalone validator). + // Each proxy will just have one entry for their own external node ID. + // Used for proving itself as a validator in the handshake for externally exposed nodes, + // or by saving latest generated certificate messages by proxied validators to send + // to their proxies. + enodeCertificateMsgMap map[enode.ID]*istanbul.EnodeCertMsg + enodeCertificateMsgVersion uint + enodeCertificateMsgMapMu sync.RWMutex // This protects both enodeCertificateMsgMap and enodeCertificateMsgVersion +} + +func NewLockedHolder() EnodeCertificateMsgHolder { + return &lockedHolder{ + logger: log.New("module", "lockedHolder"), + } +} + +// RetrieveEnodeCertificateMsgMap gets the most recent enode certificate messages. +// May be nil if no message was generated as a result of the core not being +// started, or if a proxy has not received a message from its proxied validator +func (h *lockedHolder) Get() map[enode.ID]*istanbul.EnodeCertMsg { + h.enodeCertificateMsgMapMu.Lock() + defer h.enodeCertificateMsgMapMu.Unlock() + return h.enodeCertificateMsgMap +} + +func (h *lockedHolder) Set(enodeCertMsgMap map[enode.ID]*istanbul.EnodeCertMsg) error { + logger := h.logger.New("func", "Set") + var enodeCertVersion *uint + + // Verify that all of the certificates have the same version + for _, enodeCertMsg := range enodeCertMsgMap { + enodeCert := enodeCertMsg.Msg.EnodeCertificate() + + if enodeCertVersion == nil { + enodeCertVersion = &enodeCert.Version + } else { + if enodeCert.Version != *enodeCertVersion { + logger.Error("enode certificate messages within enode certificate msgs array don't all have the same version") + return errInvalidEnodeCertMsgMapInconsistentVersion + } + } + } + + h.enodeCertificateMsgMapMu.Lock() + defer h.enodeCertificateMsgMapMu.Unlock() + + // Already have a more recent enodeCertificate + if *enodeCertVersion < h.enodeCertificateMsgVersion { + logger.Error("Ignoring enode certificate msgs since it's an older version", "enodeCertVersion", *enodeCertVersion, "sb.enodeCertificateMsgVersion", h.enodeCertificateMsgVersion) + return istanbul.ErrInvalidEnodeCertMsgMapOldVersion + } else if *enodeCertVersion == h.enodeCertificateMsgVersion { + // This function may be called with the same enode certificate. + // Proxied validators will periodically send the same enode certificate to it's proxies, + // to ensure that the proxies to eventually get their enode certificates. + logger.Trace("Attempting to set an enode certificate with the same version as the previous set enode certificate's") + } else { + logger.Debug("Setting enode certificate", "version", *enodeCertVersion) + h.enodeCertificateMsgMap = enodeCertMsgMap + h.enodeCertificateMsgVersion = *enodeCertVersion + } + + return nil +}
diff --git go-ethereum/consensus/istanbul/validator/default.go celo/consensus/istanbul/validator/default.go new file mode 100644 index 0000000000000000000000000000000000000000..5891a5cd0ec902e1389d8cf5a47c45cc65192ee1 --- /dev/null +++ celo/consensus/istanbul/validator/default.go @@ -0,0 +1,387 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package validator + +import ( + "encoding/json" + "fmt" + "io" + "math" + "math/big" + "strings" + "sync" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +type defaultValidator struct { + address common.Address + blsPublicKey blscrypto.SerializedPublicKey + uncompressedBlsPublicKey []byte +} + +func newValidatorFromDataWithBLSKeyCache(data *istanbul.ValidatorDataWithBLSKeyCache) *defaultValidator { + return &defaultValidator{ + address: data.Address, + blsPublicKey: data.BLSPublicKey, + uncompressedBlsPublicKey: data.UncompressedBLSPublicKey, + } +} + +func newValidatorFromData(data *istanbul.ValidatorData) *defaultValidator { + return &defaultValidator{ + address: data.Address, + blsPublicKey: data.BLSPublicKey, + } +} + +func (val *defaultValidator) AsData() *istanbul.ValidatorData { + return &istanbul.ValidatorData{ + Address: val.address, + BLSPublicKey: val.blsPublicKey, + } +} + +func (val *defaultValidator) AsDataWithBLSKeyCache() *istanbul.ValidatorDataWithBLSKeyCache { + return &istanbul.ValidatorDataWithBLSKeyCache{ + Address: val.address, + BLSPublicKey: val.blsPublicKey, + UncompressedBLSPublicKey: val.uncompressedBlsPublicKey, + } +} + +func (val *defaultValidator) Address() common.Address { return val.address } +func (val *defaultValidator) BLSPublicKey() blscrypto.SerializedPublicKey { return val.blsPublicKey } +func (val *defaultValidator) String() string { return val.Address().String() } + +func (val *defaultValidator) BLSPublicKeyUncompressed() []byte { + if len(val.uncompressedBlsPublicKey) == 0 { + log.Warn("Uncompressed BLS key wasn't cached", "address", val.address) + val.CacheUncompressedBLSKey() + } + return val.uncompressedBlsPublicKey +} + +func (val *defaultValidator) Copy() istanbul.Validator { + return &defaultValidator{ + address: val.address, + blsPublicKey: val.blsPublicKey, + uncompressedBlsPublicKey: val.uncompressedBlsPublicKey, + } +} + +func (val *defaultValidator) CacheUncompressedBLSKey() { + if len(val.uncompressedBlsPublicKey) == 0 { + uncompressed, err := blscrypto.UncompressKey(val.blsPublicKey) + if err != nil { + log.Error("Bad BLS public key", "adddress", val.address, "bls", val.blsPublicKey) + } + val.uncompressedBlsPublicKey = uncompressed + } +} + +func (val *defaultValidator) Serialize() ([]byte, error) { return rlp.EncodeToBytes(val) } + +// JSON Encoding ----------------------------------------------------------------------- + +func (val *defaultValidator) MarshalJSON() ([]byte, error) { return json.Marshal(val.AsData()) } +func (val *defaultValidator) UnmarshalJSON(b []byte) error { + var data istanbul.ValidatorData + if err := json.Unmarshal(b, &data); err != nil { + return err + } + *val = *newValidatorFromData(&data) + return nil +} + +// RLP Encoding ----------------------------------------------------------------------- + +func (val *defaultValidator) EncodeRLP(w io.Writer) error { return rlp.Encode(w, val.AsData()) } +func (val *defaultValidator) DecodeRLP(stream *rlp.Stream) error { + var v istanbul.ValidatorData + if err := stream.Decode(&v); err != nil { + return err + } + + *val = *newValidatorFromData(&v) + return nil +} + +// ---------------------------------------------------------------------------- + +type defaultSet struct { + validators []istanbul.Validator + validatorMu sync.RWMutex + // This is set when we call `getOrderedValidators` + // TODO Rename to `EpochState` that has validators & randomness + randomness common.Hash +} + +func newDefaultSet(validators []istanbul.ValidatorData) *defaultSet { + return &defaultSet{ + validators: mapDataToValidators(validators), + } +} + +func newDefaultSetFromDataWithBLSKeyCache(validators []istanbul.ValidatorDataWithBLSKeyCache) *defaultSet { + return &defaultSet{ + validators: mapDataToValidatorsWithBLSKeyCache(validators), + } +} + +// We include the optimization described at https://arxiv.org/pdf/1901.07160.pdf as “PM-6” and +// discussed in Lemma 22. For values of N=3F for integer F=1,2,3,.. we can tolerate a quorum +// size one smaller than anticipated by Q = N - F. The intersection of any two sets of Q +// nodes of N=3F must contain an honest validator. +// +// For example, with N=9, F=2, Q=6. Any two sets of Q=6 from N=9 nodes must overlap +// by >9-6=3 nodes. At least 3-F=3-2=1 must be honest. +// +// 1 2 3 4 5 6 7 8 9 +// x x x x x x +// y y y y y y +// F F H +// +// For N=10, F=3, Q=7. Any two sets of Q=7 nodes from N=10 must overlap by >4 nodes. +// At least 4-F=4-3=1 must be honest. +// +// 1 2 3 4 5 6 7 8 9 10 +// x x x x x x x +// y y y y y y y +// F F F H + +func (valSet *defaultSet) F() int { return int(math.Ceil(float64(valSet.Size())/3)) - 1 } +func (valSet *defaultSet) MinQuorumSize() int { return int(math.Ceil(float64(2*valSet.Size()) / 3)) } + +func (valSet *defaultSet) SetRandomness(seed common.Hash) { valSet.randomness = seed } +func (valSet *defaultSet) GetRandomness() common.Hash { return valSet.randomness } + +func (valSet *defaultSet) String() string { + var buf strings.Builder + if _, err := buf.WriteString("["); err != nil { + return fmt.Sprintf("String() error: %s", err) + } + for _, v := range valSet.List() { + if _, err := buf.WriteString(v.String()); err != nil { + return fmt.Sprintf("String() error: %s", err) + } + if _, err := buf.WriteString(" "); err != nil { + return fmt.Sprintf("String() error: %s", err) + } + + } + if _, err := buf.WriteString("]"); err != nil { + return fmt.Sprintf("String() error: %s", err) + } + + return fmt.Sprintf("{randomness: %s, validators: %s}", valSet.randomness.String(), buf.String()) +} + +func (valSet *defaultSet) CacheUncompressedBLSKey() { + valSet.validatorMu.RLock() + defer valSet.validatorMu.RUnlock() + for _, v := range valSet.validators { + v.CacheUncompressedBLSKey() + } +} + +func (valSet *defaultSet) Size() int { + valSet.validatorMu.RLock() + defer valSet.validatorMu.RUnlock() + + return len(valSet.validators) +} + +func (valSet *defaultSet) List() []istanbul.Validator { + valSet.validatorMu.RLock() + defer valSet.validatorMu.RUnlock() + return valSet.validators +} + +func (valSet *defaultSet) GetByIndex(i uint64) istanbul.Validator { + valSet.validatorMu.RLock() + defer valSet.validatorMu.RUnlock() + if i < uint64(valSet.Size()) { + return valSet.validators[i] + } + return nil +} + +func (valSet *defaultSet) GetByAddress(addr common.Address) (int, istanbul.Validator) { + for i, val := range valSet.List() { + if addr == val.Address() { + return i, val + } + } + return -1, nil +} + +func (valSet *defaultSet) ContainsByAddress(addr common.Address) bool { + i, _ := valSet.GetByAddress(addr) + return i != -1 +} + +func (valSet *defaultSet) GetIndex(addr common.Address) int { + i, _ := valSet.GetByAddress(addr) + return i +} + +func (valSet *defaultSet) AddValidators(validators []istanbul.ValidatorData) bool { + newValidators := make([]istanbul.Validator, 0, len(validators)) + newAddressesMap := make(map[common.Address]bool) + for i := range validators { + newAddressesMap[validators[i].Address] = true + newValidators = append(newValidators, newValidatorFromData(&validators[i])) + } + + valSet.validatorMu.Lock() + defer valSet.validatorMu.Unlock() + + // Verify that the validators to add is not already in the valset + for _, v := range valSet.validators { + if _, ok := newAddressesMap[v.Address()]; ok { + return false + } + } + + valSet.validators = append(valSet.validators, newValidators...) + + return true +} + +func (valSet *defaultSet) RemoveValidators(removedValidators *big.Int) bool { + if removedValidators.BitLen() == 0 { + return true + } + + if removedValidators.BitLen() > len(valSet.validators) { + return false + } + + valSet.validatorMu.Lock() + defer valSet.validatorMu.Unlock() + + // Using this method to filter the validators list: https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating, so that no + // new memory will be allocated + tempList := valSet.validators[:0] + for i, v := range valSet.validators { + if removedValidators.Bit(i) == 0 { + tempList = append(tempList, v) + } + } + + valSet.validators = tempList + return true +} + +func (valSet *defaultSet) Copy() istanbul.ValidatorSet { + valSet.validatorMu.RLock() + defer valSet.validatorMu.RUnlock() + newValSet := &defaultSet{} + newValSet.validators = make([]istanbul.Validator, len(valSet.validators)) + for i, v := range valSet.validators { + newValSet.validators[i] = v.Copy() + } + newValSet.SetRandomness(valSet.randomness) + return newValSet +} + +func (valSet *defaultSet) HasBLSKeyCache() bool { + for _, v := range valSet.validators { + if v.AsDataWithBLSKeyCache().UncompressedBLSPublicKey == nil && v.BLSPublicKey() != (blscrypto.SerializedPublicKey{}) { + return false + } + } + return true +} + +func (valSet *defaultSet) AsData() *istanbul.ValidatorSetData { + valSet.validatorMu.RLock() + defer valSet.validatorMu.RUnlock() + return &istanbul.ValidatorSetData{ + Validators: MapValidatorsToData(valSet.validators), + Randomness: valSet.randomness, + } +} + +// JSON Encoding ----------------------------------------------------------------------- + +func (val *defaultSet) MarshalJSON() ([]byte, error) { return json.Marshal(val.AsData()) } + +func (val *defaultSet) UnmarshalJSON(b []byte) error { + var data istanbul.ValidatorSetData + if err := json.Unmarshal(b, &data); err != nil { + return err + } + *val = *newDefaultSet(data.Validators) + val.SetRandomness(data.Randomness) + return nil +} + +// RLP Encoding ----------------------------------------------------------------------- + +func (val *defaultSet) EncodeRLP(w io.Writer) error { return rlp.Encode(w, val.AsData()) } + +func (val *defaultSet) DecodeRLP(stream *rlp.Stream) error { + var data istanbul.ValidatorSetData + if err := stream.Decode(&data); err != nil { + return err + } + *val = *newDefaultSet(data.Validators) + val.SetRandomness(data.Randomness) + return nil +} + +func (val *defaultSet) Serialize() ([]byte, error) { return rlp.EncodeToBytes(val) } + +// Utility Functions + +func MapValidatorsToData(validators []istanbul.Validator) []istanbul.ValidatorData { + validatorsData := make([]istanbul.ValidatorData, len(validators)) + for i, v := range validators { + validatorsData[i] = *v.AsData() + } + return validatorsData +} + +func mapDataToValidators(data []istanbul.ValidatorData) []istanbul.Validator { + validators := make([]istanbul.Validator, len(data)) + for i, v := range data { + validators[i] = newValidatorFromData(&v) + } + return validators +} + +func MapValidatorsToDataWithBLSKeyCache(validators []istanbul.Validator) []istanbul.ValidatorDataWithBLSKeyCache { + validatorsData := make([]istanbul.ValidatorDataWithBLSKeyCache, len(validators)) + for i, v := range validators { + validatorsData[i] = *v.AsDataWithBLSKeyCache() + } + return validatorsData +} + +func mapDataToValidatorsWithBLSKeyCache(data []istanbul.ValidatorDataWithBLSKeyCache) []istanbul.Validator { + validators := make([]istanbul.Validator, len(data)) + for i, v := range data { + validators[i] = newValidatorFromDataWithBLSKeyCache(&v) + } + return validators +}
diff --git go-ethereum/consensus/istanbul/announce/const.go celo/consensus/istanbul/announce/const.go new file mode 100644 index 0000000000000000000000000000000000000000..ad149bf0395c1a245e2ea4f83a357473777b8c1c --- /dev/null +++ celo/consensus/istanbul/announce/const.go @@ -0,0 +1,8 @@ +package announce + +import "time" + +const ( + QueryEnodeGossipCooldownDuration = 5 * time.Minute + VersionCertificateGossipCooldownDuration = 5 * time.Minute +)
diff --git go-ethereum/consensus/istanbul/db/generic_db.go celo/consensus/istanbul/db/generic_db.go new file mode 100644 index 0000000000000000000000000000000000000000..b47dec8164714aa8bf31e9af6efb09340b3def2c --- /dev/null +++ celo/consensus/istanbul/db/generic_db.go @@ -0,0 +1,183 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package db + +import ( + "bytes" + "encoding/binary" + "os" + + "github.com/syndtr/goleveldb/leveldb" + lvlerrors "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/syndtr/goleveldb/leveldb/util" + + "github.com/ethereum/go-ethereum/log" +) + +const ( + dbVersionKey = "version" // Version of the database to flush if changes +) + +// GenericDB manages a levelDB database +type GenericDB struct { + db *leveldb.DB + writeOptions *opt.WriteOptions +} + +type GenericEntry interface{} + +// New will open a new db at the given file path with the given version. +// If the path is empty, the db will be created in memory. +// If there is a version mismatch in the existing db, the contents are flushed. +func New(dbVersion int64, path string, logger log.Logger, writeOptions *opt.WriteOptions) (*GenericDB, error) { + db, err := NewDB(dbVersion, path, logger) + if err != nil { + return nil, err + } + return &GenericDB{ + db: db, + writeOptions: writeOptions, + }, nil +} + +// Close flushes and closes the database files. +func (gdb *GenericDB) Close() error { + return gdb.db.Close() +} + +// Upsert iterates through each provided entry and determines if the entry is +// new. If there is an existing entry in the db, `onUpdatedEntry` is called. +// If there is no existing entry, `onNewEntry` is called. Db content modifications are left to those functions +// by providing a leveldb Batch that is written after all entries are processed. +func (gdb *GenericDB) Upsert( + entries []GenericEntry, + getExistingEntry func(entry GenericEntry) (GenericEntry, error), + onUpdatedEntry func(batch *leveldb.Batch, existingEntry GenericEntry, newEntry GenericEntry) error, + onNewEntry func(batch *leveldb.Batch, entry GenericEntry) error, +) error { + batch := new(leveldb.Batch) + for _, entry := range entries { + existingEntry, err := getExistingEntry(entry) + isNew := err == leveldb.ErrNotFound + if !isNew && err != nil { + return err + } + if isNew { + if err := onNewEntry(batch, entry); err != nil { + return err + } + } else { + if err := onUpdatedEntry(batch, existingEntry, entry); err != nil { + return err + } + } + } + + if batch.Len() > 0 { + err := gdb.Write(batch) + if err != nil { + return err + } + } + return nil +} + +// Get gets the bytes at a given key in the db +func (gdb *GenericDB) Get(key []byte) ([]byte, error) { + return gdb.db.Get(key, nil) +} + +// Write writes a Batch to modify the db +func (gdb *GenericDB) Write(batch *leveldb.Batch) error { + return gdb.db.Write(batch, gdb.writeOptions) +} + +// Iterate will iterate through each entry in the db whose key has the prefix +// keyPrefix, and call `onEntry` with the bytes of the key (without the prefix) +// and the bytes of the value +func (gdb *GenericDB) Iterate(keyPrefix []byte, onEntry func([]byte, []byte) error) error { + iter := gdb.db.NewIterator(util.BytesPrefix(keyPrefix), nil) + defer iter.Release() + + for iter.Next() { + key := iter.Key()[len(keyPrefix):] + err := onEntry(key, iter.Value()) + if err != nil { + return err + } + } + return iter.Error() +} + +// newDB creates/opens a leveldb persistent database at the given path. +// If no path is given, an in-memory, temporary database is constructed. +func NewDB(dbVersion int64, path string, logger log.Logger) (*leveldb.DB, error) { + if path == "" { + return NewMemoryDB() + } + return NewPersistentDB(dbVersion, path, logger) +} + +// newMemoryDB creates a new in-memory node database without a persistent backend. +func NewMemoryDB() (*leveldb.DB, error) { + db, err := leveldb.Open(storage.NewMemStorage(), nil) + if err != nil { + return nil, err + } + return db, nil +} + +// newPersistentNodeDB creates/opens a leveldb backed persistent database, +// also flushing its contents in case of a version mismatch. +func NewPersistentDB(dbVersion int64, path string, logger log.Logger) (*leveldb.DB, error) { + opts := &opt.Options{OpenFilesCacheCapacity: 5} + db, err := leveldb.OpenFile(path, opts) + if _, iscorrupted := err.(*lvlerrors.ErrCorrupted); iscorrupted { + db, err = leveldb.RecoverFile(path, nil) + } + if err != nil { + return nil, err + } + currentVer := make([]byte, binary.MaxVarintLen64) + currentVer = currentVer[:binary.PutVarint(currentVer, dbVersion)] + + blob, err := db.Get([]byte(dbVersionKey), nil) + switch err { + case leveldb.ErrNotFound: + // Version not found (i.e. empty cache), insert it + if err := db.Put([]byte(dbVersionKey), currentVer, nil); err != nil { + db.Close() + return nil, err + } + + case nil: + // Version present, flush if different + if !bytes.Equal(blob, currentVer) { + oldVersion, _ := binary.Varint(blob) + newVersion, _ := binary.Varint(currentVer) + logger.Info("DB version has changed. Creating a new leveldb.", "old version", oldVersion, "new version", newVersion) + db.Close() + if err = os.RemoveAll(path); err != nil { + return nil, err + } + return NewPersistentDB(dbVersion, path, logger) + } + } + return db, nil +}
diff --git go-ethereum/consensus/istanbul/backend/snapshot.go celo/consensus/istanbul/backend/snapshot.go new file mode 100644 index 0000000000000000000000000000000000000000..f792576ffb5befafe113813aa300e1b0a1285eda --- /dev/null +++ celo/consensus/istanbul/backend/snapshot.go @@ -0,0 +1,205 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" +) + +const ( + dbKeySnapshotPrefix = "istanbul-snapshot" +) + +// Snapshot is the state of the authorization voting at a given point in time. +type Snapshot struct { + Epoch uint64 // The number of blocks for each epoch + + Number uint64 // Block number where the snapshot was created + Hash common.Hash // Block hash where the snapshot was created + ValSet istanbul.ValidatorSet // Set of authorized validators at this moment +} + +// newSnapshot create a new snapshot with the specified startup parameters. This +// method does not initialize the set of recent validators, so only ever use if for +// the genesis block. +func newSnapshot(epoch uint64, number uint64, hash common.Hash, valSet istanbul.ValidatorSet) *Snapshot { + snap := &Snapshot{ + Epoch: epoch, + Number: number, + Hash: hash, + ValSet: valSet, + } + return snap +} + +// loadSnapshot loads an existing snapshot from the database. +func loadSnapshot(epoch uint64, db ethdb.Database, hash common.Hash) (*Snapshot, error) { + blob, err := db.Get(append([]byte(dbKeySnapshotPrefix), hash[:]...)) + if err != nil { + return nil, err + } + snap := new(Snapshot) + if err := json.Unmarshal(blob, snap); err != nil { + return nil, err + } + + if !snap.ValSet.HasBLSKeyCache() { + log.Debug("Updating outdated snapshot", "hash", hash) + if err := snap.store(db); err != nil { + return nil, err + } + } + + snap.Epoch = epoch + + return snap, nil +} + +// store inserts the snapshot into the database. +func (s *Snapshot) store(db ethdb.Database) error { + s.ValSet.CacheUncompressedBLSKey() + blob, err := json.Marshal(s) + if err != nil { + return err + } + return db.Put(append([]byte(dbKeySnapshotPrefix), s.Hash[:]...), blob) +} + +// copy creates a deep copy of the snapshot, though not the individual votes. +func (s *Snapshot) copy() *Snapshot { + cpy := &Snapshot{ + Epoch: s.Epoch, + Number: s.Number, + Hash: s.Hash, + ValSet: s.ValSet.Copy(), + } + + return cpy +} + +// apply creates a new authorization snapshot by applying the given headers to +// the original one. +func (s *Snapshot) apply(headers []*types.Header, db ethdb.Database) (*Snapshot, error) { + // Allow passing in no headers for cleaner code + if len(headers) == 0 { + return s, nil + } + + // Sanity check that the headers can be applied + for i := 0; i < len(headers)-1; i++ { + if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+s.Epoch { + return nil, errInvalidVotingChain + } + } + if headers[0].Number.Uint64() != s.Number+s.Epoch { + return nil, errInvalidVotingChain + } + + // Iterate through the headers and create a new snapshot + snap := s.copy() + + for _, header := range headers { + // Resolve the authorization key and check against validators + validator, err := ecrecover(header) + if err != nil { + return nil, err + } + if _, v := snap.ValSet.GetByAddress(validator); v == nil { + return nil, errUnauthorized + } + + // Ensure that the extra data format is satisfied + istExtra, err := header.IstanbulExtra() + if err != nil { + log.Error("Unable to extract the istanbul extra field from the header", "header", header) + return nil, err + } + + validators, err := istanbul.CombineIstanbulExtraToValidatorData(istExtra.AddedValidators, istExtra.AddedValidatorsPublicKeys) + if err != nil { + log.Error("Error in combining addresses and public keys") + return nil, errInvalidValidatorSetDiff + } + + if !snap.ValSet.RemoveValidators(istExtra.RemovedValidators) { + log.Error("Error in removing the header's RemovedValidators") + return nil, errInvalidValidatorSetDiff + } + if !snap.ValSet.AddValidators(validators) { + log.Error("Error in adding the header's AddedValidators") + return nil, errInvalidValidatorSetDiff + } + + snap.Epoch = s.Epoch + snap.Number += s.Epoch + snap.Hash = header.Hash() + snap.store(db) + log.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash) + } + + return snap, nil +} + +func (s *Snapshot) validators() []istanbul.ValidatorData { + return validator.MapValidatorsToData(s.ValSet.List()) +} + +type snapshotJSON struct { + Epoch uint64 `json:"epoch"` + Number uint64 `json:"number"` + Hash common.Hash `json:"hash"` + + // for validator set + Validators []istanbul.ValidatorDataWithBLSKeyCache `json:"validators"` +} + +func (s *Snapshot) toJSONStruct() *snapshotJSON { + validators := validator.MapValidatorsToDataWithBLSKeyCache(s.ValSet.List()) + return &snapshotJSON{ + Epoch: s.Epoch, + Number: s.Number, + Hash: s.Hash, + Validators: validators, + } +} + +// UnmarshalJSON from a json byte array +func (s *Snapshot) UnmarshalJSON(b []byte) error { + var j snapshotJSON + if err := json.Unmarshal(b, &j); err != nil { + return err + } + + s.Epoch = j.Epoch + s.Number = j.Number + s.Hash = j.Hash + s.ValSet = validator.NewSetFromDataWithBLSKeyCache(j.Validators) + return nil +} + +// MarshalJSON to a json byte array +func (s *Snapshot) MarshalJSON() ([]byte, error) { + j := s.toJSONStruct() + return json.Marshal(j) +}
diff --git go-ethereum/consensus/istanbul/proxy/forward_message.go celo/consensus/istanbul/proxy/forward_message.go new file mode 100644 index 0000000000000000000000000000000000000000..782fc1797ab8a4210102a4f527a36d0264c44455 --- /dev/null +++ celo/consensus/istanbul/proxy/forward_message.go @@ -0,0 +1,93 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +func (pv *proxiedValidatorEngine) sendForwardMsg(ps *proxySet, destAddresses []common.Address, ethMsgCode uint64, payload []byte) error { + logger := pv.logger.New("func", "SendForwardMsg") + + logger.Info("Sending forward msg", "ethMsgCode", ethMsgCode, "destAddresses", common.ConvertToStringSlice(destAddresses)) + + // Send the forward messages to the proxies + for _, proxy := range ps.proxiesByID { + if proxy.IsPeered() { + + // Convert the message to a fwdMessage + msg := istanbul.NewForwardMessage(&istanbul.ForwardMessage{ + Code: ethMsgCode, + DestAddresses: destAddresses, + Msg: payload, + }, pv.backend.Address()) + + // Sign the message + if err := msg.Sign(pv.backend.Sign); err != nil { + logger.Error("Error in signing an Istanbul Forward Message", "ForwardMsg", msg.String(), "err", err) + return err + } + + fwdMsgPayload, err := msg.Payload() + if err != nil { + return err + } + + pv.backend.Unicast(proxy.peer, fwdMsgPayload, istanbul.FwdMsg) + } + } + + return nil +} + +func (p *proxyEngine) handleForwardMsg(peer consensus.Peer, payload []byte) (bool, error) { + logger := p.logger.New("func", "HandleForwardMsg") + + logger.Trace("Handling a forward message") + + // Verify that it's coming from the proxied validator + p.proxiedValidatorsMu.RLock() + defer p.proxiedValidatorsMu.RUnlock() + if ok := p.proxiedValidatorIDs[peer.Node().ID()]; !ok { + logger.Warn("Got a forward consensus message from a peer that is not the proxy's proxied validator. Ignoring it", "from", peer.Node().ID()) + return false, nil + } + + istMsg := new(istanbul.Message) + + if err := istMsg.FromPayload(payload, istanbul.GetSignatureAddress); err != nil { + logger.Error("Failed to decode message from payload", "from", peer.Node().ID(), "err", err) + return true, err + } + + // Verify that the sender is from the proxied validator + if istMsg.Address != p.config.ProxiedValidatorAddress { + logger.Error("Unauthorized forward message", "sender address", istMsg.Address, "authorized sender address", p.config.ProxiedValidatorAddress) + return true, errUnauthorizedMessageFromProxiedValidator + } + + fwdMsg := istMsg.ForwardMessage() + logger.Trace("Forwarding a message", "msg code", fwdMsg.Code) + if err := p.backend.Multicast(fwdMsg.DestAddresses, fwdMsg.Msg, fwdMsg.Code, false); err != nil { + logger.Error("Error in multicasting a forwarded message", "error", err) + return true, err + } + + return true, nil +}
diff --git go-ethereum/consensus/istanbul/core/core.go celo/consensus/istanbul/core/core.go new file mode 100644 index 0000000000000000000000000000000000000000..08a1b011a3c5588dac85544a1af9664dddaad302 --- /dev/null +++ celo/consensus/istanbul/core/core.go @@ -0,0 +1,879 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "bytes" + "fmt" + "math" + "math/big" + "sync" + "time" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/prque" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/params" + "github.com/syndtr/goleveldb/leveldb" +) + +// CoreBackend provides the Istanbul backend application specific functions for Istanbul core +type CoreBackend interface { + // Address returns the owner's address + Address() common.Address + + // ChainConfig retrieves the blockchain's chain configuration. + ChainConfig() *params.ChainConfig + + // Validators returns the validator set + Validators(proposal istanbul.Proposal) istanbul.ValidatorSet + NextBlockValidators(proposal istanbul.Proposal) (istanbul.ValidatorSet, error) + + // EventMux returns the event mux in backend + EventMux() *event.TypeMux + + // Gossip will send a message to all connnected peers + Gossip(payload []byte, ethMsgCode uint64) error + + // Multicast sends a message to it's connected nodes filtered on the 'addresses' parameter (where each address + // is associated with those node's signing key) + // If sendToSelf is set to true, then the function will send an event to self via a message event + Multicast(addresses []common.Address, payload []byte, ethMsgCode uint64, sendToSelf bool) error + + // Commit delivers an approved proposal to backend. + // The delivered proposal will be put into blockchain. + Commit(proposal istanbul.Proposal, aggregatedSeal types.IstanbulAggregatedSeal, aggregatedEpochValidatorSetSeal types.IstanbulEpochValidatorSetSeal, stateProcessResult *StateProcessResult) error + + // Verify verifies the proposal. If a consensus.ErrFutureBlock error is returned, + // the time difference of the proposal and current time is also returned. + Verify(istanbul.Proposal) (*StateProcessResult, time.Duration, error) + + // Sign signs input data with the backend's private key + Sign([]byte) ([]byte, error) + + // Sign with the data with the BLS key, using either a direct or composite hasher and optional cip22 encoding + SignBLS([]byte, []byte, bool, bool) (blscrypto.SerializedSignature, error) + + // CheckSignature verifies the signature by checking if it's signed by + // the given validator + CheckSignature(data []byte, addr common.Address, sig []byte) error + + // GetCurrentHeadBlock retrieves the last block + GetCurrentHeadBlock() istanbul.Proposal + + // GetCurrentHeadBlockAndAuthor retrieves the last block alongside the author for that block + GetCurrentHeadBlockAndAuthor() (istanbul.Proposal, common.Address) + + // LastSubject retrieves latest committed subject (view and digest) + LastSubject() (istanbul.Subject, error) + + // HasBlock checks if the combination of the given hash and height matches any existing blocks + HasBlock(hash common.Hash, number *big.Int) bool + + // AuthorForBlock returns the proposer of the given block height + AuthorForBlock(number uint64) common.Address + + // HashForBlock returns the block header hash of the given block height + HashForBlock(number uint64) common.Hash + + // ParentBlockValidators returns the validator set of the given proposal's parent block + ParentBlockValidators(proposal istanbul.Proposal) istanbul.ValidatorSet + + IsPrimaryForSeq(seq *big.Int) bool + UpdateReplicaState(seq *big.Int) +} + +type core struct { + config *istanbul.Config + address common.Address + logger log.Logger + selectProposer istanbul.ProposerSelector + + backend CoreBackend + events *event.TypeMuxSubscription + finalCommittedSub *event.TypeMuxSubscription + timeoutSub *event.TypeMuxSubscription + + futurePreprepareTimer *time.Timer + resendRoundChangeMessageTimer *time.Timer + resendRoundChangeMessageTimerMu sync.Mutex + + roundChangeTimer *time.Timer + roundChangeTimerMu sync.RWMutex + + validateFn istanbul.ValidateFn + + backlog MsgBacklog + + rsdb RoundStateDB + current RoundState + currentMu sync.RWMutex + handlerWg *sync.WaitGroup + + roundChangeSetV2 *roundChangeSetV2 + + pendingRequests *prque.Prque + pendingRequestsMu *sync.Mutex + + consensusTimestamp time.Time + + // Time from accepting a pre-prepare (after block verifcation) to preparing or committing + consensusPrepareTimeGauge metrics.Gauge + consensusCommitTimeGauge metrics.Gauge + // Time to verify blocks. Only records cache misses. + verifyGauge metrics.Gauge + // Historgram of the time to handle each message type + handlePrePrepareTimer metrics.Timer + handlePrepareTimer metrics.Timer + handleCommitTimer metrics.Timer +} + +// New creates an Istanbul consensus core +func New(backend CoreBackend, config *istanbul.Config) Engine { + + c := &core{ + config: config, + address: backend.Address(), + logger: log.New(), + selectProposer: validator.GetProposerSelector(config.ProposerPolicy), + handlerWg: new(sync.WaitGroup), + backend: backend, + pendingRequests: prque.New(nil), + pendingRequestsMu: new(sync.Mutex), + consensusTimestamp: time.Time{}, + consensusPrepareTimeGauge: metrics.NewRegisteredGauge("consensus/istanbul/core/consensus_prepare", nil), + consensusCommitTimeGauge: metrics.NewRegisteredGauge("consensus/istanbul/core/consensus_commit", nil), + verifyGauge: metrics.NewRegisteredGauge("consensus/istanbul/core/verify", nil), + handlePrePrepareTimer: metrics.NewRegisteredTimer("consensus/istanbul/core/handle_preprepare", nil), + handlePrepareTimer: metrics.NewRegisteredTimer("consensus/istanbul/core/handle_prepare", nil), + handleCommitTimer: metrics.NewRegisteredTimer("consensus/istanbul/core/handle_commit", nil), + } + msgBacklog := newMsgBacklog( + func(msg *istanbul.Message) { + c.sendEvent(backlogEvent{ + msg: msg, + }) + }, c.checkMessage) + c.backlog = msgBacklog + c.validateFn = c.checkValidatorSignature + return c +} + +// ---------------------------------------------------------------------------- + +func (c *core) SetAddress(address common.Address) { + c.address = address + c.logger = log.New("address", address) +} + +func (c *core) CurrentView() *istanbul.View { + // CurrentView is called by Prepare which is called by miner.worker the + // main loop, we need to synchronise this access with the write which occurs + // in Stop, which is called from the miner's update loop. + c.currentMu.RLock() + defer c.currentMu.RUnlock() + if c.current == nil { + return nil + } + return c.current.View() +} + +func (c *core) CurrentRoundState() RoundState { return c.current } + +func (c *core) ParentCommits() MessageSet { + // ParentCommits is called by Prepare which is called by miner.worker the + // main loop, we need to synchronise this access with the write which + // occurs in Stop, which is called from the miner's update loop. + c.currentMu.RLock() + defer c.currentMu.RUnlock() + if c.current == nil { + return nil + } + return c.current.ParentCommits() +} + +func (c *core) ForceRoundChange() { + // timeout current DesiredView + view := &istanbul.View{Sequence: c.current.Sequence(), Round: c.current.DesiredRound()} + c.sendEvent(timeoutAndMoveToNextRoundEvent{view}) +} + +// PrepareCommittedSeal returns a committed seal for the given hash and round number. +func PrepareCommittedSeal(hash common.Hash, round *big.Int) []byte { + var buf bytes.Buffer + buf.Write(hash.Bytes()) + buf.Write(round.Bytes()) + buf.Write([]byte{byte(istanbul.MsgCommit)}) + return buf.Bytes() +} + +// GetAggregatedSeal aggregates all the given seals for a given message set to a bls aggregated +// signature and bitmap +func GetAggregatedSeal(seals MessageSet, round *big.Int) (types.IstanbulAggregatedSeal, error) { + bitmap := big.NewInt(0) + committedSeals := make([][]byte, seals.Size()) + for i, v := range seals.Values() { + committedSeals[i] = make([]byte, types.IstanbulExtraBlsSignature) + commit := v.Commit() + copy(committedSeals[i][:], commit.CommittedSeal[:]) + + j, err := seals.GetAddressIndex(v.Address) + if err != nil { + return types.IstanbulAggregatedSeal{}, err + } + bitmap.SetBit(bitmap, int(j), 1) + } + + asig, err := blscrypto.AggregateSignatures(committedSeals) + if err != nil { + return types.IstanbulAggregatedSeal{}, err + } + return types.IstanbulAggregatedSeal{Bitmap: bitmap, Signature: asig, Round: round}, nil +} + +// UnionOfSeals combines a BLS aggregated signature with an array of signatures. Accounts for +// double aggregating the same signature by only adding aggregating if the +// validator was not found in the previous bitmap. +// This function assumes that the provided seals' validator set is the same one +// which produced the provided bitmap +func UnionOfSeals(aggregatedSignature types.IstanbulAggregatedSeal, seals MessageSet) (types.IstanbulAggregatedSeal, error) { + // TODO(asa): Check for round equality... + // Check who already has signed the message + newBitmap := new(big.Int).Set(aggregatedSignature.Bitmap) + committedSeals := [][]byte{} + committedSeals = append(committedSeals, aggregatedSignature.Signature) + for _, v := range seals.Values() { + valIndex, err := seals.GetAddressIndex(v.Address) + if err != nil { + return types.IstanbulAggregatedSeal{}, err + } + + // if the bit was not set, this means we should add this signature to + // the batch + if newBitmap.Bit(int(valIndex)) == 0 { + newBitmap.SetBit(newBitmap, (int(valIndex)), 1) + committedSeals = append(committedSeals, v.Commit().CommittedSeal) + } + } + + asig, err := blscrypto.AggregateSignatures(committedSeals) + if err != nil { + return types.IstanbulAggregatedSeal{}, err + } + + return types.IstanbulAggregatedSeal{ + Bitmap: newBitmap, + Signature: asig, + Round: aggregatedSignature.Round, + }, nil +} + +// Appends the current view and state to the given context. +func (c *core) newLogger(ctx ...interface{}) log.Logger { + var seq, round, desired *big.Int + var state State + var epoch uint64 + if c.current != nil { + state = c.current.State() + seq = c.current.Sequence() + epoch = istanbul.GetEpochNumber(seq.Uint64(), c.config.Epoch) + round = c.current.Round() + desired = c.current.DesiredRound() + } else { + seq = common.Big0 + epoch = 0 + round = big.NewInt(-1) + desired = big.NewInt(-1) + } + logger := c.logger.New(ctx...) + return logger.New("cur_seq", seq, "cur_epoch", epoch, "cur_round", round, "des_round", desired, "state", state, "address", c.address) +} + +func (c *core) finalizeMessage(msg *istanbul.Message) ([]byte, error) { + // Add sender address + msg.Address = c.address + + if err := msg.Sign(c.backend.Sign); err != nil { + return nil, err + } + + // Convert to payload + payload, err := msg.Payload() + if err != nil { + return nil, err + } + + return payload, nil +} + +// gossip broadcasts an already existing & signed message. +func (c *core) gossip(msg *istanbul.Message) { + c.gossipTo(msg, istanbul.MapValidatorsToAddresses(c.current.ValidatorSet().List())) +} + +// gossipTo broadcasts an already existing & signed message to the specified addresses. +func (c *core) gossipTo(msg *istanbul.Message, addresses []common.Address) { + logger := c.newLogger("func", "gossipTo") + + if len(msg.Signature) == 0 { + // should use broadcast() instead + logger.Error("Tried to gossip unsigned istanbul message", "m", msg) + return + } + logger.Trace("Gossipping message", "msg.Address", msg.Address.Hex(), "msg.Code", msg.Code) + // Convert to payload + payload, err := msg.Payload() + if err != nil { + logger.Error("Failed to convert message to payload", "m", msg, "err", err) + return + } + + // Send payload to the specified addresses + if err := c.backend.Multicast(addresses, payload, istanbul.ConsensusMsg, true); err != nil { + logger.Error("Failed to send message", "m", msg, "err", err) + return + } +} + +// Send message to all current validators +func (c *core) broadcast(msg *istanbul.Message) { + c.sendMsgTo(msg, istanbul.MapValidatorsToAddresses(c.current.ValidatorSet().List())) +} + +// Send message to a specific address +func (c *core) unicast(msg *istanbul.Message, addr common.Address) { + c.sendMsgTo(msg, []common.Address{addr}) +} + +func (c *core) sendMsgTo(msg *istanbul.Message, addresses []common.Address) { + logger := c.newLogger("func", "sendMsgTo") + + payload, err := c.finalizeMessage(msg) + if err != nil { + logger.Error("Failed to finalize message", "m", msg, "err", err) + return + } + + // Send payload to the specified addresses + if err := c.backend.Multicast(addresses, payload, istanbul.ConsensusMsg, true); err != nil { + logger.Error("Failed to send message", "m", msg, "err", err) + return + } +} + +func (c *core) commit() error { + logger := c.newLogger("func", "commit", "proposal", c.current.Proposal()) + err := c.current.TransitionToCommitted() + if err != nil { + return err + } + + // Update metrics. + if !c.consensusTimestamp.IsZero() { + c.consensusCommitTimeGauge.Update(time.Since(c.consensusTimestamp).Nanoseconds()) + c.consensusTimestamp = time.Time{} + } + + // Process Backlog Messages + c.backlog.updateState(c.current.View(), c.current.State()) + + proposal := c.current.Proposal() + if proposal != nil { + aggregatedSeal, err := GetAggregatedSeal(c.current.Commits(), c.current.Round()) + if err != nil { + nextRound := new(big.Int).Add(c.current.Round(), common.Big1) + logger.Warn("Error on commit, waiting for desired round", "reason", "getAggregatedSeal", "err", err, "desired_round", nextRound) + c.waitForDesiredRound(nextRound) + return nil + } + aggregatedEpochValidatorSetSeal, err := GetAggregatedEpochValidatorSetSeal(proposal.Number().Uint64(), c.config.Epoch, c.current.Commits()) + if err != nil { + nextRound := new(big.Int).Add(c.current.Round(), common.Big1) + c.logger.Warn("Error on commit, waiting for desired round", "reason", "GetAggregatedEpochValidatorSetSeal", "err", err, "desired_round", nextRound) + c.waitForDesiredRound(nextRound) + return nil + } + + // Query the StateProcessResult cache, nil if it's cache miss + result := c.current.GetStateProcessResult(proposal.Hash()) + if err := c.backend.Commit(proposal, aggregatedSeal, aggregatedEpochValidatorSetSeal, result); err != nil { + nextRound := new(big.Int).Add(c.current.Round(), common.Big1) + logger.Warn("Error on commit, waiting for desired round", "reason", "backend.Commit", "err", err, "desired_round", nextRound) + c.waitForDesiredRound(nextRound) + return nil + } + } + + logger.Info("Committed") + return nil +} + +// GetAggregatedEpochValidatorSetSeal aggregates all the given seals for the SNARK-friendly epoch encoding +// to a bls aggregated signature. Returns an empty signature on a non-epoch block. +func GetAggregatedEpochValidatorSetSeal(blockNumber, epoch uint64, seals MessageSet) (types.IstanbulEpochValidatorSetSeal, error) { + if !istanbul.IsLastBlockOfEpoch(blockNumber, epoch) { + return types.IstanbulEpochValidatorSetSeal{}, nil + } + bitmap := big.NewInt(0) + epochSeals := make([][]byte, seals.Size()) + for i, v := range seals.Values() { + epochSeals[i] = make([]byte, types.IstanbulExtraBlsSignature) + + copy(epochSeals[i], v.Commit().EpochValidatorSetSeal[:]) + + j, err := seals.GetAddressIndex(v.Address) + if err != nil { + return types.IstanbulEpochValidatorSetSeal{}, err + } + bitmap.SetBit(bitmap, int(j), 1) + } + + asig, err := blscrypto.AggregateSignatures(epochSeals) + if err != nil { + return types.IstanbulEpochValidatorSetSeal{}, err + } + return types.IstanbulEpochValidatorSetSeal{Bitmap: bitmap, Signature: asig}, nil +} + +// getPreprepareWithRoundChangeCertificateV2 Generates the next preprepare request and associated round change certificate +func (c *core) getPreprepareWithRoundChangeCertificateV2(round *big.Int) (*istanbul.Request, istanbul.RoundChangeCertificateV2, error) { + logger := c.newLogger("func", "getPreprepareWithRoundChangeCertificate", "for_round", round) + + roundChangeCertificateV2, proposals, err := c.roundChangeSetV2.getCertificate(round, c.current.ValidatorSet().MinQuorumSize()) + if err != nil { + return &istanbul.Request{}, istanbul.RoundChangeCertificateV2{}, err + } + // Do View verification + for _, req := range roundChangeCertificateV2.Requests { + if !req.HasPreparedCertificate() { + continue + } + + _, err := c.getViewFromVerifiedPreparedCertificateV2(req.PreparedCertificateV2) + if err != nil { + logger.Error("Unexpected: could not verify a previously received PreparedCertificate round change request", "src_m", req) + return &istanbul.Request{}, istanbul.RoundChangeCertificateV2{}, err + } + } + + // Start with pending request + request := c.current.PendingRequest() + // Search for a valid request in round change messages. + // The proposal must come from the prepared certificate with the highest round number. + // All prepared certificates from the same round are assumed to be the same proposal or no proposal (guaranteed by quorum intersection) + maxPC := roundChangeCertificateV2.AnyHighestPreparedCertificate() + if maxPC == nil { + return request, roundChangeCertificateV2, nil + } + if proposal, ok := proposals[maxPC.ProposalHash]; ok { + return &istanbul.Request{ + Proposal: proposal, + }, roundChangeCertificateV2, nil + } else { + logger.Error("Proposal not found from roundChangeSetV2.getCertificate") + } + return request, roundChangeCertificateV2, nil +} + +// startNewRound starts a new round with the desired round +func (c *core) startNewRound(round *big.Int, propose bool) error { + logger := c.newLogger("func", "startNewRound", "tag", "stateTransition") + + if round.Cmp(c.current.Round()) == 0 { + logger.Trace("Already in the desired round.") + return nil + } else if round.Cmp(c.current.Round()) < 0 { + logger.Warn("New round should not be smaller than current round", "new_round", round) + return nil + } + + // Generate next view and preprepare + newView := &istanbul.View{ + Sequence: new(big.Int).Set(c.current.Sequence()), + Round: new(big.Int).Set(round), + } + + // Calculate new proposer + prevProposer := c.current.Proposer() + prevBlock := c.current.Sequence().Uint64() - 1 + blockAuthor := c.backend.AuthorForBlock(prevBlock) + valSet := c.current.ValidatorSet() + nextProposer := c.selectProposer(valSet, blockAuthor, newView.Round.Uint64()) + + var err error + var request *istanbul.Request + var roundChangeCertificateV2 istanbul.RoundChangeCertificateV2 + + // startNewRound is called from two different places: handleRoundChange and handleRoundChangeCertificate. + // The first occurs when receiving a RoundChange(V1 or V2) message, and the second when receiving a Preprepare(V1 or V2) message (round >= 1). + + // In the second case, during a preprepare handling, this function is creating a preprepare and a + // roundchangecertificate with the round change messages that it received in the roundchangecertificate; This generated RCC + // won't be used. + + // With the V2 istanbul version of the RoundChangeCertificate, the round change messages may not be available, + // therefore it is not possible to create the RCC_V2 by using the same RoundChangeSet + // The solution was to modify completely how the roundChangeSet works, + // but since the co-existence of V1 and V2 are temporary, the propose flag should be enough. + // If necessary, removal of this flag should be done after successfully removing al v1 code. + if c.address == nextProposer.Address() && propose { + request, roundChangeCertificateV2, err = c.getPreprepareWithRoundChangeCertificateV2(round) + if err != nil { + logger.Error("Unable to produce round change certificate v2", "err", err, "new_round", round) + return nil + } + } + + // Update the roundstate db + c.current.StartNewRound(round, valSet, nextProposer) + + // Process backlog + c.processPendingRequests() + c.backlog.updateState(c.current.View(), c.current.State()) + + if c.isProposer() && request != nil { + c.sendPreprepareV2(request, roundChangeCertificateV2) + } + c.resetRoundChangeTimer() + + // Some round info will have changed. + logger = c.newLogger("func", "startNewRound", "tag", "stateTransition", "old_proposer", prevProposer) + logger.Debug("New round", "new_round", newView.Round, "new_seq", newView.Sequence, "new_proposer", c.current.Proposer(), "valSet", c.current.ValidatorSet().List(), "size", c.current.ValidatorSet().Size(), "isProposer", c.isProposer()) + return nil +} + +// startNewSequence starts a new sequence with round 0. +func (c *core) startNewSequence() error { + // Try to get most recent block + headBlock, headAuthor := c.backend.GetCurrentHeadBlockAndAuthor() + + logger := c.newLogger("func", "startNewSequence", "tag", "stateTransition", "head_block", headBlock.Number().Uint64(), "head_block_hash", headBlock.Hash()) + + if headBlock.Number().Cmp(c.current.Sequence()) == 0 { + logger.Trace("Moving to the next block") + } else if headBlock.Number().Cmp(c.current.Sequence()) > 0 { + logger.Trace("Catching up the the head block") + } else { + logger.Warn("New sequence should be larger than current sequence") + // TODO(Joshua): figure out if we need to wait for the next block to be mined here + // This function is called on a final committed event which should occur once the block is inserted into the chain. + return nil + } + + // Generate next view and preprepare + newView := &istanbul.View{ + Sequence: new(big.Int).Add(headBlock.Number(), common.Big1), + Round: new(big.Int).Set(common.Big0), + } + valSet := c.backend.Validators(headBlock) + c.roundChangeSetV2 = newRoundChangeSetV2(valSet) + + // Inform the backend that a new sequence has started & bail if the backed stopped the core + if primary := c.backend.IsPrimaryForSeq(newView.Sequence); !primary { + // We need to run UpdateReplicaState outside of the core b/c when it stops the core + // it runs c.handlerWg.Wait(). To empty that wait group, we need to return from this + // function. We unsubscribe from events to stop the core from processing more events + // prior to being fully shut down. + c.unsubscribeEvents() + go c.backend.UpdateReplicaState(newView.Sequence) + return nil + } + + // Calculate new proposer + prevProposer := c.current.Proposer() + nextProposer := c.selectProposer(valSet, headAuthor, newView.Round.Uint64()) + + // Update the roundstate + err := c.resetRoundState(newView, valSet, nextProposer) + if err != nil { + return err + } + + // Process backlog + c.processPendingRequests() + c.backlog.updateState(c.current.View(), c.current.State()) + + c.resetRoundChangeTimer() + + // Some round info will have changed. + logger = c.newLogger("func", "startNewSequence", "tag", "stateTransition", "old_proposer", prevProposer, "head_block", headBlock.Number().Uint64(), "head_block_hash", headBlock.Hash()) + logger.Debug("New sequence", "new_round", newView.Round, "new_seq", newView.Sequence, "new_proposer", nextProposer, "valSet", c.current.ValidatorSet().List(), "size", c.current.ValidatorSet().Size(), "isProposer", c.isProposer()) + return nil +} + +// All actions that occur when transitioning to waiting for round change state. +func (c *core) waitForDesiredRound(r *big.Int) error { + logger := c.newLogger("func", "waitForDesiredRound", "new_desired_round", r) + + // Don't wait for an older or equal round + if c.current.DesiredRound().Cmp(r) >= 0 { + logger.Trace("New desired round not greater than current desired round") + return nil + } + + logger.Debug("Round Change: Waiting for desired round") + + // Perform all of the updates + _, headAuthor := c.backend.GetCurrentHeadBlockAndAuthor() + nextProposer := c.selectProposer(c.current.ValidatorSet(), headAuthor, r.Uint64()) + err := c.current.TransitionToWaitingForNewRound(r, nextProposer) + if err != nil { + return err + } + + c.resetRoundChangeTimer() + + // Process Backlog Messages + c.backlog.updateState(c.current.View(), c.current.State()) + + // Send round change + c.sendRoundChange() + return nil +} + +func (c *core) createRoundState() (RoundState, error) { + var roundState RoundState + + logger := c.newLogger("func", "createRoundState") + + if c.current != nil { + return nil, fmt.Errorf("BUG? Attempting to Start() core with existing c.current") + } + + headBlock, headAuthor := c.backend.GetCurrentHeadBlockAndAuthor() + nextSequence := new(big.Int).Add(headBlock.Number(), common.Big1) + lastStoredView, err := c.rsdb.GetLastView() + + if err != nil && err != leveldb.ErrNotFound { + logger.Error("Failed to fetch lastStoredView", "err", err) + return nil, err + } + + if err == leveldb.ErrNotFound || lastStoredView.Sequence.Cmp(nextSequence) < 0 { + if err == leveldb.ErrNotFound { + logger.Info("Creating new RoundState", "reason", "No storedView found") + } else { + logger.Info("Creating new RoundState", "reason", "old view", "stored_view", lastStoredView, "requested_seq", nextSequence) + } + valSet := c.backend.Validators(headBlock) + proposer := c.selectProposer(valSet, headAuthor, 0) + roundState = newRoundState(&istanbul.View{Sequence: nextSequence, Round: common.Big0}, valSet, proposer) + } else { + logger.Info("Retrieving stored RoundState", "stored_view", lastStoredView, "requested_seq", nextSequence) + roundState, err = c.rsdb.GetRoundStateFor(lastStoredView) + + if err != nil { + logger.Error("Failed to fetch lastStoredRoundState", "err", err) + return nil, err + } + } + + return withSavingDecorator(c.rsdb, roundState), nil +} + +// resetRoundState will modify the RoundState to start a new sequence +func (c *core) resetRoundState(view *istanbul.View, validatorSet istanbul.ValidatorSet, nextProposer istanbul.Validator) error { + // TODO remove this when we refactor startNewRound() + if view.Round.Cmp(common.Big0) != 0 { + c.logger.Crit("BUG: DevError: trying to start a new sequence with round != 0", "wanted_round", view.Round) + } + + var newParentCommits MessageSet + lastSubject, err := c.backend.LastSubject() + if err == nil && c.current.Proposal() != nil && c.current.Proposal().Hash() == lastSubject.Digest && c.current.Round().Cmp(lastSubject.View.Round) == 0 { + // When changing sequences, if our current Commit messages match the latest block in the chain + // (i.e. they're for the same block hash and round), we use this sequence's commits as the ParentCommits field + // in the next round. + newParentCommits = c.current.Commits() + } else { + // Otherwise, we will initialize an empty ParentCommits field with the validator set of the last proposal. + headBlock := c.backend.GetCurrentHeadBlock() + newParentCommits = newMessageSet(c.backend.ParentBlockValidators(headBlock)) + } + return c.current.StartNewSequence(view.Sequence, validatorSet, nextProposer, newParentCommits) +} + +func (c *core) isProposer() bool { + if c.current == nil { + return false + } + return c.current.IsProposer(c.address) +} + +func (c *core) stopFuturePreprepareTimer() { + if c.futurePreprepareTimer != nil { + c.futurePreprepareTimer.Stop() + c.futurePreprepareTimer = nil + } +} + +func (c *core) stopRoundChangeTimer() { + c.roundChangeTimerMu.Lock() + if c.roundChangeTimer != nil { + c.roundChangeTimer.Stop() + c.roundChangeTimer = nil + } + c.roundChangeTimerMu.Unlock() +} + +func (c *core) stopResendRoundChangeTimer() { + c.resendRoundChangeMessageTimerMu.Lock() + defer c.resendRoundChangeMessageTimerMu.Unlock() + if c.resendRoundChangeMessageTimer != nil { + c.resendRoundChangeMessageTimer.Stop() + c.resendRoundChangeMessageTimer = nil + } +} + +func (c *core) stopAllTimers() { + c.stopFuturePreprepareTimer() + c.stopRoundChangeTimer() + c.stopResendRoundChangeTimer() +} + +func (c *core) getRoundChangeTimeout() time.Duration { + /* + - Prior to Espresso: + Round 0 = baseTimeout + block time + Round n = baseTimeout + 2^n * backoff factor + + - After Espresso: + Round 0 = baseTimeout + block time + Round n = baseTimeout + block time + 2^n * backoff factor + + - Compare: + Round before E after E + 0 8 8 + 1 5 10 + 2 7 12 + 3 11 16 + 4 19 24 + 5 35 40 + 6 67 72 + 7 131 136 + 8 259 264 + 9 515 520 + 10 1027 1032 + */ + baseTimeout := time.Duration(c.config.RequestTimeout) * time.Millisecond + blockTime := time.Duration(c.config.BlockPeriod) * time.Second + round := c.current.DesiredRound().Uint64() + if round == 0 { + return baseTimeout + blockTime + } else { + if c.backend.ChainConfig().IsEspresso(c.current.Sequence()) { + return baseTimeout + blockTime + time.Duration(math.Pow(2, float64(round)))*time.Duration(c.config.TimeoutBackoffFactor)*time.Millisecond + } else { + return baseTimeout + time.Duration(math.Pow(2, float64(round)))*time.Duration(c.config.TimeoutBackoffFactor)*time.Millisecond + } + } +} + +// Reset then set the timer that causes a timeoutAndMoveToNextRoundEvent to be processed. +// This may also reset the timer for the next resendRoundChangeEvent. +func (c *core) resetRoundChangeTimer() { + // Stop all timers here since all 'resends' happen within the interval of a round's timeout. + // (Races are handled anyway by checking the seq and desired round haven't changed between + // submitting and processing events). + c.stopAllTimers() + + view := &istanbul.View{Sequence: c.current.Sequence(), Round: c.current.DesiredRound()} + timeout := c.getRoundChangeTimeout() + c.roundChangeTimerMu.Lock() + c.roundChangeTimer = time.AfterFunc(timeout, func() { + c.sendEvent(timeoutAndMoveToNextRoundEvent{view}) + }) + c.roundChangeTimerMu.Unlock() + + if c.current.DesiredRound().Cmp(common.Big1) > 0 { + logger := c.newLogger("func", "resetRoundChangeTimer") + logger.Info("Reset timer to do round change", "timeout", timeout) + } + + c.resetResendRoundChangeTimer() +} + +// Reset then, if in StateWaitingForNewRound and on round whose timeout is greater than MinResendRoundChangeTimeout, +// set a timer that is at most MaxResendRoundChangeTimeout that causes a resendRoundChangeEvent to be processed. +func (c *core) resetResendRoundChangeTimer() { + c.stopResendRoundChangeTimer() + if c.current.State() == StateWaitingForNewRound { + minResendTimeout := time.Duration(c.config.MinResendRoundChangeTimeout) * time.Millisecond + resendTimeout := c.getRoundChangeTimeout() / 2 + if resendTimeout < minResendTimeout { + return + } + maxResendTimeout := time.Duration(c.config.MaxResendRoundChangeTimeout) * time.Millisecond + if resendTimeout > maxResendTimeout { + resendTimeout = maxResendTimeout + } + view := &istanbul.View{Sequence: c.current.Sequence(), Round: c.current.DesiredRound()} + c.resendRoundChangeMessageTimerMu.Lock() + defer c.resendRoundChangeMessageTimerMu.Unlock() + c.resendRoundChangeMessageTimer = time.AfterFunc(resendTimeout, func() { + c.sendEvent(resendRoundChangeEvent{view}) + }) + + logger := c.newLogger("func", "resetResendRoundChangeTimer") + logger.Info("Reset timer to resend RoundChange msg", "timeout", resendTimeout) + } +} + +// Rebroadcast RoundChange message for desired round if still in StateWaitingForNewRound. +// Do not advance desired round. Then clear/reset timer so we may rebroadcast again. +func (c *core) resendRoundChangeMessage() { + if c.current.State() == StateWaitingForNewRound { + c.sendRoundChange() + } + c.resetResendRoundChangeTimer() +} + +func (c *core) checkValidatorSignature(data []byte, sig []byte) (common.Address, error) { + return istanbul.CheckValidatorSignature(c.current.ValidatorSet(), data, sig) +} + +func (c *core) verifyProposal(proposal istanbul.Proposal) (time.Duration, error) { + logger := c.newLogger("func", "verifyProposal", "proposal", proposal.Hash()) + if verificationStatus, isCached := c.current.GetProposalVerificationStatus(proposal.Hash()); isCached { + logger.Trace("verification status cache hit", "verificationStatus", verificationStatus) + return 0, verificationStatus + } else { + logger.Trace("verification status cache miss") + defer func(start time.Time) { c.verifyGauge.Update(time.Since(start).Nanoseconds()) }(time.Now()) + + result, duration, err := c.backend.Verify(proposal) + logger.Trace("proposal verify return values", "duration", duration, "err", err) + + // Don't cache the verification status if it's a future block + if err != consensus.ErrFutureBlock { + c.current.SetProposalVerificationStatus(proposal.Hash(), err) + } + // If err is nil, then result is non-nil, only then we set the cache + if err == nil { + c.current.SetStateProcessResult(proposal.Hash(), result) + } + + return duration, err + } +}
diff --git go-ethereum/consensus/istanbul/core/commit.go celo/consensus/istanbul/core/commit.go new file mode 100644 index 0000000000000000000000000000000000000000..4c55544a4b2e661dc8c058f44d924cbe9141c3bf --- /dev/null +++ celo/consensus/istanbul/core/commit.go @@ -0,0 +1,300 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "errors" + "math/big" + "reflect" + "time" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +// maxValidators represents the maximum number of validators the SNARK circuit supports +// The prover code will then pad any proofs to this maximum to ensure consistent proof structure +// TODO: Eventually make this governable +const maxValidators = uint32(150) + +func (c *core) sendCommit() { + logger := c.newLogger("func", "sendCommit") + logger.Trace("Sending commit") + sub := c.current.Subject() + c.broadcastCommit(sub) +} + +func (c *core) generateCommittedSeal(sub *istanbul.Subject) (blscrypto.SerializedSignature, error) { + seal := PrepareCommittedSeal(sub.Digest, sub.View.Round) + committedSeal, err := c.backend.SignBLS(seal, []byte{}, false, false) + if err != nil { + return blscrypto.SerializedSignature{}, err + } + return committedSeal, nil +} + +// Generates serialized epoch data for use in the Plumo SNARK circuit. +// Block number and hash may be information for a pending block. +func (c *core) generateEpochValidatorSetData(blockNumber uint64, round uint8, blockHash common.Hash, newValSet istanbul.ValidatorSet) ([]byte, []byte, bool, error) { + if !istanbul.IsLastBlockOfEpoch(blockNumber, c.config.Epoch) { + return nil, nil, false, errNotLastBlockInEpoch + } + + // Serialize the public keys for the validators in the validator set. + blsPubKeys := []blscrypto.SerializedPublicKey{} + for _, v := range newValSet.List() { + blsPubKeys = append(blsPubKeys, v.BLSPublicKey()) + } + + maxNonSigners := uint32(newValSet.Size() - newValSet.MinQuorumSize()) + + // Before the Donut fork, use the snark data encoding with epoch entropy. + if !c.backend.ChainConfig().IsDonut(big.NewInt(int64(blockNumber))) { + message, extraData, err := blscrypto.EncodeEpochSnarkData( + blsPubKeys, maxNonSigners, + uint16(istanbul.GetEpochNumber(blockNumber, c.config.Epoch)), + ) + // This is before the Donut hardfork, so signify this doesn't use CIP22. + return message, extraData, false, err + } + + // Retrieve the block hash for the last block of the previous epoch. + parentEpochBlockHash := c.backend.HashForBlock(blockNumber - c.config.Epoch) + if blockNumber > 0 && parentEpochBlockHash == (common.Hash{}) { + return nil, nil, false, errors.New("unknown block") + } + + maxNonSigners = maxValidators - uint32(newValSet.MinQuorumSize()) + message, extraData, err := blscrypto.EncodeEpochSnarkDataCIP22( + blsPubKeys, maxNonSigners, maxValidators, + uint16(istanbul.GetEpochNumber(blockNumber, c.config.Epoch)), + round, + blscrypto.EpochEntropyFromHash(blockHash), + blscrypto.EpochEntropyFromHash(parentEpochBlockHash), + ) + // This is after the Donut hardfork, so signify this uses CIP22. + return message, extraData, true, err +} + +func (c *core) broadcastCommit(sub *istanbul.Subject) { + logger := c.newLogger("func", "broadcastCommit") + + committedSeal, err := c.generateCommittedSeal(sub) + if err != nil { + logger.Error("Failed to commit seal", "err", err) + return + } + + currentBlockNumber := c.current.Proposal().Number().Uint64() + newValSet, err := c.backend.NextBlockValidators(c.current.Proposal()) + if err != nil { + logger.Error("Failed to get next block's validators", "err", err) + return + } + epochValidatorSetData, epochValidatorSetExtraData, cip22, err := c.generateEpochValidatorSetData(currentBlockNumber, uint8(sub.View.Round.Uint64()), sub.Digest, newValSet) + if err != nil && err != errNotLastBlockInEpoch { + logger.Error("Failed to create epoch validator set data", "err", err) + return + } + var epochValidatorSetSeal blscrypto.SerializedSignature + if err == nil { + epochValidatorSetSeal, err = c.backend.SignBLS(epochValidatorSetData, epochValidatorSetExtraData, true, cip22) + if err != nil { + logger.Error("Failed to sign epoch validator set seal", "err", err) + return + } + } + istMsg := istanbul.NewCommitMessage(&istanbul.CommittedSubject{ + Subject: sub, + CommittedSeal: committedSeal[:], + EpochValidatorSetSeal: epochValidatorSetSeal[:], + }, c.address) + c.broadcast(istMsg) +} + +func (c *core) handleCommit(msg *istanbul.Message) error { + defer c.handleCommitTimer.UpdateSince(time.Now()) + commit := msg.Commit() + err := c.checkMessage(istanbul.MsgCommit, commit.Subject.View) + if err == errOldMessage { + // Discard messages from previous views, unless they are commits from the previous sequence, + // with the same round as what we wound up finalizing, as we would be able to include those + // to create the ParentAggregatedSeal for our next proposal. + lastSubject, err := c.backend.LastSubject() + if err != nil { + return err + } else if commit.Subject.View.Cmp(lastSubject.View) != 0 { + return errOldMessage + } else if lastSubject.View.Sequence.Cmp(common.Big0) == 0 { + // Don't handle commits for the genesis block, will cause underflows + return errOldMessage + } + return c.handleCheckedCommitForPreviousSequence(msg, commit) + } else if err != nil { + return err + } + + return c.handleCheckedCommitForCurrentSequence(msg, commit) +} + +func (c *core) handleCheckedCommitForPreviousSequence(msg *istanbul.Message, commit *istanbul.CommittedSubject) error { + logger := c.newLogger("func", "handleCheckedCommitForPreviousSequence", "tag", "handleMsg", "msg_view", commit.Subject.View) + headBlock := c.backend.GetCurrentHeadBlock() + // Retrieve the validator set for the previous proposal (which should + // match the one broadcast) + parentValset := c.backend.ParentBlockValidators(headBlock) + _, validator := parentValset.GetByAddress(msg.Address) + if validator == nil { + return errInvalidValidatorAddress + } + if err := c.verifyCommittedSeal(commit, validator); err != nil { + return errInvalidCommittedSeal + } + if headBlock.Number().Uint64() > 0 { + if err := c.verifyEpochValidatorSetSeal(commit, headBlock.Number().Uint64(), c.current.ValidatorSet(), validator); err != nil { + return errInvalidEpochValidatorSetSeal + } + } + + // Ensure that the commit's digest (ie the received proposal's hash) matches the head block's hash + if headBlock.Number().Uint64() > 0 && commit.Subject.Digest != headBlock.Hash() { + logger.Debug("Received a commit message for the previous sequence with an unexpected hash", "expected", headBlock.Hash().String(), "received", commit.Subject.Digest.String()) + return errInconsistentSubject + } + + // Add the ParentCommit to current round state + if err := c.current.AddParentCommit(msg); err != nil { + logger.Error("Failed to record parent seal", "m", msg, "err", err) + return err + } + return nil +} + +func (c *core) handleCheckedCommitForCurrentSequence(msg *istanbul.Message, commit *istanbul.CommittedSubject) error { + logger := c.newLogger("func", "handleCheckedCommitForCurrentSequence", "tag", "handleMsg") + validator := c.current.GetValidatorByAddress(msg.Address) + if validator == nil { + return errInvalidValidatorAddress + } + + if err := c.verifyCommittedSeal(commit, validator); err != nil { + return errInvalidCommittedSeal + } + + newValSet, err := c.backend.NextBlockValidators(c.current.Proposal()) + if err != nil { + return err + } + + if err := c.verifyEpochValidatorSetSeal(commit, c.current.Proposal().Number().Uint64(), newValSet, validator); err != nil { + return errInvalidEpochValidatorSetSeal + } + + // ensure that the commit is in the current proposal + if err := c.verifyCommit(commit); err != nil { + return err + } + + // Add the COMMIT message to current round state + if err := c.current.AddCommit(msg); err != nil { + logger.Error("Failed to record commit message", "m", msg, "err", err) + return err + } + numberOfCommits := c.current.Commits().Size() + minQuorumSize := c.current.ValidatorSet().MinQuorumSize() + logger.Trace("Accepted commit for current sequence", "Number of commits", numberOfCommits) + + // Commit the proposal once we have enough COMMIT messages and we are not in the Committed state. + // + // If we already have a proposal, we may have chance to speed up the consensus process + // by committing the proposal without PREPARE messages. + // TODO(joshua): Remove state comparisons (or change the cmp function) + if numberOfCommits >= minQuorumSize && c.current.State().Cmp(StateCommitted) < 0 { + logger.Trace("Got a quorum of commits", "tag", "stateTransition", "commits", numberOfCommits, "quorum", minQuorumSize) + err := c.commit() + if err != nil { + logger.Error("Failed to commit()", "err", err) + return err + } + + } else if c.current.GetPrepareOrCommitSize() >= minQuorumSize && c.current.State().Cmp(StatePrepared) < 0 { + err := c.current.TransitionToPrepared(minQuorumSize) + if err != nil { + logger.Error("Failed to create and set prepared certificate", "err", err) + return err + } + // Process Backlog Messages + c.backlog.updateState(c.current.View(), c.current.State()) + + logger.Trace("Got quorum prepares or commits", "tag", "stateTransition", "commits", c.current.Commits, "prepares", c.current.Prepares) + c.sendCommit() + } + return nil + +} + +// verifyCommit verifies if the received COMMIT message is equivalent to our subject +func (c *core) verifyCommit(commit *istanbul.CommittedSubject) error { + logger := c.newLogger("func", "verifyCommit") + + sub := c.current.Subject() + if !reflect.DeepEqual(commit.Subject, sub) { + logger.Warn("Inconsistent subjects between commit and proposal", "expected", sub, "got", commit) + return errInconsistentSubject + } + + return nil +} + +// verifyCommittedSeal verifies the commit seal in the received COMMIT message +func (c *core) verifyCommittedSeal(comSub *istanbul.CommittedSubject, src istanbul.Validator) error { + seal := PrepareCommittedSeal(comSub.Subject.Digest, comSub.Subject.View.Round) + return blscrypto.VerifySignature(src.BLSPublicKey(), seal, []byte{}, comSub.CommittedSeal, false, false) +} + +// verifyEpochValidatorSetSeal verifies the epoch validator set seal in the received COMMIT message +func (c *core) verifyEpochValidatorSetSeal(comSub *istanbul.CommittedSubject, blockNumber uint64, newValSet istanbul.ValidatorSet, src istanbul.Validator) error { + if blockNumber == 0 { + return nil + } + epochData, epochExtraData, cip22, err := c.generateEpochValidatorSetData(blockNumber, uint8(comSub.Subject.View.Round.Uint64()), comSub.Subject.Digest, newValSet) + if err != nil { + if err == errNotLastBlockInEpoch { + return nil + } + return err + } + return blscrypto.VerifySignature(src.BLSPublicKey(), epochData, epochExtraData, comSub.EpochValidatorSetSeal, true, cip22) +} + +// GossipCommits gossips to other validators all the commits received in the current round. +func (c *core) GossipCommits() error { + logger := c.newLogger("func", "gossipCommits") + st := c.current.State() + if st != StatePreprepared && st != StatePrepared && st != StateCommitted { + return errors.New("Cant gossip commits if not in preprepared, prepared, or committed state") + } + commits := c.current.Commits().Values() + logger.Debug("Gossipping commits", "len", len(commits)) + for _, commit := range commits { + c.gossip(commit) + // let the bandwidth breathe a little + time.Sleep(10 * time.Millisecond) + } + return nil +}
diff --git go-ethereum/consensus/istanbul/announce/ecertmsg_gen.go celo/consensus/istanbul/announce/ecertmsg_gen.go new file mode 100644 index 0000000000000000000000000000000000000000..d4615bb02f02e05a45dd6b78d37b8f30f78e996b --- /dev/null +++ celo/consensus/istanbul/announce/ecertmsg_gen.go @@ -0,0 +1,54 @@ +package announce + +import ( + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +type EnodeCertificateMsgGenerator interface { + // GenerateEnodeCertificateMsgs generates a map of enode certificate messages. + // One certificate message is generated for each external enode this node possesses generated for + // each external enode this node possesses. A unproxied validator will have one enode, while a + // proxied validator may have one for each proxy.. Each enode is a key in the returned map, and the + // value is the certificate message. + GenerateEnodeCertificateMsgs(ei *istanbul.EcdsaInfo, version uint) (map[enode.ID]*istanbul.EnodeCertMsg, error) +} + +type ecmg struct { + logger log.Logger + efeg ExternalFacingEnodeGetter +} + +func NewEnodeCertificateMsgGenerator(efeg ExternalFacingEnodeGetter) EnodeCertificateMsgGenerator { + return &ecmg{ + logger: log.New("module", "enodeCertificateMsgGenerator"), + efeg: efeg, + } +} + +func (e *ecmg) GenerateEnodeCertificateMsgs(ei *istanbul.EcdsaInfo, version uint) (map[enode.ID]*istanbul.EnodeCertMsg, error) { + logger := e.logger.New("func", "generateEnodeCertificateMsgs") + + enodeCertificateMsgs := make(map[enode.ID]*istanbul.EnodeCertMsg) + externalEnodes, valDestinations, err := e.efeg.GetEnodeCertNodesAndDestAddresses() + if err != nil { + return nil, err + } + + for _, externalNode := range externalEnodes { + msg := istanbul.NewEnodeCeritifcateMessage( + &istanbul.EnodeCertificate{EnodeURL: externalNode.URLv4(), Version: version}, + ei.Address, + ) + // Sign the message + if err := msg.Sign(ei.Sign); err != nil { + return nil, err + } + + enodeCertificateMsgs[externalNode.ID()] = &istanbul.EnodeCertMsg{Msg: msg, DestAddresses: valDestinations[externalNode.ID()]} + } + + logger.Trace("Generated Istanbul Enode Certificate messages", "enodeCertificateMsgs", enodeCertificateMsgs) + return enodeCertificateMsgs, nil +}
diff --git go-ethereum/consensus/istanbul/core/roundchange_v2_test.go celo/consensus/istanbul/core/roundchange_v2_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a9ccd5ef8b0bb2aa3bf64a5c42d2dcd772753598 --- /dev/null +++ celo/consensus/istanbul/core/roundchange_v2_test.go @@ -0,0 +1,734 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" +) + +func TestRoundChangeSetV2(t *testing.T) { + vals, _, _ := generateValidators(4) + vset := validator.NewSet(vals) + rc := newRoundChangeSetV2(vset) + + view := &istanbul.View{ + Sequence: big.NewInt(1), + Round: big.NewInt(1), + } + r := &istanbul.Subject{ + View: view, + Digest: common.Hash{}, + } + + // Test Add() + // Add message from all validators + for i, v := range vset.List() { + rc.Add(view.Round, istanbul.NewPrepareMessage(r, v.Address())) + if rc.msgsForRound[view.Round.Uint64()].Size() != i+1 { + t.Errorf("the size of round change messages mismatch: have %v, want %v", rc.msgsForRound[view.Round.Uint64()].Size(), i+1) + } + } + + // Add message again from all validators, but the size should be the same + for _, v := range vset.List() { + rc.Add(view.Round, istanbul.NewPrepareMessage(r, v.Address())) + if rc.msgsForRound[view.Round.Uint64()].Size() != vset.Size() { + t.Errorf("the size of round change messages mismatch: have %v, want %v", rc.msgsForRound[view.Round.Uint64()].Size(), vset.Size()) + } + } + + // Test MaxRound() + for i := 0; i < 10; i++ { + maxRound := rc.MaxRound(i) + if i <= vset.Size() { + if maxRound == nil || maxRound.Cmp(view.Round) != 0 { + t.Errorf("MaxRound mismatch: have %v, want %v", maxRound, view.Round) + } + } else if maxRound != nil { + t.Errorf("MaxRound mismatch: have %v, want nil", maxRound) + } + } + + // Test Clear() + for i := int64(0); i < 2; i++ { + rc.Clear(big.NewInt(i)) + if rc.msgsForRound[view.Round.Uint64()].Size() != vset.Size() { + t.Errorf("the size of round change messages mismatch: have %v, want %v", rc.msgsForRound[view.Round.Uint64()].Size(), vset.Size()) + } + } + rc.Clear(big.NewInt(2)) + if rc.msgsForRound[view.Round.Uint64()] != nil { + t.Errorf("the change messages mismatch: have %v, want nil", rc.msgsForRound[view.Round.Uint64()]) + } + + // Test Add() + // Add message from all validators + for i, v := range vset.List() { + rc.Add(view.Round, istanbul.NewPrepareMessage(r, v.Address())) + if rc.msgsForRound[view.Round.Uint64()].Size() != i+1 { + t.Errorf("the size of round change messages mismatch: have %v, want %v", rc.msgsForRound[view.Round.Uint64()].Size(), i+1) + } + } + + rc.Clear(big.NewInt(2)) + if rc.msgsForRound[view.Round.Uint64()] != nil { + t.Errorf("the change messages mismatch: have %v, want nil", rc.msgsForRound[view.Round.Uint64()]) + } + + // Test that we only store the msg with the highest round for each validator + roundMultiplier := 1 + for j := 1; j <= roundMultiplier; j++ { + for i, v := range vset.List() { + view := &istanbul.View{ + Sequence: big.NewInt(1), + Round: big.NewInt(int64((i + 1) * j)), + } + msg := istanbul.NewRoundChangeV2Message(&istanbul.RoundChangeV2{ + Request: istanbul.RoundChangeRequest{ + Address: v.Address(), + View: *view, + }, + }, v.Address()) + err := rc.Add(view.Round, msg) + if err != nil { + t.Errorf("Round change message: unexpected error %v", err) + } + } + } + + for i, v := range vset.List() { + lookingForValAtRound := uint64(roundMultiplier * (i + 1)) + if rc.msgsForRound[lookingForValAtRound].Size() != 1 { + t.Errorf("Round change messages at unexpected rounds: %v", rc.msgsForRound) + } + if rc.latestRoundForVal[v.Address()] != lookingForValAtRound { + t.Errorf("Round change messages at unexpected rounds: for %v want %v have %v", + i+1, rc.latestRoundForVal[v.Address()], lookingForValAtRound) + } + } + + for threshold := 1; threshold <= vset.Size(); threshold++ { + r := rc.MaxRound(threshold).Uint64() + expectedR := uint64((vset.Size() - threshold + 1) * roundMultiplier) + if r != expectedR { + t.Errorf("MaxRound: %v want %v have %v", rc.String(), expectedR, r) + } + } + + // Test getCertificate + for r := 1; r < vset.Size(); r += roundMultiplier { + expectedMsgsAtRound := vset.Size() - r + 1 + for quorum := 1; quorum < 10; quorum++ { + cert, _, err := rc.getCertificate(big.NewInt(int64(r)), quorum) + if expectedMsgsAtRound < quorum { + // Expecting fewer than quorum. + if err != errFailedCreateRoundChangeCertificate || len(cert.Requests) != 0 { + t.Errorf("problem in getCertificate r=%v q=%v expMsgs=%v - want 0 have %v err=%v -- %v -- %v", r, quorum, expectedMsgsAtRound, len(cert.Requests), err, cert, rc) + } + } else { + // Number msgs available at this round is >= quorum. Expecting a cert with =quorum RC messages. + if err != nil || len(cert.Requests) != quorum { + t.Errorf("problem in getCertificate r=%v q=%v expMsgs=%v - want %v have %v -- %v -- %v", r, quorum, quorum, expectedMsgsAtRound, len(cert.Requests), cert, rc) + } + } + } + } +} + +func TestHandleRoundChangeCertificateV2(t *testing.T) { + N := uint64(4) // replica 0 is the proposer, it will send messages to others + F := uint64(1) + view := istanbul.View{ + Round: big.NewInt(1), + Sequence: big.NewInt(1), + } + + testCases := []struct { + name string + getCertificate func(*testing.T, *testSystem) (istanbul.RoundChangeCertificateV2, istanbul.Proposal) + expectedErr error + }{ + { + "Valid round change certificate without PREPARED certificate", + func(t *testing.T, sys *testSystem) (istanbul.RoundChangeCertificateV2, istanbul.Proposal) { + pc, prop := istanbul.EmptyPreparedCertificateV2() + return sys.getRoundChangeCertificateV2(t, []istanbul.View{view}, pc), prop + }, + nil, + }, + { + "Valid round change certificate with PREPARED certificate", + func(t *testing.T, sys *testSystem) (istanbul.RoundChangeCertificateV2, istanbul.Proposal) { + proposal := makeBlock(0) + pc := sys.getPreparedCertificateV2(t, []istanbul.View{view}, proposal) + return sys.getRoundChangeCertificateV2(t, []istanbul.View{view}, pc), proposal + }, + nil, + }, + { + "Invalid round change certificate, duplicate message", + func(t *testing.T, sys *testSystem) (istanbul.RoundChangeCertificateV2, istanbul.Proposal) { + pc, prop := istanbul.EmptyPreparedCertificateV2() + roundChangeCertificateV2 := sys.getRoundChangeCertificateV2(t, []istanbul.View{view}, pc) + roundChangeCertificateV2.Requests[1] = roundChangeCertificateV2.Requests[0] + return roundChangeCertificateV2, prop + }, + errInvalidRoundChangeCertificateDuplicate, + }, + { + "Empty certificate", + func(t *testing.T, sys *testSystem) (istanbul.RoundChangeCertificateV2, istanbul.Proposal) { + return istanbul.RoundChangeCertificateV2{}, makeBlock(0) + }, + errInvalidRoundChangeCertificateNumMsgs, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + sys := NewTestSystemWithBackend(N, F) + for i, backend := range sys.backends { + c := backend.engine.(*core) + c.Start() + certificate, prop := test.getCertificate(t, sys) + err := c.handleRoundChangeCertificateV2(view, certificate, prop) + + if err != test.expectedErr { + t.Errorf("error mismatch for test case %v: have %v, want %v", i, err, test.expectedErr) + } + if err == nil && c.current.View().Cmp(&view) != 0 { + t.Errorf("view mismatch for test case %v: have %v, want %v", i, c.current.View(), view) + } + } + for _, backend := range sys.backends { + backend.engine.Stop() + } + close(sys.quit) + }) + } +} + +func TestHandleRoundChangeV2(t *testing.T) { + N := uint64(4) // replica 0 is the proposer, it will send messages to others + F := uint64(1) // F does not affect tests + + buildEmptyCertificate := func(_ *testing.T, _ *testSystem) (istanbul.PreparedCertificateV2, istanbul.Proposal) { + return istanbul.EmptyPreparedCertificateV2() + } + + noopPrepare := func(_ *testSystem) {} + + testCases := []struct { + name string + prepareSystem func(*testSystem) + getCert func(*testing.T, *testSystem) (istanbul.PreparedCertificateV2, istanbul.Proposal) + expectedErr error + }{ + { + "normal case", + noopPrepare, + buildEmptyCertificate, + nil, + }, + { + "normal case with valid prepared certificate", + noopPrepare, + func(t *testing.T, sys *testSystem) (istanbul.PreparedCertificateV2, istanbul.Proposal) { + proposal := makeBlock(1) + pc := sys.getPreparedCertificateV2(t, []istanbul.View{*sys.backends[0].engine.(*core).current.View()}, proposal) + return pc, proposal + }, + nil, + }, + { + "normal case with invalid prepared certificate", + noopPrepare, + func(t *testing.T, sys *testSystem) (istanbul.PreparedCertificateV2, istanbul.Proposal) { + proposal := makeBlock(1) + preparedCert := sys.getPreparedCertificateV2(t, []istanbul.View{*sys.backends[0].engine.(*core).current.View()}, proposal) + preparedCert.PrepareOrCommitMessages[0] = preparedCert.PrepareOrCommitMessages[1] + return preparedCert, proposal + }, + errInvalidPreparedCertificateDuplicate, + }, + { + "valid message for future round", + func(sys *testSystem) { + sys.backends[0].engine.(*core).current.(*rsSaveDecorator).rs.(*roundStateImpl).round = big.NewInt(10) + }, + func(t *testing.T, _ *testSystem) (istanbul.PreparedCertificateV2, istanbul.Proposal) { + return istanbul.EmptyPreparedCertificateV2() + }, + nil, + }, + { + "invalid message for future sequence", + func(sys *testSystem) { + sys.backends[0].engine.(*core).current.(*rsSaveDecorator).rs.(*roundStateImpl).sequence = big.NewInt(10) + }, + buildEmptyCertificate, + errFutureMessage, + }, + { + "invalid message for previous round", + func(sys *testSystem) { + sys.backends[0].engine.(*core).current.(*rsSaveDecorator).rs.(*roundStateImpl).round = big.NewInt(0) + }, + buildEmptyCertificate, + nil, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + sys := NewTestSystemWithBackend(N, F) + + closer := sys.Run(false) + defer closer() + + for _, v := range sys.backends { + c := v.engine.(*core) + c.Start() + } + test.prepareSystem(sys) + + v0 := sys.backends[0] + r0 := v0.engine.(*core) + + curView := r0.current.View() + nextView := &istanbul.View{ + Round: new(big.Int).Add(curView.Round, common.Big1), + Sequence: curView.Sequence, + } + pc, prop := test.getCert(t, sys) + req := istanbul.RoundChangeRequest{ + Address: v0.Address(), + View: *nextView, + PreparedCertificateV2: pc, + } + if err := req.Sign(r0.backend.Sign); err != nil { + t.Errorf("error signing RoundChangeRequest: %v", err) + } + msg := istanbul.NewRoundChangeV2Message(&istanbul.RoundChangeV2{ + Request: req, + PreparedProposal: prop, + }, v0.Address()) + + for i, v := range sys.backends { + // i == 0 is primary backend, it is responsible for send ROUND CHANGE messages to others. + if i == 0 { + continue + } + + c := v.engine.(*core) + + // run each backends and verify handlePreprepare function. + err := c.handleRoundChangeV2(msg) + if err != test.expectedErr { + t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) + } + return + } + }) + } +} + +func (ts *testSystem) distributeIstMsgs(t *testing.T, sys *testSystem, istMsgDistribution map[uint64]map[int]bool) { + for { + select { + case <-ts.quit: + return + case event := <-ts.queuedMessage: + msg := new(istanbul.Message) + if err := msg.FromPayload(event.Payload, nil); err != nil { + t.Errorf("Could not decode payload") + } + + targets := istMsgDistribution[msg.Code] + for index, b := range sys.backends { + if targets[index] || msg.Address == b.address { + go b.EventMux().Post(event) + } else { + testLogger.Info("not sending msg", "index", index, "from", msg.Address, "to", b.address, "code", msg.Code) + } + } + } + } +} + +var gossip = map[int]bool{ + 0: true, + 1: true, + 2: true, + 3: true, +} + +var sendTo2FPlus1 = map[int]bool{ + 0: true, + 1: true, + 2: true, + 3: false, +} + +var sendToF = map[int]bool{ + 0: false, + 1: false, + 2: false, + 3: true, +} + +var sendToFPlus1 = map[int]bool{ + 0: false, + 1: false, + 2: true, + 3: true, +} +var sendToHalf1 = map[int]bool{ + 0: true, + 1: true, + 2: false, + 3: false, +} +var sendToHalf2 = map[int]bool{ + 0: false, + 1: false, + 2: true, + 3: true, +} +var noGossip = map[int]bool{ + 0: false, + 1: false, + 2: false, + 3: false, +} + +// This tests the liveness issue present in the initial implementation of Istanbul, described in +// more detail here: https://arxiv.org/pdf/1901.07160.pdf +// To test this, a block is proposed, for which 2F + 1 PREPARE messages are sent to F nodes. +// In the original implementation, these F nodes would lock onto that block, and eventually everyone would +// round change. If the next proposer was byzantine, they could send a PREPREPARE with a different block, +// get the remaining 2F non-byzantine nodes to lock onto that new block, causing a deadlock. +// In the new implementation, the PREPREPARE will include a ROUND CHANGE certificate, +// and all nodes will accept the newly proposed block. +func TestCommitsBlocksAfterRoundChangeV2(t *testing.T) { + sys := NewTestSystemWithBackend(4, 1) + + for _, b := range sys.backends { + b.engine.Start() // start Istanbul core + block := makeBlock(1) + b.NewRequest(block) + } + + newBlocks := sys.backends[0].EventMux().Subscribe(istanbul.FinalCommittedEvent{}) + defer newBlocks.Unsubscribe() + + timeout := sys.backends[3].EventMux().Subscribe(timeoutAndMoveToNextRoundEvent{}) + defer timeout.Unsubscribe() + + istMsgDistribution := map[uint64]map[int]bool{} + + // Allow everyone to see the initial proposal + // Send all PREPARE messages to F nodes. + // Send COMMIT messages (we don't expect these to be sent in the first round anyway). + // Send ROUND CHANGE messages to the remaining 2F + 1 nodes. + istMsgDistribution[istanbul.MsgPreprepareV2] = gossip + istMsgDistribution[istanbul.MsgPrepare] = sendToF + istMsgDistribution[istanbul.MsgCommit] = gossip + istMsgDistribution[istanbul.MsgRoundChangeV2] = sendTo2FPlus1 + + go sys.distributeIstMsgs(t, sys, istMsgDistribution) + + <-timeout.Chan() + + // Turn PREPAREs back on for round 1. + testLogger.Info("Turn PREPAREs back on for round 1") + istMsgDistribution[istanbul.MsgPrepare] = gossip + + // Eventually we should get a block again + select { + case <-time.After(2 * time.Second): + t.Error("Did not finalize a block within 2 secs") + case _, ok := <-newBlocks.Chan(): + if !ok { + t.Error("Error reading block") + } + // Wait for all backends to finalize the block. + <-time.After(1 * time.Second) + testLogger.Info("Expected all backends to finalize") + expectedCommitted, _ := sys.backends[0].GetCurrentHeadBlockAndAuthor() + for i, b := range sys.backends { + committed, _ := b.GetCurrentHeadBlockAndAuthor() + // We don't expect any particular block to be committed here. We do expect them to be consistent. + if committed.Number().Cmp(common.Big1) != 0 { + t.Errorf("Backend %v got committed block with unexpected number: expected %v, got %v", i, 1, committed.Number()) + } + if expectedCommitted.Hash() != committed.Hash() { + t.Errorf("Backend %v got committed block with unexpected hash: expected %v, got %v", i, expectedCommitted.Hash(), committed.Hash()) + } + } + } + + // Manually open and close b/c hijacking sys.listen + for _, b := range sys.backends { + b.engine.Stop() // stop Istanbul core + } + close(sys.quit) +} + +func TestUseRoundChangeCertificateWithPC(t *testing.T) { + sys := NewTestSystemWithBackend(4, 1) + + for i, b := range sys.backends { + b.engine.Start() // start Istanbul core + block := makeBlock(1) + block.Header().GasUsed = uint64(i) + b.NewRequest(block) + } + + newBlocks := sys.backends[0].EventMux().Subscribe(istanbul.FinalCommittedEvent{}) + defer newBlocks.Unsubscribe() + + timeout1 := sys.backends[1].EventMux().Subscribe(timeoutAndMoveToNextRoundEvent{}) + defer timeout1.Unsubscribe() + timeout2 := sys.backends[2].EventMux().Subscribe(timeoutAndMoveToNextRoundEvent{}) + defer timeout2.Unsubscribe() + timeout3 := sys.backends[3].EventMux().Subscribe(timeoutAndMoveToNextRoundEvent{}) + defer timeout3.Unsubscribe() + + istMsgDistribution := map[uint64]map[int]bool{} + + // Allow everyone to see the initial proposal + // Send all PREPARE messages to everyone + // Do not commit, so the round change will have a prepare certificate + // Send ROUND CHANGE messages to everyone + istMsgDistribution[istanbul.MsgPreprepareV2] = gossip + istMsgDistribution[istanbul.MsgPrepare] = sendToHalf1 + istMsgDistribution[istanbul.MsgCommit] = noGossip + istMsgDistribution[istanbul.MsgRoundChangeV2] = gossip + + go sys.distributeIstMsgs(t, sys, istMsgDistribution) + + <-timeout1.Chan() + <-timeout2.Chan() + <-timeout3.Chan() + + istMsgDistribution[istanbul.MsgPrepare] = sendToHalf2 + testLogger.Info("Empty round passing through") + + <-timeout1.Chan() + <-timeout2.Chan() + <-timeout3.Chan() + + // Turn COMMITS back on for round 2. + testLogger.Info("Turn COMMITs back on for round 2") + istMsgDistribution[istanbul.MsgCommit] = gossip + istMsgDistribution[istanbul.MsgPrepare] = gossip + + // Eventually we should get a block again + select { + case <-time.After(2 * time.Second): + t.Error("Did not finalize a block within 2 secs") + case _, ok := <-newBlocks.Chan(): + if !ok { + t.Error("Error reading block") + } + // Wait for all backends to finalize the block. + <-time.After(1 * time.Second) + testLogger.Info("Expected all backends to finalize") + expectedCommitted, _ := sys.backends[0].GetCurrentHeadBlockAndAuthor() + for i, b := range sys.backends { + committed, _ := b.GetCurrentHeadBlockAndAuthor() + // We don't expect any particular block to be committed here. We do expect them to be consistent. + if committed.Number().Cmp(common.Big1) != 0 { + t.Errorf("Backend %v got committed block with unexpected number: expected %v, got %v", i, 1, committed.Number()) + } + if expectedCommitted.Hash() != committed.Hash() { + t.Errorf("Backend %v got committed block with unexpected hash: expected %v, got %v", i, expectedCommitted.Hash(), committed.Hash()) + } + } + } + + // Manually open and close b/c hijacking sys.listen + for _, b := range sys.backends { + b.engine.Stop() // stop Istanbul core + } + close(sys.quit) +} + +// This tests that when F+1 nodes receive 2F+1 PREPARE messages for a particular proposal, the +// system enforces that as the only valid proposal for this sequence. +func TestPreparedCertificatePersistsThroughRoundChangesV2(t *testing.T) { + sys := NewTestSystemWithBackend(4, 1) + + for _, b := range sys.backends { + b.engine.Start() // start Istanbul core + block := makeBlock(1) + b.NewRequest(block) + } + + newBlocks := sys.backends[3].EventMux().Subscribe(istanbul.FinalCommittedEvent{}) + defer newBlocks.Unsubscribe() + + timeout := sys.backends[3].EventMux().Subscribe(timeoutAndMoveToNextRoundEvent{}) + defer timeout.Unsubscribe() + + istMsgDistribution := map[uint64]map[int]bool{} + + // Send PREPARE messages to F + 1 nodes so we guarantee a PREPARED certificate in the ROUND CHANGE certificate.. + istMsgDistribution[istanbul.MsgPreprepareV2] = gossip + istMsgDistribution[istanbul.MsgPrepare] = sendToFPlus1 + istMsgDistribution[istanbul.MsgCommit] = gossip + istMsgDistribution[istanbul.MsgRoundChangeV2] = gossip + + go sys.distributeIstMsgs(t, sys, istMsgDistribution) + + // Turn PREPARE messages off for round 1 to force reuse of the PREPARED certificate. + <-time.After(1 * time.Second) + istMsgDistribution[istanbul.MsgPrepare] = noGossip + + // Wait for round 1 to start. + <-timeout.Chan() + // Turn PREPARE messages back on in time for round 2. + <-time.After(1 * time.Second) + istMsgDistribution[istanbul.MsgPrepare] = gossip + + // Wait for round 2 to start. + <-timeout.Chan() + + select { + case <-timeout.Chan(): + t.Error("Did not finalize a block in round 2.") + case _, ok := <-newBlocks.Chan(): + if !ok { + t.Error("Error reading block") + } + // Wait for all backends to finalize the block. + <-time.After(2 * time.Second) + for i, b := range sys.backends { + committed, _ := b.GetCurrentHeadBlockAndAuthor() + // We expect to commit the block proposed by the first proposer. + expectedCommitted := makeBlock(1) + if committed.Number().Cmp(common.Big1) != 0 { + t.Errorf("Backend %v got committed block with unexpected number: expected %v, got %v", i, 1, committed.Number()) + } + if expectedCommitted.Hash() != committed.Hash() { + t.Errorf("Backend %v got committed block with unexpected hash: expected %v, got %v", i, expectedCommitted.Hash(), committed.Hash()) + } + } + } + + // Manually open and close b/c hijacking sys.listen + for _, b := range sys.backends { + b.engine.Stop() // stop Istanbul core + } + close(sys.quit) +} + +// Test periodic round changes at high rounds +func TestPeriodicRoundChangesV2(t *testing.T) { + sys := NewTestSystemWithBackend(4, 1) + + for _, b := range sys.backends { + b.engine.Start() // start Istanbul core + block := makeBlock(1) + b.NewRequest(block) + } + + newBlocks := sys.backends[3].EventMux().Subscribe(istanbul.FinalCommittedEvent{}) + defer newBlocks.Unsubscribe() + + timeoutMoveToNextRound := sys.backends[3].EventMux().Subscribe(timeoutAndMoveToNextRoundEvent{}) + defer timeoutMoveToNextRound.Unsubscribe() + + timeoutResendRC := sys.backends[3].EventMux().Subscribe(resendRoundChangeEvent{}) + defer timeoutResendRC.Unsubscribe() + + istMsgDistribution := map[uint64]map[int]bool{} + istMsgDistribution[istanbul.MsgPreprepareV2] = noGossip + istMsgDistribution[istanbul.MsgPrepare] = noGossip + istMsgDistribution[istanbul.MsgCommit] = noGossip + istMsgDistribution[istanbul.MsgRoundChangeV2] = noGossip + + go sys.distributeIstMsgs(t, sys, istMsgDistribution) + + for _, b := range sys.backends { + b.engine.(*core).waitForDesiredRound(big.NewInt(5)) + } + + // Expect at least one repeat RC before move to next round. + timeoutResends := 0 +loop: + for { + select { + case <-timeoutResendRC.Chan(): + testLogger.Info("Got timeoutResendRC") + timeoutResends++ + case <-timeoutMoveToNextRound.Chan(): + if timeoutResends == 0 { + t.Errorf("No Repeat events before moving to next round") + } + break loop + } + } + + istMsgDistribution[istanbul.MsgPreprepareV2] = gossip + istMsgDistribution[istanbul.MsgPrepare] = gossip + istMsgDistribution[istanbul.MsgCommit] = gossip + istMsgDistribution[istanbul.MsgRoundChangeV2] = gossip + + // Make sure we finalize block in next two rounds. + roundTimeouts := 0 +loop2: + for { + select { + case <-timeoutMoveToNextRound.Chan(): + roundTimeouts++ + if roundTimeouts > 1 { + t.Error("Did not finalize a block.") + } + case _, ok := <-newBlocks.Chan(): + if !ok { + t.Error("Error reading block") + } + // Wait for all backends to finalize the block. + <-time.After(2 * time.Second) + for i, b := range sys.backends { + committed, _ := b.GetCurrentHeadBlockAndAuthor() + // We expect to commit the block proposed by proposer 6 mod 4 = 2. + expectedCommitted := makeBlock(1) + if committed.Number().Cmp(common.Big1) != 0 { + t.Errorf("Backend %v got committed block with unexpected number: expected %v, got %v", i, 1, committed.Number()) + } + if expectedCommitted.Hash() != committed.Hash() { + t.Errorf("Backend %v got committed block with unexpected hash: expected %v, got %v", i, expectedCommitted.Hash(), committed.Hash()) + } + } + break loop2 + } + } + + // Manually open and close b/c hijacking sys.listen + for _, b := range sys.backends { + b.engine.Stop() // stop Istanbul core + } + close(sys.quit) +}
diff --git go-ethereum/consensus/istanbul/proxy/types.go celo/consensus/istanbul/proxy/types.go new file mode 100644 index 0000000000000000000000000000000000000000..efe32658122ac0974ee0715c709e9c422854f5a0 --- /dev/null +++ celo/consensus/istanbul/proxy/types.go @@ -0,0 +1,183 @@ +// Copyright 2017 The celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "errors" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +var ( + // errUnauthorizedMessageFromProxiedValidator is returned when the received message expected to be signed + // by the proxied validator, but signed from another key + errUnauthorizedMessageFromProxiedValidator = errors.New("message not signed by proxied validator") + + // errUnauthorizedProxiedValidator is returned if the peer connecting is not the + // authorized proxied validator + errUnauthorizedProxiedValidator = errors.New("connection from unauthorized unauthorized peer") + + // ErrNodeNotProxiedValidator is returned if this node is not a proxied validator + ErrNodeNotProxiedValidator = errors.New("node not a proxied validator") + + // ErrNodeNotProxy is returned if this node is not a proxy + ErrNodeNotProxy = errors.New("node not a proxy") + + // ErrNoProxiedValidator is returned if the proxy has no connected proxied validator + ErrNoProxiedValidator = errors.New("no connected proxied validator") + + // ErrNoCelostatsProxy is returned if there is no connected proxy that sent the celostats message to be signed + ErrNoCelostatsProxy = errors.New("no connected proxy that sent the celostats message to be signed") +) + +type ProxyEngine interface { + // HandleMsg is the `celo` subprotocol message handler for proxies. + HandleMsg(peer consensus.Peer, msgCode uint64, payload []byte) (bool, error) + + // RegisterProxiedValidatorPeer is the callback function that should be called + // when a proxied validator connects to a proxy. This function will save + // the proxied validator's peer in the proxy's state. + RegisterProxiedValidatorPeer(proxiedValidatorPeer consensus.Peer) + + // UnregisterProxiedValidatorPeer is the callback function that should be + // called when a proxied validator disconnects from a proxy. This function + // will remove the proxied validator's peer from the proxy's state. + UnregisterProxiedValidatorPeer(proxiedValidatorPeer consensus.Peer) + + // SendDelegateSignMsgToProxiedValidator will send a delegate sign message to the proxied validator. + SendDelegateSignMsgToProxiedValidator(msg []byte) error + + // SendMsgToProxiedValidators will send the `celo` message to the proxied validators. + SendMsgToProxiedValidators(msgCode uint64, msg *istanbul.Message) error + + // GetProxiedValidatorsInfo will return information about the proxied validators. + GetProxiedValidatorsInfo() ([]*ProxiedValidatorInfo, error) +} + +type ProxiedValidatorEngine interface { + // Start will start the proxied validator engine. Specifically, it will start the + // proxy handler thread. + Start() error + + // Stop will stop the proxied validator engine. Specifically, it will stop the + // proxy handler thread. + Stop() error + + // AddProxy will add a new proxy to the proxy handler + AddProxy(node, externalNode *enode.Node) error + + // RemoveProxy will remove a proxy from the proxy handler + RemoveProxy(node *enode.Node) error + + // RegisterProxyPeer is the callback function that should be called + // when a proxy connects to a proxied validator. This function will + // notify the proxy handler that a proxy has connected. + RegisterProxyPeer(proxyPeer consensus.Peer) error + + // UnregisterProxyPeer is the callback function that should be called + // when a proxy is disconnected from a proxied validator. This function will + // notify the proxy handler that a proxy has disconnected. + UnregisterProxyPeer(proxyPeer consensus.Peer) error + + // SendDelegateSignMsgToProxy will send a delegate sign message back to the proxy that is designated to + // handle celostats. + SendDelegateSignMsgToProxy(msg []byte, peerID enode.ID) error + + // SendForwardMsg will send a forward message to all of the proxies. + SendForwardMsgToAllProxies(finalDestAddresses []common.Address, ethMsgCode uint64, payload []byte) error + + // SendValEnodeShareMsgToAllProxies will send the appropriate val enode share message to each + // connected proxy. + SendValEnodesShareMsgToAllProxies() error + + // SendEnodeCertsToAllProxies will send the enode certs to the appropriate proxy. + SendEnodeCertsToAllProxies(map[enode.ID]*istanbul.EnodeCertMsg) error + + // GetValidatorProxyAssignments will retrieve all the remote validator to proxy assignments. + GetValidatorProxyAssignments(validators []common.Address) (map[common.Address]*Proxy, error) + + // GetProxiesAndValAssignments will retrieve all of the proxies (connected or not yet connected) and + // the proxy to validator assignments. + GetProxiesAndValAssignments() ([]*Proxy, map[enode.ID][]common.Address, error) + + // IsProxyPeer will check if the peerID is a proxy. + IsProxyPeer(peerID enode.ID) (bool, error) + + // NewEpoch will notify the proxied validator's thread that a new epoch started + NewEpoch() error +} + +// ============================================== +// +// define the proxy object + +type Proxy struct { + node *enode.Node // Enode for the internal network interface + externalNode *enode.Node // Enode for the external network interface + peer consensus.Peer // Connected proxy peer. Is nil if this node is not connected to the proxy + disconnectTS time.Time // Timestamp when this proxy's peer last disconnected. Initially set to the timestamp of when the proxy was added +} + +func (p *Proxy) ID() enode.ID { + return p.node.ID() +} + +func (p *Proxy) ExternalNode() *enode.Node { + return p.externalNode +} + +func (p *Proxy) IsPeered() bool { + return p.peer != nil +} + +func (p *Proxy) String() string { + return fmt.Sprintf("{internalNode: %v, externalNode %v, dcTimestamp: %v, ID: %v}", p.node, p.externalNode, p.disconnectTS, p.ID()) +} + +// ProxyInfo is used to provide info on a proxy that can be given via an RPC +type ProxyInfo struct { + InternalNode *enode.Node `json:"internalEnodeUrl"` + ExternalNode *enode.Node `json:"externalEnodeUrl"` + IsPeered bool `json:"isPeered"` + AssignedRemoteValidators []common.Address `json:"validators"` // All validator addresses assigned to the proxy + DisconnectTS int64 `json:"disconnectedTimestamp"` // Unix time of the last disconnect of the peer +} + +func NewProxyInfo(p *Proxy, assignedVals []common.Address) *ProxyInfo { + return &ProxyInfo{ + InternalNode: p.node, + ExternalNode: p.ExternalNode(), + IsPeered: p.IsPeered(), + DisconnectTS: p.disconnectTS.Unix(), + AssignedRemoteValidators: assignedVals, + } +} + +// ============================================== +// +// define the proxied validator info object + +type ProxiedValidatorInfo struct { + Address common.Address `json:"address"` + IsPeered bool `json:"isPeered"` + Node *enode.Node `json:"enodeURL"` +}
diff --git go-ethereum/consensus/istanbul/announce/state.go celo/consensus/istanbul/announce/state.go new file mode 100644 index 0000000000000000000000000000000000000000..ddd9262c37933aca084c05702bcd235bcdb80b08 --- /dev/null +++ celo/consensus/istanbul/announce/state.go @@ -0,0 +1,75 @@ +package announce + +import ( + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" +) + +type AnnounceState struct { + ValEnodeTable *ValidatorEnodeDB + VersionCertificateTable *VersionCertificateDB + + LastVersionCertificatesGossiped *AddressTime + LastQueryEnodeGossiped *AddressTime +} + +func NewAnnounceState(valEnodeTable *ValidatorEnodeDB, versionCertificateTable *VersionCertificateDB) *AnnounceState { + return &AnnounceState{ + ValEnodeTable: valEnodeTable, + VersionCertificateTable: versionCertificateTable, + LastQueryEnodeGossiped: NewAddressTime(), + LastVersionCertificatesGossiped: NewAddressTime(), + } +} + +type AnnounceStatePruner interface { + Prune(*AnnounceState) error +} + +func NewAnnounceStatePruner(retrieveValidatorConnSetFn func() (map[common.Address]bool, error)) AnnounceStatePruner { + return &pruner{ + logger: log.New("module", "announceStatePruner"), + retrieveValidatorConnSet: retrieveValidatorConnSetFn, + } +} + +type pruner struct { + logger log.Logger + retrieveValidatorConnSet func() (map[common.Address]bool, error) +} + +// Prune will remove entries that are not in the validator connection set from all announce related data structures. +// The data structures that it prunes are: +// 1) lastQueryEnodeGossiped +// 2) valEnodeTable +// 3) lastVersionCertificatesGossiped +// 4) versionCertificateTable +func (p *pruner) Prune(state *AnnounceState) error { + // retrieve the validator connection set + validatorConnSet, err := p.retrieveValidatorConnSet() + if err != nil { + p.logger.Warn("Error in pruning announce data structures", "err", err) + } + + state.LastQueryEnodeGossiped.RemoveIf(func(remoteAddress common.Address, t time.Time) bool { + return !validatorConnSet[remoteAddress] && time.Since(t) >= QueryEnodeGossipCooldownDuration + }) + + if err := state.ValEnodeTable.PruneEntries(validatorConnSet); err != nil { + p.logger.Trace("Error in pruning valEnodeTable", "err", err) + return err + } + + state.LastVersionCertificatesGossiped.RemoveIf(func(remoteAddress common.Address, t time.Time) bool { + return !validatorConnSet[remoteAddress] && time.Since(t) >= VersionCertificateGossipCooldownDuration + }) + + if err := state.VersionCertificateTable.Prune(validatorConnSet); err != nil { + p.logger.Trace("Error in pruning versionCertificateTable", "err", err) + return err + } + + return nil +}
diff --git go-ethereum/consensus/istanbul/core/core_test.go celo/consensus/istanbul/core/core_test.go new file mode 100644 index 0000000000000000000000000000000000000000..699cd5ddd73c14326ba978a750bbaf418a555f62 --- /dev/null +++ celo/consensus/istanbul/core/core_test.go @@ -0,0 +1,226 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "errors" + "math/big" + "reflect" + "testing" + "time" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/celo-org/celo-bls-go/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/core/types" + elog "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/trie" +) + +func makeBlock(number int64) *types.Block { + header := &types.Header{ + Number: big.NewInt(number), + GasUsed: 0, + Time: uint64(0), + } + return types.NewBlock(header, nil, nil, nil, new(trie.Trie)) +} + +func newTestProposalWithNum(num int64) istanbul.Proposal { + return makeBlock(num) +} + +func newTestProposal() istanbul.Proposal { + return makeBlock(1) +} + +var InvalidProposalError = errors.New("invalid proposal") + +func TestNewRequest(t *testing.T) { + + testLogger.SetHandler(elog.StdoutHandler) + + N := uint64(4) + F := uint64(1) + + sys := NewTestSystemWithBackend(N, F) + + close := sys.Run(true) + defer close() + + request1 := makeBlock(1) + sys.backends[0].NewRequest(request1) + + <-time.After(1 * time.Second) + + request2 := makeBlock(2) + sys.backends[0].NewRequest(request2) + + <-time.After(1 * time.Second) + + for _, backend := range sys.backends { + if len(backend.committedMsgs) != 2 { + t.Errorf("the number of executed requests mismatch: have %v, want 2", len(backend.committedMsgs)) + } else { + if !reflect.DeepEqual(request1.Number(), backend.committedMsgs[0].commitProposal.Number()) { + t.Errorf("the number of requests mismatch: have %v, want %v", request1.Number(), backend.committedMsgs[0].commitProposal.Number()) + } + if !reflect.DeepEqual(request2.Number(), backend.committedMsgs[1].commitProposal.Number()) { + t.Errorf("the number of requests mismatch: have %v, want %v", request2.Number(), backend.committedMsgs[1].commitProposal.Number()) + } + } + } +} + +func TestVerifyProposal(t *testing.T) { + // Check that it should not be in the cache + sys := NewTestSystemWithBackend(1, 0) + + close := sys.Run(true) + defer close() + + backendCore := sys.backends[0].engine.(*core) + backend := backendCore.backend.(*testSystemBackend) + + testCases := []struct { + name string + proposal istanbul.Proposal + verifyImpl func(proposal istanbul.Proposal) (*StateProcessResult, time.Duration, error) + expectedErr error + expectedDuration time.Duration + }{ + // Test case with valid proposal + { + "Valid proposal", + newTestProposalWithNum(1), + backend.verifyWithSuccess, + nil, + 0, + }, + + // Test case with invalid proposal + { + "Invalid proposal", + newTestProposalWithNum(2), + backend.verifyWithFailure, + InvalidProposalError, + 0, + }, + + // Test case with future proposal + { + "Future proposal", + newTestProposalWithNum(3), + backend.verifyWithFutureProposal, + consensus.ErrFutureBlock, + 5, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, + func(t *testing.T) { + // Inject in the verification function implementation + backend.setVerifyImpl(testCase.verifyImpl) + + // Verify a cache miss + _, isCached := backendCore.current.GetProposalVerificationStatus(testCase.proposal.Hash()) + if isCached { + t.Errorf("Should of had a cache miss") + } + + // Do a verification with success + _, err := backendCore.verifyProposal(testCase.proposal) + if err != testCase.expectedErr { + t.Errorf("Unexpected return status on first verifyProposal call. Want: %v, Actual: %v", testCase.expectedErr, err) + } + + // The cache entry for this proposal should be created, if it wasn't the future proposal case + err, isCached = backendCore.current.GetProposalVerificationStatus(testCase.proposal.Hash()) + if testCase.name != "Future proposal" { + if !isCached { + t.Errorf("Should of had a cache hit") + } + + if err != testCase.expectedErr { + t.Errorf("Unexpected cached proposal verification status. Want: %v, actual: %v", testCase.expectedErr, err) + } + } else { // testCase.name == "Future proposal" + if isCached { + t.Errorf("Should of had a cache miss for the future proposal test case") + } + } + + // Call verify proposal again to check for the cached verifcation result and duration + duration, err := backendCore.verifyProposal(testCase.proposal) + if duration != testCase.expectedDuration || err != testCase.expectedErr { + t.Errorf("Unexpected return status on second verifyProposal call. Want: err - %v, duration - %v; Actual: err - %v, duration - %v", testCase.expectedErr, testCase.expectedDuration, err, duration) + } + }) + } +} + +func TestEpochSnarkData(t *testing.T) { + N := uint64(4) + F := uint64(1) + + sys := NewTestSystemWithBackendDonut(N, F, 1, 2) + + close := sys.Run(true) + defer close() + + request1 := makeBlock(1) + sys.backends[0].NewRequest(request1) + + <-time.After(1 * time.Second) + + request2 := makeBlock(2) + sys.backends[0].NewRequest(request2) + + <-time.After(1 * time.Second) + + backendCore := sys.backends[0].engine.(*core) + privateKey, _ := bls.DeserializePrivateKey(sys.backends[0].blsKey) + defer privateKey.Destroy() + + serializedPrivateKey, _ := privateKey.Serialize() + + publicKey, _ := blscrypto.PrivateToPublic(serializedPrivateKey) + + message, extraData, cip22, _ := backendCore.generateEpochValidatorSetData(0, 0, common.Hash{}, sys.backends[0].Validators(backendCore.current.Proposal())) + if cip22 || len(extraData) > 0 { + t.Errorf("Unexpected cip22 (%t != false) or extraData length (%v > 0)", cip22, len(extraData)) + } + epochValidatorSetSeal, _ := backendCore.backend.SignBLS(message, extraData, true, cip22) + + if err := blscrypto.VerifySignature(publicKey, message, extraData, epochValidatorSetSeal[:], true, cip22); err != nil { + t.Errorf("Failed verifying BLS signature") + } + + message, extraData, cip22, _ = backendCore.generateEpochValidatorSetData(2, 0, common.Hash{}, sys.backends[0].Validators(backendCore.current.Proposal())) + if !cip22 || len(extraData) == 0 { + t.Errorf("Unexpected cip22 (%t != true) or extraData length (%v == 0)", cip22, len(extraData)) + } + epochValidatorSetSeal, _ = backendCore.backend.SignBLS(message, extraData, true, cip22) + + if err := blscrypto.VerifySignature(publicKey, message, extraData, epochValidatorSetSeal[:], true, cip22); err != nil { + t.Errorf("Failed verifying BLS signature after Donut") + } + +}
diff --git go-ethereum/consensus/istanbul/proxy/enode_certificate.go celo/consensus/istanbul/proxy/enode_certificate.go new file mode 100644 index 0000000000000000000000000000000000000000..bfa8d69c3ac15478653dab066b552f54321d44b4 --- /dev/null +++ celo/consensus/istanbul/proxy/enode_certificate.go @@ -0,0 +1,102 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "encoding/hex" + + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" +) + +// handleEnodeCertificateMsgFromRemoteVal will handle an enode certificate sent from +// a remote validator. +func (p *proxyEngine) handleEnodeCertificateMsgFromRemoteVal(peer consensus.Peer, payload []byte) (bool, error) { + logger := p.logger.New("func", "handleEnodeCertificateMsgFromRemoteVal") + + msg := new(istanbul.Message) + + // Verify that this message is created by a legitimate validator before forwarding to the proxied validator (it should + // be within the validator connection set). + if err := msg.FromPayload(payload, p.backend.VerifyValidatorConnectionSetSignature); err != nil { + logger.Error("Got an enodeCertificate message signed by a validator not within the validator connection set.", "err", err) + return true, istanbul.ErrUnauthorizedAddress + } + + // Need to forward the message to the proxied validators + logger.Trace("Forwarding enode certificate message to proxied validators", "from", peer.Node().ID()) + for proxiedValidator := range p.proxiedValidators { + p.backend.Unicast(proxiedValidator, payload, istanbul.EnodeCertificateMsg) + } + + // We could add an optimization here where the proxy will save thie enodeCertificate in it's own valEnodeTable. + // For now, the proxies entry will get updated via a valEnodesShare message from the proxied validator. + + return true, nil +} + +// handleEnodeCertificateFromProxiedValidator will handle an enode certifcate message sent from the proxied validator +func (p *proxyEngine) handleEnodeCertificateMsgFromProxiedValidator(peer consensus.Peer, payload []byte) (bool, error) { + logger := p.logger.New("func", "handleEnodeCertificateMsgFromProxiedValidator") + + logger.Trace("Handling an enode certificate msg from proxied validator") + + msg := new(istanbul.Message) + // Decode message + err := msg.FromPayload(payload, istanbul.GetSignatureAddress) + if err != nil { + logger.Error("Error in decoding received Enode Certificate message from forward message", "err", err, "payload", hex.EncodeToString(payload)) + return false, err + } + + // Verify that the sender is from the proxied validator + if msg.Address != p.config.ProxiedValidatorAddress { + logger.Error("Unauthorized Enode Certificate message", "sender address", msg.Address, "authorized sender address", p.config.ProxiedValidatorAddress) + return false, errUnauthorizedMessageFromProxiedValidator + } + + var enodeCertificate istanbul.EnodeCertificate + if err := rlp.DecodeBytes(msg.Msg, &enodeCertificate); err != nil { + logger.Warn("Error in decoding received Istanbul Enode Certificate message content", "err", err, "IstanbulMsg", msg.String()) + return false, err + } + + enodeCertificateNode, err := enode.ParseV4(enodeCertificate.EnodeURL) + if err != nil { + logger.Warn("Malformed v4 node in received Istanbul Enode Certificate message", "enodeCertificate", enodeCertificate, "err", err) + return false, err + } + + // If this enode certificate's nodeID is the same as the node's external nodeID, then save it. + selfNode := p.backend.SelfNode() + if enodeCertificateNode.ID() == selfNode.ID() { + enodeCertMsgMap := make(map[enode.ID]*istanbul.EnodeCertMsg) + enodeCertMsgMap[selfNode.ID()] = &istanbul.EnodeCertMsg{Msg: msg} + if err := p.backend.SetEnodeCertificateMsgMap(enodeCertMsgMap); err != nil { + logger.Warn("Error in setting proxy's enode certificate", "err", err, "enodeCertificate", enodeCertificate) + // Don't drop validators when switching over. + if err == istanbul.ErrInvalidEnodeCertMsgMapOldVersion { + return true, nil + } + return true, err + } + } + + return true, nil +}
diff --git go-ethereum/consensus/istanbul/backend/api.go celo/consensus/istanbul/backend/api.go new file mode 100644 index 0000000000000000000000000000000000000000..73f815c4687b994a8be17fc16ddd6841a7460dcd --- /dev/null +++ celo/consensus/istanbul/backend/api.go @@ -0,0 +1,307 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "errors" + "fmt" + "math/big" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/announce" + "github.com/ethereum/go-ethereum/consensus/istanbul/backend/internal/replica" + "github.com/ethereum/go-ethereum/consensus/istanbul/core" + "github.com/ethereum/go-ethereum/consensus/istanbul/proxy" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rpc" +) + +// API is a user facing RPC API to dump Istanbul state +type API struct { + chain consensus.ChainHeaderReader + istanbul *Backend +} + +// getHeaderByNumber retrieves the header requested block or current if unspecified. +func (api *API) getHeaderByNumber(number *rpc.BlockNumber) (*types.Header, error) { + var header *types.Header + if number == nil || *number == rpc.LatestBlockNumber { + header = api.chain.CurrentHeader() + } else if *number == rpc.PendingBlockNumber { + return nil, fmt.Errorf("can't use pending block within istanbul") + } else if *number == rpc.EarliestBlockNumber { + header = api.chain.GetHeaderByNumber(0) + } else { + header = api.chain.GetHeaderByNumber(uint64(*number)) + } + + if header == nil { + return nil, errUnknownBlock + } + return header, nil +} + +// getParentHeaderByNumber retrieves the parent header requested block or current if unspecified. +func (api *API) getParentHeaderByNumber(number *rpc.BlockNumber) (*types.Header, error) { + var parent uint64 + if number == nil || *number == rpc.LatestBlockNumber || *number == rpc.PendingBlockNumber { + head := api.chain.CurrentHeader() + if head == nil { + return nil, errUnknownBlock + } + if number == nil || *number == rpc.LatestBlockNumber { + parent = head.Number.Uint64() - 1 + } else { + parent = head.Number.Uint64() + } + } else if *number == rpc.EarliestBlockNumber { + return nil, errUnknownBlock + } else { + parent = uint64(*number - 1) + } + + header := api.chain.GetHeaderByNumber(parent) + if header == nil { + return nil, errUnknownBlock + } + return header, nil +} + +// GetSnapshot retrieves the state snapshot at a given block. +func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) { + // Retrieve the requested block number (or current if none requested) + var header *types.Header + if number == nil || *number == rpc.LatestBlockNumber { + header = api.chain.CurrentHeader() + } else { + header = api.chain.GetHeaderByNumber(uint64(number.Int64())) + } + // Ensure we have an actually valid block and return its snapshot + if header == nil { + return nil, errUnknownBlock + } + return api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) +} + +// GetValidators retrieves the list validators that must sign a given block. +func (api *API) GetValidators(number *rpc.BlockNumber) ([]common.Address, error) { + header, err := api.getParentHeaderByNumber(number) + if err != nil { + return nil, err + } + validators := api.istanbul.GetValidators(header.Number, header.Hash()) + return istanbul.MapValidatorsToAddresses(validators), nil +} + +// GetValidatorsBLSPublicKeys retrieves the list of validators BLS public keys that must sign a given block. +func (api *API) GetValidatorsBLSPublicKeys(number *rpc.BlockNumber) ([]blscrypto.SerializedPublicKey, error) { + header, err := api.getParentHeaderByNumber(number) + if err != nil { + return nil, err + } + validators := api.istanbul.GetValidators(header.Number, header.Hash()) + return istanbul.MapValidatorsToPublicKeys(validators), nil +} + +// GetProposer retrieves the proposer for a given block number (i.e. sequence) and round. +func (api *API) GetProposer(sequence *rpc.BlockNumber, round *uint64) (common.Address, error) { + header, err := api.getParentHeaderByNumber(sequence) + if err != nil { + return common.Address{}, err + } + + valSet := api.istanbul.getOrderedValidators(header.Number.Uint64(), header.Hash()) + if valSet == nil { + return common.Address{}, err + } + previousProposer, err := api.istanbul.Author(header) + if err != nil { + return common.Address{}, err + } + if round == nil { + round = new(uint64) + } + proposer := validator.GetProposerSelector(api.istanbul.config.ProposerPolicy)(valSet, previousProposer, *round) + return proposer.Address(), nil +} + +// AddProxy peers with a remote node that acts as a proxy, even if slots are full +func (api *API) AddProxy(url, externalUrl string) (bool, error) { + if !api.istanbul.config.Proxied { + api.istanbul.logger.Error("Add proxy node failed: this node is not configured to be proxied") + return false, errors.New("Can't add proxy for node that is not configured to be proxied") + } + + node, err := enode.ParseV4(url) + if err != nil { + return false, fmt.Errorf("invalid enode: %v", err) + } + + externalNode, err := enode.ParseV4(externalUrl) + if err != nil { + return false, fmt.Errorf("invalid external enode: %v", err) + } + + err = api.istanbul.AddProxy(node, externalNode) + return true, err +} + +// RemoveProxy removes a node from acting as a proxy +func (api *API) RemoveProxy(url string) (bool, error) { + // Try to remove the url as a proxy and return + node, err := enode.ParseV4(url) + if err != nil { + return false, fmt.Errorf("invalid enode: %v", err) + } + if err = api.istanbul.RemoveProxy(node); err != nil { + return false, err + } + + return true, nil +} + +// Retrieve the Validator Enode Table +func (api *API) GetValEnodeTable() (map[string]*announce.ValEnodeEntryInfo, error) { + return api.istanbul.valEnodeTable.ValEnodeTableInfo() +} + +func (api *API) GetVersionCertificateTableInfo() (map[string]*announce.VersionCertificateEntryInfo, error) { + return api.istanbul.announceManager.GetVersionCertificateTableInfo() +} + +// GetCurrentRoundState retrieves the current IBFT RoundState +func (api *API) GetCurrentRoundState() (*core.RoundStateSummary, error) { + api.istanbul.coreMu.RLock() + defer api.istanbul.coreMu.RUnlock() + + if !api.istanbul.isCoreStarted() { + return nil, istanbul.ErrStoppedEngine + } + return api.istanbul.core.CurrentRoundState().Summary(), nil +} + +// GetCurrentRoundChangeSet retrieves the current round change set +func (api *API) GetCurrentRoundChangeSet() (*core.RoundChangeSetSummary, error) { + api.istanbul.coreMu.RLock() + defer api.istanbul.coreMu.RUnlock() + + if !api.istanbul.isCoreStarted() { + return nil, istanbul.ErrStoppedEngine + } + return api.istanbul.core.CurrentRoundChangeSet(), nil +} + +func (api *API) ForceRoundChange() (bool, error) { + api.istanbul.coreMu.RLock() + defer api.istanbul.coreMu.RUnlock() + + if !api.istanbul.isCoreStarted() { + return false, istanbul.ErrStoppedEngine + } + api.istanbul.core.ForceRoundChange() + return true, nil +} + +// GetProxiesInfo retrieves all the proxied validator's proxies' info +func (api *API) GetProxiesInfo() ([]*proxy.ProxyInfo, error) { + if api.istanbul.IsProxiedValidator() { + proxies, valAssignments, err := api.istanbul.proxiedValidatorEngine.GetProxiesAndValAssignments() + + if err != nil { + return nil, err + } + + proxyInfoArray := make([]*proxy.ProxyInfo, 0, len(proxies)) + + for _, proxyObj := range proxies { + proxyInfoArray = append(proxyInfoArray, proxy.NewProxyInfo(proxyObj, valAssignments[proxyObj.ID()])) + } + + return proxyInfoArray, nil + } else { + return nil, proxy.ErrNodeNotProxiedValidator + } +} + +// ProxiedValidators retrieves all of the proxies connected proxied validators. +// Note that we plan to support validators per proxy in the future, so this function +// is plural and returns an array of proxied validators. This is to prevent +// future backwards compatibility issues. +func (api *API) GetProxiedValidators() ([]*proxy.ProxiedValidatorInfo, error) { + if api.istanbul.IsProxy() { + return api.istanbul.proxyEngine.GetProxiedValidatorsInfo() + } else { + return nil, proxy.ErrNodeNotProxy + } +} + +// StartValidating starts the consensus engine +func (api *API) StartValidating() error { + return api.istanbul.MakePrimary() +} + +// StopValidating stops the consensus engine from participating in consensus +func (api *API) StopValidating() error { + return api.istanbul.MakeReplica() +} + +// StartValidatingAtBlock starts the consensus engine on the given +// block number. +func (api *API) StartValidatingAtBlock(blockNumber int64) error { + seq := big.NewInt(blockNumber) + return api.istanbul.SetStartValidatingBlock(seq) +} + +// StopValidatingAtBlock stops the consensus engine from participating in consensus +// on the given block number. +func (api *API) StopValidatingAtBlock(blockNumber int64) error { + seq := big.NewInt(blockNumber) + return api.istanbul.SetStopValidatingBlock(seq) +} + +// IsValidating returns true if this node is participating in the consensus protocol +func (api *API) IsValidating() bool { + return api.istanbul.IsValidating() +} + +// GetCurrentReplicaState retrieves the current replica state +func (api *API) GetCurrentReplicaState() (*replica.ReplicaStateSummary, error) { + if api.istanbul.replicaState != nil { + return api.istanbul.replicaState.Summary(), nil + } + return &replica.ReplicaStateSummary{State: "Not a validator"}, nil +} + +// ResendPreprepare sends again the preprepare message +func (api *API) ResendPreprepare() error { + return api.istanbul.core.ResendPreprepare() +} + +// GossipPrepares gossips the prepare messages +func (api *API) GossipPrepares() error { + return api.istanbul.core.GossipPrepares() +} + +// GossipCommits gossips the commit messages +func (api *API) GossipCommits() error { + return api.istanbul.core.GossipCommits() +}
diff --git go-ethereum/consensus/istanbul/backend/snapshot_test.go celo/consensus/istanbul/backend/snapshot_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6035d49f706eff1f5c712ab954efbf1e0ad35bb8 --- /dev/null +++ celo/consensus/istanbul/backend/snapshot_test.go @@ -0,0 +1,386 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "bytes" + "crypto/ecdsa" + "math/big" + "reflect" + "sort" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +type testerValSetDiff struct { + proposer string + addedValidators []string + removedValidators []string +} + +// testerAccountPool is a pool to maintain currently active tester accounts, +// mapped from textual names used in the tests below to actual Ethereum private +// keys capable of signing transactions. +type testerAccountPool struct { + accounts map[string]*ecdsa.PrivateKey +} + +func newTesterAccountPool() *testerAccountPool { + return &testerAccountPool{ + accounts: make(map[string]*ecdsa.PrivateKey), + } +} + +func (ap *testerAccountPool) sign(header *types.Header, validator string) { + // Ensure we have a persistent key for the validator + if ap.accounts[validator] == nil { + ap.accounts[validator], _ = crypto.GenerateKey() + } + // Sign the header and embed the signature in extra data + hashData := crypto.Keccak256(sigHash(header).Bytes()) + sig, _ := crypto.Sign(hashData, ap.accounts[validator]) + + writeSeal(header, sig) +} + +func (ap *testerAccountPool) address(account string) common.Address { + // Ensure we have a persistent key for the account + if account == "" { + return common.Address{} + } + if ap.accounts[account] == nil { + ap.accounts[account], _ = crypto.GenerateKey() + } + // Resolve and return the Ethereum address + return crypto.PubkeyToAddress(ap.accounts[account].PublicKey) +} + +func convertValNamesToRemovedValidators(accounts *testerAccountPool, oldVals []istanbul.ValidatorData, valNames []string) *big.Int { + bitmap := big.NewInt(0) + for _, v := range valNames { + for j := range oldVals { + if accounts.address(v) == oldVals[j].Address { + bitmap = bitmap.SetBit(bitmap, j, 1) + } + } + } + + return bitmap +} + +func convertValNames(accounts *testerAccountPool, valNames []string) []common.Address { + returnArray := make([]common.Address, len(valNames)) + + for i, valName := range valNames { + returnArray[i] = accounts.address(valName) + } + + return returnArray +} + +func convertValNamesToValidatorsData(accounts *testerAccountPool, valNames []string) []istanbul.ValidatorData { + returnArray := make([]istanbul.ValidatorData, len(valNames)) + + for i, valName := range valNames { + returnArray[i] = istanbul.ValidatorData{ + Address: accounts.address(valName), + BLSPublicKey: blscrypto.SerializedPublicKey{}, + } + } + + return returnArray +} + +// Define a mock blockchain +type mockBlockchain struct { + headers map[uint64]*types.Header +} + +func (bc *mockBlockchain) AddHeader(number uint64, header *types.Header) { + bc.headers[number] = header +} + +func (bc *mockBlockchain) GetHeaderByNumber(number uint64) *types.Header { + return bc.headers[number] +} + +func (bc *mockBlockchain) Config() *params.ChainConfig { + return &params.ChainConfig{FullHeaderChainAvailable: true} +} + +func (bc *mockBlockchain) CurrentHeader() *types.Header { + return nil +} + +func (bc *mockBlockchain) GetHeader(hash common.Hash, number uint64) *types.Header { + return nil +} + +func (bc *mockBlockchain) GetHeaderByHash(hash common.Hash) *types.Header { + return nil +} + +func (bc *mockBlockchain) GetBlock(hash common.Hash, number uint64) *types.Block { + return nil +} + +// Tests that validator set changes are evaluated correctly for various simple and complex scenarios. +func TestValSetChange(t *testing.T) { + // Define the various voting scenarios to test + tests := []struct { + epoch uint64 + validators []string + valsetdiffs []testerValSetDiff + results []string + err error + }{ + { + // Single validator, empty val set diff + epoch: 1, + validators: []string{"A"}, + valsetdiffs: []testerValSetDiff{{proposer: "A", addedValidators: []string{}, removedValidators: []string{}}}, + results: []string{"A"}, + err: nil, + }, { + // Single validator, add two new validators + epoch: 1, + validators: []string{"A"}, + valsetdiffs: []testerValSetDiff{{proposer: "A", addedValidators: []string{"B", "C"}, removedValidators: []string{}}}, + results: []string{"A", "B", "C"}, + err: nil, + }, { + // Two validator, remove two validators + epoch: 1, + validators: []string{"A", "B"}, + valsetdiffs: []testerValSetDiff{{proposer: "B", addedValidators: []string{}, removedValidators: []string{"A", "B"}}}, + results: []string{}, + err: nil, + }, { + // Three validator, add two validators and remove two validators + epoch: 1, + validators: []string{"A", "B", "C"}, + valsetdiffs: []testerValSetDiff{{proposer: "A", addedValidators: []string{"D", "E"}, removedValidators: []string{"B", "C"}}}, + results: []string{"A", "D", "E"}, + err: nil, + }, { + // Three validator, add two validators and remove two validators with an unauthorized proposer + epoch: 1, + validators: []string{"A", "B", "C"}, + valsetdiffs: []testerValSetDiff{{proposer: "D", addedValidators: []string{"D", "E"}, removedValidators: []string{"B", "C"}}}, + results: []string{"A", "D", "E"}, + err: errUnauthorized, + }, + { + // Three validator, add two validators and remove two validators. Second header will add 1 validators and remove 2 validators. + epoch: 1, + validators: []string{"A", "B", "C"}, + valsetdiffs: []testerValSetDiff{{proposer: "A", addedValidators: []string{"D", "E"}, removedValidators: []string{"B", "C"}}, + {proposer: "E", addedValidators: []string{"F"}, removedValidators: []string{"A", "D"}}}, + results: []string{"F", "E"}, + err: nil, + }, { + // Three validator, add two validators and remove two validators. Second header will add 1 validators and remove 2 validators. The first header will + // be ignored, since it's no the last header of an epoch + epoch: 2, + validators: []string{"A", "B", "C"}, + valsetdiffs: []testerValSetDiff{{proposer: "A", addedValidators: []string{"D", "E"}, removedValidators: []string{"B", "C"}}, + {proposer: "A", addedValidators: []string{"F"}, removedValidators: []string{"A", "B"}}}, + results: []string{"F", "C"}, + err: nil, + }, + } + // Run through the scenarios and test them + for i, tt := range tests { + // Create the account pool and generate the initial set of validators + accounts := newTesterAccountPool() + + validators := make([]istanbul.ValidatorData, len(tt.validators)) + for j, validator := range tt.validators { + validators[j] = istanbul.ValidatorData{ + Address: accounts.address(validator), + BLSPublicKey: blscrypto.SerializedPublicKey{}, + } + } + + // Create the genesis block with the initial set of validators + genesis := &core.Genesis{ + Config: params.IstanbulTestChainConfig, + } + extra, _ := rlp.EncodeToBytes(&types.IstanbulExtra{}) + genesis.ExtraData = append(make([]byte, types.IstanbulExtraVanity), extra...) + b := genesis.ToBlock(nil) + h := b.Header() + err := writeValidatorSetDiff(h, []istanbul.ValidatorData{}, validators) + if err != nil { + t.Errorf("Could not update genesis validator set, got err: %v", err) + } + genesis.ExtraData = h.Extra + db := rawdb.NewMemoryDatabase() + defer db.Close() + + config := *istanbul.DefaultConfig + config.ReplicaStateDBPath = "" + config.Validator = true + config.ValidatorEnodeDBPath = "" + config.VersionCertificateDBPath = "" + config.RoundStateDBPath = "" + if tt.epoch != 0 { + config.Epoch = tt.epoch + } + + chain := &mockBlockchain{ + headers: make(map[uint64]*types.Header), + } + + engine := New(&config, db).(*Backend) + + privateKey := accounts.accounts[tt.validators[0]] + address := crypto.PubkeyToAddress(privateKey.PublicKey) + + engine.Authorize(address, address, &privateKey.PublicKey, DecryptFn(privateKey), SignFn(privateKey), SignBLSFn(privateKey), SignHashFn(privateKey)) + + chain.AddHeader(0, genesis.ToBlock(nil).Header()) + + // Assemble a chain of headers from header validator set diffs + var prevHeader *types.Header + var currentVals []istanbul.ValidatorData + var snap *Snapshot + for j, valsetdiff := range tt.valsetdiffs { + header := &types.Header{ + Number: big.NewInt(int64(j) + 1), + Time: uint64(j) * config.BlockPeriod, + } + + var buf bytes.Buffer + + buf.Write(bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)) + + var oldVals []istanbul.ValidatorData + if currentVals == nil { + oldVals = convertValNamesToValidatorsData(accounts, tests[i].validators) + } else { + oldVals = currentVals + } + + ist := &types.IstanbulExtra{ + AddedValidators: convertValNames(accounts, valsetdiff.addedValidators), + AddedValidatorsPublicKeys: make([]blscrypto.SerializedPublicKey, len(valsetdiff.addedValidators)), + RemovedValidators: convertValNamesToRemovedValidators(accounts, oldVals, valsetdiff.removedValidators), + AggregatedSeal: types.IstanbulAggregatedSeal{}, + ParentAggregatedSeal: types.IstanbulAggregatedSeal{}, + } + + payload, err := rlp.EncodeToBytes(&ist) + if err != nil { + t.Errorf("test %d, valsetdiff %d: error in encoding extra header info", i, j) + } + header.Extra = append(buf.Bytes(), payload...) + + if j > 0 { + header.ParentHash = prevHeader.Hash() + } + + accounts.sign(header, valsetdiff.proposer) + + chain.AddHeader(uint64(j+1), header) + + prevHeader = header + snap, err = engine.snapshot(chain, prevHeader.Number.Uint64(), prevHeader.Hash(), nil) + if err != tt.err { + t.Errorf("test %d: error mismatch: have %v, want %v", i, err, tt.err) + } + + if err != nil { + continue + } + + currentVals = snap.validators() + } + if tt.err != nil { + continue + } + + // Verify the final list of validators against the expected ones + validators = make([]istanbul.ValidatorData, len(tt.results)) + for j, validator := range tt.results { + validators[j] = istanbul.ValidatorData{ + Address: accounts.address(validator), + BLSPublicKey: blscrypto.SerializedPublicKey{}, + } + } + result := snap.validators() + if len(result) != len(validators) { + t.Errorf("test %d: validators mismatch: have %x, want %x", i, result, validators) + continue + } + + sort.Sort(istanbul.ValidatorsDataByAddress(result)) + sort.Sort(istanbul.ValidatorsDataByAddress(validators)) + for j := 0; j < len(result); j++ { + if !bytes.Equal(result[j].Address[:], validators[j].Address[:]) { + t.Errorf("test %d, validator %d: validator mismatch: have %x, want %x", i, j, result[j], validators[j]) + } + } + } +} + +func TestSaveAndLoad(t *testing.T) { + snap := &Snapshot{ + Epoch: 5, + Number: 10, + Hash: common.HexToHash("1234567890"), + ValSet: validator.NewSet([]istanbul.ValidatorData{ + { + Address: common.BytesToAddress([]byte("1234567894")), + BLSPublicKey: blscrypto.SerializedPublicKey{}, + }, + { + Address: common.BytesToAddress([]byte("1234567895")), + BLSPublicKey: blscrypto.SerializedPublicKey{}, + }, + }), + } + db := rawdb.NewMemoryDatabase() + err := snap.store(db) + if err != nil { + t.Errorf("store snapshot failed: %v", err) + } + + snap1, err := loadSnapshot(snap.Epoch, db, snap.Hash) + if err != nil { + t.Errorf("load snapshot failed: %v", err) + } + if snap.Epoch != snap1.Epoch { + t.Errorf("epoch mismatch: have %v, want %v", snap1.Epoch, snap.Epoch) + } + if snap.Hash != snap1.Hash { + t.Errorf("hash mismatch: have %v, want %v", snap1.Number, snap.Number) + } + if !reflect.DeepEqual(snap.ValSet, snap.ValSet) { + t.Errorf("validator set mismatch: have %v, want %v", snap1.ValSet, snap.ValSet) + } +}
diff --git go-ethereum/consensus/istanbul/announce/version.go celo/consensus/istanbul/announce/version.go new file mode 100644 index 0000000000000000000000000000000000000000..018d6a95057a1a3c98e537680335edfc63424de3 --- /dev/null +++ celo/consensus/istanbul/announce/version.go @@ -0,0 +1,32 @@ +package announce + +import "sync/atomic" + +type Version interface { + Get() uint + Set(version uint) +} + +type VersionReader interface { + Get() uint +} + +type atomicVersion struct { + v atomic.Value +} + +func NewAtomicVersion() Version { + return &atomicVersion{} +} + +func (av *atomicVersion) Get() uint { + vuint := av.v.Load() + if vuint == nil { + return 0 + } + return vuint.(uint) +} + +func (av *atomicVersion) Set(version uint) { + av.v.Store(version) +}
diff --git go-ethereum/consensus/istanbul/announce/efeg.go celo/consensus/istanbul/announce/efeg.go new file mode 100644 index 0000000000000000000000000000000000000000..f5df4ef39d895930b67f5936e778ab01af5cfc9f --- /dev/null +++ celo/consensus/istanbul/announce/efeg.go @@ -0,0 +1,51 @@ +package announce + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul/proxy" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +type ExternalFacingEnodeGetter interface { + // GetEnodeCertNodesAndDestAddresses will retrieve all the external facing external nodes for this validator + // (one for each of it's proxies, or itself for standalone validators) for the purposes of generating enode certificates + // for those enodes. It will also return the destination validators for each enode certificate. If the destAddress is a + // `nil` value, then that means that the associated enode certificate should be sent to all of the connected validators. + GetEnodeCertNodesAndDestAddresses() ([]*enode.Node, map[enode.ID][]common.Address, error) +} + +func NewSelfExternalFacingEnodeGetter(selfNodeFn func() *enode.Node) ExternalFacingEnodeGetter { + return &selfEFEG{selfNode: selfNodeFn} +} + +func NewProxiedExternalFacingEnodeGetter(getProxiesAndValAssignmentsFn func() ([]*proxy.Proxy, map[enode.ID][]common.Address, error)) ExternalFacingEnodeGetter { + return &proxiedEFEG{getProxiesAndValAssignments: getProxiesAndValAssignmentsFn} +} + +type selfEFEG struct { + selfNode func() *enode.Node +} + +func (s *selfEFEG) GetEnodeCertNodesAndDestAddresses() ([]*enode.Node, map[enode.ID][]common.Address, error) { + self := s.selfNode() + valDestinations := make(map[enode.ID][]common.Address) + valDestinations[self.ID()] = nil + return []*enode.Node{self}, valDestinations, nil +} + +type proxiedEFEG struct { + getProxiesAndValAssignments func() ([]*proxy.Proxy, map[enode.ID][]common.Address, error) +} + +func (p *proxiedEFEG) GetEnodeCertNodesAndDestAddresses() ([]*enode.Node, map[enode.ID][]common.Address, error) { + proxies, valDestinations, err := p.getProxiesAndValAssignments() + if err != nil { + return nil, nil, err + } + + externalEnodes := make([]*enode.Node, len(proxies)) + for i, proxy := range proxies { + externalEnodes[i] = proxy.ExternalNode() + } + return externalEnodes, valDestinations, nil +}
diff --git go-ethereum/consensus/istanbul/core/commit_v2_test.go celo/consensus/istanbul/core/commit_v2_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e44740462f3883fd6d70fc58f705db058c2227eb --- /dev/null +++ celo/consensus/istanbul/core/commit_v2_test.go @@ -0,0 +1,384 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/celo-org/celo-bls-go/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +func TestHandleCommitV2(t *testing.T) { + N := uint64(4) + F := uint64(1) + + // create block 4 + proposal := newTestProposalWithNum(4) + expectedSubject := &istanbul.Subject{ + View: &istanbul.View{ + Round: big.NewInt(0), + Sequence: proposal.Number(), + }, + Digest: proposal.Hash(), + } + + testCases := []struct { + system *testSystem + expectedErr error + checkParentCommits bool + }{ + { + // normal case + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + // same view as the expected one to everyone + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePrepared + } + } + return sys + }(), + nil, + false, + }, + { + // future message + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePreprepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + // proposal from 1 round in the future + Sequence: big.NewInt(0).Add(proposal.Number(), common.Big1), + }, + backend.peers, + ) + } + } + return sys + }(), + errFutureMessage, + false, + }, + { + // past message + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePreprepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + // we're 2 blocks before so this is indeed a + // very old proposal and will error as expected + // with an old error message + Sequence: big.NewInt(0).Sub(proposal.Number(), common.Big2), + }, + backend.peers, + ) + } + } + return sys + }(), + errOldMessage, + false, + }, + { + // jump state + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: proposal.Number(), + }, + backend.peers, + ) + + // only replica0 stays at StatePreprepared + // other replicas are at StatePrepared + if i != 0 { + c.current.(*roundStateImpl).state = StatePrepared + } else { + c.current.(*roundStateImpl).state = StatePreprepared + } + } + return sys + }(), + nil, + false, + }, + { + // message from previous sequence and round matching last proposal + // this should pass the message check, but will return an error in + // handleCheckedCommitForPreviousSequence, because the proposal hashes won't match. + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + + for i, backend := range sys.backends { + backend.Commit(newTestProposalWithNum(3), types.IstanbulAggregatedSeal{}, types.IstanbulEpochValidatorSetSeal{}, nil) + c := backend.engine.(*core) + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePrepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(1), + Sequence: big.NewInt(0).Sub(proposal.Number(), common.Big1), + }, + backend.peers, + ) + } + } + return sys + }(), + errInconsistentSubject, + true, + }, + // TODO: double send message + } + +OUTER: + for _, test := range testCases { + test.system.Run(false) + + v0 := test.system.backends[0] + r0 := v0.engine.(*core) + + for i, v := range test.system.backends { + validator := r0.current.ValidatorSet().GetByIndex(uint64(i)) + privateKey, _ := bls.DeserializePrivateKey(test.system.validatorsKeys[i]) + defer privateKey.Destroy() + + hash := PrepareCommittedSeal(v.engine.(*core).current.Proposal().Hash(), v.engine.(*core).current.Round()) + signature, _ := privateKey.SignMessage(hash, []byte{}, false, false) + defer signature.Destroy() + signatureBytes, _ := signature.Serialize() + + msg := istanbul.NewCommitMessage( + &istanbul.CommittedSubject{Subject: v.engine.(*core).current.Subject(), CommittedSeal: signatureBytes}, + validator.Address(), + ) + + if err := r0.handleCommit(msg); err != nil { + if err != test.expectedErr { + t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) + } + continue OUTER + } + } + + // core should have received a parent seal from each of its neighbours + // how can we add our signature to the ParentCommit? Broadcast to ourselve + // does not make much sense + if test.checkParentCommits { + if r0.current.ParentCommits().Size() != r0.current.ValidatorSet().Size()-1 { // TODO: Maybe remove the -1? + t.Errorf("parent seals mismatch: have %v, want %v", r0.current.ParentCommits().Size(), r0.current.ValidatorSet().Size()-1) + } + } + + // prepared is normal case + if r0.current.State() != StateCommitted { + // There are not enough commit messages in core + if r0.current.State() != StatePrepared { + t.Errorf("state mismatch: have %v, want %v", r0.current.State(), StatePrepared) + } + if r0.current.Commits().Size() > r0.current.ValidatorSet().MinQuorumSize() { + t.Errorf("the size of commit messages should be less than %v", r0.current.ValidatorSet().MinQuorumSize()) + } + continue + } + + // core should have min quorum size prepare messages + if r0.current.Commits().Size() < r0.current.ValidatorSet().MinQuorumSize() { + t.Errorf("the size of commit messages should be greater than or equal to minQuorumSize: size %v", r0.current.Commits().Size()) + } + + // check signatures large than MinQuorumSize + signedCount := 0 + for i := 0; i < r0.current.ValidatorSet().Size(); i++ { + if v0.committedMsgs[0].aggregatedSeal.Bitmap.Bit(i) == 1 { + signedCount++ + } + } + if signedCount < r0.current.ValidatorSet().MinQuorumSize() { + t.Errorf("the expected signed count should be greater than or equal to %v, but got %v", r0.current.ValidatorSet().MinQuorumSize(), signedCount) + } + } +} + +// round is not checked for now +func TestVerifyCommitV2(t *testing.T) { + // for log purpose + privateKey, _ := crypto.GenerateKey() + blsPrivateKey, _ := blscrypto.ECDSAToBLS(privateKey) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + peer := validator.New(getPublicKeyAddress(privateKey), blsPublicKey) + valSet := validator.NewSet([]istanbul.ValidatorData{ + { + Address: peer.Address(), + BLSPublicKey: blsPublicKey, + }, + }) + // }, istanbul.RoundRobin) + + sys := NewTestSystemWithBackendV2(uint64(1), uint64(0)) + + testCases := []struct { + expected error + commit *istanbul.CommittedSubject + roundState RoundState + }{ + { + // normal case + expected: nil, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + { + // old message + expected: errInconsistentSubject, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // different digest + expected: errInconsistentSubject, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: common.BytesToHash([]byte("1234567890")), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // malicious package(lack of sequence) + expected: errInconsistentSubject, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: nil}, + Digest: newTestProposal().Hash(), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // wrong prepare message with same sequence but different round + expected: errInconsistentSubject, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + { + // wrong prepare message with same round but different sequence + expected: errInconsistentSubject, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)}, + Digest: newTestProposal().Hash(), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + } + for i, test := range testCases { + c := sys.backends[0].engine.(*core) + c.current = test.roundState + + if err := c.verifyCommit(test.commit); err != test.expected { + t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected) + } + } +}
diff --git go-ethereum/mobile/geth_other.go celo/consensus/istanbul/core/final_committed.go rename from mobile/geth_other.go rename to consensus/istanbul/core/final_committed.go index c5cad4a7ba8ee0a4a31fe9e63ffb9a14e8418c01..894544779a7ee892233c89953163d507f1e0995c 100644 --- go-ethereum/mobile/geth_other.go +++ celo/consensus/istanbul/core/final_committed.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -14,10 +14,10 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.   -//go:build !android && !ios -// +build !android,!ios - -package geth +package core   -// clientIdentifier is a hard coded identifier to report into the network. -var clientIdentifier = "GethMobile" +func (c *core) handleFinalCommitted() error { + logger := c.newLogger("func", "handleFinalCommitted") + logger.Trace("Received a final committed proposal") + return c.startNewSequence() +}
diff --git go-ethereum/consensus/istanbul/core/roundstate.go celo/consensus/istanbul/core/roundstate.go new file mode 100644 index 0000000000000000000000000000000000000000..7c1a326fb10650bc07b8060a23debd6f87a2fbb7 --- /dev/null +++ celo/consensus/istanbul/core/roundstate.go @@ -0,0 +1,722 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "bytes" + "errors" + "io" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + // errFailedCreatePreparedCertificate is returned when there aren't enough PREPARE messages to create a PREPARED certificate. + errFailedCreatePreparedCertificate = errors.New("failed to create PREPARED certficate") +) + +type RoundState interface { + // mutation functions + StartNewRound(nextRound *big.Int, validatorSet istanbul.ValidatorSet, nextProposer istanbul.Validator) error + StartNewSequence(nextSequence *big.Int, validatorSet istanbul.ValidatorSet, + nextProposer istanbul.Validator, parentCommits MessageSet) error + TransitionToPrepreparedV2(preprepareV2 *istanbul.PreprepareV2) error + TransitionToWaitingForNewRound(r *big.Int, nextProposer istanbul.Validator) error + TransitionToCommitted() error + TransitionToPrepared(quorumSize int) error + AddCommit(msg *istanbul.Message) error + AddPrepare(msg *istanbul.Message) error + AddParentCommit(msg *istanbul.Message) error + SetPendingRequest(pendingRequest *istanbul.Request) error + SetProposalVerificationStatus(proposalHash common.Hash, verificationStatus error) + SetStateProcessResult(proposalHash common.Hash, blockProcessResult *StateProcessResult) + + // view functions + DesiredRound() *big.Int + State() State + GetPrepareOrCommitSize() int + GetValidatorByAddress(address common.Address) istanbul.Validator + ValidatorSet() istanbul.ValidatorSet + Proposer() istanbul.Validator + IsProposer(address common.Address) bool + Subject() *istanbul.Subject + PreprepareV2() *istanbul.PreprepareV2 + Proposal() istanbul.Proposal + Round() *big.Int + Commits() MessageSet + Prepares() MessageSet + ParentCommits() MessageSet + PendingRequest() *istanbul.Request + Sequence() *big.Int + View() *istanbul.View + PreparedCertificate() istanbul.PreparedCertificate + GetProposalVerificationStatus(proposalHash common.Hash) (verificationStatus error, isCached bool) + GetStateProcessResult(proposalHash common.Hash) (result *StateProcessResult) + Summary() *RoundStateSummary +} + +// RoundState stores the consensus state +type roundStateImpl struct { + state State + round *big.Int + desiredRound *big.Int + sequence *big.Int + + // data for current round + preprepareV2 *istanbul.PreprepareV2 + prepares MessageSet + commits MessageSet + proposer istanbul.Validator + + // data saves across rounds, same sequence + validatorSet istanbul.ValidatorSet + parentCommits MessageSet + pendingRequest *istanbul.Request + preparedCertificate istanbul.PreparedCertificate + + // Verification status for proposals seen in this view + // Note that this field will not get RLP enoded and persisted, since it contains an error type, + // which doesn't have a native RLP encoding. Also, this is a cache, so it's not necessary for it + // to be persisted. + proposalVerificationStatus map[common.Hash]error + + // Cache for StateProcessResult in this sequence. + stateProcessResults map[common.Hash]*StateProcessResult + + mu *sync.RWMutex + logger log.Logger + + // Gauges for current round, desiredRound, and sequence + roundGauge metrics.Gauge + desiredRoundGauge metrics.Gauge + sequenceGauge metrics.Gauge +} + +type RoundStateSummary struct { + State string `json:"state"` + Sequence *big.Int `json:"sequence"` + Round *big.Int `json:"round"` + DesiredRound *big.Int `json:"desiredRound"` + PendingRequestHash *common.Hash `json:"pendingRequestHash"` + + ValidatorSet []common.Address `json:"validatorSet"` + Proposer common.Address `json:"proposer"` + + Prepares []common.Address `json:"prepares"` + Commits []common.Address `json:"commits"` + ParentCommits []common.Address `json:"parentCommits"` + + Preprepare *istanbul.PreprepareSummary `json:"preprepare"` + PreparedCertificate *istanbul.PreparedCertificateSummary `json:"preparedCertificate"` +} + +func newRoundState(view *istanbul.View, validatorSet istanbul.ValidatorSet, proposer istanbul.Validator) RoundState { + if proposer == nil { + log.Crit("Proposer cannot be nil") + } + return &roundStateImpl{ + state: StateAcceptRequest, + round: view.Round, + desiredRound: view.Round, + sequence: view.Sequence, + + // data for current round + // preprepare: nil, + prepares: newMessageSet(validatorSet), + commits: newMessageSet(validatorSet), + proposer: proposer, + + // data saves across rounds, same sequence + validatorSet: validatorSet, + parentCommits: newMessageSet(validatorSet), + pendingRequest: nil, + preparedCertificate: istanbul.EmptyPreparedCertificate(), + + mu: new(sync.RWMutex), + logger: log.New(), + + roundGauge: metrics.NewRegisteredGauge("consensus/istanbul/core/round", nil), + desiredRoundGauge: metrics.NewRegisteredGauge("consensus/istanbul/core/desiredround", nil), + sequenceGauge: metrics.NewRegisteredGauge("consensus/istanbul/core/sequence", nil), + } +} + +func (rs *roundStateImpl) Commits() MessageSet { return rs.commits } +func (rs *roundStateImpl) Prepares() MessageSet { return rs.prepares } +func (rs *roundStateImpl) ParentCommits() MessageSet { return rs.parentCommits } + +func (rs *roundStateImpl) State() State { + rs.mu.RLock() + defer rs.mu.RUnlock() + return rs.state +} + +func (rs *roundStateImpl) View() *istanbul.View { + rs.mu.RLock() + defer rs.mu.RUnlock() + + return &istanbul.View{ + Sequence: new(big.Int).Set(rs.sequence), + Round: new(big.Int).Set(rs.round), + } +} + +func (rs *roundStateImpl) GetPrepareOrCommitSize() int { + rs.mu.RLock() + defer rs.mu.RUnlock() + + result := rs.prepares.Size() + rs.commits.Size() + + // find duplicate one + for _, m := range rs.prepares.Values() { + if rs.commits.Get(m.Address) != nil { + result-- + } + } + return result +} + +func (rs *roundStateImpl) lockedPreprepareProposal() *istanbul.Proposal { + // Assume locked + if rs.preprepareV2 == nil { + return nil + } + return &rs.preprepareV2.Proposal +} + +func (rs *roundStateImpl) Subject() *istanbul.Subject { + rs.mu.RLock() + defer rs.mu.RUnlock() + + proposal := rs.lockedPreprepareProposal() + + if proposal == nil { + return nil + } + + return &istanbul.Subject{ + View: &istanbul.View{ + Round: new(big.Int).Set(rs.round), + Sequence: new(big.Int).Set(rs.sequence), + }, + Digest: (*proposal).Hash(), + } +} + +func (rs *roundStateImpl) IsProposer(address common.Address) bool { + rs.mu.RLock() + defer rs.mu.RUnlock() + + return rs.proposer.Address() == address +} + +func (rs *roundStateImpl) Proposer() istanbul.Validator { + rs.mu.RLock() + defer rs.mu.RUnlock() + + return rs.proposer +} + +func (rs *roundStateImpl) ValidatorSet() istanbul.ValidatorSet { + rs.mu.RLock() + defer rs.mu.RUnlock() + + return rs.validatorSet +} + +func (rs *roundStateImpl) GetValidatorByAddress(address common.Address) istanbul.Validator { + rs.mu.RLock() + defer rs.mu.RUnlock() + + _, validator := rs.validatorSet.GetByAddress(address) + return validator +} + +func (rs *roundStateImpl) PreprepareV2() *istanbul.PreprepareV2 { + rs.mu.RLock() + defer rs.mu.RUnlock() + + return rs.preprepareV2 +} + +func (rs *roundStateImpl) Proposal() istanbul.Proposal { + rs.mu.RLock() + defer rs.mu.RUnlock() + + proposal := rs.lockedPreprepareProposal() + if proposal == nil { + return nil + } + return *proposal +} + +func (rs *roundStateImpl) Round() *big.Int { + rs.mu.RLock() + defer rs.mu.RUnlock() + + return rs.round +} + +func (rs *roundStateImpl) changeRound(nextRound *big.Int, validatorSet istanbul.ValidatorSet, nextProposer istanbul.Validator) { + if nextProposer == nil { + log.Crit("Proposer cannot be nil") + } + + rs.state = StateAcceptRequest + rs.round = nextRound + rs.desiredRound = nextRound + + // Update gauges + rs.roundGauge.Update(nextRound.Int64()) + rs.desiredRoundGauge.Update(nextRound.Int64()) + + // TODO MC use old valset + rs.prepares = newMessageSet(validatorSet) + rs.commits = newMessageSet(validatorSet) + rs.proposer = nextProposer + + // ?? + rs.preprepareV2 = nil +} + +func (rs *roundStateImpl) StartNewRound(nextRound *big.Int, validatorSet istanbul.ValidatorSet, nextProposer istanbul.Validator) error { + rs.mu.Lock() + defer rs.mu.Unlock() + logger := rs.newLogger() + rs.changeRound(nextRound, validatorSet, nextProposer) + logger.Debug("Starting new round", "next_round", nextRound, "next_proposer", nextProposer.Address().Hex()) + return nil +} + +func (rs *roundStateImpl) StartNewSequence(nextSequence *big.Int, validatorSet istanbul.ValidatorSet, + nextProposer istanbul.Validator, parentCommits MessageSet) error { + rs.mu.Lock() + defer rs.mu.Unlock() + logger := rs.newLogger() + + rs.validatorSet = validatorSet + + rs.changeRound(big.NewInt(0), validatorSet, nextProposer) + + rs.sequence = nextSequence + rs.preparedCertificate = istanbul.EmptyPreparedCertificate() + rs.pendingRequest = nil + rs.parentCommits = parentCommits + rs.proposalVerificationStatus = nil + rs.stateProcessResults = nil + + // Update sequence gauge + rs.sequenceGauge.Update(nextSequence.Int64()) + + logger.Debug("Starting new sequence", "next_sequence", nextSequence, "next_proposer", nextProposer.Address().Hex()) + return nil +} + +func (rs *roundStateImpl) TransitionToCommitted() error { + rs.mu.Lock() + defer rs.mu.Unlock() + + rs.state = StateCommitted + return nil +} + +func (rs *roundStateImpl) TransitionToPrepreparedV2(preprepareV2 *istanbul.PreprepareV2) error { + rs.mu.Lock() + defer rs.mu.Unlock() + + rs.preprepareV2 = preprepareV2 + rs.state = StatePreprepared + return nil +} + +func (rs *roundStateImpl) TransitionToWaitingForNewRound(desiredRound *big.Int, nextProposer istanbul.Validator) error { + rs.mu.Lock() + defer rs.mu.Unlock() + + if rs.round.Cmp(desiredRound) > 0 { + return errInvalidState + } + if nextProposer == nil { + log.Crit("Proposer cannot be nil") + } + + rs.desiredRound = new(big.Int).Set(desiredRound) + rs.proposer = nextProposer + rs.state = StateWaitingForNewRound + + // Update gauge + rs.desiredRoundGauge.Update(desiredRound.Int64()) + + return nil +} + +// TransitionToPrepared will create a PreparedCertificate and change state to Prepared +func (rs *roundStateImpl) TransitionToPrepared(quorumSize int) error { + rs.mu.Lock() + defer rs.mu.Unlock() + + messages := make([]istanbul.Message, quorumSize) + i := 0 + for _, message := range rs.prepares.Values() { + if i == quorumSize { + break + } + messages[i] = *message + i++ + } + for _, message := range rs.commits.Values() { + if i == quorumSize { + break + } + if rs.prepares.Get(message.Address) == nil { + messages[i] = *message + i++ + } + } + if i != quorumSize { + return errFailedCreatePreparedCertificate + } + rs.preparedCertificate = istanbul.PreparedCertificate{ + Proposal: *rs.lockedPreprepareProposal(), + PrepareOrCommitMessages: messages, + } + + rs.state = StatePrepared + return nil +} + +func (rs *roundStateImpl) AddCommit(msg *istanbul.Message) error { + // IMPORTANT: due to how in the roundstate_save_decorator + // & roundstate_db we are breaking encapsulation of this type, + // this method CANNOT and SHOULD NOT modify ANYTHING OTHER than + // the 'commits' message set. + rs.mu.Lock() + defer rs.mu.Unlock() + return rs.commits.Add(msg) +} + +func (rs *roundStateImpl) AddPrepare(msg *istanbul.Message) error { + // IMPORTANT: due to how in the roundstate_save_decorator + // & roundstate_db we are breaking encapsulation of this type, + // this method CANNOT and SHOULD NOT modify ANYTHING OTHER than + // the 'prepares' message set. + rs.mu.Lock() + defer rs.mu.Unlock() + return rs.prepares.Add(msg) +} + +func (rs *roundStateImpl) AddParentCommit(msg *istanbul.Message) error { + // IMPORTANT: due to how in the roundstate_save_decorator + // & roundstate_db we are breaking encapsulation of this type, + // this method CANNOT and SHOULD NOT modify ANYTHING OTHER than + // the 'parentCommits' message set. + rs.mu.Lock() + defer rs.mu.Unlock() + return rs.parentCommits.Add(msg) +} + +func (rs *roundStateImpl) DesiredRound() *big.Int { + rs.mu.RLock() + defer rs.mu.RUnlock() + + return rs.desiredRound +} + +func (rs *roundStateImpl) SetPendingRequest(pendingRequest *istanbul.Request) error { + rs.mu.Lock() + defer rs.mu.Unlock() + + rs.pendingRequest = pendingRequest + return nil +} + +func (rs *roundStateImpl) PendingRequest() *istanbul.Request { + rs.mu.RLock() + defer rs.mu.RUnlock() + return rs.pendingRequest +} + +func (rs *roundStateImpl) Sequence() *big.Int { + rs.mu.RLock() + defer rs.mu.RUnlock() + + return rs.sequence +} + +func (rs *roundStateImpl) PreparedCertificate() istanbul.PreparedCertificate { + rs.mu.RLock() + defer rs.mu.RUnlock() + return rs.preparedCertificate +} + +func (rs *roundStateImpl) SetProposalVerificationStatus(proposalHash common.Hash, verificationStatus error) { + rs.mu.Lock() + defer rs.mu.Unlock() + + if rs.proposalVerificationStatus == nil { + rs.proposalVerificationStatus = make(map[common.Hash]error) + } + + rs.proposalVerificationStatus[proposalHash] = verificationStatus +} + +func (rs *roundStateImpl) GetProposalVerificationStatus(proposalHash common.Hash) (verificationStatus error, isCached bool) { + rs.mu.RLock() + defer rs.mu.RUnlock() + + verificationStatus, isCached = nil, false + + if rs.proposalVerificationStatus != nil { + verificationStatus, isCached = rs.proposalVerificationStatus[proposalHash] + } + + return +} + +// StateProcessResult represents processing results from StateProcessor. +type StateProcessResult struct { + State *state.StateDB + Receipts types.Receipts + Logs []*types.Log +} + +func (rs *roundStateImpl) SetStateProcessResult(proposalHash common.Hash, stateProcessResult *StateProcessResult) { + rs.mu.Lock() + defer rs.mu.Unlock() + + if rs.stateProcessResults == nil { + rs.stateProcessResults = make(map[common.Hash]*StateProcessResult) + } + + rs.stateProcessResults[proposalHash] = stateProcessResult +} + +func (rs *roundStateImpl) GetStateProcessResult(proposalHash common.Hash) (stateProcessResult *StateProcessResult) { + rs.mu.RLock() + defer rs.mu.RUnlock() + + if rs.stateProcessResults != nil { + stateProcessResult = rs.stateProcessResults[proposalHash] + } + + return +} + +func (rs *roundStateImpl) Summary() *RoundStateSummary { + rs.mu.RLock() + defer rs.mu.RUnlock() + + summary := &RoundStateSummary{ + State: rs.state.String(), + Sequence: rs.sequence, + Round: rs.round, + DesiredRound: rs.desiredRound, + + Proposer: rs.proposer.Address(), + ValidatorSet: istanbul.MapValidatorsToAddresses(rs.validatorSet.List()), + + Prepares: rs.prepares.Addresses(), + Commits: rs.commits.Addresses(), + ParentCommits: rs.parentCommits.Addresses(), + } + + if rs.pendingRequest != nil { + hash := rs.pendingRequest.Proposal.Hash() + summary.PendingRequestHash = &hash + } + + if rs.preprepareV2 != nil { + summary.Preprepare = rs.preprepareV2.Summary() + } + + if !rs.preparedCertificate.IsEmpty() { + summary.PreparedCertificate = rs.preparedCertificate.Summary() + } + + return summary +} + +func (rs *roundStateImpl) newLogger(ctx ...interface{}) log.Logger { + logger := rs.logger.New(ctx...) + return logger.New("cur_seq", rs.sequence, "cur_round", rs.round, "state", rs.state) +} + +type roundStateRLP struct { + State State + Round *big.Int + DesiredRound *big.Int + Sequence *big.Int + PreparedCertificate *istanbul.PreparedCertificate + + // custom serialized fields + SerializedValSet []byte + SerializedProposer []byte + SerializedParentCommits []byte + SerializedPrepares []byte + SerializedCommits []byte + SerializedPreprepare []byte + SerializedPendingRequest []byte +} + +// EncodeRLP should write the RLP encoding of its receiver to w. +// If the implementation is a pointer method, it may also be +// called for nil pointers. +// +// Implementations should generate valid RLP. The data written is +// not verified at the moment, but a future version might. It is +// recommended to write only a single value but writing multiple +// values or no value at all is also permitted. +func (rs *roundStateImpl) EncodeRLP(w io.Writer) error { + rs.mu.RLock() + defer rs.mu.RUnlock() + + serializedValSet, err := rs.validatorSet.Serialize() + if err != nil { + return err + } + if rs.proposer == nil { + return errors.New("Invalid RoundState: no proposer") + } + serializedProposer, err := rs.proposer.Serialize() + if err != nil { + return err + } + + serializedParentCommits, err := rs.parentCommits.Serialize() + if err != nil { + return err + } + serializedPrepares, err := rs.prepares.Serialize() + if err != nil { + return err + } + serializedCommits, err := rs.commits.Serialize() + if err != nil { + return err + } + + // handle nullable field. Serialized them to rlp.EmptyList or the rlp version of them + var serializedPendingRequest []byte + if rs.pendingRequest == nil { + serializedPendingRequest = rlp.EmptyList + } else { + serializedPendingRequest, err = rlp.EncodeToBytes(rs.pendingRequest) + if err != nil { + return err + } + } + + var serializedPreprepare []byte + if rs.preprepareV2 == nil { + serializedPreprepare = rlp.EmptyList + } else { + serializedPreprepare, err = rlp.EncodeToBytes(rs.preprepareV2) + if err != nil { + return err + } + } + + entry := roundStateRLP{ + State: rs.state, + Round: rs.round, + DesiredRound: rs.desiredRound, + Sequence: rs.sequence, + PreparedCertificate: &rs.preparedCertificate, + + SerializedValSet: serializedValSet, + SerializedProposer: serializedProposer, + SerializedParentCommits: serializedParentCommits, + SerializedPrepares: serializedPrepares, + SerializedCommits: serializedCommits, + SerializedPendingRequest: serializedPendingRequest, + SerializedPreprepare: serializedPreprepare, + } + return rlp.Encode(w, entry) +} + +// The DecodeRLP method should read one value from the given +// Stream. It is not forbidden to read less or more, but it might +// be confusing. +func (rs *roundStateImpl) DecodeRLP(stream *rlp.Stream) error { + var data roundStateRLP + err := stream.Decode(&data) + if err != nil { + return err + } + + rs.logger = log.New() + rs.mu = new(sync.RWMutex) + rs.roundGauge = metrics.NewRegisteredGauge("consensus/istanbul/core/round", nil) + rs.desiredRoundGauge = metrics.NewRegisteredGauge("consensus/istanbul/core/desiredround", nil) + rs.sequenceGauge = metrics.NewRegisteredGauge("consensus/istanbul/core/sequence", nil) + rs.state = data.State + rs.round = data.Round + rs.desiredRound = data.DesiredRound + rs.sequence = data.Sequence + rs.preparedCertificate = *data.PreparedCertificate + + rs.prepares, err = deserializeMessageSet(data.SerializedPrepares) + if err != nil { + return err + } + rs.parentCommits, err = deserializeMessageSet(data.SerializedParentCommits) + if err != nil { + return err + } + rs.commits, err = deserializeMessageSet(data.SerializedCommits) + if err != nil { + return err + } + rs.validatorSet, err = validator.DeserializeValidatorSet(data.SerializedValSet) + if err != nil { + return err + } + rs.proposer, err = validator.DeserializeValidator(data.SerializedProposer) + if err != nil { + return err + } + + if !bytes.Equal(data.SerializedPendingRequest, rlp.EmptyList) { + var value istanbul.Request + err := rlp.DecodeBytes(data.SerializedPendingRequest, &value) + if err != nil { + return err + } + rs.pendingRequest = &value + + } + + if !bytes.Equal(data.SerializedPreprepare, rlp.EmptyList) { + var value istanbul.PreprepareV2 + err := rlp.DecodeBytes(data.SerializedPreprepare, &value) + if err != nil { + return err + } + rs.preprepareV2 = &value + } + + return nil +}
diff --git go-ethereum/consensus/istanbul/backend/handler.go celo/consensus/istanbul/backend/handler.go new file mode 100644 index 0000000000000000000000000000000000000000..f38b3996b3e1267e9f68118f2435be65da2fe491 --- /dev/null +++ celo/consensus/istanbul/backend/handler.go @@ -0,0 +1,553 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "errors" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + // errDecodeFailed is returned when decode message fails + errDecodeFailed = errors.New("fail to decode istanbul message") +) + +const ( + handshakeTimeout = 5 * time.Second +) + +// HandleMsg implements consensus.Handler.HandleMsg +func (sb *Backend) HandleMsg(addr common.Address, msg p2p.Msg, peer consensus.Peer) (bool, error) { + logger := sb.logger.New("func", "HandleMsg", "msgCode", msg.Code) + + if !istanbul.IsIstanbulMsg(msg) { + return false, nil + } + + var data []byte + if err := msg.Decode(&data); err != nil { + logger.Error("Failed to decode message payload", "err", err, "from", addr) + return true, errDecodeFailed + } + + if sb.IsProxy() { + switch msg.Code { + // TODO(Joshua): Decide to pull out specific proxy handlers + case istanbul.ValEnodesShareMsg: + fallthrough + case istanbul.FwdMsg: + fallthrough + case istanbul.ConsensusMsg: + fallthrough + case istanbul.EnodeCertificateMsg: + // This will handle the following messages: + // 1) ValEnodesShareMsg + // 2) FwdMsg + // 3) ConsensusMsg + // 4) EnodeCertificateMsg + // No error on skipped messages + return sb.proxyEngine.HandleMsg(peer, msg.Code, data) + case istanbul.DelegateSignMsg: + go sb.delegateSignFeed.Send(istanbul.MessageWithPeerIDEvent{ + PeerID: peer.Node().ID(), + Payload: data, + }) + return true, nil + case istanbul.QueryEnodeMsg: + go sb.announceManager.HandleQueryEnodeMsg(addr, peer, data) + return true, nil + case istanbul.VersionCertificatesMsg: + go sb.announceManager.HandleVersionCertificatesMsg(addr, peer, data) + return true, nil + case istanbul.ValidatorHandshakeMsg: + logger.Warn("Received unexpected Istanbul validator handshake message") + return true, nil + default: + logger.Error("Unhandled istanbul message as proxy", "address", addr, "peer's enodeURL", peer.Node().String(), "ethMsgCode", msg.Code) + return false, nil + } + } else if sb.IsValidating() { + // Handle messages as primary validator + switch msg.Code { + case istanbul.ConsensusMsg: + go sb.istanbulEventMux.Post(istanbul.MessageEvent{ + Payload: data, + }) + return true, nil + case istanbul.DelegateSignMsg: + if sb.shouldHandleDelegateSign(peer) { + go sb.delegateSignFeed.Send(istanbul.MessageWithPeerIDEvent{ + PeerID: peer.Node().ID(), + Payload: data, + }) + return true, nil + } + logger.Error("Delegate Sign message sent from a node that is not a valid proxy", "peer", peer) + // Do not return an error, otherwise bad ethstat setup might cause disconnecting from proxy + return true, nil + case istanbul.EnodeCertificateMsg: + go sb.announceManager.HandleEnodeCertificateMsg(peer, data) + return true, nil + case istanbul.QueryEnodeMsg: + go sb.announceManager.HandleQueryEnodeMsg(addr, peer, data) + return true, nil + case istanbul.VersionCertificatesMsg: + go sb.announceManager.HandleVersionCertificatesMsg(addr, peer, data) + return true, nil + case istanbul.ValidatorHandshakeMsg: + logger.Warn("Received unexpected Istanbul validator handshake message") + return true, nil + default: + logger.Error("Unhandled istanbul message as primary", "address", addr, "peer's enodeURL", peer.Node().String(), "ethMsgCode", msg.Code) + return false, nil + } + } else if !sb.IsValidating() { + // Handle messages as replica validator + switch msg.Code { + case istanbul.ConsensusMsg: + // Ignore consensus messages + return true, nil + case istanbul.DelegateSignMsg: + if sb.shouldHandleDelegateSign(peer) { + go sb.delegateSignFeed.Send(istanbul.MessageWithPeerIDEvent{ + PeerID: peer.Node().ID(), + Payload: data, + }) + return true, nil + } + logger.Error("Delegate Sign message sent from a node that is not a valid proxy", "peer", peer) + // Do not return an error, otherwise bad ethstat setup might cause disconnecting from proxy + return true, nil + case istanbul.EnodeCertificateMsg: + go sb.announceManager.HandleEnodeCertificateMsg(peer, data) + return true, nil + case istanbul.QueryEnodeMsg: + go sb.announceManager.HandleQueryEnodeMsg(addr, peer, data) + return true, nil + case istanbul.VersionCertificatesMsg: + go sb.announceManager.HandleVersionCertificatesMsg(addr, peer, data) + return true, nil + case istanbul.ValidatorHandshakeMsg: + logger.Warn("Received unexpected Istanbul validator handshake message") + return true, nil + default: + logger.Error("Unhandled istanbul message as replica", "address", addr, "peer's enodeURL", peer.Node().String(), "ethMsgCode", msg.Code) + return false, nil + } + } + + // If we got here, then that means that there is an istanbul message type that either there + // is an istanbul message that is not handled, or it's a forward message not handled (e.g. a + // node other than a proxy received the message). + logger.Error("Unhandled istanbul message", "address", addr, "peer's enodeURL", peer.Node().String(), "ethMsgCode", msg.Code) + return false, nil +} + +func (sb *Backend) shouldHandleDelegateSign(peer consensus.Peer) bool { + if sb.IsProxy() { + return true + } else if sb.IsProxiedValidator() { + isStatsProxy, err := sb.proxiedValidatorEngine.IsProxyPeer(peer.Node().ID()) + if err != nil { + sb.logger.Warn("Error when checking if peer handling delegateSign is a proxy", "err", err) + } + return isStatsProxy + } + + return false +} + +// SubscribeNewDelegateSignEvent subscribes a channel to any new delegate sign messages +func (sb *Backend) SubscribeNewDelegateSignEvent(ch chan<- istanbul.MessageWithPeerIDEvent) event.Subscription { + return sb.delegateSignScope.Track(sb.delegateSignFeed.Subscribe(ch)) +} + +// SetBroadcaster implements consensus.Handler.SetBroadcaster +func (sb *Backend) SetBroadcaster(broadcaster consensus.Broadcaster) { + sb.broadcaster = broadcaster +} + +// SetP2PServer implements consensus.Handler.SetP2PServer +func (sb *Backend) SetP2PServer(p2pserver consensus.P2PServer) { + sb.p2pserver = p2pserver +} + +// NewWork is called by miner/worker.go whenever it's mainLoop gets a newWork event. +func (sb *Backend) NewWork() error { + sb.logger.Debug("NewWork called, acquiring core lock", "func", "NewWork") + + sb.coreMu.RLock() + defer sb.coreMu.RUnlock() + if !sb.isCoreStarted() { + return istanbul.ErrStoppedEngine + } + + sb.logger.Debug("Posting FinalCommittedEvent", "func", "NewWork") + + go sb.istanbulEventMux.Post(istanbul.FinalCommittedEvent{}) + return nil +} + +// UpdateMetricsForParentOfBlock maintains metrics around the *parent* of the supplied block. +// To figure out if this validator signed the parent block: +// * First check the grandparent's validator set. If not elected, it didn't. +// * Then, check the parent seal on the supplied (child) block. +// We cannot determine any specific info from the validators in the seal of +// the parent block, because different nodes circulate different versions. +// The bitmap of signed validators only becomes canonical when the child block is proposed. +func (sb *Backend) UpdateMetricsForParentOfBlock(child *types.Block) { + sb.coreMu.RLock() + defer sb.coreMu.RUnlock() + + // Check the parent is not the genesis block. + number := child.Number().Uint64() + if number <= 1 { + return + } + + childHeader := child.Header() + parentHeader := sb.chain.GetHeader(childHeader.ParentHash, number-1) + + // Check validator in grandparent valset. + gpValSet := sb.getValidators(number-2, parentHeader.ParentHash) + gpValSetIndex, _ := gpValSet.GetByAddress(sb.Address()) + + // Now check if this validator signer is in the "parent seal" on the child block. + // The parent seal is used for downtime calculations. + childExtra, err := child.Header().IstanbulExtra() + if err != nil { + return + } + + // valSetSize is the total number of possible signers. + valSetSize := gpValSet.Size() + sb.blocksValSetSizeGauge.Update(int64(valSetSize)) + + // countInParentSeal is the number of signers represented in the parent seal of the child block. + countInParentSeal := 0 + for i := 0; i < gpValSet.Size(); i++ { + countInParentSeal += int(childExtra.ParentAggregatedSeal.Bitmap.Bit(i)) + } + sb.blocksTotalSigsGauge.Update(int64(countInParentSeal)) + + // Cumulative count of rounds missed (i.e sequences not agreed on round=0) + missedRounds := childExtra.ParentAggregatedSeal.Round.Int64() + if missedRounds > 0 { + sb.blocksTotalMissedRoundsMeter.Mark(missedRounds) + } + + // Is this validator signer elected? + elected := gpValSetIndex >= 0 + if !elected { + sb.blocksElectedButNotSignedGauge.Update(0) + return + } + sb.blocksElectedMeter.Mark(1) + + // The following metrics are only tracked if the validator is elected. + + // Did this validator propose the block that was finalized? + if parentHeader.Coinbase == sb.Address() { + sb.blocksElectedAndProposedMeter.Mark(1) + } else { + // could have proposed a block that was not finalized? + // This is a could, not did because the consensus algo may have forced the proposer + // to re-propose an existing block, thus not placing it's own signature on it. + gpAuthor := sb.AuthorForBlock(number - 2) + for i := int64(0); i < missedRounds; i++ { + proposer := validator.GetProposerSelector(sb.config.ProposerPolicy)(gpValSet, gpAuthor, uint64(i)) + if sb.Address() == proposer.Address() { + sb.blocksMissedRoundsAsProposerMeter.Mark(1) + break + } + } + } + + // signed, or missed? + inParentSeal := childExtra.ParentAggregatedSeal.Bitmap.Bit(gpValSetIndex) != 0 + if inParentSeal { + sb.blocksElectedAndSignedMeter.Mark(1) + sb.blocksElectedButNotSignedGauge.Update(0) + } else { + sb.blocksElectedButNotSignedMeter.Mark(1) + sb.blocksElectedButNotSignedGauge.Inc(1) + if sb.blocksElectedButNotSignedGauge.Value() != 0 { + sb.logger.Warn("Elected but didn't sign block", "number", number-1, "address", sb.ValidatorAddress(), "missed in a row", sb.blocksElectedButNotSignedGauge.Value()) + } else { + sb.logger.Warn("Elected but didn't sign block", "number", number-1, "address", sb.ValidatorAddress()) + } + } + + // Clear downtime counter on end of epoch. + if istanbul.IsLastBlockOfEpoch(number-1, sb.config.Epoch) { + sb.blocksElectedButNotSignedGauge.Update(0) + } +} + +// Actions triggered by a new block being added to the chain. +func (sb *Backend) newChainHead(newBlock *types.Block) { + + sb.logger.Trace("Start newChainHead", "number", newBlock.Number().Uint64()) + + // Update metrics for whether we were elected and signed the parent of this block. + sb.UpdateMetricsForParentOfBlock(newBlock) + + // If this is the last block of the epoch: + // * Print an easy to find log message giving our address and whether we're elected in next epoch. + // * If this is a node maintaining validator connections (e.g. a proxy or a standalone validator), refresh the validator enode table. + // * If this is a proxied validator, notify the proxied validator engine of a new epoch. + if istanbul.IsLastBlockOfEpoch(newBlock.Number().Uint64(), sb.config.Epoch) { + + sb.coreMu.RLock() + defer sb.coreMu.RUnlock() + + valSet := sb.getValidators(newBlock.Number().Uint64(), newBlock.Hash()) + valSetIndex, _ := valSet.GetByAddress(sb.ValidatorAddress()) + + sb.logger.Info("Validator Election Results", "address", sb.ValidatorAddress(), "elected", valSetIndex >= 0, "number", newBlock.Number().Uint64()) + + // We lock here (inside IsAnnounceRunning) to protect access to announceRunning because + // announceRunning is also accessed in StartAnnouncing and + // StopAnnouncing. + if sb.announceManager.IsAnnounceRunning() { + sb.logger.Trace("At end of epoch and going to refresh validator peers", "new_block_number", newBlock.Number().Uint64()) + if err := sb.RefreshValPeers(); err != nil { + sb.logger.Warn("Error refreshing validator peers", "err", err) + } + } + + if sb.IsProxiedValidator() { + if err := sb.proxiedValidatorEngine.NewEpoch(); err != nil { + sb.logger.Warn("Error while notifying proxied validator engine of new epoch", "err", err) + } + } + } + + sb.blocksFinalizedTransactionsGauge.Update(int64(len(newBlock.Transactions()))) + sb.blocksFinalizedGasUsedGauge.Update(int64(newBlock.GasUsed())) + sb.logger.Trace("End newChainHead", "number", newBlock.Number().Uint64()) +} + +func (sb *Backend) RegisterPeer(peer consensus.Peer, isProxiedPeer bool) error { + // TODO: For added security, we may want verify that all newly connected proxied peer has the + // correct validator key + logger := sb.logger.New("func", "RegisterPeer") + + logger.Trace("RegisterPeer called", "peer", peer, "isProxiedPeer", isProxiedPeer) + + // Check to see if this connecting peer is a proxied validator + if sb.IsProxy() && isProxiedPeer { + sb.proxyEngine.RegisterProxiedValidatorPeer(peer) + } else if sb.IsProxiedValidator() { + if err := sb.proxiedValidatorEngine.RegisterProxyPeer(peer); err != nil { + return err + } + } + + if err := sb.announceManager.SendVersionCertificateTable(peer); err != nil { + logger.Debug("Error sending all version certificates", "err", err) + } + + return nil +} + +func (sb *Backend) UnregisterPeer(peer consensus.Peer, isProxiedPeer bool) { + if sb.IsProxy() && isProxiedPeer { + sb.proxyEngine.UnregisterProxiedValidatorPeer(peer) + } else if sb.IsProxiedValidator() { + sb.proxiedValidatorEngine.UnregisterProxyPeer(peer) + } +} + +// Handshake allows the initiating peer to identify itself as a validator +func (sb *Backend) Handshake(peer consensus.Peer) (bool, error) { + // Only written to if there was a non-nil error when sending or receiving + errCh := make(chan error) + isValidatorCh := make(chan bool) + + sendHandshake := func() { + var msg *istanbul.Message + var err error + peerIsValidator := peer.PurposeIsSet(p2p.ValidatorPurpose) + if peerIsValidator { + enodeCertMsg := sb.RetrieveEnodeCertificateMsgMap()[sb.SelfNode().ID()] + if enodeCertMsg != nil { + msg = enodeCertMsg.Msg + } + } + // Even if we decide not to identify ourselves, + // send an empty message to complete the handshake + if msg == nil { + msg = &istanbul.Message{} + } + msgBytes, err := msg.Payload() + if err != nil { + errCh <- err + return + } + // No need to use sb.AsyncSendCeloMsg, since this is already + // being called within a goroutine. + err = peer.Send(istanbul.ValidatorHandshakeMsg, msgBytes) + if err != nil { + errCh <- err + return + } + isValidatorCh <- peerIsValidator + } + readHandshake := func() { + isValidator, err := sb.readValidatorHandshakeMessage(peer) + if err != nil { + errCh <- err + return + } + isValidatorCh <- isValidator + } + + // Only the initating peer sends the message + if peer.Inbound() { + go readHandshake() + } else { + go sendHandshake() + } + + timeout := time.NewTimer(handshakeTimeout) + defer timeout.Stop() + select { + case err := <-errCh: + return false, err + case <-timeout.C: + return false, p2p.DiscReadTimeout + case isValidator := <-isValidatorCh: + return isValidator, nil + } +} + +// readValidatorHandshakeMessage reads a validator handshake message. +// Returns if the peer is a validator or if an error occurred. +func (sb *Backend) readValidatorHandshakeMessage(peer consensus.Peer) (bool, error) { + logger := sb.logger.New("func", "readValidatorHandshakeMessage") + peerMsg, err := peer.ReadMsg() + if err != nil { + return false, err + } + if peerMsg.Code != istanbul.ValidatorHandshakeMsg { + logger.Warn("Read incorrect message code", "code", peerMsg.Code) + return false, errors.New("Incorrect message code") + } + + var payload []byte + if err := peerMsg.Decode(&payload); err != nil { + return false, err + } + + var msg istanbul.Message + err = msg.FromPayload(payload, sb.verifyValidatorHandshakeMessage) + if err != nil { + return false, err + } + // If the Signature is empty, the peer has decided not to reveal its info + if len(msg.Signature) == 0 { + return false, nil + } + + var enodeCertificate istanbul.EnodeCertificate + err = rlp.DecodeBytes(msg.Msg, &enodeCertificate) + if err != nil { + return false, err + } + + node, err := enode.ParseV4(enodeCertificate.EnodeURL) + if err != nil { + return false, err + } + + // Ensure the node in the enodeCertificate matches the peer node + if node.ID() != peer.Node().ID() { + logger.Warn("Peer provided incorrect node ID in enodeCertificate", "enodeCertificate enode url", enodeCertificate.EnodeURL, "peer enode url", peer.Node().URLv4()) + return false, errors.New("Incorrect node in enodeCertificate") + } + + // Check if the peer is within the validator conn set. + validatorConnSet := sb.retrieveCachedValidatorConnSet() + // If no set has ever been cached, update it and try again. This is an expensive + // operation and risks the handshake timing out, but will happen at most once + // and is unlikely to occur. + if validatorConnSet == nil { + if err := sb.updateCachedValidatorConnSet(); err != nil { + logger.Trace("Error updating cached validator conn set") + return false, err + } + validatorConnSet = sb.retrieveCachedValidatorConnSet() + } + if !validatorConnSet[sb.ValidatorAddress()] { + logger.Trace("This validator is not in the validator conn set") + return false, nil + } + if !validatorConnSet[msg.Address] { + logger.Debug("Received a validator handshake message from peer not in the validator conn set", "msg.Address", msg.Address) + return false, nil + } + + // If the enodeCertificate message is too old, we don't count the msg as valid + // An error is given if the entry doesn't exist, so we ignore the error. + knownVersion, err := sb.valEnodeTable.GetVersionFromAddress(msg.Address) + if err == nil && enodeCertificate.Version < knownVersion { + logger.Debug("Received a validator handshake message with an old version", "received version", enodeCertificate.Version, "known version", knownVersion) + return false, nil + } + + // Forward this message to the proxied validator if this is a proxy. + // We leave the validator to determine if the proxy should add this node + // to its val enode table, which occurs if the proxied validator sends back + // the enode certificate to this proxy. + if sb.IsProxy() { + if err := sb.proxyEngine.SendMsgToProxiedValidators(istanbul.EnodeCertificateMsg, &msg); err != nil { + logger.Warn("Error in sending enode certificate to proxied validator", "err", err) + } + return false, nil + } + + // By this point, this node and the peer are both validators and we update + // our val enode table accordingly. Upsert will only use this entry if the version is new + err = sb.valEnodeTable.UpsertVersionAndEnode([]*istanbul.AddressEntry{{Address: msg.Address, Node: node, Version: enodeCertificate.Version}}) + if err != nil { + return false, err + } + return true, nil +} + +// verifyValidatorHandshakeMessage allows messages that are not signed to still be +// decoded in case the peer has decided not to identify itself with the validator handshake +// message +func (sb *Backend) verifyValidatorHandshakeMessage(data []byte, sig []byte) (common.Address, error) { + // If the message was not signed, allow it to still be decoded. + // A later check will verify if the signature was empty or not. + if len(sig) == 0 { + return common.ZeroAddress, nil + } + return istanbul.GetSignatureAddress(data, sig) +}
diff --git go-ethereum/consensus/istanbul/core/handler_test.go celo/consensus/istanbul/core/handler_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8195d657ff0a17f1cc132d9bd23f215132335b02 --- /dev/null +++ celo/consensus/istanbul/core/handler_test.go @@ -0,0 +1,306 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// notice: the normal case have been tested in integration tests. +func TestMalformedMessageDecoding(t *testing.T) { + N := uint64(4) + F := uint64(1) + sys := NewTestSystemWithBackend(N, F) + + closer := sys.Run(true) + defer closer() + + v0 := sys.backends[0] + r0 := v0.engine.(*core) + + m := istanbul.NewPrepareMessage(&istanbul.Subject{ + View: &istanbul.View{ + Sequence: big.NewInt(0), + Round: big.NewInt(0), + }, + Digest: common.BytesToHash([]byte("1234567890")), + }, v0.Address()) + + // Prepare message but preprepare message code + m.Code = istanbul.MsgPreprepareV2 + + payload, err := m.Payload() + require.NoError(t, err) + + msg := &istanbul.Message{} + err = msg.FromPayload(payload, r0.validateFn) + assert.Error(t, err) + + m = istanbul.NewPreprepareV2Message(&istanbul.PreprepareV2{ + View: &istanbul.View{ + Sequence: big.NewInt(0), + Round: big.NewInt(0), + }, + Proposal: makeBlock(1), + }, v0.Address()) + + // Preprepare message but prepare message code + m.Code = istanbul.MsgPrepare + + payload, err = m.Payload() + require.NoError(t, err) + + msg = &istanbul.Message{} + err = msg.FromPayload(payload, r0.validateFn) + assert.Error(t, err) + + m = istanbul.NewPreprepareV2Message(&istanbul.PreprepareV2{ + View: &istanbul.View{ + Sequence: big.NewInt(0), + Round: big.NewInt(0), + }, + Proposal: makeBlock(2), + }, v0.Address()) + + // Preprepare message but commit message code + m.Code = istanbul.MsgCommit + + payload, err = m.Payload() + require.NoError(t, err) + + msg = &istanbul.Message{} + err = msg.FromPayload(payload, r0.validateFn) + assert.Error(t, err) + + m = istanbul.NewPreprepareV2Message(&istanbul.PreprepareV2{ + View: &istanbul.View{ + Sequence: big.NewInt(0), + Round: big.NewInt(0), + }, + Proposal: makeBlock(3), + }, v0.Address()) + + // invalid message code. message code is not exists in list + m.Code = uint64(99) + + payload, err = m.Payload() + require.NoError(t, err) + + msg = &istanbul.Message{} + err = msg.FromPayload(payload, r0.validateFn) + assert.Error(t, err) + + // check fails with garbage message + err = r0.handleMsg([]byte{1}) + assert.Error(t, err) +} + +func BenchmarkHandleMsg(b *testing.B) { + N := uint64(2) + F := uint64(1) // F does not affect tests + + sys := NewMutedTestSystemWithBackend(N, F) + // sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + backend.peers, + ) + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePreprepared + } + } + + sys.Run(false) + + v0 := sys.backends[0] + v1 := sys.backends[1] + c := v0.engine.(*core) + sub := v0.engine.(*core).current.Subject() + + payload, _ := Encode(sub) + msg := istanbul.Message{ + Code: 1000, + Msg: payload, + } + + msg, _ = v1.finalizeAndReturnMessage(&msg) + payload, _ = c.finalizeMessage(&msg) + + // benchmarked portion + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := c.handleMsg(payload) + if err != errInvalidMessage { + b.Errorf("error mismatch: have %v, want errInvalidMessage", err) + } + } +} + +// newInitializedTestSystem creates a test system +// It optionally creates a round state db in a temporary directory +func newInitializedTestSystem(b *testing.B, useRoundStateDB bool) *testSystem { + N := uint64(2) + F := uint64(1) // F does not affect tests + + sys := NewMutedTestSystemWithBackend(N, F) + // sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + + if useRoundStateDB { + rsdb, err := newRoundStateDB(b.TempDir(), nil) + if err != nil { + b.Errorf("Failed to create rsdb: %v", err) + } + + c.current = withSavingDecorator(rsdb, newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + backend.peers, + )) + + if i == 0 { + // replica 0 is the proposer + c.current.(*rsSaveDecorator).rs.(*roundStateImpl).state = StatePreprepared + } + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + backend.peers, + ) + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePreprepared + } + } + } + + sys.Run(false) + return sys +} + +func BenchmarkE2EHandleCommit(b *testing.B) { + sys := newInitializedTestSystem(b, false) + bemchmarkE2EHandleCommit(b, sys) +} + +func BenchmarkE2EHandleCommitWithSave(b *testing.B) { + sys := newInitializedTestSystem(b, true) + bemchmarkE2EHandleCommit(b, sys) + +} + +func bemchmarkE2EHandleCommit(b *testing.B, sys *testSystem) { + + v0 := sys.backends[0] + v1 := sys.backends[1] + c := v0.engine.(*core) + subject := v0.engine.(*core).current.Subject() + + committedSeal, err := v0.engine.(*core).generateCommittedSeal(subject) + if err != nil { + b.Errorf("Got error: %v", err) + } + committedSubject := &istanbul.CommittedSubject{ + Subject: subject, + CommittedSeal: committedSeal[:], + } + payload, err := Encode(committedSubject) + if err != nil { + b.Errorf("Got error: %v", err) + } + + msg := istanbul.Message{ + Code: istanbul.MsgCommit, + Msg: payload, + } + + msg, err = v1.finalizeAndReturnMessage(&msg) + if err != nil { + b.Errorf("Got error: %v", err) + } + payload, _ = c.finalizeMessage(&msg) + if err != nil { + b.Errorf("Got error: %v", err) + } + + // benchmarked portion + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := c.handleMsg(payload) + if err != nil { + b.Errorf("error mismatch: have %v, want nil", err) + } + } +} + +func BenchmarkE2EHandlePrepareWithSave(b *testing.B) { + sys := newInitializedTestSystem(b, true) + benchmarkE2EHandlePrepare(b, sys) +} + +func BenchmarkE2EHandlePrepare(b *testing.B) { + sys := newInitializedTestSystem(b, false) + benchmarkE2EHandlePrepare(b, sys) + +} + +func benchmarkE2EHandlePrepare(b *testing.B, sys *testSystem) { + v0 := sys.backends[0] + v1 := sys.backends[1] + c := v0.engine.(*core) + sub := v0.engine.(*core).current.Subject() + + payload, _ := Encode(sub) + msg := istanbul.Message{ + Code: istanbul.MsgPrepare, + Msg: payload, + } + + msg, _ = v1.finalizeAndReturnMessage(&msg) + payload, _ = c.finalizeMessage(&msg) + + // benchmarked portion + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := c.handleMsg(payload) + if err != nil { + b.Errorf("error mismatch: have %v, want nil", err) + } + } +}
diff --git go-ethereum/consensus/istanbul/announce/address_time.go celo/consensus/istanbul/announce/address_time.go new file mode 100644 index 0000000000000000000000000000000000000000..399459214d70f587b004e12839669dc17fa1ffc2 --- /dev/null +++ celo/consensus/istanbul/announce/address_time.go @@ -0,0 +1,42 @@ +package announce + +import ( + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" +) + +type AddressTime struct { + at map[common.Address]time.Time + mx sync.RWMutex +} + +func NewAddressTime() *AddressTime { + return &AddressTime{ + at: make(map[common.Address]time.Time), + } +} + +func (a *AddressTime) RemoveIf(predicate func(common.Address, time.Time) bool) { + a.mx.Lock() + defer a.mx.Unlock() + for addr, t := range a.at { + if predicate(addr, t) { + delete(a.at, addr) + } + } +} + +func (a *AddressTime) Set(addr common.Address, t time.Time) { + a.mx.Lock() + defer a.mx.Unlock() + a.at[addr] = t +} + +func (a *AddressTime) Get(addr common.Address) (time.Time, bool) { + a.mx.RLock() + defer a.mx.RUnlock() + t, ok := a.at[addr] + return t, ok +}
diff --git go-ethereum/consensus/istanbul/announce/version_certificate_db.go celo/consensus/istanbul/announce/version_certificate_db.go new file mode 100644 index 0000000000000000000000000000000000000000..3f77d83c3938bee0d236fe663f371a7c32511460 --- /dev/null +++ celo/consensus/istanbul/announce/version_certificate_db.go @@ -0,0 +1,286 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package announce + +import ( + "fmt" + "strings" + + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/opt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/db" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +const ( + versionCertificateDBVersion = 0 +) + +// VersionCertificateDB stores +type VersionCertificateDB struct { + gdb *db.GenericDB + logger log.Logger +} + +func versionCertificateEntryFromGenericEntry(entry db.GenericEntry) (*istanbul.VersionCertificate, error) { + signedAnnVersionEntry, ok := entry.(*istanbul.VersionCertificate) + if !ok { + return nil, errIncorrectEntryType + } + return signedAnnVersionEntry, nil +} + +// OpenVersionCertificateDB opens a signed announce version database for storing +// VersionCertificates. If no path is given an in-memory, temporary database is constructed. +func OpenVersionCertificateDB(path string) (*VersionCertificateDB, error) { + logger := log.New("db", "VersionCertificateDB") + + gdb, err := db.New(int64(versionCertificateDBVersion), path, logger, &opt.WriteOptions{NoWriteMerge: true}) + if err != nil { + logger.Error("Error creating db", "err", err) + return nil, err + } + + return &VersionCertificateDB{ + gdb: gdb, + logger: logger, + }, nil +} + +// Close flushes and closes the database files. +func (svdb *VersionCertificateDB) Close() error { + return svdb.gdb.Close() +} + +// String gives a string representation of the entire db +func (svdb *VersionCertificateDB) String() string { + var b strings.Builder + b.WriteString("VersionCertificateDB:") + + err := svdb.iterate(func(address common.Address, entry *istanbul.VersionCertificate) error { + fmt.Fprintf(&b, " [%s => %s]", address.String(), entry.String()) + return nil + }) + + if err != nil { + svdb.logger.Error("ValidatorEnodeDB.String error", "err", err) + } + + return b.String() +} + +// Upsert inserts any new entries or entries with a Version higher than the +// existing version. Returns any new or updated entries +func (svdb *VersionCertificateDB) Upsert(savEntries []*istanbul.VersionCertificate) ([]*istanbul.VersionCertificate, error) { + logger := svdb.logger.New("func", "Upsert") + + var newEntries []*istanbul.VersionCertificate + + getExistingEntry := func(entry db.GenericEntry) (db.GenericEntry, error) { + savEntry, err := versionCertificateEntryFromGenericEntry(entry) + if err != nil { + return entry, err + } + return svdb.Get(savEntry.Address()) + } + + onNewEntry := func(batch *leveldb.Batch, entry db.GenericEntry) error { + savEntry, err := versionCertificateEntryFromGenericEntry(entry) + if err != nil { + return err + } + savEntryBytes, err := encodeVersionCertificate(savEntry) + if err != nil { + return err + } + batch.Put(addressKey(savEntry.Address()), savEntryBytes) + newEntries = append(newEntries, savEntry) + logger.Trace("Updating with new entry", + "address", savEntry.Address, "new version", savEntry.Version) + return nil + } + + onUpdatedEntry := func(batch *leveldb.Batch, existingEntry db.GenericEntry, newEntry db.GenericEntry) error { + existingSav, err := versionCertificateEntryFromGenericEntry(existingEntry) + if err != nil { + return err + } + newSav, err := versionCertificateEntryFromGenericEntry(newEntry) + if err != nil { + return err + } + if newSav.Version <= existingSav.Version { + logger.Trace("Skipping new entry whose version is not greater than the existing entry", "existing version", existingSav.Version, "new version", newSav.Version) + return nil + } + return onNewEntry(batch, newEntry) + } + + entries := make([]db.GenericEntry, len(savEntries)) + for i, sav := range savEntries { + entries[i] = db.GenericEntry(sav) + } + + if err := svdb.gdb.Upsert(entries, getExistingEntry, onUpdatedEntry, onNewEntry); err != nil { + logger.Warn("Error upserting entries", "err", err) + return nil, err + } + return newEntries, nil +} + +// Get gets the istanbul.VersionCertificateEntry entry with address `address`. +// Returns an error if no entry exists. +func (svdb *VersionCertificateDB) Get(address common.Address) (*istanbul.VersionCertificate, error) { + entryBytes, err := svdb.gdb.Get(addressKey(address)) + if err != nil { + return nil, err + } + return decodeVersionCertificate(entryBytes) +} + +// GetVersion gets the version for the entry with address `address` +// Returns an error if no entry exists +func (svdb *VersionCertificateDB) GetVersion(address common.Address) (uint, error) { + signedAnnVersion, err := svdb.Get(address) + if err != nil { + return 0, err + } + return signedAnnVersion.Version, nil +} + +// GetAll gets each istanbul.VersionCertificateEntry in the db +func (svdb *VersionCertificateDB) GetAll() ([]*istanbul.VersionCertificate, error) { + var entries []*istanbul.VersionCertificate + err := svdb.iterate(func(address common.Address, entry *istanbul.VersionCertificate) error { + entries = append(entries, entry) + return nil + }) + if err != nil { + return nil, err + } + return entries, nil +} + +// Remove will remove an entry from the table +func (svdb *VersionCertificateDB) Remove(address common.Address) error { + batch := new(leveldb.Batch) + batch.Delete(addressKey(address)) + return svdb.gdb.Write(batch) +} + +// Prune will remove entries for all addresses not present in addressesToKeep +func (svdb *VersionCertificateDB) Prune(addressesToKeep map[common.Address]bool) error { + batch := new(leveldb.Batch) + err := svdb.iterate(func(address common.Address, entry *istanbul.VersionCertificate) error { + if !addressesToKeep[address] { + svdb.logger.Trace("Deleting entry", "address", address) + batch.Delete(addressKey(address)) + } + return nil + }) + if err != nil { + return err + } + return svdb.gdb.Write(batch) +} + +// Version certificates are serialised differently to network serialisation for +// storage in the version certificate db. Instead of storing just the version +// and signature, all fields are stored. It's not clear why this approach was +// chosen since it is not necessary to store the public key and address because +// they can be derived from the version and signature. Nevertheless we continue +// to use this approach because we can't easily change it without changing the +// storage format and breaking backwards compatibility. +func decodeVersionCertificate(value []byte) (*istanbul.VersionCertificate, error) { + var content struct { + Address common.Address + PublicKey []byte + Version uint + Signature []byte + } + if err := rlp.DecodeBytes(value, &content); err != nil { + return nil, err + } + decodedPublicKey, err := crypto.UnmarshalPubkey(content.PublicKey) + if err != nil { + return nil, err + } + return istanbul.NewVersionCertificateFromFields(content.Version, content.Signature, content.Address, decodedPublicKey), nil +} + +// Version certificates are serialised differently to network serialisation for +// storage in the version certificate db. Instead of storing just the version +// and signature, all fields are stored. It's not clear why this approach was +// chosen since it is not necessary to store the public key and address because +// they can be derived from the version and signature. Nevertheless we continue +// to use this approach because we can't easily change it without changing the +// storage format and breaking backwards compatibility. +func encodeVersionCertificate(vc *istanbul.VersionCertificate) ([]byte, error) { + encodedPublicKey := crypto.FromECDSAPub(vc.PublicKey()) + return rlp.EncodeToBytes([]interface{}{vc.Address(), encodedPublicKey, vc.Version, vc.Signature}) +} + +// iterate will call `onEntry` for each entry in the db +func (svdb *VersionCertificateDB) iterate(onEntry func(common.Address, *istanbul.VersionCertificate) error) error { + logger := svdb.logger.New("func", "iterate") + // Only target address keys + keyPrefix := []byte(dbAddressPrefix) + + onDBEntry := func(key []byte, value []byte) error { + entry, err := decodeVersionCertificate(value) + if err != nil { + return err + } + + address := common.BytesToAddress(key) + if err := onEntry(address, entry); err != nil { + return err + } + return nil + } + + if err := svdb.gdb.Iterate(keyPrefix, onDBEntry); err != nil { + logger.Warn("Error iterating through db entries", "err", err) + return err + } + return nil +} + +// VersionCertificateEntryInfo gives basic information for an entry in the DB +type VersionCertificateEntryInfo struct { + Address string `json:"address"` + Version uint `json:"version"` +} + +// Info gives a map VersionCertificateEntryInfo where each key is the address. +// Intended for RPC use +func (svdb *VersionCertificateDB) Info() (map[string]*VersionCertificateEntryInfo, error) { + dbInfo := make(map[string]*VersionCertificateEntryInfo) + err := svdb.iterate(func(address common.Address, entry *istanbul.VersionCertificate) error { + dbInfo[address.Hex()] = &VersionCertificateEntryInfo{ + Address: entry.Address().Hex(), + Version: entry.Version, + } + return nil + }) + return dbInfo, err +}
diff --git go-ethereum/consensus/istanbul/proxy/proxy_engine_test.go celo/consensus/istanbul/proxy/proxy_engine_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9eaa733076d60f643a94f2096f3b35e68bae0bb0 --- /dev/null +++ celo/consensus/istanbul/proxy/proxy_engine_test.go @@ -0,0 +1,364 @@ +// Copyright 2017 The celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "crypto/ecdsa" + "reflect" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/backend/backendtest" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p" +) + +func TestHandleValEnodeShare(t *testing.T) { + // Create two validators and one proxy backend. + // 1) Proxied validator (val0) + // 2) Non proxied validator (val1) + numValidators := 2 + genesisCfg, nodeKeys := backendtest.GetGenesisAndKeys(numValidators, true) + + val0BEi, _ := backendtest.NewTestBackend(false, common.Address{}, true, genesisCfg, nodeKeys[0]) + val0BE := val0BEi.(BackendForProxiedValidatorEngine) + val0Peer := consensustest.NewMockPeer(val0BE.SelfNode(), p2p.AnyPurpose) + + proxyBEi, _ := backendtest.NewTestBackend(true, val0BE.Address(), false, genesisCfg, nil) + proxyBE := proxyBEi.(BackendForProxyEngine) + + val1BEi, _ := backendtest.NewTestBackend(false, common.Address{}, false, genesisCfg, nodeKeys[1]) + val1BE := val1BEi.(BackendForProxiedValidatorEngine) + val1Peer := consensustest.NewMockPeer(val1BE.SelfNode(), p2p.AnyPurpose) + + // Get the proxied validator engine object + pvi := val0BE.GetProxiedValidatorEngine() + pv := pvi.(*proxiedValidatorEngine) + + // Get the proxy engine object + pi := proxyBE.GetProxyEngine() + p := pi.(*proxyEngine) + + // Sleep for 6 seconds so that val1BE will generate it's enode certificate. + time.Sleep(6 * time.Second) + + // Register the proxied validator with the proxy object + p.RegisterProxiedValidatorPeer(val0Peer) + + // "Send" an enode certificate msg from val1 to val0 + val1EnodeCertMap := val1BE.RetrieveEnodeCertificateMsgMap() + val1EnodeCert := val1EnodeCertMap[val1BE.SelfNode().ID()].Msg + val1EnodeCertPayload, _ := val1EnodeCert.Payload() + + p2pMsg, err := backendtest.CreateP2PMsg(istanbul.EnodeCertificateMsg, val1EnodeCertPayload) + if err != nil { + t.Errorf("Error in creating p2p message. Error: %v", err) + } + handled, err := val0BEi.HandleMsg(val1BE.Address(), + p2pMsg, + val1Peer) + if !handled || err != nil { + t.Errorf("Error in handling enode certificate msg. Handled: %v, Error: %v", handled, err) + } + + // Sleep for a bit since the handling of the enode certificate message is done in a separate thread + time.Sleep(1 * time.Second) + + // Generate val enode share message + vesMsg, err := pv.generateValEnodesShareMsg([]common.Address{val1BE.Address()}) + if err != nil { + t.Errorf("Error in generating a val enode share msg") + } + + vesMsgPayload, _ := vesMsg.Payload() + // "Send" it to the proxy + handled, err = p.handleValEnodesShareMsg(val0Peer, vesMsgPayload) + if !handled || err != nil { + t.Errorf("Uncessfully handled val enode share message. Handled: %v, Err: %v", handled, err) + } + // Sleep for a bit since the handling of the val enode share message is done in a separate thread + time.Sleep(1 * time.Second) + + // Verify that the proxy has val1's enode in it's val enode table + vetEntries, err := proxyBE.GetValEnodeTableEntries(nil) + if err != nil { + t.Errorf("Error in retrieving val enode table entries from proxy. Err: %v", err) + } + + if len(vetEntries) != 1 { + t.Errorf("Incorrect number of val enode table entries. Have %d, Want: 1", len(vetEntries)) + } + + expectedVetEntry := istanbul.AddressEntry{Address: val1BE.Address(), Node: val1BE.SelfNode()} + if vetEntries[val1BE.Address()].Address != expectedVetEntry.Address || vetEntries[val1BE.Address()].Node.URLv4() != expectedVetEntry.Node.URLv4() { + t.Errorf("Proxy has incorrect val enode table entry. Want: %v, Have: %v", expectedVetEntry, vetEntries[val1BE.Address()]) + } +} + +func TestHandleEnodeCertificateMessage(t *testing.T) { + // Create two validators and one proxy backend. + // 1) Proxied validator (val0) + // 2) Non proxied validator (val1) + numValidators := 2 + genesisCfg, nodeKeys := backendtest.GetGenesisAndKeys(numValidators, true) + + val0BEi, _ := backendtest.NewTestBackend(false, common.Address{}, true, genesisCfg, nodeKeys[0]) + val0BE := val0BEi.(BackendForProxiedValidatorEngine) + val0Peer := consensustest.NewMockPeer(val0BE.SelfNode(), p2p.AnyPurpose) + + proxyBEi, _ := backendtest.NewTestBackend(true, val0BE.Address(), false, genesisCfg, nil) + proxyBE := proxyBEi.(BackendForProxyEngine) + proxyPeer := consensustest.NewMockPeer(proxyBE.SelfNode(), p2p.ProxyPurpose) + + // Get the proxied validator engine object + pvi := val0BE.GetProxiedValidatorEngine() + pv := pvi.(*proxiedValidatorEngine) + + // Add the proxy to the proxied validator + pv.AddProxy(proxyBE.SelfNode(), proxyBE.SelfNode()) + pv.RegisterProxyPeer(proxyPeer) + + // Register the proxied validator with the proxy object + pi := proxyBE.GetProxyEngine() + p := pi.(*proxyEngine) + p.RegisterProxiedValidatorPeer(val0Peer) + + val1BEi, _ := backendtest.NewTestBackend(false, common.Address{}, false, genesisCfg, nodeKeys[1]) + val1BE := val1BEi.(BackendForProxiedValidatorEngine) + val1Peer := consensustest.NewMockPeer(val1BE.SelfNode(), p2p.ValidatorPurpose) + + // Create a non elected validator + unelectedValKey, _ := crypto.GenerateKey() + unelectedValBEi, _ := backendtest.NewTestBackend(false, common.Address{}, false, genesisCfg, unelectedValKey) + unelectedValBE := unelectedValBEi.(BackendForProxiedValidatorEngine) + unelectedValPeer := consensustest.NewMockPeer(unelectedValBE.SelfNode(), p2p.AnyPurpose) + + // Sleep for 6 seconds so that val1BE will generate it's enode certificate. + time.Sleep(6 * time.Second) + + // Test that the node will forward a message from val within val connection set + testEnodeCertFromRemoteVal(t, val1BE, val1Peer, proxyBEi) + + // Test that the node will not forward a message not within val connetion set + testEnodeCertFromUnelectedRemoteVal(t, unelectedValBE, unelectedValKey, unelectedValPeer, proxyBEi) + + // Test that the proxy will save the enode certificate into it's enode certificate msg map + // when it's proxy sends it. + testEnodeCertFromProxiedVal(t, val0BE, val0Peer, proxyBEi, proxyBE) +} + +func testEnodeCertFromRemoteVal(t *testing.T, valBE BackendForProxiedValidatorEngine, valPeer consensus.Peer, proxyBEi backendtest.TestBackendInterface) { + valEnodeCertMap := valBE.RetrieveEnodeCertificateMsgMap() + valEnodeCert := valEnodeCertMap[valBE.SelfNode().ID()].Msg + valEnodeCertPayload, _ := valEnodeCert.Payload() + + p2pMsg, err := backendtest.CreateP2PMsg(istanbul.EnodeCertificateMsg, valEnodeCertPayload) + if err != nil { + t.Errorf("Error in creating p2p message. Error: %v", err) + } + handled, err := proxyBEi.HandleMsg(valBE.Address(), + p2pMsg, + valPeer) + if !handled || err != nil { + t.Errorf("Error in handling enode certificate msg. Handled: %v, Error: %v", handled, err) + } +} + +func testEnodeCertFromUnelectedRemoteVal(t *testing.T, unelectedValBE BackendForProxiedValidatorEngine, unelectedValKey *ecdsa.PrivateKey, unelectedValPeer consensus.Peer, proxyBEi backendtest.TestBackendInterface) { + ecMsg := istanbul.NewEnodeCeritifcateMessage(&istanbul.EnodeCertificate{ + EnodeURL: unelectedValBE.SelfNode().URLv4(), + Version: 1, + }, unelectedValBE.Address()) + // Sign the message + if err := ecMsg.Sign(func(data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), unelectedValKey) + }); err != nil { + t.Errorf("Error in signing enode certificate message. Error: %v", err) + } + + ecMsgPayload, _ := ecMsg.Payload() + + p2pMsg, err := backendtest.CreateP2PMsg(istanbul.EnodeCertificateMsg, ecMsgPayload) + if err != nil { + t.Errorf("Error in creating p2p message. Error: %v", err) + } + handled, err := proxyBEi.HandleMsg(unelectedValBE.Address(), + p2pMsg, + unelectedValPeer) + if !handled || err != istanbul.ErrUnauthorizedAddress { + t.Errorf("Error in handling enode certificate msg. Handled: %v, Error: %v", handled, err) + } +} + +func testEnodeCertFromProxiedVal(t *testing.T, proxiedValBE BackendForProxiedValidatorEngine, proxiedValPeer consensus.Peer, proxyBEi backendtest.TestBackendInterface, proxyBE BackendForProxyEngine) { + valEnodeCertMap := proxiedValBE.RetrieveEnodeCertificateMsgMap() + valEnodeCert := valEnodeCertMap[proxyBE.SelfNode().ID()].Msg + valEnodeCertPayload, _ := valEnodeCert.Payload() + + p2pMsg, err := backendtest.CreateP2PMsg(istanbul.EnodeCertificateMsg, valEnodeCertPayload) + if err != nil { + t.Errorf("Error in creating p2p message. Error: %v", err) + } + handled, err := proxyBEi.HandleMsg(proxiedValBE.Address(), + p2pMsg, + proxiedValPeer) + if !handled || err != nil { + t.Errorf("Error in handling enode certificate msg. Handled: %v, Error: %v", handled, err) + } + + // Sleep for a bit since the handling of the val enode share message is done in a separate thread + time.Sleep(1 * time.Second) + + // Verify that the proxy has val1's enode in it's enode certificate msg map + msgMap := proxyBE.RetrieveEnodeCertificateMsgMap() + + if len(msgMap) != 1 { + t.Errorf("Incorrect number of val enode table entries. Have %d, Want: 1", len(msgMap)) + } + + if msgMap[proxyBE.SelfNode().ID()] == nil { + t.Errorf("Proxy enode cert message map has incorrect entry. Want: %v, Have: %v", proxyBE.SelfNode().ID(), reflect.ValueOf(msgMap).MapKeys()[0]) + } +} + +func TestHandleConsensusMsg(t *testing.T) { + // Create two validators and one proxy backend. + // 1) Proxied validator (val0) + // 2) Non proxied validator (val1) + numValidators := 2 + genesisCfg, nodeKeys := backendtest.GetGenesisAndKeys(numValidators, true) + + val0BEi, _ := backendtest.NewTestBackend(false, common.Address{}, true, genesisCfg, nodeKeys[0]) + val0BE := val0BEi.(BackendForProxiedValidatorEngine) + val0Peer := consensustest.NewMockPeer(val0BE.SelfNode(), p2p.AnyPurpose) + + proxyBEi, _ := backendtest.NewTestBackend(true, val0BE.Address(), false, genesisCfg, nil) + proxyBE := proxyBEi.(BackendForProxyEngine) + proxyPeer := consensustest.NewMockPeer(proxyBE.SelfNode(), p2p.ProxyPurpose) + + // Get the proxied validator engine object + pvi := val0BE.GetProxiedValidatorEngine() + pv := pvi.(*proxiedValidatorEngine) + + // Add the proxy to the proxied validator + pv.AddProxy(proxyBE.SelfNode(), proxyBE.SelfNode()) + pv.RegisterProxyPeer(proxyPeer) + + // Register the proxied validator with the proxy object + pi := proxyBE.GetProxyEngine() + p := pi.(*proxyEngine) + p.RegisterProxiedValidatorPeer(val0Peer) + + val1BEi, _ := backendtest.NewTestBackend(false, common.Address{}, false, genesisCfg, nodeKeys[1]) + val1BE := val1BEi.(BackendForProxiedValidatorEngine) + val1Peer := consensustest.NewMockPeer(val1BE.SelfNode(), p2p.ValidatorPurpose) + + // Create a non elected validator + unelectedValKey, _ := crypto.GenerateKey() + unelectedValBEi, _ := backendtest.NewTestBackend(false, common.Address{}, false, genesisCfg, unelectedValKey) + unelectedValBE := unelectedValBEi.(BackendForProxiedValidatorEngine) + unelectedValPeer := consensustest.NewMockPeer(unelectedValBE.SelfNode(), p2p.AnyPurpose) + + // Sleep for 5 seconds so that val1BE will generate it's enode certificate. + time.Sleep(5 * time.Second) + + subject := &istanbul.Subject{ + View: &istanbul.View{ + Round: common.Big0, + Sequence: common.Big0, + }, + Digest: common.Hash{}, + } + + // Test that the node will forward a consensus message from val within val connection set + testConsensusMsgFromRemoteVal(t, istanbul.NewPrepareMessage(subject, val1BE.Address()), val1BE, nodeKeys[1], val1Peer, proxyBEi) + + // Test that the node will not forward a consensus message not within val connetion set + testConsensusMsgFromUnelectedRemoteVal(t, istanbul.NewPrepareMessage(subject, unelectedValBE.Address()), unelectedValBE, unelectedValKey, unelectedValPeer, proxyBEi) + + // Test that the proxy will not handle a consensus message from the proxied validator + testConsensusMsgFromProxiedVal(t, istanbul.NewPrepareMessage(subject, val0BE.Address()), val0BE, nodeKeys[0], val0Peer, proxyBEi) +} + +func testConsensusMsgFromRemoteVal(t *testing.T, consMsg *istanbul.Message, valBE BackendForProxiedValidatorEngine, valKey *ecdsa.PrivateKey, valPeer consensus.Peer, proxyBEi backendtest.TestBackendInterface) { + if err := consMsg.Sign(func(data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), valKey) + }); err != nil { + t.Errorf("Error in signing consensus message. Error: %v", err) + } + + consMsgPayload, _ := consMsg.Payload() + + p2pMsg, err := backendtest.CreateP2PMsg(istanbul.ConsensusMsg, consMsgPayload) + if err != nil { + t.Errorf("Error in creating p2p message. Error: %v", err) + } + handled, err := proxyBEi.HandleMsg(valBE.Address(), + p2pMsg, + valPeer) + if !handled || err != nil { + t.Errorf("Error in handling consensus msg. Handled: %v, Error: %v", handled, err) + } +} + +func testConsensusMsgFromUnelectedRemoteVal(t *testing.T, consMsg *istanbul.Message, unelectedValBE BackendForProxiedValidatorEngine, unelectedValKey *ecdsa.PrivateKey, unelectedValPeer consensus.Peer, proxyBEi backendtest.TestBackendInterface) { + // Sign the message + if err := consMsg.Sign(func(data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), unelectedValKey) + }); err != nil { + t.Errorf("Error in signing consensus message. Error: %v", err) + } + + consMsgPayload, _ := consMsg.Payload() + + p2pMsg, err := backendtest.CreateP2PMsg(istanbul.ConsensusMsg, consMsgPayload) + if err != nil { + t.Errorf("Error in creating p2p message. Error: %v", err) + } + handled, err := proxyBEi.HandleMsg(unelectedValBE.Address(), + p2pMsg, + unelectedValPeer) + if !handled || err != istanbul.ErrUnauthorizedAddress { + t.Errorf("Error in handling consensus msg. Handled: %v, Error: %v", handled, err) + } +} + +func testConsensusMsgFromProxiedVal(t *testing.T, consMsg *istanbul.Message, proxiedValBE BackendForProxiedValidatorEngine, proxiedValKey *ecdsa.PrivateKey, proxiedValPeer consensus.Peer, proxyBEi backendtest.TestBackendInterface) { + // Sign the message + if err := consMsg.Sign(func(data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), proxiedValKey) + }); err != nil { + t.Errorf("Error in signing consensus message. Error: %v", err) + } + + consMsgPayload, _ := consMsg.Payload() + + p2pMsg, err := backendtest.CreateP2PMsg(istanbul.ConsensusMsg, consMsgPayload) + if err != nil { + t.Errorf("Error in creating p2p message. Error: %v", err) + } + handled, _ := proxyBEi.HandleMsg(proxiedValBE.Address(), + p2pMsg, + proxiedValPeer) + if handled { + t.Errorf("Unexpectedly handled a consensus message from the proxied validator") + } +}
diff --git go-ethereum/consensus/istanbul/core/message_set_test.go celo/consensus/istanbul/core/message_set_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e12f8a43bc5e51453ac8f51054e80179306255da --- /dev/null +++ celo/consensus/istanbul/core/message_set_test.go @@ -0,0 +1,111 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/rlp" +) + +func mockSub() *istanbul.Subject { + view := &istanbul.View{ + Round: new(big.Int), + Sequence: new(big.Int), + } + + return &istanbul.Subject{ + View: view, + Digest: common.BytesToHash([]byte("1234567890")), + } +} + +func mockMsg(rawSub []byte, valIndex uint64, valSet istanbul.ValidatorSet) *istanbul.Message { + return &istanbul.Message{ + Code: istanbul.MsgPrepare, + Msg: rawSub, + Address: valSet.GetByIndex(valIndex).Address(), + } +} + +func TestMessageSetWithSubject(t *testing.T) { + valSet := newTestValidatorSet(4) + + ms := newMessageSet(valSet) + + rawSub, err := rlp.EncodeToBytes(mockSub()) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + msg := mockMsg(rawSub, 0, valSet) + + err = ms.Add(msg) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + err = ms.Add(msg) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + if ms.Size() != 1 { + t.Errorf("the size of message set mismatch: have %v, want 1", ms.Size()) + } +} + +func TestAddAll(t *testing.T) { + valSet := newTestValidatorSet(16) + + ms := newMessageSet(valSet) + + rawSub, err := rlp.EncodeToBytes(mockSub()) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + ms.Add(mockMsg(rawSub, 0, valSet)) + ms.Add(mockMsg(rawSub, 1, valSet)) + ms.Add(mockMsg(rawSub, 2, valSet)) + ms.Add(mockMsg(rawSub, 3, valSet)) + ms.Add(mockMsg(rawSub, 4, valSet)) + + if ms.Size() != 5 { + t.Errorf("the size of message set mismatch: have %v, want 5", ms.Size()) + } + + msgs := []*istanbul.Message{ + mockMsg(rawSub, 3, valSet), + mockMsg(rawSub, 5, valSet), + mockMsg(rawSub, 6, valSet), + mockMsg(rawSub, 7, valSet), + } + + added := ms.AddAll(msgs) + + if ms.Size() != 8 { + t.Errorf("the size of message set mismatch: have %v, want 8", ms.Size()) + } + + if added != 3 { + t.Errorf("added amount to message set mismatch: have %v, want 3", added) + } +}
diff --git go-ethereum/consensus/istanbul/validator/validator.go celo/consensus/istanbul/validator/validator.go new file mode 100644 index 0000000000000000000000000000000000000000..08d4321406b060d3d52e5c29665881da3b1dc910 --- /dev/null +++ celo/consensus/istanbul/validator/validator.go @@ -0,0 +1,77 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package validator + +import ( + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/rlp" +) + +func New(addr common.Address, blsPublicKey blscrypto.SerializedPublicKey) istanbul.Validator { + return &defaultValidator{ + address: addr, + blsPublicKey: blsPublicKey, + uncompressedBlsPublicKey: nil, + } +} + +func DeserializeValidator(binaryData []byte) (istanbul.Validator, error) { + var value defaultValidator + + err := rlp.DecodeBytes(binaryData, &value) + if err != nil { + return nil, err + } + return &value, nil +} + +func NewSet(validators []istanbul.ValidatorData) istanbul.ValidatorSet { + return newDefaultSet(validators) +} + +func NewSetFromDataWithBLSKeyCache(validators []istanbul.ValidatorDataWithBLSKeyCache) istanbul.ValidatorSet { + return newDefaultSetFromDataWithBLSKeyCache(validators) +} + +func DeserializeValidatorSet(binaryData []byte) (istanbul.ValidatorSet, error) { + var value defaultSet + + err := rlp.DecodeBytes(binaryData, &value) + if err != nil { + return nil, err + } + return &value, nil +} + +func ExtractValidators(extraData []byte) []istanbul.ValidatorData { + // get the validator addresses + validatorLength := common.AddressLength + blscrypto.PUBLICKEYBYTES + validators := make([]istanbul.ValidatorData, (len(extraData) / validatorLength)) + for i := 0; i < len(validators); i++ { + copy(validators[i].Address[:], extraData[i*validatorLength:i*validatorLength+common.AddressLength]) + copy(validators[i].BLSPublicKey[:], extraData[i*validatorLength+common.AddressLength:]) + } + + return validators +} + +// Check whether the extraData is presented in prescribed form +func ValidExtraData(extraData []byte) bool { + return len(extraData)%common.AddressLength == 0 +}
diff --git go-ethereum/consensus/istanbul/proxy/proxy_set_test.go celo/consensus/istanbul/proxy/proxy_set_test.go new file mode 100644 index 0000000000000000000000000000000000000000..00260f1bd0ec2f037c1ee8f8501c469ec60626a7 --- /dev/null +++ celo/consensus/istanbul/proxy/proxy_set_test.go @@ -0,0 +1,349 @@ +// Copyright 2017 The celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "crypto/ecdsa" + "math/rand" + "net" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +func createProxyConfig(randomSeed int64) *istanbul.ProxyConfig { + rng := rand.New(rand.NewSource(randomSeed)) + + key, err := ecdsa.GenerateKey(crypto.S256(), rng) + if err != nil { + panic("couldn't generate key: " + err.Error()) + } + + internalPort := rng.Int() + externalPort := internalPort + 1 + + return &istanbul.ProxyConfig{ + InternalNode: enode.NewV4(&key.PublicKey, net.IPv6loopback, internalPort, 0), + ExternalNode: enode.NewV4(&key.PublicKey, net.IPv6loopback, externalPort, 0), + } +} + +func TestProxySet(t *testing.T) { + proxy0Config := createProxyConfig(0) + proxy1Config := createProxyConfig(1) + proxy2Config := createProxyConfig(2) + + proxy0ID := proxy0Config.InternalNode.ID() + proxy1ID := proxy1Config.InternalNode.ID() + proxy2ID := proxy2Config.InternalNode.ID() + + proxy0Peer := consensustest.NewMockPeer(proxy0Config.InternalNode, p2p.ProxyPurpose) + proxy1Peer := consensustest.NewMockPeer(proxy1Config.InternalNode, p2p.ProxyPurpose) + proxy2Peer := consensustest.NewMockPeer(proxy2Config.InternalNode, p2p.ProxyPurpose) + + proxy0 := &Proxy{node: proxy0Config.InternalNode, + externalNode: proxy0Config.ExternalNode, + peer: proxy0Peer} + + proxy1 := &Proxy{node: proxy1Config.InternalNode, + externalNode: proxy1Config.ExternalNode, + peer: proxy1Peer} + + proxy2 := &Proxy{node: proxy2Config.InternalNode, + externalNode: proxy2Config.ExternalNode, + peer: proxy2Peer} + + remoteVal0Address := common.BytesToAddress([]byte("32526362351")) + remoteVal1Address := common.BytesToAddress([]byte("64362643436")) + remoteVal2Address := common.BytesToAddress([]byte("72436452463")) + remoteVal3Address := common.BytesToAddress([]byte("46346373463")) + remoteVal4Address := common.BytesToAddress([]byte("25364624352")) + remoteVal5Address := common.BytesToAddress([]byte("73576426242")) + remoteVal6Address := common.BytesToAddress([]byte("75375374457")) + remoteVal7Address := common.BytesToAddress([]byte("64262735373")) + remoteVal8Address := common.BytesToAddress([]byte("23575764262")) + remoteVal9Address := common.BytesToAddress([]byte("74364626427")) + + proxySetOps := []struct { + addProxies []*istanbul.ProxyConfig // Proxies to add to the proxy set + removeProxies []enode.ID // Proxies to remove from the proxy set + setProxyPeer map[enode.ID]consensus.Peer // Map of added proxy that gets peered + removeProxyPeer *enode.ID // Proxy that becomes unpeered + addRemoteValidators []common.Address // Remote validator addresses to add to the proxy set + removeRemoteValidators []common.Address // Remote validator addresses to remove from the proxy set + expectedValProxyAssignments map[common.Address]*Proxy // Expected validator to proxy assignments after all proxy set operations are applied + }{ + // Test with no proxies and no validators + { + expectedValProxyAssignments: map[common.Address]*Proxy{}, + }, + + // Test with 3 remote validator addresses. Emulate a proxied validator starting with initial valset at genesis block. + { + addRemoteValidators: []common.Address{remoteVal0Address, remoteVal1Address, remoteVal2Address}, + expectedValProxyAssignments: map[common.Address]*Proxy{remoteVal0Address: nil, remoteVal1Address: nil, remoteVal2Address: nil}, + }, + + // Test with adding 1 proxy. Emulate when a proxied validator adds a proxy but that proxy hasn't peered yet. + { + addProxies: []*istanbul.ProxyConfig{proxy0Config}, + expectedValProxyAssignments: map[common.Address]*Proxy{remoteVal0Address: nil, remoteVal1Address: nil, remoteVal2Address: nil}, + }, + + // Test with the proxy being peered. + { + setProxyPeer: map[enode.ID]consensus.Peer{proxy0ID: proxy0Peer}, + expectedValProxyAssignments: map[common.Address]*Proxy{remoteVal0Address: proxy0, remoteVal1Address: proxy0, remoteVal2Address: proxy0}, + }, + + // Test with adding an additional proxy + { + addProxies: []*istanbul.ProxyConfig{proxy1Config}, + expectedValProxyAssignments: map[common.Address]*Proxy{remoteVal0Address: proxy0, remoteVal1Address: proxy0, remoteVal2Address: proxy0}, + }, + + // Test with additional proxy peered + { + setProxyPeer: map[enode.ID]consensus.Peer{proxy1ID: proxy1Peer}, + expectedValProxyAssignments: map[common.Address]*Proxy{remoteVal0Address: proxy0, remoteVal1Address: proxy1, remoteVal2Address: proxy0}, + }, + + // Test with one of the proxies getting unpeered. Emulate when a proxy gets disconnected. + { + removeProxyPeer: &proxy0ID, + expectedValProxyAssignments: map[common.Address]*Proxy{remoteVal0Address: proxy1, remoteVal1Address: proxy1, remoteVal2Address: proxy1}, + }, + + // Test the the unpeered proxy getting repeered + { + setProxyPeer: map[enode.ID]consensus.Peer{proxy0ID: proxy0Peer}, + expectedValProxyAssignments: map[common.Address]*Proxy{remoteVal0Address: proxy0, remoteVal1Address: proxy1, remoteVal2Address: proxy0}, + }, + + // Test with adding 5 more valiators + { + addRemoteValidators: []common.Address{remoteVal3Address, remoteVal4Address, remoteVal5Address, remoteVal6Address, remoteVal7Address}, + expectedValProxyAssignments: map[common.Address]*Proxy{remoteVal0Address: proxy0, remoteVal1Address: proxy1, remoteVal2Address: proxy0, + remoteVal3Address: proxy0, remoteVal4Address: proxy0, remoteVal5Address: proxy1, remoteVal6Address: proxy1, remoteVal7Address: proxy0}, + }, + + // Test with two remote validators removed and two added. Emulate a change in valset at epoch transition + { + addRemoteValidators: []common.Address{remoteVal8Address, remoteVal9Address}, + removeRemoteValidators: []common.Address{remoteVal3Address, remoteVal5Address}, + expectedValProxyAssignments: map[common.Address]*Proxy{remoteVal0Address: proxy0, remoteVal1Address: proxy1, remoteVal2Address: proxy0, + remoteVal4Address: proxy0, remoteVal6Address: proxy1, remoteVal7Address: proxy0, remoteVal8Address: proxy1, remoteVal9Address: proxy0}, + }, + + // Test with adding a new peered proxy + { + addProxies: []*istanbul.ProxyConfig{proxy2Config}, + setProxyPeer: map[enode.ID]consensus.Peer{proxy2ID: proxy2Peer}, + expectedValProxyAssignments: map[common.Address]*Proxy{remoteVal0Address: proxy0, remoteVal1Address: proxy1, remoteVal2Address: proxy2, + remoteVal4Address: proxy0, remoteVal6Address: proxy1, remoteVal7Address: proxy0, remoteVal8Address: proxy1, remoteVal9Address: proxy0}, + }, + } + + proxiesAdded := make(map[enode.ID]*istanbul.ProxyConfig) + valsAdded := make(map[common.Address]struct{}) + proxiesPeered := make(map[enode.ID]struct{}) + proxiesNotPeered := make(map[enode.ID]struct{}) + + // Testing with consistentHashingPolicy but keeping all tests as + // implementation-agnostic as possible + ps := newProxySet(newConsistentHashingPolicy()) + + for i, proxySetOp := range proxySetOps { + // Add proxies + if proxySetOp.addProxies != nil { + for _, proxyConfig := range proxySetOp.addProxies { + ps.addProxy(proxyConfig) + proxiesAdded[proxyConfig.InternalNode.ID()] = proxyConfig + proxiesNotPeered[proxyConfig.InternalNode.ID()] = struct{}{} + } + } + + // Remove proxies + if proxySetOp.removeProxies != nil { + for _, proxyID := range proxySetOp.removeProxies { + ps.removeProxy(proxyID) + delete(proxiesAdded, proxyID) + delete(proxiesPeered, proxyID) + delete(proxiesNotPeered, proxyID) + } + } + + verifyAddedProxies(t, i, ps, proxiesAdded) + + // Set proxy peer + if proxySetOp.setProxyPeer != nil { + for proxyID, proxyPeer := range proxySetOp.setProxyPeer { + ps.setProxyPeer(proxyID, proxyPeer) + proxiesPeered[proxyID] = struct{}{} + delete(proxiesNotPeered, proxyID) + } + } + + // Remove proxy peer + if proxySetOp.removeProxyPeer != nil { + ps.removeProxyPeer(*proxySetOp.removeProxyPeer) + ps.unassignDisconnectedProxies(0) + delete(proxiesPeered, *proxySetOp.removeProxyPeer) + proxiesNotPeered[*proxySetOp.removeProxyPeer] = struct{}{} + } + + verifyProxyPeers(t, i, ps, proxiesPeered, proxiesNotPeered) + + // Add validator(s) + if proxySetOp.addRemoteValidators != nil { + ps.addRemoteValidators(proxySetOp.addRemoteValidators) + for _, remoteVal := range proxySetOp.addRemoteValidators { + valsAdded[remoteVal] = struct{}{} + } + } + + // Remove validator(s) + if proxySetOp.removeRemoteValidators != nil { + ps.removeRemoteValidators(proxySetOp.removeRemoteValidators) + for _, remoteVal := range proxySetOp.removeRemoteValidators { + delete(valsAdded, remoteVal) + } + } + + verifyAddedValidators(t, i, ps, valsAdded) + + // Verifying that the validator assignments are correct + verifyValidatorAssignments(t, i, ps, proxySetOp.expectedValProxyAssignments) + } +} + +func verifyAddedProxies(t *testing.T, opID int, ps *proxySet, addedProxies map[enode.ID]*istanbul.ProxyConfig) { + // Test that adding the proxies results in the correct number of proxies within the proxyset + if len(ps.proxiesByID) != len(addedProxies) { + t.Errorf("opID: %d - ps.proxiedByID length is incorrect. Want: %d, Have: %d", opID, len(addedProxies), len(ps.proxiesByID)) + } + + // Testing that getting the proxies from the proxyset works as intended + for proxyID, proxyConfig := range addedProxies { + proxyFromPS := ps.getProxy(proxyID) + + if proxyFromPS == nil || proxyFromPS.node != proxyConfig.InternalNode || proxyFromPS.externalNode != proxyConfig.ExternalNode { + t.Errorf("opID: %d - ps.getProxy() did not get the correct proxy. Internal Node (Want: %v, Have: %v) External Node(Want: %v, Have: %v)", opID, + proxyConfig.InternalNode, proxyFromPS.node, + proxyConfig.ExternalNode, proxyFromPS.externalNode) + } + } +} + +func verifyProxyPeers(t *testing.T, opID int, ps *proxySet, proxiesPeered map[enode.ID]struct{}, proxiesNotPeered map[enode.ID]struct{}) { + // Verify that the sum of proxiesPeered length and proxiesNotPeered length is + // equal to the total number of proxies in the proxySet + if len(proxiesPeered)+len(proxiesNotPeered) != len(ps.proxiesByID) { + t.Errorf("opID: %d - len(proxiesPeered) + len(proxiesNotPeered) != len(ps.proxiesByID). Want: %d, Have %d", opID, len(proxiesPeered)+len(proxiesNotPeered), len(ps.proxiesByID)) + } + + // iteration through proxiedPeered and verify that those proxies are peered + for proxyID := range proxiesPeered { + proxyFromPS := ps.getProxy(proxyID) + + if proxyFromPS == nil { + t.Errorf("opID: %d - expected proxy not in proxy set. Want: %s, Have: nil", opID, proxyID) + } + + if proxyFromPS.peer == nil { + t.Errorf("opID: %d - expected proxy to be peered. ProxyID: %s", opID, proxyID) + } + } + + for proxyID := range proxiesNotPeered { + proxyFromPS := ps.getProxy(proxyID) + + if proxyFromPS == nil { + t.Errorf("opID: %d - expected proxy not in proxy set. Want: %s, Have: nil", opID, proxyID) + } + + if proxyFromPS.peer != nil { + t.Errorf("opID: %d - expected proxy to be not peered. ProxyID: %s", opID, proxyID) + } + } +} + +func verifyAddedValidators(t *testing.T, opID int, ps *proxySet, addedValidators map[common.Address]struct{}) { + // Test that ps.GetValidators() is the right length and returns the correct set + valsFromPS := ps.getValidators() + + // Test that the lengths are the same + if len(valsFromPS) != len(addedValidators) { + t.Errorf("opID: %d - ps.getValidators() returned incorrect lengthed set. Want :%d, Have: %d", opID, len(addedValidators), len(valsFromPS)) + } + + valsFromPSMap := make(map[common.Address]struct{}) + for _, valAddress := range valsFromPS { + valsFromPSMap[valAddress] = struct{}{} + } + + for expectedValAddress := range addedValidators { + if _, ok := valsFromPSMap[expectedValAddress]; !ok { + t.Errorf("opID: %d - expected val address %s not in the proxy set's validators", opID, expectedValAddress.Hex()) + } + } +} + +func verifyValidatorAssignments(t *testing.T, opID int, ps *proxySet, expectedValAssignments map[common.Address]*Proxy) { + valAssignmentsFromPS := ps.getValidatorAssignments(nil, nil) + + // Test that the lengths of the proxy set's assignments and expected assignments are the same + if len(expectedValAssignments) != len(valAssignmentsFromPS) { + t.Errorf("opID: %d - val assignment lengths don't match. Want: %d, Have: %d", opID, len(expectedValAssignments), len(valAssignmentsFromPS)) + } + + // Test that all entries in the expected assignments is equal to the proxy set's assignments + for val, expectedProxy := range expectedValAssignments { + proxyFromPS := valAssignmentsFromPS[val] + if !proxyCompare(expectedProxy, proxyFromPS) { + t.Errorf("opID: %d - unexpected val assignement. Want: %s -> %s, Have: %s -> %s", opID, val, proxyOutput(expectedProxy), val, proxyOutput(proxyFromPS)) + } + } +} + +func proxyCompare(p1 *Proxy, p2 *Proxy) bool { + if p1 == nil && p2 == nil { + return true + } else if p1 == nil && p2 != nil { + return false + } else if p1 != nil && p2 == nil { + return false + } else if p1.node != p2.node || p1.externalNode != p2.externalNode || p1.peer != p2.peer { + return false + } else { + return true + } +} + +func proxyOutput(p *Proxy) string { + if p == nil { + return "nil" + } else { + return p.node.ID().String() + } +}
diff --git go-ethereum/consensus/istanbul/db/generic_db_test.go celo/consensus/istanbul/db/generic_db_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7750b3c76916edbcdf7c208fa7ac09a401768e9d --- /dev/null +++ celo/consensus/istanbul/db/generic_db_test.go @@ -0,0 +1,82 @@ +package db + +import ( + "testing" + + "github.com/ethereum/go-ethereum/log" + "github.com/syndtr/goleveldb/leveldb" +) + +type mockEntry struct{} + +func TestUpsert(t *testing.T) { + gdb, err := New(int64(0), "", log.New(), nil) + if err != nil { + t.Fatal("Failed to create DB") + } + + type testCase struct { + ExistingEntry *mockEntry + NewEntry *mockEntry + ExpectedOnExistingEntryCalled bool + ExpectedOnNewEntryCalled bool + } + + testCases := []*testCase{ + { + ExistingEntry: nil, + NewEntry: &mockEntry{}, + ExpectedOnExistingEntryCalled: false, + ExpectedOnNewEntryCalled: true, + }, + { + ExistingEntry: &mockEntry{}, + NewEntry: &mockEntry{}, + ExpectedOnExistingEntryCalled: true, + ExpectedOnNewEntryCalled: false, + }, + } + + for i, testCase := range testCases { + onExistingEntryCalled, onNewEntryCalled, err := upsertEntry(gdb, testCase.ExistingEntry, testCase.NewEntry) + if err != nil { + t.Fatal("Failed to upsert entry") + } + if testCase.ExpectedOnExistingEntryCalled != onExistingEntryCalled { + t.Errorf("Unexpected onExistingEntryCalled value for test case %d. Expected %v, got %v", i, testCase.ExpectedOnExistingEntryCalled, onExistingEntryCalled) + } + if testCase.ExpectedOnNewEntryCalled != onNewEntryCalled { + t.Errorf("Unexpected onExistingEntryCalled value for test case %d. Expected %v, got %v", i, testCase.ExpectedOnNewEntryCalled, onNewEntryCalled) + } + } +} + +func upsertEntry(gdb *GenericDB, existingEntry *mockEntry, newEntry *mockEntry) (bool, bool, error) { + var ( + onExistingEntryCalled bool + onNewEntryCalled bool + ) + + getExistingEntry := func(_ GenericEntry) (GenericEntry, error) { + if existingEntry == nil { + return nil, leveldb.ErrNotFound + } + return existingEntry, nil + } + onExistingEntry := func(_ *leveldb.Batch, _ GenericEntry, _ GenericEntry) error { + onExistingEntryCalled = true + return nil + } + onNewEntry := func(_ *leveldb.Batch, _ GenericEntry) error { + onNewEntryCalled = true + return nil + } + + err := gdb.Upsert( + []GenericEntry{GenericEntry(newEntry)}, + getExistingEntry, + onExistingEntry, + onNewEntry, + ) + return onExistingEntryCalled, onNewEntryCalled, err +}
diff --git go-ethereum/consensus/istanbul/backend/engine.go celo/consensus/istanbul/backend/engine.go new file mode 100644 index 0000000000000000000000000000000000000000..aa708bba3e782279b828bf0885342bb5b007d91d --- /dev/null +++ celo/consensus/istanbul/backend/engine.go @@ -0,0 +1,1162 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "bytes" + "errors" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + istanbulCore "github.com/ethereum/go-ethereum/consensus/istanbul/core" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + gpm "github.com/ethereum/go-ethereum/contracts/gasprice_minimum" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/core" + ethCore "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/trie" + lru "github.com/hashicorp/golang-lru" + "golang.org/x/crypto/sha3" +) + +const ( + inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory + inmemoryPeers = 40 + inmemoryMessages = 1024 + mobileAllowedClockSkew uint64 = 5 +) + +var ( + // errInvalidProposal is returned when a prposal is malformed. + errInvalidProposal = errors.New("invalid proposal") + // errInvalidSignature is returned when given signature is not signed by given + // address. + errInvalidSignature = errors.New("invalid signature") + // errInsufficientSeals is returned when there is not enough signatures to + // pass the quorum check. + errInsufficientSeals = errors.New("not enough seals to reach quorum") + // errUnknownBlock is returned when the list of validators or header is requested for a block + // that is not part of the local blockchain. + errUnknownBlock = errors.New("unknown block") + // errUnauthorized is returned if a header is signed by a non authorized entity. + errUnauthorized = errors.New("not an elected validator") + // errInvalidExtraDataFormat is returned when the extra data format is incorrect + errInvalidExtraDataFormat = errors.New("invalid extra data format") + // errCoinbase is returned if a block's coinbase is invalid + errInvalidCoinbase = errors.New("invalid coinbase") + // errInvalidTimestamp is returned if the timestamp of a block is lower than the previous block's timestamp + the minimum block period. + errInvalidTimestamp = errors.New("invalid timestamp") + // errInvalidVotingChain is returned if an authorization list is attempted to + // be modified via out-of-range or non-contiguous headers. + errInvalidVotingChain = errors.New("invalid voting chain") + // errInvalidAggregatedSeal is returned if the aggregated seal is invalid. + errInvalidAggregatedSeal = errors.New("invalid aggregated seal") + // errEmptyAggregatedSeal is returned if the aggregated seal is missing. + errEmptyAggregatedSeal = errors.New("empty aggregated seal") + // errNonEmptyAggregatedSeal is returned if the aggregated seal is not empty during preprepase proposal phase. + errNonEmptyAggregatedSeal = errors.New("Non empty aggregated seal during preprepare") + // errMismatchTxhashes is returned if the TxHash in header is mismatch. + errMismatchTxhashes = errors.New("mismatch transactions hashes") + // errInvalidValidatorSetDiff is returned if the header contains invalid validator set diff + errInvalidValidatorSetDiff = errors.New("invalid validator set diff") + // errNotAValidator is returned when the node is not configured as a validator + errNotAValidator = errors.New("Not configured as a validator") +) + +var ( + now = time.Now + + inmemoryAddresses = 20 // Number of recent addresses from ecrecover + recentAddresses, _ = lru.NewARC(inmemoryAddresses) +) + +// Author retrieves the Ethereum address of the account that minted the given +// block, which may be different from the header's coinbase if a consensus +// engine is based on signatures. +func (sb *Backend) Author(header *types.Header) (common.Address, error) { + return ecrecover(header) +} + +// VerifyHeader checks whether a header conforms to the consensus rules of a +// given engine. Verifies the seal regardless of given "seal" argument. +func (sb *Backend) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { + return sb.verifyHeader(chain, header, false, nil) +} + +// verifyHeaderFromProposal checks whether a header conforms to the consensus rules from the +// preprepare istanbul phase. +func (sb *Backend) verifyHeaderFromProposal(chain consensus.ChainHeaderReader, header *types.Header) error { + return sb.verifyHeader(chain, header, true, nil) +} + +// verifyHeader checks whether a header conforms to the consensus rules.The +// caller may optionally pass in a batch of parents (ascending order) to avoid +// looking those up from the database. This is useful for concurrently verifying +// a batch of new headers. +// If emptyAggregatedSeal is set, the aggregatedSeal will be checked to be completely empty. Otherwise +// it will be checked as a normal aggregated seal. +func (sb *Backend) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, emptyAggregatedSeal bool, parents []*types.Header) error { + if header.Number == nil { + return errUnknownBlock + } + + // If the full chain isn't available (as on mobile devices), don't reject future blocks + // This is due to potential clock skew + allowedFutureBlockTime := uint64(now().Unix()) + if !chain.Config().FullHeaderChainAvailable { + allowedFutureBlockTime = allowedFutureBlockTime + mobileAllowedClockSkew + } + + // Don't waste time checking blocks from the future + if header.Time > allowedFutureBlockTime { + return consensus.ErrFutureBlock + } + + // Ensure that the extra data format is satisfied + if _, err := header.IstanbulExtra(); err != nil { + return errInvalidExtraDataFormat + } + + return sb.verifyCascadingFields(chain, header, emptyAggregatedSeal, parents) +} + +// A sanity check for lightest mode. Checks that the correct epoch block exists for this header +func (sb *Backend) checkEpochBlockExists(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { + number := header.Number.Uint64() + // Check that latest epoch block is available + epoch := istanbul.GetEpochNumber(number, sb.config.Epoch) + epochBlockNumber := istanbul.GetEpochLastBlockNumber(epoch-1, sb.config.Epoch) + if number == epochBlockNumber { + epochBlockNumber = istanbul.GetEpochLastBlockNumber(epoch-2, sb.config.Epoch) + } + for _, hdr := range parents { + if hdr.Number.Uint64() == epochBlockNumber { + return nil + } + } + parent := chain.GetHeaderByNumber(epochBlockNumber) + if parent == nil || parent.Number.Uint64() != epochBlockNumber { + return consensus.ErrUnknownAncestor + } + return nil +} + +// verifyCascadingFields verifies all the header fields that are not standalone, +// rather depend on a batch of previous headers. The caller may optionally pass +// in a batch of parents (ascending order) to avoid looking those up from the +// database. This is useful for concurrently verifying a batch of new headers. +// If emptyAggregatedSeal is set, the aggregatedSeal will be checked to be completely empty. Otherwise +// it will be checked as a normal aggregated seal. +func (sb *Backend) verifyCascadingFields(chain consensus.ChainHeaderReader, header *types.Header, emptyAggregatedSeal bool, parents []*types.Header) error { + // The genesis block is the always valid dead-end + number := header.Number.Uint64() + if number == 0 { + return nil + } + // Ensure that the block's timestamp isn't too close to it's parent + var parent *types.Header + if len(parents) > 0 { + parent = parents[len(parents)-1] + } else { + parent = chain.GetHeader(header.ParentHash, number-1) + } + if chain.Config().FullHeaderChainAvailable { + + if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash { + return consensus.ErrUnknownAncestor + } + if parent.Time+sb.config.BlockPeriod > header.Time { + return errInvalidTimestamp + } + // Verify validators in extraData. Validators in snapshot and extraData should be the same. + if err := sb.verifySigner(chain, header, parents); err != nil { + return err + } + } else if err := sb.checkEpochBlockExists(chain, header, parents); err != nil { + return err + } + + return sb.verifyAggregatedSeals(chain, header, emptyAggregatedSeal, parents) +} + +// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers +// concurrently. The method returns a quit channel to abort the operations and +// a results channel to retrieve the async verifications (the order is that of +// the input slice). +func (sb *Backend) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { + abort := make(chan struct{}) + results := make(chan error, len(headers)) + go func() { + errored := false + for i, header := range headers { + var err error + if errored { + err = consensus.ErrUnknownAncestor + } else { + err = sb.verifyHeader(chain, header, false, headers[:i]) + } + + if err != nil { + errored = true + } + + select { + case <-abort: + return + case results <- err: + } + } + }() + return abort, results +} + +// verifySigner checks whether the signer is in parent's validator set +func (sb *Backend) verifySigner(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { + // Verifying the genesis block is not supported + number := header.Number.Uint64() + if number == 0 { + return errUnknownBlock + } + + // Retrieve the snapshot needed to verify this header and cache it + snap, err := sb.snapshot(chain, number-1, header.ParentHash, parents) + if err != nil { + return err + } + + // resolve the authorization key and check against signers + signer, err := ecrecover(header) + if err != nil { + return err + } + + // Signer should be in the validator set of previous block's extraData. + if _, v := snap.ValSet.GetByAddress(signer); v == nil { + return errUnauthorized + } + return nil +} + +// verifyAggregatedSeals checks whether the aggregated seal and parent seal in the header is +// signed on by the block's validators and the parent block's validators respectively +// If emptyAggregatedSeal is set, the aggregatedSeal will be checked to be completely empty. Otherwise +// it will be checked as a normal aggregated seal. +func (sb *Backend) verifyAggregatedSeals(chain consensus.ChainHeaderReader, header *types.Header, emptyAggregatedseal bool, parents []*types.Header) error { + number := header.Number.Uint64() + // We don't need to verify committed seals in the genesis block + if number == 0 { + return nil + } + + extra, err := header.IstanbulExtra() + if err != nil { + return err + } + + // Check the signatures on the current header + snap, err := sb.snapshot(chain, number-1, header.ParentHash, parents) + if err != nil { + return err + } + validators := snap.ValSet.Copy() + + if emptyAggregatedseal { + // The length of Committed seals should be exactly 0 (preprepare proposal check) + if len(extra.AggregatedSeal.Signature) != 0 { + return errNonEmptyAggregatedSeal + } + // Should we also verify that the bitmap and round are nil? + } else { + // The length of Committed seals should be larger than 0 + if len(extra.AggregatedSeal.Signature) == 0 { + return errEmptyAggregatedSeal + } + + err = sb.verifyAggregatedSeal(header.Hash(), validators, extra.AggregatedSeal) + if err != nil { + return err + } + } + + // The genesis block is skipped since it has no parents. + // The first block is also skipped, since its parent + // is the genesis block which contains no parent signatures. + // The parent commit messages are only used for the uptime calculation, + // so ultralight clients don't need to verify them + if number > 1 && chain.Config().FullHeaderChainAvailable { + sb.logger.Trace("verifyAggregatedSeals: verifying parent seals for block", "num", number) + var parentValidators istanbul.ValidatorSet + // The first block in an epoch will have a different validator set than the block + // before it. If the current block is the first block in an epoch, we need to fetch the previous + // validator set to validate the parent signatures. + if number%sb.config.Epoch == 1 { + snap, err := sb.snapshot(chain, number-2, common.Hash{}, nil) + if err != nil { + return err + } + parentValidators = snap.ValSet.Copy() + } else { + parentValidators = validators.Copy() + } + + // Check the signatures made by the validator set corresponding to the + // parent block's hash. We use header.ParentHash to handle both + // ultralight and non-ultralight cases. + // parent.Hash() would correspond to the previous epoch + // block in ultralight, while the extra.ParentCommit is made on the block which was + // immediately before the current block. + return sb.verifyAggregatedSeal(header.ParentHash, parentValidators, extra.ParentAggregatedSeal) + } + + return nil +} + +func (sb *Backend) verifyAggregatedSeal(headerHash common.Hash, validators istanbul.ValidatorSet, aggregatedSeal types.IstanbulAggregatedSeal) error { + logger := sb.logger.New("func", "Backend.verifyAggregatedSeal()") + if len(aggregatedSeal.Signature) != types.IstanbulExtraBlsSignature { + return errInvalidAggregatedSeal + } + + proposalSeal := istanbulCore.PrepareCommittedSeal(headerHash, aggregatedSeal.Round) + // Find which public keys signed from the provided validator set + publicKeys := []blscrypto.SerializedPublicKey{} + for i := 0; i < validators.Size(); i++ { + if aggregatedSeal.Bitmap.Bit(i) == 1 { + pubKey := validators.GetByIndex(uint64(i)).BLSPublicKey() + publicKeys = append(publicKeys, pubKey) + } + } + // The length of a valid seal should be greater than the minimum quorum size + if len(publicKeys) < validators.MinQuorumSize() { + logger.Error("Aggregated seal does not aggregate enough seals", "numSeals", len(publicKeys), "minimum quorum size", validators.MinQuorumSize()) + return errInsufficientSeals + } + err := blscrypto.VerifyAggregatedSignature(publicKeys, proposalSeal, []byte{}, aggregatedSeal.Signature, false, false) + if err != nil { + logger.Error("Unable to verify aggregated signature", "err", err) + return errInvalidSignature + } + + return nil +} + +// VerifySeal checks whether the crypto seal on a header is valid according to +// the consensus rules of the given engine. +func (sb *Backend) VerifySeal(header *types.Header) error { + // Ensure the block number is greater than zero, but less or equal to than max uint64. + if header.Number.Cmp(common.Big0) <= 0 || !header.Number.IsUint64() { + return errUnknownBlock + } + + extra, err := header.IstanbulExtra() + if err != nil { + return errInvalidExtraDataFormat + } + + // Acquire the validator set whose signatures will be verified. + // FIXME: Based on the current implemenation of validator set construction, only validator sets + // from the canonical chain will be used. This means that if the provided header is a valid + // member of a non-canonical chain, seal verification will only succeed if the validator set + // happens to be the same as the canonical chain at the same block number (as would be the case + // for a fork from the canonical chain which does not cross an epoch boundary) + valSet := sb.getValidators(header.Number.Uint64()-1, header.ParentHash) + return sb.verifyAggregatedSeal(header.Hash(), valSet, extra.AggregatedSeal) +} + +// Prepare initializes the consensus fields of a block header according to the +// rules of a particular engine. The changes are executed inline. +// The parent seal is not included when the node is not validating. +func (sb *Backend) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { + // copy the parent extra data as the header extra data + number := header.Number.Uint64() + parent := chain.GetHeader(header.ParentHash, number-1) + if parent == nil { + return consensus.ErrUnknownAncestor + } + + // set header's timestamp + header.Time = parent.Time + sb.config.BlockPeriod + nowTime := uint64(now().Unix()) + if header.Time < nowTime { + header.Time = nowTime + } + + // Record what the delay should be and sleep if greater than 0. + // TODO(victor): Sleep here was previously removed and added to the miner instead, that change + // has been temporarily reverted until it can be reimplemented without causing fewer signatures + // to be included by the block producer. + delay := time.Until(time.Unix(int64(header.Time), 0)) + if delay < 0 { + sb.sleepGauge.Update(0) + } else { + sb.sleepGauge.Update(delay.Nanoseconds()) + time.Sleep(delay) + } + + if err := writeEmptyIstanbulExtra(header); err != nil { + return err + } + + // addParentSeal blocks for up to 500ms waiting for the core to reach the target sequence. + // Prepare is called from non-validators, so don't bother with the parent seal unless this + // block is to be proposed instead of for the local state. + if sb.IsValidating() { + return sb.addParentSeal(chain, header) + } else { + return nil + } +} + +// UpdateValSetDiff will update the validator set diff in the header, if the mined header is the last block of the epoch +func (sb *Backend) UpdateValSetDiff(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB) error { + // If this is the last block of the epoch, then get the validator set diff, to save into the header + log.Trace("Called UpdateValSetDiff", "number", header.Number.Uint64(), "epoch", sb.config.Epoch) + if istanbul.IsLastBlockOfEpoch(header.Number.Uint64(), sb.config.Epoch) { + newValSet, err := sb.getNewValidatorSet(header, state) + if err == nil { + // Get the last epoch's validator set + snap, err := sb.snapshot(chain, header.Number.Uint64()-1, header.ParentHash, nil) + if err != nil { + return err + } + + // add validators in snapshot to extraData's validators section + return writeValidatorSetDiff(header, snap.validators(), newValSet) + } + } + // If it's not the last block or we were unable to pull the new validator set, then the validator set diff should be empty + return writeValidatorSetDiff(header, []istanbul.ValidatorData{}, []istanbul.ValidatorData{}) +} + +// IsLastBlockOfEpoch returns whether or not a particular header represents the last block in the epoch. +func (sb *Backend) IsLastBlockOfEpoch(header *types.Header) bool { + return istanbul.IsLastBlockOfEpoch(header.Number.Uint64(), sb.config.Epoch) +} + +// EpochSize returns the size of epochs in blocks. +func (sb *Backend) EpochSize() uint64 { + return sb.config.Epoch +} + +// Finalize runs any post-transaction state modifications (e.g. block rewards) +// but does not assemble the block. +// +// Note: The block header and state database might be updated to reflect any +// consensus rules that happen at finalization (e.g. block rewards). +func (sb *Backend) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction) { + start := time.Now() + defer sb.finalizationTimer.UpdateSince(start) + + logger := sb.logger.New("func", "Finalize", "block", header.Number.Uint64(), "epochSize", sb.config.Epoch) + logger.Trace("Finalizing") + + // The contract calls in Finalize() may emit logs, which we later add to an extra "block" receipt + // (in FinalizeAndAssemble() during construction or in `StateProcessor.process()` during verification). + // They are looked up using the zero hash instead of a transaction hash, and so we need to first call + // `state.Prepare()` so that they get filed under the zero hash. Otherwise, they would get filed under + // the hash of the last transaction in the block (if there were any). + state.Prepare(common.Hash{}, len(txs)) + + vmRunner := sb.chain.NewEVMRunner(header, state) + + // Trigger an update to the gas price minimum in the GasPriceMinimum contract based on block congestion + snapshot := state.Snapshot() + _, err := gpm.UpdateGasPriceMinimum(vmRunner, header.GasUsed) + if err != nil { + state.RevertToSnapshot(snapshot) + } + + lastBlockOfEpoch := istanbul.IsLastBlockOfEpoch(header.Number.Uint64(), sb.config.Epoch) + + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + logger.Debug("Finalized", "duration", now().Sub(start), "lastInEpoch", lastBlockOfEpoch) +} + +// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block +// rewards) and assembles the final block. +// +// Note: The block header and state database might be updated to reflect any +// consensus rules that happen at finalization (e.g. block rewards). +func (sb *Backend) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, receipts []*types.Receipt, randomness *types.Randomness) (*types.Block, error) { + + sb.Finalize(chain, header, state, txs) + // Add the block receipt with logs from the non-transaction core contract calls (if there were any) + receipts = core.AddBlockReceipt(receipts, state, header.Hash()) + + // Assemble and return the final block for sealing + block := types.NewBlock(header, txs, receipts, randomness, new(trie.Trie)) + return block, nil +} + +// checkIsValidSigner checks if validator is a valid signer for the block +// returns an error if not +func (sb *Backend) checkIsValidSigner(chain consensus.ChainHeaderReader, header *types.Header) error { + snap, err := sb.snapshot(chain, header.Number.Uint64()-1, header.ParentHash, nil) + if err != nil { + return err + } + + _, v := snap.ValSet.GetByAddress(sb.wallets().Ecdsa.Address) + if v == nil { + return errUnauthorized + } + return nil +} + +// Seal generates a new block for the given input block with the local miner's +// seal place on top and submits it the the consensus engine. +func (sb *Backend) Seal(chain consensus.ChainHeaderReader, block *types.Block) error { + + header := block.Header() + + // Bail out if we're unauthorized to sign a block + if err := sb.checkIsValidSigner(chain, header); err != nil { + return err + } + + if parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1); parent == nil { + return consensus.ErrUnknownAncestor + } + + // update the block header timestamp and signature and propose the block to core engine + block, err := sb.signBlock(block) + if err != nil { + return err + } + + // post block into Istanbul engine + if err := sb.EventMux().Post(istanbul.RequestEvent{Proposal: block}); err != nil { + return err + } + + return nil +} + +// signBlock signs block with a seal +func (sb *Backend) signBlock(block *types.Block) (*types.Block, error) { + header := block.Header() + // sign the hash + seal, err := sb.Sign(sigHash(header).Bytes()) + if err != nil { + return nil, err + } + + err = writeSeal(header, seal) + if err != nil { + return nil, err + } + + return block.WithHeader(header), nil +} + +// APIs returns the RPC APIs this consensus engine provides. +func (sb *Backend) APIs(chain consensus.ChainHeaderReader) []rpc.API { + return []rpc.API{{ + Namespace: "istanbul", + Version: "1.0", + Service: &API{chain: chain, istanbul: sb}, + Public: true, + }} +} + +func (sb *Backend) SetChain(chain consensus.ChainContext, currentBlock func() *types.Block, stateAt func(common.Hash) (*state.StateDB, error)) { + sb.chain = chain + sb.currentBlock = currentBlock + sb.stateAt = stateAt + + if bc, ok := chain.(*ethCore.BlockChain); ok { + // Batched. For stats & announce + chainHeadCh := make(chan ethCore.ChainHeadEvent, 10) + chainHeadSub := bc.SubscribeChainHeadEvent(chainHeadCh) + + go func() { + defer chainHeadSub.Unsubscribe() + // Loop to run on new chain head events. Chain head events may be batched. + for { + select { + case chainHeadEvent := <-chainHeadCh: + sb.newChainHead(chainHeadEvent.Block) + case err := <-chainHeadSub.Err(): + log.Error("Error in istanbul's subscription to the blockchain's chainhead event", "err", err) + return + } + } + }() + + // Unbatched event listener + chainEventCh := make(chan ethCore.ChainEvent, 10) + chainEventSub := bc.SubscribeChainEvent(chainEventCh) + + go func() { + defer chainEventSub.Unsubscribe() + // Loop to update replica state. Listens to chain events to avoid batching. + for { + select { + case chainEvent := <-chainEventCh: + if !sb.isCoreStarted() && sb.replicaState != nil { + consensusBlock := new(big.Int).Add(chainEvent.Block.Number(), common.Big1) + sb.replicaState.NewChainHead(consensusBlock) + } + case err := <-chainEventSub.Err(): + log.Error("Error in istanbul's subscription to the blockchain's chain event", "err", err) + return + } + } + }() + } +} + +// SetCallBacks implements consensus.Istanbul.SetCallBacks +func (sb *Backend) SetCallBacks(hasBadBlock func(common.Hash) bool, + processBlock func(*types.Block, *state.StateDB) (types.Receipts, []*types.Log, uint64, error), + validateState func(*types.Block, *state.StateDB, types.Receipts, uint64) error, + onNewConsensusBlock func(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB)) error { + sb.coreMu.RLock() + defer sb.coreMu.RUnlock() + if sb.isCoreStarted() { + return istanbul.ErrStartedEngine + } + + sb.hasBadBlock = hasBadBlock + sb.processBlock = processBlock + sb.validateState = validateState + sb.onNewConsensusBlock = onNewConsensusBlock + return nil +} + +// StartValidating implements consensus.Istanbul.StartValidating +func (sb *Backend) StartValidating() error { + sb.coreMu.Lock() + defer sb.coreMu.Unlock() + if sb.isCoreStarted() { + return istanbul.ErrStartedEngine + } + + if sb.hasBadBlock == nil || sb.processBlock == nil || sb.validateState == nil { + return errors.New("Must SetCallBacks prior to StartValidating") + } + + sb.logger.Info("Starting istanbul.Engine validating") + if err := sb.core.Start(); err != nil { + return err + } + + // Having coreStarted as false at this point guarantees that announce versions + // will be updated by the time announce messages in the announceThread begin + // being generated + if !sb.IsProxiedValidator() { + sb.UpdateAnnounceVersion() + } + + sb.coreStarted.Store(true) + + // coreStarted must be true by this point for validator peers to be successfully added + if !sb.config.Proxied { + if err := sb.RefreshValPeers(); err != nil { + sb.logger.Warn("Error refreshing validator peers", "err", err) + } + } + + return nil +} + +// StopValidating implements consensus.Istanbul.StopValidating +func (sb *Backend) StopValidating() error { + sb.coreMu.Lock() + defer sb.coreMu.Unlock() + if !sb.isCoreStarted() { + return istanbul.ErrStoppedEngine + } + sb.logger.Info("Stopping istanbul.Engine validating") + if err := sb.core.Stop(); err != nil { + return err + } + sb.coreStarted.Store(false) + + return nil +} + +// StartProxiedValidatorEngine implements consensus.Istanbul.StartProxiedValidatorEngine +func (sb *Backend) StartProxiedValidatorEngine() error { + sb.proxiedValidatorEngineMu.Lock() + defer sb.proxiedValidatorEngineMu.Unlock() + + if sb.proxiedValidatorEngineRunning { + return istanbul.ErrStartedProxiedValidatorEngine + } + + if !sb.config.Proxied { + return istanbul.ErrValidatorNotProxied + } + + sb.proxiedValidatorEngine.Start() + sb.proxiedValidatorEngineRunning = true + + return nil +} + +// StopProxiedValidatorEngine implements consensus.Istanbul.StopProxiedValidatorEngine +func (sb *Backend) StopProxiedValidatorEngine() error { + sb.proxiedValidatorEngineMu.Lock() + defer sb.proxiedValidatorEngineMu.Unlock() + + if !sb.proxiedValidatorEngineRunning { + return istanbul.ErrStoppedProxiedValidatorEngine + } + + sb.proxiedValidatorEngine.Stop() + sb.proxiedValidatorEngineRunning = false + + return nil +} + +// MakeReplica clears the start/stop state & stops this node from participating in consensus +func (sb *Backend) MakeReplica() error { + if sb.replicaState != nil { + return sb.replicaState.MakeReplica() + } + return istanbul.ErrUnauthorizedAddress +} + +// MakePrimary clears the start/stop state & makes this node participate in consensus +func (sb *Backend) MakePrimary() error { + if sb.replicaState != nil { + return sb.replicaState.MakePrimary() + } + return istanbul.ErrUnauthorizedAddress +} + +// snapshot retrieves the validator set needed to sign off on the block immediately after 'number'. E.g. if you need to find the validator set that needs to sign off on block 6, +// this method should be called with number set to 5. +// +// hash - The requested snapshot's block's hash. Only used for snapshot cache storage. +// number - The requested snapshot's block number +// parents - (Optional argument) An array of headers from directly previous blocks. +func (sb *Backend) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) { + // Search for a snapshot in memory or on disk + var ( + headers []*types.Header + header *types.Header + snap *Snapshot + ) + + numberIter := number + + // If numberIter is not the last block of an epoch, then adjust it to be the last block of the previous epoch + if !istanbul.IsLastBlockOfEpoch(numberIter, sb.config.Epoch) { + epochNum := istanbul.GetEpochNumber(numberIter, sb.config.Epoch) + numberIter = istanbul.GetEpochLastBlockNumber(epochNum-1, sb.config.Epoch) + } + + // At this point, numberIter will always be the last block number of an epoch. Namely, it will be + // block numbers where the header contains the validator set diff. + // Note that block 0 (the genesis block) is one of those headers. It contains the initial set of validators in the + // 'addedValidators' field in the header. + + // Retrieve the most recent cached or on disk snapshot. + for ; ; numberIter = numberIter - sb.config.Epoch { + // If an in-memory snapshot was found, use that + if s, ok := sb.recentSnapshots.Get(numberIter); ok { + snap = s.(*Snapshot) + break + } + + var blockHash common.Hash + if numberIter == number && hash != (common.Hash{}) { + blockHash = hash + } else { + header = chain.GetHeaderByNumber(numberIter) + if header == nil { + log.Trace("Unable to find header in chain", "number", number) + } else { + blockHash = chain.GetHeaderByNumber(numberIter).Hash() + } + } + + if (blockHash != common.Hash{}) { + if s, err := loadSnapshot(sb.config.Epoch, sb.db, blockHash); err == nil { + log.Trace("Loaded validator set snapshot from disk", "number", numberIter, "hash", blockHash) + snap = s + sb.recentSnapshots.Add(numberIter, snap) + break + } + } + + if numberIter == 0 { + break + } + + // Panic if numberIter underflows (becomes greater than number). + if numberIter > number { + panic(fmt.Sprintf("There is a bug in the code. NumberIter underflowed, and should of stopped at 0. NumberIter: %v, number: %v", numberIter, number)) + } + } + + // If snapshot is still nil, then create a snapshot from genesis block + if snap == nil { + log.Debug("Snapshot is nil, creating from genesis") + // Panic if the numberIter does not equal 0 + if numberIter != 0 { + panic(fmt.Sprintf("There is a bug in the code. NumberIter should be 0. NumberIter: %v", numberIter)) + } + + genesis := chain.GetHeaderByNumber(0) + if genesis == nil { + log.Error("Cannot load genesis") + return nil, errors.New("Cannot load genesis") + } + + istanbulExtra, err := genesis.IstanbulExtra() + if err != nil { + log.Error("Unable to extract istanbul extra", "err", err) + return nil, err + } + + // The genesis block should have an empty RemovedValidators set. If not, throw an error + if istanbulExtra.RemovedValidators.BitLen() != 0 { + log.Error("Genesis block has a non empty RemovedValidators set") + return nil, errInvalidValidatorSetDiff + } + + validators, err := istanbul.CombineIstanbulExtraToValidatorData(istanbulExtra.AddedValidators, istanbulExtra.AddedValidatorsPublicKeys) + if err != nil { + log.Error("Cannot construct validators data from istanbul extra") + return nil, errInvalidValidatorSetDiff + } + snap = newSnapshot(sb.config.Epoch, 0, genesis.Hash(), validator.NewSet(validators)) + + if err := snap.store(sb.db); err != nil { + log.Error("Unable to store snapshot", "err", err) + return nil, err + } + } + + log.Trace("Most recent snapshot found", "number", numberIter) + // Calculate the returned snapshot by applying epoch headers' val set diffs to the intermediate snapshot (the one that is retrieved/created from above). + // This will involve retrieving all of those headers into an array, and then call snapshot.apply on that array and the intermediate snapshot. + // Note that the callee of this method may have passed in a set of previous headers, so we may be able to use some of them. + for numberIter+sb.config.Epoch <= number { + numberIter += sb.config.Epoch + + log.Trace("Retrieving ancestor header", "number", number, "numberIter", numberIter, "parents size", len(parents)) + inParents := -1 + for i := len(parents) - 1; i >= 0; i-- { + if parents[i].Number.Uint64() == numberIter { + inParents = i + break + } + } + if inParents >= 0 { + header = parents[inParents] + log.Trace("Retrieved header from parents param", "header num", header.Number.Uint64()) + } else { + header = chain.GetHeaderByNumber(numberIter) + if header == nil { + log.Error("The header retrieved from the chain is nil", "block num", numberIter) + return nil, errUnknownBlock + } + } + + headers = append(headers, header) + } + + if len(headers) > 0 { + var err error + log.Trace("Snapshot headers len greater than 0", "headers", headers) + snap, err = snap.apply(headers, sb.db) + if err != nil { + log.Error("Unable to apply headers to snapshots", "headers", headers) + return nil, err + } + + sb.recentSnapshots.Add(numberIter, snap) + } + // Make a copy of the snapshot to return, since a few fields will be modified. + // The original snap is probably stored within the LRU cache, so we don't want to + // modify that one. + returnSnap := snap.copy() + + returnSnap.Number = number + returnSnap.Hash = hash + + return returnSnap, nil +} + +func (sb *Backend) addParentSeal(chain consensus.ChainHeaderReader, header *types.Header) error { + number := header.Number.Uint64() + logger := sb.logger.New("func", "addParentSeal", "number", number) + + // only do this for blocks which start with block 1 as a parent + if number <= 1 { + return nil + } + + // Get parent's extra to fetch it's AggregatedSeal + parent := chain.GetHeader(header.ParentHash, number-1) + parentExtra, err := parent.IstanbulExtra() + if err != nil { + return err + } + + createParentSeal := func() types.IstanbulAggregatedSeal { + // In some cases, "addParentSeal" may be called before sb.core has moved to the next sequence, + // preventing signature aggregation. + // This typically happens in round > 0, since round 0 typically hits the "time.Sleep()" + // above. + // When this happens, loop until sb.core moves to the next sequence, with a limit of 500ms. + seq := waitCoreToReachSequence(sb.core, header.Number) + if seq == nil { + return parentExtra.AggregatedSeal + } + + logger = logger.New("parentAggregatedSeal", parentExtra.AggregatedSeal.String(), "cur_seq", seq) + + parentCommits := sb.core.ParentCommits() + if parentCommits == nil || parentCommits.Size() == 0 { + logger.Debug("No additional seals to combine with ParentAggregatedSeal") + return parentExtra.AggregatedSeal + } + + logger = logger.New("numParentCommits", parentCommits.Size()) + logger.Trace("Found commit messages from previous sequence to combine with ParentAggregatedSeal") + + // if we had any seals gossiped to us, proceed to add them to the + // already aggregated signature + unionAggregatedSeal, err := istanbulCore.UnionOfSeals(parentExtra.AggregatedSeal, parentCommits) + if err != nil { + logger.Error("Failed to combine commit messages with ParentAggregatedSeal", "err", err) + return parentExtra.AggregatedSeal + } + + // need to pass the previous block from the parent to get the parent's validators + // (otherwise we'd be getting the validators for the current block) + parentValidators := sb.getValidators(parent.Number.Uint64()-1, parent.ParentHash) + // only update to use the union if we indeed provided a valid aggregate signature for this block + if err := sb.verifyAggregatedSeal(parent.Hash(), parentValidators, unionAggregatedSeal); err != nil { + logger.Error("Failed to verify combined ParentAggregatedSeal", "err", err) + return parentExtra.AggregatedSeal + } + + logger.Debug("Succeeded in verifying combined ParentAggregatedSeal", "combinedParentAggregatedSeal", unionAggregatedSeal.String()) + return unionAggregatedSeal + } + + return writeAggregatedSeal(header, createParentSeal(), true) +} + +// SetStartValidatingBlock sets block that the validator will start validating on (inclusive) +func (sb *Backend) SetStartValidatingBlock(blockNumber *big.Int) error { + if sb.replicaState == nil { + return errNotAValidator + } + if blockNumber.Cmp(sb.currentBlock().Number()) < 0 { + return errors.New("blockNumber should be greater than the current block number") + } + return sb.replicaState.SetStartValidatingBlock(blockNumber) +} + +// SetStopValidatingBlock sets the block that the validator will stop just before (exclusive range) +func (sb *Backend) SetStopValidatingBlock(blockNumber *big.Int) error { + if sb.replicaState == nil { + return errNotAValidator + } + if blockNumber.Cmp(sb.currentBlock().Number()) < 0 { + return errors.New("blockNumber should be greater than the current block number") + } + return sb.replicaState.SetStopValidatingBlock(blockNumber) +} + +// FIXME: Need to update this for Istanbul +// sigHash returns the hash which is used as input for the Istanbul +// signing. It is the hash of the entire header apart from the 65 byte signature +// contained at the end of the extra data. +// +// Note, the method requires the extra data to be at least 65 bytes, otherwise it +// panics. This is done to avoid accidentally using both forms (signature present +// or not), which could be abused to produce different hashes for the same header. +func sigHash(header *types.Header) (hash common.Hash) { + hasher := sha3.NewLegacyKeccak256() + + // Clean seal is required for calculating proposer seal. + rlp.Encode(hasher, types.IstanbulFilteredHeader(header, false)) + hasher.Sum(hash[:0]) + return hash +} + +// ecrecover extracts the Ethereum account address from a signed header. +func ecrecover(header *types.Header) (common.Address, error) { + hash := header.Hash() + if addr, ok := recentAddresses.Get(hash); ok { + return addr.(common.Address), nil + } + + // Retrieve the signature from the header extra-data + istanbulExtra, err := header.IstanbulExtra() + if err != nil { + return common.Address{}, err + } + + addr, err := istanbul.GetSignatureAddress(sigHash(header).Bytes(), istanbulExtra.Seal) + if err != nil { + return addr, err + } + recentAddresses.Add(hash, addr) + return addr, nil +} + +func writeEmptyIstanbulExtra(header *types.Header) error { + extra := types.IstanbulExtra{ + AddedValidators: []common.Address{}, + AddedValidatorsPublicKeys: []blscrypto.SerializedPublicKey{}, + RemovedValidators: big.NewInt(0), + Seal: []byte{}, + AggregatedSeal: types.IstanbulAggregatedSeal{}, + ParentAggregatedSeal: types.IstanbulAggregatedSeal{}, + } + payload, err := rlp.EncodeToBytes(&extra) + if err != nil { + return err + } + + if len(header.Extra) < types.IstanbulExtraVanity { + header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity-len(header.Extra))...) + } + header.Extra = append(header.Extra[:types.IstanbulExtraVanity], payload...) + + return nil +} + +// writeValidatorSetDiff initializes the header's Extra field with any changes in the +// validator set that occurred since the last block +func writeValidatorSetDiff(header *types.Header, oldValSet []istanbul.ValidatorData, newValSet []istanbul.ValidatorData) error { + // compensate the lack bytes if header.Extra is not enough IstanbulExtraVanity bytes. + if len(header.Extra) < types.IstanbulExtraVanity { + header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity-len(header.Extra))...) + } + + addedValidators, removedValidators := istanbul.ValidatorSetDiff(oldValSet, newValSet) + addedValidatorsAddresses, addedValidatorsPublicKeys := istanbul.SeparateValidatorDataIntoIstanbulExtra(addedValidators) + + if len(addedValidators) > 0 || removedValidators.BitLen() > 0 { + oldValidatorsAddresses, _ := istanbul.SeparateValidatorDataIntoIstanbulExtra(oldValSet) + newValidatorsAddresses, _ := istanbul.SeparateValidatorDataIntoIstanbulExtra(newValSet) + log.Debug("Setting istanbul header validator fields", "oldValSet", common.ConvertToStringSlice(oldValidatorsAddresses), "newValSet", common.ConvertToStringSlice(newValidatorsAddresses), + "addedValidators", common.ConvertToStringSlice(addedValidatorsAddresses), "removedValidators", removedValidators.Text(16)) + } + + extra, err := header.IstanbulExtra() + if err != nil { + return nil + } + + extra.AddedValidators = addedValidatorsAddresses + extra.AddedValidatorsPublicKeys = addedValidatorsPublicKeys + extra.RemovedValidators = removedValidators + + // update the header's extra with the new diff + payload, err := rlp.EncodeToBytes(extra) + if err != nil { + return err + } + header.Extra = append(header.Extra[:types.IstanbulExtraVanity], payload...) + + return nil +} + +// writeSeal writes the extra-data field of the given header with the given seal. +func writeSeal(h *types.Header, seal []byte) error { + if len(seal) != types.IstanbulExtraSeal { + return errInvalidSignature + } + + istanbulExtra, err := h.IstanbulExtra() + if err != nil { + return err + } + + istanbulExtra.Seal = seal + payload, err := rlp.EncodeToBytes(&istanbulExtra) + if err != nil { + return err + } + + h.Extra = append(h.Extra[:types.IstanbulExtraVanity], payload...) + return nil +} + +// writeAggregatedSeal writes the extra-data field of a block header with given committed +// seals. If isParent is set to true, then it will write to the fields related +// to the parent commits of the block +func writeAggregatedSeal(h *types.Header, aggregatedSeal types.IstanbulAggregatedSeal, isParent bool) error { + if len(aggregatedSeal.Signature) != types.IstanbulExtraBlsSignature { + return errInvalidAggregatedSeal + } + + istanbulExtra, err := h.IstanbulExtra() + if err != nil { + return err + } + + if isParent { + istanbulExtra.ParentAggregatedSeal = aggregatedSeal + } else { + istanbulExtra.AggregatedSeal = aggregatedSeal + } + + payload, err := rlp.EncodeToBytes(&istanbulExtra) + if err != nil { + return err + } + + // compensate the lack bytes if header.Extra is not enough IstanbulExtraVanity bytes. + if len(h.Extra) < types.IstanbulExtraVanity { + h.Extra = append(h.Extra, bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity-len(h.Extra))...) + } + + h.Extra = append(h.Extra[:types.IstanbulExtraVanity], payload...) + return nil +} + +func waitCoreToReachSequence(core istanbulCore.Engine, expectedSequence *big.Int) *big.Int { + logger := log.New("func", "waitCoreToReachSequence") + timeout := time.After(500 * time.Millisecond) + ticker := time.NewTicker(10 * time.Millisecond) + defer ticker.Stop() + for { + select { + case <-ticker.C: + view := core.CurrentView() + if view != nil && view.Sequence != nil && view.Sequence.Cmp(expectedSequence) == 0 { + logger.Trace("Current sequence matches header", "cur_seq", view.Sequence) + return view.Sequence + } + case <-timeout: + log.Trace("Timed out while waiting for core to sequence change, unable to combine commit messages with ParentAggregatedSeal", "cur_view", core.CurrentView()) + return nil + } + } +}
diff --git go-ethereum/consensus/istanbul/announce/version_sharer.go celo/consensus/istanbul/announce/version_sharer.go new file mode 100644 index 0000000000000000000000000000000000000000..1b110cf3e19ec49cea2c4cde7c5833d5ddc93c4a --- /dev/null +++ celo/consensus/istanbul/announce/version_sharer.go @@ -0,0 +1,136 @@ +package announce + +import ( + "sync/atomic" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +type VersionSharer interface { + // ShareVersion generates announce data structures and + // and shares them with relevant nodes. + // It will: + // 1) Generate a new enode certificate + // 2) Multicast the new enode certificate to all peers in the validator conn set + // * Note: If this is a proxied validator, it's multicast message will be wrapped within a forward + // message to the proxy, which will in turn send the enode certificate to remote validators. + // 3) Generate a new version certificate + // 4) Gossip the new version certificate to all peers + ShareVersion(version uint) error +} + +type OnNewEnodeCertsMsgSentFn func(map[enode.ID]*istanbul.EnodeCertMsg) error + +func NewVersionSharer( + aWallets *atomic.Value, + network Network, + state *AnnounceState, + ovcp OutboundVersionCertificateProcessor, + ecertGenerator EnodeCertificateMsgGenerator, + ecertHolder EnodeCertificateMsgHolder, + onNewEnodeMsgsFn OnNewEnodeCertsMsgSentFn, +) VersionSharer { + // noop func + var onNewMsgsFn OnNewEnodeCertsMsgSentFn = func(map[enode.ID]*istanbul.EnodeCertMsg) error { return nil } + if onNewEnodeMsgsFn != nil { + onNewMsgsFn = onNewEnodeMsgsFn + } + return &avs{ + logger: log.New("module", "AnnounceVersionSharer"), + aWallets: aWallets, + network: network, + state: state, + ovcp: ovcp, + ecertGenerator: ecertGenerator, + ecertHolder: ecertHolder, + onNewMsgsFn: onNewMsgsFn, + } +} + +type avs struct { + logger log.Logger + + aWallets *atomic.Value + + network Network + + state *AnnounceState + + ovcp OutboundVersionCertificateProcessor + + ecertGenerator EnodeCertificateMsgGenerator + ecertHolder EnodeCertificateMsgHolder + + onNewMsgsFn OnNewEnodeCertsMsgSentFn +} + +func (a *avs) ShareVersion(version uint) error { + // Send new versioned enode msg to all other registered or elected validators + validatorConnSet, err := a.network.RetrieveValidatorConnSet() + if err != nil { + return err + } + + w := a.aWallets.Load().(*istanbul.Wallets) + + // Don't send any of the following messages if this node is not in the validator conn set + if !validatorConnSet[w.Ecdsa.Address] { + a.logger.Trace("Not in the validator conn set, not updating announce version") + return nil + } + + enodeCertificateMsgs, err := a.ecertGenerator.GenerateEnodeCertificateMsgs(&w.Ecdsa, version) + if err != nil { + return err + } + + if len(enodeCertificateMsgs) > 0 { + if err := a.ecertHolder.Set(enodeCertificateMsgs); err != nil { + a.logger.Error("Error in SetEnodeCertificateMsgMap", "err", err) + return err + } + } + + valConnArray := make([]common.Address, 0, len(validatorConnSet)) + for address := range validatorConnSet { + valConnArray = append(valConnArray, address) + } + + for _, enodeCertMsg := range enodeCertificateMsgs { + var destAddresses []common.Address + if enodeCertMsg.DestAddresses != nil { + destAddresses = enodeCertMsg.DestAddresses + } else { + // Send to all of the addresses in the validator connection set + destAddresses = valConnArray + } + + payload, err := enodeCertMsg.Msg.Payload() + if err != nil { + a.logger.Error("Error getting payload of enode certificate message", "err", err) + return err + } + + if err := a.network.Multicast(destAddresses, payload, istanbul.EnodeCertificateMsg, false); err != nil { + return err + } + } + + a.onNewMsgsFn(enodeCertificateMsgs) + + // Generate and gossip a new version certificate + newVersionCertificate, err := istanbul.NewVersionCertificate(version, w.Ecdsa.Sign) + if err != nil { + return err + } + return a.ovcp.Process( + a.state, + []*istanbul.VersionCertificate{ + newVersionCertificate, + }, + w.Ecdsa.Address, + ) +}
diff --git go-ethereum/consensus/istanbul/core/roundchangeset_v2.go celo/consensus/istanbul/core/roundchangeset_v2.go new file mode 100644 index 0000000000000000000000000000000000000000..c0527df914a587ae912687a706ca71a083a22fcc --- /dev/null +++ celo/consensus/istanbul/core/roundchangeset_v2.go @@ -0,0 +1,226 @@ +package core + +import ( + "fmt" + "math/big" + "sort" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +func newRoundChangeSetV2(valSet istanbul.ValidatorSet) *roundChangeSetV2 { + return &roundChangeSetV2{ + validatorSet: valSet, + msgsForRound: make(map[uint64]MessageSet), + latestRoundForVal: make(map[common.Address]uint64), + mu: new(sync.Mutex), + } +} + +type roundChangeSetV2 struct { + validatorSet istanbul.ValidatorSet + msgsForRound map[uint64]MessageSet + latestRoundForVal map[common.Address]uint64 + mu *sync.Mutex +} + +// RoundChangeSetSummary holds a print friendly view of a RoundChangeSet. +type RoundChangeSetSummary struct { + RoundForVal map[common.Address]uint64 `json:"roundForVal"` + ValsInRound map[uint64][]common.Address `json:"valsInRound"` +} + +// Summary returns a print friendly summary of the messages in the set. +func (rcs *roundChangeSetV2) Summary() *RoundChangeSetSummary { + rcs.mu.Lock() + defer rcs.mu.Unlock() + rounds := make(map[common.Address]uint64) + for v, r := range rcs.latestRoundForVal { + rounds[v] = r + } + vals := make(map[uint64][]common.Address) + for r, vs := range rcs.msgsForRound { + vs2 := make([]common.Address, 0, vs.Size()) + for _, v := range vs.Values() { + vs2 = append(vs2, v.Address) + } + vals[r] = vs2 + } + return &RoundChangeSetSummary{ + RoundForVal: rounds, + ValsInRound: vals, + } +} + +// Add adds the round and message into round change set +func (rcs *roundChangeSetV2) Add(r *big.Int, msg *istanbul.Message) error { + rcs.mu.Lock() + defer rcs.mu.Unlock() + + src := msg.Address + round := r.Uint64() + + if prevLatestRound, ok := rcs.latestRoundForVal[src]; ok { + if prevLatestRound > round { + // Reject as we have an RC for a later round from this validator. + return errOldMessage + } else if prevLatestRound < round { + // Already got an RC for an earlier round from this validator. + // Forget that and remember this. + if rcs.msgsForRound[prevLatestRound] != nil { + rcs.msgsForRound[prevLatestRound].Remove(src) + if rcs.msgsForRound[prevLatestRound].Size() == 0 { + delete(rcs.msgsForRound, prevLatestRound) + } + } + } + } + + rcs.latestRoundForVal[src] = round + + if rcs.msgsForRound[round] == nil { + rcs.msgsForRound[round] = newMessageSet(rcs.validatorSet) + } + return rcs.msgsForRound[round].Add(msg) +} + +// Clear deletes the messages with smaller round +func (rcs *roundChangeSetV2) Clear(round *big.Int) { + rcs.mu.Lock() + defer rcs.mu.Unlock() + + for k, rms := range rcs.msgsForRound { + if rms.Size() == 0 || k < round.Uint64() { + for _, msg := range rms.Values() { + delete(rcs.latestRoundForVal, msg.Address) // no need to check if msg.Address is present + } + delete(rcs.msgsForRound, k) + } + } +} + +// MaxRound returns the max round which the number of messages is equal or larger than num +func (rcs *roundChangeSetV2) MaxRound(num int) *big.Int { + rcs.mu.Lock() + defer rcs.mu.Unlock() + + // Sort rounds descending + var sortedRounds []uint64 + for r := range rcs.msgsForRound { + sortedRounds = append(sortedRounds, r) + } + sort.Slice(sortedRounds, func(i, j int) bool { return sortedRounds[i] > sortedRounds[j] }) + + acc := 0 + for _, r := range sortedRounds { + rms := rcs.msgsForRound[r] + acc += rms.Size() + if acc >= num { + return new(big.Int).SetUint64(r) + } + } + + return nil +} + +// MaxOnOneRound returns the max round which the number of messages is >= num +func (rcs *roundChangeSetV2) MaxOnOneRound(num int) *big.Int { + rcs.mu.Lock() + defer rcs.mu.Unlock() + + // Sort rounds descending + var sortedRounds []uint64 + for r := range rcs.msgsForRound { + sortedRounds = append(sortedRounds, r) + } + sort.Slice(sortedRounds, func(i, j int) bool { return sortedRounds[i] > sortedRounds[j] }) + + for _, r := range sortedRounds { + rms := rcs.msgsForRound[r] + if rms.Size() >= num { + return new(big.Int).SetUint64(r) + } + } + return nil +} + +func (rcs *roundChangeSetV2) String() string { + rcs.mu.Lock() + defer rcs.mu.Unlock() + + // Sort rounds descending + var sortedRounds []uint64 + for r := range rcs.msgsForRound { + sortedRounds = append(sortedRounds, r) + } + sort.Slice(sortedRounds, func(i, j int) bool { return sortedRounds[i] > sortedRounds[j] }) + + modeRound := uint64(0) + modeRoundSize := 0 + msgsForRoundStr := make([]string, 0, len(sortedRounds)) + for _, r := range sortedRounds { + rms := rcs.msgsForRound[r] + if rms.Size() > modeRoundSize { + modeRound = r + modeRoundSize = rms.Size() + } + msgsForRoundStr = append(msgsForRoundStr, fmt.Sprintf("%v: %v", r, rms.String())) + } + + return fmt.Sprintf("RCS len=%v mode_round=%v mode_round_len=%v unique_rounds=%v %v", + len(rcs.latestRoundForVal), + modeRound, + modeRoundSize, + len(rcs.msgsForRound), + strings.Join(msgsForRoundStr, ", ")) +} + +// Gets a round change certificate for a specific round. Includes quorumSize messages of that round or later. +// If the total is less than quorumSize, returns an empty cert and errFailedCreateRoundChangeCertificate. +func (rcs *roundChangeSetV2) getCertificate(minRound *big.Int, + quorumSize int) (istanbul.RoundChangeCertificateV2, map[common.Hash]istanbul.Proposal, error) { + + rcs.mu.Lock() + defer rcs.mu.Unlock() + + proposals := make(map[common.Hash]istanbul.Proposal) + + // Sort rounds descending + var sortedRounds []uint64 + for r := range rcs.msgsForRound { + sortedRounds = append(sortedRounds, r) + } + sort.Slice(sortedRounds, func(i, j int) bool { return sortedRounds[i] > sortedRounds[j] }) + + var requests []istanbul.RoundChangeRequest + for _, r := range sortedRounds { + if r < minRound.Uint64() { + break + } + for _, message := range rcs.msgsForRound[r].Values() { + rv2 := message.RoundChangeV2() + if rv2.Request.HasPreparedCertificate() { + if rv2.Request.PreparedCertificateV2.ProposalHash != rv2.PreparedProposal.Hash() { + // This should never happen, since it should have been checked when + // received in handleRoundChangeV2 + // Still, makes sense as an extra sanity check. + return istanbul.RoundChangeCertificateV2{}, nil, errRoundChangeProposalHashMismatch + } + proposals[rv2.Request.PreparedCertificateV2.ProposalHash] = rv2.PreparedProposal + } + requests = append(requests, rv2.Request) + // Stop when we've added a quorum of the highest-round messages. + if len(requests) >= quorumSize { + return istanbul.RoundChangeCertificateV2{ + Requests: requests, + }, proposals, nil + } + } + } + + // Didn't find a quorum of messages. Return an empty certificate with error. + return istanbul.RoundChangeCertificateV2{}, nil, errFailedCreateRoundChangeCertificate +}
diff --git go-ethereum/consensus/istanbul/core/roundstate_db_test.go celo/consensus/istanbul/core/roundstate_db_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2185a9b1cd3e6e9e67d2d5b76f51ff8529f2c30f --- /dev/null +++ celo/consensus/istanbul/core/roundstate_db_test.go @@ -0,0 +1,296 @@ +package core + +import ( + "bytes" + "encoding/hex" + "math/rand" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/rlp" + "github.com/stretchr/testify/assert" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" +) + +func mockViewMsg(view *istanbul.View, code uint64, addr common.Address) *istanbul.Message { + var msg []byte = []byte{} + subj := &istanbul.Subject{ + View: view, + Digest: common.BytesToHash([]byte("1234567890")), + } + if code == istanbul.MsgPrepare { + msg, _ = rlp.EncodeToBytes(subj) + } + if code == istanbul.MsgCommit { + cs := &istanbul.CommittedSubject{ + Subject: subj, + CommittedSeal: []byte("commSeal"), + EpochValidatorSetSeal: []byte("epochVS"), + } + msg, _ = rlp.EncodeToBytes(cs) + } + return &istanbul.Message{ + Code: code, + Address: addr, + Msg: msg, + Signature: []byte("sigg"), + } +} + +func TestRSDBRoundStateDB(t *testing.T) { + pubkey1 := blscrypto.SerializedPublicKey{1, 2, 3} + pubkey2 := blscrypto.SerializedPublicKey{3, 1, 4} + dummyRoundState := func() RoundState { + valSet := validator.NewSet([]istanbul.ValidatorData{ + {Address: common.BytesToAddress([]byte(string(rune(2)))), BLSPublicKey: pubkey1}, + {Address: common.BytesToAddress([]byte(string(rune(4)))), BLSPublicKey: pubkey2}, + }) + view := newView(2, 1) + rs := newRoundState(view, valSet, valSet.GetByIndex(0)) + rs.AddPrepare(mockViewMsg(view, istanbul.MsgPrepare, valSet.GetByIndex(0).Address())) + rs.AddCommit(mockViewMsg(view, istanbul.MsgCommit, valSet.GetByIndex(0).Address())) + rs.AddParentCommit(mockViewMsg(view, istanbul.MsgCommit, valSet.GetByIndex(0).Address())) + return rs + } + + t.Run("Should save view & roundState", func(t *testing.T) { + rsdb, _ := newRoundStateDB("", &RoundStateDBOptions{withGarbageCollector: false}) + rs := dummyRoundState() + err := rsdb.UpdateLastRoundState(rs) + finishOnError(t, err) + + view, err := rsdb.GetLastView() + finishOnError(t, err) + assertEqualView(t, view, rs.View()) + + savedRs, err := rsdb.GetRoundStateFor(view) + finishOnError(t, err) + assertEqualRoundState(t, savedRs, rs) + }) + + t.Run("Should save rcvd messages", func(t *testing.T) { + rsdb, _ := newRoundStateDB("", &RoundStateDBOptions{withGarbageCollector: false}) + rs := dummyRoundState() + rs.Prepares().Add( + mockViewMsg( + rs.View(), istanbul.MsgPrepare, rs.ValidatorSet().GetByIndex(1).Address(), + ), + ) + + // prepares: 2(0, 1), commits: 1(0), parentCommits: 1(0) + assert.Nil(t, rsdb.UpdateLastRoundState(rs)) + + // Delete messages to be reconstructed from rcvd + // Prepares will have 1 less + // Commits will have 1 more + // ParentCommits will be equal as before + // save the msg for comparing later + prepRemoved := rs.Prepares().Values()[0] + rs.Prepares().Remove(prepRemoved.Address) + rs.Commits().Add(mockViewMsg(rs.View(), istanbul.MsgCommit, rs.ValidatorSet().GetByIndex(1).Address())) + + err := rsdb.UpdateLastRcvd(rs) + finishOnError(t, err) + + view, err := rsdb.GetLastView() + finishOnError(t, err) + assertEqualView(t, view, rs.View()) + + savedRs, err := rsdb.GetRoundStateFor(view) + assert.NoError(t, err) + assert.NotNil(t, savedRs) + finishOnError(t, err) + + // ReAdd to the original RoundState + rs.Prepares().Add(prepRemoved) + // Commits should both have the new amount + assertEqualRoundState(t, savedRs, rs) + + // Add one more ParentCommit + newPc := mockViewMsg(view, istanbul.MsgCommit, rs.ValidatorSet().GetByIndex(1).Address()) + rs.ParentCommits().Add(newPc) + + err = rsdb.UpdateLastRcvd(rs) + finishOnError(t, err) + + savedRs2, err := rsdb.GetRoundStateFor(view) + assert.NoError(t, err) + assert.NotNil(t, savedRs2) + finishOnError(t, err) + + assertEqualRoundState(t, savedRs2, rs) + }) + + t.Run("Should save view from last saved roundState", func(t *testing.T) { + rsdb, _ := newRoundStateDB("", &RoundStateDBOptions{withGarbageCollector: false}) + rs := dummyRoundState() + err := rsdb.UpdateLastRoundState(rs) + finishOnError(t, err) + rs.StartNewSequence(common.Big32, rs.ValidatorSet(), rs.ValidatorSet().GetByIndex(1), rs.ParentCommits()) + err = rsdb.UpdateLastRoundState(rs) + finishOnError(t, err) + + view, err := rsdb.GetLastView() + finishOnError(t, err) + assertEqualView(t, view, rs.View()) + }) + +} + +func TestRSDBDeleteEntriesOlderThan(t *testing.T) { + pubkey1 := blscrypto.SerializedPublicKey{1, 2, 3} + pubkey2 := blscrypto.SerializedPublicKey{3, 1, 4} + createRoundState := func(view *istanbul.View) RoundState { + valSet := validator.NewSet([]istanbul.ValidatorData{ + {Address: common.BytesToAddress([]byte(string(rune(2)))), BLSPublicKey: pubkey1}, + {Address: common.BytesToAddress([]byte(string(rune(4)))), BLSPublicKey: pubkey2}, + }) + return newRoundState(view, valSet, valSet.GetByIndex(0)) + } + + rsdb, _ := newRoundStateDB("", &RoundStateDBOptions{withGarbageCollector: false}) + for seq := uint64(1); seq <= 10; seq++ { + for r := uint64(0); r < 10; r++ { + rs := createRoundState(newView(seq, r)) + err := rsdb.UpdateLastRoundState(rs) + finishOnError(t, err) + } + } + + // Will delete all entries from seq 1 + count, err := rsdb.(*roundStateDBImpl).deleteEntriesOlderThan(newView(2, 0)) + if err != nil { + t.Fatalf("Error %v", err) + } + if count != 10 { + t.Fatalf("Expected 10 deleted entries but got %d", count) + } + + // Will delete all entries from seq 2,3 and seq 4 until round 5 + count, err = rsdb.(*roundStateDBImpl).deleteEntriesOlderThan(newView(4, 5)) + if err != nil { + t.Fatalf("Error %v", err) + } + if count != 25 { + t.Fatalf("Expected 10 deleted entries but got %d", count) + } + +} + +func TestRcvdSerialization(t *testing.T) { + valSet := newTestValidatorSet(0) + r := rcvd{ + Prepares: newMessageSet(valSet), + Commits: newMessageSet(valSet), + ParentCommits: newMessageSet(valSet), + } + rRLP, err := r.ToRLP() + assert.NoError(t, err) + bytes, err := rlp.EncodeToBytes(rRLP) + assert.NoError(t, err) + var r2RLP *rcvdRLP = &rcvdRLP{} + err = rlp.DecodeBytes(bytes, r2RLP) + assert.NoError(t, err) + var r2 rcvd + err = r2.FromRLP(r2RLP) + assert.NoError(t, err) + assert.Equal(t, r, r2) +} + +func TestRSDBKeyEncodingOrder(t *testing.T) { + iterations := 1000 + + t.Run("ViewKey encoding should decode the same view", func(t *testing.T) { + for i := 0; i < iterations; i++ { + view := newView(rand.Uint64(), rand.Uint64()) + key := view2Key(view) + parsedView := key2View(key) + if view.Cmp(parsedView) != 0 { + t.Errorf("parsedView != view: %v != %v", parsedView, view) + } + } + }) + + t.Run("ViewKey encoding should maintain sort order", func(t *testing.T) { + for i := 0; i < iterations; i++ { + viewA := newView(rand.Uint64(), rand.Uint64()) + keyA := view2Key(viewA) + + viewB := newView(rand.Uint64(), rand.Uint64()) + keyB := view2Key(viewB) + + if viewA.Cmp(viewB) != bytes.Compare(keyA, keyB) { + t.Errorf("view order != key order (viewA: %v, viewB: %v, keyA:%v, keyB:%v )", + viewA, + viewB, + hex.EncodeToString(keyA), + hex.EncodeToString(keyB), + ) + + } + } + }) + + t.Run("RcvdViewKey encoding should maintain sort order", func(t *testing.T) { + for i := 0; i < iterations; i++ { + viewA := newView(rand.Uint64(), rand.Uint64()) + keyA := rcvdView2Key(viewA) + + viewB := newView(rand.Uint64(), rand.Uint64()) + keyB := rcvdView2Key(viewB) + + if viewA.Cmp(viewB) != bytes.Compare(keyA, keyB) { + t.Errorf("view order != key order (viewA: %v, viewB: %v, keyA:%v, keyB:%v )", + viewA, + viewB, + hex.EncodeToString(keyA), + hex.EncodeToString(keyB), + ) + + } + } + }) +} + +func TestRSDBGetOldestValidView(t *testing.T) { + pubkey1 := blscrypto.SerializedPublicKey{1, 2, 3} + pubkey2 := blscrypto.SerializedPublicKey{3, 1, 4} + valSet := validator.NewSet([]istanbul.ValidatorData{ + {Address: common.BytesToAddress([]byte(string(rune(2)))), BLSPublicKey: pubkey1}, + {Address: common.BytesToAddress([]byte(string(rune(4)))), BLSPublicKey: pubkey2}, + }) + sequencesToSave := uint64(100) + runTestCase := func(name string, viewToStore, expectedView *istanbul.View) { + t.Run(name, func(t *testing.T) { + rsdb, _ := newRoundStateDB("", &RoundStateDBOptions{ + withGarbageCollector: false, + sequencesToSave: sequencesToSave, + }) + + if viewToStore != nil { + t.Logf("Saving RoundState") + err := rsdb.UpdateLastRoundState(newRoundState(viewToStore, valSet, valSet.GetByIndex(0))) + if err != nil { + t.Fatalf("UpdateLastRoundState error: %v", err) + } + } + + view, err := rsdb.GetOldestValidView() + if err != nil { + t.Fatalf("GetOldestValidView error: %v", err) + } + if view.Cmp(expectedView) != 0 { + t.Errorf("Expected %v, got %v", expectedView, view) + } + }) + } + + runTestCase("When Nothing Stored", nil, newView(0, 0)) + runTestCase("When StoredSequence < sequencesToSave", newView(sequencesToSave-1, 90), newView(0, 0)) + runTestCase("When StoredSequence == sequencesToSave", newView(sequencesToSave, 90), newView(0, 0)) + runTestCase("When StoredSequence > sequencesToSave", newView(sequencesToSave+1, 90), newView(1, 0)) + runTestCase("When StoredSequence >> sequencesToSave", newView(sequencesToSave+1000, 90), newView(1000, 0)) +}
diff --git go-ethereum/consensus/istanbul/backend/announce.md celo/consensus/istanbul/backend/announce.md new file mode 100644 index 0000000000000000000000000000000000000000..3aa5758de76e4fed0402d575f15cd38cd9b90d77 --- /dev/null +++ celo/consensus/istanbul/backend/announce.md @@ -0,0 +1,224 @@ +# Celo Announce Protocol + +While here referred as a protocol by itself, Announce is technically a subset of the Istanbul protocol, on the [RLPx] transport, that facilitates Validators to privately discover the `eNodeURL` of other Validators in the network, using the underlying p2p eth network. This is used by Istanbul to have direct connections between Validators, speeding up the consensus phase of block generation. + +It is important to note that a validator's `eNodeURL` is never shared among third parties, but directly published to specific peers. Therefore it is allowed for a validator to publish different external facing `eNodeURL` values to different validator peers. + +To clarify, the `eNodeURL` value itself is public, since it's used in the p2p network. What is NOT public, and the announce protocol helps in sharing among validators, is the _mapping_ between a validator and its `eNodeURL`. That is, p2p full nodes don't know (and should not know) if a peer is a validator or not. + +## Terminology + +For the purpose of this specification, certain terms are used that have a specific meaning, those are: + +* Full node +* Validator +* Nearly Elected Validator +* eNodeURL + +Note that node labels (Full node, Validator, Nearly Elected Validator) are not set in stone. A Full node can be restarted into a Validator, and a Validator can become a Nearly Elected Validator with enough votes from the election contract. + +### Full Node + +A Full Node is a Celo node fully operating in the Celo p2p network. + +### Validator + +A Validator is a [FullNode] that has been started with the ability to produce blocks, even if it has not yet been elected. In the geth program, +this is equivalent as being started with the `--mine` param. + +### Nearly Elected Validator (NEV) + +A node is a Nearly Elected Validator (NEV) if and only if: + +1) It is a [Validator] +2) It is in the result set from calling `ElectNValidatorSigners` with `additionalAboveMaxElectable = 10` + +In loose terms, it means it's a validator that has a good chance of becoming an elected validator in the following epoch. + +### eNodeURL + +An `eNodeURL` is a string specifyng the location of an eNode in the p2p network (//TODO add link to what an enode is?), and it has the following format: + +`enode://<hex node id>@<IP/hostname>:<tcp port>?discport=<udp discovery port>` + +Where `<hex node id>` is the node's serialized and hex encoded ecdsa public key. The URL parameter `discport` should only be specified if the udp discovery port differs from the tcp port. + +Some example `eNodeURLs` (with partially elided hex encoded public keys): + +``` +enode://517318.......607ff3@127.0.0.1:34055 + +enode://e5e2fdf.......348ce8@127.0.0.1:33503?discport=22042 +``` + +## Objective + +The Announce protocol objective is to allow all [NearlyElectedValidator] to maintain an updated table of `validatorAddress -> eNodeURL` for all other [NearlyElectedValidator], while somewhat concealing these values from the rest of the nodes in the network, and also avoiding an unnecessary high message traffic to achieve it. [NearlyElectedValidator] nodes can use the information from this table to open direct connections between themselves, speeding up the consensus phase. + +To ensure that validator [eNodeURL] tables don't get stale, each validator's [eNodeURL] is paired with a version number. [NearlyElectedValidator] nodes should ignore [eNodeURL] entries with values from versions older than one currently in their table. The current convention used is that versions are unix timestamps from the moment of the update. + +As part of the protocol's design, a [NearlyElectedValidator] can advertise different [eNodeURL] values for different destinations. This is important since validators can live behind multiple proxies and thus have more than one [eNodeURL]. That being said, the announce protocol itself is agnostic to the concept of proxies, it cares only for the sharing of `<validator address, validator eNodeUrl>` mapping tuples. It is the proxy implementation's responsibility to ensure the correct behavior of this specification. + +### Concealing eNodeURL values + +It is important to ensure that validator [eNodeURL] values can't be discovered by nodes that are not in the [NearlyElectedValidator] set. To achieve this, they are shared in one of two ways: + +* Sent through a direct p2p connection from one [NearlyElectedValidator] to another ( [enodeCertificateMsg] ) +* Gossipped through the network, but encrypted with the public key of the recipient [NearlyElectedValidator] ( [queryEnodeMsg] ) + + +### Minimizing network traffic + +In order to reduce the amount of message flooding, every [FullNode] participating in the p2p network must maintain a version table for the [NearlyElectedValidator] nodes in the network. This table is built and updated with one specific message from the protocol ( [versionCertificatesMsg] ). + +[NearlyElectedValidator] can also use this table to understand which [eNodeURL] entries are stale, if the [eNodeURL] table entry has a version older than the version table entry. + +## Basic Operation + +When a validator is close to being elected ([NearlyElectedValidator]), it starts periodically sending [queryEnodeMsg] messages through the p2p network. These message are regossipped by nodes, and validators reply with a direct message to the originator with an [eNodeCertificateMsg], holding their `eNodeURL`. The initial [queryEnodeMsg] contained the origin validator's `eNodeURL` encrypted with the destination public key, therefore after the direct reply, both validators are aware of each others' `eNodeURL`. + +### p2p connection management + +Wether a validator opens a connection or not against another validator is not in the scope of this spec, but only how and when should the protocol messages should be sent, replied and regossipped. For example, it IS part of the spec what messages should be sent to a newly connected peer or validator peer. + +## Protocol Messages (RLP) + +All messages presented here are Istanbul messages, following the same format as the consensus messages (see `istanbul.Message`), which are serialized in the `payload` field for standard p2p messages. + +`[code: P, msg: B, sender_address: B_20, signature: B]` + +- `code`: the message code (0x12, 0x16, or 0x17). +- `msg`: rlp encoded data, depending on the message type (`code`), and detailed in the following subsections. +- `sender_address`: the originator of the message. +- `signature`: the message signature. + +// TODO: double check RLP types + +### queryEnodeMsg (0x12) + +This message is sent from a [NearlyElectedValidator] and regossipped through the network, with the intention of reach out for +other [NearlyElectedValidator] nodes and discover their [eNodeURL] values, while at the same time sending its own. Note that +the sender sends many queries in one message. A query is a pair `<destination address, encrypted eNodeURL>` so that only the +destination address can decrypt the encrypted [eNodeURL] in it. + +`[[dest_address: B_20, encrypted_enode_url: B], version: P, timestamp: P]` + +- `dest_address`: the destination validator address this message is querying to. +- `encrypted_enode_url`: the origin's `eNodeURL` encrypted with the destination public key. +- `version`: the current announce version for the origin's `eNodeURL`. +- `timestamp`: a message generation timestamp, used to bypass hash caches for messages. + +Max amount of encrypted node urls is 2 * size(set of [NearlyElectedValidator]). + +### enodeCertificateMsg (0x17) + +This message holds only ONE [eNodeURL] and it's meant to be send directly from [NearlyElectedValidator] to [NearlyElectedValidator] in a direct +connection as a response to a [queryEnodeMsg]. + +`[enode_url: B, version: P]` + +- `enode_url`: the origin's plaintext `eNodeURL`. +- `version`: the current announce version for the origin's `eNodeURL`. + +### versionCertificatesMsg (0x16) + +This messages holds MANY version certificates. It is used mostly in two ways: + +- To share the WHOLE version table a [FullNode] has. +- To share updated parts of the table of a [FullNode]. + +`[[version: P, signature: B]]` + +- `version`: the current highest known announce version for a validator (deduced from the signature), according to the peer that gossipped this message. +- `signature`: the signature for the version payload string (`'versionCertificate'|version`) by the emmitter of the certificate. Note that the address and public key can be deduced from this `signature`. + +## Spec by node type + +Note that validators are full nodes too, therefore they should follow the same rules as full nodes. This is specially important to not give away the status of the node as a validator. + +### Full node (non validator) Spec + +#### Peer registered + +When a peer is registered, the whole version table should be sent to the peer in a [versionCertificatesMsg]. + +#### Handling [queryEnodeMsg] + +Messages received of this type should be only processed once. +Should be regossipped as is, unless another message from the same validator origin has been regossipped in the past 5 minutes. + +#### Handling [enodeCertificateMsg] + +[enodeCertificateMsg] should be ignored by [FullNode] instances, since they should never receive one if all other participants are behaving properly. We make +this explicit in the spec since the regossipping of this particular message would make the [eNodeURL] of the sender public, which defeats the purpose of the +protocol. While this should never happen, there are some border cases where a node can be a [Validator] with queries yet unanswered, and then restarted into a [FullNode], thus receiving query replies that should no longer apply to it. + +#### Handling [versionCertificatesMsg] + +Messages received of this type should be only processed once. +When received, it should upsert its version table, and then multicast to its peers a new filtered [versionCertificatesMsg] without the certificates that were not updated in the table, and also filtering out certificates if another one for the same [NearlyElectedValidator] has been gossiped in the past 5 minutes. + +#### Message spawning + +Every 5 minutes it should multicast its version certificate table to all peers. + +### Validator Spec + +#### Peer handshake + +When a [NearlyElectedValidator] connects to another [NearlyElectedValidator] (identified by being present at the [eNodeURL] table), the initiator peer can send an [enodeCertificateMsg] to identify itself as a [NearlyElectedValidator]. This allows for preferential treatment for the p2p connection. + +This can happen for example if: Say `A` and `B` are both [NearlyElectedValidator] nodes and not directly connected in the p2p network. `A` sends a [queryEnodeMsg] to the network, `B` receives it and therefore decides to directly open a p2p connection to `A`. As soon as the conection is registered, it identifies itself by sending the [enodeCertificateMsg] to `A`. + +#### Handling [queryEnodeMsg] as a Validator + +If: + +* this [Validator] is a [NearlyElectedValidator] +* there is an entry in the [queryEnodeMsg] addressed to this [Validator] + +Then this [Validator] should upsert the [eNodeURL] received. + +If, in addition, this [Validator] is already connected to the sender of the query, it should reply with an [enodeCertificateMsg] directly. + +Regardless of all that, the message should be regossipped through the message like any [FullNode] would do. + +#### Receiving [enodeCertificateMsg] + +Should upsert the [eNodeURL] received in the local [eNodeURL] table. + +#### Query spawning + +One minute (60 seconds) after a validator enters the set of [NearlyElectedValidator], it should start sending [queryEnodeMsg] messages to the network, for all other [NearlyElectedValidator] nodes that have a higher version `eNodeURL` than the one currently known. + +Messages should be spaced at least 5 minutes (300 seconds) apart. A query for a specific `<validator, version>` tuple has a retry back off period. The `nth` attempt should be spaced from the previous one by: + +``` +timeoutMinutes = 1.5 ^ (min(n - 1, 5)) +``` + +Optionally, the first ten (10) query messages can be spaced by at least 60 seconds and ignoring retry back off periods for unanswered queries. This is known as the `AggressiveQueryEnodeGossip`. + +If the [Validator] is no longer in the list of [NearlyElectedValidator] then it should stop sending [queryEnodeMsg] messages. + +#### Version certificates spawning + +When a [Validator] enters the [NearlyElectedValidator] set, it should update its [eNodeURL] version, gossip a new [versionCertificatesMsg] with its new certificate. After that, it should be renewed and regossipped every 5 minutes, until its removed from the [NearlyElectedValidator] set. + +## Change Log + +### Previous relevant PRs + +https://github.com/ethereum/go-ethereum/pull/816 + +https://github.com/ethereum/go-ethereum/pull/873 + +https://github.com/ethereum/go-ethereum/pull/893 + +[queryEnodeMsg]: #queryEnodeMsg-0x12 +[versionCertificatesMsg]: #versionCertificatesMsg-0x16 +[enodeCertificateMsg]: #enodeCertificateMsg-0x17 +[NearlyElectedValidator]: #nearly-Elected-Validator-NEV +[Validator]: #validator +[FullNode]: #full-node +[eNodeURL]: #eNodeURL
diff --git go-ethereum/consensus/istanbul/proxy/validator_assignment.go celo/consensus/istanbul/proxy/validator_assignment.go new file mode 100644 index 0000000000000000000000000000000000000000..9476b4072b7f9c4710e05172000451a0fc62e684 --- /dev/null +++ celo/consensus/istanbul/proxy/validator_assignment.go @@ -0,0 +1,228 @@ +// Copyright 2017 The celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "github.com/buraksezer/consistent" + "github.com/cespare/xxhash/v2" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// This type stores the assignment of remote validators to proxies, as well as the +// reverse assignment. +// If a validator is assigned to a nil proxy, then that means that it's +// not assigned yet. +// WARNING: None of this object's functions are threadsafe, so it's +// the user's responsibility to ensure that. +type valAssignments struct { + valToProxy map[common.Address]*enode.ID // map of validator address -> proxy assignment ID + proxyToVals map[enode.ID]map[common.Address]struct{} // map of proxy ID to set of validator addresses + logger log.Logger +} + +func newValAssignments() *valAssignments { + return &valAssignments{ + valToProxy: make(map[common.Address]*enode.ID), + proxyToVals: make(map[enode.ID]map[common.Address]struct{}), + logger: log.New(), + } +} + +// addValidators adds validators to valToProxy without an assigned proxy +func (va *valAssignments) addValidators(vals []common.Address) { + logger := va.logger.New("func", "addValidators") + logger.Info("adding validators to val assignments", "new vals", common.ConvertToStringSlice(vals)) + for _, val := range vals { + va.valToProxy[val] = nil + } +} + +// removeValidators removes validators from any proxy assignments and deletes +// them from valToProxy +func (va *valAssignments) removeValidators(vals []common.Address) { + logger := va.logger.New("func", "removeValidators") + logger.Info("removing validators from val assignments", "removed vals", common.ConvertToStringSlice(vals)) + for _, val := range vals { + va.unassignValidator(val) + delete(va.valToProxy, val) + } +} + +// assignValidator assigns a validator with address valAddress to the proxy +// with ID proxyID +func (va *valAssignments) assignValidator(valAddress common.Address, proxyID enode.ID) { + va.valToProxy[valAddress] = &proxyID + + if _, ok := va.proxyToVals[proxyID]; !ok { + va.proxyToVals[proxyID] = make(map[common.Address]struct{}) + } + + va.proxyToVals[proxyID][valAddress] = struct{}{} +} + +// unassignValidator unassigns a validator with address valAddress from +// its proxy. If it was never assigned, this does nothing +func (va *valAssignments) unassignValidator(valAddress common.Address) { + proxyID := va.valToProxy[valAddress] + + if proxyID != nil { + va.valToProxy[valAddress] = nil + delete(va.proxyToVals[*proxyID], valAddress) + + if len(va.proxyToVals[*proxyID]) == 0 { + delete(va.proxyToVals, *proxyID) + } + } +} + +// getValidators returns all validator addresses that are found in valToProxy. +// Note that it will also return both assigned and unassigne validators. +func (va *valAssignments) getValidators() []common.Address { + vals := make([]common.Address, 0, len(va.valToProxy)) + + for val := range va.valToProxy { + vals = append(vals, val) + } + return vals +} + +// assignmentPolicy is intended to allow multiple implementations of validator assignment +// policies +type assignmentPolicy interface { + assignProxy(proxy *Proxy, valAssignments *valAssignments) bool + removeProxy(proxy *Proxy, valAssignments *valAssignments) bool + assignRemoteValidators(validators []common.Address, valAssignments *valAssignments) bool + removeRemoteValidators(validators []common.Address, valAssignments *valAssignments) bool +} + +// ============================================== +// +// define the consistent hashing assignment policy implementation + +type hasher struct{} + +func (h hasher) Sum64(data []byte) uint64 { + return xxhash.Sum64(data) +} + +// consistentHashingPolicy uses consistent hashing to assign validators to proxies. +// Validator <-> proxy pairings are recalculated every time a proxy or validator +// is added/removed +// WARNING: None of this object's functions are threadsafe, so it's +// the user's responsibility to ensure that. +type consistentHashingPolicy struct { + c *consistent.Consistent // used for consistent hashing + logger log.Logger +} + +func newConsistentHashingPolicy() *consistentHashingPolicy { + // This sets up a consistent hasher with bounded loads: + // https://ai.googleblog.com/2017/04/consistent-hashing-with-bounded-loads.html + // Partitions are assigned to members (proxies in this case). + // using a hash ring. + // When locating a validator's proxy using `LocateKey`, the validator is assigned + // to a partition using hash(validator's address) % PartitionCount in constant time. + cfg := consistent.Config{ + // Prime to distribute validators more uniformly. + // Higher partition count generally gives a more even distribution + PartitionCount: 271, + // The number of replications of a proxy on the hash ring + ReplicationFactor: 40, + // Used to enforce a max # of partitions assigned per proxy, which is + // (PartitionCount / len(proxies)) * Load. A load closer to 1 gives + // more uniformity in the # of partitions assigned to specific members, + // but a higher load results in less relocations when proxies are added/removed + Load: 1.2, + Hasher: hasher{}, + } + + return &consistentHashingPolicy{ + c: consistent.New(nil, cfg), + logger: log.New(), + } +} + +// assignProxy adds a proxy to the consistent hasher and recalculates all validator assignments +func (ch *consistentHashingPolicy) assignProxy(proxy *Proxy, valAssignments *valAssignments) bool { + ch.c.Add(proxy.ID()) + return ch.reassignValidators(valAssignments) +} + +// removeProxy removes a proxy from the consistent hasher and recalculates all validator assignments +func (ch *consistentHashingPolicy) removeProxy(proxy *Proxy, valAssignments *valAssignments) bool { + ch.c.Remove(proxy.ID().String()) + return ch.reassignValidators(valAssignments) +} + +// assignRemoteValidators adds remote validators to the valAssignments struct and recalculates +// all validator assignments +func (ch *consistentHashingPolicy) assignRemoteValidators(vals []common.Address, valAssignments *valAssignments) bool { + valAssignments.addValidators(vals) + return ch.reassignValidators(valAssignments) +} + +// removeRemoteValidators removes remote validators from the valAssignments struct and recalculates +// all validator assignments +func (ch *consistentHashingPolicy) removeRemoteValidators(vals []common.Address, valAssignments *valAssignments) bool { + valAssignments.removeValidators(vals) + return ch.reassignValidators(valAssignments) +} + +// reassignValidators recalculates all validator <-> proxy pairings +func (ch *consistentHashingPolicy) reassignValidators(valAssignments *valAssignments) bool { + logger := ch.logger.New("func", "reassignValidators") + anyAssignmentsChanged := false + for val, proxyID := range valAssignments.valToProxy { + newProxyID := ch.c.LocateKey(val.Bytes()) + + if newProxyID == nil { + logger.Trace("Unassigning validator", "validator", val) + valAssignments.unassignValidator(val) + anyAssignmentsChanged = true + } else if proxyID == nil || newProxyID.String() != proxyID.String() { + proxyIDStr := "nil" + if proxyID != nil { + proxyIDStr = proxyID.String() + } + logger.Trace("Reassigning validator", "validator", val, "original proxy", proxyIDStr, "new proxy", newProxyID.String()) + + valAssignments.unassignValidator(val) + valAssignments.assignValidator(val, enode.HexID(newProxyID.String())) + anyAssignmentsChanged = true + } + } + + if anyAssignmentsChanged { + outputMap := make(map[enode.ID][]string) + + for proxyID, validatorSet := range valAssignments.proxyToVals { + validatorSlice := make([]common.Address, 0, len(validatorSet)) + + for valAddress := range validatorSet { + validatorSlice = append(validatorSlice, valAddress) + } + + outputMap[proxyID] = common.ConvertToStringSlice(validatorSlice) + } + logger.Info("remote validator to proxy assignment has changed", "new assignment", outputMap) + } + + return anyAssignmentsChanged +}
diff --git go-ethereum/consensus/istanbul/core/types_test.go celo/consensus/istanbul/core/types_test.go new file mode 100644 index 0000000000000000000000000000000000000000..982149ffb521f412f4eef1796acf2c9e0768e458 --- /dev/null +++ celo/consensus/istanbul/core/types_test.go @@ -0,0 +1,168 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" +) + +func testPreprepare(t *testing.T) { + pp := &istanbul.PreprepareV2{ + View: &istanbul.View{ + Round: big.NewInt(1), + Sequence: big.NewInt(2), + }, + Proposal: makeBlock(1), + } + m := istanbul.NewPreprepareV2Message(pp, common.HexToAddress("0x1234567890")) + msgPayload, err := m.Payload() + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + decodedMsg := new(istanbul.Message) + err = decodedMsg.FromPayload(msgPayload, nil) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + decodedPP := decodedMsg.PreprepareV2() + // if block is encoded/decoded by rlp, we cannot to compare interface data type using reflect.DeepEqual. (like istanbul.Proposal) + // so individual comparison here. + if !reflect.DeepEqual(pp.Proposal.Hash(), decodedPP.Proposal.Hash()) { + t.Errorf("proposal hash mismatch: have %v, want %v", decodedPP.Proposal.Hash(), pp.Proposal.Hash()) + } + + if !reflect.DeepEqual(pp.View, decodedPP.View) { + t.Errorf("view mismatch: have %v, want %v", decodedPP.View, pp.View) + } + + if !reflect.DeepEqual(pp.Proposal.Number(), decodedPP.Proposal.Number()) { + t.Errorf("proposal number mismatch: have %v, want %v", decodedPP.Proposal.Number(), pp.Proposal.Number()) + } +} + +func testSubject(t *testing.T) { + s := &istanbul.Subject{ + View: &istanbul.View{ + Round: big.NewInt(1), + Sequence: big.NewInt(2), + }, + Digest: common.BytesToHash([]byte("1234567890")), + } + + m := istanbul.NewPrepareMessage(s, common.HexToAddress("0x1234567890")) + + msgPayload, err := m.Payload() + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + decodedMsg := new(istanbul.Message) + err = decodedMsg.FromPayload(msgPayload, nil) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + if !reflect.DeepEqual(s, decodedMsg.Prepare()) { + t.Errorf("subject mismatch: have %v, want %v", decodedMsg.Prepare(), s) + } +} + +func testSubjectWithSignature(t *testing.T) { + s := &istanbul.Subject{ + View: &istanbul.View{ + Round: big.NewInt(1), + Sequence: big.NewInt(2), + }, + Digest: common.BytesToHash([]byte("1234567890")), + } + correctKey, err := crypto.GenerateKey() + require.NoError(t, err) + correctAddress := crypto.PubkeyToAddress(correctKey.PublicKey) + signCorrect := func(data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), correctKey) + } + + spooferAddress := common.HexToAddress("0x2") + + // 1. Encode test + m := istanbul.NewPrepareMessage(s, correctAddress) + + err = m.Sign(signCorrect) + require.NoError(t, err) + msgPayload, err := m.Payload() + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + // 2. Decode test + // 2.1 Test normal validate func + decodedMsg := new(istanbul.Message) + err = decodedMsg.FromPayload(msgPayload, func(data []byte, sig []byte) (common.Address, error) { + return correctAddress, nil + }) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + if !reflect.DeepEqual(decodedMsg, m) { + t.Errorf("error mismatch: have %v, want nil", err) + } + + // 2.2 Test nil validate func + decodedMsg = new(istanbul.Message) + err = decodedMsg.FromPayload(msgPayload, nil) + if err != nil { + t.Error(err) + } + + if !reflect.DeepEqual(decodedMsg, m) { + t.Errorf("message mismatch: have %v, want %v", decodedMsg, m) + } + + // 2.3 Test failed validate func + decodedMsg = new(istanbul.Message) + err = decodedMsg.FromPayload(msgPayload, func(data []byte, sig []byte) (common.Address, error) { + return common.Address{}, istanbul.ErrUnauthorizedAddress + }) + if err != istanbul.ErrUnauthorizedAddress { + t.Errorf("error mismatch: have %v, want %v", err, istanbul.ErrUnauthorizedAddress) + } + + // 2.4 Test spoofing signature by another validator validate func + decodedMsg = new(istanbul.Message) + err = decodedMsg.FromPayload(msgPayload, func(data []byte, sig []byte) (common.Address, error) { + return spooferAddress, nil + }) + if err != istanbul.ErrInvalidSigner { + t.Errorf("error mismatch: have %v, want ErrInvalidSigner", err) + } +} + +func TestMessageEncodeDecode(t *testing.T) { + testPreprepare(t) + testSubject(t) + testSubjectWithSignature(t) +}
diff --git go-ethereum/consensus/istanbul/backend/random.go celo/consensus/istanbul/backend/random.go new file mode 100644 index 0000000000000000000000000000000000000000..6bab3cf08504b328f27923b94ffc9d93ef06996a --- /dev/null +++ celo/consensus/istanbul/backend/random.go @@ -0,0 +1,67 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/random" + "github.com/ethereum/go-ethereum/crypto" +) + +// String for creating the random seed +var randomSeedString = []byte("Randomness seed string") + +// GenerateRandomness will generate the random beacon randomness +func (sb *Backend) GenerateRandomness(parentHash common.Hash) (common.Hash, common.Hash, error) { + logger := sb.logger.New("func", "GenerateRandomness") + + // TODO(HF) check which state the vm runner should use (probably not current block's) + vmRunner, err := sb.chain.NewEVMRunnerForCurrentBlock() + if err != nil { + return common.Hash{}, common.Hash{}, nil + } + + if !random.IsRunning(vmRunner) { + return common.Hash{}, common.Hash{}, nil + } + + sb.randomSeedMu.Lock() + if sb.randomSeed == nil { + var err error + w := sb.wallets() + sb.randomSeed, err = w.Ecdsa.SignHash(common.BytesToHash(randomSeedString)) + if err != nil { + logger.Error("Failed to create randomSeed", "err", err) + sb.randomSeedMu.Unlock() + return common.Hash{}, common.Hash{}, err + } + } + sb.randomSeedMu.Unlock() + + randomness := crypto.Keccak256Hash(append(sb.randomSeed, parentHash.Bytes()...)) + + // The logic to compute the commitment via the randomness is in the random smart contract. + // That logic is stateless, so passing in any block header and state is fine. There is a TODO for + // that commitment computation logic to be removed fromthe random smart contract. + commitment, err := random.ComputeCommitment(vmRunner, randomness) + if err != nil { + logger.Error("Failed to compute commitment", "err", err) + return common.Hash{}, common.Hash{}, err + } + + return randomness, commitment, nil +}
diff --git go-ethereum/consensus/istanbul/core/commit_test.go celo/consensus/istanbul/core/commit_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b7374ceb1b89bb49c6bd66af5294a345f2969b34 --- /dev/null +++ celo/consensus/istanbul/core/commit_test.go @@ -0,0 +1,446 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/celo-org/celo-bls-go/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +func TestHandleCommit(t *testing.T) { + N := uint64(4) + F := uint64(1) + + // create block 4 + proposal := newTestProposalWithNum(4) + expectedSubject := &istanbul.Subject{ + View: &istanbul.View{ + Round: big.NewInt(0), + Sequence: proposal.Number(), + }, + Digest: proposal.Hash(), + } + + testCases := []struct { + system *testSystem + expectedErr error + checkParentCommits bool + }{ + { + // normal case + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + // same view as the expected one to everyone + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePrepared + } + } + return sys + }(), + nil, + false, + }, + { + // future message + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePreprepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + // proposal from 1 round in the future + Sequence: big.NewInt(0).Add(proposal.Number(), common.Big1), + }, + backend.peers, + ) + } + } + return sys + }(), + errFutureMessage, + false, + }, + { + // past message + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePreprepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + // we're 2 blocks before so this is indeed a + // very old proposal and will error as expected + // with an old error message + Sequence: big.NewInt(0).Sub(proposal.Number(), common.Big2), + }, + backend.peers, + ) + } + } + return sys + }(), + errOldMessage, + false, + }, + { + // jump state + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: proposal.Number(), + }, + backend.peers, + ) + + // only replica0 stays at StatePreprepared + // other replicas are at StatePrepared + if i != 0 { + c.current.(*roundStateImpl).state = StatePrepared + } else { + c.current.(*roundStateImpl).state = StatePreprepared + } + } + return sys + }(), + nil, + false, + }, + { + // message from previous sequence and round matching last proposal + // this should pass the message check, but will return an error in + // handleCheckedCommitForPreviousSequence, because the proposal hashes won't match. + func() *testSystem { + sys := NewTestSystemWithBackend(N, F) + + for i, backend := range sys.backends { + backend.Commit(newTestProposalWithNum(3), types.IstanbulAggregatedSeal{}, types.IstanbulEpochValidatorSetSeal{}, nil) + c := backend.engine.(*core) + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePrepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(1), + Sequence: big.NewInt(0).Sub(proposal.Number(), common.Big1), + }, + backend.peers, + ) + } + } + return sys + }(), + errInconsistentSubject, + true, + }, + // TODO: double send message + } + +OUTER: + for _, test := range testCases { + test.system.Run(false) + + v0 := test.system.backends[0] + r0 := v0.engine.(*core) + + for i, v := range test.system.backends { + validator := r0.current.ValidatorSet().GetByIndex(uint64(i)) + privateKey, _ := bls.DeserializePrivateKey(test.system.validatorsKeys[i]) + defer privateKey.Destroy() + + hash := PrepareCommittedSeal(v.engine.(*core).current.Proposal().Hash(), v.engine.(*core).current.Round()) + signature, _ := privateKey.SignMessage(hash, []byte{}, false, false) + defer signature.Destroy() + signatureBytes, _ := signature.Serialize() + + msg := istanbul.NewCommitMessage( + &istanbul.CommittedSubject{Subject: v.engine.(*core).current.Subject(), CommittedSeal: signatureBytes}, + validator.Address(), + ) + + if err := r0.handleCommit(msg); err != nil { + if err != test.expectedErr { + t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) + } + continue OUTER + } + } + + // core should have received a parent seal from each of its neighbours + // how can we add our signature to the ParentCommit? Broadcast to ourselve + // does not make much sense + if test.checkParentCommits { + if r0.current.ParentCommits().Size() != r0.current.ValidatorSet().Size()-1 { // TODO: Maybe remove the -1? + t.Errorf("parent seals mismatch: have %v, want %v", r0.current.ParentCommits().Size(), r0.current.ValidatorSet().Size()-1) + } + } + + // prepared is normal case + if r0.current.State() != StateCommitted { + // There are not enough commit messages in core + if r0.current.State() != StatePrepared { + t.Errorf("state mismatch: have %v, want %v", r0.current.State(), StatePrepared) + } + if r0.current.Commits().Size() > r0.current.ValidatorSet().MinQuorumSize() { + t.Errorf("the size of commit messages should be less than %v", r0.current.ValidatorSet().MinQuorumSize()) + } + continue + } + + // core should have min quorum size prepare messages + if r0.current.Commits().Size() < r0.current.ValidatorSet().MinQuorumSize() { + t.Errorf("the size of commit messages should be greater than or equal to minQuorumSize: size %v", r0.current.Commits().Size()) + } + + // check signatures large than MinQuorumSize + signedCount := 0 + for i := 0; i < r0.current.ValidatorSet().Size(); i++ { + if v0.committedMsgs[0].aggregatedSeal.Bitmap.Bit(i) == 1 { + signedCount++ + } + } + if signedCount < r0.current.ValidatorSet().MinQuorumSize() { + t.Errorf("the expected signed count should be greater than or equal to %v, but got %v", r0.current.ValidatorSet().MinQuorumSize(), signedCount) + } + } +} + +// round is not checked for now +func TestVerifyCommit(t *testing.T) { + // for log purpose + privateKey, _ := crypto.GenerateKey() + blsPrivateKey, _ := blscrypto.ECDSAToBLS(privateKey) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + peer := validator.New(getPublicKeyAddress(privateKey), blsPublicKey) + valSet := validator.NewSet([]istanbul.ValidatorData{ + { + Address: peer.Address(), + BLSPublicKey: blsPublicKey, + }, + }) + // }, istanbul.RoundRobin) + + sys := NewTestSystemWithBackend(uint64(1), uint64(0)) + + testCases := []struct { + expected error + commit *istanbul.CommittedSubject + roundState RoundState + }{ + { + // normal case + expected: nil, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + { + // old message + expected: errInconsistentSubject, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // different digest + expected: errInconsistentSubject, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: common.BytesToHash([]byte("1234567890")), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // malicious package(lack of sequence) + expected: errInconsistentSubject, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: nil}, + Digest: newTestProposal().Hash(), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // wrong prepare message with same sequence but different round + expected: errInconsistentSubject, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + { + // wrong prepare message with same round but different sequence + expected: errInconsistentSubject, + commit: &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)}, + Digest: newTestProposal().Hash(), + }, + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + } + for i, test := range testCases { + c := sys.backends[0].engine.(*core) + c.current = test.roundState + + if err := c.verifyCommit(test.commit); err != test.expected { + t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected) + } + } +} + +// BenchmarkHandleCommit benchmarks handling a commit message +func BenchmarkHandleCommit(b *testing.B) { + N := uint64(2) + F := uint64(1) // F does not affect tests + + sys := NewMutedTestSystemWithBackend(N, F) + // sys := NewTestSystemWithBackend(N, F) + + // create block 4 + proposal := newTestProposalWithNum(4) + expectedSubject := &istanbul.Subject{ + View: &istanbul.View{ + Round: big.NewInt(0), + Sequence: proposal.Number(), + }, + Digest: proposal.Hash(), + } + + for i, backend := range sys.backends { + c := backend.engine.(*core) + // same view as the expected one to everyone + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePrepared + } + } + + sys.Run(false) + + v0 := sys.backends[0] + r0 := v0.engine.(*core) + + var im *istanbul.Message + for i, v := range sys.backends { + validator := r0.current.ValidatorSet().GetByIndex(uint64(i)) + privateKey, _ := bls.DeserializePrivateKey(sys.validatorsKeys[i]) + defer privateKey.Destroy() + + hash := PrepareCommittedSeal(v.engine.(*core).current.Proposal().Hash(), v.engine.(*core).current.Round()) + signature, _ := privateKey.SignMessage(hash, []byte{}, false, false) + defer signature.Destroy() + signatureBytes, _ := signature.Serialize() + im = istanbul.NewCommitMessage(&istanbul.CommittedSubject{ + Subject: v.engine.(*core).current.Subject(), + CommittedSeal: signatureBytes, + }, validator.Address()) + } + // benchmarked portion + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := r0.handleCommit(im) + if err != nil { + b.Errorf("Error handling the pre-prepare message. err: %v", err) + } + } +}
diff --git go-ethereum/consensus/istanbul/core/request_test.go celo/consensus/istanbul/core/request_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f1753a3c079d5e0bc5da150e68eb49f0b19feaa2 --- /dev/null +++ celo/consensus/istanbul/core/request_test.go @@ -0,0 +1,138 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "reflect" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common/prque" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" +) + +func TestCheckRequestMsg(t *testing.T) { + valSet := newTestValidatorSet(4) + c := &core{ + current: newRoundState(&istanbul.View{ + Sequence: big.NewInt(1), + Round: big.NewInt(0), + }, valSet, valSet.GetByIndex(0)), + } + + // invalid request + err := c.checkRequestMsg(nil) + if err != errInvalidMessage { + t.Errorf("error mismatch: have %v, want %v", err, errInvalidMessage) + } + r := &istanbul.Request{ + Proposal: nil, + } + err = c.checkRequestMsg(r) + if err != errInvalidMessage { + t.Errorf("error mismatch: have %v, want %v", err, errInvalidMessage) + } + + // old request + r = &istanbul.Request{ + Proposal: makeBlock(0), + } + err = c.checkRequestMsg(r) + if err != errOldMessage { + t.Errorf("error mismatch: have %v, want %v", err, errOldMessage) + } + + // future request + r = &istanbul.Request{ + Proposal: makeBlock(2), + } + err = c.checkRequestMsg(r) + if err != errFutureMessage { + t.Errorf("error mismatch: have %v, want %v", err, errFutureMessage) + } + + // current request + r = &istanbul.Request{ + Proposal: makeBlock(1), + } + err = c.checkRequestMsg(r) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } +} + +func TestStoreRequestMsg(t *testing.T) { + backend := &testSystemBackend{ + events: new(event.TypeMux), + } + valSet := newTestValidatorSet(4) + c := &core{ + config: istanbul.DefaultConfig, + logger: log.New("backend", "test", "id", 0), + backend: backend, + current: newRoundState(&istanbul.View{ + Sequence: big.NewInt(0), + Round: big.NewInt(0), + }, valSet, valSet.GetByIndex(0)), + pendingRequests: prque.New(nil), + pendingRequestsMu: new(sync.Mutex), + } + requests := []istanbul.Request{ + { + Proposal: makeBlock(1), + }, + { + Proposal: makeBlock(2), + }, + { + Proposal: makeBlock(3), + }, + } + + c.storeRequestMsg(&requests[1]) + c.storeRequestMsg(&requests[0]) + c.storeRequestMsg(&requests[2]) + if c.pendingRequests.Size() != len(requests) { + t.Errorf("the size of pending requests mismatch: have %v, want %v", c.pendingRequests.Size(), len(requests)) + } + + c.current.(*roundStateImpl).sequence = big.NewInt(3) + + c.subscribeEvents() + defer c.unsubscribeEvents() + + c.processPendingRequests() + + const timeoutDura = 2 * time.Second + timeout := time.NewTimer(timeoutDura) + select { + case ev := <-c.events.Chan(): + e, ok := ev.Data.(istanbul.RequestEvent) + if !ok { + t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data)) + } + if e.Proposal.Number().Cmp(requests[2].Proposal.Number()) != 0 { + t.Errorf("the number of proposal mismatch: have %v, want %v", e.Proposal.Number(), requests[2].Proposal.Number()) + } + case <-timeout.C: + t.Error("unexpected timeout occurs") + } +}
diff --git go-ethereum/consensus/istanbul/validator/selectors.go celo/consensus/istanbul/validator/selectors.go new file mode 100644 index 0000000000000000000000000000000000000000..c343ae3a0aaf7dc9c764b6cdf853dabafb41afe7 --- /dev/null +++ celo/consensus/istanbul/validator/selectors.go @@ -0,0 +1,90 @@ +// Copyright 2019 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package validator + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator/random" +) + +func proposerIndex(valSet istanbul.ValidatorSet, proposer common.Address) uint64 { + if idx := valSet.GetIndex(proposer); idx >= 0 { + return uint64(idx) + } + return 0 +} + +// ShuffledRoundRobinProposer selects the next proposer with a round robin strategy according to a shuffled order. +func ShuffledRoundRobinProposer(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) istanbul.Validator { + if valSet.Size() == 0 { + return nil + } + seed := valSet.GetRandomness() + + shuffle := random.Permutation(seed, valSet.Size()) + reverse := make([]int, len(shuffle)) + for i, n := range shuffle { + reverse[n] = i + } + idx := round + if proposer != (common.Address{}) { + idx += uint64(reverse[proposerIndex(valSet, proposer)]) + 1 + } + return valSet.List()[shuffle[idx%uint64(valSet.Size())]] +} + +// RoundRobinProposer selects the next proposer with a round robin strategy according to storage order. +func RoundRobinProposer(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) istanbul.Validator { + if valSet.Size() == 0 { + return nil + } + idx := round + if proposer != (common.Address{}) { + idx += proposerIndex(valSet, proposer) + 1 + } + return valSet.List()[idx%uint64(valSet.Size())] +} + +// StickyProposer selects the next proposer with a sticky strategy, advancing on round change. +func StickyProposer(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) istanbul.Validator { + if valSet.Size() == 0 { + return nil + } + idx := round + if proposer != (common.Address{}) { + idx += proposerIndex(valSet, proposer) + } + return valSet.List()[idx%uint64(valSet.Size())] +} + +// GetProposerSelector returns the ProposerSelector for the given Policy +func GetProposerSelector(pp istanbul.ProposerPolicy) istanbul.ProposerSelector { + switch pp { + case istanbul.Sticky: + return StickyProposer + case istanbul.RoundRobin: + return RoundRobinProposer + case istanbul.ShuffledRoundRobin: + return ShuffledRoundRobinProposer + default: + // Programming error. + panic(fmt.Sprintf("unknown proposer selection policy: %v", pp)) + } +}
diff --git go-ethereum/consensus/istanbul/core/prepare_v2_test.go celo/consensus/istanbul/core/prepare_v2_test.go new file mode 100644 index 0000000000000000000000000000000000000000..08d5d5aa92eb2c2602e319ccfbba9f4cebd33898 --- /dev/null +++ celo/consensus/istanbul/core/prepare_v2_test.go @@ -0,0 +1,550 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "reflect" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/crypto" +) + +func TestVerifyPreparedCertificateV2(t *testing.T) { + N := uint64(4) // replica 0 is the proposer, it will send messages to others + F := uint64(1) + sys := NewTestSystemWithBackendV2(N, F) + view := istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + } + proposal := makeBlock(0) + + for _, b := range sys.backends { + b.engine.Start() // start Istanbul core + } + + testCases := []struct { + name string + certificate istanbul.PreparedCertificateV2 + proposal istanbul.Proposal + expectedErr error + expectedView *istanbul.View + }{ + { + "Valid PREPARED certificate", + sys.getPreparedCertificateV2(t, []istanbul.View{view}, proposal), + proposal, + nil, + &view, + }, + { + "Invalid PREPARED certificate, duplicate message", + func() istanbul.PreparedCertificateV2 { + preparedCertificate := sys.getPreparedCertificateV2(t, []istanbul.View{view}, proposal) + preparedCertificate.PrepareOrCommitMessages[1] = preparedCertificate.PrepareOrCommitMessages[0] + return preparedCertificate + }(), + proposal, + errInvalidPreparedCertificateDuplicate, + nil, + }, + { + "Invalid PREPARED certificate, future message", + func() istanbul.PreparedCertificateV2 { + futureView := istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(10), + } + preparedCertificate := sys.getPreparedCertificateV2(t, []istanbul.View{futureView}, proposal) + return preparedCertificate + }(), + proposal, + errInvalidPreparedCertificateMsgView, + nil, + }, + { + "Invalid PREPARED certificate, includes preprepare message", + func() istanbul.PreparedCertificateV2 { + preparedCertificate := sys.getPreparedCertificateV2(t, []istanbul.View{view}, proposal) + testInvalidMsg, _ := sys.backends[0].getRoundChangeV2Message(view, sys.getPreparedCertificateV2(t, []istanbul.View{view}, proposal), proposal) + preparedCertificate.PrepareOrCommitMessages[0] = testInvalidMsg + return preparedCertificate + }(), + proposal, + errInvalidPreparedCertificateMsgCode, + nil, + }, + { + "Invalid PREPARED certificate, hash mismatch", + func() istanbul.PreparedCertificateV2 { + preparedCertificate := sys.getPreparedCertificateV2(t, []istanbul.View{view}, proposal) + preparedCertificate.PrepareOrCommitMessages[1] = preparedCertificate.PrepareOrCommitMessages[0] + preparedCertificate.ProposalHash = makeBlock(1).Hash() + return preparedCertificate + }(), + makeBlock(1), + errInvalidPreparedCertificateDigestMismatch, + nil, + }, + { + "Invalid PREPARED certificate, view inconsistencies", + func() istanbul.PreparedCertificateV2 { + var view2 istanbul.View + view2.Sequence = big.NewInt(view.Sequence.Int64()) + view2.Round = big.NewInt(view.Round.Int64() + 1) + preparedCertificate := sys.getPreparedCertificateV2(t, []istanbul.View{view, view2}, proposal) + return preparedCertificate + }(), + proposal, + errInvalidPreparedCertificateInconsistentViews, + nil, + }, + { + "Empty certificate", + func() istanbul.PreparedCertificateV2 { + pc, _ := istanbul.EmptyPreparedCertificateV2() + return pc + }(), + proposal, + errInvalidPreparedCertificateNumMsgs, + nil, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + for _, backend := range sys.backends { + c := backend.engine.(*core) + view, err := c.verifyPCV2WithProposal(test.certificate, test.proposal) + if err != test.expectedErr { + t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) + } + if err == nil { + if view.Cmp(test.expectedView) != 0 { + t.Errorf("view mismatch: have %v, want %v", view, test.expectedView) + } + view, err := c.getViewFromVerifiedPreparedCertificateV2(test.certificate) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + if view.Cmp(test.expectedView) != 0 { + t.Errorf("view mismatch: have %v, want %v", view, test.expectedView) + } + } + } + }) + } +} +func TestHandlePrepareV2(t *testing.T) { + N := uint64(4) + F := uint64(1) + + proposal := newTestProposal() + expectedSubject := &istanbul.Subject{ + View: &istanbul.View{ + Round: big.NewInt(0), + Sequence: proposal.Number(), + }, + Digest: proposal.Hash(), + } + + testCases := []struct { + name string + system *testSystem + expectedErr error + }{ + { + "normal case", + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + backend.peers, + ) + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePreprepared + } + } + return sys + }(), + nil, + }, + { + "normal case with prepared certificate", + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + preparedCert := sys.getPreparedCertificateV2( + t, + []istanbul.View{ + { + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + }, + proposal) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + backend.peers, + ) + c.current.(*roundStateImpl).preparedCertificate = istanbul.PreparedCertificate{ + Proposal: proposal, + PrepareOrCommitMessages: preparedCert.PrepareOrCommitMessages, + } + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePreprepared + } + } + return sys + }(), + nil, + }, + { + "Inconsistent subject due to prepared certificate", + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + preparedCert := sys.getPreparedCertificateV2( + t, + []istanbul.View{ + { + Round: big.NewInt(0), + Sequence: big.NewInt(10), + }, + }, + proposal) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + }, + backend.peers, + ) + c.current.(*roundStateImpl).preparedCertificate = istanbul.PreparedCertificate{ + Proposal: proposal, + PrepareOrCommitMessages: preparedCert.PrepareOrCommitMessages, + } + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePreprepared + } + } + return sys + }(), + errInconsistentSubject, + }, + { + "future message", + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePreprepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(2), + Sequence: big.NewInt(3), + }, + backend.peers, + ) + } + } + return sys + }(), + errFutureMessage, + }, + { + "subject not match", + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePreprepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(0), + }, + backend.peers, + ) + } + } + return sys + }(), + errOldMessage, + }, + { + "subject not match", + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + + for i, backend := range sys.backends { + c := backend.engine.(*core) + if i == 0 { + // replica 0 is the proposer + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + c.current.(*roundStateImpl).state = StatePreprepared + } else { + c.current = newTestRoundStateV2( + &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1)}, + backend.peers, + ) + } + } + return sys + }(), + errInconsistentSubject, + }, + { + "less than 2F+1", + func() *testSystem { + sys := NewTestSystemWithBackendV2(N, F) + + // save less than 2*F+1 replica + sys.backends = sys.backends[2*int(F)+1:] + + for i, backend := range sys.backends { + c := backend.engine.(*core) + c.current = newTestRoundStateV2( + expectedSubject.View, + backend.peers, + ) + + if i == 0 { + // replica 0 is the proposer + c.current.(*roundStateImpl).state = StatePreprepared + } + } + return sys + }(), + nil, + }, + // TODO: double send message + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + + test.system.Run(false) + + v0 := test.system.backends[0] + r0 := v0.engine.(*core) + + for i, v := range test.system.backends { + validator := r0.current.ValidatorSet().GetByIndex(uint64(i)) + msg := istanbul.NewPrepareMessage(v.engine.(*core).current.Subject(), validator.Address()) + err := r0.handlePrepare(msg) + if err != nil { + if err != test.expectedErr { + t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) + } + return + } + } + + // prepared is normal case + if r0.current.State() != StatePrepared { + // There are not enough PREPARE messages in core + if r0.current.State() != StatePreprepared { + t.Errorf("state mismatch: have %v, want %v", r0.current.State(), StatePreprepared) + } + if r0.current.Prepares().Size() >= r0.current.ValidatorSet().MinQuorumSize() { + t.Errorf("the size of PREPARE messages should be less than %v", 2*r0.current.ValidatorSet().MinQuorumSize()+1) + } + + return + } + + // core should have MinQuorumSize PREPARE messages + if r0.current.Prepares().Size() < r0.current.ValidatorSet().MinQuorumSize() { + t.Errorf("the size of PREPARE messages should be greater than or equal to MinQuorumSize: size %v", r0.current.Prepares().Size()) + } + + // a message will be delivered to backend if 2F+1 + if int64(len(v0.sentMsgs)) != 1 { + t.Errorf("the Send() should be called once: times %v", len(test.system.backends[0].sentMsgs)) + } + + // verify COMMIT messages + decodedMsg := new(istanbul.Message) + err := decodedMsg.FromPayload(v0.sentMsgs[0], nil) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + if decodedMsg.Code != istanbul.MsgCommit { + t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, istanbul.MsgCommit) + } + subject := decodedMsg.Commit().Subject + if !reflect.DeepEqual(subject, expectedSubject) { + t.Errorf("subject mismatch: have %v, want %v", subject, expectedSubject) + } + }) + } +} + +// round is not checked for now +func TestVerifyPrepareV2(t *testing.T) { + + // for log purpose + privateKey, _ := crypto.GenerateKey() + blsPrivateKey, _ := blscrypto.ECDSAToBLS(privateKey) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + peer := validator.New(getPublicKeyAddress(privateKey), blsPublicKey) + valSet := validator.NewSet([]istanbul.ValidatorData{ + { + Address: peer.Address(), + BLSPublicKey: blsPublicKey, + }, + }) + + sys := NewTestSystemWithBackendV2(uint64(1), uint64(0)) + + testCases := []struct { + expected error + + prepare *istanbul.Subject + roundState RoundState + }{ + { + // normal case + expected: nil, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + { + // old message + expected: errInconsistentSubject, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // different digest + expected: errInconsistentSubject, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + Digest: common.BytesToHash([]byte("1234567890")), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // malicious package(lack of sequence) + expected: errInconsistentSubject, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: nil}, + Digest: newTestProposal().Hash(), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, + valSet, + ), + }, + { + // wrong PREPARE message with same sequence but different round + expected: errInconsistentSubject, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)}, + Digest: newTestProposal().Hash(), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + { + // wrong PREPARE message with same round but different sequence + expected: errInconsistentSubject, + prepare: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)}, + Digest: newTestProposal().Hash(), + }, + roundState: newTestRoundStateV2( + &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, + valSet, + ), + }, + } + for i, test := range testCases { + c := sys.backends[0].engine.(*core) + c.current = test.roundState + + if err := c.verifyPrepare(test.prepare); err != nil { + if err != test.expected { + t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected) + } + } + } +}
diff --git go-ethereum/consensus/istanbul/core/testbackend_test.go celo/consensus/istanbul/core/testbackend_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2f20047e2302f4356a771a791cc6b3935e2505b6 --- /dev/null +++ celo/consensus/istanbul/core/testbackend_test.go @@ -0,0 +1,616 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "bytes" + "crypto/ecdsa" + "encoding/binary" + "fmt" + "math" + "math/big" + "time" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/celo-org/celo-bls-go/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + elog "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" +) + +// ErrorReporter is the intersection of the testing.B and testing.T interfaces. +// This enables setup functions to be used by both benchmarks and tests. +type ErrorReporter interface { + Errorf(format string, args ...interface{}) +} + +var testLogger = elog.New() + +type testSystemBackend struct { + id uint64 + sys *testSystem + + engine Engine + peers istanbul.ValidatorSet + events *event.TypeMux + + committedMsgs []testCommittedMsgs + sentMsgs [][]byte // store the message when Send is called by core + + key ecdsa.PrivateKey + blsKey []byte + address common.Address + db ethdb.Database + + // Function pointer to a verify function, so that the test core_test.go/TestVerifyProposal + // can inject in different proposal verification statuses. + verifyImpl func(proposal istanbul.Proposal) (*StateProcessResult, time.Duration, error) + + donutBlock *big.Int +} + +type testCommittedMsgs struct { + commitProposal istanbul.Proposal + aggregatedSeal types.IstanbulAggregatedSeal + aggregatedEpochValidatorSetSeal types.IstanbulEpochValidatorSetSeal + stateProcessResult *StateProcessResult +} + +// ============================================== +// +// define the functions that needs to be provided for Istanbul. + +func (self *testSystemBackend) Authorize(address, _ common.Address, _ *ecdsa.PublicKey, _ istanbul.DecryptFn, _ istanbul.SignerFn, _ istanbul.BLSSignerFn) { + self.address = address + self.engine.SetAddress(address) +} + +func (self *testSystemBackend) Address() common.Address { + return self.address +} + +// Peers returns all connected peers +func (self *testSystemBackend) Validators(proposal istanbul.Proposal) istanbul.ValidatorSet { + return self.peers +} + +func (self *testSystemBackend) IsValidating() bool { + return true +} + +func (self *testSystemBackend) ChainConfig() *params.ChainConfig { + return &params.ChainConfig{ + DonutBlock: self.donutBlock, + } +} + +func (self *testSystemBackend) HashForBlock(number uint64) common.Hash { + buffer := new(bytes.Buffer) + _ = binary.Write(buffer, binary.LittleEndian, number) + hash := common.Hash{} + copy(hash[:], buffer.Bytes()) + return hash +} + +func (self *testSystemBackend) IsPrimary() bool { + return true +} + +func (self *testSystemBackend) IsPrimaryForSeq(seq *big.Int) bool { + return true +} + +func (self *testSystemBackend) NextBlockValidators(proposal istanbul.Proposal) (istanbul.ValidatorSet, error) { + //This doesn't really return the next block validators + return self.peers, nil +} + +func (self *testSystemBackend) EventMux() *event.TypeMux { + return self.events +} + +func (self *testSystemBackend) Send(message []byte, target common.Address) error { + testLogger.Info("enqueuing a message...", "address", self.Address()) + self.sentMsgs = append(self.sentMsgs, message) + self.sys.queuedMessage <- istanbul.MessageEvent{ + Payload: message, + } + return nil +} + +func (self *testSystemBackend) Multicast(validators []common.Address, message []byte, msgCode uint64, sendToSelf bool) error { + testLogger.Info("enqueuing a message...", "address", self.Address()) + self.sentMsgs = append(self.sentMsgs, message) + send := func() { + self.sys.queuedMessage <- istanbul.MessageEvent{ + Payload: message, + } + } + go send() + return nil +} + +func (self *testSystemBackend) Gossip(payload []byte, ethMsgCode uint64) error { + return nil +} + +func (self *testSystemBackend) SignBLS(data []byte, extra []byte, useComposite, cip22 bool) (blscrypto.SerializedSignature, error) { + privateKey, _ := bls.DeserializePrivateKey(self.blsKey) + defer privateKey.Destroy() + + signature, _ := privateKey.SignMessage(data, extra, useComposite, cip22) + defer signature.Destroy() + signatureBytes, _ := signature.Serialize() + + return blscrypto.SerializedSignatureFromBytes(signatureBytes) +} + +func (self *testSystemBackend) Commit(proposal istanbul.Proposal, aggregatedSeal types.IstanbulAggregatedSeal, aggregatedEpochValidatorSetSeal types.IstanbulEpochValidatorSetSeal, stateProcessResult *StateProcessResult) error { + testLogger.Info("commit message", "address", self.Address()) + self.committedMsgs = append(self.committedMsgs, testCommittedMsgs{ + commitProposal: proposal, + aggregatedSeal: aggregatedSeal, + aggregatedEpochValidatorSetSeal: aggregatedEpochValidatorSetSeal, + stateProcessResult: stateProcessResult, + }) + + // fake new head events + go self.events.Post(istanbul.FinalCommittedEvent{}) + return nil +} + +func (self *testSystemBackend) Verify(proposal istanbul.Proposal) (*StateProcessResult, time.Duration, error) { + if self.verifyImpl == nil { + return self.verifyWithSuccess(proposal) + } else { + return self.verifyImpl(proposal) + } +} + +func (self *testSystemBackend) verifyWithSuccess(proposal istanbul.Proposal) (*StateProcessResult, time.Duration, error) { + return nil, 0, nil +} + +func (self *testSystemBackend) verifyWithFailure(proposal istanbul.Proposal) (*StateProcessResult, time.Duration, error) { + return nil, 0, InvalidProposalError +} + +func (self *testSystemBackend) verifyWithFutureProposal(proposal istanbul.Proposal) (*StateProcessResult, time.Duration, error) { + return nil, 5, consensus.ErrFutureBlock +} + +func (self *testSystemBackend) Sign(data []byte) ([]byte, error) { + hashData := crypto.Keccak256(data) + return crypto.Sign(hashData, &self.key) +} + +func (self *testSystemBackend) CheckSignature([]byte, common.Address, []byte) error { + return nil +} + +func (self *testSystemBackend) CheckValidatorSignature(data []byte, sig []byte) (common.Address, error) { + return istanbul.CheckValidatorSignature(self.peers, data, sig) +} + +func (self *testSystemBackend) Hash(b interface{}) common.Hash { + return common.BytesToHash([]byte("Test")) +} + +func (self *testSystemBackend) NewRequest(request istanbul.Proposal) { + go self.events.Post(istanbul.RequestEvent{ + Proposal: request, + }) +} + +func (self *testSystemBackend) GetCurrentHeadBlock() istanbul.Proposal { + l := len(self.committedMsgs) + if l > 0 { + testLogger.Info("have proposal for block", "num", l) + return self.committedMsgs[l-1].commitProposal + } + return makeBlock(0) +} + +func (self *testSystemBackend) GetCurrentHeadBlockAndAuthor() (istanbul.Proposal, common.Address) { + l := len(self.committedMsgs) + if l > 0 { + testLogger.Info("have proposal for block", "num", l) + return self.committedMsgs[l-1].commitProposal, common.Address{} + } + return makeBlock(0), common.Address{} +} + +func (self *testSystemBackend) LastSubject() (istanbul.Subject, error) { + lastProposal := self.GetCurrentHeadBlock() + lastView := &istanbul.View{Sequence: lastProposal.Number(), Round: big.NewInt(1)} + return istanbul.Subject{View: lastView, Digest: lastProposal.Hash()}, nil +} + +// Only block height 5 will return true +func (self *testSystemBackend) HasBlock(hash common.Hash, number *big.Int) bool { + return number.Cmp(big.NewInt(5)) == 0 +} + +func (self *testSystemBackend) AuthorForBlock(number uint64) common.Address { + return common.Address{} +} + +func (self *testSystemBackend) ParentBlockValidators(proposal istanbul.Proposal) istanbul.ValidatorSet { + return self.peers +} + +func (self *testSystemBackend) UpdateReplicaState(seq *big.Int) { /* pass */ } + +func (self *testSystemBackend) finalizeAndReturnMessage(msg *istanbul.Message) (istanbul.Message, error) { + message := new(istanbul.Message) + data, err := self.engine.(*core).finalizeMessage(msg) + if err != nil { + return *message, err + } + err = message.FromPayload(data, self.engine.(*core).validateFn) + return *message, err +} + +func (self *testSystemBackend) getPreprepareV2Message(view istanbul.View, + roundChangeCertificateV2 istanbul.RoundChangeCertificateV2, + proposal istanbul.Proposal) (istanbul.Message, error) { + msg := istanbul.NewPreprepareV2Message(&istanbul.PreprepareV2{ + View: &view, + RoundChangeCertificateV2: roundChangeCertificateV2, + Proposal: proposal, + }, self.address) + + return self.finalizeAndReturnMessage(msg) +} + +func (self *testSystemBackend) getPrepareMessage(view istanbul.View, digest common.Hash) (istanbul.Message, error) { + msg := istanbul.NewPrepareMessage(&istanbul.Subject{ + View: &view, + Digest: digest, + }, self.address) + return self.finalizeAndReturnMessage(msg) +} + +func (self *testSystemBackend) getCommitMessage(view istanbul.View, proposal istanbul.Proposal) (istanbul.Message, error) { + subject := &istanbul.Subject{ + View: &view, + Digest: proposal.Hash(), + } + + committedSeal, err := self.engine.(*core).generateCommittedSeal(subject) + if err != nil { + return istanbul.Message{}, err + } + + msg := istanbul.NewCommitMessage(&istanbul.CommittedSubject{ + Subject: subject, + CommittedSeal: committedSeal[:], + }, self.address) + + // // We swap in the provided proposal so that the message is finalized for the provided proposal + // // and not for the current preprepare. + // cachePreprepare := self.engine.(*core).current.Preprepare() + // fmt.Println("5") + // self.engine.(*core).current.TransitionToPreprepared(&istanbul.Preprepare{ + // View: &view, + // Proposal: proposal, + // }) + message, err := self.finalizeAndReturnMessage(msg) + // self.engine.(*core).current.TransitionToPreprepared(cachePreprepare) + return message, err +} + +func (self *testSystemBackend) getRoundChangeV2Message(view istanbul.View, preparedCertV2 istanbul.PreparedCertificateV2, proposal istanbul.Proposal) (istanbul.Message, error) { + req, err := self.getRoundChangeRequest(view, preparedCertV2) + if err != nil { + return istanbul.Message{}, err + } + msg := istanbul.NewRoundChangeV2Message(&istanbul.RoundChangeV2{ + Request: *req, + PreparedProposal: proposal, + }, common.Address{}) + + return self.finalizeAndReturnMessage(msg) +} + +func (self *testSystemBackend) getRoundChangeRequest(view istanbul.View, preparedCertV2 istanbul.PreparedCertificateV2) (*istanbul.RoundChangeRequest, error) { + req := &istanbul.RoundChangeRequest{ + View: view, + PreparedCertificateV2: preparedCertV2, + Address: self.engine.(*core).address, + } + err := req.Sign(self.engine.(*core).backend.Sign) + return req, err +} + +func (self *testSystemBackend) Enode() *enode.Node { + return nil +} + +func (self *testSystemBackend) RefreshValPeers() error { + return nil +} + +func (self *testSystemBackend) setVerifyImpl(verifyImpl func(proposal istanbul.Proposal) (*StateProcessResult, time.Duration, error)) { + self.verifyImpl = verifyImpl +} + +// ============================================== +// +// define the struct that need to be provided for integration tests. + +type testSystem struct { + backends []*testSystemBackend + f uint64 + n uint64 + validatorsKeys [][]byte + + queuedMessage chan istanbul.MessageEvent + quit chan struct{} +} + +func newTestSystem(n uint64, f uint64, keys [][]byte) *testSystem { + return &testSystem{ + backends: make([]*testSystemBackend, n), + validatorsKeys: keys, + f: f, + n: n, + + queuedMessage: make(chan istanbul.MessageEvent), + quit: make(chan struct{}), + } +} + +func generateValidators(n int) ([]istanbul.ValidatorData, [][]byte, []*ecdsa.PrivateKey) { + vals := make([]istanbul.ValidatorData, 0) + blsKeys := make([][]byte, 0) + keys := make([]*ecdsa.PrivateKey, 0) + for i := 0; i < n; i++ { + privateKey, _ := crypto.GenerateKey() + blsPrivateKey, _ := blscrypto.ECDSAToBLS(privateKey) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + vals = append(vals, istanbul.ValidatorData{ + Address: crypto.PubkeyToAddress(privateKey.PublicKey), + BLSPublicKey: blsPublicKey, + }) + keys = append(keys, privateKey) + blsKeys = append(blsKeys, blsPrivateKey) + } + return vals, blsKeys, keys +} + +func newTestValidatorSet(n int) istanbul.ValidatorSet { + validators, _, _ := generateValidators(n) + return validator.NewSet(validators) +} + +func newTestSystemWithBackend(n, f uint64, v2Block *big.Int) *testSystem { + + validators, blsKeys, keys := generateValidators(int(n)) + sys := newTestSystem(n, f, blsKeys) + config := *istanbul.DefaultConfig + config.ProposerPolicy = istanbul.RoundRobin + config.RoundStateDBPath = "" + config.RequestTimeout = 300 + config.TimeoutBackoffFactor = 100 + config.MinResendRoundChangeTimeout = 1000 + config.MaxResendRoundChangeTimeout = 10000 + + for i := uint64(0); i < n; i++ { + vset := validator.NewSet(validators) + backend := sys.NewBackend(i, nil) + backend.peers = vset + backend.address = vset.GetByIndex(i).Address() + backend.key = *keys[i] + backend.blsKey = blsKeys[i] + + core := New(backend, &config).(*core) + core.logger = testLogger + core.validateFn = backend.CheckValidatorSignature + + backend.engine = core + } + + return sys +} + +// FIXME: int64 is needed for N and F +func NewTestSystemWithBackendDonut(n, f, epoch uint64, donutBlock int64) *testSystem { + testLogger.SetHandler(elog.StdoutHandler) + + validators, blsKeys, keys := generateValidators(int(n)) + sys := newTestSystem(n, f, blsKeys) + config := *istanbul.DefaultConfig + config.ProposerPolicy = istanbul.RoundRobin + config.RoundStateDBPath = "" + config.RequestTimeout = 300 + config.TimeoutBackoffFactor = 100 + config.MinResendRoundChangeTimeout = 1000 + config.MaxResendRoundChangeTimeout = 10000 + config.Epoch = epoch + + for i := uint64(0); i < n; i++ { + vset := validator.NewSet(validators) + backend := sys.NewBackend(i, big.NewInt(donutBlock)) + backend.peers = vset + backend.address = vset.GetByIndex(i).Address() + backend.key = *keys[i] + backend.blsKey = blsKeys[i] + + core := New(backend, &config).(*core) + core.logger = testLogger + core.validateFn = backend.CheckValidatorSignature + + backend.engine = core + } + + return sys +} + +// FIXME: int64 is needed for N and F +func NewTestSystemWithBackend(n, f uint64) *testSystem { + testLogger.SetHandler(elog.StdoutHandler) + return newTestSystemWithBackend(n, f, nil) +} + +func NewTestSystemWithBackendV2(n, f uint64) *testSystem { + testLogger.SetHandler(elog.StdoutHandler) + return newTestSystemWithBackend(n, f, big.NewInt(0)) +} + +// FIXME: int64 is needed for N and F +func NewMutedTestSystemWithBackend(n, f uint64) *testSystem { + testLogger.SetHandler(elog.DiscardHandler()) + return newTestSystemWithBackend(n, f, nil) +} + +func NewMutedTestSystemWithBackendV2(n, f uint64) *testSystem { + testLogger.SetHandler(elog.DiscardHandler()) + return newTestSystemWithBackend(n, f, big.NewInt(0)) +} + +// listen will consume messages from queue and deliver a message to core +func (t *testSystem) listen() { + for { + select { + case <-t.quit: + return + case queuedMessage := <-t.queuedMessage: + testLogger.Info("consuming a queue message...") + for _, backend := range t.backends { + go backend.EventMux().Post(queuedMessage) + } + } + } +} + +// Run will start system components based on given flag, and returns a closer +// function that caller can control lifecycle +// +// Given a true for core if you want to initialize core engine. +func (t *testSystem) Run(core bool) func() { + for _, b := range t.backends { + if core { + err := b.engine.Start() // start Istanbul core + if err != nil { + fmt.Printf("Error Starting istanbul engine: %s", err) + panic("Error Starting istanbul engine") + } + } + } + + go t.listen() + closer := func() { t.Stop(core) } + return closer +} + +func (t *testSystem) Stop(core bool) { + close(t.quit) + + for _, b := range t.backends { + if core { + err := b.engine.Stop() + if err != nil { + fmt.Printf("Error Stopping istanbul engine: %s", err) + panic("Error Stopping istanbul engine") + } + } + } +} + +func (t *testSystem) NewBackend(id uint64, donutBlock *big.Int) *testSystemBackend { + // assume always success + backend := &testSystemBackend{ + id: id, + sys: t, + events: new(event.TypeMux), + db: rawdb.NewMemoryDatabase(), + donutBlock: donutBlock, + } + + t.backends[id] = backend + return backend +} + +func (t *testSystem) F() uint64 { + return t.f +} + +func (t *testSystem) MinQuorumSize() uint64 { + return uint64(math.Ceil(float64(2*t.n) / 3)) +} + +func (sys *testSystem) getPreparedCertificate(t ErrorReporter, views []istanbul.View, proposal istanbul.Proposal) istanbul.PreparedCertificate { + preparedCertificate := istanbul.PreparedCertificate{ + Proposal: proposal, + PrepareOrCommitMessages: []istanbul.Message{}, + } + for i, backend := range sys.backends { + if uint64(i) == sys.MinQuorumSize() { + break + } + var err error + var msg istanbul.Message + if i%2 == 0 { + msg, err = backend.getPrepareMessage(views[i%len(views)], proposal.Hash()) + } else { + msg, err = backend.getCommitMessage(views[i%len(views)], proposal) + } + if err != nil { + t.Errorf("Failed to create message %v: %v", i, err) + } + preparedCertificate.PrepareOrCommitMessages = append(preparedCertificate.PrepareOrCommitMessages, msg) + } + return preparedCertificate +} + +func (sys *testSystem) getPreparedCertificateV2(t ErrorReporter, views []istanbul.View, proposal istanbul.Proposal) istanbul.PreparedCertificateV2 { + pc := sys.getPreparedCertificate(t, views, proposal) + return istanbul.PCV2FromPCV1(pc) +} + +func (sys *testSystem) getRoundChangeCertificateV2(t ErrorReporter, views []istanbul.View, preparedCertificateV2 istanbul.PreparedCertificateV2) istanbul.RoundChangeCertificateV2 { + var roundChangeCertificateV2 istanbul.RoundChangeCertificateV2 + for i, backend := range sys.backends { + if uint64(i) == sys.MinQuorumSize() { + break + } + req, err := backend.getRoundChangeRequest(views[i%len(views)], preparedCertificateV2) + if err != nil { + t.Errorf("Failed to create ROUND CHANGE message: %v", err) + } + roundChangeCertificateV2.Requests = append(roundChangeCertificateV2.Requests, *req) + } + return roundChangeCertificateV2 +} + +// ============================================== +// +// helper functions. + +func getPublicKeyAddress(privateKey *ecdsa.PrivateKey) common.Address { + return crypto.PubkeyToAddress(privateKey.PublicKey) +}
diff --git go-ethereum/consensus/istanbul/validator/default_test.go celo/consensus/istanbul/validator/default_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0c3dc6d98241be5517be17568afff2f24f1bdeb3 --- /dev/null +++ celo/consensus/istanbul/validator/default_test.go @@ -0,0 +1,255 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package validator + +import ( + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/rlp" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + testAddress = "70524d664ffe731100208a0154e556f9bb679ae6" + testAddress2 = "b37866a925bccd69cfa98d43b510f1d23d78a851" +) + +func TestValidatorSet(t *testing.T) { + t.Run("NewValidatorSet", testNewValidatorSet) + t.Run("NormalValSet", testNormalValSet) + t.Run("EmptyValSet", testEmptyValSet) + t.Run("AddAndRemoveValidator", testAddAndRemoveValidator) + t.Run("QuorumSizes", testQuorumSizes) +} + +func testNewValidatorSet(t *testing.T) { + const ValCnt = 100 + + // Create 100 validators with random addresses + b := []byte{} + for i := 0; i < ValCnt; i++ { + key, _ := crypto.GenerateKey() + blsPrivateKey, _ := blscrypto.ECDSAToBLS(key) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + addr := crypto.PubkeyToAddress(key.PublicKey) + val := New(addr, blsPublicKey) + b = append(b, val.Address().Bytes()...) + b = append(b, blsPublicKey[:]...) + } + + // Create ValidatorSet + valSet := NewSet(ExtractValidators(b)) + if valSet == nil { + t.Errorf("the validator byte array cannot be parsed") + t.FailNow() + } +} + +func testNormalValSet(t *testing.T) { + b1 := common.Hex2Bytes(testAddress) + b2 := common.Hex2Bytes(testAddress2) + addr1 := common.BytesToAddress(b1) + addr2 := common.BytesToAddress(b2) + val1 := New(addr1, blscrypto.SerializedPublicKey{}) + val2 := New(addr2, blscrypto.SerializedPublicKey{}) + + validators, _ := istanbul.CombineIstanbulExtraToValidatorData([]common.Address{addr1, addr2}, []blscrypto.SerializedPublicKey{{}, {}}) + valSet := newDefaultSet(validators) + if valSet == nil { + t.Errorf("the format of validator set is invalid") + t.FailNow() + } + + // check size + if size := valSet.Size(); size != 2 { + t.Errorf("the size of validator set is wrong: have %v, want 2", size) + } + // test get by index + if val := valSet.GetByIndex(uint64(0)); !reflect.DeepEqual(val, val1) { + t.Errorf("validator mismatch: have %v, want %v", val, val1) + } + // test get by invalid index + if val := valSet.GetByIndex(uint64(2)); val != nil { + t.Errorf("validator mismatch: have %v, want nil", val) + } + // test get by address + if _, val := valSet.GetByAddress(addr2); !reflect.DeepEqual(val, val2) { + t.Errorf("validator mismatch: have %v, want %v", val, val2) + } + // test get by invalid address + invalidAddr := common.HexToAddress("0x9535b2e7faaba5288511d89341d94a38063a349b") + if _, val := valSet.GetByAddress(invalidAddr); val != nil { + t.Errorf("validator mismatch: have %v, want nil", val) + } +} + +func testEmptyValSet(t *testing.T) { + valSet := NewSet(ExtractValidators([]byte{})) + if valSet == nil { + t.Errorf("validator set should not be nil") + } +} + +func testAddAndRemoveValidator(t *testing.T) { + valSet := NewSet(ExtractValidators([]byte{})) + if !valSet.AddValidators( + []istanbul.ValidatorData{ + { + Address: common.BytesToAddress([]byte(string(rune(3)))), + BLSPublicKey: blscrypto.SerializedPublicKey{}, + }, + }, + ) { + t.Error("the validator should be added") + } + if valSet.AddValidators( + []istanbul.ValidatorData{ + { + Address: common.BytesToAddress([]byte(string(rune(3)))), + BLSPublicKey: blscrypto.SerializedPublicKey{}, + }, + }, + ) { + t.Error("the existing validator should not be added") + } + valSet.AddValidators( + []istanbul.ValidatorData{ + { + Address: common.BytesToAddress([]byte(string(rune(2)))), + BLSPublicKey: blscrypto.SerializedPublicKey{}, + }, + { + Address: common.BytesToAddress([]byte(string(rune(1)))), + BLSPublicKey: blscrypto.SerializedPublicKey{}, + }, + }, + ) + if valSet.Size() != 3 { + t.Error("the size of validator set should be 3") + } + + expectedOrder := []int{3, 2, 1} + for i, v := range valSet.List() { + expected := common.BytesToAddress([]byte(string(rune(expectedOrder[i])))) + if v.Address() != expected { + t.Errorf("the order of validators is wrong: have %v, want %v", v.Address().Hex(), expected.Hex()) + } + } + + if !valSet.RemoveValidators(big.NewInt(1)) { // remove first falidator + t.Error("the validator should be removed") + } + + if len(valSet.List()) != 2 || len(valSet.List()) != valSet.Size() { // validators set should have the same size + t.Error("the size of validator set should be 2") + } + valSet.RemoveValidators(big.NewInt(2)) // remove second validator + if len(valSet.List()) != 1 || len(valSet.List()) != valSet.Size() { // validators set should have the same size + t.Error("the size of validator set should be 1") + } + valSet.RemoveValidators(big.NewInt(1)) // remove third validator + if len(valSet.List()) != 0 || len(valSet.List()) != valSet.Size() { // validators set should have the same size + t.Error("the size of validator set should be 0") + } +} + +func generateValidators(n int) ([]istanbul.ValidatorData, [][]byte) { + vals := make([]istanbul.ValidatorData, 0) + keys := make([][]byte, 0) + for i := 0; i < n; i++ { + privateKey, _ := crypto.GenerateKey() + blsPrivateKey, _ := blscrypto.ECDSAToBLS(privateKey) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + vals = append(vals, istanbul.ValidatorData{ + Address: crypto.PubkeyToAddress(privateKey.PublicKey), + BLSPublicKey: blsPublicKey, + }) + keys = append(keys, blsPrivateKey) + } + return vals, keys +} + +func testQuorumSizes(t *testing.T) { + testCases := []struct { + validatorSetSize int + expectedMinQuorumSize int + }{ + {validatorSetSize: 1, expectedMinQuorumSize: 1}, + {validatorSetSize: 2, expectedMinQuorumSize: 2}, + {validatorSetSize: 3, expectedMinQuorumSize: 2}, + {validatorSetSize: 4, expectedMinQuorumSize: 3}, + {validatorSetSize: 5, expectedMinQuorumSize: 4}, + {validatorSetSize: 6, expectedMinQuorumSize: 4}, + {validatorSetSize: 7, expectedMinQuorumSize: 5}, + } + + for _, testCase := range testCases { + vals, _ := generateValidators(testCase.validatorSetSize) + valSet := newDefaultSet(vals) + + if valSet.MinQuorumSize() != testCase.expectedMinQuorumSize { + t.Errorf("error mismatch quorum size for valset of size %d: have %d, want %d", valSet.Size(), valSet.MinQuorumSize(), testCase.expectedMinQuorumSize) + } + } +} + +func TestValidatorRLPEncoding(t *testing.T) { + + val := New(common.BytesToAddress([]byte(string(rune(2)))), blscrypto.SerializedPublicKey{1, 2, 3}) + + rawVal, err := rlp.EncodeToBytes(val) + if err != nil { + t.Errorf("Error %v", err) + } + + var result *defaultValidator + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Errorf("Error %v", err) + } + + if !reflect.DeepEqual(val, result) { + t.Errorf("validator mismatch: have %v, want %v", val, result) + } +} + +func TestValidatorSetRLPEncoding(t *testing.T) { + + valSet := NewSet([]istanbul.ValidatorData{ + {Address: common.BytesToAddress([]byte(string(rune(2)))), BLSPublicKey: blscrypto.SerializedPublicKey{1, 2, 3}}, + {Address: common.BytesToAddress([]byte(string(rune(4)))), BLSPublicKey: blscrypto.SerializedPublicKey{3, 1, 4}}, + }) + + rawVal, err := rlp.EncodeToBytes(valSet) + if err != nil { + t.Errorf("Error %v", err) + } + + var result *defaultSet + if err = rlp.DecodeBytes(rawVal, &result); err != nil { + t.Errorf("Error %v", err) + } + + if !reflect.DeepEqual(valSet, result) { + t.Errorf("validatorSet mismatch: have %v, want %v", valSet, result) + } +}
diff --git go-ethereum/consensus/istanbul/core/request.go celo/consensus/istanbul/core/request.go new file mode 100644 index 0000000000000000000000000000000000000000..e02b0f312705267ac23fdf0f4fb082147923317c --- /dev/null +++ celo/consensus/istanbul/core/request.go @@ -0,0 +1,118 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" +) + +func (c *core) handleRequest(request *istanbul.Request) error { + logger := c.newLogger("func", "handleRequest") + + err := c.checkRequestMsg(request) + if err == errInvalidMessage { + logger.Warn("invalid request") + return err + } else if err != nil { + logger.Warn("unexpected request", "err", err, "number", request.Proposal.Number(), "hash", request.Proposal.Hash()) + return err + } + + logger.Trace("handleRequest", "number", request.Proposal.Number(), "hash", request.Proposal.Hash()) + + if err = c.current.SetPendingRequest(request); err != nil { + return err + } + + // Must go through startNewRound to send proposals for round > 0 to ensure a round change certificate is generated. + if c.current.State() == StateAcceptRequest && c.current.Round().Cmp(common.Big0) == 0 { + c.sendPreprepareV2(request, istanbul.RoundChangeCertificateV2{}) + } + return nil +} + +// check request state +// return errInvalidMessage if the message is invalid +// return errFutureMessage if the sequence of proposal is larger than current sequence +// return errOldMessage if the sequence of proposal is smaller than current sequence +func (c *core) checkRequestMsg(request *istanbul.Request) error { + if request == nil || request.Proposal == nil { + return errInvalidMessage + } + + if c := c.current.Sequence().Cmp(request.Proposal.Number()); c > 0 { + return errOldMessage + } else if c < 0 { + return errFutureMessage + } else { + return nil + } +} + +var ( + maxNumberForRequestsQueue = big.NewInt(2 << (63 - 2)) +) + +func (c *core) storeRequestMsg(request *istanbul.Request) { + logger := c.newLogger("func", "storeRequestMsg") + + if request.Proposal.Number().Cmp(maxNumberForRequestsQueue) >= 0 { + logger.Debug("Dropping future request", "number", request.Proposal.Number(), "hash", request.Proposal.Hash()) + return + } + + logger.Trace("Store future request", "number", request.Proposal.Number(), "hash", request.Proposal.Hash()) + + c.pendingRequestsMu.Lock() + defer c.pendingRequestsMu.Unlock() + + c.pendingRequests.Push(request, -request.Proposal.Number().Int64()) +} + +func (c *core) processPendingRequests() { + c.pendingRequestsMu.Lock() + defer c.pendingRequestsMu.Unlock() + + for !(c.pendingRequests.Empty()) { + m, prio := c.pendingRequests.Pop() + r, ok := m.(*istanbul.Request) + if !ok { + c.logger.Warn("Malformed request, skip", "m", m) + continue + } + + // Push back if it's a future message + err := c.checkRequestMsg(r) + if err == nil { + c.logger.Trace("Post pending request", "number", r.Proposal.Number(), "hash", r.Proposal.Hash()) + + go c.sendEvent(istanbul.RequestEvent{ + Proposal: r.Proposal, + }) + } else if err == errFutureMessage { + c.logger.Trace("Stop processing request", "number", r.Proposal.Number(), "hash", r.Proposal.Hash()) + c.pendingRequests.Push(m, prio) + break + } else if err != nil { + c.logger.Trace("Skip the pending request", "number", r.Proposal.Number(), "hash", r.Proposal.Hash(), "err", err) + } + + } +}
diff --git go-ethereum/consensus/istanbul/proxy/proxy_engine.go celo/consensus/istanbul/proxy/proxy_engine.go new file mode 100644 index 0000000000000000000000000000000000000000..9b9ffd0fae5c8f3e77146d1645211175ac0530a7 --- /dev/null +++ celo/consensus/istanbul/proxy/proxy_engine.go @@ -0,0 +1,182 @@ +// Copyright 2017 The celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// BackendForProxyEngine provides the Istanbul backend application specific functions for Istanbul proxy engine +type BackendForProxyEngine interface { + // IsProxy returns true if this node is a proxy + IsProxy() bool + + // SelfNode returns the owner's node (if this is a proxy, it will return the external node) + SelfNode() *enode.Node + + // Multicast sends a message to it's connected nodes filtered on the 'addresses' parameter (where each address + // is associated with those node's signing key) + // If sendToSelf is set to true, then the function will send an event to self via a message event + Multicast(addresses []common.Address, payload []byte, ethMsgCode uint64, sendToSelf bool) error + + // Unicast will asynchronously send a celo message to peer + Unicast(peer consensus.Peer, payload []byte, ethMsgCode uint64) + + // GetValEnodeTableEntries retrieves the entries in the valEnodeTable filtered on the "validators" parameter. + // If the parameter is nil, then no filter will be applied. + GetValEnodeTableEntries(validators []common.Address) (map[common.Address]*istanbul.AddressEntry, error) + + // RewriteValEnodeTableEntries will rewrite the val enode table with "entries". + RewriteValEnodeTableEntries(entries map[common.Address]*istanbul.AddressEntry) error + + // SetEnodeCertificateMsgs will set this node's enodeCertificate to be used for connection handshakes + SetEnodeCertificateMsgMap(enodeCertificateMsgMap map[enode.ID]*istanbul.EnodeCertMsg) error + + // RetrieveEnodeCertificateMsgMap will retrieve this node's handshake enodeCertificate + RetrieveEnodeCertificateMsgMap() map[enode.ID]*istanbul.EnodeCertMsg + + // VerifyPendingBlockValidatorSignature is a message validation function to verify that a message's sender is within the validator set + // of the current pending block and that the message's address field matches the message's signature's signer + VerifyPendingBlockValidatorSignature(data []byte, sig []byte) (common.Address, error) + + // VerifyValidatorConnectionSetSignature is a message validation function to verify that a message's sender is within the + // validator connection set and that the message's address field matches the message's signature's signer + VerifyValidatorConnectionSetSignature(data []byte, sig []byte) (common.Address, error) + + // GetProxy returns the proxy engine created for this Backend. Note: This should be only used for the unit tests. + GetProxyEngine() ProxyEngine +} + +type proxyEngine struct { + config *istanbul.Config + logger log.Logger + backend BackendForProxyEngine + + // Proxied Validators peers and IDs + proxiedValidators map[consensus.Peer]bool + proxiedValidatorIDs map[enode.ID]bool + proxiedValidatorsMu sync.RWMutex +} + +// NewProxyEngine creates a new proxy engine. +func NewProxyEngine(backend BackendForProxyEngine, config *istanbul.Config) (ProxyEngine, error) { + if !backend.IsProxy() { + return nil, ErrNodeNotProxy + } + + p := &proxyEngine{ + config: config, + logger: log.New(), + backend: backend, + proxiedValidators: make(map[consensus.Peer]bool), + proxiedValidatorIDs: make(map[enode.ID]bool), + } + + return p, nil +} + +func (p *proxyEngine) HandleMsg(peer consensus.Peer, msgCode uint64, payload []byte) (bool, error) { + if msgCode == istanbul.ValEnodesShareMsg { + return p.handleValEnodesShareMsg(peer, payload) + } else if msgCode == istanbul.FwdMsg { + return p.handleForwardMsg(peer, payload) + } else if msgCode == istanbul.ConsensusMsg { + return p.handleConsensusMsg(peer, payload) + } else if msgCode == istanbul.EnodeCertificateMsg { + // See if the message is coming from the proxied validator + p.proxiedValidatorsMu.RLock() + msgFromProxiedVal := p.proxiedValidatorIDs[peer.Node().ID()] + p.proxiedValidatorsMu.RUnlock() + + if msgFromProxiedVal { + return p.handleEnodeCertificateMsgFromProxiedValidator(peer, payload) + } else { + return p.handleEnodeCertificateMsgFromRemoteVal(peer, payload) + } + } + + return false, nil +} + +// Callback once validator dials us and is properly registered. +func (p *proxyEngine) RegisterProxiedValidatorPeer(proxiedValidatorPeer consensus.Peer) { + p.proxiedValidatorsMu.Lock() + defer p.proxiedValidatorsMu.Unlock() + + logger := p.logger.New("func", "RegisterProxiedValidatorPeer") + logger.Info("Validator connected to proxy", "ID", proxiedValidatorPeer.Node().ID(), "enode", proxiedValidatorPeer.Node()) + + p.proxiedValidators[proxiedValidatorPeer] = true + p.proxiedValidatorIDs[proxiedValidatorPeer.Node().ID()] = true + +} + +func (p *proxyEngine) UnregisterProxiedValidatorPeer(proxiedValidatorPeer consensus.Peer) { + p.proxiedValidatorsMu.Lock() + defer p.proxiedValidatorsMu.Unlock() + + logger := p.logger.New("func", "UnregisterProxiedValidatorPeer") + logger.Info("Removing validator", "ID", proxiedValidatorPeer.Node().ID(), "enode", proxiedValidatorPeer.Node()) + + delete(p.proxiedValidators, proxiedValidatorPeer) + delete(p.proxiedValidatorIDs, proxiedValidatorPeer.Node().ID()) + +} + +func (p *proxyEngine) GetProxiedValidatorsInfo() ([]*ProxiedValidatorInfo, error) { + p.proxiedValidatorsMu.RLock() + defer p.proxiedValidatorsMu.RUnlock() + + proxiedValidatorsInfo := []*ProxiedValidatorInfo{} + for proxiedValidatorPeer := range p.proxiedValidators { + pubKey := proxiedValidatorPeer.Node().Pubkey() + addr := crypto.PubkeyToAddress(*pubKey) + proxiedValidatorInfo := &ProxiedValidatorInfo{ + Address: addr, + IsPeered: true, + Node: proxiedValidatorPeer.Node()} + proxiedValidatorsInfo = append(proxiedValidatorsInfo, proxiedValidatorInfo) + } + return proxiedValidatorsInfo, nil +} + +// SendMsgToProxiedValidators will send a `celo` message to the proxied validators. +func (p *proxyEngine) SendMsgToProxiedValidators(msgCode uint64, msg *istanbul.Message) error { + logger := p.logger.New("func", "SendMsgToProxiedValidators") + p.proxiedValidatorsMu.RLock() + defer p.proxiedValidatorsMu.RUnlock() + if len(p.proxiedValidators) == 0 { + logger.Warn("Proxy has no connected proxied validator. Not sending message.") + return nil + } + payload, err := msg.Payload() + if err != nil { + logger.Error("Error getting payload of message", "err", err) + return err + } + for proxiedValidator := range p.proxiedValidators { + p.backend.Unicast(proxiedValidator, payload, msgCode) + } + return nil +}
diff --git go-ethereum/consensus/istanbul/announce/valproxy.go celo/consensus/istanbul/announce/valproxy.go new file mode 100644 index 0000000000000000000000000000000000000000..52c99ad0a3be3d5d7dbd942d56499979ebeb385e --- /dev/null +++ celo/consensus/istanbul/announce/valproxy.go @@ -0,0 +1,79 @@ +package announce + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul/proxy" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// ValProxyAssigmentProvider is responsible for, given a list of validator addresses, +// returning a mapping <validator address, assigned eNode>, where each validator will +// be assigned to an external eNode this node. +// E.g if this node has two proxies: A and B, it is possible to assign A to a set of +// validators, and B to another set, therefore splitting the load of validator to +// validator connections through these proxies instances. +// If this node has no proxy, then all values should be the self eNode. +type ValProxyAssigmnentProvider interface { + // GetValProxyAssignments returns the remote validator -> external node assignments. + // If this is a standalone validator, it will set the external node to itself. + // If this is a proxied validator, it will set external node to the proxy's external node. + GetValProxyAssignments(valAddresses []common.Address) (map[common.Address]*enode.Node, error) +} + +type proxyVPAP struct { + proxyAssigmentsFn func([]common.Address) (map[common.Address]*proxy.Proxy, error) +} + +func NewProxiedValProxyAssigmentProvider(proxyAssigmentsFn func([]common.Address) (map[common.Address]*proxy.Proxy, error)) ValProxyAssigmnentProvider { + return &proxyVPAP{ + proxyAssigmentsFn: proxyAssigmentsFn, + } +} + +func (p *proxyVPAP) GetValProxyAssignments(valAddresses []common.Address) (map[common.Address]*enode.Node, error) { + var valProxyAssignments map[common.Address]*enode.Node = make(map[common.Address]*enode.Node) + var proxies map[common.Address]*proxy.Proxy // This var is only used if this is a proxied validator + + for _, valAddress := range valAddresses { + var externalNode *enode.Node + + if proxies == nil { + var err error + proxies, err = p.proxyAssigmentsFn(nil) + if err != nil { + return nil, err + } + } + proxyObj := proxies[valAddress] + if proxyObj == nil { + continue + } + + externalNode = proxyObj.ExternalNode() + + valProxyAssignments[valAddress] = externalNode + } + + return valProxyAssignments, nil +} + +type selfVPAP struct { + selfNodeFn func() *enode.Node +} + +func NewSelfValProxyAssigmentProvider(selfNodeFn func() *enode.Node) ValProxyAssigmnentProvider { + return &selfVPAP{ + selfNodeFn: selfNodeFn, + } +} + +func (p *selfVPAP) GetValProxyAssignments(valAddresses []common.Address) (map[common.Address]*enode.Node, error) { + var valProxyAssignments map[common.Address]*enode.Node = make(map[common.Address]*enode.Node) + var selfEnode *enode.Node = p.selfNodeFn() + + for _, valAddress := range valAddresses { + valProxyAssignments[valAddress] = selfEnode + } + + return valProxyAssignments, nil +}
diff --git go-ethereum/consensus/istanbul/backend/peer_handler.go celo/consensus/istanbul/backend/peer_handler.go new file mode 100644 index 0000000000000000000000000000000000000000..ed79be25ead4b62987483e6c0a5408b00fad25ac --- /dev/null +++ celo/consensus/istanbul/backend/peer_handler.go @@ -0,0 +1,166 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backend + +import ( + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +type validatorPeerHandler struct { + sb *Backend + + threadRunning bool + threadRunningMu sync.RWMutex + threadWg *sync.WaitGroup + threadQuit chan struct{} +} + +func newVPH(sb *Backend) *validatorPeerHandler { + return &validatorPeerHandler{ + sb: sb, + threadWg: new(sync.WaitGroup), + threadQuit: make(chan struct{}), + } +} + +func (vph *validatorPeerHandler) startThread() error { + vph.threadRunningMu.Lock() + defer vph.threadRunningMu.Unlock() + if vph.threadRunning { + return istanbul.ErrStartedVPHThread + } + + vph.threadRunning = true + vph.threadWg.Add(1) + go vph.thread() + + return nil +} + +func (vph *validatorPeerHandler) stopThread() error { + vph.threadRunningMu.Lock() + defer vph.threadRunningMu.Unlock() + + if !vph.threadRunning { + return istanbul.ErrStoppedVPHThread + } + + vph.threadQuit <- struct{}{} + vph.threadWg.Wait() + + vph.threadRunning = false + return nil +} + +func (vph *validatorPeerHandler) thread() { + defer vph.threadWg.Done() + + refreshValidatorPeersTicker := time.NewTicker(1 * time.Minute) + + refreshValPeersFunc := func() { + if vph.MaintainValConnections() { + if err := vph.sb.RefreshValPeers(); err != nil { + vph.sb.logger.Warn("Error refreshing validator peers", "err", err) + } + } + } + + refreshValPeersFunc() + // Every 5 minute, check to see if we need to refresh the validator peers + for { + select { + case <-refreshValidatorPeersTicker.C: + refreshValPeersFunc() + + case <-vph.threadQuit: + refreshValidatorPeersTicker.Stop() + return + } + } +} + +// Returns whether this node should maintain validator connections +// Only proxies and non proxied validators need to connect maintain validator connections +func (vph *validatorPeerHandler) MaintainValConnections() bool { + return vph.sb.IsProxy() || (vph.sb.IsValidator() && !vph.sb.IsProxiedValidator()) +} + +func (vph *validatorPeerHandler) AddValidatorPeer(node *enode.Node, address common.Address) { + if !vph.MaintainValConnections() { + return + } + + // Connect to the remote peer if it's part of the current epoch's valset and + // if this node is also part of the current epoch's valset + valConnSet, err := vph.sb.RetrieveValidatorConnSet() + if err != nil { + vph.sb.logger.Error("Error in retrieving val conn set in AddValidatorPeer", "err", err) + return + } + if valConnSet[address] && valConnSet[vph.sb.ValidatorAddress()] { + vph.sb.p2pserver.AddPeer(node, p2p.ValidatorPurpose) + vph.sb.p2pserver.AddTrustedPeer(node, p2p.ValidatorPurpose) + } +} + +func (vph *validatorPeerHandler) RemoveValidatorPeer(node *enode.Node) { + vph.sb.p2pserver.RemovePeer(node, p2p.ValidatorPurpose) + vph.sb.p2pserver.RemoveTrustedPeer(node, p2p.ValidatorPurpose) +} + +func (vph *validatorPeerHandler) ReplaceValidatorPeers(newNodes []*enode.Node) { + nodeIDSet := make(map[enode.ID]bool) + for _, node := range newNodes { + nodeIDSet[node.ID()] = true + } + + // Remove old Validator Peers + for existingPeerID, existingPeer := range vph.sb.broadcaster.FindPeers(nil, p2p.ValidatorPurpose) { + if !nodeIDSet[existingPeerID] { + vph.RemoveValidatorPeer(existingPeer.Node()) + } + } + + if vph.MaintainValConnections() { + // Add new Validator Peers (adds all the nodes in newNodes. Note that add is noOp on already existent ones) + for _, newNode := range newNodes { + vph.sb.p2pserver.AddPeer(newNode, p2p.ValidatorPurpose) + vph.sb.p2pserver.AddTrustedPeer(newNode, p2p.ValidatorPurpose) + } + } +} + +func (vph *validatorPeerHandler) ClearValidatorPeers() { + for _, peer := range vph.sb.broadcaster.FindPeers(nil, p2p.ValidatorPurpose) { + vph.sb.p2pserver.RemovePeer(peer.Node(), p2p.ValidatorPurpose) + vph.sb.p2pserver.RemoveTrustedPeer(peer.Node(), p2p.ValidatorPurpose) + } +} + +func (sb *Backend) AddPeer(node *enode.Node, purpose p2p.PurposeFlag) { + sb.p2pserver.AddPeer(node, purpose) +} + +func (sb *Backend) RemovePeer(node *enode.Node, purpose p2p.PurposeFlag) { + sb.p2pserver.RemovePeer(node, purpose) +}
diff --git go-ethereum/consensus/istanbul/core/handler.go celo/consensus/istanbul/core/handler.go new file mode 100644 index 0000000000000000000000000000000000000000..d400b9961f6a20f4b40e26b6a7777da4d26c6ff9 --- /dev/null +++ celo/consensus/istanbul/core/handler.go @@ -0,0 +1,247 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" +) + +// Start implements core.Engine.Start +func (c *core) Start() error { + rsdb, err := newRoundStateDB(c.config.RoundStateDBPath, nil) + if err != nil { + log.Crit("Failed to open RoundStateDB", "err", err) + } + c.rsdb = rsdb + roundState, err := c.createRoundState() + if err != nil { + return err + } + + c.current = roundState + c.roundChangeSetV2 = newRoundChangeSetV2(c.current.ValidatorSet()) + + // Reset the Round Change timer for the current round to timeout. + // (If we've restored RoundState such that we are in StateWaitingForRoundChange, + // this may also start a timer to send a repeat round change message.) + c.resetRoundChangeTimer() + + // Process backlog + c.processPendingRequests() + c.backlog.updateState(c.CurrentView(), c.current.State()) + + // Tests will handle events itself, so we have to make subscribeEvents() + // be able to call in test. + c.subscribeEvents() + + c.handlerWg.Add(1) + go c.handleEvents() + + return nil +} + +// Stop implements core.Engine.Stop +func (c *core) Stop() error { + c.stopAllTimers() + c.unsubscribeEvents() + + // Make sure the handler goroutine exits + c.handlerWg.Wait() + + err := c.rsdb.Close() + c.currentMu.Lock() + defer c.currentMu.Unlock() + c.current = nil + return err +} + +// ---------------------------------------------------------------------------- + +// Subscribe both internal and external events +func (c *core) subscribeEvents() { + c.events = c.backend.EventMux().Subscribe( + // external events + istanbul.RequestEvent{}, + istanbul.MessageEvent{}, + // internal events + backlogEvent{}, + ) + c.timeoutSub = c.backend.EventMux().Subscribe( + timeoutAndMoveToNextRoundEvent{}, + resendRoundChangeEvent{}, + ) + c.finalCommittedSub = c.backend.EventMux().Subscribe( + istanbul.FinalCommittedEvent{}, + ) +} + +// Unsubscribe all events +func (c *core) unsubscribeEvents() { + c.events.Unsubscribe() + c.timeoutSub.Unsubscribe() + c.finalCommittedSub.Unsubscribe() +} + +func (c *core) handleEvents() { + // Clear state + defer c.handlerWg.Done() + + for { + logger := c.newLogger("func", "handleEvents") + select { + case event, ok := <-c.events.Chan(): + if !ok { + return + } + // A real event arrived, process interesting content + switch ev := event.Data.(type) { + case istanbul.RequestEvent: + r := &istanbul.Request{ + Proposal: ev.Proposal, + } + err := c.handleRequest(r) + if err == errFutureMessage { + c.storeRequestMsg(r) + } + case istanbul.MessageEvent: + if err := c.handleMsg(ev.Payload); err != nil && err != errFutureMessage && err != errOldMessage { + logger.Warn("Error in handling istanbul message", "err", err) + } + case backlogEvent: + if payload, err := ev.msg.Payload(); err != nil { + logger.Error("Error in retrieving payload from istanbul message that was sent from a backlog event", "err", err) + } else { + if err := c.handleMsg(payload); err != nil && err != errFutureMessage && err != errOldMessage { + logger.Warn("Error in handling istanbul message that was sent from a backlog event", "err", err) + } + } + } + case event, ok := <-c.timeoutSub.Chan(): + if !ok { + return + } + switch ev := event.Data.(type) { + case timeoutAndMoveToNextRoundEvent: + if err := c.handleTimeoutAndMoveToNextRound(ev.view); err != nil { + logger.Error("Error on handleTimeoutAndMoveToNextRound", "err", err) + } + case resendRoundChangeEvent: + if err := c.handleResendRoundChangeEvent(ev.view); err != nil { + logger.Error("Error on handleResendRoundChangeEvent", "err", err) + } + } + case event, ok := <-c.finalCommittedSub.Chan(): + if !ok { + return + } + switch event.Data.(type) { + case istanbul.FinalCommittedEvent: + if err := c.handleFinalCommitted(); err != nil { + logger.Error("Error on handleFinalCommit", "err", err) + } + } + } + } +} + +// sendEvent sends events to mux +func (c *core) sendEvent(ev interface{}) { + c.backend.EventMux().Post(ev) +} + +func (c *core) handleMsg(payload []byte) error { + logger := c.newLogger("func", "handleMsg") + + // Decode message and check its signature + msg := new(istanbul.Message) + logger.Debug("Got new message", "payload", hexutil.Encode(payload)) + if err := msg.FromPayload(payload, c.validateFn); err != nil { + logger.Debug("Failed to decode message from payload", "err", err) + return err + } + + // Only accept message if the address is valid + _, src := c.current.ValidatorSet().GetByAddress(msg.Address) + if src == nil { + logger.Error("Invalid address in message", "m", msg) + return istanbul.ErrUnauthorizedAddress + } + + return c.handleCheckedMsg(msg, src) +} + +func (c *core) handleCheckedMsg(msg *istanbul.Message, src istanbul.Validator) error { + logger := c.newLogger("func", "handleCheckedMsg", "from", msg.Address) + + // Store the message if it's a future message + catchFutureMessages := func(err error) error { + if err == errFutureMessage { + // Store in backlog (if it's not from self) + if msg.Address != c.address { + c.backlog.store(msg) + } + } + return err + } + + switch msg.Code { + case istanbul.MsgPreprepareV2: + return catchFutureMessages(c.handlePreprepareV2(msg)) + case istanbul.MsgPrepare: + return catchFutureMessages(c.handlePrepare(msg)) + case istanbul.MsgCommit: + return catchFutureMessages(c.handleCommit(msg)) + case istanbul.MsgRoundChangeV2: + return catchFutureMessages(c.handleRoundChangeV2(msg)) + default: + logger.Error("Invalid message", "m", msg) + } + + return errInvalidMessage +} + +func (c *core) handleTimeoutAndMoveToNextRound(timedOutView *istanbul.View) error { + logger := c.newLogger("func", "handleTimeoutAndMoveToNextRound", "timed_out_seq", timedOutView.Sequence, "timed_out_round", timedOutView.Round) + + // Avoid races where message is enqueued then a later event advances sequence or desired round. + if c.current.Sequence().Cmp(timedOutView.Sequence) != 0 || c.current.DesiredRound().Cmp(timedOutView.Round) != 0 { + logger.Trace("Timed out but now on a different view") + return nil + } + + logger.Debug("Timed out, trying to wait for next round") + nextRound := new(big.Int).Add(timedOutView.Round, common.Big1) + return c.waitForDesiredRound(nextRound) +} + +func (c *core) handleResendRoundChangeEvent(desiredView *istanbul.View) error { + logger := c.newLogger("func", "handleResendRoundChangeEvent", "set_at_seq", desiredView.Sequence, "set_at_desiredRound", desiredView.Round) + + // Avoid races where message is enqueued then a later event advances sequence or desired round. + if c.current.Sequence().Cmp(desiredView.Sequence) != 0 || c.current.DesiredRound().Cmp(desiredView.Round) != 0 { + logger.Trace("Timed out but now on a different view") + return nil + } + + c.resendRoundChangeMessage() + return nil +}
diff --git go-ethereum/consensus/istanbul/announce/util.go celo/consensus/istanbul/announce/util.go new file mode 100644 index 0000000000000000000000000000000000000000..717850855b87c389e0e6131ccf725c24b18d5dfb --- /dev/null +++ celo/consensus/istanbul/announce/util.go @@ -0,0 +1,23 @@ +package announce + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// DecryptAndParseEnodeURL decrypts an encrypted enodeURL with the given ecdsa key pair, and parses the +// resulting Node. +func DecryptAndParseEnodeURL(ecdsa *istanbul.EcdsaInfo, encEnodeURL []byte) (*enode.Node, error) { + enodeBytes, err := ecdsa.Decrypt(encEnodeURL) + if err != nil { + return nil, fmt.Errorf("Error decrypting endpoint. err=%v. encEnodeURL: '%v'", err, encEnodeURL) + } + enodeURL := string(enodeBytes) + node, err := enode.ParseV4(enodeURL) + if err != nil { + return nil, fmt.Errorf("Error parsing enodeURL. err=%v. enodeURL: '%v'", err, enodeURL) + } + return node, err +}
diff --git go-ethereum/consensus/istanbul/core/backlog_test.go celo/consensus/istanbul/core/backlog_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5588dbc816e58ffd00281640a8578cb4df5c29a8 --- /dev/null +++ celo/consensus/istanbul/core/backlog_test.go @@ -0,0 +1,473 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "fmt" + "math/big" + "reflect" + "testing" + "time" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/event" + elog "github.com/ethereum/go-ethereum/log" +) + +func TestCheckMessage(t *testing.T) { + testLogger.SetHandler(elog.StdoutHandler) + backend := &testSystemBackend{ + events: new(event.TypeMux), + } + valSet := newTestValidatorSet(4) + c := &core{ + logger: testLogger, + backend: backend, + current: newRoundState(&istanbul.View{ + Sequence: big.NewInt(2), + Round: big.NewInt(2), + }, valSet, valSet.GetByIndex(0)), + } + + t.Run("invalid view format", func(t *testing.T) { + err := c.checkMessage(istanbul.MsgPreprepareV2, nil) + if err != errInvalidMessage { + t.Errorf("error mismatch: have %v, want %v", err, errInvalidMessage) + } + }) + + testStates := []State{StateAcceptRequest, StatePreprepared, StatePrepared, StateCommitted, StateWaitingForNewRound} + testCodes := []uint64{istanbul.MsgPreprepareV2, istanbul.MsgPrepare, istanbul.MsgCommit, istanbul.MsgRoundChangeV2} + + // accept Commits from sequence, round matching LastSubject + t.Run("Rejects all other older rounds", func(t *testing.T) { + v := &istanbul.View{ + Sequence: big.NewInt(2), + Round: big.NewInt(1), + } + for _, testState := range testStates { + for _, testCode := range testCodes { + c.current.(*roundStateImpl).state = testState + err := c.checkMessage(testCode, v) + + if err != errOldMessage { + t.Errorf("error mismatch: have %v, want %v", err, errOldMessage) + } + + } + } + }) + + t.Run("Rejects all other older sequences", func(t *testing.T) { + v := &istanbul.View{ + Sequence: big.NewInt(0), + Round: big.NewInt(0), + } + for _, testState := range testStates { + for _, testCode := range testCodes { + c.current.(*roundStateImpl).state = testState + err := c.checkMessage(testCode, v) + if err != errOldMessage { + t.Errorf("error mismatch: have %v, want %v", err, errOldMessage) + } + } + } + }) + + t.Run("Future sequence", func(t *testing.T) { + v := &istanbul.View{ + Sequence: big.NewInt(3), + Round: big.NewInt(0), + } + for _, testState := range testStates { + for _, testCode := range testCodes { + c.current.(*roundStateImpl).state = testState + err := c.checkMessage(testCode, v) + if err != errFutureMessage { + t.Errorf("error mismatch: have %v, want %v", err, errFutureMessage) + } + } + } + }) + + t.Run("future round", func(t *testing.T) { + v := &istanbul.View{ + Sequence: big.NewInt(2), + Round: big.NewInt(3), + } + for _, testState := range testStates { + for _, testCode := range testCodes { + c.current.(*roundStateImpl).state = testState + err := c.checkMessage(testCode, v) + if testCode == istanbul.MsgRoundChangeV2 { + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + } else if err != errFutureMessage { + t.Errorf("error mismatch: have %v, want %v", err, errFutureMessage) + } + } + } + }) + + t.Run("current view, state = StateAcceptRequest", func(t *testing.T) { + v := c.current.View() + c.current.(*roundStateImpl).state = StateAcceptRequest + + for _, testCode := range testCodes { + err := c.checkMessage(testCode, v) + if testCode == istanbul.MsgRoundChangeV2 { + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + } else if testCode == istanbul.MsgPreprepareV2 { + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + } else { + if err != errFutureMessage { + t.Errorf("error mismatch: have %v, want %v", err, errFutureMessage) + } + } + } + }) + + t.Run("current view, state = StatePreprepared", func(t *testing.T) { + v := c.current.View() + c.current.(*roundStateImpl).state = StatePreprepared + for _, testCode := range testCodes { + err := c.checkMessage(testCode, v) + if testCode == istanbul.MsgRoundChangeV2 { + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + } else if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + } + }) + + t.Run("current view, state = StatePrepared", func(t *testing.T) { + v := c.current.View() + c.current.(*roundStateImpl).state = StatePrepared + for _, testCode := range testCodes { + err := c.checkMessage(testCode, v) + if testCode == istanbul.MsgRoundChangeV2 { + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + } else if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + } + }) + + t.Run("current view, state = StateCommited", func(t *testing.T) { + v := c.current.View() + c.current.(*roundStateImpl).state = StateCommitted + for _, testCode := range testCodes { + err := c.checkMessage(testCode, v) + if testCode == istanbul.MsgRoundChangeV2 { + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + } else if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + } + }) + + t.Run("current view, state = StateWaitingForNewRound", func(t *testing.T) { + v := c.current.View() + c.current.(*roundStateImpl).state = StateWaitingForNewRound + for _, testCode := range testCodes { + err := c.checkMessage(testCode, v) + if testCode == istanbul.MsgRoundChangeV2 || testCode == istanbul.MsgPreprepareV2 { + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + } else if err != errFutureMessage { + t.Errorf("error mismatch: have %v, want %v", err, errFutureMessage) + } + } + }) + +} + +func TestStoreBacklog(t *testing.T) { + testLogger.SetHandler(elog.StdoutHandler) + backlog := newMsgBacklog( + func(msg *istanbul.Message) {}, + func(msgCode uint64, msgView *istanbul.View) error { return nil }, + ).(*msgBacklogImpl) + defer backlog.clearBacklogForSeq(12) + + v10 := &istanbul.View{ + Round: big.NewInt(10), + Sequence: big.NewInt(10), + } + + v11 := &istanbul.View{ + Round: big.NewInt(12), + Sequence: big.NewInt(11), + } + p1 := validator.New(common.BytesToAddress([]byte("12345667890")), blscrypto.SerializedPublicKey{}) + p2 := validator.New(common.BytesToAddress([]byte("47324349949")), blscrypto.SerializedPublicKey{}) + + mPreprepare := istanbul.NewPreprepareV2Message( + &istanbul.PreprepareV2{View: v10, Proposal: makeBlock(10)}, + p1.Address(), + ) + backlog.store(mPreprepare) + + msg := backlog.backlogBySeq[v10.Sequence.Uint64()].PopItem() + if !reflect.DeepEqual(msg, mPreprepare) { + t.Errorf("message mismatch: have %v, want %v", msg, mPreprepare) + } + + mPrepare := istanbul.NewPrepareMessage( + &istanbul.Subject{View: v10, Digest: common.BytesToHash([]byte("1234567890"))}, + p1.Address(), + ) + mPreprepare2 := istanbul.NewPreprepareV2Message( + &istanbul.PreprepareV2{View: v11, Proposal: makeBlock(11)}, + p2.Address(), + ) + + backlog.store(mPreprepare) + backlog.store(mPrepare) + backlog.store(mPreprepare2) + + if backlog.msgCountBySrc[p1.Address()] != 3 { + t.Errorf("msgCountBySrc mismatch: have %v, want 3", backlog.msgCountBySrc[p1.Address()]) + } + + mCommit := istanbul.NewCommitMessage( + &istanbul.CommittedSubject{Subject: mPrepare.Prepare(), CommittedSeal: []byte{0x63, 0x65, 0x6C, 0x6F}}, // celo in hex! + p1.Address(), + ) + + backlog.store(mCommit) + if backlog.msgCountBySrc[p2.Address()] != 1 { + t.Errorf("msgCountBySrc mismatch: have %v, want 1", backlog.msgCountBySrc[p2.Address()]) + } + if backlog.msgCount != 5 { + t.Errorf("msgCount mismatch: have %v, want 5", backlog.msgCount) + } + + // Should get back v10 preprepare then commit + msg = backlog.backlogBySeq[v10.Sequence.Uint64()].PopItem() + if !reflect.DeepEqual(msg, mPreprepare) { + t.Errorf("message mismatch: have %v, want %v", msg, mPreprepare2) + } + msg = backlog.backlogBySeq[v10.Sequence.Uint64()].PopItem() + if !reflect.DeepEqual(msg, mCommit) { + t.Errorf("message mismatch: have %v, want %v", msg, mCommit) + + } + msg = backlog.backlogBySeq[v11.Sequence.Uint64()].PopItem() + if !reflect.DeepEqual(msg, mPreprepare2) { + t.Errorf("message mismatch: have %v, want %v", msg, mPreprepare2) + } +} + +func TestClearBacklogForSequence(t *testing.T) { + testLogger.SetHandler(elog.StdoutHandler) + + processed := false + backlog := newMsgBacklog( + func(msg *istanbul.Message) { processed = true }, + func(msgCode uint64, msgView *istanbul.View) error { return nil }, + ).(*msgBacklogImpl) + + // The backlog's state is sequence number 1, round 0. Store future messages with sequence number 2 + p1 := validator.New(common.BytesToAddress([]byte("12345667890")), blscrypto.SerializedPublicKey{}) + + mPreprepare := istanbul.NewPreprepareV2Message( + &istanbul.PreprepareV2{ + View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(2)}, + Proposal: makeBlock(2), + }, + p1.Address(), + ) + + numMsgs := 20 + for i := 0; i < numMsgs; i++ { + backlog.store(mPreprepare) + } + + // Sanity check that storing the messages worked + if backlog.msgCount != numMsgs { + t.Errorf("initial message count mismatch: have %d, want %d", backlog.msgCount, numMsgs) + } + // Try clearing a different sequence number, there should be no effect + backlog.clearBacklogForSeq(3) + if backlog.msgCount != numMsgs { + t.Errorf("middle message count mismatch: have %d, want %d", backlog.msgCount, numMsgs) + } + // Clear the messages with the right sequence number, should empty the backlog + backlog.clearBacklogForSeq(2) + if backlog.msgCount > 0 { + t.Errorf("backlog was not empty: msgCount %d", backlog.msgCount) + } + // The processor should not be called with the messages when clearBacklogForSeq() is called + if processed { + t.Errorf("backlog messages were processed during clearing") + } +} + +func TestProcessFutureBacklog(t *testing.T) { + testLogger.SetHandler(elog.StdoutHandler) + + backlog := newMsgBacklog( + func(msg *istanbul.Message) {}, + func(msgCode uint64, msgView *istanbul.View) error { return nil }, + ).(*msgBacklogImpl) + defer backlog.clearBacklogForSeq(12) + + futureSequence := big.NewInt(10) + oldSequence := big.NewInt(0) + + // push a future msg + valSet := newTestValidatorSet(4) + mFuture := istanbul.NewCommitMessage( + &istanbul.CommittedSubject{ + Subject: &istanbul.Subject{ + View: &istanbul.View{Round: big.NewInt(10), Sequence: futureSequence}, + Digest: common.BytesToHash([]byte("1234567890")), + }, + CommittedSeal: []byte{0x63, 0x65, 0x6C, 0x6F}, + }, + valSet.GetByIndex(0).Address()) + + backlog.store(mFuture) + + // push a message from the past and check we expire it + addr := valSet.GetByIndex(1).Address() + roundChangeV2 := &istanbul.RoundChangeV2{ + Request: istanbul.RoundChangeRequest{ + Address: addr, + View: istanbul.View{ + Round: big.NewInt(0), + Sequence: oldSequence, + }, + PreparedCertificateV2: istanbul.PCV2FromPCV1(istanbul.PreparedCertificate{ + Proposal: makeBlock(0), + }), + }, + } + // No need to sign the RoundChangeRequest for this test + mPast := istanbul.NewRoundChangeV2Message(roundChangeV2, addr) + + backlog.store(mPast) + + // Should prune old messages + backlog.processBacklog() + + backlogSeqs := backlog.getSortedBacklogSeqs() + if len(backlogSeqs) != 1 || backlogSeqs[0] != futureSequence.Uint64() { + t.Errorf("getSortedBacklogSeqs mismatch: have %v", backlogSeqs) + } + + backlog.updateState(&istanbul.View{ + Sequence: big.NewInt(1), + Round: big.NewInt(0), + }, StateAcceptRequest) + + // Check message from future remains, past expired + if backlog.msgCount != 1 || backlog.msgCountBySrc[valSet.GetByIndex(1).Address()] > 0 { + t.Errorf("backlog mismatch: %v", backlog.msgCountBySrc) + } +} + +func TestProcessBacklog(t *testing.T) { + v := &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + } + + subject := &istanbul.Subject{ + View: v, + Digest: common.BytesToHash([]byte("1234567890")), + } + address := common.BytesToAddress([]byte("0xce10ce10")) + + msgs := []*istanbul.Message{ + istanbul.NewPreprepareV2Message( + &istanbul.PreprepareV2{View: v, Proposal: makeBlock(1)}, + address, + ), + istanbul.NewPrepareMessage(subject, address), + istanbul.NewCommitMessage( + &istanbul.CommittedSubject{Subject: subject, CommittedSeal: []byte{0x63, 0x65, 0x6C, 0x6F}}, + address, + ), + istanbul.NewRoundChangeV2Message( + &istanbul.RoundChangeV2{ + Request: istanbul.RoundChangeRequest{View: *v}, PreparedProposal: makeBlock(1)}, + address, + ), + } + for i := 0; i < len(msgs); i++ { + t.Run(fmt.Sprintf("Msg with code %d", msgs[i].Code), func(t *testing.T) { + testProcessBacklog(t, msgs[i]) + }) + } +} + +func testProcessBacklog(t *testing.T, msg *istanbul.Message) { + + testLogger.SetHandler(elog.StdoutHandler) + + processedMsgs := make(chan uint64, 100) + registerCall := func(msg *istanbul.Message) { + processedMsgs <- msg.Code + // we expect only one msg + close(processedMsgs) + } + + backlog := newMsgBacklog( + registerCall, + func(msgCode uint64, msgView *istanbul.View) error { return nil }, + ).(*msgBacklogImpl) + defer backlog.clearBacklogForSeq(12) + + v := &istanbul.View{ + Round: big.NewInt(0), + Sequence: big.NewInt(1), + } + + msg.Address = common.Address{50} + backlog.store(msg) + + backlog.updateState(v, State(msg.Code)) + + timeout := time.NewTimer(1 * time.Second) + + select { + case got := <-processedMsgs: + if got != msg.Code { + t.Errorf("Expected different msg: have: %v, want: %v", got, msg.Code) + } + case <-timeout.C: + t.Errorf("No Message was processed") + } + +}
diff --git go-ethereum/mobile/geth_android.go celo/consensus/istanbul/core/events.go rename from mobile/geth_android.go rename to consensus/istanbul/core/events.go index cfdf1c28c97f3d380f2a6a29f88c44a299c50aa5..2bdafbcbe2019d1c5b8c529d879bd105de452a87 100644 --- go-ethereum/mobile/geth_android.go +++ celo/consensus/istanbul/core/events.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -14,10 +14,19 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.   -//go:build android -// +build android +package core + +import ( + "github.com/ethereum/go-ethereum/consensus/istanbul" +)   -package geth +type backlogEvent struct { + msg *istanbul.Message +}   -// clientIdentifier is a hard coded identifier to report into the network. -var clientIdentifier = "GethDroid" +type resendRoundChangeEvent struct { + view *istanbul.View +} +type timeoutAndMoveToNextRoundEvent struct { + view *istanbul.View +}
diff --git go-ethereum/consensus/istanbul/core/message_set.go celo/consensus/istanbul/core/message_set.go new file mode 100644 index 0000000000000000000000000000000000000000..e40c50a34118e225f0a018f0b9a5cc4d979bcd89 --- /dev/null +++ celo/consensus/istanbul/core/message_set.go @@ -0,0 +1,232 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "fmt" + "io" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/rlp" +) + +type MessageSet interface { + fmt.Stringer + Add(msg *istanbul.Message) error + AddAll(msgs []*istanbul.Message) int + GetAddressIndex(addr common.Address) (uint64, error) + Remove(address common.Address) + Values() (result []*istanbul.Message) + Size() int + Get(addr common.Address) *istanbul.Message + Addresses() []common.Address + Serialize() ([]byte, error) +} + +type messageSetImpl struct { + valSet istanbul.ValidatorSet + messagesMu *sync.Mutex + messages map[common.Address]*istanbul.Message +} + +// Construct a new message set to accumulate messages for given sequence/view number. +func newMessageSet(valSet istanbul.ValidatorSet) MessageSet { + return &messageSetImpl{ + messagesMu: new(sync.Mutex), + messages: make(map[common.Address]*istanbul.Message), + valSet: valSet, + } +} + +func deserializeMessageSet(binaryData []byte) (MessageSet, error) { + var ms messageSetImpl + + err := rlp.DecodeBytes(binaryData, &ms) + if err != nil { + return nil, err + } + return &ms, nil +} + +// ---------------------------------------------------------------------------- + +func (ms *messageSetImpl) checkValidToAdd(msg *istanbul.Message) error { + if !ms.valSet.ContainsByAddress(msg.Address) { + return istanbul.ErrUnauthorizedAddress + } + return nil +} + +func (ms *messageSetImpl) Add(msg *istanbul.Message) error { + ms.messagesMu.Lock() + defer ms.messagesMu.Unlock() + + if err := ms.checkValidToAdd(msg); err != nil { + return err + } + ms.messages[msg.Address] = msg + + return nil +} + +func (ms *messageSetImpl) AddAll(msgs []*istanbul.Message) int { + ms.messagesMu.Lock() + defer ms.messagesMu.Unlock() + var added = 0 + for _, msg := range msgs { + err := ms.checkValidToAdd(msg) + // Do not overwrite existing message by the same address + if err == nil && ms.messages[msg.Address] == nil { + ms.messages[msg.Address] = msg + added++ + } + } + return added +} + +func (ms *messageSetImpl) GetAddressIndex(addr common.Address) (uint64, error) { + ms.messagesMu.Lock() + defer ms.messagesMu.Unlock() + + i := ms.valSet.GetIndex(addr) + if i == -1 { + return 0, istanbul.ErrUnauthorizedAddress + } + + return uint64(i), nil +} + +func (ms *messageSetImpl) Remove(address common.Address) { + ms.messagesMu.Lock() + defer ms.messagesMu.Unlock() + + delete(ms.messages, address) +} + +func (ms *messageSetImpl) Values() (result []*istanbul.Message) { + ms.messagesMu.Lock() + defer ms.messagesMu.Unlock() + + for _, v := range ms.messages { + result = append(result, v) + } + + return result +} + +func (ms *messageSetImpl) Size() int { + ms.messagesMu.Lock() + defer ms.messagesMu.Unlock() + return len(ms.messages) +} + +func (ms *messageSetImpl) Get(addr common.Address) *istanbul.Message { + ms.messagesMu.Lock() + defer ms.messagesMu.Unlock() + return ms.messages[addr] +} + +func (ms *messageSetImpl) Addresses() []common.Address { + ms.messagesMu.Lock() + defer ms.messagesMu.Unlock() + returnList := make([]common.Address, len(ms.messages)) + + i := 0 + for addr := range ms.messages { + returnList[i] = addr + i++ + } + + return returnList +} + +func (ms *messageSetImpl) String() string { + ms.messagesMu.Lock() + defer ms.messagesMu.Unlock() + addresses := make([]string, 0, len(ms.messages)) + for _, v := range ms.messages { + addresses = append(addresses, v.Address.String()) + } + return fmt.Sprintf("[<%v> %v]", len(ms.messages), strings.Join(addresses, ", ")) +} + +func (s *messageSetImpl) Serialize() ([]byte, error) { + return rlp.EncodeToBytes(s) +} + +// RLP Encoding ----------------------------------------------------------------------- + +// EncodeRLP impl +func (s *messageSetImpl) EncodeRLP(w io.Writer) error { + serializedValSet, err := s.valSet.Serialize() + if err != nil { + return err + } + + messageKeys := make([]common.Address, len(s.messages)) + messageValues := make([]*istanbul.Message, len(s.messages)) + + i := 0 + for k, v := range s.messages { + messageKeys[i] = k + messageValues[i] = v + i++ + } + + return rlp.Encode(w, []interface{}{ + serializedValSet, + messageKeys, + messageValues, + }) +} + +// DecodeRLP Impl +func (s *messageSetImpl) DecodeRLP(stream *rlp.Stream) error { + var data struct { + SerializedValSet []byte + MessageKeys []common.Address + MessageValues []*istanbul.Message + } + + err := stream.Decode(&data) + if err != nil { + return err + } + + valSet, err := validator.DeserializeValidatorSet(data.SerializedValSet) + if err != nil { + return err + } + + messages := make(map[common.Address]*istanbul.Message) + for i, addr := range data.MessageKeys { + messages[addr] = data.MessageValues[i] + } + + *s = messageSetImpl{ + valSet: valSet, + messages: messages, + messagesMu: new(sync.Mutex), + } + + return nil +}
diff --git go-ethereum/consensus/istanbul/announce/enodequery.go celo/consensus/istanbul/announce/enodequery.go new file mode 100644 index 0000000000000000000000000000000000000000..8f89b7bb9006e96ce589dfe1b21770581f0af75c --- /dev/null +++ celo/consensus/istanbul/announce/enodequery.go @@ -0,0 +1,121 @@ +package announce + +import ( + "crypto/ecdsa" + "crypto/rand" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/log" +) + +type EnodeQuery struct { + RecipientAddress common.Address + RecipientPublicKey *ecdsa.PublicKey + EnodeURL string +} + +// generateEncryptedEnodeURLs returns the encryptedEnodeURLs to be sent in an enode query. +func generateEncryptedEnodeURLs(plogger log.Logger, enodeQueries []*EnodeQuery) ([]*istanbul.EncryptedEnodeURL, error) { + logger := plogger.New("func", "generateEncryptedEnodeURLs") + + var encryptedEnodeURLs []*istanbul.EncryptedEnodeURL + for _, param := range enodeQueries { + logger.Debug("encrypting enodeURL", "externalEnodeURL", param.EnodeURL, "publicKey", param.RecipientPublicKey) + publicKey := ecies.ImportECDSAPublic(param.RecipientPublicKey) + encEnodeURL, err := ecies.Encrypt(rand.Reader, publicKey, []byte(param.EnodeURL), nil, nil) + if err != nil { + logger.Error("Error in encrypting enodeURL", "enodeURL", param.EnodeURL, "publicKey", publicKey) + return nil, err + } + + encryptedEnodeURLs = append(encryptedEnodeURLs, &istanbul.EncryptedEnodeURL{ + DestAddress: param.RecipientAddress, + EncryptedEnodeURL: encEnodeURL, + }) + } + + return encryptedEnodeURLs, nil +} + +// generateQueryEnodeMsg returns a queryEnode message from this node with a given version. +// A query enode message contains a number of individual enode queries, each of which is intended +// for a single recipient validator. A query contains of this nodes external enode URL, to which +// the recipient validator is intended to connect, and is ECIES encrypted with the recipient's +// public key, from which their validator signer address is derived. +// Note: It is referred to as a "query" because the sender does not know the recipients enode. +// The recipient is expected to respond by opening a direct connection with an enode certificate. +func generateQueryEnodeMsg(plogger log.Logger, ei *istanbul.EcdsaInfo, version uint, enodeQueries []*EnodeQuery) (*istanbul.Message, error) { + logger := plogger.New("func", "generateQueryEnodeMsg") + + encryptedEnodeURLs, err := generateEncryptedEnodeURLs(logger, enodeQueries) + if err != nil { + logger.Warn("Error generating encrypted enodeURLs", "err", err) + return nil, err + } + if len(encryptedEnodeURLs) == 0 { + logger.Trace("No encrypted enodeURLs were generated, will not generate encryptedEnodeMsg") + return nil, nil + } + + msg := istanbul.NewQueryEnodeMessage(&istanbul.QueryEnodeData{ + EncryptedEnodeURLs: encryptedEnodeURLs, + Version: version, + Timestamp: istanbul.GetTimestamp(), + }, ei.Address) + // Sign the announce message + if err := msg.Sign(ei.Sign); err != nil { + logger.Error("Error in signing a QueryEnode Message", "QueryEnodeMsg", msg.String(), "err", err) + return nil, err + } + + logger.Debug("Generated a queryEnode message", "IstanbulMsg", msg.String(), "QueryEnodeData", msg.QueryEnodeMsg().String()) + + return msg, nil +} + +type EnodeQueryGossiper interface { + // GossipEnodeQueries will generate, encrypt, and gossip through the p2p network a new + // QueryEnodeMsg with the enodeQueries given. + GossipEnodeQueries(*istanbul.EcdsaInfo, []*EnodeQuery) (*istanbul.Message, error) +} + +type eqg struct { + logger log.Logger + announceVersion VersionReader + gossip func([]byte) error +} + +func NewEnodeQueryGossiper(announceVersion VersionReader, gossipFn func([]byte) error) EnodeQueryGossiper { + return &eqg{ + logger: log.New("module", "enodeQueryGossiper"), + announceVersion: announceVersion, + gossip: gossipFn, + } +} + +func (e *eqg) GossipEnodeQueries(ei *istanbul.EcdsaInfo, enodeQueries []*EnodeQuery) (*istanbul.Message, error) { + version := e.announceVersion.Get() + var err error + qeMsg, err := generateQueryEnodeMsg(e.logger, ei, version, enodeQueries) + if err != nil { + return nil, err + } + + if qeMsg == nil { + return nil, nil + } + + // Convert to payload + payload, err := qeMsg.Payload() + if err != nil { + e.logger.Error("Error in converting Istanbul QueryEnode Message to payload", "QueryEnodeMsg", qeMsg.String(), "err", err) + return nil, err + } + + if err = e.gossip(payload); err != nil { + return nil, err + } + return qeMsg, nil +}
diff --git go-ethereum/consensus/istanbul/core/errors.go celo/consensus/istanbul/core/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..04f0bd221c46e89fde3ec50e2d97967776e4c403 --- /dev/null +++ celo/consensus/istanbul/core/errors.go @@ -0,0 +1,87 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import "errors" + +var ( + // errInconsistentSubject is returned when received subject is different from + // current subject. + errInconsistentSubject = errors.New("inconsistent subjects") + // errNotFromProposer is returned when received message is supposed to be from + // proposer. + errNotFromProposer = errors.New("message does not come from proposer") + // errFutureMessage is returned when current view is earlier than the + // view of the received message. + errFutureMessage = errors.New("future message") + // errOldMessage is returned when the received message's view is earlier + // than current view. + errOldMessage = errors.New("old message") + // errInvalidMessage is returned when the message is malformed. + errInvalidMessage = errors.New("invalid message") + // errInvalidPreparedCertificateProposal is returned when the PREPARED certificate has an invalid proposal. + errInvalidPreparedCertificateProposal = errors.New("invalid proposal in PREPARED certificate") + // errInvalidPreparedCertificateNumMsgs is returned when the PREPARED certificate has an incorrect number of messages. + errInvalidPreparedCertificateNumMsgs = errors.New("invalid number of PREPARE messages in certificate") + // errInvalidPreparedCertificateMsgSignature is returned when the PREPARED certificate has a message with an invalid signature. + errInvalidPreparedCertificateMsgSignature = errors.New("invalid signature in PREPARED certificate") + // errInvalidPreparedCertificateDuplicate is returned when the PREPARED certificate has multiple messages from the same validator. + errInvalidPreparedCertificateDuplicate = errors.New("duplicate message in PREPARED certificate") + // errInvalidPreparedCertificateMsgCode is returned when the PREPARED certificate contains a non-PREPARE/COMMIT message. + errInvalidPreparedCertificateMsgCode = errors.New("non-PREPARE message in PREPARED certificate") + // errInvalidPreparedCertificateMsgView is returned when the PREPARED certificate contains a message for the wrong view + errInvalidPreparedCertificateMsgView = errors.New("message in PREPARED certificate for wrong view") + // errInvalidPreparedCertificateDigestMismatch is returned when the PREPARED certificate proposal doesn't match one of the messages. + errInvalidPreparedCertificateDigestMismatch = errors.New("message in PREPARED certificate for different digest than proposal") + // errInvalidRoundChangeViewMismatch is returned when the PREPARED certificate view is greater than the round change view + errInvalidRoundChangeViewMismatch = errors.New("View for PREPARED certificate is greater than the view in the round change message") + // errInvalidRoundChangeRequestSignature is returned when a RoundChangeRequest is improperly signed + errInvalidRoundChangeRequestSignature = errors.New("Round Change Request improperly signed") + // errRoundChangeRequestAddressMismatch is returned when a RoundChangeRequest address does not match the message address + errRoundChangeRequestAddressMismatch = errors.New("Round Change Request address does not match message address") + // errInvalidPreparedCertificateInconsistentViews is returned when the PREPARED certificate view is inconsistent among it's messages + errInvalidPreparedCertificateInconsistentViews = errors.New("View is inconsistent among the PREPARED certificate messages") + // errRoundChangeProposalHashMismatch is returned when the proposal hash of a round change prepared certificate does not match the proposal's hash. + errRoundChangeProposalHashMismatch = errors.New("Proposal hash mismatch in round change message") + + // errInvalidRoundChangeCertificateNumMsgs is returned when the ROUND CHANGE certificate has an incorrect number of ROUND CHANGE messages. + errInvalidRoundChangeCertificateNumMsgs = errors.New("invalid number of ROUND CHANGE messages in certificate") + // errInvalidRoundChangeCertificateMsgSignature is returned when the ROUND CHANGE certificate has a ROUND CHANGE message with an invalid signature. + errInvalidRoundChangeCertificateMsgSignature = errors.New("invalid signature in ROUND CHANGE certificate") + // errInvalidRoundChangeCertificateDuplicate is returned when the ROUND CHANGE certificate has multiple ROUND CHANGE messages from the same validator. + errInvalidRoundChangeCertificateDuplicate = errors.New("duplicate message in ROUND CHANGE certificate") + // errInvalidRoundChangeCertificateMsgView is returned when the ROUND CHANGE certificate contains a message for the wrong view + errInvalidRoundChangeCertificateMsgView = errors.New("message in ROUND CHANGE certificate for wrong view") + + // errInvalidCommittedSeal is returned when a COMMIT message has an invalid committed seal. + errInvalidCommittedSeal = errors.New("invalid committed seal in COMMIT message") + // errInvalidEpochValidatorSetSeal is returned when a COMMIT message has an invalid epoch validator seal. + errInvalidEpochValidatorSetSeal = errors.New("invalid epoch validator set seal in COMMIT message") + // errNotLastBlockInEpoch is returned when the block number was not the last block in the epoch + errNotLastBlockInEpoch = errors.New("not last block in epoch") + // errMissingRoundChangeCertificate is returned when ROUND CHANGE certificate is missing from a PREPREPARE for round > 0. + errMissingRoundChangeCertificate = errors.New("missing ROUND CHANGE certificate in PREPREPARE") + // errFailedCreateRoundChangeCertificate is returned when there aren't enough ROUND CHANGE messages to create a ROUND CHANGE certificate. + errFailedCreateRoundChangeCertificate = errors.New("failed to create ROUND CHANGE certficate") + // errInvalidProposal is returned when a PREPARED certificate exists for proposal A in the ROUND CHANGE certificate for a PREPREPARE with proposal B. + errInvalidProposal = errors.New("invalid proposal in PREPREPARE") + // errInvalidValidatorAddress is returned when the COMMIT message address doesn't + // correspond to a validator in the current set. + errInvalidValidatorAddress = errors.New("failed to find an existing validator by address") + // Invalid round state + errInvalidState = errors.New("invalid round state") +)
diff --git go-ethereum/consensus/istanbul/proxy/val_enodes_share.go celo/consensus/istanbul/proxy/val_enodes_share.go new file mode 100644 index 0000000000000000000000000000000000000000..a9718e9c032717afccf38c86bc424700a1c0ab38 --- /dev/null +++ celo/consensus/istanbul/proxy/val_enodes_share.go @@ -0,0 +1,154 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package proxy + +import ( + "encoding/hex" + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" +) + +func (pv *proxiedValidatorEngine) generateValEnodesShareMsg(remoteValidators []common.Address) (*istanbul.Message, error) { + logger := pv.logger.New("func", "generateValEnodesShareMsg") + + logger.Trace("generateValEnodesShareMsg called", "remoteValidators", common.ConvertToStringSlice(remoteValidators)) + vetEntries, err := pv.backend.GetValEnodeTableEntries(remoteValidators) + logger.Trace("GetValEnodeTableEntries returned", "vetEntries", vetEntries) + + if err != nil { + logger.Error("Error in retrieving all the entries from the ValEnodeTable", "err", err) + return nil, err + } + + sharedValidatorEnodes := make([]istanbul.SharedValidatorEnode, 0, len(vetEntries)) + for address, vetEntry := range vetEntries { + if vetEntry.GetNode() == nil { + continue + } + sharedValidatorEnodes = append(sharedValidatorEnodes, istanbul.SharedValidatorEnode{ + Address: address, + EnodeURL: vetEntry.GetNode().String(), + Version: vetEntry.GetVersion(), + }) + } + + msg := istanbul.NewValEnodesShareMessage(&istanbul.ValEnodesShareData{ + ValEnodes: sharedValidatorEnodes, + }, pv.backend.Address()) + + // Sign the validator enode share message + if err := msg.Sign(pv.backend.Sign); err != nil { + logger.Error("Error in signing an Istanbul ValEnodesShare Message", "ValEnodesShareMsg", msg.String(), "err", err) + return nil, err + } + + logger.Trace("Generated a Istanbul Validator Enodes Share message", "IstanbulMsg", msg.String(), "istanbul.ValEnodesShareData", msg.ValEnodesShareData().String()) + + return msg, nil +} + +// sendValEnodesShareMsg generates and then sends a ValEnodesShare message to the proxy +// This is a no-op for replica validators. +func (pv *proxiedValidatorEngine) sendValEnodesShareMsg(proxyPeer consensus.Peer, remoteValidators []common.Address) error { + logger := pv.logger.New("func", "sendValEnodesShareMsg") + + if !pv.backend.IsValidating() { + logger.Info("Skipping sending ValEnodesShareMsg b/c not validating") + return errors.New("Not validating") + } + + msg, err := pv.generateValEnodesShareMsg(remoteValidators) + if err != nil { + logger.Error("Error generating Istanbul ValEnodesShare Message", "err", err) + return err + } + + // Convert to payload + payload, err := msg.Payload() + if err != nil { + logger.Error("Error in converting Istanbul ValEnodesShare Message to payload", "ValEnodesShareMsg", msg.String(), "err", err) + return err + } + + logger.Trace("Sending Istanbul Validator Enodes Share payload to proxy peer", "proxyPeer", proxyPeer) + if err := proxyPeer.Send(istanbul.ValEnodesShareMsg, payload); err != nil { + logger.Error("Error sending Istanbul ValEnodesShare Message to proxy", "err", err) + return err + } + + return nil +} + +func (p *proxyEngine) handleValEnodesShareMsg(peer consensus.Peer, payload []byte) (bool, error) { + logger := p.logger.New("func", "handleValEnodesShareMsg") + + logger.Trace("Handling an Istanbul Validator Enodes Share message") + + p.proxiedValidatorsMu.RLock() + + // Verify that it's coming from the proxied peer + if ok := p.proxiedValidatorIDs[peer.Node().ID()]; !ok { + logger.Warn("Got a valEnodesShare message from a peer that is not the proxy's proxied validator. Ignoring it", "from", peer.Node().ID()) + p.proxiedValidatorsMu.RUnlock() + return false, nil + } + + p.proxiedValidatorsMu.RUnlock() + msg := new(istanbul.Message) + // Decode message + err := msg.FromPayload(payload, istanbul.GetSignatureAddress) + if err != nil { + logger.Error("Error in decoding received Istanbul Validator Enode Share message", "err", err, "payload", hex.EncodeToString(payload), "sender address", msg.Address) + return true, err + } + + // Verify that the sender is from the proxied validator + if msg.Address != p.config.ProxiedValidatorAddress { + logger.Error("Unauthorized valEnodesShare message", "sender address", msg.Address, "authorized sender address", p.config.ProxiedValidatorAddress) + return true, errUnauthorizedMessageFromProxiedValidator + } + + var valEnodesShareData istanbul.ValEnodesShareData + err = rlp.DecodeBytes(msg.Msg, &valEnodesShareData) + if err != nil { + logger.Error("Error in decoding received Istanbul Validator Enodes Share message content", "err", err, "IstanbulMsg", msg.String()) + return true, err + } + + logger.Trace("Received an Istanbul Validator Enodes Share message", "IstanbulMsg", msg.String(), "ValEnodesShareData", valEnodesShareData.String()) + + valEnodeEntries := make(map[common.Address]*istanbul.AddressEntry) + for _, sharedValidatorEnode := range valEnodesShareData.ValEnodes { + if node, err := enode.ParseV4(sharedValidatorEnode.EnodeURL); err != nil { + logger.Warn("Error in parsing enodeURL", "enodeURL", sharedValidatorEnode.EnodeURL) + continue + } else { + valEnodeEntries[sharedValidatorEnode.Address] = &istanbul.AddressEntry{Address: sharedValidatorEnode.Address, Node: node, Version: sharedValidatorEnode.Version} + } + } + + if err := p.backend.RewriteValEnodeTableEntries(valEnodeEntries); err != nil { + logger.Warn("Error in rewriting the valEnodeTable", "IstanbulMsg", msg.String(), "valEnodeEntries", valEnodeEntries, "error", err) + } + + return true, nil +}
diff --git go-ethereum/consensus/istanbul/announce/worker.go celo/consensus/istanbul/announce/worker.go new file mode 100644 index 0000000000000000000000000000000000000000..8edbd7853db51045b8cb16fe09707b7b4a7b1164 --- /dev/null +++ celo/consensus/istanbul/announce/worker.go @@ -0,0 +1,243 @@ +package announce + +import ( + "errors" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" +) + +// Worker is responsible for, while running, spawn all messages that this node +// should send: VersionCertificates sharing, and QueryEnode messages if the node +// is a NearlyElectedValidator. +// +// It automatically polls to check if it entered (or exited) NearlyElectedValidator status. +// +// It also periodically runs Prune in an AnnounceStatePruner. +type Worker interface { + Run() + UpdateVersion() + GetVersion() uint + Stop() + + // UpdateVersionTo is only public for testing purposes + UpdateVersionTo(version uint) error + // GenerateAndGossipQueryEnode is only public for testing purposes + GenerateAndGossipQueryEnode(enforceRetryBackoff bool) (*istanbul.Message, error) +} + +type worker struct { + logger log.Logger + aWallets *atomic.Value + version Version + initialWaitPeriod time.Duration + checker ValidatorChecker + state *AnnounceState + pruner AnnounceStatePruner + vcGossiper VersionCertificateGossiper + enodeGossiper EnodeQueryGossiper + config *istanbul.Config + countPeers PeerCounterFn + vpap ValProxyAssigmnentProvider + avs VersionSharer + + updateAnnounceVersionCh chan struct{} + announceThreadQuit chan struct{} +} + +type PeerCounterFn func(purpose p2p.PurposeFlag) int + +func NewWorker(initialWaitPeriod time.Duration, + aWallets *atomic.Value, + version Version, + state *AnnounceState, + checker ValidatorChecker, + pruner AnnounceStatePruner, + vcGossiper VersionCertificateGossiper, + enodeGossiper EnodeQueryGossiper, + config *istanbul.Config, + countPeersFn PeerCounterFn, + vpap ValProxyAssigmnentProvider, + avs VersionSharer) Worker { + return &worker{ + logger: log.New("module", "announceWorker"), + aWallets: aWallets, + version: version, + initialWaitPeriod: initialWaitPeriod, + checker: checker, + state: state, + pruner: pruner, + vcGossiper: vcGossiper, + enodeGossiper: enodeGossiper, + config: config, + countPeers: countPeersFn, + vpap: vpap, + avs: avs, + updateAnnounceVersionCh: make(chan struct{}, 1), + announceThreadQuit: make(chan struct{}), + } +} + +func (m *worker) wallets() *istanbul.Wallets { + return m.aWallets.Load().(*istanbul.Wallets) +} + +func (w *worker) Stop() { + w.announceThreadQuit <- struct{}{} +} + +func (w *worker) GetVersion() uint { + return w.version.Get() +} + +func (w *worker) UpdateVersion() { + // Send to the channel iff it does not already have a message. + select { + case w.updateAnnounceVersionCh <- struct{}{}: + default: + } +} + +func (w *worker) Run() { + shouldQueryAndAnnounce := func() (bool, bool) { + var err error + shouldQuery, err := w.checker.IsElectedOrNearValidator() + if err != nil { + w.logger.Warn("Error in checking if should announce", "err", err) + return false, false + } + shouldAnnounce := shouldQuery && w.checker.IsValidating() + return shouldQuery, shouldAnnounce + } + + st := NewAnnounceTaskState(w.config) + for { + select { + case <-st.checkIfShouldAnnounceTicker.C: + w.logger.Trace("Checking if this node should announce it's enode") + st.shouldQuery, st.shouldAnnounce = shouldQueryAndAnnounce() + st.updateAnnounceThreadStatus(w.logger, w.initialWaitPeriod, w.updateAnnounceVersion) + + case <-st.shareVersionCertificatesTicker.C: + if err := w.vcGossiper.GossipAllFrom(w.state.VersionCertificateTable); err != nil { + w.logger.Warn("Error gossiping all version certificates") + } + + case <-st.updateAnnounceVersionTickerCh: + if st.shouldAnnounce { + w.updateAnnounceVersion() + } + + case <-st.queryEnodeTickerCh: + st.startGossipQueryEnodeTask() + + case <-st.generateAndGossipQueryEnodeCh: + if st.shouldQuery { + peers := w.countPeers(p2p.AnyPurpose) + st.UpdateFrequencyOnGenerate(peers) + // This node may have recently sent out an announce message within + // the gossip cooldown period imposed by other nodes. + // Regardless, send the queryEnode so that it will at least be + // processed by this node's peers. This is especially helpful when a network + // is first starting up. + if _, err := w.GenerateAndGossipQueryEnode(st.queryEnodeFrequencyState == LowFreqState); err != nil { + w.logger.Warn("Error in generating and gossiping queryEnode", "err", err) + } + } + + case <-w.updateAnnounceVersionCh: + if st.shouldAnnounce { + w.updateAnnounceVersion() + } + + case <-st.pruneAnnounceDataStructuresTicker.C: + if err := w.pruner.Prune(w.state); err != nil { + w.logger.Warn("Error in pruning announce data structures", "err", err) + } + + case <-w.announceThreadQuit: + st.OnAnnounceThreadQuitting() + return + } + } +} + +// GenerateAndGossipAnnounce will generate the lastest announce msg from this node +// and then broadcast it to it's peers, which should then gossip the announce msg +// message throughout the p2p network if there has not been a message sent from +// this node within the last announceGossipCooldownDuration. +// Note that this function must ONLY be called by the announceThread. +func (w *worker) GenerateAndGossipQueryEnode(enforceRetryBackoff bool) (*istanbul.Message, error) { + logger := w.logger.New("func", "generateAndGossipQueryEnode") + logger.Trace("generateAndGossipQueryEnode called") + + wts := w.wallets() + // Retrieve the set valEnodeEntries (and their publicKeys) + // for the queryEnode message + qeep := NewQueryEnodeEntryProvider(w.state.ValEnodeTable) + valEnodeEntries, err := qeep.GetQueryEnodeValEnodeEntries(enforceRetryBackoff, wts.Ecdsa.Address) + if err != nil { + return nil, err + } + + valAddresses := make([]common.Address, len(valEnodeEntries)) + for i, valEnodeEntry := range valEnodeEntries { + valAddresses[i] = valEnodeEntry.Address + } + valProxyAssignments, err := w.vpap.GetValProxyAssignments(valAddresses) + if err != nil { + return nil, err + } + + var enodeQueries []*EnodeQuery + for _, valEnodeEntry := range valEnodeEntries { + if valEnodeEntry.PublicKey != nil { + externalEnode := valProxyAssignments[valEnodeEntry.Address] + if externalEnode == nil { + continue + } + + externalEnodeURL := externalEnode.URLv4() + enodeQueries = append(enodeQueries, &EnodeQuery{ + RecipientAddress: valEnodeEntry.Address, + RecipientPublicKey: valEnodeEntry.PublicKey, + EnodeURL: externalEnodeURL, + }) + } + } + + var qeMsg *istanbul.Message + if len(enodeQueries) > 0 { + if qeMsg, err = w.enodeGossiper.GossipEnodeQueries(&wts.Ecdsa, enodeQueries); err != nil { + return nil, err + } + if err = w.state.ValEnodeTable.UpdateQueryEnodeStats(valEnodeEntries); err != nil { + return nil, err + } + } + + return qeMsg, err +} + +func (w *worker) updateAnnounceVersion() { + w.UpdateVersionTo(istanbul.GetTimestamp()) +} + +func (w *worker) UpdateVersionTo(version uint) error { + currVersion := w.version.Get() + if version <= currVersion { + w.logger.Debug("Announce version is not newer than the existing version", "existing version", currVersion, "attempted new version", version) + return errors.New("Announce version is not newer than the existing version") + } + if err := w.avs.ShareVersion(version); err != nil { + w.logger.Warn("Error updating announce version", "err", err) + return err + } + w.logger.Debug("Updating announce version", "announceVersion", version) + w.version.Set(version) + return nil +}
diff --git go-ethereum/consensus/istanbul/core/testutils_test.go celo/consensus/istanbul/core/testutils_test.go new file mode 100644 index 0000000000000000000000000000000000000000..40b0bb1f09da2c91b5f1ec38fe5cd00d03cd9aec --- /dev/null +++ celo/consensus/istanbul/core/testutils_test.go @@ -0,0 +1,106 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/stretchr/testify/assert" +) + +func newView(seq, round uint64) *istanbul.View { + return &istanbul.View{Round: new(big.Int).SetUint64(round), Sequence: new(big.Int).SetUint64(seq)} +} + +func newTestRoundStateV2(view *istanbul.View, validatorSet istanbul.ValidatorSet) RoundState { + current := newRoundState(view, validatorSet, validatorSet.GetByIndex(0)) + current.(*roundStateImpl).preprepareV2 = newTestPreprepareV2(view) + return current +} + +func finishOnError(t *testing.T, err error) { + if err != nil { + t.Fatalf("Error %v", err) + } +} + +func assertEqualView(t *testing.T, have, want *istanbul.View) { + if !reflect.DeepEqual(have, want) { + t.Errorf("View are not equal: have %v, want: %v", have, want) + } +} +func assertEqualRoundState(t *testing.T, have, want RoundState) { + testEqual := func(name string, have, want interface{}) { + if !reflect.DeepEqual(have, want) { + t.Errorf("RoundState.%s mismatch: have %v, want %v", name, have, want) + } + } + + testEqualMessageSet := func(name string, have, want MessageSet) { + wantValues := want.Values() + haveValues := have.Values() + if len(haveValues) != len(wantValues) { + t.Errorf("RoundState.%s size mismatch: have %v, want %v", name, have, want) + return + } + for i := range wantValues { + wantValues[i] = wantValues[i].Copy() + haveValues[i] = haveValues[i].Copy() + } + assert.ElementsMatch(t, wantValues, haveValues, + "RoundState.%s mismatch (values w/o order): have %v, want %v", name, haveValues, wantValues) + } + + testEqual("State", have.State(), want.State()) + testEqual("Round", have.Round(), want.Round()) + testEqual("DesiredRound", have.DesiredRound(), want.DesiredRound()) + testEqual("Sequence", have.Sequence(), want.Sequence()) + testEqual("ValidatorSet", have.ValidatorSet(), want.ValidatorSet()) + testEqual("Proposer", have.Proposer(), want.Proposer()) + testEqualMessageSet("ParentCommits", have.ParentCommits(), want.ParentCommits()) + testEqualMessageSet("Commits", have.Commits(), want.Commits()) + testEqualMessageSet("Prepares", have.Prepares(), want.Prepares()) + + if have.PendingRequest() == nil || want.PendingRequest() == nil { + testEqual("PendingRequest", have.PendingRequest(), want.PendingRequest()) + } else { + haveBlock := have.PendingRequest().Proposal + wantBlock := want.PendingRequest().Proposal + testEqual("PendingRequest.Proposal.Hash", haveBlock.Hash(), wantBlock.Hash()) + } + + if have.PreprepareV2() == nil || want.PreprepareV2() == nil { + testEqual("PreprepareV2", have.PreprepareV2(), want.PreprepareV2()) + } else { + testEqual("PreprepareV2.Proposal.Hash", have.PreprepareV2().Proposal.Hash(), want.PreprepareV2().Proposal.Hash()) + testEqual("PreprepareV2.View", have.PreprepareV2().View, want.PreprepareV2().View) + testEqual("PreprepareV2.RoundChangeCertificateV2.IsEmpty", have.PreprepareV2().RoundChangeCertificateV2.IsEmpty(), want.PreprepareV2().RoundChangeCertificateV2.IsEmpty()) + + if !have.PreprepareV2().RoundChangeCertificateV2.IsEmpty() && !want.PreprepareV2().RoundChangeCertificateV2.IsEmpty() { + testEqual("PreprepareV2.RoundChangeCertificateV2.RoundChangeMessages", have.PreprepareV2().RoundChangeCertificateV2.Requests, want.PreprepareV2().RoundChangeCertificateV2.Requests) + } + + } + + havePPBlock := have.PreparedCertificate().Proposal + wantPPBlock := want.PreparedCertificate().Proposal + testEqual("PreparedCertificate().Proposal.Hash", havePPBlock.Hash(), wantPPBlock.Hash()) + testEqual("PreparedCertificate().PrepareOrCommitMessages", have.PreparedCertificate().PrepareOrCommitMessages, want.PreparedCertificate().PrepareOrCommitMessages) +}
diff --git go-ethereum/consensus/istanbul/announce/val_enode_db.go celo/consensus/istanbul/announce/val_enode_db.go new file mode 100644 index 0000000000000000000000000000000000000000..88904fea0e90394658ee16538e929b9e18ab403b --- /dev/null +++ celo/consensus/istanbul/announce/val_enode_db.go @@ -0,0 +1,571 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package announce + +import ( + "fmt" + "strings" + "sync" + "time" + + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/opt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/db" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" +) + +// Keys in the node database. +const ( + valEnodeDBVersion = 4 +) + +// ValidatorEnodeHandler is handler to Add/Remove events. Events execute within write lock +type ValidatorEnodeHandler interface { + // AddValidatorPeer adds a validator peer + AddValidatorPeer(node *enode.Node, address common.Address) + + // RemoveValidatorPeer removes a validator peer + RemoveValidatorPeer(node *enode.Node) + + // ReplaceValidatorPeers replace all validator peers for new list of enodeURLs + ReplaceValidatorPeers(newNodes []*enode.Node) + + // Clear all validator peers + ClearValidatorPeers() +} + +func addressEntryFromGenericEntry(entry db.GenericEntry) (*istanbul.AddressEntry, error) { + addressEntry, ok := entry.(*istanbul.AddressEntry) + if !ok { + return nil, errIncorrectEntryType + } + return addressEntry, nil +} + +// ValidatorEnodeDB represents a Map that can be accessed either +// by address or enode +type ValidatorEnodeDB struct { + gdb *db.GenericDB + lock sync.RWMutex + handler ValidatorEnodeHandler + logger log.Logger +} + +// OpenValidatorEnodeDB opens a validator enode database for storing and retrieving infos about validator +// enodes. If no path is given an in-memory, temporary database is constructed. +func OpenValidatorEnodeDB(path string, handler ValidatorEnodeHandler) (*ValidatorEnodeDB, error) { + logger := log.New("db", "ValidatorEnodeDB") + + gdb, err := db.New(int64(valEnodeDBVersion), path, logger, &opt.WriteOptions{NoWriteMerge: true}) + if err != nil { + logger.Error("Error creating db", "err", err) + return nil, err + } + + return &ValidatorEnodeDB{ + gdb: gdb, + handler: handler, + logger: logger, + }, nil +} + +// Close flushes and closes the database files. +func (vet *ValidatorEnodeDB) Close() error { + return vet.gdb.Close() +} + +func (vet *ValidatorEnodeDB) String() string { + vet.lock.RLock() + defer vet.lock.RUnlock() + var b strings.Builder + b.WriteString("ValEnodeTable:") + + err := vet.iterateOverAddressEntries(func(address common.Address, entry *istanbul.AddressEntry) error { + fmt.Fprintf(&b, " [%s => %s]", address.String(), entry.String()) + return nil + }) + + if err != nil { + vet.logger.Error("ValidatorEnodeDB.String error", "err", err) + } + + return b.String() +} + +// GetNodeFromAddress will return the enodeURL for an address if it's known +func (vet *ValidatorEnodeDB) GetNodeFromAddress(address common.Address) (*enode.Node, error) { + vet.lock.RLock() + defer vet.lock.RUnlock() + entry, err := vet.getAddressEntry(address) + if err != nil { + return nil, err + } + return entry.Node, nil +} + +// GetVersionFromAddress will return the version for an address if it's known +func (vet *ValidatorEnodeDB) GetVersionFromAddress(address common.Address) (uint, error) { + vet.lock.RLock() + defer vet.lock.RUnlock() + entry, err := vet.getAddressEntry(address) + if err != nil { + return 0, err + } + return entry.Version, nil +} + +// GetAddressFromNodeID will return the address for an nodeID if it's known +func (vet *ValidatorEnodeDB) GetAddressFromNodeID(nodeID enode.ID) (common.Address, error) { + vet.lock.RLock() + defer vet.lock.RUnlock() + + entryBytes, err := vet.gdb.Get(nodeIDKey(nodeID)) + if err != nil { + return common.ZeroAddress, err + } + return common.BytesToAddress(entryBytes), nil +} + +// GetHighestKnownVersionFromAddress will return the highest known version for an address if it's known +func (vet *ValidatorEnodeDB) GetHighestKnownVersionFromAddress(address common.Address) (uint, error) { + vet.lock.RLock() + defer vet.lock.RUnlock() + + entry, err := vet.getAddressEntry(address) + if err != nil { + return 0, err + } + return entry.HighestKnownVersion, nil +} + +// GetValEnodes will return entries in the valEnodeDB filtered on the valAddresses parameter. +// If it's set to nil, then no filter will be applied. +func (vet *ValidatorEnodeDB) GetValEnodes(valAddresses []common.Address) (map[common.Address]*istanbul.AddressEntry, error) { + vet.lock.RLock() + defer vet.lock.RUnlock() + var entries = make(map[common.Address]*istanbul.AddressEntry) + var valAddressesMap map[common.Address]struct{} + + if valAddresses != nil { + valAddressesMap = make(map[common.Address]struct{}) + for _, address := range valAddresses { + valAddressesMap[address] = struct{}{} + } + } + + err := vet.iterateOverAddressEntries(func(address common.Address, entry *istanbul.AddressEntry) error { + if valAddressesMap != nil { + if _, ok := valAddressesMap[address]; ok { + entries[address] = entry + } + } else { + entries[address] = entry + } + + return nil + }) + + if err != nil { + vet.logger.Error("ValidatorEnodeDB.GetValEnodes error", "err", err) + return nil, err + } + + return entries, nil +} + +// UpsertHighestKnownVersion function will do the following +// 1. Check if the updated HighestKnownVersion is higher than the existing HighestKnownVersion +// 2. Update the fields HighestKnownVersion, NumQueryAttempsForHKVersion, and PublicKey +func (vet *ValidatorEnodeDB) UpsertHighestKnownVersion(valEnodeEntries []*istanbul.AddressEntry) error { + logger := vet.logger.New("func", "UpsertHighestKnownVersion") + + onNewEntry := func(batch *leveldb.Batch, entry db.GenericEntry) error { + addressEntry, err := addressEntryFromGenericEntry(entry) + if err != nil { + return err + } + entryBytes, err := rlp.EncodeToBytes(addressEntry) + if err != nil { + return err + } + if addressEntry.Node != nil { + batch.Put(nodeIDKey(addressEntry.Node.ID()), addressEntry.Address.Bytes()) + } + batch.Put(addressKey(addressEntry.Address), entryBytes) + return nil + } + + onUpdatedEntry := func(batch *leveldb.Batch, existingEntry db.GenericEntry, newEntry db.GenericEntry) error { + existingAddressEntry, err := addressEntryFromGenericEntry(existingEntry) + if err != nil { + return err + } + newAddressEntry, err := addressEntryFromGenericEntry(newEntry) + if err != nil { + return err + } + + if newAddressEntry.HighestKnownVersion < existingAddressEntry.HighestKnownVersion { + logger.Trace("Skipping entry whose HighestKnownVersion is less than the existing entry's", "existing HighestKnownVersion", existingAddressEntry.HighestKnownVersion, "new version", newAddressEntry.HighestKnownVersion) + return nil + } + + // "Backfill" all other fields + newAddressEntry.Node = existingAddressEntry.Node + newAddressEntry.Version = existingAddressEntry.Version + newAddressEntry.LastQueryTimestamp = existingAddressEntry.LastQueryTimestamp + + // Set NumQueryAttemptsForHKVersion to 0 + newAddressEntry.NumQueryAttemptsForHKVersion = 0 + + return onNewEntry(batch, newAddressEntry) + } + + if err := vet.upsert(valEnodeEntries, onNewEntry, onUpdatedEntry); err != nil { + logger.Warn("Error upserting entries", "err", err) + return err + } + + return nil +} + +// UpsertVersionAndEnode will do the following +// 1. Check if the updated Version higher than the existing Version +// 2. Update Node, Version, HighestKnownVersion (if it's less than the new Version) +// 3. If the Node has been updated, establish new validator peer +func (vet *ValidatorEnodeDB) UpsertVersionAndEnode(valEnodeEntries []*istanbul.AddressEntry) error { + logger := vet.logger.New("func", "UpsertVersionAndEnode") + + peersToRemove := make([]*enode.Node, 0, len(valEnodeEntries)) + peersToAdd := make(map[common.Address]*enode.Node) + + onNewEntry := func(batch *leveldb.Batch, entry db.GenericEntry) error { + addressEntry, err := addressEntryFromGenericEntry(entry) + if err != nil { + return err + } + entryBytes, err := rlp.EncodeToBytes(addressEntry) + if err != nil { + return err + } + if addressEntry.Node != nil { + batch.Put(nodeIDKey(addressEntry.Node.ID()), addressEntry.Address.Bytes()) + peersToAdd[addressEntry.Address] = addressEntry.Node + } + batch.Put(addressKey(addressEntry.Address), entryBytes) + return nil + } + + onUpdatedEntry := func(batch *leveldb.Batch, existingEntry db.GenericEntry, newEntry db.GenericEntry) error { + existingAddressEntry, err := addressEntryFromGenericEntry(existingEntry) + if err != nil { + return err + } + newAddressEntry, err := addressEntryFromGenericEntry(newEntry) + if err != nil { + return err + } + + if newAddressEntry.Version < existingAddressEntry.Version { + logger.Trace("Skipping entry whose Version is less than the existing entry's", "existing Version", existingAddressEntry.Version, "new version", newAddressEntry.Version) + return nil + } + + // "Backfill" all other fields + newAddressEntry.PublicKey = existingAddressEntry.PublicKey + newAddressEntry.LastQueryTimestamp = existingAddressEntry.LastQueryTimestamp + + // Update HighestKnownVersion, if needed + if newAddressEntry.Version > existingAddressEntry.HighestKnownVersion { + newAddressEntry.HighestKnownVersion = newAddressEntry.Version + newAddressEntry.NumQueryAttemptsForHKVersion = 0 + } else { + newAddressEntry.HighestKnownVersion = existingAddressEntry.HighestKnownVersion + } + + enodeChanged := existingAddressEntry.Node != nil && newAddressEntry.Node != nil && existingAddressEntry.Node.String() != newAddressEntry.Node.String() + if enodeChanged { + batch.Delete(nodeIDKey(existingAddressEntry.Node.ID())) + peersToRemove = append(peersToRemove, existingAddressEntry.Node) + } + + return onNewEntry(batch, newAddressEntry) + } + + if err := vet.upsert(valEnodeEntries, onNewEntry, onUpdatedEntry); err != nil { + logger.Warn("Error upserting entries", "err", err) + return err + } + + for _, node := range peersToRemove { + vet.handler.RemoveValidatorPeer(node) + } + + for address, node := range peersToAdd { + vet.handler.AddValidatorPeer(node, address) + } + + return nil +} + +// UpdateQueryEnodeStats function will do the following +// 1. Increment each entry's NumQueryAttemptsForHKVersion by 1 is existing HighestKnownVersion is the same +// 2. Set each entry's LastQueryTimestamp to the current time +func (vet *ValidatorEnodeDB) UpdateQueryEnodeStats(valEnodeEntries []*istanbul.AddressEntry) error { + logger := vet.logger.New("func", "UpdateEnodeQueryStats") + + onNewEntry := func(batch *leveldb.Batch, entry db.GenericEntry) error { + addressEntry, err := addressEntryFromGenericEntry(entry) + if err != nil { + return err + } + entryBytes, err := rlp.EncodeToBytes(addressEntry) + if err != nil { + return err + } + if addressEntry.Node != nil { + batch.Put(nodeIDKey(addressEntry.Node.ID()), addressEntry.Address.Bytes()) + } + batch.Put(addressKey(addressEntry.Address), entryBytes) + return nil + } + + onUpdatedEntry := func(batch *leveldb.Batch, existingEntry db.GenericEntry, newEntry db.GenericEntry) error { + existingAddressEntry, err := addressEntryFromGenericEntry(existingEntry) + if err != nil { + return err + } + newAddressEntry, err := addressEntryFromGenericEntry(newEntry) + if err != nil { + return err + } + + if existingAddressEntry.HighestKnownVersion == newAddressEntry.HighestKnownVersion { + newAddressEntry.NumQueryAttemptsForHKVersion = existingAddressEntry.NumQueryAttemptsForHKVersion + 1 + } + + currentTime := time.Now() + newAddressEntry.LastQueryTimestamp = &currentTime + + // "Backfill" all other fields + newAddressEntry.PublicKey = existingAddressEntry.PublicKey + newAddressEntry.Node = existingAddressEntry.Node + newAddressEntry.Version = existingAddressEntry.Version + newAddressEntry.HighestKnownVersion = existingAddressEntry.HighestKnownVersion + + return onNewEntry(batch, newAddressEntry) + } + + if err := vet.upsert(valEnodeEntries, onNewEntry, onUpdatedEntry); err != nil { + logger.Warn("Error upserting entries", "err", err) + return err + } + + return nil +} + +// upsert will update or insert a validator enode entry given that the existing entry +// is older (determined by the version) than the new one +// TODO - In addition to modifying the val_enode_db, this function also will disconnect +// and/or connect the corresponding validator connenctions. The validator connections +// should be managed be a separate thread (see https://github.com/ethereum/go-ethereum/issues/607) +func (vet *ValidatorEnodeDB) upsert(valEnodeEntries []*istanbul.AddressEntry, + onNewEntry func(batch *leveldb.Batch, entry db.GenericEntry) error, + onUpdatedEntry func(batch *leveldb.Batch, existingEntry db.GenericEntry, newEntry db.GenericEntry) error) error { + logger := vet.logger.New("func", "Upsert") + vet.lock.Lock() + defer vet.lock.Unlock() + + getExistingEntry := func(entry db.GenericEntry) (db.GenericEntry, error) { + addressEntry, err := addressEntryFromGenericEntry(entry) + if err != nil { + return entry, err + } + return vet.getAddressEntry(addressEntry.Address) + } + + entries := make([]db.GenericEntry, len(valEnodeEntries)) + for i, valEnodeEntry := range valEnodeEntries { + entries[i] = db.GenericEntry(valEnodeEntry) + } + + if err := vet.gdb.Upsert(entries, getExistingEntry, onUpdatedEntry, onNewEntry); err != nil { + logger.Warn("Error upserting entries", "err", err) + return err + } + + return nil +} + +// RemoveEntry will remove an entry from the table +func (vet *ValidatorEnodeDB) RemoveEntry(address common.Address) error { + vet.lock.Lock() + defer vet.lock.Unlock() + batch := new(leveldb.Batch) + err := vet.addDeleteToBatch(batch, address) + if err != nil { + return err + } + return vet.gdb.Write(batch) +} + +// PruneEntries will remove entries for all address not present in addressesToKeep +func (vet *ValidatorEnodeDB) PruneEntries(addressesToKeep map[common.Address]bool) error { + vet.lock.Lock() + defer vet.lock.Unlock() + batch := new(leveldb.Batch) + err := vet.iterateOverAddressEntries(func(address common.Address, entry *istanbul.AddressEntry) error { + if !addressesToKeep[address] { + vet.logger.Trace("Deleting entry from valEnodeTable", "address", address) + return vet.addDeleteToBatch(batch, address) + } + return nil + }) + if err != nil { + return err + } + return vet.gdb.Write(batch) +} + +func (vet *ValidatorEnodeDB) RefreshValPeers(valConnSet map[common.Address]bool, ourAddress common.Address) { + // We use a R lock since we don't modify levelDB table + vet.lock.RLock() + defer vet.lock.RUnlock() + + if valConnSet[ourAddress] { + // transform address to enodeURLs + newNodes := []*enode.Node{} + for val := range valConnSet { + entry, err := vet.getAddressEntry(val) + if entry != nil && entry.Node != nil { + if err == nil { + newNodes = append(newNodes, entry.Node) + } else if err != leveldb.ErrNotFound { + vet.logger.Error("Error reading valEnodeTable: GetEnodeURLFromAddress", "err", err) + } + } + } + + vet.handler.ReplaceValidatorPeers(newNodes) + } else { + // Disconnect all validator peers if this node is not in the valset + vet.handler.ClearValidatorPeers() + } +} + +func (vet *ValidatorEnodeDB) addDeleteToBatch(batch *leveldb.Batch, address common.Address) error { + entry, err := vet.getAddressEntry(address) + if err != nil { + return err + } + + batch.Delete(addressKey(address)) + if entry.Node != nil { + batch.Delete(nodeIDKey(entry.Node.ID())) + if vet.handler != nil { + vet.handler.RemoveValidatorPeer(entry.Node) + } + } + return nil +} + +func (vet *ValidatorEnodeDB) getAddressEntry(address common.Address) (*istanbul.AddressEntry, error) { + var entry istanbul.AddressEntry + entryBytes, err := vet.gdb.Get(addressKey(address)) + if err != nil { + return nil, err + } + + if err = rlp.DecodeBytes(entryBytes, &entry); err != nil { + return nil, err + } + return &entry, nil +} + +func (vet *ValidatorEnodeDB) iterateOverAddressEntries(onEntry func(common.Address, *istanbul.AddressEntry) error) error { + logger := vet.logger.New("func", "iterateOverAddressEntries") + // Only target address keys + keyPrefix := []byte(dbAddressPrefix) + + onDBEntry := func(key []byte, value []byte) error { + var entry istanbul.AddressEntry + if err := rlp.DecodeBytes(value, &entry); err != nil { + return err + } + address := common.BytesToAddress(key) + if err := onEntry(address, &entry); err != nil { + return err + } + return nil + } + + if err := vet.gdb.Iterate(keyPrefix, onDBEntry); err != nil { + logger.Warn("Error iterating through db entries", "err", err) + return err + } + return nil +} + +// ValEnodeEntryInfo contains information for an entry of the val enode table +type ValEnodeEntryInfo struct { + PublicKey string `json:"publicKey"` + Enode string `json:"enode"` + Version uint `json:"version"` + HighestKnownVersion uint `json:"highestKnownVersion"` + NumQueryAttemptsForHKVersion uint `json:"numQueryAttemptsForHKVersion"` + LastQueryTimestamp string `json:"lastQueryTimestamp"` // Unix timestamp +} + +// ValEnodeTableInfo gives basic information for each entry of the table +func (vet *ValidatorEnodeDB) ValEnodeTableInfo() (map[string]*ValEnodeEntryInfo, error) { + vet.lock.RLock() + defer vet.lock.RUnlock() + + valEnodeTableInfo := make(map[string]*ValEnodeEntryInfo) + + valEnodeTable, err := vet.GetValEnodes(nil) + if err == nil { + for address, valEnodeEntry := range valEnodeTable { + entryInfo := &ValEnodeEntryInfo{ + Version: valEnodeEntry.Version, + HighestKnownVersion: valEnodeEntry.HighestKnownVersion, + NumQueryAttemptsForHKVersion: valEnodeEntry.NumQueryAttemptsForHKVersion, + } + if valEnodeEntry.PublicKey != nil { + publicKeyBytes := crypto.CompressPubkey(valEnodeEntry.PublicKey) + entryInfo.PublicKey = hexutil.Encode(publicKeyBytes) + } + if valEnodeEntry.Node != nil { + entryInfo.Enode = valEnodeEntry.Node.String() + } + if valEnodeEntry.LastQueryTimestamp != nil { + entryInfo.LastQueryTimestamp = valEnodeEntry.LastQueryTimestamp.String() + } + + valEnodeTableInfo[address.Hex()] = entryInfo + } + } + + return valEnodeTableInfo, err +}
diff --git go-ethereum/consensus/istanbul/validator/random/random_test.go celo/consensus/istanbul/validator/random/random_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fed930b4f6680f7ccb6cf69153362a1e68a17805 --- /dev/null +++ celo/consensus/istanbul/validator/random/random_test.go @@ -0,0 +1,62 @@ +package random + +import ( + "math/rand" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func randomHash() common.Hash { + var value common.Hash + // Always returns nil https://golang.org/pkg/math/rand/#Read + rand.Read(value[:]) + return value +} + +func BenchmarkPermutation(b *testing.B) { + // Setup the random values that will be fed in to the method. + seeds := make([]common.Hash, b.N) + for i := range seeds { + seeds[i] = randomHash() + } + + b.ResetTimer() + for _, seed := range seeds { + Permutation(seed, 1000) + } +} + +func TestUniform(t *testing.T) { + randomness := rand.New(rand.NewSource(rand.Int63())) + + // Verify that the returned value is always in the desired range. + t.Run("bounds", func(t *testing.T) { + for i := uint64(1); i < 10000; i++ { + if got := uniform(randomness, i); got > i { + t.Errorf("uniform(_, %d) = %d, want < %d", i, got, i) + } + } + }) + + // Verify the algorithm will output every number in the range + t.Run("coverage", func(t *testing.T) { + bound := uint64(100) + coverage := make([]bool, bound) + var count uint64 + for i := 0; i < 1_000_000; i++ { + sample := uniform(randomness, bound) + if !coverage[sample] { + count++ + coverage[sample] = true + } + + // Check for full coverage. + if count == bound { + return + } + } + // Chance of success with correct code is (1 - (1 - 1/bound)^runs)^bound ~= 1 with runs=1e6, bound=100 + t.Errorf("uniform(_, %d) did not cover [0, %d)", bound, bound) + }) +}
diff --git go-ethereum/consensus/istanbul/validator/random/random.go celo/consensus/istanbul/validator/random/random.go new file mode 100644 index 0000000000000000000000000000000000000000..bfa9fffa6fe240ea26fe87ce9a0bd7faba105479 --- /dev/null +++ celo/consensus/istanbul/validator/random/random.go @@ -0,0 +1,63 @@ +// Package random implements a language independent method of producing random validator orderings. +// It is designed to use standard methods that could be implemented in any language or platform. +package random + +import ( + "encoding/binary" + "io" + + "github.com/ethereum/go-ethereum/common" + "golang.org/x/crypto/sha3" +) + +// Permutation produces an array with a random permutation of [0, 1, ... n-1] +// Based on the Fisher-Yates method https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle +func Permutation(seed common.Hash, n int) []int { + if n <= 0 { + return nil + } + + // Create the unshuffled array [0, 1, ... n-1] + array := make([]int, n) + for i := 0; i < n; i++ { + array[i] = i + } + + // Create the Shake256 pseudo random stream. + randomness := sha3.NewShake256() + _, err := randomness.Write(seed[:]) + if err != nil { + // ShakeHash never returns an error. + panic(err) + } + + // Shuffle the array using the Fisher-Yates method. + for i := 0; i < n-1; i++ { + j := i + int(uniform(randomness.(io.Reader), uint64(n-i))) // j in [i, n) + array[i], array[j] = array[j], array[i] + } + return array +} + +// compress produces a 64-bit random value from a byte stream. +func randUint64(randomness io.Reader) uint64 { + raw := make([]byte, 8) + _, err := randomness.Read(raw) + if err != nil { + // Random stream should never return an error. + panic(err) + } + return binary.BigEndian.Uint64(raw) +} + +// uniform produces an integer in the range [0, k) from the provided randomness. +// Based on Algorithm 4 of https://arxiv.org/pdf/1805.10941.pdf +func uniform(randomness io.Reader, k uint64) uint64 { + x := randUint64(randomness) + r := x % k + for x-r > (-k) { + x = randUint64(randomness) + r = x % k + } + return r +}
diff --git go-ethereum/consensus/istanbul/backend/backendtest/test_utils.go celo/consensus/istanbul/backend/backendtest/test_utils.go new file mode 100644 index 0000000000000000000000000000000000000000..2043543853a6ce5cbeb6514ad6a00a6bc4200919 --- /dev/null +++ celo/consensus/istanbul/backend/backendtest/test_utils.go @@ -0,0 +1,63 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package backendtest + +import ( + "crypto/ecdsa" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" +) + +type TestBackendInterface interface { + HandleMsg(addr common.Address, msg p2p.Msg, peer consensus.Peer) (bool, error) + + Address() common.Address +} + +type TestBackendFactory interface { + New(isProxy bool, proxiedValAddress common.Address, isProxied bool, genesisCfg *core.Genesis, privateKey *ecdsa.PrivateKey) (TestBackendInterface, *istanbul.Config) + + GetGenesisAndKeys(numValidators int, isFullChain bool) (*core.Genesis, []*ecdsa.PrivateKey) +} + +var testBackendFactoryImpl TestBackendFactory + +func InitTestBackendFactory(impl TestBackendFactory) { + testBackendFactoryImpl = impl +} + +func NewTestBackend(isProxy bool, proxiedValAddress common.Address, isProxied bool, genesisCfg *core.Genesis, privateKey *ecdsa.PrivateKey) (TestBackendInterface, *istanbul.Config) { + return testBackendFactoryImpl.New(isProxy, proxiedValAddress, isProxied, genesisCfg, privateKey) +} + +func GetGenesisAndKeys(numValidators int, isFullChain bool) (*core.Genesis, []*ecdsa.PrivateKey) { + return testBackendFactoryImpl.GetGenesisAndKeys(numValidators, isFullChain) +} + +func CreateP2PMsg(code uint64, payload []byte) (p2p.Msg, error) { + size, r, err := rlp.EncodeToReader(payload) + if err != nil { + return p2p.Msg{}, err + } + + return p2p.Msg{Code: code, Size: uint32(size), Payload: r}, nil +}
diff --git go-ethereum/consensus/istanbul/backend/internal/replica/state.go celo/consensus/istanbul/backend/internal/replica/state.go new file mode 100644 index 0000000000000000000000000000000000000000..c68f972d6e7e5afa42ec3314f01c7cb47305cb52 --- /dev/null +++ celo/consensus/istanbul/backend/internal/replica/state.go @@ -0,0 +1,442 @@ +// Copyright 2020 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package replica + +import ( + "errors" + "fmt" + "io" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + lvlerrors "github.com/syndtr/goleveldb/leveldb/errors" +) + +type state uint64 + +// Different possible states for validators wrt replica/primary +// Set start & stop block to the range [start, stop) +// Permanent primary / replica when the node will not change state in the future +// primaryInRange when inside the range [start, stop) +// replicaWaiting when before the startValidatingBlock +const ( + primaryPermanent state = iota + primaryInRange + replicaPermanent + replicaWaiting +) + +func (s state) String() string { + switch s { + case primaryPermanent: + return "Primary" + case primaryInRange: + return "Primary in given range" + case replicaPermanent: + return "Replica" + case replicaWaiting: + return "Replica waiting to start" + } + return "Unknown" +} + +type State interface { + // Functions exposed through to the management RPC. + SetStartValidatingBlock(blockNumber *big.Int) error + SetStopValidatingBlock(blockNumber *big.Int) error + MakeReplica() error + MakePrimary() error + + // Internal functions + // Updates replica state given the current block undergoing consensus. + NewChainHead(blockNumber *big.Int) + // Closes the replica state database. + Close() error + + // view functions + IsPrimary() bool + IsPrimaryForSeq(blockNumber *big.Int) bool + Summary() *ReplicaStateSummary +} + +// ReplicaState stores info on this node being a primary or replica +type replicaStateImpl struct { + state state + startValidatingBlock *big.Int + stopValidatingBlock *big.Int + + rsdb *ReplicaStateDB + mu *sync.RWMutex + + startFn func() error + stopFn func() error +} + +// NewState creates a replicaState in the given replica state and opens or creates the replica state DB at `path`. +func NewState(isReplica bool, path string, startFn, stopFn func() error) (State, error) { + db, err := OpenReplicaStateDB(path) + if err != nil { + log.Crit("Can't open ReplicaStateDB", "err", err, "dbpath", path) + return nil, err + } + rs, err := db.GetReplicaState() + // First startup + if err == lvlerrors.ErrNotFound { + var state state + if isReplica { + state = replicaPermanent + } else { + state = primaryPermanent + } + rs = &replicaStateImpl{ + state: state, + mu: new(sync.RWMutex), + } + } else if err != nil { + log.Warn("Can't read ReplicaStateDB at startup", "err", err, "dbpath", path) + return nil, err + } + rs.rsdb = db + rs.startFn = startFn + rs.stopFn = stopFn + if err := db.StoreReplicaState(rs); err != nil { + log.Warn("Can't store replica state to ReplicaStateDB", "err", err) + return rs, err + } + return rs, nil +} + +// Close closes the replica state database +func (rs *replicaStateImpl) Close() error { + rs.mu.Lock() + defer rs.mu.Unlock() + return rs.rsdb.Close() +} + +// NewChainHead updates replica state and starts/stops the core if needed +func (rs *replicaStateImpl) NewChainHead(blockNumber *big.Int) { + rs.mu.Lock() + defer rs.mu.Unlock() + + logger := log.New("func", "NewChainHead", "seq", blockNumber) + switch rs.state { + case primaryInRange: + if blockNumber.Cmp(rs.stopValidatingBlock) >= 0 { + logger.Info("About to stop validating") + if err := rs.stopFn(); err != nil { + logger.Warn("Error stopping core", "err", err) + return + } + oldState := rs.state + oldStart := rs.startValidatingBlock + oldStop := rs.stopValidatingBlock + + rs.state = replicaPermanent + rs.startValidatingBlock = nil + rs.stopValidatingBlock = nil + + if err := rs.rsdb.StoreReplicaState(rs); err != nil { + if startErr := rs.startFn(); startErr != nil { + // Stopped, but could not restart + logger.Crit("Error when saving rsdb in NewChainHead in transition to replica. Tried to restart core, but that also failed", "rsdb_err", err, "start_err", startErr) + return + } + rs.state = oldState + rs.startValidatingBlock = oldStart + rs.stopValidatingBlock = oldStop + logger.Crit("Error when saving rsdb in NewChainHead in transition to replica. Rolled back transition.", "err", err) + } + } + case replicaWaiting: + if blockNumber.Cmp(rs.startValidatingBlock) >= 0 { + logger.Info("About to start validating") + if err := rs.startFn(); err != nil { + logger.Warn("Error starting core", "err", err) + return + } + oldState := rs.state + oldStart := rs.startValidatingBlock + oldStop := rs.stopValidatingBlock + + if rs.stopValidatingBlock == nil { + logger.Info("Switching to primary (permanent)") + rs.state = primaryPermanent + rs.startValidatingBlock = nil + rs.stopValidatingBlock = nil + } else { + logger.Info("Switching to primary in range") + rs.state = primaryInRange + } + + if err := rs.rsdb.StoreReplicaState(rs); err != nil { + if stopErr := rs.stopFn(); stopErr != nil { + // Started, but could not stop + logger.Crit("Error when saving rsdb in NewChainHead in transition to primary. Tried to stop core, but that also failed", "rsdb_err", err, "stop_err", stopErr) + return + } + rs.state = oldState + rs.startValidatingBlock = oldStart + rs.stopValidatingBlock = oldStop + + logger.Crit("Error when saving rsdb in NewChainHead in transition to primary. Rolled back transition.", "err", err) + } + } + default: + // pass + } +} + +// SetStartValidatingBlock sets the start block in the range [start, stop) +func (rs *replicaStateImpl) SetStartValidatingBlock(blockNumber *big.Int) error { + rs.mu.Lock() + defer rs.mu.Unlock() + + if blockNumber.Cmp(common.Big0) <= 0 { + return errors.New("blockNumber must be > 0") + } + if rs.stopValidatingBlock != nil && blockNumber.Cmp(rs.stopValidatingBlock) >= 0 { + return errors.New("Start block number should be less than the stop block number") + } + + // Save state in case we need to revert on failing to store to db + oldState := rs.state + oldStart := rs.startValidatingBlock + + switch rs.state { + case replicaPermanent: + rs.state = replicaWaiting + case replicaWaiting: + // pass. Changed start block while waiting to start. + default: + return fmt.Errorf("Can't change set start validating block when primary (%v)", rs.state) + } + rs.startValidatingBlock = blockNumber + + if err := rs.rsdb.StoreReplicaState(rs); err != nil { + rs.state = oldState + rs.startValidatingBlock = oldStart + return fmt.Errorf("Error when saving rsdb in SetStartValidatingBlock. err: %v", err) + } + + return nil +} + +// SetStopValidatingBlock sets the stop block in the range [start, stop) +func (rs *replicaStateImpl) SetStopValidatingBlock(blockNumber *big.Int) error { + rs.mu.Lock() + defer rs.mu.Unlock() + + if blockNumber.Cmp(common.Big0) <= 0 { + return errors.New("blockNumber must be > 0") + } + if rs.startValidatingBlock != nil && !(blockNumber.Cmp(rs.startValidatingBlock) > 0) { + return errors.New("Stop block number should be greater than the start block number") + } + + // Save state in case we need to revert on failing to store to db + oldState := rs.state + oldStop := rs.stopValidatingBlock + + switch rs.state { + case primaryPermanent: + rs.state = primaryInRange + case primaryInRange: + // pass. Changes stop block while waiting to stop. + case replicaPermanent: + return errors.New("Can't change stop validating block when permanent replica") + case replicaWaiting: + // pass. Changed stop block while waiting to start. + } + rs.stopValidatingBlock = blockNumber + + if err := rs.rsdb.StoreReplicaState(rs); err != nil { + rs.state = oldState + rs.stopValidatingBlock = oldStop + return fmt.Errorf("Error when saving rsdb in SetStopValidatingBlock. err: %v", err) + } + + return nil +} + +// MakeReplica makes this node a replica & clears all start/stop blocks. +func (rs *replicaStateImpl) MakeReplica() error { + rs.mu.Lock() + defer rs.mu.Unlock() + + oldState := rs.state + oldStart := rs.startValidatingBlock + oldStop := rs.stopValidatingBlock + + if rs.state == primaryPermanent || rs.state == primaryInRange { + if err := rs.stopFn(); err != nil { + return err + } + } + rs.startValidatingBlock = nil + rs.stopValidatingBlock = nil + rs.state = replicaPermanent + + if err := rs.rsdb.StoreReplicaState(rs); err != nil { + if startErr := rs.startFn(); startErr != nil { + // Stopped, but could not restart + return fmt.Errorf("Error when saving rsdb in MakeReplica: %v. Tried to restart core, but failed with: %v.", err, startErr) + } + rs.state = oldState + rs.startValidatingBlock = oldStart + rs.stopValidatingBlock = oldStop + return fmt.Errorf("Error when saving rsdb in MakeReplica. err: %v", err) + } + return nil +} + +// MakePrimary makes this node a primary & clears all start/stop blocks. +func (rs *replicaStateImpl) MakePrimary() error { + rs.mu.Lock() + defer rs.mu.Unlock() + + oldState := rs.state + oldStart := rs.startValidatingBlock + oldStop := rs.stopValidatingBlock + + if rs.state == replicaPermanent || rs.state == replicaWaiting { + if err := rs.startFn(); err != nil { + return err + } + } + rs.startValidatingBlock = nil + rs.stopValidatingBlock = nil + rs.state = primaryPermanent + + if err := rs.rsdb.StoreReplicaState(rs); err != nil { + if stopErr := rs.stopFn(); stopErr != nil { + // Started, but could not stop + return fmt.Errorf("Error when saving rsdb in MakePrimary: %v. Tried to stop core, but failed with: %v.", err, stopErr) + } + rs.state = oldState + rs.startValidatingBlock = oldStart + rs.stopValidatingBlock = oldStop + return fmt.Errorf("Error when saving rsdb in MakePrimary. err: %v", err) + } + return nil +} + +// IsPrimary determines is this node is the primary validator. +func (rs *replicaStateImpl) IsPrimary() bool { + rs.mu.RLock() + defer rs.mu.RUnlock() + return rs.state == primaryPermanent || rs.state == primaryInRange +} + +// IsPrimaryForSeq determines is this node is the primary validator. +// If start/stop checking is enabled (via a call to start/stop at block) +// determine if start <= seq < stop. If not enabled, check if this was +// set up with replica mode. +func (rs *replicaStateImpl) IsPrimaryForSeq(seq *big.Int) bool { + rs.mu.RLock() + defer rs.mu.RUnlock() + + switch rs.state { + case primaryPermanent: + return true + case replicaPermanent: + return false + case replicaWaiting: + return seq.Cmp(rs.startValidatingBlock) >= 0 + case primaryInRange: + return seq.Cmp(rs.stopValidatingBlock) < 0 + } + return false +} + +type ReplicaStateSummary struct { + State string `json:"state"` + IsPrimary bool `json:"isPrimary"` + StartValidatingBlock *big.Int `json:"startValidatingBlock"` + StopValidatingBlock *big.Int `json:"stopValidatingBlock"` +} + +func (rs *replicaStateImpl) Summary() *ReplicaStateSummary { + rs.mu.RLock() + defer rs.mu.RUnlock() + + summary := &ReplicaStateSummary{ + State: rs.state.String(), + IsPrimary: rs.state == primaryPermanent || rs.state == primaryInRange, + StartValidatingBlock: rs.startValidatingBlock, + StopValidatingBlock: rs.stopValidatingBlock, + } + + return summary +} + +type replicaStateRLP struct { + State state + StartValidatingBlock *big.Int + StopValidatingBlock *big.Int +} + +// EncodeRLP should write the RLP encoding of its receiver to w. +// If the implementation is a pointer method, it may also be +// called for nil pointers. +// +// Implementations should generate valid RLP. The data written is +// not verified at the moment, but a future version might. It is +// recommended to write only a single value but writing multiple +// values or no value at all is also permitted. +// +// Note: This is called when StoreReplicaState is called so +// mu should be held by functions that call StoreReplicaState +// but mu should not be locked in this function. +func (rs *replicaStateImpl) EncodeRLP(w io.Writer) error { + entry := replicaStateRLP{ + State: rs.state, + StartValidatingBlock: rs.startValidatingBlock, + StopValidatingBlock: rs.stopValidatingBlock, + } + return rlp.Encode(w, entry) +} + +// The DecodeRLP method should read one value from the given +// Stream. It is not forbidden to read less or more, but it might +// be confusing. +func (rs *replicaStateImpl) DecodeRLP(stream *rlp.Stream) error { + var data replicaStateRLP + err := stream.Decode(&data) + if err != nil { + return err + } + log.Trace("decode replica state RLP", "startValidatingBlock", data.StartValidatingBlock, "stopValidatingBlock", data.StopValidatingBlock) + + rs.mu = new(sync.RWMutex) + rs.state = data.State + if data.StartValidatingBlock.Cmp(common.Big0) == 0 { + rs.startValidatingBlock = nil + } else { + rs.startValidatingBlock = data.StartValidatingBlock + + } + if data.StopValidatingBlock.Cmp(common.Big0) == 0 { + rs.stopValidatingBlock = nil + } else { + rs.stopValidatingBlock = data.StopValidatingBlock + } + + return nil +}
diff --git go-ethereum/consensus/istanbul/backend/internal/replica/state_db.go celo/consensus/istanbul/backend/internal/replica/state_db.go new file mode 100644 index 0000000000000000000000000000000000000000..6b6f92ac41c37425f66bc16fa933ae8f9063e05e --- /dev/null +++ celo/consensus/istanbul/backend/internal/replica/state_db.go @@ -0,0 +1,105 @@ +// Copyright 2020 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package replica + +import ( + "sync" + + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/opt" + + "github.com/ethereum/go-ethereum/consensus/istanbul/db" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +// Keys in the node database. +const ( + replicaStateDBVersion = 1 + replicaStateKey = "replicaState" // Info about start/stop state + +) + +// ReplicaStateDB represents a Map that can be accessed either +// by address or enode +type ReplicaStateDB struct { + gdb *db.GenericDB + lock sync.RWMutex + logger log.Logger +} + +// OpenReplicaStateDB opens a validator enode database for storing and retrieving infos about validator +// enodes. If no path is given an in-memory, temporary database is constructed. +func OpenReplicaStateDB(path string) (*ReplicaStateDB, error) { + logger := log.New("db", "ReplicaStateDB") + + gdb, err := db.New(int64(replicaStateDBVersion), path, logger, &opt.WriteOptions{NoWriteMerge: true}) + if err != nil { + logger.Error("Error creating db", "err", err) + return nil, err + } + + return &ReplicaStateDB{ + gdb: gdb, + logger: logger, + }, nil +} + +// Close flushes and closes the database files. +func (rsdb *ReplicaStateDB) Close() error { + rsdb.lock.Lock() + defer rsdb.lock.Unlock() + return rsdb.gdb.Close() +} + +func (rsdb *ReplicaStateDB) GetReplicaState() (*replicaStateImpl, error) { + rsdb.lock.Lock() + defer rsdb.lock.Unlock() + + rawEntry, err := rsdb.gdb.Get([]byte(replicaStateKey)) + if err != nil { + return nil, err + } + + var entry replicaStateImpl + if err = rlp.DecodeBytes(rawEntry, &entry); err != nil { + return nil, err + } + return &entry, err +} + +// StoreReplicaState will store the latest replica state +func (rsdb *ReplicaStateDB) StoreReplicaState(rs State) error { + rsdb.lock.Lock() + defer rsdb.lock.Unlock() + logger := rsdb.logger.New("func", "StoreReplicaState") + + entryBytes, err := rlp.EncodeToBytes(rs) + if err != nil { + logger.Error("Failed to save roundState", "reason", "rlp encoding", "err", err) + return err + } + + batch := new(leveldb.Batch) + batch.Put([]byte(replicaStateKey), entryBytes) + err = rsdb.gdb.Write(batch) + if err != nil { + logger.Error("Failed to save roundState", "reason", "levelDB write", "err", err) + } + + return err +}
diff --git go-ethereum/consensus/istanbul/backend/internal/replica/state_test.go celo/consensus/istanbul/backend/internal/replica/state_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6c28f194a5e60482c873544f02df97d3edf0edfd --- /dev/null +++ celo/consensus/istanbul/backend/internal/replica/state_test.go @@ -0,0 +1,298 @@ +// Copyright 2020 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package replica + +import ( + "errors" + "fmt" + "math/big" + "testing" +) + +func noop() error { + return nil +} + +func (rs *replicaStateImpl) CheckRSDB() error { + // load DB + loaded, err := rs.rsdb.GetReplicaState() + if err != nil { + return err + } + if loaded == nil { + return errors.New("Could not load rsdb") + } + if loaded.state != rs.state { + return fmt.Errorf("Expected loaded state to equal rs. loaded: %v; rs: %v.", loaded.state, rs.state) + } + hasLoadedStart := loaded.startValidatingBlock != nil + hasRsStart := rs.startValidatingBlock != nil + hasLoadedStop := loaded.stopValidatingBlock != nil + hasRsStop := rs.stopValidatingBlock != nil + + if !hasLoadedStart && !hasRsStart { + // pass + } else if !hasLoadedStart || !hasRsStart { + return fmt.Errorf("Expected loaded start block to equal rs. loaded: %v; rs: %v.", loaded.startValidatingBlock, rs.startValidatingBlock) + } else if loaded.startValidatingBlock.Cmp(rs.startValidatingBlock) != 0 { + return fmt.Errorf("Expected loaded start block to equal rs. loaded: %v; rs: %v.", loaded.startValidatingBlock, rs.startValidatingBlock) + } + if !hasLoadedStop && !hasRsStop { + // pass + } else if !hasLoadedStop || !hasRsStop { + return fmt.Errorf("Expected loaded stop block to equal rs. loaded: %v; rs: %v.", loaded.stopValidatingBlock, rs.stopValidatingBlock) + } else if loaded.stopValidatingBlock.Cmp(rs.stopValidatingBlock) != 0 { + return fmt.Errorf("Expected loaded stop bloc to equal rs. loaded: %v; rs: %v.", loaded.stopValidatingBlock, rs.stopValidatingBlock) + } + return nil +} + +func TestIsPrimaryForSeq(t *testing.T) { + t.Run("permanent primary", func(t *testing.T) { + + seqs := []int64{0, 1, 2, 4, 8, 16, 32, 64, 128} + rsState, _ := NewState(false, "", noop, noop) + rs := rsState.(*replicaStateImpl) + for _, seq := range seqs { + n := big.NewInt(seq) + primary := rs.IsPrimaryForSeq(n) + rs.NewChainHead(n) + if err := rs.CheckRSDB(); err != nil { + t.Errorf("expected RSDB to be the same for seq %v, err: %v", seq, err) + } + + if !primary { + t.Errorf("expected to be primary for seq %v", seq) + } + } + }) + + t.Run("permanent replica", func(t *testing.T) { + seqs := []int64{0, 1, 2, 4, 8, 16, 32, 64, 128} + rsState, _ := NewState(true, "", noop, noop) + rs := rsState.(*replicaStateImpl) + for _, seq := range seqs { + n := big.NewInt(seq) + primary := rs.IsPrimaryForSeq(n) + rs.NewChainHead(n) + if err := rs.CheckRSDB(); err != nil { + t.Errorf("expected RSDB to be the same for seq %v, err: %v", seq, err) + } + + if primary { + t.Errorf("expected to be replica for seq %v", seq) + } + primary = rs.IsPrimary() + if primary { + t.Errorf("expected to be replica for seq %v", seq) + } + } + }) + + t.Run("replica waiting", func(t *testing.T) { + seqs := []int64{1, 2, 4, 8, 16, 32, 64, 128} + rsState, _ := NewState(true, "", noop, noop) + rs := rsState.(*replicaStateImpl) + rs.SetStartValidatingBlock(big.NewInt(200)) + for _, seq := range seqs { + n := big.NewInt(seq) + primary := rs.IsPrimaryForSeq(n) + rs.NewChainHead(n) + if err := rs.CheckRSDB(); err != nil { + t.Errorf("expected RSDB to be the same for seq %v, err: %v", seq, err) + } + + if primary { + t.Errorf("expected to be replica for seq %v", seq) + } + primary = rs.IsPrimary() + if primary { + t.Errorf("expected to be replica for seq %v", seq) + } + } + seqs = []int64{200, 205, 210} + for _, seq := range seqs { + n := big.NewInt(seq) + primary := rs.IsPrimaryForSeq(n) + rs.NewChainHead(n) + if err := rs.CheckRSDB(); err != nil { + t.Errorf("expected RSDB to be the same for seq %v, err: %v", seq, err) + } + + if !primary { + t.Errorf("expected to be primary for seq %v", seq) + } + primary = rs.IsPrimary() + if !primary { + t.Errorf("expected to be primary for seq %v", seq) + } + } + + }) + + t.Run("replica waiting to primary in range to permanent replica", func(t *testing.T) { + seqs := []int64{1, 2, 4, 8, 16, 32, 64, 128} + rsState, _ := NewState(true, "", noop, noop) + rs := rsState.(*replicaStateImpl) + rs.SetStartValidatingBlock(big.NewInt(200)) + rs.SetStopValidatingBlock(big.NewInt(210)) + + for _, seq := range seqs { + n := big.NewInt(seq) + primary := rs.IsPrimaryForSeq(n) + rs.NewChainHead(n) + if err := rs.CheckRSDB(); err != nil { + t.Errorf("expected RSDB to be the same for seq %v, err: %v", seq, err) + } + + if primary { + t.Errorf("expected to be replica for seq %v", seq) + } + primary = rs.IsPrimary() + if primary { + t.Errorf("expected to be replica for seq %v", seq) + } + } + seqs = []int64{200, 205, 209} + for _, seq := range seqs { + n := big.NewInt(seq) + primary := rs.IsPrimaryForSeq(n) + rs.NewChainHead(n) + if err := rs.CheckRSDB(); err != nil { + t.Errorf("expected RSDB to be the same for seq %v, err: %v", seq, err) + } + + if rs.state != primaryInRange { + t.Errorf("expected rs.state to be %v, got %v", primaryInRange, rs.state) + } + + if !primary { + t.Errorf("expected to be primary for seq %v", seq) + } + primary = rs.IsPrimary() + if !primary { + t.Errorf("expected to be primary for seq %v", seq) + } + } + seqs = []int64{210, 211, 220} + for _, seq := range seqs { + n := big.NewInt(seq) + primary := rs.IsPrimaryForSeq(n) + rs.NewChainHead(n) + if err := rs.CheckRSDB(); err != nil { + t.Errorf("expected RSDB to be the same for seq %v, err: %v", seq, err) + } + + if primary { + t.Errorf("expected to be replica for seq %v", seq) + } + primary = rs.IsPrimary() + if primary { + t.Errorf("expected to be replica for seq %v", seq) + } + } + }) + + t.Run("primary in range to permanent replica", func(t *testing.T) { + seqs := []int64{1, 2, 4, 8, 16, 32, 64, 128, 209} + rsState, _ := NewState(false, "", noop, noop) + rs := rsState.(*replicaStateImpl) + rs.SetStopValidatingBlock(big.NewInt(210)) + + for _, seq := range seqs { + n := big.NewInt(seq) + primary := rs.IsPrimaryForSeq(n) + rs.NewChainHead(n) + if err := rs.CheckRSDB(); err != nil { + t.Errorf("expected RSDB to be the same for seq %v, err: %v", seq, err) + } + + if !primary { + t.Errorf("expected to be primary for seq %v", seq) + } + primary = rs.IsPrimary() + if !primary { + t.Errorf("expected to be primary for seq %v", seq) + } + } + + seqs = []int64{210, 211, 220} + for _, seq := range seqs { + n := big.NewInt(seq) + primary := rs.IsPrimaryForSeq(n) + rs.NewChainHead(n) + if err := rs.CheckRSDB(); err != nil { + t.Errorf("expected RSDB to be the same for seq %v, err: %v", seq, err) + } + + if primary { + t.Errorf("expected to be replica for seq %v", seq) + } + primary = rs.IsPrimary() + if primary { + t.Errorf("expected to be replica for seq %v", seq) + } + } + }) + +} + +func TestSetStartValidatingBlock(t *testing.T) { + + t.Run("Respects start/stop block ordering", func(t *testing.T) { + rsState, _ := NewState(true, "", noop, noop) + rs := rsState.(*replicaStateImpl) + rs.state = replicaWaiting + rs.SetStopValidatingBlock(big.NewInt(10)) + + err := rs.SetStartValidatingBlock(big.NewInt(11)) + if err == nil { + t.Errorf("error mismatch: have %v, want %v", err, errors.New("Start block number should be less than the stop block number")) + } + err = rs.SetStartValidatingBlock(big.NewInt(9)) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + }) + +} + +func TestSetStopValidatingBlock(t *testing.T) { + + //start <= seq < stop + t.Run("Respects start/stop block ordering", func(t *testing.T) { + rsState, _ := NewState(true, "", noop, noop) + rs := rsState.(*replicaStateImpl) + rs.SetStartValidatingBlock(big.NewInt(10)) + + err := rs.SetStopValidatingBlock(big.NewInt(9)) + if err == nil { + t.Errorf("error mismatch: have %v, want %v", err, errors.New("Stop block number should be greater than the start block number")) + } + err = rs.SetStopValidatingBlock(big.NewInt(10)) + if err == nil { + t.Errorf("error mismatch: have %v, want %v", err, errors.New("Stop block number should be greater than the start block number")) + } + err = rs.SetStopValidatingBlock(big.NewInt(11)) + if err != nil { + t.Errorf("error mismatch: have %v, want nil", err) + } + + }) + +}
diff --git go-ethereum/consensus/misc/eip1559.go celo/consensus/misc/eip1559.go deleted file mode 100644 index cc51e4678cf105e372207a0eae112c50d7a8dce5..0000000000000000000000000000000000000000 --- go-ethereum/consensus/misc/eip1559.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package misc - -import ( - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" -) - -// VerifyEip1559Header verifies some header attributes which were changed in EIP-1559, -// - gas limit check -// - basefee check -func VerifyEip1559Header(config *params.ChainConfig, parent, header *types.Header) error { - // Verify that the gas limit remains within allowed bounds - parentGasLimit := parent.GasLimit - if !config.IsLondon(parent.Number) { - parentGasLimit = parent.GasLimit * params.ElasticityMultiplier - } - if err := VerifyGaslimit(parentGasLimit, header.GasLimit); err != nil { - return err - } - // Verify the header is not malformed - if header.BaseFee == nil { - return fmt.Errorf("header is missing baseFee") - } - // Verify the baseFee is correct based on the parent header. - expectedBaseFee := CalcBaseFee(config, parent) - if header.BaseFee.Cmp(expectedBaseFee) != 0 { - return fmt.Errorf("invalid baseFee: have %s, want %s, parentBaseFee %s, parentGasUsed %d", - expectedBaseFee, header.BaseFee, parent.BaseFee, parent.GasUsed) - } - return nil -} - -// CalcBaseFee calculates the basefee of the header. -func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { - // If the current block is the first EIP-1559 block, return the InitialBaseFee. - if !config.IsLondon(parent.Number) { - return new(big.Int).SetUint64(params.InitialBaseFee) - } - - var ( - parentGasTarget = parent.GasLimit / params.ElasticityMultiplier - parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget) - baseFeeChangeDenominator = new(big.Int).SetUint64(params.BaseFeeChangeDenominator) - ) - // If the parent gasUsed is the same as the target, the baseFee remains unchanged. - if parent.GasUsed == parentGasTarget { - return new(big.Int).Set(parent.BaseFee) - } - if parent.GasUsed > parentGasTarget { - // If the parent block used more gas than its target, the baseFee should increase. - gasUsedDelta := new(big.Int).SetUint64(parent.GasUsed - parentGasTarget) - x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta) - y := x.Div(x, parentGasTargetBig) - baseFeeDelta := math.BigMax( - x.Div(y, baseFeeChangeDenominator), - common.Big1, - ) - - return x.Add(parent.BaseFee, baseFeeDelta) - } else { - // Otherwise if the parent block used less gas than its target, the baseFee should decrease. - gasUsedDelta := new(big.Int).SetUint64(parentGasTarget - parent.GasUsed) - x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta) - y := x.Div(x, parentGasTargetBig) - baseFeeDelta := x.Div(y, baseFeeChangeDenominator) - - return math.BigMax( - x.Sub(parent.BaseFee, baseFeeDelta), - common.Big0, - ) - } -}
diff --git go-ethereum/consensus/misc/dao.go celo/consensus/misc/dao.go deleted file mode 100644 index f917022d5d31f9dc3ec5fc06aeb112a7d1386c51..0000000000000000000000000000000000000000 --- go-ethereum/consensus/misc/dao.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package misc - -import ( - "bytes" - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" -) - -var ( - // ErrBadProDAOExtra is returned if a header doesn't support the DAO fork on a - // pro-fork client. - ErrBadProDAOExtra = errors.New("bad DAO pro-fork extra-data") - - // ErrBadNoDAOExtra is returned if a header does support the DAO fork on a no- - // fork client. - ErrBadNoDAOExtra = errors.New("bad DAO no-fork extra-data") -) - -// VerifyDAOHeaderExtraData validates the extra-data field of a block header to -// ensure it conforms to DAO hard-fork rules. -// -// DAO hard-fork extension to the header validity: -// a) if the node is no-fork, do not accept blocks in the [fork, fork+10) range -// with the fork specific extra-data set -// b) if the node is pro-fork, require blocks in the specific range to have the -// unique extra-data set. -func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error { - // Short circuit validation if the node doesn't care about the DAO fork - if config.DAOForkBlock == nil { - return nil - } - // Make sure the block is within the fork's modified extra-data range - limit := new(big.Int).Add(config.DAOForkBlock, params.DAOForkExtraRange) - if header.Number.Cmp(config.DAOForkBlock) < 0 || header.Number.Cmp(limit) >= 0 { - return nil - } - // Depending on whether we support or oppose the fork, validate the extra-data contents - if config.DAOForkSupport { - if !bytes.Equal(header.Extra, params.DAOForkBlockExtra) { - return ErrBadProDAOExtra - } - } else { - if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { - return ErrBadNoDAOExtra - } - } - // All ok, header has the same extra-data we expect - return nil -} - -// ApplyDAOHardFork modifies the state database according to the DAO hard-fork -// rules, transferring all balances of a set of DAO accounts to a single refund -// contract. -func ApplyDAOHardFork(statedb *state.StateDB) { - // Retrieve the contract to refund balances into - if !statedb.Exist(params.DAORefundContract) { - statedb.CreateAccount(params.DAORefundContract) - } - - // Move every DAO account and extra-balance account funds into the refund contract - for _, addr := range params.DAODrainList() { - statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr)) - statedb.SetBalance(addr, new(big.Int)) - } -}
diff --git go-ethereum/consensus/misc/gaslimit.go celo/consensus/misc/gaslimit.go deleted file mode 100644 index 25f35300b94d7b8413b3be47dbb13fc0fb0ba4b1..0000000000000000000000000000000000000000 --- go-ethereum/consensus/misc/gaslimit.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package misc - -import ( - "errors" - "fmt" - - "github.com/ethereum/go-ethereum/params" -) - -// VerifyGaslimit verifies the header gas limit according increase/decrease -// in relation to the parent gas limit. -func VerifyGaslimit(parentGasLimit, headerGasLimit uint64) error { - // Verify that the gas limit remains within allowed bounds - diff := int64(parentGasLimit) - int64(headerGasLimit) - if diff < 0 { - diff *= -1 - } - limit := parentGasLimit / params.GasLimitBoundDivisor - if uint64(diff) >= limit { - return fmt.Errorf("invalid gas limit: have %d, want %d +-= %d", headerGasLimit, parentGasLimit, limit-1) - } - if headerGasLimit < params.MinGasLimit { - return errors.New("invalid gas limit below 5000") - } - return nil -}
diff --git go-ethereum/consensus/misc/eip1559_test.go celo/consensus/misc/eip1559_test.go deleted file mode 100644 index a44adc64e6b2b7d8efb02ab5028ac8d76649a697..0000000000000000000000000000000000000000 --- go-ethereum/consensus/misc/eip1559_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package misc - -import ( - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" -) - -// copyConfig does a _shallow_ copy of a given config. Safe to set new values, but -// do not use e.g. SetInt() on the numbers. For testing only -func copyConfig(original *params.ChainConfig) *params.ChainConfig { - return &params.ChainConfig{ - ChainID: original.ChainID, - HomesteadBlock: original.HomesteadBlock, - DAOForkBlock: original.DAOForkBlock, - DAOForkSupport: original.DAOForkSupport, - EIP150Block: original.EIP150Block, - EIP150Hash: original.EIP150Hash, - EIP155Block: original.EIP155Block, - EIP158Block: original.EIP158Block, - ByzantiumBlock: original.ByzantiumBlock, - ConstantinopleBlock: original.ConstantinopleBlock, - PetersburgBlock: original.PetersburgBlock, - IstanbulBlock: original.IstanbulBlock, - MuirGlacierBlock: original.MuirGlacierBlock, - BerlinBlock: original.BerlinBlock, - LondonBlock: original.LondonBlock, - CatalystBlock: original.CatalystBlock, - Ethash: original.Ethash, - Clique: original.Clique, - } -} - -func config() *params.ChainConfig { - config := copyConfig(params.TestChainConfig) - config.LondonBlock = big.NewInt(5) - return config -} - -// TestBlockGasLimits tests the gasLimit checks for blocks both across -// the EIP-1559 boundary and post-1559 blocks -func TestBlockGasLimits(t *testing.T) { - initial := new(big.Int).SetUint64(params.InitialBaseFee) - - for i, tc := range []struct { - pGasLimit uint64 - pNum int64 - gasLimit uint64 - ok bool - }{ - // Transitions from non-london to london - {10000000, 4, 20000000, true}, // No change - {10000000, 4, 20019530, true}, // Upper limit - {10000000, 4, 20019531, false}, // Upper +1 - {10000000, 4, 19980470, true}, // Lower limit - {10000000, 4, 19980469, false}, // Lower limit -1 - // London to London - {20000000, 5, 20000000, true}, - {20000000, 5, 20019530, true}, // Upper limit - {20000000, 5, 20019531, false}, // Upper limit +1 - {20000000, 5, 19980470, true}, // Lower limit - {20000000, 5, 19980469, false}, // Lower limit -1 - {40000000, 5, 40039061, true}, // Upper limit - {40000000, 5, 40039062, false}, // Upper limit +1 - {40000000, 5, 39960939, true}, // lower limit - {40000000, 5, 39960938, false}, // Lower limit -1 - } { - parent := &types.Header{ - GasUsed: tc.pGasLimit / 2, - GasLimit: tc.pGasLimit, - BaseFee: initial, - Number: big.NewInt(tc.pNum), - } - header := &types.Header{ - GasUsed: tc.gasLimit / 2, - GasLimit: tc.gasLimit, - BaseFee: initial, - Number: big.NewInt(tc.pNum + 1), - } - err := VerifyEip1559Header(config(), parent, header) - if tc.ok && err != nil { - t.Errorf("test %d: Expected valid header: %s", i, err) - } - if !tc.ok && err == nil { - t.Errorf("test %d: Expected invalid header", i) - } - } -} - -// TestCalcBaseFee assumes all blocks are 1559-blocks -func TestCalcBaseFee(t *testing.T) { - tests := []struct { - parentBaseFee int64 - parentGasLimit uint64 - parentGasUsed uint64 - expectedBaseFee int64 - }{ - {params.InitialBaseFee, 20000000, 10000000, params.InitialBaseFee}, // usage == target - {params.InitialBaseFee, 20000000, 9000000, 987500000}, // usage below target - {params.InitialBaseFee, 20000000, 11000000, 1012500000}, // usage above target - } - for i, test := range tests { - parent := &types.Header{ - Number: common.Big32, - GasLimit: test.parentGasLimit, - GasUsed: test.parentGasUsed, - BaseFee: big.NewInt(test.parentBaseFee), - } - if have, want := CalcBaseFee(config(), parent), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 { - t.Errorf("test %d: have %d want %d, ", i, have, want) - } - } -}
diff --git go-ethereum/consensus/misc/forks.go celo/consensus/misc/forks.go index 4a5e7c37e03c40e7c6abf77809ad60fd2968fef3..5ab56a6ef79d2c7ec8e5c432c46610035e54dea7 100644 --- go-ethereum/consensus/misc/forks.go +++ celo/consensus/misc/forks.go @@ -27,11 +27,7 @@ // VerifyForkHashes verifies that blocks conforming to network hard-forks do have // the correct hashes, to avoid clients going off on different chains. This is an // optional feature. -func VerifyForkHashes(config *params.ChainConfig, header *types.Header, uncle bool) error { - // We don't care about uncles - if uncle { - return nil - } +func VerifyForkHashes(config *params.ChainConfig, header *types.Header) error { // If the homestead reprice hash is set, validate it if config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 { if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() {
diff --git go-ethereum/consensus/ethash/api.go celo/consensus/ethash/api.go deleted file mode 100644 index 5dec93ad956a886c406a7f0750858daa736797d8..0000000000000000000000000000000000000000 --- go-ethereum/consensus/ethash/api.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package ethash - -import ( - "errors" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" -) - -var errEthashStopped = errors.New("ethash stopped") - -// API exposes ethash related methods for the RPC interface. -type API struct { - ethash *Ethash -} - -// GetWork returns a work package for external miner. -// -// The work package consists of 3 strings: -// result[0] - 32 bytes hex encoded current block header pow-hash -// result[1] - 32 bytes hex encoded seed hash used for DAG -// result[2] - 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty -// result[3] - hex encoded block number -func (api *API) GetWork() ([4]string, error) { - if api.ethash.remote == nil { - return [4]string{}, errors.New("not supported") - } - - var ( - workCh = make(chan [4]string, 1) - errc = make(chan error, 1) - ) - select { - case api.ethash.remote.fetchWorkCh <- &sealWork{errc: errc, res: workCh}: - case <-api.ethash.remote.exitCh: - return [4]string{}, errEthashStopped - } - select { - case work := <-workCh: - return work, nil - case err := <-errc: - return [4]string{}, err - } -} - -// SubmitWork can be used by external miner to submit their POW solution. -// It returns an indication if the work was accepted. -// Note either an invalid solution, a stale work a non-existent work will return false. -func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) bool { - if api.ethash.remote == nil { - return false - } - - var errc = make(chan error, 1) - select { - case api.ethash.remote.submitWorkCh <- &mineResult{ - nonce: nonce, - mixDigest: digest, - hash: hash, - errc: errc, - }: - case <-api.ethash.remote.exitCh: - return false - } - err := <-errc - return err == nil -} - -// SubmitHashrate can be used for remote miners to submit their hash rate. -// This enables the node to report the combined hash rate of all miners -// which submit work through this node. -// -// It accepts the miner hash rate and an identifier which must be unique -// between nodes. -func (api *API) SubmitHashrate(rate hexutil.Uint64, id common.Hash) bool { - if api.ethash.remote == nil { - return false - } - - var done = make(chan struct{}, 1) - select { - case api.ethash.remote.submitRateCh <- &hashrate{done: done, rate: uint64(rate), id: id}: - case <-api.ethash.remote.exitCh: - return false - } - - // Block until hash rate submitted successfully. - <-done - return true -} - -// GetHashrate returns the current hashrate for local CPU miner and remote miner. -func (api *API) GetHashrate() uint64 { - return uint64(api.ethash.Hashrate()) -}
diff --git go-ethereum/consensus/ethash/algorithm_test.go celo/consensus/ethash/algorithm_test.go deleted file mode 100644 index 708454852b5cac07d9d04539ce1035023855690d..0000000000000000000000000000000000000000 --- go-ethereum/consensus/ethash/algorithm_test.go +++ /dev/null @@ -1,818 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package ethash - -import ( - "bytes" - "encoding/binary" - "io/ioutil" - "math/big" - "os" - "reflect" - "sync" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" -) - -// prepare converts an ethash cache or dataset from a byte stream into the internal -// int representation. All ethash methods work with ints to avoid constant byte to -// int conversions as well as to handle both little and big endian systems. -func prepare(dest []uint32, src []byte) { - for i := 0; i < len(dest); i++ { - dest[i] = binary.LittleEndian.Uint32(src[i*4:]) - } -} - -// Tests whether the dataset size calculator works correctly by cross checking the -// hard coded lookup table with the value generated by it. -func TestSizeCalculations(t *testing.T) { - // Verify all the cache and dataset sizes from the lookup table. - for epoch, want := range cacheSizes { - if size := calcCacheSize(epoch); size != want { - t.Errorf("cache %d: cache size mismatch: have %d, want %d", epoch, size, want) - } - } - for epoch, want := range datasetSizes { - if size := calcDatasetSize(epoch); size != want { - t.Errorf("dataset %d: dataset size mismatch: have %d, want %d", epoch, size, want) - } - } -} - -// Tests that verification caches can be correctly generated. -func TestCacheGeneration(t *testing.T) { - tests := []struct { - size uint64 - epoch uint64 - cache []byte - }{ - { - size: 1024, - epoch: 0, - cache: hexutil.MustDecode("0x" + - "7ce2991c951f7bf4c4c1bb119887ee07871eb5339d7b97b8588e85c742de90e5bafd5bbe6ce93a134fb6be9ad3e30db99d9528a2ea7846833f52e9ca119b6b54" + - "8979480c46e19972bd0738779c932c1b43e665a2fd3122fc3ddb2691f353ceb0ed3e38b8f51fd55b6940290743563c9f8fa8822e611924657501a12aafab8a8d" + - "88fb5fbae3a99d14792406672e783a06940a42799b1c38bc28715db6d37cb11f9f6b24e386dc52dd8c286bd8c36fa813dffe4448a9f56ebcbeea866b42f68d22" + - "6c32aae4d695a23cab28fd74af53b0c2efcc180ceaaccc0b2e280103d097a03c1d1b0f0f26ce5f32a90238f9bc49f645db001ef9cd3d13d44743f841fad11a37" + - "fa290c62c16042f703578921f30b9951465aae2af4a5dad43a7341d7b4a62750954965a47a1c3af638dc3495c4d62a9bab843168c9fc0114e79cffd1b2827b01" + - "75d30ba054658f214e946cf24c43b40d3383fbb0493408e5c5392434ca21bbcf43200dfb876c713d201813934fa485f48767c5915745cf0986b1dc0f33e57748" + - "bf483ee2aff4248dfe461ec0504a13628401020fc22638584a8f2f5206a13b2f233898c78359b21c8226024d0a7a93df5eb6c282bdbf005a4aab497e096f2847" + - "76c71cee57932a8fb89f6d6b8743b60a4ea374899a94a2e0f218d5c55818cefb1790c8529a76dba31ebb0f4592d709b49587d2317970d39c086f18dd244291d9" + - "eedb16705e53e3350591bd4ff4566a3595ac0f0ce24b5e112a3d033bc51b6fea0a92296dea7f5e20bf6ee6bc347d868fda193c395b9bb147e55e5a9f67cfe741" + - "7eea7d699b155bd13804204df7ea91fa9249e4474dddf35188f77019c67d201e4c10d7079c5ad492a71afff9a23ca7e900ba7d1bdeaf3270514d8eb35eab8a0a" + - "718bb7273aeb37768fa589ed8ab01fbf4027f4ebdbbae128d21e485f061c20183a9bc2e31edbda0727442e9d58eb0fe198440fe199e02e77c0f7b99973f1f74c" + - "c9089a51ab96c94a84d66e6aa48b2d0a4543adb5a789039a2aa7b335ca85c91026c7d3c894da53ae364188c3fd92f78e01d080399884a47385aa792e38150cda" + - "a8620b2ebeca41fbc773bb837b5e724d6eb2de570d99858df0d7d97067fb8103b21757873b735097b35d3bea8fd1c359a9e8a63c1540c76c9784cf8d975e995c" + - "778401b94a2e66e6993ad67ad3ecdc2acb17779f1ea8606827ec92b11c728f8c3b6d3f04a3e6ed05ff81dd76d5dc5695a50377bc135aaf1671cf68b750315493" + - "6c64510164d53312bf3c41740c7a237b05faf4a191bd8a95dafa068dbcf370255c725900ce5c934f36feadcfe55b687c440574c1f06f39d207a8553d39156a24" + - "845f64fd8324bb85312979dead74f764c9677aab89801ad4f927f1c00f12e28f22422bb44200d1969d9ab377dd6b099dc6dbc3222e9321b2c1e84f8e2f07731c"), - }, - { - size: 1024, - epoch: 1, - cache: hexutil.MustDecode("0x" + - "1f56855d59cc5a085720899b4377a0198f1abe948d85fe5820dc0e346b7c0931b9cde8e541d751de3b2b3275d0aabfae316209d5879297d8bd99f8a033c9d4df" + - "35add1029f4e6404a022d504fb8023e42989aba985a65933b0109c7218854356f9284983c9e7de97de591828ae348b63d1fc78d8db58157344d4e06530ffd422" + - "5c7f6080d451ff94961ec2dd9e28e6d81b49102451676dbdcb6ef1094c1e8b29e7e808d47b2ba5aeb52dabf00d5f0ee08c116289cbf56d8132e5ca557c3d6220" + - "5ba3a48539acabfd4ca3c89e3aaa668e24ffeaeb9eb0136a9fc5a8a676b6d5ad76175eeda0a1fa44b5ff5591079e4b7f581569b6c82416adcb82d7e92980df67" + - "2248c4024013e7be52cf91a82491627d9e6d80eda2770ab82badc5e120cd33a4c84495f718b57396a8f397e797087fad81fa50f0e2f5da71e40816a85de35a96" + - "3cd351364905c45b3116ff25851d43a2ca1d2aa5cdb408440dabef8c57778fc18608bf431d0c7ffd37649a21a7bb9d90def39c821669dbaf165c0262434dfb08" + - "5d057a12de4a7a59fd2dfc931c29c20371abf748b69b618a9bd485b3fb3166cad4d3d27edf0197aabeceb28b96670bdf020f26d1bb9b564aaf82d866bdffd6d4" + - "1aea89e20b15a5d1264ab01d1556bfc2a266081609d60928216bd9646038f07de9fedcc9f2b86ab1b07d7bd88ba1df08b3d89b2ac789001b48a723f217debcb7" + - "090303a3ef50c1d5d99a75c640ec2b401ab149e06511753d8c49cafdde2929ae61e09cc0f0319d262869d21ead9e0cf5ff2de3dbedfb994f32432d2e4aa44c82" + - "7c42781d1477fe03ea0772998e776d63363c6c3edd2d52c89b4d2c9d89cdd90fa33b2b41c8e3f78ef06fe90bcf5cc5756d33a032f16b744141aaa8852bb4cb3a" + - "40792b93489c6d6e56c235ec4aa36c263e9b766a4daaff34b2ea709f9f811aef498a65bfbc1deffd36fcc4d1a123345fac7bf57a1fb50394843cd28976a6c7ff" + - "fe70f7b8d8f384aa06e2c9964c92a8788cef397fffdd35181b42a35d5d98cd7244bbd09e802888d7efc0311ae58e0961e3656205df4bdc553f317df4b6ede4ca" + - "846294a32aec830ab1aa5aac4e78b821c35c70fd752fec353e373bf9be656e775a0111bcbeffdfebd3bd5251d27b9f6971aa561a2bd27a99d61b2ce3965c3726" + - "1e114353e6a31b09340f4078b8a8c6ce6ff4213067a8f21020f78aff4f8b472b701ef730aacb8ce7806ea31b14abe8f8efdd6357ca299d339abc4e43ba324ad1" + - "efe6eb1a5a6e137daa6ec9f6be30931ca368a944cfcf2a0a29f9a9664188f0466e6f078c347f9fe26a9a89d2029462b19245f24ace47aecace6ef85a4e96b31b" + - "5f470eb0165c6375eb8f245d50a25d521d1e569e3b2dccce626752bb26eae624a24511e831a81fab6898a791579f462574ca4851e6588116493dbccc3072e0c5"), - }, - } - for i, tt := range tests { - cache := make([]uint32, tt.size/4) - generateCache(cache, tt.epoch, seedHash(tt.epoch*epochLength+1)) - - want := make([]uint32, tt.size/4) - prepare(want, tt.cache) - - if !reflect.DeepEqual(cache, want) { - t.Errorf("cache %d: content mismatch: have %x, want %x", i, cache, want) - } - } -} - -func TestDatasetGeneration(t *testing.T) { - tests := []struct { - epoch uint64 - cacheSize uint64 - datasetSize uint64 - dataset []byte - }{ - { - epoch: 0, - cacheSize: 1024, - datasetSize: 32 * 1024, - dataset: hexutil.MustDecode("0x" + - "4bc09fbd530a041dd2ec296110a29e8f130f179c59d223f51ecce3126e8b0c74326abc2f32ccd9d7f976bd0944e3ccf8479db39343cbbffa467046ca97e2da63" + - "da5f9d9688c7c33ab7b8aace570e422fa48b24659b72fc534669209d66389ca15b099c5604601e7581488e3bd6925cec0f12d465f8004d4fa84793f8e1e46a1b" + - "31b7298991c6142f4f0b6e6b296728ae5fa63ccb667b61fbb1b078003d18d97b906af157debed5e6c55d5a61cae90c85f9e97d565314a2f9fd9e0c08430547d0" + - "7cfcee3271f921b95c32a11596219abaa30abc62c2c72c6725078c436c677320594df6bcb92134c1b114fffec982a1f68f13a9f812f074b9fb9c78f2cd4c1c90" + - "7ebf1e447f7a422b06303921e3d54f430584d849eaa4b7d652e92a5d659bdfc462adcdd7991e8c66a19da4ddb5390463d073941491859397f135ebbbdbdf5801" + - "cafb873c383893390141ae385515504d74a33608273310c312ba468046d2e20c271a38cc0e3920b39705050e752f34f244fc23ddd17ff18677756a87671d4145" + - "3aebf97e4890da1d645f41eb20da92a8537c787ce419580073c46aa3bb154952993142ec5b4fb6e8f108fd15fc618cd5c27b45a37ee6dcd52a4ce656c0f58604" + - "717ec55f5e592355f1f20e8316f8fd77243734a8b0f50ad93c1d95b5b0482afb22cd0667d935bd6053d7198b54974e10d100df7ca3ec2e0bb5ccce5807b266e0" + - "8429d5fec2ae6ae1cc7c5efc27f19c89d4b4a6c5c0b9397886dac635ba37446ff528b582457a4fe7f803f1a47903574f8982d4a679b627396a4e97aaa12fa179" + - "0d31ba52e9010bc3c26ace81f702f86649fe9eeda9ec03b74a8a5cf540d82e22af33ab893564397dfc4edd8b1677350df5b82ab61d24db95f58fd2d78afb49c7" + - "2d2b1fefa8ff6606b8623829cc752ea37d663b945f3f1d48ad07b1416af252f81b55acd8f164da4faa9d9453721b3b795041ce7df7c77edc13865dbe04fee331" + - "47daebe18c183c4a6594a6df3a4d2dc5e3811d805102c9c49286e3d12b38927fa49a7b0cdcb1d799f57118953e31c560aae213a1799d59a78ae68f0590347061" + - "fc2668caf08f860452f6b7d3ebc1efecc2e1227d33296b1f1850360dee7236e85274eaede4d18a58b4261ce1f6a7d283dcf64e6d021813f82a566354445327e5" + - "6217279b2393fe5aa0f9eb149d4866e1105106bcc221810ceaf053f2ec733d8a22f409c1baf955e50184005c5d55de907de97f5f713b62ae10937e1a7af6267b" + - "d2a239e8589017197c343b81540bc26bc52bffd5336fb1da1202a511c7175014d2f500b9d9ce78e4b9f2b158d0fb27af352b6f78c129cad642fe909612c9d658" + - "17a8d7f9195ee97201675a918e3cf520fdc19f92b7e6a3db806d4f3799361334082cc58a22ddb4e4f5760bd1667c177b26be325166c6bbed669a158fc87acd43" + - "a2462e12578d72db6606f9e24ae659ff411ac9b31d696b8354fd08a591622967a14f8468eaaae3907b7818154ba2d6e4581589354d178bb6ae1c03651c44bbf0" + - "e7fa52cb0da09508b5a444aed05a54f416841247a4fe36bd5529029e3adf78b105e22468ed775f4d0954504dd55f2c9b9e6b3a086370b2c0b6fec7efd6914e07" + - "26627edb7a04869a874e31f448271077a7de3031cf81bdbc39848efee6075e0d65fa3a32640e9f0395cf7ec12139992aff0a54e0a7dfe5048b3cc03246b56f7d" + - "3093538a7b87538d8792a665bc589373621b2f3cf47d2c1f8f580fe34d79c6b2a66323ce89808ce0e5cf77700f5a4446c4be01a310e8f7c7ebefe756b0044886" + - "a0477c88ee8ea8c71503748a4cf9eb40ad5c1c8accf7c63c0f43a94ed2b8a5999df3ab9b11b80de73310e036ca88668e640015fcf9cd18eed05517d54896f43e" + - "25e7931b44872c4e4183500e0e8c5103292bca1c0d6b0b00c9acce25d31204bb3e4f255c03a0a0916664e9c831b28b364078109a74411a11afb1e610c7d1c9d4" + - "ba5e10d0ee0da409654d9e7308395e17caeb9caebccb0192679866e6f2ecb5f10044333bb70d61712adb6d74cdec6918ed9a71d9925da576a1e6f4e906a5cd5f" + - "0e94a25e48a4141e4e2770144b63e2449b0f84c82879f34d78440cc430196ba85a213fdac1bcf279a46d7592fa29a876bb7a2efb7081365522a3f06fdceaedd3" + - "cc0335cef9ea570733fe8799bb1b918aa7732b4d175929d80c7844a78e19f2dc6a6febf648f49b40320b0f7d784e7f84e45408d70b046bd01cbd8fdaf606fcd3" + - "02f4e5a48ab8d13e93a246adfcc94f3109e02a7a969986e75b6ced6bf2d11a55ab77488e131b65a06398fa8e384dc90d875584c9b17cdcf2da5dd72a461cd07c" + - "4a955c5fe48509b3284476c42247e086de7d63839b7358cf4ebd9edf9ac8b6fd0c096166405de19c51e8785009d30feb67cdb8ff9ba55459dfdffba8c022e26c" + - "0ebd399e4b76ccb4d5491a862c2c4d8cdf1461a96c9b98150e170efacec980edc00a2c7f6d7c6bea3075627e1eb386a7f1ede1059da81a4ac5cf35aa173c88c5" + - "1818dc0fbc688b68b82ddc225b6c87588e0c680e303e737c82a13e34be58df8b0cb336aeacc698c79e7682ebb69e6cd6bdc5d11790c96afcfa9290f39515142f" + - "5f90b938216a1d14bc049ce3f0ac135722208b989d2557d3520c2186479f179e50fe5b125b8d6638a65047729c6249b9b2c6381c9103c97d1b389cc9cdb31c21" + - "8a2eecbf4b9ad1dcfa57446cde88f96563a544c49d6f5303a84a1b7cf074fca78e67e72c9ffa0c542fb646418c6434b16b771088140725cf2dc723c1a975c4ca" + - "8a80e633721274907353f51e95952c2b403b45750b42ad10961f60473eb54616f61f7b038c5b7eca475d6a2b844994a9eeddce4f7bb49782e50ef78bc13b85d1" + - "9e956f47c60823f3d1981413cb78d309f63a844694861b11b5238961c71f61d82daef6795734f0961e92b9167c57f48e91693e9656fcc6e88f9ce2d373da26bf" + - "45b3dff50211fec72387005a7e04828e4ae7ddd10fc2332acf5f1b0f67adcd863752573c2d24488857bfc58c41af45be7641f5cfff611f184612fc0d695866f4" + - "2b396b1d9881f442c4a995f4b500f02d4ab4b53ad6e01776ab0e244583f01301203a1515f3dbb73906014e36c7143bf882b005f0228ca0562623893c8a24b7c6" + - "4c2c561912010c121b5c3a1e35e75c0b094731e9c0d6acf5a2b1e5b179355525a175640579705f898feb98bffa25633bc126613fa27d2ceb214812902ada23f4" + - "367a78655d0d2276095c9e83dfa79153730103963499c367c5621fecfd0888253df82b3d5716703ef92594cf269310b9e6c892c488edb3bba1d0b216e92f622a" + - "7f8f7f00d2926d81a4c7ca6cef40d240576a8d5541ccf561c8e0e699925d20347ba7493ed6e182cfe3b633e70b3ce3a0d90813574f6fe329c495d3cd46fd5d7e" + - "bdde58d7eafcb134a9a5d3e5d66136e8c9b5d9ecac195dcc44158941c9fe2d87db52a7ddcedc9f82ec160901cc36a9c877af80ceae0563dfa75cabde5d7a7c94" + - "9f24bc190f7c2045368356474ff6eee284e7125d1c5f9a036fbde24cecfd3a30481ce077f20cbcb31924368296abf66ce4834102cf7cb949d1b4c6faa6d006ef" + - "21379cead5d5a39324d41555c46e0b42a1e871143e47f8e6b3d794e75d7a43c282732766d856e04e666ea346657b157404b0fc8534a2dee8243d40a5e37609e6" + - "18bc1d52b91a7623aaf8214a97e4c8c5d860b31c3792b129354a121a7a7e42b50dfbe3ab6590769401eb280545547a43c3a1455355d5d5fdedccb472abfe75b8" + - "f5e7d62b0b31553d8d55de0c3c71e6f5a2abba6fe81e9a42ec1968f235bc4296c1ac5df7430917453384450ab56dafa7c7af764cefa3b0bc861c52ae27692365" + - "9d7d9ed7609958884147acca867909a75bb6a2c364debefaf98c7ff70c7f4acb5cdb81100fd79a48c5139f8bbdc6553b509f1eb0f5d5d31886a602cd669b3f9f" + - "59195a1fa2bcff1170003ba1b2e5e9ad7f2bfcd0573d0f2be9d8fc1773c3a63a2b9292cdbf9b4515c0b1d51772e5ee95303ff493d85314c989e269df4ec3a916" + - "40988a11c6a4ad96f7d0541a150edf444c2b1672aa6d37564453b835c2d39864c05c4366492fc9164bf73795410e7aae8206430403357fec6389142b4976b218" + - "d70622b4098e322f73020a0d045f07668d1e512c6eeed6e2befbfc3a6ac64054396df96fd41f7aeefa0ab1f66bb52ee1a1df066f365fc43ff0800b0398b621f9" + - "a415895268505a81517c44a56dc94e76580fd107dba034bab9f4f4b8a9f881ff34c60c406c47b6d4a998894401006aa88f328393f9cd55a2b4d24db5abbcb05e" + - "20d392f3feab3ca12dac475eb3690f2bf9c699d7d90900d9a840068c8cdda2ca7a27bebd685a26eb01a768259a65ab4d7efc1811c87a5a1f4e5038f6b3dc74a6" + - "b46d9ac58d31bfc22dac23645aeef819329c9419326f22e1c24c53457baf62ae9b92ab5f999d4ef0ccfb5a21b7598340eb2d399ec81588b6a674c5a1e45aa238" + - "c55cae8e1af0f5d64ea378b8afeab263a3a2e5c71cdda4cdb824ae55df2b0260aadf386275ef57781d46f6da3d0b300ea68c14a620c25b5df738c54aef04d63b" + - "7dee06cd225e9ed00e78abcddca5a133d8b5e0d9b287e6436014c5da729442239bddb7ecd3fe34e6f6e530134a03ef45de4ae4fe3bf507f16cdfb9bab1fc90e8" + - "dc565e4a7ead95352c5a894661e5d82c6d0fc47843d5cab12c4013db76c90734cbff34c73d0d873ac9b27b417665f4948469865f33179624860604a9aba2ceb1" + - "68e74b6af3d1ad0bfcac4180ea844339a034b6b2c3e2f61f0c7afbaa76c1ebe93727df1d3db27d59a5cf51b2baaf637b6eb8a20302ef9af0b25dbe3a5e74331c" + - "6b0c8a0cf2a2ad72d2e19797983e09468ea95270dc229f2fa084dd2aa96e722016504f6d82508572d9c30711c3ef41ae3ae2f36cc6f5dddbcb0b40d9499b24c5" + - "4cd36d2927a6b9d57e335e4fca7f0f16887711a8c1ffa0b48bda46c506ca444b7c23e2c8dd086c2a87283d5fc0d58e9a384106837318dc84ffe65b52d4cb9141" + - "2672adfe139c3327350fe3cf355a08c0ca43598a253833e114243c5253077d65643323f5d69b3c7902d91bab7a0928754e7d80afab8d48539fcbe0d9ab83b4db" + - "43a6594c4071df2ef35acd1f53006a570f09104f1776b26a303e2aec93a00d2fd8c952d1ca0e54504cd9b469be7c1e71557ec31467ecc773ee817b17c4418712" + - "163ae86646b20b80c85860e828c48e88f1309c9ff018e6a95f4c1178de6a4f9f5860039511845da7d8727b5d824ba2502d0a3d76ce74372db77c2050c728dd65" + - "b3a15da4f1e1e41c3c2acfebc5618e5e923d503c43a3421d2628ac037c5ce13c74c4ee14d47af02323872f6bf2e8bf09d017ea6e8ec4d3f9fc4fb203ac4e1663" + - "756b11629224c676713a42b1f43dfd6362876be1c4865928688765589e26c8dd8bc04ca18d76ced7f786cdb0fa5028ae53991d5b7b45f93bbd50aeb97300f04e" + - "69c6736f270907f6a7ad76dde0a365183a961bc8385511e0f22ce0cb8f3c42c5d3928621841e30285fb625294865409267dbb0cf91730ba2fb1088fb79789a54" + - "a856311bdca5b0ac0e95fbc79b11c561dc03ea82db182808031e86ec327097143ee761bb62dae8a9f4101fabcac1fc87b3c2080820582dc8a7a8287364550013" + - "08053c781b3eb279c89e817fe97103b6930fef2dbf7728def389403a4283f63ec04ae953784b749f0ea6f08749781cd17fadfd15bb197afd2f4e0a8aade2b1ad" + - "5100cbdce49ed59658993c00e06bf57c0026b97beadc30cd25f586ff03ab40fcd731535c9a1ccb2c99dc7f8815feab767e1237cb069981f28d8fe26bdec24218" + - "488e6086c0ab0efc5d4211fa0726b3a11387df9bb62b863a7b154ca390a268f5e49f50dec45d24bece2a06575cc07a24bfff017d7445024739efb050ace5f345" + - "98dacda843d4ef5bfb2c931dc16ee3dd8b61a6f01d9a7de8bbb6d89ca8695f8ef8bd1cc6e0455848fac7691e6789218790270aef40fba114557fd88ff74fe8fc" + - "476d9b9665d7e45582540710ce92c8dcad1ad8c05642a23a0d58c02db37ae1a0e70fbc5f71b1300fe398c74cbad37fd57379f58dd3e2d3de6860a17acf3c9321" + - "02eb4f9d596497bd849c5bfaf59a83113ef389b6896aa4d4665504a22486299993a9987b2bbdb47d59b3f6ce5d2c9f9ba33b5f0760388ca7f8d8af07c1cd28f5" + - "67a417a59ebde4bb9867d4e7b7b79dd8665602c029e9a16a7718efde3d034f13f7f0b9af1702c335893526cb87afc2100e874b25c37fd666bf34bf6a653c7cf5" + - "44e1fe0286a6723c7d33461dea380b392dad68f79a78fe1b785d7833ca0d1cd68cff472991a625e3099f3ad2cdc99bd37eae35353cecf424098389dbaf1885fd" + - "7db54909a92ee879609eb2e9ef4de1f4338f0df53dbde486ede944ae69869fac701d4f1f48c83757b470ea28c9de2ae5f1ef5d1c91118d16ca0d80b1baf3d314" + - "056949df27a09eff70c9ac50b54feff67a165ce5e22ba2222defedc7c39e02356c3553e97524c1506441527da4f5de121142ccd494f83114b3ca2dc37e15c752" + - "e2faed7d50254124d68f67e26f4f50c9f0edf6e58b916ca830c4e33801dc11039b18292b87b08f4f2edbaaacddcdab78ff3a0004f86034080f2ca4394b14aed4" + - "31e38e3605e6b257bd772954d2f4b846a17df7ed6e5dafa33964d9e56a07a19898fb4dfe8b2ddbd11fa0013e6ebc0e429a5166a43d1ec45557cd1fc1bddbec4b" + - "2e9ca26395394c96395ff8f557bd0f7f805c09f0c18534585b7c7fc1d07f145372983ad77fa804fbb7765934e72beae0929a87cc6bf7f6c242ff5db2d4d5541c" + - "8c366d22e24e1da5379836fc0eb484683285f99f178b98ca170464bdff60ee04584c12c65408102ac6dc7d10bf58a7d770bf1b3c636a48f934f6f4bbdbcc75d3" + - "fc551de3ebaf77006707f6120b3804f2bef9b4bd59f5996610c09ba3953994d1b78a9f3bc3bafeb52266f10755ea842e5b4370c937c09afd34a092ff9b98b4d3" + - "518bc2480d4b132455b7f03774ad76b83b254742117921c31cebeab5f39c145f7f373a5603d17dd95217ba1af37a0aa95b2992efcd02d0bb4ad08ebafb31440f" + - "1ccfce45882b547ee4bf6ec7ecae11ed79fc63b03636c8a14ec4e0f6877eb658d839be2eac0f10a8948e74203f46078ce66aad2764ff05590e2ac7a8dd8b3036" + - "901fcb7ff3369ee989a28e34b9b62e1e607d14da3049ded1a4ee50257195eaaef995bed79ec85111abb522aba1fb306869a1ab381e82943f35345bb5502bb90a" + - "e2a0af77526a84754ee4d600ba7f8ac98705ee687bab949a081849889d7b83a21a3dd34af84dc2b9458230ef0ff44c6398d3c6e48e5c09c399ac4d4c7b285549" + - "e0bcab7fd96de42f072f1cb633e3e250745321049d0d7ecdef4636e70e94c8414e76ecaedd6ee0792e97de11e7dc1e1e1801ad68f9147278e268d7ad76c5bbb7" + - "98386fdc13ca8c77569d96e0debba8ea3b751352136c8f1c8d611a69f1baa9aa4b9d0a476ebd5dd21339ef7f97f09aa86b69a7b114cebe17a6b0e58bf52803d6" + - "fd47d9eac3a988b51e9bca95c546d49367a3126bf8ee44fbd0e77611473a1d3d2de0ce4ea54f9bb7f9dd0d0c065f613a623fad43a445eba294fd00037492914f" + - "b74d10d0b97a0cf9bd3151c3cade89521f36b6fe1aca7f352e79a77d063da5337a7c88d90e9e566bcd97732baa4459305967c2f65adf1a4a4c7991cbc99df3b7" + - "14335a107a97a4ab104bc94fecd1d003fe6d2f22e717853c449881c4ccaa7e7a1e44961a14a47a0d0aa1b1493dd02760ff4d31fbddf5941f93c8e5925d1886e2" + - "8761baef8610fa6be016c8f4fe65bb0f335152d5e94893e274f2ab90118e4c07957d252963755b4b638ffc0a734fbe6e32c2e304b10a46a4eed330d101c4f0ae" + - "011e7f94b89bc0eb9d358a6548b3f0c47ccc3c2d986d381437c49041629c6cbf61bdf0825efe17e4abef128003681450ceeff0e28842895d8e338c247abf81cb" + - "7260fd45042c1f6c630a4b195579721392e577fbfdb9f5b003a8b9a6bc15ae754f6255131a0be600c7b07e2cee1ffe32aad4687f9a429998ed9059a99fd879ea" + - "c4dcb55f4551bbb70c187cf1b162e2ca4a929edd6ec9260877df652622ae073fc63c0d8522d3882ba888ac50a67a68fb6530193f93165093a1d8132e87d8887e" + - "ff2fdab0fbae6ab9506dae61fada4023133d166bcf1956aedc3237c77d1c81dcc84ae957d89367b0fc950c78e58f2cb9c4fd93e16b94421fdecd46c3ff55592e" + - "4374a7f7d8ede9923115770cb416071e8f102d4ad78b891464ffd14f589c238c8e13a4e2a81744d179e7d3ae36cffee75ceb99633face85d077d0c15b3970930" + - "075dc08b420e0a545200895207c5a746a18ce9ab64a50d3dcf44da857fb65e4efc29b2b4d532dc6a03b699dcfd77030a4945e6431273e25f06ad8f913c2a9eb7" + - "59d8d3049868d337e451726d95c4cf8baf381096fc9b62679175dc8f14e52f8b99f212cab6544414c62f17c8323256cce95356dcd351e34c7a1576b17c1406d7" + - "5b8bcca8099a1993df1541ded61b876ae83396b191b719c4b1cbe50d73fc13da352d827ba09aa7fdfef3e4e0273c31ef4fd38b93cf64199c3969a7c09dd5e0f3" + - "ff93a5a7db9c2c1ec25e3060bcb5481c6802e1eca78f31862842ea08e8f92b8e52856c4c9fedd0bf20e386cfdf926425f7756ff41fd3567c5bf334e96e3f492f" + - "74bd0519d8d98efa0b427ba681b8b1be8fab041ff084dc5f8c4d5d48f481115d7e407ad8a6034f481c2be86f8451980c3aa83a3fff245d90d13801a54527e97b" + - "e392b25867882d43e3819f4a8aa380db63954ec23d2f0c11a7aa5bc7a3aedc43ecd3b024280ed8843399e28deb954bfc11a3197fb14a9c9a895859e390e9586e" + - "2ad21da39bb9ba79a62222d228a0fc96a24e801f00afc3f98d2168a8a253f24deffe461f6313de9b433e1d2e307239c0e3fd5d9fe4c8352c2c6797b1737e93fc" + - "14d411bc69bbc9d78cf91734052b8aa1dab348e4c243b8e6d623865c135f807de8d5fd88f3921327affa37066dd538351bc4ec52eece88856de0a424a87d062a" + - "f68cf24db37dbaa8e8e96a812fbf32ccafdf1b9d27f11bea23df02143bd09061a881c010819a315a5b6ee44b3c60979b3f7b41f488b2429d49377d6542fb0e22" + - "d09a0ef5b81aa7c8134c0aecdc7a4f9228559d0bb826d30fd77fe0f834212647ce61e22fef0a1c10eb4177de81c31c12054a15f81b605619f3045646110673d0" + - "b2d79d80577fa43284266fd2ed54f9a3b9df3509f79559c5bc51a58521bfeb2f95d8851527b7ea47b92a694f6ea2b67dc2d4f506d11d2db32c2929cdf5c8816b" + - "7f0c310cceb7ede08d5965ed2c7be6c0a317251c7d31cc4a15f6d7976a8a1e6a2f386fe0071d43a50bd0ce5e864a8e449fe9600c6e4a84866879c490de9f9d46" + - "3f22708abf34d3e180dbb6005484a6afad373838cdac335f05c034e3090b2fbaaa53fa2db1f96bbe141d570f17363ff98672500e16994b79be74634755b09e66" + - "f1b37e338c946bf85e06c97e31dbddf257d58fd10468278648d86f38710c2ca0b6ea7cac4ea0e2c49b96bf1998bde1b3d38aa853736308e12b4a0d467fdb8a73" + - "0d810ce45518614bd5845f58a9835a5cfbe745f45ef59ce9a677d10d8c9f6294f1a0565301efb3c6610afda35167150bd326c77057e530c213da63af3e6a600a" + - "d87b16ec5cbf76a13764f71b3e7e0c867086ebd9fad02e1d747030064e071a13da4758cd0fa20872b3dc350f4cbfcde1b68a97aca41e32207b40beddec412c0e" + - "c75d87c6671ed94bda5170aa2866509161c28d550190675f60139a7b460469f3d4829b3c65f5d185936582629160522fcfdcc53fd0dcc8fc46de11d52bfcc5e6" + - "3407ecbbb682cc1693d6543756fa4e068e92ae1a94924a1ff6891361e5f262b7d3c3a3bc2866f54e6d03ebd5479afa3f424077d51668cc60e23b35fb0222ae22" + - "5223ba8a8c416b68c8853022d150c951f06f8f85c2078d3035b8ac3ae984ffcfb024431acaae8bfbeb981870f9ad6bbb88d7d5ff34ba21a44cbffd0aeaa435ba" + - "7d40d22602e807ac9a69db514ab13248133142cf03fac999a2b185f34d83fdb495ef042d4a5e92f2624193c88858d91c0812b18fd67046cf50635e6ab1ea9ade" + - "7b1fe783dc5147f14f9194cfa92c03a0456f4171f9e5c156fee1c607a1e9e06535f2dac49b92ddf5fdacbf88a062bd7ca5439bae645100121e598deee6043baa" + - "85cc0d727f08d37a766a55a9ca21ffb6594fb73f9aad15be4a64bafddb6c85d00f7bb5705d9e56b410dd80df8b087b6d67c7ca84eff2ad699f901415fab21343" + - "6351a9bdf83b440e29f3950c7e4c49963ab109686d78fce629e9207db2e17eb5f02f01db6441002d72c06c6c6bbcdc0a7443589ba29909a5a78864ad51e1dfda" + - "14782d869e4989ac3c5ef0aa1eabe540e9e7cd4e8eabe25b07f300a134a92718186f085d5c10a711ed0e574bf7550f6bccfc3c094d6e59619bde9fd892af8ef2" + - "50e1cc3afdcd9c84ccb97344542843028b00064b0c3d18ac0f0703fe6f9683d40813abbb883e164c5797bc1555338566cf8cdd358e9fcb0e93f08f7ae06a5121" + - "c67a231106ad8fd42f0798d7185c2de78b8b76c10e82272a405212ce3b904f90236eeea02054953b967cb614e8f8ac49b977152a52df981c86fa4a92f7f70eb6" + - "cd4eb65986564039b0d77f8bafedb4fcbf9c34b8fe9c5fa87b0785c118a8624498fb0184a0dbbfb16777579e1964330c12e494449f6aa5cf69ec4a32054be553" + - "6027e0d27c7044abd4c0b8e43db703209037efcfd08944647a90a1ab0c71011753354990cac5a472fae44dc370aac8131ebdf31456a8484e7fdefd268cbf5cb5" + - "85ac615039d3655b1348fc0b3b078ac41cbcaf6aaedcc1153bb8d55c307f45405ad6a959abb37bf8891c8dec79a9d7ccd9b791cb60361d4a28f33ec0dfd13fa8" + - "e0b9b29e14bf36f5047e51a39c2efcefcc156bd08e46c5c1000a3cdc2bb20713e19d6f492c40e51eb93628cf85d07041ae5353e7decc824cbb1db8ab3a7a7fca" + - "ff04c2af423bcfb1864ddc864624b827ddcff2a2f8fdb7a3d86d76e72b4f850ec1262d8fc89e7b12e4cc618afe6a2bdf205075c2008f93b7281d80180199409c" + - "de850d1f14ca0ff960f69772385cf0f0a0f47cafd5489ea4fd8b68ec7aa539b942379139756c95bb90818842cd43511edbb7577ae469f46728b13a61e6eede06" + - "3a4cdfab5ed590feb807d55d76e518d1d74bfa6704f7c8ccc672824b4d5ef5fa5b3ab8fdf2b6c1753404ba35b76aaa931a4e0e5ca7e440524166b23e9a8be9e8" + - "635381f6c9086802d428fece81395dada6b3b866e905ec00ccc4fb9b8415dd15e443f84b7220e3b28700ce3d88f9c6df2afea39e0ead537a50ee11f0c247ee86" + - "d4b3074e8761de4de611c409c6d4c369c2c11742a7763f6550edfaae49afeec33353a14d2ae60687dbeefd2fe29689da6ae79d7b06042dfd25a68bde9182fba4" + - "1ac53706a8b96535057fc2f99ac84a9cfc6549920c3e2cab44e48a08e77207b6a95b2f6179d6dfd6c2d9e3c91106a7a687e40bb2a1c5ccf566c0e31a0fdbd0a4" + - "f270f9812208f939efd9698a8b28ce9c5633f18ace7ab0a7550d9e7e26cf62eef49200331e19a64bed648b5d18ceb389bafbcb3f280ba78e4cf03b053f2a5f08" + - "3c852452837138004073cf6726143179386279f1a8f15d44876c19bf6c2e2992ce6056191bb1a386f0e1f6f249495cff126991c6560e3f613e56525c0c49b5cc" + - "2ea4e736d83480f2b45d7dc840b849887f54a2aa072e72e3fd0db34e5cddb02221fdf2a40fb6ec271ba3a09de8dc73c24328c5d9a33ae0adc9874902f25d5bef" + - "4d85914557e2983c93fba16cdd4bd929e878b5d51b142b6e9aa0ce84871b7b03ee6cc13251e17547c2d20a7d4e948760e207e29de58a7ccb71b87f99d79837db" + - "d0f293ad3d33ffe91435598e8a4584b7b7ef5b1a895a2827b4976f81d335e4aa6feda3539690899619a4cb34fdcbbecf1b8b38cec2ec7c07ce84ec3044f49656" + - "28fdba8971585afb509526640d36425777b6ddf5b2a49d795fdcf71e57fd35f29fff37890541b6e152f14fb6ea4c70a1b9f159d02ed895a68dcc276f5d5ae83e" + - "47c021392ee22a398c8c73b3446d61562b3ec596036959aa645a65e5d24f733e142ec0e184b72a2adcbe3913932b2c9503c856a7e989d24f306e01e99268188d" + - "f858694e297803effeb8e28bf8fb63ed6787acc2c61f509e19099607512d40928a08e649474a43728b63523175fad12ad088aade0c1e20815c7c12773bc959e8" + - "640ee23eef2b1653ae8918615b45158a01be5a5f39a75a7c6cd8f1f6b463516539771ad251d5c2d40c5049877765512c44e58bd3b9ac3a0ac281771097880fe2" + - "c9516dcd6f1373e1e8a52fc485d104004dcc839fe3d120f1432b213388dd37980ce8238c87a70d5abe95d78d696d2436eb23a8f620ce74335d5e47f6524b11c3" + - "e22288644b539e3ab664dd5fd6bafb02897aab35adaef204f82d9318b22f45b787f5bacd74b01d23537973060868a47f2e3a45c1d8805a1d657f2332af8170e2" + - "9435d7540e70e92a8c8794bf22d3e11d54ff2d48cbc7a1ac3cecfc48f80fe521f6852f97aafa0605f3e7084b15e61a74869512c9c2d84180686ea07b562cf35b" + - "5a0ca529481ddbdba9c60729f821dc7a5a8b5c7eaef1ea7927d455a702aab538e7441933c4fff2d27de5659d6fa41f0ee72bb13a829839267f3a7b51a81a85b0" + - "d737194d94e1bf8173248cb057cee19eb5e2cdda38c529298f3c4d3b95400198063c5b27e9262f9c66425c65568a09035bed9cd55c1f2ec4becb6b9c59445398" + - "ad5b7c85142e713b6dd32493dcb817c8bcdbd728e325c25c5a14d764b63f960d1e48a0bc7f4d2bf51060f83b1d1f2591c6a9b79182e686b887a2c1461442e2f9" + - "16e8582e298f87ca95a8052df33af20ebded7bb1c528920233d1aca3b3789494d97084890fa3db0ea7eb561b0087c4a90000db41ea072613f91ebba82790f33c" + - "fd52cdd92d2ef1246911ef1dd82ad083881b72a08a40ee55884380dd136a7c0724cded69c6abf1f156b14ecd7284abcbf66522264145ee78ab0ef0d2a74eb390" + - "10946d5efefb7175164e96621d3f158de8b57956b8b1155c35b32007e47d915cb61dabd556a370537737574741fcf9a8a23f7155bf1f0e3d3c0d2088d1191d9c" + - "9c974139303f3dda55a70ab4810fddca3561114969d370f4e6bad60a53815eab1c4613854d04ba8b049dd7ab1a935c728299d1502ff9aa3fbb356f87f2a52b6e" + - "947dc79b5fd211ed31dee722d3fd857f43aad973fbfacb7cbfe1b2553bdc76142ccae5b4021a4647b8d8087925dd3191a57198792b6f918de87a92705ce57905" + - "f2dcfb67a20f8c77e700933432d60a4536d0959415f15f3eb8a788f1b19c497d3b68194e27ee736231835469d8bf0ce1717ecf533ab77dd97b35881d8eda959f" + - "54a7935b1bc11d7f2e472757734afaf0463da3fad9804eb948e8d6444e8394b33f1c187618c7c02371ee6d378ebb7a20b6049a5504daa71999d15944ee82650a" + - "2388f374f3ec3afd4ca58ef3f2588997d194a2741252cf6562e00cd6b5c5fe4066454d2b3150317694052b4dafb40c2f04c850e4062cd8f0af2da75280046850" + - "77990788b27fa457ae9d0b622d18fc070f1d2661ecab529b5cb82f30a29610dc6a9e93ca9a2617ab0109957a45c1204e5eedb8860c6f4d57122060f39a4194fc" + - "a285f1e9e7a75cc3511b8cb4865719c2260a630845051876e7795bba59573b6ce5faf7e5708eda7be25dd49c8cace4c04c541074d703e6601e043f6c63a0a371" + - "1a381f0ff83d136f4aa29de266169ce5b3105cbfeffba370fa306a93830e3c0519a495b8b9f4b72078e2c45421b4b0667f903676a1339c70ddd1a90dbd21853b" + - "2826ac3fa5add5073c634d4c5e87db0efe18638ee93c460257e52aacb8600ff36739818056110b2e974a1959e3784903aa97b0fcd9264f7d8f6bb5d8b7d9f03c" + - "4b643955bf7966250936d4e7d651712db5e695a6a36b5e6f56c651ff737042b5bb73638e21ca6ce9a3e63fbb1906675d97001d7ee240d277d62df18acb169677" + - "963d231c5276bdf5767ec35fbedb062e61c23d759aefd287b2dd62a0d6f0518d90b3c1756fde50afd33cab395ddf3cd538b9ad8862a199141331c63110c9ddaf" + - "fa3d6c63a1fb1b45529eace826cc29a1df5df327bb782e573c41864c18e6d31401d19719326e5c35bb50de7fdc67177a6a6015b4264fecba2360ab72ae8b060a" + - "6c66c5a05782a15fe3c1833b47e3495d29f2cfa579fcb08f02fd064e9ef2ef5564ac6a43cfbcae7d79e9f87ebc2176611823c6624db11892f8c47f8c96a49539" + - "1c18f821ecdefb343eae3fd98dae1ef96fa3527788543c0d06d9793579cc62d91dc4d25312901c6368ba81c8536c6287230e8f97d25f6c77366609580cf26a27" + - "88502a9aada84a794d3674ae11cd1742cf245e9d9502dbb5b340c2a6c79e3607f6b47666e1ea991ccfbdf6cc41ede46d043bc4d3e5e6882414dc65d62f9f47b9" + - "fb7b828a89afd6361ae458c2cdc82f459c54977072702ee5a4c22955b8019d8b8d91f558897c4b661f8e5412ccdc10c40521303c0ffd353a0c04cebca5622a71" + - "192b144d0f9c5c0706a130df887526b7b6e0f358ad9f7d0fd4d87c5fdb29a7453388c0d009da0d4c47a5d6cf8363892ac42b6ce3388771f698802b4dbfd66aa3" + - "5fa6a6f8b42dd8446324501c807b6e72cdd35cfe08956a52f86bb4709fe2980f62152dba3571f18fcc4c1cf7a25384c4b5174e93e5afc9b9f12db2bd505ddade" + - "d670d0d71b9548f9a07ef98521961cd96e8f363cf3222336bc4baa284b5305aab47dace615c1b3f3fb1ee23ad9ca3f58b086d9169ee5b2d3c2831e1db4f905da" + - "11e1fe79e3d48c01bd9879ed68391e4d24d6db8d6774cb8747e7ea368aba3bbf355386408af4a59b23fce74a5e673a1044db66ed8529a65462269480736cdaa5" + - "0784fbd77e1c41197335b4c517af8a67eef5b7165c5fd6022cceed0396089c3985c36595497db0a0fcae478e4e4d68c57b93f466aae86dd4244633beaa8116a0" + - "de25d2a54353b7ee85fee58ad4780a2957d69816585a64f65e75f332614aa6786d1a1432f6acde385d3d6e870bc968c60c81401726a958f0caae336c83a9523a" + - "c174faed43ec67473dcd151506e334a6aaf1731dd3aaa831f934be83beaefafa11810e7eb140f4fe80cfba574e6106c1bfe9f0b20173a4ec2663ce0580df6daa" + - "7966a3a8906677ab680025782c61b95cec6a73b5deb16599e6521f9c6c4cae0d9286566388d5181d6ba11c51a25c62b510d9b1793f3ce9f73ff0c9226c8aae69" + - "5d014287df074a244014720ee38e3968557db00aa63dab71854b8573c42c65116e3d88bf040d53ef3165a5827c717179e2939e310be5eaf6fb75447ba98ce925" + - "98e83a32a90eea848500a30eaaaceb307d37b1201b83a744468a1a52632ce5525c1fce5f702421e42e7cc4c61caed539dc09001cd31a8a2b48a783c36c56a3a2" + - "d50de42c63981c86642cc92bcceeec8a66b4afad3c1be1df4bcb8beedd442c281080c94692bf453196ed1a66a074d56a8e7f60238ce18358373efc173e70c691" + - "f832e1139bc04e6258d77cf7529af7ce5eca28ca5cda818625c0bb5beca96d99fc9b6689a7771434aa96e23c55a41cff7b7b718df58260b3bc91762034debf49" + - "7d8ca8d5764c52bc9665bf86db5407ee1b786d90f8d7772597eceb98f0121e3996e771d951568a162f6b71042998db8208ece5b8b0c68107b8e2079765b0d8c3" + - "2747597072756208b0d84415a5334a88d916bda390e26ccf3046b860e7ccbe22c48cd3d3f51bb65a98ace74d52613f782db726babd02780b8d620655bf9d551c" + - "ae9ef3056e3d24f5e7c3105c4857492fedd244ac2b8c30a874c1446630b042d819bc6b6d2d96829de903db22af706e93c5ae876d72c633600222443d1765bc62" + - "a8a20c458ae55bce8cbbef753cccc5e7d929408d6a3709467373651f0163128aba4142ecc56ef11ff1fabf5eaf6e955b4252d1350e9002300a1236ab2fa0ed34" + - "c9cc7dc1d4f09bd31296cec1493e725b57cc496fdac4e8d26197376bda7f74c0965c4352bc9d5c731df04f9908899cce6ec3afe15210d115992b2d95308dd032" + - "13c557ac527424c7db02475a2fc78b88d022d212c3d02d5ee490e2436e6e572e8a1330465b9052f8a3de01aab76662d18fa3d076fb77103fe432d549bc861fcf" + - "f63f3401cda31673ee48826b68b387802fea4471deb1fc928586f1b1614c16311c9820b563ab0112c28af5c1af5121818540c4b7d7f549b33906c1b86c6674ad" + - "799acee7342e4a79d9295493b2430fd08f373338795764621bca444868f3f42b0e40abd4b8e148cad2861fb4980b83bb58d40eeecd8d8cb1ef1ece17b0ab72e5" + - "06c6e650a3a43081f545acbac51ed7e121df51edb75120cce30ef7dbf41fad331120e537fb35be45d93de4fac0cadc7e5f644e2b767a285facd5f12845559785" + - "57f4afc276e21d77f6162062430dc8918435f035f435ea419ae9f1ddb6afd46b243f8bd6a3a33e7970e7e76fab9ba6afa72a4806189462f9d0f231a23e3ee1cc" + - "51cd10cb9043a27deecaca866751f971254fbe3084c243ef5f516bb652988b770896ae5abfa12db2eb2abe404cf694e9f60d47e734e260ae668b750e11b26001" + - "0d2bee5ca555a44523742fb069e484f7a69c12d4bad026c03ed7af10ebc9cf2f54d143fbe4de83448df80668217a11f5a1187f35ff306e6c685cfc2417c14aa7" + - "aeba1fb7dab05c913fbcbb8e677dd0f89324048862220ab6f5340c38b70804f625f5a526d6675a49fdc22ea6ceed477097fc723a7b6eaffd65c48dbee13df566" + - "f8f3449d91abb367cf37a8460fc8072c4ac75f88be8b9c840ef438cbf12a2e7d55799f641316e3381f72265425f3e90fbeaa9919533d8f9262da27f1f933d4f9" + - "a83e07aeb968016fed89e7b16babf0b6af3800a27c9c3d330b6bf8be447d31bedcc526b1bb53ecb10c3ea098bfa7d014d93274bec70b6e82bd5c443e860835f0" + - "ae82b7be7c78cd996e0990e3cac8c1c431481c8159ae1dbc40c03f4ac543e5758f347e12715822d86c881030de83a76ba1c49e4d4836bab7b5287122ccf523d2" + - "33935d802d2bca303cf57b36a5ff17e7c611f1cf99699881ae464da2911d77580587a7228db8325f204adb14413a13fe318e995d60e35c88bb47b99ba9ee8daa" + - "3e40ce5818876a3911107a159125dcf768ba04074e5771334e0de430c439070422508577e474e9532f7dfbc489d0c87d37103920415b6c116a422ac15e0736a8" + - "1e1e317adc87005f868815950882fc7497794c5eaf76f9def434d198304ff495bd2f9f4026aea330450741fb969700b953ab265aabf1fe146d861ba2aedc53d4" + - "f929abec2dee710aed8fa605fbb9bba914eaff01fdc113836d34d855383e4a311b4ec6ef6e80dfe32bc8035d84ddc4e2c305c112b93560112c1f3dff800d6043" + - "7eab01991f924075b4dea4db01c377ee1ca374d383ff1fbb463bf7078f6cc7509a0ecf536871abe7c95bf89f29c71f72f1a2002854113cb0d6d2192c00123010" + - "8dc9477808a218f84afb81f0274718c024393d5be66edaac7406e520b0c8e2c02ab98ee7b290db261f2122ea68bd79f2cc6dc64936af5064cce2b4d1b7078703" + - "951b6b81b9b60b99da4c2d12bbb50351a5b7713541db0958740910ff69e748c71bc7470a3c05489febefd384e06d267371935f652736bbcaacb20c34bd50144c" + - "71923b5a521ac4b1ba694d024ba51b4bef3ffcff74d5dc63810b2c0f529073e13ec3232d8647ad124b21ff73402d371c0db39d46cf4d2d4cf7ad43fd8dd365f6" + - "9b6b7bcdf664df0e62ba58f3ca0c62ad6fdcc9b091fb4926cb47b5ff8de7d3b12bd8709a46e5c3d5f0d22934c7a0574ee70b87af97d0fa46f7d9673915fed1d5" + - "a6c57197524ec9978d1bdf65633721ea2ccc25626dcb5e7f5e090b00e413c10a6d20b45fb8e98c22928de6dda184e856c86792c7cd09d38e4333a76882d363f1" + - "7f4d773ba104b2d04fd81027da087258fb175bfa8005c035a4719bac5b9630ae57889fb3b52a0fd47ec4060137b0f95fa5d5684172d07ca91e91eaf20dbfdea8" + - "a3e23937f33d8774f30c7e8e5d4b2d5371e5ea5e8d290970904c4c1ff33baf675ed79599653808f652ec4fd0088877f7dd7973023ccc8377d1ada2b80c07d077" + - "d7208686354f511925a3514c9e93c13525353b3d9528ab678e3e783c290ead88c2c3d6230bd4cb3bf79fce6dc3e95bfebda41e5d994e61ab083d73408ff6b627" + - "6996a263d2920170fff6869c2311441837a2fc190bee104328591b402defa38b421b972b01d020bd20b1b6a6ae884b23eb829fdf032a81d4f199a87ef125d4cc" + - "8662e24deb93700980e6ebc6882bcbaaa0283492e81f81e76bbe2ce18df4fb665436310658918ee217b5da262f1a1adbd59eb3c555cfebb12280058c75b5b33f" + - "8aa8c2d7cebf12ce46c5f49ecec5a865a9f0b65476793884f0021f8731b1bd288f55dfa1665776b2aee1007bcaa6d92a76a2ba9925bcfa68db7cc727b2a07ebc" + - "e24c0314c96ee4d6164c699e585461388dd73476a1e0519d92f51b64eb2842a7b17bb55d512d52da802df63206ee926f6a6a8c32de7b30e7cd3f23e37e0fd82a" + - "556323736ecd9de77494a2f8702463f40fb837c2a99270b9050b0cbbc2c305a32380ff5fa94bf9c101c667f36293c12ff9aaf6e0a810b75230caf915135cbe6e" + - "63ffb2a0e8632d32f72a65aa965fc556e10ddf6d5e40be919066eebda09d581a32156e1675300f52c8b355e88696fc2a67dd8e350a6e902e082af28a9809ba11" + - "ae0a5fd9c6627fb808d757147e5d59cffd9c45874478ab226e72909ccba6592a54391d072c7eb0221f1ff7be9924b9d037e4f8c31e94fdc814a8c4cc7ad4c9f6" + - "eacd5af66dd76bb6222b2fd3ea50a828fc3a91ef8b084214bfdcca56348517be18ca472166dd7f18c8e444e3641486e7dada626ced8710fc73a2b09b6e9395b0" + - "31ee2c48c9183851357d230204c911b345457de602824273193b795fc21e90a0c1cdaaba36787424b23ce73e2116947f143f9641d39a4c07c2e40e02f3bd7c68" + - "6899fd57e3eb23c6f5615c9dbc279fca0d4218bc79d928e70018533a85b4646bdc78015149b4d41d77ec7b46900e7fd5250116ce978f825569bd887bf3fd0365" + - "e1259a7514116fdcdd6da3ffdf432bbe8e59b9bca9222c5dca1eaa61caf29b8461ddced6f312838fe490f742db696fadddd19bab8de6bedaade878be07aca4ac" + - "76d69b81a6890e66dccd702720c3bd5601c6abdab95fbe4ccde6e35385b75e1977d5085ace928adfa382ea2890889017b9c4c81d9ba4629771f84cced6280db7" + - "a6cd83ff9375ffb0a75a6bebba9a209f048788ba39127c1036e4bd0aad9be40754fd75295611e455909a818a3541af32eae98df7222353a4405da0e7be9f1cf1" + - "bcb823fdea7976a810e8a3c7bf93fd947f961a344a93aa1ba99bf2df48ec82769d8c08e7b14191050d5706a9467c9122f34e27f060dd4d6e936c414c4e551b9e" + - "5d6b5b58347ed0012a8a323f41b43bf5e960b2806de59da85b998affdb490fbc965d569114223db3ca65df69a617f6808bea23017327ddaf32990070aaf5f444" + - "a9db44a57b5c92bc27bc71c5f8a2b6929edfed8e182bf5942564ef045c75448450eb1a4e4e09a1875e8a4a74f229879ccb7a2f2cd0359abd91a782c2ec1f68bb" + - "40ce0a63bcc014b198adc222fc957eec0483f5b93f0db91b7ab3b3e3c59841dae057eec97abb55fc42b2de124946e66ed2a7fe8cd047cb79051b55f82594ab45" + - "711c92364f932a5fd274fe184c85583ac7cfaf258c57e296f9c18fd181308565315e27272cbad3b21cb4490ca0e5f675365caac42f299e22d8a74ca51a9d0883" + - "bb376804e234502db66067e7a434d38c3dc075346e888e4558b1745d00458df99db02f0e4c37702fb0989387f74d002a924790a6b7351ee0f41684bef079be26" + - "ee9d70b560c006cff4b08b9578afb5019c21ab9418ae4ecaa7a1cfed2d880a06a03c2c7711b601a2cb3d9193e1577b4f1d0e614c0be1f69205fa6524fee80bf1" + - "e1f1906b50e75fea2d19b8a83071a460145e1730581e5e9538888d2e797ee3cbd3b31399ecb4d6244ee44362493802b142ea397c2e7a3c1bc86f0ea0546a38ce" + - "574e1df0c27ad8a28dca70f659ae6a1369d8b3aee7d0dd24ea370cc2bc1b1a4dc9f63911b63e60fe4ed8552bbca10e01c82d11b0ddf748d234b4aa3b31683c09" + - "86358fad680dd2178902beadc4646b3eceff572631ff9e6b64d8a622ad9f0308cc46b7d422ce792fe5573e9b9480e1ae9fedf31edaaac3b08c5a2c6c27d6b033" + - "6b92a3da7b838bb0a2916ebb6ee72bf33a7fa70630491f49c67031ce4b9dec2315088d0a5cbf7473fd121e0ef5f4e92d43114014c9f8c6e671086a446eb1f66f" + - "70f0cb0c668998ed96ee0ad2687946681fe40dc46cbd170e0cabb6f6216be61221f171fb2f4273f58c10d5c4eccafd1df62fdc8ac2c5c8f6d5eb637b71fa89e3" + - "f8347343f89667a4450c5c6e3791034d2dc3a593185b55bb95d8f8f2984ef981e4b692c1383ace4cb2c4adb80d5d582857b5d0e3ccb12845a59587b47232ad20" + - "926efa78e05a57b136e284401c516296b6b194d541ec165d11ef94f166cb52f45145d745ff3deaf643b5c45573ed0e69a22f0e0c9c5367f6d1398105516729b6" + - "3f2edf1b01ad9633edf80efbba6555d4253fd99b45a36f16ba98ea0bb0d80533aed806544a084a398a692f698c78b9bcfc9b4d3328dd869dbf7085893b8dafe1" + - "59e0517c2f6a3ddfd4a8c670072b30c96b90f81fcc08523e4fd75919752bfa52a1db7c374debbd83ca8e311b98b0d8275bedad215847fa8984cb50e108f69550" + - "f6517d719dbb5dade1d3c283357e14b6d9e85d61e33813546517e1262a7cbac814d79cf6b7e21b0fbbee9b6314f02b2d4e6995d2231670884c78cfd86a2acbcf" + - "0a178ba64de2f13f022e22b9b968ceefaff374aff02b703811f3dc541a69a21d6e1c5d1aca48889b125ff1274e65413f61e42bb0194b60b65a3454c696033cc8" + - "e3cc3613a52850296a0154bde0e2a81b7a6489bfce505dbe1bc44e0e1052f678297bb19cbdf7970bfa5268af8a54eee004063f9894118ddce7fae8bbba53a428" + - "678cec8a2bf6cca2b1a5f4a2e95562437e4eae41167f39d2a150f7c46c1eb6da35587f7234d870b16ed91c7db548ddc99967381b4bb4f3a2b0a5ebcbc7ab1b06" + - "7d5418768eaf7d526ca116e239ceb3ab393c45f3b32b713c11fa8e5ae8d7611e6008fa08d1305d5655315a72c85a04dc853da3e8ea9d46674194e15226f126c1" + - "a233c26dd7d3cc04ae572320d0c351911b6fcdbc0b8450523e96022f4b964d4e479b6cb1c40a6d27699b57ed2952ef7fb3172c69ba7beb8c8633a01070ac4344" + - "d4c401acf8ca7fcafeaa59e1d4c2ff251bb67dbe10a862103df1b416fd2097fe412b3da9d4095b48ea094fc3bbf2ca41e4452af3a179580e3bc11a7d97ba050c" + - "ae1d6b8075da267b3ae2231a1fcfce0c976402f34963c007d4f85d9ca95646990d1bb09691ceea3b34211dc58409e052d0acf8c2296a7e8fb52d7c673506d89b" + - "847c369daec7909da8657e8976f59f2ef4c8a049b46fdf30d6d223ea4175e4d60e469bcea0eb3bdcaa4d6024f2b43cf6de9bb40efa9172381291079dd82ac5b2" + - "39f2051a7f1aabcb8d50333e8c160de19ce1c76ced8056a0724ac630dd45ec4e315437391158a633c179a3d1f364b475454fd29c1e539077b9d5f7227786a5d9" + - "d8ec78e5615c25e517e9fcaf07611b85dff2c131a1b11a901a431a601854e5cb627cf7b8b0c5e66ad6cf60b7ffd6c6441f9ecd58f414013279e9de533d8d797b" + - "936cfdbfcc78342b7ab586457541df5f3b7d1873612df200896e2929f44c6fe10d24f7e6dbe52b6c42c0a40c947c1cbda2a41437079eebcdc29716d80957c159" + - "627e7366cc16df92cdedfa9f52edc848335f1c7152652fe24661a469fd503393229063c7ab20d8d895139a2f580dceac9f6dd4c4ac652b1d60c2b8a1b0b2923a" + - "86c31742807549e6d523b3c88d31e8534b9e05a6c63f6c8fb8a1eb4dad733d92e7071e410f0087ca3074f4a2df511ae89cefe9ed09a8df603d61f23754e43cc2" + - "e42bcdcbe58b0587aba9a62f32c7507116fdc8a9db3d65d6c0097c8f473eb7f3bcd11ab81d5b636b0812b7982201a63d0b8d40f2c38f65ffd953668eaa5751b3" + - "dab7f038aa7adbcd1f1102267c9d55d43649f9b4f65f1851546c5a9ef2c7ef56e84b16f12641e9d5ddaa78ec778b5f113b2e06bad5821e1a5203b006a774e36f" + - "56c9336d92c8cd8bddcf014b6d58c394e2a93554af6361fc1bbd13c359fed98bb5adfa4dd1266e2744e126e1bc029ab28fd68b648a2ab26ac23252171b298641" + - "2621f2a8697a00ab3fdc1b3b04921390ee16d213601ab249a51830661051d34eb777f690fc2d8dfb8e0898567e388830bac8b0bc896f43003feadf34256a927e" + - "b4d9293e32ca135351a19d1246cda30551c87de1e148ff5ea576b67e19e1a0389b88a5548b3b1a8cbee19eecc7de5c2333264c711d50d688a1c57eebc28dd6f3" + - "3dc0e4cb857973c3d0f28683a6f3c09db9f54b8fabeca9e4f9b86d794ca55d6611858f0d48736adc10dd6763ed7199bad81369ab1f3de30f521d43382bcccb7b" + - "be0178f716d5c3cb87488cebd7d9e2bbe671dfcf2512f1b815075777ea92a867f35e09ff0110e61db24423d0598eb6fd078dde0dc2b5d7f5e0bb6fee207da109" + - "2e656b5c982866d5fe01e6db79809646559a6f2b9088e977789aac74435dc625b54296b25788bfbbda9bbb25247d428f5141b03172fa11f12339b91ca96c92e7" + - "ea5a128c8046087dc7a7eba63e3bdb200565d8a103e7b3c292b088eb06aa27b43688c8516bbffcf123499574f00908ff43d66b79106cebaf16725f1dee600a29" + - "7b3a3da878940867f9549e65c73ea798ca923b012fb8a7ef3e2ded1d2c4e85635219f627dc4feb90f884ae6436e7b44f9159f9889d8e194828e079cd2ee60a7a" + - "6fbb6b8fc1f7355d7322709fabddd76e4283ddda3018b7882ad79b32bac133da415453eecd5bb1f0deb4f3b987a71a2f2e60194cde63a42b91b39bfe51b4aa8c" + - "20952b601df11d170c65a7fe935915890849a367936e97bd242edf305eaf2f4f4fb9e5ee1464c51a899ba5cc69cf56731502c1b75d0d565b1dce15440b0de0f5" + - "58bd4f810bf058af99c158a2be0dd02a01bb5317f55675f4d42c6766fc61271954b6988c33a84518bcedbac8de305946d060d19c4691c026953ebd680a4c9012" + - "0e8bd54675d6c33cc86e65f5cd3c34cb1e6fd47784a64f39e95a1945b5c21df2b3288f963863b33366908b05c2bd499dd25c1b8e97329d7e435899afeaed174d" + - "2a2471b6e8d6ad7a0b1b6a8b19fbd976362283e5abffcbd2cd310245092749b23e0d114e727622953487f373c833281a74a1b97742ca99e49cac14d9102e3680" + - "404509889ace009c47d075ba9891e7f67b89aca3e213150f3c715cbab1869135601612d7dffda3cc104b6508f56eb8b7e7f379b21e1ce290ce5fb96f53e3a7eb" + - "c7f7bddcbdfc266f23b775602d8d12527d30446cb4144df7fe3c2756e232a8ffca625d7b6ea2c8c0a92e6425ba67ab75160623c39f01fd96856b582e257a6930" + - "224c6da90a6eac4249214c3b85aef52835d904a8a5e224d59eae0c80a33b3141ffb31a7d8e62833fa4c850fa6be135558fff5434777df45feed00316c475759f" + - "ac6e014e9d3cf23e7322281ed75623ed69a81d6f05ee7de193f6b44ede4a94ced27aef5ab9056144593a836da80f5297875e7bd84d8ca6df95de8650b00b3528" + - "123132f26aabf755d00450648e44f3beafa4dc746775958c6dd88bee825c29112a3af582bb2ebe628d70364fe9ad01b8a9961d5b71018690440151486114af1a" + - "d85679bcd3eca510c6d6887e70e0d04b04fc2db5ab1eb21fff925b66f08f4fcbf31be3d743154056ba137727b63576e72f1756029c86bbcf9452fc6cfd89f3b5" + - "9f243d84c410253ba7c9284191a0ed87b2513901a93606f1aeb736c90dfe40c0a343d45e9a992ea894b22ee5d49e0f7d55d9bddaa6c74bde8ca5839db67b77a9" + - "ef740f9a47241f05e5dc1b9c95c459cc9db560b1db090daa3f4c6de46f695a158baaf357a1fc63ebc0d9db8144137ec4bd69c5af89cdf9cfa66e06bff6339d62" + - "2c372fbe5a855d14fa7ff3726512f966e4da0556b29ca6d7517803f897d0e1911f9b46a291002a8320091aa7016cb7ac993e35c8b0f5aed3c94ff0b5dadd8b77" + - "056d06d1bed59aaf7bca8516c3bba6b33e12df2e5ca4aa40664b3bf48c4dc2c57cfd74c765fe9f794f55b5df6ac6dd2b3592bbc71354c8dd9ae41b0a05e1c7c0" + - "d3bd1a0ac6b671c48c01d4a0fec7a01ad11040f213461759f9e029c835ca1d22f9a661b69d72bc46e34b1be7ed85a21830fb87baa74d7ab145ac1647f5f2df68" + - "671100d4d9e41082d3c81f3b5a6e603bb33fd56c1dbcbdce5e213c651da45d9d1dd7532d9a955202338387af6315137dc458fed62920a0e721aa7ff1660981c0" + - "e4c3de0a4863f6f660a7c1b9745ea26036a25cfa37e1337ded405ebb0401d7041a7938800a97a032fcababcc06391a77a580b1a61de014db9d7e280ffa6b2381" + - "ab6969ae5cfcca00a47ac2fa05be02aae7beb806d2afcc11dc0642d2a12ecde2d2926efd9fe790e1bee19f9114d22ca42f438ef656a1311e4931ab7fac93ed17" + - "3f68ea0abed18cc2c8905bb2d599780690eabe4996e38872a3190fee361df9fecd5906f664106de4835f8fbb657366327871a2d38cbb671df04e0d14fe97e260" + - "c42eb07bd1d70514913c7a64a51e405cc92e06845e5a78981fef9822fc79e9937ce0513138f6bbf247f5c457da708cf84e30d083b4ba48d2d43d70e7c31e9482" + - "4472617910a3de4369217b4daf892c2c3250d1de0457e88b3bcb5c4568f9b26aa675c551a9a730fe9ea8145ce7f8e23ec825be9be3b9edd588c391295fe31ac5" + - "bfc97d2e438ca9bf6551728b3be6d6c6ac064baca763e0eaa24f754f4bbc84a4377de45fb6a8f37150865df18749df1af4ea911b62f616dd4dd4b25b27c7b6fd" + - "99d8c00ce8a53fde3ced091891e8daf43cade10086be046ee5607003de24101db49b1a4fb0ac270d05bab12583e263e903e94dab8bba7c785e40499ab01ff92b" + - "b82c2e5342dce84881adedf77cab593f541e4c963f4f9ffc80a16bd4eb7f20ef4bf3f57abc7cbd86332d8be80f0794fc82767d13c71d8ee20468ee35c13308b0" + - "dc29ebe8c6a81e02ee9a21807ff57e4d932edcaf59ae9e76f7cdad46b32f94a74982f0887d7083c90ff54058e873b10cec67fba1b717deba5356e170dec1a40d" + - "36c57674ad8d43c5c98022b553fe060251b994271585f702de3e71fb1c8e36293dd44a4b99a1baf33f6205e9fbc9acdfe8cfdf007224f93a7104e7803454fdc0" + - "9fc5a20be59f600ee734847257a5ad62c599a7fa836d1174a6291e61c1be4b310bd4d7b7cb9be976dbdfdd2b99340a9863c8c0e5009165d7097317e6c3a29cfc" + - "dc84b19bc68f38694998f626567b80ce6699124b12bae4bb9e661c2484f5109517318341287e142a849d61d0d7b11d4996547e7325f28842dcaed26367f7a888" + - "e58c24c857da2f48a9fb91c78cf351a23e82ae443223580a9fe15a6a778f6c13be66888219e3e15971170712b6c356520cc15e4e75167993b66e6f125799cd40" + - "86c72588a85f68361f1c2f09e87f9a4de95ef9a3b92c3313664a706cb72916b96a9cb50771f6917ddcf696ce8d7f2525745fb6edc30bf3fdaad66ca5b013300a" + - "7ec7cd274327b1b9cd931c068d8fa9fd6336d59f6ac79b84a24b34c47e408b3bcb8ead49428c123922e54bfcaec7e39c4d6ef79e5645a35f715d151e679ef5c6" + - "6f86cd013fdaab978ee4e52eea5e2753e693271344a1f215e1c690de06f29c856c469ccb484d445bacb16694f4def1537cdb32260705e8a50fd65e98a24967a2" + - "456af6cf90643638999389a35de6e192068fd2e2ec29aced58611560c792ea5c7fa37583ebd5452a8d94cbf1898937dd8aa6656047e6e03f84dfd0bded514a6c" + - "b47ca71c2cf1e76f606c04374663712fd96925eecf0ac1c38392390c8cb095f39e1814252ded78b55ebeb9915dc5e2ec14fe99e3a075bd389ac601681f154286" + - "885289e568a8646d94abc806b4637492e3a407cde582d42764eef0d56ab14b00e9aa1f64d8fdd533d4314145c8255c44d0c746af6da844d285eb044d57e8cafb" + - "ab6c3b962e0177f11a839f4a5c0d2c2e8d5f76375ac115e0a89f460ea1be238f974a68e0693d15790117106c1a65ab5f7aa08e738aa888d5b56be39d2078837d" + - "fb2357d86f5be85a9af41aed611b231495564493e46acc90c6a3e67d5b055320290aef508aa6a1896f19cb5633edc0fec023216726e50960a44d81e0614ce748" + - "6ccfdaf620eaac0517e8cdeb1095d55f3a60d61dd27d967eb26128b84c9ea8418779e074cee8961c5dac811ce5ee8134d3910a47de7a1344293f5c64ae8f1b38" + - "9d6c457dc74e7005c339394f5f24630f5e40cf270640d1e4c27cb6a74fb440f3203026acfcd31f39cd4844ede7e785290878fac8770f930e96c3edf61748dc6f" + - "b7476832cf77ddfbe8eb8e12fd002038630301439ef8a7659bb10593a92cb84018e1ec78856f403e1eb9d6343aa0bbd77a63d776f1d12838f27f3cf6296ab0b3" + - "b4436f0ec545a5a1e92a5351fb273b3ed56a40e5a5d25e0057f4077bfeba2e2d8cb17a553b157609b20bfb5cd2699af9936f50d823bb59a950a24b8fd15ed705" + - "b1628663f0eb5b5c2b18f000ab039bc425ebafb2010e1a2264c38fa2bbd0f77e38eac8acd670565490fd60cac7fd28d988c8dc0658505dd98425f22c94647d44" + - "5d0236b97ea58b3c71feee90be0055ce1fabda5ebfced9d9bf5efaeac8408c4b6bcdb39851cfe038d88ada5211de2f0f69e9e3c62453106c366cf0c40971c0e8" + - "e8f2a790aa66999a0cb4cdb57a8c2d812e9e4a66df2f001a57e291864339257ec26c9bc2dc6cb2eb5c3301c167e1ed0387f9ce9f76c6759ebe5c68e8be378c42" + - "e0350b344acbc8b40c95cee9e82bb43cef5e91a32a6be8a727d5fbe089321ede3abee4da6b9f41775d7e9abc36f6a5d26ab88ba32978b5ea0ad63f0ce8a772be" + - "5aa51143bcd00d78bdcbd69beb652139ad658dc7ad242b2057eacee092aab4940d6ff993a8c7d8fbe93c08c93c45d5f3a01058f3c75c94be9da1a19a97754734" + - "b713e1ad6b7cd472619ec1abd4cf42f50b0648661c2b8dbe8976037c094c7176090ba94618e1918db44f5d2c367a0c7f911132d9a8b2398b9417542c7ad99b53" + - "a7ca48253bab8382a1a24d35b9b9818bda513f4b52fc576a71fa63e72aa8042ee1fc806c6fd3fc16e07ed2caf9f82bd3bb6b393b2708c051c24c2e05aaf72531" + - "d865888db06f719314d6094b2c4f0718c151c88958d2d6c8a6f49464f81cc46709dde026f4e05325ea4ca2dddf9a79bf98bff3aa5eb412434f0b7457b4ed47ab" + - "85a212e0c7720c78c961d56141bff0f964622d4d3984c1017de6f5846c72fae0c771a819ba6c111bd739fcf16f4b85f8101e7c3f0daefe753ec130a6f34c7697" + - "4dc531f83715ecae28bf2e55111778ae42aef17fa95340584cfae3d4599af9dbd10211baf3aafa8ac8a07edf8243daffd6a6486b1e3be4b60711194261e2b646" + - "e2667554cc0bb2fc07054b653231cede43154c9002890ca20b0ac81c4788847c6ecf7c174e528f36f8cfc53f3366fa9ce07b1843939cf6d318ed11f7ba6eb791" + - "ce25e75cbe37d2ee3d45bea487d969de041011959c0fed4e6c86802a7485fad70059ece14a29b03d4df41677acf71419ee63f1101060ca5e4ca0ab2edd71fe77" + - "46c6bd9f36bdbbf0a9956eaaf974f7bef982cd34881abd686fe77b536c85d042d77dadd00c5cb0130737e5318a025e6ae6af96ca28cbd41094d86a85765ff891" + - "af825793910c406470cc61be5d9282911d2faf82abfb309598fce0101ca64abe3920701a958c20ac35927733466a23de809afa2bdf331f68c3ab0cfa08b0c549" + - "a20e9b50dbe85d22d215d0e5fef854ba271a4c0f95e6abca19018bdd4a042721887418136b4a60cf291bf06ec47a5a4d2f9b29f988733c6bf6f65da5a95f8939" + - "fe0f2bab0bdce98569a81f861014e532f6a995542db02b6bdf3169191d300fb0429c1cae1d2dd4d29e0b61751576e04b558d38d3afcce8326c2871e969c1492a" + - "8391c0becec29edcf7f038a8093471763db9f13b97114acf7a979f5ba3bf6f990317890ee0705850fb97bfacf306a0ad621b2c3b633af01fc5aa059c0e22ed17" + - "23584dde6cf140bd1d0087ca9090ca9f07d3b93c60938af8df976555455bafbd8cc986ba32fc3f15b5962dbb2d37b6ae55a7de0c0c6f2366be0278e26bf9a725" + - "f61f2bcb545d66f79261783f7f03395f2a5d27e56af62a01ffcf778c3c686e244bd9b7e5029d1d40dd2250705c6825bf78e83730212640cb5ba54191b61fce33" + - "ce6df7721b15662162b631d99e6431efd24ec35639c2b97f10374fa5b9e2ca4231f523195206fb9695ec7721c98d74f29533cf714866adae8edbe8ed2d0969c4" + - "9ed36200c4b8b75131e6d1efa913106bb0759aa8255bd6a9dc2b00407358f4523486575b111676730094f46d0a7b95427df74f053c6611b4c465efa5310f760c" + - "5ff081e841e5f90c2de35855d45a7f35ce73d7c7f9f61fbfa953398e042c3946aaa4b7a2094d95410b8a5ef76c8b57d49f77311192b3f4578f37bda1a426de7c" + - "7cc54b5400bd16bb30cd8d1b7b42ff31c5e3759e3c9a7668174c02bc5a08f1bcc7e3ba145fa5f5c41e48877b41b0ef8fffd0f75c6547047c2e7b7c7e1aef2cda" + - "c4a778adbf71257618b4eb3c6dbd8211f829c1d6373415b969cc48f33d586d2678e7c1b441364a9fe2bb426a33b2a132741fac547766d196df3505fdb17977de" + - "7853cdcd8d9932eb9452620aa4921b4416f65055d77573b132a40795bf142815b655e670bf2c4464adb5d826a1744c8049d7a6cfbc8a4634e66eb32f0cb6fa17" + - "ffa8925131c3a253101733406a2a3a0dc61ec3ca1448623b6295791d4e2d65d303f78038e15d0ef75d823759bcb4b277b51410c37d5efbbb2e3a9e0cd78a8475" + - "05d44bb1fed7f72b1bf1a96ad0148e816d34c66b1b5faf172b8141ba007bf2e5dbbfee4b09ef66656ea3cde54f086040d14116aa7f3584ab6773f6091a2fbcee" + - "f59d6ea115f88ef9fcb358c87c35caf7c1a6022e141a3c688beef17da5a619e733d854218b30d5edc39b933b19dedd6750acabc52234934b08f930b608a18008" + - "838cb0fb73d4c78af0c468d9fa4fc5852135ae91ae00a99a6c603371d09b031ee37f87586cdc83897d8fd8ee2e07b9d0478a812d3f7eddca08860386e3ad9521" + - "98d5fc04fd0aba4b3da6ab8bdd9eca8e0399a2012d6158ed75ced5f432a223449b4e3db3fd4b19c494a69e9f2670833f8a88f7b2873319e9495f03fc69b6d098" + - "6006e3ffd8cdb9c1b98f72345848deea1b98ff6ef766f4398e642e5f2b217c1a87a608c1dc701bbb79d75a4433ca1d600061836888a220ee262124d145d371f6" + - "576f04cf71701133787a97aaa615ca98138c2be1046604d885e2f274b0de8743af50ad5dfc4c3a09164448e102be577eecf77ffaec1724f91f00f908ff6af41e" + - "57056dfa8f5dbcade85a66c10e524bae55922b4084407fb36ca8d6b7322f76a8139be9455a34440c719d0db8a36385efa48841170c8d35046407b586f5bbd169" + - "7cbaf6819b663fb17d0f0ae89691a099a8ccf47fa61fb6dbb22b3298e5cf2465e4b93c49da70fa76924fdf29389194cc5c61cb4b3084d0851bc3018270d1a24c" + - "b4b04e8af927d9fec9ea1c9ce18d4dbe61f7aac0ffd4e7c2e9729b49ed9874b883ec644864c6d9ad0422c4d89f87df1dfb2c96314b6a3e19afd21783f365445f" + - "bef10562a26b48df42dc344ddd63fcc03220dbde98f1109cade221027c40f0f996f4beb29513c3979ba374c4c6a2c2dc6276ca6be66eecf1dcb245d6efe78aea" + - "e49ece37f87894bef3c0cb1b993d974685564e2476c12c8d8f63a1aaf142fe34a6840be340b64f96d441f4537dff434ddce630101ed9f78e807881f6b7590697" + - "bc97e60accd7a135d8915781f4fc22e437145154dad0a39e5e306c117b11deb10462ba74d58e81de7674ef0bcb20b38511991447f63ad906b11abd4ba88df3c9" + - "e6931f87fece49f48543fed0439c88ad78f82aabb32dea03d030bdd76efef6b737daac2de2db1cce10e2ec74565b0a609606cbb6aa259ba88715229b8176c874" + - "db3fc4f6db9f167e7b2d55b33a261f9eecb69a0d36ecf9ec4f8f9cee5b74bcdc5d77b02ada89f56259edeec0d9ea866ccf454b9abd29d5d21041179912a5c302" + - "1862d850c3ff483e09479957df5bde03a29504b4a43e1fd40af2b8a2653a37cae89c8d917aecdec3959fecd32b7fd313a61e134abc15ad008aa993aba9629a5f" + - "0af0ec713f742bee096e171729e70530b60f910ef83746a61580f0cc6d67723792c0e0e94775d5b1edf37864a50678d197bb34a97e84d7f764c0bfe05f4b2d0c" + - "dc431d1f4410500dbe2758eb05bb6b19b154707c255a97cedc6aec1841f1817f6bcba0b9a9c1d3aebf747bec4423c71309fb8b4ada90dd9f7adbcffebbc905de" + - "74ce531403df33457c4d0b970fea5df4f85732e3c33c5b8242b041141a8c51a62f0bb14dbe07b14d3f5ce646d76e87b258e9b62128f9c0c0a8014f2c5b3d3dbd" + - "a3a77be6222419cd3fbbd3b842c46c099f142bcd36442961e8205ec5d7fd159befdbff12693953307026f1e06fd57b6447dd3cb52df466f0352cc46f27d1fc56" + - "56e06f68ca2847d291421bc9e0af6bbcfb7b3ce07600827809506ba3f96f40ca22766f8cba32d4461488f6596082a52c11e9ac908922075a7b443c41e55b719d" + - "9cac9fb587cf02432e1accf3cb7a16de0d5bc3a1c0aeff5a1795680b4551316e3d7b5a9bc63a09c6f75b0f00eb69fb6ef5130c1ec40c7a7d5d6ccce364b74f63" + - "a836a4a711027e592d6a70e10e573cc6d730a0def4a7a2d4dfcf3b0aba37fa2060ae6935710191c023a0b8e123a67ee811ed43b5127a1c4cf82d52ad6c40fd66" + - "1160f77dc320bbed349c8b6d08b2a7a6234a8dc88e4744b51d2d7c56e02f1c3bea9e6c2c3d5522ca02ec7e0b8160555eaf28797ed30b5c931a73562791f5f0a1" + - "b7ce83824bae17de449cff41312bd441f34df62904f4a0265d6fb9b8a352895ac6f0025d6b2074570970b4e679c559d03ef40794708eae36567008d9c33f7fc3" + - "5f8df7e901c27f408cc7fcc52631f1178695ea660d07df541e5a32721d145a32e8d32f06301b5073149e8798371fdb1a2daa5e1d02c24da07682a2cbacf5af55" + - "64810e479e5966dc6bfe14b4472c42cb154e19f7b8659d42de5ac926224cc6b0d8f3fa797058fd6e21ea85146838c4612760d84e24341825b6931a6417327394" + - "0154125254d4e11ac80e475a178605e851e1be39695cdc0781da241f232cedb32a04b1cc7352882fb635162ec3f5fc5004cfa7d03780753c14173ae7b12a71cd" + - "b40d4835023a00a4803bdfb6916956ade9f687af567e6f29981120d306084ad061ca1585f0e9497fdb27f9d54cbac8fecff176145114ebfc17e3f346b246ab91" + - "094dac0e684a708b45dcea16378fc29683dd033310068339b13d995dce77a50f9ee9cb4cf564566b05ce352a21159ad21e720e05ce6069a5ef4e9fe8ffd28516" + - "8356a0b80c4d1da547776f486a117f6f7ff6557edae7d68834cd71973517cfe4af045ad0fecdead68edc8017000958b69410247a51bd9bd3152dd57389f25223" + - "d5e88c0d343ab3aeb89b763eeb7ee48b3966fee147a4614e436c9a1a398487c80a001700666251b3dd6a2e5dc96814d21e6498e75811ba4c51160cbecb7d5510" + - "62697171a25a6abbc41fd806c3dfc83daaa10d7ce47f5a29ef0d85dda5a61429c637520e6a4048624cbb25f53977254cf803848ad81f25eda07690fe7a0466e4" + - "d18a2fd145dda1c94a994bc4ba5ce1aa1b50c38151febee757afceeaa91c7b35e57b90ff7b62efa929dcb962d32dde5a0bc3159524728621a3d7487eb7c3edd8" + - "6df3f8a18e590039bfc84a22b23b11468c90dcdc8506187233d8a6b3dc9785ddf6f341709fefccde91a7a0925a8446b1896aabd6a6826ef88b756a9711cb3b78" + - "1ab1f4df4d0515070e41fd5b0c5483270307e60eaaaf0b3a48f6bb96eef6141075445285675bb12f2ce38b42c91c1e063400d7bba9b322a0783e7d2f5d3f8874" + - "52ceb65bdedaa032336d969d2e0e3007d2ac07bcf054b9d0330f2e26c486c054bfa709fdabe283ed9a4ae67cee24f40f2a5c4e70160e6ceead208ca400959922" + - "70bc35be104c9ad94cdbe288b1c599db1758331340c9e927bc9d688e4186d5badd463bd3ba116bdc22a39c604778cd95503ce4ce642041e89bcdeb86fb19ab25" + - "e1f94ed2a2f857b228ed4a582ad411d7273a0d5189bf7a2b87a135753e03383033b989ea532041ab9ed397ecb3ce61e01923b3729068f6828ffd12e2ab1d28db" + - "6ed7423d458decb00476657a0580b4af3aa5615bf07df55beaa2bec71447aeb39791477dd09349bf573e29e9c4fd454b4bbf1e19591bf38dc47c83bf39babdc8" + - "737d69ce4b586cd10ed406426b88e686c11072f04c680e8b6275166e2dbe91f701642b1b4ed92d23d6fe14f39ff7f5a09401f3a398eb4bb742f6cf10aa35e767" + - "7e6e92aec791e94f8122e8c9cc9d0bc979e3eac6562ab614ff20330b00d9cdbd08e9deaeaf5cd67b49164f550c5f5c2d7523fe5ad71a2bd03fe2a97329980cb3" + - "049ecf6d677d815e56f7cc27407cf73528549ea98265ef90277c14763d5acb3572f5a482432cd8288972af580fdd3418889bfa6a373c4813c4c45e933ea4ec72" + - "cdd068089c2a30897dd57031445834de9f23faf506ad930d843b1cabad2c0aa8965d1b5e57032c969f9f55fe2a3049f4e63d5b5c6f5f760da5ba44e3bb9307e9" + - "ea39973d05a74a49e15bb71eaecf62373153ca316fdd40b1c64ce2896c95a7b5df970c2bec85edbd5ed84fa7949c08d5ec4d987052fffe357d444e2408a22295" + - "6ac1fb272f5023740b381e00dae9f09751a33bc6ad673c4221ce3f932164deb99f1da3eb3581caf475e385bcc56d47a7a1615a9543403750f0121d5482c4ea5c" + - "94fa3428178f6a4deea08d754ba2abb3d1aa48c3e06f06ffcdf4571579d398cd991e60599e9633fae6a0c07e10e538aebb7d33aa127c830f14b083728f6ad7eb" + - "c9a60a0ba222f47780eaa82a21393a49defee97aa8c3aa2fa53a2af86059a7587074128c2fee7060f398ae70b156d53aba0bf1af4bff10a966ce7e6382cec4b6" + - "054a8f6b9ef0e8729ee182f86c862f9b7d5ea36ef7e15bed10ed41b25738c380e58cf42795e3202749074fe5cb6e8fcb49a116f54d84734a834609a3443b8b42" + - "97c05ced428f5756ba59bfc1535bc7e16d370d81b72b1f3f78ba75c820b22e485dc042e4f38e93cc2918a491750c92998f03aee571cbe9abce4d00fdf9801f9e" + - "8e0fa276822e1e5349945f1d337e656b431c48c1a2e9d4142ea14e9427881bf201ad8cd8effaa6fe6a7e07c8112299db1b327a0cc34c9fbd35596f4ee25caeee" + - "221afad93ce7df64aa6ac766cf4fe1660446dcbafdfb86b4e0fea78c29c3e84ce42da4a503178bd250a6dbc4fc65e397062229001da05d5be118dea7ca5ce67f" + - "b4ee07a8b01e408aebef2c913434921df686a242b7d015a559f9efdc54ad62d7f31ceb72463041843d7fb60f948fed03ff143fe24ab81bd4ef6bdabb856ef1b7" + - "174cc987436322271bf48423114e05758a08cdbf300931fc7e950830b7ee920f7033541f1db9b0d2b91cad80d06c049b05fd0a76d6dc211bef2a08d53b1d16d4" + - "2232fb263941daac4e004542517807341bde98e9990a97739ef86d66c7a51324f1f6911cce4c3db37ebacb6e58eb02d8f7d6ea31338b56a99649c4e730a01bca" + - "deb6fc87cabe00addf1bf76b83927de26bc2bb3f0cd5945d863b0c31cfe8fd4b60462000a911755cbecdb6a98139041d52df498aa99aa3876836ce5b5bb426e7" + - "c22b5977902e0b3425fdbdb8f44e8758b207b469c3e5363f552c89fbf778e95e8b7ff6566ab591fb68a8bde38d8169c708a321b669c08d9ecf1a06c5321bb1cc" + - "9c8a585b6381645edfbd1ea4a2ad7e7eb8be6c431958add393c0a257aeb283644c6fe97580aef613f1b9d83e5b009f7a4d059025c11e0a0a67801be511dc097e" + - "4e7c065684effcafab83e0e716e2d0862e83b295f82089ed3ba4f6897c8d8eb2b358231f95eef840e1fe22e9065de2b3dfb3633e2968135756cd9c109e8acbb3" + - "172bbb6680c2e4fd69e179916a7849315c9f4dc86991d75cc6358617454694b3fcf2471ec7fca6ea2d99f704b9aa37a25a3b3183c5e32e3711346ba2336d6001" + - "489afb9cbd8822dbe4f0323ebf7cfa9367b6548213d473c0f07b1bb6d16e1c66fd2bfa1ca623e03149fc81eb6f71c12e7b4b76ca588548bb4872469687f334f9" + - "7e114a16a0a58ec70ed74ef69dd96666a85aa52d1ca812235796d90b9af4296247f4c1ab632effeaaef6acbb637f1aa9379195b3b668ca541bc6eb595bbc430b" + - "28adc5d1a26fd4cc2239516ac9ca9c0c028110926a2f88881a5886554c31539f4c8260e16364f4ac27710d2becdadf573f4a2b7b55d76ef059432c91c6f5beb1" + - "56686a620bdb4aea50df564cc0c5ccd8a93c454e06b8969a0f59d63ae5a29105149c08a5de65e87b0dc445dc5d86db8788ba77b83e22125c69621140d7f17906" + - "4ec0157a877cc51ce3c0d565bdf6c884f69b0ca631d6863770f6db30eff847e33c8b30d5714668a38a09f454ee44ff2b7f97207f10efcba74325378f6f272ef9" + - "9f09c501c12bd0a4155f559a604204b36751ce8d4c0af35a8b445a9290c305d5d3ea21f944e31df9a711ee90bd16a37850e2a87c3bd3fdecdc6e2f261a5d6d0d" + - "580990fcab9228cbb39f8c79608d821ce27c10b0ee0b5a96474759f67970cbe03cec9fe594765bc935abccf867b9717a4087465c8604eae89497c8ffae7e46f7" + - "ade2848916b54aa796809cb98a4364b7b44c17944dbc408909a92d4cbb24a514b72fe8de7d1cbba0a101973fa9b29d97dcf1f4ed8a05d5e0cb38849dc6e2d041" + - "16892ce649e0a553a727bfbb1d5794a059d6a411e43876e561d83bd22c054680cc8fa928f5f4be2d849f02ddf9c6d11ba35810b81553e1938ab013663f6ef35b" + - "08f06260932d7acf99f57967eec57a61f03d880c3225e53102a672f5842da21aaaee02444d372ab8ed7096235a4926e3288912d9c736c2c4dc49918abdfdd6d6" + - "d0df5be0133abd61b02a6f008909c5350f9077598ab2e612603431bddd3826e314feb280585b37eb89e597f7f0bdb738a9a93d9af224659d50c8f7479b240487" + - "76c2a960eb18923fa2d3b31b3d20ef538759cf22f5b415d19bdae689f2bab651d79ff99f77a721bc1b2233da12c12be0c9881ad82fc97a6343b3af8207dda8b6" + - "5c600644d741b8a16750964e341e060260c4de26f991f3a1f6a606d1153565f1c9cfee58eef327edc0e9cfaa206ce930b191f521be2344949bc75d583a413a96" + - "ee4edac424cbf9bdad2883c96a1306b96ee059d8044e3b7af4e7138697f142774ed6409a86a3c6c456600d4e405e6117ec759f4b22d7e5a185b0f9c67ad987bc" + - "58d2e8c929d4a487e5b77201d7c1416878e8d63258b2f58727cb631494cf1d68b99c28493b99b0409ccc1f9c218a2b95c45ff36563f0045ae5c3098f641ea6a9" + - "b48a3e1489831b2d176a1e0cb2afe6bd8cc5e797de01391e47e798c1aa945d33d5e7dd607aa73c9efe93f0646adcd7e211303ac7deea4d02c80370e8e867e2ad" + - "9942bfd5a66143560a1f59e5be1f3aeecd7eab689a4a481aec78045ae0604f69d9eea550152f6e2bc692529357b509d60e5a497bd94e63dc698cdfa2a3a55976" + - "0b2d072a2fe9c1fd41f31518aae0edaab532591476a9c5a61cd76937575cef71ff5dd66e158e7820b4b6bff4067cc26ee9aa66f41b80f078645b920512b5efd8" + - "88b3644601a72e3f665b9c8f0ee246593667379b8fa043718f2d75c21d2a11640c328971c32d5743c11ada6c95cfabf1c6b66e0b09342afc899e1f272ec48a7f" + - "ba5a51943763bf969cbac879363e14dad1952517d8f4b463511adccf25e655bced7cd9666d01dd4f2a0a21729ac4f44970c9c478a995d1c3b358a244110f1db9" + - "fe6335685701e0c2660ae69d33a93a75e44f5374b979a5af140200db43ff612be2728548192ebfd0a3860a9e135b910fe3fb249926d334167622bf4123bdf0d5" + - "38e9ff2a3bb67a44f8407328e3c94b47d92e0401aa1db85459967699804df245a7808f972683afded9cad8fbce15c1be38fd10c62c7abc302eb0537d5cc573ec" + - "245513a87c1a8d386f7ef0c4a91ec3c602b14a14ae395da13284df3391b929c7379e181c5d3d4597e3c955ef6e3dc2fc55890df04785bdd4e3fa35ac775f44ef" + - "9d7813cc036d6bcc316e869eeddf7b30e4b837e9285eb20397b4d7e0d12080c502c750268bcd6ffc323cb094afbe8304ae840d37be833878697f2cf931faf06d" + - "28dc6c7e1b5df69327127b47eddd0237f1bb5942ee5903d8cbfe1b11484199e90fe7c8e7f2f725deb2293630bd8c8a377d539736e2ccc2b90c08b97abd8c5ce4" + - "ea91a6219ab06c61c31eb48a35587b3c1719f387bd8c2063c5a79d041ca8a9ffac2e3c728f74efdb74ee0730f84cb3a8aefff7c8a1b570127cfc93eb6d3327f5" + - "ba7f886dee8be0548f710d6bfb18cbe5910bf61aed2c95028006f419241d968933aa00bb0760a41d2693465827a00837a84cadaf8a8e804d175adc5915c6cb6e" + - "fefb2cf70db063f2f3812da17586436c176aa0a815dfc7983eb88bfb1b6d1db7ab119cd3058c0db4d1910034f70f6eedfee8b742ea45af9780f415be2f851061" + - "313a218ad48e992b75afaa07c33ca47ee0155fe72e13d7e5736e512c5e5a45d351f7c7902d8b0fa31b34569a9aea31b018d63d572a9898c389d07caa427f114d" + - "251263d56cf5d6663159c2b32683b266fb909ba9d4caadaeda6700c03b25307cdea597a3287fd76082dbf33f073482872fdb494b892112c594d7f265d2799b5e" + - "5ec46a30fbf1557fa344a664a7af457a4e8ce2c014a270215d3f95d47a42d8f86a61d6d6b363d04a99a0d8f06c5b15cd803d951aea0ca185a807ca4c677db789" + - "fca64f0c5ba95b8c64f930eda658f9f773a9e1c8669589a7d98ade8dbc2c2c4cbbaf6ea2bbc6e762d4098f4db0d3f055958ae9da15ae57ee0b60fb9513dacf5a" + - "d65e34613570186acecf9e165bfa470aabcd35f22620497fbcabf220c53cff84eec12cf9965297b364f0e9122895c175d213fc2a9c9cbf27ebe1cf96fdacaf1c" + - "1c79ede66cfaa5057d33e09b31b43869812e5a0ce730663c18c4333141ae9565e437d99ade6b2cbe005214e8b3392c55bf4d7b38ef16e7f84b4ba3c85e1dfd1a" + - "ca8da1a5c75fd190e7752926533327880aed1461c7e9de2443ba0a2d094f4a14d5fffd3b102ea78acd34d162e82ab78fbb82bfbc8a9708ab532aa861643c39cd" + - "2bc89f2be53c583f9930fb2da14f1c5d5f218384b1740a76bd8b7ddd2c9888c8d7e7e78cc7a3304fa41995c7c1c3316894296caeeb9711f0e6bf16abc380bd41" + - "10448be3cb03cc3246ee7b9559c858307001033c84ecf89690526544c05c146f206d4a21e710597bb57759d232154a1f9d88eb3f3440374bad1e901da7a154b8" + - "39a6d1b1b6b2ab0be872ff036a9f9f769a169fbf91bada732d8f28c453b9be49011b211155fa5c588b43018775f99e3b92b322a4c41282326b79fd26541ccafc" + - "c0e2f09797e3217fb0e5785b72e654dbcde8ba14b2d56faa2218748c6789c158bb635d43c9a64690b004ab70f457e9fd959b2d90875966968c7ac44b103283e7" + - "50b60deeb1f89444aee25fbdb7fa3a96d70c3dce38246f111e466cdfa3b807c54ed584f5b1a64456e923dcf37f45b36cea3d602ba3a55a4fd883ebb6dc198650" + - "b522461614656897b9b7d408d48b12e594af06c91f715b32a4ed65a379f0ab461acb9b8b20d1f1b12e9f7fea422c0c7d545eff4152e06002cbd120fd74b483d3" + - "a0ee30cfd851c98e9aa8fb19b60528de4a75b412bed656933ae8ab600aeaef5befdcca4d35fa472ed38ffb91a9017c19c5d500426f262ba379034c45cf5d1627" + - "48da223207721b4bc4504b79309f3d622c53dfe3c83ff8866dd7614a2e90a85c077b2e18bf1cb6008f0d785d6a0ffd5f15a83a343036f3fdd25314bfe47b5a12" + - "58a7c89475f39a58a671d0a17f6fd100a8928181b94d8d53149316d5addf14bd398b538e2593273f02cf296fd73ff92d02230de939dae94e03d44ce93dd4dfa1" + - "b9219fd369c854ec409d7bf94b316e5e9c16e1ba525a783e24bd3fc0ecc949be245c402efae8ea77aaca74c78703506cfd5a5a614793e04c76652b4f344f79fd" + - "f2da1e34f650fc1094116ead723813d204ffe375d20707fd94d90f21c009194201c88d22afaee83a8a6be7518dc915331b863664e033d397c64e1516c0fd9324" + - "11614a1bdf2feb86e0d0ae21e784a55086c596c7eedd44d3afd7295455450f507f1c1a33c9ba94d50931ec054d8740510ea23990c266f30678a74fdd485b482b" + - "cbfb4070e3f10b66c65a4210794a3137adabe887ffb9bcf2a30c625138f840b2666610e76e5a0abc183088a94930c025836653eddbc440621bbf94761c74e108" + - "3672c6a914a753fd452e8e7a02c54b21d7bab4b705b4509b9b5b27e2e5144289eece950c3634b410b5e3cf8c5a5f74d98b55d17d45d7014390cf696a7e693777" + - "4c028517062a69276910cf5f139078e8ef6e77aa8b35aa55fd4f53e48ae6b4875d1732b286ffe8bf852b73af7b964fdf1aa4c4f16d9f14485a2b1a704c2615ac" + - "8ac74eaaacec7e8e4e506e1b418d377e4d5a271dfab47b3d3c11a809beda596fdf37935dfe06c147dfe7d5be696ffb2a0cff907d1eb2a88477c261d5a7aba06c" + - "d70dc52d00b9a9d851e849f86e1cba91b4c40d1ae3d4f21a2763369dde34d084adfc09d2a6cb5f09114cd8d6fa26d15f1ec428adc245064e5b8e80f21b0b3ff2" + - "6690398d3080f5355fc082bc4bf3a38576c7da00efbc80839dc9a06fab2b998a152553c36fab42e03e3e4b54456ed954e53bd63902d89e2617a263e70146d1eb" + - "71557baf43aeb0a681f600a784778c895afce26fe17e3ac33990c54cd96fcb2432de79d4f95ab2fb96effdd37f4e4247ae5b4c1fa461ca3269d45a90af090333" + - "fc3ab5152bd5aed4445eab93466701382ba76fc8745abd911bdb45a494e1c62647670380c04377bcdb5e631318dfa79850469a988094acd48a4110bbc7289617" + - "ce436294ff242302d154ad75437ae2f551df5b84f884c87497de0bb2ef7bd41a8c758e4b158084c78ef047389d88974faafa00ce7396e849509d39c403fdcca6" + - "8f47e1d0fc294e5510a07af24c165e1a4b4ba9498e7b333c4e8624c552801079775fc684b6e98b72ff133164a2052c2aadcef168d9cdeab8a935c98f08e23b95" + - "859277381a2ce23ea61fbe9ec1439a489523161ed370b0069aa6a5c7981e4a80c04e304ff2fd85f80b51e3de3484b53084f376cc72a390aaefc49baddf4d2545" + - "93dcb5a49326c9c15c3d1c0e0709c9879d68bee07b956d018a995bf1e7f8fa03ef2079d01e0bec601519704cced98854c94f1f0ae837653f14c0221e12f2cbdb" + - "1564066062bc1d4dcf7ed8b2c980b90e8101842d5844375cb370f402d858dffd9eb52572f8420d4d246462230ca0dbd567250e4f065730a6aecbd804b1acf949" + - "30e2890a39fdd4c1eb693f7e345504dbad5ac207f1a649968c3a7b416bd972b6a6bfde04375337a93b0ac08f6fae62c0fa7df8ae9deeee421f7ac62d8cf5ecf3" + - "b5ff39877ee4abaeb9db03d8a8f13f7925e54267a2651c55ecf580d5cbb24bf504fb01291e3e97ad1696ed995608fceda79f2441ca67bfe3c31f4f4bf0fffcd6" + - "55408744524412cd4d3cbdbdd216694aa7477e88b25f7efeb34abf491a50695ff686829a8fea9e999877bcb37291b8dbeeddfd44465a2c28a215aa532590c487" + - "d4747b6ece4e1aeaef725cb305d11b965a9647bef36a5c2fb45cc334d35ff4e308cd8813b6de3953b35a4ef6a3ae07794f8b54ef6365a573135320612bd1acfe" + - "6cac5524c0e98b6f2a33a790b94f5134f0cba075a6fa93c191f4176ca62ea2e365557d6b3363a17b9ee52f3c347c82cd19f8432d16a934ae9c5d4d4505e7d20e" + - "1ae31bb64ccb084f7a59744b27d58c2388d449ff4b63604878ae858240348ecfcb51761678265bd60d5dd7d51e25e91668fdf80f6b726b29ef6c3f0f229d8af4" + - "b2cdadc3ca7fbadab49b28819b9c9b92b49cbe9a281e5891f4eae7616013777605a0623dd7a650baf9a9dad66ca9aae3c76ef1e27db32bd9514a2776eb0c8d05" + - "65eec06fc4c8a69c417efa336842e248e5a51e3b5f3ba3227e3f78f1bd12d81595e03a01f4259c772fd481ab5f3d7a945e1c95fe0dc3c4742eeb7e15c9426ec3" + - "ed4c90ee07d56acc78fecfd7c5ce1e04e7db1a970091f15c90f0aae2865d135395d27787aaf68c6a179064d82691e0b6c795f61875f317dc6d2e8feea55a28f2" + - "461d74e14e350351720b6f536adfe3addd4111f08e3a84da2656fd4bc83989b329b383da9f01cf2392aa0b19577884d1281f2e6c106df451c078a472b36057d3" + - "065dfc4bbb47ce4e5dce4acf6da095bdd10322f3ae12bcdd1f805e73b303f1fc7a7e16cf3ffd822bd8b25fbc93be065019e394113182713f1ad299ae6537f6bf" + - "57116e8dc9ec775519b797ab4107c2ac5129ba85188852c3bc5f116044bbd8985b6dc8b8da4659589bf9d2351c4c3adaed87fe2ea20ef6bf62224c7af86fe8b4" + - "973e558f39465dff43bf23cf1f78957514e4e82a3009d40d97bf8d8442a11deabde806e2fa84c1ba75273da75ce8dad3b2a34786b2958ac4bfd248ebe604a173" + - "83c727b11dd922b1f72476af700b663fbd7033d0ac74b463d40a92d26c938b69f96fb4a9cb7a9ca2bd9496251270c0c5fcae6b3c2eda5377b897891648a97125" + - "8ac71fed8dce8e02c30961a299cb7f3145845dbe8f4dfaaaf4baf0ca3fb730abdd258e98215f072a943d5aee8d8bc4c86023524f7b69186d99ad88ccdfc0b4bd" + - "7db422bbad7eaa0824ce24b5c186e172c8c584f1cc5c126c901a69ebee8dbd230a653a3643b7875672d22fd86079daf8d834ba33664f5ad0b6eec767b4f58b45" + - "e67b776b90e0a5e130aa5365003eb7fd78b757b1cf9133f6a1d51064b293cb42c8c41b15b7e95e2a39fa5dae19c6e20031d2bfa4632c37779163fdecc6b45624" + - "4d6bfd01a8877f6fe7739591917a86e7dd795ad85cc3f256cff5961e8b62e92a0754a51f2c6d59819446eec8bdd08b87cf9f4fb5373e809d52240d2dd691cd50" + - "37fc79d35b61d63851917cfdba164868a3f79e2061bd4610c1f5216ed77df00baa75f949ad37142db4fd282a5c7d2e2636ca590f92fc4781d4f51efa69f53947" + - "d4fca1dc7dd2429837b6d7e5c9528effdecf6f731f676587785e5c4096bdb6f1f44e72f5f77d9025813e848881506f65bfb0f2b2d3ae6f9e00731929b5ac083d" + - "b1c9c324703e63fef6319e1d8150aa0ff7d9a2049961df9158f3e1f2e540a91feb742625d2a859a452186d2ccaa3ec2ba086ee0868a4dc24ae6818fc02f9c1df" + - "dc326cc31c46feefda97265238592f638968627ec24903b97513ab05ed58ce5b516decda0e2fbf01a70e6cb2e53c3e7b8855f80cd7e007b78da727ef0893e099" + - "592ba684d62ae2d1f06ad148fa7f34cfc724d804149cda21aee7eac064ad20d29132b260c2c2867fa6a2e747739fc30df2f002c2a99da6c7e64ee51e806af7d9" + - "768aec456b93a05002666cb61b2229c99f2cdef9afc9cea1c4ee3a85dd189823399781ee33cde2abedff09c47960c035e075a29156005d75845a11fa06abcc50" + - "5f7f849a0caaf683f334e9e7bbbef90fe6cf94cbf87767219440d31713daef6ad1e4a1cc720ce59fee4cf7731e46bbba9ec1648908ea345030aa8f10ade10ffa" + - "3d2acfd480b0b11eadc4fb2b740596b204e911456cb2f35ad9993ab7dd6a48b35ba0c207625384bb3c2ff24437810bd13c7ee96cd6f97f19ffd537ad182a3657" + - "b0e83d42fd6e2ebac6cbf5ea1bde97465b7cec6954ff5b5be049e59a49ea25ed6667dbace765401bde12031e5cfabf2df7afb728d2a0b2a38b24d79bf23a313c" + - "40fe5adef97487641c6088dd8712c0c352708e474b02c08fd2d71b6d44f16d82f291ccd61c43a339408379a8de54cfdbbae5e421e084112fbc17fb5561e084d1" + - "4149bf4bb06fd161878d8574f856867cff974d5898e161923d55bdac8699c9df6a220bcb6c800d3ae7f107b8c4acab206d780aafaf6c2e2379de8c900700d9c9" + - "c87d464772514c5aa3e5f5bfc00fb54f2b74702838b4731c5ac8a070b50783e81dd97fa8d55c739d026b607a2a78aba1bb79b1a7a3c22c78368672ac020061e6" + - "d9683d57d6989c6c6f08b8d5d74720f5cb25505fbe81d2bf53a68e972a54784705b20f83fd1ab5afff30764ef89dba4465b56f48b325ab3810bf8dcbf4faa61f" + - "676e2043ac8540df9e3af4c0f51d816e89c09bf67253be45fc5f75f64be97f6c7dc0c6392af6fa8e75aab58eda976b36773cd37d315771400a2cb846fdef3d8a" + - "a15bce5dfda0379e526f87cf67767a2ab93d41c85b4ed016ed0a89d2f94737433a3ebade813def29eaa18a1fb925fca7d08d1020f64caebc562cb4ad2fb241e2" + - "94923b2f2df5e6c4953c4b73be0f5568defe57ce49d16e2a205323e46cbb5a3e77fff1557671503bd7b5de5320f1fb951fbe26400cfa854af2d12fab0215310c" + - "f070add34dc4565d1757d7e10a03e3bb73a607ed7e10861b1274ddee76183cf7e56c1de7162c805c2dba0e0331d36f3a4e2019a2e0705ee2747ed1e52bc3a6fb" + - "3b061f784348204cdf8d643ff6c271fa72b56900edcc2f77201f3bd4fc296ad6534a7029ea66761bb9a3589a1f6ef566504c70297b98fbb603214fed2e4b7ca1" + - "06e3f0e993118897fa641fb9722d4667fa98d07a6837e5ab2144e5ec1548a7dbca28c559f2a9a99d54b8e55f56d0e59bcef1ac45e2046835b60579da0d2261e7" + - "30dab9009d138421c6458d146e870358b0b3fa20257e50b58f167c6b47edf7053513d58f33547d06ce52458baaa4dcf15f77b103565c66a81f183c827801b455" + - "b61b6287a46a37a96884075a7eada9ba7f0ddcc14654bf87a26d2e27a978b415257773796923a220e06b25af16fb5aaded9b2d081a4c64106df460ddce9c3b2a" + - "c8553e1521e501ad29a4b7f7681c9b60576a127087a5237c4c2bacf9b163dd590e63f2bc80e7f1e613773f87d034313064710404739d63363d204be7b14800c4" + - "d8c1b6a2a21da70223be51d281fee302ef806454f9d7d28244ba537c1d9f8f1bcc5d47038d986a8f95ca48437ffe94fd44a90bb03014a259112a97508adb3db4" + - "34f72a5268c1af6bc6d5801e579aab2228ca33600ebbf1a1959081c3a4ca99e444f97409f5e0ca4779241c9aacad1f4ee7fb4369bd6ae076378e4f63000b9a5c" + - "849ba6e72e47e2454a44659149338ac0767cd25d8693c0d143e354bd600f1c1d3a44eeb024923ea659060665d5cd9a4ca1ca86162819556535fd59b9fde90caa" + - "29920efe99479fe7e4b4e5371e13ccb43a1419cf023433239d840900d31bab37fabc3fd20e31bb7dbcb3ae8df66f67e2844944bcf544b658364f9e3d0b6d84b4" + - "63ad4eb621644fd7d774b501407a1178814b15149345d551b474347188067db2ab4d7f4d1abd3027133039e855e129f3f5649550da8c04fe2db57cb89bf1bf4f" + - "72eb35ccfe31afb92f6136d4c2a1c115b07b721b2da43151f11c356256230408979c5d95243714429e2c9500e7b043b20dac8a9763e5b487d1cbdb34ac379b9c" + - "6409454c79385b6e562459c4fdaad1b7f9297c1473e9b90fffe6d1c5390e241a187a4cefa2eb0cb0c11f4ca6c5b961c18ceb57892295290dbc991692556bffa3" + - "b8c405cf285e6bcb8a90246ad0ac15122f4cf73adc129d23aa2240733404beb6d74bf698e5589288a522573c774ce9f514b5d5c086382ea1dd4e89ff5facbf23" + - "d36bc3d203941e17747ede4b82820351f4df278ddb787ce8f6f1cc468ef953399efb072ce706e253f1bab76444bb70be6443cd0db633e958dc57bd223e00418e" + - "915a7c2e4d94c0623f9788276480cdfe798387d35e2ea2d304d066aec7627794cdd4200a44208d6c87f242c76e2d4a3f966b6fb96eaa63d892c1a177bef249b4" + - "fdd1a4c06c791f677dd9919f739ccf318bd77835330b0219786249c9c9736161dac771a838724f2dd70afba46a6782fd27601cf8a7126ae95a66e526131a68d7" + - "7a809e513533ed8021eecdbc5851dfcf95e10f1bbe47b5c7f079275a1837836245266b66d89fab25ac4bd6c1225560bea3259b67bf50a58ee056754d574da79e" + - "f9a1a0df3a5defed0f74fe74ce0bf65a04086f17e94a8451828c723c97932f26f9349f1a2c7866c617a528602721de4f3cc8916bcfc66cdc106bafa26ea87a13" + - "94dfa37e396365fb7f92df007b46a50ff04c7f85bfa679230ebedf18c2fb876fc7098dd1c4328adf85de71c31d94687a308053bfcdc158cfa7772170fbed63f5" + - "37dda41f65196dfacdd1186b5de0f3369d841ce6502192292d05a19ce7464f5bcab3015c721cac13ddca561b92dc1ee25d3068dc1945a1b4e2bd1e6604c42e4c" + - "3c04b490f6365828957990007394557854a903e19feb06906e41cbc8766bf37bd7dece90f4cdc987709b1129e84bfdc502543b5bfa887bf78553a5ec10ad68c5" + - "d10eff75f7aa495e7d934a55577fdc0aead31aee4522db0259d7d4ea8438a7996d80a787465a2980457193d1c4bf1a0a1e01741d72e5fc4dfe58475c1c01026b" + - "5a3bc973b902280753e9c3226db9cc778e2506c56ee86ae85b4d54dbf05394107329b2d1ee56522cb1ce562fb1aa4e592199d9c29f64cc3ab1d757531e209eec" + - "aa138d8388169b5e28c45f5aba267eeaa57f69869f0b6855d82b0eafcde63895251f41e8e676a0ab12ef3f569bb7de91b79fa46ad9637da01ca004f4d30259c1" + - "f5b00761f6ca9c17721a6718390624a10a11f7f52d7afb71ee5f8338828910e48f94a1347761abac87897b2dd0e23f1d325aec5031ef58f2972e8b402e05f8c1" + - "ae7053a90380a1ae0d4d06645548c23e13afa31aac8ff83b10f8341418af4114632f6406d6e33076391696c9161d63c8bcfd1c625fc737f68198046212d1638a" + - "d2d2d42ff7029c1fcc682a046edc4d4f24862d82c600180b1e8f57ff6a3865dfe9274f9886d00efa523a1b3b3757c4489200fec3dc5583854c955492336253dd" + - "767f2a60ce3d224afcff9cdc19e9b28830d33affda6af99942a8fe39562055f3e884fd6c1ebc1908ac159061f35e9b0da80434ce9673d9c6b87265170077c670" + - "743e37474d7605cd01c44af600f16d9ffaf24acf87fbe5ccf39bac41047a810d210051c87f06147a0bb8f1427a406700483679638f1af23f1dafb7aa0c468669" + - "71c3a82f535c26cf6cb335e8e915fda393799d3dbe0e04b907ed3612d12ac95783a6876cd986d2a13b82192532e02c250eaa42f891d2481655fa4494c723fe00" + - "87d224444245eb5b0eade5f741b025db1992a8ad0dce51b0c1af4a18a9e244f9f755891adf0f19179c7baa6c32bffc91e0b03c4ed3aaee1978b6a1f03b87ac6a" + - "fc3b9e7030bb212b17de198edfccde29d04224798c1204e47ea235f048724fac62d637d1ba0ee3922048fcf79c746b6c0c036d882e3491fd72bad6e009c6403e" + - "55876f4d31330caa02aedd0b0c121c3c41e736853a08071f0dd4ddc7412db0bbe274a9ac2932552bb37c40e72c2ef1d7cca8236942e480d709d3ea9d5ae0a1b7", - ), - }, - } - for i, tt := range tests { - cache := make([]uint32, tt.cacheSize/4) - generateCache(cache, tt.epoch, seedHash(tt.epoch*epochLength+1)) - - dataset := make([]uint32, tt.datasetSize/4) - generateDataset(dataset, tt.epoch, cache) - - want := make([]uint32, tt.datasetSize/4) - prepare(want, tt.dataset) - - if !reflect.DeepEqual(dataset, want) { - t.Errorf("dataset %d: content mismatch: have %x, want %x", i, dataset, want) - } - } -} - -// Tests whether the hashimoto lookup works for both light as well as the full -// datasets. -func TestHashimoto(t *testing.T) { - // Create the verification cache and mining dataset - cache := make([]uint32, 1024/4) - generateCache(cache, 0, make([]byte, 32)) - - dataset := make([]uint32, 32*1024/4) - generateDataset(dataset, 0, cache) - - // Create a block to verify - hash := hexutil.MustDecode("0xc9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f") - nonce := uint64(0) - - wantDigest := hexutil.MustDecode("0xe4073cffaef931d37117cefd9afd27ea0f1cad6a981dd2605c4a1ac97c519800") - wantResult := hexutil.MustDecode("0xd3539235ee2e6f8db665c0a72169f55b7f6c605712330b778ec3944f0eb5a557") - - digest, result := hashimotoLight(32*1024, cache, hash, nonce) - if !bytes.Equal(digest, wantDigest) { - t.Errorf("light hashimoto digest mismatch: have %x, want %x", digest, wantDigest) - } - if !bytes.Equal(result, wantResult) { - t.Errorf("light hashimoto result mismatch: have %x, want %x", result, wantResult) - } - digest, result = hashimotoFull(dataset, hash, nonce) - if !bytes.Equal(digest, wantDigest) { - t.Errorf("full hashimoto digest mismatch: have %x, want %x", digest, wantDigest) - } - if !bytes.Equal(result, wantResult) { - t.Errorf("full hashimoto result mismatch: have %x, want %x", result, wantResult) - } -} - -// Tests that caches generated on disk may be done concurrently. -func TestConcurrentDiskCacheGeneration(t *testing.T) { - // Create a temp folder to generate the caches into - cachedir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatalf("Failed to create temporary cache dir: %v", err) - } - defer os.RemoveAll(cachedir) - - // Define a heavy enough block, one from mainnet should do - block := types.NewBlockWithHeader(&types.Header{ - Number: big.NewInt(3311058), - ParentHash: common.HexToHash("0xd783efa4d392943503f28438ad5830b2d5964696ffc285f338585e9fe0a37a05"), - UncleHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"), - Coinbase: common.HexToAddress("0xc0ea08a2d404d3172d2add29a45be56da40e2949"), - Root: common.HexToHash("0x77d14e10470b5850332524f8cd6f69ad21f070ce92dca33ab2858300242ef2f1"), - TxHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), - ReceiptHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), - Difficulty: big.NewInt(167925187834220), - GasLimit: 4015682, - GasUsed: 0, - Time: 1488928920, - Extra: []byte("www.bw.com"), - MixDigest: common.HexToHash("0x3e140b0784516af5e5ec6730f2fb20cca22f32be399b9e4ad77d32541f798cd0"), - Nonce: types.EncodeNonce(0xf400cd0006070c49), - }) - // Simulate multiple processes sharing the same datadir - var pend sync.WaitGroup - - for i := 0; i < 3; i++ { - pend.Add(1) - go func(idx int) { - defer pend.Done() - - config := Config{ - CacheDir: cachedir, - CachesOnDisk: 1, - } - ethash := New(config, nil, false) - defer ethash.Close() - if err := ethash.verifySeal(nil, block.Header(), false); err != nil { - t.Errorf("proc %d: block verification failed: %v", idx, err) - } - }(i) - } - pend.Wait() -} - -// Benchmarks the cache generation performance. -func BenchmarkCacheGeneration(b *testing.B) { - for i := 0; i < b.N; i++ { - cache := make([]uint32, cacheSize(1)/4) - generateCache(cache, 0, make([]byte, 32)) - } -} - -// Benchmarks the dataset (small) generation performance. -func BenchmarkSmallDatasetGeneration(b *testing.B) { - cache := make([]uint32, 65536/4) - generateCache(cache, 0, make([]byte, 32)) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - dataset := make([]uint32, 32*65536/4) - generateDataset(dataset, 0, cache) - } -} - -// Benchmarks the light verification performance. -func BenchmarkHashimotoLight(b *testing.B) { - cache := make([]uint32, cacheSize(1)/4) - generateCache(cache, 0, make([]byte, 32)) - - hash := hexutil.MustDecode("0xc9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - hashimotoLight(datasetSize(1), cache, hash, 0) - } -} - -// Benchmarks the full (small) verification performance. -func BenchmarkHashimotoFullSmall(b *testing.B) { - cache := make([]uint32, 65536/4) - generateCache(cache, 0, make([]byte, 32)) - - dataset := make([]uint32, 32*65536/4) - generateDataset(dataset, 0, cache) - - hash := hexutil.MustDecode("0xc9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - hashimotoFull(dataset, hash, 0) - } -} - -func benchmarkHashimotoFullMmap(b *testing.B, name string, lock bool) { - b.Run(name, func(b *testing.B) { - tmpdir, err := ioutil.TempDir("", "ethash-test") - if err != nil { - b.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - d := &dataset{epoch: 0} - d.generate(tmpdir, 1, lock, false) - var hash [common.HashLength]byte - b.ResetTimer() - for i := 0; i < b.N; i++ { - binary.PutVarint(hash[:], int64(i)) - hashimotoFull(d.dataset, hash[:], 0) - } - }) -} - -// Benchmarks the full verification performance for mmap -func BenchmarkHashimotoFullMmap(b *testing.B) { - benchmarkHashimotoFullMmap(b, "WithLock", true) - benchmarkHashimotoFullMmap(b, "WithoutLock", false) -}
diff --git go-ethereum/consensus/ethash/sealer_test.go celo/consensus/ethash/sealer_test.go deleted file mode 100644 index 29e284780a38718a5ec41c0b0c30e8d2e8150c04..0000000000000000000000000000000000000000 --- go-ethereum/consensus/ethash/sealer_test.go +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package ethash - -import ( - "encoding/json" - "io/ioutil" - "math/big" - "net/http" - "net/http/httptest" - "strconv" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/internal/testlog" - "github.com/ethereum/go-ethereum/log" -) - -// Tests whether remote HTTP servers are correctly notified of new work. -func TestRemoteNotify(t *testing.T) { - // Start a simple web server to capture notifications. - sink := make(chan [3]string) - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - blob, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Errorf("failed to read miner notification: %v", err) - } - var work [3]string - if err := json.Unmarshal(blob, &work); err != nil { - t.Errorf("failed to unmarshal miner notification: %v", err) - } - sink <- work - })) - defer server.Close() - - // Create the custom ethash engine. - ethash := NewTester([]string{server.URL}, false) - defer ethash.Close() - - // Stream a work task and ensure the notification bubbles out. - header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} - block := types.NewBlockWithHeader(header) - - ethash.Seal(nil, block, nil, nil) - select { - case work := <-sink: - if want := ethash.SealHash(header).Hex(); work[0] != want { - t.Errorf("work packet hash mismatch: have %s, want %s", work[0], want) - } - if want := common.BytesToHash(SeedHash(header.Number.Uint64())).Hex(); work[1] != want { - t.Errorf("work packet seed mismatch: have %s, want %s", work[1], want) - } - target := new(big.Int).Div(new(big.Int).Lsh(big.NewInt(1), 256), header.Difficulty) - if want := common.BytesToHash(target.Bytes()).Hex(); work[2] != want { - t.Errorf("work packet target mismatch: have %s, want %s", work[2], want) - } - case <-time.After(3 * time.Second): - t.Fatalf("notification timed out") - } -} - -// Tests whether remote HTTP servers are correctly notified of new work. (Full pending block body / --miner.notify.full) -func TestRemoteNotifyFull(t *testing.T) { - // Start a simple web server to capture notifications. - sink := make(chan map[string]interface{}) - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - blob, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Errorf("failed to read miner notification: %v", err) - } - var work map[string]interface{} - if err := json.Unmarshal(blob, &work); err != nil { - t.Errorf("failed to unmarshal miner notification: %v", err) - } - sink <- work - })) - defer server.Close() - - // Create the custom ethash engine. - config := Config{ - PowMode: ModeTest, - NotifyFull: true, - Log: testlog.Logger(t, log.LvlWarn), - } - ethash := New(config, []string{server.URL}, false) - defer ethash.Close() - - // Stream a work task and ensure the notification bubbles out. - header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} - block := types.NewBlockWithHeader(header) - - ethash.Seal(nil, block, nil, nil) - select { - case work := <-sink: - if want := "0x" + strconv.FormatUint(header.Number.Uint64(), 16); work["number"] != want { - t.Errorf("pending block number mismatch: have %v, want %v", work["number"], want) - } - if want := "0x" + header.Difficulty.Text(16); work["difficulty"] != want { - t.Errorf("pending block difficulty mismatch: have %s, want %s", work["difficulty"], want) - } - case <-time.After(3 * time.Second): - t.Fatalf("notification timed out") - } -} - -// Tests that pushing work packages fast to the miner doesn't cause any data race -// issues in the notifications. -func TestRemoteMultiNotify(t *testing.T) { - // Start a simple web server to capture notifications. - sink := make(chan [3]string, 64) - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - blob, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Errorf("failed to read miner notification: %v", err) - } - var work [3]string - if err := json.Unmarshal(blob, &work); err != nil { - t.Errorf("failed to unmarshal miner notification: %v", err) - } - sink <- work - })) - defer server.Close() - - // Create the custom ethash engine. - ethash := NewTester([]string{server.URL}, false) - ethash.config.Log = testlog.Logger(t, log.LvlWarn) - defer ethash.Close() - - // Provide a results reader. - // Otherwise the unread results will be logged asynchronously - // and this can happen after the test is finished, causing a panic. - results := make(chan *types.Block, cap(sink)) - - // Stream a lot of work task and ensure all the notifications bubble out. - for i := 0; i < cap(sink); i++ { - header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)} - block := types.NewBlockWithHeader(header) - ethash.Seal(nil, block, results, nil) - } - - for i := 0; i < cap(sink); i++ { - select { - case <-sink: - <-results - case <-time.After(10 * time.Second): - t.Fatalf("notification %d timed out", i) - } - } -} - -// Tests that pushing work packages fast to the miner doesn't cause any data race -// issues in the notifications. Full pending block body / --miner.notify.full) -func TestRemoteMultiNotifyFull(t *testing.T) { - // Start a simple web server to capture notifications. - sink := make(chan map[string]interface{}, 64) - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - blob, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Errorf("failed to read miner notification: %v", err) - } - var work map[string]interface{} - if err := json.Unmarshal(blob, &work); err != nil { - t.Errorf("failed to unmarshal miner notification: %v", err) - } - sink <- work - })) - defer server.Close() - - // Create the custom ethash engine. - config := Config{ - PowMode: ModeTest, - NotifyFull: true, - Log: testlog.Logger(t, log.LvlWarn), - } - ethash := New(config, []string{server.URL}, false) - defer ethash.Close() - - // Provide a results reader. - // Otherwise the unread results will be logged asynchronously - // and this can happen after the test is finished, causing a panic. - results := make(chan *types.Block, cap(sink)) - - // Stream a lot of work task and ensure all the notifications bubble out. - for i := 0; i < cap(sink); i++ { - header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)} - block := types.NewBlockWithHeader(header) - ethash.Seal(nil, block, results, nil) - } - - for i := 0; i < cap(sink); i++ { - select { - case <-sink: - <-results - case <-time.After(10 * time.Second): - t.Fatalf("notification %d timed out", i) - } - } -} - -// Tests whether stale solutions are correctly processed. -func TestStaleSubmission(t *testing.T) { - ethash := NewTester(nil, true) - defer ethash.Close() - api := &API{ethash} - - fakeNonce, fakeDigest := types.BlockNonce{0x01, 0x02, 0x03}, common.HexToHash("deadbeef") - - testcases := []struct { - headers []*types.Header - submitIndex int - submitRes bool - }{ - // Case1: submit solution for the latest mining package - { - []*types.Header{ - {ParentHash: common.BytesToHash([]byte{0xa}), Number: big.NewInt(1), Difficulty: big.NewInt(100000000)}, - }, - 0, - true, - }, - // Case2: submit solution for the previous package but have same parent. - { - []*types.Header{ - {ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000000)}, - {ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000001)}, - }, - 0, - true, - }, - // Case3: submit stale but acceptable solution - { - []*types.Header{ - {ParentHash: common.BytesToHash([]byte{0xc}), Number: big.NewInt(3), Difficulty: big.NewInt(100000000)}, - {ParentHash: common.BytesToHash([]byte{0xd}), Number: big.NewInt(9), Difficulty: big.NewInt(100000000)}, - }, - 0, - true, - }, - // Case4: submit very old solution - { - []*types.Header{ - {ParentHash: common.BytesToHash([]byte{0xe}), Number: big.NewInt(10), Difficulty: big.NewInt(100000000)}, - {ParentHash: common.BytesToHash([]byte{0xf}), Number: big.NewInt(17), Difficulty: big.NewInt(100000000)}, - }, - 0, - false, - }, - } - results := make(chan *types.Block, 16) - - for id, c := range testcases { - for _, h := range c.headers { - ethash.Seal(nil, types.NewBlockWithHeader(h), results, nil) - } - if res := api.SubmitWork(fakeNonce, ethash.SealHash(c.headers[c.submitIndex]), fakeDigest); res != c.submitRes { - t.Errorf("case %d submit result mismatch, want %t, get %t", id+1, c.submitRes, res) - } - if !c.submitRes { - continue - } - select { - case res := <-results: - if res.Header().Nonce != fakeNonce { - t.Errorf("case %d block nonce mismatch, want %x, get %x", id+1, fakeNonce, res.Header().Nonce) - } - if res.Header().MixDigest != fakeDigest { - t.Errorf("case %d block digest mismatch, want %x, get %x", id+1, fakeDigest, res.Header().MixDigest) - } - if res.Header().Difficulty.Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() { - t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Header().Difficulty) - } - if res.Header().Number.Uint64() != c.headers[c.submitIndex].Number.Uint64() { - t.Errorf("case %d block number mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Number.Uint64(), res.Header().Number.Uint64()) - } - if res.Header().ParentHash != c.headers[c.submitIndex].ParentHash { - t.Errorf("case %d block parent hash mismatch, want %s, get %s", id+1, c.headers[c.submitIndex].ParentHash.Hex(), res.Header().ParentHash.Hex()) - } - case <-time.NewTimer(time.Second).C: - t.Errorf("case %d fetch ethash result timeout", id+1) - } - } -}
diff --git go-ethereum/consensus/ethash/difficulty.go celo/consensus/ethash/difficulty.go deleted file mode 100644 index c3f2ab9685ebf69bc32ba2bb9d89627f31729c62..0000000000000000000000000000000000000000 --- go-ethereum/consensus/ethash/difficulty.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package ethash - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/core/types" - "github.com/holiman/uint256" -) - -const ( - // frontierDurationLimit is for Frontier: - // The decision boundary on the blocktime duration used to determine - // whether difficulty should go up or down. - frontierDurationLimit = 13 - // minimumDifficulty The minimum that the difficulty may ever be. - minimumDifficulty = 131072 - // expDiffPeriod is the exponential difficulty period - expDiffPeriodUint = 100000 - // difficultyBoundDivisorBitShift is the bound divisor of the difficulty (2048), - // This constant is the right-shifts to use for the division. - difficultyBoundDivisor = 11 -) - -// CalcDifficultyFrontierU256 is the difficulty adjustment algorithm. It returns the -// difficulty that a new block should have when created at time given the parent -// block's time and difficulty. The calculation uses the Frontier rules. -func CalcDifficultyFrontierU256(time uint64, parent *types.Header) *big.Int { - /* - Algorithm - block_diff = pdiff + pdiff / 2048 * (1 if time - ptime < 13 else -1) + int(2^((num // 100000) - 2)) - - Where: - - pdiff = parent.difficulty - - ptime = parent.time - - time = block.timestamp - - num = block.number - */ - - pDiff, _ := uint256.FromBig(parent.Difficulty) // pDiff: pdiff - adjust := pDiff.Clone() - adjust.Rsh(adjust, difficultyBoundDivisor) // adjust: pDiff / 2048 - - if time-parent.Time < frontierDurationLimit { - pDiff.Add(pDiff, adjust) - } else { - pDiff.Sub(pDiff, adjust) - } - if pDiff.LtUint64(minimumDifficulty) { - pDiff.SetUint64(minimumDifficulty) - } - // 'pdiff' now contains: - // pdiff + pdiff / 2048 * (1 if time - ptime < 13 else -1) - - if periodCount := (parent.Number.Uint64() + 1) / expDiffPeriodUint; periodCount > 1 { - // diff = diff + 2^(periodCount - 2) - expDiff := adjust.SetOne() - expDiff.Lsh(expDiff, uint(periodCount-2)) // expdiff: 2 ^ (periodCount -2) - pDiff.Add(pDiff, expDiff) - } - return pDiff.ToBig() -} - -// CalcDifficultyHomesteadU256 is the difficulty adjustment algorithm. It returns -// the difficulty that a new block should have when created at time given the -// parent block's time and difficulty. The calculation uses the Homestead rules. -func CalcDifficultyHomesteadU256(time uint64, parent *types.Header) *big.Int { - /* - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md - Algorithm: - block_diff = pdiff + pdiff / 2048 * max(1 - (time - ptime) / 10, -99) + 2 ^ int((num / 100000) - 2)) - - Our modification, to use unsigned ints: - block_diff = pdiff - pdiff / 2048 * max((time - ptime) / 10 - 1, 99) + 2 ^ int((num / 100000) - 2)) - - Where: - - pdiff = parent.difficulty - - ptime = parent.time - - time = block.timestamp - - num = block.number - */ - - pDiff, _ := uint256.FromBig(parent.Difficulty) // pDiff: pdiff - adjust := pDiff.Clone() - adjust.Rsh(adjust, difficultyBoundDivisor) // adjust: pDiff / 2048 - - x := (time - parent.Time) / 10 // (time - ptime) / 10) - var neg = true - if x == 0 { - x = 1 - neg = false - } else if x >= 100 { - x = 99 - } else { - x = x - 1 - } - z := new(uint256.Int).SetUint64(x) - adjust.Mul(adjust, z) // adjust: (pdiff / 2048) * max((time - ptime) / 10 - 1, 99) - if neg { - pDiff.Sub(pDiff, adjust) // pdiff - pdiff / 2048 * max((time - ptime) / 10 - 1, 99) - } else { - pDiff.Add(pDiff, adjust) // pdiff + pdiff / 2048 * max((time - ptime) / 10 - 1, 99) - } - if pDiff.LtUint64(minimumDifficulty) { - pDiff.SetUint64(minimumDifficulty) - } - // for the exponential factor, a.k.a "the bomb" - // diff = diff + 2^(periodCount - 2) - if periodCount := (1 + parent.Number.Uint64()) / expDiffPeriodUint; periodCount > 1 { - expFactor := adjust.Lsh(adjust.SetOne(), uint(periodCount-2)) - pDiff.Add(pDiff, expFactor) - } - return pDiff.ToBig() -} - -// MakeDifficultyCalculatorU256 creates a difficultyCalculator with the given bomb-delay. -// the difficulty is calculated with Byzantium rules, which differs from Homestead in -// how uncles affect the calculation -func MakeDifficultyCalculatorU256(bombDelay *big.Int) func(time uint64, parent *types.Header) *big.Int { - // Note, the calculations below looks at the parent number, which is 1 below - // the block number. Thus we remove one from the delay given - bombDelayFromParent := bombDelay.Uint64() - 1 - return func(time uint64, parent *types.Header) *big.Int { - /* - https://github.com/ethereum/EIPs/issues/100 - pDiff = parent.difficulty - BLOCK_DIFF_FACTOR = 9 - a = pDiff + (pDiff // BLOCK_DIFF_FACTOR) * adj_factor - b = min(parent.difficulty, MIN_DIFF) - child_diff = max(a,b ) - */ - x := (time - parent.Time) / 9 // (block_timestamp - parent_timestamp) // 9 - c := uint64(1) // if parent.unclehash == emptyUncleHashHash - if parent.UncleHash != types.EmptyUncleHash { - c = 2 - } - xNeg := x >= c - if xNeg { - // x is now _negative_ adjustment factor - x = x - c // - ( (t-p)/p -( 2 or 1) ) - } else { - x = c - x // (2 or 1) - (t-p)/9 - } - if x > 99 { - x = 99 // max(x, 99) - } - // parent_diff + (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)) - y := new(uint256.Int) - y.SetFromBig(parent.Difficulty) // y: p_diff - pDiff := y.Clone() // pdiff: p_diff - z := new(uint256.Int).SetUint64(x) //z : +-adj_factor (either pos or negative) - y.Rsh(y, difficultyBoundDivisor) // y: p__diff / 2048 - z.Mul(y, z) // z: (p_diff / 2048 ) * (+- adj_factor) - - if xNeg { - y.Sub(pDiff, z) // y: parent_diff + parent_diff/2048 * adjustment_factor - } else { - y.Add(pDiff, z) // y: parent_diff + parent_diff/2048 * adjustment_factor - } - // minimum difficulty can ever be (before exponential factor) - if y.LtUint64(minimumDifficulty) { - y.SetUint64(minimumDifficulty) - } - // calculate a fake block number for the ice-age delay - // Specification: https://eips.ethereum.org/EIPS/eip-1234 - var pNum = parent.Number.Uint64() - if pNum >= bombDelayFromParent { - if fakeBlockNumber := pNum - bombDelayFromParent; fakeBlockNumber >= 2*expDiffPeriodUint { - z.SetOne() - z.Lsh(z, uint(fakeBlockNumber/expDiffPeriodUint-2)) - y.Add(z, y) - } - } - return y.ToBig() - } -}
diff --git go-ethereum/consensus/ethash/ethash_test.go celo/consensus/ethash/ethash_test.go deleted file mode 100644 index dd007d72f490bb498c7d0d169b88fc7a272452d1..0000000000000000000000000000000000000000 --- go-ethereum/consensus/ethash/ethash_test.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package ethash - -import ( - "io/ioutil" - "math/big" - "math/rand" - "os" - "sync" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" -) - -// Tests that ethash works correctly in test mode. -func TestTestMode(t *testing.T) { - header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} - - ethash := NewTester(nil, false) - defer ethash.Close() - - results := make(chan *types.Block) - err := ethash.Seal(nil, types.NewBlockWithHeader(header), results, nil) - if err != nil { - t.Fatalf("failed to seal block: %v", err) - } - select { - case block := <-results: - header.Nonce = types.EncodeNonce(block.Nonce()) - header.MixDigest = block.MixDigest() - if err := ethash.verifySeal(nil, header, false); err != nil { - t.Fatalf("unexpected verification error: %v", err) - } - case <-time.NewTimer(4 * time.Second).C: - t.Error("sealing result timeout") - } -} - -// This test checks that cache lru logic doesn't crash under load. -// It reproduces https://github.com/ethereum/go-ethereum/issues/14943 -func TestCacheFileEvict(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "ethash-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - config := Config{ - CachesInMem: 3, - CachesOnDisk: 10, - CacheDir: tmpdir, - PowMode: ModeTest, - } - e := New(config, nil, false) - defer e.Close() - - workers := 8 - epochs := 100 - var wg sync.WaitGroup - wg.Add(workers) - for i := 0; i < workers; i++ { - go verifyTest(&wg, e, i, epochs) - } - wg.Wait() -} - -func verifyTest(wg *sync.WaitGroup, e *Ethash, workerIndex, epochs int) { - defer wg.Done() - - const wiggle = 4 * epochLength - r := rand.New(rand.NewSource(int64(workerIndex))) - for epoch := 0; epoch < epochs; epoch++ { - block := int64(epoch)*epochLength - wiggle/2 + r.Int63n(wiggle) - if block < 0 { - block = 0 - } - header := &types.Header{Number: big.NewInt(block), Difficulty: big.NewInt(100)} - e.verifySeal(nil, header, false) - } -} - -func TestRemoteSealer(t *testing.T) { - ethash := NewTester(nil, false) - defer ethash.Close() - - api := &API{ethash} - if _, err := api.GetWork(); err != errNoMiningWork { - t.Error("expect to return an error indicate there is no mining work") - } - header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} - block := types.NewBlockWithHeader(header) - sealhash := ethash.SealHash(header) - - // Push new work. - results := make(chan *types.Block) - ethash.Seal(nil, block, results, nil) - - var ( - work [4]string - err error - ) - if work, err = api.GetWork(); err != nil || work[0] != sealhash.Hex() { - t.Error("expect to return a mining work has same hash") - } - - if res := api.SubmitWork(types.BlockNonce{}, sealhash, common.Hash{}); res { - t.Error("expect to return false when submit a fake solution") - } - // Push new block with same block number to replace the original one. - header = &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(1000)} - block = types.NewBlockWithHeader(header) - sealhash = ethash.SealHash(header) - ethash.Seal(nil, block, results, nil) - - if work, err = api.GetWork(); err != nil || work[0] != sealhash.Hex() { - t.Error("expect to return the latest pushed work") - } -} - -func TestHashrate(t *testing.T) { - var ( - hashrate = []hexutil.Uint64{100, 200, 300} - expect uint64 - ids = []common.Hash{common.HexToHash("a"), common.HexToHash("b"), common.HexToHash("c")} - ) - ethash := NewTester(nil, false) - defer ethash.Close() - - if tot := ethash.Hashrate(); tot != 0 { - t.Error("expect the result should be zero") - } - - api := &API{ethash} - for i := 0; i < len(hashrate); i += 1 { - if res := api.SubmitHashrate(hashrate[i], ids[i]); !res { - t.Error("remote miner submit hashrate failed") - } - expect += uint64(hashrate[i]) - } - if tot := ethash.Hashrate(); tot != float64(expect) { - t.Error("expect total hashrate should be same") - } -} - -func TestClosedRemoteSealer(t *testing.T) { - ethash := NewTester(nil, false) - time.Sleep(1 * time.Second) // ensure exit channel is listening - ethash.Close() - - api := &API{ethash} - if _, err := api.GetWork(); err != errEthashStopped { - t.Error("expect to return an error to indicate ethash is stopped") - } - - if res := api.SubmitHashrate(hexutil.Uint64(100), common.HexToHash("a")); res { - t.Error("expect to return false when submit hashrate to a stopped ethash") - } -}
diff --git go-ethereum/consensus/ethash/consensus_test.go celo/consensus/ethash/consensus_test.go deleted file mode 100644 index f06f406be0a5add36ed1e1e0e02e980b9bdcf384..0000000000000000000000000000000000000000 --- go-ethereum/consensus/ethash/consensus_test.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package ethash - -import ( - "encoding/binary" - "encoding/json" - "math/big" - "math/rand" - "os" - "path/filepath" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" -) - -type diffTest struct { - ParentTimestamp uint64 - ParentDifficulty *big.Int - CurrentTimestamp uint64 - CurrentBlocknumber *big.Int - CurrentDifficulty *big.Int -} - -func (d *diffTest) UnmarshalJSON(b []byte) (err error) { - var ext struct { - ParentTimestamp string - ParentDifficulty string - CurrentTimestamp string - CurrentBlocknumber string - CurrentDifficulty string - } - if err := json.Unmarshal(b, &ext); err != nil { - return err - } - - d.ParentTimestamp = math.MustParseUint64(ext.ParentTimestamp) - d.ParentDifficulty = math.MustParseBig256(ext.ParentDifficulty) - d.CurrentTimestamp = math.MustParseUint64(ext.CurrentTimestamp) - d.CurrentBlocknumber = math.MustParseBig256(ext.CurrentBlocknumber) - d.CurrentDifficulty = math.MustParseBig256(ext.CurrentDifficulty) - - return nil -} - -func TestCalcDifficulty(t *testing.T) { - file, err := os.Open(filepath.Join("..", "..", "tests", "testdata", "BasicTests", "difficulty.json")) - if err != nil { - t.Skip(err) - } - defer file.Close() - - tests := make(map[string]diffTest) - err = json.NewDecoder(file).Decode(&tests) - if err != nil { - t.Fatal(err) - } - - config := &params.ChainConfig{HomesteadBlock: big.NewInt(1150000)} - - for name, test := range tests { - number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1)) - diff := CalcDifficulty(config, test.CurrentTimestamp, &types.Header{ - Number: number, - Time: test.ParentTimestamp, - Difficulty: test.ParentDifficulty, - }) - if diff.Cmp(test.CurrentDifficulty) != 0 { - t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff) - } - } -} - -func randSlice(min, max uint32) []byte { - var b = make([]byte, 4) - rand.Read(b) - a := binary.LittleEndian.Uint32(b) - size := min + a%(max-min) - out := make([]byte, size) - rand.Read(out) - return out -} - -func TestDifficultyCalculators(t *testing.T) { - rand.Seed(2) - for i := 0; i < 5000; i++ { - // 1 to 300 seconds diff - var timeDelta = uint64(1 + rand.Uint32()%3000) - diffBig := big.NewInt(0).SetBytes(randSlice(2, 10)) - if diffBig.Cmp(params.MinimumDifficulty) < 0 { - diffBig.Set(params.MinimumDifficulty) - } - //rand.Read(difficulty) - header := &types.Header{ - Difficulty: diffBig, - Number: new(big.Int).SetUint64(rand.Uint64() % 50_000_000), - Time: rand.Uint64() - timeDelta, - } - if rand.Uint32()&1 == 0 { - header.UncleHash = types.EmptyUncleHash - } - bombDelay := new(big.Int).SetUint64(rand.Uint64() % 50_000_000) - for i, pair := range []struct { - bigFn func(time uint64, parent *types.Header) *big.Int - u256Fn func(time uint64, parent *types.Header) *big.Int - }{ - {FrontierDifficultyCalulator, CalcDifficultyFrontierU256}, - {HomesteadDifficultyCalulator, CalcDifficultyHomesteadU256}, - {DynamicDifficultyCalculator(bombDelay), MakeDifficultyCalculatorU256(bombDelay)}, - } { - time := header.Time + timeDelta - want := pair.bigFn(time, header) - have := pair.u256Fn(time, header) - if want.BitLen() > 256 { - continue - } - if want.Cmp(have) != 0 { - t.Fatalf("pair %d: want %x have %x\nparent.Number: %x\np.Time: %x\nc.Time: %x\nBombdelay: %v\n", i, want, have, - header.Number, header.Time, time, bombDelay) - } - } - } -} - -func BenchmarkDifficultyCalculator(b *testing.B) { - x1 := makeDifficultyCalculator(big.NewInt(1000000)) - x2 := MakeDifficultyCalculatorU256(big.NewInt(1000000)) - h := &types.Header{ - ParentHash: common.Hash{}, - UncleHash: types.EmptyUncleHash, - Difficulty: big.NewInt(0xffffff), - Number: big.NewInt(500000), - Time: 1000000, - } - b.Run("big-frontier", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - calcDifficultyFrontier(1000014, h) - } - }) - b.Run("u256-frontier", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - CalcDifficultyFrontierU256(1000014, h) - } - }) - b.Run("big-homestead", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - calcDifficultyHomestead(1000014, h) - } - }) - b.Run("u256-homestead", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - CalcDifficultyHomesteadU256(1000014, h) - } - }) - b.Run("big-generic", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - x1(1000014, h) - } - }) - b.Run("u256-generic", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - x2(1000014, h) - } - }) -}
diff --git go-ethereum/consensus/ethash/consensus.go celo/consensus/ethash/consensus.go deleted file mode 100644 index 2543cc7138e9d20506fda1d68219ec67bf4662b0..0000000000000000000000000000000000000000 --- go-ethereum/consensus/ethash/consensus.go +++ /dev/null @@ -1,668 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package ethash - -import ( - "bytes" - "errors" - "fmt" - "math/big" - "runtime" - "time" - - mapset "github.com/deckarep/golang-set" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/misc" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie" - "golang.org/x/crypto/sha3" -) - -// Ethash proof-of-work protocol constants. -var ( - FrontierBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block - ByzantiumBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium - ConstantinopleBlockReward = big.NewInt(2e+18) // Block reward in wei for successfully mining a block upward from Constantinople - maxUncles = 2 // Maximum number of uncles allowed in a single block - allowedFutureBlockTimeSeconds = int64(15) // Max seconds from current time allowed for blocks, before they're considered future blocks - - // calcDifficultyEip3554 is the difficulty adjustment algorithm as specified by EIP 3554. - // It offsets the bomb a total of 9.7M blocks. - // Specification EIP-3554: https://eips.ethereum.org/EIPS/eip-3554 - calcDifficultyEip3554 = makeDifficultyCalculator(big.NewInt(9700000)) - - // calcDifficultyEip2384 is the difficulty adjustment algorithm as specified by EIP 2384. - // It offsets the bomb 4M blocks from Constantinople, so in total 9M blocks. - // Specification EIP-2384: https://eips.ethereum.org/EIPS/eip-2384 - calcDifficultyEip2384 = makeDifficultyCalculator(big.NewInt(9000000)) - - // calcDifficultyConstantinople is the difficulty adjustment algorithm for Constantinople. - // It returns the difficulty that a new block should have when created at time given the - // parent block's time and difficulty. The calculation uses the Byzantium rules, but with - // bomb offset 5M. - // Specification EIP-1234: https://eips.ethereum.org/EIPS/eip-1234 - calcDifficultyConstantinople = makeDifficultyCalculator(big.NewInt(5000000)) - - // calcDifficultyByzantium is the difficulty adjustment algorithm. It returns - // the difficulty that a new block should have when created at time given the - // parent block's time and difficulty. The calculation uses the Byzantium rules. - // Specification EIP-649: https://eips.ethereum.org/EIPS/eip-649 - calcDifficultyByzantium = makeDifficultyCalculator(big.NewInt(3000000)) -) - -// Various error messages to mark blocks invalid. These should be private to -// prevent engine specific errors from being referenced in the remainder of the -// codebase, inherently breaking if the engine is swapped out. Please put common -// error types into the consensus package. -var ( - errOlderBlockTime = errors.New("timestamp older than parent") - errTooManyUncles = errors.New("too many uncles") - errDuplicateUncle = errors.New("duplicate uncle") - errUncleIsAncestor = errors.New("uncle is ancestor") - errDanglingUncle = errors.New("uncle's parent is not ancestor") - errInvalidDifficulty = errors.New("non-positive difficulty") - errInvalidMixDigest = errors.New("invalid mix digest") - errInvalidPoW = errors.New("invalid proof-of-work") -) - -// Author implements consensus.Engine, returning the header's coinbase as the -// proof-of-work verified author of the block. -func (ethash *Ethash) Author(header *types.Header) (common.Address, error) { - return header.Coinbase, nil -} - -// VerifyHeader checks whether a header conforms to the consensus rules of the -// stock Ethereum ethash engine. -func (ethash *Ethash) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { - // If we're running a full engine faking, accept any input as valid - if ethash.config.PowMode == ModeFullFake { - return nil - } - // Short circuit if the header is known, or its parent not - number := header.Number.Uint64() - if chain.GetHeader(header.Hash(), number) != nil { - return nil - } - parent := chain.GetHeader(header.ParentHash, number-1) - if parent == nil { - return consensus.ErrUnknownAncestor - } - // Sanity checks passed, do a proper verification - return ethash.verifyHeader(chain, header, parent, false, seal, time.Now().Unix()) -} - -// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers -// concurrently. The method returns a quit channel to abort the operations and -// a results channel to retrieve the async verifications. -func (ethash *Ethash) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { - // If we're running a full engine faking, accept any input as valid - if ethash.config.PowMode == ModeFullFake || len(headers) == 0 { - abort, results := make(chan struct{}), make(chan error, len(headers)) - for i := 0; i < len(headers); i++ { - results <- nil - } - return abort, results - } - - // Spawn as many workers as allowed threads - workers := runtime.GOMAXPROCS(0) - if len(headers) < workers { - workers = len(headers) - } - - // Create a task channel and spawn the verifiers - var ( - inputs = make(chan int) - done = make(chan int, workers) - errors = make([]error, len(headers)) - abort = make(chan struct{}) - unixNow = time.Now().Unix() - ) - for i := 0; i < workers; i++ { - go func() { - for index := range inputs { - errors[index] = ethash.verifyHeaderWorker(chain, headers, seals, index, unixNow) - done <- index - } - }() - } - - errorsOut := make(chan error, len(headers)) - go func() { - defer close(inputs) - var ( - in, out = 0, 0 - checked = make([]bool, len(headers)) - inputs = inputs - ) - for { - select { - case inputs <- in: - if in++; in == len(headers) { - // Reached end of headers. Stop sending to workers. - inputs = nil - } - case index := <-done: - for checked[index] = true; checked[out]; out++ { - errorsOut <- errors[out] - if out == len(headers)-1 { - return - } - } - case <-abort: - return - } - } - }() - return abort, errorsOut -} - -func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, index int, unixNow int64) error { - var parent *types.Header - if index == 0 { - parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1) - } else if headers[index-1].Hash() == headers[index].ParentHash { - parent = headers[index-1] - } - if parent == nil { - return consensus.ErrUnknownAncestor - } - return ethash.verifyHeader(chain, headers[index], parent, false, seals[index], unixNow) -} - -// VerifyUncles verifies that the given block's uncles conform to the consensus -// rules of the stock Ethereum ethash engine. -func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { - // If we're running a full engine faking, accept any input as valid - if ethash.config.PowMode == ModeFullFake { - return nil - } - // Verify that there are at most 2 uncles included in this block - if len(block.Uncles()) > maxUncles { - return errTooManyUncles - } - if len(block.Uncles()) == 0 { - return nil - } - // Gather the set of past uncles and ancestors - uncles, ancestors := mapset.NewSet(), make(map[common.Hash]*types.Header) - - number, parent := block.NumberU64()-1, block.ParentHash() - for i := 0; i < 7; i++ { - ancestorHeader := chain.GetHeader(parent, number) - if ancestorHeader == nil { - break - } - ancestors[parent] = ancestorHeader - // If the ancestor doesn't have any uncles, we don't have to iterate them - if ancestorHeader.UncleHash != types.EmptyUncleHash { - // Need to add those uncles to the banned list too - ancestor := chain.GetBlock(parent, number) - if ancestor == nil { - break - } - for _, uncle := range ancestor.Uncles() { - uncles.Add(uncle.Hash()) - } - } - parent, number = ancestorHeader.ParentHash, number-1 - } - ancestors[block.Hash()] = block.Header() - uncles.Add(block.Hash()) - - // Verify each of the uncles that it's recent, but not an ancestor - for _, uncle := range block.Uncles() { - // Make sure every uncle is rewarded only once - hash := uncle.Hash() - if uncles.Contains(hash) { - return errDuplicateUncle - } - uncles.Add(hash) - - // Make sure the uncle has a valid ancestry - if ancestors[hash] != nil { - return errUncleIsAncestor - } - if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == block.ParentHash() { - return errDanglingUncle - } - if err := ethash.verifyHeader(chain, uncle, ancestors[uncle.ParentHash], true, true, time.Now().Unix()); err != nil { - return err - } - } - return nil -} - -// verifyHeader checks whether a header conforms to the consensus rules of the -// stock Ethereum ethash engine. -// See YP section 4.3.4. "Block Header Validity" -func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, uncle bool, seal bool, unixNow int64) error { - // Ensure that the header's extra-data section is of a reasonable size - if uint64(len(header.Extra)) > params.MaximumExtraDataSize { - return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize) - } - // Verify the header's timestamp - if !uncle { - if header.Time > uint64(unixNow+allowedFutureBlockTimeSeconds) { - return consensus.ErrFutureBlock - } - } - if header.Time <= parent.Time { - return errOlderBlockTime - } - // Verify the block's difficulty based on its timestamp and parent's difficulty - expected := ethash.CalcDifficulty(chain, header.Time, parent) - - if expected.Cmp(header.Difficulty) != 0 { - return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected) - } - // Verify that the gas limit is <= 2^63-1 - cap := uint64(0x7fffffffffffffff) - if header.GasLimit > cap { - return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap) - } - // Verify that the gasUsed is <= gasLimit - if header.GasUsed > header.GasLimit { - return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) - } - // Verify the block's gas usage and (if applicable) verify the base fee. - if !chain.Config().IsLondon(header.Number) { - // Verify BaseFee not present before EIP-1559 fork. - if header.BaseFee != nil { - return fmt.Errorf("invalid baseFee before fork: have %d, expected 'nil'", header.BaseFee) - } - if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil { - return err - } - } else if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil { - // Verify the header's EIP-1559 attributes. - return err - } - // Verify that the block number is parent's +1 - if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 { - return consensus.ErrInvalidNumber - } - // Verify the engine specific seal securing the block - if seal { - if err := ethash.verifySeal(chain, header, false); err != nil { - return err - } - } - // If all checks passed, validate any special fields for hard forks - if err := misc.VerifyDAOHeaderExtraData(chain.Config(), header); err != nil { - return err - } - if err := misc.VerifyForkHashes(chain.Config(), header, uncle); err != nil { - return err - } - return nil -} - -// CalcDifficulty is the difficulty adjustment algorithm. It returns -// the difficulty that a new block should have when created at time -// given the parent block's time and difficulty. -func (ethash *Ethash) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { - return CalcDifficulty(chain.Config(), time, parent) -} - -// CalcDifficulty is the difficulty adjustment algorithm. It returns -// the difficulty that a new block should have when created at time -// given the parent block's time and difficulty. -func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int { - next := new(big.Int).Add(parent.Number, big1) - switch { - case config.IsCatalyst(next): - return big.NewInt(1) - case config.IsLondon(next): - return calcDifficultyEip3554(time, parent) - case config.IsMuirGlacier(next): - return calcDifficultyEip2384(time, parent) - case config.IsConstantinople(next): - return calcDifficultyConstantinople(time, parent) - case config.IsByzantium(next): - return calcDifficultyByzantium(time, parent) - case config.IsHomestead(next): - return calcDifficultyHomestead(time, parent) - default: - return calcDifficultyFrontier(time, parent) - } -} - -// Some weird constants to avoid constant memory allocs for them. -var ( - expDiffPeriod = big.NewInt(100000) - big1 = big.NewInt(1) - big2 = big.NewInt(2) - big9 = big.NewInt(9) - big10 = big.NewInt(10) - bigMinus99 = big.NewInt(-99) -) - -// makeDifficultyCalculator creates a difficultyCalculator with the given bomb-delay. -// the difficulty is calculated with Byzantium rules, which differs from Homestead in -// how uncles affect the calculation -func makeDifficultyCalculator(bombDelay *big.Int) func(time uint64, parent *types.Header) *big.Int { - // Note, the calculations below looks at the parent number, which is 1 below - // the block number. Thus we remove one from the delay given - bombDelayFromParent := new(big.Int).Sub(bombDelay, big1) - return func(time uint64, parent *types.Header) *big.Int { - // https://github.com/ethereum/EIPs/issues/100. - // algorithm: - // diff = (parent_diff + - // (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)) - // ) + 2^(periodCount - 2) - - bigTime := new(big.Int).SetUint64(time) - bigParentTime := new(big.Int).SetUint64(parent.Time) - - // holds intermediate values to make the algo easier to read & audit - x := new(big.Int) - y := new(big.Int) - - // (2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9 - x.Sub(bigTime, bigParentTime) - x.Div(x, big9) - if parent.UncleHash == types.EmptyUncleHash { - x.Sub(big1, x) - } else { - x.Sub(big2, x) - } - // max((2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9, -99) - if x.Cmp(bigMinus99) < 0 { - x.Set(bigMinus99) - } - // parent_diff + (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)) - y.Div(parent.Difficulty, params.DifficultyBoundDivisor) - x.Mul(y, x) - x.Add(parent.Difficulty, x) - - // minimum difficulty can ever be (before exponential factor) - if x.Cmp(params.MinimumDifficulty) < 0 { - x.Set(params.MinimumDifficulty) - } - // calculate a fake block number for the ice-age delay - // Specification: https://eips.ethereum.org/EIPS/eip-1234 - fakeBlockNumber := new(big.Int) - if parent.Number.Cmp(bombDelayFromParent) >= 0 { - fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, bombDelayFromParent) - } - // for the exponential factor - periodCount := fakeBlockNumber - periodCount.Div(periodCount, expDiffPeriod) - - // the exponential factor, commonly referred to as "the bomb" - // diff = diff + 2^(periodCount - 2) - if periodCount.Cmp(big1) > 0 { - y.Sub(periodCount, big2) - y.Exp(big2, y, nil) - x.Add(x, y) - } - return x - } -} - -// calcDifficultyHomestead is the difficulty adjustment algorithm. It returns -// the difficulty that a new block should have when created at time given the -// parent block's time and difficulty. The calculation uses the Homestead rules. -func calcDifficultyHomestead(time uint64, parent *types.Header) *big.Int { - // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md - // algorithm: - // diff = (parent_diff + - // (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) - // ) + 2^(periodCount - 2) - - bigTime := new(big.Int).SetUint64(time) - bigParentTime := new(big.Int).SetUint64(parent.Time) - - // holds intermediate values to make the algo easier to read & audit - x := new(big.Int) - y := new(big.Int) - - // 1 - (block_timestamp - parent_timestamp) // 10 - x.Sub(bigTime, bigParentTime) - x.Div(x, big10) - x.Sub(big1, x) - - // max(1 - (block_timestamp - parent_timestamp) // 10, -99) - if x.Cmp(bigMinus99) < 0 { - x.Set(bigMinus99) - } - // (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) - y.Div(parent.Difficulty, params.DifficultyBoundDivisor) - x.Mul(y, x) - x.Add(parent.Difficulty, x) - - // minimum difficulty can ever be (before exponential factor) - if x.Cmp(params.MinimumDifficulty) < 0 { - x.Set(params.MinimumDifficulty) - } - // for the exponential factor - periodCount := new(big.Int).Add(parent.Number, big1) - periodCount.Div(periodCount, expDiffPeriod) - - // the exponential factor, commonly referred to as "the bomb" - // diff = diff + 2^(periodCount - 2) - if periodCount.Cmp(big1) > 0 { - y.Sub(periodCount, big2) - y.Exp(big2, y, nil) - x.Add(x, y) - } - return x -} - -// calcDifficultyFrontier is the difficulty adjustment algorithm. It returns the -// difficulty that a new block should have when created at time given the parent -// block's time and difficulty. The calculation uses the Frontier rules. -func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int { - diff := new(big.Int) - adjust := new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor) - bigTime := new(big.Int) - bigParentTime := new(big.Int) - - bigTime.SetUint64(time) - bigParentTime.SetUint64(parent.Time) - - if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 { - diff.Add(parent.Difficulty, adjust) - } else { - diff.Sub(parent.Difficulty, adjust) - } - if diff.Cmp(params.MinimumDifficulty) < 0 { - diff.Set(params.MinimumDifficulty) - } - - periodCount := new(big.Int).Add(parent.Number, big1) - periodCount.Div(periodCount, expDiffPeriod) - if periodCount.Cmp(big1) > 0 { - // diff = diff + 2^(periodCount - 2) - expDiff := periodCount.Sub(periodCount, big2) - expDiff.Exp(big2, expDiff, nil) - diff.Add(diff, expDiff) - diff = math.BigMax(diff, params.MinimumDifficulty) - } - return diff -} - -// Exported for fuzzing -var FrontierDifficultyCalulator = calcDifficultyFrontier -var HomesteadDifficultyCalulator = calcDifficultyHomestead -var DynamicDifficultyCalculator = makeDifficultyCalculator - -// verifySeal checks whether a block satisfies the PoW difficulty requirements, -// either using the usual ethash cache for it, or alternatively using a full DAG -// to make remote mining fast. -func (ethash *Ethash) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, fulldag bool) error { - // If we're running a fake PoW, accept any seal as valid - if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake { - time.Sleep(ethash.fakeDelay) - if ethash.fakeFail == header.Number.Uint64() { - return errInvalidPoW - } - return nil - } - // If we're running a shared PoW, delegate verification to it - if ethash.shared != nil { - return ethash.shared.verifySeal(chain, header, fulldag) - } - // Ensure that we have a valid difficulty for the block - if header.Difficulty.Sign() <= 0 { - return errInvalidDifficulty - } - // Recompute the digest and PoW values - number := header.Number.Uint64() - - var ( - digest []byte - result []byte - ) - // If fast-but-heavy PoW verification was requested, use an ethash dataset - if fulldag { - dataset := ethash.dataset(number, true) - if dataset.generated() { - digest, result = hashimotoFull(dataset.dataset, ethash.SealHash(header).Bytes(), header.Nonce.Uint64()) - - // Datasets are unmapped in a finalizer. Ensure that the dataset stays alive - // until after the call to hashimotoFull so it's not unmapped while being used. - runtime.KeepAlive(dataset) - } else { - // Dataset not yet generated, don't hang, use a cache instead - fulldag = false - } - } - // If slow-but-light PoW verification was requested (or DAG not yet ready), use an ethash cache - if !fulldag { - cache := ethash.cache(number) - - size := datasetSize(number) - if ethash.config.PowMode == ModeTest { - size = 32 * 1024 - } - digest, result = hashimotoLight(size, cache.cache, ethash.SealHash(header).Bytes(), header.Nonce.Uint64()) - - // Caches are unmapped in a finalizer. Ensure that the cache stays alive - // until after the call to hashimotoLight so it's not unmapped while being used. - runtime.KeepAlive(cache) - } - // Verify the calculated values against the ones provided in the header - if !bytes.Equal(header.MixDigest[:], digest) { - return errInvalidMixDigest - } - target := new(big.Int).Div(two256, header.Difficulty) - if new(big.Int).SetBytes(result).Cmp(target) > 0 { - return errInvalidPoW - } - return nil -} - -// Prepare implements consensus.Engine, initializing the difficulty field of a -// header to conform to the ethash protocol. The changes are done inline. -func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { - parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) - if parent == nil { - return consensus.ErrUnknownAncestor - } - header.Difficulty = ethash.CalcDifficulty(chain, header.Time, parent) - return nil -} - -// Finalize implements consensus.Engine, accumulating the block and uncle rewards, -// setting the final state on the header -func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { - // Accumulate any block and uncle rewards and commit the final state root - accumulateRewards(chain.Config(), state, header, uncles) - header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) -} - -// FinalizeAndAssemble implements consensus.Engine, accumulating the block and -// uncle rewards, setting the final state and assembling the block. -func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { - // Finalize block - ethash.Finalize(chain, header, state, txs, uncles) - - // Header seems complete, assemble into a block and return - return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil -} - -// SealHash returns the hash of a block prior to it being sealed. -func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) { - hasher := sha3.NewLegacyKeccak256() - - enc := []interface{}{ - header.ParentHash, - header.UncleHash, - header.Coinbase, - header.Root, - header.TxHash, - header.ReceiptHash, - header.Bloom, - header.Difficulty, - header.Number, - header.GasLimit, - header.GasUsed, - header.Time, - header.Extra, - } - if header.BaseFee != nil { - enc = append(enc, header.BaseFee) - } - rlp.Encode(hasher, enc) - hasher.Sum(hash[:0]) - return hash -} - -// Some weird constants to avoid constant memory allocs for them. -var ( - big8 = big.NewInt(8) - big32 = big.NewInt(32) -) - -// AccumulateRewards credits the coinbase of the given block with the mining -// reward. The total reward consists of the static block reward and rewards for -// included uncles. The coinbase of each uncle block is also rewarded. -func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { - // Skip block reward in catalyst mode - if config.IsCatalyst(header.Number) { - return - } - // Select the correct block reward based on chain progression - blockReward := FrontierBlockReward - if config.IsByzantium(header.Number) { - blockReward = ByzantiumBlockReward - } - if config.IsConstantinople(header.Number) { - blockReward = ConstantinopleBlockReward - } - // Accumulate the rewards for the miner and any included uncles - reward := new(big.Int).Set(blockReward) - r := new(big.Int) - for _, uncle := range uncles { - r.Add(uncle.Number, big8) - r.Sub(r, header.Number) - r.Mul(r, blockReward) - r.Div(r, big8) - state.AddBalance(uncle.Coinbase, r) - - r.Div(blockReward, big32) - reward.Add(reward, r) - } - state.AddBalance(header.Coinbase, reward) -}
diff --git go-ethereum/consensus/ethash/ethash.go celo/consensus/ethash/ethash.go deleted file mode 100644 index 91b38b1bdf53acedef0a202fb122d8da4b2b7b4e..0000000000000000000000000000000000000000 --- go-ethereum/consensus/ethash/ethash.go +++ /dev/null @@ -1,690 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Package ethash implements the ethash proof-of-work consensus engine. -package ethash - -import ( - "errors" - "fmt" - "math" - "math/big" - "math/rand" - "os" - "path/filepath" - "reflect" - "runtime" - "strconv" - "sync" - "sync/atomic" - "time" - "unsafe" - - "github.com/edsrzf/mmap-go" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" - "github.com/ethereum/go-ethereum/rpc" - "github.com/hashicorp/golang-lru/simplelru" -) - -var ErrInvalidDumpMagic = errors.New("invalid dump magic") - -var ( - // two256 is a big integer representing 2^256 - two256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) - - // sharedEthash is a full instance that can be shared between multiple users. - sharedEthash *Ethash - - // algorithmRevision is the data structure version used for file naming. - algorithmRevision = 23 - - // dumpMagic is a dataset dump header to sanity check a data dump. - dumpMagic = []uint32{0xbaddcafe, 0xfee1dead} -) - -func init() { - sharedConfig := Config{ - PowMode: ModeNormal, - CachesInMem: 3, - DatasetsInMem: 1, - } - sharedEthash = New(sharedConfig, nil, false) -} - -// isLittleEndian returns whether the local system is running in little or big -// endian byte order. -func isLittleEndian() bool { - n := uint32(0x01020304) - return *(*byte)(unsafe.Pointer(&n)) == 0x04 -} - -// memoryMap tries to memory map a file of uint32s for read only access. -func memoryMap(path string, lock bool) (*os.File, mmap.MMap, []uint32, error) { - file, err := os.OpenFile(path, os.O_RDONLY, 0644) - if err != nil { - return nil, nil, nil, err - } - mem, buffer, err := memoryMapFile(file, false) - if err != nil { - file.Close() - return nil, nil, nil, err - } - for i, magic := range dumpMagic { - if buffer[i] != magic { - mem.Unmap() - file.Close() - return nil, nil, nil, ErrInvalidDumpMagic - } - } - if lock { - if err := mem.Lock(); err != nil { - mem.Unmap() - file.Close() - return nil, nil, nil, err - } - } - return file, mem, buffer[len(dumpMagic):], err -} - -// memoryMapFile tries to memory map an already opened file descriptor. -func memoryMapFile(file *os.File, write bool) (mmap.MMap, []uint32, error) { - // Try to memory map the file - flag := mmap.RDONLY - if write { - flag = mmap.RDWR - } - mem, err := mmap.Map(file, flag, 0) - if err != nil { - return nil, nil, err - } - // The file is now memory-mapped. Create a []uint32 view of the file. - var view []uint32 - header := (*reflect.SliceHeader)(unsafe.Pointer(&view)) - header.Data = (*reflect.SliceHeader)(unsafe.Pointer(&mem)).Data - header.Cap = len(mem) / 4 - header.Len = header.Cap - return mem, view, nil -} - -// memoryMapAndGenerate tries to memory map a temporary file of uint32s for write -// access, fill it with the data from a generator and then move it into the final -// path requested. -func memoryMapAndGenerate(path string, size uint64, lock bool, generator func(buffer []uint32)) (*os.File, mmap.MMap, []uint32, error) { - // Ensure the data folder exists - if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { - return nil, nil, nil, err - } - // Create a huge temporary empty file to fill with data - temp := path + "." + strconv.Itoa(rand.Int()) - - dump, err := os.Create(temp) - if err != nil { - return nil, nil, nil, err - } - if err = dump.Truncate(int64(len(dumpMagic))*4 + int64(size)); err != nil { - return nil, nil, nil, err - } - // Memory map the file for writing and fill it with the generator - mem, buffer, err := memoryMapFile(dump, true) - if err != nil { - dump.Close() - return nil, nil, nil, err - } - copy(buffer, dumpMagic) - - data := buffer[len(dumpMagic):] - generator(data) - - if err := mem.Unmap(); err != nil { - return nil, nil, nil, err - } - if err := dump.Close(); err != nil { - return nil, nil, nil, err - } - if err := os.Rename(temp, path); err != nil { - return nil, nil, nil, err - } - return memoryMap(path, lock) -} - -// lru tracks caches or datasets by their last use time, keeping at most N of them. -type lru struct { - what string - new func(epoch uint64) interface{} - mu sync.Mutex - // Items are kept in a LRU cache, but there is a special case: - // We always keep an item for (highest seen epoch) + 1 as the 'future item'. - cache *simplelru.LRU - future uint64 - futureItem interface{} -} - -// newlru create a new least-recently-used cache for either the verification caches -// or the mining datasets. -func newlru(what string, maxItems int, new func(epoch uint64) interface{}) *lru { - if maxItems <= 0 { - maxItems = 1 - } - cache, _ := simplelru.NewLRU(maxItems, func(key, value interface{}) { - log.Trace("Evicted ethash "+what, "epoch", key) - }) - return &lru{what: what, new: new, cache: cache} -} - -// get retrieves or creates an item for the given epoch. The first return value is always -// non-nil. The second return value is non-nil if lru thinks that an item will be useful in -// the near future. -func (lru *lru) get(epoch uint64) (item, future interface{}) { - lru.mu.Lock() - defer lru.mu.Unlock() - - // Get or create the item for the requested epoch. - item, ok := lru.cache.Get(epoch) - if !ok { - if lru.future > 0 && lru.future == epoch { - item = lru.futureItem - } else { - log.Trace("Requiring new ethash "+lru.what, "epoch", epoch) - item = lru.new(epoch) - } - lru.cache.Add(epoch, item) - } - // Update the 'future item' if epoch is larger than previously seen. - if epoch < maxEpoch-1 && lru.future < epoch+1 { - log.Trace("Requiring new future ethash "+lru.what, "epoch", epoch+1) - future = lru.new(epoch + 1) - lru.future = epoch + 1 - lru.futureItem = future - } - return item, future -} - -// cache wraps an ethash cache with some metadata to allow easier concurrent use. -type cache struct { - epoch uint64 // Epoch for which this cache is relevant - dump *os.File // File descriptor of the memory mapped cache - mmap mmap.MMap // Memory map itself to unmap before releasing - cache []uint32 // The actual cache data content (may be memory mapped) - once sync.Once // Ensures the cache is generated only once -} - -// newCache creates a new ethash verification cache and returns it as a plain Go -// interface to be usable in an LRU cache. -func newCache(epoch uint64) interface{} { - return &cache{epoch: epoch} -} - -// generate ensures that the cache content is generated before use. -func (c *cache) generate(dir string, limit int, lock bool, test bool) { - c.once.Do(func() { - size := cacheSize(c.epoch*epochLength + 1) - seed := seedHash(c.epoch*epochLength + 1) - if test { - size = 1024 - } - // If we don't store anything on disk, generate and return. - if dir == "" { - c.cache = make([]uint32, size/4) - generateCache(c.cache, c.epoch, seed) - return - } - // Disk storage is needed, this will get fancy - var endian string - if !isLittleEndian() { - endian = ".be" - } - path := filepath.Join(dir, fmt.Sprintf("cache-R%d-%x%s", algorithmRevision, seed[:8], endian)) - logger := log.New("epoch", c.epoch) - - // We're about to mmap the file, ensure that the mapping is cleaned up when the - // cache becomes unused. - runtime.SetFinalizer(c, (*cache).finalizer) - - // Try to load the file from disk and memory map it - var err error - c.dump, c.mmap, c.cache, err = memoryMap(path, lock) - if err == nil { - logger.Debug("Loaded old ethash cache from disk") - return - } - logger.Debug("Failed to load old ethash cache", "err", err) - - // No previous cache available, create a new cache file to fill - c.dump, c.mmap, c.cache, err = memoryMapAndGenerate(path, size, lock, func(buffer []uint32) { generateCache(buffer, c.epoch, seed) }) - if err != nil { - logger.Error("Failed to generate mapped ethash cache", "err", err) - - c.cache = make([]uint32, size/4) - generateCache(c.cache, c.epoch, seed) - } - // Iterate over all previous instances and delete old ones - for ep := int(c.epoch) - limit; ep >= 0; ep-- { - seed := seedHash(uint64(ep)*epochLength + 1) - path := filepath.Join(dir, fmt.Sprintf("cache-R%d-%x%s", algorithmRevision, seed[:8], endian)) - os.Remove(path) - } - }) -} - -// finalizer unmaps the memory and closes the file. -func (c *cache) finalizer() { - if c.mmap != nil { - c.mmap.Unmap() - c.dump.Close() - c.mmap, c.dump = nil, nil - } -} - -// dataset wraps an ethash dataset with some metadata to allow easier concurrent use. -type dataset struct { - epoch uint64 // Epoch for which this cache is relevant - dump *os.File // File descriptor of the memory mapped cache - mmap mmap.MMap // Memory map itself to unmap before releasing - dataset []uint32 // The actual cache data content - once sync.Once // Ensures the cache is generated only once - done uint32 // Atomic flag to determine generation status -} - -// newDataset creates a new ethash mining dataset and returns it as a plain Go -// interface to be usable in an LRU cache. -func newDataset(epoch uint64) interface{} { - return &dataset{epoch: epoch} -} - -// generate ensures that the dataset content is generated before use. -func (d *dataset) generate(dir string, limit int, lock bool, test bool) { - d.once.Do(func() { - // Mark the dataset generated after we're done. This is needed for remote - defer atomic.StoreUint32(&d.done, 1) - - csize := cacheSize(d.epoch*epochLength + 1) - dsize := datasetSize(d.epoch*epochLength + 1) - seed := seedHash(d.epoch*epochLength + 1) - if test { - csize = 1024 - dsize = 32 * 1024 - } - // If we don't store anything on disk, generate and return - if dir == "" { - cache := make([]uint32, csize/4) - generateCache(cache, d.epoch, seed) - - d.dataset = make([]uint32, dsize/4) - generateDataset(d.dataset, d.epoch, cache) - - return - } - // Disk storage is needed, this will get fancy - var endian string - if !isLittleEndian() { - endian = ".be" - } - path := filepath.Join(dir, fmt.Sprintf("full-R%d-%x%s", algorithmRevision, seed[:8], endian)) - logger := log.New("epoch", d.epoch) - - // We're about to mmap the file, ensure that the mapping is cleaned up when the - // cache becomes unused. - runtime.SetFinalizer(d, (*dataset).finalizer) - - // Try to load the file from disk and memory map it - var err error - d.dump, d.mmap, d.dataset, err = memoryMap(path, lock) - if err == nil { - logger.Debug("Loaded old ethash dataset from disk") - return - } - logger.Debug("Failed to load old ethash dataset", "err", err) - - // No previous dataset available, create a new dataset file to fill - cache := make([]uint32, csize/4) - generateCache(cache, d.epoch, seed) - - d.dump, d.mmap, d.dataset, err = memoryMapAndGenerate(path, dsize, lock, func(buffer []uint32) { generateDataset(buffer, d.epoch, cache) }) - if err != nil { - logger.Error("Failed to generate mapped ethash dataset", "err", err) - - d.dataset = make([]uint32, dsize/2) - generateDataset(d.dataset, d.epoch, cache) - } - // Iterate over all previous instances and delete old ones - for ep := int(d.epoch) - limit; ep >= 0; ep-- { - seed := seedHash(uint64(ep)*epochLength + 1) - path := filepath.Join(dir, fmt.Sprintf("full-R%d-%x%s", algorithmRevision, seed[:8], endian)) - os.Remove(path) - } - }) -} - -// generated returns whether this particular dataset finished generating already -// or not (it may not have been started at all). This is useful for remote miners -// to default to verification caches instead of blocking on DAG generations. -func (d *dataset) generated() bool { - return atomic.LoadUint32(&d.done) == 1 -} - -// finalizer closes any file handlers and memory maps open. -func (d *dataset) finalizer() { - if d.mmap != nil { - d.mmap.Unmap() - d.dump.Close() - d.mmap, d.dump = nil, nil - } -} - -// MakeCache generates a new ethash cache and optionally stores it to disk. -func MakeCache(block uint64, dir string) { - c := cache{epoch: block / epochLength} - c.generate(dir, math.MaxInt32, false, false) -} - -// MakeDataset generates a new ethash dataset and optionally stores it to disk. -func MakeDataset(block uint64, dir string) { - d := dataset{epoch: block / epochLength} - d.generate(dir, math.MaxInt32, false, false) -} - -// Mode defines the type and amount of PoW verification an ethash engine makes. -type Mode uint - -const ( - ModeNormal Mode = iota - ModeShared - ModeTest - ModeFake - ModeFullFake -) - -// Config are the configuration parameters of the ethash. -type Config struct { - CacheDir string - CachesInMem int - CachesOnDisk int - CachesLockMmap bool - DatasetDir string - DatasetsInMem int - DatasetsOnDisk int - DatasetsLockMmap bool - PowMode Mode - - // When set, notifications sent by the remote sealer will - // be block header JSON objects instead of work package arrays. - NotifyFull bool - - Log log.Logger `toml:"-"` -} - -// Ethash is a consensus engine based on proof-of-work implementing the ethash -// algorithm. -type Ethash struct { - config Config - - caches *lru // In memory caches to avoid regenerating too often - datasets *lru // In memory datasets to avoid regenerating too often - - // Mining related fields - rand *rand.Rand // Properly seeded random source for nonces - threads int // Number of threads to mine on if mining - update chan struct{} // Notification channel to update mining parameters - hashrate metrics.Meter // Meter tracking the average hashrate - remote *remoteSealer - - // The fields below are hooks for testing - shared *Ethash // Shared PoW verifier to avoid cache regeneration - fakeFail uint64 // Block number which fails PoW check even in fake mode - fakeDelay time.Duration // Time delay to sleep for before returning from verify - - lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields - closeOnce sync.Once // Ensures exit channel will not be closed twice. -} - -// New creates a full sized ethash PoW scheme and starts a background thread for -// remote mining, also optionally notifying a batch of remote services of new work -// packages. -func New(config Config, notify []string, noverify bool) *Ethash { - if config.Log == nil { - config.Log = log.Root() - } - if config.CachesInMem <= 0 { - config.Log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem) - config.CachesInMem = 1 - } - if config.CacheDir != "" && config.CachesOnDisk > 0 { - config.Log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk) - } - if config.DatasetDir != "" && config.DatasetsOnDisk > 0 { - config.Log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk) - } - ethash := &Ethash{ - config: config, - caches: newlru("cache", config.CachesInMem, newCache), - datasets: newlru("dataset", config.DatasetsInMem, newDataset), - update: make(chan struct{}), - hashrate: metrics.NewMeterForced(), - } - if config.PowMode == ModeShared { - ethash.shared = sharedEthash - } - ethash.remote = startRemoteSealer(ethash, notify, noverify) - return ethash -} - -// NewTester creates a small sized ethash PoW scheme useful only for testing -// purposes. -func NewTester(notify []string, noverify bool) *Ethash { - return New(Config{PowMode: ModeTest}, notify, noverify) -} - -// NewFaker creates a ethash consensus engine with a fake PoW scheme that accepts -// all blocks' seal as valid, though they still have to conform to the Ethereum -// consensus rules. -func NewFaker() *Ethash { - return &Ethash{ - config: Config{ - PowMode: ModeFake, - Log: log.Root(), - }, - } -} - -// NewFakeFailer creates a ethash consensus engine with a fake PoW scheme that -// accepts all blocks as valid apart from the single one specified, though they -// still have to conform to the Ethereum consensus rules. -func NewFakeFailer(fail uint64) *Ethash { - return &Ethash{ - config: Config{ - PowMode: ModeFake, - Log: log.Root(), - }, - fakeFail: fail, - } -} - -// NewFakeDelayer creates a ethash consensus engine with a fake PoW scheme that -// accepts all blocks as valid, but delays verifications by some time, though -// they still have to conform to the Ethereum consensus rules. -func NewFakeDelayer(delay time.Duration) *Ethash { - return &Ethash{ - config: Config{ - PowMode: ModeFake, - Log: log.Root(), - }, - fakeDelay: delay, - } -} - -// NewFullFaker creates an ethash consensus engine with a full fake scheme that -// accepts all blocks as valid, without checking any consensus rules whatsoever. -func NewFullFaker() *Ethash { - return &Ethash{ - config: Config{ - PowMode: ModeFullFake, - Log: log.Root(), - }, - } -} - -// NewShared creates a full sized ethash PoW shared between all requesters running -// in the same process. -func NewShared() *Ethash { - return &Ethash{shared: sharedEthash} -} - -// Close closes the exit channel to notify all backend threads exiting. -func (ethash *Ethash) Close() error { - ethash.closeOnce.Do(func() { - // Short circuit if the exit channel is not allocated. - if ethash.remote == nil { - return - } - close(ethash.remote.requestExit) - <-ethash.remote.exitCh - }) - return nil -} - -// cache tries to retrieve a verification cache for the specified block number -// by first checking against a list of in-memory caches, then against caches -// stored on disk, and finally generating one if none can be found. -func (ethash *Ethash) cache(block uint64) *cache { - epoch := block / epochLength - currentI, futureI := ethash.caches.get(epoch) - current := currentI.(*cache) - - // Wait for generation finish. - current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.CachesLockMmap, ethash.config.PowMode == ModeTest) - - // If we need a new future cache, now's a good time to regenerate it. - if futureI != nil { - future := futureI.(*cache) - go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.CachesLockMmap, ethash.config.PowMode == ModeTest) - } - return current -} - -// dataset tries to retrieve a mining dataset for the specified block number -// by first checking against a list of in-memory datasets, then against DAGs -// stored on disk, and finally generating one if none can be found. -// -// If async is specified, not only the future but the current DAG is also -// generates on a background thread. -func (ethash *Ethash) dataset(block uint64, async bool) *dataset { - // Retrieve the requested ethash dataset - epoch := block / epochLength - currentI, futureI := ethash.datasets.get(epoch) - current := currentI.(*dataset) - - // If async is specified, generate everything in a background thread - if async && !current.generated() { - go func() { - current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest) - - if futureI != nil { - future := futureI.(*dataset) - future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest) - } - }() - } else { - // Either blocking generation was requested, or already done - current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest) - - if futureI != nil { - future := futureI.(*dataset) - go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest) - } - } - return current -} - -// Threads returns the number of mining threads currently enabled. This doesn't -// necessarily mean that mining is running! -func (ethash *Ethash) Threads() int { - ethash.lock.Lock() - defer ethash.lock.Unlock() - - return ethash.threads -} - -// SetThreads updates the number of mining threads currently enabled. Calling -// this method does not start mining, only sets the thread count. If zero is -// specified, the miner will use all cores of the machine. Setting a thread -// count below zero is allowed and will cause the miner to idle, without any -// work being done. -func (ethash *Ethash) SetThreads(threads int) { - ethash.lock.Lock() - defer ethash.lock.Unlock() - - // If we're running a shared PoW, set the thread count on that instead - if ethash.shared != nil { - ethash.shared.SetThreads(threads) - return - } - // Update the threads and ping any running seal to pull in any changes - ethash.threads = threads - select { - case ethash.update <- struct{}{}: - default: - } -} - -// Hashrate implements PoW, returning the measured rate of the search invocations -// per second over the last minute. -// Note the returned hashrate includes local hashrate, but also includes the total -// hashrate of all remote miner. -func (ethash *Ethash) Hashrate() float64 { - // Short circuit if we are run the ethash in normal/test mode. - if ethash.config.PowMode != ModeNormal && ethash.config.PowMode != ModeTest { - return ethash.hashrate.Rate1() - } - var res = make(chan uint64, 1) - - select { - case ethash.remote.fetchRateCh <- res: - case <-ethash.remote.exitCh: - // Return local hashrate only if ethash is stopped. - return ethash.hashrate.Rate1() - } - - // Gather total submitted hash rate of remote sealers. - return ethash.hashrate.Rate1() + float64(<-res) -} - -// APIs implements consensus.Engine, returning the user facing RPC APIs. -func (ethash *Ethash) APIs(chain consensus.ChainHeaderReader) []rpc.API { - // In order to ensure backward compatibility, we exposes ethash RPC APIs - // to both eth and ethash namespaces. - return []rpc.API{ - { - Namespace: "eth", - Version: "1.0", - Service: &API{ethash}, - Public: true, - }, - { - Namespace: "ethash", - Version: "1.0", - Service: &API{ethash}, - Public: true, - }, - } -} - -// SeedHash is the seed to use for generating a verification cache and the mining -// dataset. -func SeedHash(block uint64) []byte { - return seedHash(block) -}
diff --git go-ethereum/consensus/ethash/algorithm.go celo/consensus/ethash/algorithm.go deleted file mode 100644 index 6502686ba127eec1397a04dcfb0d541f3535e124..0000000000000000000000000000000000000000 --- go-ethereum/consensus/ethash/algorithm.go +++ /dev/null @@ -1,1152 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package ethash - -import ( - "encoding/binary" - "hash" - "math/big" - "reflect" - "runtime" - "sync" - "sync/atomic" - "time" - "unsafe" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/bitutil" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "golang.org/x/crypto/sha3" -) - -const ( - datasetInitBytes = 1 << 30 // Bytes in dataset at genesis - datasetGrowthBytes = 1 << 23 // Dataset growth per epoch - cacheInitBytes = 1 << 24 // Bytes in cache at genesis - cacheGrowthBytes = 1 << 17 // Cache growth per epoch - epochLength = 30000 // Blocks per epoch - mixBytes = 128 // Width of mix - hashBytes = 64 // Hash length in bytes - hashWords = 16 // Number of 32 bit ints in a hash - datasetParents = 256 // Number of parents of each dataset element - cacheRounds = 3 // Number of rounds in cache production - loopAccesses = 64 // Number of accesses in hashimoto loop -) - -// cacheSize returns the size of the ethash verification cache that belongs to a certain -// block number. -func cacheSize(block uint64) uint64 { - epoch := int(block / epochLength) - if epoch < maxEpoch { - return cacheSizes[epoch] - } - return calcCacheSize(epoch) -} - -// calcCacheSize calculates the cache size for epoch. The cache size grows linearly, -// however, we always take the highest prime below the linearly growing threshold in order -// to reduce the risk of accidental regularities leading to cyclic behavior. -func calcCacheSize(epoch int) uint64 { - size := cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes - for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64 - size -= 2 * hashBytes - } - return size -} - -// datasetSize returns the size of the ethash mining dataset that belongs to a certain -// block number. -func datasetSize(block uint64) uint64 { - epoch := int(block / epochLength) - if epoch < maxEpoch { - return datasetSizes[epoch] - } - return calcDatasetSize(epoch) -} - -// calcDatasetSize calculates the dataset size for epoch. The dataset size grows linearly, -// however, we always take the highest prime below the linearly growing threshold in order -// to reduce the risk of accidental regularities leading to cyclic behavior. -func calcDatasetSize(epoch int) uint64 { - size := datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes - for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64 - size -= 2 * mixBytes - } - return size -} - -// hasher is a repetitive hasher allowing the same hash data structures to be -// reused between hash runs instead of requiring new ones to be created. -type hasher func(dest []byte, data []byte) - -// makeHasher creates a repetitive hasher, allowing the same hash data structures to -// be reused between hash runs instead of requiring new ones to be created. The returned -// function is not thread safe! -func makeHasher(h hash.Hash) hasher { - // sha3.state supports Read to get the sum, use it to avoid the overhead of Sum. - // Read alters the state but we reset the hash before every operation. - type readerHash interface { - hash.Hash - Read([]byte) (int, error) - } - rh, ok := h.(readerHash) - if !ok { - panic("can't find Read method on hash") - } - outputLen := rh.Size() - return func(dest []byte, data []byte) { - rh.Reset() - rh.Write(data) - rh.Read(dest[:outputLen]) - } -} - -// seedHash is the seed to use for generating a verification cache and the mining -// dataset. -func seedHash(block uint64) []byte { - seed := make([]byte, 32) - if block < epochLength { - return seed - } - keccak256 := makeHasher(sha3.NewLegacyKeccak256()) - for i := 0; i < int(block/epochLength); i++ { - keccak256(seed, seed) - } - return seed -} - -// generateCache creates a verification cache of a given size for an input seed. -// The cache production process involves first sequentially filling up 32 MB of -// memory, then performing two passes of Sergio Demian Lerner's RandMemoHash -// algorithm from Strict Memory Hard Hashing Functions (2014). The output is a -// set of 524288 64-byte values. -// This method places the result into dest in machine byte order. -func generateCache(dest []uint32, epoch uint64, seed []byte) { - // Print some debug logs to allow analysis on low end devices - logger := log.New("epoch", epoch) - - start := time.Now() - defer func() { - elapsed := time.Since(start) - - logFn := logger.Debug - if elapsed > 3*time.Second { - logFn = logger.Info - } - logFn("Generated ethash verification cache", "elapsed", common.PrettyDuration(elapsed)) - }() - // Convert our destination slice to a byte buffer - var cache []byte - cacheHdr := (*reflect.SliceHeader)(unsafe.Pointer(&cache)) - dstHdr := (*reflect.SliceHeader)(unsafe.Pointer(&dest)) - cacheHdr.Data = dstHdr.Data - cacheHdr.Len = dstHdr.Len * 4 - cacheHdr.Cap = dstHdr.Cap * 4 - - // Calculate the number of theoretical rows (we'll store in one buffer nonetheless) - size := uint64(len(cache)) - rows := int(size) / hashBytes - - // Start a monitoring goroutine to report progress on low end devices - var progress uint32 - - done := make(chan struct{}) - defer close(done) - - go func() { - for { - select { - case <-done: - return - case <-time.After(3 * time.Second): - logger.Info("Generating ethash verification cache", "percentage", atomic.LoadUint32(&progress)*100/uint32(rows)/(cacheRounds+1), "elapsed", common.PrettyDuration(time.Since(start))) - } - } - }() - // Create a hasher to reuse between invocations - keccak512 := makeHasher(sha3.NewLegacyKeccak512()) - - // Sequentially produce the initial dataset - keccak512(cache, seed) - for offset := uint64(hashBytes); offset < size; offset += hashBytes { - keccak512(cache[offset:], cache[offset-hashBytes:offset]) - atomic.AddUint32(&progress, 1) - } - // Use a low-round version of randmemohash - temp := make([]byte, hashBytes) - - for i := 0; i < cacheRounds; i++ { - for j := 0; j < rows; j++ { - var ( - srcOff = ((j - 1 + rows) % rows) * hashBytes - dstOff = j * hashBytes - xorOff = (binary.LittleEndian.Uint32(cache[dstOff:]) % uint32(rows)) * hashBytes - ) - bitutil.XORBytes(temp, cache[srcOff:srcOff+hashBytes], cache[xorOff:xorOff+hashBytes]) - keccak512(cache[dstOff:], temp) - - atomic.AddUint32(&progress, 1) - } - } - // Swap the byte order on big endian systems and return - if !isLittleEndian() { - swap(cache) - } -} - -// swap changes the byte order of the buffer assuming a uint32 representation. -func swap(buffer []byte) { - for i := 0; i < len(buffer); i += 4 { - binary.BigEndian.PutUint32(buffer[i:], binary.LittleEndian.Uint32(buffer[i:])) - } -} - -// fnv is an algorithm inspired by the FNV hash, which in some cases is used as -// a non-associative substitute for XOR. Note that we multiply the prime with -// the full 32-bit input, in contrast with the FNV-1 spec which multiplies the -// prime with one byte (octet) in turn. -func fnv(a, b uint32) uint32 { - return a*0x01000193 ^ b -} - -// fnvHash mixes in data into mix using the ethash fnv method. -func fnvHash(mix []uint32, data []uint32) { - for i := 0; i < len(mix); i++ { - mix[i] = mix[i]*0x01000193 ^ data[i] - } -} - -// generateDatasetItem combines data from 256 pseudorandomly selected cache nodes, -// and hashes that to compute a single dataset node. -func generateDatasetItem(cache []uint32, index uint32, keccak512 hasher) []byte { - // Calculate the number of theoretical rows (we use one buffer nonetheless) - rows := uint32(len(cache) / hashWords) - - // Initialize the mix - mix := make([]byte, hashBytes) - - binary.LittleEndian.PutUint32(mix, cache[(index%rows)*hashWords]^index) - for i := 1; i < hashWords; i++ { - binary.LittleEndian.PutUint32(mix[i*4:], cache[(index%rows)*hashWords+uint32(i)]) - } - keccak512(mix, mix) - - // Convert the mix to uint32s to avoid constant bit shifting - intMix := make([]uint32, hashWords) - for i := 0; i < len(intMix); i++ { - intMix[i] = binary.LittleEndian.Uint32(mix[i*4:]) - } - // fnv it with a lot of random cache nodes based on index - for i := uint32(0); i < datasetParents; i++ { - parent := fnv(index^i, intMix[i%16]) % rows - fnvHash(intMix, cache[parent*hashWords:]) - } - // Flatten the uint32 mix into a binary one and return - for i, val := range intMix { - binary.LittleEndian.PutUint32(mix[i*4:], val) - } - keccak512(mix, mix) - return mix -} - -// generateDataset generates the entire ethash dataset for mining. -// This method places the result into dest in machine byte order. -func generateDataset(dest []uint32, epoch uint64, cache []uint32) { - // Print some debug logs to allow analysis on low end devices - logger := log.New("epoch", epoch) - - start := time.Now() - defer func() { - elapsed := time.Since(start) - - logFn := logger.Debug - if elapsed > 3*time.Second { - logFn = logger.Info - } - logFn("Generated ethash verification cache", "elapsed", common.PrettyDuration(elapsed)) - }() - - // Figure out whether the bytes need to be swapped for the machine - swapped := !isLittleEndian() - - // Convert our destination slice to a byte buffer - var dataset []byte - datasetHdr := (*reflect.SliceHeader)(unsafe.Pointer(&dataset)) - destHdr := (*reflect.SliceHeader)(unsafe.Pointer(&dest)) - datasetHdr.Data = destHdr.Data - datasetHdr.Len = destHdr.Len * 4 - datasetHdr.Cap = destHdr.Cap * 4 - - // Generate the dataset on many goroutines since it takes a while - threads := runtime.NumCPU() - size := uint64(len(dataset)) - - var pend sync.WaitGroup - pend.Add(threads) - - var progress uint64 - for i := 0; i < threads; i++ { - go func(id int) { - defer pend.Done() - - // Create a hasher to reuse between invocations - keccak512 := makeHasher(sha3.NewLegacyKeccak512()) - - // Calculate the data segment this thread should generate - batch := (size + hashBytes*uint64(threads) - 1) / (hashBytes * uint64(threads)) - first := uint64(id) * batch - limit := first + batch - if limit > size/hashBytes { - limit = size / hashBytes - } - // Calculate the dataset segment - percent := size / hashBytes / 100 - for index := first; index < limit; index++ { - item := generateDatasetItem(cache, uint32(index), keccak512) - if swapped { - swap(item) - } - copy(dataset[index*hashBytes:], item) - - if status := atomic.AddUint64(&progress, 1); status%percent == 0 { - logger.Info("Generating DAG in progress", "percentage", (status*100)/(size/hashBytes), "elapsed", common.PrettyDuration(time.Since(start))) - } - } - }(i) - } - // Wait for all the generators to finish and return - pend.Wait() -} - -// hashimoto aggregates data from the full dataset in order to produce our final -// value for a particular header hash and nonce. -func hashimoto(hash []byte, nonce uint64, size uint64, lookup func(index uint32) []uint32) ([]byte, []byte) { - // Calculate the number of theoretical rows (we use one buffer nonetheless) - rows := uint32(size / mixBytes) - - // Combine header+nonce into a 64 byte seed - seed := make([]byte, 40) - copy(seed, hash) - binary.LittleEndian.PutUint64(seed[32:], nonce) - - seed = crypto.Keccak512(seed) - seedHead := binary.LittleEndian.Uint32(seed) - - // Start the mix with replicated seed - mix := make([]uint32, mixBytes/4) - for i := 0; i < len(mix); i++ { - mix[i] = binary.LittleEndian.Uint32(seed[i%16*4:]) - } - // Mix in random dataset nodes - temp := make([]uint32, len(mix)) - - for i := 0; i < loopAccesses; i++ { - parent := fnv(uint32(i)^seedHead, mix[i%len(mix)]) % rows - for j := uint32(0); j < mixBytes/hashBytes; j++ { - copy(temp[j*hashWords:], lookup(2*parent+j)) - } - fnvHash(mix, temp) - } - // Compress mix - for i := 0; i < len(mix); i += 4 { - mix[i/4] = fnv(fnv(fnv(mix[i], mix[i+1]), mix[i+2]), mix[i+3]) - } - mix = mix[:len(mix)/4] - - digest := make([]byte, common.HashLength) - for i, val := range mix { - binary.LittleEndian.PutUint32(digest[i*4:], val) - } - return digest, crypto.Keccak256(append(seed, digest...)) -} - -// hashimotoLight aggregates data from the full dataset (using only a small -// in-memory cache) in order to produce our final value for a particular header -// hash and nonce. -func hashimotoLight(size uint64, cache []uint32, hash []byte, nonce uint64) ([]byte, []byte) { - keccak512 := makeHasher(sha3.NewLegacyKeccak512()) - - lookup := func(index uint32) []uint32 { - rawData := generateDatasetItem(cache, index, keccak512) - - data := make([]uint32, len(rawData)/4) - for i := 0; i < len(data); i++ { - data[i] = binary.LittleEndian.Uint32(rawData[i*4:]) - } - return data - } - return hashimoto(hash, nonce, size, lookup) -} - -// hashimotoFull aggregates data from the full dataset (using the full in-memory -// dataset) in order to produce our final value for a particular header hash and -// nonce. -func hashimotoFull(dataset []uint32, hash []byte, nonce uint64) ([]byte, []byte) { - lookup := func(index uint32) []uint32 { - offset := index * hashWords - return dataset[offset : offset+hashWords] - } - return hashimoto(hash, nonce, uint64(len(dataset))*4, lookup) -} - -const maxEpoch = 2048 - -// datasetSizes is a lookup table for the ethash dataset size for the first 2048 -// epochs (i.e. 61440000 blocks). -var datasetSizes = [maxEpoch]uint64{ - 1073739904, 1082130304, 1090514816, 1098906752, 1107293056, - 1115684224, 1124070016, 1132461952, 1140849536, 1149232768, - 1157627776, 1166013824, 1174404736, 1182786944, 1191180416, - 1199568512, 1207958912, 1216345216, 1224732032, 1233124736, - 1241513344, 1249902464, 1258290304, 1266673792, 1275067264, - 1283453312, 1291844992, 1300234112, 1308619904, 1317010048, - 1325397376, 1333787776, 1342176128, 1350561664, 1358954368, - 1367339392, 1375731584, 1384118144, 1392507008, 1400897408, - 1409284736, 1417673344, 1426062464, 1434451072, 1442839168, - 1451229056, 1459615616, 1468006016, 1476394112, 1484782976, - 1493171584, 1501559168, 1509948032, 1518337664, 1526726528, - 1535114624, 1543503488, 1551892096, 1560278656, 1568669056, - 1577056384, 1585446272, 1593831296, 1602219392, 1610610304, - 1619000192, 1627386752, 1635773824, 1644164224, 1652555648, - 1660943488, 1669332608, 1677721216, 1686109312, 1694497664, - 1702886272, 1711274624, 1719661184, 1728047744, 1736434816, - 1744829056, 1753218944, 1761606272, 1769995904, 1778382464, - 1786772864, 1795157888, 1803550592, 1811937664, 1820327552, - 1828711552, 1837102976, 1845488768, 1853879936, 1862269312, - 1870656896, 1879048064, 1887431552, 1895825024, 1904212096, - 1912601216, 1920988544, 1929379456, 1937765504, 1946156672, - 1954543232, 1962932096, 1971321728, 1979707264, 1988093056, - 1996487552, 2004874624, 2013262208, 2021653888, 2030039936, - 2038430848, 2046819968, 2055208576, 2063596672, 2071981952, - 2080373632, 2088762752, 2097149056, 2105539712, 2113928576, - 2122315136, 2130700672, 2139092608, 2147483264, 2155872128, - 2164257664, 2172642176, 2181035392, 2189426048, 2197814912, - 2206203008, 2214587264, 2222979712, 2231367808, 2239758208, - 2248145024, 2256527744, 2264922752, 2273312128, 2281701248, - 2290086272, 2298476672, 2306867072, 2315251072, 2323639168, - 2332032128, 2340420224, 2348808064, 2357196416, 2365580416, - 2373966976, 2382363008, 2390748544, 2399139968, 2407530368, - 2415918976, 2424307328, 2432695424, 2441084288, 2449472384, - 2457861248, 2466247808, 2474637184, 2483026816, 2491414144, - 2499803776, 2508191872, 2516582272, 2524970368, 2533359232, - 2541743488, 2550134144, 2558525056, 2566913408, 2575301504, - 2583686528, 2592073856, 2600467328, 2608856192, 2617240448, - 2625631616, 2634022016, 2642407552, 2650796416, 2659188352, - 2667574912, 2675965312, 2684352896, 2692738688, 2701130624, - 2709518464, 2717907328, 2726293376, 2734685056, 2743073152, - 2751462016, 2759851648, 2768232832, 2776625536, 2785017728, - 2793401984, 2801794432, 2810182016, 2818571648, 2826959488, - 2835349376, 2843734144, 2852121472, 2860514432, 2868900992, - 2877286784, 2885676928, 2894069632, 2902451584, 2910843008, - 2919234688, 2927622784, 2936011648, 2944400768, 2952789376, - 2961177728, 2969565568, 2977951616, 2986338944, 2994731392, - 3003120256, 3011508352, 3019895936, 3028287104, 3036675968, - 3045063808, 3053452928, 3061837696, 3070228352, 3078615424, - 3087003776, 3095394944, 3103782272, 3112173184, 3120562048, - 3128944768, 3137339264, 3145725056, 3154109312, 3162505088, - 3170893184, 3179280256, 3187669376, 3196056704, 3204445568, - 3212836736, 3221224064, 3229612928, 3238002304, 3246391168, - 3254778496, 3263165824, 3271556224, 3279944576, 3288332416, - 3296719232, 3305110912, 3313500032, 3321887104, 3330273152, - 3338658944, 3347053184, 3355440512, 3363827072, 3372220288, - 3380608384, 3388997504, 3397384576, 3405774208, 3414163072, - 3422551936, 3430937984, 3439328384, 3447714176, 3456104576, - 3464493952, 3472883584, 3481268864, 3489655168, 3498048896, - 3506434432, 3514826368, 3523213952, 3531603584, 3539987072, - 3548380288, 3556763264, 3565157248, 3573545344, 3581934464, - 3590324096, 3598712704, 3607098752, 3615488384, 3623877248, - 3632265856, 3640646528, 3649043584, 3657430144, 3665821568, - 3674207872, 3682597504, 3690984832, 3699367808, 3707764352, - 3716152448, 3724541056, 3732925568, 3741318016, 3749706368, - 3758091136, 3766481536, 3774872704, 3783260032, 3791650432, - 3800036224, 3808427648, 3816815488, 3825204608, 3833592704, - 3841981568, 3850370432, 3858755968, 3867147904, 3875536256, - 3883920512, 3892313728, 3900702592, 3909087872, 3917478784, - 3925868416, 3934256512, 3942645376, 3951032192, 3959422336, - 3967809152, 3976200064, 3984588416, 3992974976, 4001363584, - 4009751168, 4018141312, 4026530432, 4034911616, 4043308928, - 4051695488, 4060084352, 4068472448, 4076862848, 4085249408, - 4093640576, 4102028416, 4110413696, 4118805632, 4127194496, - 4135583104, 4143971968, 4152360832, 4160746112, 4169135744, - 4177525888, 4185912704, 4194303616, 4202691968, 4211076736, - 4219463552, 4227855488, 4236246656, 4244633728, 4253022848, - 4261412224, 4269799808, 4278184832, 4286578048, 4294962304, - 4303349632, 4311743104, 4320130432, 4328521088, 4336909184, - 4345295488, 4353687424, 4362073472, 4370458496, 4378852736, - 4387238528, 4395630208, 4404019072, 4412407424, 4420790656, - 4429182848, 4437571456, 4445962112, 4454344064, 4462738048, - 4471119232, 4479516544, 4487904128, 4496289664, 4504682368, - 4513068416, 4521459584, 4529846144, 4538232704, 4546619776, - 4555010176, 4563402112, 4571790208, 4580174464, 4588567936, - 4596957056, 4605344896, 4613734016, 4622119808, 4630511488, - 4638898816, 4647287936, 4655675264, 4664065664, 4672451968, - 4680842624, 4689231488, 4697620352, 4706007424, 4714397056, - 4722786176, 4731173248, 4739562368, 4747951744, 4756340608, - 4764727936, 4773114496, 4781504384, 4789894784, 4798283648, - 4806667648, 4815059584, 4823449472, 4831835776, 4840226176, - 4848612224, 4857003392, 4865391488, 4873780096, 4882169728, - 4890557312, 4898946944, 4907333248, 4915722368, 4924110976, - 4932499328, 4940889728, 4949276032, 4957666432, 4966054784, - 4974438016, 4982831488, 4991221376, 4999607168, 5007998848, - 5016386432, 5024763776, 5033164672, 5041544576, 5049941888, - 5058329728, 5066717056, 5075107456, 5083494272, 5091883904, - 5100273536, 5108662144, 5117048192, 5125436032, 5133827456, - 5142215296, 5150605184, 5158993024, 5167382144, 5175769472, - 5184157568, 5192543872, 5200936064, 5209324928, 5217711232, - 5226102656, 5234490496, 5242877312, 5251263872, 5259654016, - 5268040832, 5276434304, 5284819328, 5293209728, 5301598592, - 5309986688, 5318374784, 5326764416, 5335151488, 5343542144, - 5351929472, 5360319872, 5368706944, 5377096576, 5385484928, - 5393871232, 5402263424, 5410650496, 5419040384, 5427426944, - 5435816576, 5444205952, 5452594816, 5460981376, 5469367936, - 5477760896, 5486148736, 5494536832, 5502925952, 5511315328, - 5519703424, 5528089984, 5536481152, 5544869504, 5553256064, - 5561645696, 5570032768, 5578423936, 5586811264, 5595193216, - 5603585408, 5611972736, 5620366208, 5628750464, 5637143936, - 5645528192, 5653921408, 5662310272, 5670694784, 5679082624, - 5687474048, 5695864448, 5704251008, 5712641408, 5721030272, - 5729416832, 5737806208, 5746194304, 5754583936, 5762969984, - 5771358592, 5779748224, 5788137856, 5796527488, 5804911232, - 5813300608, 5821692544, 5830082176, 5838468992, 5846855552, - 5855247488, 5863636096, 5872024448, 5880411008, 5888799872, - 5897186432, 5905576832, 5913966976, 5922352768, 5930744704, - 5939132288, 5947522432, 5955911296, 5964299392, 5972688256, - 5981074304, 5989465472, 5997851008, 6006241408, 6014627968, - 6023015552, 6031408256, 6039796096, 6048185216, 6056574848, - 6064963456, 6073351808, 6081736064, 6090128768, 6098517632, - 6106906496, 6115289216, 6123680896, 6132070016, 6140459648, - 6148849024, 6157237376, 6165624704, 6174009728, 6182403712, - 6190792064, 6199176064, 6207569792, 6215952256, 6224345216, - 6232732544, 6241124224, 6249510272, 6257899136, 6266287744, - 6274676864, 6283065728, 6291454336, 6299843456, 6308232064, - 6316620928, 6325006208, 6333395584, 6341784704, 6350174848, - 6358562176, 6366951296, 6375337856, 6383729536, 6392119168, - 6400504192, 6408895616, 6417283456, 6425673344, 6434059136, - 6442444672, 6450837376, 6459223424, 6467613056, 6476004224, - 6484393088, 6492781952, 6501170048, 6509555072, 6517947008, - 6526336384, 6534725504, 6543112832, 6551500672, 6559888768, - 6568278656, 6576662912, 6585055616, 6593443456, 6601834112, - 6610219648, 6618610304, 6626999168, 6635385472, 6643777408, - 6652164224, 6660552832, 6668941952, 6677330048, 6685719424, - 6694107776, 6702493568, 6710882176, 6719274112, 6727662976, - 6736052096, 6744437632, 6752825984, 6761213824, 6769604224, - 6777993856, 6786383488, 6794770816, 6803158144, 6811549312, - 6819937664, 6828326528, 6836706176, 6845101696, 6853491328, - 6861880448, 6870269312, 6878655104, 6887046272, 6895433344, - 6903822208, 6912212864, 6920596864, 6928988288, 6937377152, - 6945764992, 6954149248, 6962544256, 6970928768, 6979317376, - 6987709312, 6996093824, 7004487296, 7012875392, 7021258624, - 7029652352, 7038038912, 7046427776, 7054818944, 7063207808, - 7071595136, 7079980928, 7088372608, 7096759424, 7105149824, - 7113536896, 7121928064, 7130315392, 7138699648, 7147092352, - 7155479168, 7163865728, 7172249984, 7180648064, 7189036672, - 7197424768, 7205810816, 7214196608, 7222589824, 7230975104, - 7239367552, 7247755904, 7256145536, 7264533376, 7272921472, - 7281308032, 7289694848, 7298088832, 7306471808, 7314864512, - 7323253888, 7331643008, 7340029568, 7348419712, 7356808832, - 7365196672, 7373585792, 7381973888, 7390362752, 7398750592, - 7407138944, 7415528576, 7423915648, 7432302208, 7440690304, - 7449080192, 7457472128, 7465860992, 7474249088, 7482635648, - 7491023744, 7499412608, 7507803008, 7516192384, 7524579968, - 7532967296, 7541358464, 7549745792, 7558134656, 7566524032, - 7574912896, 7583300992, 7591690112, 7600075136, 7608466816, - 7616854912, 7625244544, 7633629824, 7642020992, 7650410368, - 7658794112, 7667187328, 7675574912, 7683961984, 7692349568, - 7700739712, 7709130368, 7717519232, 7725905536, 7734295424, - 7742683264, 7751069056, 7759457408, 7767849088, 7776238208, - 7784626816, 7793014912, 7801405312, 7809792128, 7818179968, - 7826571136, 7834957184, 7843347328, 7851732352, 7860124544, - 7868512384, 7876902016, 7885287808, 7893679744, 7902067072, - 7910455936, 7918844288, 7927230848, 7935622784, 7944009344, - 7952400256, 7960786048, 7969176704, 7977565312, 7985953408, - 7994339968, 8002730368, 8011119488, 8019508096, 8027896192, - 8036285056, 8044674688, 8053062272, 8061448832, 8069838464, - 8078227328, 8086616704, 8095006592, 8103393664, 8111783552, - 8120171392, 8128560256, 8136949376, 8145336704, 8153726848, - 8162114944, 8170503296, 8178891904, 8187280768, 8195669632, - 8204058496, 8212444544, 8220834176, 8229222272, 8237612672, - 8246000768, 8254389376, 8262775168, 8271167104, 8279553664, - 8287944064, 8296333184, 8304715136, 8313108352, 8321497984, - 8329885568, 8338274432, 8346663296, 8355052928, 8363441536, - 8371828352, 8380217984, 8388606592, 8396996224, 8405384576, - 8413772672, 8422161536, 8430549376, 8438939008, 8447326592, - 8455715456, 8464104832, 8472492928, 8480882048, 8489270656, - 8497659776, 8506045312, 8514434944, 8522823808, 8531208832, - 8539602304, 8547990656, 8556378752, 8564768384, 8573154176, - 8581542784, 8589933952, 8598322816, 8606705024, 8615099264, - 8623487872, 8631876992, 8640264064, 8648653952, 8657040256, - 8665430656, 8673820544, 8682209152, 8690592128, 8698977152, - 8707374464, 8715763328, 8724151424, 8732540032, 8740928384, - 8749315712, 8757704576, 8766089344, 8774480768, 8782871936, - 8791260032, 8799645824, 8808034432, 8816426368, 8824812928, - 8833199488, 8841591424, 8849976448, 8858366336, 8866757248, - 8875147136, 8883532928, 8891923328, 8900306816, 8908700288, - 8917088384, 8925478784, 8933867392, 8942250368, 8950644608, - 8959032704, 8967420544, 8975809664, 8984197504, 8992584064, - 9000976256, 9009362048, 9017752448, 9026141312, 9034530688, - 9042917504, 9051307904, 9059694208, 9068084864, 9076471424, - 9084861824, 9093250688, 9101638528, 9110027648, 9118416512, - 9126803584, 9135188096, 9143581312, 9151969664, 9160356224, - 9168747136, 9177134464, 9185525632, 9193910144, 9202302848, - 9210690688, 9219079552, 9227465344, 9235854464, 9244244864, - 9252633472, 9261021824, 9269411456, 9277799296, 9286188928, - 9294574208, 9302965888, 9311351936, 9319740032, 9328131968, - 9336516736, 9344907392, 9353296768, 9361685888, 9370074752, - 9378463616, 9386849408, 9395239808, 9403629184, 9412016512, - 9420405376, 9428795008, 9437181568, 9445570688, 9453960832, - 9462346624, 9470738048, 9479121536, 9487515008, 9495903616, - 9504289664, 9512678528, 9521067904, 9529456256, 9537843584, - 9546233728, 9554621312, 9563011456, 9571398784, 9579788672, - 9588178304, 9596567168, 9604954496, 9613343104, 9621732992, - 9630121856, 9638508416, 9646898816, 9655283584, 9663675776, - 9672061312, 9680449664, 9688840064, 9697230464, 9705617536, - 9714003584, 9722393984, 9730772608, 9739172224, 9747561088, - 9755945344, 9764338816, 9772726144, 9781116544, 9789503872, - 9797892992, 9806282624, 9814670464, 9823056512, 9831439232, - 9839833984, 9848224384, 9856613504, 9865000576, 9873391232, - 9881772416, 9890162816, 9898556288, 9906940544, 9915333248, - 9923721088, 9932108672, 9940496512, 9948888448, 9957276544, - 9965666176, 9974048384, 9982441088, 9990830464, 9999219584, - 10007602816, 10015996544, 10024385152, 10032774016, 10041163648, - 10049548928, 10057940096, 10066329472, 10074717824, 10083105152, - 10091495296, 10099878784, 10108272256, 10116660608, 10125049216, - 10133437312, 10141825664, 10150213504, 10158601088, 10166991232, - 10175378816, 10183766144, 10192157312, 10200545408, 10208935552, - 10217322112, 10225712768, 10234099328, 10242489472, 10250876032, - 10259264896, 10267656064, 10276042624, 10284429184, 10292820352, - 10301209472, 10309598848, 10317987712, 10326375296, 10334763392, - 10343153536, 10351541632, 10359930752, 10368318592, 10376707456, - 10385096576, 10393484672, 10401867136, 10410262144, 10418647424, - 10427039104, 10435425664, 10443810176, 10452203648, 10460589952, - 10468982144, 10477369472, 10485759104, 10494147712, 10502533504, - 10510923392, 10519313536, 10527702656, 10536091264, 10544478592, - 10552867712, 10561255808, 10569642368, 10578032768, 10586423168, - 10594805632, 10603200128, 10611588992, 10619976064, 10628361344, - 10636754048, 10645143424, 10653531776, 10661920384, 10670307968, - 10678696832, 10687086464, 10695475072, 10703863168, 10712246144, - 10720639616, 10729026688, 10737414784, 10745806208, 10754190976, - 10762581376, 10770971264, 10779356288, 10787747456, 10796135552, - 10804525184, 10812915584, 10821301888, 10829692288, 10838078336, - 10846469248, 10854858368, 10863247232, 10871631488, 10880023424, - 10888412032, 10896799616, 10905188992, 10913574016, 10921964672, - 10930352768, 10938742912, 10947132544, 10955518592, 10963909504, - 10972298368, 10980687488, 10989074816, 10997462912, 11005851776, - 11014241152, 11022627712, 11031017344, 11039403904, 11047793024, - 11056184704, 11064570752, 11072960896, 11081343872, 11089737856, - 11098128256, 11106514816, 11114904448, 11123293568, 11131680128, - 11140065152, 11148458368, 11156845696, 11165236864, 11173624192, - 11182013824, 11190402688, 11198790784, 11207179136, 11215568768, - 11223957376, 11232345728, 11240734592, 11249122688, 11257511296, - 11265899648, 11274285952, 11282675584, 11291065472, 11299452544, - 11307842432, 11316231296, 11324616832, 11333009024, 11341395584, - 11349782656, 11358172288, 11366560384, 11374950016, 11383339648, - 11391721856, 11400117376, 11408504192, 11416893568, 11425283456, - 11433671552, 11442061184, 11450444672, 11458837888, 11467226752, - 11475611776, 11484003968, 11492392064, 11500780672, 11509169024, - 11517550976, 11525944448, 11534335616, 11542724224, 11551111808, - 11559500672, 11567890304, 11576277376, 11584667008, 11593056128, - 11601443456, 11609830016, 11618221952, 11626607488, 11634995072, - 11643387776, 11651775104, 11660161664, 11668552576, 11676940928, - 11685330304, 11693718656, 11702106496, 11710496128, 11718882688, - 11727273088, 11735660416, 11744050048, 11752437376, 11760824704, - 11769216128, 11777604736, 11785991296, 11794381952, 11802770048, - 11811157888, 11819548544, 11827932544, 11836324736, 11844713344, - 11853100928, 11861486464, 11869879936, 11878268032, 11886656896, - 11895044992, 11903433088, 11911822976, 11920210816, 11928600448, - 11936987264, 11945375872, 11953761152, 11962151296, 11970543488, - 11978928512, 11987320448, 11995708288, 12004095104, 12012486272, - 12020875136, 12029255552, 12037652096, 12046039168, 12054429568, - 12062813824, 12071206528, 12079594624, 12087983744, 12096371072, - 12104759936, 12113147264, 12121534592, 12129924992, 12138314624, - 12146703232, 12155091584, 12163481216, 12171864704, 12180255872, - 12188643968, 12197034112, 12205424512, 12213811328, 12222199424, - 12230590336, 12238977664, 12247365248, 12255755392, 12264143488, - 12272531584, 12280920448, 12289309568, 12297694592, 12306086528, - 12314475392, 12322865024, 12331253632, 12339640448, 12348029312, - 12356418944, 12364805248, 12373196672, 12381580928, 12389969024, - 12398357632, 12406750592, 12415138432, 12423527552, 12431916416, - 12440304512, 12448692352, 12457081216, 12465467776, 12473859968, - 12482245504, 12490636672, 12499025536, 12507411584, 12515801728, - 12524190592, 12532577152, 12540966272, 12549354368, 12557743232, - 12566129536, 12574523264, 12582911872, 12591299456, 12599688064, - 12608074624, 12616463488, 12624845696, 12633239936, 12641631616, - 12650019968, 12658407296, 12666795136, 12675183232, 12683574656, - 12691960192, 12700350592, 12708740224, 12717128576, 12725515904, - 12733906816, 12742295168, 12750680192, 12759071872, 12767460736, - 12775848832, 12784236928, 12792626816, 12801014656, 12809404288, - 12817789312, 12826181504, 12834568832, 12842954624, 12851345792, - 12859732352, 12868122496, 12876512128, 12884901248, 12893289088, - 12901672832, 12910067584, 12918455168, 12926842496, 12935232896, - 12943620736, 12952009856, 12960396928, 12968786816, 12977176192, - 12985563776, 12993951104, 13002341504, 13010730368, 13019115392, - 13027506304, 13035895168, 13044272512, 13052673152, 13061062528, - 13069446272, 13077838976, 13086227072, 13094613632, 13103000192, - 13111393664, 13119782528, 13128157568, 13136559232, 13144945024, - 13153329536, 13161724288, 13170111872, 13178502784, 13186884736, - 13195279744, 13203667072, 13212057472, 13220445824, 13228832128, - 13237221248, 13245610624, 13254000512, 13262388352, 13270777472, - 13279166336, 13287553408, 13295943296, 13304331904, 13312719488, - 13321108096, 13329494656, 13337885824, 13346274944, 13354663808, - 13363051136, 13371439232, 13379825024, 13388210816, 13396605056, - 13404995456, 13413380224, 13421771392, 13430159744, 13438546048, - 13446937216, 13455326848, 13463708288, 13472103808, 13480492672, - 13488875648, 13497269888, 13505657728, 13514045312, 13522435712, - 13530824576, 13539210112, 13547599232, 13555989376, 13564379008, - 13572766336, 13581154432, 13589544832, 13597932928, 13606320512, - 13614710656, 13623097472, 13631477632, 13639874944, 13648264064, - 13656652928, 13665041792, 13673430656, 13681818496, 13690207616, - 13698595712, 13706982272, 13715373184, 13723762048, 13732150144, - 13740536704, 13748926592, 13757316224, 13765700992, 13774090112, - 13782477952, 13790869376, 13799259008, 13807647872, 13816036736, - 13824425344, 13832814208, 13841202304, 13849591424, 13857978752, - 13866368896, 13874754688, 13883145344, 13891533184, 13899919232, - 13908311168, 13916692096, 13925085056, 13933473152, 13941866368, - 13950253696, 13958643584, 13967032192, 13975417216, 13983807616, - 13992197504, 14000582272, 14008973696, 14017363072, 14025752192, - 14034137984, 14042528384, 14050918016, 14059301504, 14067691648, - 14076083584, 14084470144, 14092852352, 14101249664, 14109635968, - 14118024832, 14126407552, 14134804352, 14143188608, 14151577984, - 14159968384, 14168357248, 14176741504, 14185127296, 14193521024, - 14201911424, 14210301824, 14218685056, 14227067264, 14235467392, - 14243855488, 14252243072, 14260630144, 14269021568, 14277409408, - 14285799296, 14294187904, 14302571392, 14310961792, 14319353728, - 14327738752, 14336130944, 14344518784, 14352906368, 14361296512, - 14369685376, 14378071424, 14386462592, 14394848128, 14403230848, - 14411627392, 14420013952, 14428402304, 14436793472, 14445181568, - 14453569664, 14461959808, 14470347904, 14478737024, 14487122816, - 14495511424, 14503901824, 14512291712, 14520677504, 14529064832, - 14537456768, 14545845632, 14554234496, 14562618496, 14571011456, - 14579398784, 14587789184, 14596172672, 14604564608, 14612953984, - 14621341312, 14629724288, 14638120832, 14646503296, 14654897536, - 14663284864, 14671675264, 14680061056, 14688447616, 14696835968, - 14705228416, 14713616768, 14722003328, 14730392192, 14738784128, - 14747172736, 14755561088, 14763947648, 14772336512, 14780725376, - 14789110144, 14797499776, 14805892736, 14814276992, 14822670208, - 14831056256, 14839444352, 14847836032, 14856222848, 14864612992, - 14872997504, 14881388672, 14889775744, 14898165376, 14906553472, - 14914944896, 14923329664, 14931721856, 14940109696, 14948497024, - 14956887424, 14965276544, 14973663616, 14982053248, 14990439808, - 14998830976, 15007216768, 15015605888, 15023995264, 15032385152, - 15040768384, 15049154944, 15057549184, 15065939072, 15074328448, - 15082715008, 15091104128, 15099493504, 15107879296, 15116269184, - 15124659584, 15133042304, 15141431936, 15149824384, 15158214272, - 15166602368, 15174991232, 15183378304, 15191760512, 15200154496, - 15208542592, 15216931712, 15225323392, 15233708416, 15242098048, - 15250489216, 15258875264, 15267265408, 15275654528, 15284043136, - 15292431488, 15300819584, 15309208192, 15317596544, 15325986176, - 15334374784, 15342763648, 15351151744, 15359540608, 15367929728, - 15376318336, 15384706432, 15393092992, 15401481856, 15409869952, - 15418258816, 15426649984, 15435037568, 15443425664, 15451815296, - 15460203392, 15468589184, 15476979328, 15485369216, 15493755776, - 15502146944, 15510534272, 15518924416, 15527311232, 15535699072, - 15544089472, 15552478336, 15560866688, 15569254528, 15577642624, - 15586031488, 15594419072, 15602809472, 15611199104, 15619586432, - 15627975296, 15636364928, 15644753792, 15653141888, 15661529216, - 15669918848, 15678305152, 15686696576, 15695083136, 15703474048, - 15711861632, 15720251264, 15728636288, 15737027456, 15745417088, - 15753804928, 15762194048, 15770582656, 15778971008, 15787358336, - 15795747712, 15804132224, 15812523392, 15820909696, 15829300096, - 15837691264, 15846071936, 15854466944, 15862855808, 15871244672, - 15879634816, 15888020608, 15896409728, 15904799104, 15913185152, - 15921577088, 15929966464, 15938354816, 15946743424, 15955129472, - 15963519872, 15971907968, 15980296064, 15988684928, 15997073024, - 16005460864, 16013851264, 16022241152, 16030629248, 16039012736, - 16047406976, 16055794816, 16064181376, 16072571264, 16080957824, - 16089346688, 16097737856, 16106125184, 16114514816, 16122904192, - 16131292544, 16139678848, 16148066944, 16156453504, 16164839552, - 16173236096, 16181623424, 16190012032, 16198401152, 16206790528, - 16215177344, 16223567744, 16231956352, 16240344704, 16248731008, - 16257117824, 16265504384, 16273898624, 16282281856, 16290668672, - 16299064192, 16307449216, 16315842176, 16324230016, 16332613504, - 16341006464, 16349394304, 16357783168, 16366172288, 16374561664, - 16382951296, 16391337856, 16399726208, 16408116352, 16416505472, - 16424892032, 16433282176, 16441668224, 16450058624, 16458448768, - 16466836864, 16475224448, 16483613056, 16492001408, 16500391808, - 16508779648, 16517166976, 16525555328, 16533944192, 16542330752, - 16550719616, 16559110528, 16567497088, 16575888512, 16584274816, - 16592665472, 16601051008, 16609442944, 16617832064, 16626218624, - 16634607488, 16642996096, 16651385728, 16659773824, 16668163712, - 16676552576, 16684938112, 16693328768, 16701718144, 16710095488, - 16718492288, 16726883968, 16735272832, 16743661184, 16752049792, - 16760436608, 16768827008, 16777214336, 16785599104, 16793992832, - 16802381696, 16810768768, 16819151744, 16827542656, 16835934848, - 16844323712, 16852711552, 16861101952, 16869489536, 16877876864, - 16886265728, 16894653056, 16903044736, 16911431296, 16919821696, - 16928207488, 16936592768, 16944987776, 16953375616, 16961763968, - 16970152832, 16978540928, 16986929536, 16995319168, 17003704448, - 17012096896, 17020481152, 17028870784, 17037262208, 17045649536, - 17054039936, 17062426496, 17070814336, 17079205504, 17087592064, - 17095978112, 17104369024, 17112759424, 17121147776, 17129536384, - 17137926016, 17146314368, 17154700928, 17163089792, 17171480192, - 17179864192, 17188256896, 17196644992, 17205033856, 17213423488, - 17221811072, 17230198912, 17238588032, 17246976896, 17255360384, - 17263754624, 17272143232, 17280530048, 17288918912, 17297309312, - 17305696384, 17314085504, 17322475136, 17330863744, 17339252096, - 17347640192, 17356026496, 17364413824, 17372796544, 17381190016, - 17389583488, 17397972608, 17406360704, 17414748544, 17423135872, - 17431527296, 17439915904, 17448303232, 17456691584, 17465081728, - 17473468288, 17481857408, 17490247552, 17498635904, 17507022464, - 17515409024, 17523801728, 17532189824, 17540577664, 17548966016, - 17557353344, 17565741184, 17574131584, 17582519168, 17590907008, - 17599296128, 17607687808, 17616076672, 17624455808, 17632852352, - 17641238656, 17649630848, 17658018944, 17666403968, 17674794112, - 17683178368, 17691573376, 17699962496, 17708350592, 17716739968, - 17725126528, 17733517184, 17741898112, 17750293888, 17758673024, - 17767070336, 17775458432, 17783848832, 17792236928, 17800625536, - 17809012352, 17817402752, 17825785984, 17834178944, 17842563968, - 17850955648, 17859344512, 17867732864, 17876119424, 17884511872, - 17892900224, 17901287296, 17909677696, 17918058112, 17926451072, - 17934843776, 17943230848, 17951609216, 17960008576, 17968397696, - 17976784256, 17985175424, 17993564032, 18001952128, 18010339712, - 18018728576, 18027116672, 18035503232, 18043894144, 18052283264, - 18060672128, 18069056384, 18077449856, 18085837184, 18094225792, - 18102613376, 18111004544, 18119388544, 18127781248, 18136170368, - 18144558976, 18152947328, 18161336192, 18169724288, 18178108544, - 18186498944, 18194886784, 18203275648, 18211666048, 18220048768, - 18228444544, 18236833408, 18245220736} - -// cacheSizes is a lookup table for the ethash verification cache size for the -// first 2048 epochs (i.e. 61440000 blocks). -var cacheSizes = [maxEpoch]uint64{ - 16776896, 16907456, 17039296, 17170112, 17301056, 17432512, 17563072, - 17693888, 17824192, 17955904, 18087488, 18218176, 18349504, 18481088, - 18611392, 18742336, 18874304, 19004224, 19135936, 19267264, 19398208, - 19529408, 19660096, 19791424, 19922752, 20053952, 20184896, 20315968, - 20446912, 20576576, 20709184, 20840384, 20971072, 21102272, 21233216, - 21364544, 21494848, 21626816, 21757376, 21887552, 22019392, 22151104, - 22281536, 22412224, 22543936, 22675264, 22806464, 22935872, 23068096, - 23198272, 23330752, 23459008, 23592512, 23723968, 23854912, 23986112, - 24116672, 24247616, 24378688, 24509504, 24640832, 24772544, 24903488, - 25034432, 25165376, 25296704, 25427392, 25558592, 25690048, 25820096, - 25951936, 26081728, 26214208, 26345024, 26476096, 26606656, 26737472, - 26869184, 26998208, 27131584, 27262528, 27393728, 27523904, 27655744, - 27786688, 27917888, 28049344, 28179904, 28311488, 28441792, 28573504, - 28700864, 28835648, 28966208, 29096768, 29228608, 29359808, 29490752, - 29621824, 29752256, 29882816, 30014912, 30144448, 30273728, 30406976, - 30538432, 30670784, 30799936, 30932672, 31063744, 31195072, 31325248, - 31456192, 31588288, 31719232, 31850432, 31981504, 32110784, 32243392, - 32372672, 32505664, 32636608, 32767808, 32897344, 33029824, 33160768, - 33289664, 33423296, 33554368, 33683648, 33816512, 33947456, 34076992, - 34208704, 34340032, 34471744, 34600256, 34734016, 34864576, 34993984, - 35127104, 35258176, 35386688, 35518528, 35650624, 35782336, 35910976, - 36044608, 36175808, 36305728, 36436672, 36568384, 36699968, 36830656, - 36961984, 37093312, 37223488, 37355072, 37486528, 37617472, 37747904, - 37879232, 38009792, 38141888, 38272448, 38403392, 38535104, 38660672, - 38795584, 38925632, 39059264, 39190336, 39320768, 39452096, 39581632, - 39713984, 39844928, 39974848, 40107968, 40238144, 40367168, 40500032, - 40631744, 40762816, 40894144, 41023552, 41155904, 41286208, 41418304, - 41547712, 41680448, 41811904, 41942848, 42073792, 42204992, 42334912, - 42467008, 42597824, 42729152, 42860096, 42991552, 43122368, 43253696, - 43382848, 43515712, 43646912, 43777088, 43907648, 44039104, 44170432, - 44302144, 44433344, 44564288, 44694976, 44825152, 44956864, 45088448, - 45219008, 45350464, 45481024, 45612608, 45744064, 45874496, 46006208, - 46136768, 46267712, 46399424, 46529344, 46660672, 46791488, 46923328, - 47053504, 47185856, 47316928, 47447872, 47579072, 47710144, 47839936, - 47971648, 48103232, 48234176, 48365248, 48496192, 48627136, 48757312, - 48889664, 49020736, 49149248, 49283008, 49413824, 49545152, 49675712, - 49807168, 49938368, 50069056, 50200256, 50331584, 50462656, 50593472, - 50724032, 50853952, 50986048, 51117632, 51248576, 51379904, 51510848, - 51641792, 51773248, 51903296, 52035136, 52164032, 52297664, 52427968, - 52557376, 52690112, 52821952, 52952896, 53081536, 53213504, 53344576, - 53475776, 53608384, 53738816, 53870528, 54000832, 54131776, 54263744, - 54394688, 54525248, 54655936, 54787904, 54918592, 55049152, 55181248, - 55312064, 55442752, 55574336, 55705024, 55836224, 55967168, 56097856, - 56228672, 56358592, 56490176, 56621888, 56753728, 56884928, 57015488, - 57146816, 57278272, 57409216, 57540416, 57671104, 57802432, 57933632, - 58064576, 58195264, 58326976, 58457408, 58588864, 58720192, 58849984, - 58981696, 59113024, 59243456, 59375552, 59506624, 59637568, 59768512, - 59897792, 60030016, 60161984, 60293056, 60423872, 60554432, 60683968, - 60817216, 60948032, 61079488, 61209664, 61341376, 61471936, 61602752, - 61733696, 61865792, 61996736, 62127808, 62259136, 62389568, 62520512, - 62651584, 62781632, 62910784, 63045056, 63176128, 63307072, 63438656, - 63569216, 63700928, 63831616, 63960896, 64093888, 64225088, 64355392, - 64486976, 64617664, 64748608, 64879424, 65009216, 65142464, 65273792, - 65402816, 65535424, 65666752, 65797696, 65927744, 66060224, 66191296, - 66321344, 66453056, 66584384, 66715328, 66846656, 66977728, 67108672, - 67239104, 67370432, 67501888, 67631296, 67763776, 67895104, 68026304, - 68157248, 68287936, 68419264, 68548288, 68681408, 68811968, 68942912, - 69074624, 69205568, 69337024, 69467584, 69599168, 69729472, 69861184, - 69989824, 70122944, 70253888, 70385344, 70515904, 70647232, 70778816, - 70907968, 71040832, 71171648, 71303104, 71432512, 71564992, 71695168, - 71826368, 71958464, 72089536, 72219712, 72350144, 72482624, 72613568, - 72744512, 72875584, 73006144, 73138112, 73268672, 73400128, 73530944, - 73662272, 73793344, 73924544, 74055104, 74185792, 74316992, 74448832, - 74579392, 74710976, 74841664, 74972864, 75102784, 75233344, 75364544, - 75497024, 75627584, 75759296, 75890624, 76021696, 76152256, 76283072, - 76414144, 76545856, 76676672, 76806976, 76937792, 77070016, 77200832, - 77331392, 77462464, 77593664, 77725376, 77856448, 77987776, 78118336, - 78249664, 78380992, 78511424, 78642496, 78773056, 78905152, 79033664, - 79166656, 79297472, 79429568, 79560512, 79690816, 79822784, 79953472, - 80084672, 80214208, 80346944, 80477632, 80608576, 80740288, 80870848, - 81002048, 81133504, 81264448, 81395648, 81525952, 81657536, 81786304, - 81919808, 82050112, 82181312, 82311616, 82443968, 82573376, 82705984, - 82835776, 82967744, 83096768, 83230528, 83359552, 83491264, 83622464, - 83753536, 83886016, 84015296, 84147776, 84277184, 84409792, 84540608, - 84672064, 84803008, 84934336, 85065152, 85193792, 85326784, 85458496, - 85589312, 85721024, 85851968, 85982656, 86112448, 86244416, 86370112, - 86506688, 86637632, 86769344, 86900672, 87031744, 87162304, 87293632, - 87424576, 87555392, 87687104, 87816896, 87947968, 88079168, 88211264, - 88341824, 88473152, 88603712, 88735424, 88862912, 88996672, 89128384, - 89259712, 89390272, 89521984, 89652544, 89783872, 89914816, 90045376, - 90177088, 90307904, 90438848, 90569152, 90700096, 90832832, 90963776, - 91093696, 91223744, 91356992, 91486784, 91618496, 91749824, 91880384, - 92012224, 92143552, 92273344, 92405696, 92536768, 92666432, 92798912, - 92926016, 93060544, 93192128, 93322816, 93453632, 93583936, 93715136, - 93845056, 93977792, 94109504, 94240448, 94371776, 94501184, 94632896, - 94764224, 94895552, 95023424, 95158208, 95287744, 95420224, 95550016, - 95681216, 95811904, 95943872, 96075328, 96203584, 96337856, 96468544, - 96599744, 96731072, 96860992, 96992576, 97124288, 97254848, 97385536, - 97517248, 97647808, 97779392, 97910464, 98041408, 98172608, 98303168, - 98434496, 98565568, 98696768, 98827328, 98958784, 99089728, 99220928, - 99352384, 99482816, 99614272, 99745472, 99876416, 100007104, - 100138048, 100267072, 100401088, 100529984, 100662592, 100791872, - 100925248, 101056064, 101187392, 101317952, 101449408, 101580608, - 101711296, 101841728, 101973824, 102104896, 102235712, 102366016, - 102498112, 102628672, 102760384, 102890432, 103021888, 103153472, - 103284032, 103415744, 103545152, 103677248, 103808576, 103939648, - 104070976, 104201792, 104332736, 104462528, 104594752, 104725952, - 104854592, 104988608, 105118912, 105247808, 105381184, 105511232, - 105643072, 105774784, 105903296, 106037056, 106167872, 106298944, - 106429504, 106561472, 106691392, 106822592, 106954304, 107085376, - 107216576, 107346368, 107478464, 107609792, 107739712, 107872192, - 108003136, 108131392, 108265408, 108396224, 108527168, 108657344, - 108789568, 108920384, 109049792, 109182272, 109312576, 109444928, - 109572928, 109706944, 109837888, 109969088, 110099648, 110230976, - 110362432, 110492992, 110624704, 110755264, 110886208, 111017408, - 111148864, 111279296, 111410752, 111541952, 111673024, 111803456, - 111933632, 112066496, 112196416, 112328512, 112457792, 112590784, - 112715968, 112852672, 112983616, 113114944, 113244224, 113376448, - 113505472, 113639104, 113770304, 113901376, 114031552, 114163264, - 114294592, 114425536, 114556864, 114687424, 114818624, 114948544, - 115080512, 115212224, 115343296, 115473472, 115605184, 115736128, - 115867072, 115997248, 116128576, 116260288, 116391488, 116522944, - 116652992, 116784704, 116915648, 117046208, 117178304, 117308608, - 117440192, 117569728, 117701824, 117833024, 117964096, 118094656, - 118225984, 118357312, 118489024, 118617536, 118749632, 118882112, - 119012416, 119144384, 119275328, 119406016, 119537344, 119668672, - 119798464, 119928896, 120061376, 120192832, 120321728, 120454336, - 120584512, 120716608, 120848192, 120979136, 121109056, 121241408, - 121372352, 121502912, 121634752, 121764416, 121895744, 122027072, - 122157632, 122289088, 122421184, 122550592, 122682944, 122813888, - 122945344, 123075776, 123207488, 123338048, 123468736, 123600704, - 123731264, 123861952, 123993664, 124124608, 124256192, 124386368, - 124518208, 124649024, 124778048, 124911296, 125041088, 125173696, - 125303744, 125432896, 125566912, 125696576, 125829056, 125958592, - 126090304, 126221248, 126352832, 126483776, 126615232, 126746432, - 126876608, 127008704, 127139392, 127270336, 127401152, 127532224, - 127663552, 127794752, 127925696, 128055232, 128188096, 128319424, - 128449856, 128581312, 128712256, 128843584, 128973632, 129103808, - 129236288, 129365696, 129498944, 129629888, 129760832, 129892288, - 130023104, 130154048, 130283968, 130416448, 130547008, 130678336, - 130807616, 130939456, 131071552, 131202112, 131331776, 131464384, - 131594048, 131727296, 131858368, 131987392, 132120256, 132250816, - 132382528, 132513728, 132644672, 132774976, 132905792, 133038016, - 133168832, 133299392, 133429312, 133562048, 133692992, 133823296, - 133954624, 134086336, 134217152, 134348608, 134479808, 134607296, - 134741056, 134872384, 135002944, 135134144, 135265472, 135396544, - 135527872, 135659072, 135787712, 135921472, 136052416, 136182848, - 136313792, 136444864, 136576448, 136707904, 136837952, 136970048, - 137099584, 137232064, 137363392, 137494208, 137625536, 137755712, - 137887424, 138018368, 138149824, 138280256, 138411584, 138539584, - 138672832, 138804928, 138936128, 139066688, 139196864, 139328704, - 139460032, 139590208, 139721024, 139852864, 139984576, 140115776, - 140245696, 140376512, 140508352, 140640064, 140769856, 140902336, - 141032768, 141162688, 141294016, 141426496, 141556544, 141687488, - 141819584, 141949888, 142080448, 142212544, 142342336, 142474432, - 142606144, 142736192, 142868288, 142997824, 143129408, 143258944, - 143392448, 143523136, 143653696, 143785024, 143916992, 144045632, - 144177856, 144309184, 144440768, 144570688, 144701888, 144832448, - 144965056, 145096384, 145227584, 145358656, 145489856, 145620928, - 145751488, 145883072, 146011456, 146144704, 146275264, 146407232, - 146538176, 146668736, 146800448, 146931392, 147062336, 147193664, - 147324224, 147455936, 147586624, 147717056, 147848768, 147979456, - 148110784, 148242368, 148373312, 148503232, 148635584, 148766144, - 148897088, 149028416, 149159488, 149290688, 149420224, 149551552, - 149683136, 149814976, 149943616, 150076352, 150208064, 150338624, - 150470464, 150600256, 150732224, 150862784, 150993088, 151125952, - 151254976, 151388096, 151519168, 151649728, 151778752, 151911104, - 152042944, 152174144, 152304704, 152435648, 152567488, 152698816, - 152828992, 152960576, 153091648, 153222976, 153353792, 153484096, - 153616192, 153747008, 153878336, 154008256, 154139968, 154270912, - 154402624, 154533824, 154663616, 154795712, 154926272, 155057984, - 155188928, 155319872, 155450816, 155580608, 155712064, 155843392, - 155971136, 156106688, 156237376, 156367424, 156499264, 156630976, - 156761536, 156892352, 157024064, 157155008, 157284416, 157415872, - 157545536, 157677248, 157810496, 157938112, 158071744, 158203328, - 158334656, 158464832, 158596288, 158727616, 158858048, 158988992, - 159121216, 159252416, 159381568, 159513152, 159645632, 159776192, - 159906496, 160038464, 160169536, 160300352, 160430656, 160563008, - 160693952, 160822208, 160956352, 161086784, 161217344, 161349184, - 161480512, 161611456, 161742272, 161873216, 162002752, 162135872, - 162266432, 162397888, 162529216, 162660032, 162790976, 162922048, - 163052096, 163184576, 163314752, 163446592, 163577408, 163707968, - 163839296, 163969984, 164100928, 164233024, 164364224, 164494912, - 164625856, 164756672, 164887616, 165019072, 165150016, 165280064, - 165412672, 165543104, 165674944, 165805888, 165936832, 166067648, - 166198336, 166330048, 166461248, 166591552, 166722496, 166854208, - 166985408, 167116736, 167246656, 167378368, 167508416, 167641024, - 167771584, 167903168, 168034112, 168164032, 168295744, 168427456, - 168557632, 168688448, 168819136, 168951616, 169082176, 169213504, - 169344832, 169475648, 169605952, 169738048, 169866304, 169999552, - 170131264, 170262464, 170393536, 170524352, 170655424, 170782016, - 170917696, 171048896, 171179072, 171310784, 171439936, 171573184, - 171702976, 171835072, 171966272, 172097216, 172228288, 172359232, - 172489664, 172621376, 172747712, 172883264, 173014208, 173144512, - 173275072, 173407424, 173539136, 173669696, 173800768, 173931712, - 174063424, 174193472, 174325696, 174455744, 174586816, 174718912, - 174849728, 174977728, 175109696, 175242688, 175374272, 175504832, - 175636288, 175765696, 175898432, 176028992, 176159936, 176291264, - 176422592, 176552512, 176684864, 176815424, 176946496, 177076544, - 177209152, 177340096, 177470528, 177600704, 177731648, 177864256, - 177994816, 178126528, 178257472, 178387648, 178518464, 178650176, - 178781888, 178912064, 179044288, 179174848, 179305024, 179436736, - 179568448, 179698496, 179830208, 179960512, 180092608, 180223808, - 180354752, 180485696, 180617152, 180748096, 180877504, 181009984, - 181139264, 181272512, 181402688, 181532608, 181663168, 181795136, - 181926592, 182057536, 182190016, 182320192, 182451904, 182582336, - 182713792, 182843072, 182976064, 183107264, 183237056, 183368384, - 183494848, 183631424, 183762752, 183893824, 184024768, 184154816, - 184286656, 184417984, 184548928, 184680128, 184810816, 184941248, - 185072704, 185203904, 185335616, 185465408, 185596352, 185727296, - 185859904, 185989696, 186121664, 186252992, 186383552, 186514112, - 186645952, 186777152, 186907328, 187037504, 187170112, 187301824, - 187429184, 187562048, 187693504, 187825472, 187957184, 188087104, - 188218304, 188349376, 188481344, 188609728, 188743616, 188874304, - 189005248, 189136448, 189265088, 189396544, 189528128, 189660992, - 189791936, 189923264, 190054208, 190182848, 190315072, 190447424, - 190577984, 190709312, 190840768, 190971328, 191102656, 191233472, - 191364032, 191495872, 191626816, 191758016, 191888192, 192020288, - 192148928, 192282176, 192413504, 192542528, 192674752, 192805952, - 192937792, 193068608, 193198912, 193330496, 193462208, 193592384, - 193723456, 193854272, 193985984, 194116672, 194247232, 194379712, - 194508352, 194641856, 194772544, 194900672, 195035072, 195166016, - 195296704, 195428032, 195558592, 195690304, 195818176, 195952576, - 196083392, 196214336, 196345792, 196476736, 196607552, 196739008, - 196869952, 197000768, 197130688, 197262784, 197394368, 197523904, - 197656384, 197787584, 197916608, 198049472, 198180544, 198310208, - 198442432, 198573632, 198705088, 198834368, 198967232, 199097792, - 199228352, 199360192, 199491392, 199621696, 199751744, 199883968, - 200014016, 200146624, 200276672, 200408128, 200540096, 200671168, - 200801984, 200933312, 201062464, 201194944, 201326144, 201457472, - 201588544, 201719744, 201850816, 201981632, 202111552, 202244032, - 202374464, 202505152, 202636352, 202767808, 202898368, 203030336, - 203159872, 203292608, 203423296, 203553472, 203685824, 203816896, - 203947712, 204078272, 204208192, 204341056, 204472256, 204603328, - 204733888, 204864448, 204996544, 205125568, 205258304, 205388864, - 205517632, 205650112, 205782208, 205913536, 206044736, 206176192, - 206307008, 206434496, 206569024, 206700224, 206831168, 206961856, - 207093056, 207223616, 207355328, 207486784, 207616832, 207749056, - 207879104, 208010048, 208141888, 208273216, 208404032, 208534336, - 208666048, 208796864, 208927424, 209059264, 209189824, 209321792, - 209451584, 209582656, 209715136, 209845568, 209976896, 210106432, - 210239296, 210370112, 210501568, 210630976, 210763712, 210894272, - 211024832, 211156672, 211287616, 211418176, 211549376, 211679296, - 211812032, 211942592, 212074432, 212204864, 212334016, 212467648, - 212597824, 212727616, 212860352, 212991424, 213120832, 213253952, - 213385024, 213515584, 213645632, 213777728, 213909184, 214040128, - 214170688, 214302656, 214433728, 214564544, 214695232, 214826048, - 214956992, 215089088, 215219776, 215350592, 215482304, 215613248, - 215743552, 215874752, 216005312, 216137024, 216267328, 216399296, - 216530752, 216661696, 216790592, 216923968, 217054528, 217183168, - 217316672, 217448128, 217579072, 217709504, 217838912, 217972672, - 218102848, 218233024, 218364736, 218496832, 218627776, 218759104, - 218888896, 219021248, 219151936, 219281728, 219413056, 219545024, - 219675968, 219807296, 219938624, 220069312, 220200128, 220331456, - 220461632, 220592704, 220725184, 220855744, 220987072, 221117888, - 221249216, 221378368, 221510336, 221642048, 221772736, 221904832, - 222031808, 222166976, 222297536, 222428992, 222559936, 222690368, - 222820672, 222953152, 223083968, 223213376, 223345984, 223476928, - 223608512, 223738688, 223869376, 224001472, 224132672, 224262848, - 224394944, 224524864, 224657344, 224788288, 224919488, 225050432, - 225181504, 225312704, 225443776, 225574592, 225704768, 225834176, - 225966784, 226097216, 226229824, 226360384, 226491712, 226623424, - 226754368, 226885312, 227015104, 227147456, 227278528, 227409472, - 227539904, 227669696, 227802944, 227932352, 228065216, 228196288, - 228326464, 228457792, 228588736, 228720064, 228850112, 228981056, - 229113152, 229243328, 229375936, 229505344, 229636928, 229769152, - 229894976, 230030272, 230162368, 230292416, 230424512, 230553152, - 230684864, 230816704, 230948416, 231079616, 231210944, 231342016, - 231472448, 231603776, 231733952, 231866176, 231996736, 232127296, - 232259392, 232388672, 232521664, 232652608, 232782272, 232914496, - 233043904, 233175616, 233306816, 233438528, 233569984, 233699776, - 233830592, 233962688, 234092224, 234221888, 234353984, 234485312, - 234618304, 234749888, 234880832, 235011776, 235142464, 235274048, - 235403456, 235535936, 235667392, 235797568, 235928768, 236057152, - 236190272, 236322752, 236453312, 236583616, 236715712, 236846528, - 236976448, 237108544, 237239104, 237371072, 237501632, 237630784, - 237764416, 237895232, 238026688, 238157632, 238286912, 238419392, - 238548032, 238681024, 238812608, 238941632, 239075008, 239206336, - 239335232, 239466944, 239599168, 239730496, 239861312, 239992384, - 240122816, 240254656, 240385856, 240516928, 240647872, 240779072, - 240909632, 241040704, 241171904, 241302848, 241433408, 241565248, - 241696192, 241825984, 241958848, 242088256, 242220224, 242352064, - 242481856, 242611648, 242744896, 242876224, 243005632, 243138496, - 243268672, 243400384, 243531712, 243662656, 243793856, 243924544, - 244054592, 244187072, 244316608, 244448704, 244580032, 244710976, - 244841536, 244972864, 245104448, 245233984, 245365312, 245497792, - 245628736, 245759936, 245889856, 246021056, 246152512, 246284224, - 246415168, 246545344, 246675904, 246808384, 246939584, 247070144, - 247199552, 247331648, 247463872, 247593536, 247726016, 247857088, - 247987648, 248116928, 248249536, 248380736, 248512064, 248643008, - 248773312, 248901056, 249036608, 249167552, 249298624, 249429184, - 249560512, 249692096, 249822784, 249954112, 250085312, 250215488, - 250345792, 250478528, 250608704, 250739264, 250870976, 251002816, - 251133632, 251263552, 251395136, 251523904, 251657792, 251789248, - 251919424, 252051392, 252182464, 252313408, 252444224, 252575552, - 252706624, 252836032, 252968512, 253099712, 253227584, 253361728, - 253493056, 253623488, 253754432, 253885504, 254017216, 254148032, - 254279488, 254410432, 254541376, 254672576, 254803264, 254933824, - 255065792, 255196736, 255326528, 255458752, 255589952, 255721408, - 255851072, 255983296, 256114624, 256244416, 256374208, 256507712, - 256636096, 256768832, 256900544, 257031616, 257162176, 257294272, - 257424448, 257555776, 257686976, 257818432, 257949632, 258079552, - 258211136, 258342464, 258473408, 258603712, 258734656, 258867008, - 258996544, 259127744, 259260224, 259391296, 259522112, 259651904, - 259784384, 259915328, 260045888, 260175424, 260308544, 260438336, - 260570944, 260700992, 260832448, 260963776, 261092672, 261226304, - 261356864, 261487936, 261619648, 261750592, 261879872, 262011968, - 262143424, 262274752, 262404416, 262537024, 262667968, 262799296, - 262928704, 263061184, 263191744, 263322944, 263454656, 263585216, - 263716672, 263847872, 263978944, 264108608, 264241088, 264371648, - 264501184, 264632768, 264764096, 264895936, 265024576, 265158464, - 265287488, 265418432, 265550528, 265681216, 265813312, 265943488, - 266075968, 266206144, 266337728, 266468032, 266600384, 266731072, - 266862272, 266993344, 267124288, 267255616, 267386432, 267516992, - 267648704, 267777728, 267910592, 268040512, 268172096, 268302784, - 268435264, 268566208, 268696256, 268828096, 268959296, 269090368, - 269221312, 269352256, 269482688, 269614784, 269745856, 269876416, - 270007616, 270139328, 270270272, 270401216, 270531904, 270663616, - 270791744, 270924736, 271056832, 271186112, 271317184, 271449536, - 271580992, 271711936, 271843136, 271973056, 272105408, 272236352, - 272367296, 272498368, 272629568, 272759488, 272891456, 273022784, - 273153856, 273284672, 273415616, 273547072, 273677632, 273808448, - 273937088, 274071488, 274200896, 274332992, 274463296, 274595392, - 274726208, 274857536, 274988992, 275118656, 275250496, 275382208, - 275513024, 275643968, 275775296, 275906368, 276037184, 276167872, - 276297664, 276429376, 276560576, 276692672, 276822976, 276955072, - 277085632, 277216832, 277347008, 277478848, 277609664, 277740992, - 277868608, 278002624, 278134336, 278265536, 278395328, 278526784, - 278657728, 278789824, 278921152, 279052096, 279182912, 279313088, - 279443776, 279576256, 279706048, 279838528, 279969728, 280099648, - 280230976, 280361408, 280493632, 280622528, 280755392, 280887104, - 281018176, 281147968, 281278912, 281411392, 281542592, 281673152, - 281803712, 281935552, 282066496, 282197312, 282329024, 282458816, - 282590272, 282720832, 282853184, 282983744, 283115072, 283246144, - 283377344, 283508416, 283639744, 283770304, 283901504, 284032576, - 284163136, 284294848, 284426176, 284556992, 284687296, 284819264, - 284950208, 285081536}
diff --git go-ethereum/consensus/ethash/sealer.go celo/consensus/ethash/sealer.go deleted file mode 100644 index 8a81e31ecce002da2d129fdb1934dbdce86ecd6d..0000000000000000000000000000000000000000 --- go-ethereum/consensus/ethash/sealer.go +++ /dev/null @@ -1,450 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package ethash - -import ( - "bytes" - "context" - crand "crypto/rand" - "encoding/json" - "errors" - "math" - "math/big" - "math/rand" - "net/http" - "runtime" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/core/types" -) - -const ( - // staleThreshold is the maximum depth of the acceptable stale but valid ethash solution. - staleThreshold = 7 -) - -var ( - errNoMiningWork = errors.New("no mining work available yet") - errInvalidSealResult = errors.New("invalid or stale proof-of-work solution") -) - -// Seal implements consensus.Engine, attempting to find a nonce that satisfies -// the block's difficulty requirements. -func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { - // If we're running a fake PoW, simply return a 0 nonce immediately - if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake { - header := block.Header() - header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{} - select { - case results <- block.WithSeal(header): - default: - ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "fake", "sealhash", ethash.SealHash(block.Header())) - } - return nil - } - // If we're running a shared PoW, delegate sealing to it - if ethash.shared != nil { - return ethash.shared.Seal(chain, block, results, stop) - } - // Create a runner and the multiple search threads it directs - abort := make(chan struct{}) - - ethash.lock.Lock() - threads := ethash.threads - if ethash.rand == nil { - seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) - if err != nil { - ethash.lock.Unlock() - return err - } - ethash.rand = rand.New(rand.NewSource(seed.Int64())) - } - ethash.lock.Unlock() - if threads == 0 { - threads = runtime.NumCPU() - } - if threads < 0 { - threads = 0 // Allows disabling local mining without extra logic around local/remote - } - // Push new work to remote sealer - if ethash.remote != nil { - ethash.remote.workCh <- &sealTask{block: block, results: results} - } - var ( - pend sync.WaitGroup - locals = make(chan *types.Block) - ) - for i := 0; i < threads; i++ { - pend.Add(1) - go func(id int, nonce uint64) { - defer pend.Done() - ethash.mine(block, id, nonce, abort, locals) - }(i, uint64(ethash.rand.Int63())) - } - // Wait until sealing is terminated or a nonce is found - go func() { - var result *types.Block - select { - case <-stop: - // Outside abort, stop all miner threads - close(abort) - case result = <-locals: - // One of the threads found a block, abort all others - select { - case results <- result: - default: - ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "local", "sealhash", ethash.SealHash(block.Header())) - } - close(abort) - case <-ethash.update: - // Thread count was changed on user request, restart - close(abort) - if err := ethash.Seal(chain, block, results, stop); err != nil { - ethash.config.Log.Error("Failed to restart sealing after update", "err", err) - } - } - // Wait for all miners to terminate and return the block - pend.Wait() - }() - return nil -} - -// mine is the actual proof-of-work miner that searches for a nonce starting from -// seed that results in correct final block difficulty. -func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan struct{}, found chan *types.Block) { - // Extract some data from the header - var ( - header = block.Header() - hash = ethash.SealHash(header).Bytes() - target = new(big.Int).Div(two256, header.Difficulty) - number = header.Number.Uint64() - dataset = ethash.dataset(number, false) - ) - // Start generating random nonces until we abort or find a good one - var ( - attempts = int64(0) - nonce = seed - powBuffer = new(big.Int) - ) - logger := ethash.config.Log.New("miner", id) - logger.Trace("Started ethash search for new nonces", "seed", seed) -search: - for { - select { - case <-abort: - // Mining terminated, update stats and abort - logger.Trace("Ethash nonce search aborted", "attempts", nonce-seed) - ethash.hashrate.Mark(attempts) - break search - - default: - // We don't have to update hash rate on every nonce, so update after after 2^X nonces - attempts++ - if (attempts % (1 << 15)) == 0 { - ethash.hashrate.Mark(attempts) - attempts = 0 - } - // Compute the PoW value of this nonce - digest, result := hashimotoFull(dataset.dataset, hash, nonce) - if powBuffer.SetBytes(result).Cmp(target) <= 0 { - // Correct nonce found, create a new header with it - header = types.CopyHeader(header) - header.Nonce = types.EncodeNonce(nonce) - header.MixDigest = common.BytesToHash(digest) - - // Seal and return a block (if still needed) - select { - case found <- block.WithSeal(header): - logger.Trace("Ethash nonce found and reported", "attempts", nonce-seed, "nonce", nonce) - case <-abort: - logger.Trace("Ethash nonce found but discarded", "attempts", nonce-seed, "nonce", nonce) - } - break search - } - nonce++ - } - } - // Datasets are unmapped in a finalizer. Ensure that the dataset stays live - // during sealing so it's not unmapped while being read. - runtime.KeepAlive(dataset) -} - -// This is the timeout for HTTP requests to notify external miners. -const remoteSealerTimeout = 1 * time.Second - -type remoteSealer struct { - works map[common.Hash]*types.Block - rates map[common.Hash]hashrate - currentBlock *types.Block - currentWork [4]string - notifyCtx context.Context - cancelNotify context.CancelFunc // cancels all notification requests - reqWG sync.WaitGroup // tracks notification request goroutines - - ethash *Ethash - noverify bool - notifyURLs []string - results chan<- *types.Block - workCh chan *sealTask // Notification channel to push new work and relative result channel to remote sealer - fetchWorkCh chan *sealWork // Channel used for remote sealer to fetch mining work - submitWorkCh chan *mineResult // Channel used for remote sealer to submit their mining result - fetchRateCh chan chan uint64 // Channel used to gather submitted hash rate for local or remote sealer. - submitRateCh chan *hashrate // Channel used for remote sealer to submit their mining hashrate - requestExit chan struct{} - exitCh chan struct{} -} - -// sealTask wraps a seal block with relative result channel for remote sealer thread. -type sealTask struct { - block *types.Block - results chan<- *types.Block -} - -// mineResult wraps the pow solution parameters for the specified block. -type mineResult struct { - nonce types.BlockNonce - mixDigest common.Hash - hash common.Hash - - errc chan error -} - -// hashrate wraps the hash rate submitted by the remote sealer. -type hashrate struct { - id common.Hash - ping time.Time - rate uint64 - - done chan struct{} -} - -// sealWork wraps a seal work package for remote sealer. -type sealWork struct { - errc chan error - res chan [4]string -} - -func startRemoteSealer(ethash *Ethash, urls []string, noverify bool) *remoteSealer { - ctx, cancel := context.WithCancel(context.Background()) - s := &remoteSealer{ - ethash: ethash, - noverify: noverify, - notifyURLs: urls, - notifyCtx: ctx, - cancelNotify: cancel, - works: make(map[common.Hash]*types.Block), - rates: make(map[common.Hash]hashrate), - workCh: make(chan *sealTask), - fetchWorkCh: make(chan *sealWork), - submitWorkCh: make(chan *mineResult), - fetchRateCh: make(chan chan uint64), - submitRateCh: make(chan *hashrate), - requestExit: make(chan struct{}), - exitCh: make(chan struct{}), - } - go s.loop() - return s -} - -func (s *remoteSealer) loop() { - defer func() { - s.ethash.config.Log.Trace("Ethash remote sealer is exiting") - s.cancelNotify() - s.reqWG.Wait() - close(s.exitCh) - }() - - ticker := time.NewTicker(5 * time.Second) - defer ticker.Stop() - - for { - select { - case work := <-s.workCh: - // Update current work with new received block. - // Note same work can be past twice, happens when changing CPU threads. - s.results = work.results - s.makeWork(work.block) - s.notifyWork() - - case work := <-s.fetchWorkCh: - // Return current mining work to remote miner. - if s.currentBlock == nil { - work.errc <- errNoMiningWork - } else { - work.res <- s.currentWork - } - - case result := <-s.submitWorkCh: - // Verify submitted PoW solution based on maintained mining blocks. - if s.submitWork(result.nonce, result.mixDigest, result.hash) { - result.errc <- nil - } else { - result.errc <- errInvalidSealResult - } - - case result := <-s.submitRateCh: - // Trace remote sealer's hash rate by submitted value. - s.rates[result.id] = hashrate{rate: result.rate, ping: time.Now()} - close(result.done) - - case req := <-s.fetchRateCh: - // Gather all hash rate submitted by remote sealer. - var total uint64 - for _, rate := range s.rates { - // this could overflow - total += rate.rate - } - req <- total - - case <-ticker.C: - // Clear stale submitted hash rate. - for id, rate := range s.rates { - if time.Since(rate.ping) > 10*time.Second { - delete(s.rates, id) - } - } - // Clear stale pending blocks - if s.currentBlock != nil { - for hash, block := range s.works { - if block.NumberU64()+staleThreshold <= s.currentBlock.NumberU64() { - delete(s.works, hash) - } - } - } - - case <-s.requestExit: - return - } - } -} - -// makeWork creates a work package for external miner. -// -// The work package consists of 3 strings: -// result[0], 32 bytes hex encoded current block header pow-hash -// result[1], 32 bytes hex encoded seed hash used for DAG -// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty -// result[3], hex encoded block number -func (s *remoteSealer) makeWork(block *types.Block) { - hash := s.ethash.SealHash(block.Header()) - s.currentWork[0] = hash.Hex() - s.currentWork[1] = common.BytesToHash(SeedHash(block.NumberU64())).Hex() - s.currentWork[2] = common.BytesToHash(new(big.Int).Div(two256, block.Difficulty()).Bytes()).Hex() - s.currentWork[3] = hexutil.EncodeBig(block.Number()) - - // Trace the seal work fetched by remote sealer. - s.currentBlock = block - s.works[hash] = block -} - -// notifyWork notifies all the specified mining endpoints of the availability of -// new work to be processed. -func (s *remoteSealer) notifyWork() { - work := s.currentWork - - // Encode the JSON payload of the notification. When NotifyFull is set, - // this is the complete block header, otherwise it is a JSON array. - var blob []byte - if s.ethash.config.NotifyFull { - blob, _ = json.Marshal(s.currentBlock.Header()) - } else { - blob, _ = json.Marshal(work) - } - - s.reqWG.Add(len(s.notifyURLs)) - for _, url := range s.notifyURLs { - go s.sendNotification(s.notifyCtx, url, blob, work) - } -} - -func (s *remoteSealer) sendNotification(ctx context.Context, url string, json []byte, work [4]string) { - defer s.reqWG.Done() - - req, err := http.NewRequest("POST", url, bytes.NewReader(json)) - if err != nil { - s.ethash.config.Log.Warn("Can't create remote miner notification", "err", err) - return - } - ctx, cancel := context.WithTimeout(ctx, remoteSealerTimeout) - defer cancel() - req = req.WithContext(ctx) - req.Header.Set("Content-Type", "application/json") - - resp, err := http.DefaultClient.Do(req) - if err != nil { - s.ethash.config.Log.Warn("Failed to notify remote miner", "err", err) - } else { - s.ethash.config.Log.Trace("Notified remote miner", "miner", url, "hash", work[0], "target", work[2]) - resp.Body.Close() - } -} - -// submitWork verifies the submitted pow solution, returning -// whether the solution was accepted or not (not can be both a bad pow as well as -// any other error, like no pending work or stale mining result). -func (s *remoteSealer) submitWork(nonce types.BlockNonce, mixDigest common.Hash, sealhash common.Hash) bool { - if s.currentBlock == nil { - s.ethash.config.Log.Error("Pending work without block", "sealhash", sealhash) - return false - } - // Make sure the work submitted is present - block := s.works[sealhash] - if block == nil { - s.ethash.config.Log.Warn("Work submitted but none pending", "sealhash", sealhash, "curnumber", s.currentBlock.NumberU64()) - return false - } - // Verify the correctness of submitted result. - header := block.Header() - header.Nonce = nonce - header.MixDigest = mixDigest - - start := time.Now() - if !s.noverify { - if err := s.ethash.verifySeal(nil, header, true); err != nil { - s.ethash.config.Log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)), "err", err) - return false - } - } - // Make sure the result channel is assigned. - if s.results == nil { - s.ethash.config.Log.Warn("Ethash result channel is empty, submitted mining result is rejected") - return false - } - s.ethash.config.Log.Trace("Verified correct proof-of-work", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start))) - - // Solutions seems to be valid, return to the miner and notify acceptance. - solution := block.WithSeal(header) - - // The submitted solution is within the scope of acceptance. - if solution.NumberU64()+staleThreshold > s.currentBlock.NumberU64() { - select { - case s.results <- solution: - s.ethash.config.Log.Debug("Work submitted is acceptable", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash()) - return true - default: - s.ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "remote", "sealhash", sealhash) - return false - } - } - // The submitted block is too old to accept, drop it. - s.ethash.config.Log.Warn("Work submitted is too old", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash()) - return false -}
diff --git go-ethereum/consensus/clique/snapshot.go celo/consensus/clique/snapshot.go deleted file mode 100644 index 94f235c872592fd51d45ef4d92f6459c999fe616..0000000000000000000000000000000000000000 --- go-ethereum/consensus/clique/snapshot.go +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package clique - -import ( - "bytes" - "encoding/json" - "sort" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" - lru "github.com/hashicorp/golang-lru" -) - -// Vote represents a single vote that an authorized signer made to modify the -// list of authorizations. -type Vote struct { - Signer common.Address `json:"signer"` // Authorized signer that cast this vote - Block uint64 `json:"block"` // Block number the vote was cast in (expire old votes) - Address common.Address `json:"address"` // Account being voted on to change its authorization - Authorize bool `json:"authorize"` // Whether to authorize or deauthorize the voted account -} - -// Tally is a simple vote tally to keep the current score of votes. Votes that -// go against the proposal aren't counted since it's equivalent to not voting. -type Tally struct { - Authorize bool `json:"authorize"` // Whether the vote is about authorizing or kicking someone - Votes int `json:"votes"` // Number of votes until now wanting to pass the proposal -} - -// Snapshot is the state of the authorization voting at a given point in time. -type Snapshot struct { - config *params.CliqueConfig // Consensus engine parameters to fine tune behavior - sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover - - Number uint64 `json:"number"` // Block number where the snapshot was created - Hash common.Hash `json:"hash"` // Block hash where the snapshot was created - Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment - Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections - Votes []*Vote `json:"votes"` // List of votes cast in chronological order - Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating -} - -// signersAscending implements the sort interface to allow sorting a list of addresses -type signersAscending []common.Address - -func (s signersAscending) Len() int { return len(s) } -func (s signersAscending) Less(i, j int) bool { return bytes.Compare(s[i][:], s[j][:]) < 0 } -func (s signersAscending) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// newSnapshot creates a new snapshot with the specified startup parameters. This -// method does not initialize the set of recent signers, so only ever use if for -// the genesis block. -func newSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *Snapshot { - snap := &Snapshot{ - config: config, - sigcache: sigcache, - Number: number, - Hash: hash, - Signers: make(map[common.Address]struct{}), - Recents: make(map[uint64]common.Address), - Tally: make(map[common.Address]Tally), - } - for _, signer := range signers { - snap.Signers[signer] = struct{}{} - } - return snap -} - -// loadSnapshot loads an existing snapshot from the database. -func loadSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*Snapshot, error) { - blob, err := db.Get(append([]byte("clique-"), hash[:]...)) - if err != nil { - return nil, err - } - snap := new(Snapshot) - if err := json.Unmarshal(blob, snap); err != nil { - return nil, err - } - snap.config = config - snap.sigcache = sigcache - - return snap, nil -} - -// store inserts the snapshot into the database. -func (s *Snapshot) store(db ethdb.Database) error { - blob, err := json.Marshal(s) - if err != nil { - return err - } - return db.Put(append([]byte("clique-"), s.Hash[:]...), blob) -} - -// copy creates a deep copy of the snapshot, though not the individual votes. -func (s *Snapshot) copy() *Snapshot { - cpy := &Snapshot{ - config: s.config, - sigcache: s.sigcache, - Number: s.Number, - Hash: s.Hash, - Signers: make(map[common.Address]struct{}), - Recents: make(map[uint64]common.Address), - Votes: make([]*Vote, len(s.Votes)), - Tally: make(map[common.Address]Tally), - } - for signer := range s.Signers { - cpy.Signers[signer] = struct{}{} - } - for block, signer := range s.Recents { - cpy.Recents[block] = signer - } - for address, tally := range s.Tally { - cpy.Tally[address] = tally - } - copy(cpy.Votes, s.Votes) - - return cpy -} - -// validVote returns whether it makes sense to cast the specified vote in the -// given snapshot context (e.g. don't try to add an already authorized signer). -func (s *Snapshot) validVote(address common.Address, authorize bool) bool { - _, signer := s.Signers[address] - return (signer && !authorize) || (!signer && authorize) -} - -// cast adds a new vote into the tally. -func (s *Snapshot) cast(address common.Address, authorize bool) bool { - // Ensure the vote is meaningful - if !s.validVote(address, authorize) { - return false - } - // Cast the vote into an existing or new tally - if old, ok := s.Tally[address]; ok { - old.Votes++ - s.Tally[address] = old - } else { - s.Tally[address] = Tally{Authorize: authorize, Votes: 1} - } - return true -} - -// uncast removes a previously cast vote from the tally. -func (s *Snapshot) uncast(address common.Address, authorize bool) bool { - // If there's no tally, it's a dangling vote, just drop - tally, ok := s.Tally[address] - if !ok { - return false - } - // Ensure we only revert counted votes - if tally.Authorize != authorize { - return false - } - // Otherwise revert the vote - if tally.Votes > 1 { - tally.Votes-- - s.Tally[address] = tally - } else { - delete(s.Tally, address) - } - return true -} - -// apply creates a new authorization snapshot by applying the given headers to -// the original one. -func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) { - // Allow passing in no headers for cleaner code - if len(headers) == 0 { - return s, nil - } - // Sanity check that the headers can be applied - for i := 0; i < len(headers)-1; i++ { - if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 { - return nil, errInvalidVotingChain - } - } - if headers[0].Number.Uint64() != s.Number+1 { - return nil, errInvalidVotingChain - } - // Iterate through the headers and create a new snapshot - snap := s.copy() - - var ( - start = time.Now() - logged = time.Now() - ) - for i, header := range headers { - // Remove any votes on checkpoint blocks - number := header.Number.Uint64() - if number%s.config.Epoch == 0 { - snap.Votes = nil - snap.Tally = make(map[common.Address]Tally) - } - // Delete the oldest signer from the recent list to allow it signing again - if limit := uint64(len(snap.Signers)/2 + 1); number >= limit { - delete(snap.Recents, number-limit) - } - // Resolve the authorization key and check against signers - signer, err := ecrecover(header, s.sigcache) - if err != nil { - return nil, err - } - if _, ok := snap.Signers[signer]; !ok { - return nil, errUnauthorizedSigner - } - for _, recent := range snap.Recents { - if recent == signer { - return nil, errRecentlySigned - } - } - snap.Recents[number] = signer - - // Header authorized, discard any previous votes from the signer - for i, vote := range snap.Votes { - if vote.Signer == signer && vote.Address == header.Coinbase { - // Uncast the vote from the cached tally - snap.uncast(vote.Address, vote.Authorize) - - // Uncast the vote from the chronological list - snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) - break // only one vote allowed - } - } - // Tally up the new vote from the signer - var authorize bool - switch { - case bytes.Equal(header.Nonce[:], nonceAuthVote): - authorize = true - case bytes.Equal(header.Nonce[:], nonceDropVote): - authorize = false - default: - return nil, errInvalidVote - } - if snap.cast(header.Coinbase, authorize) { - snap.Votes = append(snap.Votes, &Vote{ - Signer: signer, - Block: number, - Address: header.Coinbase, - Authorize: authorize, - }) - } - // If the vote passed, update the list of signers - if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 { - if tally.Authorize { - snap.Signers[header.Coinbase] = struct{}{} - } else { - delete(snap.Signers, header.Coinbase) - - // Signer list shrunk, delete any leftover recent caches - if limit := uint64(len(snap.Signers)/2 + 1); number >= limit { - delete(snap.Recents, number-limit) - } - // Discard any previous votes the deauthorized signer cast - for i := 0; i < len(snap.Votes); i++ { - if snap.Votes[i].Signer == header.Coinbase { - // Uncast the vote from the cached tally - snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize) - - // Uncast the vote from the chronological list - snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) - - i-- - } - } - } - // Discard any previous votes around the just changed account - for i := 0; i < len(snap.Votes); i++ { - if snap.Votes[i].Address == header.Coinbase { - snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) - i-- - } - } - delete(snap.Tally, header.Coinbase) - } - // If we're taking too much time (ecrecover), notify the user once a while - if time.Since(logged) > 8*time.Second { - log.Info("Reconstructing voting history", "processed", i, "total", len(headers), "elapsed", common.PrettyDuration(time.Since(start))) - logged = time.Now() - } - } - if time.Since(start) > 8*time.Second { - log.Info("Reconstructed voting history", "processed", len(headers), "elapsed", common.PrettyDuration(time.Since(start))) - } - snap.Number += uint64(len(headers)) - snap.Hash = headers[len(headers)-1].Hash() - - return snap, nil -} - -// signers retrieves the list of authorized signers in ascending order. -func (s *Snapshot) signers() []common.Address { - sigs := make([]common.Address, 0, len(s.Signers)) - for sig := range s.Signers { - sigs = append(sigs, sig) - } - sort.Sort(signersAscending(sigs)) - return sigs -} - -// inturn returns if a signer at a given block height is in-turn or not. -func (s *Snapshot) inturn(number uint64, signer common.Address) bool { - signers, offset := s.signers(), 0 - for offset < len(signers) && signers[offset] != signer { - offset++ - } - return (number % uint64(len(signers))) == uint64(offset) -}
diff --git go-ethereum/consensus/clique/snapshot_test.go celo/consensus/clique/snapshot_test.go deleted file mode 100644 index 1fa26845dac0c7e6ede1fd94a783aec9c17de65b..0000000000000000000000000000000000000000 --- go-ethereum/consensus/clique/snapshot_test.go +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package clique - -import ( - "bytes" - "crypto/ecdsa" - "math/big" - "sort" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/params" -) - -// testerAccountPool is a pool to maintain currently active tester accounts, -// mapped from textual names used in the tests below to actual Ethereum private -// keys capable of signing transactions. -type testerAccountPool struct { - accounts map[string]*ecdsa.PrivateKey -} - -func newTesterAccountPool() *testerAccountPool { - return &testerAccountPool{ - accounts: make(map[string]*ecdsa.PrivateKey), - } -} - -// checkpoint creates a Clique checkpoint signer section from the provided list -// of authorized signers and embeds it into the provided header. -func (ap *testerAccountPool) checkpoint(header *types.Header, signers []string) { - auths := make([]common.Address, len(signers)) - for i, signer := range signers { - auths[i] = ap.address(signer) - } - sort.Sort(signersAscending(auths)) - for i, auth := range auths { - copy(header.Extra[extraVanity+i*common.AddressLength:], auth.Bytes()) - } -} - -// address retrieves the Ethereum address of a tester account by label, creating -// a new account if no previous one exists yet. -func (ap *testerAccountPool) address(account string) common.Address { - // Return the zero account for non-addresses - if account == "" { - return common.Address{} - } - // Ensure we have a persistent key for the account - if ap.accounts[account] == nil { - ap.accounts[account], _ = crypto.GenerateKey() - } - // Resolve and return the Ethereum address - return crypto.PubkeyToAddress(ap.accounts[account].PublicKey) -} - -// sign calculates a Clique digital signature for the given block and embeds it -// back into the header. -func (ap *testerAccountPool) sign(header *types.Header, signer string) { - // Ensure we have a persistent key for the signer - if ap.accounts[signer] == nil { - ap.accounts[signer], _ = crypto.GenerateKey() - } - // Sign the header and embed the signature in extra data - sig, _ := crypto.Sign(SealHash(header).Bytes(), ap.accounts[signer]) - copy(header.Extra[len(header.Extra)-extraSeal:], sig) -} - -// testerVote represents a single block signed by a parcitular account, where -// the account may or may not have cast a Clique vote. -type testerVote struct { - signer string - voted string - auth bool - checkpoint []string - newbatch bool -} - -// Tests that Clique signer voting is evaluated correctly for various simple and -// complex scenarios, as well as that a few special corner cases fail correctly. -func TestClique(t *testing.T) { - // Define the various voting scenarios to test - tests := []struct { - epoch uint64 - signers []string - votes []testerVote - results []string - failure error - }{ - { - // Single signer, no votes cast - signers: []string{"A"}, - votes: []testerVote{{signer: "A"}}, - results: []string{"A"}, - }, { - // Single signer, voting to add two others (only accept first, second needs 2 votes) - signers: []string{"A"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: true}, - {signer: "B"}, - {signer: "A", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, { - // Two signers, voting to add three others (only accept first two, third needs 3 votes already) - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B", voted: "C", auth: true}, - {signer: "A", voted: "D", auth: true}, - {signer: "B", voted: "D", auth: true}, - {signer: "C"}, - {signer: "A", voted: "E", auth: true}, - {signer: "B", voted: "E", auth: true}, - }, - results: []string{"A", "B", "C", "D"}, - }, { - // Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this) - signers: []string{"A"}, - votes: []testerVote{ - {signer: "A", voted: "A", auth: false}, - }, - results: []string{}, - }, { - // Two signers, actually needing mutual consent to drop either of them (not fulfilled) - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Two signers, actually needing mutual consent to drop either of them (fulfilled) - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: false}, - {signer: "B", voted: "B", auth: false}, - }, - results: []string{"A"}, - }, { - // Three signers, two of them deciding to drop the third - signers: []string{"A", "B", "C"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Four signers, consensus of two not being enough to drop anyone - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - }, - results: []string{"A", "B", "C", "D"}, - }, { - // Four signers, consensus of three already being enough to drop someone - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - }, - results: []string{"A", "B", "C"}, - }, { - // Authorizations are counted once per signer per target - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, { - // Authorizing multiple accounts concurrently is permitted - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A", voted: "D", auth: true}, - {signer: "B"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: true}, - {signer: "A"}, - {signer: "B", voted: "C", auth: true}, - }, - results: []string{"A", "B", "C", "D"}, - }, { - // Deauthorizations are counted once per signer per target - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "B", auth: false}, - {signer: "B"}, - {signer: "A", voted: "B", auth: false}, - {signer: "B"}, - {signer: "A", voted: "B", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Deauthorizing multiple accounts concurrently is permitted - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - {signer: "A"}, - {signer: "B", voted: "C", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Votes from deauthorized signers are discarded immediately (deauth votes) - signers: []string{"A", "B", "C"}, - votes: []testerVote{ - {signer: "C", voted: "B", auth: false}, - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "A", voted: "B", auth: false}, - }, - results: []string{"A", "B"}, - }, { - // Votes from deauthorized signers are discarded immediately (auth votes) - signers: []string{"A", "B", "C"}, - votes: []testerVote{ - {signer: "C", voted: "D", auth: true}, - {signer: "A", voted: "C", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "A", voted: "D", auth: true}, - }, - results: []string{"A", "B"}, - }, { - // Cascading changes are not allowed, only the account being voted on may change - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - }, - results: []string{"A", "B", "C"}, - }, { - // Changes reaching consensus out of bounds (via a deauth) execute on touch - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - {signer: "A"}, - {signer: "C", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, { - // Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch - signers: []string{"A", "B", "C", "D"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: false}, - {signer: "B"}, - {signer: "C"}, - {signer: "A", voted: "D", auth: false}, - {signer: "B", voted: "C", auth: false}, - {signer: "C"}, - {signer: "A"}, - {signer: "B", voted: "D", auth: false}, - {signer: "C", voted: "D", auth: false}, - {signer: "A"}, - {signer: "B", voted: "C", auth: true}, - }, - results: []string{"A", "B", "C"}, - }, { - // Ensure that pending votes don't survive authorization status changes. This - // corner case can only appear if a signer is quickly added, removed and then - // readded (or the inverse), while one of the original voters dropped. If a - // past vote is left cached in the system somewhere, this will interfere with - // the final signer outcome. - signers: []string{"A", "B", "C", "D", "E"}, - votes: []testerVote{ - {signer: "A", voted: "F", auth: true}, // Authorize F, 3 votes needed - {signer: "B", voted: "F", auth: true}, - {signer: "C", voted: "F", auth: true}, - {signer: "D", voted: "F", auth: false}, // Deauthorize F, 4 votes needed (leave A's previous vote "unchanged") - {signer: "E", voted: "F", auth: false}, - {signer: "B", voted: "F", auth: false}, - {signer: "C", voted: "F", auth: false}, - {signer: "D", voted: "F", auth: true}, // Almost authorize F, 2/3 votes needed - {signer: "E", voted: "F", auth: true}, - {signer: "B", voted: "A", auth: false}, // Deauthorize A, 3 votes needed - {signer: "C", voted: "A", auth: false}, - {signer: "D", voted: "A", auth: false}, - {signer: "B", voted: "F", auth: true}, // Finish authorizing F, 3/3 votes needed - }, - results: []string{"B", "C", "D", "E", "F"}, - }, { - // Epoch transitions reset all votes to allow chain checkpointing - epoch: 3, - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A", voted: "C", auth: true}, - {signer: "B"}, - {signer: "A", checkpoint: []string{"A", "B"}}, - {signer: "B", voted: "C", auth: true}, - }, - results: []string{"A", "B"}, - }, { - // An unauthorized signer should not be able to sign blocks - signers: []string{"A"}, - votes: []testerVote{ - {signer: "B"}, - }, - failure: errUnauthorizedSigner, - }, { - // An authorized signer that signed recenty should not be able to sign again - signers: []string{"A", "B"}, - votes: []testerVote{ - {signer: "A"}, - {signer: "A"}, - }, - failure: errRecentlySigned, - }, { - // Recent signatures should not reset on checkpoint blocks imported in a batch - epoch: 3, - signers: []string{"A", "B", "C"}, - votes: []testerVote{ - {signer: "A"}, - {signer: "B"}, - {signer: "A", checkpoint: []string{"A", "B", "C"}}, - {signer: "A"}, - }, - failure: errRecentlySigned, - }, { - // Recent signatures should not reset on checkpoint blocks imported in a new - // batch (https://github.com/ethereum/go-ethereum/issues/17593). Whilst this - // seems overly specific and weird, it was a Rinkeby consensus split. - epoch: 3, - signers: []string{"A", "B", "C"}, - votes: []testerVote{ - {signer: "A"}, - {signer: "B"}, - {signer: "A", checkpoint: []string{"A", "B", "C"}}, - {signer: "A", newbatch: true}, - }, - failure: errRecentlySigned, - }, - } - // Run through the scenarios and test them - for i, tt := range tests { - // Create the account pool and generate the initial set of signers - accounts := newTesterAccountPool() - - signers := make([]common.Address, len(tt.signers)) - for j, signer := range tt.signers { - signers[j] = accounts.address(signer) - } - for j := 0; j < len(signers); j++ { - for k := j + 1; k < len(signers); k++ { - if bytes.Compare(signers[j][:], signers[k][:]) > 0 { - signers[j], signers[k] = signers[k], signers[j] - } - } - } - // Create the genesis block with the initial set of signers - genesis := &core.Genesis{ - ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal), - BaseFee: big.NewInt(params.InitialBaseFee), - } - for j, signer := range signers { - copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:]) - } - // Create a pristine blockchain with the genesis injected - db := rawdb.NewMemoryDatabase() - genesis.Commit(db) - - // Assemble a chain of headers from the cast votes - config := *params.TestChainConfig - config.Clique = &params.CliqueConfig{ - Period: 1, - Epoch: tt.epoch, - } - engine := New(config.Clique, db) - engine.fakeDiff = true - - blocks, _ := core.GenerateChain(&config, genesis.ToBlock(db), engine, db, len(tt.votes), func(j int, gen *core.BlockGen) { - // Cast the vote contained in this block - gen.SetCoinbase(accounts.address(tt.votes[j].voted)) - if tt.votes[j].auth { - var nonce types.BlockNonce - copy(nonce[:], nonceAuthVote) - gen.SetNonce(nonce) - } - }) - // Iterate through the blocks and seal them individually - for j, block := range blocks { - // Get the header and prepare it for signing - header := block.Header() - if j > 0 { - header.ParentHash = blocks[j-1].Hash() - } - header.Extra = make([]byte, extraVanity+extraSeal) - if auths := tt.votes[j].checkpoint; auths != nil { - header.Extra = make([]byte, extraVanity+len(auths)*common.AddressLength+extraSeal) - accounts.checkpoint(header, auths) - } - header.Difficulty = diffInTurn // Ignored, we just need a valid number - - // Generate the signature, embed it into the header and the block - accounts.sign(header, tt.votes[j].signer) - blocks[j] = block.WithSeal(header) - } - // Split the blocks up into individual import batches (cornercase testing) - batches := [][]*types.Block{nil} - for j, block := range blocks { - if tt.votes[j].newbatch { - batches = append(batches, nil) - } - batches[len(batches)-1] = append(batches[len(batches)-1], block) - } - // Pass all the headers through clique and ensure tallying succeeds - chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil) - if err != nil { - t.Errorf("test %d: failed to create test chain: %v", i, err) - continue - } - failed := false - for j := 0; j < len(batches)-1; j++ { - if k, err := chain.InsertChain(batches[j]); err != nil { - t.Errorf("test %d: failed to import batch %d, block %d: %v", i, j, k, err) - failed = true - break - } - } - if failed { - continue - } - if _, err = chain.InsertChain(batches[len(batches)-1]); err != tt.failure { - t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) - } - if tt.failure != nil { - continue - } - // No failure was produced or requested, generate the final voting snapshot - head := blocks[len(blocks)-1] - - snap, err := engine.snapshot(chain, head.NumberU64(), head.Hash(), nil) - if err != nil { - t.Errorf("test %d: failed to retrieve voting snapshot: %v", i, err) - continue - } - // Verify the final list of signers against the expected ones - signers = make([]common.Address, len(tt.results)) - for j, signer := range tt.results { - signers[j] = accounts.address(signer) - } - for j := 0; j < len(signers); j++ { - for k := j + 1; k < len(signers); k++ { - if bytes.Compare(signers[j][:], signers[k][:]) > 0 { - signers[j], signers[k] = signers[k], signers[j] - } - } - } - result := snap.signers() - if len(result) != len(signers) { - t.Errorf("test %d: signers mismatch: have %x, want %x", i, result, signers) - continue - } - for j := 0; j < len(result); j++ { - if !bytes.Equal(result[j][:], signers[j][:]) { - t.Errorf("test %d, signer %d: signer mismatch: have %x, want %x", i, j, result[j], signers[j]) - } - } - } -}
diff --git go-ethereum/consensus/clique/clique_test.go celo/consensus/clique/clique_test.go deleted file mode 100644 index 5e35189f308ad2d021b5ee3f1b1014b01056664c..0000000000000000000000000000000000000000 --- go-ethereum/consensus/clique/clique_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package clique - -import ( - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/params" -) - -// This test case is a repro of an annoying bug that took us forever to catch. -// In Clique PoA networks (Rinkeby, Görli, etc), consecutive blocks might have -// the same state root (no block subsidy, empty block). If a node crashes, the -// chain ends up losing the recent state and needs to regenerate it from blocks -// already in the database. The bug was that processing the block *prior* to an -// empty one **also completes** the empty one, ending up in a known-block error. -func TestReimportMirroredState(t *testing.T) { - // Initialize a Clique chain with a single signer - var ( - db = rawdb.NewMemoryDatabase() - key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - addr = crypto.PubkeyToAddress(key.PublicKey) - engine = New(params.AllCliqueProtocolChanges.Clique, db) - signer = new(types.HomesteadSigner) - ) - genspec := &core.Genesis{ - ExtraData: make([]byte, extraVanity+common.AddressLength+extraSeal), - Alloc: map[common.Address]core.GenesisAccount{ - addr: {Balance: big.NewInt(10000000000000000)}, - }, - BaseFee: big.NewInt(params.InitialBaseFee), - } - copy(genspec.ExtraData[extraVanity:], addr[:]) - genesis := genspec.MustCommit(db) - - // Generate a batch of blocks, each properly signed - chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil) - defer chain.Stop() - - blocks, _ := core.GenerateChain(params.AllCliqueProtocolChanges, genesis, engine, db, 3, func(i int, block *core.BlockGen) { - // The chain maker doesn't have access to a chain, so the difficulty will be - // lets unset (nil). Set it here to the correct value. - block.SetDifficulty(diffInTurn) - - // We want to simulate an empty middle block, having the same state as the - // first one. The last is needs a state change again to force a reorg. - if i != 1 { - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr), common.Address{0x00}, new(big.Int), params.TxGas, block.BaseFee(), nil), signer, key) - if err != nil { - panic(err) - } - block.AddTxWithChain(chain, tx) - } - }) - for i, block := range blocks { - header := block.Header() - if i > 0 { - header.ParentHash = blocks[i-1].Hash() - } - header.Extra = make([]byte, extraVanity+extraSeal) - header.Difficulty = diffInTurn - - sig, _ := crypto.Sign(SealHash(header).Bytes(), key) - copy(header.Extra[len(header.Extra)-extraSeal:], sig) - blocks[i] = block.WithSeal(header) - } - // Insert the first two blocks and make sure the chain is valid - db = rawdb.NewMemoryDatabase() - genspec.MustCommit(db) - - chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil) - defer chain.Stop() - - if _, err := chain.InsertChain(blocks[:2]); err != nil { - t.Fatalf("failed to insert initial blocks: %v", err) - } - if head := chain.CurrentBlock().NumberU64(); head != 2 { - t.Fatalf("chain head mismatch: have %d, want %d", head, 2) - } - - // Simulate a crash by creating a new chain on top of the database, without - // flushing the dirty states out. Insert the last block, triggering a sidechain - // reimport. - chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil) - defer chain.Stop() - - if _, err := chain.InsertChain(blocks[2:]); err != nil { - t.Fatalf("failed to insert final block: %v", err) - } - if head := chain.CurrentBlock().NumberU64(); head != 3 { - t.Fatalf("chain head mismatch: have %d, want %d", head, 3) - } -} - -func TestSealHash(t *testing.T) { - have := SealHash(&types.Header{ - Difficulty: new(big.Int), - Number: new(big.Int), - Extra: make([]byte, 32+65), - BaseFee: new(big.Int), - }) - want := common.HexToHash("0xbd3d1fa43fbc4c5bfcc91b179ec92e2861df3654de60468beb908ff805359e8f") - if have != want { - t.Errorf("have %x, want %x", have, want) - } -}
diff --git go-ethereum/consensus/clique/api.go celo/consensus/clique/api.go deleted file mode 100644 index 2e218afb1d84c049b8014965d74f60b5b7f73c48..0000000000000000000000000000000000000000 --- go-ethereum/consensus/clique/api.go +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package clique - -import ( - "encoding/json" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" -) - -// API is a user facing RPC API to allow controlling the signer and voting -// mechanisms of the proof-of-authority scheme. -type API struct { - chain consensus.ChainHeaderReader - clique *Clique -} - -// GetSnapshot retrieves the state snapshot at a given block. -func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) { - // Retrieve the requested block number (or current if none requested) - var header *types.Header - if number == nil || *number == rpc.LatestBlockNumber { - header = api.chain.CurrentHeader() - } else { - header = api.chain.GetHeaderByNumber(uint64(number.Int64())) - } - // Ensure we have an actually valid block and return its snapshot - if header == nil { - return nil, errUnknownBlock - } - return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) -} - -// GetSnapshotAtHash retrieves the state snapshot at a given block. -func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) { - header := api.chain.GetHeaderByHash(hash) - if header == nil { - return nil, errUnknownBlock - } - return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) -} - -// GetSigners retrieves the list of authorized signers at the specified block. -func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) { - // Retrieve the requested block number (or current if none requested) - var header *types.Header - if number == nil || *number == rpc.LatestBlockNumber { - header = api.chain.CurrentHeader() - } else { - header = api.chain.GetHeaderByNumber(uint64(number.Int64())) - } - // Ensure we have an actually valid block and return the signers from its snapshot - if header == nil { - return nil, errUnknownBlock - } - snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) - if err != nil { - return nil, err - } - return snap.signers(), nil -} - -// GetSignersAtHash retrieves the list of authorized signers at the specified block. -func (api *API) GetSignersAtHash(hash common.Hash) ([]common.Address, error) { - header := api.chain.GetHeaderByHash(hash) - if header == nil { - return nil, errUnknownBlock - } - snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) - if err != nil { - return nil, err - } - return snap.signers(), nil -} - -// Proposals returns the current proposals the node tries to uphold and vote on. -func (api *API) Proposals() map[common.Address]bool { - api.clique.lock.RLock() - defer api.clique.lock.RUnlock() - - proposals := make(map[common.Address]bool) - for address, auth := range api.clique.proposals { - proposals[address] = auth - } - return proposals -} - -// Propose injects a new authorization proposal that the signer will attempt to -// push through. -func (api *API) Propose(address common.Address, auth bool) { - api.clique.lock.Lock() - defer api.clique.lock.Unlock() - - api.clique.proposals[address] = auth -} - -// Discard drops a currently running proposal, stopping the signer from casting -// further votes (either for or against). -func (api *API) Discard(address common.Address) { - api.clique.lock.Lock() - defer api.clique.lock.Unlock() - - delete(api.clique.proposals, address) -} - -type status struct { - InturnPercent float64 `json:"inturnPercent"` - SigningStatus map[common.Address]int `json:"sealerActivity"` - NumBlocks uint64 `json:"numBlocks"` -} - -// Status returns the status of the last N blocks, -// - the number of active signers, -// - the number of signers, -// - the percentage of in-turn blocks -func (api *API) Status() (*status, error) { - var ( - numBlocks = uint64(64) - header = api.chain.CurrentHeader() - diff = uint64(0) - optimals = 0 - ) - snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) - if err != nil { - return nil, err - } - var ( - signers = snap.signers() - end = header.Number.Uint64() - start = end - numBlocks - ) - if numBlocks > end { - start = 1 - numBlocks = end - start - } - signStatus := make(map[common.Address]int) - for _, s := range signers { - signStatus[s] = 0 - } - for n := start; n < end; n++ { - h := api.chain.GetHeaderByNumber(n) - if h == nil { - return nil, fmt.Errorf("missing block %d", n) - } - if h.Difficulty.Cmp(diffInTurn) == 0 { - optimals++ - } - diff += h.Difficulty.Uint64() - sealer, err := api.clique.Author(h) - if err != nil { - return nil, err - } - signStatus[sealer]++ - } - return &status{ - InturnPercent: float64(100*optimals) / float64(numBlocks), - SigningStatus: signStatus, - NumBlocks: numBlocks, - }, nil -} - -type blockNumberOrHashOrRLP struct { - *rpc.BlockNumberOrHash - RLP hexutil.Bytes `json:"rlp,omitempty"` -} - -func (sb *blockNumberOrHashOrRLP) UnmarshalJSON(data []byte) error { - bnOrHash := new(rpc.BlockNumberOrHash) - // Try to unmarshal bNrOrHash - if err := bnOrHash.UnmarshalJSON(data); err == nil { - sb.BlockNumberOrHash = bnOrHash - return nil - } - // Try to unmarshal RLP - var input string - if err := json.Unmarshal(data, &input); err != nil { - return err - } - sb.RLP = hexutil.MustDecode(input) - return nil -} - -// GetSigner returns the signer for a specific clique block. -// Can be called with either a blocknumber, blockhash or an rlp encoded blob. -// The RLP encoded blob can either be a block or a header. -func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (common.Address, error) { - if len(rlpOrBlockNr.RLP) == 0 { - blockNrOrHash := rlpOrBlockNr.BlockNumberOrHash - var header *types.Header - if blockNrOrHash == nil { - header = api.chain.CurrentHeader() - } else if hash, ok := blockNrOrHash.Hash(); ok { - header = api.chain.GetHeaderByHash(hash) - } else if number, ok := blockNrOrHash.Number(); ok { - header = api.chain.GetHeaderByNumber(uint64(number.Int64())) - } - return api.clique.Author(header) - } - block := new(types.Block) - if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, block); err == nil { - return api.clique.Author(block.Header()) - } - header := new(types.Header) - if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, header); err != nil { - return common.Address{}, err - } - return api.clique.Author(header) -}
diff --git go-ethereum/consensus/clique/clique.go celo/consensus/clique/clique.go deleted file mode 100644 index 52e784f745270df0a8cf8444bb3eaa5ea5f4d64c..0000000000000000000000000000000000000000 --- go-ethereum/consensus/clique/clique.go +++ /dev/null @@ -1,754 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Package clique implements the proof-of-authority consensus engine. -package clique - -import ( - "bytes" - "errors" - "fmt" - "io" - "math/big" - "math/rand" - "sync" - "time" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/misc" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/trie" - lru "github.com/hashicorp/golang-lru" - "golang.org/x/crypto/sha3" -) - -const ( - checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database - inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory - inmemorySignatures = 4096 // Number of recent block signatures to keep in memory - - wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers -) - -// Clique proof-of-authority protocol constants. -var ( - epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes - - extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity - extraSeal = crypto.SignatureLength // Fixed number of extra-data suffix bytes reserved for signer seal - - nonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer - nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer. - - uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW. - - diffInTurn = big.NewInt(2) // Block difficulty for in-turn signatures - diffNoTurn = big.NewInt(1) // Block difficulty for out-of-turn signatures -) - -// Various error messages to mark blocks invalid. These should be private to -// prevent engine specific errors from being referenced in the remainder of the -// codebase, inherently breaking if the engine is swapped out. Please put common -// error types into the consensus package. -var ( - // errUnknownBlock is returned when the list of signers is requested for a block - // that is not part of the local blockchain. - errUnknownBlock = errors.New("unknown block") - - // errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition - // block has a beneficiary set to non-zeroes. - errInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero") - - // errInvalidVote is returned if a nonce value is something else that the two - // allowed constants of 0x00..0 or 0xff..f. - errInvalidVote = errors.New("vote nonce not 0x00..0 or 0xff..f") - - // errInvalidCheckpointVote is returned if a checkpoint/epoch transition block - // has a vote nonce set to non-zeroes. - errInvalidCheckpointVote = errors.New("vote nonce in checkpoint block non-zero") - - // errMissingVanity is returned if a block's extra-data section is shorter than - // 32 bytes, which is required to store the signer vanity. - errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing") - - // errMissingSignature is returned if a block's extra-data section doesn't seem - // to contain a 65 byte secp256k1 signature. - errMissingSignature = errors.New("extra-data 65 byte signature suffix missing") - - // errExtraSigners is returned if non-checkpoint block contain signer data in - // their extra-data fields. - errExtraSigners = errors.New("non-checkpoint block contains extra signer list") - - // errInvalidCheckpointSigners is returned if a checkpoint block contains an - // invalid list of signers (i.e. non divisible by 20 bytes). - errInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block") - - // errMismatchingCheckpointSigners is returned if a checkpoint block contains a - // list of signers different than the one the local node calculated. - errMismatchingCheckpointSigners = errors.New("mismatching signer list on checkpoint block") - - // errInvalidMixDigest is returned if a block's mix digest is non-zero. - errInvalidMixDigest = errors.New("non-zero mix digest") - - // errInvalidUncleHash is returned if a block contains an non-empty uncle list. - errInvalidUncleHash = errors.New("non empty uncle hash") - - // errInvalidDifficulty is returned if the difficulty of a block neither 1 or 2. - errInvalidDifficulty = errors.New("invalid difficulty") - - // errWrongDifficulty is returned if the difficulty of a block doesn't match the - // turn of the signer. - errWrongDifficulty = errors.New("wrong difficulty") - - // errInvalidTimestamp is returned if the timestamp of a block is lower than - // the previous block's timestamp + the minimum block period. - errInvalidTimestamp = errors.New("invalid timestamp") - - // errInvalidVotingChain is returned if an authorization list is attempted to - // be modified via out-of-range or non-contiguous headers. - errInvalidVotingChain = errors.New("invalid voting chain") - - // errUnauthorizedSigner is returned if a header is signed by a non-authorized entity. - errUnauthorizedSigner = errors.New("unauthorized signer") - - // errRecentlySigned is returned if a header is signed by an authorized entity - // that already signed a header recently, thus is temporarily not allowed to. - errRecentlySigned = errors.New("recently signed") -) - -// SignerFn hashes and signs the data to be signed by a backing account. -type SignerFn func(signer accounts.Account, mimeType string, message []byte) ([]byte, error) - -// ecrecover extracts the Ethereum account address from a signed header. -func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { - // If the signature's already cached, return that - hash := header.Hash() - if address, known := sigcache.Get(hash); known { - return address.(common.Address), nil - } - // Retrieve the signature from the header extra-data - if len(header.Extra) < extraSeal { - return common.Address{}, errMissingSignature - } - signature := header.Extra[len(header.Extra)-extraSeal:] - - // Recover the public key and the Ethereum address - pubkey, err := crypto.Ecrecover(SealHash(header).Bytes(), signature) - if err != nil { - return common.Address{}, err - } - var signer common.Address - copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) - - sigcache.Add(hash, signer) - return signer, nil -} - -// Clique is the proof-of-authority consensus engine proposed to support the -// Ethereum testnet following the Ropsten attacks. -type Clique struct { - config *params.CliqueConfig // Consensus engine configuration parameters - db ethdb.Database // Database to store and retrieve snapshot checkpoints - - recents *lru.ARCCache // Snapshots for recent block to speed up reorgs - signatures *lru.ARCCache // Signatures of recent blocks to speed up mining - - proposals map[common.Address]bool // Current list of proposals we are pushing - - signer common.Address // Ethereum address of the signing key - signFn SignerFn // Signer function to authorize hashes with - lock sync.RWMutex // Protects the signer fields - - // The fields below are for testing only - fakeDiff bool // Skip difficulty verifications -} - -// New creates a Clique proof-of-authority consensus engine with the initial -// signers set to the ones provided by the user. -func New(config *params.CliqueConfig, db ethdb.Database) *Clique { - // Set any missing consensus parameters to their defaults - conf := *config - if conf.Epoch == 0 { - conf.Epoch = epochLength - } - // Allocate the snapshot caches and create the engine - recents, _ := lru.NewARC(inmemorySnapshots) - signatures, _ := lru.NewARC(inmemorySignatures) - - return &Clique{ - config: &conf, - db: db, - recents: recents, - signatures: signatures, - proposals: make(map[common.Address]bool), - } -} - -// Author implements consensus.Engine, returning the Ethereum address recovered -// from the signature in the header's extra-data section. -func (c *Clique) Author(header *types.Header) (common.Address, error) { - return ecrecover(header, c.signatures) -} - -// VerifyHeader checks whether a header conforms to the consensus rules. -func (c *Clique) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { - return c.verifyHeader(chain, header, nil) -} - -// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The -// method returns a quit channel to abort the operations and a results channel to -// retrieve the async verifications (the order is that of the input slice). -func (c *Clique) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { - abort := make(chan struct{}) - results := make(chan error, len(headers)) - - go func() { - for i, header := range headers { - err := c.verifyHeader(chain, header, headers[:i]) - - select { - case <-abort: - return - case results <- err: - } - } - }() - return abort, results -} - -// verifyHeader checks whether a header conforms to the consensus rules.The -// caller may optionally pass in a batch of parents (ascending order) to avoid -// looking those up from the database. This is useful for concurrently verifying -// a batch of new headers. -func (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { - if header.Number == nil { - return errUnknownBlock - } - number := header.Number.Uint64() - - // Don't waste time checking blocks from the future - if header.Time > uint64(time.Now().Unix()) { - return consensus.ErrFutureBlock - } - // Checkpoint blocks need to enforce zero beneficiary - checkpoint := (number % c.config.Epoch) == 0 - if checkpoint && header.Coinbase != (common.Address{}) { - return errInvalidCheckpointBeneficiary - } - // Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints - if !bytes.Equal(header.Nonce[:], nonceAuthVote) && !bytes.Equal(header.Nonce[:], nonceDropVote) { - return errInvalidVote - } - if checkpoint && !bytes.Equal(header.Nonce[:], nonceDropVote) { - return errInvalidCheckpointVote - } - // Check that the extra-data contains both the vanity and signature - if len(header.Extra) < extraVanity { - return errMissingVanity - } - if len(header.Extra) < extraVanity+extraSeal { - return errMissingSignature - } - // Ensure that the extra-data contains a signer list on checkpoint, but none otherwise - signersBytes := len(header.Extra) - extraVanity - extraSeal - if !checkpoint && signersBytes != 0 { - return errExtraSigners - } - if checkpoint && signersBytes%common.AddressLength != 0 { - return errInvalidCheckpointSigners - } - // Ensure that the mix digest is zero as we don't have fork protection currently - if header.MixDigest != (common.Hash{}) { - return errInvalidMixDigest - } - // Ensure that the block doesn't contain any uncles which are meaningless in PoA - if header.UncleHash != uncleHash { - return errInvalidUncleHash - } - // Ensure that the block's difficulty is meaningful (may not be correct at this point) - if number > 0 { - if header.Difficulty == nil || (header.Difficulty.Cmp(diffInTurn) != 0 && header.Difficulty.Cmp(diffNoTurn) != 0) { - return errInvalidDifficulty - } - } - // Verify that the gas limit is <= 2^63-1 - cap := uint64(0x7fffffffffffffff) - if header.GasLimit > cap { - return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap) - } - // If all checks passed, validate any special fields for hard forks - if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil { - return err - } - // All basic checks passed, verify cascading fields - return c.verifyCascadingFields(chain, header, parents) -} - -// verifyCascadingFields verifies all the header fields that are not standalone, -// rather depend on a batch of previous headers. The caller may optionally pass -// in a batch of parents (ascending order) to avoid looking those up from the -// database. This is useful for concurrently verifying a batch of new headers. -func (c *Clique) verifyCascadingFields(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { - // The genesis block is the always valid dead-end - number := header.Number.Uint64() - if number == 0 { - return nil - } - // Ensure that the block's timestamp isn't too close to its parent - var parent *types.Header - if len(parents) > 0 { - parent = parents[len(parents)-1] - } else { - parent = chain.GetHeader(header.ParentHash, number-1) - } - if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash { - return consensus.ErrUnknownAncestor - } - if parent.Time+c.config.Period > header.Time { - return errInvalidTimestamp - } - // Verify that the gasUsed is <= gasLimit - if header.GasUsed > header.GasLimit { - return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) - } - if !chain.Config().IsLondon(header.Number) { - // Verify BaseFee not present before EIP-1559 fork. - if header.BaseFee != nil { - return fmt.Errorf("invalid baseFee before fork: have %d, want <nil>", header.BaseFee) - } - if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil { - return err - } - } else if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil { - // Verify the header's EIP-1559 attributes. - return err - } - // Retrieve the snapshot needed to verify this header and cache it - snap, err := c.snapshot(chain, number-1, header.ParentHash, parents) - if err != nil { - return err - } - // If the block is a checkpoint block, verify the signer list - if number%c.config.Epoch == 0 { - signers := make([]byte, len(snap.Signers)*common.AddressLength) - for i, signer := range snap.signers() { - copy(signers[i*common.AddressLength:], signer[:]) - } - extraSuffix := len(header.Extra) - extraSeal - if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) { - return errMismatchingCheckpointSigners - } - } - // All basic checks passed, verify the seal and return - return c.verifySeal(chain, header, parents) -} - -// snapshot retrieves the authorization snapshot at a given point in time. -func (c *Clique) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) { - // Search for a snapshot in memory or on disk for checkpoints - var ( - headers []*types.Header - snap *Snapshot - ) - for snap == nil { - // If an in-memory snapshot was found, use that - if s, ok := c.recents.Get(hash); ok { - snap = s.(*Snapshot) - break - } - // If an on-disk checkpoint snapshot can be found, use that - if number%checkpointInterval == 0 { - if s, err := loadSnapshot(c.config, c.signatures, c.db, hash); err == nil { - log.Trace("Loaded voting snapshot from disk", "number", number, "hash", hash) - snap = s - break - } - } - // If we're at the genesis, snapshot the initial state. Alternatively if we're - // at a checkpoint block without a parent (light client CHT), or we have piled - // up more headers than allowed to be reorged (chain reinit from a freezer), - // consider the checkpoint trusted and snapshot it. - if number == 0 || (number%c.config.Epoch == 0 && (len(headers) > params.FullImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) { - checkpoint := chain.GetHeaderByNumber(number) - if checkpoint != nil { - hash := checkpoint.Hash() - - signers := make([]common.Address, (len(checkpoint.Extra)-extraVanity-extraSeal)/common.AddressLength) - for i := 0; i < len(signers); i++ { - copy(signers[i][:], checkpoint.Extra[extraVanity+i*common.AddressLength:]) - } - snap = newSnapshot(c.config, c.signatures, number, hash, signers) - if err := snap.store(c.db); err != nil { - return nil, err - } - log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash) - break - } - } - // No snapshot for this header, gather the header and move backward - var header *types.Header - if len(parents) > 0 { - // If we have explicit parents, pick from there (enforced) - header = parents[len(parents)-1] - if header.Hash() != hash || header.Number.Uint64() != number { - return nil, consensus.ErrUnknownAncestor - } - parents = parents[:len(parents)-1] - } else { - // No explicit parents (or no more left), reach out to the database - header = chain.GetHeader(hash, number) - if header == nil { - return nil, consensus.ErrUnknownAncestor - } - } - headers = append(headers, header) - number, hash = number-1, header.ParentHash - } - // Previous snapshot found, apply any pending headers on top of it - for i := 0; i < len(headers)/2; i++ { - headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i] - } - snap, err := snap.apply(headers) - if err != nil { - return nil, err - } - c.recents.Add(snap.Hash, snap) - - // If we've generated a new checkpoint snapshot, save to disk - if snap.Number%checkpointInterval == 0 && len(headers) > 0 { - if err = snap.store(c.db); err != nil { - return nil, err - } - log.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash) - } - return snap, err -} - -// VerifyUncles implements consensus.Engine, always returning an error for any -// uncles as this consensus mechanism doesn't permit uncles. -func (c *Clique) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { - if len(block.Uncles()) > 0 { - return errors.New("uncles not allowed") - } - return nil -} - -// verifySeal checks whether the signature contained in the header satisfies the -// consensus protocol requirements. The method accepts an optional list of parent -// headers that aren't yet part of the local blockchain to generate the snapshots -// from. -func (c *Clique) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { - // Verifying the genesis block is not supported - number := header.Number.Uint64() - if number == 0 { - return errUnknownBlock - } - // Retrieve the snapshot needed to verify this header and cache it - snap, err := c.snapshot(chain, number-1, header.ParentHash, parents) - if err != nil { - return err - } - - // Resolve the authorization key and check against signers - signer, err := ecrecover(header, c.signatures) - if err != nil { - return err - } - if _, ok := snap.Signers[signer]; !ok { - return errUnauthorizedSigner - } - for seen, recent := range snap.Recents { - if recent == signer { - // Signer is among recents, only fail if the current block doesn't shift it out - if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit { - return errRecentlySigned - } - } - } - // Ensure that the difficulty corresponds to the turn-ness of the signer - if !c.fakeDiff { - inturn := snap.inturn(header.Number.Uint64(), signer) - if inturn && header.Difficulty.Cmp(diffInTurn) != 0 { - return errWrongDifficulty - } - if !inturn && header.Difficulty.Cmp(diffNoTurn) != 0 { - return errWrongDifficulty - } - } - return nil -} - -// Prepare implements consensus.Engine, preparing all the consensus fields of the -// header for running the transactions on top. -func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { - // If the block isn't a checkpoint, cast a random vote (good enough for now) - header.Coinbase = common.Address{} - header.Nonce = types.BlockNonce{} - - number := header.Number.Uint64() - // Assemble the voting snapshot to check which votes make sense - snap, err := c.snapshot(chain, number-1, header.ParentHash, nil) - if err != nil { - return err - } - if number%c.config.Epoch != 0 { - c.lock.RLock() - - // Gather all the proposals that make sense voting on - addresses := make([]common.Address, 0, len(c.proposals)) - for address, authorize := range c.proposals { - if snap.validVote(address, authorize) { - addresses = append(addresses, address) - } - } - // If there's pending proposals, cast a vote on them - if len(addresses) > 0 { - header.Coinbase = addresses[rand.Intn(len(addresses))] - if c.proposals[header.Coinbase] { - copy(header.Nonce[:], nonceAuthVote) - } else { - copy(header.Nonce[:], nonceDropVote) - } - } - c.lock.RUnlock() - } - // Set the correct difficulty - header.Difficulty = calcDifficulty(snap, c.signer) - - // Ensure the extra data has all its components - if len(header.Extra) < extraVanity { - header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...) - } - header.Extra = header.Extra[:extraVanity] - - if number%c.config.Epoch == 0 { - for _, signer := range snap.signers() { - header.Extra = append(header.Extra, signer[:]...) - } - } - header.Extra = append(header.Extra, make([]byte, extraSeal)...) - - // Mix digest is reserved for now, set to empty - header.MixDigest = common.Hash{} - - // Ensure the timestamp has the correct delay - parent := chain.GetHeader(header.ParentHash, number-1) - if parent == nil { - return consensus.ErrUnknownAncestor - } - header.Time = parent.Time + c.config.Period - if header.Time < uint64(time.Now().Unix()) { - header.Time = uint64(time.Now().Unix()) - } - return nil -} - -// Finalize implements consensus.Engine, ensuring no uncles are set, nor block -// rewards given. -func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { - // No block rewards in PoA, so the state remains as is and uncles are dropped - header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) - header.UncleHash = types.CalcUncleHash(nil) -} - -// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, -// nor block rewards given, and returns the final block. -func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { - // Finalize block - c.Finalize(chain, header, state, txs, uncles) - - // Assemble and return the final block for sealing - return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil -} - -// Authorize injects a private key into the consensus engine to mint new blocks -// with. -func (c *Clique) Authorize(signer common.Address, signFn SignerFn) { - c.lock.Lock() - defer c.lock.Unlock() - - c.signer = signer - c.signFn = signFn -} - -// Seal implements consensus.Engine, attempting to create a sealed block using -// the local signing credentials. -func (c *Clique) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { - header := block.Header() - - // Sealing the genesis block is not supported - number := header.Number.Uint64() - if number == 0 { - return errUnknownBlock - } - // For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing) - if c.config.Period == 0 && len(block.Transactions()) == 0 { - log.Info("Sealing paused, waiting for transactions") - return nil - } - // Don't hold the signer fields for the entire sealing procedure - c.lock.RLock() - signer, signFn := c.signer, c.signFn - c.lock.RUnlock() - - // Bail out if we're unauthorized to sign a block - snap, err := c.snapshot(chain, number-1, header.ParentHash, nil) - if err != nil { - return err - } - if _, authorized := snap.Signers[signer]; !authorized { - return errUnauthorizedSigner - } - // If we're amongst the recent signers, wait for the next block - for seen, recent := range snap.Recents { - if recent == signer { - // Signer is among recents, only wait if the current block doesn't shift it out - if limit := uint64(len(snap.Signers)/2 + 1); number < limit || seen > number-limit { - log.Info("Signed recently, must wait for others") - return nil - } - } - } - // Sweet, the protocol permits us to sign the block, wait for our time - delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple - if header.Difficulty.Cmp(diffNoTurn) == 0 { - // It's not our turn explicitly to sign, delay it a bit - wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime - delay += time.Duration(rand.Int63n(int64(wiggle))) - - log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle)) - } - // Sign all the things! - sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeClique, CliqueRLP(header)) - if err != nil { - return err - } - copy(header.Extra[len(header.Extra)-extraSeal:], sighash) - // Wait until sealing is terminated or delay timeout. - log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay)) - go func() { - select { - case <-stop: - return - case <-time.After(delay): - } - - select { - case results <- block.WithSeal(header): - default: - log.Warn("Sealing result is not read by miner", "sealhash", SealHash(header)) - } - }() - - return nil -} - -// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty -// that a new block should have: -// * DIFF_NOTURN(2) if BLOCK_NUMBER % SIGNER_COUNT != SIGNER_INDEX -// * DIFF_INTURN(1) if BLOCK_NUMBER % SIGNER_COUNT == SIGNER_INDEX -func (c *Clique) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { - snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil) - if err != nil { - return nil - } - return calcDifficulty(snap, c.signer) -} - -func calcDifficulty(snap *Snapshot, signer common.Address) *big.Int { - if snap.inturn(snap.Number+1, signer) { - return new(big.Int).Set(diffInTurn) - } - return new(big.Int).Set(diffNoTurn) -} - -// SealHash returns the hash of a block prior to it being sealed. -func (c *Clique) SealHash(header *types.Header) common.Hash { - return SealHash(header) -} - -// Close implements consensus.Engine. It's a noop for clique as there are no background threads. -func (c *Clique) Close() error { - return nil -} - -// APIs implements consensus.Engine, returning the user facing RPC API to allow -// controlling the signer voting. -func (c *Clique) APIs(chain consensus.ChainHeaderReader) []rpc.API { - return []rpc.API{{ - Namespace: "clique", - Version: "1.0", - Service: &API{chain: chain, clique: c}, - Public: false, - }} -} - -// SealHash returns the hash of a block prior to it being sealed. -func SealHash(header *types.Header) (hash common.Hash) { - hasher := sha3.NewLegacyKeccak256() - encodeSigHeader(hasher, header) - hasher.(crypto.KeccakState).Read(hash[:]) - return hash -} - -// CliqueRLP returns the rlp bytes which needs to be signed for the proof-of-authority -// sealing. The RLP to sign consists of the entire header apart from the 65 byte signature -// contained at the end of the extra data. -// -// Note, the method requires the extra data to be at least 65 bytes, otherwise it -// panics. This is done to avoid accidentally using both forms (signature present -// or not), which could be abused to produce different hashes for the same header. -func CliqueRLP(header *types.Header) []byte { - b := new(bytes.Buffer) - encodeSigHeader(b, header) - return b.Bytes() -} - -func encodeSigHeader(w io.Writer, header *types.Header) { - enc := []interface{}{ - header.ParentHash, - header.UncleHash, - header.Coinbase, - header.Root, - header.TxHash, - header.ReceiptHash, - header.Bloom, - header.Difficulty, - header.Number, - header.GasLimit, - header.GasUsed, - header.Time, - header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short - header.MixDigest, - header.Nonce, - } - if header.BaseFee != nil { - enc = append(enc, header.BaseFee) - } - if err := rlp.Encode(w, enc); err != nil { - panic("can't encode: " + err.Error()) - } -}
diff --git go-ethereum/console/console.go celo/console/console.go index f6b17511fea11390aca0058a87e51634e073f914..e1ff4189b9f4e0faace436a21fed1a2752851616 100644 --- go-ethereum/console/console.go +++ celo/console/console.go @@ -299,7 +299,7 @@ // Welcome show summary of current Geth instance and some metadata about the // console's available modules. func (c *Console) Welcome() { - message := "Welcome to the Geth JavaScript console!\n\n" + message := "Welcome to the Celo JavaScript console!\n\n"   // Print some generic Geth metadata if res, err := c.jsre.Run(`
diff --git go-ethereum/console/bridge.go celo/console/bridge.go index e25cdfc6e408b68877b03a8c89a20c135316a314..b0a0b18c04e9f6efea7c4c104608a9b3608aa1c2 100644 --- go-ethereum/console/bridge.go +++ celo/console/bridge.go @@ -25,7 +25,6 @@ "strings" "time"   "github.com/dop251/goja" - "github.com/ethereum/go-ethereum/accounts/scwallet" "github.com/ethereum/go-ethereum/accounts/usbwallet" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/console/prompt" @@ -131,55 +130,6 @@ return val, nil } val, err = b.readPassphraseAndReopenWallet(call) if err != nil { - return nil, err - } - - case strings.HasSuffix(err.Error(), scwallet.ErrPairingPasswordNeeded.Error()): - // PUK input requested, fetch from the user and call open again - input, err := b.prompter.PromptPassword("Please enter the pairing password: ") - if err != nil { - return nil, err - } - passwd = call.VM.ToValue(input) - if val, err = openWallet(goja.Null(), wallet, passwd); err != nil { - if !strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()) { - return nil, err - } - // PIN input requested, fetch from the user and call open again - input, err := b.prompter.PromptPassword("Please enter current PIN: ") - if err != nil { - return nil, err - } - if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(input)); err != nil { - return nil, err - } - } - - case strings.HasSuffix(err.Error(), scwallet.ErrPINUnblockNeeded.Error()): - // PIN unblock requested, fetch PUK and new PIN from the user - var pukpin string - input, err := b.prompter.PromptPassword("Please enter current PUK: ") - if err != nil { - return nil, err - } - pukpin = input - input, err = b.prompter.PromptPassword("Please enter new PIN: ") - if err != nil { - return nil, err - } - pukpin += input - - if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(pukpin)); err != nil { - return nil, err - } - - case strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()): - // PIN input requested, fetch from the user and call open again - input, err := b.prompter.PromptPassword("Please enter current PIN: ") - if err != nil { - return nil, err - } - if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(input)); err != nil { return nil, err }
diff --git go-ethereum/console/console_test.go celo/console/console_test.go index 55dd46d04ea20850a27a35cb5717cf260d42a7b9..1befb56798adcc0ce614aafebc5689e12585d050 100644 --- go-ethereum/console/console_test.go +++ celo/console/console_test.go @@ -27,7 +27,6 @@ "testing" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/console/prompt" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth" @@ -98,13 +97,11 @@ stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance}) if err != nil { t.Fatalf("failed to create node: %v", err) } - ethConf := &ethconfig.Config{ - Genesis: core.DeveloperGenesisBlock(15, common.Address{}), + ethConf := &eth.Config{ + Genesis: core.DeveloperGenesisBlock(1), + TxFeeRecipient: common.HexToAddress(testAddress), Miner: miner.Config{ - Etherbase: common.HexToAddress(testAddress), - }, - Ethash: ethash.Config{ - PowMode: ethash.ModeTest, + Validator: common.HexToAddress(testAddress), }, } if confOverride != nil {
diff --git go-ethereum/contracts/errors.go celo/contracts/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..55535a2c901017f7aeeca3c5b04573df5cb834f4 --- /dev/null +++ celo/contracts/errors.go @@ -0,0 +1,12 @@ +package contracts + +import ( + "errors" +) + +var ( + // ErrSmartContractNotDeployed is returned when the RegisteredAddresses mapping does not contain the specified contract + ErrSmartContractNotDeployed = errors.New("Contract not in Registry") + ErrRegistryContractNotDeployed = errors.New("Registry not deployed") + ErrExchangeRateZero = errors.New("Exchange rate returned from the network is zero") +)
diff --git go-ethereum/contracts/contract_call.go celo/contracts/contract_call.go new file mode 100644 index 0000000000000000000000000000000000000000..9e2d36be4651626a37f772a10faaf8c70af3455e --- /dev/null +++ celo/contracts/contract_call.go @@ -0,0 +1,121 @@ +package contracts + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" +) + +// Method represents a contract's method +type Method struct { + abi *abi.ABI + method string + maxGas uint64 +} + +// NewMethod creates a new Method +func NewMethod(abi *abi.ABI, method string, maxGas uint64) Method { + return Method{ + abi: abi, + method: method, + maxGas: maxGas, + } +} + +// Bind returns a BoundMethod instance which can be used to call the contract method represented by am +// and residing at contracAddress. +func (am Method) Bind(contractAddress common.Address) *BoundMethod { + return &BoundMethod{ + Method: am, + resolveAddress: noopResolver(contractAddress), + } +} + +// encodeCall will encodes the msg into []byte format for EVM consumption +func (am Method) encodeCall(args ...interface{}) ([]byte, error) { + return am.abi.Pack(am.method, args...) +} + +// decodeResult will decode the result of msg execution into the result parameter +func (am Method) decodeResult(result interface{}, output []byte) error { + if result == nil { + return nil + } + return am.abi.UnpackIntoInterface(result, am.method, output) +} + +// NewBoundMethod constructs a new bound method instance bound to the given address. +func NewBoundMethod(contractAddress common.Address, abi *abi.ABI, methodName string, maxGas uint64) *BoundMethod { + return NewMethod(abi, methodName, maxGas).Bind(contractAddress) +} + +func NewRegisteredContractMethod(registryId common.Hash, abi *abi.ABI, methodName string, maxGas uint64) *BoundMethod { + return &BoundMethod{ + Method: NewMethod(abi, methodName, maxGas), + resolveAddress: func(vmRunner vm.EVMRunner) (common.Address, error) { + return resolveAddressForCall(vmRunner, registryId, methodName) + }, + } +} + +// BoundMethod represents a Method that is bounded to an address +// In particular, instead of address we use an address resolver to cope the fact +// that addresses need to be obtained from the Registry before making a call +type BoundMethod struct { + Method + resolveAddress func(vm.EVMRunner) (common.Address, error) +} + +// Query executes the method with the given EVMRunner as a read only action, the returned +// value is unpacked into result. +func (bm *BoundMethod) Query(vmRunner vm.EVMRunner, result interface{}, args ...interface{}) error { + return bm.run(vmRunner, result, true, nil, args...) +} + +// Execute executes the method with the given EVMRunner and unpacks the return value into result. +// If the method does not return a value then result should be nil. +func (bm *BoundMethod) Execute(vmRunner vm.EVMRunner, result interface{}, value *big.Int, args ...interface{}) error { + return bm.run(vmRunner, result, false, value, args...) +} + +func (bm *BoundMethod) run(vmRunner vm.EVMRunner, result interface{}, readOnly bool, value *big.Int, args ...interface{}) error { + defer meterExecutionTime(bm.method)() + + contractAddress, err := bm.resolveAddress(vmRunner) + if err != nil { + return err + } + + logger := log.New("to", contractAddress, "method", bm.method) + + input, err := bm.encodeCall(args...) + if err != nil { + logger.Error("Error invoking evm function: can't encode method arguments", "args", args, "err", err) + return err + } + + var output []byte + if readOnly { + output, err = vmRunner.Query(contractAddress, input, bm.maxGas) + } else { + output, err = vmRunner.Execute(contractAddress, input, bm.maxGas, value) + } + + if err != nil { + message, _ := unpackError(output) + logger.Error("Error invoking evm function: EVM call failure", "input", hexutil.Encode(input), "maxgas", bm.maxGas, "err", err, "message", message) + return err + } + + if err := bm.decodeResult(result, output); err != nil { + logger.Error("Error invoking evm function: can't unpack result", "err", err, "maxgas", bm.maxGas) + return err + } + + logger.Trace("EVM call successful", "input", hexutil.Encode(input), "output", hexutil.Encode(output)) + return nil +}
diff --git go-ethereum/contracts/registry.go celo/contracts/registry.go new file mode 100644 index 0000000000000000000000000000000000000000..70b67a4e1ff54e4a2431d34f1ce8c77797246518 --- /dev/null +++ celo/contracts/registry.go @@ -0,0 +1,43 @@ +package contracts + +import ( + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/abis" + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/contracts/internal/n" + "github.com/ethereum/go-ethereum/core/vm" +) + +const ( + MaxGasForGetAddressFor uint64 = 100 * n.Thousand +) + +var getAddressMethod = NewBoundMethod(config.RegistrySmartContractAddress, abis.Registry, "getAddressFor", MaxGasForGetAddressFor) + +// TODO(kevjue) - Re-Enable caching of the retrieved registered address +// See this commit for the removed code for caching: https://github.com/celo-org/geth/commit/43a275273c480d307a3d2b3c55ca3b3ee31ec7dd. + +// GetRegisteredAddress returns the address on the registry for a given id +func GetRegisteredAddress(vmRunner vm.EVMRunner, registryId common.Hash) (common.Address, error) { + + vmRunner.StopGasMetering() + defer vmRunner.StartGasMetering() + + var contractAddress common.Address + err := getAddressMethod.Query(vmRunner, &contractAddress, registryId) + + // TODO (mcortesi) Remove ErrEmptyArguments check after we change Proxy to fail on unset impl + // TODO(asa): Why was this change necessary? + if err == abi.ErrEmptyArguments || err == vm.ErrExecutionReverted { + return common.ZeroAddress, ErrRegistryContractNotDeployed + } else if err != nil { + return common.ZeroAddress, err + } + + if contractAddress == common.ZeroAddress { + return common.ZeroAddress, ErrSmartContractNotDeployed + } + + return contractAddress, nil +}
diff --git go-ethereum/contracts/utils.go celo/contracts/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..e5fc38c855fd32b0967005b73ab9188195ce086c --- /dev/null +++ celo/contracts/utils.go @@ -0,0 +1,60 @@ +package contracts + +import ( + "bytes" + "errors" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" +) + +var ( + errorSig = []byte{0x08, 0xc3, 0x79, 0xa0} // Keccak256("Error(string)")[:4] + abiString, _ = abi.NewType("string", "", nil) +) + +// meterExecutionTime tracks contract execution time for a given contract method identifier +func meterExecutionTime(method string) func() { + // Record a metrics data point about execution time. + timer := metrics.GetOrRegisterTimer("contracts/systemcall/"+method, nil) + start := time.Now() + return func() { timer.UpdateSince(start) } +} + +func unpackError(result []byte) (string, error) { + if len(result) < 4 || !bytes.Equal(result[:4], errorSig) { + return "<tx result not Error(string)>", errors.New("TX result not of type Error(string)") + } + vs, err := abi.Arguments{{Type: abiString}}.UnpackValues(result[4:]) + if err != nil { + return "<invalid tx result>", err + } + return vs[0].(string), nil +} + +func resolveAddressForCall(caller vm.EVMRunner, registryId common.Hash, method string) (common.Address, error) { + contractAddress, err := GetRegisteredAddress(caller, registryId) + + if err != nil { + hexRegistryId := hexutil.Encode(registryId[:]) + if err == ErrSmartContractNotDeployed { + log.Debug("Contract not yet registered", "function", method, "registryId", hexRegistryId) + } else if err == ErrRegistryContractNotDeployed { + log.Debug("Registry contract not yet deployed", "function", method, "registryId", hexRegistryId) + } else { + log.Error("Error in getting registered address", "function", method, "registryId", hexRegistryId, "err", err) + } + return common.ZeroAddress, err + } + return contractAddress, nil +} + +// noopResolver returns a address resolver function that always resolve to the same address +func noopResolver(addr common.Address) func(vm.EVMRunner) (common.Address, error) { + return func(e vm.EVMRunner) (common.Address, error) { return addr, nil } +}
diff --git go-ethereum/contracts/abis/parsed_abi.go celo/contracts/abis/parsed_abi.go new file mode 100644 index 0000000000000000000000000000000000000000..24f58759510912c755181197084a358365cfaa78 --- /dev/null +++ celo/contracts/abis/parsed_abi.go @@ -0,0 +1,48 @@ +package abis + +import ( + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/config" +) + +var ( + Registry *abi.ABI = mustParseAbi("Registry", RegistryStr) + BlockchainParameters *abi.ABI = mustParseAbi("BlockchainParameters", BlockchainParametersStr) + SortedOracles *abi.ABI = mustParseAbi("SortedOracles", SortedOraclesStr) + ERC20 *abi.ABI = mustParseAbi("ERC20", ERC20Str) + FeeCurrency *abi.ABI = mustParseAbi("FeeCurrency", FeeCurrencyStr) + Elections *abi.ABI = mustParseAbi("Elections", ElectionsStr) + GasPriceMinimum *abi.ABI = mustParseAbi("GasPriceMinimum", GasPriceMinimumStr) + Random *abi.ABI = mustParseAbi("Random", RandomStr) + Validators *abi.ABI = mustParseAbi("Validators", ValidatorsStr) +) + +func mustParseAbi(name, abiStr string) *abi.ABI { + parsedAbi, err := abi.JSON(strings.NewReader(abiStr)) + if err != nil { + panic(fmt.Sprintf("Error reading ABI %s err=%s", name, err)) + } + return &parsedAbi +} + +var byRegistryId = map[common.Hash]*abi.ABI{ + config.BlockchainParametersRegistryId: BlockchainParameters, + config.SortedOraclesRegistryId: SortedOracles, + config.FeeCurrencyWhitelistRegistryId: FeeCurrency, + config.ElectionRegistryId: Elections, + config.GasPriceMinimumRegistryId: GasPriceMinimum, + config.RandomRegistryId: Random, + config.ValidatorsRegistryId: Validators, +} + +func AbiFor(registryId common.Hash) *abi.ABI { + found, ok := byRegistryId[registryId] + if !ok { + return nil + } + return found +}
diff --git go-ethereum/contracts/abis/abi_str.go celo/contracts/abis/abi_str.go new file mode 100644 index 0000000000000000000000000000000000000000..be7f6f1b45c757fabd9709c31422715e9f389fba --- /dev/null +++ celo/contracts/abis/abi_str.go @@ -0,0 +1,554 @@ +package abis + +// This is taken from celo-monorepo/packages/protocol/build/<env>/contracts/Registry.json +const RegistryStr = `[ + { + "constant": true, + "inputs": [ + { + "name": "identifier", + "type": "bytes32" + } + ], + "name": "getAddressFor", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +]` + +const BlockchainParametersStr = `[ + { + "constant": true, + "inputs": [], + "name": "getMinimumClientVersion", + "outputs": [ + { + "name": "major", + "type": "uint256" + }, + { + "name": "minor", + "type": "uint256" + }, + { + "name": "patch", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "blockGasLimit", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "intrinsicGasForAlternativeFeeCurrency", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +]` + +// This is taken from celo-monorepo/packages/protocol/build/<env>/contracts/SortedOracles.json +const SortedOraclesStr = `[ + { + "constant": true, + "inputs": [ + { + "name": "token", + "type": "address" + } + ], + "name": "medianRate", + "outputs": [ + { + "name": "", + "type": "uint128" + }, + { + "name": "", + "type": "uint128" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +]` + +// This is taken from celo-monorepo/packages/protocol/build/<env>/contracts/ERC20.json +const ERC20Str = `[ + { + "constant": true, + "inputs": [ + { + "name": "who", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" +}]` + +// This is taken from celo-monorepo/packages/protocol/build/<env>/contracts/FeeCurrency.json +const FeeCurrencyStr = `[ + { + "constant": true, + "inputs": [], + "name": "getWhitelist", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +]` + +const ElectionsStr string = `[ + { + "constant": true, + "inputs": [], + "name": "electValidatorSigners", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "group", + "type": "address" + }, + { + "name": "maxTotalRewards", + "type": "uint256" + }, + { + "name": "uptimes", + "type": "uint256[]" + } + ], + "name": "getGroupEpochRewards", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "minElectableValidators", + "type": "uint256" + }, + { + "name": "maxElectableValidators", + "type": "uint256" + } + ], + "name": "electNValidatorSigners", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getElectableValidators", + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +]` + +// This is taken from celo-monorepo/packages/protocol/build/<env>/contracts/Freezer.json +const FreezerStr = `[ + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "isFrozen", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +]` + +const GasPriceMinimumStr = `[ + { + "constant": true, + "inputs": [ + { + "name": "_tokenAddress", + "type": "address" + } + ], + "name": "getGasPriceMinimum", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "gasPriceMinimumFloor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_blockGasTotal", + "type": "uint256" + }, + { + "name": "_blockGasLimit", + "type": "uint256" + } + ], + "name": "updateGasPriceMinimum", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +]` + +const RandomStr = `[ + { + "constant": false, + "inputs": [ + { + "name": "randomness", + "type": "bytes32" + }, + { + "name": "newCommitment", + "type": "bytes32" + }, + { + "name": "proposer", + "type": "address" + } + ], + "name": "revealAndCommit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "commitments", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "randomness", + "type": "bytes32" + } + ], + "name": "computeCommitment", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "random", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getBlockRandomness", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +]` + +// This is taken from celo-monorepo/packages/protocol/build/<env>/contracts/Validators.json +const ValidatorsStr = `[ + { + "constant": true, + "inputs": [], + "name": "getRegisteredValidatorSigners", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getRegisteredValidators", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "signer", + "type": "address" + } + ], + "name": "getValidatorBlsPublicKeyFromSigner", + "outputs": [ + { + "name": "blsKey", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "getValidator", + "outputs": [ + { + "name": "ecdsaPublicKey", + "type": "bytes" + }, + { + "name": "blsPublicKey", + "type": "bytes" + }, + { + "name": "affiliation", + "type": "address" + }, + { + "name": "score", + "type": "uint256" + }, + { + "name": "signer", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "validator", + "type": "address" + }, + { + "name": "maxPayment", + "type": "uint256" + } + ], + "name": "distributeEpochPaymentsFromSigner", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "validator", + "type": "address" + }, + { + "name": "uptime", + "type": "uint256" + } + ], + "name": "updateValidatorScoreFromSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "account", + "type": "address" + } + ], + "name": "getMembershipInLastEpochFromSigner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +]`
diff --git go-ethereum/contracts/blockchain_parameters/blockchain_parameters_test.go celo/contracts/blockchain_parameters/blockchain_parameters_test.go new file mode 100644 index 0000000000000000000000000000000000000000..36c15281057b946b496d788b3164b5242bbd42f3 --- /dev/null +++ celo/contracts/blockchain_parameters/blockchain_parameters_test.go @@ -0,0 +1,110 @@ +package blockchain_parameters + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/contracts" + + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/contracts/testutil" + "github.com/ethereum/go-ethereum/params" + . "github.com/onsi/gomega" +) + +func TestGetMinimumVersion(t *testing.T) { + testutil.TestFailOnFailingRunner(t, getMinimumVersion) + testutil.TestFailsWhenContractNotDeployed(t, contracts.ErrSmartContractNotDeployed, getMinimumVersion) + + t.Run("should return minimum version", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.NewSingleMethodRunner( + config.BlockchainParametersRegistryId, + "getMinimumClientVersion", + func() (*big.Int, *big.Int, *big.Int) { + return big.NewInt(5), big.NewInt(4), big.NewInt(3) + }, + ) + + version, err := getMinimumVersion(runner) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(version).To(Equal(&config.VersionInfo{Major: 5, Minor: 4, Patch: 3})) + }) +} + +func TestGetIntrinsicGasForAlternativeFeeCurrency(t *testing.T) { + testutil.TestFailOnFailingRunner(t, getIntrinsicGasForAlternativeFeeCurrency) + testutil.TestFailsWhenContractNotDeployed(t, contracts.ErrSmartContractNotDeployed, getIntrinsicGasForAlternativeFeeCurrency) + + t.Run("should return gas for alternative currency", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.NewSingleMethodRunner( + config.BlockchainParametersRegistryId, + "intrinsicGasForAlternativeFeeCurrency", + func() *big.Int { + return big.NewInt(50000) + }, + ) + + gas, err := getIntrinsicGasForAlternativeFeeCurrency(runner) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(gas).To(Equal(uint64(50000))) + }) +} + +func TestGetIntrinsicGasForAlternativeFeeCurrencyOrDefault(t *testing.T) { + testutil.TestReturnsDefaultOnFailingRunner(t, DefaultIntrinsicGasForAlternativeFeeCurrency, GetIntrinsicGasForAlternativeFeeCurrencyOrDefault) + t.Run("should return gas for alternative currency", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.NewSingleMethodRunner( + config.BlockchainParametersRegistryId, + "intrinsicGasForAlternativeFeeCurrency", + func() *big.Int { + return big.NewInt(50000) + }, + ) + + gas := GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(runner) + g.Expect(gas).To(Equal(uint64(50000))) + }) +} + +func TestGetBlockGasLimit(t *testing.T) { + testutil.TestFailOnFailingRunner(t, GetBlockGasLimit) + testutil.TestFailsWhenContractNotDeployed(t, contracts.ErrSmartContractNotDeployed, GetBlockGasLimit) + t.Run("should return block gas limit", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.NewSingleMethodRunner( + config.BlockchainParametersRegistryId, + "blockGasLimit", + func() *big.Int { + return big.NewInt(50000) + }, + ) + + gas, err := GetBlockGasLimit(runner) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(gas).To(Equal(uint64(50000))) + }) +} +func TestGetBlockGasLimitOrDefault(t *testing.T) { + testutil.TestReturnsDefaultOnFailingRunner(t, params.DefaultGasLimit, GetBlockGasLimitOrDefault) + t.Run("should return block gas limit", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.NewSingleMethodRunner( + config.BlockchainParametersRegistryId, + "blockGasLimit", + func() *big.Int { + return big.NewInt(50000) + }, + ) + + gas := GetBlockGasLimitOrDefault(runner) + g.Expect(gas).To(Equal(uint64(50000))) + }) +}
diff --git go-ethereum/contracts/blockchain_parameters/blockchain_parameters.go celo/contracts/blockchain_parameters/blockchain_parameters.go new file mode 100644 index 0000000000000000000000000000000000000000..59c4067d796cd8ef93074833c87afb83571a30e8 --- /dev/null +++ celo/contracts/blockchain_parameters/blockchain_parameters.go @@ -0,0 +1,147 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package blockchain_parameters + +import ( + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/contracts" + "github.com/ethereum/go-ethereum/contracts/abis" + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/contracts/internal/n" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" +) + +const ( + maxGasForReadBlockchainParameter uint64 = 40 * n.Thousand // ad-hoc measurement is ~26k +) + +var ( + getMinimumClientVersionMethod = contracts.NewRegisteredContractMethod(config.BlockchainParametersRegistryId, abis.BlockchainParameters, "getMinimumClientVersion", maxGasForReadBlockchainParameter) + intrinsicGasForAlternativeFeeCurrencyMethod = contracts.NewRegisteredContractMethod(config.BlockchainParametersRegistryId, abis.BlockchainParameters, "intrinsicGasForAlternativeFeeCurrency", maxGasForReadBlockchainParameter) + blockGasLimitMethod = contracts.NewRegisteredContractMethod(config.BlockchainParametersRegistryId, abis.BlockchainParameters, "blockGasLimit", maxGasForReadBlockchainParameter) +) + +const DefaultIntrinsicGasForAlternativeFeeCurrency = config.IntrinsicGasForAlternativeFeeCurrency + +// getMinimumVersion retrieves the client required minimum version +// If a node is running a version smaller than this, it should exit/stop +func getMinimumVersion(vmRunner vm.EVMRunner) (*config.VersionInfo, error) { + version := [3]*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)} + err := getMinimumClientVersionMethod.Query(vmRunner, &version) + if err != nil { + return nil, err + } + return &config.VersionInfo{ + Major: version[0].Uint64(), + Minor: version[1].Uint64(), + Patch: version[2].Uint64(), + }, nil +} + +// GetIntrinsicGasForAlternativeFeeCurrencyOrDefault retrieves the intrisic gas for transactions that pay gas in +// with an alternative currency (not CELO). +// In case of error, it returns the default value +func GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(vmRunner vm.EVMRunner) uint64 { + gas, err := getIntrinsicGasForAlternativeFeeCurrency(vmRunner) + if err != nil { + log.Trace("Default gas", "gas", config.IntrinsicGasForAlternativeFeeCurrency, "method", "intrinsicGasForAlternativeFeeCurrency") + return config.IntrinsicGasForAlternativeFeeCurrency + } + log.Trace("Reading gas", "gas", gas) + return gas +} + +// getIntrinsicGasForAlternativeFeeCurrency retrieves the intrisic gas for transactions that pay gas in +// with an alternative currency (not CELO) +func getIntrinsicGasForAlternativeFeeCurrency(vmRunner vm.EVMRunner) (uint64, error) { + var gas *big.Int + err := intrinsicGasForAlternativeFeeCurrencyMethod.Query(vmRunner, &gas) + + if err != nil { + return 0, err + } + + return gas.Uint64(), nil +} + +// GetBlockGasLimitOrDefault retrieves the block max gas limit +// In case of error, it returns the default value +func GetBlockGasLimitOrDefault(vmRunner vm.EVMRunner) uint64 { + val, err := GetBlockGasLimit(vmRunner) + if err != nil { + logError("blockGasLimit", err) + return params.DefaultGasLimit + } + return val +} + +// GetBlockGasLimit retrieves the block max gas limit +func GetBlockGasLimit(vmRunner vm.EVMRunner) (uint64, error) { + var gasLimit *big.Int + err := blockGasLimitMethod.Query(vmRunner, &gasLimit) + if err != nil { + return 0, err + } + return gasLimit.Uint64(), nil +} + +// checkMinimumVersion performs a check on the client's minimum version +// In case of not passing hte check it will exit the node +func checkMinimumVersion(vmRunner vm.EVMRunner) { + version, err := getMinimumVersion(vmRunner) + + if err != nil { + logError("getMinimumClientVersion", err) + return + } + + if config.CurrentVersionInfo.Cmp(version) == -1 { + time.Sleep(10 * time.Second) + // TODO this should exist gracefully, not like this + log.Crit("Client version older than required", "current", params.Version, "required", version) + } + +} + +func logError(method string, err error) { + if err == contracts.ErrRegistryContractNotDeployed { + log.Debug("Error calling "+method, "err", err, "contract", hexutil.Encode(config.BlockchainParametersRegistryId[:])) + } else { + log.Warn("Error calling "+method, "err", err, "contract", hexutil.Encode(config.BlockchainParametersRegistryId[:])) + } +} + +// SpawnCheck starts a goroutine that will periodically check the client's minimun version +// In case of not passing hte check it will exit the node +func SpawnCheck(runnerFactory func() (vm.EVMRunner, error)) { + go func() { + for { + time.Sleep(60 * time.Second) + + vmRunner, err := runnerFactory() + if err != nil { + continue + } + checkMinimumVersion(vmRunner) + } + }() +}
diff --git go-ethereum/contracts/checkpointoracle/oracle_test.go celo/contracts/checkpointoracle/oracle_test.go index 66c0a7e2dd9443c8da03045691f60d4526b7bcc4..5e355425bbaba63aa8b392947cd5f84f173ae7a2 100644 --- go-ethereum/contracts/checkpointoracle/oracle_test.go +++ celo/contracts/checkpointoracle/oracle_test.go @@ -180,7 +180,7 @@ core.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(10000000000000000)}, accounts[1].addr: {Balance: big.NewInt(10000000000000000)}, accounts[2].addr: {Balance: big.NewInt(10000000000000000)}, - }, 10000000, + }, ) defer contractBackend.Close()
diff --git go-ethereum/contracts/currency/currency.go celo/contracts/currency/currency.go new file mode 100644 index 0000000000000000000000000000000000000000..36627d1b63d714229a7051a4e16c7662c700cce5 --- /dev/null +++ celo/contracts/currency/currency.go @@ -0,0 +1,283 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package currency + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts" + "github.com/ethereum/go-ethereum/contracts/abis" + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/contracts/internal/n" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" +) + +const ( + maxGasToReadErc20Balance uint64 = 100 * n.Thousand + maxGasForGetWhiteList uint64 = 200 * n.Thousand + maxGasForMedianRate uint64 = 100 * n.Thousand +) + +var ( + medianRateMethod = contracts.NewRegisteredContractMethod(config.SortedOraclesRegistryId, abis.SortedOracles, "medianRate", maxGasForMedianRate) + getWhitelistMethod = contracts.NewRegisteredContractMethod(config.FeeCurrencyWhitelistRegistryId, abis.FeeCurrency, "getWhitelist", maxGasForGetWhiteList) + getBalanceMethod = contracts.NewMethod(abis.ERC20, "balanceOf", maxGasToReadErc20Balance) +) + +// NoopExchangeRate represents an exchange rate of 1 to 1 +var NoopExchangeRate = ExchangeRate{common.Big1, common.Big1} + +var CELOCurrency = Currency{ + Address: common.ZeroAddress, + toCELORate: NoopExchangeRate, +} + +// Currency represent a system currency +// than can be converted to CELO +// Two currencies are deemed equal if they have the same address +type Currency struct { + Address common.Address + toCELORate ExchangeRate +} + +// NewCurrency creates a new currency object +func NewCurrency(address common.Address, toCELORate ExchangeRate) *Currency { + return &Currency{ + Address: address, + toCELORate: toCELORate, + } +} + +// ToCELO converts an currency's token amount to a CELO amount +func (c *Currency) ToCELO(tokenAmount *big.Int) *big.Int { + return c.toCELORate.ToBase(tokenAmount) +} + +// FromCELO converts an CELO amount to a currency tokens amount +func (c *Currency) FromCELO(celoAmount *big.Int) *big.Int { + return c.toCELORate.FromBase(celoAmount) +} + +// CmpToCurrency compares a currency amount to an amount in a different currency +func (c *Currency) CmpToCurrency(currencyAmount *big.Int, sndCurrencyAmount *big.Int, sndCurrency *Currency) int { + if c == sndCurrency || c.Address == sndCurrency.Address { + return currencyAmount.Cmp(sndCurrencyAmount) + } + + // Below code block is basically evaluating this comparison: + // currencyAmount * c.toCELORate.denominator / c.toCELORate.numerator < sndCurrencyAmount * sndCurrency.toCELORate.denominator / sndCurrency.toCELORate.numerator + // It will transform that comparison to this, to remove having to deal with fractional values. + // currencyAmount * c.toCELORate.denominator * sndCurrency.toCELORate.numerator < sndCurrencyAmount * sndCurrency.toCELORate.denominator * c.toCELORate.numerator + leftSide := new(big.Int).Mul( + currencyAmount, + new(big.Int).Mul( + c.toCELORate.denominator, + sndCurrency.toCELORate.numerator, + ), + ) + rightSide := new(big.Int).Mul( + sndCurrencyAmount, + new(big.Int).Mul( + sndCurrency.toCELORate.denominator, + c.toCELORate.numerator, + ), + ) + + return leftSide.Cmp(rightSide) +} + +// ExchangeRate represent the exchangeRate [Base -> Token] +// Follows the equation: 1 base * ExchangeRate = X token +type ExchangeRate struct { + numerator *big.Int + denominator *big.Int +} + +// NewExchangeRate creates an exchange rate. +// Requires numerator >=0 && denominator >= 0 +func NewExchangeRate(numerator *big.Int, denominator *big.Int) (*ExchangeRate, error) { + if numerator == nil || common.Big0.Cmp(numerator) >= 0 { + return nil, contracts.ErrExchangeRateZero + } + if denominator == nil || common.Big0.Cmp(denominator) >= 0 { + return nil, contracts.ErrExchangeRateZero + } + return &ExchangeRate{numerator, denominator}, nil +} + +// ToBase converts from token to base +func (er *ExchangeRate) ToBase(tokenAmount *big.Int) *big.Int { + return new(big.Int).Div(new(big.Int).Mul(tokenAmount, er.denominator), er.numerator) +} + +// FromGold converts from base to token +func (er *ExchangeRate) FromBase(goldAmount *big.Int) *big.Int { + return new(big.Int).Div(new(big.Int).Mul(goldAmount, er.numerator), er.denominator) +} + +// CurrencyManager provides an interface to access different fee currencies on a given point in time (header,state) +// and doing comparison or fetching exchange rates +// +// It's implements an internal cache to avoid perfoming duplicated EVM calls +type CurrencyManager struct { + vmRunner vm.EVMRunner + + currencyCache map[common.Address]*Currency // map of exchange rates of the form (CELO, token) + _getExchangeRate func(vm.EVMRunner, *common.Address) (*ExchangeRate, error) // function to obtain exchange rate from blockchain state +} + +type Provider interface { + // GetCurrency retrieves fee currency + GetCurrency(currencyAddress *common.Address) (*Currency, error) +} + +// NewManager creates a new CurrencyManager +func NewManager(vmRunner vm.EVMRunner) *CurrencyManager { + return newManager(GetExchangeRate, vmRunner) +} + +func newManager(_getExchangeRate func(vm.EVMRunner, *common.Address) (*ExchangeRate, error), vmRunner vm.EVMRunner) *CurrencyManager { + return &CurrencyManager{ + vmRunner: vmRunner, + currencyCache: make(map[common.Address]*Currency), + _getExchangeRate: _getExchangeRate, + } +} + +// GetCurrency retrieves fee currency +func (cc *CurrencyManager) GetCurrency(currencyAddress *common.Address) (*Currency, error) { + if currencyAddress == nil { + return &CELOCurrency, nil + } + + val, ok := cc.currencyCache[*currencyAddress] + if ok { + return val, nil + } + + currencyExchangeRate, err := cc._getExchangeRate(cc.vmRunner, currencyAddress) + if err != nil { + return nil, err + } + + val = NewCurrency(*currencyAddress, *currencyExchangeRate) + + cc.currencyCache[*currencyAddress] = val + + return val, nil +} + +// CmpValues compares values of potentially different currencies +func (cc *CurrencyManager) CmpValues(val1 *big.Int, currencyAddr1 *common.Address, val2 *big.Int, currencyAddr2 *common.Address) int { + // Short circuit if the fee currency is the same. nil currency => native currency + if (currencyAddr1 == nil && currencyAddr2 == nil) || (currencyAddr1 != nil && currencyAddr2 != nil && *currencyAddr1 == *currencyAddr2) { + return val1.Cmp(val2) + } + + currency1, err1 := cc.GetCurrency(currencyAddr1) + currency2, err2 := cc.GetCurrency(currencyAddr2) + + if err1 != nil || err2 != nil { + currency1Output := "nil" + if currencyAddr1 != nil { + currency1Output = currencyAddr1.Hex() + } + currency2Output := "nil" + if currencyAddr2 != nil { + currency2Output = currencyAddr2.Hex() + } + log.Warn("Error in retrieving exchange rate. Will do comparison of two values without exchange rate conversion.", "currency1", currency1Output, "err1", err1, "currency2", currency2Output, "err2", err2) + return val1.Cmp(val2) + } + + return currency1.CmpToCurrency(val1, val2, currency2) +} + +// GetExchangeRate retrieves currency-to-CELO exchange rate +func GetExchangeRate(vmRunner vm.EVMRunner, currencyAddress *common.Address) (*ExchangeRate, error) { + if currencyAddress == nil { + return &NoopExchangeRate, nil + } + + var returnArray [2]*big.Int + + err := medianRateMethod.Query(vmRunner, &returnArray, currencyAddress) + + if err == contracts.ErrSmartContractNotDeployed { + log.Warn("Registry address lookup failed", "err", err) + return &NoopExchangeRate, nil + } else if err != nil { + log.Error("medianRate invocation error", "feeCurrencyAddress", currencyAddress.Hex(), "err", err) + return &NoopExchangeRate, nil + } + + log.Trace("medianRate invocation success", "feeCurrencyAddress", currencyAddress, "returnArray", returnArray) + return NewExchangeRate(returnArray[0], returnArray[1]) +} + +// GetBalanceOf returns an account's balance on a given ERC20 currency +func GetBalanceOf(vmRunner vm.EVMRunner, accountOwner common.Address, contractAddress common.Address) (result *big.Int, err error) { + log.Trace("GetBalanceOf() Called", "accountOwner", accountOwner.Hex(), "contractAddress", contractAddress) + + err = getBalanceMethod.Bind(contractAddress).Query(vmRunner, &result, accountOwner) + + if err != nil { + log.Error("GetBalanceOf evm invocation error", "err", err) + } else { + log.Trace("GetBalanceOf evm invocation success", "accountOwner", accountOwner.Hex(), "Balance", result.String()) + } + + return result, err +} + +// CurrencyWhitelist retrieves the list of currencies that can be used to pay transaction fees +func CurrencyWhitelist(vmRunner vm.EVMRunner) ([]common.Address, error) { + returnList := []common.Address{} + + err := getWhitelistMethod.Query(vmRunner, &returnList) + + if err == contracts.ErrSmartContractNotDeployed { + log.Warn("Registry address lookup failed", "err", err) + } else if err != nil { + log.Error("getWhitelist invocation failed", "err", err) + } else { + log.Trace("getWhitelist invocation success") + } + + return returnList, err +} + +// IsWhitelisted indicates if a currency is whitelisted for transaction fee payments +func IsWhitelisted(vmRunner vm.EVMRunner, feeCurrency *common.Address) bool { + if feeCurrency == nil { + return true + } + + whitelistedCurrencies, err := CurrencyWhitelist(vmRunner) + if err != nil { + return true + } + + for _, addr := range whitelistedCurrencies { + if addr == *feeCurrency { + return true + } + } + return false +}
diff --git go-ethereum/contracts/currency/currency_test.go celo/contracts/currency/currency_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4f3fb2871cdb925a5cb123a560f5769b85bf63b7 --- /dev/null +++ celo/contracts/currency/currency_test.go @@ -0,0 +1,250 @@ +package currency + +import ( + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + . "github.com/onsi/gomega" +) + +type getExchangeRateMock struct { + calls []*common.Address + returns []struct { + rate *ExchangeRate + err error + } + returnIdx int +} + +func (m *getExchangeRateMock) totalCalls() int { + return len(m.calls) +} + +func (m *getExchangeRateMock) getExchangeRate(vmRunner vm.EVMRunner, currency *common.Address) (*ExchangeRate, error) { + m.calls = append(m.calls, currency) + + if len(m.returns) <= m.returnIdx { + return nil, errors.New("mock: missing return info") + } + + ret := m.returns[m.returnIdx] + m.returnIdx++ + return ret.rate, ret.err +} + +func (m *getExchangeRateMock) nextReturn(rate *ExchangeRate, err error) { + m.returns = append(m.returns, struct { + rate *ExchangeRate + err error + }{ + rate, err, + }) +} + +func TestCurrencyManager(t *testing.T) { + twoToOne := MustNewExchangeRate(common.Big1, common.Big2) + oneToTwo := MustNewExchangeRate(common.Big2, common.Big1) + + t.Run("should not call getExchange rate if both currencies are gold", func(t *testing.T) { + g := NewGomegaWithT(t) + mock := getExchangeRateMock{} + manager := newManager(mock.getExchangeRate, nil) + + g.Expect(manager.CmpValues(common.Big1, nil, common.Big2, nil)).To(Equal(-1)) + // no call to getExchange Rate + g.Expect(mock.totalCalls()).To(BeZero()) + }) + + t.Run("should not call getExchange rate if both currencies are the same", func(t *testing.T) { + g := NewGomegaWithT(t) + mock := getExchangeRateMock{} + manager := newManager(mock.getExchangeRate, nil) + + g.Expect(manager.CmpValues(common.Big1, &common.Address{12}, common.Big2, &common.Address{12})).To(Equal(-1)) + // no call to getExchange Rate + g.Expect(mock.totalCalls()).To(BeZero()) + }) + + t.Run("should not call getExchange rate on goldToken currency", func(t *testing.T) { + g := NewGomegaWithT(t) + + mock := getExchangeRateMock{} + + mock.nextReturn(twoToOne, nil) + + manager := newManager(mock.getExchangeRate, nil) + + g.Expect(manager.CmpValues(common.Big1, nil, common.Big1, &common.Address{12})).To(Equal(-1)) + // call to the exchange rate only for non goldToken currency + g.Expect(mock.totalCalls()).To(Equal(1)) + g.Expect(*mock.calls[0]).To(Equal(common.Address{12})) + }) + + t.Run("should use returned exchange rate", func(t *testing.T) { + g := NewGomegaWithT(t) + + mock := getExchangeRateMock{} + manager := newManager(mock.getExchangeRate, nil) + + // case 1: 2 gold = 1 usd + // then 1 gold < 1 usd + mock.nextReturn(twoToOne, nil) + g.Expect(manager.CmpValues(common.Big1, nil, common.Big1, &common.Address{10})).To(Equal(-1)) + + // case 2: 1 gold = 2 usd + // then 1 gold > 1 usd + mock.nextReturn(oneToTwo, nil) + g.Expect(manager.CmpValues(common.Big1, nil, common.Big1, &common.Address{20})).To(Equal(1)) + + // case 3: 1 gold = 2 usd && 1 gold = 2 eur + // then 2 eur > 1 usd + mock.nextReturn(oneToTwo, nil) + mock.nextReturn(oneToTwo, nil) + g.Expect(manager.CmpValues(common.Big2, &common.Address{30}, common.Big1, &common.Address{40})).To(Equal(1)) + }) + + t.Run("should work with zero values", func(t *testing.T) { + g := NewGomegaWithT(t) + + mock := getExchangeRateMock{} + manager := newManager(mock.getExchangeRate, nil) + + // case 1: both values == 0 + g.Expect(manager.CmpValues(common.Big0, nil, common.Big0, nil)).To(Equal(0)) + + // case 2: first value == 0 + g.Expect(manager.CmpValues(common.Big0, nil, common.Big1, nil)).To(Equal(-1)) + + // case 3: second value == 0 + g.Expect(manager.CmpValues(common.Big1, nil, common.Big0, nil)).To(Equal(1)) + }) + + t.Run("should compare value if first get exchange rate fails", func(t *testing.T) { + g := NewGomegaWithT(t) + + mock := getExchangeRateMock{} + mock.nextReturn(twoToOne, nil) + mock.nextReturn(nil, errors.New("boom!")) + + manager := newManager(mock.getExchangeRate, nil) + g.Expect(manager.CmpValues(common.Big2, &common.Address{30}, common.Big1, &common.Address{12})).To(Equal(1)) + }) + + t.Run("should compare value if second get exchange rate fails", func(t *testing.T) { + g := NewGomegaWithT(t) + + mock := getExchangeRateMock{} + mock.nextReturn(nil, errors.New("boom!")) + mock.nextReturn(twoToOne, nil) + + manager := newManager(mock.getExchangeRate, nil) + g.Expect(manager.CmpValues(common.Big2, &common.Address{30}, common.Big1, &common.Address{12})).To(Equal(1)) + }) + + t.Run("should cache exchange rate on subsequent calls", func(t *testing.T) { + g := NewGomegaWithT(t) + + mock := getExchangeRateMock{} + mock.nextReturn(twoToOne, nil) + mock.nextReturn(oneToTwo, nil) + + manager := newManager(mock.getExchangeRate, nil) + + for i := 0; i < 10; i++ { + g.Expect(manager.CmpValues(common.Big1, &common.Address{30}, common.Big1, &common.Address{12})).To(Equal(1)) + } + + // call to the exchange rate only for non goldToken currency + g.Expect(mock.totalCalls()).To(Equal(2)) + g.Expect(*mock.calls[0]).To(Equal(common.Address{30})) + g.Expect(*mock.calls[1]).To(Equal(common.Address{12})) + }) + + t.Run("should NOT cache exchange rate on errors", func(t *testing.T) { + g := NewGomegaWithT(t) + + mock := getExchangeRateMock{} + // default return is an error + + manager := newManager(mock.getExchangeRate, nil) + + for i := 0; i < 10; i++ { + g.Expect(manager.CmpValues(common.Big1, &common.Address{30}, common.Big1, &common.Address{12})).To(Equal(0)) + } + + // expect 10 call for address{30} and 10 for address{12} + g.Expect(mock.totalCalls()).To(Equal(20)) + }) + +} + +// MustNewExchangeRate creates an exchange rate, panic on error +func MustNewExchangeRate(numerator *big.Int, denominator *big.Int) *ExchangeRate { + rate, err := NewExchangeRate(numerator, denominator) + if err != nil { + panic(err) + } + return rate +} + +func EqualBigInt(n int64) OmegaMatcher { + return WithTransform(func(b *big.Int) int64 { return b.Int64() }, Equal(n)) +} + +func TestExchangeRate(t *testing.T) { + + t.Run("can't create with numerator <= 0", func(t *testing.T) { + g := NewGomegaWithT(t) + + _, err := NewExchangeRate(common.Big0, common.Big1) + g.Expect(err).Should((HaveOccurred())) + + _, err = NewExchangeRate(big.NewInt(-1), common.Big1) + g.Expect(err).Should((HaveOccurred())) + }) + + t.Run("can't create with denominator <= 0", func(t *testing.T) { + g := NewGomegaWithT(t) + + _, err := NewExchangeRate(common.Big1, common.Big0) + g.Expect(err).Should((HaveOccurred())) + + _, err = NewExchangeRate(common.Big1, big.NewInt(-1)) + g.Expect(err).Should((HaveOccurred())) + }) + + t.Run("should convert to base and back", func(t *testing.T) { + g := NewGomegaWithT(t) + twoToOne := MustNewExchangeRate(common.Big2, common.Big1) + + g.Expect(twoToOne.FromBase(common.Big1)).Should(EqualBigInt(2)) + g.Expect(twoToOne.ToBase(common.Big2)).Should(EqualBigInt(1)) + }) + +} + +func TestCurrency(t *testing.T) { + + t.Run("should compare with another currency values", func(t *testing.T) { + g := NewGomegaWithT(t) + + // 1 gold => 2 expensiveToken + expensiveToken := MustNewExchangeRate(common.Big2, common.Big1) + // 1 gold => 5 cheapToken + cheapToken := MustNewExchangeRate(big.NewInt(5), common.Big1) + + expensiveCurrency := Currency{ + Address: common.HexToAddress("0x1"), + toCELORate: *expensiveToken, + } + cheapCurrency := Currency{ + Address: common.HexToAddress("0x2"), + toCELORate: *cheapToken, + } + + g.Expect(expensiveCurrency.CmpToCurrency(big.NewInt(10), big.NewInt(10), &cheapCurrency)).Should(Equal(1)) + }) +}
diff --git go-ethereum/contracts/election/election_test.go celo/contracts/election/election_test.go new file mode 100644 index 0000000000000000000000000000000000000000..122e437416252cd68ebe3c7a534384fed5c417fc --- /dev/null +++ celo/contracts/election/election_test.go @@ -0,0 +1,14 @@ +package election + +import ( + "testing" + + "github.com/ethereum/go-ethereum/contracts" + "github.com/ethereum/go-ethereum/contracts/testutil" +) + +func TestGetElectedValidators(t *testing.T) { + testutil.TestFailOnFailingRunner(t, GetElectedValidators) + testutil.TestFailsWhenContractNotDeployed(t, contracts.ErrSmartContractNotDeployed, GetElectedValidators) + +}
diff --git go-ethereum/contracts/election/election.go celo/contracts/election/election.go new file mode 100644 index 0000000000000000000000000000000000000000..f08f61c9e943c45fb7ec0223e0d49ba706c0f33d --- /dev/null +++ celo/contracts/election/election.go @@ -0,0 +1,70 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. +package election + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts" + "github.com/ethereum/go-ethereum/contracts/abis" + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/contracts/internal/n" + "github.com/ethereum/go-ethereum/core/vm" +) + +const ( + maxGasForGetElectableValidators uint64 = 100 * n.Thousand + maxGasForElectValidators uint64 = 50 * n.Million + maxGasForElectNValidatorSigners uint64 = 50 * n.Million +) + +var ( + electValidatorSignersMethod = contracts.NewRegisteredContractMethod(config.ElectionRegistryId, abis.Elections, "electValidatorSigners", maxGasForElectValidators) + getElectableValidatorsMethod = contracts.NewRegisteredContractMethod(config.ElectionRegistryId, abis.Elections, "getElectableValidators", maxGasForGetElectableValidators) + electNValidatorSignersMethod = contracts.NewRegisteredContractMethod(config.ElectionRegistryId, abis.Elections, "electNValidatorSigners", maxGasForElectNValidatorSigners) +) + +func GetElectedValidators(vmRunner vm.EVMRunner) ([]common.Address, error) { + // Get the new epoch's validator set + var newValSet []common.Address + err := electValidatorSignersMethod.Query(vmRunner, &newValSet) + + if err != nil { + return nil, err + } + return newValSet, nil +} + +func ElectNValidatorSigners(vmRunner vm.EVMRunner, additionalAboveMaxElectable int64) ([]common.Address, error) { + // Get the electable min and max + var minElectableValidators *big.Int + var maxElectableValidators *big.Int + err := getElectableValidatorsMethod.Query(vmRunner, &[]interface{}{&minElectableValidators, &maxElectableValidators}) + if err != nil { + return nil, err + } + + // Run the validator election for up to maxElectable + getTotalVotesForEligibleValidatorGroup + var electedValidators []common.Address + err = electNValidatorSignersMethod.Query(vmRunner, &electedValidators, minElectableValidators, maxElectableValidators.Add(maxElectableValidators, big.NewInt(additionalAboveMaxElectable))) + if err != nil { + return nil, err + } + + return electedValidators, nil + +}
diff --git go-ethereum/contracts/gasprice_minimum/gasprice_minimum_test.go celo/contracts/gasprice_minimum/gasprice_minimum_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b92d2dcaa9c5611d1f48fefb27bfca70edabeb9a --- /dev/null +++ celo/contracts/gasprice_minimum/gasprice_minimum_test.go @@ -0,0 +1,178 @@ +package gasprice_minimum + +import ( + "math/big" + "testing" + + . "github.com/onsi/gomega" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/contracts/testutil" +) + +func TestGetGasPriceSuggestion(t *testing.T) { + gpmAddress := common.HexToAddress("0x090") + + t.Run("should return gas price minimum multiplied by 5", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.NewMockEVMRunner() + registry := testutil.NewRegistryMock() + runner.RegisterContract(config.RegistrySmartContractAddress, registry) + + contract := testutil.NewSingleMethodContract(config.GasPriceMinimumRegistryId, "getGasPriceMinimum", + func(currency common.Address) *big.Int { return big.NewInt(777777) }, + ) + runner.RegisterContract(gpmAddress, contract) + registry.AddContract(config.GasPriceMinimumRegistryId, gpmAddress) + + suggestedGpm, err := GetGasPriceSuggestion(runner, nil) + g.Expect(err).NotTo(HaveOccurred()) + + g.Expect(suggestedGpm.Uint64()).To(Equal(uint64(777777 * 5))) + + }) +} +func TestGetGasPriceMinimum(t *testing.T) { + cusdAddress := common.HexToAddress("0x077") + gpmAddress := common.HexToAddress("0x090") + + t.Run("should fail when vmRunner is failing", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.FailingVmRunner{} + + // with gold currency + ret, err := GetGasPriceMinimum(runner, nil) + g.Expect(err).To(MatchError(testutil.ErrFailingRunner)) + g.Expect(ret).To(Equal(FallbackGasPriceMinimum)) + + // with non gold currency + ret, err = GetGasPriceMinimum(runner, &cusdAddress) + g.Expect(err).To(MatchError(testutil.ErrFailingRunner)) + g.Expect(ret).To(Equal(FallbackGasPriceMinimum)) + }) + + t.Run("should return fallback price when registry is not deployed", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.NewMockEVMRunner() + + // with gold currency + ret, err := GetGasPriceMinimum(runner, nil) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ret).To(Equal(FallbackGasPriceMinimum)) + }) + + t.Run("should return fallback price when goldToken is not deployed", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.NewMockEVMRunner() + registry := testutil.NewRegistryMock() + runner.RegisterContract(config.RegistrySmartContractAddress, registry) + + // with gold currency + ret, err := GetGasPriceMinimum(runner, nil) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ret).To(Equal(FallbackGasPriceMinimum)) + }) + + t.Run("should return fallback price when gasPriceMinimum is not deployed", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.NewMockEVMRunner() + registry := testutil.NewRegistryMock() + runner.RegisterContract(config.RegistrySmartContractAddress, registry) + registry.AddContract(config.StableTokenRegistryId, cusdAddress) + + // with gold currency + ret, err := GetGasPriceMinimum(runner, nil) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ret).To(Equal(FallbackGasPriceMinimum)) + + // with non gold currency + ret, err = GetGasPriceMinimum(runner, &cusdAddress) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ret).To(Equal(FallbackGasPriceMinimum)) + }) + + t.Run("should return gasPriceMinimum for CELO", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.NewMockEVMRunner() + registry := testutil.NewRegistryMock() + runner.RegisterContract(config.RegistrySmartContractAddress, registry) + + contract := testutil.NewSingleMethodContract(config.GasPriceMinimumRegistryId, "getGasPriceMinimum", func(currency common.Address) *big.Int { + g.Expect(currency).To(Equal(common.Address{})) + return big.NewInt(777777) + }) + runner.RegisterContract(gpmAddress, contract) + registry.AddContract(config.GasPriceMinimumRegistryId, gpmAddress) + + ret, err := GetGasPriceMinimum(runner, nil) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ret.Uint64()).To(Equal(uint64(777777))) + }) + + t.Run("should return gasPriceMinimum for CELO", func(t *testing.T) { + g := NewGomegaWithT(t) + + runner := testutil.NewMockEVMRunner() + registry := testutil.NewRegistryMock() + runner.RegisterContract(config.RegistrySmartContractAddress, registry) + registry.AddContract(config.StableTokenRegistryId, cusdAddress) + + contract := testutil.NewSingleMethodContract(config.GasPriceMinimumRegistryId, "getGasPriceMinimum", func(currency common.Address) *big.Int { + g.Expect(currency).To(Equal(cusdAddress)) + return big.NewInt(777777) + }) + runner.RegisterContract(gpmAddress, contract) + registry.AddContract(config.GasPriceMinimumRegistryId, gpmAddress) + + // with non gold currency + ret, err := GetGasPriceMinimum(runner, &cusdAddress) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ret.Uint64()).To(Equal(uint64(777777))) + }) + +} +func TestUpdateGasPriceMinimum(t *testing.T) { + t.Run("should update gasPriceMinimum with current blockGasLimit", func(t *testing.T) { + g := NewGomegaWithT(t) + + var ( + gpmAddress = common.HexToAddress("0x99") + blockchainParametersAddress = common.HexToAddress("0xAA") + lastUsedGas uint64 = 50000 + blockGasLimit uint64 = 100000 + ) + + runner := testutil.NewMockEVMRunner() + registry := testutil.NewRegistryMock() + runner.RegisterContract(config.RegistrySmartContractAddress, registry) + registry.AddContract(config.BlockchainParametersRegistryId, blockchainParametersAddress) + registry.AddContract(config.GasPriceMinimumRegistryId, gpmAddress) + + runner.RegisterContract(blockchainParametersAddress, + testutil.NewSingleMethodContract(config.BlockchainParametersRegistryId, "blockGasLimit", + func() *big.Int { + return big.NewInt(int64(blockGasLimit)) + }), + ) + runner.RegisterContract(gpmAddress, + testutil.NewSingleMethodContract(config.GasPriceMinimumRegistryId, "updateGasPriceMinimum", + func(gas *big.Int, maxGas *big.Int) *big.Int { + g.Expect(gas.Uint64()).To(Equal(lastUsedGas)) + g.Expect(maxGas.Uint64()).To(Equal(blockGasLimit)) + return new(big.Int).SetUint64(999999) + }), + ) + + newGpm, err := UpdateGasPriceMinimum(runner, lastUsedGas) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(newGpm.Uint64()).To(Equal(uint64(999999))) + }) +}
diff --git go-ethereum/contracts/gasprice_minimum/gasprice_minimum.go celo/contracts/gasprice_minimum/gasprice_minimum.go new file mode 100644 index 0000000000000000000000000000000000000000..6fbedb397f4ce2952f99c69e51770f52b604563c --- /dev/null +++ celo/contracts/gasprice_minimum/gasprice_minimum.go @@ -0,0 +1,139 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package gasprice_minimum + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts" + "github.com/ethereum/go-ethereum/contracts/abis" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/contracts/currency" + "github.com/ethereum/go-ethereum/contracts/internal/n" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" +) + +// Gas price minimum serves as baseFee(EIP1559) after Espresso HF. + +var ( + FallbackGasPriceMinimum *big.Int = big.NewInt(0) // gas price minimum to return if unable to fetch from contract + suggestionMultiplier *big.Int = big.NewInt(5) // The multiplier that we apply to the minimum when suggesting gas price +) + +const ( + maxGasForGetGasPriceMinimum uint64 = 2 * n.Million + maxGasForUpdateGasPriceMinimum uint64 = 2 * n.Million +) + +var ( + getGasPriceMinimumMethod = contracts.NewRegisteredContractMethod(config.GasPriceMinimumRegistryId, abis.GasPriceMinimum, "getGasPriceMinimum", maxGasForGetGasPriceMinimum) + getGasPriceMinimumFloorMethod = contracts.NewRegisteredContractMethod(config.GasPriceMinimumRegistryId, abis.GasPriceMinimum, "gasPriceMinimumFloor", maxGasForGetGasPriceMinimum) + updateGasPriceMinimumMethod = contracts.NewRegisteredContractMethod(config.GasPriceMinimumRegistryId, abis.GasPriceMinimum, "updateGasPriceMinimum", maxGasForUpdateGasPriceMinimum) +) + +// GetGasTipCapSuggestion suggests a max tip of 2GWei in the appropriate currency. +// TODO: Switch to using a caching currency manager under high load. +func GetGasTipCapSuggestion(vmRunner vm.EVMRunner, currencyAddress *common.Address) (*big.Int, error) { + celoTipSuggestion := new(big.Int).Mul(common.Big2, big.NewInt(params.GWei)) + exchangeRate, err := currency.GetExchangeRate(vmRunner, currencyAddress) + if err != nil { + return nil, err + } + return exchangeRate.FromBase(celoTipSuggestion), nil +} + +// GetGasPriceSuggestion suggests a gas price the suggestionMultiplier times higher than the GPM in the appropriate currency. +// TODO: Switch to using a caching GPM manager under high load. +func GetGasPriceSuggestion(vmRunner vm.EVMRunner, currency *common.Address) (*big.Int, error) { + gasPriceMinimum, err := GetGasPriceMinimum(vmRunner, currency) + return new(big.Int).Mul(gasPriceMinimum, suggestionMultiplier), err +} + +func GetGasPriceMinimum(vmRunner vm.EVMRunner, currency *common.Address) (*big.Int, error) { + var currencyAddress common.Address + var err error + + if currency != nil { + currencyAddress = *currency + } + + var gasPriceMinimum *big.Int + err = getGasPriceMinimumMethod.Query(vmRunner, &gasPriceMinimum, currencyAddress) + + if err == contracts.ErrSmartContractNotDeployed || err == contracts.ErrRegistryContractNotDeployed { + return FallbackGasPriceMinimum, nil + } + if err != nil { + return FallbackGasPriceMinimum, err + } + + return gasPriceMinimum, err +} + +// GetRealGasPriceMinimum is similar to GetRealGasPriceMinimum but if there is +// a problem retrieving the gas price minimum it will return the error and a +// nil gas price minimum. +func GetRealGasPriceMinimum(vmRunner vm.EVMRunner, currency *common.Address) (*big.Int, error) { + var currencyAddress common.Address + var err error + + if currency != nil { + currencyAddress = *currency + } + + var gasPriceMinimum *big.Int + err = getGasPriceMinimumMethod.Query(vmRunner, &gasPriceMinimum, currencyAddress) + if err != nil { + return nil, fmt.Errorf("failed to retrieve gas price minimum for currency %v, error: %w", currencyAddress.String(), err) + } + + return gasPriceMinimum, nil +} + +func GetGasPriceMinimumFloor(vmRunner vm.EVMRunner) (*big.Int, error) { + var err error + + var gasPriceMinimumFloor *big.Int + err = getGasPriceMinimumFloorMethod.Query(vmRunner, &gasPriceMinimumFloor) + + if err == contracts.ErrSmartContractNotDeployed || err == contracts.ErrRegistryContractNotDeployed { + return FallbackGasPriceMinimum, nil + } + if err != nil { + return FallbackGasPriceMinimum, err + } + + return gasPriceMinimumFloor, err +} + +func UpdateGasPriceMinimum(vmRunner vm.EVMRunner, lastUsedGas uint64) (*big.Int, error) { + var updatedGasPriceMinimum *big.Int + + // If an error occurs, the default block gas limit will be returned and a log statement will be produced by GetBlockGasLimitOrDefault + gasLimit := blockchain_parameters.GetBlockGasLimitOrDefault(vmRunner) + + err := updateGasPriceMinimumMethod.Execute(vmRunner, &updatedGasPriceMinimum, common.Big0, big.NewInt(int64(lastUsedGas)), big.NewInt(int64(gasLimit))) + + if err != nil { + return nil, err + } + return updatedGasPriceMinimum, err +}
diff --git go-ethereum/contracts/random/random_test.go celo/contracts/random/random_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3d7dac9afbc51f6120f68192c874d4776be23eb8 --- /dev/null +++ celo/contracts/random/random_test.go @@ -0,0 +1,140 @@ +package random + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts" + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/contracts/testutil" + . "github.com/onsi/gomega" +) + +func TestIsRunning(t *testing.T) { + t.Run("should be False if runner fails", func(t *testing.T) { + g := NewGomegaWithT(t) + g.Expect(IsRunning(testutil.FailingVmRunner{})).To(BeFalse()) + }) + t.Run("should be False if Registry Not deployed", func(t *testing.T) { + g := NewGomegaWithT(t) + vmrunner := testutil.NewMockEVMRunner() + g.Expect(IsRunning(vmrunner)).To(BeFalse()) + + }) + t.Run("should be False if Random Not deployed", func(t *testing.T) { + g := NewGomegaWithT(t) + vmrunner := testutil.NewMockEVMRunner() + registry := testutil.NewRegistryMock() + vmrunner.RegisterContract(config.RegistrySmartContractAddress, registry) + g.Expect(IsRunning(vmrunner)).To(BeFalse()) + + }) + t.Run("should be True if Random is deployed", func(t *testing.T) { + g := NewGomegaWithT(t) + vmrunner := testutil.NewMockEVMRunner() + registry := testutil.NewRegistryMock() + vmrunner.RegisterContract(config.RegistrySmartContractAddress, registry) + registry.AddContract(config.RandomRegistryId, common.HexToAddress("0x033")) + g.Expect(IsRunning(vmrunner)).To(BeTrue()) + + }) +} + +func TestGetLastCommitment(t *testing.T) { + validatorAddress := common.HexToAddress("0x09") + someCommitment := common.HexToHash("0x666") + + testutil.TestFailOnFailingRunner(t, GetLastCommitment, validatorAddress) + testutil.TestFailsWhenContractNotDeployed(t, contracts.ErrSmartContractNotDeployed, GetLastCommitment, validatorAddress) + + t.Run("should retrieve last commitment", func(t *testing.T) { + g := NewGomegaWithT(t) + vmrunner := testutil.NewSingleMethodRunner(config.RandomRegistryId, "commitments", func(validator common.Address) common.Hash { + g.Expect(validator).To(Equal(validatorAddress)) + return someCommitment + }) + + ret, err := GetLastCommitment(vmrunner, validatorAddress) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ret).To(Equal(someCommitment)) + }) +} + +func TestComputeCommitment(t *testing.T) { + someRandomness := common.HexToHash("0x077777") + someCommitment := common.HexToHash("0x666") + + testutil.TestFailOnFailingRunner(t, ComputeCommitment, someRandomness) + testutil.TestFailsWhenContractNotDeployed(t, contracts.ErrSmartContractNotDeployed, ComputeCommitment, someRandomness) + + t.Run("should compute commitment", func(t *testing.T) { + g := NewGomegaWithT(t) + vmrunner := testutil.NewSingleMethodRunner(config.RandomRegistryId, "computeCommitment", func(randomness common.Hash) common.Hash { + g.Expect(randomness).To(Equal(someRandomness)) + return someCommitment + }) + + ret, err := ComputeCommitment(vmrunner, someRandomness) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ret).To(Equal(someCommitment)) + }) +} +func TestRevealAndCommit(t *testing.T) { + t.Run("should reveal and commit", func(t *testing.T) { + g := NewGomegaWithT(t) + + var ( + someRandomness = common.HexToHash("0x077777") + someCommitment = common.HexToHash("0x666") + someProposer = common.HexToAddress("0x99") + ) + + vmrunner := testutil.NewSingleMethodRunner(config.RandomRegistryId, "revealAndCommit", func(randomness common.Hash, commitment common.Hash, proposer common.Address) error { + g.Expect(randomness).To(Equal(someRandomness)) + g.Expect(commitment).To(Equal(someCommitment)) + g.Expect(proposer).To(Equal(someProposer)) + return nil + }) + + err := RevealAndCommit(vmrunner, someRandomness, someCommitment, someProposer) + g.Expect(err).NotTo(HaveOccurred()) + }) +} +func TestRandom(t *testing.T) { + someRandomness := common.HexToHash("0x077777") + + testutil.TestFailOnFailingRunner(t, Random) + testutil.TestFailsWhenContractNotDeployed(t, contracts.ErrSmartContractNotDeployed, Random) + + t.Run("should retrieve current randomness", func(t *testing.T) { + g := NewGomegaWithT(t) + vmrunner := testutil.NewSingleMethodRunner(config.RandomRegistryId, "random", func() common.Hash { + return someRandomness + }) + + ret, err := Random(vmrunner) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ret).To(Equal(someRandomness)) + }) +} + +func TestBlockRandomness(t *testing.T) { + blockNumber := uint64(999) + someRandomness := common.HexToHash("0x077777") + + testutil.TestFailOnFailingRunner(t, BlockRandomness, blockNumber) + testutil.TestFailsWhenContractNotDeployed(t, contracts.ErrSmartContractNotDeployed, BlockRandomness, blockNumber) + + t.Run("should retrieve randomness for block", func(t *testing.T) { + g := NewGomegaWithT(t) + vmrunner := testutil.NewSingleMethodRunner(config.RandomRegistryId, "getBlockRandomness", func(block *big.Int) common.Hash { + g.Expect(block.Uint64()).To(Equal(blockNumber)) + return someRandomness + }) + + ret, err := BlockRandomness(vmrunner, blockNumber) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ret).To(Equal(someRandomness)) + }) +}
diff --git go-ethereum/contracts/random/random.go celo/contracts/random/random.go new file mode 100644 index 0000000000000000000000000000000000000000..907ba89fd04f70972a075315fc4d54127d0716bb --- /dev/null +++ celo/contracts/random/random.go @@ -0,0 +1,94 @@ +package random + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/contracts" + "github.com/ethereum/go-ethereum/contracts/abis" + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/contracts/internal/n" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" +) + +const ( + maxGasForCommitments uint64 = 2 * n.Million + maxGasForComputeCommitment uint64 = 2 * n.Million + maxGasForBlockRandomness uint64 = 2 * n.Million + maxGasForRevealAndCommit uint64 = 2 * n.Million +) + +var ( + revealAndCommitMethod = contracts.NewRegisteredContractMethod(config.RandomRegistryId, abis.Random, "revealAndCommit", maxGasForRevealAndCommit) + commitmentsMethod = contracts.NewRegisteredContractMethod(config.RandomRegistryId, abis.Random, "commitments", maxGasForCommitments) + computeCommitmentMethod = contracts.NewRegisteredContractMethod(config.RandomRegistryId, abis.Random, "computeCommitment", maxGasForComputeCommitment) + randomMethod = contracts.NewRegisteredContractMethod(config.RandomRegistryId, abis.Random, "random", maxGasForBlockRandomness) + getBlockRandomnessMethod = contracts.NewRegisteredContractMethod(config.RandomRegistryId, abis.Random, "getBlockRandomness", maxGasForBlockRandomness) +) + +func IsRunning(vmRunner vm.EVMRunner) bool { + randomAddress, err := contracts.GetRegisteredAddress(vmRunner, config.RandomRegistryId) + + if err == contracts.ErrSmartContractNotDeployed || err == contracts.ErrRegistryContractNotDeployed { + log.Debug("Registry address lookup failed", "err", err, "contract", hexutil.Encode(config.RandomRegistryId[:])) + } else if err != nil { + log.Error(err.Error()) + } + + return err == nil && randomAddress != common.ZeroAddress +} + +// GetLastCommitment returns up the last commitment in the smart contract +func GetLastCommitment(vmRunner vm.EVMRunner, validator common.Address) (common.Hash, error) { + lastCommitment := common.Hash{} + err := commitmentsMethod.Query(vmRunner, &lastCommitment, validator) + if err != nil { + log.Error("Failed to get last commitment", "err", err) + return lastCommitment, err + } + + if (lastCommitment == common.Hash{}) { + log.Debug("Unable to find last randomness commitment in smart contract") + } + + return lastCommitment, nil +} + +// ComputeCommitment calulcates the commitment for a given randomness. +func ComputeCommitment(vmRunner vm.EVMRunner, randomness common.Hash) (common.Hash, error) { + commitment := common.Hash{} + // TODO(asa): Make an issue to not have to do this via StaticCall + err := computeCommitmentMethod.Query(vmRunner, &commitment, randomness) + if err != nil { + log.Error("Failed to call computeCommitment()", "err", err) + return common.Hash{}, err + } + + return commitment, err +} + +// RevealAndCommit performs an internal call to the EVM that reveals a +// proposer's previously committed to randomness, and commits new randomness for +// a future block. +func RevealAndCommit(vmRunner vm.EVMRunner, randomness, newCommitment common.Hash, proposer common.Address) error { + + log.Trace("Revealing and committing randomness", "randomness", randomness.Hex(), "commitment", newCommitment.Hex()) + err := revealAndCommitMethod.Execute(vmRunner, nil, common.Big0, randomness, newCommitment, proposer) + + return err +} + +// Random performs an internal call to the EVM to retrieve the current randomness from the official Random contract. +func Random(vmRunner vm.EVMRunner) (common.Hash, error) { + randomness := common.Hash{} + err := randomMethod.Query(vmRunner, &randomness) + return randomness, err +} + +func BlockRandomness(vmRunner vm.EVMRunner, blockNumber uint64) (common.Hash, error) { + randomness := common.Hash{} + err := getBlockRandomnessMethod.Query(vmRunner, &randomness, big.NewInt(int64(blockNumber))) + return randomness, err +}
diff --git go-ethereum/contracts/testutil/celo.go celo/contracts/testutil/celo.go new file mode 100644 index 0000000000000000000000000000000000000000..7c890cc6de529787d82fce8a1156f2533a37c2cb --- /dev/null +++ celo/contracts/testutil/celo.go @@ -0,0 +1,27 @@ +package testutil + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/config" +) + +type CeloMock struct { + Runner *MockEVMRunner + Registry *RegistryMock + BlockchainParameters *BlockchainParametersMock +} + +func NewCeloMock() CeloMock { + celo := CeloMock{ + Runner: NewMockEVMRunner(), + Registry: NewRegistryMock(), + BlockchainParameters: NewBlockchainParametersMock(), + } + + celo.Runner.RegisterContract(config.RegistrySmartContractAddress, celo.Registry) + + celo.Registry.AddContract(config.BlockchainParametersRegistryId, common.HexToAddress("0x01")) + celo.Runner.RegisterContract(common.HexToAddress("0x01"), celo.BlockchainParameters) + + return celo +}
diff --git go-ethereum/contracts/testutil/registry.go celo/contracts/testutil/registry.go new file mode 100644 index 0000000000000000000000000000000000000000..9f7b3ea3930fa31f65a1358eaa67e8653ae0af89 --- /dev/null +++ celo/contracts/testutil/registry.go @@ -0,0 +1,47 @@ +package testutil + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/abis" + "github.com/ethereum/go-ethereum/contracts/config" +) + +type RegistryMock struct { + ContractMock + Contracts map[common.Hash]common.Address +} + +func (rm *RegistryMock) GetAddressFor(id common.Hash) common.Address { + addr, ok := rm.Contracts[id] + if !ok { + return common.ZeroAddress + } + return addr +} + +func NewRegistryMock() *RegistryMock { + registryMock := &RegistryMock{ + Contracts: make(map[common.Hash]common.Address), + } + contract := NewContractMock(abis.Registry, registryMock) + registryMock.ContractMock = contract + return registryMock +} + +func (rm *RegistryMock) AddContract(id common.Hash, address common.Address) { + rm.Contracts[id] = address +} + +func NewSingleMethodRunner(registryId common.Hash, methodName string, mockFn interface{}) *MockEVMRunner { + runner := NewMockEVMRunner() + registry := NewRegistryMock() + runner.RegisterContract(config.RegistrySmartContractAddress, registry) + + contract := NewSingleMethodContract(registryId, methodName, mockFn) + + someAdddress := common.HexToAddress("0x045454545") + registry.AddContract(registryId, someAdddress) + runner.RegisterContract(someAdddress, contract) + + return runner +}
diff --git go-ethereum/contracts/testutil/fail_runner.go celo/contracts/testutil/fail_runner.go new file mode 100644 index 0000000000000000000000000000000000000000..e251b458bcc53632007e7d2b5d01c4a39bd2a44e --- /dev/null +++ celo/contracts/testutil/fail_runner.go @@ -0,0 +1,34 @@ +package testutil + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" +) + +// ErrFailingRunner error for FailingVmRunner +var ErrFailingRunner = errors.New("failing VMRunner") + +// FailingVmRunner is a VMRunner that always fails with a ErrFailingRunner +type FailingVmRunner struct{} + +func (fvm FailingVmRunner) Execute(recipient common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, err error) { + return nil, ErrFailingRunner +} + +func (fvm FailingVmRunner) Query(recipient common.Address, input []byte, gas uint64) (ret []byte, err error) { + return nil, ErrFailingRunner +} + +func (fvm FailingVmRunner) StopGasMetering() {} +func (fvm FailingVmRunner) StartGasMetering() {} + +func (fvm FailingVmRunner) GetStateDB() vm.StateDB { + return &mockStateDB{ + isContract: func(a common.Address) bool { + return true + }, + } +}
diff --git go-ethereum/contracts/testutil/blockchain_parameters.go celo/contracts/testutil/blockchain_parameters.go new file mode 100644 index 0000000000000000000000000000000000000000..bb935ff650be0f8e5c5289812091ee4aba26bdb1 --- /dev/null +++ celo/contracts/testutil/blockchain_parameters.go @@ -0,0 +1,38 @@ +package testutil + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/contracts/abis" + "github.com/ethereum/go-ethereum/contracts/config" +) + +type BlockchainParametersMock struct { + ContractMock + + MinimumVersion config.VersionInfo + BlockGasLimitValue *big.Int + IntrinsicGasForAlternativeFeeCurrencyValue *big.Int +} + +func NewBlockchainParametersMock() *BlockchainParametersMock { + mock := &BlockchainParametersMock{ + MinimumVersion: config.VersionInfo{Major: 1, Minor: 0, Patch: 0}, + BlockGasLimitValue: big.NewInt(20000000), + IntrinsicGasForAlternativeFeeCurrencyValue: big.NewInt(10000), + } + + contract := NewContractMock(abis.BlockchainParameters, mock) + mock.ContractMock = contract + return mock +} + +func (bp *BlockchainParametersMock) GetMinimumClientVersion() (*big.Int, *big.Int, *big.Int) { + return big.NewInt(int64(bp.MinimumVersion.Major)), big.NewInt(int64(bp.MinimumVersion.Minor)), big.NewInt(int64(bp.MinimumVersion.Patch)) +} +func (bp *BlockchainParametersMock) BlockGasLimit() *big.Int { + return bp.BlockGasLimitValue +} +func (bp *BlockchainParametersMock) IntrinsicGasForAlternativeFeeCurrency() *big.Int { + return bp.IntrinsicGasForAlternativeFeeCurrencyValue +}
diff --git go-ethereum/contracts/testutil/runner.go celo/contracts/testutil/runner.go new file mode 100644 index 0000000000000000000000000000000000000000..4438fc3e7e166ac2129986efb777be97b3e9cd00 --- /dev/null +++ celo/contracts/testutil/runner.go @@ -0,0 +1,197 @@ +package testutil + +import ( + "bytes" + "fmt" + + "math/big" + "reflect" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/abis" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" +) + +// Check we actually implement EVMRunner +var _ vm.EVMRunner = &MockEVMRunner{} + +type Contract interface { + Call(input []byte) (ret []byte, err error) +} + +type MockEVMRunner struct { + contracts map[common.Address]Contract +} + +func NewMockEVMRunner() *MockEVMRunner { + return &MockEVMRunner{ + contracts: make(map[common.Address]Contract), + } +} + +func (ev *MockEVMRunner) RegisterContract(address common.Address, mock Contract) { + ev.contracts[address] = mock +} + +func (ev *MockEVMRunner) Execute(recipient common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, err error) { + mock, ok := ev.contracts[recipient] + if !ok { + /// following evm.go when recipient doesn't exist, we return nil + return nil, nil + } + + return mock.Call(input) +} + +func (ev *MockEVMRunner) Query(recipient common.Address, input []byte, gas uint64) (ret []byte, err error) { + mock, ok := ev.contracts[recipient] + if !ok { + /// following evm.go when recipient doesn't exist, we return nil + return nil, nil + } + + return mock.Call(input) +} + +func (ev *MockEVMRunner) StopGasMetering() { + // noop +} + +func (ev *MockEVMRunner) StartGasMetering() { + // noop +} + +// GetStateDB implements Backend.GetStateDB +func (ev *MockEVMRunner) GetStateDB() vm.StateDB { + return &mockStateDB{ + isContract: func(a common.Address) bool { + _, ok := ev.contracts[a] + return ok + }, + } +} + +type ContractMock struct { + methods []MethodMock +} + +func NewContractMock(parsedAbi *abi.ABI, handler interface{}) ContractMock { + methodMocks := make([]MethodMock, 0) + + handlerType := reflect.TypeOf(handler) + handlerValue := reflect.ValueOf(handler) + for i := 0; i < handlerValue.NumMethod(); i++ { + methodVal := handlerValue.Method(i) + methodType := handlerType.Method(i) + + if abiMethod, ok := parsedAbi.Methods[decapitalise(methodType.Name)]; ok { + log.Debug("Registering method handler", "method", abiMethod.Name) + methodMocks = append( + methodMocks, + *NewMethod(&abiMethod, methodVal), + ) + } + + } + + return ContractMock{methods: methodMocks} +} + +func (cm *ContractMock) methodById(id []byte) (*MethodMock, bool) { + for _, method := range cm.methods { + if bytes.Equal(method.Id(), id[:4]) { + return &method, true + } + } + + return nil, false +} + +func (cm *ContractMock) Call(input []byte) (ret []byte, err error) { + method, ok := cm.methodById(input[:4]) + if !ok { + return nil, vm.ErrExecutionReverted + } + + return method.Call(input) +} + +type MethodMock struct { + method *abi.Method + fn reflect.Value +} + +func NewMethod(m *abi.Method, fnVal reflect.Value) *MethodMock { + fnType := fnVal.Type() + + if fnType.Kind() != reflect.Func { + panic("fn must be a function") + } + + if fnType.NumIn() != len(m.Inputs) { + panic(fmt.Sprintf("fn %s() must match number of input arguments [fn: %d, abi: %d]", m.Name, fnType.NumIn(), len(m.Inputs))) + } + if !(fnType.NumOut() == len(m.Outputs) || fnType.NumOut() == 1+len(m.Outputs)) { + panic(fmt.Sprintf("fn %s() must match number of output arguments [fn: %d, abi: %d]", m.Name, fnType.NumOut(), len(m.Outputs))) + } + + return &MethodMock{ + method: m, + fn: fnVal, + } +} + +func (mm *MethodMock) Id() []byte { + return mm.method.ID +} + +func (mm *MethodMock) Call(input []byte) (ret []byte, err error) { + inputs, err := mm.method.Inputs.UnpackValues(input[4:]) + if err != nil { + return nil, err + } + + ins := make([]reflect.Value, len(inputs)) + for i, arg := range inputs { + ins[i] = reflect.ValueOf(arg) + } + + outs := mm.fn.Call(ins) + retValues := make([]interface{}, len(outs)) + + // check if we have an error + if len(outs) == len(mm.method.Outputs)+1 { + errValue := outs[len(outs)-1] + if errValue.IsNil() { + return nil, nil + } + return nil, (errValue.Interface()).(error) + } + + for i, outArg := range outs { + retValues[i] = outArg.Interface() + } + + return mm.method.Outputs.PackValues(retValues) +} + +func NewSingleMethodContract(registryId common.Hash, methodName string, mockFn interface{}) *ContractMock { + contractAbi := abis.AbiFor(registryId) + if contractAbi == nil { + panic(fmt.Sprintf("no abi for id: %s", registryId.Hex())) + } + + method, ok := contractAbi.Methods[methodName] + if !ok { + panic(fmt.Sprintf("no method named: %s", methodName)) + } + + contract := ContractMock{ + methods: []MethodMock{ + *NewMethod(&method, reflect.ValueOf(mockFn)), + }, + } + return &contract +}
diff --git go-ethereum/contracts/testutil/utils.go celo/contracts/testutil/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..d99e53623babd20d59367339a44df06c6a581582 --- /dev/null +++ celo/contracts/testutil/utils.go @@ -0,0 +1,40 @@ +package testutil + +import ( + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" +) + +var ( + // When executing the transaction, we expect to send base fee to governance contract. + // Hence, we mock registryMock, registry and register governance without implementation. + RegistryProxyOpcodes = common.FromHex("0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029") + RegistryOpcodes = common.FromHex("0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c80638932cbf41161008c578063c586579311610066578063c586579314610407578063dcf0aaed146104a0578063dd9272331461050e578063f2fde38b1461057c576100cf565b80638932cbf4146102e25780638da5cb5b1461039b5780638f32d59b146103e5576100cf565b8063158ef93e146100d457806317c50818146100f6578063715018a6146101a75780637ef50298146101b15780638129fc1c1461021f578063853db32314610229575b600080fd5b6100dc6105c0565b604051808215151515815260200191505060405180910390f35b61018d6004803603604081101561010c57600080fd5b810190808035906020019064010000000081111561012957600080fd5b82018360208201111561013b57600080fd5b8035906020019184602083028401116401000000008311171561015d57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506105d3565b604051808215151515815260200191505060405180910390f35b6101af610691565b005b6101dd600480360360208110156101c757600080fd5b81019080803590602001909291905050506107ca565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102276107fd565b005b6102a06004803603602081101561023f57600080fd5b810190808035906020019064010000000081111561025c57600080fd5b82018360208201111561026e57600080fd5b8035906020019184600183028401116401000000008311171561029057600080fd5b90919293919293905050506108a6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610359600480360360208110156102f857600080fd5b810190808035906020019064010000000081111561031557600080fd5b82018360208201111561032757600080fd5b8035906020019184600183028401116401000000008311171561034957600080fd5b9091929391929390505050610918565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103a3610a60565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103ed610a89565b604051808215151515815260200191505060405180910390f35b61049e6004803603604081101561041d57600080fd5b810190808035906020019064010000000081111561043a57600080fd5b82018360208201111561044c57600080fd5b8035906020019184600183028401116401000000008311171561046e57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ae7565b005b6104cc600480360360208110156104b657600080fd5b8101908080359060200190929190505050610c68565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61053a6004803603602081101561052457600080fd5b8101908080359060200190929190505050610d7a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105be6004803603602081101561059257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610db7565b005b600060149054906101000a900460ff1681565b600080600090505b84849050811015610684578273ffffffffffffffffffffffffffffffffffffffff166001600087878581811061060d57fe5b90506020020135815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561066957600191505061068a565b61067d600182610e3d90919063ffffffff16565b90506105db565b50600090505b9392505050565b610699610a89565b61070b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60016020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1615610880576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff0219169083151502179055506108a433610ec5565b565b60008083836040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090506001600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691505092915050565b6000808383604051602001808383808284378083019250505092505050604051602081830303815290604052805190602001209050600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a23576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6964656e74696669657220686173206e6f20726567697374727920656e74727981525060200191505060405180910390fd5b6001600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16610acb611009565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b610aef610a89565b610b61576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008383604051602001808383808284378083019250505092505050604051602081830303815290604052805190602001209050816001600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff16817f4166d073a7a5e704ce0db7113320f88da2457f872d46dc020c805c562c1582a0868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a350505050565b60008073ffffffffffffffffffffffffffffffffffffffff166001600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610d3f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6964656e74696669657220686173206e6f20726567697374727920656e74727981525060200191505060405180910390fd5b6001600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60006001600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b610dbf610a89565b610e31576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b610e3a81610ec5565b50565b600080828401905083811015610ebb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610f4b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806110126026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60003390509056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373a265627a7a7231582021f804e21a59bd673149265b250bf50ab40b86b057ee8f9aeab3d3c904a82cdc64736f6c634300050d0032") +) + +// decapitalise makes a camel-case string which starts with a lower case character. +func decapitalise(input string) string { + if len(input) == 0 { + return input + } + + goForm := abi.ToCamelCase(input) + return strings.ToLower(goForm[:1]) + goForm[1:] +} + +type mockStateDB struct { + vm.StateDB + isContract func(common.Address) bool +} + +func (msdb *mockStateDB) GetCodeSize(addr common.Address) int { + if msdb.isContract(addr) { + return 100 + } + return 0 +} + +func (msdb *mockStateDB) Finalise(bool) {}
diff --git go-ethereum/contracts/testutil/basic_tests.go celo/contracts/testutil/basic_tests.go new file mode 100644 index 0000000000000000000000000000000000000000..0830fab37be211a14e2feb865abba20fd017f4ca --- /dev/null +++ celo/contracts/testutil/basic_tests.go @@ -0,0 +1,115 @@ +package testutil + +import ( + "fmt" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/core/vm" + . "github.com/onsi/gomega" +) + +// TestFailOnFailingRunner checks that function doesn't silent VMRunner error +func TestFailOnFailingRunner(t *testing.T, fn interface{}, args ...interface{}) { + fnType := reflect.TypeOf(fn) + if fnType.Kind() != reflect.Func { + t.Fatalf("Bad test: Calling testFailOnFailingRunner without a function") + } + + if fnType.NumIn() != len(args)+1 { + t.Fatalf("Bad test: function bad number of arguments") + } + + if fnType.NumOut() < 2 { + t.Fatalf("Bad test: function must return an error as last parameter (outs:%d)", fnType.NumOut()) + } + + t.Run("should fail if vmRunner fails", func(t *testing.T) { + t.Parallel() + g := NewGomegaWithT(t) + fnValue := reflect.ValueOf(fn) + + argsValues := vmRunnerArguments(FailingVmRunner{}, args...) + outs := fnValue.Call(argsValues) + + err := outs[len(outs)-1].Interface() + g.Expect(err).To(MatchError(ErrFailingRunner)) + }) +} + +// TestReturnsDefaultOnFailingRunner checks that function will return a default value if there's an error on VMRunner +func TestReturnsDefaultOnFailingRunner(t *testing.T, defaultValue interface{}, fn interface{}, args ...interface{}) { + fnType := reflect.TypeOf(fn) + if fnType.Kind() != reflect.Func { + t.Fatalf("Bad test: Calling testFailOnFailingRunner without a function") + } + + if fnType.NumIn() != len(args)+1 { + t.Fatalf("Bad test: function bad number of arguments") + } + + if fnType.NumOut() != 1 { + t.Fatalf("Bad test: function must return single value") + } + + t.Run("should returns default if vmRunner fails", func(t *testing.T) { + t.Parallel() + g := NewGomegaWithT(t) + fnValue := reflect.ValueOf(fn) + + argsValues := vmRunnerArguments(FailingVmRunner{}, args...) + fmt.Printf("inputs: %v\n", argsValues) + outs := fnValue.Call(argsValues) + + retValue := outs[0].Interface() + g.Expect(retValue).To(Equal(defaultValue)) + }) +} + +// TestFailsWhenContractNotDeployed check calls fails when contract is not registered +func TestFailsWhenContractNotDeployed(t *testing.T, expectedError error, fn interface{}, args ...interface{}) { + fnType := reflect.TypeOf(fn) + if fnType.Kind() != reflect.Func { + t.Fatalf("Bad test: Calling testFailOnFailingRunner without a function") + } + + if fnType.NumIn() != len(args)+1 { + t.Fatalf("Bad test: function bad number of arguments") + } + + if fnType.NumOut() < 2 { + t.Fatalf("Bad test: function must return an error as last parameter (outs:%d)", fnType.NumOut()) + } + + t.Run("should fail when contract not deployed", func(t *testing.T) { + t.Parallel() + g := NewGomegaWithT(t) + fnValue := reflect.ValueOf(fn) + + vmRunner := NewMockEVMRunner() + registryMock := NewRegistryMock() + vmRunner.RegisterContract(config.RegistrySmartContractAddress, registryMock) + + argsValues := vmRunnerArguments(vmRunner, args...) + outs := fnValue.Call(argsValues) + + err := outs[len(outs)-1].Interface() + g.Expect(err).To(MatchError(expectedError)) + }) +} + +func vmRunnerArguments(vmRunner vm.EVMRunner, args ...interface{}) []reflect.Value { + finalArgs := make([]interface{}, 1) + finalArgs[0] = vmRunner + finalArgs = append(finalArgs, args...) + return mapToValues(finalArgs...) +} + +func mapToValues(args ...interface{}) []reflect.Value { + argsValues := make([]reflect.Value, len(args)) + for i, v := range args { + argsValues[i] = reflect.ValueOf(v) + } + return argsValues +}
diff --git go-ethereum/contracts/validators/validators.go celo/contracts/validators/validators.go new file mode 100644 index 0000000000000000000000000000000000000000..0caeec62d2dc41d040ae5c1ce3a4a35cd1f8e19d --- /dev/null +++ celo/contracts/validators/validators.go @@ -0,0 +1,106 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. +package validators + +import ( + "fmt" + "math/big" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/contracts" + "github.com/ethereum/go-ethereum/contracts/abis" + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/contracts/internal/n" + "github.com/ethereum/go-ethereum/core/vm" +) + +type ValidatorContractData struct { + EcdsaPublicKey []byte + BlsPublicKey []byte + Affiliation common.Address + Score *big.Int + Signer common.Address +} + +const ( + maxGasForGetRegisteredValidators uint64 = 2 * n.Million + maxGasForGetValidator uint64 = 100 * n.Thousand +) + +var ( + getRegisteredValidatorSignersMethod = contracts.NewRegisteredContractMethod(config.ValidatorsRegistryId, abis.Validators, "getRegisteredValidatorSigners", maxGasForGetRegisteredValidators) + getRegisteredValidatorsMethod = contracts.NewRegisteredContractMethod(config.ValidatorsRegistryId, abis.Validators, "getRegisteredValidators", maxGasForGetRegisteredValidators) + getValidatorBlsPublicKeyFromSignerMethod = contracts.NewRegisteredContractMethod(config.ValidatorsRegistryId, abis.Validators, "getValidatorBlsPublicKeyFromSigner", maxGasForGetValidator) + + getValidatorMethod = contracts.NewRegisteredContractMethod(config.ValidatorsRegistryId, abis.Validators, "getValidator", maxGasForGetValidator) +) + +func RetrieveRegisteredValidatorSigners(vmRunner vm.EVMRunner) ([]common.Address, error) { + // Get the new epoch's validator signer set + var regVals []common.Address + if err := getRegisteredValidatorSignersMethod.Query(vmRunner, &regVals); err != nil { + return nil, err + } + + return regVals, nil +} + +func RetrieveRegisteredValidators(vmRunner vm.EVMRunner) ([]common.Address, error) { + // Get the new epoch's validator set + var regVals []common.Address + if err := getRegisteredValidatorsMethod.Query(vmRunner, &regVals); err != nil { + return nil, err + } + + return regVals, nil +} + +func GetValidator(vmRunner vm.EVMRunner, validatorAddress common.Address) (ValidatorContractData, error) { + var validator ValidatorContractData + err := getValidatorMethod.Query(vmRunner, &validator, validatorAddress) + if err != nil { + return validator, err + } + if len(validator.BlsPublicKey) != blscrypto.PUBLICKEYBYTES { + return validator, fmt.Errorf("length of bls public key incorrect. Expected %d, got %d", blscrypto.PUBLICKEYBYTES, len(validator.BlsPublicKey)) + } + return validator, nil +} + +func GetValidatorData(vmRunner vm.EVMRunner, validatorAddresses []common.Address) ([]istanbul.ValidatorData, error) { + var validatorData []istanbul.ValidatorData + for _, addr := range validatorAddresses { + var blsKey []byte + err := getValidatorBlsPublicKeyFromSignerMethod.Query(vmRunner, &blsKey, addr) + if err != nil { + return nil, err + } + + if len(blsKey) != blscrypto.PUBLICKEYBYTES { + return nil, fmt.Errorf("length of bls public key incorrect. Expected %d, got %d", blscrypto.PUBLICKEYBYTES, len(blsKey)) + } + blsKeyFixedSize := blscrypto.SerializedPublicKey{} + copy(blsKeyFixedSize[:], blsKey) + validator := istanbul.ValidatorData{ + Address: addr, + BLSPublicKey: blsKeyFixedSize, + } + validatorData = append(validatorData, validator) + } + return validatorData, nil +}
diff --git go-ethereum/core/state_processor.go celo/core/state_processor.go index 26cc728b00b7ec5764397b00ac5086eacf79bd8b..110e98950146ec1e402bddc04198025ace316a1a 100644 --- go-ethereum/core/state_processor.go +++ celo/core/state_processor.go @@ -22,7 +22,8 @@ "math/big"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/misc" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" + "github.com/ethereum/go-ethereum/contracts/random" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -50,8 +51,7 @@ } }   // Process processes the state changes according to the Ethereum rules by running -// the transaction messages using the statedb and applying any rewards to both -// the processor (coinbase) and any included uncles. +// the transaction messages using the statedb and applying any rewards to the processor (coinbase). // // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the @@ -64,22 +64,46 @@ header = block.Header() blockHash = block.Hash() blockNumber = block.Number() allLogs []*types.Log - gp = new(GasPool).AddGas(block.GasLimit()) + vmRunner = p.bc.NewEVMRunner(block.Header(), statedb) + gp = new(GasPool).AddGas(blockchain_parameters.GetBlockGasLimitOrDefault(vmRunner)) + ) + if random.IsRunning(vmRunner) { + author, err := p.bc.Engine().Author(header) + if err != nil { + return nil, nil, 0, err + } + + err = random.RevealAndCommit(vmRunner, block.Randomness().Revealed, block.Randomness().Committed, author) + if err != nil { + return nil, nil, 0, err + } + // always true (EIP158) + statedb.IntermediateRoot(true) + } + + var ( + baseFee *big.Int + sysCtx *SysContractCallCtx ) - // Mutate the block and state according to any hard-fork specs - if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { - misc.ApplyDAOHardFork(statedb) + if p.bc.Config().IsEspresso(blockNumber) { + sysCtx = NewSysContractCallCtx(header, statedb, p.bc) + if p.bc.Config().Faker { + sysCtx = MockSysContractCallCtx() + } } blockContext := NewEVMBlockContext(header, p.bc, nil) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) // Iterate over and process the individual transactions for i, tx := range block.Transactions() { - msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee) + if p.bc.chainConfig.IsEspresso(header.Number) { + baseFee = sysCtx.GetGasPriceMinimum(tx.FeeCurrency()) + } + msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), baseFee) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.Prepare(tx.Hash(), i) - receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv) + receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, vmRunner, sysCtx) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -87,18 +111,25 @@ receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) + p.engine.Finalize(p.bc, header, statedb, block.Transactions()) + + // Add the block receipt with logs from the non-transaction core contract calls (if there were any) + receipts = AddBlockReceipt(receipts, statedb, block.Hash())   return receipts, allLogs, *usedGas, nil }   -func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { - // Create a new context to be used in the EVM environment. +func applyTransaction(msg types.Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, vmRunner vm.EVMRunner, sysCtx *SysContractCallCtx) (*types.Receipt, error) { + if config.IsDonut(blockNumber) && !config.IsEspresso(blockNumber) && !tx.Protected() { + return nil, ErrUnprotectedTransaction + } + + // Create a new context to be used in the EVM environment txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb)   // Apply the transaction to the current state (included in the env). - result, err := ApplyMessage(evm, msg, gp) + result, err := ApplyMessage(evm, msg, gp, vmRunner, sysCtx) if err != nil { return nil, err } @@ -141,13 +172,17 @@ // ApplyTransaction attempts to apply a transaction to the given state database // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. -func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { - msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee) +func ApplyTransaction(config *params.ChainConfig, bc ChainContext, txFeeRecipient *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, vmRunner vm.EVMRunner, sysCtx *SysContractCallCtx) (*types.Receipt, error) { + var baseFee *big.Int + if config.IsEspresso(header.Number) { + baseFee = sysCtx.GetGasPriceMinimum(tx.FeeCurrency()) + } + msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), baseFee) if err != nil { return nil, err } // Create a new context to be used in the EVM environment - blockContext := NewEVMBlockContext(header, bc, author) + blockContext := NewEVMBlockContext(header, bc, txFeeRecipient) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) - return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) + return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, vmRunner, sysCtx) }
diff --git go-ethereum/core/blockchain.go celo/core/blockchain.go index 5d13278444a5dce577a6517b83e1f75d019c0eaa..54b0c10234e540f16482fd581073d60751b1a63e 100644 --- go-ethereum/core/blockchain.go +++ celo/core/blockchain.go @@ -37,8 +37,10 @@ "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/vmcontext" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/internal/syncx" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" @@ -80,6 +82,8 @@ blockPrefetchExecuteTimer = metrics.NewRegisteredTimer("chain/prefetch/executes", nil) blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil)   errInsertionInterrupted = errors.New("insertion is interrupted") + errCommitmentNotFound = errors.New("randomness commitment not found") + errChainStopped = errors.New("blockchain is stopped") )   const ( @@ -183,7 +187,9 @@ blockProcFeed event.Feed scope event.SubscriptionScope genesisBlock *types.Block   - chainmu sync.RWMutex // blockchain insertion lock + // This mutex synchronizes chain write operations. + // Readers don't need to take it, they can just read the database. + chainmu *syncx.ClosableMutex   currentBlock atomic.Value // Current head of the block chain currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!) @@ -196,8 +202,8 @@ blockCache *lru.Cache // Cache for the most recent entire blocks txLookupCache *lru.Cache // Cache for the most recent transaction lookup data. futureBlocks *lru.Cache // future blocks are blocks added for later processing   - quit chan struct{} // blockchain quit channel - wg sync.WaitGroup // chain processing wait group for shutting down + wg sync.WaitGroup // + quit chan struct{} // shutdown signal, closed in Stop. running int32 // 0 if chain is running, 1 when stopped procInterrupt int32 // interrupt signaler for block processing   @@ -235,6 +241,7 @@ Journal: cacheConfig.TrieCleanJournal, Preimages: cacheConfig.Preimages, }), quit: make(chan struct{}), + chainmu: syncx.NewClosableMutex(), shouldPreserve: shouldPreserve, bodyCache: bodyCache, bodyRLPCache: bodyRLPCache, @@ -278,6 +285,7 @@ } if err := bc.loadLastState(); err != nil { return nil, err } + // Make sure the state associated with the block is available head := bc.CurrentBlock() if _, err := state.New(head.Root(), bc.stateCache, bc.snaps); err != nil { @@ -306,6 +314,7 @@ return nil, err } } } + // Ensure that a previous crash in SetHead doesn't leave extra ancients if frozen, err := bc.db.Ancients(); err == nil && frozen > 0 { var ( @@ -357,6 +366,7 @@ log.Error("Chain rewind was successful, resuming normal operation") } } } + // Load any existing snapshot, regenerating it if loading failed if bc.cacheConfig.SnapshotLimit > 0 { // If the chain was rewound past the snapshot persistent layer (causing @@ -372,14 +382,19 @@ recover = true } bc.snaps, _ = snapshot.New(bc.db, bc.stateCache.TrieDB(), bc.cacheConfig.SnapshotLimit, head.Root(), !bc.cacheConfig.SnapshotWait, true, recover) } - // Take ownership of this particular state - go bc.update() + + // Start future block processor. + bc.wg.Add(1) + go bc.futureBlocksLoop() + + // Start tx indexer/unindexer. if txLookupLimit != nil { bc.txLookupLimit = *txLookupLimit   bc.wg.Add(1) go bc.maintainTxIndex(txIndexBlock) } + // If periodic cache journal is required, spin it up. if bc.cacheConfig.TrieCleanRejournal > 0 { if bc.cacheConfig.TrieCleanRejournal < time.Minute { @@ -401,6 +416,22 @@ func (bc *BlockChain) GetVMConfig() *vm.Config { return &bc.vmConfig }   +// NewEVMRunner creates the System's EVMRunner for given header & sttate +func (bc *BlockChain) NewEVMRunner(header *types.Header, state vm.StateDB) vm.EVMRunner { + return vmcontext.NewEVMRunner(bc, header, state) +} + +// NewEVMRunnerForCurrentBlock creates the System's EVMRunner for current block & state +func (bc *BlockChain) NewEVMRunnerForCurrentBlock() (vm.EVMRunner, error) { + block := bc.CurrentBlock() + state, err := bc.StateAt(block.Header().Root) + if err != nil { + log.Error("Can't create EVMRunner for current block (error fetching state)", "number", block.Number(), "stateRoot", block.Root().Hex(), "err", err) + return nil, err + } + return vmcontext.NewEVMRunner(bc, block.Header(), state), nil +} + // empty returns an indicator whether the blockchain is empty. // Note, it's a special case that we connect a non-empty ancient // database with an empty node, so that we can plugin the ancient @@ -443,6 +474,7 @@ if header := bc.GetHeaderByHash(head); header != nil { currentHeader = header } } + log.Debug(fmt.Sprintf("Loading Last State: %v", currentHeader.Number)) bc.hc.SetCurrentHeader(currentHeader)   // Restore the last known head fast block @@ -488,7 +520,9 @@ // retaining chain consistency. // // The method returns the block number where the requested root cap was found. func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64, error) { - bc.chainmu.Lock() + if !bc.chainmu.TryLock() { + return 0, errChainStopped + } defer bc.chainmu.Unlock()   // Track the block number of the requested root hash @@ -566,6 +600,7 @@ // last step, however the direction of SetHead is from high // to low, so it's safe the update in-memory markers directly. bc.currentFastBlock.Store(newHeadFastBlock) headFastBlockGauge.Update(int64(newHeadFastBlock.NumberU64())) + log.Info("Rewound fast block", "number", newHeadFastBlock.NumberU64()) } head := bc.CurrentBlock().NumberU64()   @@ -633,8 +668,11 @@ } if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB()); err != nil { return err } - // If all checks out, manually set the head block - bc.chainmu.Lock() + + // If all checks out, manually set the head block. + if !bc.chainmu.TryLock() { + return errChainStopped + } bc.currentBlock.Store(block) headBlockGauge.Update(int64(block.NumberU64())) bc.chainmu.Unlock() @@ -648,11 +686,6 @@ log.Info("Committed new head block", "number", block.Number(), "hash", hash) return nil }   -// GasLimit returns the gas limit of the current HEAD block. -func (bc *BlockChain) GasLimit() uint64 { - return bc.CurrentBlock().GasLimit() -} - // CurrentBlock retrieves the current head block of the canonical chain. The // block is retrieved from the blockchain's internal cache. func (bc *BlockChain) CurrentBlock() *types.Block { @@ -707,12 +740,14 @@ // Dump the entire block chain and purge the caches if err := bc.SetHead(0); err != nil { return err } - bc.chainmu.Lock() + if !bc.chainmu.TryLock() { + return errChainStopped + } defer bc.chainmu.Unlock()   // Prepare the genesis block and reinitialise the chain batch := bc.db.NewBatch() - rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()) + rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.TotalDifficulty()) rawdb.WriteBlock(batch, genesis) if err := batch.Write(); err != nil { log.Crit("Failed to write genesis block", "err", err) @@ -737,8 +772,10 @@ }   // ExportN writes a subset of the active chain to the given writer. func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { - bc.chainmu.RLock() - defer bc.chainmu.RUnlock() + if !bc.chainmu.TryLock() { + return errChainStopped + } + defer bc.chainmu.Unlock()   if first > last { return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last) @@ -802,7 +839,7 @@ func (bc *BlockChain) Genesis() *types.Block { return bc.genesisBlock }   -// GetBody retrieves a block body (transactions and uncles) from the database by +// GetBody retrieves a block body (transactions) from the database by // hash, caching it if found. func (bc *BlockChain) GetBody(hash common.Hash) *types.Body { // Short circuit if the body's already in the cache, retrieve otherwise @@ -950,17 +987,6 @@ } return }   -// GetUnclesInChain retrieves all the uncles from a given block backwards until -// a specific distance is reached. -func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header { - uncles := []*types.Header{} - for i := 0; block != nil && i < length; i++ { - uncles = append(uncles, block.Uncles()...) - block = bc.GetBlock(block.ParentHash(), block.NumberU64()-1) - } - return uncles -} - // TrieNode retrieves a blob of data associated with a trie node // either from ephemeral in-memory cache, or from persistent storage. func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) { @@ -991,10 +1017,21 @@ func (bc *BlockChain) Stop() { if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { return } - // Unsubscribe all subscriptions registered from blockchain + + // Unsubscribe all subscriptions registered from blockchain. bc.scope.Close() + + // Signal shutdown to all goroutines. close(bc.quit) bc.StopInsert() + + // Now wait for all chain modifications to end and persistent goroutines to exit. + // + // Note: Close waits for the mutex to become available, i.e. any running chain + // modification will have exited when Close returns. Since we also called StopInsert, + // the mutex should become available quickly. It cannot be taken again after Close has + // returned. + bc.chainmu.Close() bc.wg.Wait()   // Ensure that the entirety of the state snapshot is journalled to disk. @@ -1005,6 +1042,7 @@ if snapBase, err = bc.snaps.Journal(bc.CurrentBlock().Root()); err != nil { log.Error("Failed to journal state snapshot", "err", err) } } + // Ensure the state of a recent block is also stored to disk before exiting. // We're writing three different states to catch different restart scenarios: // - HEAD: So we don't need to reprocess any blocks in the general case @@ -1128,13 +1166,29 @@ // updateHead updates the head fast sync block if the inserted blocks are better // and returns an indicator whether the inserted blocks are canonical. updateHead := func(head *types.Block) bool { - bc.chainmu.Lock() + if !bc.chainmu.TryLock() { + return false + } defer bc.chainmu.Unlock()   // Rewind may have occurred, skip in that case. if bc.CurrentHeader().Number.Cmp(head.Number()) >= 0 { currentFastBlock, td := bc.CurrentFastBlock(), bc.GetTd(head.Hash(), head.NumberU64()) - if bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64()).Cmp(td) < 0 { + fastBlockTd := bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64()) + + // These two should not be nil under normal circumstances, + // but it apparently can happen in edge cases. See + // https://github.com/ethereum/go-ethereum/issues/1920 + if fastBlockTd == nil { + log.Warn("InsertReceiptChain: fastBlockTd is nil") + return false + } + if td == nil { + log.Warn("InsertReceiptChain: td is nil") + return false + } + + if fastBlockTd.Cmp(td) < 0 { rawdb.WriteHeadFastBlockHash(bc.db, head.Hash()) bc.currentFastBlock.Store(head) headFastBlockGauge.Update(int64(head.NumberU64())) @@ -1156,8 +1210,7 @@ // Ensure genesis is in ancients. if first.NumberU64() == 1 { if frozen, _ := bc.db.Ancients(); frozen == 0 { b := bc.genesisBlock - td := bc.genesisBlock.Difficulty() - writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{b}, []types.Receipts{nil}, td) + writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{b}, []types.Receipts{nil}, big.NewInt(1)) size += writeSize if err != nil { log.Error("Error writing genesis to ancients", "err", err) @@ -1372,8 +1425,9 @@ // writeBlockWithoutState writes only the block and its metadata to the database, // but does not write any state. This is used to construct competing side forks // up to the point where they exceed the canonical total difficulty. func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (err error) { - bc.wg.Add(1) - defer bc.wg.Done() + if bc.insertStopped() { + return errInsertionInterrupted + }   batch := bc.db.NewBatch() rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td) @@ -1387,9 +1441,6 @@ // writeKnownBlock updates the head block flag with a known block // and introduces chain reorg if necessary. func (bc *BlockChain) writeKnownBlock(block *types.Block) error { - bc.wg.Add(1) - defer bc.wg.Done() - current := bc.CurrentBlock() if block.ParentHash() != current.Hash() { if err := bc.reorg(current, block); err != nil { @@ -1400,19 +1451,86 @@ bc.writeHeadBlock(block) return nil }   -// WriteBlockWithState writes the block and all associated state to the database. -func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { - bc.chainmu.Lock() +// InsertPreprocessedBlock inserts a block which is already processed. This is +// only called by validators when committing, since that is the only flow where +// a block would have previously been preprocessed. It can happen that the +// validator has already inserted the block that they attempt to insert here, +// in the case that they received it over the network while the engine was +// processing a message that caused a validator to commit. This is possible +// because there is no synchronisation between the area of code that inserts +// blocks received from the network and the engine. +func (bc *BlockChain) InsertPreprocessedBlock(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB) error { + if !bc.chainmu.TryLock() { + return errInsertionInterrupted + } defer bc.chainmu.Unlock()   - return bc.writeBlockWithState(block, receipts, logs, state, emitHeadEvent) + current := bc.CurrentHeader() + currentNum := current.Number.Uint64() + blockNum := block.NumberU64() + + switch { + case blockNum > currentNum+1: + // If the block we are trying to insert is a future block, then something + // is badly wrong since validators should only start working on a block + // once they have agreed they previous block. + return fmt.Errorf( + "tried to insert preprocessed block that is in the future, current: (num:%d,hash:%v), preprocessed: (num:%d, hash:%v)", + currentNum, + current.Hash(), + blockNum, + block.Hash(), + ) + case blockNum <= currentNum: + // We expect this to ocur sometimes, we log an info. + log.Info("Tried to insert preprocessed bock but it has already been inserted", "preprocessedNum", blockNum, "currentNum", currentNum) + return nil + default: // blockNum == currentNum + 1 + if block.Header().ParentHash != current.Hash() { + return fmt.Errorf( + "tried to insert preprocessed block but parent-hash does not match parent, current: (num:%d, hash:%v), preprocessed: (num:%d, hash:%v, parentHash:%v)", + currentNum, + current.Hash(), + blockNum, + block.Hash(), + block.ParentHash(), + ) + } + // Insert the block and return any error + _, err := bc.insertPreprocessedBlock(block, receipts, logs, state, true) + return err + } }   -// writeBlockWithState writes the block and all associated state to the database, +// insertPreprocessedBlock writes the block and all associated state to the database, // but is expects the chain mutex to be held. -func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { - bc.wg.Add(1) - defer bc.wg.Done() +func (bc *BlockChain) insertPreprocessedBlock(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { + if bc.insertStopped() { + return NonStatTy, errInsertionInterrupted + } + + randomCommitment := common.Hash{} + if istEngine, isIstanbul := bc.engine.(consensus.Istanbul); isIstanbul { + + if hash := bc.GetCanonicalHash(block.NumberU64()); (hash != common.Hash{} && hash != block.Hash()) { + log.Error("Found two blocks with same height", "old", hash, "new", block.Hash()) + } + + blockAuthor, err := istEngine.Author(block.Header()) + if err != nil { + log.Warn("Unable to retrieve the author for block", "blockNum", block.NumberU64(), "err", err) + } + + if blockAuthor == istEngine.ValidatorAddress() && !istEngine.IsProxy() { + // Calculate the randomness commitment + _, randomCommitment, err = istEngine.GenerateRandomness(block.ParentHash()) + + if err != nil { + log.Error("Couldn't generate the randomness for the block", "blockNum", block.NumberU64(), "err", err) + return NonStatTy, err + } + } + }   // Calculate the total difficulty of the block ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1) @@ -1422,7 +1540,7 @@ } // Make sure no inconsistent state is leaked during insertion currentBlock := bc.CurrentBlock() localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) - externTd := new(big.Int).Add(block.Difficulty(), ptd) + externTd := big.NewInt(int64(block.NumberU64() + 1))   // Irrelevant of the canonical status, write the block itself to the database. // @@ -1433,6 +1551,11 @@ rawdb.WriteTd(blockBatch, block.Hash(), block.NumberU64(), externTd) rawdb.WriteBlock(blockBatch, block) rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts) rawdb.WritePreimages(blockBatch, state.Preimages()) + if (randomCommitment != common.Hash{}) { + // Note that the random commitment cache entry is never transferred over to the freezer, + // unlike all of the other saved data within this batch write + rawdb.WriteRandomCommitmentCache(blockBatch, randomCommitment, block.ParentHash()) + } if err := blockBatch.Write(); err != nil { log.Crit("Failed to write block into disk", "err", err) } @@ -1576,14 +1699,9 @@ bc.blockProcFeed.Send(true) defer bc.blockProcFeed.Send(false)   - // Remove already known canon-blocks - var ( - block, prev *types.Block - ) - // Do a sanity check that the provided chain is actually ordered and linked + // Do a sanity check that the provided chain is actually ordered and linked. for i := 1; i < len(chain); i++ { - block = chain[i] - prev = chain[i-1] + block, prev := chain[i], chain[i-1] if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() { // Chain broke ancestry, log a message (programming error) and skip insertion log.Error("Non contiguous block insert", "number", block.Number(), "hash", block.Hash(), @@ -1593,14 +1711,13 @@ return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, prev.NumberU64(), prev.Hash().Bytes()[:4], i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4]) } } - // Pre-checks passed, start the full block imports - bc.wg.Add(1) - bc.chainmu.Lock() - n, err := bc.insertChain(chain, true) - bc.chainmu.Unlock() - bc.wg.Done()   - return n, err + // Pre-check passed, start the full block imports. + if !bc.chainmu.TryLock() { + return 0, errChainStopped + } + defer bc.chainmu.Unlock() + return bc.insertChain(chain, true) }   // InsertChainWithoutSealVerification works exactly the same @@ -1609,14 +1726,11 @@ func (bc *BlockChain) InsertChainWithoutSealVerification(block *types.Block) (int, error) { bc.blockProcFeed.Send(true) defer bc.blockProcFeed.Send(false)   - // Pre-checks passed, start the full block imports - bc.wg.Add(1) - bc.chainmu.Lock() - n, err := bc.insertChain(types.Blocks([]*types.Block{block}), false) - bc.chainmu.Unlock() - bc.wg.Done() - - return n, err + if !bc.chainmu.TryLock() { + return 0, errChainStopped + } + defer bc.chainmu.Unlock() + return bc.insertChain(types.Blocks([]*types.Block{block}), false) }   // insertChain is the internal implementation of InsertChain, which assumes that @@ -1628,10 +1742,11 @@ // racey behaviour. If a sidechain import is in progress, and the historic state // is imported, but then new canon-head is added before the actual sidechain // completes, then the historic state could be pruned again func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, error) { - // If the chain is terminating, don't even bother starting up - if atomic.LoadInt32(&bc.procInterrupt) == 1 { + // If the chain is terminating, don't even bother starting up. + if bc.insertStopped() { return 0, nil } + // Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss) senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)   @@ -1666,15 +1781,15 @@ if err == ErrKnownBlock { // First block (and state) is known // 1. We did a roll-back, and should now do a re-import // 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot - // from the canonical chain, which has not been verified. - // Skip all known blocks that are behind us + // from the canonical chain, which has not been verified. + // Skip all known blocks that are behind us. var ( current = bc.CurrentBlock() localTd = bc.GetTd(current.Hash(), current.NumberU64()) externTd = bc.GetTd(block.ParentHash(), block.NumberU64()-1) // The first block can't be nil ) for block != nil && err == ErrKnownBlock { - externTd = new(big.Int).Add(externTd, block.Difficulty()) + externTd = new(big.Int).Add(externTd, big.NewInt(1)) if localTd.Cmp(externTd) < 0 { break } @@ -1753,18 +1868,11 @@ if BadHashes[block.Hash()] { bc.reportBlock(block, nil, ErrBannedHash) return it.index, ErrBannedHash } - // If the block is known (in the middle of the chain), it's a special case for - // Clique blocks where they can share state among each other, so importing an - // older block might complete the state of the subsequent one. In this case, - // just skip the block (we already validated it once fully (and crashed), since - // its header and body was already in the database). + // In this case, just skip the block we already validated it once fully + // (and crashed), since its header and body was already in the database). if err == ErrKnownBlock { - logger := log.Debug - if bc.chainConfig.Clique == nil { - logger = log.Warn - } - logger("Inserted known block", "number", block.Number(), "hash", block.Hash(), - "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(), + logger := log.Warn + logger("Inserted known block", "number", block.Number(), "hash", block.Hash(), "txs", len(block.Transactions()), "gas", block.GasUsed(), "root", block.Root())   // Special case. Commit the empty receipt slice if we meet the known @@ -1787,13 +1895,12 @@ } stats.processed++   // We can assume that logs are empty here, since the only way for consecutive - // Clique blocks to have the same state is if there are no transactions. lastCanon = block continue } + // Retrieve the parent block and it's state to execute on top start := time.Now() - parent := it.previous() if parent == nil { parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1) @@ -1805,7 +1912,6 @@ } // Enable prefetching to pull in trie node paths while processing transactions statedb.StartPrefetcher("chain") activeState = statedb - // If we have a followup block, run that against the current state to pre-cache // transactions and probabilistically some of the account/storage trie nodes. var followupInterrupt uint32 @@ -1823,6 +1929,7 @@ } }(time.Now(), followup, throwaway, &followupInterrupt) } } + // Process block using the parent state as reference point substart := time.Now() receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) @@ -1831,6 +1938,7 @@ bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) return it.index, err } + // Update the metrics touched during block processing accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete, we can mark them @@ -1861,7 +1969,7 @@ blockValidationTimer.Update(time.Since(substart) - (statedb.AccountHashes + statedb.StorageHashes - triehash))   // Write the block to the chain and get the status. substart = time.Now() - status, err := bc.writeBlockWithState(block, receipts, logs, statedb, false) + status, err := bc.insertPreprocessedBlock(block, receipts, logs, statedb, false) atomic.StoreUint32(&followupInterrupt, 1) if err != nil { return it.index, err @@ -1876,8 +1984,7 @@ blockInsertTimer.UpdateSince(start)   switch status { case CanonStatTy: - log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(), - "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(), + log.Debug("Inserted new block", "number", block.Number(), "txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(start)), "root", block.Root())   @@ -1888,16 +1995,16 @@ bc.gcproc += proctime   case SideStatTy: log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), - "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)), - "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()), + "elapsed", common.PrettyDuration(time.Since(start)), + "txs", len(block.Transactions()), "gas", block.GasUsed(), "root", block.Root())   default: // This in theory is impossible, but lets be nice to our future selves and leave // a log, instead of trying to track down blocks imports that don't emit logs. log.Warn("Inserted block with unknown status", "number", block.Number(), "hash", block.Hash(), - "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)), - "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()), + "elapsed", common.PrettyDuration(time.Since(start)), + "txs", len(block.Transactions()), "gas", block.GasUsed(), "root", block.Root()) } stats.processed++ @@ -1906,6 +2013,7 @@ dirty, _ := bc.stateCache.TrieDB().Size() stats.report(chain, it.index, dirty) } + // Any blocks remaining here? The only ones we care about are the future ones if block != nil && errors.Is(err, consensus.ErrFutureBlock) { if err := bc.addFutureBlock(block); err != nil { @@ -1948,9 +2056,6 @@ canonical := bc.GetBlockByNumber(number) if canonical != nil && canonical.Hash() == block.Hash() { // Not a sidechain block, this is a re-import of a canon block which has it's state pruned   - // Collect the TD of the block. Since we know it's a canon one, - // we can get it directly, and not (like further below) use - // the parent and then add the block on top externTd = bc.GetTd(block.Hash(), block.NumberU64()) continue } @@ -1970,10 +2075,8 @@ // mechanism. return it.index, errors.New("sidechain ghost-state attack") } } - if externTd == nil { - externTd = bc.GetTd(block.ParentHash(), block.NumberU64()-1) - } - externTd = new(big.Int).Add(externTd, block.Difficulty()) + + externTd = big.NewInt(int64(block.NumberU64() + 1))   if !bc.HasBlock(block.Hash(), block.NumberU64()) { start := time.Now() @@ -1981,8 +2084,8 @@ if err := bc.writeBlockWithoutState(block, externTd); err != nil { return it.index, err } log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(), - "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)), - "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()), + "elapsed", common.PrettyDuration(time.Since(start)), + "txs", len(block.Transactions()), "gas", block.GasUsed(), "root", block.Root()) } } @@ -2010,6 +2113,20 @@ parent = bc.GetHeader(parent.ParentHash, parent.Number.Uint64()-1) } if parent == nil { + log.Info("Found orphaned sidechain, removing", "length", len(hashes)) + batch := bc.db.NewBatch() + + for i := 0; i < len(hashes); i++ { + rawdb.DeleteBlock(batch, hashes[i], numbers[i]) + } + + if err := batch.Write(); err != nil { + log.Error("Error when removing sidechain", "err", err) + } + + // Clear out any stale content from the caches + bc.purge() + return it.index, errors.New("missing parent") } // Import all the pruned blocks to make the state available @@ -2046,6 +2163,20 @@ log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64()) return bc.insertChain(blocks, false) } return 0, nil +} + +func (bc *BlockChain) purge() { + // Clear out any stale content from the caches + bc.hc.headerCache.Purge() + bc.hc.tdCache.Purge() + bc.hc.numberCache.Purge() + // Clear out any stale content from the caches + bc.bodyCache.Purge() + bc.bodyRLPCache.Purge() + bc.receiptsCache.Purge() + bc.blockCache.Purge() + bc.txLookupCache.Purge() + bc.futureBlocks.Purge() }   // reorg takes two blocks, an old chain and a new chain and will reconstruct the @@ -2151,15 +2282,9 @@ if newBlock == nil { return fmt.Errorf("invalid new chain") } } - // Ensure the user sees large reorgs + // Reorgs should not happen with Istanbul consensus. Warn the user. if len(oldChain) > 0 && len(newChain) > 0 { - logFn := log.Info - msg := "Chain reorg detected" - if len(oldChain) > 63 { - msg = "Large chain reorg detected" - logFn = log.Warn - } - logFn(msg, "number", commonBlock.Number(), "hash", commonBlock.Hash(), + log.Error("Chain reorg detected", "number", commonBlock.Number(), "hash", commonBlock.Hash(), "drop", len(oldChain), "dropfrom", oldChain[0].Hash(), "add", len(newChain), "addfrom", newChain[0].Hash()) blockReorgAddMeter.Mark(int64(len(newChain))) blockReorgDropMeter.Mark(int64(len(oldChain))) @@ -2215,7 +2340,10 @@ } return nil }   -func (bc *BlockChain) update() { +// futureBlocksLoop processes the 'future block' queue. +func (bc *BlockChain) futureBlocksLoop() { + defer bc.wg.Done() + futureTimer := time.NewTicker(5 * time.Second) defer futureTimer.Stop() for { @@ -2252,6 +2380,7 @@ from = ancients - bc.txLookupLimit } rawdb.IndexTransactions(bc.db, from, ancients, bc.quit) } + // indexBlocks reindexes or unindexes transactions depending on user configuration indexBlocks := func(tail *uint64, head uint64, done chan struct{}) { defer func() { done <- struct{}{} }() @@ -2284,6 +2413,7 @@ // Unindex a part of stale indices and forward index tail to HEAD-limit rawdb.UnindexTransactions(bc.db, *tail, head-bc.txLookupLimit+1, bc.quit) } } + // Any reindexing done, start listening to chain events and moving the index window var ( done chan struct{} // Non-nil if background unindexing or reindexing routine is active. @@ -2314,6 +2444,11 @@ } } }   +// HasBadBlock returns whether the block with the hash is a bad block +func (bc *BlockChain) HasBadBlock(hash common.Hash) bool { + return (rawdb.ReadBadBlock(bc.db, hash) != nil) +} + // reportBlock logs a bad block error. func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) { rawdb.WriteBadBlock(bc.db, block) @@ -2340,23 +2475,17 @@ // InsertHeaderChain attempts to insert the given header chain in to the local // chain, possibly creating a reorg. If an error is returned, it will return the // index number of the failing header as well an error describing what went wrong. -// -// The verify parameter can be used to fine tune whether nonce verification -// should be done or not. The reason behind the optional check is because some -// of the header retrieval mechanisms already need to verify nonces, as well as -// because nonces can be verified sparsely, not needing to check each. -func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) { +func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int, contiguousHeaders bool) (int, error) { start := time.Now() - if i, err := bc.hc.ValidateHeaderChain(chain, checkFreq); err != nil { + if i, err := bc.hc.ValidateHeaderChain(chain, checkFreq, true); err != nil { return i, err }   - // Make sure only one thread manipulates the chain at once - bc.chainmu.Lock() + if !bc.chainmu.TryLock() { + return 0, errChainStopped + } defer bc.chainmu.Unlock()   - bc.wg.Add(1) - defer bc.wg.Done() _, err := bc.hc.InsertHeaderChain(chain, start) return 0, err } @@ -2485,3 +2614,54 @@ // block processing has started while false means it has stopped. func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscription { return bc.scope.Track(bc.blockProcFeed.Subscribe(ch)) } + +// RecoverRandomnessCache will do a search for the block that was used to generate the given commitment. +// Specifically, it will find the block that this node authored and that block's parent hash is used to +// created the commitment. The search is a reverse iteration of the node's local chain starting at +// the block where it's hash is the given commitmentBlockHash. +func (bc *BlockChain) RecoverRandomnessCache(commitment common.Hash, commitmentBlockHash common.Hash) error { + istEngine, isIstanbul := bc.engine.(consensus.Istanbul) + if !isIstanbul { + return nil + } + + log.Info("Recovering randomness cache entry", "commitment", commitment.Hex(), "initial block search", commitmentBlockHash.Hex()) + + blockHashIter := commitmentBlockHash + var parentHash common.Hash + for { + blockHeader := bc.GetHeaderByHash(blockHashIter) + + // We got to the genesis block, so search didn't find the latest + // block authored by this validator. + if blockHeader.Number.Uint64() == 0 { + return errCommitmentNotFound + } + + blockAuthor, err := istEngine.Author(blockHeader) + if err != nil { + log.Error("Error is retrieving block author", "block number", blockHeader.Number.Uint64(), "block hash", blockHeader.Hash(), "error", err) + return err + } + + if blockAuthor == istEngine.ValidatorAddress() { + parentHash = blockHeader.ParentHash + break + } + + blockHashIter = blockHeader.ParentHash + } + + // Calculate the randomness commitment + // The calculation is stateless (e.g. it's just a hash operation of a string), so any passed in block header and state + // will do. Will use the previously fetched current header and state. + _, randomCommitment, err := istEngine.GenerateRandomness(parentHash) + if err != nil { + log.Error("Couldn't generate the randomness from the parent hash", "parent hash", parentHash, "err", err) + return err + } + + rawdb.WriteRandomCommitmentCache(bc.db, randomCommitment, parentHash) + + return nil +}
diff --git go-ethereum/core/tx_pool_test.go celo/core/tx_pool_test.go index 35b77b0ec11fd32a74be7c2cfa1d7f7a76007810..0e07382e55d80bbd5b8ddd82d0ffab50347b205e 100644 --- go-ethereum/core/tx_pool_test.go +++ celo/core/tx_pool_test.go @@ -24,14 +24,18 @@ "io/ioutil" "math/big" "math/rand" "os" - "sync/atomic" "testing" "time"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/contracts/currency" + "github.com/ethereum/go-ethereum/contracts/testutil" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" @@ -42,6 +46,8 @@ var ( // testTxPoolConfig is a transaction pool configuration without stateful disk // sideeffects used during testing. testTxPoolConfig TxPoolConfig + // eip155Signer to use for generating replay-protected transactions + eip155Signer = types.NewEIP155Signer(params.TestChainConfig.ChainID)   // eip1559Config is a chain config with EIP-1559 enabled at block 0. eip1559Config *params.ChainConfig @@ -53,20 +59,29 @@ testTxPoolConfig.Journal = ""   cpy := *params.TestChainConfig eip1559Config = &cpy - eip1559Config.BerlinBlock = common.Big0 - eip1559Config.LondonBlock = common.Big0 + eip1559Config.EspressoBlock = common.Big0 }   type testBlockChain struct { gasLimit uint64 // must be first field for 64 bit alignment (atomic access) statedb *state.StateDB chainHeadFeed *event.Feed + celoMock testutil.CeloMock +} + +func newTestBlockchain() *testBlockChain { + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + return &testBlockChain{ + statedb: statedb, + gasLimit: 1000000, + + chainHeadFeed: new(event.Feed), + celoMock: testutil.NewCeloMock(), + } }   func (bc *testBlockChain) CurrentBlock() *types.Block { - return types.NewBlock(&types.Header{ - GasLimit: atomic.LoadUint64(&bc.gasLimit), - }, nil, nil, nil, trie.NewStackTrie(nil)) + return types.NewBlock(&types.Header{}, nil, nil, nil, trie.NewStackTrie(nil)) }   func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { @@ -81,12 +96,33 @@ func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription { return bc.chainHeadFeed.Subscribe(ch) }   +func (bc *testBlockChain) Engine() consensus.Engine { + return mockEngine.NewFaker() +} + +func (bc *testBlockChain) GetHeader(common.Hash, uint64) *types.Header { + return nil +} + +func (bc *testBlockChain) NewEVMRunner(header *types.Header, state vm.StateDB) vm.EVMRunner { + return bc.celoMock.Runner +} + +func (bc *testBlockChain) GetVMConfig() *vm.Config { + return nil +} + func transaction(nonce uint64, gaslimit uint64, key *ecdsa.PrivateKey) *types.Transaction { return pricedTransaction(nonce, gaslimit, big.NewInt(1), key) }   func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction { - tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, gasprice, nil), types.HomesteadSigner{}, key) + tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, gasprice, nil, nil, nil, nil), types.HomesteadSigner{}, key) + return tx +} + +func lesTransaction(nonce uint64, gaslimit uint64, gatewayFee *big.Int, key *ecdsa.PrivateKey) *types.Transaction { + tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, big.NewInt(1), nil, &common.Address{}, gatewayFee, nil), types.HomesteadSigner{}, key) return tx }   @@ -94,7 +130,12 @@ func pricedDataTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey, bytes uint64) *types.Transaction { data := make([]byte, bytes) rand.Read(data)   - tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(0), gaslimit, gasprice, data), types.HomesteadSigner{}, key) + tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(0), gaslimit, gasprice, nil, nil, nil, data), types.HomesteadSigner{}, key) + return tx +} + +func protectedTransaction(nonce uint64, gaslimit uint64, key *ecdsa.PrivateKey) *types.Transaction { + tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, big.NewInt(1), nil, nil, nil, nil), eip155Signer, key) return tx }   @@ -118,9 +159,10 @@ return setupTxPoolWithConfig(params.TestChainConfig) }   func setupTxPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateKey) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{10000000, statedb, new(event.Feed)} + // statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + // blockchain := &testBlockChain{10000000, statedb, new(event.Feed)}   + blockchain := newTestBlockchain() key, _ := crypto.GenerateKey() pool := NewTxPool(testTxPoolConfig, config, blockchain)   @@ -225,13 +267,15 @@ var ( key, _ = crypto.GenerateKey() address = crypto.PubkeyToAddress(key.PublicKey) - statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + trigger = false )   + // blockchain := &testChain{&testBlockChain{statedb, 1000000000, new(event.Feed), testutil.NewCeloMock()}, address, &trigger} + blockchain := &testChain{newTestBlockchain(), address, &trigger} + // setup pool with 2 transaction in it - statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether)) - blockchain := &testChain{&testBlockChain{1000000000, statedb, new(event.Feed)}, address, &trigger} + blockchain.testBlockChain.statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether))   tx0 := transaction(0, 100000, key) tx1 := transaction(1, 100000, key) @@ -297,8 +341,22 @@ if err := pool.AddRemote(tx); !errors.Is(err, ErrIntrinsicGas) { t.Error("expected", ErrIntrinsicGas, "got", err) }   + // TODO(joshua): Convert this to testAddGatewayFee + // Adding a gateway fee should result in insufficient funds again. + tx = lesTransaction(0, 100, big.NewInt(50), key) + if err := pool.AddRemote(tx); err != ErrInsufficientFunds { + t.Error("expected", ErrInsufficientFunds) + } + + // Should return to intrinsic gas error when gateway fee is covered. + pool.currentState.AddBalance(from, tx.GatewayFee()) + if err := pool.AddRemote(tx); err != ErrIntrinsicGas { + t.Error("expected", ErrIntrinsicGas, "got", err) + } + testSetNonce(pool, from, 1) testAddBalance(pool, from, big.NewInt(0xffffffffffffff)) + tx = transaction(0, 100000, key) if err := pool.AddRemote(tx); !errors.Is(err, ErrNonceTooLow) { t.Error("expected", ErrNonceTooLow) @@ -377,7 +435,7 @@ pool, key := setupTxPool() defer pool.Stop()   - tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil), types.HomesteadSigner{}, key) + tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil, nil, nil, nil), types.HomesteadSigner{}, key) from, _ := deriveSender(tx) testAddBalance(pool, from, big.NewInt(1)) if err := pool.AddRemote(tx); err != ErrNegativeValue { @@ -415,6 +473,23 @@ tx2 := dynamicFeeTx(0, 100, veryBigNumber, big.NewInt(1), key) if err := pool.AddRemote(tx2); err != ErrFeeCapVeryHigh { t.Error("expected", ErrFeeCapVeryHigh, "got", err) + } +} + +func TestTransactionGasPriceLessThanFloor(t *testing.T) { + t.Parallel() + + pool, key := setupTxPool() + defer pool.Stop() + + ctx := pool.currentCtx.Load().(txPoolContext) + ctx.celoGasPriceMinimumFloor = common.Big2 + pool.currentCtx.Store(ctx) + tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(1), 200000, big.NewInt(1), nil, nil, nil, nil), types.HomesteadSigner{}, key) + from, _ := deriveSender(tx) + pool.currentState.AddBalance(from, big.NewInt(100000000000000)) + if err := pool.AddRemote(tx); err != ErrGasPriceDoesNotExceedMinimumFloor { + t.Error("expected", ErrGasPriceDoesNotExceedMinimumFloor, "got", err) } }   @@ -426,10 +501,10 @@ defer pool.Stop()   addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - statedb.AddBalance(addr, big.NewInt(100000000000000)) + tb := newTestBlockchain() + tb.statedb.AddBalance(addr, big.NewInt(100000000000000)) + pool.chain = tb   - pool.chain = &testBlockChain{1000000, statedb, new(event.Feed)} <-pool.requestReset(nil, nil) } resetState() @@ -455,18 +530,17 @@ defer pool.Stop()   addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - statedb.AddBalance(addr, big.NewInt(100000000000000)) - - pool.chain = &testBlockChain{1000000, statedb, new(event.Feed)} + tb := newTestBlockchain() + tb.statedb.AddBalance(addr, big.NewInt(100000000000000)) + pool.chain = tb <-pool.requestReset(nil, nil) } resetState()   signer := types.HomesteadSigner{} - tx1, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 100000, big.NewInt(1), nil), signer, key) - tx2, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(2), nil), signer, key) - tx3, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(1), nil), signer, key) + tx1, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 100000, big.NewInt(1), nil, nil, nil, nil), signer, key) + tx2, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(2), nil, nil, nil, nil), signer, key) + tx3, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(1), nil, nil, nil, nil), signer, key)   // Add the first two transaction, ensure higher priced stays only if replace, err := pool.add(tx1, false); err != nil || replace { @@ -628,7 +702,7 @@ if pool.all.Count() != 4 { t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 4) } // Reduce the block gas limit, check that invalidated transactions are dropped - atomic.StoreUint64(&pool.chain.(*testBlockChain).gasLimit, 100) + pool.setGasLimit(100) <-pool.requestReset(nil, nil)   if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok { @@ -654,9 +728,7 @@ // postponed back into the future queue to prevent broadcasting them. func TestTransactionPostponing(t *testing.T) { t.Parallel()   - // Create the pool to test the postponing with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) defer pool.Stop() @@ -817,6 +889,84 @@ t.Fatalf("pool internal state corrupted: %v", err) } }   +// Tests that pool.handleDonutActivation() removes transactions without replay protection +// When we change TestChangeConfig to enable Donut this test will need: +// (a) to set pool.donut = false at its start (so we can add unprotected transactions) +// (b) different functions to generate protected vs unprotected transactions, since we will +// need to update transaction() and the others to use replay protection +func TestPoolReAcceptingUnprotectedTxsFromEFork(t *testing.T) { + t.Parallel() + + pool, key := setupTxPool() + // Create a test account and fund it + defer pool.Stop() + + account := crypto.PubkeyToAddress(key.PublicKey) + pool.currentState.AddBalance(account, big.NewInt(1000000)) + + // flag it as before donut + pool.donut = false + pool.espresso = false + + pool.AddRemotesSync([]*types.Transaction{ + protectedTransaction(0, 100000, key), + transaction(1, 100000, key), + + protectedTransaction(10, 100000, key), + transaction(11, 100000, key), + }) + + pending, queued := pool.Stats() + if pending != 2 { + t.Fatalf("before donut, pending transactions mismatched: have %d, want %d", pending, 2) + } + if queued != 2 { + t.Fatalf("before donut, queued transactions mismatched: have %d, want %d", queued, 2) + } + + // In donut fork + pool.donut = true + + pool.AddRemotesSync([]*types.Transaction{ + protectedTransaction(2, 100000, key), + transaction(3, 100000, key), + + protectedTransaction(12, 100000, key), + transaction(13, 100000, key), + }) + + pending, queued = pool.Stats() + if pending != 3 { + t.Fatalf("after donut, pending transactions mismatched: have %d, want %d", pending, 3) + } + if queued != 3 { + t.Fatalf("after donut, queued transactions mismatched: have %d, want %d", queued, 3) + } + + // In E fork + // flag it as E hard fork + pool.espresso = true + pool.AddRemotesSync([]*types.Transaction{ + transaction(3, 100000, key), + protectedTransaction(4, 100000, key), + + transaction(13, 100000, key), + protectedTransaction(14, 100000, key), + }) + + pending, queued = pool.Stats() + if pending != 5 { + t.Fatalf("after espresso, pending transactions mismatched: have %d, want %d", pending, 5) + } + if queued != 5 { + t.Fatalf("after espresso, queued transactions mismatched: have %d, want %d", queued, 5) + } + + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } +} + // Tests that if the transaction count belonging to a single account goes above // some threshold, the higher transactions are dropped to prevent DOS attacks. func TestTransactionQueueAccountLimiting(t *testing.T) { @@ -868,8 +1018,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) { t.Parallel()   // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   config := testTxPoolConfig config.NoLocals = nolocals @@ -960,8 +1109,7 @@ defer func(old time.Duration) { evictionInterval = old }(evictionInterval) evictionInterval = time.Millisecond * 100   // Create the pool to test the non-expiration enforcement - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   config := testTxPoolConfig config.Lifetime = time.Second @@ -1031,8 +1179,8 @@ t.Fatalf("pool internal state corrupted: %v", err) }   // remove current transactions and increase nonce to prepare for a reset and cleanup - statedb.SetNonce(crypto.PubkeyToAddress(remote.PublicKey), 2) - statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 2) + blockchain.statedb.SetNonce(crypto.PubkeyToAddress(remote.PublicKey), 2) + blockchain.statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 2) <-pool.requestReset(nil, nil)   // make sure queue, pending are cleared @@ -1145,8 +1293,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) { t.Parallel()   // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   config := testTxPoolConfig config.GlobalSlots = config.AccountSlots * 10 @@ -1247,8 +1394,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) { t.Parallel()   // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   config := testTxPoolConfig config.AccountSlots = 2 @@ -1281,8 +1427,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { t.Parallel()   // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   config := testTxPoolConfig config.GlobalSlots = 1 @@ -1329,8 +1474,7 @@ func TestTransactionPoolRepricing(t *testing.T) { t.Parallel()   // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) defer pool.Stop() @@ -1577,8 +1721,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) { t.Parallel()   // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain) defer pool.Stop() @@ -1650,8 +1793,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { t.Parallel()   // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   config := testTxPoolConfig config.GlobalSlots = 2 @@ -1756,9 +1898,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) { t.Parallel()   // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} - + blockchain := newTestBlockchain() config := testTxPoolConfig config.GlobalSlots = 128 config.GlobalQueue = 0 @@ -1970,8 +2110,16 @@ } }   add(false) + txCtx := txPoolContext{ + &SysContractCallCtx{ + gasPriceMinimums: make(map[common.Address]*big.Int), + }, + &currency.CurrencyManager{}, + common.Big0, + } for baseFee = 0; baseFee <= 1000; baseFee += 100 { - pool.priced.SetBaseFee(big.NewInt(int64(baseFee))) + txCtx.SysContractCallCtx.gasPriceMinimums[common.ZeroAddress] = big.NewInt(int64(baseFee)) + pool.priced.SetBaseFee(&txCtx) add(true) check(highCap, "fee cap") add(false) @@ -1988,8 +2136,7 @@ func TestTransactionDeduplication(t *testing.T) { t.Parallel()   // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) defer pool.Stop() @@ -2054,8 +2201,7 @@ func TestTransactionReplacement(t *testing.T) { t.Parallel()   // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) defer pool.Stop() @@ -2259,8 +2405,7 @@ file.Close() os.Remove(journal)   // Create the original pool to inject transaction into the journal - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   config := testTxPoolConfig config.NoLocals = nolocals @@ -2301,8 +2446,10 @@ t.Fatalf("pool internal state corrupted: %v", err) } // Terminate the old pool, bump the local nonce, create a new pool and ensure relevant transaction survive pool.Stop() - statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1) - blockchain = &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain.statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1) + oldstatedb := blockchain.statedb + blockchain = newTestBlockchain() + blockchain.statedb = oldstatedb   pool = NewTxPool(config, params.TestChainConfig, blockchain)   @@ -2323,13 +2470,15 @@ if err := validateTxPoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Bump the nonce temporarily and ensure the newly invalidated transaction is removed - statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 2) + blockchain.statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 2) <-pool.requestReset(nil, nil) time.Sleep(2 * config.Rejournal) pool.Stop()   - statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1) - blockchain = &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain.statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1) + oldstatedb = blockchain.statedb + blockchain = newTestBlockchain() + blockchain.statedb = oldstatedb pool = NewTxPool(config, params.TestChainConfig, blockchain)   pending, queued = pool.Stats() @@ -2357,8 +2506,7 @@ func TestTransactionStatusCheck(t *testing.T) { t.Parallel()   // Create the pool to test the status retrievals with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - blockchain := &testBlockChain{1000000, statedb, new(event.Feed)} + blockchain := newTestBlockchain()   pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) defer pool.Stop()
diff --git go-ethereum/core/state_transition.go celo/core/state_transition.go index 0575edc149625d3d6e777dce29e3c50990fce1ef..8a78ecf46634b9d01f52ed9f60414b4fb47132ef 100644 --- go-ethereum/core/state_transition.go +++ celo/core/state_transition.go @@ -22,14 +22,22 @@ "math" "math/big"   "github.com/ethereum/go-ethereum/common" - cmath "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/contracts" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/contracts/currency" + "github.com/ethereum/go-ethereum/contracts/erc20gas" + gpm "github.com/ethereum/go-ethereum/contracts/gasprice_minimum" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/core/vm/vmcontext" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + // cmath "github.com/ethereum/go-ethereum/common/math" )   -var emptyCodeHash = crypto.Keccak256Hash(nil) +// Used for EOA Check +// var emptyCodeHash = crypto.Keccak256Hash(nil)   /* The State Transitioning Model @@ -60,6 +68,9 @@ value *big.Int data []byte state vm.StateDB evm *vm.EVM + vmRunner vm.EVMRunner + gasPriceMinimum *big.Int + sysCtx *SysContractCallCtx }   // Message represents a message sent to a contract. @@ -71,12 +82,30 @@ GasPrice() *big.Int GasFeeCap() *big.Int GasTipCap() *big.Int Gas() uint64 + + // FeeCurrency specifies the currency for gas and gateway fees. + // nil correspond to Celo Gold (native currency). + // All other values should correspond to ERC20 contract addresses extended to be compatible with gas payments. + FeeCurrency() *common.Address + GatewayFeeRecipient() *common.Address + GatewayFee() *big.Int Value() *big.Int   Nonce() uint64 IsFake() bool Data() []byte AccessList() types.AccessList + + // Whether this transaction omitted the 3 Celo-only fields (FeeCurrency & co.) + EthCompatible() bool +} + +func CheckEthCompatibility(msg Message) error { + if msg.EthCompatible() && !(msg.FeeCurrency() == nil && msg.GatewayFeeRecipient() == nil && msg.GatewayFee().Sign() == 0) { + return types.ErrEthCompatibleTransactionIsntCompatible + } + return nil + }   // ExecutionResult includes all output after executing given evm @@ -115,10 +144,10 @@ return common.CopyBytes(result.ReturnData) }   // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool) (uint64, error) { +func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, feeCurrency *common.Address, gasForAlternativeCurrency uint64, isEIP2028 bool) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 - if isContractCreation && isHomestead { + if isContractCreation { gas = params.TxGasContractCreation } else { gas = params.TxGas @@ -137,17 +166,39 @@ nonZeroGas := params.TxDataNonZeroGasFrontier if isEIP2028 { nonZeroGas = params.TxDataNonZeroGasEIP2028 } + if (math.MaxUint64-gas)/nonZeroGas < nz { + log.Debug("IntrinsicGas", "gas uint overflow") return 0, ErrGasUintOverflow } gas += nz * nonZeroGas   z := uint64(len(data)) - nz if (math.MaxUint64-gas)/params.TxDataZeroGas < z { + log.Debug("IntrinsicGas", "gas uint overflow") return 0, ErrGasUintOverflow } gas += z * params.TxDataZeroGas } + + // This gas is used for charging user for one `debitFrom` transaction to deduct their balance in + // non-native currency and two `creditTo` transactions, one covers for the miner fee in + // non-native currency at the end and the other covers for the user refund at the end. + // A user might or might not have a gas refund at the end and even if they do the gas refund might + // be smaller than maxGasForDebitAndCreditTransactions. We still decide to deduct and do the refund + // since it makes the mining fee more consistent with respect to the gas fee. Otherwise, we would + // have to expect the user to estimate the mining fee right or else end up losing + // min(gas sent - gas charged, maxGasForDebitAndCreditTransactions) extra. + // In this case, however, the user always ends up paying maxGasForDebitAndCreditTransactions + // keeping it consistent. + if feeCurrency != nil { + if (math.MaxUint64 - gas) < gasForAlternativeCurrency { + log.Debug("IntrinsicGas", "gas uint overflow") + return 0, ErrGasUintOverflow + } + gas += gasForAlternativeCurrency + } + if accessList != nil { gas += uint64(len(accessList)) * params.TxAccessListAddressGas gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas @@ -156,10 +207,18 @@ return gas, nil }   // NewStateTransition initialises and returns a new state transition object. -func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { +func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner, sysCtx *SysContractCallCtx) *StateTransition { + var gasPriceMinimum *big.Int + if evm.ChainConfig().IsEspresso(evm.Context.BlockNumber) { + gasPriceMinimum = sysCtx.GetGasPriceMinimum(msg.FeeCurrency()) + } else { + gasPriceMinimum, _ = gpm.GetGasPriceMinimum(vmRunner, msg.FeeCurrency()) + } + return &StateTransition{ gp: gp, evm: evm, + vmRunner: vmRunner, msg: msg, gasPrice: msg.GasPrice(), gasFeeCap: msg.GasFeeCap(), @@ -167,6 +226,8 @@ gasTipCap: msg.GasTipCap(), value: msg.Value(), data: msg.Data(), state: evm.StateDB, + gasPriceMinimum: gasPriceMinimum, + sysCtx: sysCtx, } }   @@ -177,8 +238,20 @@ // ApplyMessage returns the bytes returned by any EVM execution (if it took place), // the gas used (which includes gas refunds) and an error if it failed. An error always // indicates a core error meaning that the message would always fail for that particular // state and would never be accepted within a block. -func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) (*ExecutionResult, error) { - return NewStateTransition(evm, msg, gp).TransitionDb() +func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner, sysCtx *SysContractCallCtx) (*ExecutionResult, error) { + log.Trace("Applying state transition message", "from", msg.From(), "nonce", msg.Nonce(), "to", msg.To(), "gas price", msg.GasPrice(), "fee currency", msg.FeeCurrency(), "gateway fee recipient", msg.GatewayFeeRecipient(), "gateway fee", msg.GatewayFee(), "gas", msg.Gas(), "value", msg.Value(), "data", msg.Data()) + return NewStateTransition(evm, msg, gp, vmRunner, sysCtx).TransitionDb() +} + +// ApplyMessageWithoutGasPriceMinimum applies the given message with the gas price minimum +// set to zero. It's only for use in eth_call and eth_estimateGas, so that they can be used +// with gas price set to zero if the sender doesn't have funds to pay for gas. +// Returns the gas used (which does not include gas refunds) and an error if it failed. +func ApplyMessageWithoutGasPriceMinimum(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner, sysCtx *SysContractCallCtx) (*ExecutionResult, error) { + log.Trace("Applying state transition message without gas price minimum", "from", msg.From(), "nonce", msg.Nonce(), "to", msg.To(), "fee currency", msg.FeeCurrency(), "gateway fee recipient", msg.GatewayFeeRecipient(), "gateway fee", msg.GatewayFee(), "gas limit", msg.Gas(), "value", msg.Value(), "data", msg.Data()) + st := NewStateTransition(evm, msg, gp, vmRunner, sysCtx) + st.gasPriceMinimum = common.Big0 + return st.TransitionDb() }   // to returns the recipient of the message. @@ -189,28 +262,112 @@ } return *st.msg.To() }   -func (st *StateTransition) buyGas() error { - mgval := new(big.Int).SetUint64(st.msg.Gas()) - mgval = mgval.Mul(mgval, st.gasPrice) - balanceCheck := mgval - if st.gasFeeCap != nil { - balanceCheck = new(big.Int).SetUint64(st.msg.Gas()) - balanceCheck = balanceCheck.Mul(balanceCheck, st.gasFeeCap) - balanceCheck.Add(balanceCheck, st.value) +// payFees deducts gas and gateway fees from sender balance and adds the purchased amount of gas to the state. +func (st *StateTransition) payFees(espresso bool) error { + var isWhiteListed bool + if espresso { + isWhiteListed = st.sysCtx.IsWhitelisted(st.msg.FeeCurrency()) + } else { + isWhiteListed = currency.IsWhitelisted(st.vmRunner, st.msg.FeeCurrency()) + } + if !isWhiteListed { + log.Trace("Fee currency not whitelisted", "fee currency address", st.msg.FeeCurrency()) + return ErrNonWhitelistedFeeCurrency } - if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 { - return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want) + + if err := st.canPayFee(st.msg.From(), st.msg.FeeCurrency(), espresso); err != nil { + return err } if err := st.gp.SubGas(st.msg.Gas()); err != nil { return err } + + st.initialGas = st.msg.Gas() st.gas += st.msg.Gas() + err := st.debitFee(st.msg.From(), st.msg.FeeCurrency()) + return err +}   - st.initialGas = st.msg.Gas() - st.state.SubBalance(st.msg.From(), mgval) +// canPayFee checks whether accountOwner's balance can cover transaction fee. +// +// For native token(CELO) as feeCurrency: +// - Pre-Espresso: it ensures balance >= GasPrice * gas + gatewayFee (1) +// - Post-Espresso: it ensures balance >= GasFeeCap * gas + value + gatewayFee (2) +// For non-native tokens(cUSD, cEUR, ...) as feeCurrency: +// - Pre-Espresso: it ensures balance > GasPrice * gas + gatewayFee (3) +// - Post-Espresso: it ensures balance >= GasFeeCap * gas + gatewayFee (4) +func (st *StateTransition) canPayFee(accountOwner common.Address, feeCurrency *common.Address, espresso bool) error { + if feeCurrency == nil { + balance := st.state.GetBalance(st.msg.From()) + if espresso { + // feeGap = GasFeeCap * gas + value + gatewayFee, as in (2) + feeGap := new(big.Int).SetUint64(st.msg.Gas()) + feeGap.Mul(feeGap, st.gasFeeCap) + feeGap.Add(feeGap, st.value) + if st.msg.GatewayFeeRecipient() != nil { + feeGap.Add(feeGap, st.msg.GatewayFee()) + } + + if balance.Cmp(feeGap) < 0 { + return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), balance, feeGap) + } + } else { + // effectiveFee = GasPrice * gas + gatewayFee, as in (1) + effectiveFee := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) + if st.msg.GatewayFeeRecipient() != nil { + effectiveFee.Add(effectiveFee, st.msg.GatewayFee()) + } + + if balance.Cmp(effectiveFee) < 0 { + return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), balance, effectiveFee) + } + } + return nil + } else { + balance, err := currency.GetBalanceOf(st.vmRunner, accountOwner, *feeCurrency) + if err != nil { + return err + } + if espresso { + // feeGap = GasFeeCap * gas + gatewayFee, as in (4) + feeGap := new(big.Int).SetUint64(st.msg.Gas()) + feeGap.Mul(feeGap, st.gasFeeCap) + if st.msg.GatewayFeeRecipient() != nil { + feeGap.Add(feeGap, st.msg.GatewayFee()) + } + if balance.Cmp(feeGap) < 0 { + return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), balance, feeGap) + } + } else { + // effectiveFee = GasPrice * gas + gatewayFee, as in (3) + effectiveFee := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) + if st.msg.GatewayFeeRecipient() != nil { + effectiveFee.Add(effectiveFee, st.msg.GatewayFee()) + } + if balance.Cmp(effectiveFee) <= 0 { + return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), balance, effectiveFee) + } + } + } return nil }   +func (st *StateTransition) debitFee(from common.Address, feeCurrency *common.Address) (err error) { + effectiveFee := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) + // If GatewayFeeRecipient is unspecified, the gateway fee value is ignore and the sender is not charged. + if st.msg.GatewayFeeRecipient() != nil { + effectiveFee.Add(effectiveFee, st.msg.GatewayFee()) + } + log.Trace("Debiting fee", "from", from, "amount", effectiveFee, "feeCurrency", feeCurrency) + // native currency + if feeCurrency == nil { + st.state.SubBalance(from, effectiveFee) + return nil + } else { + return erc20gas.DebitFees(st.evm, from, effectiveFee, feeCurrency) + } +} + func (st *StateTransition) preCheck() error { // Only check transactions that are not fake if !st.msg.IsFake() { @@ -223,14 +380,10 @@ } else if stNonce > msgNonce { return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow, st.msg.From().Hex(), msgNonce, stNonce) } - // Make sure the sender is an EOA - if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) { - return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, - st.msg.From().Hex(), codeHash) - } } - // Make sure that transaction gasFeeCap is greater than the baseFee (post london) - if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { + + // Make sure that transaction gasFeeCap >= baseFee (post Espresso) + if st.evm.ChainConfig().IsEspresso(st.evm.Context.BlockNumber) { // Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call) if !st.evm.Config.NoBaseFee || st.gasFeeCap.BitLen() > 0 || st.gasTipCap.BitLen() > 0 { if l := st.gasFeeCap.BitLen(); l > 256 { @@ -245,15 +398,26 @@ if st.gasFeeCap.Cmp(st.gasTipCap) < 0 { return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap, st.msg.From().Hex(), st.gasTipCap, st.gasFeeCap) } - // This will panic if baseFee is nil, but basefee presence is verified - // as part of header validation. - if st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 { + if st.gasFeeCap.Cmp(st.gasPriceMinimum) < 0 { return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow, - st.msg.From().Hex(), st.gasFeeCap, st.evm.Context.BaseFee) + st.msg.From().Hex(), st.gasFeeCap, st.gasPriceMinimum) } } + } else { // Make sure this transaction's gas price >= baseFee (pre Espresso) + if st.gasPrice.Cmp(st.gasPriceMinimum) < 0 { + log.Debug("Tx gas price is less than minimum", "minimum", st.gasPriceMinimum, "price", st.gasPrice) + return ErrGasPriceDoesNotExceedMinimum + } } - return st.buyGas() + + // // Make sure the sender is an EOA + // if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) { + // return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, + // st.msg.From().Hex(), codeHash) + // } + + return nil + }   // TransitionDb will transition the state by applying the current message and @@ -273,41 +437,68 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { // First check this message satisfies all consensus rules before // applying the message. The rules include these clauses // + // 0. If the message is from an eth-compatible transaction, that we support those + // and that none of the non-eth-compatible fields are present // 1. the nonce of the message caller is correct - // 2. caller has enough balance to cover transaction fee(gaslimit * gasprice) - // 3. the amount of gas required is available in the block - // 4. the purchased gas is enough to cover intrinsic usage - // 5. there is no overflow when calculating intrinsic gas - // 6. caller has enough balance to cover asset transfer for **topmost** call + // 2. the gas price meets the minimum gas price + // 3. caller has enough balance (in the right currency) to cover transaction fee + // 4. the amount of gas required is available in the block + // 5. the purchased gas is enough to cover intrinsic usage + // 6. there is no overflow when calculating intrinsic gas + // 7. caller has enough balance to cover asset transfer for **topmost** call   - // Check clauses 1-3, buy gas if everything is correct + // Clause 0 + if st.msg.EthCompatible() && !st.evm.ChainConfig().IsDonut(st.evm.Context.BlockNumber) { + return nil, ErrEthCompatibleTransactionsNotSupported + } + if err := CheckEthCompatibility(st.msg); err != nil { + return nil, err + } + + // Check clauses 1-2 if err := st.preCheck(); err != nil { return nil, err } msg := st.msg sender := vm.AccountRef(msg.From()) - homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber) istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber) - london := st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) + espresso := st.evm.ChainConfig().IsEspresso(st.evm.Context.BlockNumber) contractCreation := msg.To() == nil   - // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul) + // Calculate intrinsic gas, check clauses 5-6 + gasForAlternativeCurrency := uint64(0) + // If the fee currency is nil, do not retrieve the intrinsic gas adjustment from the chain state, as it will not be used. + if msg.FeeCurrency() != nil { + if espresso { + gasForAlternativeCurrency = st.sysCtx.GetIntrinsicGasForAlternativeFeeCurrency() + } else { + gasForAlternativeCurrency = blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(st.vmRunner) + } + } + gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, msg.FeeCurrency(), gasForAlternativeCurrency, istanbul) if err != nil { return nil, err } - if st.gas < gas { - return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas) + + // If the intrinsic gas is more than provided in the tx, return without failing. + if gas > st.msg.Gas() { + return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.msg.Gas(), gas) + } + // Check clauses 3-4, pay the fees (which buys gas), and subtract the intrinsic gas + err = st.payFees(espresso) + if err != nil { + log.Error("Transaction failed to buy gas", "err", err, "gas", gas) + return nil, err } st.gas -= gas   - // Check clause 6 + // Check clause 7 if msg.Value().Sign() > 0 && !st.evm.Context.CanTransfer(st.state, msg.From(), msg.Value()) { return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()) }   // Set up the initial access list. - if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsBerlin { + if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); espresso { st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) } var ( @@ -322,19 +513,18 @@ st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1) ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value) }   - if !london { + if !espresso { // Before EIP-3529: refunds were capped to gasUsed / 2 st.refundGas(params.RefundQuotient) } else { // After EIP-3529: refunds are capped to gasUsed / 5 st.refundGas(params.RefundQuotientEIP3529) } - effectiveTip := st.gasPrice - if london { - effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee)) + + err = st.distributeTxFees() + if err != nil { + return nil, err } - st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)) - return &ExecutionResult{ UsedGas: st.gasUsed(), Err: vmerr, @@ -342,18 +532,76 @@ ReturnData: ret, }, nil }   +// distributeTxFees calculates the amounts and recipients of transaction fees and credits the accounts. +func (st *StateTransition) distributeTxFees() error { + // Run only primary evm.Call() with tracer + if st.evm.GetDebug() { + st.evm.SetDebug(false) + defer func() { st.evm.SetDebug(true) }() + } + + // Determine the refund and transaction fee to be distributed. + refund := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) + gasUsed := new(big.Int).SetUint64(st.gasUsed()) + totalTxFee := new(big.Int).Mul(gasUsed, st.gasPrice) + from := st.msg.From() + + // Divide the transaction into a base (the minimum transaction fee) and tip (any extra, or min(max tip, feecap - GPM) if espresso). + baseTxFee := new(big.Int).Mul(gasUsed, st.gasPriceMinimum) + // No need to do effectiveTip calculation, because st.gasPrice == effectiveGasPrice, and effectiveTip = effectiveGasPrice - baseTxFee + tipTxFee := new(big.Int).Sub(totalTxFee, baseTxFee) + + feeCurrency := st.msg.FeeCurrency() + + gatewayFeeRecipient := st.msg.GatewayFeeRecipient() + if gatewayFeeRecipient == nil { + gatewayFeeRecipient = &common.ZeroAddress + } + + caller := &vmcontext.SharedEVMRunner{EVM: st.evm} + governanceAddress, err := contracts.GetRegisteredAddress(caller, config.GovernanceRegistryId) + if err != nil { + if err != contracts.ErrSmartContractNotDeployed && err != contracts.ErrRegistryContractNotDeployed { + return err + } + log.Trace("Cannot credit gas fee to community fund: refunding fee to sender", "error", err, "fee", baseTxFee) + governanceAddress = common.ZeroAddress + refund.Add(refund, baseTxFee) + baseTxFee = new(big.Int) + } + + log.Trace("distributeTxFees", "from", from, "refund", refund, "feeCurrency", st.msg.FeeCurrency(), + "gatewayFeeRecipient", *gatewayFeeRecipient, "gatewayFee", st.msg.GatewayFee(), + "coinbaseFeeRecipient", st.evm.Context.Coinbase, "coinbaseFee", tipTxFee, + "comunityFundRecipient", governanceAddress, "communityFundFee", baseTxFee) + if feeCurrency == nil { + if gatewayFeeRecipient != &common.ZeroAddress { + st.state.AddBalance(*gatewayFeeRecipient, st.msg.GatewayFee()) + } + if governanceAddress != common.ZeroAddress { + st.state.AddBalance(governanceAddress, baseTxFee) + } + st.state.AddBalance(st.evm.Context.Coinbase, tipTxFee) + st.state.AddBalance(from, refund) + } else { + if err = erc20gas.CreditFees(st.evm, from, st.evm.Context.Coinbase, gatewayFeeRecipient, governanceAddress, refund, tipTxFee, st.msg.GatewayFee(), baseTxFee, feeCurrency); err != nil { + log.Error("Error crediting", "from", from, "coinbase", st.evm.Context.Coinbase, "gateway", gatewayFeeRecipient, "fund", governanceAddress) + return err + } + + } + return nil +} + +// refundGas adds unused gas back the state transition and gas pool. func (st *StateTransition) refundGas(refundQuotient uint64) { // Apply refund counter, capped to a refund quotient refund := st.gasUsed() / refundQuotient if refund > st.state.GetRefund() { refund = st.state.GetRefund() } + st.gas += refund - - // Return ETH for remaining gas, exchanged at the original rate. - remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) - st.state.AddBalance(st.msg.From(), remaining) - // Also return remaining gas to the block gas counter so it is // available for the next transaction. st.gp.AddGas(st.gas)
diff --git go-ethereum/core/genesis_alloc.go celo/core/genesis_alloc.go index 9db3cb48ed3ee7d6978edfa8d8d0e1a9031fa0a1..32c2357dc74cde9a88fdfb816bc0d81d9101b0e1 100644 --- go-ethereum/core/genesis_alloc.go +++ celo/core/genesis_alloc.go @@ -17,12 +17,612 @@ package core   // Constants containing the genesis allocation of built-in genesis blocks. -// Their content is an RLP-encoded list of (address, balance) tuples. -// Use mkalloc.go to create/update them. + +const devAllocJSON = "{\"47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95\":{\"balance\":\"103010030000000000000000000\"},\"000000000000000000000000000000000000ce10\":{\"code\":\"0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f70f17a9af47d0d17d25b03e7411c6dbdccbabce22c6ca2de80c004eab28f9870029\",\"storage\":{\"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103\":\"47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95\"},\"balance\":\"0\"}}"   -// nolint: misspell -const mainnetAllocData = "\xfa\x04]X\u0793\r\x83b\x011\x8e\u0189\x9agT\x06\x908'\x80t2\x80\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0793\x17bC\x0e\xa9\u00e2nWI\xaf\xdbp\xda_x\u077b\x8c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0793\x1d\x14\x80K9\x9cn\xf8\x0edWoev`\x80O\xec\v\x89\u3bb5sr@\xa0\x00\x00\u07932@5\x87\x94{\x9f\x15b*h\xd1\x04\xd5M3\xdb\xd1\u0349\x043\x87Oc,\xc6\x00\x00\u0793I~\x92\xcd\xc0\xe0\xb9c\xd7R\xb2)j\u02c7\u0682\x8b$\x89\n\x8fd\x9f\xe7\xc6\x18\x00\x00\u0793K\xfb\xe1Tk\xc6\xc6[\\~\xaaU0K8\xbb\xfe\xc6\u04c9lk\x93[\x8b\xbd@\x00\x00\u0793Z\x9c\x03\xf6\x9d\x17\xd6l\xbb\x8a\xd7!\x00\x8a\x9e\xbb\xb86\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u0793]\x0e\xe8\x15^\xc0\xa6\xffh\bU,\xa5\xf1k\xb5\xbe2:\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u0793v\"\xd8J#K\xb8\xb0x#\x0f\u03c4\xb6z\u9a2c\xae\x89%\xe1\xccQ\x99R\xf8\x00\x00\u0793{\x9f\xc3\x19\x05\xb4\x99K\x04\xc9\xe2\xcf\xdc^'pP?B\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u0793\u007fJ#\xca\x00\xcd\x04=%\u0088\x8c\x1a\xa5h\x8f\x81\xa3D\x89)\xf0\xa9[\xfb\xf7)\x00\x00\u0793\x869\u06bb\u3bac\x88{]\xc0\xe4>\x13\xbc\u0487\xd7l\x89\x10\xd0\xe3\xc8}n,\x00\x00\u0793\x89P\x86y\xab\xf8\xc7\x1b\xf6x\x16\x87\x12\x0e>j\x84XM\x89a\x94\x04\x9f0\xf7 \x00\x00\u0793\x8f\xc7\u02ed\xff\xbd\r\u007f\xe4O\x8d\xfd`\xa7\x9dr\x1a\x1c\x9c\x8965\u026d\xc5\u07a0\x00\x00\u0793\x95`\xa3\xdebxh\xf9\x1f\xa8\xbf\xe1\xc1\xb7\xaf\xaf\b\x18k\x89\x1cg\xf5\xf7\xba\xa0\xb0\x00\x00\u0793\x96\x97G\xf7\xa5\xb3\x06E\xfe\x00\xe4I\x01CZ\xce$\xcc7\x89\\(=A\x03\x94\x10\x00\x00\u0793\x9am}\xb3&g\x9bw\xc9\x03\x91\xa7Gm#\x8f;\xa3>\x89\n\xdaUGK\x814\x00\x00\u0793\x9e\xef\n\b\x86\x05n?i!\x18S\xb9\xb7E\u007f7\x82\u4262\xa8x\x06\x9b(\xe0\x00\x00\u0793\x9f\xdb\xf4N\x1fJcb\xb7i\u00daG_\x95\xa9l+\u01c9\x1e\x93\x12\x83\xcc\xc8P\x00\x00\u07d3\xa5y\u007fR\xc9\u054f\x18\x9f6\xb1\xd4]\x1b\xf6\x04\x1f/k\x8a\x01'\u0473F\x1a\xcd\x1a\x00\x00\u0793\xaaS\x81\xb2\x13\x8e\xbe\xff\xc1\x91\xd5\xd8\u00d1u;p\x98\u04895\xab\xb0\x9f\xfe\u07b6\x80\x00\u0793\xaa\xda%\xea\"\x86p\x9a\xbbB-A\x92?\u04c0\xcd\x04\u01c9#=\xf3)\x9far\x00\x00\u0793\xac\xbf\xb2\xf2ZT\x85\xc79\xefp\xa4N\xee\xeb|e\xa6o\x89\x05k\xc7^-c\x10\x00\x00\u07d3\xac\xc6\xf0\x82\xa4B\x82\x87d\xd1\x1fX\u0589J\xe4\b\xf0s\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u0793\xb2w\xb0\x99\xa8\xe8f\xca\x0e\xc6[\u02c7(O\xd1B\xa5\x82\x89j\xcb=\xf2~\x1f\x88\x00\x00\u0753\xbd\xd4\x01:\xa3\x1c\x04al+\xc9x_'\x88\xf9\x15g\x9b\x88\xb9\xf6]\x00\xf6<\x00\x00\u0793\xc2}c\xfd\xe2K\x92\xee\x8a\x1e~\xd5\xd2m\x8d\xc5\xc8;\x03\x89lk\x93[\x8b\xbd@\x00\x00\u0553\xc4\x0f\xe2\tT#P\x9b\x9f\u0677T21X\xaf#\x10\xf3\x80\u0793\xd7^\xd6\fwO\x8b:ZQs\xfb\x183\xadq\x05\xa2\u0649l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d3\u05cd\x89\xb3_G'\x16\xec\xea\xfe\xbf`\x05'\u04e1\xf9i\x8a\x05\xe0T\x9c\x962\xe1\xd8\x00\x00\u0793\xda\xe2{5\v\xae \xc5e!$\xaf]\x8b\\\xba\x00\x1e\xc1\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u0793\xdc\x01\xcb\xf4Ix\xa4.\x8d\xe8\xe46\xed\xf9B\x05\u03f6\xec\x89O\x0f\xeb\xbc\u068c\xb4\x00\x00\u07d3\u607c-\x10\xdbb\u0785\x84\x83$I\"P4\x8e\x90\xbf\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d3\xf4c\xe17\xdc\xf6%\xfb\U000fc8de\u0298\u04b9h\xcf\u007f\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\x01\x00\a9K\x8bue\xa1e\x8a\xf8\x8c\xe4cI\x915\u05b7\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x01\r\xf1\xdfK\xed#v\r-\x1c\x03x\x15\x86\xdd\xf7\x91\x8eT\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\x01\x0fJ\x98\u07e1\xd9y\x9b\xf5\u01d6\xfbU\x0e\xfb\xe7\xec\xd8w\x8a\x01\xb2\xf2\x92#b\x92\xc7\x00\x00\u07d4\x01\x15PW\x00/k\r\x18\xac\xb98\x8d;\xc8\x12\x9f\x8fz \x89H\xa4<T`/p\x00\x00\u07d4\x01\"n\n\xd8\xd6\"w\xb1bb\x1cb\xc9(\xe9n\v\x9a\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01&\xe1.\xbc\x17\x03_5\xc0\xe9\xd1\x1d\xd1H9<@]z\x89lf\x06E\xaaG\x18\x00\x00\u07d4\x01/9j+^\xb85Y\xba\xc5\x15\xe5!\r\xf2\xc8\xc3b\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x014\xff8\x15_\xab\xae\x94\xfd5\xc4\xff\xe1\u05dd\xe7\xef\x9cY\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x016\xa5\xafl2\x99\u01b5\xf0\x05\xfd\xad\xdb\x14\x8c\a\v)\x9b\x89\x01\x1a\xa9\xac\x15\xf1(\x00\x00\u07d4\x01H\x8a\xd3\xda`<L\xddl\xb0\xb7\xa1\xe3\r*0\xc8\xfc8\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01It\xa1\xf4k\xf2\x04\x94J\x851\x11\xe5/\x16\x02a}\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01K\u007fg\xb1O]\x98=\x87\x01OW\f\x8b\x99;\x98r\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01Q\xfa]\x17\xa2\xdc\xe2\xd7\xf1\xeb9\xef\u007f\xe2\xad!=]\x89\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x01Wz\xfdNP\x89\x02G\u0271\rD\xafs\"\x9a\xec\x88O\x89$\xdc\xe5M4\xa1\xa0\x00\x00\xe0\x94\x01_\t}\x9a\xcd\xdc\u076f\xaf*\x10~\xb9:@\xfc\x94\xb0L\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x01i\xc1\xc2\x10\xea\xe8E\xe5h@A.\x1fe\x99>\xa9\x0f\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01k`\xbbmg\x92\x8c)\xfd\x03\x13\xc6f\u068f\x16\x98\xd9\u0149lk\x93[\x8b\xbd@\x00\x00\u07d4\x01l\x85\xe1a;\x90\x0f\xa3W\xb8(;\x12\x0ee\xae\xfc\xdd\b\x89+]\x97\x84\xa9|\xd5\x00\x00\u07d4\x01\x84\x92H\x8b\xa1\xa2\x924\"G\xb3\x18U\xa5Y\x05\xfe\xf2i\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x01\x8f \xa2{'\xecD\x1a\xf7#\xfd\x90\x99\xf2\u02f7\x9dbc\x89uy*\x8a\xbd\xef|\x00\x00\u07d4\x01\x91\xebT~{\xf6\x97k\x9b\x1bWuFv\x1d\xe6V\"\xe2\x89lkLM\xa6\u077e\x00\x00\u07d4\x01\x9dp\x95y\xffK\xc0\x9f\xdc\xdd\xe41\xdc\x14G\xd2\xc2`\xbc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x01\xa2Z_Z\xf0\x16\x9b0\x86L;\xe4\xd7V<\xcdD\xf0\x9e\x89M\x85<\x8f\x89\b\x98\x00\x00\xe0\x94\x01\xa7\xd9\xfa}\x0e\xb1\x18\\g\xe5M\xa8<.u\xdbi\u37ca\x01\x9dJ\xdd\xd0\u063c\x96\x00\x00\u07d4\x01\xa8\x18\x13ZAB\x10\xc3|b\xb6%\xac\xa1\xa5F\x11\xac6\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\x01\xb1\xca\xe9\x1a;\x95Y\xaf\xb3<\xdcmh\x94B\xfd\xbf\xe07\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01\xb5\xb5\xbcZ\x11\u007f\xa0\x8b4\xed\x1d\xb9D\x06\bYz\xc5H\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01\xbb\xc1Og\xaf\x069\xaa\xb1D\x1ej\b\xd4\xceqb\t\x0f\x89F\xfc\xf6\x8f\xf8\xbe\x06\x00\x00\xe0\x94\x01\xd08\x15\xc6\x1fAkq\xa2a\n-\xab\xa5\x9f\xf6\xa6\xde[\x8a\x02\x05\xdf\xe5\v\x81\xc8.\x00\x00\u07d4\x01\u0559\xee\r_\x8c8\xab-9.,e\xb7L<\xe3\x18 \x89\x1b\xa5\xab\xf9\xe7y8\x00\x00\u07d4\x01\xe4\x05!\x12%0\u066c\x91\x11<\x06\xa0\x19\vmc\x85\v\x89Hz\x9a0E9D\x00\x00\u07d4\x01\xe6A]X{\x06T\x90\xf1\xed\u007f!\xd6\xe0\xf3\x86\xeegG\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x01\xe8d\xd3Tt\x1bB>oB\x85\x17$F\x8ct\xf5\xaa\x9c\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x01\xed_\xba\x8d.\xabg:\xec\x04-0\xe4\xe8\xa6\x11\xd8\xc5Z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01\xfb\x8e\xc1$%\xa0O\x81>F\xc5L\x05t\x8c\xa6\xb2\x9a\xa9\x89\x0e\x15s\x03\x85F|\x00\x00\u07d4\x01\xff\x1e\xb1\u07adP\xa7\xf2\xf9c\x8f\xde\xe6\xec\xcf:{*\u0209 \x86\xac5\x10R`\x00\x00\u07d4\x02\x03b\u00ed\xe8x\u0290\u05b2\u0609\xa4\xccU\x10\xee\xd5\xf3\x898\x88\xe8\xb3\x11\xad\xb3\x80\x00\u07d4\x02\x03\xae\x01\xd4\xc4\x1c\xae\x18e\xe0K\x1f[S\xcd\xfa\xec\xae1\x896\x89\xcd\u03b2\x8c\xd7\x00\x00\u07d4\x02\b\x93a\xa3\xfetQ\xfb\x1f\x87\xf0\x1a-\x86fS\xdc\v\a\x89\x02*\xc7H2\xb5\x04\x00\x00\u07d4\x02\x1fi\x04=\xe8\x8cI\x17\xca\x10\xf1\x84(\x97\xee\xc0X\x9c|\x89kD\u03f8\x14\x87\xf4\x00\x00\u07d4\x02)\x0f\xb5\xf9\xa5\x17\xf8(E\xac\xde\xca\x0f\xc8F\x03\x9b\xe23\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x029\xb4\xf2\x1f\x8e\x05\xcd\x01Q++\xe7\xa0\xe1\x8am\x97F\a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x02Gr\x12\xff\xddu\xe5\x15VQ\xb7e\x06\xb1dfq\xa1\xeb\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x02J\t\x8a\xe7\x02\xbe\xf5@l\x9c\"\xb7\x8b\xd4\xeb,\u01e2\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x02K\xdd,{\xfdP\x0e\xe7@O\u007f\xb3\xe9\xfb1\xdd \xfb\u0449\t\xc2\x00vQ\xb2P\x00\x00\u07d4\x02Sg\x96\x03\x04\xbe\xee4Y\x11\x18\xe9\xac-\x13X\xd8\x02\x1a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x02V\x14\x9f[Pc\xbe\xa1N\x15f\x1f\xfbX\xf9\xb4Y\xa9W\x89&)\xf6n\fS\x00\x00\x00\u07d4\x02`=z;\xb2\x97\xc6|\x87~]4\xfb\u0579\x13\xd4\xc6:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x02a\xad:\x17*\xbf\x13\x15\xf0\xff\xec2p\x98j\x84\t\xcb%\x89\v\b!;\u03cf\xfe\x00\x00\u07d4\x02d2\xaf7\xdcQ\x13\xf1\xf4mH\nM\u0c80R#~\x89\x13I\xb7\x86\xe4\v\xfc\x00\x00\u07d4\x02f\xab\x1ck\x02\x16#\v\x93\x95D=_\xa7^hEh\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x02u\x1d\u018c\xb5\xbdsp'\xab\xf7\u0777s\x90\xcdw\xc1k\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x02w\x8e9\x0f\xa1u\x10\xa3B\x8a\xf2\x87\fBsT}8l\x8a\x03l<f\x17\f\rr\x00\x00\xe0\x94\x02\xad\xe5\xdb\"\xf8\xb7X\xee\x14Cbld\xec/2\xaa\n\x15\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x02\xaf$Y\xa9=\v?M\x06&6#l\u0532\x9e;\xce\u03c9g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x02\xb1\xafr3\x9b*\"V8\x9f\xd6F\a\xde$\xf0\xde`\n\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x02\xb6C\xd6\xfa\xbdCz\x85\x1a\u033ey\xab\xb7\xfd\xe1&\xdc\u03ca\x01\x86P\x12|\xc3\u0700\x00\x00\xe0\x94\x02\xb6\xd6\\\xb0\v{6\xe1\xfb^\xd3c,L\xb2\n\x89A0\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x02\xb7\xb1\u05b3L\xe0S\xa4\x0e\xb6\\\u0524\xf7\xdd\xdd\x0e\x9f0\x89%\"H\u07b6\xe6\x94\x00\x00\xe0\x94\x02\xc9\xf7\x94\n{\x8bzA\v\xf8=\xc9\xc2#3\xd4']\u04ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x02\u0523\th\xa3\x9e+4\x98\u00e6\xa4\xedE\xc1\xc6dh\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x02\xdf\xcb\x17\xa1\xb8tA\x03ct\xb7b\xa5\xd3A\x8b\x1c\xb4\u0509H\xb0+\xa9\u047aF\x00\x00\u07d4\x02\xe4\xcb\"\xbeF%\x8a@\xe1mC8\xd8\x02\xff\xfd\x00\xc1Q\x89\x14\x96\x96\xea\u03ba\x81\x00\x00\u07d4\x02\xe8\x16\xaf\xc1\xb5\xc0\xf3\x98R\x13\x19Y\xd9F\xeb;\a\xb5\xad\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x02\xf7\xf6r\t\xb1j\x17U\fiLrX8\x19\xc8\vT\xad\x89\x05U\x93\x06\xa7\x8ap\x00\x00\xe0\x94\x03\ts\x80{/Bi\x14\xad\x00\x18\x12p\xac\xd2{\x8f\xf6\x1f\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\x03\ty#\xba\x15^\x16\xd8/:\xd3\xf6\xb8\x15T\b\x84\xb9,\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x03\x0f\xb3@\x1fr\xbd4\x18\xb7\xd1\xdau\xbf\x8cQ\x9d\xd7\a\u0709\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x03\x1e%\xdbQk\x0f\t\x9f\xae\xbf\xd9O\x89\f\xf9f`\x83k\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x03(Q\f\t\xdb\u0345\x19J\x98\xd6|3\xacI\xf2\xf9M`\x8a\x02TO\xaaw\x80\x90\xe0\x00\x00\u07d4\x03)\x18\x8f\b\x06W\xab:*\xfaR$g\x17\x82y\x83 \x85\x89\v\xbfQ\r\xdf\xcb&\x00\x00\u07d4\x031x&\xd1\xf7\n\xa4\xbd\u07e0\x9b\xe0\xc4\x10UR\xd25\x8b\x89\x02\x1auJm\xc5(\x00\x00\u07d4\x033p\x12\xae\x1d\u007f\xf3\xee\u007fi|@>w\x80\x18\x8b\xf0\xef\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x037|\x0eUkd\x01\x03(\x9aa\x89\u1baecI4g\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03IcM\u00a9\xe8\f?w!\xee+PF\xae\xaa\xed\xfb\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x03U\xbc\xac\xbd!D\x1e\x95\xad\xee\xdc0\xc1r\x18\u0224\b\u0389\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x03n\xef\xf5\xba\x90\xa6\x87\x9a\x14\xdf\xf4\xc5\x04;\x18\xca\x04`\u0249\x05k\xc7^-c\x10\x00\x00\xe0\x94\x03qKA\u04a6\xf7Q\x00\x8e\xf8\xddM+)\xae\u02b8\xf3n\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x03r\xe8RX.\t44J\x0f\xed!x0M\xf2]F(\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03r\xeeU\b\xbf\x81c\xed(N^\xef\x94\xceMsg\xe5\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x03}\xd0V\xe7\xfd\xbdd\x1d\xb5\xb6\xbe\xa2\xa8x\n\x83\xfa\u1009\a\x96\xe3\xea?\x8a\xb0\x00\x00\xe0\x94\x03\x83#\xb1\x84\xcf\xf7\xa8*\xe2\u1f67y?\xe41\x9c\xa0\xbf\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03\x87y\xca-\xbef>c\xdb?\xe7V\x83\xea\x0e\xc6.#\x83\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\x03\x8eE\xea\xdd=\x88\xb8\u007f\xe4\u06b0fh\x05\"\xf0\xdf\xc8\xf9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x03\x92T\x9ar\u007f\x81eT)\u02d2\x8bR\x9f%\xdfM\x13\x85\x89\x01lC\xa0\xee\xa0t\x00\x00\u07d4\x03\x94\xb9\x0f\xad\xb8`O\x86\xf4?\xc1\xe3]1$\xb3*Y\x89\x89)j\xa1@'\x8ep\x00\x00\u0794\x03\x9ezN\xbc(N,\xcdB\xb1\xbd\xd6\v\xd6Q\x1c\x0fw\x06\x88\xf0\x15\xf2W6B\x00\x00\u07d4\x03\x9e\xf1\xceR\xfeyc\xf1f\u0562u\u0131\x06\x9f\xe3\xa82\x89\x15\xaf9\u4ab2t\x00\x00\u07d4\x03\xa2l\xfcL\x181op\u055e\x9e\x1ay\xee>\x8b\x96/L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x03\xaab(\x81#m\xd0\xf4\x94\f$\xc3$\xff\x8b{~!\x86\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4\x03\xafz\xd9\xd5\"<\xf7\xc8\xc1? \xdfg\xeb\xe5\xff\u017bA\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x03\xb0\xf1|\xd4F\x9d\xdc\u03f7\xdai~\x82\xa9\x1a_\x9ewt\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x03\xb4\x1bQ\xf4\x1d\xf2\r\xd2y\xba\xe1\x8c\x12w_w\xadw\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x03\xbe[F)\xae\xfb\xbc\xab\x9d\xe2m9Wl\xb7\xf6\x91\xd7d\x89\n\xdf0\xbap\u0217\x00\x00\u07d4\x03\xc6G\xa9\xf9)\xb0x\x1f\xe9\xae\x01\u02a3\xe1\x83\xe8vw~\x89\x18*\xb7\xc2\f\xe5$\x00\x00\u07d4\x03\xc9\x1d\x92\x946\x03\xe7R >\x054\x0eV`\x13\xb9\x00E\x89+|\xc2\xe9\xc3\"\\\x00\x00\u07d4\x03\xcbLOE\x16\xc4\xffy\xa1\xb6$O\xbfW.\x1c\u007f\xeay\x89\x94\x89#z\u06daP\x00\x00\u07d4\x03\u02d8\u05ec\xd8\x17\u079d\x88m\"\xfa\xb3\xf1\xb5}\x92\xa6\b\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x03\u031d-!\xf8k\x84\xac\x8c\xea\xf9q\u06e7\x8a\x90\xe6%p\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\x03\xd1rO\xd0\x0eT\xaa\xbc\xd2\xde*\x91\xe8F+\x10I\xdd:\x89\x8f\x1d\\\x1c\xae7@\x00\x00\u07d4\x03\xde\xdf\xcd\v<.\x17\xc7\x05\xda$\x87\x90\uf626\xbdWQ\x89Hz\x9a0E9D\x00\x00\u07d4\x03\u8c04SuW\xe7\t\xea\xe2\xe1\u1966\xbc\xe1\xef\x83\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x03\xeam&\u0400\xe5z\xee9&\xb1\x8e\x8e\xd7:N[(&\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x03\xeb<\xb8`\xf6\x02\x8d\xa5T\xd3D\xa2\xbbZP\n\xe8\xb8o\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x03\xeb\xc6?\xdaf`\xa4e\x04^#_\xben\\\xf1\x95s_\x89\a\xb0l\xe8\u007f\xddh\x00\x00\xe0\x94\x03\xefj\xd2\x0f\xf7\xbdO\x00+\xacX\xd4uD\u03c7\x9a\xe7(\x8a\x01u\xc7X\u0439n\\\x00\x00\u07d4\x03\xf7\xb9 \b\x81:\xe0\xa6v\xeb!(\x14\xaf\xab5\"\x10i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\x11p\xf5\x81\u0780\xe5\x8b*\x04\\\x8f|\x14\x93\xb0\x01\xb7\u02c90<t\xa1\xa36\x94\x00\x00\u07d4\x04\x13\xd0\xcfx\xc0\x01\x89\x8a7\x8b\x91\x8c\xd6\xe4\x98\xeaw<M\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\x04$\x1bA\xec\xbd\v\xfd\xf1)^\x9dO\xa5\x9e\xa0\x9ela\x86\x89e_v\x94P\xbcx\x00\x00\u07d4\x047\a\a\x1e*\xe2\x1e\xed\x97x\x91\xdcy\xcd]\x8e\xe1\xc2\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\x04N\x851D\xe36D\x95\u799f\xa1\xd4j\xbe\xa3\xac\td\x89\x02\xab\"T\xb1\u071a\x80\x00\u07d4\x04U\xdc\xec\x8a\u007f\xc4F\x1b\xfd\u007f7Eo\xce?L<\xaa\u01c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x04^\xd7\xf6\xd9\xee\x9f%.\a2h\xdb\x02,c&\xad\xfc[\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x04cw\xf8d\xb0\x14?(!t\xa8\x92\xa7=>\xc8\xeca2\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\x04i\xe8\xc4@E\v\x0eQ&&\xfe\x81~gT\xa8\x15(0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04m'K\x1a\xf6\x15\xfbPZvJ\xd8\u0767p\xb1\xdb/=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x04}Z&\u05ed\x8f\x8ep`\x0fp\xa3\x98\u076a\x1c-\xb2o\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x04~\x87\xc8\xf7\xd1\xfc\xe3\xb0\x13S\xa8Xb\xa9H\xac\x04\x9f>\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x04\u007f\x9b\xf1R\x9d\xaf\x87\xd4\a\x17^o\x17\x1b^Y\xe9\xff>\x89#<\x8f\xe4'\x03\xe8\x00\x00\xe0\x94\x04\x85'2\xb4\xc6R\xf6\xc2\u53b3e\x87\xe6\nb\xda\x14\u06ca\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x04\x8a\x89p\xeaAE\xc6MU\x17\xb8\xde[F\xd0YZ\xad\x06\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x04\x9c]K\xc6\xf2]NEli{R\xa0x\x11\xcc\u045f\xb1\x89\x10D\x00\xa2G\x0eh\x00\x00\u07d4\x04\xa1\xca\xda\x1c\xc7Q\b/\xf8\u0692\x8e<\xfa\x00\b \xa9\xe9\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x04\xa8\n\xfa\xd5>\xf1\xf8Ae\xcf\xd8R\xb0\xfd\xf1\xb1\xc2K\xa8\x89\x03$\xe9d\xb3\xec\xa8\x00\x00\u07d4\x04\xaa\xfc\x8a\xe5\xceoI\x03\u021d\u007f\xac\x9c\xb1\x95\x12\"Gw\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x04\xbaK\xb8q@\x02,!Jo\xacB\xdbZ\x16\u0755@E\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x04\xba\x8a?\x03\xf0\x8b\x89P\x95\x99M\xdaa\x9e\u06ac\xee>z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\xc2\xc6K\xb5L>\xcc\xd0U\x85\xe1\x0e\xc6\xf9\x9a\f\xdb\x01\xa3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x04\xceE\xf6\x00\xdb\x18\xa9\u0405\x1b)\xd99>\xbd\xaa\xfe=\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x04\u05b8\xd4\u0686t\a\xbb\x99wI\u07bb\xcd\xc0\xb3XS\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x04\xd78\x96\xcfe\x93\xa6\x91\x97*\x13\xa6\xe4\x87\x1f\xf2\xc4+\x13\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\xd8*\xf9\xe0\x1a\x93m\x97\xf8\xf8Y@\xb9p\xf9\xd4\u06d96\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x04\xe5\xf5\xbc|\x92?\xd1\xe3\x175\xe7.\xf9h\xfdg\x11\fn\x89WU\x1d\xbc\x8ebL\x00\x00\u07d4\x04\xec\xa5\x01c\n\xbc\xe3R\x18\xb1t\x95k\x89\x1b\xa2^\xfb#\x8966\x9e\xd7t}&\x00\x00\u07d4\x05\x05\xa0\x8e\"\xa1\t\x01Z\"\xf6\x850STf*U1\u0549\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\x05\x14\x95L</\xb6W\xf9\xa0oQ\x0e\xa2'H\xf0'\xcd\u04c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x05\x163\b\r\a\xa5W\xad\xde1\x92a\xb0t\x99\u007f\x14i-\x8a\x01:k+VHq\xa0\x00\x00\u07d4\x05\x17D\x8d\xad\xa7a\xcc[\xa4\x03>\xe8\x81\xc807\x03d\x00\x89lO\xd1\xee$nx\x00\x00\u07d4\x05\x1dBBv\xb2\x129fQ\x86\x13=e;\xb8\xb1\x86/\x89\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x05!\xbc:\x9f\x87\x11\xfe\xcb\x10\xf5\a\x97\xd7\x10\x83\xe3A\ub749\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x05#mL\x90\xd0e\xf9\u34c3X\xaa\xff\xd7w\xb8j\xecI\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x05*X\xe05\xf1\xfe\x9c\xdd\x16\x9b\xcf \x97\x03E\xd1+\x9cQ\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x05.\xab\x1fa\xb6\xd4U\x17(?A\xd1D\x18$\x87\x87I\u0409\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x053n\x9ar'(\xd9c\xe7\xa1\xcf'Y\xfd\x02tS\x0f\u02891\xa2D?\x88\x8ay\x80\x00\u07d4\x054q\u035aA\x92[9\x04\xa5\xa8\xff\xca6Y\xe04\xbe#\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4\x056\x1d\x8e\xb6\x94\x1dN\x90\xfb~\x14\x18\xa9Z2\xd5%w2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x05B:T\xc8\xd0\xf9p~pAs\xd9#\xb9F\xed\xc8\xe7\x00\x89\x06\xea\x03\u00bf\x8b\xa5\x80\x00\u07d4\x05D\f[\a;R\x9bH) \x9d\xff\x88\t\x0e\a\xc4\xf6\xf5\x89E\u04977\xe2/ \x00\x00\u07d4\x05Z\xb6X\xc6\xf0\xedO\x87^\xd6t.K\xc7)-\x1a\xbb\xf0\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\u07d4\x05[\xd0,\xaf\x19\xd6 +\xbc\u0703m\x18{\xd1\xc0\x1c\xf2a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x05^\xacO\x1a\xd3\xf5\x8f\v\xd0$\u058e\xa6\r\xbe\x01\u01af\xb3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x05fQU\xccI\xcb\xf6\xaa\xbd\u056e\x92\xcb\xfa\xad\x82\xb8\xc0\xc1\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x05f\x86\a\x8f\xb6\xbc\xf9\xba\n\x8a\x8d\xc6:\x90o_\xea\xc0\xea\x89\x1b\x18\x1eK\xf24<\x00\x00\u07d4\x05iks\x91k\xd3\x03>\x05R\x1e2\x11\xdf\xec\x02n\x98\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x05k\x15F\x89O\x9a\x85\xe2\x03\xfb3m\xb5i\xb1l%\xe0O\x89\t.\xdb\t\xff\b\u0600\x00\u07d4\x05yI\xe1\xca\x05pF\x9eL\xe3\u0190\xaea:k\x01\xc5Y\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x05}\u049f-\x19\xaa=\xa4#'\xeaP\xbc\xe8o\xf5\xc9\x11\u0649\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x05\u007f\u007f\x81\xcdz@o\xc4Y\x94@\x8bPI\x91,Vdc\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x05\x91]N\"Zf\x81b\xae\xe7\xd6\xc2_\xcf\xc6\xed\x18\xdb\x03\x89\x03\x98\xc3ry%\x9e\x00\x00\u07d4\x05\x96\xa2}\xc3\xee\x11_\xce/\x94\xb4\x81\xbc z\x9e&\x15%\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x05\xa80rC\x02\xbc\x0fn\xbd\xaa\x1e\xbe\xee\xb4nl\xe0\v9\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\x05\xae\u007f\u053b\u0300\xca\x11\xa9\n\x1e\u01e3\x01\xf7\xcc\u0303\u06c91T\xc9r\x9d\x05x\x00\x00\u07d4\x05\xbbd\xa9\x16\xbef\xf4`\xf5\xe3\xb6C2\x11\r \x9e\x19\xae\x89\u3bb5sr@\xa0\x00\x00\xe0\x94\x05\xbfO\xcf\xe7r\xe4[\x82dC\x85.l5\x13P\xcer\xa2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\x05\xc6@\x04\xa9\xa8&\xe9N^N\xe2g\xfa*v2\xddNo\x8a\x03m\xc4.\xbf\xf9\v\u007f\x80\x00\xe0\x94\x05\xc76\xd3e\xaa7\xb5\xc0\xbe\x9c\x12\u022d\\\xd9\x03\xc3,\xf9\x8a\x01E^{\x80\n\x86\x88\x00\x00\xe0\x94\x05\xcbl;\x00r\xd3\x11ga\xb52\xb2\x18D;S\xe8\xf6\u014a\x1e\x02\xc3\xd7\xfc\xa9\xb6(\x00\x00\u07d4\x05\xd0\xf4\xd7(\xeb\xe8.\x84\xbfYu\x15\xadA\xb6\v\xf2\x8b9\x89\u3bb5sr@\xa0\x00\x00\u07d4\x05\u058d\xada\u04fb\u07f3\xf7y&\\IGJ\xff?\xcd0\x89\x02\"\xc5]\xc1Q\x9d\x80\x00\u07d4\x05\xe6q\xdeU\xaf\xec\x96K\aM\xe5t\xd5\x15\x8d]!\xb0\xa3\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x05\xe9{\tI,\u058fc\xb1+\x89.\xd1\xd1\x1d\x15,\x0e\u02897\b\xba\xed=h\x90\x00\x00\u07d4\x05\xf3c\x1fVd\xbd\xad]\x012\xc88\x8d6\xd7\u0612\t\x18\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x06\t\xd8:l\xe1\xff\u0276\x90\xf3\xe9\xa8\x1e\x98>\x8b\xdcM\x9d\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\u07d4\x06\x1e\xa4\x87|\u0409D\xebd\u0096n\x9d\xb8\xde\xdc\xfe\xc0k\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x06%\xd0`V\x96\x8b\x00\"\x06\xff\x91\x98\x01@$+\xfa\xa4\x99\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x06(\xbf\xbeU5x/\xb5\x88@k\xc9f`\xa4\x9b\x01\x1a\xf5\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\x061\u044b\xbb\xbd0\xd9\xe1s+\xf3n\xda\xe2\u0389\x01\xab\x80\x89\xa3\xf9\x88U\xec9\x90\x00\x00\u07d4\x061\xdc@\xd7NP\x95\xe3r\x9e\xdd\xf4\x95D\xec\xd49og\x89\b\xacr0H\x9e\x80\x00\x00\xe0\x94\x067Y\xdd\x1cN6.\xb1\x93\x98\x95\x1f\xf9\xf8\xfa\xd1\xd3\x10h\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x06_\xf5u\xfd\x9c\x16\xd3\xcbo\u058f\xfc\x8fH?\xc3.\xc85\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x06a\x8e\x9dWb\xdfb\x02\x86\x01\xa8\x1dD\x87\u05a0\xec\xb8\x0e\x89Hz\x9a0E9D\x00\x00\xe0\x94\x06fG\xcf\xc8]#\xd3v\x05W= \x8c\xa1T\xb2D\xd7l\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x06xeJ\xc6v\x1d\xb9\x04\xa2\xf7\xe8Y^\xc1\xea\xacsC\b\x89/\x98\xb2\x9c(\x18\xf8\x00\x00\u07d4\x06\x86\n\x93RYU\xffbI@\xfa\xdc\xff\xb8\xe1I\xfdY\x9c\x89lh\xcc\u041b\x02,\x00\x00\xe0\x94\x06\x8c\xe8\xbdn\x90*E\u02c3\xb5\x15A\xb4\x0f9\xc4F\x97\x12\x8a\x01\x1c\x0f\x9b\xadJF\xe0\x00\x00\u07d4\x06\x8e)\xb3\xf1\x91\xc8\x12\xa699\x18\xf7\x1a\xb93\xaehG\xf2\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x06\x8eeWf\xb9D\xfb&6\x19e\x87@\xb8P\xc9J\xfa1\x89\x01\xe8\u007f\x85\x80\x9d\xc0\x00\x00\u0794\x06\x96N-\x17\xe9\x18\x9f\x88\xa8 96\xb4\n\xc9nS<\x06\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x06\x99L\xd8:\xa2d\n\x97\xb2`\vA3\x9d\x1e\r>\xdel\x89\r\x8drkqw\xa8\x00\x00\u07d4\x06\x9e\u042bz\xa7}\xe5q\xf1a\x06\x05\x1d\x92\xaf\xe1\x95\xf2\u0409\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x06\xac&\xad\x92\u02c5\x9b\u0550]\xdc\xe4&j\xa0\xecP\xa9\u0149*\x03I\x19\u07ff\xbc\x00\x00\u07d4\x06\xb0\xc1\xe3\u007fZ^\u013b\xf5\b@T\x8f\x9d:\xc0(\x88\x97\x89\xd8\u0602\u148e}\x00\x00\u07d4\x06\xb0\xff\x83@s\xcc\xe1\xcb\xc9\xeaU~\xa8{`Yc\u8d09\x10CV\x1a\x88)0\x00\x00\xe0\x94\x06\xb1\x06d\x9a\xa8\xc4!\xdd\xcd\x1b\x8c2\xcd\x04\x18\xcf0\xda\x1f\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x06\xb5\xed\xe6\xfd\xf1\xd6\xe9\xa3G!7\x9a\xea\xa1|q=\xd8*\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x06\xcb\xfa\b\xcd\xd4\xfb\xa77\xba\xc4\a\xbe\x82$\xf4\xee\xf3X(\x89 +\xe5\xe88.\x8b\x80\x00\u07d4\x06\xd6\xcb0\x84\x81\xc36\xa6\xe1\xa2%\xa9\x12\xf6\xe65Y@\xa1\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x06\xdc\u007f\x18\xce\xe7\xed\xab[yS7\xb1\xdfj\x9e\x8b\u062eY\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x06\xf6\x8d\xe3\xd79\xdbA\x12\x1e\xac\xf7y\xaa\xda=\xe8v!\a\x89\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\x06\xf7\u070d\x1b\x94b\xce\xf6\xfe\xb13h\xa7\xe3\x97K\t\u007f\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\a\x01\xf9\xf1G\xecHhV\xf5\xe1\xb7\x1d\xe9\xf1\x17\xe9\x9e!\x05\x89\te\xdaq\u007f\u0578\x00\x00\u07d4\a\r]6L\xb7\xbb\xf8\"\xfc,\xa9\x1a5\xbd\xd4A\xb2\x15\u0549lk\x93[\x8b\xbd@\x00\x00\xe0\x94\a\x1d\xd9\r\x14\xd4\x1fO\xf7\xc4\x13\xc2B8\xd35\x9c\xd6\x1a\a\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\u07d4\a&\xc4.\x00\xf4T\x04\x83n\xb1\xe2\x80\xd0s\xe7\x05\x96\x87\xf5\x89X\x00>?\xb9G\xa3\x80\x00\xe0\x94\a'\xbe\n*\x00! H\xb5R\x0f\xbe\xfb\x95>\xbc\x9dT\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\a)\xa8\xa4\xa5\xba#\xf5y\xd0\x02[\x1a\xd0\xf8\xa0\xd3\\\xdf\u048a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4\a)\xb4\xb4|\t\xeb\x16\x15\x84d\u022a\u007f\xd9i\vC\x889\x89lh\xcc\u041b\x02,\x00\x00\u0794\a4\xa0\xa8\x1c\x95b\xf4\xd9\xe9\xe1\n\x85\x03\xda\x15\xdbF\xd7n\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\a<g\xe0\x9b\\q<R!\u0220\xc7\xf3\xf7Df\xc3G\xb0\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\a?\x1e\xd1\xc9\xc3\xe9\xc5*\x9b\x02I\xa5\xc1\u02a0W\x1f\xdf\x05\x89\x03\xd0\xff\v\x01;\x80\x00\x00\u07d4\aHq1E\xef\x83\xc3\xf0\xefM1\xd8#xo~\x9c\u0189\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\u07d4\a]\x15\xe2\xd3=\x8bO\xa7\u06e8\xb9\xe6\a\xf0J&\x1e4\v\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\aea\xa8VE]~\xf8nc\xf8|s\u06f6(\xa5_E\x890\xca\x02O\x98{\x90\x00\x00\u07d4\an\xe9\x9d5Hb:\x03\xb5\xf9\x98Y\xd2\u05c5\xa1w\x8dH\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\ap\xb4=\xba\xe4\xb1\xf3Z\x92{O\xa8\x12M8f\xca\xf9{\x897\x19>\xa7\xef[G\x00\x00\xe0\x94\ap\xc6\x1b\xe7\x87r#\f\xb5\xa3\xbb$)\xa7&\x14\xa0\xb36\x8a\x01n\u0899\xb7\x13A\x80\x00\u07d4\ar><0\xe8\xb71\xeeEj)\x1e\xe0\u7630 Jw\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\as\xee\xac\xc0P\xf7G \xb4\xa1\xbdW\x89[\x1c\xce\xebI]\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\a\x80\r/\x80h\xe4H\u01daOi\xb1\xf1^\xf6\x82\xaa\xe5\xf6\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\a\xa8\xda\xde\xc1BW\x1a}S\xa4)pQxm\a,\xbaU\x89\x01;m\xa1\x13\x9b\u0680\x00\u07d4\a\xaf\x93\x8c\x127\xa2|\x900\tM\xcf$\aP$n=,\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\a\xb1\xa3\x06\xcbC\x12\xdffH,,\xaer\xd1\xe0a@\x0f\u034a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\a\xb7\xa5p3\xf8\xf1\x130\xe4f^\x18]#N\x83\xec\x14\v\x89\xea~\xe9*\f\x9a\v\x80\x00\u07d4\a\xbc,\xc8\xee\xdc\x01\x97\a\x00\xef\xc9\xc4\xfb6s^\x98\xcdq\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\a\xd4\x12\x17\xba\u0725\xe0\xe6\x03'\xd8E\xa3FO\x0f'\xf8J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\a\xd43N\u00c5\xe8\xaaT\xee\xda\xea\xdb0\x02/\f\u07e4\xab\x89\x8e\x91\xd5 \xf2\xeby\x00\x00\u07d4\a\xda\xe6\"c\r\x1168\x193\u04adk\"\xb89\xd8!\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\a\xdc+\xf8;\u01af\x19\xa8B\xff\xeaf\x1a\xf5\xb4\x1bg\xfd\xa1\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\a\u070c\x8b\x92z\xdb\xed\xfa\x8f]c\x9bCR5\x1f/6\u0489\x11\n\xed;U0\xdb\x00\x00\u07d4\a\xdd\xd0B,\x86\xefe\xbf\f\u007f\xc3E(b\xb1\"\x8b\b\xb8\x89o\xf5\u04aa\x8f\x9f\xcf\x00\x00\u07d4\a\xe1\x16,\xea\xe3\xcf!\xa3\xf6-\x10Y\x900.0\u007fN;\x89R\xf1\x03\xed\xb6k\xa8\x00\x00\u07d4\a\xe2\xb4\xcd\xee\xd9\u0407\xb1.Um\x9ew\f\x13\xc0\x99a_\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\a\xfe\xefT\xc16\x85\b)\xba\xdcKI\xc3\xf2\xa7<\x89\xfb\x9e\x89\x06hZ\xc1\xbf\xe3,\x00\x00\u07d4\b\x05FP\x8a=&\x82\u0239\x88O\x13c{\x88G\xb4M\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\t\bv\xba\xad\xfe\xe6\\=6;\xa5S\x12t\x8c\xfa\x87=\x89\\*\x997\x1c\xff\xe1\x00\x00\u07d4\b\x16o\x021?\xea\u12f0D\xe7\x87|\x80\x8bU\xb5\xbfX\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\b)\xd0\xf7\xbb|Dl\xfb\xb0\u07ad\xb29M\x9d\xb7$\x9a\x87\x89\x02,\xa3X|\xf4\xeb\x00\x00\u07d4\b0m\xe5\x19\x81\u7b21\x85hY\xb7\xc7xijki\xf9\x89\xadx\xeb\u016cb\x00\x00\x00\xe0\x94\b7S\x9b_jR*H,\xdc\u04e9\xbbpC\xaf9\xbd\u048a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\b8\xa7v\x8d\x9c*\u028b\xa2y\xad\xfe\xe4\xb1\xf4\x91\xe3&\xf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\bA\x16R\xc8qq6\t\xaf\x00b\xa8\xa1(\x1b\xf1\xbb\xcf\u0649K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\bM\x102Tu\x9b4<\xb2\xb9\xc2\xd8\xff\x9e\x1a\xc5\xf1E\x96\x8a\x01\x9b\xff/\xf5yh\xc0\x00\x00\u07d4\bPO\x05d?\xabY\x19\xf5\xee\xa5Y%\u05e3\xed}\x80z\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\b[J\xb7]\x83b\xd9\x14C\\\xed\xee\x1d\xaa+\x1e\xe1\xa2;\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\b[\xa6_\xeb\xe2>\xef\xc2\xc8\x02fj\xb1&#\x82\xcf\u0114\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\bt\x98\xc0FFh\xf3\x11P\xf4\xd3\u013c\u0765\"\x1b\xa1\x02\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\bw\uebabx\xd5\xc0\x0e\x83\xc3+-\x98\xfay\xadQH/\x89\x17\xd2-q\xdab&\x00\x00\u0794\b\x93j7\u07c5\xb3\xa1X\xca\xfd\x9d\xe0!\xf5\x817h\x13G\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\b\xa9\xa4N\x1fA\xde=\xbb\xa7\xa3c\xa3\xabA,\x12L\xd1^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\b\xb7\xbd\u03d4MUp\x83\x8b\xe7\x04`$:\x86\x94HXX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xb8E6\xb7L\x8c\x01T=\xa8\x8b\x84\u05cb\xb9WG\xd8\"\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\b\xc2\xf26\xacJ\xdc\xd3\xfd\xa9\xfb\xc6\xe4S\"S\xf9\xda;\xec\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\b\xc8\x02\xf8wX4\x9f\xa0>k\xc2\xe2\xfd\a\x91\x19~\ua689lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xc9\U0007fd89\xfd\xf8\x04\xd7i\xf8!#6\x02\x15\xaf\xf9;\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\b\xca\u0215&A\xd8\xfcRn\xc1\xabO-\xf8&\xa5\xe7q\x0f\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\b\xcc\xdaP\xe4\xb2j\x0f\xfc\x0e\xf9.\x92\x051\a\x06\xbe\xc2\u01ca\x01Iul8W\xc6\x00\x00\x00\u07d4\b\u0406M\xc3/\x9a\xcb6\xbfN\xa4G\xe8\xddg&\x90j\x15\x89lnY\xe6|xT\x00\x00\u07d4\b\xd4&\u007f\xeb\x15\u0697\x00\xf7\xcc\xc3\xc8J\x89\x18\xbf\x17\xcf\u0789a\t=|,m8\x00\x00\xe0\x94\b\xd41\x1c\x9c\x1b\xba\xf8\u007f\xab\xe1\xa1\xd0\x14c\x82\x8d]\x98\u038a\x13\x0e\xe8\xe7\x17\x90D@\x00\x00\u07d4\b\xd5N\x83\xadHj\x93L\xfa\xea\u20e3>\xfd\"|\x0e\x99\x898S\x05\x83$^\xdc\x00\x00\u07d4\b\xd9~\xad\xfc\xb7\xb0d\xe1\xcc\xd9\u0217\x9f\xbe\xe5\xe7z\x97\x19\x89\x0el]\xa8\xd6z\xc1\x80\x00\u07d4\b\xda:z\x0fE!a\u03fc\xec1\x1b\xb6\x8e\xbf\xde\xe1~\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xe3\x8e\xe0\xceH\xc9\xcad\\\x10\x19\xf7;SUX\x1cV\xe6\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\b\xef?\xa4\xc4<\xcd\xc5{\"\xa4\xb9\xb23\x1a\x82\xe58\x18\xf2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\t\td\x8c\x18\xa3\xce[\xaez\x04~\xc2\xf8h\xd2L\u0768\x1d\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\t\f\xd6{`\xe8\x1dT\xe7\xb5\xf6\a\x8f>\x02\x1b\xa6[\x9a\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\f\xeb\xef),>\xb0\x81\xa0_\u062a\xf7\u04db\xf0{\x89\u0509\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\t\x0f\xa96{\xdaW\xd0\xd3%:\n\x8f\xf7l\xe0\xb8\xe1\x9as\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\x14n\xa3\x88Qv\xf0w\x82\xe1\xfe0\xdc\xe3\xce$\u011e\x1f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\t!`_\x99\x16N;\xcc(\xf3\x1c\xae\xcex\x971\x82V\x1d\x89+\ai*\x90e\xa8\x00\x00\xe0\x94\t&\x1f\x9a\xcbE\x1c7\x88\x84O\f\x14Q\xa3[\xadP\x98\xe3\x8a\x01\u056d'P) `\x00\x00\xe0\x94\t'\"\x04\x92\x19K.\u069f\u013b\xe3\x8f%\u0581\xdf\xd3l\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\t*\xcbbK\b\xc0U\x10\x18\x9b\xbb\xe2\x1ee$\xd6D\u032d\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\t.\x81UX@-g\xf9\rk\xfem\xa0\xb2\xff\xfa\x91EZ\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\tP0\xe4\xb8&\x92\xdc\xf8\xb8\u0411$\x94\xb9\xb3x\xec\x93(\x89H\xa4<T`/p\x00\x00\u07d4\tRp\xccB\x14\x1d\u0658\xad(b\xdb\xd1\xfe\x9bD\xe7\xe6P\x89A\rXj \xa4\xc0\x00\x00\u07d4\tTW\xf8\xef\x8e+\xdc6!\x96\xb9\xa9\x12]\xa0\x9cg\u3ac9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\tT\xa8\xcb]2\x1f\xc35\x1au#\xa6\x17\xd0\xf5\x8d\xa6v\xa7\x89\x87\u067cz\xa4\x98\xe8\x00\x00\xe0\x94\t[\x0e\xa2\xb2\x18\xd8.\n\xea|(\x89#\x8a9\u027f\x90w\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\t[\x94\x9d\xe33:7}P\x19\u0613uJ^FV\xff\x97\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94\t^\x01t\x82\x9f4\xc3x\x1b\xe1\xa5\xe3\x8d\x15A\xeaC\x9b\u007f\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\t_ZQ\xd0oc@\xd8\vm)\xea.\x88\x11\x8a\xd70\xfe\x89lnY\xe6|xT\x00\x00\u07d4\th\xeeZ7\x8f\x8c\xad\xb3\xba\xfd\xbe\xd1\u045a\xaa\u03d3g\x11\x8965\u026d\xc5\u07a0\x00\x00\u07d4\tw\xbf\xba\x03\x8aD\xfbI\xb09p\xd8\xd8\xcf,\xb6\x1f\x8b%\x89\x16\u012b\xbe\xbe\xa0\x10\x00\x00\u07d4\t}\xa1,\xfc\x1f|\x1a$d\xde\xf0\x8c)\xbe\xd5\xe2\xf8Q\xe9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\t~\u0362%g\xc2\xd9\x1c\xb0?\x8cR\x15\xc2.\x9d\u0369I\x89\x01\x16Q\xac>zu\x80\x00\u07d4\t\x89\xc2\x00D\v\x87\x89\x91\xb6\x9d`\x95\xdf\xe6\x9e3\xa2.p\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\t\x90\xe8\x1c\u05c5Y\x9e\xa26\xbd\x19f\xcfRc\x02\xc3[\x9c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\x98\xd8'1\x15\xb5j\xf4<P^\bz\xff\x06v\xed6Y\x89\xd8\xd6\xed\xdf-.\x18\x00\x00\u07d4\t\xa0%1o\x96\u007f\xa8\xb9\xa1\xd6\a\x00\x06?Zh\x00\x1c\xaa\x89\x02\x12!\xa9\x9b\x93\xec\x00\x00\u0794\t\xa9(\xd5(\xec\x1b>%\xff\xc8>!\x8c\x1e\n\xfe\x89(\u01c8\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\t\xaeI\xe3\u007f\x12\x1d\xf5\xdc\x15\x8c\xfd\xe8\x06\xf1s\xa0k\f\u007f\x89\xd80\x9e&\xab\xa1\xd0\x00\x00\u07d4\t\xaf\xa7;\xc0G\xefF\xb9w\xfd\x97c\xf8r\x86\xa6\xbeh\u0189\x1b/\xb5\xe8\xf0jf\x00\x00\u07d4\t\xb4f\x86\x96\xf8j\b\x0f\x8b\xeb\xb9\x1d\xb8\xe6\xf8p\x15\x91Z\x89#\x8f\xf7\xb3O`\x01\x00\x00\xe0\x94\t\xb5\x9b\x86\x98\xa7\xfb\xd3\xd2\xf8\xc7:\x00\x89\x88\xde>@k+\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\t\xb7\xa9\x88\xd1?\xf8\x91\x86so\x03\xfd\xf4au\xb5=\x16\xe0\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\t\xc1w\xf1\xaeD$\x11\u076c\xf1\x87\xd4m\xb9V\x14\x83`\xe7\x8a\x01\xe5.3l\xde\"\x18\x00\x00\xe0\x94\t\u020f\x91~Mj\xd4s\xfa\x12\u93a3\xc4G*^\xd6\u068a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\t\u0438\xcd\a|i\xd9\xf3-\x9c\xcaC\xb3\xc2\b\xa2\x1e\u050b\x89\b!\xd2!\xb5)\x1f\x80\x00\xe0\x94\t\xd6\xce\xfdu\xb0\u0133\xf8\xf1\u0587\xa5\"\xc9a#\xf1\xf59\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\t\xe47\xd4H\x86\x12(\xa22\xb6.\xe8\xd3ye\xa9\x04\ud70a\x04\x98\xcf@\x1d\xf8\x84.\x80\x00\u07d4\t\xee\x12\xb1\xb4+\x05\xaf\x9c\xf2\a\xd5\xfc\xac%[.\xc4\x11\xf2\x89\x031\xcd\xddG\xe0\xfe\x80\x00\u07d4\t\xf3\xf6\x01\xf6\x05D\x11@Xl\xe0eo\xa2J\xa5\xb1\u066e\x89Sswo\xe8\xc4T\x00\x00\u07d4\t\xf9W[\xe5}\x00G\x93\u01e4\ub137\x15\x87\xf9|\xbbj\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n\x06P\x86\x1fx^\xd8\xe4\xbf\x10\x05\xc4P\xbb\xd0n\xb4\x8f\xb6\x89\xa6A;y\x14N~\x00\x00\u07d4\n\x06\xfa\xd7\xdc\u05e4\x92\xcb\xc0S\xee\xab\xdei4\xb3\x9d\x867\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\n\a}\xb1?\xfe\xb0\x94\x84\xc2\x17p\x9dX\x86\xb8\xbf\x9cZ\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\n\x0e\u0366cow\x16\xef\x19saF\x87\xfd\x89\xa8 \xa7\x06\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\n)\xa8\xa4\xd5\xfd\x95\x00u\xff\xb3Mw\xaf\xeb-\x82;\u0589\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n*\u0795\xb2\xe8\xc6m\x8a\xe6\xf0\xbad\xcaW\u05c3\xbemD\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\n+O\xc5\xd8\x1a\xceg\xdcK\xba\x03\xf7\xb4UA=F\xfe=\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\n-\xcbzg\x17\x01\u06f8\xf4\x95r\x80\x88&Xs5l\x8e\x89\b?\x16\xce\b\xa0l\x00\x00\u07d4\n=\xe1U\xd5\xec\xd8\xe8\x1c\x1f\xf9\xbb\xf07\x83\x01\xf8\xd4\xc6#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\nG\xad\x90Y\xa2I\xfc\x93k&b5=\xa6\x90_u\u00b9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\nH)ov1p\x8c\x95\u04b7Iu\xbcJ\xb8\x8a\xc19*\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\nJ\x01\x19\x95\u0181\xbc\x99\x9f\xddyuN\x9a2J\xe3\xb3y\x8a\b\xc1\x9a\xb0n\xb8\x9a\xf6\x00\x00\u07d4\nX\xfd\xddq\x89\x8d\xe7s\xa7O\xda\xe4^{\xd8N\xf46F\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\n[y\xd8\xf2;d\x83\xdb\u2f6ab\xb1\x06L\xc7cf\xae\x89j\u0202\x10\tR\u01c0\x00\u07d4\ne.*\x8bw\xbd\x97\xa7\x90\xd0\xe9\x13a\u0248\x90\u06f0N\x8965\u026d\xc5\u07a0\x00\x00\u07d4\nn\xber;n\xd1\xf9\xa8ji\xdd\xdah\xdcGF\\+\x1b\x89@=-\xb5\x99\xd5\xe4\x00\x00\u07d4\nw\xe7\xf7+C{WO\x00\x12\x8b!\xf2\xac&Q3R\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\n\x91\u007f;\\\xb0\xb8\x83\x04\u007f\u0676Y=\xbc\xd5W\xf4S\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\n\x93\x1bD\x9e\xa8\xf1,\xdb\xd5\xe2\xc8\xccv\xba\xd2\xc2|\x069\x89\x01?\x9e\x8cy\xfe\x05\x80\x00\u0794\n\x98\x04\x13x\x03\xbahh\xd9:U\xf9\x98_\xcdT\x04Q\u4239\x8b\xc8)\xa6\xf9\x00\x00\u07d4\n\x9a\xb2c\x8b\x1c\xfdeM%\u06b0\x18\xa0\xae\xbd\u07c5\xfdU\x89\x01.\x8c\xb5\xfeLJ\x80\x00\u07d4\n\xb3f\xe6\xe7\u056b\xbc\xe6\xb4JC\x8di\xa1\u02bb\x90\xd13\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\n\xb4(\x1e\xbb1\x85\x90\xab\xb8\x9a\x81\xdf\a\xfa:\xf9\x04%\x8a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\n\xb5\x9d9\a\x02\xc9\xc0Y\xdb\x14\x8e\xb4\xf3\xfc\xfa}\x04\xc7\xe7\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\n\xbf\xb3\x9b\x11HmyW(f\x19[\xa2lc\vg\x84\u06ca\x19\xba\x877\xf9i(\xf0\x00\x00\u07d4\n\u029aV&\x91;\b\xcf\u0266m@P\x8d\xceR\xb6\x0f\x87\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\n\xd3\xe4M<\x00\x1f\xa2\x90\xb3\x93ap0TA\b\xacn\xb9\x89j\xbd\xa0\xbc0\xb2\u07c0\x00\u07d4\n\xec.Bn\xd6\xcc\f\xf3\xc2I\xc1\x89~\xacG\xa7\xfa\xa9\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n\xf6_\x14xNU\xa6\xf9Vg\xfds%*\x1c\x94\a-*\x89\nv;\x8e\x02\xd4O\x80\x00\u07d4\n\xf6\xc8\xd59\xc9mP%\x9e\x1b\xa6q\x9e\x9c\x80`\xf3\x88\u008965\u026d\xc5\u07a0\x00\x00\u07d4\v\x069\x0f$7\xb2\x0e\u0123\xd3C\x1b2y\xc6X>^\u05c9\n\x84Jt$\xd9\xc8\x00\x00\u07d4\v\v8b\x11*\xee\u00e04\x92\xb1\xb0_D\x0e\xcaT%n\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\v\x0e\x05[(\xcb\xd0=\xc5\xffD\xaad\xf3\xdc\xe0O^c\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\x11\x9d\xf9\x9ck\x8d\xe5\x8a\x1e,?)zgD\xbfU\"w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\x14\x89\x19\x99\xa6\\\x9e\xf73\b\xef\xe3\x10\f\xa1\xb2\x0e\x81\x92\x89+^:\xf1k\x18\x80\x00\x00\u07d4\v!\x13PE4d*\x1d\xaf\x10.\xee\x10\xb9\xeb\xdev\xe2a\x89\x94,\xdd|\x95\xf2\xbd\x80\x00\xe0\x94\v(\x8aZ\x8bu\xf3\xdcA\x91\xeb\x04W\xe1\xc8=\xbd M%\x8a\x01\a\x14\xe7{\xb4:\xb4\x00\x00\u07d4\v6\x9e\x00.\x1bLy\x13\xfc\xf0\x0f-^\x19\u0141eG\x8f\x89\x03\u007fe\x16(\x8c4\x00\x00\u07d4\vC\xbd#\x91\x02U\x81\u0615l\xe4*\a%y\u02ff\xcb\x14\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u07d4\vP|\xf5SV\x8d\xaa\xf6U\x04\xaeN\xaa\x17\xa8\xea<\xdb\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v]f\xb1<\x87\xb3\x92\xe9M\x91\xd5\xf7l\rE\nU(C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v^ \x11\xeb\xc2Z\x00\u007f!6)`I\x8a\xfb\x8a\xf2\x80\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vd\x9d\xa3\xb9j\x10,\xdcm\xb6R\xa0\xc0}e\xb1\xe4C\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vi \xa6K6;\x8d]\x90\x80$\x94\xcfVKT|C\r\x89A\rXj \xa4\xc0\x00\x00\u07d4\vp\x11\x01\xa4\x10\x9f\x9c\xb3`\xdcW\xb7tBg=^Y\x83\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vq\xf5T\x12$i\uf5ce/\x1f\xef\xd7\u02f4\x10\x98'r\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\xe0\x94\v{\xb3B\xf0\x1b\u0248\x8ej\x9a\xf4\xa8\x87\xcb\xf4\xc2\xdd,\xaf\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\v}3\x93q\xe5\xbeg'\xe6\xe31\xb5\x82\x1f\xa2K\u06ddZ\x89.\u007f\x81\x86\x82b\x01\x00\x00\u07d4\v\u007f\xc9\xdd\xf7\x05v\xf63\x06i\xea\xaaq\xb6\xa81\xe9\x95(\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\v\x80\xfcp(,\xbd\xd5\xfd\xe3[\xf7\x89\x84\xdb;\xdb\x12\x01\x88\x8968\x02\x1c\xec\u06b0\x00\x00\u07d4\v\x92M\xf0\a\xe9\xc0\x87\x84\x17\xcf\xe6;\x97n\xa1\xa3\x82\xa8\x97\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\v\x93\xfc\xa4\xa4\xf0\x9c\xac \xdb`\xe0e\xed\xcc\xcc\x11\u0976\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\v\x9d\xf8\x0f\xbe# \t\xda\xcf\n\xa8\xca\u0153v\xe2Gb\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xa6\xe4j\xf2Z\x13\xf5qi%Z4\xa4\xda\xc7\xce\x12\xbe\x04\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\v\xa8p[\xf5\\\xf2\x19\xc0\x95k^?\xc0\x1cDt\xa6\xcd\xc1\x89\x05%\xe0Y]Mk\x80\x00\u07d4\v\xafn\u0379\x1a\xcb6\x06\xa85|\v\xc4\xf4\\\xfd-~o\x8965\u026d\xc5\u07a0\x00\x00\u07d4\v\xb0_r$\xbbX\x04\x85eV\xc0~\xea\xdb\ud1fa\x8f|\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\v\xb0\xc1&\x82\xa2\xf1\\\x9bWA\xb28\\\xbeA\xf04\x06\x8e\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\v\xb2\\\xa7\u0448\xe7\x1eMi={\x17\a\x17\xd6\xf8\xf0\xa7\n\x89\x12C\x02\xa8/\xad\xd7\x00\x00\u07d4\v\xb2e\x0e\xa0\x1a\xcau[\xc0\xc0\x17\xb6K\x1a\xb5\xa6m\x82\xe3\x89Hz\x9a0E9D\x00\x00\u07d4\v\xb5Lr\xfdf\x10\xbf\xa463\x97\xe0 8K\x02+\fI\x89Hz\x9a0E9D\x00\x00\u07d4\v\xb7\x16\n\xba)7b\xf8sO>\x03&\xff\u0264\xca\xc1\x90\x8965\u026d\xc5\u07a0\x00\x00\u07d4\v\xc9\\\xb3-\xbbWL\x83/\xa8\x17J\x815m8\xbc\x92\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xd6}\xbd\xe0z\x85n\xbd\x89;^\xdcO:[\xe4 &\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xdb\xc5L\u023d\xbb\xb4\x02\xa0\x89\x11\xe2#*T`\u0386k\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\v\xddX\xb9n|\x91m\xd2\xfb05o*\xeb\xfa\xaf\x1d\x860\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\v\u1f39\x03C\xfa\xe501s\xf4a\xbd\x91JH9\x05l\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\v\xe1\xfd\xf6&\xeea\x89\x10-p\xd1;1\x01,\x95\xcd\x1c\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xe2\xb9J\xd9P\xa2\xa6&@\xc3[\xfc\xcdlg\xda\xe4P\xf6\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\v\u681eC\a\xfeH\xd4\x12\xb8\u0461\xa8(M\xceHba\x8a\x04\x0f\xbf\xf8\\\x0180\x00\x00\u07d4\v\xef\xb5G\a\xf6\x1b,\x9f\xb0G\x15\xab\x02n\x1b\xb7 B\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\v\xf0dB\x8f\x83bg\"\xa7\xb5\xb2j\x9a\xb2\x04!\xa7r>\x89\a?u\u0460\x85\xba\x00\x00\u07d4\v\xfb\xb6\x92]\xc7^R\xcf&\x84\"K\xbe\x05P\xfe\xa6\x85\u04c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\f\b\x80\x06\xc6K0\xc4\u076f\xbc6\xcb_\x05F\x9e\xb6(4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f s\xbaD\xd3\u077d\xb69\xc0N\x19\x109\xa7\x17\x16#\u007f\x89M\x85<\x8f\x89\b\x98\x00\x00\xe0\x94\f\",|A\u0270H\xef\xcc\xe0\xa22CCb\xe1-g;\x8a\x02\x1e\x83Yivw8\x00\x00\xe0\x94\f(\b\xb9Q\ud787-{2y\x0f\xccY\x94\xaeA\xff\u070a\x15\x99n[<\u05b3\xc0\x00\x00\u07d4\f(\x84~O\t\xdf\xce_\x9b%\xaf|NS\x0fY\u0200\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f-\\\x92\x058\xe9S\u02af$\xf0s\u007fUL\u0192wB\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f0\xca\xcc?r&\x9f\x8bO\x04\xcf\a=+\x05\xa8=\x9a\u0449lyt\x12?d\xa4\x00\x00\u07d4\f29\xe2\xe8A$-\xb9\x89\xa6\x15\x18\xc2\"G\xe8\xc5R\b\x89\x0eJ\xf6G\x174d\x00\x00\xe0\x94\fH\r\xe9\xf7F\x10\x02\x90\x8bI\xf6\x0f\xc6\x1e+b\xd3\x14\v\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\fH\xaeb\xd1S\x97\x88\xeb\xa0\x13\xd7^\xa6\vd\xee\xbaN\x80\x89w\xfb\xdcC\xe00\x99\x80\x00\u07d4\fU\x89\xa7\xa8\x9b\x9a\xd1[\x02u\x190AYH\xa8u\xfb\xef\x89\x06\u0519\xeclc8\x00\x00\u07d4\fg\x03=\xd8\xee\u007f\f\x8a\xe54\xd4*Q\xf7\xd9\xd4\xf7\x97\x8f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\fhE\xbfA\xd5\xee'<>\u6d70\u059fo\xd5\xea\xbb\xf7\x89\xa2\xa1\xb9h.X\t\x00\x00\xe0\x94\f\u007f\x86\x9f\x8e\x90\xd5?\xdc\x03\u8c81\x9b\x01k\x9d\x18\xeb&\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\f\x86\x92\xee\xff*S\xd6\xd1h\x8e\xd5j\x9d\u06fdh\u06bb\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f\x8ff\xc6\x01{\xce[ 4r\x04\xb6\x02\xb7C\xba\u05cd`\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\f\x8f\xd7w^T\xa6\xd9\u0263\xbf\x89\x0ev\x1fewi?\xf0\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\f\x92Z\xd5\xeb5,\x8e\xf7m\f\"-\x11[\a\x91\xb9b\xa1\x89\xacc]\u007f\xa3N0\x00\x00\u07d4\f\x96~0a\xb8zu>\x84P~\xb6\t\x86x,\x8f0\x13\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\f\xa1*\xb0\xb9fl\xf0\xce\xc6g\x1a\x15)/&SGj\xb2\x8a,x'\xc4-\"\xd0|\x00\x00\u07d4\f\xa6p\xeb,\x8b\x96\u02e3y!\u007fY)\u00b8\x92\xf3\x9e\xf6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f\xae\x10\x8em\xb9\x9b\x9ecxv\xb0d\xc60>\u068ae\u0209\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\f\xbd\x92\x1d\xbe\x12\x15c\xb9\x8ahq\xfe\xcb\x14\xf1\xcc~\x88\u05c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\f\xbf\x87p\xf0\xd1\b.\\ \u016e\xad4\xe5\xfc\xa9\xaez\xe2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f\xc6\u007f\x82s\xe1\xba\xe0\x86\u007f\xd4.\x8b\x81\x93\xd7&y\xdb\xf8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\f\u05a1A\x91\x8d\x12k\x10m\x9f.\xbfi\xe1\x02\xdeM2w\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\f\xda\x12\xbfr\xd4a\xbb\xc4y\xeb\x92\xe6I\x1d\x05~kZ\u044a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\f\u0716\v\x99\x8c\x14\x19\x98\x16\r\xc1y\xb3l\x15\u0484p\xed\x89\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\f\xfb\x17#5\xb1l\x87\xd5\x19\xcd\x14uS\r W\u007f^\x0e\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\r\x1f*Wq>\xbcn\x94\xde)\x84n\x88D\xd3vfWc\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\r2e\xd3\u7f79=^\x8e\x8b\x1c\xa4\u007f!\ny>\u030e\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\r5@\x8f\"ef\x11o\xb8\xac\u06a9\xe2\xc9\u055bvh?\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\rU\x1e\xc1\xa2\x13<\x98\x1d_\u01a8\xc8\x17?\x9e|OG\xaf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r]\x98V\\d|\xa5\xf1w\xa2\xad\xb9\xd3\x02/\xac(\u007f!\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\re\x80\x14\xa1\x99\x06\x1c\xf6\xb3\x943\x14\x03\x03\xc2\x0f\xfdNZ\x8a\x01\xbc\x85\xdc*\x89\xbb \x00\x00\u07d4\rg\x87\x06\xd07\x18\u007f>\"\xe6\xf6\x9b\x99\xa5\x92\xd1\x1e\xbcY\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\ri\x10\f9\\\xe6\xc5\xea\xad\xf9]\x05\xd8r\x83~\xde\xdd!\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\rt~\u559b\xf7\x9dW8\x1do\xe3\xa2@l\xd0\xd8\xce'\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\r\x80#\x92\x9d\x91r4\xae@Q+\x1a\xab\xb5\xe8\xa4Q'q\x89\b\x05\xe9\x9f\xdc\xc5\xd0\x00\x00\xe0\x94\r\x8a\xab\x8ft\xea\x86,\xdfvh\x05\x00\x9d?>B\xd8\xd0\v\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\r\x8c@\xa7\x9e\x18\x99O\xf9\x9e\xc2Q\xee\x10\u0408\u00d1.\x80\x89\x066d\xfc\u04bb\xc4\x00\x00\u07d4\r\x8e\xd7\xd0\xd1V83\x0e\xd7\xe4\xea\u032b\x8aE\x8dus~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r\x92X/\u06e0^\xab\xc3\xe5\x158\xc5m\xb8\x817\x85\xb3(\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\r\x94C\xa7\x94h\xa5\xbb\xf7\xc1<n\"]\x1d\xe9\x1a\xee\a\u07c9\x03\xcbq\xf5\x1f\xc5X\x00\x00\xe0\x94\r\x9a\x82_\xf2\xbc\u04d7\u02ed[q\x1d\x9d\u0315\xf1\xcc\x11-\x8a\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\x00\u07d4\r\x9d?\x9b\u0124\xc6\xef\xbdYg\x9bi\x82k\xc1\xf6=\x99\x16\x89 \x86\xac5\x10R`\x00\x00\u07d4\r\xa52\xc9\x10\xe3\xac\r\xfb\x14\xdba\xcds\x9a\x935?\xd0_\x89Hx\xbe\x1f\xfa\xf9]\x00\x00\u07d4\r\xa7@\x12b8N.\x8bK&\xdd\x15G\x99\xb5QE\uf809\x10CV\x1a\x88)0\x00\x00\u07d4\r\xae>\xe5\xb9\x15\xb3d\x87\xf9\x16\x1f\x19\x84m\x10\x1431\x8a\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\r\xbdA|7+\x8b\r\x01\xbc\xd9Dpk\xd3.`\xae(\u0449\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\r\xc1\x00\xb1\a\x01\x1c\u007f\xc0\xa13\x96\x12\xa1l\xce\xc3(R\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r\u03dd\x8c\x98\x04E\x9fd|\x14\x13\x8e\xd5\x0f\xadV;AT\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4\r\xcf\xe87\xea\x1c\xf2\x8ce\xfc\xce\u00fe\xf1\xf8NY\xd1P\xc0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\r\xd4\xe6t\xbb\xad\xb1\xb0\u0702D\x98q=\xce;QV\xda)\x89\t79SM(h\x00\x00\u07d4\r\xfb\u0501pP\xd9\x1d\x9db\\\x02\x05<\xf6\x1a>\xe2\x85r\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x0e\x02N\u007f\x02\x9cj\xaf:\x8b\x91\x0f^\b\bs\xb8W\x95\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0e\tdl\x99\xafC\x8e\x99\xfa'L\xb2\xf9\xc8V\xcbe\xf76\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x0e\f\x9d\x00^\xa0\x16\u0095\xcdy\\\xc9!>\x87\xfe\xbc3\xeb\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\x0e\rf3\xdb\x1e\f\u007f#Jm\xf1c\xa1\x0e\n\xb3\x9c \x0f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x0e\x11\xd7z\x89w\xfa\xc3\r&\x84E\xe51\x14\x9b1T\x1a$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\x12=}\xa6\xd1\xe6\xfa\xc2\u072d\xd2p)$\v\xb3\x90R\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0e\x18\x01\xe7\vbb\x86\x1b\x114\u033c9\x1fV\x8a\xfc\x92\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0e \x94\xac\x16T\xa4k\xa1\xc4\u04e4\v\xb8\xc1}\xa7\U000d6209\x13h?\u007f<\x15\xd8\x00\x00\u07d4\x0e!\xaf\x1b\x8d\xbf'\xfc\xf6?7\xe0G\xb8z\x82\\\xbe|'\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x0e.PJ-\x11\"\xb5\xa9\xfe\xee\\\xb1E\x1b\xf4\u00ac\xe8{\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x0e/\x8e(\xa6\x81\xf7|X;\xd0\xec\xde\x16cK\xdd~\x00\u0349\x05'8\xf6Y\xbc\xa2\x00\x00\u07d4\x0e2\x02\x19\x83\x8e\x85\x9b/\x9f\x18\xb7.=@s\xcaP\xb3}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e3\xfc\xbb\xc0\x03Q\v\xe3W\x85\xb5*\x9c]!k\xc0\x05\xf4\x89e\xea=\xb7UF`\x00\x00\u07d4\x0e6\x96\xcf\x1fB\x17\xb1c\u047c\x12\xa5\xeas\x0f\x1c2\xa1J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0e9\x0fD\x05=\xdf\xce\xf0\xd6\b\xb3^M\x9c,\xbe\x98q\xbb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x0e:(\xc1\u07ef\xb0P[\xdc\xe1\x9f\xe0%\xf5\x06\xa6\xd0\x1c\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e=\xd7\xd4\xe4)\xfe90\xa6A@5\xf5+\xdcY\x9dxM\x89\x02,\xa3X|\xf4\xeb\x00\x00\u07d4\x0eGey\x03Rek\xc6Vh,$\xfc^\xf3\xe7j#\u01c9\x02\x86\xd7\xfc\f\xb4\xf5\x00\x00\u07d4\x0eI\x88\x00Dqw\xb8\u022f\xc3\xfd\xfa\u007fi\xf4\x05\x1b\xb6)\x89t\x05\xb6\x9b\x8d\xe5a\x00\x00\u07d4\x0ek\xaa\xa3\u07b9\x89\xf2\x89b\x00vf\x86\x18\xe9\xac3(e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x0el\xd6d\xad\x9c\x1e\xd6K\xf9\x87I\xf4\x06D\xb6&\xe3y,\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\xe0\x94\x0em\xfdU;.\x87=*\xec\x15\xbd_\xbb?\x84r\xd8\u04d4\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\x0en\xc3\x137bq\xdf\xf5T#\xabT\"\xcc:\x8b\x06\xb2+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x0en\u0399\x11\x1c\xad\x19a\xc7H\xed=\xf5\x1e\xddi\u04a3\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x0e\x83\xb8PH\x1a\xb4MI\xe0\xa2)\xa2\xe4d\x90,iS\x9b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0e\x89\xed\xdd?\xa0\xd7\x1d\x8a\xb0\xff\x8d\xa5X\x06\x86\xe3\xd4\xf7O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\x90\x96\xd3C\xc0`\xdbX\x1a\x12\x01\x12\xb2x`~\xc6\xe5+\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x0e\x9cQ\x18d\xa1w\xf4\x9b\xe7\x82\x02w?`H\x9f\xe0NR\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x0e\xa2\xa2\x101+>\x86~\xe0\xd1\xcch,\xe1\xd6f\xf1\x8e\u054a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0e\xb1\x89\xef,-Wb\xa9c\u05b7\xbd\xf9i\x8e\xa8\u7d0a\x89Hz\x9a0E9D\x00\x00\xe0\x94\x0e\xb5\xb6b\xa1\xc7\x18`\x8f\xd5/\f%\xf97\x880\x17\x85\x19\x8a\x01J7(\x1aa.t\x00\x00\xe0\x94\x0e\xc4f\x96\xff\xac\x1fX\x00_\xa8C\x98$\xf0\x8e\xed\x1d\xf8\x9b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x0e\xc5\n\xa8#\xf4e\xb9FK\v\xc0\u0125w$\xa5U\xf5\u058a\f\x83\xd1Bj\u01f1\xf0\x00\x00\u07d4\x0e\xc50\x8b1(.!\x8f\xc9\xe7Y\xd4\xfe\xc5\xdb7\b\xce\u01096C\xaady\x86\x04\x00\x00\u07d4\x0e\xcc\xf6\x17\x84O\xd6\x1f\xbab\xcb\x0eD[z\u018b\xcc\x1f\xbe\x89\x14\xfeO\xe65e\xc6\x00\x00\u07d4\x0e\u04fb:N\xb5T\xcf\u0297\x94}WU\a\xcd\xfdm!\u0609\x1d\xb3 _\xcc#\u0540\x00\u07d4\x0e\xd7l,;]P\xff\x8f\xb5\v>\xea\xcdh\x15\x90\xbe\x1c-\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0e\u0680\xf4\xed\aJ\xeaiz\xed\xdf(;c\xdb\xca=\xc4\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\xddKX\x0f\xf1\x0f\xe0lJ\x03\x11b9\xef\x96b+\xae5\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x0e\xe3\x91\xf0<v[\x11\u0590&\xfd\x1a\xb3S\x95\xdc8\x02\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x0e\xe4\x14\x94\x04\x87\xfd$\xe3\x907\x82\x85\xc5\u05f93M\x8be\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\x0e\xf5J\xc7&M\"T\xab\xbb_\x8bA\xad\u0787QW\xdb|\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x0e\xf8[I\u040au\x19\x86\x92\x91N\u0774\xb2,\xf5\xfaDP\x89l\xae0b\x1dG \x00\x00\u07d4\x0e\xfd\x17\x89\xeb\x12D\xa3\xde\xde\x0f]\xe5\x82\u0616<\xb1\xf3\x9f\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94\x0f\x04,\x9c/\xb1\x87f\xf86\xbbY\xf75\xf2}\xc3)\xfe<\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0f\x04\x9a\x8b\xdf\xd7a\u078e\xc0,\xee()\xc4\x00[#\xc0k\x89\r\xa93\xd8\xd8\xc6p\x00\x00\xe0\x94\x0f\x05\xf1 \u021e\x9f\xbc\x93\u052b\f^+J\r\xf0\x92\xb4$\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\x0f\x12{\xbf\x8e1\x1c\xae\xa2\xbaP*3\xfe\xce\xd3\xf70\xbaB\x89\n1\x06+\xee\xedp\x00\x00\u07d4\x0f\x1c$\x9c\xd9b\xb0\x0f\xd1\x14\xa94\x9fjl\xc7x\xd7lM\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f n\x1a\x1d\xa7 ~\xa5\x18\xb1\x12A\x8b\xaa\x8b\x06&\x03(\x89 \x86\xac5\x10R`\x00\x00\u07d4\x0f$\x10Z\xbb\u06a0?\xa60\x9e\xf6\xc1\x88\xe5\x1fqJnY\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x0f&H\n\x15\ta\xb8\xe3\aPq:\x94\xeeo.G\xfc\x00\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x0f-\x8d\xaf\x04\xb5AJ\x02a\xf5I\xffdw\xb8\x0f/\x1d\a\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\x0f/\xb8\x84\u022a\xffoT:\xc6\"\x8b\u040eO`\xb0\xa5\xfd\x89\xaa}\xa4\x85\x13k\x84\x00\x00\u07d4\x0f2\xd9\xcbM\x0f\u06a0\x15\x06V\xbb`\x8d\xccC\xed}\x93\x01\x89(\u07cb\xf4@\xdby\x00\x00\u07d4\x0f6e\u050e\x9f\x14\x19\u0358O\xc7\xfa\x92x\x87\x10\xc8\xf2\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f:\x10#\xca\xc0M\xbfD\xf5\xa5\xfaj\x9c\xf8P\x8c\xd4\xfd\u07c9b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\x0f@s\xc1\xb9\x9d\xf6\n\x15I\u0597\x89\xc71\x8d\x94\x03\xa8\x14\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x0fF\xc8\x1d\xb7\x80\xc1gJ\xc7=1O\x06S\x9e\xe5n\xbc\x83\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\x0fO\x94\xb9\x19\x1b\xb7\xbbUj\xaa\xd7\xc7M\xdb(\x84\x17\xa5\v\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x0f`\x00\xde\x15xa\x93 \xab\xa5\xe3\x92pk\x13\x1f\xb1\xdeo\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4\x0fn\x84\n?*$d}\x8eC\xe0\x9dE\xc7\xc35\xdfBH\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\x0fu\x15\xff\x0e\x80\x8fi^\f H_\xf9n\xd2\xf7\xb7\x93\x10\x8968\"\x16`\xa5\xaa\x80\x00\u07d4\x0fx\x9e09|S\xbf%o\xc3d\xe6\xef9\xf8SPA\x14\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\x0f{a\u015b\x01c\"\xe8\"l\xaf\xae\xe9\xd9\xe7mP\xa1\xb3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0f{\xeaN\xf3\xf7:\xe0#=\xf1\xe1\x00q\x8c\xbe)1\v\xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f{\xf67?w\x1aF\x01v,M\xae_\xbb\xf4\xfe\u075c\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\x83*\x93\u07dd\u007ft\xcd\x0f\xb8Tkq\x98\xbfSw\xd9%\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\x0f\x83F\x1b\xa2$\xbb\x1e\x8f\u075d\xaeSQr\xb75\xac\xb4\xe0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x0f\x85\xe4+\x1d\xf3!\xa4\xb3\xe85\xb5\f\x00\xb0as\x96\x846\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\x0f\x88\xaa\xc94l\xb0\xe74\u007f\xbap\x90Tu\xba\x8b>^\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0f\x92\x9c\xf8\x95\xdb\x01z\xf7\x9f>\xad\"\x16\xb1\xbdi\xc3}\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\xa0\x10\xce\fs\x1d;b\x8e6\xb9\x1fW\x13\x00\u477e\xab\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\x0f\xa5\xd8\u0173\xf2\x94\xef\u0515\xabi\xd7h\xf8\x18rP\x85H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\xa6\u01f0\x97=\v\xae)@T\x0e$}6'\xe3|\xa3G\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0f\xad\x05P|\u070f$\xb2\xbeL\xb7\xfa]\x92}\u06d1\x1b\x88\x89\xa2\xdf\x13\xf4A\xf0\t\x80\x00\u07d4\x0f\xb5\xd2\xc6s\xbf\xb1\xdd\xca\x14\x1b\x98\x94\xfdm?\x05\xdag \x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0f\u0260\xe3AE\xfb\xfd\xd2\xc9\u04a4\x99\xb6\x17\u05e0)i\xb9\x89\t\xc2\x00vQ\xb2P\x00\x00\xe0\x94\x0f\xcf\xc4\x06P\b\xcf\xd3#0_b\x86\xb5zM\xd7\xee\xe2;\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x0f\xdde@#\x95\u07db\u045f\xeeE\a\xefSE\xf7E\x10L\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x0f\xecN\xe0\xd7\xca\x18\x02\x90\xb6\xbd \xf9\x99#B\xf6\x0f\xf6\x8d\x89\x12 \u007f\x0e\xdc\xe9q\x80\x00\u07d4\x0f\ue06c3\x1e\xfd\x8f\x81\x16\x1cW8+\xb4P{\xb9\xeb\xec\x89\x15\xaf\x88\r\x8c\u06c3\x00\x00\u07d4\x0f\xfe\xa0mq\x13\xfbj\xec(i\xf4\xa9\u07f0\x90\a\xfa\xce\xf4\x89\f8F\x81\xb1\xe1t\x00\x00\u07d4\x10\tq\x98\xb4\xe7\xee\x91\xff\x82\xcc/;\xd9_\xeds\xc5@\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x10\vM\tw\xfc\xba\xd4\u07bd^d\xa0Iz\xea\xe5\x16\x8f\xab\x89\x11\f\x90s\xb5$Z\x00\x00\xe0\x94\x10\x1a\nd\xf9\xaf\xccD\x8a\x8a\x13\rM\xfc\xbe\xe8\x957\xd8T\x8a\x037\xfe_\xea\xf2\u0440\x00\x00\u07d4\x10,G}i\xaa\u06e9\xa0\xb0\xf6+tY\xe1\u007f\xbb\x1c\x15a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x101\xe0\xec\xb5I\x85\xae!\xaf\x17\x93\x95\r\xc8\x11\x88\x8f\xde|\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x104d\x14\xbe\xc6\xd3\xdc\xc4NP\xe5MT\u00b8\xc3sN>\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x108\x98X\xb8\x00\xe8\xc0\xec2\xf5\x1e\xd6\x1a5YF\xcc@\x9b\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x10Y\xcb\xc6>6\xc4>\x88\xf3\x00\b\xac\xa7\xce\x05\x8e\ua816\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\x10n\xd5\xc7\x19\xb5&\x14w\x89\x04%\xaeuQ\xdcY\xbd%\\\x8a\x02\x89jX\xc9[\xe5\x88\x00\x00\u07d4\x10q\x1c=\xda21x\x85\xf0\xa2\xfd\x8a\xe9.\x82\x06\x9b\r\v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x10sy\xd4\xc4gFO#[\xc1\x8eU\x93\x8a\xad>h\x8a\u05c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x10v!-Ou\x8c\x8e\xc7\x12\x1c\x1c}t%I&E\x92\x84\x8a\ai[Y\xb5\xc1{L\x00\x00\u07d4\x10x\xd7\xf6\x1b\x0eV\xc7N\xe6c[.\x18\x19\xef\x1e=\x87\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x10z\x03\xcf\bB\xdb\u07b0a\x8f\xb5\x87\xcai\x18\x9e\xc9/\xf5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x10\x80\xc1\xd85\x8a\x15\xbc\x84\xda\xc8%<h\x831\x90 \xdf,\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\x10\x8a+|3oxGy\u0635M\x02\xa8\xd3\x1d\x9a\x13\x9c\n\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x10\x8b\xa7\u0089\\P\xe0r\xdco\x96I2\xd5\f(-04\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x10\x8f\xe8\xee*\x13\xdaH{\"\u01abmX.\xa7\x10d\u064c\x89\x15\xacV\xed\xc4\xd1,\x00\x00\xe0\x94\x10\x91\x17k\u16d9d\xa8\xf7.\x0e\xcek\xf8\xe3\u03edn\x9c\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\u07d4\x10\x98\xc7t\xc2\f\xa1\u06ac]\xdbb\x03e1m5?\x10\x9c\x89\x05k\xc7^-c\x10\x00\x00\u0794\x10\x98\xcc \uf13a\xd5\x14f9\xc4\xcd\x1c\xa6\u00d9l\xb9\x9b\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x10\xa1\xc4-\xc1\xbati\x86\xb9\x85\xa5\"\xa7<\x93\xea\xe6Lc\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x10\xa94WIo\x11\b\u0358\xe1@\xa1\xec\u06ee^m\xe1q\x89\x15\xa9\x90b\xd4\x16\x18\x00\x00\u07d4\x10\xb5\xb3M\x12H\xfc\xf0\x17\xf8\xc8\xff\xc4\b\u0389\x9c\xee\xf9/\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\x10\xcfV\td\xff\x83\xc1\xc9gLx<\x0fs\xfc\u0619C\xfc\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x10\xd3$\x16r,\xa4\xe6Hc\x05H\xea\xd9\x1e\xddy\xc0j\xff\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x10\xd9E3N\xcd\xe4{\ub723\x81l\x17=\xfb\xbd\vS3\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x10\xdfh\x15\x06\xe3I0\xacz\\g\xa5L>\x89\u0392\xb9\x81\x89t\xc1\xfa\xb8\xad\xb4T\x00\x00\u07d4\x10\xe1\xe37x\x85\xc4-}\xf2\x18R.\xe7vh\x87\xc0^j\x89\x10C\xc4<\xde\x1d9\x80\x00\u07d4\x10\u342d+\xa3=\x82\xb3s\x88\u041cED\u01b0\"]\xe5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x10\xf4\xbf\xf0\u02a5\x02|\nj-\xcf\xc9R\x82M\xe2\x94\t\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11\x00\x1b\x89\xed\x87>:\xae\xc1\x15V4\xb4h\x16C\x98c#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x11\x027\u03d1\x17\xe7g\x92/\u0121\xb7\x8dyd\u0682\xdf \x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x11\x11\xe5\xdb\xf4^o\x90mb\x86o\x17\b\x10\x17\x88\xdd\xd5q\x89F{\xe6S>\xc2\xe4\x00\x00\xe0\x94\x11\x17+'\x8d\xddD\xee\xa2\xfd\xf4\xcb\x1d\x16\x96#\x91\xc4S\u064a\xc6/=\x9b\xfdH\x95\xf0\x00\x00\u07d4\x11&4\xb4\xec0\xffxn\x02AY\xf7\x96\xa5y9\xea\x14N\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x110l}WX\x867x\x0f\xc9\xfd\xe8\xe9\x8e\xcb\x00\x8f\x01d\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x116\x12\xbc;\xa0\xeeH\x98\xb4\x9d\xd2\x023\x90_/E\x8fb\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x11A_\xaba\xe0\xdf\u0539\x06v\x14\x1aUz\x86\x9b\xa0\xbd\xe9\x89o\x05\xb5\x9d; \x00\x00\x00\u07d4\x11L\xbb\xbfo\xb5*\xc4\x14\xbe~\xc6\x1f{\xb7\x14\x95\xce\x1d\xfa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x11L\xfe\xfeP\x17\r\xd9z\xe0\x8f\nDTIx\u0159T\x8d\x89.\u0207\xe7\xa1J\x1c\x00\x00\u07d4\x11a\b\xc1 \x84a.\xed\xa7\xa9=\xdc\xf8\xd2`.'\x9e\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11d\u02aa\x8c\u0157z\xfe\x1f\xad\x8a}`(\xce-W)\x9b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x11gZ%UF\a\xa3\xb6\xc9*\x9e\xe8\xf3ou\xed\xd3\xe36\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4\x11j\t\xdff\xcb\x15\x0e\x97W\x8e)\u007f\xb0n\x13\x04\f\x89<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11o\xef^`\x16B\xc9\x18\u02c9\x16\x0f\xc2);\xa7\x1d\xa96\x89+|\xc2\xe9\xc3\"\\\x00\x00\u07d4\x11xP\x1f\xf9J\xdd\x1cX\x81\xfe\x88a6\xf6\xdf\xdb\xe6\x1a\x94\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x11y\xc6\r\xbd\x06\x8b\x15\v\aM\xa4\xbe#\x03; \u0185X\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\x11}\x9a\xa3\xc4\xd1;\xee\x12\xc7P\x0f\t\xf5\xdd\x1cf\xc4e\x04\x89\v*\xd3\x04\x90\xb2x\x00\x00\xe0\x94\x11}\xb867\u007f\xe1TU\xe0,.\xbd\xa4\v\x1c\xebU\x1b\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x11\x8c\x18\xb2\xdc\xe1p\xe8\xf4Eu;\xa5\xd7Q<\xb7cm-\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\x11\x8f\xbdu;\x97\x929Z\xefzMx\xd2c\xcd\u02ab\xd4\xf7\x8963\x03\"\xd5#\x8c\x00\x00\xe0\x94\x11\x92\x83x\xd2}U\xc5 \xce\xed\xf2L\xeb\x1e\x82-\x89\r\xf0\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x11\x9a\xa6M[}\x18\x1d\xae\x9d<\xb4I\x95\\\x89\xc1\xf9c\xfa\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\x11\xc05\x8a\xa6G\x9d\xe2\x18f\xfe!\a\x19$\xb6^p\xf8\xb9\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\xe0\x94\x11\xd2$z\"\x1ep\xc2\xd6m\x17\xee\x13\x8d8\xc5_\xfb\x86@\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x11\u05c4JG\x1e\xf8\x9a\x8d\x87uUX<\xee\xbd\x149\xea&\x8a\x02#i\u6e80\u0188\x00\x00\u07d4\x11\xdda\x85\u0668\xd7=\xdf\u06a7\x1e\x9bwtC\x1cM\xfe\u008965\u026d\xc5\u07a0\x00\x00\u07d4\x11\xe7\x99~\u0750E\x03\xd7}\xa6\x03\x8a\xb0\xa4\xc84\xbb\xd5c\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\x11\xec\x00\xf8I\xb61\x9c\xf5\x1a\xa8\u074ff\xb3U)\xc0\xbew\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11\ufe22\x04Q\x16\x1bdJ\x8c\u03bb\xc1\xd3C\xa3\xbb\xcbR\x89\xadx\xeb\u016cb\x00\x00\x00\xe0\x94\x11\xfe\xfb]\xc1\xa4Y\x8a\xa7\x12d\fQwu\u07e1\xd9\x1f\x8c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x12\x0f\x9d\xe6\xe0\xaf~\xc0*\a\xc6\t\u0284G\xf1W\xe64L\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x12\x10\xf8\v\u06c2l\x17Tb\xab\a\x16\xe6\x9eF\xc2J\xd0v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x12\x13N\u007fk\x01{\xf4\x8e\x85Z9\x9c\xa5\x8e.\x89/\xa5\u020965\u026d\xc5\u07a0\x00\x00\u07d4\x12\x170t\x98\x01S\xae\xaaK\r\xcb\xc7\x13.\xad\xce\xc2\x1bd\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4\x12\x1f\x85[p\x14\x9a\xc84s\xb9po\xb4MG\x82\x8b\x98;\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x12'\xe1\nM\xbf\x9c\xac\xa3\x1b\x17\x80#\x9fUv\x15\xfc5\xc1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x12-\xcf\xd8\x1a\u0779}\x1a\x0eI%\u0135I\x80n\x9f;\xeb\x89R 5\xccn\x01!\x00\x00\u07d4\x12/V\x12%I\xd1h\xa5\xc5\xe2g\xf5&b\xe5\xc5\xcc\xe5\u0209\n\ad\a\xd3\xf7D\x00\x00\xe0\x94\x121o\xc7\xf1x\xea\xc2.\xb2\xb2Z\xed\xea\xdf=u\xd0\x01w\x8a\x04<3\xbe\x05\xf6\xbf\xb9\x80\x00\xe0\x94\x127Y\xf33\xe1>0i\xe2\x03KO\x059\x89\x18\x11\x9d6\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x12\\\xc5\xe4\xd5k+\xcc.\xe1\xc7\t\xfb\x9eh\xfb\x17t@\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x12c#\x88\xb2v^\xe4E+P\x16\x1d\x1f\xff\xd9\x1a\xb8\x1fJ\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\u07d4\x12h\x97\xa3\x11\xa1J\xd4;x\xe0\x92\x01\x00\xc4Bk\xfdk\u07494\xc7&\x89?-\x94\x80\x00\u07d4\x12m\x91\xf7\xad\x86\u07bb\x05W\xc6\x12\xca'n\xb7\xf9m\x00\xa1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x12}?\xc5\x00;\xf6<\r\x83\xe99W\x83e\x15\xfd'\x90E\x89\x06\x10\xc9\".nu\x00\x00\xe0\x94\x12}\xb1\xca\xdf\x1bw\x1c\xbdtu\xe1\xb2ri\x0fU\x8c\x85e\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x12\x84\xf0\xce\xe9\xd2\xff)\x89\xb6Ut\xd0o\xfd\x9a\xb0\xf7\xb8\x05\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x12\x8b\x90\x8f\xe7C\xa44 =\xe2\x94\xc4A\xc7\xe2\n\x86\xeag\x89&\xab\x14\xe0\xc0\xe1<\x00\x00\xe0\x94\x12\x93\u01cc}jD;\x9dt\xb0\xba^\xe7\xbbG\xfdA\x85\x88\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\x12\x96\xac\xde\xd1\xe0c\xaf9\xfe\x8b\xa0\xb4\xb6=\xf7\x89\xf7\x05\x17\x89\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d4\x12\xaa}\x86\xdd\xfb\xad0\x16\x92\xfe\xac\x8a\b\xf8A\xcb!\\7\x89\amA\xc6$\x94\x84\x00\x00\xe0\x94\x12\xaf\xbc\xba\x14'\xa6\xa3\x9e{\xa4\x84\x9fz\xb1\xc45\x8a\xc3\x1b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x12\xb5\xe2\x89E\xbb)i\xf9\xc6Lc\xcc\x05\xb6\xf1\xf8\xd6\xf4\u054a\x01\xa2\x9e\x86\x91;t\x05\x00\x00\u0794\x12\u03cb\x0eFR\x13!\x1a[S\u07f0\xdd'\x1a(,\x12\u0248\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x12\xd2\a\x90\xb7\xd3\xdb\u060c\x81\xa2y\xb8\x12\x03\x9e\x8a`;\u0409V\xf9\x85\u04c6D\xb8\x00\x00\xe0\x94\x12\xd6\re\xb7\xd9\xfcH\x84\v\xe5\xf8\x91\xc7E\xcev\xeeP\x1e\x8a\x04\x85\xe58\x8d\fv\x84\x00\x00\u0794\x12\xd9\x1a\x92\xd7O\xc8a\xa7)dm\xb1\x92\xa1%\xb7\x9fSt\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\x12\u992d*\xd5t\x84\xddp\x05e\xbd\xdbFB;\u067d1\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x12\xf3,\n\x1f-\xaa\xb6v\xfei\xab\xd9\xe0\x185-L\xcdE\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x12\xf4`\xaedl\xd2x\x0f\xd3\\P\xa6\xafK\x9a\xcc\xfa\x85\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x12\xff\xc1\x12\x86\x05\xcb\f\x13p\x9ar\x90Po&\x90\x97q\x93\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x13\x03$F\xe7\xd6\x10\xaa\x00\xec\x8cV\u0275t\xd3l\xa1\xc0\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\x1cy,\x19}\x18\xbd\x04]p$\x93|\x1f\x84\xb6\x0fD8\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13\x1d\xf8\xd30\xeb|\xc7\x14}\nUWo\x05\u078d&\xa8\xb7\x89\n1\x06+\xee\xedp\x00\x00\u07d4\x13\x1f\xae\xd1%a\xbbz\xee\x04\xe5\x18Z\xf8\x02\xb1\xc3C\x8d\x9b\x89\v\xdf<K\xb02\x8c\x00\x00\u07d4\x13!\xb6\x05\x02oO\xfb)j>\x0e\u0733\x90\xc9\xc8V\b\xb7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13!\xcc\xf2\x979\xb9t\xe5\xa5\x16\xf1\x8f:\x846q\xe3\x96B\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13'\xd7Y\xd5n\n\xb8z\xf3~\xcfc\xfe\x01\xf3\x10\xbe\x10\n\x89#\xbc<\xdbh\xa1\x80\x00\x00\u07d4\x13)\xdd\x19\xcdK\xaa\x9f\xc6C\x10\xef\xec\xea\xb2!\x17%\x1f\x12\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x137\x1f\x92\xa5n\xa88\x1eC\x05\x9a\x95\x12\x8b\xdcMC\u0166\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x13<I\x0f\xa5\xbf\u007f7(\x88\xe6\a\xd9X\xfa\xb7\xf9U\xba\xe1\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\x13>O\x15\xe1\xe3\x9cSCY0\xaa\xed\xf3\xe0\xfeV\xfd\xe8C\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x13Ac\xbe\x9f\xbb\xe1\xc5in\xe2U\xe9\v\x13%C\x95\xc3\x18\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x13\\\xec\xd9U\xe5y\x83pv\x920\x15\x93\x03\u0671\x83\x9ff\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x13]\x17\x19\xbf\x03\xe3\xf8f1$y\xfe3\x81\x18\xcd8~p\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x13^\xb8\xc0\xe9\xe1\x01\xde\xed\xec\x11\xf2\xec\xdbf\xae\x1a\xae\x88g\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x13`\xe8}\xf2Li\xeemQ\xc7nsv\u007f\xfe\x19\xa2\x13\x1c\x89\x04\xfc\xc1\xa8\x90'\xf0\x00\x00\u07d4\x13l\x83K\xf1\x112m s\x95)[.X>\xa7\xf35r\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x13mKf+\xbd\x10\x80\xcf\xe4D[\x0f\xa2\x13\x86D5\xb7\xf1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13oI\a\u02b4\x1e'\bK\x98E\x06\x9f\xf2\xfd\f\x9a\xdey\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13t\xfa\xcd{?\x8dhd\x9d`\xd4U\x0e\xe6\x9f\xf0HA3\x89\x0e\x9e\xd6\xe1\x11r\xda\x00\x00\u07d4\x13|\xf3A\xe8Ql\x81X\x14\xeb\xcds\xe6V\x9a\xf1L\xf7\xbc\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x13\x84\x8bF\xeau\xbe\xb7\xea\xa8_Y\xd8f\xd7\u007f\xd2L\xf2\x1a\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x13\x9d51\u0252*\xd5bi\xf60\x9a\xa7\x89\xfb$\x85\xf9\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13\x9eG\x97d\xb4\x99\xd6f \x8cJ\x8a\x04z\x97\x041c\u0749 w!*\xffm\xf0\x00\x00\u07d4\x13\xa5\xee\xcb80]\xf9Iq\xef-\x9e\x17\x9a\xe6\u03ba\xb37\x89\x11\u3ac3\x95\xc6\xe8\x00\x00\u07d4\x13\xac\xad\xa8\x98\n\xff\xc7PI!\xbe\x84\xebID\xc8\xfb\xb2\xbd\x89V\u04aa:\\\t\xa0\x00\x00\u07d4\x13\xb9\xb1\a\x15qL\t\xcf\xd6\x10\u03dc\x98F\x05\x1c\xb1\xd5\x13\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x13\xce3-\xffe\xa6\xab\x938\x97X\x8a\xa2>\x00\t\x80\xfa\x82\x89\x0e\x02\x056\xf0(\xf0\x00\x00\u07d4\x13\xd6z~%\xf2\xb1,\u06c5XP\t\xf8\xac\u011b\x96s\x01\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x13\xde\xe0>7\x99\x95-\a8\x84=K\xe8\xfc\n\x80?\xb2\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\xe0/\xb4H\xd6\xc8J\xe1}\xb3\x10\xad(m\x05a`\u0695\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\xe3!r\x8c\x9cWb\x80X\xe9?\xc8f\xa02\xdd\v\u0690\x89&\xbc\xca#\xfe.\xa2\x00\x00\u07d4\x13\xec\x81\"\x84\x02n@\x9b\xc0f\xdf\xeb\xf9\u0564\xa2\xbf\x80\x1e\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\x14\x01)\xea\xa7f\xb5\xa2\x9f[:\xf2WND\t\xf8\xf6\xd3\xf1\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u07d4\x14\x05\x18\xa3\x19K\xad\x13P\xb8\x94\x9ee\x05e\u07bem\xb3\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\x06\x85M\x14\x9e\b\x1a\xc0\x9c\xb4\xcaV\r\xa4c\xf3\x120Y\x89Hz\x9a0E9D\x00\x00\u07d4\x14\f\xa2\x8f\xf3;\x9ff\xd7\xf1\xfc\x00x\xf8\xc1\xee\xf6\x9a\x1b\xc0\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x14\x0f\xbaX\xdb\xc0H\x03\xd8L!0\xf0\x19x\xf9\xe0\xc71)\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x14\x1a^9\xee/h\n`\x0f\xbfo\xa2\x97\u0790\xf3\"\\\u074a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x14%N\xa1&\xb5-\x01B\xda\n~\x18\x8c\xe2U\xd8\xc4qx\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\x14+\x87\xc5\x04?\xfbZ\x91\xdf\x18\xc2\xe1\t\xce\xd6\xfeJq\u06c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x14<c\x97R\xca\xee\xcfj\x99}9p\x9f\xc8\xf1\x98x\xc7\xe8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x14=Sk\x8b\x1c\xb8OV\xa3\x9e\v\xc8\x1f\xd5D+\xca\xcc\xe1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x14?_\x16X\xd9\xe5x\xf4\xf3\xd9_\x80\xc0\xb1\xbd93\xcb\u0689P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x14A\x0f\xb3\x10q\x1b\xe0t\xa8\b\x83\xc65\xd0\xefj\xfb%9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14K\x19\xf1\xf6l\xbe1\x83G\u4344\xb1@9FlY\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14RP\xb0nO\xa7\xcb'IB.\xb8\x17\xbd\u068bT\xde_\x89\v\xdf<K\xb02\x8c\x00\x00\u07d4\x14^\x06\x00\xe2\xa9'\xb2\u074d7\x93V\xb4Z.}Q\u04ee\x89\x8a\x02\xab@\v\xb2\u02c0\x00\u07d4\x14^\x1d\xe0\x14y\x11\xcc\u0600\x87_\xbb\xeaa\xf6\xa1B\xd1\x1d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x14c\xa8sU[\xc09~W\\$q\xcfw\xfa\x9d\xb1F\xe0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x14y\xa9\xect\x80\xb7K]\xb8\xfcI\x9b\xe3R\xda\u007f\x84\ue70965\u026d\xc5\u07a0\x00\x00\u07d4\x14z\xf4j\xe9\xcc\u044b\xb3\\\xa0\x1b5;Q\x99\x0eI\xdc\xe1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x14\u007fB\x10\xabX\x04\x94\n\v}\xb8\xc1L(9kb\xa6\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\x83\a\x04\u96aa\xd5\xc5^\x1fP+'\xb2,\x12\xc9\x193\x89!\x9c:{\x19f0\x00\x00\xe0\x94\x14\x9bm\xbd\xe62\xc1\x9fZ\xf4|\xb4\x93\x11K\xeb\u0670<\x1f\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94\x14\x9b\xa1\x0f\r\xa2r]\xc7\x04s>\x87\xf5\xa5$\u0288Q^\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4\x14\xa75 f6D\x04\xdbP\xf0\xd0\u05cduJ\"\x19\x8e\xf4\x89e\xea=\xb7UF`\x00\x00\u07d4\x14\xab\x16K;RL\x82\u05ab\xfb\xc0\u0783\x11&\xae\x8d\x13u\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\xb1`>\xc6+ \x02 3\xee\xc4\xd6\xd6eZ\xc2J\x01Z\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x14\xc6;\xa2\u0731\xddM\xf3=\u06b1\x1cO\x00\a\xfa\x96\xa6-\x8a\x03HA\xb6\x05z\xfa\xb0\x00\x00\xe0\x94\x14\xcd\u077c\x8b\t\xe6gZ\x9e\x9e\x05\t\x1c\xb9\"8\u00de\x1e\x8a\x01\x14x\xb7\xc3\n\xbc0\x00\x00\u07d4\x14\xd0\n\xad9\xa0\xa7\u045c\xa0SP\xf7\xb07'\xf0\x8d\xd8.\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x14\xee\xc0\x9b\xf0>5+\xd6\xff\x1b\x1e\x87k\xe6d\xce\xff\xd0\u03c9\x01\x16\xdc:\x89\x94\xb3\x00\x00\u07d4\x14\xf2!\x15\x95\x18x;\u0127\x06go\xc4\xf3\xc5\xee@X)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x14\xfc\xd19\x1e}s/Avl\xda\u0344\xfa\x1d\xeb\x9f\xfd\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x0e=\xbc\xbc\xfc\x84\xcc\xf8\x9bsBwc\xa5e\xc2>`\u0409\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\x15\x18b{\x885\x1f\xed\xe7\x96\xd3\xf3\b3d\xfb\u0508{\f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u0794\x15\"J\xd1\xc0\xfa\xceF\xf9\xf5V\xe4wJ0%\xad\x06\xbdR\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x15/+\xd2)\xdd\xf3\xcb\x0f\xda\xf4U\xc1\x83 \x9c\x0e\x1e9\xa2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x15/N\x86\x0e\xf3\xee\x80jP'w\xa1\xb8\xdb\xc9\x1a\x90vh\x89 \x86\xac5\x10R`\x00\x00\u07d4\x15<\b\xaa\x8b\x96\xa6\x11\xefc\xc0%>*C4\x82\x9eW\x9d\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\x15<\xf2\x84,\xb9\u0787l'o\xa6Gg\u0468\xec\xf5s\xbb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15>\xf5\x8a\x1e.z>\xb6\xb4Y\xa8\n\xb2\xa5G\xc9A\x82\xa2\x8a\x14T+\xa1*3|\x00\x00\x00\u07d4\x15DY\xfa/!1\x8e44D\x97\x89\xd8&\xcd\xc1W\f\xe5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15G\xb9\xbfz\xd6bt\xf3A8'#\x1b\xa4\x05\ue308\xc1\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\x15H\xb7p\xa5\x11\x8e\u0787\u06e2\xf6\x903\u007fam\u60eb\x89\x1c\x99V\x85\u0fc7\x00\x00\u07d4\x15R\x83P\xe0\xd9g\n.\xa2\u007f{J3\xb9\xc0\xf9b\x1d!\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x15[7y\xbbmV4./\u0681{[-\x81\xc7\xf4\x13'\x89\x02\xb8\xaa:\al\x9c\x00\x00\u07d4\x15e\xaf\x83~\xf3\xb0\xbdN+#V\x8dP#\xcd4\xb1d\x98\x89\x15Q\xe9rJ\u013a\x00\x00\u07d4\x15f\x91\x80\xde\u2558\x86\x9b\b\xa7!\xc7\xd2LL\x0e\xe6?\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x15r\xcd\xfa\xb7*\x01\u0396\x8ex\xf5\xb5D\x8d\xa2\x98S\xfb\u074a\x01\x12blI\x06\x0f\xa6\x00\x00\xe0\x94\x15uY\xad\xc5Wd\xccm\xf7\x93#\t%4\xe3\xd6dZf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x15x\xbd\xbc7\x1bM$8E3\x05V\xff\xf2\xd5\xefM\xffg\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x15~\xb3\xd3\x11;\u04f5\x97qM:\x95N\xdd\x01\x89\x82\xa5\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x84\xa2\xc0f\xb7\xa4U\xdb\u05ae(\a\xa73N\x83\xc3_\xa5\x89\a\f\x1c\xc7;\x00\xc8\x00\x00\u07d4\x15\x87F\x86\xb6s=\x10\xd7\x03\xc9\xf9\xbe\xc6\xc5.\xb8b\x8dg\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x8a\ra\x92S\xbfD2\xb5\xcd\x02\u01f8b\xf7\u00b7V6\x89\a[\xac|[\x12\x18\x80\x00\u07d4\x15\x98\x12y\x82\xf2\xf8\xad;k\x8f\xc3\xcf'\xbfax\x01\xba+\x89\t`\xdbwh\x1e\x94\x00\x00\xe0\x94\x15\x9a\xdc\xe2z\xa1\vG#d)\xa3JZ\xc4,\xad[d\x16\x8a\x06\xbf\x90\xa9n\xdb\xfaq\x80\x00\u07d4\x15\xa0\xae\xc3\u007f\xf9\xff=T\t\xf2\xa4\xf0\xc1!*\xac\xcb\x02\x96\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x15\xaaS\r\xc3iX\xb4\xed\xb3\x8e\xeem\xd9\xe3\xc7}L\x91E\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15\xac\xb6\x15h\xecJ\xf7\xea(\x198a\x81\xb1\x16\xa6\xc5\xeep\x8a\x06\x90\x83l\n\xf5\xf5`\x00\x00\u07d4\x15\xb9o0\xc2;\x86d\xe7I\x06Q\x06k\x00\xc49\x1f\xbf\x84\x89\x16B\xe9\xdfHv)\x00\x00\u07d4\x15\xc7\xed\xb8\x11\x8e\xe2{4\"\x85\xebY&\xb4z\x85[\u01e5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x15\u0654hPz\xa0A?\xb6\r\xca*\xdc\u007fV\x9c\xb3kT\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15\u06f4\x8c\x980\x97d\xf9\x9c\xed6\x92\xdc\xca5\xee0k\xac\x8a\x1f\u00c4+\xd1\xf0q\xc0\x00\x00\xe0\x94\x15\u072f\xcc+\xac\xe7\xb5[T\xc0\x1a\x1cQF&\xbfa\xeb\u060a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x15\u3d44\x05kb\xc9s\xcf^\xb0\x96\xf1s>T\xc1\\\x91\x892\xc7Z\x02#\xdd\xf3\x00\x00\u07d4\x15\xeb\xd1\xc7\xca\u04af\xf1\x92u\xc6W\xc4\xd8\b\xd0\x10\xef\xa0\xf5\x89\n\xdf0\xbap\u0217\x00\x00\u07d4\x15\xee\x0f\xc6>\xbf\x1b\x1f\u011d{\xb3\x8f\x88c\x82:.\x17\u0489g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x15\xf1\xb3R\x11\rh\x90\x1d\x8fg\xaa\xc4jl\xfa\xfe\x03\x14w\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x15\xf2\xb7\xb1d2\xeeP\xa5\xf5[A#/c4\xedX\xbd\xc0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x16\x01\x9aM\xaf\xabC\xf4\u067fAc\xfa\xe0\x84}\x84\x8a\xfc\xa2\x89\x01[\xc7\x019\xf7J\x00\x00\u07d4\x16\x02&\xef\xe7\xb5:\x8a\xf4b\xd1\x17\xa0\x10\x80\x89\xbd\xec\xc2\u0449\n\xdf0\xbap\u0217\x00\x00\u07d4\x16\f\xebo\x98\x0e\x041_S\xc4\xfc\x98\x8b+\xf6\x9e(M}\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\xe0\x94\x16\x1c\xafZ\x97*\u0383y\xa6\u0420J\xe6\xe1c\xfe!\xdf+\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x16\x1d&\xefgY\xba[\x9f \xfd\xcdf\xf1a2\xc3RA^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16!\x10\xf2\x9e\xac_}\x02\xb5C\xd8\xdc\u057bY\xa5\xe3;s\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x16+\xa5\x03'b\x14\xb5\t\xf9u\x86\xbd\x84!\x10\xd1\x03\xd5\x17\x8a\x01\xe7\xff\u0609\\\"h\x00\x00\u07d4\x16-v\xc2\xe6QJ:\xfbo\xe3\xd3\u02d3\xa3\\Z\xe7\x83\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16;\xadJ\x12+E}d\xe8\x15\nA>\xaeM\a\x02>k\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u07d4\x16<\u023e\"vF\xcb\tq\x91Y\xf2\x8e\u041c]\xc0\xdc\xe0\x89Hz\x9a0E9D\x00\x00\u07d4\x16=\xcas\xd7\xd6\xea?>`b2*\x874\x18\f\vx\uf25ft \x03\xcb}\xfc\x00\x00\u07d4\x16Mz\xac>\xec\xba\uc86dQ\x91\xb7S\xf1s\xfe\x12\xec3\x89(VR\xb8\xa4hi\x00\x00\u07d4\x16Rl\x9e\u07d4>\xfaOm\x0f\v\xae\x81\xe1\x8b1\xc5@y\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x16S\x05\xb7\x872.%\xdcj\xd0\xce\xfelo3Fx\xd5i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16e\xab\x179\xd7\x11\x19\xeea2\xab\xbd\x92j'\x9f\xe6yH\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x16k\xf6\u06b2-\x84\x1bHl8\xe7\xbaj\xb3:\x14\x87\ud30a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x16v\x99\xf4\x8ax\xc6\x15Q%\x15s\x99X\x993\x12WO\a\x89\x02\x1d;\xd5^\x80<\x00\x00\u07d4\x16x\xc5\xf2\xa5\"92%\x19ca\x89OS\xccu/\xe2\xf3\x89h\xf3e\xae\xa1\xe4@\x00\x00\u07d4\x16|\xe7\xdee\xe8G\bYZRT\x97\xa3\xeb^ZfPs\x89\x1f1Gsfo\xc4\x00\x00\u07d4\x16~>:\xe2\x003HE\x93\x92\xf7\xdf\xceD\xaf|!\xadY\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x16\x80\xce\xc5\x02\x1e\xe90P\xf8\xae\x12rQ\x83\x9et\xc1\xf1\xfd\x8a\x02\xc6\x14a\xe5\xd7C\u0580\x00\u07d4\x16\x81j\xac\x0e\xde\r-<\xd4B\xday\xe0c\x88\x0f\x0f\x1dg\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x16\x8bP\x19\xb8\x18i\x16D\x83_\xe6\x9b\xf2)\xe1q\x12\xd5,\x8a\x05\xed\xe2\x0f\x01\xa4Y\x80\x00\x00\u07d4\x16\x8b\xde\xc8\x18\xea\xfcm)\x92\xe5\xefT\xaa\x0e\x16\x01\xe3\xc5a\x8967Pz0\xab\xeb\x00\x00\u07d4\x16\x8d0\xe5?\xa6\x81\t+R\xe9\xba\xe1Z\r\xcbA\xa8\u027b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x16\x9b\xbe\xfcA\xcf\xd7\xd7\u02f8\xdf\xc60 \xe9\xfb\x06\u0515F\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xa5\x8e\x98]\xcc\xd7\a\xa5\x94\u0453\xe7\u0327\x8b]\x02xI\x89I\xb9\u029aiC@\x00\x00\u07d4\x16\xa9\xe9\xb7:\u92c6M\x17(y\x8b\x87f\xdb\xc6\xea\x8d\x12\x893\xe7\xb4K\r\xb5\x04\x00\x00\u07d4\x16\xaaR\xcb\vUG#\xe7\x06\x0f!\xf3'\xb0\xa6\x83\x15\xfe\xa3\x89\r\x8drkqw\xa8\x00\x00\u07d4\x16\xab\xb8\xb0!\xa7\x10\xbd\u01ce\xa54\x94\xb2\x06\x14\xffN\xaf\xe8\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x16\xaf\xa7\x87\xfc\x9f\x94\xbd\xffiv\xb1\xa4/C\n\x8b\xf6\xfb\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xba\xe5\xd2N\xff\x91w\x8c\u064bM:\x1c\xc3\x16/D\xaaw\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\x16\xbc@!Z\xbb\u066e](\v\x95\xb8\x01\vE\x14\xff\x12\x92\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x16\xbeu\u9299Z9R\"\xd0\v\u05df\xf4\xb6\xe68\u144a\a\x9f\x90\\o\xd3N\x80\x00\x00\u07d4\x16\xc1\xbf[}\xc9\xc8<\x17\x9e\xfa\xcb\xcf.\xb1t\xe3V\x1c\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x16\u01f3\x1e\x8c7b\x82\xac\"qr\x8c1\xc9^5\xd9R\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xf3\x13\u03ca\xd0\x00\x91J\n\x17m\u01a44+y\xec%8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xff\xac\x84\x03)@\xf0\x12\x1a\tf\x8b\x85\x8a~y\xff\xa3\xbb\x89\xd2J\xdan\x10\x87\x11\x00\x00\xe0\x94\x17\x03\xb4\xb2\x92\xb8\xa9\xde\xdd\xed\xe8\x1b\xb2]\x89\x17\x9fdF\xb6\x8a\x04+e\xa4U\xe8\xb1h\x00\x00\u07d4\x17\x04\x93\x11\x10\x1d\x81~\xfb\x1de\x91\x0ff6b\xa6\x99\u024c\x89lh\xcc\u041b\x02,\x00\x00\u07d4\x17\x04\xce\xfc\xfb\x131\xeczx8\x8b)9>\x85\xc1\xafy\x16\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17\n\x88\xa8\x99\u007f\x92\xd287\x0f\x1a\xff\xde\xe64pP\xb0\x13\x89\xa2\xacw5\x14\x880\x00\x00\u07d4\x17\x10\x8d\xab,P\xf9\x9d\xe1\x10\u1cf3\xb4\u0342\xf5\xdf(\xe7\x895 ;g\xbc\xca\xd0\x00\x00\xe0\x94\x17\x12[Y\xacQ\xce\xe0)\xe4\xbdx\xd7\xf5\x94}\x1e\xa4\x9b\xb2\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4\x17\x1a\u0660K\xed\u0238a\xe8\xedK\xdd\xf5qx\x13\xb1\xbbH\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17\x1c\xa0*\x8bmb\xbfL\xa4~\x90i\x14\a\x98a\x97,\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x17\"\xc4\xcb\xe7\n\x94\xb6U\x9dBP\x84\xca\xee\xd4\xd6\xe6n!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x17X\vvotSR\\\xa4\u01a8\x8b\x01\xb5\x05p\xea\b\x8c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x17X\x9al\x00jT\xca\xd7\x01\x03\x12:\xae\n\x82\x13_\u07b4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x17Z\x18::#_\xfb\xb0;\xa85gRg\"\x94\x17\xa0\x91\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x17_\xee\xea*\xa4\xe0\xef\xda\x12\xe1X\x8d/H2\x90\xed\xe8\x1a\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x17e6\x1c.\xc2\xf86\x16\u0383c\xaa\xe2\x10%\xf2Vo@\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x17gR\\_Z\"\xed\x80\xe9\xd4\xd7q\x0f\x03b\u049e\xfa3\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17v%`\xe8*\x93\xb3\xf5\"\xe0\xe5$\xad\xb8a,:tp\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x17}\xaex\xbc\x01\x13\xd8\u04dcD\x02\xf2\xa6A\xae*\x10Z\xb8\x89b\x92BV \xb4H\x00\x00\xe0\x94\x17\x84\x94\x8b\xf9\x98H\u021eDV8PM\u0598'\x1bY$\x8a\x01GLA\r\x87\xba\xee\x00\x00\u07d4\x17\x88\u069bW\xfd\x05\xed\xc4\xff\x99\xe7\xfe\xf3\x01Q\x9c\x8a\n\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\x8e\xafk\x85T\xc4]\xfd\xe1kx\xce\f\x15\u007f.\xe3\x13Q\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\x17\x96\x1dc;\xcf \xa7\xb0)\xa7\xd9K}\xf4\xda.\xc5B\u007f\x89\fo\xf0p\U000532c0\x00\u07d4\x17\x96\xbc\xc9{\x8a\xbcq\u007fKJ|k\x106\xea!\x82c\x9f\x89\x13A\xf9\x1c\xd8\xe3Q\x00\x00\u07d4\x17\x99=1*\xa1\x10iW\x86\x8fjU\xa5\xe8\xf1/w\xc8C\x89\x18e\xe8\x14\xf4\x14.\x80\x00\u07d4\x17\x9a\x82^\x0f\x1fn\x98S\tf\x84e\xcf\xfe\xd46\xf6\xae\xa9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x17\xb2\xd6\xcfe\xc6\xf4\xa3G\xdd\xc6W&U5M\x8aA+)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xb8\a\xaf\xa3\xdd\xd6G\xe7#T.{R\xfe\xe3\x95'\xf3\x06\x89\x15\xaf@\xff\xa7\xfc\x01\x00\x00\u07d4\x17\xc0G\x86W\xe1\xd3\xd1z\xaa3\x1d\xd4)\xce\u03d1\xf8\xae]\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\x17\xc0\xfe\xf6\x98l\xfb.@A\xf9\x97\x9d\x99@\xb6\x9d\xff=\xe2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x17\u0511\x8d\xfa\xc1]w\xc4\u007f\x9e\xd4\x00\xa8P\x19\rd\xf1Q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xd5!\xa8\xd9w\x90#\xf7\x16M#<;d \xff\xd2#\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x17\xd91\xd4\xc5b\x94\u073ew\xc8e[\xe4i_\x00mJ<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xdfIQ\x8ds\xb1)\xf0\xda6\xb1\u0274\f\xb6d \xfd\u01ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x17\xe4\xa0\xe5+\xac>\xe4N\xfe\tT\xe7S\u0538]dN\x05\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xe5\x84\xe8\x10\xe5gp,a\xd5]CK4\u0375\xee0\xf6\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x17\xe8.px\xdcO\xd9\xe8y\xfb\x8aPf\u007fS\xa5\xc5E\x91\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x17\xe8o;[0\xc0\xbaY\xf2\xb2\xe8XB[\xa8\x9f\n\x10\xb0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xee\x9fT\xd4\xdd\xc8Mg\x0e\xff\x11\xe5Je\x9f\xd7/DU\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94\x17\xefJ\xcc\x1b\xf1G\xe3&t\x9d\x10\xe6w\xdc\xff\xd7o\x9e\x06\x8a\bwQ\xf4\xe0\xe1\xb50\x00\x00\u07d4\x17\xf1F2\xa7\xe2\x82\v\xe6\xe8\xf6\u07c25X(=\xad\xab-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xf5#\xf1\x17\xbc\x9f\xe9x\xaaH\x1e\xb4\xf5V\x17\x117\x1b\u0209li\xf7>)\x13N\x00\x00\u07d4\x17\xfd\x9bU\x1a\x98\xcba\xc2\xe0\u007f\xbfA\xd3\xe8\u02650\u02e5\x89\x01v\x8c0\x81\x93\x04\x80\x00\u07d4\x18\x04x\xa6U\u05cd\x0f;\fO +aH[\xc4\x00/\u0549lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\x13l\x9d\xf1g\xaa\x17\xb6\xf1\x8e\"\xa7\x02\u020fK\u0082E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x18\x15'\x9d\xff\x99R\xda;\xe8\xf7rI\xdb\xe2\"C7{\xe7\x8a\x01\x01|\xb7n{&d\x00\x00\u07d4\x18\x1f\xbb\xa8R\xa7\xf5\x01x\xb1\xc7\xf0>\xd9\xe5\x8dT\x16))\x89$\x1a\x9bOaz(\x00\x00\xe0\x94\x18'\x03\x9f\tW\x02\x94\b\x8f\xdd\xf0G\x16\\3\u65a4\x92\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\x18-\xb8R\x93\xf6\x06\u8248\xc3pL\xb3\xf0\xc0\xbb\xbf\xcaZ\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x18H\x00<%\xbf\u052a\x90\xe7\xfc\xb5\u05f1k\xcd\f\xff\xc0\u060965\u026d\xc5\u07a0\x00\x00\xe0\x94\x18JO\v\xebq\xff\xd5X\xa6\xb6\xe8\xf2(\xb7\x87\x96\xc4\xcf>\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94\x18M\x86\xf3Fj\xe6h;\x19r\x99\x82\xe7\xa7\u1903G\xb2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x18Q\xa0c\xcc\xdb0T\x90w\xf1\xd19\xe7-\xe7\x97\x11\x97\u0549lk\x93[\x8b\xbd@\x00\x00\u07d4\x18UF\xe8v\x8dPhs\x81\x8a\xc9u\x1c\x1f\x12\x11j;\xef\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x18X\xcf\x11\xae\xa7\x9fS\x98\xad+\xb2\"g\xb5\xa3\xc9R\xeat\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\xe0\x94\x18Z\u007f\u012c\xe3h\xd23\xe6 \xb2\xa4Y5f\x12\x92\xbd\xf2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x18d\xa3\u01f4\x81UD\x8cT\u020cp\x8f\x16g\tsm1\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x18j\xfd\xc0\x85\xf2\xa3\xdc\xe4a^\xdf\xfb\xad\xf7\x1a\x11x\x0fP\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x18k\x95\xf8\xe5\xef\xfd\xdc\xc9O\x1a1[\xf0)];\x1e\xa5\x88\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x18}\x9f\f\a\xf8\xebt\xfa\xaa\xd1^\xbc{\x80Dt\x17\xf7\x82\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x18\x95\xa0\xebJCrr/\xcb\u016f\xe6\x93o(\x9c\x88\xa4\x19\x891T\xc9r\x9d\x05x\x00\x00\u07d4\x18\x99\xf6\x9fe;\x05\xa5\xa6\xe8\x1fH\a\x11\u041b\xbf\x97X\x8c\x89i\xfb\x13=\xf7P\xac\x00\x00\u07d4\x18\xa6\xd2\xfcR\xbes\b@#\xc9\x18\x02\xf0[\xc2JK\xe0\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\xb0@|\xda\xd4\xceR`\x06#\xbd^\x1fj\x81\xaba\xf0&\x89\x11Q\xcc\xf0\xc6T\u0180\x00\u07d4\x18\xb8\xbc\xf9\x83!\xdaa\xfbN>\xac\xc1\xecT\x17'-\xc2~\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\x18\xc6r:gS)\x9c\xb9\x14G}\x04\xa3\xbd!\x8d\xf8\xc7u\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x18\xe1\x13\xd8\x17|i\x1aa\xbexXR\xfa[\xb4z\uef6f\x89Hz\x9a0E9D\x00\x00\xe0\x94\x18\xe4\xceGH;S\x04\n\u06eb5\x17,\x01\xefdPn\f\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\xe0\x94\x18\xe52C\x98\x1a\xab\xc8v}\xa1\fsD\x9f\x13\x91V\x0e\xaa\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x18\xfa\x86%\xc9\u0704<x\u01eb%\x9f\xf8|\x95\x99\xe0\u007f\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x18\xfb\t\x18\x8f'\xf1\x03\x8ee@1\x92Ob\x8a!\x06p=\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\xfc\xcfb\xd2\xc39TS\xb7X{\x9e&\xf5\xcf\xf9\xebt\x82\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x19\x13\x13RR8\xa2\x1cvtW\xa9\x13t\xf0\"\x00\xc5TH\x89\x06O_\xdfIOx\x00\x00\u07d4\x19\x14\xf1\xeb\x95\xd1'~\x93\xb6\xe6\x1bf\x8b}w\xf1:\x11\xa1\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x19#\xcf\u018b\x13\xea~ U\x806E\xc1\xe3 \x15k\u060d\x89Hz\x9a0E9D\x00\x00\u07d4\x193j#m\xeduXrA\x1f.\x04\x91\xd8>>\x00\x15\x9e\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94\x193\xe34\xc4\x0f:\u02ed\f\v\x85\x11X i$\xbe\xca:\x8a\x01\x99^\xaf\x01\xb8\x96\x18\x80\x00\xe0\x94\x197\xc5\xc5\x15\x05uS\u033dF\u0546dU\xcef)\x02\x84\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u07d4\x19:\xc6Q\x83e\x18\x00\xe25\x80\xf8\xf0\xea\u04fbY~\xb8\xa4\x89\x02\xb6*\xbc\xfb\x91\n\x00\x00\u07d4\x19=7\xed4}\x1c/N55\r\x9aDK\xc5|\xa4\xdbC\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\x19@\u0713d\xa8R\x16_GAN'\xf5\x00$E\xa4\xf1C\x8a\x02L-\xffj<|H\x00\x00\u07d4\x19E\xfe7\u007f\xe6\u0537\x1e>y\x1fo\x17\xdb$<\x9b\x8b\x0f\x89vy\u7fb9\x886\x00\x00\u07d4\x19Jk\xb3\x02\xb8\xab\xa7\xa5\xb5y\u07d3\xe0\xdf\x15t\x96v%\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x19L\ubd12\x98\x82\xbf;K\xf9\x86L+\x1b\x0fb\u0083\xf9\x89\x1e\xf8aS\x1ft\xaa\x00\x00\u07d4\x19O\xf4J\xef\xc1{\xd2\x0e\xfdz LG\xd1b\f\x86\xdb]\x89\xa2\x99\th\u007fj\xa4\x00\x00\xe0\x94\x19O\xfex\xbb\xf5\xd2\r\u044a\x1f\x01\xdaU.\x00\xb7\xb1\x1d\xb1\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x19S1>*\xd7F#\x9c\xb2'\x0fH\xaf4\u063b\x9cDe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19W\x1a+\x8f\x81\u01bc\xf6j\xb3\xa1\x00\x83)V\x17\x15\x00\x03\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\xe0\x94\x19h}\xaa9\xc3h\x13\x9bn{\xe6\r\xc1u:\x9f\f\xbe\xa3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x19l\x02!\nE\n\xb0\xb3cpe_qz\xa8{\xd1\xc0\x04\x89\x0e\x10\xac\xe1W\xdb\xc0\x00\x00\u07d4\x19n\x85\xdf~s+J\x8f\x0e\xd06#\xf4\u06dd\xb0\xb8\xfa1\x89\x01%\xb9/\\\xef$\x80\x00\u07d4\x19s+\xf9s\x05]\xbd\x91\xa4S:\u06a2\x14\x9a\x91\u04c3\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19vr\xfd9\xd6\xf2F\xcef\xa7\x90\xd1:\xa9\"\xd7\x0e\xa1\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x19y\x8c\xbd\xa7\x15\ua69b\x9dj\xab\x94,U\x12\x1e\x98\xbf\x91\x89A\rXj \xa4\xc0\x00\x00\u07d4\x19\x8b\xfc\xf1\xb0z\xe3\b\xfa,\x02\x06\x9a\xc9\xda\xfeq5\xfbG\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x19\x8e\xf1\xec2Z\x96\xcc5Lrf\xa08\xbe\x8b\\U\x8fg\x8a\x80\xd1\xe47>\u007f!\xda\x00\x00\xe0\x94\x19\x91\x8a\xa0\x9e}IN\x98\xff\xa5\xdbP5\b\x92\xf7\x15j\u018a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x19\xb3k\f\x87\xeafN\xd8\x03\x18\xdcw\xb6\x88\xdd\xe8}\x95\xa5\x89i\x9fI\x98\x020=\x00\x00\u07d4\x19\u07d4E\xa8\x1c\x1b=\x80J\xea\xebon NB6f?\x89\x02\x06\xd9NjI\x87\x80\x00\u07d4\x19\xe5\u07a37\n,tj\xae4\xa3|S\x1fA\xda&N\x83\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x19\xe7\xf3\xeb{\xf6\u007f5\x99 \x9e\xbe\b\xb6*\xd32\u007f\x8c\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\x19\xe9Nb\x00P\xaa\xd7f\xb9\xe1\xba\xd91#\x83\x12\u053fI\x89\x81\xe3-\xf9r\xab\xf0\x00\x00\u07d4\x19\xec\xf2\xab\xf4\f\x9e\x85{%/\xe1\xdb\xfd=L]\x8f\x81n\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19\xf5\xca\xf4\xc4\x0ei\b\x81<\aE\xb0\xae\xa9Xm\x9d\xd91\x89#\xfe\xd9\xe1\xfa+`\x00\x00\u07d4\x19\xf6C\xe1\xa8\xfa\x04\xae\x16\x00`(\x13\x833\xa5\x9a\x96\u0787\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x19\xf9\x9f,\vF\u0389\x06\x87]\xc9\xf9\n\xe1\x04\xda\xe3U\x94\x89\xf4WZ]M\x16*\x00\x00\u07d4\x19\xff$O\xcf\xe3\xd4\xfa/O\u065f\x87\xe5[\xb3\x15\xb8\x1e\xb6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1a\x04\xce\xc4 \xadC\"\x15$mw\xfe\x17\x8d3\x9e\u0435\x95\x89\x11!a\x85\u009fp\x00\x00\xe0\x94\x1a\x04\xd58\x9e\xb0\x06\xf9\u0388\f0\xd1SS\xf8\xd1\x1cK1\x8a\x03\x9d\x84\xb2\x18m\xc9\x10\x00\x00\u07d4\x1a\bA\xb9*\u007fpuV\x9d\xc4b~kv\u02b0Z\u0791\x89Rf<\u02b1\xe1\xc0\x00\x00\xe0\x94\x1a\b]C\xec\x92AN\xa2{\x91O\xe7g\xb6\xd4k\x1e\xefD\x8a\x06A\xe8\xa15c\xd8\xf8\x00\x00\u07d4\x1a\t\xfd\xc2\u01e2\x0e#WK\x97\u019e\x93\u07bag\xd3r \x89lO\xd1\xee$nx\x00\x00\u07d4\x1a\n\x1d\u07f01\xe5\xc8\xcc\x1dF\xcf\x05\x84-P\xfd\xdcq0\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1a\x1c\x9a&\xe0\xe0$\x18\xa5\xcfh}\xa7Z'\\b,\x94@\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x1a \x1bC'\u03a7\xf3\x99\x04bF\xa3\xc8~n\x03\xa3\u0368\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a$4\xccwD\"\u050dS\u055c]V,\u0384\a\xc9K\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\x1a%\xe1\u017c~_P\xec\x16\xf8\x88_!\x0e\xa1\xb98\x80\x0e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1a&\x94\xec\a\xcf^Mh\xba@\xf3\xe7\xa1LS\xf3\x03\x8cn\x8966\xcd\x06\xe2\xdb:\x80\x00\u07d4\x1a5 E5\x82\xc7\x18\xa2\x1cB7[\xc5\as%RS\xe1\x89*\xd3s\xcef\x8e\x98\x00\x00\xe0\x94\x1a7n\x1b-/Y\ai\xbb\x85\x8dEu2\rN\x14\x99p\x8a\x01\x06q%v9\x1d\x18\x00\x00\u07d4\x1a:3\x0eO\xcbi\xdb\xef^i\x01x;\xf5\x0f\xd1\xc1SB\x89\u3bb5sr@\xa0\x00\x00\u07d4\x1aN\u01a0\xae\u007fZ\x94'\xd2=\xb9rL\r\f\xff\xb2\xab/\x89\t\xb4\x1f\xbf\x9e\n\xec\x00\x00\u07d4\x1aP^b\xa7N\x87\xe5wG>O:\xfa\x16\xbe\xdd<\xfaR\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x1a^\xe53\xac\xbf\xb3\xa2\xd7m[hRw\xb7\x96\xc5j\x05+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1adJP\xcb\u00ae\xe8#\xbd+\xf2C\xe8%\xbeMG\xdf\x02\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\x1apD\xe28?\x87\b0[I[\xd1\x17k\x92\xe7\xef\x04:\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1ay\xc7\xf4\x03\x9cg\xa3\x9du\x13\x88L\xdc\x0e,4\"$\x90\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1a\x89\x89\x9c\xbe\xbd\xbbd\xbb&\xa1\x95\xa6<\bI\x1f\u035e\xee\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1a\x8a\\\xe4\x14\u079c\xd1r\x93~7\xf2\u055c\xffq\xceW\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1a\x95\xa8\xa8\b.FR\xe4\x17\r\xf9'\x1c\xb4\xbbC\x05\xf0\xb2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x1a\x95\u0277Tk]\x17\x86\u00c5\x8f\xb1#dF\xbc\f\xa4\u0389j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1a\x98~?\x83\xdeu\xa4/\x1b\xde|\x99|\x19!{J_$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1a\x9ep/8]\xcd\x10^\x8b\x9f\xa4(\xee\xa2\x1cW\xffR\x8a\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1a\xa1\x02\x1fU\n\xf1X\xc7Gf\x8d\xd1;F1`\xf9Z@\x89O\xb0Y\x1b\x9b08\x00\x00\u07d4\x1a\xa2v\x99\xca\u068d\u00e7oy3\xaaf\xc7\x19\x19\x04\x0e\x88\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x1a\xa4\x02p\xd2\x1e\\\u0786\xb61m\x1a\xc3\xc53IKy\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1a\xb5:\x11\xbc\xc6=\u07ea@\xa0+\x9e\x18d\x96\u037b\x8a\xff\x89l?*\xac\x80\f\x00\x00\x00\u07d4\x1a\xbcN%;\b\n\xebCy\x84\xab\x05\xbc\xa0\x97\x9a\xa4>\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a\xc0\x89\u00fcM\x82\xf0j \x05\x1a\x9ds-\xc0\xe74\xcba\x89%\xf6\x9dc\xa6\xce\x0e\x00\x00\xe0\x94\x1a\xd4V>\xa5xk\xe1\x15\x995\xab\xb0\xf1\u0547\x9c>sr\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1a\xd7- \xa7n\u007f\xcckv@X\xf4\x8dA}Io\xa6\u0349lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1a\xda\xf4\xab\xfa\x86}\xb1\u007f\x99\xafj\xbe\xbfpz<\xf5]\xf6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1a\xf6\x03C6\x0e\v-u%R\x107W \xdf!\xdb\\}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a\xfc\u0145\x89l\xd0\xed\xe1)\xee-\xe5\xc1\x9e\xa8\x11T\vd\x89\xaf*\xba\f\x8e[\xef\x80\x00\u07d4\x1b\x05\xeajj\u022f|\xb6\xa8\xb9\x11\xa8\xcc\xe8\xfe\x1a*\xcf\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\v1\xaf\xffKm\xf3e:\x94\xd7\xc8yx\xae5\xf3J\xae\x89\x139\x10E?\xa9\x84\x00\x00\u07d4\x1b\r\ah\x17\xe8\u058e\xe2\xdfN\x1d\xa1\xc1\x14-\x19\x8cD5\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\x1b\x13\ro\xa5\x1d\\H\xec\x8d\x1dR\u070a\"{\xe8s\\\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b#\u02c6cUHq\xfb\xbe\r\x9e`9~\xfbo\xae\xdc>\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1b&9X\x8bU\xc3D\xb0#\xe8\xde_\xd4\b{\x1f\x04\x03a\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x1b9 \xd0\x01\xc4>r\xb2N|\xa4o\x0f\xd6\xe0\xc2\n_\xf2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1b<\xb8\x1eQ\x01\x1bT\x9dx\xbfr\v\r\x92J\xc7c\xa7\u008av\x95\xa9, \xd6\xfe\x00\x00\x00\u07d4\x1bC#,\xcdH\x80\xd6\xf4o\xa7Q\xa9l\xd8$s1XA\x89\x04V9\x18$O@\x00\x00\u07d4\x1bK\xbc\xb1\x81e!\x1b&[(\a\x16\xcb?\x1f!!v\xe8\x89\x19\x9a\xd3}\x03\xd0`\x80\x00\u07d4\x1bM\a\xac\u04c1\x83\xa6\x1b\xb2x=+{\x17\x8d\xd5\x02\xac\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1bckzIo\x04MsYYn5:\x10F\x16Cok\x89\x13\x88\xea\x95\xc3?\x1d\x00\x00\u07d4\x1bd\x95\x89\x12@\xe6NYD\x93\xc2f!q\xdb^0\xce\x13\x89\tX\x87\u0595\xedX\x00\x00\u07d4\x1bf\x10\xfbh\xba\xd6\xed\x1c\xfa\xa0\xbb\xe3:$\xeb.\x96\xfa\xfb\x89\b=lz\xabc`\x00\x00\u07d4\x1by\x903\xefm\xc7\x12x\"\xf7EB\xbb\"\xdb\xfc\t\xa3\b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x1b~\xd9t\xb6\xe24\u0381$t\x98B\x9a[\u0520\xa2\xd19\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\x82o\xb3\xc0\x12\xb0\xd1Y\u253a[\x8aI\x9f\xf3\xc0\xe0<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\x8a\xa0\x16\f\u05df\x00_\x88Q\nqI\x13\xd7\n\u04fe3\x89\n\xef\xfb\x83\a\x9a\xd0\x00\x00\xe0\x94\x1b\x8b\xd6\xd2\xec\xa2\x01\x85\xa7\x8e}\x98\xe8\xe1\x85g\x8d\xacH0\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\x1b\x9b-\u0096\x0eL\xb9@\x8ft\x05\x82|\x9bY\a\x16\x12\xfd\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1b\xa9\"\x8d8\x87'\xf3\x89\x15\x0e\xa0;s\xc8-\xe8\xeb.\t\x8a\x01\x89t\xfb\xe1w\xc9(\x00\x00\u07d4\x1b\xa9\xf7\x99~S\x87\xb6\xb2\xaa\x015\xac$R\xfe6\xb4\xc2\r\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\x1b\xba\x03\xffkJ\u057f\x18\x18J\xcb!\xb1\x88\xa3\x99\xe9\xebJ\x89a\t=|,m8\x00\x00\u07d4\x1b\xbc\x19\x9eXg\x90\xbe\x87\xaf\xed\xc8I\xc0G&t\\]{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1b\xbc`\xbc\xc8\x0e\\\xdc5\xc5Aj\x1f\n@\xa8=\xae\x86{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xc4L\x87a#\x1b\xa1\xf1\x1f_\xaa@\xfaf\x9a\x01>\x12\u0389\v\tR\xc4Z\xea\xad\x00\x00\u07d4\x1b\xcf4A\xa8f\xbd\xbe\x960\t\xce3\xc8\x1c\xbb\x02a\xb0,\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\x1b\u048c\xd5\u01ca\xeeQ5|\x95\xc1\xef\x925\xe7\xc1\x8b\xc8T\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xd8\xeb\xaavt\xbb\x18\u1458\xdb$OW\x03\x13\a_C\x89\b!\xab\rD\x14\x98\x00\x00\u07d4\x1b\xd9\t\xac\rJ\x11\x02\xec\x98\xdc\xf2\u0329j\n\xdc\u05e9Q\x89\x01\x16Q\xac>zu\x80\x00\u07d4\x1b\xe3T,6\x13hte\xf1Zp\xae\xeb\x81f+e\u0328\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xeaM\xf5\x12/\xaf\u07b3`~\xdd\xda\x1e\xa4\xff\u06da\xbf*\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d4\x1b\xecM\x02\u0385\xfcH\xfe\xb6$\x89\x84\x1d\x85\xb1pXj\x9b\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\x1b\xf9t\u0650OE\u0381\xa8E\xe1\x1e\xf4\xcb\xcf'\xafq\x9e\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x1c\x04VI\xcdS\xdc#T\x1f\x8e\xd4\xd3A\x81(\b\xd5\u075c\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x1c\x12\x8b\xd6\u0365\xfc\xa2uu\xe4\xb4;2S\xc8\xc4\x17*\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\x13\u04c67\xb9\xa4|\xe7\x9d7\xa8oP\xfb@\x9c\x06\a(\x89Hz\x9a0E9D\x00\x00\u07d4\x1c \x10\xbdf-\xf4\x17\xf2\xa2q\x87\x9a\xfb\x13\xefL\x88\xa3\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x1c%z\u0525Q\x05\xea;X\xed7K\x19\x8d\xa2f\xc8_c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x1c.6\a\xe1'\xca\xca\x0f\xbd\\YH\xad\xad}\xd80\xb2\x85\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x1c5l\xfd\xb9_\xeb\xb7\x14c;(\xd5\xc12\u0744\xa9\xb46\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d4\x1c5\xaa\xb6\x88\xa0\u034e\xf8.vT\x1b\xa7\xac9R\u007ft;\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x1c>\xf0]\xae\x9d\xcb\u0509\xf3\x02D\bf\x9d\xe2D\xc5*\x02\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x1cJ\xf0\xe8c\xd2el\x865\xbco\xfe\xc8\u0759(\x90\x8c\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c`\x19\x93x\x92\a\xf9e\xbb\x86\\\xbbL\xd6W\xcc\xe7o\xc0\x89\x05T\x1ap7P?\x00\x00\u07d4\x1cc\xfa\x9e,\xbb\xf2<I\xfc\xde\xf1\u02eb\xfen\r\x1e\x14\xc1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1cg\x02\xb3\xb0ZQ\x14\xbd\xbc\xae\xca%S\x1a\xee\xb3H5\xf4\x8a\x05\x85V\xbe\xadE\u072e\x00\x00\u07d4\x1ch\xa6a8x:c\u024c\xc6u\xa9\xecw\xafE\x98\xd3^\x89\x02\xb7F\xf4\x8f\x0f\x12\x00\x00\u07d4\x1cs\xd0\vn%\xd8\xeb\x9c\x1f\xf4\xad\x82{k\x9e\x9c\xf6\xd2\f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1cu\x1e\u007f$\u07dd\x94\xa67\xa5\xde\xde\xff\u0142w\xb5\xdb\x19\x89\xae\x8ez\v\xb5u\xd0\x00\x00\xe0\x94\x1c|\xb2\xfek\xf3\xe0\x9c\xbc\xdc\x18z\xf3\x8f\xa8\xf5\x05:p\xb6\x8a\x02\x1c\x84\xf7B\xd0\u03ad\x80\x00\xe0\x94\x1c\x89\x06\x0f\x98|Q\x8f\xa0y\xec,\n^\xbf\xa3\x0f] \xf7\x8a\b\v\xfb\xef\xcb_\v\xc0\x00\x00\u07d4\x1c\x94\xd66\xe6\x84\xeb\x15X\x95\xcem\xb4\xa2X\x8f\xba\x1d\x00\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\x99\xfe\x9b\xb6\xc6\xd1\x06m\x91 \x99T\u007f\xd1\U001007ac\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xb4P\x92\x00x\xaa\xb21|}\xb3\xb3\x8a\xf7\xdd)\x8b-A\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x1c\xb5\xf3;MH\x896\xd1>1a\xda3\xa1\xda}\xf7\r\x1b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1c\xb6\xb2\xd7\xcf\xc5Y\xb7\xf4\x1eoV\xab\x95\xc7\xc9X\xcd\x0eL\x89Hz\x9a0E9D\x00\x00\u07d4\x1c\xc1\xd3\xc1O\x0f\xb8d\x0e6rM\xc42)\xd2\xeaz\x1eH\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x1c\xc9\bv\x00A\t\xcdy\xa3\u07a8f\u02c4\n\xc3d\xba\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xd1\xf0\xa3\x14\u02f2\x00\xde\n\f\xb1\xef\x97\xe9 p\x9d\x97\u0089lk\x93[\x8b\xbd@\x00\x00\u0794\x1c\xdaA\x1b\xd5\x16;\xae\xca\x1eU\x85c`\x1c\xe7 \xe2N\xe1\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1c\xe8\x1d1\xa7\x920\"\xe1%\xbfH\xa3\xe06\x93\xb9\x8d\xc9\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xeb\xf0\x98]\u007fh\n\xaa\x91\\D\xccb\xed\xb4\x9e\xab&\x9e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1c\xedg\x15\xf8b\xb1\xff\x86\x05\x82\x01\xfc\xceP\x82\xb3nb\xb2\x8a\x01j^`\xbe\xe2s\xb1\x00\x00\u07d4\x1c\xf0L\xb1C\x80\x05\x9e\xfd?#\x8be\u057e\xb8j\xfa\x14\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1c\xf1\x05\xab#\x02;ULX>\x86\u05d2\x11y\xee\x83\x16\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1c\xf2\xebz\x8c\xca\u00ad\xea\xef\x0e\xe8sG\xd55\u04f9@X\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xfc\xf7Q\u007f\f\bE\x97 \x94+dz\u0452\xaa\x9c\x88(\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\x1d\t\xad$\x12i\x1c\u0141\xc1\xab6\xb6\xf9CL\xd4\xf0\x8bT\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x1d\x15|Xv\xc5\xca\xd5S\xc9\x12\xca\xf6\xce-Rw\xe0\\s\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1d&\x15\xf8\xb6\xcaP\x12\xb6c\xbd\u0414\xb0\xc5\x13|w\x8d\u07ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1d)\u01ea\xb4+ H\u04b2R%\u0518\u06e6z\x03\xfb\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0794\x1d4\x1f\xa5\xa3\xa1\xbd\x05\x1f}\xb8\a\xb6\xdb/\u01faO\x9bE\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1d4N\x96%g\xcb'\xe4M\xb9\xf2\xfa\u01f6\x8d\xf1\xc1\xe6\xf7\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x1d6h0c\xb7\xe9\xeb\x99F-\xab\xd5i\xbd\xdc\xe7\x16\x86\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1d7aky?\x94\x91\x188\xac\x8e\x19\xee\x94I\u07d2\x1e\u0109QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94\x1d9[0\xad\xda\x1c\xf2\x1f\t\x1aOJ{u3q\x18\x94A\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x1dEXn\xb8\x03\xca!\x90e\v\xf7H\xa2\xb1t1+\xb5\a\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1dW.\xdd-\x87\xca'\x1ag\x14\xc1Z;7v\x1d\u0320\x05\x89\x06\xeb\xd5*\x8d\xdd9\x00\x00\u07d4\x1dc0\x97\xa8R%\xa1\xffC!\xb1)\x88\xfd\xd5\\+8D\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x1di\xc8=(\xff\x04t\xce\xeb\xea\xcb:\xd2'\xa1D\xec\u78ca\x01(\xcc\x03\x92\nb\u0480\x00\u07d4\x1d\x96\xbc\u0544W\xbb\xf1\xd3\u00a4o\xfa\xf1m\xbf}\x83hY\x89\tIr\t\xd8F~\x80\x00\u07d4\x1d\x9ej\xaf\x80\x19\xa0_#\x0e]\xef\x05\xaf]\x88\x9b\xd4\xd0\xf2\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x1d\xab\x17.\xff\xa6\xfb\xeeSL\x94\xb1~yN\xda\xc5OU\xf8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1d\xb9\xac\x9a\x9e\xae\xec\nR7W\x05\fq\xf4rx\xc7-P\x89Hz\x9a0E9D\x00\x00\u07d4\x1d\xbe\x8e\x1c+\x8a\x00\x9f\x85\xf1\xad<\xe8\r.\x055\x0e\u3709\aW\rn\x9e\xbb\xe4\x00\x00\u07d4\x1d\xc7\xf7\xda\xd8]\xf5?\x12q\x15$\x03\xf4\xe1\xe4\xfd\xb3\xaf\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1d\u03bc\xb7em\xf5\u072a3h\xa0U\xd2/\x9e\xd6\xcd\xd9@\x89\x1b\x18\x1eK\xf24<\x00\x00\xe0\x94\x1d\xd7tA\x84J\xfe\x9c\xc1\x8f\x15\xd8\xc7{\xcc\xfbe^\xe04\x8a\x01\x06\xebEW\x99D\x88\x00\x00\u07d4\x1d\xde\xfe\xfd5\xab\x8fe\x8b$q\xe5G\x90\xbc\x17\xaf\x98\u07a4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1d\xee\xc0\x1a\xbe\\\r\x95-\xe9\x10l=\xc3\x069\xd8P\x05\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\x1d\xf6\x91\x16rg\x9b\xb0\xef5\t\x03\x8c\f'\xe3\x94\xfd\xfe0\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\x1d\xfa\xee\ar\x12\xf1\xbe\xaf\x0eo/\x18@Sz\xe1T\xad\x86\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1e\x06\r\xc6\xc5\xf1\u02cc\xc7\xe1E.\x02\xee\x16u\b\xb5eB\x8a\x02\xb1O\x02\xc8d\xc7~\x00\x00\xe0\x94\x1e\x13\xecQ\x14,\ubde2`\x83A,<\xe3QD\xbaV\xa1\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x1e\x1aH(\x11\x9b\xe3\t\xbd\x88#nMH+PM\xc5W\x11\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\x1e\x1a\ud178leb\u02cf\xa1\xebo\x8f;\xc9\u072eny\x89\xf4\xd2\u0744%\x9b$\x00\x00\u07d4\x1e\x1ccQwj\xc3\x10\x919~\xcf\x16\x00-\x97\x9a\x1b-Q\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1e\x1dz_$h\xb9N\xa8&\x98-\xbf!%y<nJZ\x8964\xf4\x84\x17@\x1a\x00\x00\u07d4\x1e!\x0epG\x88m\xaaR\xaa\xf7\x0fK\x99\x1d\xach\xe3\x02^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1e+\xf4\xba\x8e^\xf1\x8d7\xdemj\xd66\xc4\xca\xe4\x89\xd0\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e/\xe4\xe4\xa7}\x14\x1f\xf4\x9a\f\u007f\xbc\x95\xb0\xa2\xb2\x83\xee\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e3\xd1\xc2\xfb^\bO/\x1dT\xbcRgr\u007f\xec?\x98]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x1e8\x1a\xdc\xf8\x01\xa3\xbf\x9f\u05ff\xac\x9c\xcc+\x84\x82\xad^f\x89 \x89r\xc0\x01\rt\x00\x00\xe0\x94\x1e;\xad\xb1\xb6\xe18\x0e'\x03\x9cWj\xe6\".\x96:[S\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x1eHM\x06!\xf0\xf53\x1b5\xd5@\x8d\x9a\xaeN\xb1\xac\xf2\x1e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1eX\x00\"}M\xcfu\xe3\x0fU\x95\u017e\xd3\xf7.4\x1e;\x89\ru\xda\xcesA~\x00\x00\u07d4\x1eYj\x81\xb3W\xc6\xf2Ip\xcc1=\xf6\xdb\u06ab\xd0\u041e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1ei\x15\xeb\u0661\x9c\x81\xb6\x92\xad\x99\xb1!\x8aY,\x1a\u01f1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1en\x01S\xfc\x16\x1b\xc0^ek\xbb\x14Lq\x87\xbfO\xe8M\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1epfU\xe2\x84\xdc\xf0\xbb7\xfe\a]a:\x18\xdc\x12\xffJ\x89\xedC\xbf\x1e\ue0ac\x00\x00\xe0\x94\x1ex>R*\xb7\xdf\n\u02ac\x9e\xee\xd3Y09\xe5\xacuy\x8a+\x14F\xddj\xef\xe4\x1c\x00\x00\u07d4\x1e{^M\x1fW+\xec\xf2\xc0\x0f\xc9\f\xb4v{Jn3\u0509\x06\x1f\xc6\x10u\x93\xe1\x00\x00\u07d4\x1e\x8eh\x9b\x02\x91|\xdc)$]\f\x9ch\xb0\x94\xb4\x1a\x9e\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xa34\xb5u\b\a\xeat\xaa\u016b\x86\x94\xec_(\xaaw\u03c9\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\x1e\xa4qU\x04\u01af\x10{\x01\x94\xf4\xf7\xb1\xcbo\xcc\xcdoK\x89 \x041\x97\xe0\xb0'\x00\x00\u07d4\x1e\xa4\x92\xbc\xe1\xad\x10~3\u007fK\u0527\xac\x9a{\xab\xcc\u036b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x1e\xa6\xbf/\x15\xae\x9c\x1d\xbcd\u06a7\xf8\xeaM\r\x81\xaa\xd3\xeb\x89\u3bb5sr@\xa0\x00\x00\u07d4\x1e\xb4\xbfs\x15j\x82\xa0\xa6\x82 \x80\xc6\xed\xf4\x9cF\x9a\xf8\xb9\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\x1e\xba\xcbxD\xfd\xc3\"\xf8\x05\x90O\xbf\x19b\x80-\xb1S|\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1e\xc4\xecKw\xbf\x19\u0411\xa8h\xe6\xf4\x91T\x18\x05A\xf9\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xd0n\xe5\x16b\xa8lcE\x88\xfbb\xdcC\xc8\xf2~|\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1e\u063b?\x06w\x8b\x03\x9e\x99a\xd8\x1c\xb7\x1as\xe6x|\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xda\bNye\x00\xba\x14\xc5\x12\x1c\r\x90\x84of\xe4\xbeb\x89\x1c\xfd\xd7F\x82\x16\xe8\x00\x00\u07d4\x1e\xeel\xbe\xe4\xfe\x96\xadaZ\x9c\xf5\x85zdy@\u07ccx\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4\x1e\xf2\u073f\xe0\xa5\x00A\x1d\x95n\xb8\u0213\x9c=l\xfef\x9d\x89*\x11)\u0413g \x00\x00\xe0\x94\x1e\xf5\xc9\xc76P\u03fb\xde\\\x88U1\xd4'\xc7\xc3\xfeUD\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1f\x04\x12\xbf\xed\u0356N\x83}\t,q\xa5\xfc\xba\xf3\x01&\xe2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\x17O@\xa0Dr4\xe6fS\x91Mu\xbc\x00>V\x90\u0709\b\xacr0H\x9e\x80\x00\x00\u07d4\x1f!\x86\xde\xd2>\f\xf9R\x16\x94\xe4\xe1dY>i\n\x96\x85\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x1f*\xfc\n\xed\x11\xbf\xc7\x1ew\xa9\ae{6\xeav\xe3\xfb\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\x1f9Y\xfc)\x11\x10\xe8\x822\xc3kvg\xfcx\xa3ya?\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1f=\xa6\x8f\xe8~\xafC\xa8)\xabm~\u0166\xe0\t\xb2\x04\xfb\x89\x1e\x16\x01u\x8c,~\x00\x00\u07d4\x1fI\xb8m\r9EY\x06\x98\xa6\xaa\xf1g<7u\\\xa8\r\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\x1f_;4\xbd\x13K'\x81\xaf\xe5\xa0BJ\u0144l\xde\xfd\x11\x89\x05]\xe6\xa7y\xbb\xac\x00\x00\u07d4\x1fo\x0004\x97R\x06\x1c\x96\a+\xc3\xd6\xeb5I \x8dk\x89\x01K\x8d\xe1\xeb\x88\u06c0\x00\u07d4\x1f}\x8e\x86\xd6\xee\xb0%E\xaa\xd9\x0e\x912{\xd3i\xd7\xd2\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\x81\x16\xbd\n\xf5W\x0e\xaf\fV\u011cz\xb5\xe3zX\x04X\x89lk\x93[\x8b\xbd@\x00\x00\u0794\x1f\x88\xf8\xa13\x8f\xc7\xc1\tv\xab\xcd?\xb8\u04c5T\xb5\uc708\xb9\xf6]\x00\xf6<\x00\x00\u07d4\x1f\x9c2hE\x8d\xa3\x01\xa2\xbeZ\xb0\x82W\xf7{\xb5\xa9\x8a\xa4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1f\xa21\x9f\xed\x8c-F*\xdf.\x17\xfe\xecjo0Qn\x95\x89\x06\xca\xe3\x06!\xd4r\x00\x00\u07d4\x1f\xb4c\xa08\x99\x83\xdf}Y?{\xddmxI\u007f\xed\x88y\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\xb7\xbd1\r\x95\xf2\xa6\u067a\xaf\x8a\x8aC\n\x9a\x04E:\x8b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x1f\xcc|\xe6\xa8HX\x95\xa3\x19\x9e\x16H\x1fr\xe1\xf7b\xde\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1f\xcf\xd1\xd5\u007f\x87\"\x90V\f\xb6-`\x0e\x1d\xef\xbe\xfc\xcc\x1c\x89P\xc5\xe7a\xa4D\b\x00\x00\u0794\x1f\u0496\xbe\x03\xads|\x92\xf9\u0186\x9e\x8d\x80\xa7\x1cW\x14\xaa\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x1f\xdd\xd8_\u024b\xe9\xc4\x04Ya\xf4\x0f\x93\x80^\xccEI\xe5\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4 \x01\xbe\xf7{f\xf5\x1e\x15\x99\xb0/\xb1\x10\x19J\x00\x99\xb7\x8d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 \x02d\xa0\x9f\x8ch\xe3\xe6b\x97\x95(\x0fV%O\x86@\u0409\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \x03qy\a\xa7%`\xf40\u007f\x1b\xee\xccT6\xf4=!\xe7\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4 \r\xfc\vq\xe3Y\xb2\xb4eD\n6\xa6\xcd\xc3Rw0\a\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4 \x13L\xbf\xf8\x8b\xfa\xdcFkR\xec\ua9d8W\x89\x1d\x83\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4 \x14&\x1f\x01\b\x9fSyV0\xba\x9d\xd2O\x9a4\xc2\xd9B\x89Hz\x9a0E9D\x00\x00\u07d4 \x16\x89]\xf3,\x8e\xd5G\x82iF\x84#\xae\xa7\xb7\xfb\xceP\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \x18\x1cKA\xf6\xf9r\xb6iX!_\x19\xf5p\xc1]\xdf\xf1\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4 \x18d\xa8\xf7\x84\xc2'{\v|\x9e\xe74\xf7\xb3w\xea\xb6H\x89\xf2(\x14\x00\xd1\xd5\xec\x00\x00\u07d4 \xb8\x1a\xe59&\xac\xe9\xf7\xd7AZ\x05\f\x03\x1dX_ \x89\x12\u007f\x19\xe8>\xb3H\x00\x00\xe0\x94 <b\x83\xf2\r\xf7\xbc\x86T/\u07f4\xe7c\xec\u06fb\xee\xf5\x8a\x05K@\xb1\xf8R\xbd\xa0\x00\x00\xe0\x94 J\u0248g\xa7\xc9\xc7\xedq\x1c\xb8/(\xa8x\xca\xf6\x9bH\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4 R7\u013e\x14o\xba\x99G\x8f:}\xad\x17\xb0\x918\u0695\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 S\xac\x97T\x8a\fN\x8b\x80\xbcrY\f\u05a0\x98\xfeu\x16\x89\n#%u;F\f\x00\x00\u07d4 _Qf\xf1$@\xd8Wb\xc9g\u04ee\x86\x18O\x8fM\x98\x89\x17r$\xaa\x84Lr\x00\x00\xe0\x94 _\xc8C\xe1\x9aI\x13\u0448\x1e\xb6\x9bi\xc0\xfa;\xe5\xc5\v\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4 d\x82\xeeo\x13\x8aw\x8f\xe1\xadb\xb1\x80\u0385o\xbb#\xe6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94 fwM\x82'\x93\xff%\xf1v\t\tG\x9c\xf6$\x91\xbf\x88\x8a\v\xae:\u0185\xcbr\xe0\x00\x00\u07d4 mU\xd5y*QN\xc1\b\xe0\x90Y\x9f*\x06^P\x11\x85\x89\n\xdf0\xbap\u0217\x00\x00\u07d4 p~B]*\x11\xd2\u021f9\x1b+\x80\x9fUlY$!\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94 ~\xf8\v]`\xb6\xfb\xff\xc5\x1f:d\xb8\xc7 6\xa5\xab\xbd\x8a\x01je\x02\xf1Z\x1eT\x00\x00\xe0\x94 \x82K\xa1\xdb\xeb\xbe\xf9\x84n\xf3\xd0\xf6\xc1\xb0\x17\xe6\x91.\u010a\x01\x84\xb2nM\xaf\x1d5\x00\x00\u07d4 \x84\xfc\xe5\x05\xd9{\xeb\xf1\xad\x8c_\xf6\x82o\xc6E7\x1f\xb2\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4 \x8cEs,\n7\x8f\x17\xac\x83$\x92mE\x9b\xa8\xb6X\xb4\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4 \x93w\xb6\xad?\xe1\x01\xc9h[5vT\\k\x16\x84\xe7<\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4 \x9e\x8e)\xd3;\xea\xe8\xfbk\xaax=\x13>\x1d\x9e\xc1\xbc\v\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4 \xa1RV\xd5\f\xe0X\xbf\x0e\xacC\xaaS:\xa1n\u0273\x80\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \xa2\x9cPy\xe2k?\x181\x8b\xb2\xe5\x0e\x8e\x8b4n[\xe8\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4 \xa8\x16\x80\xe4e\xf8\x87\x90\xf0\aO`\xb4\xf3_]\x1ej\xa5\x89Ea\x80'\x8f\fw\x80\x00\u07d4 \xb9\xa9\u6f48\x80\u0659J\xe0\r\u0439(*\v\xea\xb8\x16\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4 \u0084\xba\x10\xa2\b0\xfc=i\x9e\xc9}-\xfa'\xe1\xb9^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 \xd1A\u007f\x99\xc5i\u3fb0\x95\x85e0\xfe\x12\xd0\xfc\uaa89@\x15\xf9K\x11\x83i\x80\x00\u07d4 \u074f\u02f4n\xa4o\u3066\x8b\x8c\xa0\xea[\xe2\x1f\u9949lk\x93[\x8b\xbd@\x00\x00\xe0\x94 \xff>\u078c\xad\xb5\xc3{H\xcb\x14X\x0f\xb6^#\t\n{\x8a\b\xe4\xd3\x16\x82v\x86@\x00\x00\xe0\x94!\x008\x1d`\xa5\xb5J\xdc\t\u0456\x83\xa8\xf6\u057bK\xfb\u02ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94!\x18\xc1\x16\xab\f\xdfo\xd1\x1dT\xa40\x93\a\xb4w\xc3\xfc\x0f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94!\x1b)\xce\xfcy\xae\x97gD\xfd\xeb\u03bd<\xbb2\xc5\x13\x03\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4! l\xe2.\xa4\x80\xe8Y@\xd3\x13\x14\xe0\xd6ONM:\x04\x8965\u026d\xc5\u07a0\x00\x00\u07d4!2\xc0Qj.\x17\x17J\xc5G\xc4;{\x00 \xd1\xebLY\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94!@\x8bMz,\x0en\xcaAC\xf2\xca\u037b\u033a\x12\x1b\u060a\x04<3\xc1\x93ud\x80\x00\x00\u07d4!Kt9U\xa5\x12\xden\r\x88j\x8c\xbd\x02\x82\xbe\xe6\u04a2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!L\x89\u017d\x8e}\"\xbcWK\xb3^H\x95\x02\x11\xc6\xf7v\x89\x01\x06T\xf2X\xfd5\x80\x00\xe0\x94!Ti\x14\xdf\u04ef*\xddA\xb0\xff>\x83\xff\xdat\x14\xe1\xe0\x8a\x01C\x95\xe78ZP.\x00\x00\u07d4!X.\x99\xe5\x02\xcb\xf3\xd3\xc2;\xdf\xfbv\xe9\x01\xacmV\xb2\x89\x05k\xc7^-c\x10\x00\x00\u07d4!Y$\b\x13\xa70\x95\xa7\xeb\xf7\u00f3t>\x80(\xae_\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!`\xb4\xc0,\xac\n\x81\u0791\b\xdeCE\x90\xa8\xbf\xe6\x875\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94!nA\x86N\xf9\x8f\x06\r\xa0\x8e\xca\xe1\x9a\xd1\x16j\x17\xd06\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4!\x84o/\xdfZA\xed\x8d\xf3n^\xd8TM\xf7Y\x88\xec\xe3\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94!\xa6\xdbe'F{\xc6\xda\xd5K\xc1n\x9f\xe2\x95;g\x94\xed\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4!\xa6\xfe\xb6\xab\x11\xc7f\xfd\xd9w\xf8\xdfA!\x15_G\xa1\xc0\x89\x03\x19\xcf8\xf1\x00X\x00\x00\u07d4!\xb1\x82\xf2\xda+8D\x93\xcf_5\xf8=\x9d\x1e\xe1O*!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!\xbf\xe1\xb4\\\xac\xdebt\xfd\x86\b\u0661x\xbf>\xebn\u0709l\xee\x06\u077e\x15\xec\x00\x00\u07d4!\xc0s\x80HOl\xbc\x87$\xad2\xbc\x86L;Z\xd5\x00\xb7\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94!\u00e8\xbb\xa2g\xc8\u0322{\x1a\x9a\xfa\xba\xd8o`z\xf7\b\x8a\x01\xe4\xa3lI\u06580\x00\x00\u07d4!\xcem[\x90\x18\xce\xc0J\u0596yD\xbe\xa3\x9e\x800\xb6\xb8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4!\xd0'\x05\xf3\xf6I\x05\xd8\x0e\xd9\x14y\x13\xea\x8cs\a\u0595\x89I\xed\xb1\xc0\x98\x876\x00\x00\u07d4!\xd1?\f@$\xe9g\xd9G\a\x91\xb5\x0f\"\xde:\xfe\xcf\x1b\x89\xf1Z\xd3^.1\xe5\x00\x00\xe0\x94!\xdb\u06c1z\r\x84\x04\u01bd\xd6\x15\x047N\x9cC\xc9!\x0e\x8a\x02\x1e\x18\xb9\xe9\xabE\xe4\x80\x00\xe0\x94!\xdf\x1e\xc2KNK\xfey\xb0\xc0\x95\u03ba\xe1\x98\xf2\x91\xfb\u044a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94!\xdf-\u036ft\xb2\xbf\x804\x04\xddM\xe6\xa3^\xab\xec\x1b\xbd\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4!\xe2\x19\u021c\xa8\xac\x14\xaeL\xbaa0\xee\xb7}\x9em9b\x89*\u035f\xaa\xa08\xee\x00\x00\u07d4!\xe5\u04ba\xe9\x95\xcc\xfd\b\xa5\xc1k\xb5$\xe1\xf60D\x8f\x82\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4!\xe5\xd7s 0L \x1c\x1eS\xb2a\xa1#\u0421\x06>\x81\x89\x04\xb6\xfa\x9d3\xddF\x00\x00\xe0\x94!\xea\xe6\xfe\xff\xa9\xfb\xf4\u0347OG9\xac\xe50\u033eY7\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4!\xec\xb2\u07e6Wy\xc7Y-\x04\x1c\xd2\x10Z\x81\xf4\xfdNF\x8965\u026d\xc5\u07a0\x00\x00\u07d4!\uff20\x9b5\x80\xb9\x8es\xf5\xb2\xf7\xf4\xdc\v\xf0,R\x9c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!\xfd\v\xad\xe5\xf4\xeftt\xd0X\xb7\xf3\xd8T\xcb\x13\x00RN\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94!\xfdG\xc5%`\x12\x19\x8f\xa5\xab\xf11\xc0mj\xa1\x96_u\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4!\xfdl]\x97\xf9\xc6\x00\xb7h!\xdd\xd4\xe7v5\x0f\xce+\xe0\x89lj\u04c2\xd4\xfba\x00\x00\u07d4\"\r\u018d\xf0\x19\xb6\xb0\u033f\xfbxKZZ\xb4\xb1]@`\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\"\x0e+\x92\xc0\xf6\xc9\x02\xb5\x13\xd9\xf1\xe6\xfa\xb6\xa8\xb0\xde\xf3\u05c9+^:\xf1k\x18\x80\x00\x00\u07d4\"V\x1cY1\x14560\x9c\x17\xe82X{b\\9\v\x9a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\"W\xfc\xa1jn\\*d|<)\xf3l\xe2)\xab\x93\xb1~\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\"]5\xfa\xed\xb3\x91\u01fc-\xb7\xfa\x90q\x16\x04\x05\x99m\x00\x89\t\x18T\xfc\x18bc\x00\x00\u07d4\"_\x9e\xb3\xfbo\xf3\xe9\xe3\xc8D~\x14\xa6n\x8dO7y\xf6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\"r\x18n\xf2}\xcb\xe2\xf5\xfc70P\xfd\xae\u007f*\xce#\x16\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4\"s\xba\u05fcNHv\"\xd1u\xefzf\x98\x8bj\x93\xc4\xee\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\"v&K\xec\x85&\xc0\xc0\xf2pgz\xba\xf4\xf0\xe4A\xe1g\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\"\x82B\xf83n\xec\xd8$.\x1f\x00\x0fA\x93~q\xdf\xfb\xbf\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\"\x84*\xb80\xdaP\x99\x13\xf8\x1d\xd1\xf0O\x10\xaf\x9e\xdd\x1cU\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\"\x94O\xbc\xa9\xb5yc\bN\xb8M\xf7\xc8_\xb9\xbc\u07f8V\x89\xfc\x11\x8f\uf43a8\x80\x00\u07d4\"\x9c\xc4q\x1bbu^\xa2\x96DZ\u00f7\u007f\xc63\x82\x1c\xf2\x89\x02#\xe8\xb0R\x192\x80\x00\u0794\"\x9eC\r\xe2\xb7OD&Q\xdd\u0377\x01v\xbc\x05L\xadT\x88\xbb\xf9\x81\xbcJ\xaa\x80\x00\u07d4\"\x9fO\x1a*OT\atP[G\a\xa8\x1d\xe4D\x10%[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\"\x9f\xf8\v\xf5p\x80\t\xa9\xf79\xe0\xf8\xb5`\x91@\x16\u0566\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4\"\xa2X\x12\xabV\xdc\xc4#\x17^\xd1\u062d\xac\xce3\xcd\x18\x10\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94\"\xb9j\xb2\xca\xd5]\xb1\x00\xb50\x01\xf9\xe4\xdb7\x81\x04\xc8\a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\"\xbd\xff\xc2@\xa8\x8f\xf7C\x1a\xf3\xbf\xf5\x0e\x14\xda7\xd5\x18>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\"\xce4\x91Y\xee\xb1D\xef\x06\xff&6X\x8a\xefy\xf6(2\x89\n1\x06+\xee\xedp\x00\x00\u07d4\"\xdbU\x9f,<\x14u\xa2\xe6\xff\xe8:YyY\x91\x96\xa7\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\"\xe1QX\xb5\xee>\x86\xeb\x032\xe3\u6a6cl\u0675^\u0349\b\xacr0H\x9e\x80\x00\x00\u07d4\"\xe2H\x8e-\xa2jI\xae\x84\xc0\x1b\xd5K!\xf2\x94x\x91\u0189]\u0212\xaa\x111\xc8\x00\x00\u07d4\"\xe5\x12\x14\x9a\x18\xd3i\xb7<q\xef\xa4>\x86\xc9\xed\xab\xaf\x1d\x89N\xe0.g\x14a\\\x00\x00\u07d4\"\xeb}\xb0\xbaV\xb0\xf8\xb8\x16\u0332\x06\xe6\x15\xd9)\x18[\r\x89\x04])s~\"\xf2\x00\x00\u07d4\"\xee\xd3'\xf8\xeb\x1d\x138\xa3\xcb{\x0f\x8aK\xaaY\a\u0355\x89\x01E]_Hw\b\x80\x00\xe0\x94\"\xf0\x04\u07cd\xe9\xe6\xeb\xf5#\u032c\xe4W\xac\xcb&\xf9r\x81\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794\"\xf2\xdc\xffZ\u05cc>\xb6\x85\v\\\xb9Q\x12{e\x95\"\u623e -j\x0e\xda\x00\x00\u07d4\"\xf3\xc7y\xddy\x02>\xa9*x\xb6\\\x1a\x17\x80\xf6-\\J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\"\xfe\x88M\x907)\x1bMR\xe6(Z\xe6\x8d\xea\v\xe9\xff\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\x06\u07d3\x1a\x94\rX\xc0\x16e\xfaM\b\x00\x80,\x02\xed\xfe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94#\t\xd3@\x91D[22Y\v\xd7\x0fO\x10\x02[,\x95\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4#\x12\x00F\xf6\x83!\x02\xa7R\xa7fVi\x1c\x86>\x17\u5709\x11\xe0\xe4\xf8\xa5\v\xd4\x00\x00\u07d4#\x1a\x15\xac\xc1\x99\u021f\xa9\xcb\"D\x1c\xc7\x030\xbd\xcc\xe6\x17\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4#\x1d\x94\x15]\xbc\xfe*\x93\xa3\x19\xb6\x17\x1fc\xb2\v\u04b6\xfa\x89\xcf\x14{\xb9\x06\xe2\xf8\x00\x00\u07d4#(2\xcdYw\xe0\nL0\xd0\x16?.$\xf0\x88\xa6\xcb\t\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4#,m\x03\xb5\xb6\xe6q\x1e\xff\xf1\x90\xe4\x9c(\xee\xf3l\x82\xb0\x89Hz\x9a0E9D\x00\x00\xe0\x94#,\xb1\xcdI\x99<\x14J?\x88\xb3a\x1e#5i\xa8k\u058a\x03L`lB\u042c`\x00\x00\u07d4#,\xe7\x82Pb%\xfd\x98`\xa2\xed\xc1Jz0Gsm\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4#/R]U\x85\x9b}N`\x8d H\u007f\xaa\xdb\x00)15\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94#4\u0150\u01e4\x87i\x100E\u0176SL\x8a4i\xf4J\x8a\x03\xb1\x99\a=\xf7-\xc0\x00\x00\u07d4#7n\u02bftl\xe53!\xcfB\xc8fI\xb9+g\xb2\xff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#7\x8fB\x92m\x01\x84\xb7\x93\xb0\xc8'\xa6\xdd>=3O\u0349\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4#8B\xb1\xd0i/\xd1\x11@\xcfZ\u0364\xbf\x960\xba\xe5\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#9\xe9I(p\xaf\xea%7\xf3\x89\xac/\x83\x83\x02\xa3<\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#;\xdd\xdd]\xa9HR\xf4\xad\xe8\xd2\x12\x88V\x82\xd9\ak\u0189\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#OF\xba\xb7?\xe4]1\xbf\x87\xf0\xa1\xe0Fa\x99\xf2\ubb09\x1aJ\xba\"\\ t\x00\x00\u07d4#U\x1fV\x97_\xe9+1\xfaF\x9cI\xeaf\xeefb\xf4\x1e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4#V\x95B\xc9}V`\x18\xc9\a\xac\xfc\xf3\x91\xd1@g\xe8~\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94#_\xa6l\x02^\xf5T\x00p\xeb\xcf\r7-\x81w\xc4g\xab\x8a\a\x12\x9e\x1c\xdf7>\xe0\x00\x00\xe0\x94#r\xc4\xc1\u0253\x9fz\xafl\xfa\xc0@\x90\xf0\x04t\x84\n\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4#s\f5z\x91\x02nD\xb1\xd0\xe2\xfc*Q\xd0q\xd8\xd7{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#v\xad\xa9\x033\xb1\u0441\bL\x97\xe6E\xe8\x10\xaa[v\xf1\x89(\xa8WBTf\xf8\x00\x00\u07d4#x\xfdC\x82Q\x1e\x96\x8e\u0452\x10g7\xd3$\xf4T\xb55\x8965\u026d\xc5\u07a0\x00\x00\u07d4#\x82\xa9\u050e\xc8>\xa3e(\x90\xfd\x0e\u7710{[-\xc1\x89\a?u\u0460\x85\xba\x00\x00\u07d4#\x83\xc2\"\xe6~\x96\x91\x90\xd3!\x9e\xf1M\xa3xP\xe2lU\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\x8akv5%/RDHl\n\xf0\xa7: s\x85\xe09\x89JD\x91\xbdm\xcd(\x00\x00\u07d4#\x9as>k\x85Z\u0152\xd6c\x15a\x86\xa8\xa1t\xd2D\x9e\x89X\xbe7X\xb2A\xf6\x00\x00\xe0\x94#\xab\t\xe7?\x87\xaa\x0f;\xe0\x13\x9d\xf0\xc8\xebk\xe5cO\x95\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94#\xab\xd9\xe9>yW\xe5\xb66\xbeey\x05\x1c\x15\xe5\xce\v\x0e\x8a\x03\xa3\xc8\xf7\xcb\xf4,8\x00\x00\u07d4#\xb1\u0111\u007f\xbd\x93\xee=H8\x93\x06\x95s\x84\xa5Il\xbf\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94#\xba8d\xdaX=\xabV\xf4 \x87<7g\x96\x90\xe0/\x00\x8a\x02\x13BR\r_\xec \x00\x00\u07d4#\xc5Z\xebW9\x87o\n\xc8\xd7\xeb\xea\x13\xber\x96\x85\xf0\x00\x89Hz\x9a0E9D\x00\x00\u07d4#\u025b\xa0\x87D\x8e\x19\xc9p\x1d\xf6n\f\xabR6\x831\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xcc\xc3\u01ac\xd8\\.F\fO\xfd\xd8+\xc7]\xc8I\xea\x14\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#\xcd%\x98\xa2\x0e\x14\x9e\xad*\u0593yWn\xce\xdb`\u3389lk\x93[\x8b\xbd@\x00\x00\u07d4#\u07cfH\xee\x00\x92V\xeay~\x1f\xa3i\xbe\xeb\xcfk\xc6c\x89|\xd3\xfa\xc2m\x19\x81\x80\x00\u07d4#\xe2\u01a8\xbe\x8e\n\u03e5\xc4\xdf^6\x05\x8b\xb7\u02ecZ\x81\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xeaf\x9e5d\x81\x9a\x83\xb0\xc2l\x00\xa1m\x9e\x82olF\x89M\x8dl\xa9h\xca\x13\x00\x00\u07d4#\xebo\xd8Vq\xa9\x06:\xb7g\x8e\xbe&Z \xf6\x1a\x02\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xf9\xec\xf3\xe5\xdd\u0723\x88\x15\xd3\xe5\x9e\xd3K[\x90\xb4\xa3S\x89\v\x17\x81\xa3\xf0\xbb \x00\x00\u07d4#\xfa~\xb5\x1aH\"\x95\x98\xf9~v+\xe0\x86\x96R\xdf\xfcf\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94$\x03\x05rs\x13\xd0\x1esT,w_\xf5\x9d\x11\xcd5\xf8\x19\x8a\x01A\x88Vf\x80\u007f\\\x80\x00\u07d4$\x04k\x91\u069ba\xb6)\u02cb\x8e\xc0\xc3Q\xa0~\a\x03\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4$\x0eU\x9e'J\xae\xf0\xc2X\x99\x8c\x97\x9fg\x1d\x11s\xb8\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94$\x13aU\x9f\xee\xf8\x0e\xf170!S\xbd\x9e\xd2\xf2]\xb3\xef\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94$;;\xcaj)\x93Y\xe8\x86\xce3\xa3\x03A\xfa\xfeMW=\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4$<\x84\xd1$ W\f\xc4\xef;\xab\xa1\xc9Y\u0083$\x95 \x89\u007f\x1fi\x93\xa8S\x04\x00\x00\xe0\x94$CJ>2\xe5N\xcf'/\xe3G\v_oQ/gU \x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4$HYo\x91\xc0\x9b\xaa0\xbc\x96\x10j-7\xb5p^](\x89lk\x93[\x8b\xbd@\x00\x00\u0794$Xn\xc5E\x175\xee\xaa\xebG\r\xc8sj\xaeu/\x82\xe5\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4$X\xd6U_\xf9\x8a\x12\x9c\xce@7\x95=\x00 n\xffB\x87\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4$b\x91\x16[Y3-\xf5\xf1\x8c\xe5\u0248V\xfa\xe9X\x97\u0589\\(=A\x03\x94\x10\x00\x00\u07d4$g\u01a5\u0196\xed\xe9\xa1\xe5B\xbf\x1a\xd0k\xccK\x06\xac\xa0\x89\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4$v\xb2\xbbu\x1c\xe7H\xe1\xa4\xc4\xff{#\v\xe0\xc1]\"E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4$z\n\x11\xc5\u007f\x03\x83\xb9I\xdeT\vf\xde\xe6\x86\x04\xb0\xa1\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4$\x87\xc3\u013e\x86\xa2r=\x91|\x06\xb4XU\x01p\xc3\xed\xba\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\x89\xac\x12i4\xd4\u05a9M\xf0\x87C\xda{v\x91\xe9y\x8e\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\x9d\xb2\x9d\xbc\x19\xd1#]\xa7)\x8a\x04\b\x1c1WB\u9b09a\xac\xff\x81\xa7\x8a\xd4\x00\x00\u07d4$\xa4\xeb6\xa7\xe4\x98\xc3o\x99\x97\\\x1a\x8dr\x9f\u05b3\x05\u05c9\r\xfcx!\x0e\xb2\xc8\x00\x00\u07d4$\xa7P\xea\xe5\x87G\x11\x11m\xd7\xd4{q\x86\u0399\r1\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4$\xaa\x11Q\xbbv_\xa3\xa8\x9c\xa5\x0e\xb6\xe1\xb1\xc7\x06A\u007f\u0509\xa8\r$g~\xfe\xf0\x00\x00\u0794$\xac\xa0\x8d[\xe8^\xbb\x9f12\xdf\xc1\xb6 \x82N\xdf\xed\xf9\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4$\xb2\xbe\x11\x8b\x16\u0632\x17Gi\xd1{L\xf8O\a\u0294m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4$\xb8\xb4F\u07bd\x19G\x95]\u0404\xf2\xc5D\x933F\u04ed\x89\xeaim\x90@9\xbd\x80\x00\u07d4$\xb9^\xbe\xf7\x95\x00\xba\xa0\xed\xa7.w\xf8wA]\xf7\\3\x891T\xc9r\x9d\x05x\x00\x00\u07d4$\xb9\xe6dOk\xa4\xcd\xe1&'\r\x81\xf6\xab`\xf2\x86\xdf\xf4\x89\a?u\u0460\x85\xba\x00\x00\u07d4$\xbdY\x04\x05\x90\x91\xd2\xf9\xe1-j&\xa0\x10\xca\"\xab\x14\xe8\x89e\xea=\xb7UF`\x00\x00\u07d4$\xc0\u020bT\xa3TG\t\x82\x8a\xb4\xab\x06\x84\x05Y\xf6\xc5\u2250\xf54`\x8ar\x88\x00\x00\u07d4$\xc1\x17\xd1\u04b3\xa9z\xb1\x1aFy\u025awJ\x9e\xad\xe8\u044965\u026d\xc5\u07a0\x00\x00\u07d4$\xcf\xf0\xe93j\x9f\x80\xf9\xb1\u02d6\x8c\xafk\x1d\x1cI2\xa4\x89\n\xdaUGK\x814\x00\x00\u07d4$\u06aa\xdd\xf7\xb0k\xbc\ua6c0Y\x00\x85\xa8\x85gh+N\x89\x11K \x15\u04bb\xd0\x00\x00\u07d4$\xdc\xc2K\xd9\xc7!\f\xea\u03f3\r\xa9\x8a\xe0JM{\x8a\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\xf7E\r\xdb\xf1\x8b\x02\x0f\xeb\x1a 2\xd9\xd5Kc>\xdf7\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4$\xfcs\xd2\a\x93\t\x8e\t\u076bW\x98Pb$\xfa\x1e\x18P\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4$\xfd\x9al\x87L/\xab?\xf3n\x9a\xfb\xf8\xce\r2\xc7\u0792\x89Hz\x9a0E9D\x00\x00\u07d4%\n@\xce\xf3 #\x97\xf2@F\x95H\xbe\xb5bj\xf4\xf2<\x89\x05\x03\xb2\x03\xe9\xfb\xa2\x00\x00\u07d4%\niC\av\xf64w\x03\xf9R\x97\x83\x95Za\x97\xb6\x82\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4%\x0e\xb7\xc6o\x86\x9d\xdfI\u0685\xf39>\x98\f\x02\x9a\xa44\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%\x10j\xb6u]\xf8mkc\xa1\x87p;\f\xfe\xa0\u5520\x89\x01|@Z\xd4\x1d\xb4\x00\x00\xe0\x94%\x18_2Z\xcf-dP\x06\x98\xf6\\v\x9d\xdfh0\x16\x02\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4%\x1c\x12r,hy\"y\x92\xa3\x04\xeb5v\xcd\x18CN\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\x1eh8\xf7\xce\u0173\x83\xc1\xd9\x01F4\x12t\xda\xf8\xe5\x02\x89\a\xff\x1c\xcbua\xdf\x00\x00\u07d4%%\x9d\x97Z!\xd8:\xe3\x0e3\xf8\x00\xf5?7\u07e0\x198\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4%({\x81_\\\x828\ns\xb0\xb1?\xba\xf9\x82\xbe$\xc4\u04c9\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94%+eU\xaf\u0700\xf2\xd9m\x97-\x17\u06c4\xeaZ\xd5!\xac\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4%8S)6\x81<\x91\xe6S(O\x01|\x80\u00f8\xf8\xa3o\x89l\x87T\xc8\xf3\f\b\x00\x00\xe0\x94%>2\xb7N\xa4I\n\xb9&\x06\xfd\xa0\xaa%{\xf2=\u02cb\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94%?\x1et*,\uc1b0\u05f3\x06\xe5\xea\xcbl\xcb/\x85T\x8a\x04>^\xde\x1f\x87\x8c \x00\x00\u07d4%A1J\v@\x8e\x95\xa6\x94DIwq*Pq5\x91\xab\x89X\x9e\x1a]\xf4\u05f5\x00\x00\u07d4%L\x1e\xccc\f(w\u0780\x95\xf0\xa8\u06e1\xe8\xbf\x1fU\f\x89\\(=A\x03\x94\x10\x00\x00\u07d4%Z\xbc\x8d\b\xa0\x96\xa8\x8f=j\xb5_\xbcsR\xbd\u0739\u0389\x04t6\x821>\u0780\x00\u07d4%[\xdddt\u0302b\xf2j\"\u00cfE\x94\x0e\x1c\ue99b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%`\xb0\x9b\x89\xa4\xaehI\xedZ<\x99XBf1qDf\x89\\(=A\x03\x94\x10\x00\x00\u07d4%a\xa18\xdc\xf8;\xd8\x13\xe0\xe7\xf1\bd+\xe3\xde=o\x05\x8964\xf4\x84\x17@\x1a\x00\x00\u0794%a\xec\x0f7\x92\x18\xfe^\xd4\xe0(\xa3\xf7D\xaaAuLr\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u0794%b\x92\xa1\x91\xbd\xda4\xc4\xdakk\u0591G\xbfu\u2a6b\x88\xc2\xff.\r\xfb\x03\x80\x00\u07d4%i~\xf2\f\u032ap\xd3-7o\x82r\xd9\xc1\a\f=x\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4%o\xa1P\u0307\xb5\x05j\a\xd0\x04\xef\xc8E$s\x9eb\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4%r\x1c\x87\xb0\xdc!7|r\x00\xe5$\xb1J\"\xf0\xafi\xfb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%\x899\xbb\xf0\f\x9d\xe9\xafS8\xf5\xd7\x14\xab\xf6\xd0\xc1\xc6q\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94%\x90\x12hp\xe0\xbd\xe8\xa6c\xab\x04\nr\xa5W=\x8dA\u008a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4%\x9e\xc4\xd2e\xf3\xabSk|p\xfa\x97\xac\xa1Bi,\x13\xfc\x89\x01\x1b\x1b[\xea\x89\xf8\x00\x00\xe0\x94%\xa5\x00\xee\xeczf*\x84\x15R\xb5\x16\x8bp{\r\xe2\x1e\x9e\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\xe0\x94%\xa5\xa4M8\xa2\xf4Lj\x9d\xb9\u037ck\x1e.\x97\xab\xb5\t\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4%\xa7L*\xc7]\u023a\xa8\xb3\x1a\x9c|\xb4\xb7\x82\x9b$V\u0689lk\x93[\x8b\xbd@\x00\x00\xe0\x94%\xad\xb8\xf9o9I,\x9b\xb4|^\u0708bNF\aV\x97\x8a\x05\xa9\x94\v\xc5hyP\x00\x00\u07d4%\xae\xe6\x8d\t\xaf\xb7\x1d\x88\x17\xf3\xf1\x84\xecV/x\x97\xb74\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\xb0S;\x81\xd0*a{\x92)\xc7\xec]o/g.[Z\x8965\u026d\xc5\u07a0\x00\x00\u07d4%\xb7\x8c\x9f\xad\x85\xb43C\xf0\xbf\xcd\x0f\xac\x11\u0254\x9c\xa5\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\xbcI\xef(\x8c\xd1e\xe5%\xc6a\xa8\x12\u03c4\xfb\xec\x8f3\x89\x12Y!\xae\xbd\xa9\xd0\x00\x00\u07d4%\xbd\xfa>\xe2o8Ia{#\x00bX\x8a\x97\xe3\xca\xe7\x01\x8965\xe6\x19\xbb\x04\xd4\x00\x00\u07d4%\xc1\xa3~\xe5\xf0\x82e\xa1\xe1\r=\x90\xd5G)U\xf9x\x06\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4%\xc6\xe7O\xf1\xd9(\u07d8\x13z\xf4\u07c40\xdf$\xf0|\u05c9\x15$VU\xb1\x02X\x00\x00\xe0\x94%\xcf\xc4\xe2\\5\xc1;i\xf7\xe7}\xbf\xb0\x8b\xafXuk\x8d\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94%\xda\u0515\xa1\x1a\x86\xb9\xee\xec\xe1\xee\xec\x80^W\xf1W\xfa\xff\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94%\xe07\xf0\n\x18'\v\xa5\xec4 \"\x9d\xdb\n,\u33e2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4%\xe6a\xc99\x86:\xcc\x04No\x17\xb5i\x8c\xce7\x9e\xc3\u0309JD\x91\xbdm\xcd(\x00\x00\u07d4&\x04\x8f\xe8M\x9b\x01\nb\xe71b~I\xbc.\xb7?@\x8f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4&\x06\u00f3\xb4\xca\x1b\t\x14\x98`,\xb1\x97\x8b\xf3\xb9R!\xc0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4&\n#\x0eDe\a~\v\x14\xeeDB\xa4\x82\u0570\xc9\x14\xbf\x89Z\xf6\x06\xa0k[\x11\x80\x00\u07d4&\r\xf8\x94:\x8c\x9a]\xbayE2\u007f\xd7\xe0\x83|\x11\xad\a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4&\x14\xf4-]\xa8D7ux\xe6\xb4H\xdc$0[\xef+\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\x15\x10\x0e\xa7\xe2[\xba\x9b\xcat`X\xaf\xbb\xb4\xff\xbeBD\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4&\x15u\xe9\xcfY\xc8\"o\xa7\xaa\xf9\x1d\xe8o\xb7\x0fZ\u00ee\x89\x10C\xa4CjR?\x00\x00\xe0\x94&\x1e\x0f\xa6LQ\x13te\xee\xcf[\x90\xf1\x97\xf7\x93\u007f\xdb\x05\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4&*\x8b\xfd}\x9d\xc5\xdd:\u05c1a\xb6\xbbV\b$76U\x89?j\x83\x84\a+v\x00\x00\xe0\x94&*\xedK\xc0\xf4\xa4\xb2\xc6\xfb5y>\x83ZI\x18\x9c\xdf\xec\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94&-\xc16L\xcfm\xf8\\C&\x8e\xe1\x82UM\xaei.)\x8a\x01\v /\xect\xce\xd8\x00\x00\u07d4&8\x140\x9d\xe4\xe65\xcfX^\r6Tw\xfc@\xe6l\xf7\x89\a\xea(2uw\b\x00\x00\u07d4&9\xee\xe9\x87<\xee\xc2o\u0314T\xb5H\xb9\xe7\xc5J\xa6\\\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94&>W\xda\xcb\xe0\x14\x9f\x82\xfee\xa2fH\x98\x86o\xf5\xb4c\x8a\b\v\xfb\xef\xcb_\v\xc0\x00\x00\u07d4&GT\x19\xc0m_\x14z\xa5\x97$\x8e\xb4l\xf7\xbe\xfad\xa5\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4&L\xc8\bj\x87\x10\xf9\x1b!r\t\x05\x91,\u05d6J\xe8h\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94&S\x83\u058bR\xd04\x16\x1b\xfa\xb0\x1a\xe1\xb0G\x94/\xbc2\x8a\x04rq\xde\xe2\rt\\\x00\x00\u07d4&Y\xfa\xcb\x1e\x83CeS\xb5\xb4)\x89\xad\xb8\a_\x99S\xed\x89\x01\x97evw\x1a^\x00\x00\xe0\x94&o-\xa7\xf0\b^\xf3\xf3\xfa\t\xba\xee#+\x93\xc7D\xdb.\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4&qH\xfdr\xc5Ob\nY/\xb9'\x991\x9c\xc4S+\\\x89\x169\u46fa\x16(\x00\x00\xe0\x94&xJ\u0791\u0228:\x8e9e\x8c\x8d\x82wA<\u0319T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4&z~n\x82\xe1\xb9\x1dQ\xde\u0776D\xf0\xe9m\xbb\x1f\u007f~\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4&\x80q=@\x80\x8e*P\xed\x011P\xa2\xa6\x94\xb9j\u007f\x1d\x89a\t=|,m8\x00\x00\u07d4&\x97\xb39\x81;\f-\x96K$q\xeb\x1c`oN\u02d6\x16\x89>\x8e\xf7\x95\u0610\xc8\x00\x00\u07d4&\xa6\x8e\xab\x90Z\x8b=\xce\x00\xe3\x170\x82%\u06b1\xb9\xf6\xb8\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4&\xb1\x1d\x06e\x88\xcet\xa5r\xa8Zc(s\x92\x12\xaa\x8b@\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xba\xbfB\xb2g\xfd\xcf8a\xfd\xd4#j^GHH\xb3X\x8965\u026d\xc5\u07a0\x00\x00\u07d4&\xc0\x05Kp\r:|-\xcb\xe2uh\x9dOL\xad\x16\xa35\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xc2\xff\xc3\x0e\xfd\xc5'>v\x18:\x16\xc2i\x8dnS\x12\x86\x89*\x11)\u0413g \x00\x00\u07d4&\u025f\x88I\u0240+\x83\xc8a!\u007f\xd0z\x9e\x84\u0377\x9d\x89\x10CV\x1a\x88)0\x00\x00\u07d4&\xcf\xff\xd0R\x15+\xb3\xf9W\xb4x\xd5\xf9\x8b#:|+\x92\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4&\u0521h\x91\xf5)\"x\x92\x17\xfc\u0606\xf7\xfc\xe2\x96\xd4\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xd4\xec\x17\xd5\u03b2\u0214\xbd\u015d\nji]\xad+C\u0309\x9f\x1fxv\x1d4\x1a\x00\x00\u07d4&\xe8\x01\xb6,\x82q\x91\xddh\xd3\x1a\x01\x19\x90\x94\u007f\xd0\xeb\xe0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4&\xe9\xe2\xadr\x97\x02bd\x17\xef%\xde\r\xc8\x00\xf7\xa7y\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4&\xf9\xf7\xce\xfd~9K\x9d9$A+\xf2\u0083\x1f\xaf\x1f\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94&\xfe\x17L\xbfRfP\xe0\xcd\x00\x9b\xd6\x12e\x02\u038ehM\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\xe0\x94&\xff\nQ\xe7\xce\u0384\x00'ix\xdb\xd6#n\xf1b\xc0\xe6\x8a\x15.\x18V'T\nP\x00\x00\u07d4'\x10\x1a\x0fV\u04da\x88\u0168O\x9b2L\xdd\xe3>\\\xb6\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'\x14L\xa9\xa7w\x1a\x83j\xd5\x0f\x80?d\xd8i\xb2\xae+ \x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4'\x14i\x13V:\xa7E\xe2X\x840\xd94\x8e\x86\xea|5\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4'\x1d=H\x1c\xb8\x8evq\xad!iI\xb66^\x060=\xe0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4' \xf9\xcaBn\xf2\xf2\xcb\xd2\xfe\xcd9\x92\fO\x1a\x89\xe1m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'*\x13\x1aZejz:\xca5\u023d \"\"\xa7Y\"X\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4'D\xffgFA!\xe3Z\xfc)\"\x17qd\xfa/\xcb\x02g\x89\x05k\xc7^-c\x10\x00\x00\u07d4'J=w\x1a=p\x97\x96\xfb\xc4\xd5\xf4\x8f\xce/\xe3\x8cy\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4'Mi\x17\x0f\xe7\x14\x14\x01\x88+\x88j\xc4a\x8cj\xe4\x0e\u06c93\xc5I\x901r\f\x00\x00\u07d4'R\x1d\xeb;n\xf1An\xa4\u01c1\xa2\xe5\u05f3n\xe8\x1ca\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'Xu\xffO\xbb\f\xf3\xa40!1'H\u007fv\b\xd0L\xba\x89\x1b\x1c\x01\x0evmX\x00\x00\u07d4'j\x00n0(\xec\xd4L\xdbb\xba\nw\u0394\xeb\xd9\xf1\x0f\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4'k\x05!\xb0\xe6\x8b'}\xf0\xbb2\xf3\xfdH2cP\xbf\xb2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4'o\xd7\xd2O\x8f\x88?Zz()[\xf1qQ\u01e8K\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'p\xf1N\xfb\x16]\u07bay\xc1\v\xb0\xaf1\xc3\x1eY3L\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4'vw\xab\xa1\xe5,;S\xbf\xa2\a\x1dN\x85\x9a\n\xf7\xe8\xe1\x8965\u026d\xc5\u07a0\x00\x00\u07d4'\x82Ff\xd2x\xd7\x04#\xf0=\xfe\x1d\u01e3\xf0/C\u2d4966\xc2^f\xec\xe7\x00\x00\u07d4'\x83\f_`#\xaf\xaa\xf7\x97Egl J\x0f\xac\u0360\xba\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94'\x84\x90?\x1d|\x1b\\\xd9\x01\xf8\x87]\x14\xa7\x9b<\xbe*V\x8a\x04\xbd\xa7\xe9\xd7J\xd5P\x00\x00\u07d4'\x8c\v\xdec\x0e\u00d3\xb1\xe7&\u007f\xc9\xd7\xd9p\x19\xe4\x14[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'\x98q\x10\"\x1a\x88\b&\xad\xb2\xe7\xab^\xcax\xc6\xe3\x1a\xec\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94'\xac\a;\xe7\x9c\xe6W\xa9:\xa6\x93\xeeC\xbf\x0f\xa4\x1f\xef\x04\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4'\xb1iN\xaf\xa1e\xeb\xd7\xcc{\u025et\x81J\x95\x14\x19\u0709+^:\xf1k\x18\x80\x00\x00\u07d4'\xb6(\x16\xe1\xe3\xb8\u045by\xd1Q=]\xfa\x85[\f:*\x89\x05j\xf5\xc1\xfdiP\x80\x00\u07d4'\xbf\x94<\x163\xfe2\xf8\xbc\xcc\xdbc\x02\xb4\a\xa5rND\x892\xf8Lm\xf4\b\xc0\x80\x00\u07d4'\xbf\x9fD\xba}\x05\xc35@\u00e5;\xb0,\xbb\xff\xe7\xc3\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4'\xc2\xd7\xcaPM\xaa=\x90f\xdc\t\x13}\xc4/:\xaa\xb4R\x89 \x86\xac5\x10R`\x00\x00\u07d4'\xd1X\xac=>\x11\t\xabnW\x0e\x90\xe8]8\x92\xcdv\x80\x89\x05k\xc7^-c\x10\x00\x00\u07d4'\xe69\x89\xca\x1e\x90;\xc6 \xcf\x1b\x9c?g\xb9\xe2\xaee\x81\x89Hz\x9a0E9D\x00\x00\xe0\x94'\xf0<\xf1\xab\xc5\xe1\xb5\x1d\xbcDK(\x9eT,\x9d\u07f0\xe6\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4'\xfc\x85\xa4\x9c\xff\x90\xdb\xcf\xda\u071d\xdd@\u05b9\xa2!\nl\x89\x05k\xc7^-c\x10\x00\x00\u07d4(\x05A^\x1d\u007f\xde\xc6\xde\u07f8\x9eR\x1d\x10Y-t<\x10\x89\x05k\xc7^-c\x10\x00\x00\u07d4(\a>\xfc\x17\xd0\\\xab1\x95\xc2\xdb3+a\x98Gw\xa6\x12\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x12P\xa2\x91!'\nN\xe5\u05cd$\xfe\xaf\xe8,p\xba:\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x13\xd2c\xfc_\xf2G\x9e\x97\x05\x95\u05b6\xb5`\xf8\xd6\xd6\u0449lk\x93[\x8b\xbd@\x00\x00\u07d4(.\x80\xa5T\x87ZVy\x9f\xa0\xa9\u007fU\x10\u7557LN\x8965\u026d\xc5\u07a0\x00\x00\u07d4(3\x96\xce<\xac9\x8b\xcb\xe7\"\u007f2>x\xff\x96\u0407g\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4(4\x9f~\xf9t\xeaU\xfe6\xa1X;4\xce\xc3\xc4Pe\xf0\x89\f\xb63\u051eeY\x00\x00\u07d4(6\x120F\xb2\x84\xe5\xef\x10+\xfd\"\xb1v^P\x81\x16\xad\x89\x16S\xfb\xb5\xc4'\xe4\x00\x00\u07d4(<#\x14(<\x92\u0530d\xf0\xae\xf9\xbbRF\xa7\x00\u007f9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4(>\x11 7I\xb1\xfaO2\xfe\xbbq\xe4\x9d\x13Y\x198*\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94(>bR\xb4\xef\xcfFT9\x1a\xcbu\xf9\x03\u015bx\xc5\xfb\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94(Q\x0en\xff\x1f\xc8)\xb6WoC(\xbc98\xecze\x80\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4(X\xac\xac\xaf!\xea\x81\u02b7Y\x8f\xdb\xd8kE.\x9e\x8e\x15\x89$\x1a\x9bOaz(\x00\x00\u07d4(Z\xe5\x1b\x95\x00\u014dT\x13e\xd9ui\xf1K\xb2\xa3p\x9b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(f\xb8\x1d\xec\xb0.\xe7\n\xe2P\xce\xe5\xcd\xc7{Y\u05f6y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(i\x06\xb6\xbdIr\xe3\xc7\x16U\xe0K\xaf6&\f|\xb1S\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4(k\x18ma\xea\x1f\u05cd\x990\xfe\x12\xb0e7\xb0\\=Q\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94(t\xf3\xe2\x98]_{@f'\xe1{\xaaw+\x01\xab\u031e\x8a\x01F\x05\x04\x10v_8\x00\x00\xe0\x94(|\xf9\u0410.\xf8\x19\xa7\xa5\xf1ID[\xf1w^\xe8\xc4|\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4(\x81\x8e\x18\xb6\x10\x00\x13!\xb3\x1d\xf6\xfe}(\x15\u036d\xc9\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x86\x83$3~\x11\xba\x10l\xb4\x81\u0696/:\x84S\x80\x8d\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94(\x90K\xb7\xc40)C\xb7\t\xb1Myp\xe4+\x83$\u184a\x02\x1f\x97\x84j\a-~\x00\x00\u07d4(\x95\xe8\t\x99\xd4\x06\xadY.+&'7\xd3_}\xb4\xb6\x99\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4(\x96r\x80!N!\x8a\x12\f]\xda7\x04\x1b\x11\x1e\xa3mt\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4(\xa3\xda\t\xa8\x19H\x19\xae\x19\x9f.m\x9d\x13\x04\x81~(\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(\xab\x16_\xfbi\xed\xa0\xc5I\xae8\xe9\x82o_\u007f\x92\xf8S\x89FM\xf6\xd7\xc8DY\x00\x00\u07d4(\xb7u\x85\xcb=U\xa1\x99\xab)\x1d:\x18\u018f\u8684\x8a\x89j@v\xcfy\x95\xa0\x00\x00\xe0\x94(\xd4\xeb\xf4\x1e=<E\x1e\x94;\xdd~\x1f\x17_\xae\x93*=\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94(\xd7\xe5\x86o\x1d\x85\xfd\x1c\xeb2\xbf\xbe\x1d\xfc6\xdbCEf\x8a\x01\x86B1\xc6\x105\x1c\x00\x00\u0794(\xd8\xc3_\xb7\xee\xa6\"X!5\xe3\xadG\xa2'\u0266c\xbd\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4(\xe4\xaf0\u0353\xf6\x86\xa1\"\xad{\xb1\x9f\x8a\x87\x85\xee\xe3B\x89q\xe5;pl\u01f4\x00\x00\u07d4(\xea\xeax\xcdM\x95\xfa\xec\xfbh\x83n\xaf\xe85 \U000fbdc9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94(\xef\xaecVP\x9e\u07ec\xe8\x9f\xc6\x1a\u007f\xdc\xdb9\xee\xa8\xe5\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\xe0\x94(\xfa%\x80\xf9\xeb\xe4 \xf3\xe5\xee\xfd\xd3qc\x8e;z\xf4\x99\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4)\x01\xf8\a\u007f4\x19\v\xb4z\x8e\"\u007f\xa2\x9b0\xce\x11;1\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\x05\xb1\x92\xe8<\xe6Y\xaa5[\x9d\f N>\x95\xf9\xbb\x9a\x89u#\\\x1d\x009>\x80\x00\u07d4)\nV\xd4\x1fn\x9e\xfb\xdc\xea\x03B\u0dd2\x9a\x8c\xdf\xcb\x05\x89\x12\xa5\xf5\x81h\xee`\x00\x00\u07d4)\x15bK\xcbg\x917\xb8\xda\xe9\xabW\xd1\x1bI\x05\xea\xeeK\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4)\x1e\xfe\x00\x81\xdc\xe8\xc1G\x99\xf7\xb2\xa46\x19\xc0\u00f3\xfc\x1f\x89A\rXj \xa4\xc0\x00\x00\u07d4)\x1f\x92\x9c\xa5\x9bT\xf8D>=Mu\xd9]\xee$<\xefx\x89\x1b\x1a\b\x927\a=\x00\x00\xe0\x94))\x8c\xcb\xdf\xf6\x89\xf8\u007f\xe4\x1a\xa6\xe9\x8f\u07f5=\xea\xf3z\x8a\x041\\2\xd7\x1a\x9e`\x00\x00\u07d4)/\"\x8b\n\x94t\x8c\x8e\xeca-$o\x98\x93c\xe0\x8f\b\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4)3\x84\xc4+o\x8f)\x05\xceR\xb7 \\\"t7la+\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4)4\xc0\xdf{\xbc\x17+l\x18k\vrTz\u038b\xf7TT\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4)<#\x06\xdf6\x04\xaeO\xda\r z\xbasog\xde\a\x92\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94)I\xfd\x1d\xef\\v\xa2\x86\xb3\x87$$\x80\x9a\a\xdb9f\xf3\x8a\x01\x1b\xd9\x06\u06a0\xc9C\x80\x00\u07d4)OIK?.\x14</\xfc\x978\xcb\xfd\x95\x01\x85\v\x87N\x89yn>\xa3\xf8\xab\x00\x00\x00\u07d4)U\xc3W\xfd\x8fu\xd5\x15\x9a=\xfai\u0178z5\x9d\ua309lk\x93[\x8b\xbd@\x00\x00\u07d4)a\xfb9\x1ca\x95|\xb5\xc9\xe4\a\u0762\x938\u04f9,\x80\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4)h\x1d\x99\x12\xdd\xd0~\xaa\xbb\x88\xd0]\x90\xf7f\xe8bA}\x8965\u026d\xc5\u07a0\x00\x00\u07d4)kq\xc0\x01X\x19\xc2B\xa7\x86\x1eo\xf7\xed\xed\x8a_q\xe3\x89lh\xcc\u041b\x02,\x00\x00\u07d4)mf\xb5!W\x1aNA\x03\xa7\xf5b\xc5\x11\xe6\xaas-\x81\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4)o\x00\xde\x1d\u00fb\x01\xd4z\x8c\xcd\x1e]\x1d\u0661\xebw\x91\x8965\u026d\xc5\u07a0\x00\x00\u07d4)s\x85\xe8\x864FV\x85\xc21\xa3\x14\xa0\xd5\xdc\xd1F\xaf\x01\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4)v=\xd6\u069a|\x16\x11s\x88\x83!\ub9b6<\x8f\xb8E\x89\x11\xc7\xea\x16.x \x00\x00\u07d4)yt\x11t\xa8\xc1\xea\v\u007f\x9e\xdfe\x81w\x85\x94\x17\xf5\x12\x89\x19\x01\x96l\x84\x96\x83\x80\x00\u07d4)z\x88\x92\x1b_\xca\x10\u5edd\xed`\x02T7\xae\"\x16\x94\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94)}]\xbe\"//\xb5%1\xac\xbd\v\x01=\xc4F\xacsh\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4)\x82N\x94\xccCH\xbc\x962y\xdc\xdfG9\x17\x152L\u04c9i*\xe8\x89p\x81\xd0\x00\x00\u07d4)\x82\xd7j\x15\xf8G\xddA\xf1\x92*\xf3h\xfeg\x8d\x0eh\x1e\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\x88\x87\xba\xb5|[\xa4\xf0aR)\xd7R_\xa1\x13\xb7\ua249\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94)\x8e\xc7kD\r\x88\a\xb3\xf7\x8b_\x90\x97\x9b\xeeB\xedC\u06ca\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4)\x93h`\x90B\xa8X\xd1\xec\xdf\x1f\xc0\xad\xa5\xea\xce\xca)\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4)\x9e\v\xcaU\xe0i\u0785\x04\xe8\x9a\xcan\xca!\u04ca\x9a]\x89\x03\x027\x9b\xf2\xca.\x00\x00\u07d4)\xac+E\x84T\xa3l~\x96\xc7:\x86g\"*\x12$,q\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94)\xad\u03c3\xb6\xb2\n\u01a44\xab\xb1\x99<\xbd\x05\xc6\x0e\xa2\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94)\xae\xf4\x8d\xe8\xc9\xfb\xadK\x9eL\xa9pyzU3\xebr-\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4)\xb3\xf5a\xeezn%\x94\x1e\x98\xa52[x\xad\u01d7\x85\xf3\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\xbd\xc4\xf2\x8d\xe0\x18\x0fC<&\x94\xebt\xf5PL\xe9C7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4)\u0300M\x92+\xe9\x1fY\t\xf3H\xb0\xaa\xa5\xd2\x1b`x0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94)\xda>5\xb2;\xb1\xf7/\x8e\"X\xcf\u007fU3Y\xd2K\xac\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94)\xe6y\x90\xe1\xb6\xd5.\x10U\xff\xe0I\xc51\x95\xa8\x15B\u03ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4)\uab82v\x17b\xf4\xd2\xdbS\xa9\u018b\x0fk\vmNf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4)\xeb~\xef\xda\xe9\xfe\xb4I\xc6?\xf5\xf2y\xd6u\x10\xeb\x14\"\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4)\xf0\xed\xc6\x038\xe7\x11 \x85\xa1\xd1\x14\u068cB\u038fU\u0589\xa0Z\u007f\x0f\xd8%x\x00\x00\u07d4)\xf8\xfb\xa4\xc3\ar\xb0W\xed\xbb\xe6*\xe7B\f9\x05r\xe1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94)\xf9(l\x0es\x8d\x17!\xa6\x91\u01b9Z\xb3\u0667\x97\xed\xe8\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4*\b^%\xb6Hb\xf5\xe6\x8dv\x8e+\x0fz\x85)\x85\x8e\xee\x89k\x88:\xcdWf\xcd\x00\x00\u07d4**\xb6\xb7Lz\xf1\xd9Gk\xb5\xbc\xb4RG\x97\xbe\xdc5R\x8965\u026d\xc5\u07a0\x00\x00\u07d4*9\x19\nO\u0783\u07f3\xdd\xcbL_\xbb\x83\xaclIu\\\x8965\u026d\xc5\u07a0\x00\x00\u07d4*@\r\xff\x85\x94\xder(\xb4\xfd\x15\xc3#\"\xb7[\xb8}\xa8\x89\x051\xa1\u007f`z-\x00\x00\xe0\x94*D\xa7!\x8f\xe4Me\xa1\xb4\xb7\xa7\u0671\xc2\xc5,\x8c>4\x8a\r-\x06\xc3\x05\xa1\xebW\x80\x00\u07d4*F\xd3Swqv\xff\x8e\x83\xff\xa8\x00\x1fOp\xf9s:\xa5\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4*Y_\x16\xee\xe4\xcb\f\x17\u0662\xd99\xb3\xc1\x0flgrC\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4*Y\xe4~\xa5\xd8\xf0\xe7\xc0(\xa3\xe8\xe0\x93\xa4\x9c\x1bP\xb9\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*[\xa9\xe3L\u054d\xa5L\x9a'\x12f:;\xe2t\xc8\xe4{\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94*^:@\xd2\xcd\x03%vm\xe7:=g\x18\x96\xb3b\xc7;\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94*cY\x0e\xfe\x99\x86\xc3\xfe\xe0\x9b\n\n3\x8b\x15\xbe\xd9\x1f!\x8a\x01^\x1cN\x05\xee&\xd0\x00\x00\u07d4*gf\n\x13h\xef\xcdbn\xf3k+\x1b`\x19\x80\x94\x1c\x05\x89\a?u\u0460\x85\xba\x00\x00\u07d4*t+\x89\x10\x94\x1e\t2\x83\n\x1d\x96\x92\xcf\u0484\x94\xcf@\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4*tl\xd4@'\xaf>\xbd7\xc3x\xc8^\xf7\xf7T\xab_(\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4*\x81\xd2|\xb6\xd4w\x0f\xf4\xf3\u0123\xba\x18\xe5\xe5\u007f\aQ|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*\x91\xa9\xfe\xd4\x1b}\x0e\\\xd2\xd81X\xd3\xe8\xa4\x1a\x9a-q\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94*\x9cW\xfe{k\x13\x8a\x92\rgo<v\xb6\u00a0\xee\xf6\x99\x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4*\x9c\x96\xc1\x91Q\xff\xcb\xe2\x9aF\x16\xd0\xc5+93\xb4e\x9f\x89\x03\xc17\x9b\x87e\xe1\x80\x00\u07d4*\xa1\x92w|\xa5\xb9x\xb6\xb2\xc2\xff\x80\n\xc1\x86\x0fu?G\x89\x12)\x0f\x15\x18\v\xdc\x00\x00\xe0\x94*\xaa5'Mt%Fg\vt&&E!\x03*\xf4\xf4\u00ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4*\xae\xa1\xf1\x04o0\xf1\t\xfa\xec\x1cc\xef\\u\x94\xeb\b\u0689\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4*\xb9~\x8dY\xee\xe6H\xabl\xaf\x86\x96\xf8\x997\x148d\u0589\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94*\xbc\u1009@\xcdN\xf5\xb5\xe0R\x85\xf8-\xf7\xa9\xab^\x03\x8a\x02\x13BR\r_\xec \x00\x00\xe0\x94*\xbd\xf1\xa67\xeflB\xa7\xe2\xfe!ws\xd6w\xe8\x04\xeb\u074a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4*\xc1\xf8\u05ffr\x1f<\xfet\xd2\x0f\ua6c7\xa2\x8a\xaa\x98,\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\xe0\x94*\u031c\x1a2$\vM[/wz.\xa0R\xb4/\xc1'\x1c\x8a\b\xd8\a\xee\x14\xd86\x10\x00\x00\u07d4*\xd6\xc9\xd1\f&\x18\x19\xa1\xa0\xca,H\xd8\u01f2\xa7\x17(\u07c965\u026d\xc5\u07a0\x00\x00\xe0\x94*\xe58f\xfc-\x14\xd5r\xabs\xb4\xa0e\xa1\x18\x82g\xf5'\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4*\xe7:y\xae\xa0'\x853\xac\xcf!\a\t\"\xb1a?\x8f2\x89\xa7\xe9K\xbe\xaep\x1a\x80\x00\u07d4*\xe8-\xab\x92\xa6c\x89\ue86b\xb9\x01\xd1\xd5\u007fZ|\xca\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*\uc01d\xf92[\x9fH9\x96\xe9\x9fs1\t\u007f\b\xaa\x0e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94*\xed,\xe51\xc0V\xb0\t~\xfc<m\xe1\fGb\x00N\u064a\x025iS\xab}\xdc8\x00\x00\u07d4*\xfb\x05\x8c=1\x03+5;\xf2O\t\xae \xd5M\xe5}\xbe\x89;\xa1\x91\v\xf3A\xb0\x00\x00\xe0\x94+\x03bc6\x14\xbf\xcbX5iC\x8e\xccN\xa5{\x1d3~\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4+\x10\x1e\x82,\xd9b\x96*\x06\x80\n,\b\u04f1]\x82\xb75\x89\b=lz\xabc`\x00\x00\u07d4+\x12\x9c&\xb7]\xde\x12\u007f\x83 \xbd\x0fcA\f\x92\xa9\xf8v\x89wC\"\x17\xe6\x83`\x00\x00\xe0\x94+$\x1f\x03s7\xebJ\xcca\x84\x9b\xd2r\xac\x13?|\xdfK\x8aP\vk\u0296*\xb8@\x00\x00\u07d4+:h\xdbk\f\xae\x8a|zGk\xdf\u03fdb\x05\xe1\x06\x87\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4+<\xf9s\x11\xff0\xf4`\x94Z\x9d\x80\x99\xf4\xa8\x8e&\xd4V\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+I\xfb\xa2\x9806\x0f\u0376\xda#\xbb\xfe\xa5\xc0\xbb\xacR\x81\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794+OE\a\xbbk\x98\x17\x94,\xe43x\x1bp\x8f\xbc\xd1f\xfd\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94+P\x16\xe2Es\x87\x95ebXq\x15\xaa\x87Y\xd8i_\u07ca*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94+\\`\xe8E5\xee\xb4\u0540\xde\x12z\x12\xeb&w\u0333\x92\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4+\\\ud647\xc0v_\x90\x0eI\u03dd\xa2\xd9\xf9\xc1\x13\x88U\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94+_K?\x1e\x11pz\"z\xa5\u67e4\x9d\xde\xd3?\xb3!\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4+h0k\xa7\xf8\u06afs\xf4\xc6D\xef}'C\xc0\xf2hV\x89.\xe1\x82\xca\x17\xdd\xd0\x00\x00\xe0\x94+n\u049a\x95u<:\xd9H4\x8e>{\x1a%\x10\x80\xff\xb9\x8a4\xf0\x86\xf3\xb3;h@\x00\x00\u07d4+p\x1d\x16\xc0\xd3\xcc\x1eL\xd8TE\xe6\xad\x02\ue92c\x01-\x89 \x86\xac5\x10R`\x00\x00\xe0\x94+q|\xd42\xa3#\xa4e\x909\x84\x8d;\x87\xde&\xfc\x95F\x8ai\xe1\r\xe7fv\u0400\x00\x00\u07d4+t\xc3s\xd0K\xfb\x0f\xd6\n\x18\xa0\x1a\x88\xfb\xe8Gp\u5309\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4+w\xa4\u060c\rV\xa3\xdb\xe3\xba\xe0J\x05\xf4\xfc\u0477W\xe1\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94+\x84\x88\xbd-<\x19z=&\x15\x18\x15\xb5\xa7\x98\xd2qh\u070a\x01j\x1f\x9f_\xd7\xd9`\x00\x00\u07d4+\x8a\r\xee\\\xb0\xe1\xe9~\x15\xcf\xcan\x19\xad!\xf9\x95\ufb49\x1bUC\x8d\x9a$\x9b\x00\x00\xe0\x94+\x8f\xe4\x16n#\xd1\x19c\xc0\x93+\x8a\u078e\x01E\xea\ap\x8a\t(\x96R\x9b\xad\u0708\x00\x00\xe0\x94+\x99\xb4.OBa\x9e\xe3k\xaa~J\xf2\xd6^\xac\xfc\xba5\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4+\xab\x0f\xbe(\u0544 \xb5 6w\n\x12\xf9\x95*\xeai\x11\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4+\xad\xe9\x1d\x15E\x17b\x0f\u05349\xac\x97\x15zA\x02\xa9\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4+\xaf\x8dn\"\x11t\x12H \xeeI+\x94Y\xecO\xad\xaf\xbb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+\xaf\xbf\x9e\x9e\xd2\xc2\x19\xf7\xf2y\x13t\xe7\xd0\\\xb0gw\xe7\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\xe0\x94+\xb3f\xb9\xed\xcb\r\xa6\x80\xf0\xe1\v;n(t\x81\x90\xd6\u00ca\x01:b\u05f5v@d\x00\x00\xe0\x94+\xb6\xf5x\xad\xfb\u7ca1\x16\xb3UO\xac\xf9\x96\x98\x13\xc3\x19\x8a\x01\x91'\xa19\x1e\xa2\xa0\x00\x00\u07d4+\xbeb\xea\xc8\f\xa7\xf4\xd6\xfd\xee~}\x8e(\xb6:\xcfw\x0e\x89\x81\xe3-\xf9r\xab\xf0\x00\x00\u07d4+\xbeg*\x18WP\x8fc\x0f*^\xdbV=\x9e\x9d\xe9(\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+\xc4)\xd6\x18\xa6jL\xf8-\xbb-\x82N\x93V\xef\xfa\x12j\x89lj\xccg\u05f1\xd4\x00\x00\u07d4+\xd2R\xe0\xd72\xff\x1d|x\xf0\xa0.l\xb2T#\xcf\x1b\x1a\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4+\xdd\x03\xbe\xbb\xee';l\xa1\x05\x9b4\x99\x9a[\xbda\xbby\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,\x04\x11\\>R\x96\x1b\r\xc0\xb0\xbf1\xfb\xa4ToYf\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94,\x06\u0752+aQJ\xaf\xed\xd8D\x88\xc0\u008em\xcf\x0e\x99\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94,\f\xc3\xf9QH,\u0222\x92X\x15hN\xb9\xf9N\x06\x02\x00\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4,\x0e\xe14\u0633aE\xb4{\xee\u7bcd'8\xdb\xda\b\xe8\x89\n\xe5os\x0em\x84\x00\x00\u07d4,\x0f[\x9d\xf46%y\x8e~\x03\xc1\xa5\xfdjm\t\x1a\xf8+\x89\x01\xb0\xfc\xaa\xb2\x000\x00\x00\u07d4,\x12\x8c\x95\xd9W!Q\x01\xf0C\u074f\u0142EmA\x01m\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4,\x18\x00\xf3_\xa0->\xb6\xff[%(_^J\xdd\x13\xb3\x8d\x891\"\u04ed\xaf\xde\x10\x00\x00\u07d4,\x1c\x19\x11N=m\xe2xQHK\x8d'\x15\xe5\x0f\x8a\x10e\x89\x05k\xc7^-c\x10\x00\x00\u07d4,\x1c\xc6\xe1\x8c\x15$\x88\xba\x11\xc2\xcc\x1b\xce\xfa-\xf3\x06\xab\u0449Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94,\x1d\xf8\xa7oH\xf6\xb5K\u03dc\xafV\xf0\xee\x1c\xf5z\xb3=\x8a\x02$\u007fu\x00\x89\xdaX\x00\x00\u07d4,!G\x94z\xe3?\xb0\x98\xb4\x89\xa5\xc1k\xff\xf9\xab\xcdN*\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4,#OP\\\xa8\xdc\xc7}\x9b~\x01\xd2W\xc3\x18\xcc\x199m\x89\x05k\xc7^-c\x10\x00\x00\u07d4,$(\xe4\xa6it\xed\xc8\"\xd5\xdb\xfb$\x1b'(\aQX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,-\x15\xff9V\x1c\x1br\xed\xa1\xcc\x02\u007f\xfe\xf27C\xa1D\x89\u0500\xed\x9e\xf3+@\x00\x00\u07d4,-\xb2\x8c3\t7^\xea<mr\xcdm\x0e\xec\x14Z\xfc\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,BN\xe4\u007fX<\xdc\xe0z\xe3\x18\xb6\xfa\xd4b8\x1dM+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94,KG\x03\a\xa0Y\x85@U\xd9\x1e\xc3yM\x80\xb5=\x0fJ\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4,R\u0244\x10.\xe0\xcd>1\x82\x1b\x84\xd4\b\x93\x0e\xfa\x1a\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4,Z-\n\xbd\xa0;\xbe!W\x81\xb4\xff)l\x8ca\xbd\xba\xf6\x89\x01\xa8\xe5oH\xc0\"\x80\x00\u07d4,[}{\x19Z7\x1b\xf9\xab\u0774/\xe0O/\x1d\x9a\x99\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4,]\xf8ffj\x19K&\u03bb@~J\x1f\xd7> \x8d^\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94,`?\xf0\xfe\x93alCW>\xf2y\xbf\xea@\x88\x8dj\xe7\x8a\x01\x00\xf4\xb6\xd6gW\x90\x00\x00\xe0\x94,hF\xa1\xaa\x99\x9a\"F\xa2\x87\x05`\x00\xbaM\u02e8\xe6=\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\u0794,j\xfc\xd4\x03|\x1e\xd1O\xa7O\xf6u\x8e\tE\xa1\x85\xa8\xe8\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4,ki\x9d\x9e\xad4\x9f\x06\u007fEq\x1a\aJd\x1d\xb6\xa8\x97\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,o\\\x12L\u01c9\xf8\xbb9\x8e?\x88\x97Q\xbcK`-\x9e\x89\x01Y\xf2\v\xed\x00\xf0\x00\x00\u07d4,\x83\xae\xb0/\xcf\x06}e\xa4p\x82\xfd\x97x3\xab\x1c\uc449\b'8#%\x8a\xc0\x00\x00\xe0\x94,\x89\xf5\xfd\xca=\x15T\t\xb68\xb9\x8at.U\xebFR\xb7\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4,\x96HI\xb1\xf6\x9c\xc7\u03a4D%8\xed\x87\xfd\xf1l\xfc\x8f\x89lk\x93[\x8b\xbd@\x00\x00\u0794,\x9f\xa7,\x95\xf3}\b\xe9\xa3`\t\u7930\u007f)\xba\xd4\x1a\x88\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4,\xafk\xf4\xec}Z\x19\xc5\xe0\x89z^\xeb\x01\x1d\xce\xceB\x10\x89\a\x93H5\xa01\x16\x00\x00\u07d4,\xb4\xc3\xc1k\xb1\xc5^|kz\x19\xb1'\xa1\xac\x93\x90\xcc\t\x89\xb8'\x94\xa9$O\f\x80\x00\xe0\x94,\xb5IZPS6\xc2FT\x10\xd1\xca\xe0\x95\xb8\xe1\xba\\\u074a\x04<3\xc1\x93ud\x80\x00\x00\u07d4,\xb6\x15\a:@\xdc\u06d9\xfa\xa8HW.\x98{;\x05n\xfb\x89+X\xad\u06c9\xa2X\x00\x00\u07d4,\xbam]\r\xc2\x04\xea\x8a%\xad\xa2\xe2oVu\xbd_/\u0709H#\xef}\u06da\xf3\x80\x00\u07d4,\xbb\fs\u07d1\xb9\x17@\xb6i;wJ}\x05\x17~\x8eX\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4,\xcbfIM\n\xf6\x89\xab\xf9H=6]x$D\xe7\u07ad\x8965\u026d\xc5\u07a0\x00\x00\u07d4,\xcc\x1f\x1c\xb5\xf4\xa8\x00.\x18k \x88]\x9d\xbc\x03\f\b\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,\u03c0\xe2\x18\x98\x12^\xb4\xe8\a\u0342\xe0\x9b\x9d(Y/n\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,\u0456\x94\u0452j\x0f\xa9\x18\x9e\u07ba\xfcg\x1c\xf1\xb2\u02a5\x8965\u026d\xc5\u07a0\x00\x00\u07d4,\u04d34\xac~\xacyrW\xab\xe3sa\x95\xf5\xb4\xb5\xce\x0f\x89\x05kGx^7&\x00\x00\u07d4,\u05de\xb5 '\xb1,\x18\x82\x8e>\xaa\xb2\x96\x9b\xfc\u0487\xe9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,\xd8xfV\x8d\xd8\x1a\xd4}\x9d:\u0404nZePss\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4,\xdb9De\x06\x16\xe4|\xb1\x82\xe0`2/\xa1Hyx\u0389b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4,\xe1\x1a\x92\xfa\xd0$\xff+>\x87\xe3\xb5B\xe6\xc6\r\xcb\u0656\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4-\x03&\xb2?\x04\t\xc0\xc0\xe9#hc\xa13\aZ\x94\xba\x18\x89\vg\x9b\xe7[\xe6\xae\x00\x00\u07d4-\r\xecQ\xa6\xe8s0\xa6\xa8\xfa*\x0fe\u060dJ\xbc\xdfs\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4-#vkok\x05s}\xad\x80\xa4\x19\xc4\x0e\xdaMw\x10>\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4-+\x03#Y\xb3c\x96O\xc1\x1aQ\x82c\xbf\xd0T1\xe8g\x89\b\x1c\x1d\xf7b\x9ep\x00\x00\u07d4-4\x80\xbf\be\aJr\xc7u\x9e\xe5\x13{Mp\xc5\x1c\xe9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4-5\xa9\xdfbu\u007f\u007f\xfa\xd1\x04\x9a\xfb\x06\xcaJ\xfcFLQ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4-@U\x8b\x06\xf9\n9#\x14U\x92\x12;gt\xe4n1\xf4\x8965\u026d\xc5\u07a0\x00\x00\u07d4-Bi\x12\xd0Y\xfa\xd9t\v.9\n.\xea\xc0To\xf0\x1b\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4-S-\xf4\xc69\x11\xd1\u0391\xf6\xd1\xfc\xbf\xf7\x96\x0fx\xa8\x85\x89Z\x85\x96\x8aXx\u0680\x00\u07d4-S\x91\xe98\xb3HX\u03d6[\x84\x051\xd5\xef\xdaA\v\t\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94-[B\xfcY\xeb\xda\r\xfdf\xae\x91K\u008c\x1b\nn\xf8:\x8a+\u0235\x9f\xdc\xd86c\x80\x00\u07d4-]s5\xac\xb06+G\u07e3\xa8\xa4\xd3\xf5\x94\x95D\u04c0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-a\xbf\xc5hs\x92<+\x00\t]\xc3\xea\xa0\xf5\x90\u062e\x0f\x8a\x04ef\xdf\xf8\xceU`\x00\x00\u07d4-e\x11\xfdz8\x00\xb2hT\xc7\xec9\xc0\u0735\xf4\xc4\xe8\xe8\x89\x15\xad\u077a/\x9ew\x00\x00\u07d4-}\\@\u076f\xc4P\xb0Jt\xa4\u06bc+\xb5\xd6e\x00.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4-\x89\xa8\x00jO\x13z \xdc+\xecF\xfe.\xb3\x12\xea\x96T\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4-\x8cR2\x9f8\u04a2\xfa\x9c\xba\xf5\u0143\xda\xf1I\v\xb1\x1c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4-\x8e\x06\x18\x92\xa5\xdc\xce!\x96j\xe1\xbb\a\x88\xfd>\x8b\xa0Y\x89\r\x8e\\\xe6\x17\xf2\xd5\x00\x00\u07d4-\x8e[\xb8\xd3R\x16\x95\xc7~|\x83N\x02\x91\xbf\xac\xeet\b\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4-\x90\xb4\x15\xa3\x8e.\x19\xcd\xd0/\U000ed069z\xf7\xcb\xf6r\x89\x05\xf3\xc7\xf6A1\xe4\x00\x00\u07d4-\x9b\xado\x1e\xe0*p\xf1\xf1=\xef\\\u0332z\x9a'@1\x89a\t=|,m8\x00\x00\u07d4-\x9c_\xec\u04b4O\xbbj\x1e\xc72\xea\x05\x9fO\x1f\x9d+\\\x896\xca2f\x1d\x1a\xa7\x00\x00\xe0\x94-\xa6\x17iP\t\xccW\xd2j\u0510\xb3*]\xfb\xeb\x93N^\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4-\xa7k|9\xb4 \u323a,\x10 \xb0\x85k\x02pd\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4-\u01ddn\u007fU\xbc\xe2\xe2\xd0\xc0*\xd0|\uca3bR\x93T\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94-\xca\x0eD\x9a\xb6F\xdb\xdf\u04d3\xa9fb\x96\v\u02b5\xae\x1e\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4-\xd3%\xfd\xff\xb9{\x19\x99R\x84\xaf\xa5\xab\xdbWJ\x1d\xf1j\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4-\xd5x\xf7@}\xfb\xd5H\xd0^\x95\xcc\u00dcHT)bj\x89\u3bb5sr@\xa0\x00\x00\u07d4-\xd8\xee\xef\x87\x19J\xbc,\xe7X]\xa1\xe3[|\xeax\f\xb7\x8965\xc6 G9\u0640\x00\u07d4-\xdf@\x90Wi\xbc\xc4&\xcb,)8\xff\xe0w\xe1\u8758\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4-\xe0\x96D\x00\u0082\xbd\u05ca\x91\x9ck\xf7|k_yay\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-\xe3\x1a\xfd\x18\x9a\x13\xa7o\xf6\xfes\xea\xd9\xf7K\xb5\u0126)\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4-\xec\x982\x9d\x1f\x96\u00e5\x9c\xaay\x81uTR\xd4\xdaI\u0549\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-\ue422\x8f\x19-gj\x87s#+V\xf1\x8f#\x9e/\xad\x8a\x03\xef\xa7\xe7G\xb6\u046d\x00\x00\xe0\x94.\b\x80\xa3E\x96#\a \xf0Z\xc8\xf0e\xaf\x86\x81\u0736\u008a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4.\fW\xb4qP\xf9Z\xa6\xa7\xe1j\xb9\xb1\xcb\xf5C(\x97\x9a\x89\x05k\xc7^-c\x10\x00\x00\u07d4.\x10\x91\v\xa6\xe0\xbc\x17\xe0UUf\x14\u02c7\t\x0fM~[\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94.$\xb5\x97\x87;\xb1A\xbd\xb27\xea\x8aZ\xb7Gy\x9a\xf0-\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4.(\x10\xde\xe4J\xe4\xdf\xf3\xd8cB\xab\x12fW\xd6S\xc36\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4.,\xbdz\xd8%G\xb4\xf5\xff\x8b:\xb5o\x94*dE\xa3\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4.-~\xa6k\x9fG\xd8\xccR\xc0\x1cR\xb6\u147c}G\x86\x89\xd8\xd4`,&\xbfl\x00\x00\u07d4.C\x93H\u07caBw\xb2*v\x84W\xd1\x15\x8e\x97\xc4\t\x04\x89*\x1e\x9f\xf2o\xbfA\x00\x00\xe0\x94.F\xfc\xeej;\xb1E\xb5\x94\xa2C\xa3\x91?\xce]\xado\xba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794.G\xf2\x87\xf4\x98#7\x13\x85\r1&\x82<\xc6}\xce\xe2U\x88\u029d\x9e\xa5X\xb4\x00\x00\u07d4.N\u1b99j\xa0\xa1\xd9$(\xd0fR\xa6\xbe\xa6\xd2\xd1]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.R\x91+\xc1\x0e\xa3\x9dT\xe2\x93\xf7\xae\u05b9\x9a\x0fLs\xbe\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4.a\x9fW\xab\xc1\u91ea\x93j\xe3\xa2&Ib\xe7\xeb-\x9a\x89(\xfb\x9b\x8a\x8aSP\x00\x00\u07d4.d\xa8\xd7\x11\x11\xa2/L]\xe1\xe09\xb36\xf6\x8d9\x8a|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.i3T=O,\xc0\vSP\xbd\x80h\xba\x92C\u05be\xb0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94.~\x05\xe2\x9e\u0767\xe4\xae%\xc5\x175C\xef\xd7\x1fm=\x80\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4.\u007fFU \xec5\xcc#\u058eue\x1b\xb6h\x95D\xa1\x96\x898\xec[r\x1a\x1a&\x80\x00\u07d4.\x8e\xb3\nqn_\xe1\\t#>\x03\x9b\xfb\x11\x06\xe8\x1d\x12\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94.\x98$\xb5\xc12\x11\x1b\xca$\xdd\xfb\xa7\xe5u\xa5\xcdr\x96\xc1\x8a\x03\xa4\x84Qnm\u007f\xfe\x00\x00\u07d4.\xa5\xfe\xe6?3z7nK\x91\x8e\xa8!H\xf9MH\xa6&\x89e\x0f\x8e\r\u0493\xc5\x00\x00\u07d4.\xafN*F\xb7\x89\xcc\u0088\xc8\xd1\xd9)N?\xb0\x858\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.\xaf\xf9\xf8\xf8\x110d\u04d5z\xc6\xd6\xe1\x1e\xeeB\xc8\x19]\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4.\xba\fn\xe5\xa1\x14\\\x1cW9\x84\x96:`]\x88\nz \x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4.\xc9X\"\xeb\x88{\xc1\x13\xb4q*M\xfd\u007f\x13\xb0\x97\xb5\xe7\x8965\u026d\xc5\u07a0\x00\x00\u07d4.\xcaj<]\x9fD\x9d\tV\xbdC\xfa{M{\xe8CYX\x89lk\xdaip\x9c\xc2\x00\x00\xe0\x94.\xca\xc5\x04\xb23\x86n\xb5\xa4\xa9\x9e{\u0490\x13Y\xe4;=\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4.\xeb\xf5\x942\xb5(\x92\xf98\v\xd1@\xaa\x99\xdc\xf8\xad\f\x0f\x89\b=lz\xabc`\x00\x00\u07d4.\xee\xd5\x04q\xa1\xa2\xbfS\xee0\xb1#.n\x9d\x80\xef\x86m\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4.\xefk\x14\x17\u05f1\x0e\xcf\xc1\x9b\x12:\x8a\x89\xe7>RlX\x89 \x86\xac5\x10R`\x00\x00\u07d4.\xf8i\xf05\vW\xd54x\xd7\x01\xe3\xfe\xe5)\xbc\x91\x1cu\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4.\xf9\xe4eqj\xca\u03f8\xc8%/\xa8\xe7\xbcyi\xeb\xf6\u4255\x9e\xb1\xc0\xe4\xae \x00\x00\xe0\x94.\xfcLd}\xacj\xca\xc3Uw\xad\"\x17X\xfe\xf6ao\xaa\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4/\x13eu&\xb1w\xca\xd5G\u00d0\x8c\x84\x0e\xffd{E\u0649?v\x84\x9c\xf1\xee,\x80\x00\u07d4/\x18}ZpMZ3\x8c[(v\xa0\x90\xdc\xe9d(N)\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94/%#\u0303O\x00\x86\x05$\x02bb\x96gQ\x86\xa8\u508a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4/(*\xbb\xb6\u0523\xc3\xcd;\\\xa8\x12\xf7d>\x800_\x06\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4/+\xba\x1b\x17\x96\x82\x1avo\xced\xb8O(\xech\xf1Z\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4/1]\x90\x16\xe8\xee_Sf\x81 /\x90\x84\xb02TMM\x898<\xd1+\x9e\x86<\x00\x00\u07d4/M\xa7SC\x0f\xc0\x9es\xac\xbc\xcd\xcd\xe9\xdad\u007f+]7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4/P\x80\xb8?~-\xc0\xa1\xdd\x11\xb0\x92\xad\x04+\xffx\x8fL\x89\xb4\xf8\xfby#\x1d+\x80\x00\u07d4/a\uf941\x9dp_+\x1eN\xe7T\xae\xb8\xa8\x19Pju\x89O%\x91\xf8\x96\xa6P\x00\x00\xe0\x94/f\xbf\xbf\"b\xef\u030d+\xd0DO\u0170ib\x98\xff\x1e\x8a\x02\x1a\xd95\xf7\x9fv\xd0\x00\x00\u07d4/m\xce\x130\u015e\xf9!`!TW-MK\xac\xbd\x04\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4/}2\x90\x85\x1b\xe5\u01b4\xb4?}Et2\x9fa\xa7\x92\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4/\x858\x17\xaf\u04f8\xf3\xb8n\x9f`\xeew\xb5\xd9ws\xc0\xe3\x89N\xae\xeaD\xe3h\xb9\x00\x00\u07d4/\xa4\x91\xfbY \xa6WN\xbd(\x9f9\xc1\xb2C\r-\x9aj\x89lk\x93[\x8b\xbd@\x00\x00\u07d4/\xb5f\xc9K\xbb\xa4\xe3\xcbg\xcd\xda}_\xadq1S\x91\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4/\xbbPJ]\xc5'\xd3\xe3\xeb\x00\x85\xe2\xfc<}\xd58\xcbz\x89C\u00b1\x8a\xec<\n\x80\x00\u07d4/\xbc\x85y\x8aX5\x98\xb5\"\x16mn\x9d\xda\x12\x1db}\xbc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4/\xbc\xef3\x84\xd4 \xe4\xbfa\xa0f\x99\x90\xbcpT\U00065bc9lk\x93[\x8b\xbd@\x00\x00\xe0\x94/\xc8.\xf0v\x93#A&Oaz\f\x80\xddW\x1ej\xe99\x8a\x01\x84$\xf5\xf0\xb1\xb4\xe0\x00\x00\u07d4/\u075by\u07cd\xf50\xadc\xc2\x0eb\xafC\x1a\xe9\x92\x16\xb8\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4/\xe0\x02?W\"e\x0f:\x8a\xc0\x10\t\x12^t\xe3\xf8.\x9b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4/\xe0\xccBKS\xa3\x1f\t\x16\xbe\b\xec\x81\xc5\v\xf8\xea\xb0\xc1\x89 \x86\xac5\x10R`\x00\x00\u07d4/\xe1:\x8d\a\x85\u0787X\xa5\xe4\x18v\xc3n\x91l\xf7Pt\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4/\xea\x1b/\x83O\x02\xfcT3?\x8a\x80\x9f\x048\xe5\x87\n\xa9\x89\x01\x18T\xd0\xf9\xce\xe4\x00\x00\u07d4/\xee6\xa4\x9e\xe5\x0e\xcfqo\x10G\x91VFw\x9f\x8b\xa0?\x899B\"\xc4\u0686\xd7\x00\x00\u07d4/\xef\x81G\x8aK.\x80\x98\xdb_\xf3\x87\xba!S\xf4\xe2+y\x896'\xe8\xf7\x127<\x00\x00\u07d4/\xf1`\xc4Or\xa2\x99\xb5\xec-q\xe2\x8c\xe5Dm/\u02ef\x89\x13\x84\x00\xec\xa3d\xa0\x00\x00\u07d4/\xf1\xcaU\xfd\x9c\xec\x1b\x1f\xe9\U00029af7LQ<\x1e*\xaa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94/\xf5\u02b1,\r\x95\u007f\xd33\xf3\x82\xee\xb7Q\a\xa6L\xb8\xe8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94/\xf80\xcfU\xfb\x00\u0560\xe05\x14\xfe\xcdD1K\xd6\xd9\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4/\xfe\x93\xec\x1aV6\xe9\xee4\xafp\xdf\xf5&\x82\xe6\xffpy\x89lk\x93[\x8b\xbd@\x00\x00\u07d40\x03y\x88p&q\xac\xbe\x89,\x03\xfeW\x88\xaa\x98\xaf(z\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d40$\x8dX\xe4\x14\xb2\x0f\xed:lH+Y\xd9\xd8\xf5\xa4\xb7\xe2\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4019\xbcYd\x03\xd5\u04d3\x1fwLf\u013aFtT\u06c9\\%\xe1J\xea(?\x00\x00\u079408\x00\x87xie\x14\x9e\x81B;\x15\xe3\x13\xba2\xc5\u01c3\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d40:0\xacB\x86\xae\x17\xcfH=\xad{\x87\fk\xd6M{J\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d40?\xba\xeb\xbeF\xb3[n[t\x94j_\x99\xbc\x15\x85\xca\xe7\x89/\x9a\xc0i_[\xba\x00\x00\u07d40ADZ3\xba\x15\x87A\x16\r\x9c4N\xb8\x8e\\0o\x94\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d40H\x01d\xbc\xd8It\xeb\xc0\xd9\f\x9b\x9a\xfa\xb6&\xcd\x1cs\x89+^:\xf1k\x18\x80\x00\x00\u07d40N\u019atTW!\xd71j\xefM\u03f4\x1a\u015e\xe2\xf0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x940Q\x182\x91\x8d\x804\xa7\xbe\xe7.\xf2\xbf\xeeD\x0e\u02fc\xf6\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d40Q?\u029f6\xfdx\x8c\xfe\xa7\xa3@\xe8m\xf9\x82\x94\xa2D\x89\x18;_\x03\xb1G\x9c\x00\x00\u07d40U\xef\xd2`)\xe0\xd1\x1b\x93\r\xf4\xf5;\x16,\x8c?\xd2\u0389\x1b\x1a\b\x927\a=\x00\x00\u07d40]&\xc1\v\xdc\x10?k\x9c!'.\xb7\xcb-\x91\b\xc4~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d40_x\xd6\x18\xb9\x90\xb4)[\xac\x8a-\xfa&(\x84\xf8\x04\xea\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x940d\x89\x9a\x96<Gy\xcb\xf6\x13\xcdi\x80\x84j\xf1\xe6\xece\x8a\x01{w<\xe6\xe5\xdf\n\x00\x00\u07d40s\x04f\xb8\xebm\xc9\rT\x96\xaav\xa3G-}\xbe\v\xbe\x89lh\xcc\u041b\x02,\x00\x00\u07d40t,\xcd\xf4\xab\xbc\xd0\x05h\x1f\x81Y4\\\x9ey\x05K\x1a\x89$=M\x18\"\x9c\xa2\x00\x00\xe0\x940\x83\xef\x0e\xd4\xc4@\x11\x96wJ\x95\xcfN\u0703\xed\xc1HO\x8a#\xff\xb7\xedee\xd6@\x00\x00\u07d40\x8d\xd2\x1c\xeb\xe7U\x12g\x04\xb4\x8c\x0f\r\xc24\xc6\v\xa9\xb1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d40\x90\xf8\x13\x0e\xc4Df\xaf\xad\xb3n\xd3\xc9&\x139cg{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d40\x95D\xb6#,=\xd77\xf9E\xa01\x93\u045b_?e\xb9\x89:\xf3B\xf6~\xf6\xc8\x00\x00\u07d40\x96\u0723A\b\b[\xcf\x04\xaer\xb9Et\xa1>\x1a>\x1d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d40\x98\xb6]\xb9>\xca\xca\xf75<H\x80\x83\x90\xa2#\xd5v\x84\x89\x18d\x84\xcf{\xb6\xa4\x80\x00\u07d40\xa9\xdarWLQ\xe7\xee\t\x04\xba\x1fs\xa6\xb7\xb8;\x9b\x9d\x89\x01\x18T\xd0\xf9\xce\xe4\x00\x00\u07d40\xac\xd8X\x87_\xa2N\xef\rW/\xc7\xd6*\xad\x0e\xbd\xdc5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d40\xb6aP\xf1\xa64W\x02?\xddE\xd0\xccl\xb5N\f\x0f\x06\x8965\u026d\xc5\u07a0\x00\x00\u07d40\xbbCW\xcdi\x10\xc8m\"8\xbfr|\xbe\x81Vh\x0eb\x89\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d40\xbfa\xb2\xd8w\xfe\x10cQ&2o\xa1\x89\u4c31\u00f0\x897\xb4\x89\x85\xa5\xd7\xe6\x00\x00\u07d40\xc0\x11B\x90z\xcb\x15e\xf7\x048\xb9\x98\n\xe71\x81\x878\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x940\xc2j\x8e\x97\x1b\xaa\x18U\xd63\xbap?\x02\x8c\u01c71@\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d40\xdbk\x9b\x10~b\x10/CJ\x9d\u0416\f !\xf5\xceL\x89 \x83\x17\x9bnBS\x00\x00\u07d40\xe33X\xfc!\xc8P\x06\xe4\x0f25}\u0209Y@\xaa\xf0\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d40\xe6\t\x00\xca\xccr\x03\xf3\x14\xdc`CG%Qg\xfc*\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d40\u7273\xd2F^\x94nb\x10\xfa[5\xdeN\x8c\x93\b_\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x940\xe9i\x8c\xf1\xe0\x8a\x9d\x04\x8b\xd8\xd8\x04\x8f(\xbe~\xd9@\x9f\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d40\xe9\u0560\b\x8f\x1d\xdb/\u04c0\xe2\xa0I\x19\"f\xc5\x1c\xbf\x89\n\xac\xac\u0679\xe2+\x00\x00\u07d40\xea\xc7@\xe4\xf0,\xb5n\xef\x05&\xe5\xd3\x002&\x00\xd0>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d40\uc4d2$J!\b\u0247\xbc\\\xdd\xe0\ud7c3z\x81{\x89T\x99%\xf6\xc9\xc5%\x00\x00\xe0\x940\xed\x11\xb7{\xc1~^f\x94\u023c[nG\x98\xf6\x8d\x9c\xa7\x8a\x1eo\xb3B\x1f\xe0)\x9e\x00\x00\u07d40\xf7\xd0%\xd1o{\xee\x10U\x80Ho\x9fV\x1c{\xae?\xef\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x940\xfb\xe5\x88_\x9f\xcc\xe9\xea^\u06c2\xedJ\x11\x96\xdd%\x9a\xed\x8a\x01\x19\xe4\u007f!8\x1f@\x00\x00\u07d41\x04}p?c\xb94$\xfb\xbdn/\x1f\x9et\xde\x13\xe7\t\x89\x9a\x81f\xf7\u6ca7\x80\x00\u07d411?\xfdc[\xf2\xf32HA\xa8\x8c\a\xed\x14aD\xce\xeb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d41Y\xe9\fH\xa9\x15\x90J\xdf\u24b2/\xa5\xfd^ryk\x896\xaf\xe9\x8f&\x06\x10\x00\x00\u07d41]\xb7C\x9f\xa1\u0574#\xaf\xa7\xddq\x98\xc1\xcft\xc9\x18\xbc\x89 \x86\xac5\x10R`\x00\x00\u07d41^\xf2\xdab\x0f\xd30\xd1.\xe5]\xe5\xf3)\xa6\x96\xe0\xa9h\x89\b!\xab\rD\x14\x98\x00\x00\u07d41n\x92\xa9\x1b\xbd\xa6\x8b\x9e/\x98\xb3\xc0H\x93N<\xc0\xb4\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d41n\xb4\xe4}\xf7\x1bB\xe1mo\xe4h%\xb72{\xaf1$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d41q\x87~\x9d\x82\f\xc6\x18\xfc\t\x19\xb2\x9e\xfd3?\xdaI4\x8965\u026d\xc5\u07a0\x00\x00\u07d41|\xf4\xa2<\xb1\x91\xcd\xc5c\x12\u009d\x15\xe2\x10\xb3\xb9\xb7\x84\x89\a\xcef\xc5\x0e(@\x00\x00\u07d41\x8b.\xa5\xf0\xaa\xa8y\xc4\xd5\xe5H\xac\x9d\x92\xa0\xc6t\x87\xb7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d41\x8cv\xec\xfd\x8a\xf6\x8dpUSR\xe1\xf6\x01\xe3Y\x88\x04-\x89\x1b1\x19.h\xc7\xf0\x00\x00\u07d41\x8f\x1f\x8b\xd2 \xb0U\x8b\x95\xfb3\x10\x0f\xfd\xbbd\r|\xa6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d41\xaa;\x1e\xbe\x8cM\xbc\xb6\xa7\b\xb1\xd7H1\xe6\x0eIv`\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d41\xab\b\x89f\xec\xc7\"\x92X\xf6\t\x8f\xceh\xcf9\xb3\x84\x85\x8965\u026d\xc5\u07a0\x00\x00\xe0\x941\xadM\x99F\xef\t\xd8\xe9\x88\xd9F\xb1\"\u007f\x91A\x90\x176\x8a\x04\xd8S\xc8\xf8\x90\x89\x80\x00\x00\xe0\x941\xb4;\x01]\x00\x81d<l\xdaF\xa7\a:m\xfd\xbc\xa8%\x8a\n\x97\x91e \xcd\x18\xe8\x00\x00\u07d41\xcc\xc6\x16\xb3\x11\x82h\xe7]\x9a\xb8\x99l\x88X\xeb\xd7\xf3\u00c9\x15\xae\x0fw\x1c\xa1R\x00\x00\u07d41\xd8\x1dRl\x19^?\x10\xb5\xc6\xdbR\xb5\xe5\x9a\xfb\u0a55\x89\x0eO\xbciD\x9f \x00\x00\u07d41\xe9\xc0\x0f\f jNN~\x05\"\x17\r\xc8\x1e\x88\xf3\xebp\x89\x91\x8d\xdc:B\xa3\xd4\x00\x00\u07d41\xea\x12\u051a5\xa7@x\r\xde\xea\xec\xe8L\b5\xb2bp\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d41\xean\xab\x19\xd0\ad\xe9\xa9^\x18?+\x1b\"\xfc}\xc4\x0f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d41\xeb\x12<\x95\xc8+\xf6\x85\xac\xe7\xa7Z\x18\x81\xa2\x89\xef\xca\x10\x891\xe0\t`sq\xbd\x00\x00\u07d41\ud147\x88\xbd\xa4\xd5'\t\x92\"\x1c\xc0B\x06\xecba\r\x89?\xc0GIH\xf3`\x00\x00\u07d41\xf0\x06\xf3IN\xd6\xc1n\xb9*\xaf\x90D\xfa\x8a\xbb_\u0563\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x942\x01%\x9c\xafsJ\xd7X\x1cV\x10Q\xba\v\xca\u007f\u0594k\x8a&\x1d\xd1\xce/ \x88\x80\x00\x00\u07d42\x03N\x85\x81\xd9HN\x8a\xf4*(\xdf\x19\x012\xec)\xc4f\x89\xbb\x91%T\"c\x90\x00\x00\u07d42 !\x02&x\xa0\x16m K:\xaaz\xd4\xecK\x88\xb7\u0409\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d42%\xc1\xca_*\x9c\x88\x15k\xb7\xd9\xcd\xc4J2fS\xc2\x14\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d42'\x88\xb5\xe2\x9b\xf4\xf5\xf5Z\xe1\u0773 \x85\xfd\xa9\x1b\x8e\xbe\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d42-o\x9a\x14\r!?L\x80\xcd\x05\x1a\xfe%\xc6 \xbfL}\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d42.\\C\xb0\xf5$8\x96U\xa9\xb3\xff$\xf2\xd4\xdb=\xa1\x0f\x89\xfc\x13\xb6\x9b>~h\x00\x00\u07d424\x86\xcad\xb3uGO\xb2\xb7Y\xa9\xe7\xa15\x85\x9b\xd9\xf6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d427I\xa3\xb9q\x95\x9eF\u0234\x82-\xca\xfa\xf7\xaa\xf9\xbdn\x89\x01\x16q\xa5\xb2Ep\x00\x00\u07d42:\xadA\xdfKo\xc8\xfe\u038c\x93\x95\x8a\xa9\x01\xfah\bC\x894\x95tD\xb8@\xe8\x00\x00\xe0\x942;<\xfe>\xe6+\xbd\xe2\xa2a\xe5<\xb3\xec\xc0X\x10\xf2\u018a\x02\ub3b1\xa1r\u0738\x00\x00\u07d42?\xca^\xd7\u007fi\x9f\x9d\x990\xf5\xce\xef\xf8\xe5oY\xf0<\x89Hz\x9a0E9D\x00\x00\u07d42H\\\x81\x87(\xc1\x97\xfe\xa4\x87\xfb\xb6\xe8)\x15\x9e\xba\x83p\x899!\xb4\x13\xbcN\xc0\x80\x00\xe0\x942P\xe3\xe8X\xc2j\xde\u032d\xf3jVc\xc2*\xa8LAp\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x942Y\xbd/\xdd\xfb\xbco\xba\u04f6\xe8t\xf0\xbb\xc0,\xda\x18\xb5\x8a\x02\x84`VI[\r\x18\x80\x00\u07d42uIo\xd4\u07491\xfdi\xfb\n\v\x04\xc4\xd1\xff\x87\x9e\xf5\x89\x18-~L\xfd\xa08\x00\x00\u07d42{\xb4\x9euOo\xb4\xf73\xc6\xe0o9\x89\xb4\xf6]K\xee\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d42\x82y\x1do\xd7\x13\xf1\xe9OK\xfdV^\xaax\xb3\xa0Y\x9d\x89Hz\x9a0E9D\x00\x00\u07d42\x83\xeb\u007f\x917\xdd9\xbe\xd5_\xfek\x8d\xc8E\xf3\xe1\xa0y\x89\x03\x97\n\xe9!Ux\x00\x00\u07d42\x86\t\x97\xd70\xb2\xd8;s$\x1a%\xd3f}Q\xc9\b\xef\x89\x1b\x1a\b\x927\a=\x00\x00\xe0\x942\x86\u047cez1,\x88G\xd9<\xb3\xcbyP\xf2\xb0\xc6\xe3\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x942\xa2\r\x02\x8e,b\x18\xb9\xd9[D\\w\x15$cj\"\xef\x8a\x02\x02\xfe\xfb\xf2\xd7\xc2\xf0\x00\x00\u07d42\xa7\x06\x91%\\\x9f\xc9y\x1aOu\u0238\x1f8\x8e\n%\x03\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d42\xb7\xfe\xeb\xc5\u015b\xf6^\x86\x1cL\v\xe4*v\x11\xa5T\x1a\x89w\u9aa8R\\\x10\x00\x00\xe0\x942\xba\x9a}\x04#\xe0:R_\xe2\xeb\xebf\x1d \x85w\x8b\u060a\x04<3\xc1\x93ud\x80\x00\x00\u07d42\xbb.\x96\x93\xe4\xe0\x854M/\r\xbdF\xa2\x83\u3807\xfd\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x942\xc2\xfd\u2daa\xbb\x80\u5ba2\xb9I\xa2\x17\xf3\xcb\t\"\x83\x8a\x010a`\xaf\xdf 7\x80\x00\u07d42\xd9P\xd5\xe9>\xa1\u0574\x8d\xb4qO\x86{\x03 \xb3\x1c\x0f\x897\b\xba\xed=h\x90\x00\x00\u07d42\u06f6qlT\xe81e\x82\x9aJ\xbb6uxI\xb6\xe4}\x8965\u026d\xc5\u07a0\x00\x00\u07d42\xebd\xbe\x1b]\xed\xe4\b\u01bd\xef\xben@\\\x16\xb7\xed\x02\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d42\xef\\\xdcg\x1d\xf5V*\x90\x1a\xee]\xb7\x16\xb9\xbev\xdc\xf6\x89lk\x93[\x8b\xbd@\x00\x00\u07d42\xf2\x9e\x87'\xa7LkC\x01\xe3\xff\xff\x06\x87\xc1\xb8p\xda\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d42\xfa\x0e\x86\xcd\b}\u058di1\x90\xf3-\x931\t\t\xedS\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d42\xfb\xee\xd6\xf6&\xfc\xdf\xd5\x1a\xca\xfbs\v\x9e\xef\xf6\x12\xf5d\x89lk\x93[\x8b\xbd@\x00\x00\u07943\x00\xfb\x14\x9a\xde\xd6[\u02e6\xc0N\x9c\u05b7\xa0;\x89;\xb1\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x943\x01\xd9\xca/;\xfe\x02by\xcdh\x19\xf7\x9a)=\x98\x15n\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\xe0\x943\b\xb04f\xc2z\x17\xdf\xe1\xaa\xfc\xeb\x81\xe1m)4Vo\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07943\x1a\x1c&\xcci\x94\xcd\xd3\xc1K\xec\xe2v\xff\xffK\x9d\xf7|\x88\xfaz\xed\xdfO\x06\x80\x00\xe0\x943&\xb8\x8d\xe8\x06\x18DT\xc4\v'\xf3\t\xd9\xddm\u03f9x\x8a\x03\xca\\f\u067cD0\x00\x00\xe0\x943)\xeb;\xafCE\xd6\x00\xce\xd4\x0en\x99ueo\x117B\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d432\r\xd9\x0f+\xaa\x11\r\xd34\x87*\x99\x8f\x14\x84&E<\x8965f3\xeb\xd8\xea\x00\x00\u07d436\xc3\xefn\x8bP\xee\x90\xe07\xb1d\xb7\xa8\xea_\xaa\xc6]\x89\x0e\u0223\xa7\x1c\"T\x00\x00\xe0\x9438\fo\xffZ\xcd&Q0\x96)\u06daq\xbf? \u017a\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d43:\xd1Yd\x01\xe0Z\xea-6\xcaG1\x8e\xf4\xcd,\xb3\u07c9\x9d\xc0\\\xce(\u00b8\x00\x00\u07d43C@\xeeK\x9c\u0701\xf8P\xa7Q\x16\xd5\x0e\u9d98%\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d43H\x1e\x85n\xbe\u050e\xa7\b\xa2t&\xef(\xe8g\xf5|\u0449\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x943V[\xa9\xda,\x03\xe7x\xce\x12)O\b\x1d\xfe\x81\x06M$\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07943X\x1c\xee#0\x88\xc0\x86\r\x94N\f\xf1\u03ab\xb8&\x1c.\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d43XX\xf7I\xf1i\u02bc\xfeR\xb7\x96\xe3\xc1\x1e\xc4~\xa3\u0089\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x943^\"\x02[zw\u00e0t\u01cb\x8e=\xfe\a\x13A\x94n\x8a\x02'\xcas\n\xb3\xf6\xac\x00\x00\u07d43b\x9b\xd5/\x0e\x10{\xc0q\x17ld\xdf\x10\x8fdw}I\x89\x01\xcf\xddth!n\x80\x00\u07d43{;\u07c6\xd7\x13\xdb\xd0{]\xbf\xcc\x02+z{\x19F\xae\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d43|\xfe\x11W\xa5\u0191 \x10\xddV\x153y\x17i\u00b6\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d43\xb36\xf5\xba^\xdb{\x1c\xcc~\xb1\xa0\u0644\xc1#\x1d\x0e\u0709lk\x93[\x8b\xbd@\x00\x00\u07d43\xc4\a\x13;\x84\xb3\xcaL=\xed\x1fFX\x90\f8\x10\x16$\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\xe0\x943\xd1r\xab\a\\Q\xdb\x1c\xd4\n\x8c\xa8\xdb\xff\r\x93\xb8C\xbb\x8a\x016x\x05\x10\xd1-\xe3\x80\x00\u07d43\xe9\xb7\x18#\x95.\x1ff\x95\x8c'\x8f\u008b\x11\x96\xa6\u0164\x89\x05k\xc7^-c\x10\x00\x00\u07d43\xeakxU\xe0[\a\xab\x80\u06b1\xe1M\xe9\xb6I\xe9\x9bl\x89\x1c\xd6\xfb\xadW\xdb\xd0\x00\x00\u07d43\xf1R#1\rD\u078bf6h_:L=\x9cVU\xa5\x89\r\x94b\xc6\xcbKZ\x00\x00\u07d43\xf4\xa6G\x1e\xb1\xbc\xa6\xa9\xf8[;Hr\xe1\aU\xc8+\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d43\xfbWzM!O\xe0\x10\xd3,\xca|>\xed\xa6?\x87\xce\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d43\xfdq\x8f\v\x91\xb5\xce\u020a]\xc1^\xec\xf0\xec\xef\xa4\xef=\x89\x17r$\xaa\x84Lr\x00\x00\u07d44\x14\x80\u030c\xb4v\xf8\xd0\x1f\xf3\b\x12\xe7\xc7\x0e\x05\xaf\xaf]\x89lk\x93[\x8b\xbd@\x00\x00\u07d44'-^ut1]\xca\u9afd1{\xac\x90(\x9dGe\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x9440\xa1c\x81\xf8i\xf6\xeaT#\x91XU\xe8\x00\x885%\xa9\x8a\x03\xca\\f\u067cD0\x00\x00\u07d441\x86%\x81\x8e\xc1?\x11\x83Z\xe9sS\xce7}oY\n\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d449<]\x91\xb9\xdeYr\x03\xe7[\xacC\t\xb5\xfa=(\u00c9\n\x84Jt$\xd9\xc8\x00\x00\u07d449\x99\x8b$|\xb4\xbf\x8b\xc8\nm+5'\xf1\xdf\xe9\xa6\u0489\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d44C}\x14ed\v\x13l\xb5\x84\x1c?\x93O\x9b\xa0\xb7\t}\x89\t`\xdbwh\x1e\x94\x00\x00\u07d44J\x8d\xb0\x86\xfa\xedN\xfc7\x13\x1b:\"\xb0x-\xadp\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x944fM\"\x0f\xa7\xf3yX\x02J32\u0584\xbc\xc6\xd4\u023d\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d44f\xf6~9cl\x01\xf4;:!\xa0\xe8R\x93%\xc0\x86$\x89-\xb1\x16vP\xac\xd8\x00\x00\u07d44\x856\x1e\xe6\xbf\x06\xefe\b\xcc\xd2=\x94d\x1f\x81M>/\x89lk\x93[\x8b\xbd@\x00\x00\u07d44\x85\xf6!%d3\xb9\x8aB\x00\xda\xd8W\xef\xe5Y7\uc609lk\x93[\x8b\xbd@\x00\x00\u07d44\x95\x8aF\xd3\x0e0\xb2s\xec\xc6\xe5\xd3X\xa2\x12\xe50~\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d44\x97\xddf\xfd\x11\x80q\xa7\x8c,\xb3n@\xb6e\x1c\xc8%\x98\x89\x05\xf1\x01kPv\xd0\x00\x00\xe0\x944\x9a\x81k\x17\xab='\xbb\xc0\xae\x00Q\xf6\xa0p\xbe\x1f\xf2\x9d\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d44\x9d,\x91\x8f\u041e(\a1\x8ef\xceC)\t\x17k\xd5\v\x89<\xb7\x1fQ\xfcU\x80\x00\x00\u07d44\xa0C\x1f\xff^\xad\x92\u007f<id\x96\x16\xdcn\x97\x94_o\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x944\xa8]m$?\xb1\u07f7\xd1\xd2\xd4OSn\x94zL\ue78a\x04<3\xc1\x93ud\x80\x00\x00\u07d44\xa9\x01\xa6\x9f\x03k\u03dfxC\xc0\xba\x01\xb4&\xe8\xc3\xdc+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d44\xb4TAn\x9f\xb4'Nj\xdd\xf8SB\x8a\x01\x98\xd6.\xe1\x89\x16\x10Bw\x9f\x1f\xfc\x00\x00\u07d44\xc8\xe5\xf13\x0f\xcbK\x14\xcau\xcb%\x80\xa4\xb9= N6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x944\u211b\xeaX:\xb0\xcc7\x97Q\x90\xf3\"\xb3\x95\x05U\x82\x8a\x01\xa5\xc5\xe8W\xfd\xf2\xb2\x00\x00\u07d44\xfaw\x92\xba\u063b\xd7\xffd\x05b\x14\xa3>\xb6`\f\x1e\xa8\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d44\xff&\xeb`\xa8\u0469ZH\x9f\xae\x13n\xe9\x1dNX\bL\x89 \x86\xac5\x10R`\x00\x00\u07d44\xffX)R\xff$E\x8f{\x13\xd5\x1f\vO\x98p\"\xc1\xfe\x89\x98\x06\xde=\xa6\xe9x\x00\x00\u07d45\x10k\xa9N\x85c\u0533\xcb<\\i,\x10\xe6\x04\xb7\xce\u0609lk\x93[\x8b\xbd@\x00\x00\xe0\x945\x14_b\x03\x97\u019c\xb8\xe0\tb\x96\x1f\x0fH\x86d9\x89\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d45\x14t0\xc3\x10e\x00\u77e2\xf5\x02F.\x94p<#\xb1\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x945\x17\x87\x845\x05\xf8\xe4\xef\xf4ef\xcc\u695fM\x1c_\xe7\x8a\x01\xf5q\x89\x87fKH\x00\x00\xe0\x945\x1f\x16\xe5\xe0sZ\xf5gQ\xb0\xe2%\xb2B\x11q9@\x90\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\xe0\x945$\xa0\x00#N\xba\xaf\a\x89\xa14\xa2\xa4\x178<\xe5(*\x8a\x011yU\x94}\x8e,\x00\x00\u07d45&\xee\xce\x1ak\xdc>\xe7\xb4\x00\xfe\x93[HF?1\xbe\u05c9\x04w\x87\x9bm\x140\x00\x00\u07d45*x_J\x92\x162PL\xe5\xd0\x15\xf8<I\xaa\x83\x8dm\x89\xe9\xe7\xe0\xfb5\xb7x\x00\x00\xe0\x945-)\xa2n\x8aA\x81\x81\x81tdg\xf5\x82\xe6\xe8@\x12\xe0\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d45.w\xc8ain\xf9j\xd5I4\xf8\x94\xaa\x8e\xa3QQ\u074965\u026d\xc5\u07a0\x00\x00\xe0\x945/%\xba\xbfJi\x06s\xe3Q\x95\xef\xa8\xf7\x9d\x05\x84\x8a\xad\x8a\x0e%<9\xben}\xc0\x00\x00\u07d456E3\"\xc1Fl\xb9\x05\xaf\\3\\\xa8\xdbt\xbf\xf1\xe6\x89\x18;_\x03\xb1G\x9c\x00\x00\u07d45=\xbe\xc4/\x92\xb5\x0f\x97Q)\xb9<L\x99su\xf0\x90s\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d45@\u01fdz\x84B\u057e\xe2\x1a!\x80\xa1\xc4\xed\xff\x16I\xe0\x89C.\xacLo\x05\xb9\x80\x00\u07d45I\xbd@\xbb\xbc+0\t\\\xac\x8b\xe2\xc0z\x05\x88\xe0\xae\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d45R\xa4\x96\xeb\xa6\u007f\x12\xben\xed\xab6\f\xd16a\xdct\x80\x89\x10CV\x1a\x88)0\x00\x00\u07d45T\x94{{\x94{\x00@\xdaR\xca\x18\t%\xc6\u04f8\x8f\xfe\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d45\\\f9\xf5\xd5p\vA\xd3u\xb3\xf1xQ\xdc\xd5$\x01\xf9\x89\u05f3\xb7\xbaZ\xbfL\x00\x00\u07d45\\\xcf\xe0\xe7}U{\x97\x1b\xe1\xa5X\xbc\x02\u07de\xee\x05\x94\x89_\\\xb1\xaf\xc8e(\x00\x00\xe0\x945q\xcfz\xd3\x04\xec\xae\xe5\x95y/K\xbf\xa4\x84A\x85I\u058a\x01;\xcd\r\x89-\x9e\x16\x00\x00\u07d45u\xc7pf\x8a\x9d\x17\x9f\x1e\xf7h\u0093\xf8\x01f\xe2\xaa=\x89\x19\xb2\x12H\xa3\xef(\x00\x00\u07d45z\x02\xc0\xa9\xdf\xe2\x87\xdeD\u007f\xb6zp\xec[b6fG\x89\x01s\x17\x90SM\xf2\x00\x00\u07d45\x85^\xc6A\xab\x9e\b\x1e\xd0\u00a6\xdc\xd8\x13T\xd0$J\x87\x89A'\xab\u94e7\xaa\x80\x00\u07d45\x88\x89Z\xc9\xfb\xaf\xec\x01 \x92\xdc\x05\xc0\xc3\x02\xd9\a@\xfa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d45\x99I<\xe6Wr\u03d3\xe9\x8a\xf1\x19^\xc0\x95]\u0240\x02\x89QQY\fg\xb3(\x00\x00\xe0\x945\xa0\x80\x81y\x91s\xe0\x01\xcc[\xd4j\x02@m\xc9]\x17\x87\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d45\xa5I\xe8\xfdl6\x8dm\u0326\xd2\xe7\u044eM\xb9_R\x84\x89\x1b\x1a\b\x927\a=\x00\x00\u07d45\xa6\x88P\x83\u0219\u06bb\xf50\xedl\x12\xf4\xdd: L\xf5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d45\xaa\xa0F]\x1c&\fB\x0f\xa3\x0e&)\x86\x9f\xb6U\x92\a\x89&7\x81\xe0\xe0\x87\xc8\x00\x00\u07d45\xac\x1d>\xd7FO\xa3\xdb\x14\xe7r\x92\x13\u03aa7\x8c\t^\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d45\xaf\x04\n\f\xc23zv\xaf(\x81T\xc7V\x1e\x1a#3I\x8965\u026d\xc5\u07a0\x00\x00\u07d45\xb0>\xa4$W6\xf5{\x85\xd2\xebyb\x8f\x03m\xdc\xd7\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d45\xbd$he\xfa\xb4\x90\xac\bz\xc1\xf1\xd4\xf2\xc1\r\f\xda\x03\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x945\xbff\x88R/5Fz\u007fu0#\x14\xc0+\xa1v\x80\x0e\x8a\x03\xafA\x82\x02\xd9T\xe0\x00\x00\u07d45\u022d\xc1\x11%C+;w\xac\xd6F%\xfeX\xeb\xee\x9df\x89lk\x93[\x8b\xbd@\x00\x00\u07d45\u0497\x0fI\xdc\xc8\x1e\xa9\xeep~\x9c\x8a\n\xb2\xa8\xbbtc\x89N\x10\x03\xb2\x8d\x92\x80\x00\x00\u07d45\xe0\x96\x12\r\xea\xa5\xc1\xec\xb1d^,\u02cbN\xdb\xd9)\x9a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d45\xea!c\xa3\x8c\u07da\x12?\x82\xa5\xec\x00%\x8d\xae\v\xc7g\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d45\xf1\xda\x12{\x837o\x1b\x88\xc8*3Y\xf6z^g\xddP\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d45\xf2\x94\x9c\xf7\x8b\xc2\x19\xbbO\x01\x90|\xf3\xb4\xb3\u04c6T\x82\x89\x0f\xb5\xc8l\x92\xe44\x00\x00\u07d45\xf5\x86\x01I\xe4\xbb\xc0K\x8a\u0172r\xbeU\xad\x1a\xcaX\xe0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d46\x02E\x8d\xa8omj\x9d\x9e\xb0=\xaf\x97\xfeV\x19\xd4B\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x057-\x93\xa9\x01\t\x88\x01\x8f\x9f1]\x03.\u0448\x0f\xa1\x89\x1b\x1b\xcfQ\x89j}\x00\x00\u07d46\x16\xd4H\x98_]2\xae\xfa\x8b\x93\xa9\x93\xe0\x94\xbd\x85I\x86\x89\v\"\u007fc\xbe\x81<\x00\x00\u07d46\x16\xfbF\xc8\x15x\xc9\xc8\xebM;\xf8\x80E\x1a\x887\x9d}\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d46\x1cu\x93\x16\x96\xbc=B}\x93\xe7lw\xfd\x13\xb2A\xf6\xf4\x89\x1d\xc5\xd8\xfc&m\xd6\x00\x00\u07d46\x1d\x9e\xd8\v[\xd2|\xf9\xf1\"o&u2X\xee_\x9b?\x89\xbfi\x14\xba}r\xc2\x00\x00\u07d46\x1f;\xa9\xed\x95kw\x0f%}6r\xfe\x1f\xf9\xf7\xb0$\f\x89 \x86\xac5\x10R`\x00\x00\u07d46\"|\u07e0\xfd;\x9d~jtF\x85\xf5\xbe\x9a\xa3f\xa7\xf0\x89\n\xc2s\x0e\xe9\xc6\xc1\x80\x00\u07d46/\xbc\xb1\x06b7\n\x06\x8f\xc2e&\x02\xa2Wy7\xcc\xe6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d460\xc5\xe5e\u03aa\x8a\x0f\x0f\xfe2\x87^\xae*l\xe6<\x19\x89\t7r+7t\xd0\x00\x00\u07d463\x9f\x84\xa5\u00b4L\xe5=\xfd\xb6\xd4\xf9}\xf7\x82\x12\xa7\u07c9\x11o\x18\xb8\x17\x15\xa0\x00\x00\u07d464:\xec\xa0{n\u054a\x0eb\xfaN\xcbI\x8a\x12O\xc9q\x89\x10CV\x1a\x88)0\x00\x00\u07d46au@4\x81\xe0\xab\x15\xbbQF\x15\u02f9\x89\xeb\u018f\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d46ro;\x88Z$\xf9)\x96\u0681b^\u022d\x16\xd8\xcb\xe6\x89S\xafu\u0441HW\x80\x00\xe0\x946s\x95C\x99\xf6\u07feg\x18\x18%\x9b\xb2x\xe2\xe9.\xe3\x15\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d46u\x8e\x04\x9c\u064b\u03a1\"w\xa6v\xf9)sb\x89\x00#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d46\u007fY\u0302yS)8NA\xe1(1\x15\xe7\x91\xf2j\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x81\x0f\xf9\xd2\x13\xa2q\xed\xa2\xb8\xaay\x8b\xe6T\xfaK\xbe\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x8cT\x14\xb5k\x84U\x17\x1f\xbf\ab \xc1\u02e4\xb5\xca1\x89\x1e>\xf9\x11\xe8=r\x00\x00\xe0\x946\x90$k\xa3\xc8\x06y\xe2.\xacD\x12\xa1\xae\xfc\xe6\xd7\u0342\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d46\x92\x8bU\xbc\x86\x15\t\xd5\x1c\x8c\xf1\xd5F\xbf\xecn>\x90\xaf\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d46\x98\"\xf5W\x8b@\xdd\x1fDqpk\"\u0357\x13R\xdak\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d46\x9e\xf7a\x19_:7>$\xec\xe6\xcd\"R\x0f\xe0\xb9\xe8n\x89\x1c\xff\xaf\xc9M\xb2\b\x80\x00\u07d46\xa0\x8f\xd6\xfd\x1a\xc1|\xe1^\xd5~\xef\xb1*+\u2048\xbf\x89Hz\x9a0E9D\x00\x00\u07d46\xa0\xe6\x1e\x1b\xe4\u007f\xa8~0\xd3(\x88\xee\x030\x90\x1c\xa9\x91\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x946\xb2\xc8^:\xee\xeb\xb7\rc\u0124s\f\xe2\xe8\xe8\x8a6$\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x946\xbfC\xff5\u07d0\x90\x88$3l\x9b1\xce3\x06~/P\x8aIr\x15\x10\xc1\xc1\xe9H\x00\x00\u07d46\xbf\xe1\xfa;{p\xc1r\xeb\x04/h\x19\xa8\x97%\x95A>\x8965\u026d\xc5\u07a0\x00\x00\xe0\x946\xc5\x10\xbf\x8dnV\x9b\xf2\xf3}G&]\xbc\xb5\x02\xff+\u038a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x946\xd8]\xc3h1V\xe6;\xf8\x80\xa9\xfa\xb7x\x8c\xf8\x14:'\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d46\u07cf\x88<\x12s\xec\x8a\x17\x1fz3\xcf\xd6I\xb1\xfe`u\x89\fRHJ\xc4\x16\x89\x00\x00\xe0\x946\xe1Va\f\xd8\xffd\xe7\x80\u061d\x00T8\\\xa7gU\xaa\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d46\xfe\xc6,,B^!\x9b\x18D\x8a\xd7W\x00\x9d\x8cT\x02o\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d47\x00\xe3\x02t$\xd99\xdb\xde]B\xfbx\xf6\xc4\xdb\xec\x1a\x8f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d47\x02\xe7\x04\xcc!at9\xadN\xa2zW\x14\xf2\xfd\xa1\xe92\x8965\u026d\xc5\u07a0\x00\x00\u07d47\x035\fMo\xe374,\xdd\xc6[\xf1\xe28k\xf3\xf9\xb2\x89m\x81!\xa1\x94\xd1\x10\x00\x00\xe0\x947\b\xe5\x9d\xe6\xb4\x05P\x88x)\x02\xe0W\x9cr\x01\xa8\xbfP\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d47\x126~^U\xa9mZ\x19\x16\x8fn\xb2\xbc~\x99q\xf8i\x8965\u026d\xc5\u07a0\x00\x00\u07d47\x19Zc]\xccb\xf5jq\x80I\xd4~\x8f\x9f\x96\x83(\x91\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d47'4\x1f&\xc1 \x01\xe3x@^\xe3\x8b-\x84d\xecq@\x89lk\x93[\x8b\xbd@\x00\x00\u07d47.E:kb\x9f'g\x8c\u022e\xb5\xe5|\xe8^\xc0\xae\xf9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d474\xcb\x18t\x91\xed\xe7\x13\xae[;-\x12(J\xf4k\x81\x01\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d477!n\xe9\x1f\x17w2\xfbX\xfa@\x97&r\a\xe2\xcfU\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d47<T~\f\xb5\xcec.\x1cZ\xd6aUr\f\x01\xc4\t\x95\x89\xfeT\xdc\xdc\xe6\xc5Z\x00\x00\u07d47l\xd7Ws\x83\xe9\x02\x95\x1b`\xa2\x01{\xa7\xea)\xe35v\x89lk\x93[\x8b\xbd@\x00\x00\u07d47\x8e\xa1\u070e\xdc\x19\xba\xe8&8\x02\x9e\xa8u,\xe9\x8b\xcf\u0349lk\x93[\x8b\xbd@\x00\x00\u07d47\x8f7$??\xf0\xbe\xf5\xe1\u0705\xebC\b\xd94\f)\xf9\x89lnY\xe6|xT\x00\x00\u07d47\x95\x9c \xb7\xe9\x93\x1dr\xf5\xa8\xae\x86\x9d\xaf\u076d;m\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d47\x9a\u007fuZ\x81\xa1~\xdb}\xaa\xa2\x8a\xfcf]\xfak\xe6:\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d47\x9cqf\x84\x9b\xc2J\x02\xd6S^-\xef\x13\xda\xee\xf8\xaa\x8d\x89\x05k\xc7^-c\x10\x00\x00\xe0\x947\xa0Z\u03b99\\\x865\xa3\x9a|]&j\xe6\x10\xd1\v\xf2\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d47\xa1\x04Q\xf3af\xcfd=\xd2\xdel\x1c\xbb\xa8\xa0\x11\u03e3\x89\x14\x99\x8f2\xacxp\x00\x00\u07d47\xa7\xa6\xffN\xa3\xd6\x0e\xc3\a\xcaQjH\xd3\x05;\xb7\x9c\xbb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x947\xabf\b:O\xa28H\xb8\x86\xf9\xe6my\xcd\xc1P\xccp\x8a\x12\xbe\"\xff\xb5\xec\x008\x00\x00\u07d47\xac)\xbd\xa9?I{\u012e\xaa\xb95E,C\x15\x104\x1e\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d47\xb8\xbe\xac{\x1c\xa3\x88)\xd6\x1a\xb5R\xc7f\xf4\x8a\x10\xc3/\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d47\xbb\xc4r\x12\xd8/\xcb^\xe0\x8fR%\xec\xc2\x04\x1a\xd2\xda}\x89\xb1\xcf$\xdd\u0431@\x00\x00\u07d47\u02c6\x8d,?\x95\xb2Wa\x1e\xb3JA\x88\u054bt\x98\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d47\u0640\xa1.\xe3\xbf#\xcc\\\xdbc\xb4\xaeEi\x1ft\xc87\x89lk\x93[\x8b\xbd@\x00\x00\u07d47\xe1i\xa98\b\xd8\x03V\x98\xf8\x15\xc7#V\x13\xc1\xe6Y\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d47\xea\u0693\xc4u\xde\xd2\xf7\xe1^w\x87\xd4\x00G\x0f\xa5 b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d47\xfa\xc1\xe6\xbc\x12.\x93m\xfb\x84\xde\fK\xefn\r`\xc2\u05c9lk\x93[\x8b\xbd@\x00\x00\u07d48\a\xef\xf4:\xa9|v\x91\n\x19u-\xd7\x15\xee\x01\x82\xd9N\x89\r\x90\x15oo\xc2\xfb\x00\x00\u07d48\x15\xb0t?\x94\xfc\x8c\xc8eO\xd9\u0557\xed}\x8bw\xc5~\x89(\t\xd4)\u0616u\x00\x00\xe0\x948\x1d\xb4\xc8F]\xf4F\xa4\xce\x15\xbf\x81\xd4~/\x17\u0240\xbf\x8a\x06\u01b95\xb8\xbb\xd4\x00\x00\x00\u07d48 ,\\\xd7\a\x8dO\x88vs\xab\a\x10\x9a\u062d\xa8\x97 \x8965\u026d\xc5\u07a0\x00\x00\u07d48!\x86$\x93$,\n\ub139\r\xe0]%\f\x1eP\xc0t\x89\x11wlX\xe9F\xdc\x00\x00\xe0\x948%\x91\xe7!{C^\x8e\x88L\xdb\xf4\x15\xfe7zo\u278a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d48+\xa7m\xb4\x1bu`m\u050aH\xf0\x13~\x91t\xe01\xb6\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d481u~\xaeuW\u02ca7\xa4\xb1\x06D\xb6>M;<u\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x9483\x04\xddzW \xb2\x9c\x1a\x10\xf6\x03B!\x9fH\x03/\x80\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\xe0\x948:|\x89\x9e\xe1\x8b\xc2\x14\x96\x98p\xbct\x82\xf6\xd8\xf3W\x0e\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x948C\x0e\x93\x1d\x93\xbe\x01\xb4\xc3\xef\r\xc55\xf1\xe0\xa9a\x00c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d48C\x9a\xaa$\xe3co:\x18\xe0 \xea\x1d\xa7\xe1E\x16\r\x86\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d48E\x8e\x06\x85W<\xb4\u048fS\t\x88)\x90Ep\x17\x92f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d48Gfp8\xf3;\x01\xc1\xccy]\x8d\xafTu\xef\xf5\xa0\u0509'{\x9b\xf4$lA\x00\x00\u07d48d;\xab\xea`\x111l\u01d7\u0670\x93\u0217\xa1{\xda\xe7\x89\x12 \xbbtE\u06a0\x00\x00\u07d48i_\xc7\xe16|\xeb\x16>\xbb\x057Q\xf9\xf6\x8d\xdb\a\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d48r\xf4\x8d\xc5\xe3\xf8\x17\xbck*\xd2\xd00\xfc^\x04q\x19=\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48~\xea\xfdk@\t\u07af\x8b\u0578Zr\x98:\x8d\xcc4\x87\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48\x81\xde\xfa\xe1\xc0{<\xe0Lx\xab\xe2k\f\u070ds\xf0\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d48\x83\xbe\xcc\b\xb9\xbeh\xad;\b6\xaa\u00f6 \xdc\x00\x17\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d48\x85\xfe\xe6q\a\xdc:<t\x1e\xe2\x90\u0249\x18\u0279\x93\x97\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d48\x87\x19,\u007fpP\x06\xb60\t\x12v\xb3\x9a\u0180D\x8dk\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d48\x89\x8b\xbbES\xe0\v\xbf\xd0\xcf&\x8b/\xc4d\xd1T\xad\u0549\x11X\xe4`\x91=\x00\x00\x00\u07d48\x8b\xdc\xda\xe7\x94\xfcD\b.fu\x014A\x18\xea\x96\u0356\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d48\x8c\x85\xa9\xb9 }\x81F\x03?\xe3\x81C\xf6\xd3KY\\G\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d48\x96\xadt5y\u04ce#\x02EM\x1f\xb6\xe2\xabi\xe0\x1b\xfd\x89e\xea=\xb7UF`\x00\x00\u07d48\xa3\xdc\xcf/\xcf\xe0\xc9\x1a&$\xbd\f\xbf\x88\xeeJ\al3\x89lk\x93[\x8b\xbd@\x00\x00\u07d48\xa7D\xef\xa6\xd5\xc2\x13}\xef\xef\x8e\xf9\x18{d\x9e\xee\x1cx\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48\xacfN\xe8\xe0y^Bu\u02c5+\u02e6\xa4y\xad\x9c\x8d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d48\xb2\x19q\x06\x123\x87\xa0\xd4\xde6\x841\xa8\xba\xcd\xda0\xe2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d48\xb3\x96\\!\xfa\x8991\a\x9b\xea\xcf\xff\xaf\x156x\xb6\xeb\x89\t<j\nQ\xe2g\x00\x00\u07d48\xb4\x03\xfb\x1f\xb7\xc1EY\xa2\xd6\xf6VJUR\xbc\xa3\x9a\xff\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x948\xb5\x01F\xe7\x19\x16\xa5D\x8d\xe1*Mt!5\xdc\xf3\x983\x8a\x06\u0450\xc4u\x16\x9a \x00\x00\u07d48\xbf*\x1fzi\xde\x0e%F\xad\xb8\b\xb3c5d]\xa9\xff\x89lp\x049\u0675`\x00\x00\xe0\x948\xc1\v\x90\xc8Y\u02f7\x81V\x92\xf9\x9d\xaeR\n\xb5\xfe\xbf^\x8a\x02\xc9\xe4\x96o\xa5\xcf$\x00\x00\u07d48\u01c5\x1f_\xfdL\xee\x98\xdf0\xf3\xb2U\x97\xaf\x8al\xa2c\x89\x8e\xad:/}~\x18\x00\x00\u07d48\xd2\xe9\x15Id\xb4\x1c\x8dP\xa7H}9\x1e~\xe2\xc3\xd3\u0089\xbd\xbcA\xe04\x8b0\x00\x00\xe0\x948\xda\x1b\xa2\u079e,\x95K\t-\xd9\xd8\x12\x04\xfd\x01k\xa0\x16\x8a\x02&\x8e\xd0\x1f4\xb30\x00\x00\u07d48\xdf\fJ\xbe}\xed_\xe0h\xea\xdf\x15J\u0191wC$\xa4\x89a\t=|,m8\x00\x00\u07d48\xe2\xafs9>\xa9\x8a\x1d\x99:t\xdf\\\xd7T\xb9\x8dR\x9a\x89a\t=|,m8\x00\x00\u07d48\xe4m\xe4E<8\xe9A\xe7\x93\x0fC0O\x94\xbb{+\xe8\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d48\xe7\u06e8\xfdO\x1f\x85\r\xbc&I\xd8\xe8O\tR\xe3\xeb<\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d48\xe8\xa3\x1a\xf2\xd2e\xe3\x1a\x9f\xff-\x8fF(m\x12E\xa4g\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d48\xeao[Z{\x88AuQ\xb4\x12=\xc1'\xdf\xe94-\xa6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d48\xee\xc6\xe2\x17\xf4\xd4\x1a\xa9 \xe4$\xb9RQ\x97\x04\x1c\xd4\u0189\xf0\r%\xeb\x92.g\x00\x00\xe0\x948\xf3\x87\xe1\xa4\xedJs\x10n\xf2\xb4b\xe4t\xe2\xe3\x14:\u040a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d49\x11a\xb0\xe4<0 f\u898d,\xe7\xe1\x99\xec\xdb\x1dW\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x949\x15\uad6b.Yw\xd0u\xde\xc4}\x96\xb6\x8bK\\\xf5\x15\x8a\r\a\x01\x81\x85\x12\x0f@\x00\x00\u07d49\x1aw@\\\t\xa7+^\x846#z\xaa\xf9]h\xda\x17\t\x89\x02\xa9&J\xf3\u0479\x00\x00\u07d49\x1f \x17m\x126\rrMQG\n\x90p6uYJM\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d49$3\xd2\u0383\xd3\xfbJv\x02\u0323\xfa\xcaN\xc1@\xa4\xb0\x89\x02\xc3\xc4e\xcaX\xec\x00\x00\xe0\x949?x;\\\u06c6\"\x1b\xf0)O\xb7\x14\x95\x9c{E\x89\x9c\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d49?\xf4%^\\e\x8f.\u007f\x10\xec\xbd)%rg\x1b\xc2\u0489lk\x93[\x8b\xbd@\x00\x00\u07d49A2`\x0fAU\xe0\u007fME\xbc>\xb8\xd9\xfbr\xdc\u05c4\x89\x9fn\x92\xed\xea\a\xd4\x00\x00\u07d49Q\xe4\x8e<\x86\x9ekr\xa1C\xb6\xa4Ph\u0379\xd4f\u0409\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x949T\xbd\xfe\v\xf5\x87\u0195\xa3\x05\xd9$L=[\xdd\xda\u027b\x8a\x04\x10'\x83'\xf9\x85`\x80\x00\u07d49]m%U \xa8\xdb)\xab\xc4}\x83\xa5\u06ca\x1a}\xf0\x87\x89\x05k\xc7^-c\x10\x00\x00\u07d49ck%\x81\x1b\x17j\xbf\xcf\xee\xcad\xbc\x87E/\x1f\xdf\xf4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d49i\xb4\xf7\x1b\xb8u\x1e\xdeC\xc0\x166:zaOv\x11\x8e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x949x/\xfe\x06\xacx\x82*<:\x8a\xfe0^P\xa5a\x88\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d49zn\xf8v:\x18\xf0\x0f\xac!~\x05\\\r0\x94\x10\x10\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d49|\u06cc\x80\xc6yP\xb1\x8deB)a\x0e\x93\xbf\xa6\xee\x1a\x89?\x95\xc8\xe0\x82\x15!\x00\x00\u07d49\x82O\x8b\xce\xd1v\xfd>\xa2.\u01a4\x93\xd0\xcc\xc3?\xc1G\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d49\x93l'\x19E\v\x94 \xcc%\"\u03d1\xdb\x01\xf2'\xc1\xc1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d49\x95\xe0\x96\xb0\x8aZrh\x00\xfc\xd1}\x9cd\xc6N\b\x8d+\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d49\x9a\xa6\xf5\xd0x\xcb\tp\x88+\u0259 \x06\xf8\xfb\xdf4q\x8965\u026d\xc5\u07a0\x00\x00\u07d49\xaa\x05\xe5m}28T!\u03d36\xe9\r=\x15\xa9\xf8Y\x89\x01h\u048e?\x00(\x00\x00\u07d49\xaa\xf0\x85M\xb6\xeb9\xbc{.C\x84jv\x17\x1c\x04E\u0789dI\xe8NG\xa8\xa8\x00\x00\u07d49\xb1\xc4q\xae\x94\xe1!dE.\x81\x1f\xbb\xe2\xb3\xcdru\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d49\xb2\x992t\x90\xd7/\x9a\x9e\xdf\xf1\x1b\x83\xaf\xd0\xe9\xd3\xc4P\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d49\xba\u018d\x94xY\xf5\x9e\x92&\b\x9c\x96\xd6.\x9f\xbe<\u0789\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x949\xbf\xd9xh\x9b\xec\x04\x8f\xc7v\xaa\x15$\u007f^\x1d|9\xa2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d49\xc7s6|\x88%\xd3YlhoB\xbf\r\x141\x9e?\x84\x89\a?u\u0460\x85\xba\x00\x00\u07d49\u05291@,\fy\xc4W\x18o$\u07c7)\u03d5p1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d49\xd6\xca\xca\"\xbc\xcdjr\xf8~\xe7\u05b5\x9e\v\xde!\xd7\x19\x89l\x87T\xc8\xf3\f\b\x00\x00\u07d49\xe0\xdbM`V\x8c\x80\v\x8cU\x00\x02l%\x94\xf5v\x89`\x8965\u026d\xc5\u07a0\x00\x00\xe0\x949\xeeO\xe0\x0f\xbc\xeddph\xd4\xf5|\x01\xcb\"\xa8\v\xcc\u044a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d49\xf1\x983\x1eK!\xc1\xb7`\xa3\x15_J\xb2\xfe\x00\xa7F\x19\x89lk\x93[\x8b\xbd@\x00\x00\u07d49\xf4Fc\xd9%a\t\x1b\x82\xa7\r\xcfY=u@\x05\x97:\x89\n\u05cb.\xdc!Y\x80\x00\u07d4:\x03U\x94\xc7GGmB\xd1\xee\x96l6\"L\xdd\"I\x93\x89\x13J\xf7Ei\xf9\xc5\x00\x00\u07d4:\x04W(G\xd3\x1e\x81\xf7v\\\xa5\xbf\xc9\xd5W\x15\x9f6\x83\x89\a6-\r\xab\xea\xfd\x80\x00\xe0\x94:\x06\xe3\xbb\x1e\xdc\xfd\fD\xc3\aM\xe0\xbb`k\x04\x98\x94\xa2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794:\x10\x88\x8b~\x14\x9c\xae',\x010,2}\n\xf0\x1a\v$\x88\xeb\xec!\xee\x1d\xa4\x00\x00\u07d4:1\b\xc1\u6023;3l!\x13\x134@\x9d\x97\xe5\xad\xec\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4:6\x8e\xfeJ\u05c6\xe2c\x95\xec\x9f\u01adi\x8c\xae)\xfe\x01\x89\"E\x89\x96u\xf9\xf4\x00\x00\u07d4:=\xd1\x04\xcd~\xb0O!\x93/\xd43\xeaz\xff\u04d3i\xf5\x89\x13aO#\xe2B&\x00\x00\u07d4:B\x97\xda<U^F\xc0sf\x9d\x04x\xfc\xe7_/y\x0e\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4:Gk\xd2\xc9\xe6d\xc6:\xb2f\xaaLnJH%\xf5\x16\u00c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4:H\xe0\xa7\t\x8b\x06\xa9\x05\x80+\x87TW1\x11\x8e\x89\xf49\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:M\xa7\x8d\xce\x05\xae\xb8}\u9bad\x91\x85rm\xa1\x92g\x98\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4:Y\xa0\x82F\xa8 o\x8dX\xf7\v\xb1\xf0\xd3\\[\xccq\xbd\x89\n\ad\a\xd3\xf7D\x00\x00\xe0\x94:r\xd65\xaa\xde\xeeC\x824\x9d\xb9\x8a\x18\x13\xa4\xcf\xeb=\xf1\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94:}\xb2$\xac\xae\x17\xdew\x98y}\x82\xcd\xf8%0\x17\u07e8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4:\x80_\xa0\xf78\u007fs\x05[xX\u0285\x19\xed\xd9=cO\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94:\x84\xe9P\xedA\x0eQ\xb7\xe8\x80\x10I\xab&4\xb2\x85\xfe\xa1\x8a\x03\xf5/\u06a8\"\xd2\xc8\x00\x00\u07d4:\x86\ue506+t=\xd3OA\ti\xd9N,VR\u052d\x89\n\xed\xe6\x9a\xd3\x0e\x81\x00\x00\u07d4:\x912\xb7\t=>\xc4.\x1eO\xb8\xcb1\xec\xddC\xaew<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94:\x99`&m\xf6I cS\x8a\x99\xf4\x87\xc9P\xa3\xa5\uc78a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4:\x9b\x11\x10)\xce\x1f \xc9\x10\x9czt\xee\xee\xf3OO.\xb2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4:\x9eTA\xd4K$;\xe5[u\x02z\x1c\ub7ac\xf5\r\xf2\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94:\xa0z4\xa1\xaf\u0216}=\x13\x83\xb9kb\u03d6\xd5\xfa\x90\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94:\xa4,!\xb9\xb3\x1c>'\xcc\xd1~\t\x9a\xf6y\xcd\xf5i\a\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4:\xa9H\xea\x029wU\xef\xfb/\x9d\xc99-\xf1\x05\x8f~3\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4:\xad\xf9\x8ba\xe5\u0216\xe7\xd1\x00\xa39\x1d2P\"]a\u07c9\f\xafg\x007\x01h\x00\x00\u07d4:\xaeHr\xfd\x90\x93\xcb\xca\xd1@o\x1e\x80x\xba\xb5\x03Y\xe2\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4:\xbb\x8a\xdf\xc6\x04\xf4\x8dY\x84\x81\x1d\u007f\x1dR\xfe\xf6u\x82p\x89\xf2\x97\x19\xb6o\x11\f\x00\x00\u07d4:\xc2\xf0\xff\x16\x12\xe4\xa1\xc3F\xd53\x82\xab\xf6\u0622[\xaaS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:\xc9\xdczCj\xe9\x8f\xd0\x1cz\x96!\xaa\x8e\x9d\v\x8bS\x1d\x89a\t=|,m8\x00\x00\xe0\x94:\xd0aI\xb2\x1cU\xff\x86|\xc3\xfb\x97@\u04bc\xc7\x10\x121\x8a)\xb7d2\xb9DQ \x00\x00\u07d4:\xd7\x02C\u060b\xf0@\x0fW\xc8\xc1\xfdW\x81\x18H\xaf\x16*\x89.\x9e\xe5\u00c6S\xf0\x00\x00\u07d4:\xd9\x15\xd5P\xb7#AV \xf5\xa9\xb5\xb8\x8a\x85\xf3\x82\xf05\x8965\u026d\xc5\u07a0\x00\x00\u07d4:\xe1`\xe3\xcd`\xae1\xb9\xd6t-h\xe1Nv\xbd\x96\xc5\x17\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4:\xe6+\xd2q\xa7`c\u007f\xady\xc3\x1c\x94\xffb\xb4\xcd\x12\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:\xeaN\x82\xd2@\x02H\xf9\x98q\xa4\x1c\xa2W\x06\r:\"\x1b\x8965\u026d\xc5\u07a0\x00\x00\u07d4:\xf6[>(\x89ZJ\x00\x11S9\x1d\x1ei\xc3\x1f\xb9\xdb9\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4;\a\xdbZ5\u007fZ\xf2HL\xbc\x9dw\xd7;\x1f\xd0Q\x9f\u01c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4;\n\u032fK`|\xfea\xd1s4\xc2\x14\xb7\\\xde\xfd\xbd\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x13c\x1a\x1b\x89\xcbVeH\x89\x9a\x1d`\x91\\\xdc\xc4 [\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x15\x90\x99\aR\a\u0180vc\xb1\xf0\xf7\xed\xa5J\xc8\xcc\xe3\x89j\xc4\xe6[i\xf9-\x80\x00\u07d4;\x197\xd5\u74f8\x9bc\xfb\x8e\xb5\xf1\xb1\xc9\xcak\xa0\xfa\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\"\xda*\x02q\xc8\xef\xe1\x02S'scji\xb1\xc1~\t\x89\x1b6\xa6DJ>\x18\x00\x00\u07d4;\"\u07a3\xc2_\x1bY\u01fd'\xbb\x91\u04e3\xea\xec\xef9\x84\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94;#g\xf8IK_\xe1\x8dh<\x05]\x89\x99\x9c\x9f=\x1b4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4;,E\x99\x0e!GDQ\xcfOY\xf0\x19U\xb31\xc7\xd7\u0249lk\x93[\x8b\xbd@\x00\x00\xe0\x94;A\x00\xe3\ns\xb0\xc74\xb1\x8f\xfa\x84&\u045b\x191/\x1a\x8a\v\xb5\u046ap\n\xfd\x90\x00\x00\u07d4;B\xa6m\x97\x9fX(4tz\x8b`B\x8e\x9bN\xec\xcd#\x89!\xa1\u01d0\xfa\xdcX\x00\x00\u07d4;Gh\xfdq\xe2\xdb,\xbe\u007f\xa0PH<'\xb4\xeb\x93\x1d\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;Vj\x8a\xfa\u0456\x82\xdc,\xe8g\x9a<\xe4D\xa5\xb0\xfdO\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94;\\%\x1d\u007f\u05c9;\xa2\t\xfeT\x1c\xec\xd0\xce%:\x99\r\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4;^\x8b<w\xf7\x92\xde\xcbz\x89\x85\u07d1n\xfbI\n\xac#\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;n\x81Ow\aH\xa7\u00d9x\x064v\x05H\n?\xd5\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;{OS\xc4VU\xf3\xdc_\x01~\xdc#\xb1o\x9b\xc56\xfa\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94;{\x8e'\xde3\xd3\xceya\xb9\x8d\x19\xa5/\xe7\x9fl%\xbe\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4;|w\xdb\xe9]\xc2`,\xe3&\x9a\x95E\xd0Ie\xfe\xfd\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x80\x98S?}\x9b\xdc\xd3\a\u06f2>\x17w\xca\x18A\x896\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94;\x93\xb1a6\xf1\x1e\xaf\x10\x99l\x95\x99\r;'9\xcc\xea_\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4;\xabK\x01\xa7\xc8K\xa1?\uea70\xbb\x19\x1bw\xa3\xaa\u0723\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4;\xb55\x98\xcc \xe2\x05]\xc5S\xb0I@J\u0277\xdd\x1e\x83\x89!W\x1d\xf7|\x00\xbe\x00\x00\u07d4;\xbc\x13\xd0J\xcc\xc0pz\xeb\u072e\xf0\x87\u0438~\v^\u327e\xd1\xd0&=\x9f\x00\x00\x00\u07d4;\xc6\xe3\xeezV\u038f\x14\xa3u2Y\x0fcqk\x99f\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\xc8]ls[\x9c\xdaK\xba_H\xb2K\x13\xe7\x0600{\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4;\xd6$\xb5H\xcbe\x976\x90~\u062a<\fp^$\xb5u\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\u0660m\x1b\xd3lN\xdd'\xfc\r\x1f[\b\x8d\xda\xe3\xc7*\x89\x1b\x1azB\v\xa0\r\x00\x00\u0794;\u077c\x814\xf7}UY\u007f\xc9|&\xd2f\x98\t\x06\x04\ub23e -j\x0e\xda\x00\x00\xe0\x94;\xf8n\u0623\x15>\xc93xj\x02\xac\t\x03\x01\x85^Wk\x8a_J\x8c\x83u\xd1U@\x00\x00\u07d4;\xfb\u04c4|\x17\xa6\x1c\xf3\xf1{R\xf8\ub879`\xb3\U000df262\xa1]\tQ\x9b\xe0\x00\x00\u07d4<\x03\xbb\xc0#\xe1\xe9?\xa3\xa3\xa6\xe4(\xcf\f\xd8\xf9^\x1e\u0189Rf<\u02b1\xe1\xc0\x00\x00\u07d4<\f=\ufb1c\xeaz\xcc1\x9a\x96\xc3\v\x8e\x1f\xed\xabEt\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4<\x15\xb3Q\x1d\xf6\xf04.sH\u0309\xaf9\xa1h\xb7s\x0f\x8965\u026d\xc5\u07a0\x00\x00\u07d4<\x1f\x91\xf3\x01\xf4\xb5e\xbc\xa2GQ\xaa\x1fv\x13\"p\x9d\u0749a\t=|,m8\x00\x00\xe0\x94<(l\xfb0\x14n_\u05d0\xc2\xc8T\x15RW\x8d\xe34\u060a\x02)\x1b\x11\xaa0n\x8c\x00\x00\u07d4<2.a\x1f\u06c2\rG\xc6\xf8\xfcd\xb6\xfa\xd7L\xa9_^\x89\r%\x8e\xce\x1b\x13\x15\x00\x00\u07d4<Z$\x14Y\u01ab\xbfc\x029\u024a0\xd2\v\x8b:\xc5a\x89\b\x8b#\xac\xff\u0650\x00\x00\u07d4<y\xc8c\xc3\xd3r\xb3\xff\foE'4\xa7\xf9pB\xd7\x06\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\xe0\x94<\x83\xc1p\x1d\xb08\x8bh!\r\x00\xf5q|\u067d2,j\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4<\x86\x0e.f?F\xdbSB{)\xfe>\xa5\xe5\xbfb\xbb\u0309\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4<\x86\x9c\tie#\xce\xd8$\xa0pAF\x05\xbbv#\x1f\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4<\x92V\x19\u02731DF?\x057\u06165\x87\x06\xc5 \xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\x98YK\xf6\x8bW5\x1e\x88\x14\xae\x9em\xfd-%J\xa0o\x89\x10CV\x1a\x88)0\x00\x00\u07d4<\xad\xeb=>\xed?b1\x1dRU>p\xdfJ\xfc\xe5o#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94<\xae\xdbS\x19\xfe\x80eC\xc5nP!\xd3r\xf7\x1b\xe9\x06.\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4<\xaf\xaf^bPV\x15\x06\x8a\xf8\xeb\"\xa1:\u0629\xe5Pp\x89lf\x06E\xaaG\x18\x00\x00\u07d4<\xb1y\xcbH\x01\xa9\x9b\x95\u00f0\xc3$\xa2\xbd\xc1\x01\xa6S`\x89\x01h\u048e?\x00(\x00\x00\u07d4<\xb5a\u0386BK5\x98\x91\xe3d\xec\x92_\xfe\xff'}\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xcbq\xaah\x80\xcb\v\x84\x01-\x90\xe6\a@\xec\x06\xac\u05cf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\xce\xf8\x86yW9G\xe9I\x97y\x8a\x1e2~\b`:e\x89+\xc9\x16\u059f;\x02\x00\x00\xe0\x94<\xd1\xd9s\x1b\xd5H\xc1\xddo\u03a6\x1b\xebu\xd9\x17T\xf7\u04ca\x01\x16\x1d\x01\xb2\x15\xca\xe4\x80\x00\u07d4<\u04e6\xe95y\xc5mIAq\xfcS>z\x90\xe6\xf5\x94d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\u05b7Y<\xbe\xe7x0\xa8\xb1\x9d\b\x01\x95\x8f\xcdK\xc5z\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4<\xd7\xf7\xc7\xc257\x80\xcd\xe0\x81\xee\xecE\x82+%\xf2\x86\f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xe1\u0717\xfc\u05f7\xc4\u04e1\x8aI\xd6\xf2\xa5\xc1\xb1\xa9\x06\u05c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xea0*G*\x94\x03y\xdd9\x8a$\xea\xfd\xba\u07c8\xady\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94<\xec\xa9k\xb1\xcd\xc2\x14\x02\x9c\xbc^\x18\x1d9\x8a\xb9M=A\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4<\xf4\x84RO\xbd\xfa\xda\xe2m\xc1\x85\xe3++c\x0f\xd2\xe7&\x89\x18TR\xcb*\x91\xc3\x00\x00\u07d4<\xf9\xa1\xd4e\xe7\x8bp9\xe3iDx\xe2b{6\xfc\xd1A\x89J`S*\xd5\x1b\xf0\x00\x00\u07d4<\xfb\xf0fVYpc\x9e\x13\r\xf2\xa7\xd1k\x0e\x14\xd6\t\x1c\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94=\th\x8d\x93\xad\a\xf3\xab\xe6\x8cr'#\xcdh\t\x90C^\x8a\x06ZL\xe9\x9fv\x9en\x00\x00\u07d4=1X{_\u0546\x98Ex\x87%\xa6c)\nI\xd3g\x8c\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4=?\xadI\xc9\xe5\xd2u\x9c\x8e\x8eZzM`\xa0\xdd\x13V\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4=WO\xcf\x00\xfa\xe1\u064c\u023f\x9d\u07e1\xb3\x95;\x97A\xbc\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4=Z\x8b+\x80\xbe\x8b5\xd8\xec\xf7\x89\xb5\xedz\au\xc5\al\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4=f\xcdK\xd6M\\\x8c\x1b^\xea(\x1e\x10m\x1cZ\xad#s\x89i\xc4\xf3\xa8\xa1\x10\xa6\x00\x00\u0794=j\xe0S\xfc\xbc1\x8do\xd0\xfb\xc3S\xb8\xbfT.h\r'\x88\xc6s\xce<@\x16\x00\x00\u07d4=o\xf8,\x93w\x05\x9f\xb3\r\x92\x15r?`\xc7u\u0211\xfe\x89\r\x8e\\\xe6\x17\xf2\xd5\x00\x00\u07d4=y\xa8S\xd7\x1b\xe0b\x1bD\xe2\x97Yel\xa0u\xfd\xf4\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4=~\xa5\xbf\x03R\x81\x00\xed\x8a\xf8\xae\xd2e>\x92\x1bng%\x8965\u026d\xc5\u07a0\x00\x00\u07d4=\x81?\xf2\xb6\xedW\xb97\u06bf+8\x1d\x14\x8aA\x1f\xa0\x85\x89\x05k\xc7^-c\x10\x00\x00\u07d4=\x88\x143\xf0J}\r'\xf8ID\xe0\x8aQ-\xa3UR\x87\x89A\rXj \xa4\xc0\x00\x00\u07d4=\x89\xe5\x05\xcbF\xe2\x11\xa5?2\xf1g\xa8w\xbe\xc8\u007fK\n\x89\x01[5W\xf1\x93\u007f\x80\x00\xe0\x94=\x8d\a#r\x1es\xa6\xc0\xd8`\xaa\x05W\xab\xd1L\x1e\xe3b\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4=\x8f9\x88\x1b\x9e\xdf\xe9\x12'\xc3?\xa4\xcd\xd9\x1eg\x85D\xb0\x89\x04\xab\a\xbaC\xad\xa9\x80\x00\u07d4=\x9dk\xe5\u007f\xf8>\x06Y\x85fO\x12VD\x83\xf2\xe6\x00\xb2\x89n\xac\xe4?#\xbd\x80\x00\x00\u07d4=\xa3\x9c\xe3\xefJz9f\xb3.\xe7\xeaN\xbc#5\xa8\xf1\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4=\xaa\x01\u03b7\x0e\xaf\x95\x91\xfaR\x1b\xa4\xa2~\xa9\xfb\x8e\xdeJ\x89Zc\xd2\u027cvT\x00\x00\u07d4=\xb5\xfejh\xbd6\x12\xac\x15\xa9\x9aa\xe5U\x92\x8e\xec\xea\xf3\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4=\xb9\xed\u007f\x02L~&7/\xea\xcf+\x05\b\x03D^8\x10\x89E\xb1H\xb4\x99j0\x00\x00\u07d4=\xbf\r\xbf\xd7x\x90\x80\x053\xf0\x9d\xea\x83\x01\xb9\xf0%\u04a6\x8965\u026d\xc5\u07a0\x00\x00\u07d4=\xce\U0005c18b\x15\xd3N\xdaBn\xc7\xe0K\x18\xb6\x01p\x02\x89lh\xcc\u041b\x02,\x00\x00\xe0\x94=\xd1.Uj`76\xfe\xbaJo\xa8\xbdJ\xc4]f*\x04\x8a#u{\x91\x83\xe0x(\x00\x00\u07d4=\u078b\x15\xb3\u033a\xa5x\x01\x12\xc3\xd6t\xf3\x13\xbb\xa6\x80&\x89`\x1dQZ>O\x94\x00\x00\xe0\x94=\xde\xdb\xe4\x89#\xfb\xf9\xe56\xbf\x9f\xfb\aG\xc9\xcd\u04de\xef\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4=\xea\xe43'\x91?b\x80\x8f\xaa\x1bbv\xa2\xbdch\xea\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4=\xf7b\x04\x9e\u068a\u0192}\x90Lz\xf4/\x94\xe5Q\x96\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>\x04\r@\u02c0\xba\x01%\xf3\xb1_\xde\xfc\xc8?0\x05\xda\x1b\x898E$\xccp\xb7x\x00\x00\u07d4>\v\x8e\xd8n\xd6i\xe1'#\xafur\xfb\xac\xfe\x82\x9b\x1e\x16\x89QM\xe7\xf9\xb8\x12\xdc\x00\x00\xe0\x94>\f\xbejm\xcba\xf1\x10\xc4[\xa2\xaa6\x1d\u007f\xca\xd3\xdas\x8a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4>\x19KN\xce\xf8\xbbq\x1e\xa2\xff$\xfe\xc4\xe8{\xd02\xf7\u0449\x8b\x9d\xc1\xbc\x1a\x03j\x80\x00\xe0\x94>\x1b\"0\xaf\xbb\xd3\x10\xb4\x92jLwmZ\u705cf\x1d\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4>\x1cS0\x0eL\x16\x89\x12\x16<~\x99\xb9]\xa2h\xad(\n\x896b2\\\u044f\xe0\x00\x00\u07d4>\x1c\x96 c\xe0\xd5)YA\xf2\x10\u0723\xabS\x1e\xec\x88\t\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4>,\xa0\xd24\xba\xf6\a\xadFj\x1b\x85\xf4\xa6H\x8e\xf0\n\xe7\x89\x04\xda!\xa3H=V\x80\x00\u07d4>/&#^\x13zs$\xe4\xdc\x15K]\xf5\xafF\xea\x1aI\x89\x017\xaa\xd8\x03-\xb9\x00\x00\xe0\x94>1a\xf1\xea/\xbf\x12ny\xda\x18\x01\u0695\x12\xb3y\x88\u024a\nm\xd9\f\xaeQ\x14H\x00\x00\xe0\x94>6\xc1rS\xc1\x1c\xf3\x89t\xed\r\xb1\xb7Y\x16\r\xa67\x83\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4><\u04fe\xc0e\x91\xd64o%Kb\x1e\xb4\x1c\x89\x00\x8d1\x895\u07fe\u069f74\x00\x00\u07d4>E\xbdU\u06d0`\xec\xed\x92;\xb9\xcbs<\xb3W?\xb51\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4>M\x13\xc5Z\x84\xe4n\xd7\xe9\u02d0\xfd5^\x8a\u0651\u33c965\u026d\xc5\u07a0\x00\x00\u07d4>N\x92e\"<\x9782L\xf2\v\xd0`\x06\xd0\a>\u06cc\x89\a?u\u0460\x85\xba\x00\x00\xe0\x94>O\xbdf\x10\x15\xf6F\x1e\xd6s\\\xef\xef\x01\xf3\x14E\xde:\x8a\x03n4)\x98\xb8\xb0 \x00\x00\xe0\x94>S\xff!\a\xa8\u07be3(I:\x92\xa5\x86\xa7\xe1\xf4\x97X\x8a\x04\xe6\x9c*q\xa4\x05\xab\x00\x00\u07d4>Z9\xfd\xdap\xdf\x11&\xab\r\u011asx1\x1aSz\x1f\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\xe0\x94>Z\xbd\t\xceZ\xf7\xba\x84\x87\xc3Y\xe0\xf2\xa9:\x98k\v\x18\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4>\\\xb8\x92\x8cAx%\xc0:;\xfc\xc5!\x83\xe5\xc9\x1eB\u05c9\xe71\xd9\xc5,\x96/\x00\x00\u07d4>^\x93\xfbL\x9c\x9d\x12F\xf8\xf2G5\x8e\"\xc3\xc5\xd1{j\x89\b!\xab\rD\x14\x98\x00\x00\u07d4>a\x83P\xfa\x01ez\xb0\xef>\xba\xc8\xe3p\x12\xf8\xfc+o\x89\x98\x06\xde=\xa6\xe9x\x00\x00\u07d4>c\xce;$\xca(e\xb4\u0166\x87\xb7\xae\xa3Y~\xf6\xe5H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>f\xb8GiVj\xb6yE\xd5\xfa\x8175V\xbc\u00e1\xfa\x89\b=lz\xabc`\x00\x00\xe0\x94>v\xa6-\xb1\x87\xaat\xf68\x17S;0l\xea\xd0\xe8\u03be\x8a\x06\x9bZ\xfa\xc7P\xbb\x80\x00\x00\u07d4>z\x96k]\xc3W\xff\xb0~\x9f\xe0g\xc4W\x91\xfd\x8e0I\x89\x034-`\xdf\xf1\x96\x00\x00\xe0\x94>\x81w!u#~\xb4\xcb\xe0\xfe-\xca\xfd\xad\xff\xebj\x19\x99\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4>\x83I\xb6\u007fWED\x9fe\x93g\u066dG\x12\xdb[\x89Z\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4>\x83TO\x00\x82U%r\u01c2\xbe\xe5\xd2\x18\xf1\xef\x06J\x9d\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4>\x84\xb3\\[\"ePpa\xd3\vo\x12\xda\x03?\xe6\xf8\xb9\x89a\t=|,m8\x00\x00\u07d4>\x86A\xd4<B\x00?\n3\xc9)\xf7\x11\a\x9d\xeb+\x9eF\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94>\x87E\xba2/_\xd6\xcbP\x12N\xc4f\x88\u01e6\x9a\u007f\xae\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4>\x91N0\x18\xac\x00D\x93A\u011d\xa7\x1d\x04\xdf\xee\xedb!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4>\x94\x10\u04f9\xa8~\xd5\xe4Q\xa6\xb9\x1b\xb8\x92?\xe9\x0f\xb2\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4>\x94\xdfS\x13\xfaR\x05p\xef#+\xc31\x1d_b/\xf1\x83\x89lk\x93[\x8b\xbd@\x00\x00\u0794>\x9b4\xa5\u007f3u\xaeY\xc0\xa7^\x19\u0136A\"\x8d\x97\x00\x88\xf8i\x93)g~\x00\x00\u07d4>\xad\xa8\xc9/V\x06~\x1b\xb7<\xe3x\xdaV\xdc,\xdf\xd3e\x89w\xcd\xe9:\xeb\rH\x00\x00\xe0\x94>\xaf\by\xb5\xb6\xdb\x15\x9bX\x9f\x84W\x8bjt\xf6\xc1\x03W\x8a\x01\x898\xb6q\xfae\xa2\x80\x00\u07d4>\xaf1k\x87a]\x88\xf7\xad\xc7|X\xe7\x12\xedMw\x96k\x89\x05m\xbcL\xee$d\x80\x00\u07d4>\xb8\xb3;!\xd2<\u0686\xd8(\x88\x84\xabG\x0e\x16F\x91\xb5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4>\xb9\xef\x06\xd0\xc2Y\x04\x03\x19\x94~\x8czh\x12\xaa\x02S\u0609\t\r\x97/22<\x00\x00\u07d4>\u030e\x16h\xdd\xe9\x95\xdcW\x0f\xe4\x14\xf4B\x11\xc54\xa6\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>\u03752\xe3\x97W\x96b\xb2\xa4aA\u73c25\x93j_\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4>\xeeo\x1e\x966\vv\x89\xb3\x06\x9a\xda\xf9\xaf\x8e\xb6\f\u404965\u026d\xc5\u07a0\x00\x00\xe0\x94?\b\u066d\x89O\x81>\x8e!H\xc1`\xd2K5:\x8et\xb0\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4?\f\x83\xaa\xc5qybsN\\\xea\xea\xec\u04db(\xad\x06\xbe\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94?\x10\x80\x02\x82\u0477\xdd\u01cf\xa9-\x820\aN\x1b\xf6\xae\xae\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4?\x123qO M\xe9\xdeN\xe9m\a;6\x8d\x81\x97\x98\x9f\x89\x02\x17\xc4\x10t\xe6\xbb\x00\x00\u07d4?\x17:\xa6\xed\xf4i\u0445\xe5\x9b\xd2j\xe4#k\x92\xb4\xd8\xe1\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4?\x1b\xc4 \xc5<\x00,\x9e\x90\x03|D\xfej\x8e\xf4\xdd\xc9b\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4?#a\b\xee\xc7\"\x89\xba\u00e6\\\u0483\xf9^\x04\x1d\x14L\x8964\xbf9\xab\x98x\x80\x00\u07d4?-\xa0\x93\xbb\x16\xeb\x06O\x8b\xfa\x9e0\xb9)\xd1_\x8e\x1cL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?-\xd5]\xb7\xea\xb0\xeb\xeee\xb3>\xd8 ,\x1e\x99.\x95\x8b\x89,s\xc97t,P\x00\x00\u07d4?/8\x14\x91y|\xc5\xc0\u0502\x96\xc1O\xd0\xcd\x00\xcd\xfa-\x89+\x95\xbd\xcc9\xb6\x10\x00\x00\u07d4?0\u04fc\x9f`\"2\xbcrB\x88\xcaF\xcd\v\a\x88\xf7\x15\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4?<\x8ea\xe5`L\xef\x06\x05\xd46\xdd\"\xac\u0346\"\x17\xfc\x89Hz\x9a0E9D\x00\x00\u07d4??F\xb7\\\xab\xe3{\xfa\u0307`(\x1fCA\xca\u007fF=\x89 \xacD\x825\xfa\xe8\x80\x00\u07d4?G)c\x19x\x83\xbb\xdaZ\x9b}\xfc\xb2-\xb1\x14@\xad1\x89\x1a\x19d<\xb1\xef\xf0\x80\x00\u07d4?L\xd19\x9f\x8a4\xed\u06da\x17\xa4q\xfc\x92+Xp\xaa\xfc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4?U\x1b\xa9<\xd5F\x93\xc1\x83\xfb\x9a\xd6\re\xe1`\x96s\u0249lk\x93[\x8b\xbd@\x00\x00\xe0\x94?bzv\x9ej\x95\x0e\xb8p\x17\xa7\u035c\xa2\bq\x13h1\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4?m\xd3e\x0e\xe4(\u0737u\x95S\xb0\x17\xa9j\x94(j\u0249Hz\x9a0E9D\x00\x00\u07d4?tr7\x80o\xed?\x82\x8ahR\xeb\bg\xf7\x90'\xaf\x89\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4?u\xaea\xcc\x1d\x80Be;[\xae\xc4D>\x05\x1c^z\xbd\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4?\xb7\u0457\xb3\xbaO\xe0E\xef\xc2=P\xa1E\x85\xf5X\u0672\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94?\xbc\x1eE\x18\xd74\x00\xc6\xd0F5\x949\xfbh\xea\x1aI\xf4\x8a\x03y\v\xb8U\x13v@\x00\x00\u07d4?\xbe\xd6\xe7\xe0\u029c\x84\xfb\xe9\xeb\u03ddN\xf9\xbbIB\x81e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?\u043bGy\x8c\xf4L\u07feM3=\xe67\xdfJ\x00\xe4\\\x89\x05lUy\xf7\"\x14\x00\x00\xe0\x94?\xe4\x0f\xbd\x91\x9a\xad(\x18\xdf\x01\xeeM\xf4lF\x84*\xc59\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4?\xe8\x01\xe6\x135\xc5\x14\r\xc7\xed\xa2\xefR\x04F\nP\x120\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?\xf86\xb6\xf5{\x90\x1bD\f0\xe4\xdb\xd0e\xcf7\xd3\u050c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4?\xfc\xb8p\xd4\x02=%]Qg\u0625\a\xce\xfc6kh\xba\x89#4<CT\u04ac\x00\x00\u0794@\x13T\xa2\x97\x95/\xa9r\xad8<\xa0z\n(\x11\xd7Jq\x88\xc2I\xfd\xd3'x\x00\x00\u07d4@0\xa9%pk,\x10\x1c\x8c\\\xb9\xbd\x05\xfb\xb4\xf6u\x9b\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94@1E\xcbJ\xe7H\x9f\u0310\u0358\\m\u01c2\xb3\xccND\x8a\x01E?\xf3\x87\xb2|\xac\x00\x00\u07d4@2 `\n6\xf7?$\xe1\x90\xd1\xed\xb2\xd6\x1b\xe3\xf4\x13T\x89\x10z\xd8\xf5V\xc6\xc0\x00\x00\u07d4@9\xbdP\xa2\xbd\xe1_\xfe7\x19\x1fA\x03\x90\x96*+\x88\x86\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4@<d\x89ju\xca\xd8\x16\xa9\x10^\x18\u062a[\xf8\x0f#\x8e\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94@=S\xcfb\x0f\t\"\xb4\x17\x84\x8d\xee\x96\xc1\x90\xb5\xbc\x82q\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4@A\x00\xdbL]\x0e\xecUx#\xb5\x83Cu\x8b\xcc,\x80\x83\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4@A7K\x0f\xee\xf4y.K3i\x1f\xb8h\x97\xa4\xffV\f\x89\x13\xc9d~%\xa9\x94\x00\x00\u07d4@F}\x80\xe7L5@{}\xb5\x17\x89#F\x15\xfe\xa6h\x18\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4@XR\x00h:@9\x017)\x12\xa8\x984\xaa\u0735_\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4@X\x80\x88\x16\xfd\xaa:_\u024e\xd4|\xfa\xe6\xc1\x83\x15B.\x89\n\xd4\xc81j\v\f\x00\x00\u07d4@_Yk\x94\xb9G4L\x03<\xe2\u073f\xf1.%\xb7\x97\x84\x89lk\x93[\x8b\xbd@\x00\x00\u07d4@c\x00$\xbd,X\xd2H\xed\xd8FV\x17\xb2\xbf\x16G\xda\x0e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94@e#`\xd6qm\xc5\\\xf9\xaa\xb2\x1f4\x82\xf8\x16\xcc,\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94@r\x95\xeb\xd9KH&\x9c-V\x9c\x9b\x9a\xf9\xaa\x05\xe8>^\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4@s\xfaI\xb8q\x17\u02d0\x8c\xf1\xabQ-\xa7T\xa92\xd4w\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4@\x8ai\xa4\a\x15\xe1\xb3\x13\xe15N`\b\x00\xa1\xe6\xdc\x02\xa5\x89\x01\u7e11\u0312T\x00\x00\u07d4@\x9b\xd7P\x85\x82\x1c\x1d\xe7\f\xdc;\x11\xff\xc3\xd9#\xc7@\x10\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4@\x9dZ\x96.\xde\uefa1x\x01\x8c\x0f8\xb9\u0372\x13\xf2\x89\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4@\xa31\x19[\x97s%\u00aa(\xfa/B\xcb%\xec<%<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4@\xa7\xf7(g\xa7\u0706w\v\x16+uW\xa44\xedP\xcc\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d4@\xab\n>\x83\xd0\u022c\x93f\x91\x05 \xea\xb1w+\xac;\x1a\x894\xf1\f-\xc0^|\x00\x00\u07d4@\xabf\xfe!>\xa5l:\xfb\x12\xc7[\xe3?\x8e2\xfd\b]\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94@\xadt\xbc\v\xce*E\xe5/6\xc3\u07bb\x1b:\xda\x1bv\x19\x8a\x01p\x16-\xe1\t\xc6X\x00\x00\u07d4@\u03c9\x05\x91\xea\u484f\x81*)T\xcb)_c3'\xe6\x89\x02\x9b\xf76\xfcY\x1a\x00\x00\u07d4@\u03d0\xef[v\x8c]\xa5\x85\x00,\xcb\xe6avP\xd8\xe87\x8963\x03\"\xd5#\x8c\x00\x00\xe0\x94@\xd4]\x9dv%\xd1QV\xc92\xb7q\xca{\x05'\x13\tX\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4@\xdb\x1b\xa5\x85\xce4S\x1e\xde\xc5IHI9\x13\x81\xe6\xcc\u04c9a\t=|,m8\x00\x00\xe0\x94@\xdfI^\xcf?\x8bL\xef*l\x18\x99W$\x8f\u813c+\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4@\xe0\xdb\xf3\xef\uf404\xea\x1c\xd7\xe5\x03\xf4\v;J\x84C\xf6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4@\xe2D\n\xe1B\u02006j\x12\xc6\xd4\x10/K\x844\xb6*\x8965\u026d\xc5\u07a0\x00\x00\u07d4@\xe3\u0083\xf7\xe2M\xe0A\f\x12\x1b\xee`\xa5`\u007f>)\xa6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94@\xeaPD\xb2\x04\xb20v\xb1\xa5\x80;\xf1\xd3\f\x0f\x88\x87\x1a\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94@\xed\xdbD\x8di\x0e\xd7.\x05\xc2%\xd3O\xc85\x0f\xa1\xe4\u014a\x01{x\x83\xc0i\x16`\x00\x00\xe0\x94@\xf4\xf4\xc0ls,\xd3[\x11\x9b\x89;\x12~}\x9d\aq\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4A\x01\x0f\u023a\xf8C}\x17\xa0Ci\x80\x9a\x16\x8a\x17\xcaV\xfb\x89\x05k\xc7^-c\x10\x00\x00\u07d4A\x03)\x96q\xd4gc\x97\x8f\xa4\xaa\x19\xee4\xb1\xfc\x95'\x84\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4A\x03<\x1bm\x05\xe1\u0289\xb0\x94\x8f\xc6DS\xfb\xe8z\xb2^\x89Hz\x9a0E9D\x00\x00\u07d4A\t\x8a\x81E#\x17\xc1\x9e>\xef\v\xd1#\xbb\xe1x\xe9\xe9\u0289\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4A\x16\x10\xb1x\xd5a}\xfa\xb94\u0493\xf5\x12\xa9>\\\x10\xe1\x89\t79SM(h\x00\x00\u07d4A\x1c\x83\x1c\xc6\xf4O\x19e\xecWW\xabN[<\xa4\xcf\xfd\x1f\x89\x17\n\x0fP@\xe5\x04\x00\x00\xe0\x94A*h\xf6\xc6EU\x9c\xc9w\xfcId\x04z \x1d\x1b\xb0\xe2\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4A?K\x02f\x9c\xcf\xf6\x80k\xc8&\xfc\xb7\xde\xca;\x0e\xa9\xbc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4AE\x99\t.\x87\x9a\xe2Sr\xa8MsZ\xf5\xc4\xe5\x10\xcdm\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4AHV\x12\xd04F\xecL\x05\xe5$NV?\x1c\xba\xe0\xf1\x97\x894\x95tD\xb8@\xe8\x00\x00\u07d4A]\tj\xb0b\x93\x18?<\x03=%\xf6\xcfqx\xac;\u01c9\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4Af\xfc\b\u0285\xf7f\xfd\xe81F\x0e\x9d\xc9<\x0e!\xaal\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Ag\x84\xaf`\x960\xb0p\u051a\x8b\xcd\x12#\\d(\xa4\b\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Ag\xcdH\xe73A\x8e\x8f\x99\xff\xd14\x12\x1cJJ\xb2x\u0109\xc5S%\xcat\x15\xe0\x00\x00\u07d4Al\x86\xb7 \x83\xd1\xf8\x90}\x84\xef\xd2\xd2\u05c3\xdf\xfa>\xfb\x89lj\xccg\u05f1\xd4\x00\x00\u07d4AsA\x9d\\\x9fc)U\x1d\xc4\xd3\xd0\u03ac\x1bp\x1b\x86\x9e\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4At\xfa\x1b\xc1*;q\x83\u02eb\xb7z\vYU{\xa5\xf1\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4Axj\x10\xd4G\xf4\x84\xd32D\u0337\xfa\u034bB{[\x8c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Az<\u0454\x96S\nmB\x04\u00f5\xa1|\xe0\xf2\a\xb1\xa5\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4A~N&\x88\xb1\xfdf\xd8!R\x9eF\xedOB\xf8\xb3\xdb=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94A\x9aq\xa3l\x11\xd1\x05\xe0\xf2\xae\xf5\xa3\xe5\x98\a\x8e\x85\xc8\v\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94A\x9b\xdes\x16\xcc\x1e\u0495\u0205\xac\xe3B\u01db\xf7\xee3\xea\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4A\xa2\xf2\xe6\xec\xb8c\x94\xec\x0e3\x8c\x0f\xc9~\x9cU\x83\xde\u0489l\xee\x06\u077e\x15\xec\x00\x00\u07d4A\xa8\u0083\x00\x81\xb1\x02\xdfn\x011e|\a\xabc[T\u0389lj\xccg\u05f1\xd4\x00\x00\u07d4A\xa8\xe26\xa3\x0emc\xc1\xffdM\x13*\xa2\\\x89S~\x01\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4A\xa9\xa4\x04\xfc\x9f[\xfe\xe4\x8e\xc2e\xb1%#3\x8e)\xa8\xbf\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4A\xad6\x9fu\x8f\xef8\xa1\x9a\xa3\x14\x93y\x83,\x81\x8e\xf2\xa0\x8966\x9e\xd7t}&\x00\x00\u07d4A\xb2\xd3O\xde\v\x10)&+Ar\xc8\x1c\x15\x90@[\x03\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4A\xb2\xdb\u05dd\u069b\x86Ojp0'T\x19\u00dd>\xfd;\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4A\xc3\xc26u4\xd1;\xa2\xb3?\x18\\\xdb\xe6\xacC\xc2\xfa1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4A\u02d8\x96D_p\xa1\n\x14!R\x96\xda\xf6\x14\xe3,\xf4\u0549g\x8a\x93 b\xe4\x18\x00\x00\u07d4A\xcey\x95\t5\xcf\xf5[\xf7\x8eL\xce\xc2\xfec\x17\x85\u06d5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4A\u04f71\xa3&\xe7hX\xba\xa5\xf4\xbd\x89\xb5{6\x93#C\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94A\xe4\xa2\x02u\xe3\x9b\xdc\xef\xebe\\\x03\"tKvQ@\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4A\xed-\x8ep\x81H,\x91\x9f\xc2=\x8f\x00\x91\xb3\xc8,F\x85\x89F:\x1ev[\u05ca\x00\x00\xe0\x94A\xf2~tK\u049d\xe2\xb0Y\x8f\x02\xa0\xbb\x9f\x98\xe6\x81\ua90a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\u07d4A\xf4\x89\xa1\xect{\u009c>_\x9d\x8d\xb9xw\xd4\u0474\xe9\x89\a?u\u0460\x85\xba\x00\x00\u07d4B\x0f\xb8n}+Q@\x1f\xc5\xe8\xc7 \x15\xde\xcbN\xf8\xfc.\x8965\u026d\xc5\u07a0\x00\x00\u07d4B\x16\x84\xba\xa9\xc0\xb4\xb5\xf5S8\xe6\xf6\xe7\xc8\xe1F\xd4\x1c\xb7\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4B9\x96Y\xac\xa6\xa5\xa8c\xea\"E\xc93\xfe\x9a5\xb7\x88\x0e\x89n\xce2\xc2l\x82p\x00\x00\xe0\x94B;\xcaG\xab\xc0\fpW\xe3\xad4\xfc\xa6>7_\xbd\x8bJ\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4B<1\a\xf4\xba\xceANI\x9cd9\nQ\xf7F\x15\xca^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B<\xc4YL\xf4\xab\xb66\x8d\xe5\x9f\u04b1#\a4a!C\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94BD\xf13\x11X\xb9\xce&\xbb\xe0\xb9#k\x92\x03\xca5\x144\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794BQw\xebt\xad\n\x9d\x9aWR\"\x81G\xeemcV\xa6\u6239\x8b\xc8)\xa6\xf9\x00\x00\u07d4BW%\xc0\xf0\x8f\b\x11\xf5\xf0\x06\xee\xc9\x1c\\\\\x12k\x12\xae\x89\b!\xab\rD\x14\x98\x00\x00\xe0\x94BX\xfdf/\xc4\xce2\x95\xf0\xd4\xed\x8f{\xb1D\x96\x00\xa0\xa9\x8a\x01lE.\xd6\b\x8a\xd8\x00\x00\xe0\x94B\\\x18\x16\x86\x8fww\xcc+\xa6\xc6\u048c\x9e\x1eylR\xb3\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4B\\3\x8a\x13%\xe3\xa1W\x8e\xfa)\x9eW\u0646\xebGO\x81\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94BbY\xb0\xa7Vp\x1a\x8bf5(R!V\xc0(\x8f\x0f$\x8a\x02\x18\xae\x19k\x8dO0\x00\x00\u07d4Bm\x15\xf4\a\xa0\x115\xb1:kr\xf8\xf2R\v51\xe3\x02\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Box\xf7\r\xb2Y\xac\x854\x14[)4\xf4\xef\x10\x98\xb5\u0609\x13\x84\x00\xec\xa3d\xa0\x00\x00\u07d4Bs-\x8e\xf4\x9f\xfd\xa0K\x19x\x0f\xd3\xc1\x84i\xfb7A\x06\x89\x17\v\x00\xe5\u4a7e\x00\x00\u07d4Bt\x17\xbd\x16\xb1\xb3\xd2-\xbb\x90-\x8f\x96W\x01o$\xa6\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Btj\xee\xa1O'\xbe\xff\f\r\xa6BS\xf1\xe7\x97\x18\x90\xa0\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4B{F*\xb8NP\x91\xf4\x8aF\xeb\f\u0712\xdd\xcb&\xe0x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B~GQ\u00fa\xbex\xcf\xf8\x83\b\x86\xfe\xbc\x10\xf9\x90\x8dt\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94B~\xc6h\xac\x94\x04\xe8\x95\u0306\x15\x11\xd1b\nI\x12\xbe\x98\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4B\x80\xa5\x8f\x8b\xb1\v\x94@\u0794\xf4+OY! \x82\x01\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B\x8a\x1e\xe0\xed3\x1dyR\u033e\x1cyt\xb2\x85+\u0453\x8a\x89w\xb7JN\x8d\xe5e\x00\x00\u0794B\x9c\x06\xb4\x87\xe8Tj\xbd\xfc\x95\x8a%\xa3\xf0\xfb\xa5?o\x00\x88\xbbdJ\xf5B\x19\x80\x00\xe0\x94B\xa9\x8b\xf1`'\xceX\x9cN\xd2\xc9X1\xe2rB\x05\x06N\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4B\xc6\xed\xc5\x15\xd3UW\x80\x8d\x13\xcdD\xdc\xc4@\v%\x04\xe4\x89\n\xba\x14\u015b\xa72\x00\x00\u07d4B\xce\xcf\u0492\x10y\xc2\xd7\xdf?\b\xb0z\xa3\xbe\xee^!\x9a\x8965\u026d\xc5\u07a0\x00\x00\u07d4B\u04669\x9b0\x16\xa8Y\u007f\x8bd\t'\xb8\xaf\xbc\xe4\xb2\x15\x89\xa1\x8b\xce\xc3H\x88\x10\x00\x00\u07d4B\xd3I@\xed\xd2\xe7\x00]F\xe2\x18\x8eL\xfe\u0383\x11\xd7M\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4B\u04e5\xa9\x01\xf2\xf6\xbd\x93V\xf1\x12\xa7\x01\x80\xe5\xa1U\v`\x892$\xf4'#\xd4T\x00\x00\u07d4B\u05b2c\xd9\xe9\xf4\x11lA\x14$\xfc\x99Ux<v00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B\xdb\v\x90%Y\xe0@\x87\xdd\\D\x1b\xc7a\x194\x18K\x89\x89m3\xb1}%:b\x00\x00\u07d4B\xdd\xd0\x14\xdcR\xbf\xbc\xc5U2Z@\xb5\x16\xf4\x86j\x1d\u04c9lk\x93[\x8b\xbd@\x00\x00\u07d4C\x19&?u@,\vS%\xf2c\xbeJP\x80e\x10\x87\xf0\x895K\x0f\x14c\x1b\xab\x00\x00\u07d4C\x1f,\x19\xe3\x16\xb0D\xa4\xb3\xe6\x1a\fo\xf8\xc1\x04\xa1\xa1/\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94C\"}e3Ni\x1c\xf21\xb4\xa4\xe1\xd39\xb9]Y\x8a\xfb\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94C(\t\xa29\x0f\a\xc6e\x92\x1f\xf3}T}\x12\xf1\u0256j\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4C)\xfc\t1\xcb\xeb\x038\x80\xfeL\x93\x98\xcaE\xb0\xe2\xd1\x1a\x89lq qm3h\x00\x00\u07d4C-\x88K\u059d\xb1\xac\xc0\u061cd\xad\xe4\xcbO\u00e8\x8bz\x89\x86\x9a\x8c\x10\x80\x8e\xec\x00\x00\u07d4C1\xab7G\xd3W \xa9\xd8\xca%\x16\\\u0485\xac\u053d\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C:;h\xe5k\r\xf1\x86+\x90Xk\xbd9\xc8@\xff\x196\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94C>;\xa1\xc5\x1b\x81\x0f\xc4g\u057aM\xeaB\xf7\xa9\x88^i\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94C>\xb9J3\x90\x86\xed\x12\u067d\xe9\xcd\x1dE\x86\x03\xc9}\u058a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4CI\"Zb\xf7\n\xeaH\n\x02\x99\x15\xa0\x1eSy\xe6O\xa5\x89\x8c\xd6~#4\xc0\xd8\x00\x00\u07d4CT\"\x1eb\xdc\t\xe6@d6\x16:\x18^\xf0m\x11J\x81\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94CTC\xb8\x1d\xfd\xb9\xbd\x8cg\x87\xbc%\x18\xe2\xd4~W\xc1_\x8a\x01C\x8d\x93\x97\x88\x1e\xf2\x00\x00\u07d4Ca\u0504o\xaf\xb3w\xb6\xc0\xeeI\xa5\x96\xa7\x8d\xdf5\x16\xa3\x89\xc2\x12z\xf8X\xdap\x00\x00\xe0\x94Cd0\x9a\x9f\xa0p\x95`\x0fy\xed\xc6Q \xcd\xcd#\xdcd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Cg\xaeK\f\xe9d\xf4\xa5J\xfdK\\6\x84\x96\xdb\x16\x9e\x9a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Ct\x89(\xe8\xc3\xecD6\xa1\u0412\xfb\xe4:\xc7I\xbe\x12Q\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4Cv{\xf7\xfd*\xf9[r\xe91-\xa9D<\xb1h\x8eCC\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94Cy\x838\x8a\xb5\x9aO\xfc!_\x8e\x82iF\x10)\xc3\xf1\xc1\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4C\x89\x8cI\xa3MP\x9b\xfe\xd4\xf7`A\xee\x91\xca\xf3\xaaj\xa5\x89\x10CV\x1a\x88)0\x00\x00\u07d4C\x8c/T\xff\x8eb\x9b\xab6\xb1D+v\v\x12\xa8\x8f\x02\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\x98b\x8e\xa6c-9>\x92\x9c\xbd\x92\x84d\xc5h\xaaJ\f\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4C\x9d//Q\x10\xa4\u054b\x17W\x93P\x15@\x87@\xfe\xc7\xf8\x89\u03e5\xc5\x15\x0fL\x88\x80\x00\u07d4C\x9d\xee?vy\xff\x100s?\x93@\xc0\x96hkI9\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xb0y\xba\xf0ry\x99\xe6k\xf7C\u057c\xbfwl;\t\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xbc-M\xdc\xd6X;\xe2\u01fc\tK(\xfbr\xe6+\xa8;\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xc7\xeb\u0173\xe7\xaf\x16\xf4}\xc5az\xb1\x0e\x0f9\xb4\xaf\xbb\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4C\u02d6R\x81\x8coMg\x96\xb0\xe8\x94\t0ly\xdbcI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xcc\b\xd0s*\xa5\x8a\xde\xf7a\x9b\xedFU\x8a\xd7wAs\x89\xf0\xe7\u0730\x12*\x8f\x00\x00\xe0\x94C\u0567\x1c\xe8\xb8\xf8\xae\x02\xb2\xea\xf8\xea\xf2\xca(@\xb9?\xb6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794C\xdb\u007f\xf9Z\bm(\ubff8/\xb8\xfb_#\n^\xbc\u0348\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4C\xe7\xec\x84cX\xd7\xd0\xf97\xad\x1c5\v\xa0i\u05ffr\xbf\x89\x06p\xaeb\x92\x14h\x00\x00\u07d4C\xf1o\x1eu\xc3\xc0j\x94x\xe8\u0157\xa4\n<\xb0\xbf\x04\u0309\x9d\xf7\u07e8\xf7`H\x00\x00\u07d4C\xf4p\xede\x9e)\x91\xc3u\x95~]\xde\u017d\x1d8\"1\x89\x05k\xc7^-c\x10\x00\x00\u07d4C\xf7\xe8n8\x1e\xc5\x1e\u0110m\x14v\u02e9z=\xb5\x84\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4C\xff8t>\xd0\xcdC0\x8c\x06e\t\u030e~r\xc8b\xaa\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94C\xff\x88S\xe9\x8e\xd8@k\x95\x00\n\u0684\x83b\u05a09*\x8a\x04\xae\v\x1cM.\x84\xd0\x00\x00\u07d4D\t\x88f\xa6\x9bh\xc0\xb6\xbc\x16\x82)\xb9`5\x87\x05\x89g\x89\n1\x06+\xee\xedp\x00\x00\u07d4D\x19\xaca\x8d]\xea|\xdc`w o\xb0}\xbd\xd7\x1c\x17\x02\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4D\x1aR\x00\x16a\xfa\xc7\x18\xb2\u05f3Q\xb7\xc6\xfbR\x1az\xfd\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94D\x1a\u0282c\x13$\xac\xbf\xa2F\x8b\xda2[\xbdxG{\xbf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4D\x1f7\xe8\xa0)\xfd\x02H/(\x9cI\xb5\xd0m\x00\xe4\b\xa4\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4D \xaa5F[\xe6\x17\xad$\x98\xf3p\xde\n<\xc4\xd20\xaf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4D#/\xf6m\xda\xd1\xfd\x84\x12f8\x006\xaf\xd7\xcf}\u007fB\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4D%\rGn\x06$\x84\xe9\b\n9g\xbf:Js*\xd7?\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4D)\xa2\x9f\xee\x19\x84Pg,\f\x1d\a1b%\v\xecdt\x896*\xaf\x82\x02\xf2P\x00\x00\u07d4D5RS\xb2wH\xe3\xf3O\xe9\xca\xe1\xfbq\x8c\x8f$\x95)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4D8\xe8\x80\xcb'f\xb0\xc1\u03ae\xc9\xd2A\x8f\u03b9R\xa0D\x89\a?\xa0s\x90?\b\x00\x00\u07d4DL\xafy\xb7\x138\ue6a7\xc73\xb0*\u02a7\xdc\x02YH\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4D\\\xb8\xde^=\xf5 \xb4\x99\xef\u0240\xf5+\xff@\xf5\\v\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Dj\x809\xce\u03dd\xceHy\xcb\xca\xf3I;\xf5E\xa8\x86\x10\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4Dt)\x9d\x0e\xe0\x90\u0710x\x9a\x14\x86H\x9c=\rd^m\x8965\u026d\xc5\u07a0\x00\x00\u07d4D\x8b\xf4\x10\xad\x9b\xbc/\xec\xc4P\x8d\x87\xa7\xfc.K\x85a\xad\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4D\x90\x1e\r\x0e\b\xac=^\x95\xb8\xec\x9d^\x0f\xf5\xf1.\x03\x93\x89\x16\xa1\xf9\xf5\xfd}\x96\x00\x00\xe0\x94D\x93\x12<\x02\x1e\xce;3\xb1\xa4R\xc9&\x8d\xe1@\a\xf9\u04ca\x01je\x02\xf1Z\x1eT\x00\x00\xe0\x94D\x9a\xc4\xfb\xe3\x83\xe3g8\x85^6JW\xf4q\xb2\xbf\xa11\x8a)\xb7d2\xb9DQ \x00\x00\u07d4D\xa0\x1f\xb0J\xc0\xdb,\xce]\xbe(\x1e\x1cF\xe2\x8b9\xd8x\x89lj\xccg\u05f1\xd4\x00\x00\u07d4D\xa6=\x18BE\x87\xb9\xb3\a\xbf\xc3\xc3d\xae\x10\xcd\x04\xc7\x13\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94D\xa8\x98\x9e20\x81!\xf7$f\x97\x8d\xb3\x95\xd1\xf7l:K\x8a\x01\x88P)\x9fB\xb0j\x00\x00\u07d4D\xc1\x11\v\x18\x87\x0e\xc8\x11x\xd9=!X8\xc5Q\u050ed\x89\n\xd6\xf9\x85\x93\xbd\x8f\x00\x00\u07d4D\xc1Ge\x12|\xde\x11\xfa\xb4l],\xf4\u0532\x89\x00#\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94D\xc5N\xaa\x8a\xc9@\xf9\xe8\x0f\x1et\xe8/\xc1O\x16v\x85j\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4D\xcdwSZ\x89?\xa7\xc4\xd5\xeb:$\x0ey\u0419\xa7--\x89,s\xc97t,P\x00\x00\u07d4D\u07faP\xb8)\xbe\xcc_O\x14\u0470J\xab3 \xa2\x95\xe5\x8965\u026d\xc5\u07a0\x00\x00\u07d4D\xe2\xfd\xc6y\xe6\xbe\xe0\x1e\x93\xefJ:\xb1\xbc\xce\x01*\xbc|\x89\x16=\x19I\x00\xc5E\x80\x00\xe0\x94D\xf6/*\xaa\xbc)\xad:k\x04\xe1\xffo\x9c\xe4R\xd1\xc1@\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4D\xff\xf3{\xe0\x1a8\x88\u04f8\xb8\u1200\xa7\xdd\xef\xee\xea\u04c9\x0e\f[\xfc}\xae\x9a\x80\x00\u07d4E\x06\xfe\x19\xfaK\x00k\xaa9\x84R\x9d\x85\x16\xdb++P\xab\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\x1b6\x99G[\xed]y\x05\xf8\x90Z\xa3Eo\x1e\u05c8\xfc\x89\x8a\xc7#\x04\x89\xe8\x00\x00\x00\u0794E\x1bpp%\x9b\u06e2q\x00\xe3n#B\x8aS\xdf\xe3\x04\u9239\x8b\xc8)\xa6\xf9\x00\x00\u07d4E'+\x8fb\xe9\xf9\xfa\x8c\xe0D \u1ba3\xeb\xa9hn\xac\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94E+d\u06ce\xf7\xd6\u07c7\u01c8c\x9c\"\x90\xbe\x84\x82\xd5u\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4E>5\x9a3\x97\x94LZ'Z\xb1\xa2\xf7\n^Z?i\x89\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4EI\xb1Yy%_~e\xe9\x9b\rV\x04\u06d8\xdf\xca\u023f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4EKa\xb3D\xc0\xef\x96Qy#\x81U\xf2w\u00c2\x9d\v8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94EO\x01A\xd7!\xd3<\xbd\xc4\x10\x18\xbd\x01\x11\x9a\xa4xH\x18\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4ES3\x90\xe3@\xfe\r\xe3\xb3\xcf_\xb9\xfc\x8e\xa5R\xe2\x9eb\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4ES\x96\xa4\xbb\u067a\u8bdf\xb7\xc4\xd6MG\x1d\xb9\xc2E\x05\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4E[\x92\x96\x92\x1at\xd1\xfcAa\u007fC\xb80>o>\xd7l\x89\u3bb5sr@\xa0\x00\x00\u07d4E\\\xb8\xee9\xff\xbcu#1\xe5\xae\xfcX\x8e\xf0\xeeY4T\x8965F:x\r\xef\x80\x00\u07d4Ej\u0b24\x8e\xbc\xfa\xe1f\x06\x02PR_c\x96^v\x0f\x89\x10CV\x1a\x88)0\x00\x00\u07d4Eo\x8dtf\x82\xb2$g\x93I\x06M\x1b6\x8c|\x05\xb1v\x89\u0213\u041c\x8fQP\x00\x00\u07d4Ep)\xc4i\xc4T\x8d\x16\x8c\xec>e\x87.D(\xd4+g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Eq\xdeg+\x99\x04\xba\xd8t6\x92\xc2\x1cO\xdc\xeaL.\x01\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Ex\x1b\xbew\x14\xa1\xc8\xf7;\x1cty!\xdfO\x84'\x8bp\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E{\xce\xf3}\xd3\xd6\v-\xd0\x19\xe3\xfea\xd4k?\x1erR\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94E\x8e<\u025e\x94xD\xa1\x8ejB\x91\x8f\xef~\u007f_^\xb3\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\u07d4E\x93\x93\xd6:\x06>\xf3r\x1e\x16\xbd\x9f\xdeE\ue77dw\xfb\x89j\xba\u05a3\xc1S\x05\x00\x00\u07d4E\xa5p\xdc\xc2\t\f\x86\xa6\xb3\xea)\xa6\bc\xdd\xe4\x1f\x13\xb5\x89\f\x9a\x95\xee)\x86R\x00\x00\u07d4E\xa8 \xa0g/\x17\xdct\xa0\x81\x12\xbcd?\xd1\x16w6\u00c9\n\xd6\xc4;(\x15\xed\x80\x00\u07d4E\xb4q\x05\xfeB\xc4q-\xcen*!\xc0[\xff\xd5\xeaG\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\xbb\x82\x96R\u063f\xb5\x8b\x85'\xf0\xec\xb6!\u009e!.\u00c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\xc0\u045f\v\x8e\x05O\x9e\x8986\xd5\xec\xaey\x01\xaf(\x12\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4E\xc4\xec\xb4\xee\x89\x1e\xa9\x84\xa7\xc5\xce\xfd\x8d\xfb\x001\v(P\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4E\u028d\x95f\b\xf9\xe0\n/\x99t\x02\x86@\x88\x84ef\x8f\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\u0298b\x00;N@\xa3\x17\x1f\xb5\xca\xfa\x90(\xca\xc8\xde\x19\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4E\xd1\xc9\xee\xdf|\xabA\xa7y\x05{y9_T(\xd8\x05(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\u0535M7\xa8\xcfY\x98!#_\x06/\xa9\xd1p\xed\u8909\x11\x90g;_\u0690\x00\x00\xe0\x94E\xdb\x03\xbc\xcf\u05a5\xf4\xd0&k\x82\xa2*6\x87\x92\xc7}\x83\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4E\xe3\xa9>r\x14J\u0686\f\xbcV\xff\x85\x14Z\xda8\xc6\u0689WG=\x05\u06ba\xe8\x00\x00\u07d4E\u6378\u06fa\xba_\xc2\xcb3|b\xbc\xd0\xd6\x1b\x05\x91\x89\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\u6379L}\n\xb7\xacA\x85zq\xd6qG\x87\x0fNq\x8aT\xb4\v\x1f\x85+\xda\x00\x00\x00\u07d4E\xf4\xfc`\xf0\x8e\xac\xa1\x05\x98\xf03c)\x80\x1e<\x92\xcbF\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4F\rSU\xb2\xce\xebnb\x10}\x81\xe5\x12p\xb2k\xf4V \x89l\xb7\xe7Hg\xd5\xe6\x00\x00\xe0\x94F\"O2\xf4\xec\xe5\u0206p\x90\xd4@\x9dU\xe5\v\x18C-\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4F'\xc6\x06\x84&q\xab\u0782\x95\xee]\xd9L\u007fT\x954\xf4\x89\x0f\x89_\xbd\x872\xf4\x00\x00\u07d4F+g\x8bQ\xb5\x84\xf3\xedz\xda\a\v\\\u065c\v\xf7\xb8\u007f\x89\x05k\xc7^-c\x10\x00\x00\u07d4FM\x9c\x89\xcc\xe4\x84\xdf\x00\x02w\x19\x8e\xd8\a_\xa65r\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4FPNj!Z\xc8;\xcc\xf9V\xbe\xfc\x82\xabZg\x93q\u0209\x1c!(\x05\u00b4\xa5\x00\x00\xe0\x94FQ\xdcB\x0e\b\xc3);'\xd2Ix\x90\xebP\":\xe2\xf4\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4FS\x1e\x8b\x1b\xde\t\u007f\u07c4\x9dm\x11\x98\x85`\x8a\x00\x8d\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4Fb\x92\xf0\xe8\rC\xa7\x87t'u\x90\xa9\xebE\x96\x12\x14\xf4\x894\x95tD\xb8@\xe8\x00\x00\xe0\x94Fb\xa1v^\xe9!\x84-\u0708\x89\x8d\x1d\xc8bu\x97\xbd~\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Fe\xe4s\x96\xc7\u06d7\xeb*\x03\xd9\bc\xd5\u053a1\x9a\x94\x89 \x86\xac5\x10R`\x00\x00\u07d4Fo\xdak\x9bX\xc5S'P0j\x10\xa2\xa8\xc7h\x10;\a\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4Fq$\xae\u007fE/&\xb3\xd5t\xf6\b\x88\x94\xfa]\x1c\xfb;\x89\x92^\x06\xee\xc9r\xb0\x00\x00\u0794Fr*6\xa0\x1e\x84\x1d\x03\xf7\x80\x93^\x91}\x85\u0566z\xbd\x88\xce\xc7o\x0eqR\x00\x00\u07d4Fw\x9aVV\xff\x00\xd7>\xac:\xd0\u00cbl\x850\x94\xfb@\x89\f\x82S\xc9lj\xf0\x00\x00\u07d4Fw\xb0N\x03C\xa3!1\xfdj\xbb9\xb1\xb6\x15k\xba=[\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4F}Y\x88$\x9ahaG\x16e\x98@\xed\n\xe6\xf6\xf4W\xbc\x89\x15\x01\xa4\x8c\xef\xdf\xde\x00\x00\u07d4F~\x0e\xd5O;v\xae\x066\x17n\aB\b\x15\xa0!sn\x89lk\x93[\x8b\xbd@\x00\x00\u07d4F~\xa1\x04E\x82~\xf1\xe5\x02\xda\xf7k\x92\x8a \x9e\r@2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94F\u007f\xbfAD\x16\x00u\u007f\xe1X0\xc8\xcd_O\xfb\xbb\xd5`\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94F\x93Xp\x932\xc8+\x88~ \xbc\xdd\xd0\"\x0f\x8e\u06e7\u040a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4F\x97\xba\xaf\x9c\xcb`?\xd3\x040h\x9dCTE\xe9\u024b\xf5\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4F\xa3\v\x8a\x80\x891!tE\xc3\xf5\xa9>\x88,\x03E\xb4&\x89\r\x8d\xb5\xeb\u05f2c\x80\x00\u07d4F\xa40\xa2\u0528\x94\xa0\u062a?\xea\xc6\x156\x14\x15\xc3\xf8\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4F\xaaP\x18pg~\u007f\nPHv\xb4\xe8\x80\x1a\n\xd0\x1cF\x89+^:\xf1k\x18\x80\x00\x00\u07d4F\xbf\u0172\a\xeb \x13\xe2\xe6\x0fw_\xec\xd7\x18\x10\u0159\f\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4F\xc1\xaa\"D\xb9\u0229W\u028f\xacC\x1b\x05\x95\xa3\xb8h$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4F\xd8\x061(B\x03\xf6(\x8e\xcdNWX\xbb\x9dA\xd0]\xbe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\n\xc5\xd1\xf3\xef\xe2\x8f8\x02\xaf\x92[W\x1ec\x86\x8b9}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\x10\x10\xdaI/@\x18\x83;\b\x8d\x98r\x90\x1e\x06\x12\x91t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4G\x12T\x02e\xcb\xee\u00c4p\"\u015f\x1b1\x8dC@\n\x9e\x89\xbd\xbcA\xe04\x8b0\x00\x00\xe0\x94G\x14\u03e4\xf4k\u05bdps}u\x87\x81\x97\xe0\x8f\x88\xe61\x8a\x02\u007f>\u07f3Nn@\x00\x00\u07d4G H\xcc`\x9a\xeb$!e\uaa87\x05\x85\f\xf3\x12]\xe0\x8965\u026d\xc5\u07a0\x00\x00\u07d4G!\x92)\xe8\xcdVe\x9ae\u00a9C\xe2\u075a\x8fK\xfd\x89\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4G7\xd0B\xdcj\xe7>\xc7:\xe2Qz\u03a2\xfd\xd9d\x87\u014965\u026d\xc5\u07a0\x00\x00\u07d4GAX\xa1\xa9\xdci<\x13?e\xe4{\\:\xe2\xf7s\xa8o\x89\n\xdaUGK\x814\x00\x00\u07d4GE\xab\x18\x1a6\xaa\x8c\xbf\"\x89\xd0\xc4Qe\xbc~\xbe#\x81\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4GPf\xf9\xad&eQ\x96\xd5SS'\xbb\xeb\x9by)\xcb\x04\x89\xa4\xccy\x95c\u00c0\x00\x00\xe0\x94GR!\x8eT\xdeB?\x86\xc0P\x193\x91z\xea\b\xc8\xfe\u054a\x04<3\xc1\x93ud\x80\x00\x00\u07d4GZa\x93W-JNY\u05fe\t\u02d6\r\u074cS\x0e/\x89$,\xf7\x8c\xdf\a\xff\x80\x00\u07d4Gd\x8b\xed\x01\xf3\xcd2I\bNc]\x14\u06a9\xe7\xec<\x8a\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4Gh\x84\x10\xff%\xd6T\xd7.\xb2\xbc\x06\xe4\xad$\xf83\xb0\x94\x89\b\xb2\x8da\xf3\u04ec\x00\x00\u07d4GkU\x99\b\x9a?\xb6\xf2\x9clr\xe4\x9b.G@\ua00d\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4Gs\x0f_\x8e\xbf\x89\xacr\xef\x80\xe4l\x12\x19P8\xec\xdcI\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94G{$\xee\u80deO\u045d\x12P\xbd\vfEyJa\u028a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4G\x81\xa1\nM\xf5\uef02\xf4\xcf\xe1\a\xba\x1d\x8av@\xbdf\x89a\t=|,m8\x00\x00\u07d4G\x88Z\xba\xbe\xdfM\x92\x8e\x1c<q\xd7\xca@\xd5c\xedY_\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4G\x8d\xc0\x9a\x13\x117|\t?\x9c\u022et\x11\x1fe\xf8/9\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94G\x8eRN\xf2\xa3\x81\xd7\f\x82X\x8a\x93\xcaz_\xa9\xd5\x1c\xbf\x8a5\xfa\x97\"o\x88\x99p\x00\x00\xe0\x94G\x92\x98\xa9\xde\x14~c\xa1\xc7\xd6\xd2\xfc\xe0\x89\xc7\xe6@\x83\xbd\x8a\x02\x1e\x19\xdd<<\ry\x80\x00\u07d4G\x9a\xbf-\xa4\u0547\x16\xfd\x97:\r\x13\xa7_S\x01P&\n\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4G\xa2\x81\xdf\xf6Ag\x19xU\xbfnp^\xb9\xf2\xce\xf62\xea\x8966\xc9yd6t\x00\x00\u07d4G\xbe\xb2\x0fu\x91\x00T*\xa9=A\x11\x8b2\x11\xd6d\x92\x0e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94G\xc2G\xf5;\x9f\xbe\xb1{\xba\a\x03\xa0\f\x00\x9f\xdb\x0fn\xae\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794G\xc7\xe5\ufd0b:\xedK|n\x82KC_5}\xf4\xc7#\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4G\u03dc\xda\xf9/\u0259\xcc^\xfb\xb7 <a\xe4\xf1\xcd\xd4\u00c9\a\x1f\x8a\x93\xd0\x1eT\x00\x00\u07d4G\xd2\x0ej\xe4\xca\xd3\xf8)\xea\xc0~Z\xc9{f\xfd\xd5l\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4G\u05d2\xa7Vw\x9a\xed\xf14>\x88\x83\xa6a\x9cl(\x11\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94G\xe2]\xf8\x82%8\xa8Yk(\xc67\x89kM\x14<5\x1d\x8a\x11\v\xe9\xeb$\xb8\x81P\x00\x00\u07d4G\xf4ik\xd4b\xb2\r\xa0\x9f\xb8>\xd2\x03\x98\x18\xd7v%\xb3\x89\b\x13\xcaV\x90m4\x00\x00\u07d4G\xfe\xf5\x85\x84FRH\xa0\x81\r`F>\xe9>Zn\xe8\u04c9\x0fX\xcd>\x12i\x16\x00\x00\u07d4G\xffo\xebC! `\xbb\x15\x03\u05e3\x97\xfc\b\xf4\xe7\x03R\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\xff\xf4,g\x85Q\xd1A\xebu\xa6\xee9\x81\x17\xdf>J\x8d\x89\x05k\xea\xe5\x1f\xd2\xd1\x00\x00\u07d4H\x01\x0e\xf3\xb8\xe9^?0\x8f0\xa8\xcb\u007fN\xb4\xbf`\xd9e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4H\n\xf5 v\x00\x9c\xa77\x81\xb7\x0eC\xb9Y\x16\xa6\"\x03\xab\x892\x19r\xf4\b=\x87\x80\x00\u07d4H\x0f1\xb9\x891\x1eA$\u01a7F_ZD\tM6\xf9\u04097\x90\xbb\x85Q7d\x00\x00\xe0\x94H\x11\x15)j\xb7\xdbRI/\xf7\xb6G\xd63)\xfb\\\xbck\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4H\x1e:\x91\xbf\xdc/\x1c\x84(\xa0\x11\x9d\x03\xa4\x16\x01A~\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4H(\xe4\xcb\xe3N\x15\x10\xaf\xb7,+\ueb0aE\x13\xea\xeb\u0649\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94H)\x82\xac\x1f\x1cm\x17!\xfe\xec\u0679\xc9l\xd9I\x80PU\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4H0,1\x1e\xf8\xe5\xdcfAX\xddX<\x81\x19Mn\rX\x89\xb6gl\xe0\xbc\xcb\\\x00\x00\u07d4H;\xa9\x904\xe9\x00\xe3\xae\xdfaI\x9d;+\xce9\xbe\xb7\xaa\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4HT\x8bK\xa6+\xcb/\r4\xa8\x8d\u019ah\x0eS\x9c\xf0F\x89\x05l\xf1\u02fbt2\x00\x00\u07d4Hc\x84\x979&Zc\xb0\xa2\xbf#jY\x13\xe6\xf9Y\xce\x15\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4He\x9d\x8f\x8c\x9a/\xd4Oh\u06a5]#\xa6\b\xfb\xe5\x00\u0709lk\x93[\x8b\xbd@\x00\x00\xe0\x94Hf\x9e\xb5\xa8\x01\u0637_\xb6\xaaX\xc3E\x1bpX\xc2C\xbf\x8a\x06\x8dB\xc18\u06b9\xf0\x00\x00\u07d4Hjl\x85\x83\xa8D\x84\xe3\xdfC\xa1#\x83\u007f\x8c~#\x17\u0409\x11\x87\xc5q\xab\x80E\x00\x00\u07d4Hz\xdf}p\xa6t\x0f\x8dQ\xcb\xddh\xbb?\x91\u0125\xceh\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4H~\x10\x85\x02\xb0\xb1\x89\uf70cm\xa4\xd0\xdbba\xee\xc6\xc0\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94H\x88\xfb%\xcdP\u06f9\xe0H\xf4\x1c\xa4}x\xb7\x8a'\xc7\u064a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u0794H\x934\u00b6\x95\xc8\xee\a\x94\xbd\x86B\x17\xfb\x9f\xd8\xf8\xb15\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4H\xa3\r\xe1\xc9\x19\xd3\xfd1\x80\xe9}_+*\x9d\xbd\x96M-\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4H\xbf\x14\u05f1\xfc\x84\xeb\xf3\xc9k\xe1/{\xce\x01\xaai\xb0>\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4H\xc2\ue465\aV\xd8\u039a\xbe\xebu\x89\xd2,o\xee]\xfb\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4H\xc5\u0197\v\x91a\xbb\x1c{z\xdf\xed\x9c\xde\u078a\x1b\xa8d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94H\xd2CKz}\xbb\xff\b\";c\x87\xb0]\xa2\xe5\t1&\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4H\xd4\xf2F\x8f\x96?\u05da\x00a\x98\xbbg\x89]-Z\xa4\u04c9K\xe4\xe7&{j\xe0\x00\x00\u07d4H\xe0\xcb\xd6\u007f\x18\xac\xdbzb\x91\xe1%M\xb3.\trs\u007f\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4H\xf6\n5HO\xe7y+\u030a{c\x93\xd0\u0761\xf6\xb7\x17\x89\xc3(\t>a\xee@\x00\x00\u07d4H\xf8\x83\xe5g\xb46\xa2{\xb5\xa3\x12M\xbc\x84\xde\xc7u\xa8\x00\x89)\xd7n\x86\x9d\u0340\x00\x00\xe0\x94I\x01E\xaf\xa8\xb5E\"\xbb!\xf3R\xf0m\xa5\xa7\x88\xfa\x8f\x1d\x8a\x01\xf4lb\x90\x1a\x03\xfb\x00\x00\u07d4I\t\xb3\x19\x98\xea\xd4\x14\xb8\xfb\x0e\x84k\xd5\xcb\xde995\xbe\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4I\x12\xd9\x02\x93\x16v\xff9\xfc4\xfe<<\xc8\xfb!\x82\xfaz\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4I\x13o\xe6\xe2\x8btS\xfc\xb1kk\xbb\u9aac\xba\x837\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94I\x15a\u06cbo\xaf\xb9\x00~b\xd0P\u0082\xe9,Kk\u020a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4I\x18]\xd7\xc262\xf4lu\x94s\ubb96`\b\xcd5\x98\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4I,\xb5\xf8a\xb1\x87\xf9\xdf!\xcdD\x85\xbe\xd9\vP\xff\xe2-\x89\x1b\x19\xe5\vD\x97|\x00\x00\u07d4I-\xe4j\xaf\x8f\x1dp\x8dY\u05da\xf1\xd0:\xd2\xcb`\x90/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I.p\xf0M\x18@\x8c\xb4\x1e%`70Pk5\xa2\x87k\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4I:g\xfe#\xde\xccc\xb1\r\xdau\xf3(v\x95\xa8\x1b\u056b\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4I=H\xbd\xa0\x15\xa9\xbf\xcf\x16\x03\x93n\xabh\x02L\xe5Q\xe0\x89\x018\xa3\x88\xa4<\x00\x00\x00\xe0\x94IBV\xe9\x9b\x0f\x9c\xd6\xe5\xeb\xca8\x99\x862R\x90\x01e\u020a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4IM\xecM^\xe8\x8a'q\xa8\x15\xf1\xeerd\x94/\xb5\x8b(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I[d\x1b\x1c\u07a3b\u00f4\u02fd\x0f\\\xc5\v\x1e\x17k\x9c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Ih\xa2\xce\xdbEuU\xa19)Z\xea(wnT\x00<\x87\x8a\x02#\x1a\xef\u0266b\x8f\x00\x00\u07d4Im6U4S\n_\xc1W|\nRA\u02c8\xc4\xdapr\x89a\t=|,m8\x00\x00\xe0\x94In1\x95\x92\xb3A\xea\xcc\xd7x\u0767\xc8\x19mT\xca\xc7u\x8a\x01\xf5q\x89\x87fKH\x00\x00\u07d4IoXC\xf6\xd2L\u064d%^L#\xd1\xe1\xf0#\"uE\x89_\x17\x9f\u0526\xee\t\x80\x00\xe0\x94Ip\u04ec\xf7+[\x1f2\xa7\x00<\xf1\x02\xc6N\xe0TyA\x8a\x1d\xa5jK\b5\xbf\x80\x00\x00\u07d4Iw\xa7\x93\x9d\t9h\x94U\xce&9\xd0\xeeZL\xd9\x10\xed\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4Iy\x19N\xc9\xe9}\xb9\xbe\xe84;|w\xd9\xd7\xf3\xf1\u071f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Iy4c\xe1h\x10\x83\u05ab\xd6\xe7%\u057b\xa7E\xdc\xcd\xe8\x89\x1d\x98\xe9LNG\x1f\x00\x00\u07d4I\x81\xc5\xfff\xccN\x96\x80%\x1f\xc4\xcd/\xf9\a\xcb2xe\x89(\xa8WBTf\xf8\x00\x00\u07d4I\x89\u007f\xe92\xbb\xb3\x15L\x95\u04fc\xe6\xd9;ms)\x04\u0749\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4I\x89\xe1\xab^|\xd0\aF\xb3\x93\x8e\xf0\xf0\xd0d\xa2\x02[\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I\x8a\xbd\xeb\x14\xc2k{r4\xd7\x0f\u03ae\xf3a\xa7m\xffr\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4I\xa6E\xe0f}\xfd{2\xd0u\xcc$g\u074ch\t\a\u0109\a\x06\x01\x95\x8f\u02dc\x00\x00\xe0\x94I\xb7N\x16\x92e\xf0\x1a\x89\xecL\x90r\u0164\xcdr\xe4\xe85\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4I\xbd\xbc{\xa5\xab\xeb\xb68\x9e\x91\xa3(R \xd3E\x1b\xd2S\x8965\u026d\xc5\u07a0\x00\x00\u07d4I\xc9A\xe0\xe5\x01\x87&\xb7)\x0f\xc4s\xb4q\xd4\x1d\xae\x80\u0449\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94I\xc9w\x1f\xca\x19\u0579\xd2E\u0211\xf8\x15\x8f\xe4\x9fG\xa0b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4I\xcf\x1eT\xbe61\x06\xb9 r\x9d-\v\xa4o\bg\x98\x9a\x89\x0e\x87?D\x13<\xb0\x00\x00\u07d4I\xd2\u008e\xe9\xbcT^\xaa\xf7\xfd\x14\xc2|@s\xb4\xbb_\x1a\x89O\xe9\xb8\x06\xb4\r\xaf\x00\x00\u07d4I\xdd\xee\x90.\x1d\f\x99\u0471\x1a\xf3\u030a\x96\xf7\x8eM\xcf\x1a\x89\n\u03a5\xe4\xc1\x8cS\x00\x00\u07d4I\xf0(9[Z\x86\xc9\xe0\u007fwxc\x0eL.=7:w\x89\x06\xa7JP8\u06d1\x80\x00\xe0\x94J\x19 5\xe2a\x9b$\xb0p\x9dVY\x0e\x91\x83\xcc\xf2\xc1\u064a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4J@S\xb3\x1d\x0e\xe5\u06ef\xb1\xd0k\u05ec\u007f\xf3\",G\u0589K\xe4\xe7&{j\xe0\x00\x00\u07d4JC\x01p\x15-\xe5\x17&3\u0742b\xd1\a\xa0\xaf\xd9j\x0f\x89\xabM\xcf9\x9a:`\x00\x00\u07d4JG\xfc>\x17\u007fVz\x1e8\x93\xe0\x00\xe3k\xba#R\n\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4JR\xba\xd2\x03W\"\x8f\xaa\x1e\x99k\xedy\f\x93gK\xa7\u0409Hz\x9a0E9D\x00\x00\u07d4JS\xdc\xdbV\xceL\xdc\xe9\xf8.\xc0\xeb\x13\xd6sR\xe7\u020b\x89\u3bb5sr@\xa0\x00\x00\u07d4J_\xae;\x03r\xc20\xc1%\xd6\xd4p\x14\x037\xab\x91VV\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4Jq\x90a\xf5(T\x95\xb3{\x9d~\xf8\xa5\x1b\a\xd6\u6b2c\x89\n\xd4\xc81j\v\f\x00\x00\u07d4Js8\x92\x98\x03\x1b\x88\x16\u0329FB\x1c\x19\x9e\x18\xb3C\u0589\"8h\xb8y\x14o\x00\x00\u07d4Js]\"G\x927m3\x13g\xc0\x93\xd3\x1c\x87\x944\x15\x82\x89f\xff\xcb\xfd^Z0\x00\x00\u07d4Jt\x94\xcc\xe4HU\u0300X(B\xbe\x95\x8a\r\x1c\x00r\ue242\x1a\xb0\xd4AI\x80\x00\x00\u07d4Ju\xc3\xd4\xfao\u033d]\u0567\x03\xc1Sy\xa1\xe7\x83\u9dc9b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94J\x81\xab\xe4\x98L|k\xefc\u0598 \xe5WC\xc6\x1f \x1c\x8a\x03d\x01\x00N\x9a\xa3G\x00\x00\u07d4J\x82iO\xa2\x9d\x9e!2\x02\xa1\xa2\t(]\xf6\xe7E\xc2\t\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4J\x83\\%\x82LG\xec\xbf\u01d49\xbf?\\4\x81\xaau\u0349K\xe4\xe7&{j\xe0\x00\x00\u07d4J\x91\x802C\x91Y\xbb1[g%\xb6\x83\r\xc86\x97s\x9f\x89\x12\xa3.\xf6x3L\x00\x00\u07d4J\x97\xe8\xfc\xf4c^\xa7\xfc^\x96\xeeQu.\u00c8qk`\x89\x1d\x99E\xab+\x03H\x00\x00\u07d4J\x9a&\xfd\n\x8b\xa1\x0f\x97}\xa4\xf7|1\x90\x8d\xabJ\x80\x16\x89a\t=|,m8\x00\x00\u07d4J\xa1H\xc2\xc34\x01\xe6j+Xnew\u0132\x92\xd3\xf2@\x89\v\xb8`\xb2\x85\xf7t\x00\x00\u07d4J\xa6\x93\xb1\"\xf3\x14H*G\xb1\x1c\xc7|h\xa4\x97\x87ab\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4J\xb2\xd3O\x04\x83O\xbftyd\x9c\xab\x92=,G%\xc5S\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4J\xc0vs\xe4/d\xc1\xa2^\xc2\xfa-\x86\xe5\xaa+4\xe09\x89lk\x93[\x8b\xbd@\x00\x00\u07d4J\u016c\xad\x00\v\x88w!L\xb1\xae\x00\xea\u0263}Y\xa0\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4J\u0250ZL\xb6\xab\x1c\xfdbTn\xe5\x91s\x00\xb8|O\u07897\b\xba\xed=h\x90\x00\x00\u07d4J\u03e9\xd9N\xdaf%\xc9\u07e5\xf9\xf4\xf5\xd1\a\xc4\x03\x1f\u07c9\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4J\xd0G\xfa\xe6~\xf1b\xfeh\xfe\xdb\xc2};e\xca\xf1\f6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4J\xd9]\x18\x8dddp\x9a\xdd%U\xfbM\x97\xfe\x1e\xbf1\x1f\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d4J\xdb\xf4\xaa\xe0\xe3\xefD\xf7\xddM\x89\x85\u03ef\tn\u010e\x98\x89\b!\xab\rD\x14\x98\x00\x00\u07d4J\xe2\xa0M9\t\xefENTL\xcf\xd6\x14\xbf\xef\xa7\x10\x89\xae\x89\x18\x01\x15\x9d\xf1\xee\xf8\x00\x00\xe0\x94J\xe90\x82\xe4Q\x87\xc2a`\xe6g\x92\xf5\u007f\xad5Q\xc7:\x8a\x04\x96\x15 \xda\xff\x82(\x00\x00\u07d4J\xf0\xdb\a{\xb9\xba^D>!\xe1H\xe5\x9f7\x91\x05\u0152\x89 \x86\xac5\x10R`\x00\x00\u07d4K\x06\x19\xd9\u062a1:\x951\xac}\xbe\x04\xca\rjZ\u0476\x89lk\x93[\x8b\xbd@\x00\x00\u07d4K\v\u062c\xfc\xbcS\xa6\x01\v@\xd4\u040d\xdd-\x9dib-\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4K\x19\xeb\f5K\xc199`\xeb\x06\x06;\x83\x92o\rg\xb2\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4K)C|\x97\xb4\xa8D\xbeq\u0323\xb6H\xd4\xca\x0f\u075b\xa4\x89\b$q\x984\u03ec\x00\x00\u07d4K1\xbfA\xab\xc7\\\x9a\xe2\u034f\u007f5\x16;n+tPT\x89\x14\xb5P\xa0\x13\xc78\x00\x00\u07d4K:|\u00e7\u05f0\x0e\xd5(\"!\xa6\x02Y\xf2[\xf6S\x8a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94K:\xab3^\xbb\xfa\xa8p\xccM`^}.t\xc6h6\x9f\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4K<s\x88\xccv\xda=b\xd4\x00g\u06bc\xcd~\xf0C=#\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4K=\xfb\xdbEK\xe5'\x9a;\x8a\xdd\xfd\x0e\xd1\xcd7\xa9B\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4KG\x0f{\xa00\xbc|\xfc\xf38\u053f\x042\xa9\x1e.\xa5\xff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4KS\xaeY\u01c4\xb6\xb5\xc46\x16\xb9\xa0\x80\x95X\xe6\x84\xe1\f\x89A\rXj \xa4\xc0\x00\x00\u07d4KX\x10\x1fD\xf7\xe3\x89\xe1-G\x1d\x165\xb7\x16\x14\xfd\xd6\x05\x89\b\xacr0H\x9e\x80\x00\x00\u07d4K\\\xdb\x1eB\x8c\x91\xdd|\xb5Jj\xedEq\xda\x05K\xfeR\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4K`\xa3\xe2S\xbf8\xc8\xd5f \x10\xbb\x93\xa4s\xc9e\xc3\xe5\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4Kt\xf5\xe5\x8e.\xdfv\xda\xf7\x01Q\x96J\v\x8f\x1d\xe0f<\x89\x11\x90\xaeID\xba\x12\x00\x00\u07d4Kv!f\xdd\x11\x18\xe8Ci\xf8\x04\xc7_\x9c\xd6W\xbfs\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Ky.)h>\xb5\x86\u353b3Rl`\x01\xb3\x97\x99\x9e\x89 \x86\xac5\x10R`\x00\x00\u07d4K\x90N\x93K\xd0\u030b p_\x87\x9e\x90[\x93\xea\f\xcc0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94K\x92\x06\xbakT\x9a\x1a\u007f\x96\x9e\x1d]\xba\x86u9\xd1\xfag\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4K\x98N\xf2lWn\x81Z.\xae\xd2\xf5\x17\u007f\a\u06f1\xc4v\x89T\x91YV\xc4\t`\x00\x00\u07d4K\x9e\x06\x8f\xc4h\tv\xe6\x15\x04\x91)\x85\xfd\\\xe9K\xab\r\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4K\xa0\xd9\xe8\x96\x01w+IhG\xa2\xbbC@\x18g\x87\xd2e\x8965\u026d\xc5\u07a0\x00\x00\u07d4K\xa5:\xb5I\xe2\x01m\xfa\"<\x9e\u0563\x8f\xad\x91(\x8d\a\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94K\xa8\xe0\x11\u007f\xc0\xb6\xa3\xe5k$\xa3\xa5\x8f\xe6\xce\xf4B\xff\x98\x8a\x011\xbe\xb9%\xff\xd3 \x00\x00\u07d4K\xac\x84j\xf4\x16\x9f\x1d\x95C\x1b4\x1d\x88\x00\xb2!\x80\xaf\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4K\xb6\xd8k\x83\x14\xc2-\x8d7\xeaQm\x00\x19\xf1V\xaa\xe1-\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94K\xb9e\\\xfb*6\xea|cz{\x85\x9bJ1T\xe2n\xbe\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94K\xbc\xbf8\xb3\xc9\x01c\xa8K\x1c\u04a9;X\xb2\xa34\x8d\x87\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94K\xd6\xdd\f\xff#@\x0e\x170\xba{\x89E\x04W}\x14\xe7J\x8a+\xa0\xcc\xdd\xd0\xdfs\xb0\x00\x00\u07d4K\xe8b\x8a\x81T\x87N\x04\x8d\x80\xc1B\x18\x10\"\xb1\x80\xbc\xc1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4K\xe9\rA!)\u0564\xd0BCa\xd6d\x9dNG\xa6#\x16\x897\b\xba\xed=h\x90\x00\x00\xe0\x94K\xea(\x8e\xeaB\u0115^\xb9\xfa\xad*\x9f\xafG\x83\xcb\u076c\x8a\x06\x18\xbe\x16c\u012fI\x00\x00\u07d4K\xf4G\x97\x99\xef\x82\xee\xa2\tC7OV\xa1\xbfT\x00\x1e^\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4K\xf8\xbf\x1d5\xa211Wd\xfc\x80\x01\x80\x9a\x94\x92\x94\xfcI\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4K\xf8\xe2oL'\x90\xdae3\xa2\xac\x9a\xba\xc3\u019a\x19\x943\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0794L\n\xcaP\x8b<\xaf^\xe0(\xbcp}\xd1\xe8\x00\xb88\xf4S\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94L\v\x15\x15\xdf\xce\u05e1>\x13\xee\x12\xc0\xf5#\xaePO\x03+\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4L\x13\x98\f2\xdc\xf3\x92\vx\xa4\xa7\x903\x12\x90|\x1b\x12?\x89\x03A\x00\x15\xfa\xae\f\x00\x00\u07d4L\x15y\xaf3\x12\xe4\xf8\x8a\xe9<h\xe9D\x9c.\x9ah\xd9\u0109lk\x93[\x8b\xbd@\x00\x00\xe0\x94L#\xb3p\xfc\x99+\xb6|\xec\x06\xe2g\x15\xb6/\v:J\u00ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4L$\xb7\x8b\xaf+\xaf\xc7\xfc\u0190\x16Bk\xe9s\xe2\nP\xb2\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94L/\x1a\xfe\xf7\u0146\x8cD\x83/\xc7|\xb0;U\xf8\x9emn\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4L7{\xb0:\xb5,L\xb7\x9b\xef\xa1\xdd\x11I\x82\x92LJ\xe9\x89c\x16\x03\xcc\u04cd\xd7\x00\x00\u07d4L>\x95\xcc9W\xd2R\xce\v\xf0\xc8}[O\"4g.p\x89\x87\x86x2n\xac\x90\x00\x00\u07d4LB<v\x93\r\a\xf9<G\xa5\xccOaWE\xc4Z\x9dr\x89\x05k\xc7^-c\x10\x00\x00\u07d4LE\xd4\u0267%\xd1\x11\x12\xbf\xcb\xca\x00\xbf1\x18l\u02ad\xb7\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4LNo\x13\xfb^?p\xc3v\x02b\xa0>1y\x82i\x1d\x10\x89\x05k\xc7^-c\x10\x00\x00\u07d4LZ\xfe@\xf1\x8f\xfcH\u04e1\xae\xc4\x1f\u009d\xe1y\xf4\u0497\x89lk\x93[\x8b\xbd@\x00\x00\u07d4L[=\xc0\xe2\xb96\x0f\x91(\x9b\x1f\xe1<\xe1,\x0f\xbd\xa3\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Lfk\x86\xf1\xc5\ue324\x12\x85\xf5\xbd\xe4\xf7\x90R\b\x14\x06\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Lik\xe9\x9f:i\x04@\xc3CjY\xa7\xd7\xe97\u05ba\r\x89\xbb\x91%T\"c\x90\x00\x00\u07d4Lj$\x8f\xc9}p]\xefI\\\xa2\aY\x16\x9e\xf0\xd3dq\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4Lj\x9d\xc2\u02b1\n\xbb.|\x13p\x06\xf0\x8f\ucd77y\xe1\x89\x1b\r\x04 /G\xec\x00\x00\u07d4Lk\x93\xa3\xbe\xc1cIT\f\xbf\xca\xe9l\x96!\xd6dP\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Lu\x98\x13\xad\x13\x86\xbe\xd2\u007f\xfa\xe9\xe4\x81^60\u0323\x12\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Lv\f\xd9\xe1\x95\xeeO-k\xce%\x00\xff\x96\xda|C\ue44a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4Lv{e\xfd\x91\x16\x1fO\xbd\xccji\xe2\xf6\xadq\x1b\xb9\x18\x89'\b\x01\xd9F\xc9@\x00\x00\u07d4L~.+w\xad\f\xd6\xf4J\xcb(a\xf0\xfb\x8b(u\x0e\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4L\x85\xed6/$\xf6\xb9\xf0L\xdf\xcc\xd0\"\xaeSQG\u02f9\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4L\x93[\xb2Pw\x8b<L\u007f~\a\xfc%\x1f\xa601J\xab\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4L\x99y\x92\x03l[C:\xc3=%\xa8\xea\x1d\xc3\xd4\xe4\xe6\u0609\x01\x95;=J\xb1h\x00\x00\u07d4L\x99\xda\xe9d\x81\xe8\a\xc1\xf9\x9f\x8b\u007f\xbd\xe2\x9buG\u017f\x89\b!\xab\rD\x14\x98\x00\x00\u07d4L\x9a\x86*\xd1\x15\xd6\xc8'N\u0439D\xbd\u05a5P\x05\x10\xa7\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94L\xa7\x83\xb5V\xe5\xbfS\xaa\x13\xc8\x11f\x13\xd6W\x82\u0276B\x8a\x05V\x18@\xb4\xad\x83\xc0\x00\x00\u07d4L\xa7\xb7\x17\u067c\x87\x93\xb0N\x05\x1a\x8d#\xe1d\x0f[\xa5\xe3\x89C\xb5\x14T\x9e\xcfb\x00\x00\u07d4L\xa8\xdbJ^\xfe\xfc\x80\xf4\u035b\xbc\u03302e\x93\x132\xb6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4L\xac\x91\xfb\x83\xa1G\xd2\xf7l2g\x98K\x91\ny\x933H\x89uy*\x8a\xbd\xef|\x00\x00\u07d4L\xad\xf5s\xceL\xee\u01cb\x8e\x1b!\xb0\xedx\xeb\x11;,\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4L\xb5\xc6\xcdq<\xa4G\xb8H\xae/V\xb7a\xca\x14\u05edW\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4L\xc2,\x9b\u026d\x05\xd8u\xa3\x97\xdb\xe8G\xed\"\x1c\x92\fg\x89lk\x93[\x8b\xbd@\x00\x00\u07d4L\u0430\xa6CcbY\\\xea\xde\x05.\xbc\x9b\x92\x9f\xb6\xc6\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4L\xdaA\xddS9\x91)\a\x94\xe2*\xe3$\x14>0\x9b==\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4L\xee\x90\x1bJ\u0231V\xc5\xe2\xf8\xa6\xf1\xbe\xf5r\xa7\xdc\xeb~\x8965\u026d\xc5\u07a0\x00\x00\u07d4L\xef\xbe#\x98\xe4}R\u73743L\x8bivu\U00053b89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4L\xf5S{\x85\x84/\x89\xcf\xee5\x9e\xaeP\x0f\xc4I\xd2\x11\x8f\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94M\bG\x1dh\x00z\xff*\xe2y\xbc^?\xe4\x15o\xbb\xe3\u078a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4M \x01\x10\x12@\b\xd5ov\x98\x12VB\f\x94jo\xf4\\\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4M$\xb7\xacG\xd2\xf2}\xe9\tt\xba=\xe5\xea\xd2\x03TK\u0349\x05k\xc7^-c\x10\x00\x00\u0794M)\xfcR:,\x16)S!!\u0699\x98\u9d6b\x9d\x1bE\x88\xdbD\xe0I\xbb,\x00\x00\u07d4M8\xd9\x0f\x83\xf4Q\\\x03\xccx2j\x15M5\x8b\u0602\xb7\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4ML\xf5\x80t)a^0\xcd\xfa\xce\x1eZ\xaeM\xad0U\xe6\x89 \x86\xac5\x10R`\x00\x00\u07d4MW\xe7\x16\x87l\f\x95\xef^\xae\xbd5\xc8\xf4\x1b\x06\x9bk\xfe\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Mg\U000ab159\xfe\xf5\xfcA9\x99\xaa\x01\xfd\u007f\xcep\xb4=\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Mn\x8f\xe1\t\xcc\xd2\x15\x8eM\xb1\x14\x13/\xe7_\xec\u023e[\x89\x01[5W\xf1\x93\u007f\x80\x00\xe0\x94Mq\xa6\xeb=\u007f2~\x184'\x8e(\v\x03\x9e\xdd\xd3\x1c/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4M|\xfa\xa8L\xb31\x06\x80\n\x8c\x80/\xb8\xaaF8\x96\u0159\x89a\t=|,m8\x00\x00\u07d4M\x80\x10\x93\xc1\x9c\xa9\xb8\xf3B\xe3<\xc9\xc7{\xbdL\x83\x12\u03c9\x12\xb3\xe7\xfb\x95\u0364\x80\x00\u07d4M\x82\x88\x94u/o%\x17]\xaf!w\tD\x87\x95Ko\x9f\x89O!+\xc2\u011c\x83\x80\x00\xe0\x94M\x82\xd7p\f\x12;\xb9\x19A\x9b\xba\xf0Fy\x9ck\x0e,f\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4M\x83m\x9d;\x0e,\xbdM\xe0PYo\xaaI\f\xff\xb6\r]\x89\x10CV\x1a\x88)0\x00\x00\u07d4M\x86\x97\xaf\x0f\xbf,\xa3n\x87h\xf4\xaf\"\x135phZ`\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4M\x92y\x96 )\xa8\xbdEc\x977\xe9\x8bQ\x1e\xff\aL!\x89Hz\x9a0E9D\x00\x00\u07d4M\x93io\xa2HY\xf5\u0493\x9a\xeb\xfaT\xb4\xb5\x1a\xe1\xdc\u0309\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4M\x9cw\xd0u\f^o\xbc$\u007f/\u05d2thl\xb3S\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4M\xa5\xed\u0188\xb0\xcbb\xe1@=\x17\x00\xd9\u0739\x9f\xfe?\u04c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94M\xa8\x03\ai\x84K\xc3A\x86\xb8\\\xd4\xc74\x88I\xffI\xe9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4M\xb1\xc4:\x0f\x83M}\x04x\xb8\x96\ag\xec\x1a\xc4L\x9a\xeb\x89/Q\x810V'7\x00\x00\u07d4M\xb2\x12\x84\xbc\xd4\xf7\x87\xa7Ue\x00\xd6\xd7\xd8\xf3f#\xcf5\x89i(7Ow\xa3c\x00\x00\u07d4M\xc3\xda\x13\xb2\xb4\xaf\xd4O]\r1\x89\xf4D\xd4\xdd\xf9\x1b\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4M\u013f^u\x89\xc4{(7\x8du\x03\u03d6H\x80a\u06fd\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4M\xc9\u057bK\x19\xce\u0354\xf1\x9e\xc2] \x0e\xa7/%\xd7\xed\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94M\xcd\x11\x81X\x18\xae)\xb8]\x016sI\xa8\xa7\xfb\x12\xd0k\x8a\x01\xacB\x86\x10\x01\x91\xf0\x00\x00\u07d4M\xcfb\xa3\xde?\x06\x1d\xb9\x14\x98\xfda\x06\x0f\x1fc\x98\xffs\x89lj\xccg\u05f1\xd4\x00\x00\u07d4M\xd11\xc7J\x06\x8a7\xc9\n\xde\xd4\xf3\t\xc2@\x9fdx\u04c9\x15\xaf9\u4ab2t\x00\x00\xe0\x94M\u0767Xk\"7\xb0S\xa7\xf3(\x9c\xf4`\xdcW\xd3z\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4M\xe3\xfe4\xa6\xfb\xf64\xc0Q\x99\u007fG\xcc\u007fHy\x1fX$\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4M\xf1@\xbaye\x85\xddT\x891[\xcaK\xbah\n\u06f8\x18\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4N\x02\ay\xb5\xdd\xd3\xdf\"\x8a\x00\xcbH\xc2\xfc\x97\x9d\xa6\xae8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4N\v\xd3$s\xc4\xc5\x1b\xf2VT\xde\xf6\x9fy|k)\xa22\x89V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4N\"%\xa1\xbbY\xbc\x88\xa21ft\xd33\xb9\xb0\xaf\xcafU\x89\bg\x0e\x9e\xc6Y\x8c\x00\x00\u07d4N#\x10\x19\x1e\xad\x8d;\xc6H\x98s\xa5\xf0\xc2\xeck\x87\u1f8965\u026d\xc5\u07a0\x00\x00\u07d4N#-S\xb3\u6f8f\x89Sa\xd3\x1c4\xd4v+\x12\xc8.\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4N+\xfaJFo\x82g\x1b\x80\x0e\xeeBj\xd0\f\a\x1b\xa1p\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4N>\xda\u0506M\xabd\xca\xe4\xc5Azvw@S\xdcd2\x89 \b\xfbG\x8c\xbf\xa9\x80\x00\u07d4NC\x18\xf5\xe1>\x82JT\xed\xfe0\xa7\xedO&\xcd=\xa5\x04\x89lk\x93[\x8b\xbd@\x00\x00\u07d4N[w\xf9\x06aY\xe6\x15\x93?-\xdatw\xfaNG\xd6H\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94Nf\x00\x80b\x89EJ\u03630\xa2\xa3U`\x10\u07ec\xad\xe6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Ns\xcf#y\xf1$\x86\x0fs\xd6\xd9\x1b\xf5\x9a\xcc\\\xfc\x84[\x89\x02,\xa3X|\xf4\xeb\x00\x00\xe0\x94Nz\xa6~\x12\x18>\xf9\xd7F\x8e\xa2\x8a\xd29\xc2\xee\xf7\x1bv\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\xe0\x94N{TGM\x01\xfe\xfd8\x8d\xfc\xd5;\x9ff&$A\x8a\x05\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94N\x89.\x80\x81\xbf6\xe4\x88\xfd\xdb;&0\xf3\xf1\xe8\xda0\u048a\x02\x8a\xba0u$Q\xfc\x00\x00\xe0\x94N\x8amcH\x9c\xcc\x10\xa5\u007f\x88_\x96\xeb\x04\xec\xbbT`$\x8a\x03\xea\xe3\x13\x0e\u0316\x90\x00\x00\u07d4N\x8eG\xae;\x1e\xf5\f\x9dT\xa3\x8e\x14 \x8c\x1a\xbd6\x03\u0089y(\xdb\x12vf\f\x00\x00\u0794N\x90\u03312X\xac\xaa\x9fO\xeb\xc0\xa3B\x92\xf9Y\x91\xe20\x88\xdbD\xe0I\xbb,\x00\x00\u07d4N\xa5n\x11\x12d\x1c\x03\x8d\x05e\xa9\u0096\xc4c\xaf\xef\xc1~\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\xe0\x94N\xa7\x0f\x041?\xaee\xc3\xff\"J\x05\\=-\xab(\xdd\u07ca\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4N\xb1EKW8\x05\u022c\xa3~\xde\xc7\x14\x9aA\xf6\x12\x02\xf4\x89\x10CV\x1a\x88)0\x00\x00\u07d4N\xb8{\xa8x\x8e\xba\r\xf8~[\x9b\xd5\n\x8eE6\x80\x91\xc1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4N\xbcV)\xf9\xa6\xa6k,\xf36:\u0109\\\x03H\u8fc7\x8967\tlK\xcci\x00\x00\u07d4N\xc7h)^\xea\xba\xfcB\x95\x84\x15\xe2+\xe2\x16\xcd\xe7v\x18\x89\x03;\x1d\xbc9\xc5H\x00\x00\u07d4N\xcc\x19\x94\x8d\xd9\u0347\xb4\xc7 \x1a\xb4\x8eu\x8f(\xe7\xccv\x89\x1b\x1d\xaba\u04ead\x00\x00\u07d4N\xd1M\x81\xb6\v#\xfb%\x05M\x89%\u07e5s\u072eah\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94N\xe1<\rA \vF\u045d\xee\\K\xce\xc7\x1d\x82\xbb\x8e8\x8a\x01\xab\xee\x13\u033e\ufbc0\x00\u07d4N\xea\xd4\n\xad\x8cs\xef\b\xfc\x84\xbc\n\x92\xc9\t/j6\xbf\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4N\xeb\xe8\f\xb6\xf3\xaeY\x04\xf6\xf4\xb2\x8d\x90\u007f\x90q\x89\xfc\xab\x89lj\xccg\u05f1\xd4\x00\x00\u07d4N\xeb\xf1 ]\f\xc2\f\xeel\u007f\x8f\xf3\x11_V\u050f\xba&\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4N\xf1\xc2\x14c:\xd9\xc0p;N#t\xa2\xe3>>B\x92\x91\x89Hz\x9a0E9D\x00\x00\u07d4N\xfc\xd9\u01df\xb43L\xa6${\n3\xbd\x9c\xc32\b\xe2r\x89Hz\x9a0E9D\x00\x00\xe0\x94O\x06$k\x8dK\u0496a\xf4>\x93v\"\x01\u0486\x93Z\xb1\x8a\x01\x059O\xfcF6\x11\x00\x00\u07d4O\x15+/\xb8e\x9dCwn\xbb\x1e\x81g:\xa8Ai\xbe\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4O\x17\u007f\x9dV\x95=\xedq\xa5a\x1f93\"\xc3\x02y\x89\\\x89\rU\uf422\xda\x18\x00\x00\u07d4O\x1a-\xa5JLm\xa1\x9d\x14$\x12\xe5n\x81WA\xdb#%\x89\x05k\xc7^-c\x10\x00\x00\u07d4O#\xb6\xb8\x17\xff\xa5\xc6d\xac\xda\u05db\xb7\xb7&\xd3\n\xf0\xf9\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94O&i\f\x99+z1*\xb1.\x13\x85\xd9J\xcdX(\x8e{\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4O+G\xe2wZ\x1f\xa7\x17\x8d\xad\x92\x98Z[\xbeI;\xa6\u0589\n\u05ce\xbcZ\xc6 \x00\x00\u07d4O:HT\x91\x11E\xea\x01\xc6D\x04K\xdb.Z\x96\n\x98/\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4O?,g0i\xac\x97\xc2\x026\a\x15)\x81\xf5\xcd`c\xa0\x89 \x86\xac5\x10R`\x00\x00\xe0\x94OJ\x9b\xe1\f\xd5\xd3\xfb]\xe4\x8c\x17\xbe)o\x89V\x90d[\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4OR\xadap\xd2[*.\x85\x0e\xad\xbbRA?\xf20>\u007f\x89\xa4\xccy\x95c\u00c0\x00\x00\u07d4OX\x01\xb1\xeb0\xb7\x12\u0620WZ\x9aq\xff\x96]O4\xeb\x89\x10CV\x1a\x88)0\x00\x00\u07d4O]\xf5\xb9CW\u0794\x86\x04\xc5\x1bx\x93\xcd\xdf`v\xba\xad\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4Od\xa8^\x8e\x9a@I\x8c\fu\xfc\xeb\x037\xfbI\b>^\x8965\u026d\xc5\u07a0\x00\x00\u07d4Og9m%S\xf9\x98x_pN\a\xa69\x19}\u0454\x8d\x89\x10DrR\x1b\xa78\x00\x00\u07d4OmG7\u05e9@8$\x87&H\x86i|\xf7c\u007f\x80\x15\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4Os0\toy\xed&N\xe0\x12\u007f]0\xd2\xf7<R\xb3\u0609\x1b\x1azB\v\xa0\r\x00\x00\u07d4Ov{\xc8yJ\xef\x9a\n8\xfe\xa5\xc8\x1f\x14iO\xf2\x1a\x13\x89\x1b\xc43\xf2?\x83\x14\x00\x00\u07d4O\x85\xbc\x1f\xc5\xcb\xc9\xc0\x01\xe8\xf17.\aPSp\xd8\xc7\x1f\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4O\x88\xdf\xd0\x10\x91\xa4Z\x9e&v\x02\x1ed(l\xd3k\x8d4\x8965\u026d\xc5\u07a0\x00\x00\u07d4O\x89r\x83\x8fp\xc9\x03\u0276\xc6\xc4ab\xe9\x9db\x16\xd4Q\x89\xf9\xe8\x9a\x0f,V\xc8\x00\x00\u07d4O\x8a\xe8\x028\xe6\x00\bUpu\xabj\xfe\n\u007f.t\xd7)\x89\x05k\xc7^-c\x10\x00\x00\u07d4O\x8e\x8d'O\xb2*?\xd3jG\xfer\x98\x04qTK44\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94O\x9c\u2bdb\x8c^B\u0180\x8a8p\xecWo15E\u044a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4O\xa3\xf3.\xf4\bdH\xb3D\xd5\xf0\xa9\x89\r\x1c\xe4\xd6\x17\u00c9QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4O\xa5T\xab\x95\\$\x92\x178jM2c\xbb\xf7(\x95CN\x89\x01\x15NS!}\xdb\x00\x00\u07d4O\xa9\x83\xbb^0s\xa8\xed\xb5W\xef\xfe\xb4\xf9\xfb\x1d`\uf189V\xb9\xafW\xe5u\xec\x00\x00\u07d4O\xaf\x90\xb7n\u03f9c\x1b\xf9\x02!v\x03-\x8b, p\t\x8966;]\x9awp\x00\x00\u07d4O\xc4l9ngHi\xad\x94\x81c\x8f\x00\x13c\f\x87\u02ac\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94O\xcc\x19\xea\x9fLW\u073c\xe8\x93\x19<\xfb\x16j\xa9\x14\xed\u014a\x01{\x8b\xaa\u007f\x19Tj\x00\x00\xe0\x94O\u0384)\xbaI\u02a06\x9d\x1eIM\xb5~\x89\uacad9\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4O\xda\xc1\xaaQp\a\xe0\b\x940\xb31j\x1b\xad\xd1,\x01\u01c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4O\xe5j\xb3\xba\u1c24D3E\x833\u0130Z$\x8f\x82A\x89v-\x93\xd1\xddo\x90\x00\x00\u07d4O\xeb\x84k\xe40A\xfdk4 (\x97\x94>?!\xcb\u007f\x04\x89\x04\x82\xfe&\f\xbc\xa9\x00\x00\u07d4O\xeeP\xc5\xf9\x88 k\t\xa5sF\x9f\xb1\u0434.\xbbm\u0389l\xee\x06\u077e\x15\xec\x00\x00\u07d4O\xf6v\xe2\u007fh\x1a\x98-\x8f\xd9\xd2\x0ed\x8b=\xce\x05\xe9E\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4O\xf6\u007f\xb8\u007fn\xfb\xa9'\x990\u03fd\x1bz4<y\xfa\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4P\x06\xfeL\"\x179\x80\xf0\ft4+9\xcd#\x1ce1)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4P\f\x165.\x90\x1dH\xba\x8d\x04\xe2\xc7g\x12\x17ry\v\x02\x89\x01\xa3\xa6\x82Is\t\x80\x00\u07d4P\f\x90)X\xf6B\x15\x94\u0476\xde\xd7\x12I\rR\xedlD\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94P\x0e4\xcd\u5f5e+q\xbb\x92\xd7\xcfU\xee\xe1\x88\xd5\xfa\f\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4P2\xe4\xbc\xf7\x93+I\xfd\xba7{o\x14\x99ce\x13\xcf\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4P7\x8a\xf7\xefT\x04?\x89*\xb7\u0397\xd6Gy5\x11\xb1\b\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4P;\xdb\u063cB\x1c2\xa4C\x03-\xeb.>L\u057a\x8bN\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94PFf\u03891\x17^\x11\xa5\xed\x11\xc1\u072a\x06\xe5\u007fNf\x8a\x02\u007f>\u07f3Nn@\x00\x00\u0794PXM\x92\x06\xa4l\xe1\\0\x11\x17\xee(\xf1\\0\xe6\x0eu\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94PZ3\xa1\x864\xddH\x00i<g\xf4\x8a\x1di=H3\xf8\x8a\x01\x89!\xb7\x99A\xdc\xd0\x00\x00\u0794P^O|'U\x88\xc53\xa2\x0e\xbd*\xc1;@\x9b\xbd\xea<\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4Pb\xe5\x13La/\x12iM\xbd\x0e\x13\x1dL\xe1\x97\u0476\xa4\x8965\u026d\xc5\u07a0\x00\x00\u07d4Pd\x11\xfdy\x004\x80\xf6\xf2\xb6\xaa\xc2k{\xa7\x92\U00014c89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Pg\xf4T\x9a\xfb\xfe\x88LY\xcb\xc1+\x96\x93I#\xd4]\xb0\x8965\u026d\xc5\u07a0\x00\x00\u07d4Pv:\u0746\x8f\xd76\x11x4/\xc0U\ua8b9_hF\x89\x03\x9f\x90F\xe0\x89\x8f\x00\x00\u07d4P\x8c\xf1\x91\x19\xdbp\xaa\x86EBS\xdavJ,\xb1\xb2\xbe\x1a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94P\x99\x82\xf5b7\xeeE\x89Q\x04~\n\"0\xf8\x04\xe2\u854a\x03\xb4\xadIa\x06\xb7\xf0\x00\x00\u07d4P\x9a \xbcH\xe7+\xe1\u036f\x95i\xc7\x11\xe8d\x8d\x95s4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4P\x9c\x86h\x03m\x14?\xb8\xaep\xb1\x19\x95c\x1f=\xfc\xad\x87\x8965\u026d\xc5\u07a0\x00\x00\u07d4P\xad\x18z\xb2\x11g\u00b6\xe7\x8b\xe0\x15?DPJ\a\x94^\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4P\xb9\xfe\xf0\xa12\x9b\x02\xd1e\x06%_Z-\xb7\x1e\xc9-\x1f\x89G\u0682\x15d\b\\\x00\x00\u07d4P\xbbg\u0238\u063d\x0fc\xc4v\t\x04\xf2\xd33\xf4\x00\xaa\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4P\xbe\xf2ubH\xf9\xa7\xa3\x80\xf9\x1b\x05\x1b\xa3\xbe(\xa6I\xed\x89li\xf7>)\x13N\x00\x00\u07d4P\u0286\xb5\xeb\x1d\x01\x87M\xf8\xe5\xf3IE\u051cl\x1a\xb8H\x8965\u026d\xc5\u07a0\x00\x00\u07d4P\u0357\xe97\x8b\\\xf1\x8f\x179c#l\x99Q\xeft8\xa5\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4P\u073c'\xbc\xad\x98@\x93\xa2\x12\xa9\xb4\x17\x8e\xab\xe9\x01ua\x89\a\xe3by\v\\\xa4\x00\x00\u07d4P\xe10#\xbd\x9c\xa9j\xd4\xc5?\xdf\xd4\x10\xcbk\x1fB\v\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94P\xe1\xc8\xec\x98A[\xefD&\x18p\x87\x99C{\x86\xe6\xc2\x05\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4P\xf8\xfaK\xb9\xe2g|\x99\nN\xe8\xcep\xdd\x15#%\x1eO\x89\x01i=#\x16Ok\x00\x00\u07d4P\xfb6\xc2q\a\xee,\xa9\xa3#n'F\u0321\x9a\xcekI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4P\xfe\xf2\x96\x95U\x88\u02aet\xc6.\xc3*#\xa4T\xe0\x9a\xb8\x89A\x1d\xff\xab\xc5\a8\x00\x00\u07d4Q\x02\xa4\xa4 w\xe1\x1cX\xdfGs\u3b14F#\xa6m\x9f\x89lp\x15\xfdR\xed@\x80\x00\u07d4Q\x03\x93w\xee\xd0\xc5s\xf9\x86\xc5\xe8\xa9_\xb9\x9aY\xe93\x0f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Q\x03\xbc\t\x93>\x99!\xfdS\xdcSo\x11\xf0]\rG\x10}\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94Q\x04\xec\xc0\xe30\xdd\x1f\x81\xb5\x8a\xc9\u06f1\xa9\xfb\xf8\x8a<\x85\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4Q\r\x81Y\u0314Wh\xc7E\a\x90\xba\a>\xc0\xd9\xf8\x9e0\x89\x8a\xc7#\x04\x89\xe8\x00\x00\x00\u07d4Q\x0e\xdaV\x01I\x9a\r^\x1a\x00k\xff\xfd\x836r\xf2\xe2g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x12dF\xab=\x802U~\x8e\xbaeY}u\xfa\u0701\\\x89\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\xe0\x94Q\x18U}`\r\x05\xc2\xfc\xbf8\x06\xff\xbd\x93\xd0 %\xd70\x8a\x02g\u04ebd#\xf5\x80\x00\x00\u07d4Q\x1e\x0e\xfb\x04\xacN?\xf2\xe6U\x0eI\x82\x95\xbf\xcdV\xff\u0549$=M\x18\"\x9c\xa2\x00\x00\u07d4Q!\x16\x81{\xa9\xaa\xf8C\xd1P|e\xa5\xead\n{\x9e\xec\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4Q&F\ri,q\u026fo\x05WM\x93\x99\x83h\xa27\x99\x89\x02\u0465\x1c~\x00P\x00\x00\u07d4Q'\u007f\xe7\xc8\x1e\xeb\xd2R\xa0=\xf6\x9ak\x9f2n'\"\a\x89\x03@.y\u02b4L\x80\x00\u07d4Q)oPD'\r\x17pvF\x12\x9c\x86\xaa\xd1d^\xad\xc1\x89H|r\xb3\x10\xd4d\x80\x00\xe0\x94Q+\x91\xbb\xfa\xa9\xe5\x81\xefh?\xc9\r\x9d\xb2*\x8fI\xf4\x8b\x8aA\xa5\"8m\x9b\x95\xc0\x00\x00\u07d4Q5\xfb\x87W`\f\xf4tTbR\xf7M\xc0tm\x06&,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4QF2\xef\xbdd,\x04\xdel\xa3B1]@\u0750\xa2\u06e6\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4QKu\x12\u026e^\xa6<\xbf\x11q[c\xf2\x1e\x18\u0496\xc1\x89lj\xccg\u05f1\xd4\x00\x00\u07d4QS\xa0\xc3\u0211(\x81\xbf\x1c5\x01\xbfd\xb4VI\xe4\x82\"\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94QVQ\xd6\xdbO\xaf\x9e\xcd\x10:\x92\x1b\xbb\xbej\xe9p\xfd\u050a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94Q_0\xbc\x90\xcd\xf4W~\xe4}e\u05c5\xfb\xe2\xe87\u01bc\x8a\x02'\x1b^\x01\x8b\xa0X\x00\x00\u07d4Q`\xeda.\x1bH\xe7??\xc1[\xc42\x1b\x8f#\xb8\xa2K\x89\x1e\x82kB(e\xd8\x00\x00\u07d4Qa\xfdI\xe8G\xf6tU\xf1\u023bz\xbb6\xe9\x85&\r\x03\x89A\rXj \xa4\xc0\x00\x00\u07d4QiT\x02_\xca&\b\xf4}\xa8\x1c!^\xed\xfd\x84J\t\xff\x89\x14\xb5P\xa0\x13\xc78\x00\x00\u07d4Qi\xc6\n\xeeL\xee\u0444\x9a\xb3mfL\xff\x97\x06\x1e\x8e\xa8\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4Q|uC\r\xe4\x01\xc3A\x03&\x86\x11'\x90\xf4mM6\x9e\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4Q|\xd7`\x8e]\r\x83\xa2kq\u007f6\x03\xda\xc2'}\u00e4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x86]\xb1H\x88\x19Q\xf5\x12Qq\x0e\x82\xb9\xbe\r~\xad\xb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x89\x1b,\xcd\xd2\xf5\xa4K*\x8b\u011a]\x9b\xcadw%\x1c\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4Q\x8c\xef'\xb1\x05\x82\xb6\xd1OiH=\u06a0\xdd<\x87\xbb\\\x89 \x86\xac5\x10R`\x00\x00\u07d4Q\xa6\xd6'\xf6j\x89#\u060d`\x94\xc4qS\x80\xd3\x05|\xb6\x89>s\xd2z5\x94\x1e\x00\x00\u07d4Q\xa8\xc2\x166\x02\xa3.\xe2L\xf4\xaa\x97\xfd\x9e\xa4\x14QiA\x89\x03h\xf7\xe6\xb8g,\x00\x00\u07d4Q\xb4u\x8e\x9e\x14P\xe7\xafBh\xc3\u01f1\xe7\xbdo\\uP\x8965\u026d\xc5\u07a0\x00\x00\u07d4Q\u028b\xd4\xdcdO\xacG\xafgUc\u0540J\r\xa2\x1e\xeb\x89*\xb7\xb2`\xff?\xd0\x00\x00\u07d4Q\xd2K\xc3so\x88\xddc\xb7\" &\x88f0\xb6\ub1cd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\u05cb\x17\x8dp~9n\x87\x10\x96\\OA\xb1\xa1\xd9\x17\x9d\x89\x05\xfe\xe2\"\x04\x1e4\x00\x00\u07d4Q\xe3/\x14\xf4\xca^(|\xda\xc0W\xa7y^\xa9\xe0C\x99S\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Q\xe4?\xe0\xd2\\x(`\xaf\x81\xea\x89\xddy<\x13\xf0\u02f1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4Q\xe7\xb5\\/\x98 \xee\xd78\x846\x1bPf\xa5\x9boE\u0189lk\x93[\x8b\xbd@\x00\x00\xe0\x94Q\xea\x1c\t4\xe3\xd0@\"\ud715\xa0\x87\xa1P\xefp^\x81\x8a\x01Tp\x81\xe7\"M \x00\x00\u07d4Q\xee\f\xca;\xcb\x10\xcd>\x987\"\xce\xd8I=\x92l\bf\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94Q\xf4f:\xb4O\xf7\x93E\xf4'\xa0\xf6\xf8\xa6\u0225?\xf24\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Q\xf5^\xf4~dV\xa4\x18\xab2\xb9\"\x1e\xd2}\xbaf\b\xee\x89\u3bb5sr@\xa0\x00\x00\xe0\x94Q\xf9\xc42\xa4\xe5\x9a\xc8b\x82\u05ad\xabL.\xb8\x91\x91`\xeb\x8ap;[\x89\u00e6\xe7@\x00\x00\u07d4R\x0ff\xa0\xe2e\u007f\xf0\xacA\x95\xf2\xf0d\xcf/\xa4\xb2BP\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4R\x10#T\xa6\xac\xa9]\x8a.\x86\xd5\u07bd\xa6\xdei4`v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\x13\xf4Y\xe0x\xad:\xb9Z\t #\x9f\xcf\x163\xdc\x04\u0289\x8c\xf2\x18|*\xfb\x18\x80\x00\u07d4R\x15\x18;\x8f\x80\xa9\xbc\x03\xd2l\xe9\x12\a\x83*\r9\xe6 \x8965\u026d\xc5\u07a0\x00\x00\xe0\x94R!Cx\xb5@\x04\x05j|\xc0\x8c\x89\x13'y\x8a\u01b2H\x8a\x037\xfe_\xea\xf2\u0440\x00\x00\xe0\x94R##\xaa\xd7\x1d\xbc\x96\xd8Z\xf9\x0f\bK\x99\xc3\xf0\x9d\ucdca\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4R>\x14\r\xc8\x11\xb1\x86\xde\xe5\xd6\u020b\xf6\x8e\x90\xb8\xe0\x96\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R?mdi\x0f\xda\u0354(SY\x1b\xb0\xff \xd3em\x95\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4RO\xb2\x10R,^#\xbbg\u07ff\x8c&\xaaam\xa4\x99U\x8965b\xa6m4#\x80\x00\u07d4RU\xdci\x15ZE\xb9p\xc6\x04\xd3\x00G\xe2\xf50i\x0e\u007f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4R`\xdcQ\xee\a\xbd\u06ab\xab\xb9\xeetK9<\u007fG\x93\xa6\x89\x01\xd8f_\xa5\xfaL\x00\x00\u07d4Rg\xf4\xd4\x12\x92\xf3p\x86<\x90\u05d3)i\x03\x846%\u01c9K\xe4\xe7&{j\xe0\x00\x00\u07d4Rk\xb53\xb7n \xc8\xee\x1e\xbf\x12?\x1e\x9f\xf4\x14\x8e@\xbe\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4Rl\xb0\x9c\u3b63g.\xec\x1d\xebF [\xe8\x9aKV>\x89\x85\xcaa[\xf9\xc0\x10\x00\x00\u07d4Rs\x8c\x90\xd8`\xe0L\xb1/I\x8d\x96\xfd\xb5\xbf6\xfc4\x0e\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4Rz\x8c\xa1&\x863\xa6\xc99\xc5\xde\x1b\x92\x9a\ue4ae\xac\x8d\x890\xca\x02O\x98{\x90\x00\x00\u07d4R\x81\x01\xceF\xb7 \xa2!M\u036ef\x18\xa51w\xff\xa3w\x89\x1b\x96\x12\xb9\xdc\x01\xae\x00\x00\xe0\x94R\x81s4s\xe0\r\x87\xf1\x1e\x99U\u5275\x9fJ\u008ez\x8a\x8b\xd6/\xf4\xee\xc5Y \x00\x00\u07d4R\x98\xab\x18*\x195\x9f\xfc\xec\xaf\xd7\u0475\xfa!-\xed\xe6\u0749\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4R\x9a\xa0\x02\u0196*:\x85E\x02\u007f\u0630_\"\xb5\xbf\x95d\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4R\x9e\x82O\xa0rX+@2h:\xc7\xee\xcc\x1c\x04\xb4\xca\xc1\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94R\xa5\xe4\xdeC\x93\xee\xcc\xf0X\x1a\xc1\x1bR\u0183\xc7n\xa1]\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4R\xb4%|\xf4\x1bn(\x87\x8dP\xd5{\x99\x91O\xfa\x89\x87:\x89\xd5\r\u026a,Aw\x00\x00\u07d4R\xb8\xa9Y&4\xf70\v|\\Y\xa34[\x83_\x01\xb9\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\xbd\u066fYx\x85\v\xc2A\x10q\x8b7#u\x9bC~Y\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4R\xcd @;\xa7\xed\xa6\xbc0z=c\xb5\x91\x1b\x81|\x12c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794R\u04c0Q\x1d\xf1\x9d^\u0080{\xbc\xb6vX\x1bg\xfd7\xa3\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94R\xe1s\x13P\xf9\x83\xcc,A\x89\x84/\xde\x06\x13\xfa\xd5\f\xe1\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\u07d4R\xe4g\x832\x9av\x93\x01\xb1u\x00\x9d4gh\xf4\xc8~\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\xf0X\xd4aG\xe9\x00m)\xbf,\t0J\xd1\xcd\xddn\x15\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4R\xf1T#2<$\xf1\x9a\xe2\xabg7\x17\"\x9d?t}\x9b\x897\xa04\xcb\xe8\xe3\xf3\x80\x00\u07d4R\xf8\xb5\t\xfe\xe1\xa8t\xabo\x9d\x876\u007f\xbe\xaf\x15\xac\x13\u007f\x8965\u026d\xc5\u07a0\x00\x00\u07d4R\xfbF\xac]\x00\xc3Q\x8b,:\x1c\x17}D/\x81eU_\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4S\x00w\xc9\xf7\xb9\a\xff\x9c\xec\fw\xa4\x1ap\xe9\x02\x9a\xddJ\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\x03\x19\xdb\n\x8f\x93\xe5\xbb}M\xbfH\x161O\xbe\xd86\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\x04}\u022c\x90\x83\xd9\x06r\xe8\xb3G<\x10\f\xcd'\x83#\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4S\va\xe4/9Bm$\b\xd4\bR\xb9\xe3J\xb5\xeb\xeb\u0149\x0e~\xeb\xa3A\vt\x00\x00\u07d4S\x0f\xfa\u00fc4\x12\xe2\xec\x0e\xa4{y\x81\xc7p\xf5\xbb/5\x89\a?u\u0460\x85\xba\x00\x00\u07d4S\x17\xec\xb0#\x05,\xa7\xf5e+\xe2\xfa\x85L\xfeEc\xdfM\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4S\x19M\x8a\xfa>\x885\x02v~\xdb\xc3\x05\x86\xaf3\xb1\x14\u04c9lk\x93[\x8b\xbd@\x00\x00\u07d4S*}\xa0\xa5\xadt\aF\x8d;\xe8\xe0~i\xc7\xddd\xe8a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4S-2\xb0\x0f0[\xcc$\xdc\xefV\x81}b/4\xfb,$\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4S4DX@\x82\xeb\xa6T\xe1\xad0\xe1Is\\o{\xa9\"\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4S8\xefp\xea\xc9\u075a\xf5\xa0P;^\xfa\xd1\x03\x9eg\xe7%\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94S9oJ&\u00b4`D\x960lTB\xe7\xfc\xba'.6\x8a\x04?/\b\xd4\x0eZ\xfc\x00\x00\xe0\x94S:s\xa4\xa2\"\x8e\xee\x05\xc4\xff\xd7\x18\xbb\xf3\xf9\xc1\xb1)\xa7\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4S<\x06\x92\x8f\x19\u0429V\xcc(\x86k\xf6\xc8\xd8\xf4\x19\x1a\x94\x89\x0f\xd8\xc1C8\xe60\x00\x00\u07d4S@e6\x1c\xb8T\xfa\xc4+\xfb\\\x9f\xcd\xe0`J\xc9\x19\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4SC\u007f\xec\xf3J\xb9\xd45\xf4\u07b8\xca\x18\x15\x19\xe2Y 5\x89\n1\x06+\xee\xedp\x00\x00\u07d4SR\x01\xa0\xa1\xd74\"\x80\x1fU\xde\xd4\u07ee\xe4\xfb\xaan;\x89\x02&!\x1fy\x15B\x80\x00\xe0\x94S`\x81\x05\xceK\x9e\x11\xf8k\xf4\x97\xff\xca;x\x96{_\x96\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4SnM\x80)\xb7?Uy\u0723>p\xb2N\xba\x89\xe1\x1d~\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Sp\rS%MC\x0f\"x\x1aJv\xa4c\x93;]k\b\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94S\u007f\x9dM1\xefp\x83\x9d\x84\xb0\xd9\u0377+\x9a\xfe\xdb\xdf5\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\xe0\x94S\x81D\x85\x03\xc0\xc7\x02T+\x1d\xe7\xcc_\xb5\xf6\xab\x1c\xf6\xa5\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94S\x94.yI\xd6x\x8b\xb7\x80\xa7\xe8\xa0y'\x81\xb1aK\x84\x8a\x03]\xebFhO\x10\xc8\x00\x00\u07d4S\x95\xa4E]\x95\xd1x\xb4S*\xa4r[\x19?\xfeQ)a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94S\x98\x9e\xd30V?\xd5}\xfe\u027d4<7`\xb0y\x93\x90\x8a\x01P\x89N\x84\x9b9\x00\x00\x00\u07d4S\xa2Dg(\x95H\x0fJ+\x1c\xdf}\xa5\xe5\xa2B\xecM\xbc\x8965\u026d\xc5\u07a0\x00\x00\u07d4S\xa7\x14\xf9\x9f\xa0\x0f\xefu\x8e#\xa2\xe7F2m\xad$|\xa7\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4S\xaf2\xc2/\uf640?\x17\x8c\xf9\v\x80/\xb5q\xc6\x1c\xb9\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4S\xc0\xbb\u007f\u020e\xa4\"\xd2\xef~T\x0e-\x8f(\xb1\xbb\x81\x83\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94S\xc5\xfe\x01\x19\xe1\xe8Hd\f\xee0\xad\ua594\x0f*]\x8b\x8a\x04\x9a\xda_\xa8\xc1\f\x88\x00\x00\u07d4S\xc9\xec\xa4\ts\xf6;\xb5\x92{\xe0\xbcj\x8a\x8b\xe1\x95\x1ft\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\u0388\xe6lZ\xf2\U0009bf4fY*V\xa3\xd1_ l2\x89\a\xa2\x8c1\xcc6\x04\x00\x00\u07d4S\xce\xc6\u0200\x92\xf7V\xef\xe5o}\xb1\x12(\xa2\xdbE\xb1\"\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4S\xe3[\x12#\x1f\x19\xc3\xfdwL\x88\xfe\xc8\xcb\xee\xdf\x14\b\xb2\x89\x1b\xc1mgN\xc8\x00\x00\x00\u07d4S\xe4\xd9im\xcb?M{?p\u072aN\xec\xb7\x17\x82\xff\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4S\xfa\xf1e\xbe\x03\x1e\xc1\x830\xd9\xfc\xe5\xbd\x12\x81\xa1\xaf\b\u06c9\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4T\n\x18\x19\xbd|5\x86\x1ey\x18\x04\xe5\xfb\xb3\xbc\x97\u026b\xb1\x89N\xd7\xda\xc6B0 \x00\x00\xe0\x94T\f\a(\x02\x01N\xf0\xd5a4Z\xecH\x1e\x8e\x11\xcb5p\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94T\f\xf2=\xd9\\MU\x8a'\x9dw\x8d+75\xb3\x16A\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4T\x10`\xfcX\xc7P\xc4\x05\x12\xf83i\xc0\xa63@\xc1\"\xb6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4T\x13\xc9\u007f\xfaJn*{\xba\x89a\u071f\u03850\xa7\x87\u05c965\u026d\xc5\u07a0\x00\x00\u07d4T\x1d\xb2\n\x80\xcf;\x17\xf1b\x1f\x1b?\xf7\x9b\x88/P\xde\xf3\x8965\u026d\xc5\u07a0\x00\x00\u07d4T.\x80\x96\xba\xfb\x88\x16&\x06\x00.\x8c\x8a>\u0458\x14\xae\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T1\v:\xa8\x87\x03\xa7%\u07e5}\xe6\xe6F\x93Qd\x80,\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4T1\xb1\u0447Q\xb9\x8f\xc9\u220a\xc7u\x9f\x155\xa2\xdbG\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T1\xcaB~ae\xa6D\xba\xe3&\xbd\tu\n\x17\x8ce\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T5\xc6\xc1y3\x17\xd3,\xe1;\xbaLO\xfe\xb9s\xb7\x8a\u0709\r\x8ek\x1c\x12\x85\xef\x00\x00\xe0\x94T6)\xc9\\\xde\xf4(\xad7\xd4S\u02958\xa9\xf9\t\x00\xac\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4T9\x1bM\x17mGl\xea\x16N_\xb55\u0197\x00\xcb%5\x89\x05l\xd5_\xc6M\xfe\x00\x00\xe0\x94T:\x8c\x0e\xfb\x8b\xcd\x15\xc5C\u29a4\xf8\aYv1\xad\xef\x8a\x01?\x80\xe7\xe1O-D\x00\x00\u07d4T?\x8cgN$b\xd8\xd5\u06a0\xe8\x01\x95\xa8p\x8e\x11\xa2\x9e\x89\x03wX\x83;:z\x00\x00\xe0\x94TK[5\x1d\x1b\xc8.\x92\x97C\x99H\xcfHa\xda\u026e\x11\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4TM\xdaB\x1d\xc1\xebs\xbb$\xe3\xe5j$\x80\x13\xb8|\x0fD\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4TW\\1\x14u\x1e<c\x19q\xdaj*\x02\xfd?\xfb\xfc\u0209i*\xe8\x89p\x81\xd0\x00\x00\u07d4T[\xb0p\xe7\x81\x17.\xb1`\x8a\xf7\xfc(\x95\xd6\u02c7\x19~\x89y\xa5\xc1~\xc7H\x90\x00\x00\xe0\x94Tu\xd7\xf1t\xbd\xb1\xf7\x89\x01||\x17\x05\x98\x96F\a\x9dI\x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4T\x85X\u040c\xfc\xb1\x01\x18\x1d\xac\x1e\xb6\tKN\x1a\x89o\xa6\x89lj\xccg\u05f1\xd4\x00\x00\u07d4T\x93\x9f\xf0\x89!\xb4g\xcf)Fu\x1d\x85cx)lc\xed\x8965\u026d\xc5\u07a0\x00\x00\u07d4T\x9bGd\x9c\xfa\u0653\xe4\x06M&6\xa4\xba\xa0b3\x05\u0309 \x9d\x92/RY\xc5\x00\x00\u07d4T\x9dQ\xaf)\xf7$\xc9g\xf5\x94#\xb8[&\x81\xe7\xb1Q6\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4T\xa17\x01\x16\xfe\"\t\x9e\x01]\a\xcd&i\xdd)\x1c\xc9\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94T\xa6+\xf9#>\x14o\xfe\u00c7nE\xf2\x0e\xe8AJ\u07ba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4T\xb4B\x9b\x18/\x03w\xbe~bi9\xc5\xdbd@\xf7]z\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4T\xbc\xb8\xe7\xf7<\xda=s\xf4\u04cb-\bG\xe6\x00\xba\r\xf8\x89:pAX\x82\xdf\x18\x00\x00\u07d4T\xc9>\x03\xa9\xb2\xe8\xe4\xc3g(5\xa9\xeev\xf9a[\xc1N\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4T\u0388'YV\xde\xf5\xf9E\x8e;\x95\xde\xca\xcdH@!\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T\xdb^\x06\xb4\x81]1\xcbV\xa8q\x9b\xa3:\xf2\xd7>rR\x89$R\x1e*0\x17\xb8\x00\x00\xe0\x94T\xe0\x12\x83\u030b8E8\xdddgp\xb3W\xc9`\xd6\xca\u034a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4T\xecs\x00\xb8\x1a\xc8C3\xed\x1b\x03<\xd5\u05e39r\xe24\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4T\xfe\xbc\xce \xfez\x90\x98\xa7U\xbd\x90\x98\x86\x02\xa4\x8c\b\x9e\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4U\n\xad\xae\x12!\xb0z\xfe\xa3\x9f\xba.\xd6.\x05\u5df5\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4U\f0o\x81\xef]\x95\x80\xc0l\xb1\xab \x1b\x95\xc7H\xa6\x91\x89$\x17\xd4\xc4p\xbf\x14\x00\x00\xe0\x94U\x19\x99\xdd\xd2\x05V3'\xb9\xb50xZ\xcf\xf9\xbcs\xa4\xba\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4U\x1ew\x84w\x8e\xf8\xe0H\xe4\x95\xdfI\xf2aO\x84\xa4\xf1\u0709 \x86\xac5\x10R`\x00\x00\xe0\x94U)\x83\na\xc1\xf1<\x19~U\v\xed\xdf\u05bd\x19\\\x9d\x02\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4U)\x87\xf0e\x1b\x91[.\x1eS(\xc1!\x96\rK\xddj\xf4\x89a\t=|,m8\x00\x00\u07d4U;k\x1cW\x05\x0e\x88\xcf\f1\x06{\x8dL\xd1\xff\x80\xcb\t\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4U?7\xd9$fU\x0e\x9f\xd7u\xaet6-\xf00\x17\x912\x89lk\x93[\x8b\xbd@\x00\x00\u07d4UC6\xeeN\xa1U\xf9\xf2O\x87\xbc\xa9\xcar\xe2S\xe1,\u0489\x05k\xc7^-c\x10\x00\x00\u0794UC\xddm\x16\x9e\xec\x8a!;\xbfz\x8a\xf9\xff\xd1]O\xf7Y\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4UG\xfd\xb4\xae\x11\x95>\x01)+x\a\xfa\x92#\xd0\xe4`j\x89\x05]\x11}\xcb\x1d&\x00\x00\u07d4UR\xf4\xb3\xed>\x1d\xa7\x9a/x\xbb\x13\xe8\xaeZh\xa9\xdf;\x8965\u026d\xc5\u07a0\x00\x00\u07d4U\\\xa9\xf0\\\xc14\xabT\xae\x9b\xea\x1c?\xf8z\xa8Q\x98\u0289\x05k\xc7^-c\x10\x00\x00\xe0\x94U]\x8d<\xe1y\x8a\u0290'T\xf1d\xb8\xbe*\x022\x9cl\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4U]\xf1\x93\x90\xc1m\x01)\x87r\xba\xe8\xbc:\x11R\x19\x9c\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4U^\xbe\x84\u06a4+\xa2V\xeax\x91\x05\xce\u0136\x93\xf1/\x18\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94U\u007f^e\xe0\xda3\x99\x82\x19\xadN\x99W\x05E\xb2\xa9\xd5\x11\x8a\x02U\x9c\xbb\x98XB@\x00\x00\u07d4U\x83` h\x83\xdd\x1bmJYc\x9eV)\xd0\xf0\xc6u\u0409lk\x93[\x8b\xbd@\x00\x00\u07d4U\x84B0P\xe3\xc2\x05\x1f\v\xbd\x8fD\xbdm\xbc'\xec\xb6,\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4U\x85)CI)p\xf8\xd6)\xa1Sf\xcd\xda\x06\xa9OE\x13\x89lk\x93[\x8b\xbd@\x00\x00\u0794U\x86d\x86\xec\x16\x8fy\xdb\xe0\u1af1\x88d\u0649\x91\xae,\x88\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4U\x8cTd\x9a\x8an\x94r+\xd6\xd2\x1d\x14qOqx\x054\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\x91\x940O\x14\xb1\xb9:\xfeDO\x06$\xe0S\xc2:\x00\t\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4U\x93\xc9\u0536ds\x0f\xd9<\xa6\x01Q\xc2\\.\xae\xd9<;\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4U\x97\x06\xc32\xd2\ay\xc4_\x8am\x04ji\x91Y\xb7I!\x89\x14\x9bD.\x85\xa3\u03c0\x00\u07d4U\x98\xb3\xa7\x9aH\xf3+\x1f_\xc9\x15\xb8{d]\x80]\x1a\xfe\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4U\xa3\xdfW\xb7\xaa\xec\x16\xa1b\xfdS\x16\xf3[\xec\b(!\u03c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4U\xa4\xca\xc0\u02cbX-\x9f\xef8\xc5\xc9\xff\xf9\xbdS\t=\x1f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4U\xa6\x1b\x10\x94\x80\xb5\xb2\xc4\xfc\xfd\xef\x92\xd9\x05\x84\x16\f\r5\x89\x02lVM+S\xf6\x00\x00\u07d4U\xaa]1>\xbb\bM\xa0\xe7\x80\x10\x91\u2792\xc5\xde\u00ea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\xab\x99\xb0\xe0\xe5]{\xb8t\xb7\xcf\xe84\xdec\x1c\x97\xec#\x897\xe9\x8c\xe3h\x99\xe4\x00\x00\u07d4U\xaf\t/\x94\xbajy\x91\x8b\f\xf99\xea\xb3\xf0\x1b?Q\u01c9\b \xd5\xe3\x95v\x12\x00\x00\u07d4U\xc5dfAf\xa1\xed\xf3\x91>\x01i\xf1\xcdE\x1f\xdb]\f\x89\x82\x17\xeaIP\x8el\x00\x00\xe0\x94U\xcaj\xbey\xea$\x97\xf4o\u06f804`\x10\xfeF\x9c\xbe\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4U\xca\xffK\xba\x04\xd2 \u0265\xd2\x01\x86r\xec\x85\xe3\x1e\xf8>\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\xd0W\xbc\xc0K\xd0\xf4\xaf\x96BQ:\xa5\t\v\xb3\xff\x93\xfe\x89;\xfeE,\x8e\xddL\x00\x00\u07d4U\xd4.\xb4\x95\xbfF\xa64\x99{_.\xa3b\x81I\x18\u2c09\x05\xc0\xd2e\xb5\xb2\xa8\x00\x00\u07d4U\u069d\xcd\xcaa\xcb\xfe\x1f\x13<{\xce\xfc\x86{\x9c\x81\"\xf9\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4U\xe2 \x87bb\xc2\x18\xafOVxG\x98\xc7\xe5]\xa0\x9e\x91\x89\a=\x99\xc1VE\xd3\x00\x00\u07d4U\xfd\b\u0440d\xbd ,\x0e\xc3\xd2\xcc\xe0\xce\v\x9d\x16\x9cM\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4V\x00s\nU\xf6\xb2\x0e\xbd$\x81\x1f\xaa=\xe9m\x16b\xab\xab\x89e\xea=\xb7UF`\x00\x00\u07d4V\x03$\x1e\xb8\xf0\x8fr\x1e4\x8c\x9d\x9a\xd9/H\u342a$\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4V\x056yJ\x9e+\x00I\xd1\x023\xc4\x1a\xdc_A\x8a&J\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\aY\x00Y\xa9\xfe\xc1\x88\x11I\xa4K6\x94\x9a\xef\x85\xd5`\x89lk\x93[\x8b\xbd@\x00\x00\u07d4V\v\xec\xdfR\xb7\x1f=\x88'\xd9'a\x0f\x1a\x98\x0f3qo\x89\x17GMp_V\u0400\x00\xe0\x94V\r\xa3~\x95m\x86/\x81\xa7_\u0540\xa7\x13\\\x1b$cR\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94V\x0f\xc0\x8d\a\x9f\x04~\xd8\xd7\xdfuU\x1a\xa55\x01\xf5p\x13\x8a\x01\x9b\xff/\xf5yh\xc0\x00\x00\u07d4V\x1b\xe9)\x9b>k>c\xb7\x9b\t\x16\x9d\x1a\x94\x8a\xe6\xdb\x01\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94V \xe3\xedy-/\x185\xfe_UA}Q\x11F\fj\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4V \xf4m\x14Q\xc25=bC\xa5\u0534'\x13\v\xe2\xd4\a\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94V!\x05\xe8+\t\x975\xdeI\xf6&\x92\u0307\xcd8\xa8\xed\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94V*\x8d\u02fe\xee\xf7\xb3`h]'0;\u059e\tJ\xcc\xf6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4V+\xce\u04ca\xb2\xabl\b\x0f;\x05A\xb8Enp\x82K?\x89\"\xca5\x87\xcfN\xb0\x00\x00\xe0\x94V+\xe9Z\xba\x17\xc57\x1f\u2e82\x87\x99\xb1\xf5]!w\u058a\b\x16\xd3~\x87\xb9\xd1\xe0\x00\x00\u07d4V/\x16\u05da\xbf\xce\u00d4>4\xb2\x0f\x05\xf9{\xdf\u0366\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4V7=\xaa\xb4c\x16\xfd~\x15v\xc6\x1ej\xff\xcbeY\xdd\u05c9\v\xacq]\x14l\x9e\x00\x00\u07d4V9v8\xbb<\xeb\xf1\xf6 byK^\xb9B\xf9\x16\x17\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4V:\x03\xab\x9cV\xb6\x00\xf6\xd2[f\f!\xe1c5Qzu\x8965\u026d\xc5\u07a0\x00\x00\u07d4V<\xb8\x80<\x1d2\xa2['\xb6A\x14\x85+\xd0M\x9c \u0349\v\x14\x9e\xad\n\xd9\xd8\x00\x00\u07d4VXc\x91\x04\fW\xee\xc6\xf5\xaf\xfd\x8c\u052b\xde\x10\xb5\n\u0309\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Vl\x10\xd68\u8e0bG\xd6\xe6\xa4\x14Iz\xfd\xd0\x06\x00\u0509\x05k9Bc\xa4\f\x00\x00\u07d4Vl(\xe3L8\b\xd9vo\xe8B\x1e\xbfO+\x1cO}w\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4V\x8d\xf3\x18Vi\x9b\xb5\xac\xfc\x1f\xe1\u0580\u07d9`\xcaCY\x89J\xcfUR\xf3\xb2I\x80\x00\u07d4V\x91\xdd/gE\xf2\x0e\"\xd2\xe1\u0479U\xaa)\x03\xd6VV\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4V\xa1\xd6\r@\xf5\u007f0\x8e\xeb\xf0\x87\xde\xe3\xb3\u007f\x1e|,\xba\x89>\u072e\xc8-\x06\xf8\x00\x00\u07d4V\xac \xd6;\xd8\x03Y\\\xec\x03m\xa7\xed\x1d\xc6n\n\x9e\a\x89\x03w*S\xcc\xdce\x80\x00\u07d4V\xb6\xc2=\xd2\uc434r\x8f;\xb2\xe7d\xc3\xc5\f\x85\xf1D\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\xdf\x05\xba\xd4l?\x00\xaeGn\xcf\x01{\xb8\xc8w8?\xf1\x89\n\xb1]\xaa\xefp@\x00\x00\u07d4V\xee\x19\u007fK\xbf\x9f\x1b\x06b\xe4\x1c+\xbd\x9a\xa1\xf7\x99\xe8F\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\xf4\x93\xa3\xd1\b\xaa\xa2\u044d\x98\x92/\x8e\xfe\x16b\u03f7=\x89m\x81!\xa1\x94\xd1\x10\x00\x00\u07d4V\xfc\x1a{\xad@G#|\xe1\x16\x14b\x96#\x8e\a\x8f\x93\xad\x89\t\xa6?\b\xeac\x88\x00\x00\u07d4V\xfe\xbf\x9e\x10\x03\xaf\x15\xb1\xbdI\a\xec\b\x9aJ\x1b\x91\xd2h\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4W\x17\u0313\x01Q\x1dJ\x81\xb9\xf5\x83\x14\x8b\xee\xd3\xd3\u0303\t\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4W\x17\xf2\xd8\xf1\x8f\xfc\xc0\xe5\xfe$}:B\x19\x03|:d\x9c\x89\u063beI\xb0+\xb8\x00\x00\u07d4W\x19P\xea,\x90\xc1B}\x93\x9da\xb4\xf2\xdeL\xf1\u03ff\xb0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4W\x19\xf4\x9br\r\xa6\x88V\xf4\xb9\xe7\b\xf2VE\xbd\xbcKA\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4W*\xc1\xab\xa0\xde#\xaeA\xa7\xca\xe1\xdc\bB\u062b\xfc\x10;\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94W-\xd8\xcd?\xe3\x99\xd1\xd0\xec(\x121\xb7\xce\xfc \xb9\u4eca\x023\xc8\xfeBp>\x80\x00\x00\xe0\x94WI!\x83\x8c\xc7}l\x98\xb1}\x90::\xe0\xee\r\xa9[\u040a\vS(\x17\x8a\xd0\xf2\xa0\x00\x00\u07d4WJ\xd95S\x90\u421e\xf4*\xcd\x13\x8b*'\xe7\x8c\x00\xae\x89Tg\xb72\xa9\x134\x00\x00\u07d4WM\xe1\xb3\xf3\x8d\x91XF\xae7\x18VJZ\xda \xc2\xf3\xed\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94W\\\x00\u0081\x82\x10\u0085U\xa0\xff)\x01\x02\x89\xd3\xf8#\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94Ws\xb6\x02g!\xa1\xdd\x04\xb7\x82\x8c\xd6+Y\x1b\xfb4SL\x8a\x05\xb7\xacES\xdez\xe0\x00\x00\xe0\x94WwD\x1c\x83\xe0?\v\xe8\xdd4\v\xdechP\x84|b\v\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Wx\xff\u071b\x94\u0165\x9e\"N\xb9e\xb6\u0790\xf2\"\xd1p\x89\x12-\u007f\xf3f\x03\xfc\x00\x00\u07d4Wz\xee\xe8\u053c\b\xfc\x97\xab\x15n\xd5\u007f\xb9p\x92Sf\xbe\x89\x12\r\xf1\x14rX\xbf\x00\x00\u07d4W{-\a<Y\fP0o[\x11\x95\xa4\xb2\xba\x9e\u0366%\x89\x14@\xbd\u0515\x15\xf0\x00\x00\u07d4W{\xfed\xe3\xa1\xe3\x80\x0e\x94\xdb\x1cl\x18M\x8d\u022a\xfcf\x89Q4\xed\x17A\u007f(\x00\x00\u07d4W\x82Z\xeb\t\al\xaaGx\x87\xfb\u026e7\xe8\xb2|\xc9b\x89\x05k\xc7^-c\x10\x00\x00\u07d4W\x880\x10\xb4\xac\x85\u007f\xed\xac\x03\xea\xb2U\x17#\xa8D\u007f\xfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4W\x89\xd0\x1d\xb1,\x81j\xc2h\xe9\xaf\x19\xdc\r\xd6\u065f\x15\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4W\x92\x81OY\xa3:\x18C\xfa\xa0\x1b\xaa\b\x9e\xb0/\xfb\\\xf1\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4W\x93\xab\xe6\xf1S3\x11\xfdQSh\x91x;?\x96%\xef\x1c\x89,\u0626V\xf2?\xda\x00\x00\xe0\x94W\x97\xb6\x0f\u0489J\xb3\xc2\xf4\xae\u0786\xda\xf2\xe7\x88\xd7E\xad\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4W\xa8R\xfd\xb9\xb1@[\xf5<\u03d5\b\xf82\x99\xd3 lR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4W\xb2=j\x1a\xdc\x06\xc6R\xa7y\u01a7\xfbk\x95\xb9\xfe\xadf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4W\xbc \xe2\xd6+=\x19f<\xdbL0\x9d[O/\xc2\u06cf\x89\x05k\xc7^-c\x10\x00\x00\u07d4W\xbd\xdf\a\x884\x00\x9c\x89\u060eb\x82u\x9d\xc4S5\xb4p\x89tq|\xfbh\x83\x10\x00\x00\u07d4W\xbe\xeaql\xbd\x81p\ns\xd6\u007f\x9f\xf09R\x9c-\x90%\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4W\xd02\xa4=\x16Nq\xaa.\xf3\xff\xd8I\x1b\nN\xf1\xea[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4W\xd3\u07c0O+\xee\xe6\xefS\xab\x94\xcb>\xe9\xcfRJ\x18\u04c9\x15Vak\x96\x06g\x00\x00\u07d4W\xd5\xfd\x0e=0I3\x0f\xfc\xdc\xd0 Ei\x17e{\xa2\u0689k\xf2\x01\x95\xf5T\xd4\x00\x00\u07d4W\u0754q\xcb\xfa&'\t\xf5\U00106f37t\xc5\xf5'\xb8\xf8\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4W\xdf#\xbe\xbd\xc6^\xb7_\ub732\xfa\xd1\xc0si++\xaf\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4X\x00\u03410\x83\x9e\x94I]-\x84\x15\xa8\xea,\x90\xe0\xc5\u02c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94X\x03\xe6\x8b4\xda\x12\x1a\xef\b\xb6\x02\xba\u06ef\xb4\xd1$\x81\u028a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\xe0\x94X\x16\xc2hww\xb6\xd7\u04a2C-Y\xa4\x1f\xa0Y\xe3\xa4\x06\x8a\x1cO\xe4:\xdb\n^\x90\x00\x00\u07d4X\x1a:\xf2\x97\xef\xa4Cj)\xaf\x00r\x92\x9a\xbf\x98&\xf5\x8b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\x1b\x9f\xd6\xea\xe3r\xf3P\x1fB\xeb\x96\x19\xee\xc8 \xb7\x8a\x84\x8a\x04+\xe2\xc0\f\xa5;\x8d\x80\x00\u07d4X\x1b\xdf\x1b\xb2v\xdb\u0746\xae\xdc\xdb9z\x01\xef\xc0\xe0\f[\x8965\u026d\xc5\u07a0\x00\x00\u07d4X\x1f4\xb5#\xe5\xb4\x1c\t\xc8|)\x8e)\x9c\xbc\x0e)\xd0f\x89=X3\xaa\xfd9u\x80\x00\xe0\x94X$\xa7\xe2(8'q40\x8c_KP\u06b6^C\xbb1\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4X+pf\x9c\x97\xaa\xb7\u0581H\xd8\xd4\xe9\x04\x11\xe2\x81\rV\x8965f3\xeb\xd8\xea\x00\x00\u07d4X.|\xc4o\x1d{Nn\x9d\x95\x86\x8b\xfd7\x05s\x17\x8fL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4X>\x83\xbaU\xe6~\x13\xe0\xe7o\x83\x92\xd8s\xcd!\xfb\xf7\x98\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Xi\xfb\x86}q\xf18\u007f\x86;i\x8d\t\xfd\xfb\x87\u011b\\\x89\u01bb\xf8X\xb3\x16\b\x00\x00\u07d4X}hI\xb1h\xf6\xc33+z\xba\xe7\xeblB\xc3\u007fH\xbf\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4X\x87\xdcj3\xdf\xedZ\xc1\xed\xef\xe3^\xf9\x1a!b1\xac\x96\x89\r\x8drkqw\xa8\x00\x00\xe0\x94X\x8e\u0650\xa2\xaf\xf4J\x94\x10]X\xc3\x05%w5\xc8h\xac\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4X\xae-\xdc_L\x8a\u0697\xe0l\x00\x86\x17\x17g\xc4#\xf5\u05c9WG=\x05\u06ba\xe8\x00\x00\u07d4X\xae\xd6gJ\xff\xd9\xf6B3'*W\x8d\xd98k\x99\xc2c\x89\xb8Pz\x82\a( \x00\x00\xe0\x94X\xb8\b\xa6[Q\xe63\x89i\xaf\xb9^\xc7\a5\xe4Q\xd5&\x8a\bxK\xc1\xb9\x83z8\x00\x00\u07d4X\xb8\xae\x8fc\xef5\xed\ab\xf0\xb6#=J\xc1Nd\xb6M\x89lk\x93[\x8b\xbd@\x00\x00\u07d4X\xba\x15ie\x0e[\xbb\xb2\x1d5\xd3\xe1u\xc0\u05b0\xc6Q\xa9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4X\xc5U\xbc)<\xdb\x16\xc66.\xd9z\xe9U\v\x92\xea\x18\x0e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4X\xc6P\xce\xd4\v\xb6VA\xb8\xe8\xa9$\xa09\xde\xf4hT\u07c9\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4X\xc9\aT\xd2\xf2\n\x1c\xb1\xdd3\x06%\xe0KE\xfaa\x9d\\\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\xe2\xf1\x12#\xfc\x827\xf6\x9d\x99\xc6(\x9c\x14\x8c\x06\x04\xf7B\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4X\xe5T\xaf=\x87b\x96 \xdaa\xd58\xc7\xf5\xb4\xb5LJ\xfe\x89FP\x9diE4r\x80\x00\u07d4X\xe5\xc9\xe3D\xc8\x06e\r\xac\xfc\x90M3\xed\xbaQ\a\xb0\u0789\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4X\xe6a\u043as\xd6\xcf$\t\x9aUb\xb8\b\xf7\xb3g;h\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\xf0[&%`P<\xa7a\xc6\x18\x90\xa4\x03_Lsr\x80\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4X\xfb\x94sd\xe7iWe6\x1e\xbb\x1e\x80\x1f\xfb\x8b\x95\xe6\u0409\n\u05ce\xbcZ\xc6 \x00\x00\u07d4Y\x01\x81\xd4E\x00{\u0407Z\xaf\x06\x1c\x8dQ\x159\x00\x83j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\x02\xe4J\xf7i\xa8rF\xa2\x1e\a\x9c\b\xbf6\xb0n\xfe\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4Y\n\xcb\xda7)\f\r>\xc8O\xc2\x00\rv\x97\xf9\xa4\xb1]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94Y\f\xcbY\x11\xcfx\xf6\xf6\"\xf55\xc4t7_J\x12\xcf\u03ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y\x10\x10m\xeb\u0491\xa1\u0340\xb0\xfb\xbb\x8d\x8d\x9e\x93\xa7\xcc\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\x16\x17I\xfe\xdc\xf1\xc7!\xf2 -\x13\xad\xe2\xab\xcfF\v=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Y\x1b\xef1q\xd1\u0155w\x17\xa4\xe9\x8d\x17\xeb\x14,!NV\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y <\xc3u\x99\xb6H1*|\xc9\xe0m\xac\xb5\x89\xa9\xaej\x89\b\x0fyq\xb6@\x0e\x80\x00\u07d4Y&\x81q\xb83\xe0\xaa\x13\xc5KR\xcc\xc0B.O\xa0:\ub262\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94Y'w&\x1e;\xd8R\u010e\u0295\xb3\xa4L[\u007f-B,\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y0Dg\x0f\xae\xff\x00\xa5[Z\xe0Q\xeb{\xe8p\xb1\x16\x94\x89\a?u\u0460\x85\xba\x00\x00\xe0\x94Y;E\xa1\x86J\xc5\xc7\xe8\xf0\u02ae\xba\r\x87<\xd5\xd1\x13\xb2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Y<H\x93[\xea\xff\x0f\xde\x19\xb0M0\x9c\xd50\xa2\x8eR\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4YG<\xd3\x00\xff\xfa\xe2@\xf5xV&\xc6]\xfe\u01d2\xb9\xaf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4YH\xbc6P\xedQ\x9b\xf8\x91\xa5rg\x9f\u0652\xf8x\fW\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4YJv\xf0i58\x8d\xde^#F\x96\xa0f\x8b\xc2\r-\u0709\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4YV\x9a!\u048f\xbaK\xda7u4\x05\xa0\x81\xf2\x06=\xa1P\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4YV\xb2\x8e\u01c9\vv\xfc\x06\x1a\x1f\xebR\xd8*\xe8\x1f\xb65\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y^#\u05c8\xa2\u053b\x85\xa1]\xf7\x13m&JcU\x11\xb3\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94Yp8\xff\x91\xa0\x90\f\xbb\xabH\x8a\xf4\x83\u01d0\xe6\xec\x00\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94Yp\xfb\x1b\x14M\xd7Q\xe4\xce.\xca|\xaa \xe3c\xdcM\xa3\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Yu\xb9R\x8f#\xaf\x1f\x0e.\xc0\x8a\xc8\xeb\xaaxj,\xb8\xe0\x89\x12\xbfPP:\xe3\x03\x80\x00\u07d4Yu\u05cd\x97N\u5edeML\xa2\xaew\xc8K\x9c;K\x82\x89JD\x91\xbdm\xcd(\x00\x00\u07d4Y\x85\u015aD\x9d\xfc]\xa7\x87\xd8$Ntlmp\u02a5_\x89\x05k\xc7^-c\x10\x00\x00\u07d4Y\x8a\xaa\xba\xe9\xed\x83={\xc2\"\xe9\x1f\u02a0d{wX\v\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4Y\x92bLT\xcd\xec`\xa5\xae\x93\x803\xaf\x8b\xe0\xc5\f\xbb\n\x89\xc4T\xe0\xf8\x87\x0f+\x00\x00\xe0\x94Y\x97(\xa7\x86\x18\u0461{\x9e4\xe0\xfe\xd8\xe8W\xd5\xc4\x06\"\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4Y\x97\xff\xef\xb3\xc1\xd9\xd1\x0f\x1a\u2b0a\xc3\xc8\xe2\xd2)'\x83\x8965\u026d\xc5\u07a0\x00\x00\u07d4Y\xa0\x87\xb95\x1c\xa4/X\xf3n\x02\x19'\xa2)\x88(O8\x89\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4Y\xa1-\xf2\xe3\xef\x85z\xce\xff\x93\x06\xb3\t\xf6\xa5\x00\xf7\x014\x8965\u026d\xc5\u07a0\x00\x00\u07d4Y\xb9m\ub1c4\x88]\x8d;J\x16aC\xccC]%U\xa1\x89Hz\x9a0E9D\x00\x00\u07d4Y\xb9\xe73\u02e4\xbe\x00B\x9bK\xd9\u07e6G2\x05:}U\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Y\xc5\xd0k\x17\x0e\xe4\xd2n\xb0\xa0\xebF\xcb}\x90\xc1\xc9\x10\x19\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Y\xc7\xf7\x85\xc91`\xe5\x80~\xd3N^SK\xc6\x18\x86G\xa7\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4Y\xd19\xe2\xe4\f{\x97#\x9d#\u07ec\xa38X\xf6\x02\xd2+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\xf6${\rX*\xaa%\xe5\x11Ge\xe4\xbf<wOC\u0089\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4Y\xfe\x00im\xbd\x87\xb7\x97k)\xd1\x15l\x88B\xa2\xe1y\x14\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Z\r`\x9a\xae#2\xb17\xab;/&aZ\x80\x8f7\xe43\x8a!\xe1\x9e\f\x9b\xab$\x00\x00\x00\u07d4Z\x19+\x96J\xfd\x80w>_^\xdajV\xf1N%\xe0\xc6\xf3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Z\x1a3ib\xd6\xe0\xc601\u0303\u01a5\u01a6\xf4G\x8e\u02c965\u026d\xc5\u07a0\x00\x00\u07d4Z\x1d--\x1dR\x03\x04\xb6 \x88IW\x047\xeb0\x91\xbb\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Z&s1\xfa\xcb&-\xaa\xec\xd9\xddc\xa9p\f_RY\u07c9\x05k\xc7^-c\x10\x00\x00\xe0\x94Z(WU9\x1e\x91NX\x02_\xaaH\xcch_O\xd4\xf5\xb8\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4Z)\x16\xb8\xd2\xe8\xcc\x12\xe2\a\xabFMC>#p\xd8#\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4Z+\x1c\x85:\xeb(\xc4U9\xafv\xa0\n\xc2\u0628$(\x96\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d4Z-\xaa\xb2\\1\xa6\x1a\x92\xa4\xc8,\x99%\xa1\xd2\xefXX^\x89\f8\r\xa9\u01d5\f\x00\x00\u07d4Z0\xfe\xac7\xac\x9fr\u05f4\xaf\x0f+\xc79R\xc7O\xd5\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4ZTh\xfa\\\xa2&\xc7S.\xcf\x06\xe1\xbc\x1cE\"]~\u0249g\x8a\x93 b\xe4\x18\x00\x00\u07d4ZVR\x857JI\xee\xddPL\x95}Q\bt\xd0\x04U\xbc\x89\x05k\xc7^-c\x10\x00\x00\u07d4Z^\xe8\xe9\xbb\x0e\x8a\xb2\xfe\xcbK3\u0494x\xbeP\xbb\xd4K\x89*\x11)\u0413g \x00\x00\xe0\x94Z_\x85\b\xda\x0e\xbe\xbb\x90\xbe\x903\xbdM\x9e'A\x05\xae\x00\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4Z`q\xbc\xeb\xfc\xbaJ\xb5\u007fM\xb9o\u01e6\x8b\xec\xe2\xba[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z`\xc9$\x16(s\xfc~\xa4\xda\u007f\x97.5\x01g7`1\x89\x04\x87\xf2w\xa8\x85y\x80\x00\u07d4Zf\x86\xb0\xf1~\a\xed\xfcY\xb7Y\xc7}[\xef\x16M8y\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4Zp\x10o \xd6?\x87Re\xe4\x8e\r5\xf0\x0e\x17\xd0+\u0249\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794Zt\xbab\xe7\xc8\x1a4t\xe2}\x89O\xed3\xdd$\xad\x95\xfe\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94Zw5\x00}p\xb0hD\u0699\x01\xcd\xfa\xdb\x11\xa2X,/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Z\x82\xf9l\u0537\xe2\xd9=\x10\xf3\x18]\xc8\xf4=Ku\xaai\x89lc?\xba\xb9\x8c\x04\x00\x00\u07d4Z\x87\xf04\xe6\xf6\x8fNt\xff\xe6\fd\x81\x946\x03l\xf7\u05c9\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Z\x89\x11U\xf5\x0eB\aCt\xc79\xba\xad\xf7\xdf&Q\x15:\x8a\x01\x02\xdao\xd0\xf7:<\x00\x00\u07d4Z\x9c\x8bi\xfcaMiVI\x99\xb0\r\xcbB\xdbg\xf9~\x90\x89\xb9\xe6\x15\xab\xad:w\x80\x00\xe0\x94Z\xaf\x1c1%Jn\x00_\xba\u007fZ\xb0\xecy\xd7\xfc+c\x0e\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4Z\xb1\xa5aSH\x00\x1c|w]\xc7WHf\x9b\x8b\xe4\xde\x14\x89%jr\xfb)\xe6\x9c\x00\x00\xe1\x94Z\xbf\xec%\xf7L\u06047c\x1aw1\x90i2wcV\xf9\x8b\t\xd8<\xc0\u07e1\x11w\xff\x80\x00\u07d4Z\u0090\x8b\x0f9\x8c\r\xf5\xba\xc2\xcb\x13\xcas\x14\xfb\xa8\xfa=\x89\n\xd4\xc81j\v\f\x00\x00\xe0\x94Z\u025a\u05c1j\xe9\x02\x0f\xf8\xad\xf7\x9f\xa9\x86\x9b|\xeaf\x01\x8a\x04ri\x8bA;C \x00\x00\u07d4Z\xd1,^\xd4\xfa\x82~!P\u03e0\u058c\n\xa3{\x17i\xb8\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94Z\xd5\xe4 uV\x13\x88o5\xaaV\xac@>\xeb\xdf\xe4\xb0\u040a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4Z\xdew\xfd\x81\xc2\\\n\xf7\x13\xb1\a\x02v\x8c\x1e\xb2\xf9u\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Z\xe6N\x85;\xa0\xa5\x12\x82\u02cd\xb5.Aa^|\x9fs?\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z\xed\x0el\xfe\x95\xf9\u0580\xc7dr\xa8\x1a+h\n\u007f\x93\xe2\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4Z\xef\x16\xa2&\xddh\a\x1f$\x83\xe1\xdaBY\x83\x19\xf6\x9b,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z\xf4j%\xac\t\xcbsakS\xb1O\xb4/\xf0\xa5\x1c\u0772\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Z\xf7\xc0r\xb2\u016c\xd7\x1cv\xad\xdc\xceS\\\xf7\xf8\xf95\x85\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Z\xfd\xa9@\\\x8e\x976QEt\u0692\x8d\xe6tV\x01\t\x18\x8a\x01E\xb8\xb0#\x9aF\x92\x00\x00\u07d4[\x06\xd1\xe6\x93\f\x10Ti+y\xe3\xdb\xe6\xec\xceS\x96d \x89\v\"\u007fc\xbe\x81<\x00\x00\u07d4[%\xca\xe8m\xca\xfa*`\xe7r61\xfc_\xa4\x9c\x1a\xd8}\x89\x87\fXQ\x0e\x85 \x00\x00\u07d4[(|~sB\x99\xe7'bo\x93\xfb\x11\x87\xa6\rPW\xfe\x89\x05|\xd94\xa9\x14\xcb\x00\x00\u07d4[)\f\x01\x96|\x81.M\xc4\xc9\v\x17L\x1b@\x15\xba\xe7\x1e\x89\b \xeb4\x8dR\xb9\x00\x00\u07d4[+d\xe9\xc0X\u30a8\xb2\x99\"N\xec\xaa\x16\xe0\x9c\x8d\x92\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\xe0\x94[./\x16\x18U.\xab\r\xb9\x8a\xddUc|)Q\xf1\xfb\x19\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4[0`\x8cg\x8e\x1a\xc4d\xa8\x99L;3\xe5\xcd\xf3Iq\x12\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4[36\x96\xe0L\xca\x16\x92\xe7\x19\x86W\x9c\x92\rk)\x16\xf9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94[C\rw\x96\x96\xa3e?\xc6\x0et\xfb\u02ec\xf6\xb9\u00ba\xf1\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4[Cse\xae:\x9a/\xf9|h\xe6\xf9\nv \x18\x8c}\x19\x89l\x87T\xc8\xf3\f\b\x00\x00\u07d4[I\xaf\xcduDx8\xf6\xe7\xce\u068d!w}O\xc1\xc3\xc0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4[L\f`\xf1\x0e\u0489K\xdbB\xd9\xdd\x1d!\x05\x87\x81\n\r\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4[N\xa1m\xb6\x80\x9b\x03R\u0536\xe8\x1c9\x13\xf7jQ\xbb2\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4[[\xe0\xd8\xc6rv\xba\xab\xd8\xed\xb3\rH\xeaud\v\x8b)\x89,\xb1\xf5_\xb7\xbe\x10\x00\x00\u07d4[]Qp)2\x15b\x11\x1bC\bm\v\x045\x91\x10\x9ap\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\xe0\x94[]\x8c\x8e\xedl\x85\xac!Va\xde\x02fv\x82?\xaa\n\f\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4[mU\xf6q)g@\\e\x91)\xf4\xb1\xde\t\xac\xf2\xcb{\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4[p\u011c\u024b=\xf3\xfb\xe2\xb1Y\u007f\\\x1bcG\xa3\x88\xb7\x894\x95tD\xb8@\xe8\x00\x00\u07d4[sn\xb1\x83Sb\x9b\u0796v\xda\xdd\x16P4\xce^\xcch\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4[u\x9f\xa1\x10\xa3\x1c\x88F\x9fT\xd4K\xa3\x03\xd5}\xd3\xe1\x0f\x89[F\xdd/\x0e\xa3\xb8\x00\x00\u07d4[w\x84\xca\xea\x01y\x9c\xa3\x02'\x82vg\xce |\\\xbcv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4[x\xec\xa2\u007f\xbd\xeao&\xbe\xfb\xa8\x97+)^x\x146K\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94[\x80\v\xfd\x1b>\u0525}\x87Z\xed&\xd4/\x1aw\b\xd7*\x8a\x01Z\x82\xd1\u057b\x88\xe0\x00\x00\u07d4[\x85\xe6\x0e*\xf0TO/\x01\xc6N 2\x90\x0e\xbd8\xa3\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4[\xa2\xc6\xc3]\xfa\xec)h&Y\x19\x04\xd5DFJ\xea\xbd^\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94[\xafmt\x96 \x80>\x83H\xaf7\x10\xe5\xc4\xfb\xf2\x0f\u0214\x8a\x01\x0f@\x02a]\xfe\x90\x00\x00\u07d4[\xc1\xf9U\a\xb1\x01\x86B\xe4\\\xd9\xc0\xe2'3\xb9\xb1\xa3&\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94[\xd25GG\u007fm\t\u05f2\xa0\x05\xc5\xeee\fQ\fV\u05ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4[\xd2J\xac6\x12\xb2\f`\x9e\xb4gy\xbf\x95i\x84\a\xc5|\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4[\u0586-Q}M\xe4U\x9dN\xec\n\x06\xca\xd0^/\x94n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4[\xe0EQ*\x02n?\x1c\xeb\xfdZ~\xc0\xcf\xc3o-\xc1k\x89\x06\x81U\xa46v\xe0\x00\x00\xe0\x94[\xf9\xf2\"nZ\xea\xcf\x1d\x80\xae\nY\xc6\xe3\x808\xbc\x8d\xb5\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4[\xfa\xfe\x97\xb1\xdd\x1dq+\xe8mA\xdfy\x89SE\x87Z\x87\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\\\x0f.Q7\x8fk\r{\xabas1X\vn9\xad<\xa5\x8a\x02\bj\xc3Q\x05&\x00\x00\x00\u07d4\\)\xf9\xe9\xa5#\xc1\xf8f\x94H\xb5\\H\xcb\xd4|%\xe6\x10\x894F\xa0\xda\xd0L\xb0\x00\x00\xe0\x94\\0\x8b\xacHW\xd3;\xae\xa0t\xf3\x95m6!\xd9\xfa(\xe1\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\\1*V\u01c4\xb1\"\t\x9bvM\x05\x9c!\xec\xe9^\x84\u0289\x05&c\u032b\x1e\x1c\x00\x00\u07d4\\1\x99m\xca\xc0\x15\xf9\xbe\x98[a\x1fF\x870\xef$M\x90\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\\24W\xe1\x87v\x1a\x82v\xe3Y\xb7\xb7\xaf?;n=\xf6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\\<\x1cd[\x91uC\x11;>l\x1c\x05M\xa1\xfet+\x9a\x89+^:\xf1k\x18\x80\x00\x00\u0794\\=\x19D\x1d\x19l\xb4Cf \xfc\xad\u007f\xbby\xb2\x9ex\x88\xc6s\xce<@\x16\x00\x00\u07d4\\?V\u007f\xaf\xf7\xba\u0475\x12\x00\"\xe8\xcb\u02a8+I\x17\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\\Ch\x91\x8a\xced\t\u01de\u0280\u036a\xe49\x1d+bN\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\\FA\x97y\x1c\x8a=\xa3\xc9%Co'z\xb1;\xf2\xfa\xa2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\\H\x81\x16\\\xb4+\xb8.\x979l\x8e\xf4J\xdb\xf1s\xfb\x99\x89\x05\xfe\xe2\"\x04\x1e4\x00\x00\xe0\x94\\H\x92\x90z\a \xdfo\xd3A>c\xffv}k9\x80#\x8a\x02\xcb\x00\x9f\u04f5y\x0f\x80\x00\u07d4\\O$\xe9\x94\ud3c5\x0e\xa7\x81\x8fG\x1c\x8f\xac;\xcf\x04R\x89]\x80h\x8d\x9e1\xc0\x00\x00\u07d4\\T\x19V\\:\xadNqN\a92\x8e5!\u024f\x05\u0309\x1c\x9fx\u0489>@\x00\x00\u07d4\\a6\xe2\x18\xde\na\xa17\xb2\xb3\x96-*a\x12\xb8\t\u05c9\x0f\xf3\u06f6_\xf4\x86\x80\x00\xe0\x94\\a\xaby\xb4\b\xdd2)\xf6bY7\x05\xd7/\x1e\x14{\xb8\x8a\x04\xd0$=4\x98\u0344\x00\x00\u07d4\\m\x04\x1d\xa7\xafD\x87\xb9\xdcH\xe8\xe1\xf6\af\u0425m\xbc\x89O\a\n\x00>\x9ct\x00\x00\u07d4\\o6\xaf\x90\xab\x1aeln\xc8\xc7\xd5!Q'b\xbb\xa3\xe1\x89lh\xcc\u041b\x02,\x00\x00\u07d4\\{\x9e\u01e2C\x8d\x1e<v\x98\xb5E\xb9\xc3\xfdw\xb7\xcdU\x8965\u026d\xc5\u07a0\x00\x00\u07d4\\\x93o;\x9d\"\xc4\x03\xdb^s\x0f\xf1w\xd7N\xefB\u06ff\x89\x04\x10\u0546\xa2\nL\x00\x00\u07d4\\\xb71\x16\r.\x89eg\v\u0792]\x9d\xe5Q\t54}\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\\\xb9S\xa0\xe4/P0\x81\"&!\u007f\xff\xc3\xce#\x04W\xe4\x89\x05k\xc7^-c\x10\x00\x00\u0794\\\xbd\x8d\xaf'\xdd\xf7\x04\xcd\xd0\xd9\t\xa7\x89\xba6\xedO7\xb2\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94\\\xc4\u02e6!\xf2 cwB\x05\u007f`U\xb8\r\xff\xd7~\x13\x8a\bxG{}%;f\x00\x00\xe0\x94\\\xc7\xd3\x06mE\xd2v!\xf7\x8b\xb4\xb39G>D*\x86\x0f\x8a\x02\x1e\x18\x99\xf07z\xea\x00\x00\u07d4\\\xcc\xf1P\x8b\xfd5\xc2\x050\xaad%\x00\xc1\r\xeee\xea\xed\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\\\xcer\xd0h\xc7\xc3\xf5[\x1d(\x19T^w1|\xae\x82@\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\\\xd0\xe4u\xb5D!\xbd\xfc\f\x12\xea\x8e\b+\u05e5\xaf\nj\x89\x032\xca\x1bg\x94\f\x00\x00\u07d4\\\u0548\xa1N\xc6H\xcc\xf6G)\xf9\x16z\xa7\xbf\x8b\xe6\xeb=\x8965\u026d\xc5\u07a0\x00\x00\u07d4\\\u062f`\xdee\xf2M\xc3\xceW0\xba\x92e0\"\xdcYc\x89a\t=|,m8\x00\x00\u07d4\\\xdcG\b\xf1O@\xdc\xc1Zy_}\xc8\xcb\v\u007f\xaa\x9en\x89\x1d\x1c_>\xda \xc4\x00\x00\u07d4\\\u0d86,\u0391b\xe8~\bI\xe3\x87\xcb]\xf4\xf9\x11\x8c\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\\\xe2\xe7\u03aa\xa1\x8a\xf0\xf8\xaa\xfa\u007f\xba\xd7L\u021e<\xd46\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\\\xe4@h\xb8\xf4\xa3\xfey\x9ej\x83\x11\xdb\xfd\xed\xa2\x9d\xee\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u0794\\\xeb\xe3\v*\x95\xf4\xae\xfd\xa6ee\x1d\xc0\xcf~\xf5u\x81\x99\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\\\xf1\x8f\xa7\u0227\xc0\xa2\xb3\xd5\xef\u0459\x0fd\xdd\xc5i$,\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\\\xf4N\x10T\reqd#\xb1\xbc\xb5B\xd2\x1f\xf8:\x94\u034a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\\\xf8\xc0>\xb3\xe8r\xe5\x0f|\xfd\f/\x8d;?,\xb5\x18:\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\\\xfa\x8dV\x85ue\x8c\xa4\xc1\xa5\x93\xacL]\x0eD\xc6\aE\x89\x0f\xc6o\xae7F\xac\x00\x00\u07d4\\\xfa\x98w\xf7\x19\u01dd\x9eIJ\b\xd1\xe4\x1c\xf1\x03\xfc\x87\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4]\x1d\xc38{G\xb8E\x1eU\x10l\f\xc6}m\xc7+\u007f\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]#\x1ap\xc1\xdf\xeb6\n\xbd\x97\xf6\x16\xe2\xd1\r9\xf3\u02b5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4]$\xbd\xbc\x1cG\xf0\xeb\x83\xd1(\xca\xe4\x8a\xc3<H\x17\xe9\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4](\x19\xe8\xd5x!\x92.\xe4Ee\f\xca\xec}@TJ\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4]/\u007f\v\x04\xbaK\xe1a\u1736\xf1\x12\xcez^}\u007f\xe4\x89\x01\xe8\u007f\x85\x80\x9d\xc0\x00\x00\u07d4]2\xf6\xf8nx\u007f\xf7\x8ec\u05cb\x0e\xf9_\xe6\a\x18R\xb8\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4]9\uf7a6\xbd\xff\xf1]\x11\xfe\x91\xf5a\xa6\xf9\xe3\x1f]\xa5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94]?;\x1fq0\xb0\xbb!\xa0\xfd29b9\x17\x9a%e\u007f\x8a\r:\xb8\xea^\x8f\xd9\xe8\x00\x00\u07d4]WQ\x81\x9bO=&\xed\f\x1a\xc5qU'5'\x1d\xbe\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4]\\,\x10\x99\xbb\xee\xfb&~t\xb5\x88\x80\xb4D\xd9DI\xe0\x89\r\xbf\v\u0441\xe2\xe7\x00\x00\u07d4]\\\xdb\xe2[*\x04K{\x9b\u30fc\xaaX\a\xb0m<k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]]n\x82\x1cn\uf581\f\x83\u0111F\x85`\xefp\xbf\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]h2K\xcbwm?\xfd\v\xf9\xfe\xa9\x1d\x9f\x03\u007f\u05ab\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]j\xe8\xcb\u05b39<\"\xd1bT\x10\r\x028\xe8\b\x14|\x89'\a\xe5mQ\xa3\f\x00\x00\u07d4]l\\r\rf\xa6\xab\u0283\x97\x14.c\xd2h\x18\xea\xabT\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4]l\u03c0g8\t\x10B\xad\x97\xa6\xe0\x95\xfe\x8c6\xaay\u0149\n1\x06+\xee\xedp\x00\x00\u07d4]qy\x9c\x8d\xf3\xbc\xcb~\xe4F\xdfP\xb81+\xc4\xebq\u0149\n\u05ce\xbcZ\xc6 \x00\x00\u07d4]\x82-\x9b>\xf4\xb5\x02bt\a\xda'/g\x81Jk\xec\u0509\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4]\x83\xb2\x1b\xd2q#`Ckg\xa5\x97\xee3x\xdb>z\xe4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94]\x87+\x12.\x99N\xf2|q\xd7\u07b4W\xbfeB\x9e\xcal\x8a\x01\xb1\xad\xed\x81\u04d4\x10\x80\x00\xe0\x94]\x8d1\xfa\xa8d\xe2!Y\xcdoQu\xcc\xec\xc5?\xa5Mr\x8a\x05\xb6\x96\xb7\r\xd5g\x10\x00\x00\xe0\x94]\x95\x8a\x9b\u0449\u0098_\x86\u014a\x8ci\xa7\xa7\x88\x06\xe8\u068a\x02(\xf1o\x86\x15x`\x00\x00\u07d4]\xa2\xa9\xa4\xc2\xc0\xa4\xa9$\xcb\xe0\xa5:\xb9\xd0\xc6'\xa1\u03e0\x89'\xbf8\xc6TM\xf5\x00\x00\u07d4]\xa4\u0288\x93\\'\xf5\\1\x10H\x84\x0eX\x9e\x04\xa8\xa0I\x89\x04V9\x18$O@\x00\x00\u07d4]\xa5G\x85\u027d0W\\\x89\u07b5\x9d A\xd2\n9\xe1{\x89j\xa2\t\xf0\xb9\x1de\x80\x00\xe0\x94]\xb6\x9f\xe9>o\xb6\xfb\xd4P\x96k\x97#\x8b\x11\n\xd8'\x9a\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4]\xb7\xbb\xa1\xf9W?$\x11]\x8c\x8cb\xe9\u0388\x95\x06\x8e\x9f\x89\x02\xb5\xaa\xd7,e \x00\x00\xe0\x94]\xb8D\x00W\x00i\xa9W<\xab\x04\xb4\u6d955\xe2\x02\xb8\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4]\xc3m\xe55\x94P\xa1\xec\t\xcb\fD\xcf+\xb4+:\xe45\x89<\x94m\x89;3\x06\x00\x00\u07d4]\xc6\xf4_\xef&\xb0n3\x021?\x88M\xafH\xe2to\xb9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94]\u0376\xb8zP<m\x8a<e\xc2\u03da\x9a\xa8\x83G\x9a\x1e\x8a\x01\xf2\xbb\xa5\xd8O\x99\xc0\x00\x00\u07d4]\xd1\x12\xf3h\xc0\xe6\xce\xffw\xa9\xdf\x02\xa5H\x16Q\xa0/\xb7\x89\t4r\xc8\\mT\x00\x00\xe0\x94]\xd5:\xe8\x97Rk\x16}9\xf1tN\xf7\xc3\xda[7\xa2\x93\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4]\xde\xd0I\xa6\xe1\xf3)\xdcK\x97\x1er,\x9c\x1f*\u0783\xf0\x8965\u026d\xc5\u07a0\x00\x00\u07d4]\u562b\xa3D7\x8c\xabD1U[Oy\x99-\u0090\u0189Hz\x9a0E9D\x00\x00\u07d4]\xe9\xe7\xd5\u0476g\u0415\xdd4\t\x9c\x85\xb0B\x1a\v\u0181\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4]\xf3'|\xa8Y6\u01e0\xd2\xc0yV\x05\xad%\t^qY\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]\xff\x81\x1d\xad\x81\x9e\xce;\xa6\x02\u00c3\xfb]\xc6L\n:H\x89\n\x15D\xbe\x87\x9e\xa8\x00\x00\u0794^\x03\x1b\nrDq\xd4v\xf3\xbc\xd2\xeb\a\x838\xbfg\xfb\xef\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94^\a\x85S,w#\xe4\xc0\xaf\x93W\xd5'Ks\xbd\xdd\xdd\u078a\x05KA\xea\x9b\xdba\xdc\x00\x00\u07d4^\x11\xec\xf6\x9dU\x1d\u007fO\x84\xdf\x12\x80F\xb3\xa12@\xa3(\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94^\x1f\xbdNX\xe21+<x\u05ea\xaa\xfa\x10\xbf\x9c1\x89\xe3\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94^2\xc7!\x91\xb89,U\xf5\x10\xd8\xe32n:`P\x1db\x8a\tQ>\xa9\xde\x02C\x80\x00\x00\u07d4^Q\xb8\xa3\xbb\t\xd3\x03\xea|\x86\x05\x15\x82\xfd`\x0f\xb3\xdc\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794^X\xe2U\xfc\x19\x87\n\x040_\xf2\xa0F1\xf2\xff)K\xb1\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4^ZD\x19t\xa8=t\u0187\xeb\xdcc?\xb1\xa4\x9e{\x1a\u05c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^eE\x8b\xe9d\xaeD\x9fqw7\x04\x97\x97f\xf8\x89\x87a\x89\x1c\xa7\xccs[o|\x00\x00\u07d4^g\u07c9i\x10\x1a\u06bd\x91\xac\xcdk\xb1\x99\x12t\xaf\x8d\xf2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4^n\x97G\xe1b\xf8\xb4\\en\x0fl\xaez\x84\xba\xc8\x0eN\x89lk\x93[\x8b\xbd@\x00\x00\u07d4^s\x1bU\xce\xd4R\xbb??\xe8q\xdd\xc3\xed~\xe6Q\n\x8f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^t\xed\x80\xe9eW\x88\xe1\xbb&\x97R1\x96g\xfeuNZ\x89\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4^w.'\xf2\x88\x00\xc5\r\u0697;\xb3>\x10v.n\xea \x89a\t=|,m8\x00\x00\u07d4^{\x8cT\xdcW\xb0@ bq\x9d\xee~\xf5\xe3~\xa3]b\x89\x9b\xf9\x81\x0f\xd0\\\x84\x00\x00\u07d4^\u007fp7\x87uX\x9f\xc6j\x81\xd3\xf6S\xe9T\xf5U`\ub243\xf2\x89\x18\x1d\x84\xc8\x00\x00\xe0\x94^\x80n\x84W0\xf8\a>l\xc9\x01\x8e\xe9\x0f\\\x05\xf9\t\xa3\x8a\x02\x01\xe9m\xac\u03af \x00\x00\u07d4^\x8eM\xf1\x8c\xf0\xafw\tx\xa8\u07cd\xac\x90\x93\x15\x10\xa6y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4^\x90\xc8Xw\x19\x87V\xb06l\x0e\x17\xb2\x8eR\xb4FPZ\x89\x14JJ\x18\xef\xebh\x00\x00\u07d4^\x95\xfe_\xfc\xf9\x98\xf9\xf9\xac\x0e\x9a\x81\u06b8>\xadw\x00=\x89\x1dB\xc2\r2y\u007f\x00\x00\u07d4^\xad)\x03z\x12\x89dx\xb1)j\xb7\x14\xe9\u02d5B\x8c\x81\x89\x03\xe0C\a-@n\x00\x00\u07d4^\xb3q\xc4\a@lB{;}\xe2q\xad<\x1e\x04&\x95y\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^\u037a\xea\xb9\x10o\xfe]{Q\x96\x96`\x9a\x05\xba\ub16d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4^\xd0\xd63\x85Y\xefD\xdcza\xed\xeb\x89?\xa5\xd8?\xa1\xb5\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\xe0\x94^\u04fb\xc0R@\xe0\u04d9\xebm\xdf\xe6\x0fb\xdeM\x95\t\xaf\x8a)\x14\xc0$u\xf9\xd6\xd3\x00\x00\u0594^\xd3\xf1\xeb\xe2\xaegV\xb5\xd8\xdc\x19\xca\xd0,A\x9a\xa5w\x8b\x80\u07d4^\xd5a\x15\xbde\x05\xa8\x82s\xdf\\V\x83\x94p\xd2J-\xb7\x89\x03\x8ee\x91\xeeVf\x80\x00\xe0\x94^\xf8\xc9a\x86\xb3y\x84\xcb\xfe\x04\u0158@n;\n\xc3\x17\x1f\x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4^\xfb\xdf\xe58\x99\x99c<&`Z[\xfc,\x1b\xb5\x95\x93\x93\x89\x03\xc0W\xc9\\\xd9\b\x00\x00\xe0\x94_\x13\x15F1Fm\xcb\x13S\u0210\x93*|\x97\xe0\x87\x8e\x90\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4_\x16z\xa2B\xbcL\x18\x9a\xde\xcb:\u0127\xc4R\xcf\x19/\u03c9lkLM\xa6\u077e\x00\x00\xe0\x94_\x1c\x8a\x04\xc9\rs[\x8a\x15)\t\xae\xaeco\xb0\xce\x16e\x8a\x01{x'a\x8cZ7\x00\x00\u07d4_#\xba\x1f7\xa9lE\xbcI\x02YS\x8aT\u008b\xa3\xb0\u0549A\rXj \xa4\xc0\x00\x00\u07d4_&\xcf4Y\x9b\xc3n\xa6{\x9ez\x9f\x9bC0\xc9\xd5B\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4_)\xc9\xdev]\xde%\x85*\xf0}3\xf2\xceF\x8f\xd2\t\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_/\a\xd2\u0597\xe8\xc5g\xfc\xfd\xfe\x02\x0fI\xf3`\xbe!9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_2\x1b=\xaa\xa2\x96\xca\xdf)C\x9f\x9d\xab\x06*K\xff\xed\u0589\x04p%\x90>\xa7\xae\x00\x00\u07d4_3:;#\x10vZ\r\x182\xb9\xbeL\n\x03pL\x1c\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4_4K\x01\xc7\x19\x1a2\xd0v*\xc1\x88\xf0\xec-\xd4`\x91\x1d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94_6>\n\xb7G\xe0-\x1b;f\xab\xb6\x9e\xa5<{\xafR:\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\u07d4_7[\x86`\f@\u0328\xb2gkz\x1a\x1d\x16D\xc5\xf5,\x89\x04F\x18\xd7Lb?\x00\x00\u07d4_>\x1eg9\xb0\xc6\"\x00\xe0\n\x006\x91\xd9\xef\xb28\u061f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4_H?\xfb\x8fh\n\xed\xf2\xa3\x8fx3\xaf\xdc\xdeY\xb6\x1eK\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94_J\xceL\x1c\xc13\x91\xe0\x1f\x00\xb1\x98\xe1\xf2\v_\x91\xcb\xf5\x8a\x01\x0f\x0f\xa8\xb9\u04c1\x1a\x00\x00\xe0\x94_R\x12\x82\xe9\xb2x\u070c\x03Lr\xafS\xee)\xe5D=x\x8a\x01as-/\x8f:\xe0\x00\x00\u07d4_h\xa2L~\xb4\x11vgs{39?\xb3\xc2\x14\x8aS\xb6\x89\x02\xce\u0791\x8dE<\x00\x00\u07d4_p\x8e\xaf9\xd8#\x94lQ\xb3\xa3\u9df3\xc0\x03\xe2cA\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4_t.H~:\xb8\x1a\xf2\xf9J\xfd\xbe\x1b\x9b\x8f\\\u0301\xbc\x89u\xc4E\xd4\x11c\xe6\x00\x00\u07d4_t\xed\x0e$\xff\x80\u0672\u0124K\xaa\x99uB\x8c\u05b95\x89\xa1\x8b\xce\xc3H\x88\x10\x00\x00\u07d4_v\xf0\xa3\x06&\x9cx0k=e\r\xc3\xe9\xc3p\x84\xdba\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4_w\xa1\a\xab\x12&\xb3\xf9_\x10\ue0ee\xfcl]\xff>\u0709\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4_{;\xba\xc1m\xab\x83\x1aJ\x0f\xc5;\fT\x9d\xc3l1\u0289i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94_\x93\xff\x83't\xdbQ\x14\xc5[\xb4\xbfD\xcc\U000f53d0?\x8a(\xa9\xc9\x1a&4X)\x00\x00\u07d4_\x96\x16\xc4{Jg\xf4\x06\xb9Z\x14\xfeo\xc2h9o\x17!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4_\x98\x109\xfc\xf5\x02%\xe2\xad\xf7bu!\x12\xd1\xcc&\xb6\xe3\x89\x1b\x1aAj!S\xa5\x00\x00\u07d4_\x99\u070eI\xe6\x1dW\xda\xef`j\xcd\xd9\x1bMp\a2j\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4_\xa6\x1f\x15-\xe6\x125\x16\xc7Q$)y(_yj\u01d1\x89\v\x0f\x11\x97)c\xb0\x00\x00\u07d4_\xa7\xbf\xe0C\x88a'\xd4\x01\x1d\x83V\xa4~\x94yc\xac\xa8\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94_\xa8\xa5Nh\x17lO\xe2\xc0\x1c\xf6q\xc5\x15\xbf\xbd\xd5(\xa8\x8aE\xe1U\xfa\x01\x10\xfa@\x00\x00\u07d4_\xad\x96\x0fk,\x84V\x9c\x9fMG\xbf\x19\x85\xfc\xb2\xc6]\xa6\x8965f3\xeb\xd8\xea\x00\x00\u07d4_\xc6\xc1\x14&\xb4\xa1\xea\xe7\xe5\x1d\xd5\x12\xad\x10\x90\xc6\xf1\xa8[\x89\x93\xfe\\W\xd7\x10h\x00\x00\u07d4_\u0344Th\x96\xdd\b\x1d\xb1\xa3 \xbdM\x8c\x1d\xd1R\x8cL\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4_\u0368G\xaa\xf8\xd7\xfa\x8b\xca\b\x02\x9c\xa2\x84\x91f\xaa\x15\xa3\x89!\u02b8\x12Y\xa3\xbf\x00\x00\u07d4_\xd1\xc3\xe3\x17x'l\xb4.\xa7@\xf5\xea\xe9\xc6A\xdb\xc7\x01\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4_\xd3\xd6w~\xc2b\n\xe8:\x05R\x8e\xd4%\a-<\xa8\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_\xd9s\xaf6j\xa5\x15|Te\x9b\u03f2|\xbf\xa5\xac\x15\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4_\xe7w\x03\x80\x8f\x82>l9\x93R\x10\x8b\xdb,R|\xb8|\x89j@v\xcfy\x95\xa0\x00\x00\xe0\x94_\xecI\xc6e\xe6N\xe8\x9d\xd4A\xeet\x05n\x1f\x01\xe9(p\x8a\x01V\x9b\x9es4t\xc0\x00\x00\u07d4_\xf3&\xcd`\xfd\x13k$^)\xe9\bzj\u04e6R\u007f\r\x89e\xea=\xb7UF`\x00\x00\u07d4_\xf9=\xe6\xee\x05L\xadE\x9b-^\xb0\xf6\x87\x03\x89\xdf\xcbt\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\u07d4`\x06\xe3m\x92\x9b\xf4]\x8f\x16#\x1b\x12j\x01\x1a\xe2\x83\xd9%\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4`!\xe8Z\x88\x14\xfc\xe1\xe8*A\xab\xd1\u04f2\xda\xd2\xfa\xef\xe0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4`8t\n\xe2\x8df\xba\x93\xb0\xbe\bH+2\x05\xa0\xf7\xa0{\x89\x11!a\x85\u009fp\x00\x00\u07d4`?/\xabz\xfbn\x01{\x94v`i\xa4\xb4;8\x96I#\x89Y\xd2\xdb$\x14\u0699\x00\x00\u07d4`B'm\xf2\x98?\xe2\xbcGY\xdc\x19C\xe1\x8f\xdb\xc3Ow\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4`B\xc6D\xba\xe2\xb9o%\xf9M1\xf6x\xc9\r\xc9f\x90\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4`L\xdf\x18b\x8d\xbf\xa82\x91\x94\xd4x\xddR\x01\xee\xccK\xe7\x89\x01?0j$\t\xfc\x00\x00\u07d4`N\x94w\xeb\xf4r|t[\u02bb\xed\xcbl\xcf)\x99@\"\x8966\x9e\xd7t}&\x00\x00\u07d4`gm\x1f\xa2\x1f\xca\x05\"\x97\xe2K\xf9c\x89\u0171*p\u05c9\r\x17|Zzh\xd6\x00\x00\u07d4`gn\x92\u044b\x00\x05\t\xc6\x1d\xe5@\xe6\xc5\u0776v\xd5\t\x89A\rXj \xa4\xc0\x00\x00\u07d4`o\x17q!\xf7\x85\\!\xa5\x06#0\xc8v\"d\xa9{1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4`\x86B6\x93\r\x04\xd8@+]\xcb\xeb\x80\u007f<\xafa\x1e\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4`\xabq\xcd&\xeamnY\xa7\xa0\xf6'\xee\a\x9c\x88^\xbb\xf6\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4`\xaf\x0e\xe1\x18D<\x9b7\xd2\xfe\xadw\xf5\xe5!\u07be\x15s\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4`\xb3X\xcb=\xbe\xfa7\xf4}\xf2\xd76X@\u068e;\u024c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4`\xb8\u05b7;ySO\xb0\x8b\xb8\xcb\xce\xfa\xc7\xf3\x93\xc5{\xfe\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4`\xbeo\x95?*M%\xb6%o\xfd$#\xac\x148%.N\x89\b!\xab\rD\x14\x98\x00\x00\u0794`\xc3qO\xdd\xdbcFY\u48b1\xeaB\xc4r\x8c\u01f8\xba\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4`\xcc=D^\xbd\xf7j}z\xe5q\u0197\x1d\xffh\u0305\x85\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94`\xd5fq@\xd1&\x14\xb2\x1c\x8e^\x8a3\b.2\xdf\xcf#\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4`\xde\"\xa1Pt2\xa4{\x01\xcch\xc5*\v\xf8\xa2\xe0\u0418\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4`\xe0\xbd\u0422Y\xbb\x9c\xb0\x9d?7\xe5\u034b\x9d\xac\uafca\x89JD\x91\xbdm\xcd(\x00\x00\u07d4`\xe3\xccC\xbc\xdb\x02j\xadu\x9cpf\xf5U\xbb\xf2\xacf\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\x04+\x80\xfd`\x95\u0478{\xe2\xf0\x0f\x10\x9f\xab\xaf\xd1W\xa6\x89\x05k\xc7^-c\x10\x00\x00\u07d4a\a\xd7\x1d\xd6\xd0\xee\xfb\x11\xd4\xc9\x16@L\xb9\x8cu>\x11}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\x0f\xd6\xeeN\xeb\xab\x10\xa8\xc5]\vK\xd2\xe7\xd6\xef\x81qV\x89\x01\x15\x95a\x06]]\x00\x00\u07d4a\x14\xb0\xea\xe5Wi\x03\xf8\v\xfb\x98\x84-$\xed\x92#\u007f\x1e\x89\x05k\xc7^-c\x10\x00\x00\u07d4a!\xaf9\x8a[-\xa6\x9fe\xc68\x1a\xec\x88\u039c\xc6D\x1f\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4a&g\xf1r\x13[\x95\v,\xd1\xde\x10\xaf\xde\xcehW\xb8s\x8965\u026d\xc5\u07a0\x00\x00\u07d4a,\xed\x8d\xc0\u071e\x89\x9e\xe4oyb33\x15\xf3\xf5^D\x89\x12^5\xf9\xcd=\x9b\x00\x00\u07d4a4\xd9B\xf07\xf2\xcc=BJ#\f`=g\xab\xd3\xed\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a:\xc5;\xe5e\xd4e6\xb8 q[\x9b\x8d:\xe6\x8aK\x95\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4a?\xabD\xb1k\xbeUMD\xaf\xd1x\xab\x1d\x02\xf3z\ua949lk\x93[\x8b\xbd@\x00\x00\u07d4aN\x8b\xef=\xd2\u015bY\xa4\x14Vt@\x10\x185\x18\x84\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4aQ\x84d\xfd\u0637<\x1b\xb6\xacm\xb6\x00eI8\xdb\xf1z\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4aT}7nSi\xbc\xf9x\xfc\x16,<V\xaeE5G\xe8\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4aX\xe1\a\xc5\xebT\xcbv\x04\xe0\u034d\xc1\xe0u\x00\xd9\x1c<\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94aZo6w\u007f@\xd6a~\xb5\x81\x98\x96\x18i\x83\xfd71\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4a_\x826\\Q\x01\xf0q\xe7\xd2\xcbj\xf1Oz\xad,\x16\u0189\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4ap\xdd\x06\x87\xbdU\u0288\xb8z\xde\xf5\x1c\xfd\xc5\\M\xd4X\x89l\xb3/\\4\xfeD\x00\x00\u07d4as9G\xfa\xb8 \xdb\xd3Q\xef\xd6xU\xea\x0e\x88\x13s\xa0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4ay\x97\x99\a\xfe\u007f\x03~L8\x02\x9d`\xbc\xba\xb82\xb3\u0589WG=\x05\u06ba\xe8\x00\x00\xe0\x94a\u007f \x89O\xa7\x0e\x94\xa8jI\xcdt\xe028\xf6M<\u064a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4a\u007f\xf2\u0300>1\xc9\b\"3\xb8%\xd0%\xbe?{\x10V\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4a\x91\xdd\u0276J\x8e\b\x90\xb427\t\u05e0|H\xb9*d\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4a\x96\xc3\xd3\xc0\x90\x8d%Cf\xb7\xbc\xa5WE\"-\x9dM\xb1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4a\x9f\x17\x14E\xd4+\x02\xe2\xe0p\x04\xad\x8a\xfeiO\xa5=j\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4a\xad\xf5\x92\x9a^)\x81hN\xa2C\xba\xa0\x1f}\x1f^\x14\x8a\x89\x05\xfa\xbfl\x98O#\x00\x00\u07d4a\xb1\xb8\xc0\x12\xcdLx\xf6\x98\xe4p\xf9\x02V\xe6\xa3\x0fH\u0749\n\u05ce\xbcZ\xc6 \x00\x00\u07d4a\xb3\xdf.\x9e\x9f\xd9h\x13\x1f\x1e\x88\xf0\xa0\xeb[\xd7eFM\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4a\xb9\x02\u0166s\x88X&\x82\r\x1f\xe1EI\xe4\x86_\xbd\u0089\x12$\xef\xed*\u1440\x00\u07d4a\xb9\x05\xdef?\xc1s\x86R;:(\xe2\xf7\xd07\xa6U\u0349\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4a\xba\x87\xc7~\x9bYm\xe7\xba\x0e2o\xdd\xfe\xec!c\xeff\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4a\xbf\x84\u056b\x02oX\xc8s\xf8o\xf0\xdf\u0282\xb5W3\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\xc4\xee|\x86LMk^7\xea\x131\xc2\x03s\x9e\x82k/\x89\x01\xa15;8*\x91\x80\x00\u07d4a\xc80\xf1eG\x18\xf0u\u032b\xa3\x16\xfa\xac\xb8[}\x12\v\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4a\xc8\xf1\xfaC\xbf\x84i\x99\xec\xf4{+2M\xfbkc\xfe:\x89+^:\xf1k\x18\x80\x00\x00\u07d4a\xc9\xdc\u8c98\x1c\xb4\x0e\x98\xb0@+\xc3\xeb(4\x8f\x03\xac\x89\n\xac\xac\u0679\xe2+\x00\x00\u07d4a\u03a7\x1f\xa4d\xd6*\a\x06?\x92\v\f\xc9\x17S\x973\u0609Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4a\xd1\x01\xa03\xee\x0e.\xbb1\x00\xed\xe7f\xdf\x1a\xd0$IT\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4a\xedU\x96\u0197 \u007f=U\xb2\xa5\x1a\xa7\xd5\x0f\a\xfa\t\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\xff\x8eg\xb3M\x9e\xe6\xf7\x8e\xb3o\xfe\xa1\xb9\xf7\xc1W\x87\xaf\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4b\x05\xc2\xd5dtp\x84\x8a8@\xf3\x88~\x9b\x01]4u\\\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4b(\xad\xe9^\x8b\xb1}\x1a\xe2;\xfb\x05\x18AMI~\x0e\xb8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94b)\xdc\xc2\x03\xb1\xed\xcc\xfd\xf0n\x87\x91\fE*\x1fMzr\x8a\x06\xe1\xd4\x1a\x8f\x9e\xc3P\x00\x00\u0794b+\xe4\xb4T\x95\xfc\xd91C\xef\xc4\x12\u0599\xd6\xcd\xc2=\u0148\xf0\x15\xf2W6B\x00\x00\u07d4b3\x1d\xf2\xa3\xcb\xee5 \xe9\x11\u07a9\xf7>\x90_\x89%\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4bVD\xc9Z\x87>\xf8\xc0l\u06de\x9fm\x8dv\x80\x04=b\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4be\xb2\xe7s\x0f6\xb7v\xb5-\f\x9d\x02\xad\xa5]\x8e<\xb6\x8965\u026d\xc5\u07a0\x00\x00\u07d4bh\n\x15\xf8\u0338\xbd\xc0/s`\xc2Z\xd8\u03f5{\x8c\u034965\u026d\xc5\u07a0\x00\x00\u07d4b\x94\xea\xe6\xe4 \xa3\xd5`\n9\xc4\x14\x1f\x83\x8f\xf8\xe7\xccH\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4b\x97\x1b\xf2cL\xee\v\xe3\u0249\x0fQ\xa5`\x99\u06f9Q\x9b\x89#\x8f\xd4,\\\xf0@\x00\x00\u07d4b\x9b\xe7\xab\x12jS\x98\xed\xd6\u069f\x18D~x\u0192\xa4\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4b\xb4\xa9\"nah<r\xc1\x83%F\x90\xda\xf5\x11\xb4\x11z\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4b\xb9\b\x1ew\x104^8\xe0.\x16D\x9a\xce\x1b\x85\xbc\xfcN\x891T\xc9r\x9d\x05x\x00\x00\u07d4b\xc3|R\xb9\u007fK\x04\v\x1a\xa3\x91\xd6\xde\xc1R\x89<G\a\x8965\u026d\xc5\u07a0\x00\x00\u07d4b\u0272q\xff\u0577p\xa5\xee\xe4\xed\xc9x{\\\xdcp\x97\x14\x89lk\x93[\x8b\xbd@\x00\x00\u07d4b\xd5\xccq\x17\xe1\x85\x00\xac/\x9e<&\xc8k\n\x94\xb0\xde\x15\x89\x05\xb1*\ufbe8\x04\x00\x00\u07d4b\xdcrr\x90$7_\xc3|\xbb\x9c|#\x93\xd1\x0233\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4b\xe6\xb2\xf5\xeb\x94\xfazC\x83\x1f\xc8~%J?\u3fcf\x89\x89\r\x8drkqw\xa8\x00\x00\u07d4b\xf2\xe5\xcc\xec\xd5,\u0139^\x05\x97\xdf'\xcc\a\x97\x15`\x8c\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94b\xfb\x8b\xd1\xf0\xe6k\x90S>\a\x1el\xbea\x11\xfe\xf0\xbcc\x8a\x03\xba\x19\x10\xbf4\x1b\x00\x00\x00\xe0\x94c\n\x91:\x901\xc9I*\xbdLA\u06f1PT\xcf\xecD\x16\x8a\x014X\xdbg\xaf5\xe0\x00\x00\xe0\x94c\fRs\x12mQ|\xe6q\x01\x81\x1c\xab\x16\xb8SL\xf9\xa8\x8a\x01\xfe\xcc\xc6%s\xbb\u04c0\x00\u07d4c\x100\xa5\xb2{\a(\x8aEio\x18\x9e\x11\x14\xf1*\x81\xc0\x89\x1b\x1azB\v\xa0\r\x00\x00\u07d4c\x10\xb0 \xfd\x98\x04IW\x99P\x92\t\x0f\x17\xf0NR\xcd\xfd\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4c+\x91I\xd7\x01x\xa7364'^\x82\u0555?'\x96{\x89%\xf2s\x93=\xb5p\x00\x00\u07d4c,\xec\xb1\f\xfc\xf3\x8e\u0246\xb4;\x87p\xad\xec\xe9 \x02!\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4c1\x02\x8c\xbbZ!H[\xc5\x1bVQB\x99;\xdb%\x82\xa9\x89\x1c\xfd\xd7F\x82\x16\xe8\x00\x00\u07d4c3O\xcf\x17E\x84\x0eK\tJ;\xb4\v\xb7o\x96\x04\xc0L\x89\u05e5\xd7\x03\xa7\x17\xe8\x00\x00\u07d4c4\nWqk\xfac\xebl\xd13r\x12\x02W[\xf7\x96\xf0\x89\va\xe0\xa2\f\x12q\x80\x00\u07d4cN\xfc$7\x11\a\xb4\xcb\xf0?y\xa9=\xfd\x93\xe41\xd5\xfd\x89B5\x82\xe0\x8e\xdc\\\x80\x00\xe0\x94c\\\x00\xfd\xf05\xbc\xa1_\xa3a\r\xf38N\x0f\xb7\x90h\xb1\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4ca.xb\xc2{X|\xfbm\xaf\x99\x12\xcb\x05\x1f\x03\n\x9f\x89\x02[\x19\u053f\xe8\xed\x00\x00\u07d4cfgU\xbdA\xb5\x98i\x97x<\x13\x040\b$+<\xb5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4c{\xe7\x1b:\xa8\x15\xffE=VB\xf70tE\vd\xc8*\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94c}g\xd8\u007fXo\nZG\x9e \xee\x13\xea1\n\x10\xb6G\x8a\n:Y&\xaf\xa1\xe70\x00\x00\u07d4c\u007fXi\xd6\xe4i_\x0e\xb9\xe2s\x11\u0107\x8a\xff33\x80\x89j\xc0Nh\xaa\xec\x86\x00\x00\u07d4c\x97|\xad}\r\xcd\xc5+\x9a\xc9\xf2\xff\xa16\xe8d(\x82\xb8\x89\x04\x10\u0546\xa2\nL\x00\x00\u07d4c\xa6\x1d\xc3\n\x8e;0\xa7c\xc4!<\x80\x1c\xbf\x98s\x81x\x8965\u026d\xc5\u07a0\x00\x00\u07d4c\xacT\\\x99\x12C\xfa\x18\xae\xc4\x1dOoY\x8eUP\x15\u0709 \x86\xac5\x10R`\x00\x00\u07d4c\xb9uMu\xd1-8@9\xeci\x06<\v\xe2\x10\xd5\xe0\u3252\v\x86\f\xc8\xec\xfd\x80\x00\u07d4c\xbbfO\x91\x17\x03v(YM\xa7\xe3\xc5\b\x9f\xd6\x18\xb5\xb5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4c\u00a3\xd25\xe5\xee\xab\xd0\u0526\xaf\u06c9\xd9F'9d\x95\x89CN\xf0[\x9d\x84\x82\x00\x00\u07d4c\xc8\xdf\xde\v\x8e\x01\xda\xdc.t\x8c\x82L\xc06\x9d\U00010cc9\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4c\xd5Z\u065b\x917\xfd\x1b \xcc+O\x03\xd4,\xba\xdd\xf34\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4c\xd8\x00H\x87u\x96\xe0\u0084\x89\xe6P\xcdJ\xc1\x80\tjI\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94c\xe4\x14`>\x80\xd4\xe5\xa0\xf5\xc1\x87t FB%\x82\b\xe4\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94c\xe8\x8e.S\x9f\xfbE\x03\x86\xb4\xe4g\x89\xb2#\xf5GlE\x8a\x01U\x17\nw\x8e%\xd0\x00\x00\u07d4c\xef/\xbc=\xaf^\xda\xf4\xa2\x95b\x9c\xcf1\xbc\xdf@8\xe5\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4c\xf0\xe5\xa7R\xf7\x9fg\x12N\xedc:\xd3\xfd'\x05\xa3\x97\u0509\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94c\xf5\xb5=y\xbf.A\x14\x89Re0\"8E\xfa\xc6\xf6\x01\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4c\xfc\x93\x00\x13\x05\xad\xfb\u0278])\xd9)\x1a\x05\xf8\xf1A\v\x8965\u026d\xc5\u07a0\x00\x00\u0794c\xfek\xccK\x8a\x98P\xab\xbeu\x8070\xc92%\x1f\x14[\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4d\x03\xd0bT\x96\x90\xc8\xe8\xb6>\xaeA\xd6\xc1\tGn%\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4d\x04+\xa6\x8b\x12\xd4\xc1Qe\x1c\xa2\x81;sR\xbdV\xf0\x8e\x89 \x86\xac5\x10R`\x00\x00\u0794d\x05\xdd\x13\xe9:\xbc\xff7~p\x0e<\x1a\x00\x86\xec\xa2})\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94d\n\xbam\xe9\x84\xd9E\x177x\x03p^\xae\xa7\t_J\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4d\v\xf8t\x15\xe0\xcf@s\x01\xe5Y\x9ah6m\xa0\x9b\xba\u0209\x1a\xbc\x9fA`\x98\x15\x80\x00\u07d4d \xf8\xbc\xc8\x16JaR\xa9\x9dk\x99i0\x05\xcc\xf7\xe0S\x8965f3\xeb\xd8\xea\x00\x00\u07d4d$\x1axD)\x0e\n\xb8U\xf1\u052au\xb5SE\x03\"$\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4d&J\xed\xd5-\xca\xe9\x18\xa0\x12\xfb\xcd\f\x03\x0e\xe6\xf7\x18!\x8965\u026d\xc5\u07a0\x00\x00\u07d4d7\x0e\x87 &E\x12Z5\xb2\a\xaf\x121\xfb`r\xf9\xa7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4d=\x9a\xee\u0531\x80\x94~\u04b9 |\xceL=\xdcU\xe1\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4dC\xb8\xaec\x9d\xe9\x1c\xf7<Z\xe7c\xee\xee\xd3\u077b\x92S\x89lk\x93[\x8b\xbd@\x00\x00\u07d4dE\u007f\xa3;\b2PlO}\x11\x80\xdc\xe4\x8fF\xf3\xe0\xff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4dFJh\x05\xb4bA*\x90\x1d-\xb8\x17K\x06\xc2-\ue989\x19\xc8F\xa0)\xc7\xc8\x00\x00\u07d4dK\xa6\xc6\x10\x82\xe9\x89\x10\x9f\\\x11\u0534\x0e\x99\x16`\xd4\x03\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4db\x8co\xb8\xect:\xdb\xd8|\xe5\xe0\x18\xd51\xd9!\x047\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4dc\xf7\x15\u0554\xa1\xa4\xac\u4edc;(\x8at\xde\xcf)M\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4df(\xa5<,A\x93\u06885\x9c\xe7\x18\xda\u0752\xb7\xa4\x8d\x89\n\xd8\x00l/^\xf0\x00\x00\u07d4dg-\xa3\xab\x05(!\xa0$=\x1c\xe4\xb6\xe0\xa3e\x17\xb8\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4dj\xfb\xa7\x1d\x84\x9e\x80\xc0\xedY\xca\xc5\x19\xb2x\xe7\xf7\xab\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4dn\x04=\x05\x97\xa6d\x94\x8f\xbb\r\xc1Tu\xa3\xa4\xf3\xa6\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4dp\xa4\xf9.\u01b0\xfc\xcd\x01#O\xa5\x90#\xe9\xff\x1f:\xac\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4d{\x85\x04M\xf2\xcf\vN\u0508.\x88\x81\x9f\xe2*\xe5\xf7\x93\x8966;]\x9awp\x00\x00\u07d4d\x85G\x0ea\xdb\x11\n\xeb\u06ef\xd56v\x9e<Y\x9c\xc9\b\x89 \x86\xac5\x10R`\x00\x00\xe0\x94d\x8f[\u04a2\xae\x89\x02\xdb7\x84}\x1c\xb0\u06d3\x90\xb0bH\x8a\x01\xa55\xec\xf0v\n\x04\x80\x00\xe0\x94d\x9a+\x98y\u034f\xb76\xe6p;\fwG\x84\x97\x96\xf1\x0f\x8a\x01\x8e\xe2-\xa0\x1a\xd3O\x00\x00\u07d4d\x9a\x85\xb96S\a_\xa6V,@\x9aV]\b{\xa3\u1e89lk\x93[\x8b\xbd@\x00\x00\u07d4d\xad\xcc\xee\xc5=\xd9\xd9\xdd\x15\xc8\xcc\x1a\x9esm\xe4$\x1d,\x89\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4d\xcf\t5\xbf\x19\xd2\u03bb\xec\xd8x\r'\xd2\xe2\xb2\xc3Af\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4d\xd8\f;\x8b\xa6\x82\x82)\vu\xe6]\x89x\xa1Z\x87x,\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94d\u06e2\xd6a[\x8b\xd7W\x186\xdcu\xbcy\xd3\x14\xf5\xec\xee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4d\xe0!z[8\xaa@X6%\x96\u007f\xa9\x886\x908\x8bo\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4d\xe0*\xbb\x01l\xc2:)4\xf6\xbc\u0776\x81\x90P!\xd5c\x8965\u026d\xc5\u07a0\x00\x00\u07d4d\xe0>\xf0p\xa5G\x03\xb7\x18NH'l\\\x00w\xefK4\x89\x11X\xe4`\x91=\x00\x00\x00\xe0\x94d\xe2\xde! \v\x18\x99\u00e0\xc0e;P@\x13m\r\xc8B\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4d\xec\x8a[t?4y\xe7\a\xda\xe9\xee \u076aO@\xf1\u0649\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e\x03\x86\v\x19\x10\b\xc1U\x83\xbf\u0201X\t\x93\x01v((\x8965\u026d\xc5\u07a0\x00\x00\u07d4e\x051\x911\x9e\x06z%\xe66\x1dG\xf3\u007fc\x18\xf84\x19\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4e\t;#\x9b\xbf\xba#\xc7w\\\xa7\xdaZ\x86H\xa9\xf5L\xf7\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4e\t\xee\xb14~\x84/\xfbA>7\x15^,\xbcs\x82s\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94e\vBUU\xe4\xe4\xc5\x17\x18\x14h6\xa2\xc1\xeew\xa5\xb4!\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4e\f\xf6}\xb0`\xcc\xe1uh\xd5\xf2\xa4#h|Idv\t\x89\x05k\xc7^-c\x10\x00\x00\u07d4e\x10\xdfB\xa5\x99\xbc\xb0\xa5\x19\u0329a\xb4\x88u\x9aogw\x89lk\x93[\x8b\xbd@\x00\x00\u07d4e6u\xb8B\xd7\u0634a\xf7\"\xb4\x11|\xb8\x1d\xac\x8ec\x9d\x89\x01\xae6\x1f\xc1E\x1c\x00\x00\u07d4eK~\x80\x87\x99\xa8=r\x87\xc6w\x06\xf2\xab\xf4\x9aId\x04\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94eORHG\xb3\xa6\xac\xc0\xd3\xd5\xf1\xf3b\xb6\x03\xed\xf6_\x96\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4eY4\u068etN\xaa=\xe3M\xbb\xc0\x89LN\xda\va\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e]\\\xd7H\x96)\xe2A<!\x05\xb5\xa1r\xd93\xc2z\xf8\x89\xdb\x03\x18l\xd8@\xa6\x00\x00\u07d4e`\x18XA0\u06c3\xab\x05\x91\xa8\x12\x8d\x93\x81fj\x8d\x0e\x89\x03w\x9f\x91 \x19\xfc\x00\x00\u07d4e`\x94\x13(\xffX|\xbcV\u00ccx#\x8a{\xb5\xf4B\xf6\x89(a\x90kY\xc4z\x00\x00\u07d4eey\xda\xed\u0493p\u06777\xee?\\\xd9\xd8K\u00b3B\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4etswOc\xac=by\xfd\aC\xd5y\fO\x16\x15\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e\x80\xb1\xbc\x949\x0f\x04\xb3\x97\xbds\xe9]\x96\xef\x11\xea\xf3\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4e\x84\x9b\xe1\xaf \x10\x0e\xb8\xa3\xbaZ[\xe4\u04ee\x8d\xb5\xa7\x0e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4e\x9c\nr\xc7g\xa3\xa6\\\xed\x0e\x1c\xa8\x85\xa4\xc5\x1f\u0677y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4e\xa5!A\xf5k\uf619\x17$\xc6\xe7\x053\x81\u068bY%\x89\x03B\x9c3]W\xfe\x00\x00\u07d4e\xa9\xda\xd4.\x162\xba>NIb?\xabb\xa1~M6\x11\x89\x05\fL\xb2\xa1\f`\x00\x00\u07d4e\xaf\x8d\x8b[\x1d\x1e\xed\xfaw\xbc\xbc\x96\xc1\xb13\xf83\x06\u07c9\x05P\x05\xf0\xc6\x14H\x00\x00\u07d4e\xaf\x90\x87\xe0QgqT\x97\u0265\xa7I\x18\x94\x89\x00M\xef\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u0794e\xb4/\xae\xcc\x1e\u07f1B\x83\u0297\x9a\xf5E\xf6;0\xe6\f\x88\xfc\x93c\x92\x80\x1c\x00\x00\u0794e\xd3>\xb3\x9c\xdadS\xb1\x9ea\xc1\xfeM\xb91p\xef\x9d4\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4e\xd8\xddN%\x1c\xbc\x02\x1f\x05\xb0\x10\xf2\xd5\xdcR\f8r\xe0\x89-CW\x9a6\xa9\x0e\x00\x00\u07d4e\xea&\xea\xbb\xe2\xf6L\xcc\xcf\xe0h)\xc2]F7R\x02%\x89%\xf2s\x93=\xb5p\x00\x00\u07d4e\xeag\xad?\xb5j\xd5\xfb\x948}\u04ce\xb3\x83\x00\x1d|h\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94e\xeb\xae\xd2~\u06dd\xcc\x19W\xae\xe5\xf4R\xac!\x05\xa6\\\x0e\x8a\t7\u07ed\xae%\u26c0\x00\u07d4e\xee \xb0m\x9a\u0549\xa7\xe7\xce\x04\xb9\xf5\xf7\x95\xf4\x02\xae\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4e\xf544m/\xfbx\u007f\xa9\xcf\x18]t[\xa4)\x86\xbdn\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94e\xf5\x87\x0f&\xbc\xe0\x89g}\xfc#\xb5\x00\x1e\xe4\x92H4(\x8a\x01\x12\xb1\xf1U\xaa2\xa3\x00\x00\u07d4e\xfd\x02\xd7\x04\xa1*M\xac\xe9G\x1b\x06E\xf9b\xa8\x96q\u0209\x01\x8d\x1c\xe6\xe4'\u0340\x00\u07d4e\xff\x87O\xaf\xceM\xa3\x18\xd6\xc9=W\xe2\u00ca\rs\xe8 \x8968\x02\x1c\xec\u06b0\x00\x00\xe0\x94f\x05W\xbbC\xf4\xbe:\x1b\x8b\x85\xe7\xdf{<[\xcdT\x80W\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4f\b,u\xa8\xde1\xa59\x13\xbb\xd4M\xe3\xa07O\u007f\xaaA\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4f\x11\xceY\xa9\x8b\a*\xe9Y\xdcI\xadQ\x1d\xaa\xaa\xa1\x9dk\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4f \x1b\xd2'\xaem\u01bd\xfe\xd5\xfb\u0781\x1f\xec\xfe^\x9d\u0649 >\x9e\x84\x92x\x8c\x00\x00\u07d4f#4\x81G$\x93[y1\xdd\xcaa\x00\xe0\rFw'\u0349\"\x88&\x9d\a\x83\xd4\x00\x00\u07d4f'O\xea\x82\xcd0\xb6\u009b#5\x0eOO=1\nX\x99\x89p7\x05P\xab\x82\x98\x00\x00\u07d4f,\xfa\x03\x8f\xab7\xa0\x17E\xa3d\u1e41'\xc5\x03tm\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4f5\xb4oq\x1d-\xa6\xf0\xe1cp\u034e\xe4>\xfb,-R\x89lk\x93[\x8b\xbd@\x00\x00\u07d4f6\x04\xb0P0F\xe6$\xcd&\xa8\xb6\xfbGB\xdc\xe0*o\x89\x03\x8b\x9by~\xf6\x8c\x00\x00\u07d4f6\u05ecczH\xf6\x1d8\xb1L\xfdHe\xd3m\x14(\x05\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4f@\xcc\xf0SU\\\x13\n\xe2\xb6Vd~\xa6\xe3\x167\xb9\xab\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4fBK\xd8x[\x8c\xb4a\x10*\x90\x02\x83\xc3]\xfa\a\xefj\x89\x02.-\xb2ff\xfc\x80\x00\u07d4fL\xd6}\xcc\u026c\x82(\xb4\\U\u06cdvU\ve\x9c\u0709\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4fNC\x11\x98p\xaf\x10zD\x8d\xb1'\x8b\x04H8\xff\u036f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4fQso\xb5\x9b\x91\xfe\xe9\xc9:\xa0\xbdn\xa2\xf7\xb2Pa\x80\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4f[\x00\x0f\vw'P\xcc<!z^\xf4)\xa9+\xf1\u033b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4ff \x06\x01\\\x1f\x8e<\xcf\xca\xeb\xc8\xeeh\a\xee\x19c\x03\x89\x1b\x1b:\x1a\xc2a\xec\x00\x00\u07d4fgF\xfb\x93\u0453\\Z<hNrP\x10\xc4\xfa\u0431\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4fkO7\xd5]c\xb7\xd0V\xb6\x15\xbbt\xc9k;\x01\x99\x1a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4fq\x9c\x06\x82\xb2\xac\u007f\x9e'\xab\xeb\xec~\u07cd\xec\xf0\xae\r\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4fq\xb1\x82\xc9\xf7A\xa0\xcd<5ls\xc21&\xd4\xf9\xe6\xf4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4fy\xae\xec\xd8zW\xa7?3V\x81\x1d,\xf4\x9d\fM\x96\u0709 \x86\xac5\x10R`\x00\x00\u07d4f{a\xc0;\xb97\xa9\xf5\xd0\xfcZ\t\xf1\xea3c\xc7p5\x89\xe6d\x99\"\x88\xf2(\x00\x00\u07d4f\x85\xfd.%Dp,6\v\x8b\xb9\xeex\xf10\xda\xd1m\xa5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94f\x8bk\xa8\xab\b\xea\xce9\xc5\x02\xefg+\xd5\u0336\xa6z \x8a\x06\x97\xd9]B\x013<\x00\x00\u07d4f\x92]\xe3\xe4?KA\xbf\x9d\xad\xde'\xd5H\x8e\xf5i\xea\r\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4f\xb0\xc1\x00\u0111I\x93]\x14\xc0\xdc ,\u0390|\xea\x1a=\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4f\xb1\xa6=\xa4\xdc\xd9\xf8\x1f\xe5O^?\xcb@U\xef~\xc5O\x89\n\xeb'*\u07dc\xfa\x00\x00\u07d4f\xb3\x987\xcb<\xac\x8a\x80*\xfe?\x12\xa2X\xbb\xcab\xda\u0349\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94f\xc83\x1e\xfeq\x98\xe9\x8b-2\xb98h\x8e2A\xd0\xe2O\x8a\x02\t\x80Q\x97\x0e9\xd0\x00\x00\u07d4f\u030a\xb2<\x00\u0478*\xcd}s\xf3\x8c\x99\xe0\xd0ZO\xa6\x89\x05k\xc7^-c\x10\x00\x00\u07d4f\xdc\xc5\xfbN\xe7\xfe\xe0F\xe1A\x81\x9a\xa9hy\x9ddD\x91\x89Hz\x9a0E9D\x00\x00\u07d4f\xe0\x94'\xc1\xe6=\xee\xd7\xe1+\x8cU\xa6\xa1\x93 \xefKj\x89\t79SM(h\x00\x00\u07d4f\xec\x16\ue72a\xb4\x11\xc5Zf)\xe3\x18\xden\xe2\x16I\x1d\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4f\xf5\x04\x06\xeb\x1b\x11\xa9F\u02b4Y'\u0323tp\xe5\xa2\b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94f\xfd\xc9\xfe\xe3Q\xfa\x158\xeb\r\x87\xd8\x19\xfc\xf0\x9e|\x10j\x8a\x01F'\xb5\xd97\x81\xb2\x00\x00\u07d4g\x04\x8f:\x12\xa4\xdd\x1fbld&L\xb1\u05d7\x1d\xe2\xca8\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4g\x04\xf1i\xe0\u0433kW\xbb\u00df<EC{^\xe3\u048d\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4g\x10\x15\xb9vp\xb1\r^X?=b\xa6\x1c\x1cy\xc5\x14?\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94g\x10\xc2\xc0<e\x99+.wK\xe5-:\xb4\xa6\xba!~\xf7\x8a\x02t\xd6V\xac\x90\xe3@\x00\x00\u07d4g\x11\x10\xd9j\xaf\xf1\x15#\xccTk\xf9\x94\x0e\xed\xff\xb2\xfa\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4g\x15\xc1@5\xfbW\xbb=f\u007f{pt\x98\xc4\x10t\xb8U\x89%\xf2s\x93=\xb5p\x00\x00\u07d4g\x1b\xbc\xa0\x99\xff\x89\x9b\xab\a\xea\x1c\xf8ie\xc3\x05L\x89`\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4g'\xda\xf5\xb9\u058e\xfc\xabH\x9f\xed\xec\x96\xd7\xf72]\xd4#\x89lk\x93[\x8b\xbd@\x00\x00\u07d4g,\xbc\xa8D\n\x85w\t{\x19\xaf\xf5\x93\xa2\xad\x9d(\xa7V\x89\x04V9\x18$O@\x00\x00\xe0\x94g.\xc4/\xaa\x8c\u059a\xaaq\xb3,\u01f4\x04\x88\x1dR\xff\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94g/\xa0\xa0\x19\b\x8d\xb3\x16oa\x19C\x8d\a\xa9\x9f\x8b\xa2$\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\u07d4g1D\xf0\xec\x14.w\x0fH4\xfe\xe0\xee1\x182\xf3\b{\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4g5\vS1\x92o^(\xf3\xc1\xe9\x86\xf9dC\x80\x9c\x8b\x8c\x89\x13\x14\xfb7\x06)\x80\x00\x00\xe0\x94g7\x06\xb1\xb0\xe4\xdcz\x94\x9azybX\xa5\xb8;\xb5\xaa\x83\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4gB\xa2\xcf\u038dy\xa2\u0125\x1bwtt\x98\x91\"E\xcdj\x89\r\xfd[\x80\xb7\xe4h\x00\x00\u07d4gJ\xdb!\xdfL\x98\u01e3G\xacL<$&gW\xddp9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4gQ\x8e]\x02\xb2\x05\x18\x0f\x04c\xa3 \x04G\x1fu<R>\x89k\x91\x8a\xacIK\x16\x80\x00\xe0\x94g]\\\xaa`\x9b\xf7\n\x18\xac\xa5\x80F]\x8f\xb71\r\x1b\xbb\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4gc F\u0732ZT\x93i(\xa9oB?3 \xcb\ud489lk\x93[\x8b\xbd@\x00\x00\u07d4ge\xdf%(\x0e\x8eO8\u0531\xcfDo\xc5\xd7\xebe\x9e4\x89\x05k\xc7^-c\x10\x00\x00\u07d4gv\xe13\xd9\xdc5L\x12\xa9Q\b{c\x96P\xf59\xa43\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4g\x85Q<\xf72\xe4~\x87g\ap\xb5A\x9b\xe1\f\xd1\xfct\x89lk\x93[\x8b\xbd@\x00\x00\u07d4g\x947\xea\xcfCxx\xdc)=H\xa3\x9c\x87\xb7B\x1a!l\x89\x03\u007f\x81\x82\x1d\xb2h\x00\x00\u07d4g\x9b\x9a\x10\x990Q~\x89\x99\t\x9c\xcf*\x91LL\x8d\xd94\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4g\xa8\x0e\x01\x90r\x1f\x949\rh\x02r\x9d\xd1,1\xa8\x95\xad\x89lk\x13u\xbc\x91V\x00\x00\u07d4g\xb8\xa6\xe9\x0f\xdf\n\x1c\xacD\x17\x930\x1e\x87P\xa9\xfayW\x890\x84\x9e\xbe\x166\x9c\x00\x00\u07d4g\xbc\x85\xe8}\xc3LN\x80\xaa\xfa\x06k\xa8\u049d\xbb\x8eC\x8e\x89\x15\xd1\xcfAv\xae\xba\x00\x00\u07d4g\xc9&\t>\x9b\x89'\x938\x10\u0642\"\xd6.+\x82\x06\xbb\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4g\xcf\xdanp\xbfvW\u04d0Y\xb5\x97\x90\xe5\x14Z\xfd\xbea\x89#\x05\r\tXfX\x00\x00\u07d4g\u0582\xa2\x82\xefs\xfb\x8dn\x90q\xe2aOG\xab\x1d\x0f^\x8965\u026d\xc5\u07a0\x00\x00\u07d4g\u05a8\xaa\x1b\xf8\xd6\xea\xf78N\x99=\xfd\xf1\x0f\n\xf6\x8aa\x89\n\xbc\xbbW\x18\x97K\x80\x00\u07d4g\u0692.\xff\xa4r\xa6\xb1$\xe8N\xa8\xf8k$\xe0\xf5\x15\xaa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4g\xdf$-$\r\u0538\a\x1dr\xf8\xfc\xf3[\xb3\x80\x9dq\xe8\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4g\xee@n\xa4\xa7\xaej:8\x1e\xb4\xed\xd2\xf0\x9f\x17KI(\x898)c_\th\xb0\x00\x00\u07d4g\xf2\xbbx\xb8\xd3\xe1\x1f|E\x8a\x10\xb5\xc8\xe0\xa1\xd3tF}\x89a\t=|,m8\x00\x00\u07d4g\xfcR}\xce\x17\x85\xf0\xfb\x8b\xc7\xe5\x18\xb1\xc6i\xf7\xec\u07f5\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4h\x02}\x19U\x8e\xd73\x9a\b\xae\xe8\xde5Y\xbe\x06>\xc2\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4h\x06@\x83\x8b\xd0zD{\x16\x8dm\x92;\x90\xcflC\xcd\u0289]\u0212\xaa\x111\xc8\x00\x00\u07d4h\a\xdd\u020d\xb4\x89\xb03\xe6\xb2\xf9\xa8\x15SW\x1a\xb3\xc8\x05\x89\x01\x9f\x8euY\x92L\x00\x00\xe0\x94h\rY\x11\xed\x8d\xd9\xee\xc4\\\x06\f\"?\x89\xa7\xf6 \xbb\u054a\x04<3\xc1\x93ud\x80\x00\x00\u07d4h\x11\xb5L\u0456c\xb1\x1b\x94\xda\x1d\xe2D\x82\x85\u035fh\u0649;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4h\x19\f\xa8\x85\xdaB1\x87L\x1c\xfbB\xb1X\n!s\u007f8\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94h(\x97\xbcO\x8e\x89\x02\x91 \xfc\xff\xb7\x87\xc0\x1a\x93\xe6A\x84\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4h)^\x8e\xa5\xaf\xd9\t?\xc0\xa4e\xd1W\x92+]*\xe24\x89\x01\x15NS!}\xdb\x00\x00\u07d4h.\x96'oQ\x8d1\xd7\xe5n0\u07f0\t\xc1!\x82\x01\xbd\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4h5\xc8\xe8\xb7J,\xa2\xae?J\x8d\x0fk\x95J>*\x83\x92\x89\x03B\x9c3]W\xfe\x00\x00\u07d4h63\x01\n\x88hk\xeaZ\x98\xeaS\xe8y\x97\xcb\xf7>i\x89\x05k9Bc\xa4\f\x00\x00\u07d4h=\xba6\xf7\xe9O@\xeaj\xea\ry\xb8\xf5!\xdeU\an\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4hA\x9cm\xd2\xd3\xceo\u02f3\xc7>/\xa0y\xf0`Q\xbd\xe6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4hG;z}\x96Y\x04\xbe\u06e5V\u07fc\x17\x13l\xd5\xd44\x89\x05k\xc7^-c\x10\x00\x00\u07d4hG\x82[\xde\xe8$\x0e(\x04,\x83\xca\xd6B\U000868fd\u0709QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94hJD\xc0i3\x9d\b\xe1\x9auf\x8b\u06e3\x03\xbe\x85S2\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\u07d4hS\x1fM\u0680\x8fS vz\x03\x114(\xca\f\xe2\xf3\x89\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4hy'\xe3\x04\x8b\xb5\x16*\xe7\xc1\\\xf7k\xd1$\xf9I{\x9e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94h\x80\x9a\xf5\xd52\xa1\x1c\x1aMn2\xaa\xc7\\LR\xb0\x8e\xad\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4h\x86\xad\xa7\xbb\xb0a{\u0684!\x91\u018c\x92.\xa3\xa8\xac\x82\x89>\xe2;\xde\x0e} \x00\x00\xe0\x94h\x88>\x15.V`\xfe\xe5\x96&\xe7\xe3\xb4\xf0Q\x10\xe6\"/\x8a\v\x94c;\xe9u\xa6*\x00\x00\u07d4h\x8aV\x9e\x96U$\xeb\x1d\n\xc3\xd3s>\xab\x90\x9f\xb3\xd6\x1e\x89G\x8e\xae\x0eW\x1b\xa0\x00\x00\xe0\x94h\x8e\xb3\x85;\xbc\xc5\x0e\xcf\xee\x0f\xa8\u007f\n\xb6\x93\u02bd\xef\x02\x8a\x06\xb1\n\x18@\x06G\xc0\x00\x00\u07d4h\xa7B_\xe0\x9e\xb2\x8c\xf8n\xb1y>A\xb2\x11\xe5{\u058d\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4h\xa8l@#\x88\xfd\xdcY\x02\x8f\xecp!\u933f\x83\x0e\xac\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\xe0\x94h\xac\u06a9\xfb\x17\xd3\xc3\t\x91\x1aw\xb0_S\x91\xfa\x03N\xe9\x8a\x01\xe5.3l\xde\"\x18\x00\x00\u07d4h\xad\xdf\x01\x9dk\x9c\xabp\xac\xb1?\v1\x17\x99\x9f\x06.\x12\x89\x02\xb5\x12\x12\xe6\xb7\u0200\x00\u07d4h\xb3\x186\xa3\n\x01j\xda\x15{c\x8a\xc1]\xa7?\x18\xcf\u0789\x01h\u048e?\x00(\x00\x00\xe0\x94h\xb6\x85G\x88\xa7\xc6Il\xdb\xf5\xf8K\x9e\xc5\xef9+x\xbb\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4h\xc0\x84\x90\u021b\xf0\u05b6\xf3 \xb1\xac\xa9\\\x83\x12\xc0\x06\b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4h\xc7\xd1q\x1b\x01\x1a3\xf1o\x1fU\xb5\xc9\x02\xcc\xe9p\xbd\u05c9\b=lz\xabc`\x00\x00\u07d4h\xc8y\x1d\xc3B\xc3sv\x9e\xa6\x1f\xb7\xb5\x10\xf2Q\xd3 \x88\x8965\u026d\xc5\u07a0\x00\x00\u07d4h\u07d4|I[\ubbb8\u8273\xf9S\xd53\x87K\xf1\x06\x89\x1d\x99E\xab+\x03H\x00\x00\u07d4h\xe8\x02'@\xf4\xaf)\xebH\xdb2\xbc\xec\xdd\xfd\x14\x8d=\xe3\x8965\u026d\xc5\u07a0\x00\x00\u07d4h\xecy\u057eqUql@\x94\x1cy\u05cd\x17\u079e\xf8\x03\x89\x1b#8w\xb5 \x8c\x00\x00\u07d4h\xee\xc1\u222c1\xb6\xea\xba~\x1f\xbdO\x04\xadW\x9ak]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4h\xf5%\x92\x1d\xc1\x1c2\x9buO\xbf>R\x9f\xc7#\xc84\u0349WG=\x05\u06ba\xe8\x00\x00\u07d4h\xf7\x19\xae4+\xd7\xfe\xf1\x8a\x05\u02f0/pZ\u04ce\u0572\x898\xeb\xad\\\u0710(\x00\x00\xe0\x94h\xf7W<\xd4W\xe1L\x03\xfe\xa4>0-04|\x10p\\\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4h\xf8\xf4QU\xe9\x8cP)\xa4\xeb\u0175'\xa9.\x9f\xa81 \x89\xf0{D\xb4\a\x93 \x80\x00\u07d4h\xfe\x13W!\x8d\tXI\xcdW\x98B\u012a\x02\xff\x88\x8d\x93\x89lk\x93[\x8b\xbd@\x00\x00\u07d4i\x02(\xe4\xbb\x12\xa8\u0535\u09d7\xb0\xc5\xcf*u\t\x13\x1e\x89e\xea=\xb7UF`\x00\x00\u07d4i\x05\x94\xd3\x06a<\xd3\xe2\xfd$\xbc\xa9\x99J\u064a=s\xf8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94i\a2ir\x9ed\x14\xb2n\xc8\xdc\x0f\xd95\xc7;W\x9f\x1e\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x94i\x19\xdd^]\xfb\x1a\xfa@G\x03\xb9\xfa\xea\x8c\xee5\xd0\rp\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4i4\x92\xa5\xc5\x13\x96\xa4\x82\x88\x16i\xcc\xf6\xd8\xd7y\xf0\tQ\x89\x12\xbfPP:\xe3\x03\x80\x00\u07d4i=\x83\xbe\tE\x9e\xf89\v.0\xd7\xf7\u008d\xe4\xb4(N\x89lk\x93[\x8b\xbd@\x00\x00\u07d4iQp\x83\xe3\x03\xd4\xfb\xb6\xc2\x11E\x14!]i\xbcF\xa2\x99\x89\x05k\xc7^-c\x10\x00\x00\u07d4iUPel\xbf\x90\xb7]\x92\xad\x91\"\xd9\r#\xcah\xcaM\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94iX\xf8;\xb2\xfd\xfb'\xce\x04\t\xcd\x03\xf9\xc5\xed\xbfL\xbe\u074a\x04<3\xc1\x93ud\x80\x00\x00\u0794i[\x0fRBu7\x01\xb2d\xa6pq\xa2\u0708\b6\xb8\u06c8\u3601\x1b\xech\x00\x00\xe0\x94i[L\xce\bXV\xd9\xe1\xf9\xff>y\x94 #5\x9e_\xbc\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94if\x06:\xa5\xde\x1d\xb5\xc6q\xf3\xddi\x9dZ\xbe!>\xe9\x02\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4it\u0224\x14\u03ae\xfd<.M\xfd\xbe\xf40V\x8d\x9a\x96\v\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\xe0\x94iximQP\xa9\xa2cQ?\x8ft\u0196\xf8\xb19|\xab\x8a\x01g\xf4\x82\xd3\u0171\xc0\x00\x00\xe0\x94iy{\xfb\x12\u027e\u0582\xb9\x1f\xbcY5\x91\xd5\xe4\x027(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94i\u007fUSk\xf8Z\xdaQ\x84\x1f\x02\x87b:\x9f\x0e\u041a\x17\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4i\x82\xfe\x8a\x86~\x93\xebJ\v\xd0QX\x93\x99\xf2\xec\x9aR\x92\x89lk\x93[\x8b\xbd@\x00\x00\u07d4i\x8a\x8ao\x01\xf9\xabh/c|yi\xbe\x88_lS\x02\xbf\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4i\x8a\xb9\xa2\xf33\x81\xe0|\fGC=\r!\xd6\xf36\xb1'\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4i\x94\xfb21\xd7\xe4\x1dI\x1a\x9dh\xd1\xfaL\xae,\xc1Y`\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\x9c\x9e\xe4q\x95Q\x1f5\xf8b\xcaL\"\xfd5\xae\x8f\xfb\xf4\x89\x04V9\x18$O@\x00\x00\u07d4i\x9f\xc6\u058aGuW<\x1d\u036e\xc80\xfe\xfdP9|N\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4i\xaf(\xb0tl\xac\r\xa1p\x84\xb99\x8c^6\xbb:\r\xf2\x896w\x03n\xdf\n\xf6\x00\x00\u07d4i\xb8\x0e\xd9\x0f\x84\x83J\xfa?\xf8.\xb9dp;V\tw\u0589\x01s\x17\x90SM\xf2\x00\x00\xe0\x94i\xb8\x1dY\x81\x14\x1e\u01e7\x14\x10`\xdf\u03cf5\x99\xff\xc6>\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4i\xbc\xfc\x1dC\xb4\xba\x19\xde{'K\xdf\xfb5\x13\x94\x12\xd3\u05c95e\x9e\xf9?\x0f\xc4\x00\x00\u07d4i\xbd%\xad\xe1\xa34lY\xc4\xe90\xdb*\x9dq^\xf0\xa2z\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\xc0\x8dtGT\xdep\x9c\xe9n\x15\xae\r\x1d9[:\"c\x8965\u026d\xc5\u07a0\x00\x00\u07d4i\xc2\xd85\xf1>\xe9\x05\x80@\x8ej2\x83\xc8\u0326\xa44\xa2\x89#\x8f\xd4,\\\xf0@\x00\x00\u07d4i\xc9N\a\u0129\xbe3\x84\xd9]\xfa<\xb9)\x00Q\x87;{\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4i\xcb>!S\x99\x8d\x86\xe5\xee \xc1\xfc\u0466\xba\uec86?\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\u04ddQ\b\x89\xe5R\xa3\x96\x13[\xfc\xdb\x06\xe3~8v3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94i\u064f8\xa3\xba=\xbc\x01\xfa\\,\x14'\xd8b\x83//p\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94i\xe2\xe2\xe7\x040|\xcc[\\\xa3\xf1d\xfe\xce.\xa7\xb2\xe5\x12\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4i\xffB\x90t\u02dblc\xbc\x91B\x84\xbc\xe5\xf0\xc8\xfb\xf7\u0409\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4i\xff\x89\x01\xb5Av?\x81|_)\x98\xf0-\xcf\xc1\xdf)\x97\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4j\x02:\xf5}XM\x84^i\x876\xf10\u06dd\xb4\r\xfa\x9a\x89\x05[ \x1c\x89\x00\x98\x00\x00\u07d4j\x04\xf5\xd5?\xc0\xf5\x15\xbe\x94+\x8f\x12\xa9\xcbz\xb0\xf3\x97x\x89\xa9\xaa\xb3E\x9b\xe1\x94\x00\x00\u07d4j\x05\xb2\x1cO\x17\xf9\xd7?_\xb2\xb0\u02c9\xffSV\xa6\xcc~\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94j\x0f\x05`f\xc2\xd5f(\x85\x02s\xd7\xec\xb7\xf8\xe6\xe9\x12\x9e\x8a\x01\x0f\r)<\u01e5\x88\x00\x00\u07d4j\x13\xd5\xe3,\x1f\xd2m~\x91\xffn\x051`\xa8\x9b,\x8a\xad\x89\x02\xe6/ \xa6\x9b\xe4\x00\x00\u07d4j.\x86F\x9a[\xf3|\xee\x82\xe8\x8bL8c\x89](\xfc\xaf\x89\x1c\"\x92f8[\xbc\x00\x00\u07d4j6\x94BL|\u01b8\xbc\u067c\u02baT\f\xc1\xf5\xdf\x18\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94jB\u0297\x1cex\u056d\xe2\x95\xc3\xe7\xf4\xad3\x1d\xd3BN\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4jD\xaf\x96\xb3\xf02\xaed\x1b\xebg\xf4\xb6\xc83B\xd3|]\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4jL\x89\a\xb6\x00$\x80W\xb1\xe4cT\xb1\x9b\u0705\x9c\x99\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4jQNbB\xf6\xb6\x8c\x13~\x97\xfe\xa1\u73b5U\xa7\xe5\xf7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4jS\xd4\x1a\xe4\xa7R\xb2\x1a\xbe\xd57FI\x95:Q=\xe5\xe5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4jaY\aJ\xb5s\xe0\xeeX\x1f\x0f=\xf2\u05a5\x94b\x9bt\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4jc7\x83?\x8fjk\xf1\f\xa7\xec!\xaa\x81\x0e\xd4D\xf4\u02c97\xbd$4\\\xe8\xa4\x00\x00\u07d4jcS\xb9qX\x9f\x18\xf2\x95\\\xba(\xab\xe8\xac\xcejWa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4jc\xfc\x89\xab\xc7\xf3n(-\x80x{{\x04\xaf\xd6U>q\x89\b\xacr0H\x9e\x80\x00\x00\u07d4jg\x9e7\x8f\xdc\xe6\xbf\xd9\u007f\xe6/\x04<od\x05\u05de\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4jhk\xf2 \xb5\x93\u07b9\xb72F\x15\xfb\x91D\xde\xd3\xf3\x9d\x89O%\x91\xf8\x96\xa6P\x00\x00\xe0\x94jk\x18\xa4ZvF~.]Z.\xf9\x11\xc3\xe1))\x85{\x8a\x11]:\x99\xa9aO@\x00\x00\u07d4jt\x84M\x8e\x9c\xb5X\x1cE\a\x9a.\x94F*l\xee\x88!\x89:\xb5:U-\xd4\xc9\x00\x00\xe0\x94j{.\r\x88\x86\u007f\xf1] |\"+\xeb\xf9O\xa6\u0383\x97\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4j|% B\xe7F\x8a?\xf7s\xd6E\v\xba\x85\xef\xa2c\x91\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4j\x8aC\x17\xc4_\xaa\x05T\xcc\xdbH%H\x18>)Z$\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4j\x8c\xea-\xe8J\x8d\xf9\x97\xfd?\x84\xe3\b=\x93\xdeW\u0369\x89\x05k\xe0<\xa3\xe4}\x80\x00\xe0\x94j\x97Xt;`>\xea:\xa0RKB\x88\x97#\xc4\x159H\x8a\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4j\xa5s/;\x86\xfb\x8c\x81\xef\xbek[G\xb5cs\v\x06\u020965\u026d\xc5\u07a0\x00\x00\u07d4j\xb3#\xaePV\xed\nE0r\u016b\xe2\xe4/\xcf]q9\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4j\xb5\xb4\xc4\x1c\u0778)i\f/\xda\u007f \xc8^b\x9d\xd5\u0549d\u052fqL2\x90\x00\x00\u07d4j\xc4\x0fS-\xfe\xe5\x11\x81\x17\u04ad5-\xa7}Om\xa2\u0209\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4j\xc4\u053e-\xb0\u065d\xa3\xfa\xaa\xf7RZ\xf2\x82\x05\x1dj\x90\x89\x04X\xcaX\xa9b\xb2\x80\x00\u07d4j\xcd\u0723\xcd+I\x90\xe2\\\xd6\\$\x14\x9d\t\x12\t\x9ey\x89\xa2\xa1\xe0|\x9fl\x90\x80\x00\u07d4j\xd9\v\xe2R\xd9\xcdFM\x99\x81%\xfa\xb6\x93\x06\v\xa8\xe4)\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4j\u0753!\x93\xcd8IJ\xa3\xf0:\xec\xccKz\xb7\xfa\xbc\xa2\x89\x04\xdbs%Gc\x00\x00\x00\xe0\x94j\xe5\u007f'\x91|V*\x13*M\x1b\xf7\xec\n\u01c5\x83)&\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4j\xeb\x9ftt.\xa4\x91\x81=\xbb\xf0\xd6\xfc\xde\x1a\x13\x1dM\xb3\x89\x17\xe5T0\x8a\xa00\x00\x00\u07d4j\xf25\u04bb\xe0P\xe6)\x16\x15\xb7\x1c\xa5\x82\x96X\x81\x01B\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4j\xf6\xc7\xee\x99\xdf'\x1b\xa1[\xf3\x84\xc0\xb7d\xad\xcbM\xa1\x82\x8965f3\xeb\xd8\xea\x00\x00\u07d4j\xf8\xe5Yih,q_H\xadO\xc0\xfb\xb6~\xb5\x97\x95\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4j\xf9@\xf6>\u0278\xd8v'*\u0296\xfe\xf6\\\xda\xce\xcd\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4j\xf9\xf0\xdf\uebbb_d\xbf\x91\xabw\x16i\xbf\x05)US\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4j\xff\x14f\xc2b6u\xe3\xcb\x0eu\xe4#\xd3z%\xe4B\xeb\x89]\u0212\xaa\x111\xc8\x00\x00\xe0\x94k\r\xa2Z\xf2g\u05c3l\"k\xca\xe8\xd8r\xd2\xceR\xc9A\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4k\x10\xf8\xf8\xb3\xe3\xb6\r\xe9\n\xa1-\x15_\x9f\xf5\xff\xb2,P\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\x17Y\x8a\x8e\xf5Oyz\xe5\x15\u0336Q}\x18Y\xbf\x80\x11\x89\x05k\xc7^-c\x10\x00\x00\u07d4k \xc0\x80`jy\xc7;\xd8\xe7[\x11qzN\x8d\xb3\xf1\u00c9\x10?sX\x03\xf0\x14\x00\x00\u07d4k\"\x84D\x02!\xce\x16\xa88-\xe5\xff\x02)G\"i\xde\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4k0\xf1\x829\x10\xb8m:\xcbZj\xfc\x9d\xef\xb6\xf3\xa3\v\xf8\x89\u3bb5sr@\xa0\x00\x00\u07d4k8\u0784\x1f\xad\u007fS\xfe\x02\xda\x11[\xd8j\xaff$f\xbd\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4kK\x99\xcb?\xa9\xf7\xb7L\u3903\x17\xb1\xcd\x13\t\n\x1az\x89\x03\x1b2~i]\xe2\x00\x00\u07d4kZ\xe7\xbfx\xecu\xe9\f\xb5\x03\xc7x\xcc\u04f2KO\x1a\xaf\x89+^:\xf1k\x18\x80\x00\x00\u07d4kc\xa2\u07f2\xbc\xd0\xca\xec\x00\"\xb8\x8b\xe3\f\x14Q\xeaV\xaa\x89+\xdbk\xf9\x1f\u007fL\x80\x00\u07d4kew\xf3\x90\x9aMm\xe0\xf4\x11R-Ep8d\x004\\\x89e\xea=\xb7UF`\x00\x00\u07d4kr\xa8\xf0a\xcf\xe6\x99j\xd4G\xd3\xc7,(\xc0\xc0\x8a\xb3\xa7\x89\xe7\x8cj\u01d9\x12b\x00\x00\u07d4kv\rHw\xe6\xa6'\xc1\xc9g\xbe\xe4Q\xa8P}\xdd\u06eb\x891T\xc9r\x9d\x05x\x00\x00\u07d4k\x83\xba\xe7\xb5e$EXU[\xcfK\xa8\xda \x11\x89\x1c\x17\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\x92]\xd5\xd8\xeda2\xabm\b`\xb8,D\xe1\xa5\x1f\x1f\xee\x89P; >\x9f\xba \x00\x00\xe0\x94k\x94a]\xb7Pej\u00cc~\x1c\xf2\x9a\x9d\x13g\u007fN\x15\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4k\x95\x1aC'N\xea\xfc\x8a\t\x03\xb0\xaf.\xc9+\xf1\xef\xc89\x89\x05k\xc7^-c\x10\x00\x00\u07d4k\x99%!\xec\x85#p\x84\x8a\u0597\xcc-\xf6Nc\xcc\x06\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4k\xa8\xf7\xe2_\xc2\xd8qa\x8e$\xe4\x01\x84\x19\x917\xf9\xf6\xaa\x89\x15\xafd\x86\x9ak\xc2\x00\x00\u07d4k\xa9\xb2\x1b5\x10k\xe1Y\xd1\xc1\xc2ez\xc5l\u049f\xfdD\x89\xf2\xdc}G\xf1V\x00\x00\x00\u07d4k\xafz*\x02\xaex\x80\x1e\x89\x04\xadz\xc0Q\b\xfcV\xcf\xf6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94k\xb2\xac\xa2?\xa1bm\x18\xef\xd6w\u007f\xb9}\xb0-\x8e\n\xe4\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4k\xb4\xa6a\xa3:q\xd4$\u051b\xb5\xdf(b.\xd4\xdf\xfc\xf4\x89\",\x8e\xb3\xfff@\x00\x00\u07d4k\xb5\b\x13\x14j\x9a\xddB\xee\"\x03\x8c\x9f\x1fti\xd4\u007fG\x89\n\xdaUGK\x814\x00\x00\u07d4k\xbc?5\x8af\x8d\u0461\x1f\x03\x80\xf3\xf71\bBj\xbdJ\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4k\xbd\x1eq\x93\x90\xe6\xb9\x10C\xf8\xb6\xb9\u07c9\x8e\xa8\x00\x1b4\x89llO\xa6\xc3\xdaX\x80\x00\u07d4k\xc8Z\xcdY(r.\xf5\tS1\xee\x88\xf4\x84\xb8\u03c3W\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4k\xd3\xe5\x9f#\x9f\xaf\xe4wk\xb9\xbd\xddk\ue0fa]\x9d\x9f\x8965\u026d\xc5\u07a0\x00\x00\u07d4k\xd4W\xad\xe0Qy]\xf3\xf2F\\89\xae\xd3\xc5\xde\xe9x\x8964\xbf9\xab\x98x\x80\x00\u07d4k\xe1c\x13d>\xbc\x91\xff\x9b\xb1\xa2\xe1\x16\xb8T\xea\x93:E\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4k\xe7Y^\xa0\xf0hH\x9a'\x01\xecFI\x15\x8d\xdcC\xe1x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\xe9\x03\x0e\xe6\xe2\xfb\u0111\xac\xa3\xde@\"\xd3\x01w+{}\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94k\xec1\x1a\xd0P\b\xb4\xaf5<\x95\x8c@\xbd\x06s\x9a?\xf3\x8a\x03w\xf6*\x0f\nbp\x00\x00\u07d4k\xf7\xb3\xc0e\xf2\xc1\xe7\xc6\xeb\t+\xa0\xd1Pf\xf3\x93\u0478\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4k\xf8o\x1e/+\x802\xa9\\Mw8\xa1\t\xd3\xd0\xed\x81\x04\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4l\x05\xe3N^\xf2\xf4.\u041d\xef\xf1\x02l\xd6k\xcbi`\xbb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4l\b\xa6\xdc\x01s\xc74)U\xd1\xd3\xf2\xc0e\xd6/\x83\xae\u01c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4l\n\xe9\xf0C\xc84\xd4Bq\xf14\x06Y=\xfe\tO8\x9f\x89RD*\xe13\xb6*\x80\x00\u07d4l\f\xc9\x17\xcb\xee}|\t\x97c\xf1Nd\xdf}4\xe2\xbf\t\x89\r\x8drkqw\xa8\x00\x00\xe0\x94l\x0eq/@\\Yr_\xe8)\xe9wK\xf4\xdf\u007fM\xd9e\x8a\f(h\x88\x9c\xa6\x8aD\x00\x00\xe0\x94l\x10\x12\x05\xb3#\xd7uD\xd6\xdcR\xaf7\xac\xa3\xce\xc6\xf7\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4l\x15\xec5 \xbf\x8e\xbb\xc8 \xbd\x0f\xf1\x97x7T\x94\u03dd\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\xe0\x94l\x1d\xdd3\xc8\x19f\u0706!w`q\xa4\x12\x94\x82\xf2\xc6_\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4l%2\u007f\x8d\u02f2\xf4^V\x1e\x86\xe3]\x88P\xe5:\xb0Y\x89;\xcd\xf9\xba\xfe\xf2\xf0\x00\x00\u07d4l.\x9b\xe6\u052bE\x0f\xd1%1\xf3?\x02\x8caFt\xf1\x97\x89\xc2\x12z\xf8X\xdap\x00\x00\u07d4l5\x9eX\xa1=Ex\xa93\x8e3\\g\xe7c\x9f_\xb4\u05c9\v\xd1[\x94\xfc\x8b(\x00\x00\u07d4l=\x18pA&\xaa\x99\xee3B\xce`\xf5\xd4\xc8_\x18g\u0349\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4lGK\xc6jTx\x00f\xaaOQ.\xef\xa7s\xab\xf9\x19\u01c9\x05\x18\x83\x15\xf7v\xb8\x00\x00\u07d4lNBn\x8d\xc0\x05\u07e3Ql\xb8\xa6\x80\xb0.\ua56e\x8e\x89Hz\x9a0E9D\x00\x00\u07d4lR\xcf\b\x95\xbb5\xe6V\x16\x1eM\xc4j\xe0\xe9m\xd3\xe6,\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4lT\"\xfbK\x14\xe6\u064b`\x91\xfd\xecq\xf1\xf0\x86@A\x9d\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4l\\:T\u0367\xc2\xf1\x18\xed\xbaCN\xd8\x1en\xbb\x11\xddz\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4lc\xf8EV\u0490\xbf\u0359\xe44\ue657\xbf\xd7yWz\x89lk\x93[\x8b\xbd@\x00\x00\u07d4lc\xfc\x85\x02\x9a&T\u05db+\xeaM\xe3I\xe4REw\u0149#\xc7W\a+\x8d\xd0\x00\x00\u07d4led\xe5\xc9\xc2N\xaa\xa7D\xc9\xc7\xc9h\xc9\xe2\xc9\xf1\xfb\xae\x89I\x9bB\xa2\x119d\x00\x00\xe0\x94lg\xd6\xdb\x1d\x03Ql\x12\x8b\x8f\xf24\xbf=I\xb2m)A\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4lg\xe0\u05f6.*\bPiE\xa5\xdf\xe3\x82c3\x9f\x1f\"\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4lj\xa0\xd3\vdr\x19\x90\xb9PJ\x86?\xa0\xbf\xb5\xe5}\xa7\x89\x92^\x06\xee\xc9r\xb0\x00\x00\u07d4lqJX\xff\xf6\xe9}\x14\xb8\xa5\xe3\x05\xeb$@eh\x8b\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4l\x80\rKI\xba\a%\x04`\xf9\x93\xb8\xcb\xe0\v&j%S\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4l\x80\x8c\xab\xb8\xff_\xbbc\x12\xd9\xc8\xe8J\xf8\xcf\x12\xef\bu\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94l\x82 )!\x8a\xc8\xe9\x8a&\f\x1e\x06@)4\x889\x87[\x8a\x01\x0f\x97\xb7\x87\xe1\xe3\b\x00\x00\u07d4l\x84\u02e7|m\xb4\xf7\xf9\x0e\xf1=^\xe2\x1e\x8c\xfc\u007f\x83\x14\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94l\x86\x87\xe3Aw\x10\xbb\x8a\x93U\x90!\xa1F\x9ej\x86\xbcw\x8a\x02[-\xa2x\xd9k{\x80\x00\xe0\x94l\x88,'s,\xef\\|\x13\xa6\x86\xf0\xa2\xeawUZ\u0089\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4l\xa5\xde\x00\x81}\xe0\xce\xdc\xe5\xfd\x00\x01(\xde\xde\x12d\x8b<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4l\xa6\xa12\xce\x1c\u0488\xbe\xe3\x0e\xc7\xcf\xef\xfb\x85\xc1\xf5\nT\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94l\xb1\x1e\xcb2\xd3\u0382\x96\x011\x066\xf5\xa1\f\xf7\u03db_\x8a\x04?\u851c8\x01\xf5\x00\x00\u07d4l\xc1\xc8x\xfal\u078a\x9a\v\x83\x11$~t\x1eFB\xfem\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94l\xcb\x03\xac\xf7\xf5<\xe8z\xad\xcc!\xa9\x93-\xe9\x15\xf8\x98\x04\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4l\xd2\x12\xae\xe0N\x01?=*\xba\u04a0#`k\xfb\\j\u01c9lj\xccg\u05f1\xd4\x00\x00\u07d4l\xd2(\xdcq!i0\u007f\xe2|\xebtw\xb4\x8c\xfc\x82r\xe5\x89\x044\xea\x94\u06caP\x00\x00\u07d4l\xe1\xb0\xf6\xad\xc4pQ\xe8\xab8\xb3\x9e\xdbA\x86\xb0;\xab\u0309Ay\x97\x94\xcd$\xcc\x00\x00\u07d4l\xea\xe3s=\x8f\xa4=l\xd8\f\x1a\x96\xe8\xeb\x93\x10\x9c\x83\xb7\x89\x10'\x94\xad \xdah\x00\x00\u07d4m\x05i\xe5U\x8f\xc7\xdf'f\xf2\xba\x15\u070a\xef\xfc[\xebu\x89\xd8\xe6\x00\x1el0+\x00\x00\u07d4m\x12\x0f\f\xaa\xe4O\xd9K\xca\xfeU\xe2\xe2y\uf5ba\\z\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4m\x14V\xff\xf0\x10N\xe8D\xa31G7\x8438\xd2L\xd6l\x89\a\xb0l\xe8\u007f\xddh\x00\x00\u07d4m \xef\x97\x04g\nP\v\xb2i\xb5\x83.\x85\x98\x02\x04\x9f\x01\x89\a\f\x1c\xc7;\x00\xc8\x00\x00\xe0\x94m/\x97g4\xb9\xd0\a\r\x18\x83\xcfz\u02b8\xb3\xe4\x92\x0f\xc1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4m9\xa9\u93c1\xf7i\xd7:\xad,\xea\xd2v\xac\x13\x87\xba\xbe\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4m;x6\xa2\xb9\u0619r\x1aM#{R#\x85\xdc\xe8\xdf\u034966\xc2^f\xec\xe7\x00\x00\u07d4m?+\xa8V\u033b\x027\xfava\x15k\x14\xb0\x13\xf2\x12@\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94m@\b\xb4\xa8\x88\xa8&\xf2H\xeej\v\r\xfd\xe9\xf92\x10\xb9\x8a\x01'\xfc\xb8\xaf\xae \xd0\x00\x00\u07d4m@\xca'\x82m\x97s\x1b>\x86\xef\xfc\u05f9*Aa\xfe\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4mD\x97J1\u0447\xed\xa1m\xddG\xb9\xc7\xecP\x02\xd6\x1f\xbe\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94mK\\\x05\xd0j \x95~\x17H\xabm\xf2\x06\xf3C\xf9/\x01\x8a\x02\x1f6\x06\x99\xbf\x82_\x80\x00\xe0\x94mL\xbf=\x82\x84\x83:\xe9\x93D0>\b\xb4\xd6\x14\xbf\xda;\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4mY\xb2\x1c\xd0\xe2t\x88\x04\u066b\xe0d\xea\u00be\xf0\xc9_'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4mc\u04ce\xe8\xb9\x0e\x0en\xd8\xf1\x92\xed\xa0Q\xb2\u05a5\x8b\xfd\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4mf4\xb5\xb8\xa4\x01\x95\xd9I\x02z\xf4\x82\x88\x02\t,\ued89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94m}\x1c\x94\x95\x11\xf8\x83\x03\x80\x8c`\xc5\xea\x06@\xfc\xc0&\x83\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4m\x84m\xc1&W\xe9\x1a\xf2P\bQ\x9c>\x85\u007fQp}\u0589\xf8\xd3\v\xc9#B\xf8\x00\x00\u07d4m\x91\x93\x99k\x19F\x17!\x11\x06\xd1c^\xb2l\u0136ll\x89\x15\xaa\x1e~\x9d\xd5\x1c\x00\x00\u07d4m\x99\x97P\x98\x82\x02~\xa9G#\x14$\xbe\xde\xde)e\u043a\x89l\x81\u01f3\x11\x95\xe0\x00\x00\u07d4m\xa0\xed\x8f\x1di3\x9f\x05\x9f*\x0e\x02G\x1c\xb4O\xb8\u00fb\x892\xbc8\xbbc\xa8\x16\x00\x00\u07d4m\xb7+\xfdC\xfe\xf4e\xcaV2\xb4Z\xabra@N\x13\xbf\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94m\xbe\x8a\xbf\xa1t(\x06&9\x817\x1b\xf3\xd3U\x90\x80kn\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4m\xc3\xf9+\xaa\x1d!\u06b78+\x892a\xa05o\xa7\xc1\x87\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4m\xc7\x05:q\x86\x16\xcf\u01cb\xeec\x82\xeeQ\xad\xd0\xc7\x030\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94m\xcc~d\xfc\xaf\xcb\xc2\xdcl\x0e^f,\xb3G\xbf\xfc\xd7\x02\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4m\xda_x\x8alh\x8d\u07d2\x1f\xa3\x85.\xb6\xd6\xc6\xc6)f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4m\xdb`\x92w\x9dXB\xea\xd3x\xe2\x1e\x81 \xfdLk\xc12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4m\xdf\xefc\x91U\u06ab\n\\\xb4\x95:\xa8\u016f\xaa\x88\x04S\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4m\xe0/-\xd6~\xfd\xb794\x02\xfa\x9e\xaa\xcb\xcfX\x9d.V\x89@\x13\x8b\x91~\u07f8\x00\x00\u07d4m\u4d418\\\xf7\xfc\x9f\xe8\xc7}\x13\x1f\xe2\xeew$\xc7j\x89})\x97s=\xcc\xe4\x00\x00\u07d4m\xe4\xd1R\x19\x18/\xaf:\xa2\xc5\xd4\xd2Y_\xf20\x91\xa7'\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4m\xed\xf6.t?M,*K\x87\xa7\x87\xf5BJz\xeb9<\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4m\xf2Of\x85\xa6/y\x1b\xa37\xbf?\xf6~\x91\xf3\u053c:\x89ukI\xd4\nH\x18\x00\x00\u07d4m\xf5\xc8O{\x90\x9a\xab>a\xfe\x0e\xcb\x1b;\xf2`\"*\u0489\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4m\xff\x90\xe6\xdc5\x9d%\x90\x88+\x14\x83\xed\xbc\xf8\x87\xc0\xe4#\x8965\u026d\xc5\u07a0\x00\x00\u07d4n\x01\xe4\xadV\x9c\x95\xd0\a\xad\xa3\r^-\xb1(\x88I\"\x94\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\a;f\u0478\xc6gD\u0600\x96\xa8\u0759\xec~\x02(\u0689\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\x0e\xe7\x06\x12\xc9v(}I\x9d\u07e6\xc0\xdc\xc1,\x06\xde\xea\x89\a\v\u0579V!F\x00\x00\xe0\x94n\x12\xb5\x1e\"[JCr\xe5\x9a\u05e2\xa1\xa1>\xa3\u04e17\x8a\x03\x00F\xc8\xccw_\x04\x00\x00\u07d4n\x1a\x04l\xaf[JW\xf4\xfdK\xc1sb!&\xb4\xe2\xfd\x86\x89a\t=|,m8\x00\x00\u07d4n\x1e\xa4\xb1\x83\xe2R\u027bwg\xa0\x06\u05346\x96\u02ca\xe9\x89\x0f\xf3x<\x85\xee\u0400\x00\u07d4n%[p\n\xe7\x13\x8aK\xac\xf2(\x88\xa9\xe2\xc0\n(^\xec\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n'\n\xd5)\xf1\xf0\xb8\xd9\xcbm$'\xec\x1b~-\xc6Jt\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4n.\xab\x85\u0709\xfe)\xdc\n\xa1\x852G\u06b4:R=V\x89\x04V9\x18$O@\x00\x00\u07d4n:Q\xdbt=3M/\xe8\x82$\xb5\xfe|\x00\x8e\x80\xe6$\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4nL*\xb7\xdb\x02i9\xdb\u04fch8J\xf6`\xa6\x18\x16\xb2\x89\t\r\x97/22<\x00\x00\u07d4nM.9\u0203f)\u5d07\xb1\x91\x8af\x9a\xeb\u07556\x8965\u026d\xc5\u07a0\x00\x00\u07d4n\\-\x9b\x1cTj\x86\xee\xfd]\nQ \xc9\xe4\xe70\x19\x0e\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4n`\xae\u19cf\x8e\u068bBLs\xe3S5J\xe6|0B\x89\xbd5\xa4\x8d\x99\x19\xe6\x00\x00\u07d4nd\xe6\x12\x9f\"N7\x8c\x0ensj~z\x06\xc2\x11\xe9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4nm[\xbb\xb9\x05;\x89\xd7D\xa2s\x16\u00a7\xb8\xc0\x9bT}\x891Rq\n\x02>m\x80\x00\u07d4nr\xb2\xa1\x18j\x8e)\x16T;\x1c\xb3jh\x87\x0e\xa5\u0457\x89\n\x15D\xbe\x87\x9e\xa8\x00\x00\u07d4nv\x1e\xaa\x0f4_w{TA\xb7:\x0f\xa5\xb5k\x85\xf2-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4ny\xed\u0504[\anL\u060d\x18\x8bnC-\xd9?5\xaa\x893\xc5I\x901r\f\x00\x00\u07d4n\x82\x12\xb7\"\xaf\xd4\b\xa7\xa7>\xd3\xe29^\xe6EJ\x030\x89\b\x9e\x91y\x94\xf7\x1c\x00\x00\u07d4n\x84\x87m\xbb\x95\xc4\vfV\xe4+\xa9\xae\xa0\x8a\x99;T\u0709;\xbc`\xe3\xb6\u02fe\x00\x00\u07d4n\x84\xc2\xfd\x18\xd8\tW\x14\xa9h\x17\x18\x9c\xa2\x1c\xcab\xba\xb1\x89\x12{lp&!\u0340\x00\u07d4n\x86m\x03-@Z\xbd\xd6\\\xf6QA\x1d\x807\x96\xc2#\x11\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94n\x89\x9eY\xa9\xb4\x1a\xb7\xeaA\xdfu\x17\x86\x0f*\xcbY\xf4\xfd\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4n\x89\xc5\x1e\xa6\xde\x13\xe0l\xdct\x8bg\xc4A\x0f\u9f2b\x03\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\x8a&h\x9fz/\xde\xfd\x00\x9c\xba\xaaS\x10%4P\u06ba\x89o!7\x17\xba\xd8\xd3\x00\x00\u07d4n\x96\xfa\xed\xa3\x05C\x02\xc4_X\xf1a2L\x99\xa3\xee\xbbb\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4n\xb0\xa5\xa9\xae\x96\xd2,\xf0\x1d\x8f\xd6H;\x9f8\xf0\x8c,\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xb3\x81\x96\x17@@X&\x8f\f<\xff5\x96\xbf\xe9\x14\x8c\x1c\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94n\xb5W\x8ak\xb7\xc3!S\x19[\r\x80 \xa6\x91HR\xc0Y\x8a\x8b\u00ab\xf4\x02!\xf4\x80\x00\x00\u07d4n\xbb^iW\xaa\x82\x1e\xf6Y\xb6\x01\x8a9:PL\xaeDP\x89lk\x93[\x8b\xbd@\x00\x00\u07d4n\xbc\xf9\x95\u007f_\xc5\u916d\xd4u\";\x04\xb8\xc1Jz\xed\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4n\xc3e\x95q\xb1\x1f\x88\x9d\xd49\xbc\xd4\xd6u\x10\xa2[\xe5~\x89\x06\xaa\xf7\xc8Qm\f\x00\x00\u07d4n\u021b9\xf9\xf5'jU>\x8d\xa3\x0en\xc1z\xa4~\xef\u01c9\x18BO_\v\x1bN\x00\x00\u07d4n\xc9m\x13\xbd\xb2M\u01e5W)?\x02\x9e\x02\xddt\xb9zU\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xca\xef\xa6\xfc>\xe54bm\xb0,o\x85\xa0\u00d5W\x1ew\x89 \x86\xac5\x10R`\x00\x00\u07d4n\u04a1+\x02\xf8\u0188\u01f5\u04e6\xea\x14\xd66\x87\u06b3\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4n\u0604E\x9f\x80\x9d\xfa\x10\x16\xe7p\xed\xaf>\x9f\xefF\xfa0\x89\xb8R\xd6x \x93\xf1\x00\x00\xe0\x94n\xdf\u007fR\x83r\\\x95>\xe6C\x17\xf6a\x88\xaf\x11\x84\xb03\x8a\x01\xb4d1\x1dE\xa6\x88\x00\x00\u07d4n\xe8\xaa\xd7\xe0\xa0e\u0605-|;\x9an_\xdcK\xf5\f\x00\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4n\xef\u0705\x0e\x87\xb7\x15\xc7'\x91w<\x03\x16\xc3U\x9bX\xa4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xf9\xe8\u0276!}Vv\x9a\xf9}\xbb\x1c\x8e\x1b\x8b\xe7\x99\u0489\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4n\xfb\xa8\xfb*\u0176s\a)\xa9r\xec\"D&\xa2\x87\u00ed\x89\x0fY\x85\xfb\xcb\xe1h\x00\x00\xe0\x94n\xfd\x90\xb55\xe0\v\xbd\x88\x9f\xda~\x9c1\x84\xf8y\xa1Q\u06ca\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4o\x05\x16f\xcbO{\u04b1\x90r!\xb8)\xb5U\u05e3\xdbt\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4o\x0e\xdd#\xbc\xd8_`\x15\xf9(\x9c(\x84\x1f\xe0L\x83\xef\xeb\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4o\x13zq\xa6\xf1\x97\xdf,\xbb\xf0\x10\u073d<DN\xf5\xc9%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4o\x17`e\xe8\x8e<o\xe6&&}\x18\xa0\x88\xaa\xa4\u06c0\xbc\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4o\x18\xecv~2\x05\b\x19_\x13tP\x0e?.\x12V\x89\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4o\x1fI\a\xb8\xf6\x1f\fQV\x8di(\x06\xb3\x82\xf5\x03$\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4o$\u026f+v4\x80Q]\x1b\tQ\xbbw\xa5@\xf1\xe3\xf9\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4o%v\xdaM\u20fb\xe8\xe3\xeei\xdd\xd6n^q\x1d\xb3\xf5\x89DY\x1dg\xfe\u0300\x00\x00\u07d4o)\xbb7[\xe5\xed4\ud65b\xb80\xee)W\xdd\xe7m\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4o*1\x90\x0e$\x03\x95\xb1\x9f\x15\x9c\x1d\x00\xdf\xe4\u0618\xeb\u07c9lf\x06E\xaaG\x18\x00\x00\u07d4o*B\xe6\xe03\xd0\x10a\x13\x19)\xf7\xa6\xee\x158\x02\x1eR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4o9\xcc7\u02aa-\u071ba\x0fa1\xe0a\x9f\xaew*<\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4oD\xca\t\xf0\u01a8)L\xbdQ\x9c\xdcYJ\xd4,gW\x9f\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4oP\x92\x97w\x82L)\x1aI\xc4m\xc8T\xf3y\xa6\xbe\xa0\x80\x89\x13\x84\x00\xec\xa3d\xa0\x00\x00\xe0\x94ol\xf2\x06I\xa9\xe9s\x17z\xc6}\xba\xde\xe4\xeb\xe5\u01fd\u068a\x01\x13c)}\x01\xa8`\x00\x00\u07d4oy\x1d5\x9b\xc3Sj1]c\x82\xb8\x83\x11\xaf\x8e\xd6\xdaG\x89\x04\xfc\xc1\xa8\x90'\xf0\x00\x00\u07d4oyM\xbd\xf6#\u06a6\xe0\xd0\at\xadibs|\x92\x1e\xa4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4oz\u0181\xd4^A\x8f\u038b:\x1d\xb5\xbc;\xe6\xf0l\x98I\x89lk\x93[\x8b\xbd@\x00\x00\u07d4o\x81\xf3\xab\xb1\xf93\xb1\xdf9k\x8e\x9c\xc7#\xa8\x9b|\x98\x06\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4o\x8f\r\x15\u0316\xfb\u007f\xe9O\x10e\xbci@\xf8\xd1)W\xb2\x8965\u026d\xc5\u07a0\x00\x00\u07d4o\x92\xd6\xe4T\x8cx\x99e\t\xeehK.\u26e3\xc52\xb4\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94o\xa6\r\xf8\x18\xa5Dd\x18\xb1\xbb\xd6(&\u0e42^\x13\x18\x8a\x02\u02d2\u030fg\x14@\x00\x00\u07d4o\xa68\x8d@+0\xaf\xe5\x994\u00f9\xe1=\x11\x86G`\x18\x89$R\x1e*0\x17\xb8\x00\x00\u07d4o\xa7 \x15\xfaxin\xfd\x9a\x86\x17O\u007f\x1f!\x01\x92\x86\xb1\x89Hz\x9a0E9D\x00\x00\u07d4o\xc2^~\x00\xcaO`\xa9\xfeo(\xd1\xfd\xe3T.-\x10y\x89*\xef5;\xcd\xdd`\x00\x00\u07d4o\xc56b7\x1d\xcaX{Y\x85\r\xe7\x86\x06\xe25\x9d\xf3\x83\x89\t\xc2\x00vQ\xb2P\x00\x00\xe0\x94o\xcc,s+\u0753J\xf6\xcc\xd1hF\xfb&\uf272\xaa\x9b\x8a\x02\x1e+\x1dB&\x1dI\x00\x00\u07d4o\xd4\xe0\xf3\xf3+\xeem7g\xfd\xbc\x9d5:m:\xabx\x99\x89%\xb0d\xa8u\xea\x94\x00\x00\xe0\x94o\xd9G\u0567;\x17P\b\xaen\xe8\"\x81c\xda(\x9b\x16}\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4o\u064eV=\x12\xce\x0f\xd6\x0fO\x1f\x85\n\u35a9\x82<\x02\x89D[\xe3\xf2\uf1d4\x00\x00\xe0\x94o\u077d\x9b\xcaf\xe2\x87e\xc2\x16,\x843T\x8c\x10R\xed\x11\x8a\x11\x84B\x9b\x82\xa8\x18\x80\x00\x00\u07d4o\xf5\xd3a\xb5*\u0436\x8b\x15\x88`~\xc3\x04\xaeVe\xfc\x98\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4o\xf6\u0310\xd6I\xdeN\x96\xcf\xfe\xe1\az[0*\x84\x8d\u02c9\x01\x8c\xe7\x9cx\x80,\x00\x00\u07d4o\xfe\\\xf8,\xc9\xea^6\xca\xd7\u0097L\xe7$\x9f7I\xe6\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94p\x05\xa7r(+\x1fb\xaf\xdac\xf8\x9b]\u01abd\xc8L\xb9\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4p\a\x11\xe3\x11\xbb\x94sU\xf7U\xb5y%\f\xa7\xfdvZ>\x89a\t=|,m8\x00\x00\u07d4p\x10\xbe-\xf5{\u042b\x9a\xe8\x19l\xd5\n\xb0\xc5!\xab\xa9\xf9\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4p#\xc7\tV\xe0J\x92\xd7\x00%\xaa\u0497\xb59\xaf5Xi\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p%\x96]+\x88\xda\x19}DY\xbe=\xc98cD\xcc\x1f1\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d4p(\x02\xf3m\x00%\x0f\xabS\xad\xbc\u0596\xf0\x17oc\x8aI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4pH\x19\xd2\xe4Mn\xd1\xda%\xbf\u0384\u011f\u0322V\x13\xe5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4pJn\xb4\x1b\xa3O\x13\xad\xdd\xe7\xd2\xdb}\xf0I\x15\u01e2!\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4pJ\xb1\x15\r^\x10\xf5\xe3I\x95\b\xf0\xbfpe\x0f\x02\x8dK\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4pJ\xe2\x1dv-n\x1d\xde(\xc25\xd11\x04Yr6\xdb\x1a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4pM$<)x\xe4l,\x86\xad\xbe\xcd$n;)_\xf63\x89m\x12\x1b\xeb\xf7\x95\xf0\x00\x00\u07d4pM]\xe4\x84m9\xb5<\xd2\x1d\x1cI\xf0\x96\xdb\\\x19\xba)\x89\b=lz\xabc`\x00\x00\u07d4p]\xdd85T\x82\xb8\xc7\u04f5\x15\xbd\xa1P\r\xd7\u05e8\x17\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94pan(\x92\xfa&\x97\x05\xb2\x04k\x8f\xe3\xe7/\xa5X\x16\u04ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4pg\x0f\xbb\x05\xd30\x14DK\x8d\x1e\x8ew\x00%\x8b\x8c\xaam\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\x81\xfak\xaa\xd6\u03f7\xf5\x1b,\xca\x16\xfb\x89p\x99\x1ad\xba\x89\f\xae\xc0\x05\xf6\xc0\xf6\x80\x00\xe0\x94p\x85\xae~~M\x93!\x97\xb5\u01c5\x8c\x00\xa3gF&\xb7\xa5\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4p\x86\xb4\xbd\xe3\xe3]J\xeb$\xb8%\xf1\xa2\x15\xf9\x9d\x85\xf7E\x89lh\xcc\u041b\x02,\x00\x00\u07d4p\x8a*\xf4%\u03b0\x1e\x87\xff\xc1\xbeT\xc0\xf52\xb2\x0e\xac\u0589\aE\u0503\xb1\xf5\xa1\x80\x00\u07d4p\x8e\xa7\a\xba\xe45\u007f\x1e\xbe\xa9Y\u00e2P\xac\u05aa!\xb3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794p\x8f\xa1\x1f\xe3=\x85\xad\x1b\xef\u02ee8\x18\xac\xb7\x1fj}~\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4p\x9101\x16\xd5\xf28\x9b##\x8bMej\x85\x96\u0644\u04c9;N~\x80\xaaX3\x00\x00\u07d4p\x99\xd1/n\xc6V\x89\x9b\x04\x9avW\x06]b\x99h\x92\u0209\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4p\x9f\xe9\xd2\xc1\xf1\xceB |\x95\x85\x04J`\x89\x9f5\x94/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\xa05I\xaaah\xe9~\x88\xa5\b3\nZ\v\xeatq\x1a\x89Hz\x9a0E9D\x00\x00\u07d4p\xa4\x06}D\x8c\xc2]\xc8\xe7\x0ee\x1c\xea|\xf8N\x92\x10\x9e\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4p\xab4\xbc\x17\xb6o\x9c;c\xf1Q'O*r|S\x92c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\xc2\x13H\x8a\x02\f<\xfb9\x01N\xf5\xbad\x04rK\u02a3\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4p\xd2^\xd2\u022d\xa5\x9c\b\x8c\xf7\r\xd2+\xf2\u06d3\xac\xc1\x8a\x899GEE\u4b7c\x00\x00\u07d4p\xe5\xe9\xdas_\xf0w$\x9d\u02da\xaf=\xb2\xa4\x8d\x94\x98\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4p\xfe\xe0\x8b\x00\xc6\xc2\xc0J<b\\\x1f\xf7|\xaf\x1c2\xdf\x01\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4q\x01\xbdy\x9eA\x1c\xde\x14\xbd\xfa\xc2[\x06z\u0210\xea\xb8\xe8\x89N\x9b\x8a\xaeH\xdeG\x00\x00\u07d4q\t\xdd\x01\x1d\x15\xf3\x12-\x9d:'X\x8c\x10\xd7wDP\x8b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94q\v\x02t\xd7\x12\xc7~\b\xa5p}o>p\xc0\xce=\x92\u03ca\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u0794q\v\xe8\xfd^)\x18F\x8b\u2abe\xa8\r\x82\x845\u05d6\x12\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4q\x13]\x8f\x05\x96<\x90ZJ\a\x92)\t#Z\x89jR\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4q\x1e\xcfw\xd7\x1b=\x0e\xa9\\\xe4u\x8a\xfe\u0379\xc11\a\x9d\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4q!?\xca14\x04 N\u02e8q\x97t\x1a\xa9\xdf\xe9c8\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94q+vQ\x02\x14\xdcb\x0fl:\x1d\u049a\xa2+\xf6\xd2\x14\xfb\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4q/\xf77\n\x13\xed6\ts\xfe\u071f\xf5\xd2\xc9:P^\x9e\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4q3\x84:x\xd99\u019dD\x86\xe1\x0e\xbc{`*4\x9f\xf7\x89\x11\xd5\xca\xcc\xe2\x1f\x84\x00\x00\u07d4qH\xae\xf32a\xd8\x03\x1f\xac?q\x82\xff5\x92\x8d\xafT\u0649\xdeB\xee\x15D\u0750\x00\x00\u07d4qcu\x8c\xbblLR^\x04\x14\xa4\n\x04\x9d\xcc\xcc\xe9\x19\xbb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4qh\xb3\xbb\x8c\x16s!\u067d\xb0#\xa6\xe9\xfd\x11\xaf\u026f\u0649a\t=|,m8\x00\x00\u07d4qirN\xe7\"q\xc54\xca\xd6B\x0f\xb0N\xe6D\u02c6\xfe\x89\x16<+@\u06e5R\x00\x00\u07d4qj\xd3\xc3:\x9b\x9a\n\x18\x96sW\x96\x9b\x94\xee}*\xbc\x10\x89\x1a!\x17\xfeA*H\x00\x00\xe0\x94qk\xa0\x1e\xad*\x91'\x065\xf9_%\xbf\xaf-\xd6\x10\xca#\x8a\ty\xe7\x01 V\xaax\x00\x00\u07d4qmP\u0320\x1e\x93\x85\x00\xe6B\x1c\xc0p\xc3P|g\u04c7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4qv,cg\x8c\x18\xd1\xc67\x8c\xe0h\xe6f8\x13\x15\x14~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4qxL\x10Q\x17\xc1\xf6\x895y\u007f\xe1Y\xab\xc7NC\xd1j\x89l\x81\u01f3\x11\x95\xe0\x00\x00\xe0\x94qyro\\q\xae\x1bm\x16\xa6\x84(\x17Nk4\xb26F\x8a\x01\x8e\xa2P\t|\xba\xf6\x00\x00\xe0\x94q|\xf9\xbe\xab680\x8d\xed~\x19^\f\x86\x13-\x16?\xed\x8a\x032n\xe6\xf8e\xf4\"\x00\x00\u07d4q\x80\xb8>\xe5WC\x17\xf2\x1c\x80r\xb1\x91\u0615\xd4aS\u00c9\x18\xef\xc8J\xd0\u01f0\x00\x00\u07d4q\x94kq\x17\xfc\x91^\xd1\a8_B\u065d\xda\xc62I\u0089lk\x93[\x8b\xbd@\x00\x00\xe0\x94q\x9e\x89\x1f\xbc\xc0\xa3>\x19\xc1-\xc0\xf0 9\xca\x05\xb8\x01\u07ca\x01OU8F:\x1bT\x00\x00\u07d4q\xc7#\n\x1d5\xbd\u0581\x9e\u0539\xa8\x8e\x94\xa0\xeb\a\x86\u0749\uc80b5=$\x14\x00\x00\u07d4q\xd2\xccm\x02W\x8ce\xf7<W^v\u038f\xbc\xfa\xdc\xf3V\x89\x03\xec\xc0xh\x8aH\x00\x00\u07d4q\xd9INP\xc5\xddY\u0159\u06e3\x81\v\xa1u^e7\xf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4q\xe3\x8f\xf5E\xf3\x0f\xe1L\xa8c\xd4\xf5)\u007f\u050cs\xa5\u0389\xc2\x12z\xf8X\xdap\x00\x00\u07d4q\xea[\x11\xad\x8d)\xb1\xa4\xcbg\xbfX\xcal\x9f\x9c3\x8c\x16\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4q\xec:\xec?\x8f\x92!\xf9\x14\x9f\xed\xe0i\x03\xa0\xf9\xa22\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4q\xf2\xcd\u0470F\xe2\xda/\xbbZ&r4\"\xb82^%\xa3\x89\x05k9Bc\xa4\f\x00\x00\u07d4q\xfa\"\xccm3 k}p\x1a\x16:\r\xab1\xaeM1\u0589WG=\x05\u06ba\xe8\x00\x00\u07d4r\x01\xd1\xc0i \xcd9z\u8b46\x9b\u0366\xe4\u007f\xfb\x1bZ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4r\a*\x0e\xf1\xcf\xf3\xd5g\xcd\xd2`\xe7\b\xdd\xc1\x1c\xbc\x9a1\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94r\tO9Q\xff\xc9w\x1d\xce\xd2:\xda\b\v\xca\xf9\xc7\u0327\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94r\t\x94\xdb\xe5j:\x95\x92\x97t\xe2\x0e\x1f\xe5%\xcf7\x04\xe4\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4r\x0ek\"\xbfC\tf\xfa2\xb6\xac\xb9\xa5\x06\xee\xbff,a\x89\b=lz\xabc`\x00\x00\xe0\x94r\x11X\xbeWb\xb1\x19\u031b 5\xe8\x8e\xe4\xeex\U0009b08a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4r\x1f\x9d\x17\xe5\xa0\xe7B\x05\x94z\xeb\x9b\u01a7\x93\x89a\x03\x8f\x89\x02\xd0A\xd7\x05\xa2\xc6\x00\x00\u07d4r\"\xfe\xc7q\x17\x81\xd2n\xaaN\x84\x85\xf7\xaa?\xacD$\x83\x89\x18\xb8Ep\x02* \x00\x00\u07d4r9=7\xb4Q\xef\xfb\x9e\x1f\xf3\xb8U'\x12\xe2\xa9p\xd8\u00895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4r=\x8b\xaa%Q\u04ad\xdcC\xc2\x1bE\xe8\xafL\xa2\xbf\xb2\u0089_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94r@#\x00\xe8\x1d\x14l.dN+\xbd\xa1\xda\x16<\xa3\xfbV\x8a\x01{x\x83\xc0i\x16`\x00\x00\xe0\x94rH\v\xed\xe8\x1a\xd9d#\xf2\"\x8b\\a\xbeD\xfbR1\x00\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u07d4rL\xe8X\x85~\xc5H\x1c\x86\xbd\x90n\x83\xa0H\x82\xe5\x82\x1d\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94rj\x14\xc9\x0e?\x84\x14Lv\\\xff\xac\xba>\r\xf1\x1bH\xbe\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4r\x83\xcdFu\xdaX\u0116UaQ\xda\xfd\x80\xc7\xf9\x95\xd3\x18\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4r\x86\xe8\x9c\xd9\u078fz\x8a\x00\xc8o\xfd\xb59\x92\u0752Q\u0449i*\xe8\x89p\x81\xd0\x00\x00\u07d4r\x8f\x9a\xb0\x80\x15}\xb3\a1V\xdb\xca\x1a\x16\x9e\xf3\x17\x94\a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4r\x94\xc9\x18\xb1\xae\xfbM%\x92~\xf9\u05d9\xe7\x1f\x93\xa2\x8e\x85\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94r\x94\uc763\x10\xbckK\xbd\xf5C\xb0\xefE\xab\xfc>\x1bM\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4r\x9a\xadF'tNS\xf5\xd6c\t\xaatD\x8b:\xcd\xf4o\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xa2\xfc\x86u\xfe\xb9r\xfaA\xb5\r\xff\u06fa\xe7\xfa*\u07f7\x89\x9a\xb4\xfcg\xb5(\xc8\x00\x00\u07d4r\xa8&\b&)G&\xa7[\xf3\x9c\u066a\x9e\a\xa3\xea\x14\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4r\xb0Yb\xfb*\u0549\xd6Z\xd1j\"U\x9e\xba\x14X\xf3\x87\x89\a?u\u0460\x85\xba\x00\x00\u07d4r\xb5c?\xe4w\xfeT.t/\xac\xfdi\f\x13xT\xf2\x16\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4r\xb7\xa0=\xda\x14\u029cf\x1a\x1dF\x9f\xd376\xf6s\xc8\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xb9\x04D\x0e\x90\xe7 \u05ac\x1c*\u05dc2\x1d\xcc\x1c\x1a\x86\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94r\xb9\nM\xc0\x97#\x94\x92\u0179w}\xcd\x1eR\xba+\xe2\u008a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4r\xbb'\u02d9\xf3\xe2\xc2\u03d0\xa9\x8fp}0\xe4\xa2\x01\xa0q\x89X\xe7\x92n\xe8X\xa0\x00\x00\xe0\x94r\xc0\x83\xbe\xad\xbd\xc2'\xc5\xfbC\x88\x15\x97\xe3.\x83\xc2`V\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4r\xcd\x04\x8a\x11\x05tH)\x83I-\xfb\x1b\xd2yB\xa6\x96\xba\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xd0=M\xfa\xb3P\f\xf8\x9b\x86\x86o\x15\xd4R\x8e\x14\xa1\x95\x89\xf3K\x82\xfd\x8e\x91 \x00\x00\u07d4r\u06bb[n\ud799\xbe\x91X\x88\xf6V\x80V8\x16\b\xf8\x89\vL\x96\xc5,\xb4\xfe\x80\x00\u07d4r\xfbI\u009d#\xa1\x89P\u0132\xdc\r\xdfA\x0fS-oS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xfe\xaf\x12EyR9Td[\u007f\xaf\xff\x03x\xd1\xc8$.\x8965\u026d\xc5\u07a0\x00\x00\u07d4s\x01\xdcL\xf2mq\x86\xf2\xa1\x1b\xf8\xb0\x8b\xf2)F?d\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s\x04G\xf9|\xe9\xb2_\"\xba\x1a\xfb6\xdf'\xf9Xk\ub6c9,s\xc97t,P\x00\x00\u07d4s\x06\xde\x0e(\x8bV\xcf\u07d8~\xf0\xd3\xcc)f\a\x93\xf6\u0749\x1b\x8a\xbf\xb6.\xc8\xf6\x00\x00\xe0\x94s\r\x87c\u01a4\xfd\x82J\xb8\xb8Y\x16\x1e\xf7\xe3\xa9j\x12\x00\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4s\x12\x81sH\x95(\x01.v\xb4\x1a^(\u018b\xa4\xe3\xa9\u050965\u026d\xc5\u07a0\x00\x00\u07d4s\x13F\x12\bETUFTE\xa4Y\xb0l7s\xb0\xeb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s/\xea\xd6\x0f{\xfd\u05a9\xde\u0101%\xe3s]\xb1\xb6eO\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4sB#\xd2\u007f\xf2>Y\x06\xca\xed\"YW\x01\xbb4\x83\f\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4sG>r\x11Q\x10\xd0\xc3\xf1\x17\b\xf8nw\xbe+\xb0\x98<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4sRXm\x02\x1a\xd0\xcfw\xe0\xe9(@JY\xf3t\xffE\x82\x89\xb8Pz\x82\a( \x00\x00\u07d4sU\v\xebs+\xa9\u076f\xdaz\xe4\x06\xe1\x8f\u007f\xeb\x0f\x8b\xb2\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4s[\x97\xf2\xfc\x1b\xd2K\x12\an\xfa\xf3\xd1(\x80s\xd2\f\x8c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4s^2\x86f\xedV7\x14+3\x06\xb7|\xccT`\xe7,=\x89j\xb8\xf3xy\u0251\x00\x00\u07d4sc\u0350\xfb\xab[\xb8\u011a\xc2\x0f\xc6,9\x8f\xe6\xfbtL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4skDP=\xd2\xf6\xddTi\xffL[-\xb8\xeaO\xece\u0409\x11\x04\xeeu\x9f!\xe3\x00\x00\xe0\x94sk\xf1@,\x83\x80\x0f\x89>X1\x92X*\x13N\xb52\xe9\x8a\x02\x1e\x19\u0493\xc0\x1f&\x00\x00\xe0\x94s\x8c\xa9M\xb7\u038b\xe1\xc3\x05l\u0598\x8e\xb3v5\x9f3S\x8a\x05f[\x96\xcf5\xac\xf0\x00\x00\u07d4s\x91K\"\xfc/\x13\x15\x84$}\x82\xbeO\ucfd7\x8a\u053a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s\x93'\t\xa9\u007f\x02\u024eQ\xb0\x911(e\x12#\x85\xae\x8e\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4s\x93\xcb\xe7\xf9\xba!e\xe5\xa7U5\x00\xb6\xe7]\xa3\xc3:\xbf\x89\x05k\xc7^-c\x10\x00\x00\u07d4s\xb4\u0519\xde?8\xbf5\xaa\xf7i\xa6\xe3\x18\xbcm\x126\x92\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94s\xbe\xddo\xda{\xa3'!\x85\b{cQ\xfc\x13=HN7\x8a\x01\x12&\xbf\x9d\xceYx\x00\x00\u07d4s\xbf\xe7q\x0f1\u02b9I\xb7\xa2`O\xbfR9\xce\xe7\x90\x15\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94s\u03c0\xae\x96\x88\xe1X\x0eh\xe7\x82\xcd\b\x11\xf7\xaaIM,\x8a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\xe0\x94s\xd7&\x9f\xf0l\x9f\xfd3uL\xe5\x88\xf7J\x96j\xbb\xbb\xba\x8a\x01e\xc9fG\xb3\x8a \x00\x00\u07d4s\xd8\xfe\xe3\u02c6M\xce\"\xbb&\u029c/\bm^\x95\xe6;\x8965\u026d\xc5\u07a0\x00\x00\u07d4s\xdf<>yU\xf4\xf2\xd8Y\x83\x1b\xe3\x80\x00\xb1\ak8\x84\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4s\u48b6\f\U0010e2ef+w~\x17Z[\x1eM\f-\x8f\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94t\n\xf1\xee\xfd3e\u05cb\xa7\xb1,\xb1\xa6s\xe0j\arF\x8a\x04+\xf0kx\xed;P\x00\x00\xe0\x94t\v\xfdR\xe0\x16g\xa3A\x9b\x02\x9a\x1b\x8eEWj\x86\xa2\u06ca\x03\x8e\xba\xd5\xcd\xc9\x02\x80\x00\x00\u07d4t\x0fd\x16\x14w\x9d\u03e8\x8e\xd1\xd4%\xd6\r\xb4*\x06\f\xa6\x896\"\xc6v\b\x10W\x00\x00\u07d4t\x12\u027c0\xb4\xdfC\x9f\x021\x00\xe69$\x06j\xfdS\xaf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4t\x16\x93\xc3\x03vP\x85\x13\b \xcc+c\xe9\xfa\x92\x13\x1b\x89A\rXj \xa4\xc0\x00\x00\u07d4t!\xce[\xe3\x81s\x8d\u0703\xf0&!\x97O\xf0hly\xb8\x89Xx\x8c\xb9K\x1d\x80\x00\x00\u07d4t1j\xdf%7\x8c\x10\xf5v\u0574\x1aoG\xfa\x98\xfc\xe3=\x89\x128\x13\x1e\\z\xd5\x00\x00\u07d4t6Q\xb5^\xf8B\x9d\xf5\f\xf8\x198\xc2P\x8d\xe5\u0207\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t=\xe5\x00&\xcag\xc9M\xf5O\x06b`\xe1\xd1J\xcc\x11\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tE /\ft)z\x00N\xb3rj\xa6\xa8-\xd7\xc0/\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tK\x03\xbb\xa8X*\xe5I\x8e-\xc2-\x19\x94\x94g\xabS\xfc\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4tL\fw\xba\u007f#i \xd1\xe44\xde]\xa3>H\xeb\xf0,\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4tP\xff\u007f\x99\xea\xa9\x11bu\u07ach\xe4(\xdf[\xbc\u0639\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tV\u0172\xc5Cn>W\x10\b\x93?\x18\x05\xcc\xfe4\xe9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4tZ\u04eb\xc6\xee\xeb$qh\x9bS\x9ex\x9c\xe2\xb8&\x83\x06\x89=A\x94\xbe\xa0\x11\x92\x80\x00\xe0\x94tZ\xec\xba\xf9\xbb9\xb7Jg\xea\x1c\xe6#\xde6\x84\x81\xba\xa6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4t\\\xcf-\x81\x9e\u06fd\u07a8\x11{\\I\xed<*\x06n\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4tb\u021c\xaa\x9d\x8dx\x91\xb2T]\xef!otd\u057b!\x89\x05\xea\xedT\xa2\x8b1\x00\x00\u07d4td\x8c\xaa\xc7H\xdd\x13\\\xd9\x1e\xa1L(\xe1\xbdM\u007f\xf6\xae\x89\xa8\r$g~\xfe\xf0\x00\x00\xe0\x94tq\xf7.\xeb0\x06$\xeb(.\xabM\x03r<d\x9b\x1bX\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94tz\xbc\x96I\x05m9&\x04M(\u00ed\t\xed\x17\xb6}p\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4t\u007f\xf7\x94;q\xdcM\u0371f\x80x\xf8=\xd7\xccE \u0089\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94t\x80\xdeb%O+\xa8+W\x82\x19\xc0{\xa5\xbeC\r\xc3\u02ca\x01}\xa3\xa0L{>\x00\x00\x00\xe0\x94t\x84\xd2k\xec\xc1\xee\xa8\xc61^\xc3\xee\nE\x01\x17\u0706\xa0\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4t\x86:\xce\xc7]\x03\xd5>\x86\x0ed\x00/,\x16^S\x83w\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\x89\u030a\xbeu\u0364\xef\r\x01\xce\xf2`^G\xed\xa6z\xb1\x89\a?u\u0460\x85\xba\x00\x00\u07d4t\x8c(^\xf1#?\xe4\xd3\x1c\x8f\xb17\x833r\x1c\x12\xe2z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t\x90\x87\xac\x0fZ\x97\xc6\xfa\xd0!S\x8b\xf1\xd6\u0361\x8e\r\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\x95\xaex\xc0\xd9\x02a\xe2\x14\x0e\xf2\x061\x04s\x1a`\xd1\xed\x89\x01\xdbPq\x89%!\x00\x00\u07d4t\x9aJv\x8b_#rH\x93\x8a\x12\xc6#\x84{\xd4\xe6\x88\u0709\x03\xe73b\x87\x14 \x00\x00\u07d4t\x9a\xd6\xf2\xb5pk\xbe/h\x9aD\u0136@\xb5\x8e\x96\xb9\x92\x89\x05k\xc7^-c\x10\x00\x00\u07d4t\xa1\u007f\x06K4N\x84\xdbce\u0695\x91\xff\x16(%vC\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4t\xae\xec\x91]\xe0\x1c\u019b,\xb5\xa65o\xee\xa1FX\xc6\u0149\f\x9a\x95\xee)\x86R\x00\x00\u07d4t\xaf\xe5I\x02\xd6\x15x%v\xf8\xba\xac\x13\xac\x97\f\x05\x0fn\x89\t\xa1\xaa\xa3\xa9\xfb\xa7\x00\x00\u07d4t\xb7\xe0\"\x8b\xae\xd6YW\xae\xbbM\x91m3:\xae\x16O\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t\xbcJ^ E\xf4\xff\x8d\xb1\x84\xcf:\x9b\f\x06Z\xd8\a\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4t\xbc\xe9\xec86-l\x94\u032c&\xd5\xc0\xe1:\x8b;\x1d@\x8965&A\x04B\xf5\x00\x00\u07d4t\xbfzZ\xb5\x92\x93\x14\x9b\\`\xcf6Bc\xe5\xeb\xf1\xaa\r\x89\x06G\f>w\x1e<\x00\x00\xe0\x94t\xc7<\x90R\x8a\x15s6\xf1\xe7\xea b\n\xe5?\xd2G(\x8a\x01\xe6:.S\x8f\x16\xe3\x00\x00\u07d4t\u0464\xd0\xc7RN\x01\x8dN\x06\xed;d\x80\x92\xb5\xb6\xaf,\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94t\xd3f\xb0{/VG}|pw\xaco\xe4\x97\xe0\xebeY\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4t\xd3zQt{\xf8\xb7q\xbf\xbfC\x9493\xd1\x00\xd2\x14\x83\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\xd6q\u065c\xbe\xa1\xabW\x90cu\xb6?\xf4+PE\x1d\x17\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\xeb\xf4BVF\xe6\u03c1\xb1\t\xce{\xf4\xa2\xa6=\x84\x81_\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4t\xed3\xac\xf4?5\xb9\x8c\x920\xb9\xe6d.\xcbS0\x83\x9e\x89$\xf6\xdf\xfbI\x8d(\x00\x00\u07d4t\xef(i\xcb\xe6\b\x85`E\xd8\xc2\x04\x11\x18W\x9f\"6\xea\x89\x03<\xd6E\x91\x95n\x00\x00\u07d4t\xfcZ\x99\xc0\xc5F\x05\x03\xa1;\x05\tE\x9d\xa1\x9c\xe7\u0350\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4u\v\xbb\x8c\x06\xbb\xbf$\bC\xccux.\xe0/\b\xa9tS\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4u\x14\xad\xbd\xc6?H?0M\x8e\x94\xb6\u007f\xf30\x9f\x18\v\x82\x89!\u0120n-\x13Y\x80\x00\u0794u\x17\xf1l(\xd12\xbb@\xe3\xba6\u01ae\xf11\xc4b\xda\x17\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4u\x1a,\xa3Nq\x87\xc1c\u048e6\x18\xdb(\xb1<\x19m&\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94u\x1a\xbc\xb6\xcc\x030Y\x91\x18\x15\xc9o\u04516\n\xb0D-\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4u&\xe4\x82R\x9f\n\x14\xee\u0248q\xdd\xdd\x0er\x1b\f\u0662\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4u)\xf3y{\xb6\xa2\x0f~\xa6I$\x19\xc8L\x86vA\xd8\x1c\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94u*^\xe22a,\xd3\x00_\xb2n[Y}\xe1\x9fwk\xe6\x8a\x01'\xfc\xb8\xaf\xae \xd0\x00\x00\u07d4u,\x9f\xeb\xf4/f\xc4x{\xfa~\xb1|\xf53;\xbaPp\x89j\x99\xf2\xb5O\xddX\x00\x00\u07d4u930F\u07b1\xef<M\xafPa\x99\x93\xf4D\xe1\xdeh\x89@\x13\x8b\x91~\u07f8\x00\x00\u07d4uS\xaa#\xb6\x8a\xa5\xf5~\x13_\xe3\x9f\xdc#^\xac\xa8\u024c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94uZ`\xbfR/\xbd\x8f\xff\x97#Dk~4:phV~\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4u_X~^\xff\xf7s\xa2 rj\x13\xd0\xf2\x13\r\x9f\x89k\x8965\u026d\xc5\u07a0\x00\x00\u07d4ub\x18e\xb6Y\x13e`n\xd3x0\x8c-\x1d\xefO\",\x89\xa8\r$g~\xfe\xf0\x00\x00\xe0\x94ucl\xdb\x10\x90P\xe4=]n\xc4~5\x9e!\x8e\x85~\u028a\x04\u0632'l\x89b(\x00\x00\u07d4ufIab\xbaXCw\xbe\x04\nO\x87wzpz\xca\xeb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4uk\x84\xeb\x85\xfc\xc1\xf4\xfc\xdc\u00b0\x8d\xb6\xa8n\x13_\xbc%\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4uoE\xe3\xfai4z\x9a\x97:r^<\x98\xbcM\xb0\xb5\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4u{e\x87m\xbf)\xbf\x91\x1dO\x06\x92\xa2\u027e\xb1\x13\x98\b\x89\u07d3\xa5\x937\xd6\u0740\x00\u07d4u\u007f\xa5TF\xc4`\x96\x8b\xb7K^\xbc\xa9lN\xf2\xc7\t\u01497\b\xba\xed=h\x90\x00\x00\u07d4u\x80J\xacd\xb4\x19\x90\x83\x98)\x02\x99M\x9c^\u0602\x8f\x11\x89\x1e=\a\xb0\xa6 \xe4\x00\x00\u07d4u\x92\u019d\x06{Q\xb6\xccc\x9d\x11d\xd5W\x8c`\xd2\xd2D\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4u\xab\xe5'\x0f:x\xce\x00|\xf3\u007f\x8f\xbc\x04]H\x9b{\xb1\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94u\xacTp\x17\x13L\x04\xae\x1e\x11\xd6\x0ec\xec\x04\u044d\xb4\xef\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4u\xb0\xe9\xc9B\xa4\xf0\xf6\xf8m?\x95\xff\x99\x80\"\xfag\x96;\x89P\xc5\xe7a\xa4D\b\x00\x00\xe0\x94u\xb9V\x96\xe8\xecE\x10\xd5hh\xa7\xc1\xa75\u018b$H\x90\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u07d4u\xbe\x8f\xf6^W\x88\xae\u01b2\xa5-_\xa7\xb1\xe7\xa0;\xa6u\x89\x03\xab\xcd\xc54=t\x00\x00\u07d4u\xc1\x1d\x02M\x12\xaeHl\x10\x95\xb7\xa7\xb9\u012f>\x8e\u07b9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94u\xc1\xad#\xd2?$\xb3\x84\xd0\xc3\x14\x91w\xe8f\x97a\r!\x8a\x01\\[\xcdl(\x8b\xbd\x00\x00\u07d4u\xc2\xff\xa1\xbe\xf5I\x19\xd2\t\u007fz\x14-.\x14\xf9\xb0JX\x89\x90\xf3XP@2\xa1\x00\x00\u07d4u\xd6|\xe1N\x8d)\xe8\xc2\xff\u3051{\x93\v\x1a\xff\x1a\x87\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4u\xde~\x93R\xe9\v\x13\xa5\x9aXx\xff\xec\u01c3\x1c\xacM\x82\x89\x94\x89#z\u06daP\x00\x00\u07d4u\xf7S\x9d0\x9e\x909\x98\x9e\xfe.\x8b-\xbd\x86Z\r\xf0\x88\x89\x85[[\xa6\\\x84\xf0\x00\x00\u07d4v\b\xf47\xb3\x1f\x18\xbc\vd\u04c1\xae\x86\xfd\x97\x8e\u05f3\x1f\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94v\x0f\xf35N\x0f\u0793\x8d\x0f\xb5\xb8,\xef[\xa1\\=)\x16\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4v\x1an6,\x97\xfb\xbd|Yw\xac\xba-\xa7F\x876_I\x89\t\xf7J\xe1\xf9S\xd0\x00\x00\u07d4v\x1el\xae\xc1\x89\xc20\xa1b\xec\x00e0\x19>g\u03dd\x19\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94v\x1f\x8a:*\U00028f7e\x1d\xa0\t2\x1f\xb2\x97d\xebb\xa1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4v)\x98\xe1\xd7R'\xfc\xedzp\xbe\x10\x9aL\vN\xd8d\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4v-o0\u06b9\x915\xe4\xec\xa5\x1dRC\xd6\xc8b\x11\x02\u0549\x0fI\x89A\xe6d(\x00\x00\u07d4v3\x1e0yl\xe6d\xb2p\x0e\rASp\x0e\u0706\x97w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v8\x86\xe33\xc5o\xef\xf8[\xe3\x95\x1a\xb0\xb8\x89\xce&.\x95\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v:|\xba\xb7\rzd\u0427\xe5)\x80\xf6\x81G%\x93I\f\x89 \x86\xac5\x10R`\x00\x00\u07d4v>\xec\u0c0a\u021e2\xbf\xa4\xbe\xcev\x95\x14\xd8\xcb[\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4v@\xa3\u007f\x80R\x98\x15\x15\xbc\xe0x\u0693\xaf\xa4x\x9bW4\x89lk\x93[\x8b\xbd@\x00\x00\u0794vA\xf7\xd2j\x86\xcd\xdb+\xe10\x81\x81\x0e\x01\xc9\xc8<K \x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4vF\x92\xcc\xcb3@]\u042b\f3y\xb4\x9c\xaf\x8eb!\xba\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4vMR\x12&:\xffJ*\x14\xf01\xf0N\xc7I\u0708>E\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94vO\xc4mB\x8bm\xbc\"\x8a\x0f_U\xc9P\x8cw.\xab\x9f\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4vPn\xb4\xa7\x80\xc9Q\xc7J\x06\xb0=;\x83b\xf0\x99\x9dq\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94v[\xe2\xe1/b\x9ecI\xb9}!\xb6*\x17\xb7\xc80\xed\xab\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94vb\x81P\xe2\x99[['\x9f\xc8>\r\xd5\xf1\x02\xa6q\xdd\x1c\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4vk7Y\xe8yN\x92m\xacG=\x91:\x8f\xb6\x1a\xd0\xc2\u0249\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4vp\xb0/,<\xf8\xfdOG0\xf38\x1aq\xeaC\x1c3\u01c9\x0e~\xeb\xa3A\vt\x00\x00\u07d4vz\x03eZ\xf3`\x84\x1e\x81\r\x83\xf5\xe6\x1f\xb4\x0fL\xd1\x13\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4vz\u0190y\x1c.#E\x10\x89\xfelp\x83\xfeU\u07b6+\x89,s\xc97t,P\x00\x00\u07d4v\u007f\xd7y}Qi\xa0_sd2\x1c\x19\x84:\x8c4\x8e\x1e\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u0794v\x84o\r\xe0;Zv\x97\x1e\xad)\x8c\xdd\b\x84:K\xc6\u0188\xd7\x1b\x0f\u088e\x00\x00\xe0\x94v\x84\x98\x93N7\xe9\x05\xf1\xd0\xe7{D\xb5t\xbc\xf3\xecJ\xe8\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4v\x8c\xe0\u06a0)\xb7\xde\xd0\"\xe5\xfcWM\x11\xcd\xe3\xec\xb5\x17\x89\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\xe0\x94v\x93\xbd\xebo\xc8+[\xcar\x13U\"1u\xd4z\bKM\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4v\xaa\xf8\xc1\xac\x01/\x87R\xd4\xc0\x9b\xb4f\a\xb6e\x1d\\\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4v\xab\x87\xddZ\x05\xad\x83\x9aN/\xc8\xc8Z\xa6\xba\x05d\x170\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v\xaf\xc2%\xf4\xfa0}\xe4\x84U+\xbe\x1d\x9d?\x15\aLJ\x89\xa2\x90\xb5\u01ed9h\x00\x00\xe0\x94v\xbe\xca\xe4\xa3\x1d6\xf3\xcbW\u007f*CYO\xb1\xab\xc1\xbb\x96\x8a\x05C\xa9\xce\x0e\x132\xf0\x00\x00\u07d4v\xc2u5\xbc\xb5\x9c\xe1\xfa-\x8c\x91\x9c\xab\xebJk\xba\x01\u0449lk\x93[\x8b\xbd@\x00\x00\u07d4v\xca\"\xbc\xb8y\x9eS'\u012a*}\tI\xa1\xfc\xce_)\x89R\xa0?\"\x8cZ\xe2\x00\x00\u07d4v\xca\u0108\x11\x1aO\u0555\xf5h\xae:\x85\x87p\xfc\x91]_\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94v\u02dc\x8bi\xf48vu\u0102S\xe24\xcb~\rt\xa4&\x8a\x01\x90\xf4H.\xb9\x1d\xae\x00\x00\u07d4v\xf8:\xc3\xda0\xf7\t&(\xc73\x9f \x8b\xfc\x14,\xb1\ue25a\x18\xff\xe7B}d\x00\x00\xe0\x94v\xf9\xad=\x9b\xbd\x04\xae\x05\\\x14w\xc0\xc3^u\x92\xcb* \x8a\b\x83?\x11\xe3E\x8f \x00\x00\u07d4v\xff\xc1W\xadk\xf8\xd5m\x9a\x1a\u007f\u077c\x0f\xea\x01\n\xab\xf4\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\x02\x8e@\x9c\xc4:;\xd3=!\xa9\xfcS\xec`n\x94\x91\x0e\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4w\f/\xb2\u0128\x17S\xac\x01\x82\xeaF\x0e\xc0\x9c\x90\xa5\x16\xf8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4w\r\x98\xd3\x1bCS\xfc\xee\xe8V\fL\u03c0>\x88\xc0\xc4\xe0\x89 \x86\xac5\x10R`\x00\x00\xe0\x94w\x13\xab\x807A\x1c\t\xbah\u007fo\x93d\xf0\xd3#\x9f\xac(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4w\x15\a\xae\xeej%]\xc2\u035d\xf5QT\x06-\b\x97\xb2\x97\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4w\x19\x88\x87\x95\xadtY$\xc7W`\u0771\x82}\xff\xd8\u0368\x89lkLM\xa6\u077e\x00\x00\u07d4w'\xaf\x10\x1f\n\xab\xa4\xd2:\x1c\xaf\xe1|n\xb5\u06b1\xc6\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4w,)\u007f\n\u0454H.\xe8\xc3\xf06\xbd\xeb\x01\xc2\x01\xd5\u0309\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94w0o\xfe.J\x8f<\xa8&\xc1\xa2I\xf7!-\xa4:\xef\xfd\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4w1A\x12}\x8c\xf3\x18\xae\xbf\x886Z\xdd=U'\xd8[j\x8966\u05ef^\u024e\x00\x00\u07d4wF\xb6\xc6i\x9c\x8f4\xca'h\xa8 \xf1\xff\xa4\xc2\a\xfe\x05\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4wQ\xf3c\xa0\xa7\xfd\x053\x19\b\t\u076f\x93@\xd8\xd1\x12\x91\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4wW\xa4\xb9\xcc=\x02G\u032a\xeb\x99\t\xa0\xe5n\x1d\xd6\xdc\u0089\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4w\\\x10\xc9>\r\xb7 [&CE\x823\xc6O\xc3?\xd7[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4wa~\xbcK\xeb\xc5\xf5\xdd\xeb\x1bzp\xcd\xebj\xe2\xff\xa0$\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4wiC\xff\xb2\xef\\\xdd5\xb8<(\xbc\x04k\xd4\xf4gp\x98\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94wp\x1e,I=\xa4|\x1bX\xf4!\xb5I]\xeeE\xbe\xa3\x9b\x8a\x01H\xf6I\xcfaB\xa5\x80\x00\u07d4wy\x8f \x12W\xb9\xc3R\x04\x95pW\xb5Ft\xae\xfaQ\u07c9\b\x13\xcaV\x90m4\x00\x00\u07d4w\x8cC\xd1\x1a\xfe;Xo\xf3t\x19-\x96\xa7\xf2=+\x9b\u007f\x89\x8b\xb4\xfc\xfa;}k\x80\x00\u07d4w\x8cy\xf4\xde\x19S\xeb\u0398\xfe\x80\x06\xd5:\x81\xfbQ@\x12\x8963\x03\"\xd5#\x8c\x00\x00\u07d4w\x92t\xbf\x18\x03\xa36\xe4\u04f0\r\u0753\xf2\xd4\xf5\xf4\xa6.\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\xa1q\"\xfa1\xb9\x8f\x17\x11\xd3*\x99\xf0>\xc3&\xf3=\b\x89\\(=A\x03\x94\x10\x00\x00\u07d4w\xa3I\a\xf3\x05\xa5L\x85\xdb\t\xc3c\xfd\xe3\xc4~j\xe2\x1f\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4w\xa7i\xfa\xfd\xec\xf4\xa68v-[\xa3\x96\x9d\xf61 \xa4\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4w\xbekd\xd7\xc73\xa46\xad\xec^\x14\xbf\x9a\xd7@+\x1bF\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\xbf\xe9<\u0367P\x84~A\xa1\xaf\xfe\xe6\xb2\u0696\xe7!N\x89\x10CV\x1a\x88)0\x00\x00\u07d4w\u0126\x97\xe6\x03\xd4+\x12\x05l\xbb\xa7a\xe7\xf5\x1d\x04C\xf5\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4w\xcc\x02\xf6#\xa9\u03d8S\t\x97\xeag\xd9\\;I\x18Y\xae\x89Is\x03\xc3n\xa0\xc2\x00\x00\u07d4w\xd4?\xa7\xb4\x81\xdb\xf3\xdbS\f\xfb\xf5\xfd\xce\xd0\xe6W\x181\x89lk\x93[\x8b\xbd@\x00\x00\u07d4w\xda^lr\xfb6\xbc\xe1\xd9y\x8f{\xcd\xf1\u044fE\x9c.\x89\x016\x95\xbbl\xf9>\x00\x00\u07d4w\xf4\xe3\xbd\xf0V\x88<\xc8r\x80\xdb\xe6@\xa1\x8a\r\x02\xa2\a\x89\n\x81\x99:+\xfb[\x00\x00\u0794w\xf6\t\u0287 \xa0#&,U\xc4o-&\xfb90\xaci\x88\xf0\x15\xf2W6B\x00\x00\u07d4w\xf8\x1b\x1b&\xfc\x84\xd6\u0797\uf2df\xbdr\xa310\xccJ\x8965\u026d\xc5\u07a0\x00\x00\u07d4x\x19\xb0E\x8e1N+S\xbf\xe0\f8I_\u0539\xfd\xf8\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4x\x1b\x15\x01dz.\x06\xc0\xedC\xff\x19\u007f\xcc\xec5\xe1p\v\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4x/R\xf0\xa6v\xc7w\x16\xd5t\xc8\x1e\xc4hO\x9a\x02\n\x97\x89.\x14\xe2\x06\xb70\xad\x80\x00\u07d4x5]\xf0\xa20\xf8=\x03,p1TAM\xe3\xee\u06b5W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4x6\xf7\xefk\u01fd\x0f\xf3\xac\xafD\x9c\x84\xddk\x1e,\x93\x9f\x89\xe0\x8d\xe7\xa9,\xd9|\x00\x00\u07d4x7\xfc\xb8v\xda\x00\xd1\xeb;\x88\xfe\xb3\xdf?\xa4\x04/\xac\x82\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4x>\uc2a5\xda\xc7{.f#\xedQ\x98\xa41\xab\xba\xee\a\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4x\\\x8e\xa7t\xd70D\xa74\xfay\n\x1b\x1et>w\xed|\x89\f\xf1Rd\f\\\x83\x00\x00\u07d4x`\xa3\xde8\xdf8*\xe4\xa4\xdc\xe1\x8c\f\a\xb9\x8b\xce=\xfa\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94xcCq\xe1s\x04\xcb\xf39\xb1E*L\xe48\xdcvL\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4xd\u0719\x9f\xe4\xf8\xe0\x03\xc0\xf4=\xec\u00da\xae\x15\"\xdc\x0f\x89\x05\x1e\x10+\xd8\xec\xe0\x00\x00\u07d4xtj\x95\x8d\xce\xd4\xc7d\xf8vP\x8cAJh4,\uce49\x02\xbe7O\xe8\xe2\xc4\x00\x00\xe0\x94x}1?\xd3k\x05>\xee\xae\xdb\xcet\xb9\xfb\x06x32\x89\x8a\x05\xc0X\xb7\x84'\x19`\x00\x00\u07d4x\x85\x9c[T\x8bp\r\x92\x84\xce\xe4\xb6c</R\xe5)\u0089\xa00\xdc\xeb\xbd/L\x00\x00\xe0\x94x\x8e\x80\x97A\xa3\xb1J\"\xa4\xb1\xd97\xc8,\xfe\xa4\x89\uef8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4x\xa1\xe2T@\x9f\xb1\xb5Z|\xb4\u074e\xba;0\u023a\xd9\xef\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94x\xa5\xe8\x99\x00\xbd?\x81\xddq\xba\x86\x9d%\xfe\xc6Ra\xdf\x15\x8a\n\xfd\x81/\xee\x03\xd5p\x00\x00\xe0\x94x\xb9x\xa9\xd7\xe9\x1e\xe5)\xeaO\u0137o\xea\xf8v/i\x8c\x8a\x06\u01b95\xb8\xbb\xd4\x00\x00\x00\xe0\x94x\xce>=GJ\x8a\x04{\x92\xc4\x15B$-\n\b\xc7\x0f\x99\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4x\u03c36\xb3(\xdb=\x87\x81:G+\x9e\x89\xb7^\f\xf3\xbc\x8965\u026d\xc5\u07a0\x00\x00\u07d4x\xd4\xf8\xc7\x1c\x1eh\xa6\x9a\x98\xf5/\xcbE\u068a\xf5n\xa1\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4x\xdf&\x81\xd6\xd6\x02\xe2!B\xd5A\x16\u07a1]EIW\xaa\x89\x10'\x94\xad \xdah\x00\x00\u07d4x\xe0\x8b\xc53A<&\u2473\x14?\xfa|\u026f\xb9{x\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4x\xe8?\x80\xb3g\x8cz\nN>\x8c\x84\xdc\xcd\xe0dBbw\x89a\t=|,m8\x00\x00\u07d4x\xf5\xc7G\x85\xc5f\x8a\x83\x80r\x04\x8b\xf8\xb4SYM\u06ab\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4y\x0f\x91\xbd]\x1c\\\xc4s\x9a\xe9\x13\x00\u06c9\xe1\xc10<\x93\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\x17\u5f42\xa9y\x0f\xd6P\xd0C\xcd\xd90\xf7y\x963\u06c9\xd8\xd4`,&\xbfl\x00\x00\u07d4y\x19\xe7b\u007f\x9b}T\xea;\x14\xbbM\xd4d\x9fO9\xde\xe0\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4y\x1f`@\xb4\xe3\xe5\r\xcf5S\xf1\x82\u0357\xa9\x060\xb7]\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4y0\xc2\xd9\xcb\xfa\x87\xf5\x10\xf8\xf9\x87w\xff\x8a\x84H\xcaV)\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4yE)\u041d\x01rq5\x970\x02pu\xb8z\xd8=\xaen\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4yKQ\u00deS\xd9\xe7b\xb0a;\x82\x9aD\xb4r\xf4\xff\xf3\x89$5\xe0dxA\u0300\x00\xe0\x94yU\x1c\xed\xe3v\xf7G\xe3ql\x8dy@\rvm.\x01\x95\x8a\t\xcb7\xaf\xa4\xffxh\x00\x00\u07d4y^\xbc&&\xfc9\xb0\xc8b\x94\xe0\xe87\xdc\xf5#U0\x90\x8965\u026d\xc5\u07a0\x00\x00\u07d4yn\xbb\xf4\x9b>6\xd6v\x94\xady\xf8\xff6vz\xc6\xfa\xb0\x89\x03K\xc4\xfd\xde'\xc0\x00\x00\u07d4yo\x87\xbaaz)0\xb1g\v\xe9.\xd1(\x1f\xb0\xb3F\xe1\x89\x06\xf5\xe8o\xb5((\x00\x00\u07d4yt'\xe3\xdb\xf0\xfe\xaez%\x06\xf1-\xf1\xdc@2n\x85\x05\x8965\u026d\xc5\u07a0\x00\x00\u07d4yu\x10\xe3\x86\xf5c\x93\xce\xd8\xf4w7\x8aDLHO}\xad\x8965\u026d\xc5\u07a0\x00\x00\u07d4y{\xb7\xf1W\xd9\xfe\xaa\x17\xf7m\xa4\xf7\x04\xb7M\xc1\x03\x83A\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4y\x88\x90\x131\xe3\x87\xf7\x13\xfa\u03b9\x00\\\xb9\xb6Q6\xeb\x14\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4y\x89\u041f8&\xc3\u5bccu*\x81\x15r:\x84\xd8\tp\x89\x16\x86\xf8aL\xf0\xad\x00\x00\xe0\x94y\x95\xbd\x8c\xe2\xe0\xc6{\xf1\u01e51\xd4w\xbc\xa1\xb2\xb9ua\x8a\x01BH\xd6\x17\x82\x9e\xce\x00\x00\u07d4y\xae\xb3Ef\xb9t\xc3ZX\x81\xde\xc0 \x92}\xa7\xdf]%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\xb1 \xeb\x88\x06s#!(\x8fgZ'\xa9\"_\x1c\xd2\ub245\xa0\xbf7\xde\xc9\xe4\x00\x00\u07d4y\xb4\x8d-a7\u00c5Ma\x1c\x01\xeaBBz\x0fY{\xb7\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4y\xb8\xaa\xd8y\xdd0V~\x87x\xd2\xd21\xc8\xf3z\xb8sN\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\xbf/{n2\x8a\xaf&\xe0\xbb\t?\xa2-\xa2\x9e\xf2\xf4q\x89a\t=|,m8\x00\x00\u07d4y\xc10\xc7b\xb8v[\x19\u04ab\u0260\x83\xab\x8f:\xady@\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4y\xc1\xbe\x19q\x1fs\xbe\xe4\xe61j\xe7T\x94Y\xaa\u03a2\xe0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4y\xc6\x00/\x84R\xca\x15\u007f\x13\x17\xe8\n/\xaf$GUY\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4y\xca\xc6IO\x11\xef'\x98t\x8c\xb52\x85\xbd\x8e\"\xf9|\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4y\u03e9x\n\xe6\xd8{,1\x88?\t'i\x86\u021ag5\x8965\u026d\xc5\u07a0\x00\x00\u07d4y\u06e2VG-\xb4\xe0X\xf2\xe4\xcd\xc3\xeaN\x8aBw83\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4y\xed\x10\xcf\x1fm\xb4\x82\x06\xb5\t\x19\xb9\xb6\x97\b\x1f\xbd\xaa\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u0794y\xf0\x8e\x01\xce\t\x88\xe6<\u007f\x8f)\b\xfa\xdeC\xc7\xf9\xf5\u0248\xfc\x93c\x92\x80\x1c\x00\x00\u07d4y\xfdmH1Pf\xc2\x04\xf9e\x18i\xc1\tl\x14\xfc\x97\x81\x89lk\x93[\x8b\xbd@\x00\x00\u0794y\xff\xb4\xac\x13\x81*\vx\u0123{\x82u\">\x17k\xfd\xa5\x88\xf0\x15\xf2W6B\x00\x00\u07d4z\x05\x89\xb1C\xa8\xe5\xe1\a\u026cf\xa9\xf9\xf8Yz\xb3\u7ac9Q\xe92\xd7n\x8f{\x00\x00\u07d4z\nx\xa9\xcc9?\x91\xc3\xd9\xe3\x9ak\x8c\x06\x9f\a^k\xf5\x89Hz\x9a0E9D\x00\x00\u07d4z\x13p\xa7B\xec&\x87\xe7a\xa1\x9a\u0167\x942\x9e\xe6t\x04\x89\xa2\xa12ga\xe2\x92\x00\x00\xe0\x94z-\xfcw\x0e$6\x811\xb7\x84w\x95\xf2\x03\xf3\xd5\r[V\x8a\x02i\xfe\xc7\xf06\x1d \x00\x00\u07d4z3\x83N\x85\x83s>-R\xae\xadX\x9b\u046f\xfb\x1d\xd2V\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94z6\xab\xa5\xc3\x1e\xa0\xca~'{\xaa2\xecF\u0393\xcfu\x06\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94z8\x11\"\xba\xday\x1az\xb1\xf6\x03}\xac\x80C'S\xba\xad\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94zH\xd8w\xb6:\x8f\x8f\x93\x83\xe9\xd0\x1eS\xe8\fR\x8e\x95_\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4zO\x9b\x85\x06\x90\xc7\xc9F\x00\xdb\xee\f\xa4\xb0\xa4\x11\xe9\xc2!\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4zc\x86\x9f\xc7g\xa4\u01b1\xcd\x0e\x06I\xf3cL\xb1!\xd2K\x89\x043\x87Oc,\xc6\x00\x00\u07d4zg\xdd\x04:PO\xc2\xf2\xfcq\x94\xe9\xbe\xcfHL\xec\xb1\xfb\x89\r\x8drkqw\xa8\x00\x00\xe0\x94zk&\xf48\u0663RD\x91U\xb8\x87l\xbd\x17\xc9\u065bd\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4zmx\x1cw\u013a\x1f\xca\xdfhsA\xc1\xe3\x17\x99\xe9='\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4zph\xe1\xc37\\\x0eY\x9d\xb1\xfb\xe6\xb2\xea#\xb8\xf4\a\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4zt\xce\xe4\xfa\x0fcp\xa7\x89O\x11l\xd0\f\x11G\xb8>Y\x89+^:\xf1k\x18\x80\x00\x00\u07d4zy\xe3\x0f\xf0W\xf7\n=\x01\x91\xf7\xf5?v\x157\xaf}\xff\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94zzO\x80sW\xa4\xbb\xe6\x8e\x1a\xa8\x0692\x10\xc4\x11\u0333\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4z\x85c\x86y\x01 o?+\xf0\xfa>\x1c\x81\t\u02bc\u0345\x89\amA\xc6$\x94\x84\x00\x00\xe0\x94z\x87\x97i\n\xb7{Tp\xbf|\f\x1b\xbaa%\b\xe1\xac}\x8a\x01\xe0\x92\x96\xc37\x8d\xe4\x00\x00\u07d4z\x8c\x89\xc0\x14P\x9dV\u05f6\x810f\x8f\xf6\xa3\xec\xecsp\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94z\x94\xb1\x99\x92\u03b8\xcec\xbc\x92\xeeKZ\xde\xd1\fM\x97%\x8a\x03\x8d\x1a\x80d\xbbd\xc8\x00\x00\u07d4z\xa7\x9a\xc0C\x16\u030d\b\xf2\x00e\xba\xa6\xd4\x14(\x97\xd5N\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4z\xadM\xbc\u04ec\xf9\x97\u07d3XiV\xf7+d\u062d\x94\xee\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94z\xb2V\xb2\x04\x80\n\xf2\x017\xfa\xbc\xc9\x16\xa22Xu%\x01\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4z\xbaV\xf6:H\xbc\b\x17\u05b9p9\x03\x9az\xd6/\xae.\x89 \x86\xac5\x10R`\x00\x00\xe0\x94z\xbb\x10\xf5\xbd\x9b\xc3;\x8e\xc1\xa8-d\xb5[k\x18wuA\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4z\u010d@\xc6d\u031am\x89\xf1\xc5\xf5\xc8\n\x1cp\xe7D\u6263\x10b\xbe\xee\xd7\x00\x00\x00\u07d4z\u014fo\xfcO\x81\a\xaen07\x8eN\x9f\x99\xc5\u007f\xbb$\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4z\xd3\xf3\aao\x19\u0731C\xe6DM\xab\x9c<3a\x1fR\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4z\xd8,\xae\xa1\xa8\xb4\xed\x051\x9b\x9c\x98p\x17<\x81N\x06\xee\x89!d\xb7\xa0J\u0220\x00\x00\u07d4z\xde]f\xb9D\xbb\x86\f\x0e\xfd\xc8bv\u054fFS\xf7\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d4z\xdf\xed\xb0m\x91\xf3\xccs\x90E\v\x85U\x02p\x88<{\xb7\x89\x11x\xfa@Q]\xb4\x00\x00\u07d4z\xe1\xc1\x9eS\xc7\x1c\xeeLs\xfa\xe2\xd7\xfcs\xbf\x9a\xb5\u348965\u026d\xc5\u07a0\x00\x00\u07d4z\xe6Y\xeb;\xc4hR\xfa\x86\xfa\xc4\xe2\x1cv\x8dP8\x89E\x89\x0f\x81\f\x1c\xb5\x01\xb8\x00\x00\u07d4z\xea%\xd4+&\x12(n\x99\xc56\x97\u01bcA\x00\xe2\u06ff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4z\xef{U\x1f\v\x9cF\xe7U\xc0\xf3\x8e[:s\xfe\x11\x99\xf5\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4{\v1\xffn$t^\xad\x8e\u067b\x85\xfc\v\xf2\xfe\x1dU\u0509+^:\xf1k\x18\x80\x00\x00\xe0\x94{\x0f\xea\x11v\xd5!Y3:\x14<)IC\xda6\xbb\u0774\x8a\x01\xfc}\xa6N\xa1L\x10\x00\x00\u07d4{\x11g<\xc0\x19bk)\f\xbd\xce&\x04o~m\x14\x1e!\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4{\x12!b\xc9\x13\xe7\x14l\xad\v~\xd3z\xff\xc9*\v\xf2\u007f\x89Q\xaf\tk#\x01\u0440\x00\u07d4{\x1b\xf5:\x9c\xbe\x83\xa7\u07a44W\x9f\xe7*\xac\x8d*\f\u0409\n\xd4\xc81j\v\f\x00\x00\u07d4{\x1d\xaf\x14\x89\x1b\x8a\x1e\x1b\xd4)\u0633k\x9aJ\xa1\u066f\xbf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4{\x1f\xe1\xabM\xfd\x00\x88\xcd\xd7\xf6\x01c\xefY\xec*\xee\x06\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4{%\xbb\x9c\xa8\xe7\x02!~\x933\"RP\xe5<6\x80MH\x89e\xea=\xb7UF`\x00\x00\u07d4{'\xd0\xd1\xf3\xdd<\x14\x02\x94\xd0H\x8bx>\xbf@\x15'}\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94{@\a\xc4^ZW?\u06f6\xf8\xbdtk\xf9J\xd0J<&\x8a\x038!\xf5\x13]%\x9a\x00\x00\u07d4{C\xc7\xee\xa8\xd6#U\xb0\xa8\xa8\x1d\xa0\x81\xc6Dk3\xe9\xe0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4{M*8&\x90i\xc1\x85Ww\rY\x1d$\xc5\x12\x1f^\x83\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94{au\xec\x9b\xef\xc78$\x955\xdd\xde4h\x8c\xd3n\xdf%\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94{f\x12hy\x84M\xfa4\xfee\xc9\xf2\x88\x11\u007f\xef\xb4I\xad\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4{j\x84q\x8d\xd8nc3\x84)\xac\x81\x1d|\x8a\x86\x0f!\xf1\x89a\t=|,m8\x00\x00\xe0\x94{q,z\xf1\x16v\x00jf\xd2\xfc\\\x1a\xb4\xc4y\xce`7\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4{s$-u\u029a\xd5X\xd6P)\r\xf1v\x92\xd5L\u0638\x89lnY\xe6|xT\x00\x00\u07d4{v\x1f\xeb\u007f\u03e7\xde\xd1\xf0\xeb\x05\x8fJ`\v\xf3\xa7\b\u02c9\xf9]\xd2\xec'\xcc\xe0\x00\x00\xe0\x94{\x82|\xae\u007f\xf4t\t\x18\xf2\xe00\xab&\u02d8\xc4\xf4l\xf5\x8a\x01\x94hL\v9\xde\x10\x00\x00\xe0\x94{\x892\x86B~r\xdb!\x9a!\xfcM\xcd_\xbfY(<1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4{\x92&\xd4o\xe7Q\x94\v\xc4\x16\xa7\x98\xb6\x9c\xcf\r\xfa\xb6g\x89\u3bb5sr@\xa0\x00\x00\u07d4{\x98\xe2<\xb9k\xee\xe8\n\x16\x80i\ube8f \xed\xd5\\\u03c9\v\xa0\xc9\x15\x87\xc1J\x00\x00\u07d4{\xb0\xfd\xf5\xa6c\xb5\xfb\xa2\x8d\x9c\x90*\xf0\xc8\x11\xe2R\xf2\x98\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4{\xb9W\x1f9K\v\x1a\x8e\xbaVd\xe9\u0635\xe8@g{\xea\x89\x01\x11du\x9f\xfb2\x00\x00\xe0\x94{\xb9\x84\xc6\u06f9\xe2y\x96j\xfa\xfd\xa5\x9c\x01\xd0&'\xc8\x04\x8a\x01\xb4d1\x1dE\xa6\x88\x00\x00\u07d4{\xbb\xec^p\xbd\xea\u063b2\xb4(\x05\x98\x8e\x96H\xc0\xaa\x97\x8966\u05ef^\u024e\x00\x00\u07d4{\xca\x1d\xa6\xc8\nf\xba\xa5\xdbZ\u0245A\u013e'kD}\x89$\xcf\x04\x96\x80\xfa<\x00\x00\u07d4{\u0772\xee\x98\xde\x19\xeeL\x91\xf6a\xee\x8eg\xa9\x1d\x05K\x97\x8965\u026d\xc5\u07a0\x00\x00\u0794{\xe2\xf7h\f\x80-\xa6\x15L\x92\xc0\x19J\xe72Qzqi\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4{\xe7\xf2Eiq\x88;\x9a\x8d\xbeL\x91\xde\xc0\x8a\xc3N\x88b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4{\xe8\u0334\xf1\x1bf\xcan\x1dW\xc0\xb59b!\xa3\x1b\xa5:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94{\xeb\x81\xfb/^\x91Rk*\xc9y^v\u019b\xcf\xf0K\xc0\x8a\x0e\xb2.yO\n\x8d`\x00\x00\u07d4|\b\x83\x05L-\x02\xbcz\x85+\x1f\x86\xc4'w\xd0\xd5\xc8V\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4|\x0f^\a C\xc9\xeet\x02B\x19~x\xccK\x98\xcd\xf9`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|\x1d\xf2JO\u007f\xb2\u01f4r\xe0\xbb\x00l\xb2}\xcd\x16AV\x8965\u026d\xc5\u07a0\x00\x00\u07d4|)\xd4}W\xa73\xf5k\x9b!pc\xb5\x13\xdc;1Y#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4|+\x96\x03\x88JO.FN\u03b9}\x17\x93\x8d\x82\x8b\xc0,\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4|8,\x02\x96a.N\x97\xe4@\xe0-8q';U\xf5;\x89\n\xb6@9\x12\x010\x00\x00\u07d4|>\xb7\x13\xc4\xc9\xe08\x1c\xd8\x15L|\x9a}\xb8d\\\xde\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|D\x01\xae\x98\xf1.\xf6\xde9\xae$\u03df\xc5\x1f\x80\xeb\xa1k\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|E\xf0\xf8D*V\xdb\u04dd\xbf\x15\x99\x95A\\R\xedG\x9b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94|S-\xb9\xe0\xc0l&\xfd@\xac\xc5j\xc5\\\x1e\xe9-<:\x8a?\x87\bW\xa3\xe0\xe3\x80\x00\x00\u07d4|`\xa0_zJ_\x8c\xf2xC\x916.uZ\x83A\xefY\x89f\x94\xf0\x18*7\xae\x00\x00\u07d4|`\xe5\x1f\v\xe2(\xe4\xd5o\xdd)\x92\xc8\x14\xdaw@\u01bc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|i$\xd0|>\xf5\x89\x19f\xfe\nxV\xc8{\xef\x9d 4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94|\x8b\xb6Zo\xbbI\xbdA3\x96\xa9\xd7\xe3\x10S\xbb\xb3z\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94|\x9a\x11\f\xb1\x1f%\x98\xb2\xb2\x0e,\xa4\x002^A\xe9\xdb3\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4|\xbc\xa8\x8f\xcaj\x00`\xb9`\x98\\\x9a\xa1\xb0%4\xdc\"\b\x89\x19\x12z\x13\x91\xea*\x00\x00\u07d4|\xbe\xb9\x992\xe9~n\x02\x05\x8c\xfcb\u0432k\xc7\u0325+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4|\xc2Jj\x95\x8c \xc7\xd1$\x96`\xf7Xb&\x95\v\r\x9a\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4|\xd2\x0e\u0335\x18\xb6\f\xab\t[r\x0fW\x15p\u02aaD~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4|\xd5\xd8\x1e\xab7\xe1\x1ebv\xa3\xa1\t\x12Q`~\r~8\x89\x03hM^\xf9\x81\xf4\x00\x00\u07d4|\xdft!9E\x95=\xb3\x9a\xd0\xe8\xa9x\x1a\xddy.M\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4|\xe4hdF\U000547be\xd6r\x15\xeb\rZ\x1d\xd7,\x11\xb8\x89x9\xd3!\xb8\x1a\xb8\x00\x00\u07d4|\xefMC\xaaA\u007f\x9e\xf8\xb7\x87\xf8\xb9\x9dS\xf1\xfe\xa1\ue209g\x8a\x93 b\xe4\x18\x00\x00\u07d4}\x03P\xe4\v3\x8d\xdasfa\x87+\xe3?\x1f\x97R\xd7U\x89\x02\xb4\xf5\xa6\U00051500\x00\xe0\x94}\x04\xd2\xed\xc0X\xa1\xaf\xc7a\xd9\u025a\xe4\xfc\\\x85\xd4\u0226\x8aB\xa9\xc4g\\\x94g\xd0\x00\x00\u07d4}\v%^\xfbW\xe1\x0fp\b\xaa\"\xd4\x0e\x97R\xdf\xcf\x03x\x89\x01\x9f\x8euY\x92L\x00\x00\xe0\x94}\x13\xd6pX\x84\xab!W\u074d\xccpF\xca\xf5\x8e\xe9K\xe4\x8a\x1d\r\xa0|\xbb>\xe9\xc0\x00\x00\u07d4}'>c~\xf1\xea\u0101\x11\x94\x13\xb9\x1c\x98\x9d\xc5\xea\xc1\"\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4}*R\xa7\xcf\f\x846\xa8\xe0\a\x97kl&\xb7\"\x9d\x1e\x15\x89\x17\xbf\x06\xb3*$\x1c\x00\x00\u07d4}4\x805i\xe0\v\u05b5\x9f\xff\b\x1d\xfa\\\n\xb4\x19zb\x89\\\xd8|\xb7\xb9\xfb\x86\x00\x00\u07d4}4\xffY\xae\x84\nt\x13\u01baL[\xb2\xba,u\xea\xb0\x18\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4}9(R\xf3\xab\xd9/\xf4\xbb[\xb2l\xb6\bt\xf2\xbeg\x95\x8966\xc2^f\xec\xe7\x00\x00\u07d4}DRg\u015a\xb8\u04a2\xd9\xe7\t\x99\x0e\th%\x80\u011f\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94}U\x13\x97\xf7\x9a)\x88\xb0d\xaf\xd0\xef\xeb\xee\x80,w!\xbc\x8a\bW\xe0\xd6\xf1\xdav\xa0\x00\x00\u07d4}Z\xa3?\xc1KQ\x84\x1a\x06\x90n\xdb+\xb4\x9c*\x11ri\x89\x10D\x00\xa2G\x0eh\x00\x00\xe0\x94}]/s\x94\x9d\xad\xda\bV\xb2\x06\x98\x9d\xf0\a\x8dQ\xa1\xe5\x8a\x02<upr\xb8\xdd\x00\x00\x00\xe0\x94}n\x99\r\xaaq\x05\xde%&3\x983\xf7{\\\v\x85\xd8O\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4}s\x8608\xcc\xca\"\xf9j\xff\xda\x10InQ\xe1\xe6\xcdH\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4}}\xd5\xeeaM\xbbo\xbf\xbc\xd2c\x05$z\x05\x8cA\xfa\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4}~|aw\x9a\xdbw\x06\xc9M2@\x9a+\xb4\u953f`\x89.\xf2\r\x9f\xc7\x1a\x14\x00\x00\u07d4}\x82\xe5#\xcc-\u0151\xda9T\u8dbb,\xafda\u6709}\x8d\xc2\xef\xff\xb1\xa9\x00\x00\u07d4}\x85\x84\x93\xf0t\x15\xe0\x91-\x05y<\x97!\x13\xea\u8b88\x89b\x8d\xd1w\u04bc(\x00\x00\u07d4}\x90\x1b(\xbf\u007f\x88\xefs\xd8\xf7<\u0297VI\x13\xea\x8a$\x893\xc5I\x901r\f\x00\x00\u07d4}\x98\x0fKVk\xb0EQ~L\x14\xc8wP\u0793FtK\x89Hz\x9a0E9D\x00\x00\xe0\x94}\x9cYc\x1e+\xa2\xe8\xe8(\x91\xf3\x97\x99\"\xaa\xa3\xb5g\xa1\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4}\x9d\"\x1a=\xf8\x9d\xdd{_a\xc1F\x8cg\x87\u05b33\xe6\x89\a{\"|\xd8;\xe8\x00\x00\u07d4}\xa7a4E\xa2\x12\x99\xaat\xf0\xadqC\x1e\xc4?\xbb\x1b\xe9\x89\x03\xaf\xb0\x87\xb8v\x90\x00\x00\u07d4}\xb4\xc7\u0577\x97\xe9)nc\x82\xf2\x03i=\xb4\tD\x9db\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4}\xb9\xea\xccR\xe4)\u0703\xb4a\xc5\xf4\xd8`\x10\xe58:(\x8965\u026d\xc5\u07a0\x00\x00\u07d4}\xd4m\xa6w\xe1a\x82^\x12\xe8\r\xc4F\xf5\x82v\xe1\x12|\x89,s\xc97t,P\x00\x00\u07d4}\xd8\u05e1\xa3O\xa1\xf8\xe7<\xcb\x00_\u00a0:\x15\xb8\"\x9c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4}\xddW\x16\\\x87\xa2p\u007f\x02]\xcf\xc2P\x8c\t\x83GY\xbc\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4}\xe4B\xc8#\x86\x15M.\x99<\xbd\x12\x80\xbb|\xa6\xb1*\u0689\xd8\xf2\xe8$~\xc9H\x00\x00\u07d4}\xe7\xfeA\x9c\xc6\x1f\x91\xf4\b\xd24\u0300\xd5\xca=\x05M\x99\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4}\xec\u664a\xe1\x90\r\xd3w\f\xf4\xb98\x12\xba\xd8O\x03\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4}\xfc4-\xff\xcfE\xdf\xeet\xf8L\t\x959{\u04661r\x89\r\x8drkqw\xa8\x00\x00\u07d4}\xfd)b\xb5u\xbc\xbe\xee\x97\xf4\x91B\xd6<0\xab\x00\x9ff\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4~\x1e)r\x1dl\xb9\x10W\xf6\xc4\x04-\x8a\v\xbcdJ\xfes\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4~#ff\xb2\xd0nc\xeaN*\xb8CW\xe2\xdf\xc9w\xe5\x0e\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94~$\xd9\xe2,\xe1\xda<\xe1\x9f!\x9c\xce\xe5#7hs\xf3g\x8a\x01?\xd9\a\x9c\xaa`\xff\x00\x00\u07d4~$\xfb\xda\u0490\x17^\xb2\xdfm\x18\n\x19\xb9\xa9\xf4\x13p\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94~&\x8f\x13\x1d\xdfh|\xc3%\xc4\x12\xf7\x8b\xa9a ^\x91\x12\x8a\x03cd\xee}0\x1b<\x00\x00\u07d4~))\x008I5Y\x19N\x94mNF\v\x96\xfc8\xa1V\x89\x10\xc1<Rwc\x88\x00\x00\u07d4~+\xa8m\xa5.x]\x86%3O3\x97\xba\x1cK\xf2\xe8\u0449\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4~?c\xe11)\xa2!\xba\x1a\xb0c&4,\u064bQ&\xae\x89V\xa0&Y\xa5#4\x00\x00\u07d4~Gc~\x97\xc1F\"\x88+\xe0W\xbe\xa2)8o@R\xe5\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4~N\x94\tpA!\xd1\xd7y\x97\x02o\xf0n\xa9\xb1\x9a\x8b\x90\x89\x8d\x16T\x9e\u054f\xa4\x00\x00\u07d4~Y\xdc`\xbe\x8b/\xc1\x9a\xbd\nW\x82\xc5,(@\v\u0397\x8965\u026d\xc5\u07a0\x00\x00\u07d4~[\x19\xae\x1b\xe9O\xf4\xde\xe65I*\x1b\x01-\x14\xdb\x02\x13\x89\x05k\xc7^-c\x10\x00\x00\u07d4~]\x99\x93\x10NL\xb5E\xe1y\xa2\xa3\xf9q\xf7D\xf9\x84\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4~q\x17\x1f)I\xfa\f:\xc2T%K\x1f\x04@\xe5\xe6\xa08\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4~|\x1e\x9aa\xa0\x8a\x83\x98H5\xc7\x0e\xc3\x1d4\xd3\xea\xa8\u007f\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4~\u007f\x18\xa0.\u032a]a\xab\x8f\xbf\x03\x03C\xc44\xa2^\xf7\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4~\x81\xf6D\x9a\x037A\x91\xf3\xb7\xcb\x05\xd98\xb7.\t\r\xff\x89\x05k\xc7^-c\x10\x00\x00\u07d4~\x86I\xe6\x90\xfc\x8c\x1b\xfd\xa1\xb5\xe1\x86X\x1fd\x9bP\xfe3\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4~\x87\x86>\xc4:H\x1d\xf0M\x01wb\xed\xcb\\\xaab\x9bZ\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4~\x8f\x96\xcc)\xf5{\tu\x12\f\xb5\x93\xb7\u0743=`kS\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4~\x97*\x8a|*D\xc9;!Cl8\xd2\x1b\x92R\xc3E\xfe\x89a\t=|,m8\x00\x00\u07d4~\x99\u07fe\x98\x9d;\xa5)\u0457Q\xb7\xf41\u007f\x89S\xa3\xe2\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4~\xa0\xf9n\xe0\xa5s\xa30\xb5h\x97v\x1f=L\x010\xa8\xe3\x89Hz\x9a0E9D\x00\x00\u0794~\xa7\x91\xeb\xab\x04E\xa0\x0e\xfd\xfcNJ\x8e\x9a~ue\x13m\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4~\xab\xa05\xe2\xaf7\x93\xfdtgK\x10%@\xcf\x19\n\u0779\x89E\x02l\x83[`D\x00\x00\xe0\x94~\xb4\xb0\x18\\\x92\xb6C\x9a\b\xe72!h\xcb5<\x8awJ\x8a\x02'\x19l\xa0I\x83\xca\x00\x00\xe0\x94~\xbd\x95\xe9\xc4p\xf7(5\x83\xdcn\x9d,M\xce\v\ua3c4\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4~\u0425\xa8G\xbe\xf9\xa9\xda|\xba\x1dd\x11\xf5\xc3\x161&\x19\x89\x02(\xeb7\xe8u\x1d\x00\x00\u07d4~\xda\xfb\xa8\x98K\xafc\x1a\x82\vk\x92\xbb\xc2\xc56U\xf6\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4~\xdb\x02\xc6\x1a\"r\x87a\x1a\xd9Pici\xccNdzh\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4~\xe5\u0280]\xce#\xaf\x89\xc2\xd4D\xe7\xe4\af\xc5Lt\x04\x89\r\v\xd4\x12\xed\xbd\x82\x00\x00\xe0\x94~\xe6\x04\u01e9\xdc)\t\xce2\x1d\u6e72OWgWuU\x8a\x01+\xf9\u01d8\\\xf6-\x80\x00\u07d4~\xf1o\xd8\xd1[7\x8a\x0f\xba0k\x8d\x03\u0758\xfc\x92a\x9f\x89%\xf2s\x93=\xb5p\x00\x00\u07d4~\xf9\x8bR\xbe\xe9S\xbe\xf9\x92\xf3\x05\xfd\xa0'\xf8\x91\x1cXQ\x89\x1b\xe7\" i\x96\xbc\x80\x00\u07d4~\xfc\x90vj\x00\xbcR7,\xac\x97\xfa\xbd\x8a<\x83\x1f\x8e\u0349\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4~\xfe\xc0\xc6%<\xaf9\u007fq(|\x1c\a\xf6\xc9X+[\x86\x89\x1a,\xbc\xb8O0\u0540\x00\u07d4\u007f\x01\xdc|7G\xca`\x8f\x98=\xfc\x8c\x9b9\xe7U\xa3\xb9\x14\x89\v8l\xad_zZ\x00\x00\u07d4\u007f\x06b\xb4\x10)\x8c\x99\xf3\x11\u04e1EJ\x1e\xed\xba/\xeav\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\u007f\x06\u021dY\x80\u007f\xa6\v\xc6\x016\xfc\xf8\x14\u02ef%C\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u007f\v\x90\xa1\xfd\u050f'\xb2h\xfe\xb3\x83\x82\xe5]\xdbP\xef\x0f\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\u007f\x0e\xc3\u06c0F\x92\xd4\xd1\xea2E6Z\xab\x05\x90\a[\u0109\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u007f\x0f\x04\xfc\xf3zS\xa4\xe2N\xden\x93\x10Nx\xbe\x1d<\x9e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u007f\x13\xd7`I\x8dq\x93\xcahY\xbc\x95\xc9\x018d#\xd7l\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\u007f\x15\n\xfb\x1aw\u00b4Y(\xc2h\xc1\u9f74d\x1dG\u0609lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\x16\x19\x98\x8f7\x15\xe9O\xf1\xd2S&-\xc5X\x1d\xb3\xde\x1c\x890\xca\x02O\x98{\x90\x00\x00\u07d4\u007f\x1c\x81\xee\x16\x97\xfc\x14K|\v\xe5I;V\x15\xae\u007f\xdd\u0289\x1b\x1d\xaba\u04ead\x00\x00\u07d4\u007f#\x82\xff\xd8\xf89VFy7\xf9\xbar7F#\xf1\x1b8\x89 \x86\xac5\x10R`\x00\x00\u07d4\u007f7\t9\x1f?\xbe\xba5\x92\xd1u\xc7@\xe8z\tT\x1d\x02\x89\x1a\x05V\x90\xd9\u06c0\x00\x00\u07d4\u007f8\x9c\x12\xf3\xc6\x16OdFVlwf\x95\x03\xc2y%'\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\xe0\x94\u007f:\x1eE\xf6~\x92\u0200\xe5s\xb43y\xd7\x1e\xe0\x89\xdbT\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\u007f=r\x03\u0224G\xf7\xbf6\u060a\xe9\xb6\x06*^\xeex\xae\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\u007fF\xbb%F\r\xd7\xda\xe4!\x1c\xa7\xf1Z\xd3\x12\xfc}\xc7\\\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\u007fI\xe7\xa4&\x98\x82\xbd\x87\"\u0526\xf5f4v)b@y\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u007fI\xf2\a&G\x1a\xc1\u01e8>\xf1\x06\xe9w\\\xebf%f\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\u007fK^'\x85x\xc0F\xcc\xea\xf6W0\xa0\xe0h2\x9e\u0576\x89e\xea=\xb7UF`\x00\x00\u07d4\u007fOY;a\x8c3\v\xa2\xc3\xd5\xf4\x1e\xce\xeb\x92\xe2~Bl\x89\x96n\xdcuk|\xfc\x00\x00\u07d4\u007fT\x14\x91\u04ac\x00\xd2a/\x94\xaa\u007f\v\xcb\x01FQ\xfb\u0509\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\u007fZ\xe0Z\xe0\xf8\xcb\xe5\xdf\xe7!\xf0D\u05e7\xbe\xf4\xc2y\x97\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\u007f`:\xec\x17Y\xea_\a\xc7\xf8\xd4\x1a\x14(\xfb\xba\xf9\xe7b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u007falo\x00\x8a\u07e0\x82\xf3M\xa7\xd0e\x04`6\x80u\xfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u007fa\xfal\xf5\xf8\x98\xb4@\xda\u016b\xd8`\rmi\x1f\xde\xf9\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94\u007fe\\g\x89\xed\xdfE\\\xb4\xb8\x80\x99r\x0698\x9e\ubb0a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u007fk(\u0204!\xe4\x85~E\x92\x81\u05c4ai$\x89\xd3\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007fn\xfboC\x18\x87m.\xe6$\xe2u\x95\xf4DF\xf6\x8e\x93\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u007fq\x92\xc0\xdf\x1c}\xb6\xd9\xede\xd7\x11\x84\xd8\xe4\x15Z\x17\xba\x89\x04Sr\x8d3\x94,\x00\x00\u07d4\u007fz:!\xb3\xf5\xa6]\x81\xe0\xfc\xb7\xd5-\xd0\n\x1a\xa3m\xba\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u007f\x8d\xbc\xe1\x80\xed\x9cV65\xaa\xd2\xd9{L\xbcB\x89\x06\u0649\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\u007f\x99=\xdb~\x02\u0082\xb8\x98\xf6\x15_h\x0e\xf5\xb9\xaf\xf9\a\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\u007f\x9f\x9bV\xe4(\x9d\xfbX\xe7\x0f\xd5\xf1*\x97\xb5m5\u01a5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u007f\xa3~\xd6x\x87u\x1aG\x1f\x0e\xb3\x06\xbeD\xe0\xdb\xcd`\x89\x899vt\u007f\xe1\x1a\x10\x00\x00\u07d4\u007f\xaa0\xc3\x15\x19\xb5\x84\xe9rP\xed*<\xf38^\xd5\xfdP\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xcf[\xa6fo\x96lTH\xc1{\xf1\xcb\v\xbc\xd8\x01\x9b\x06\x89\x05k\xc3\u042e\xbeI\x80\x00\xe0\x94\u007f\xd6y\xe5\xfb\r\xa2\xa5\xd1\x16\x19M\xcbP\x83\x18\xed\u0140\xf3\x8a\x01c\x9eI\xbb\xa1b\x80\x00\x00\u07d4\u007f\u06e01\u01cf\x9c\tmb\xd0Z6\x9e\uac3c\xccU\u5257\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\u007f\xdb\u00e8D\xe4\r\x96\xb2\xf3\xa652.`e\xf4\xca\x0e\x84\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xdf\u020dx\xbf\x1b(Z\xc6O\x1a\xdb5\xdc\x11\xfc\xb09Q\x89|\x06\xfd\xa0/\xb06\x00\x00\u07d4\u007f\xea\x19b\xe3]b\x05\x97h\xc7I\xbe\u0756\u02b90\xd3x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xef\x8c8w\x9f\xb3\a\xeco\x04K\xeb\xe4\u007f<\xfa\xe7\x96\xf1\x89\t#@\xf8l\xf0\x9e\x80\x00\u07d4\u007f\xf0\xc6?p$\x1b\xec\xe1\x9bs~SA\xb1+\x10\x901\u0609\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\u007f\xfa\xbf\xbc9\f\xbeC\u0389\x18\x8f\bh\xb2}\xcb\x0f\f\xad\x8a\x01YQ\x82\"K&H\x00\x00\xe0\x94\u007f\xfd\x02\xed7\fp`\xb2\xaeS\xc0x\xc8\x01!\x90\u07fbu\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794\x80\x02*\x12\a\xe9\x10\x91\x1f\xc9(I\xb0i\xab\f\xda\xd0C\u04c8\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x80\t\xa7\xcb\u0452\xb3\xae\u052d\xb9\x83\xd5(ER\xc1ltQ\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80\x0e}c\x1cnW:\x903/\x17\xf7\x1f_\u045bR\x8c\xb9\x89\b=lz\xabc`\x00\x00\u07d4\x80\x15m\x10\ufa320\u0254\x10c\r7\xe2i\xd4\t<\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\x172\xa4\x81\u00c0\xe5~\xd6-l)\u0799\x8a\xf3\xfa;\x13\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x1de\xc5\x18\xb1\x1d\x0e?OG\x02!Ap\x13\xc8\xe5>\u0149\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80&CZ\xacr\x8dI{\x19\xb3\xe7\xe5|(\xc5c\x95O+\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x80-\xc3\xc4\xff-}\x92^\u215fJ\x06\u05fa`\xf10\x8c\x89\x05P\x94\f\x8f\xd3L\x00\x00\u07d4\x800\xb1\x11\u0198?\x04\x85\u076c\xa7b$\xc6\x18\x064x\x9f\x89\x04V9\x18$O@\x00\x00\u07d4\x805\xbc\xff\xae\xfd\xee\xea5\x83\fI}\x14(\x9d6 #\u0789\x10CV\x1a\x88)0\x00\x00\u07d4\x805\xfeNkj\xf2z\u44a5xQ^\x9d9\xfao\xa6[\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80C\xed\"\xf9\x97\u58a4\xc1n6D\x86\xaed\x97V\x92\u0109=I\x04\xff\xc9\x11.\x80\x00\u07d4\x80C\xfd\u043cL\x97=\x16c\xd5_\xc15P\x8e\xc5\xd4\xf4\xfa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x80L\xa9IrcOc:Q\xf3V\v\x1d\x06\xc0\xb2\x93\xb3\xb1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x80R-\u07d4N\xc5.'\xd7$\xedL\x93\xe1\xf7\xbe`\x83\u0589\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x80Y\x1aB\x17\x9f4\xe6M\x9d\xf7]\xcdF;(hoUt\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x80\\\xe5\x12\x97\xa0y;\x81 g\xf0\x17\xb3\xe7\xb2\u07db\xb1\xf9\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x80]\x84o\xb0\xbc\x02\xa73r&\u0585\xbe\x9e\xe7s\xb9\x19\x8a\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x80c7\x9a{\xf2\u02d2:\x84\xc5\t>h\xda\xc7\xf7T\x81\u0149\x11v\x10.n2\xdf\x00\x00\u07d4\x80hTX\x8e\xcc\xe5AI_\x81\u008a)\x03s\xdf\x02t\xb2\x89\x1f\x8c\xdf\\n\x8dX\x00\x00\u07d4\x80oD\xbd\xebh\x807\x01^\x84\xff!\x80I\xe3\x823*3\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\x80tF\x18\xde9jT1\x97\xeeH\x94\xab\xd0c\x98\xdd|'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80w\xc3\xe4\xc4EXn\tL\xe1\x02\x93\u007f\xa0[s{V\x8c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x90\u007fY1H\xb5|F\xc1w\xe2=%\xab\u012a\xe1\x83a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x97s\x16\x94NYB\xe7\x9b\x0e:\xba\u04cd\xa7F\be\x19\x89\x02\x1auJm\xc5(\x00\x00\xe0\x94\x80\xa0\xf6\xcc\x18l\xf6 \x14\x00sn\x06Z9\x1fR\xa9\xdfJ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x80\xab\xecZ\xa3n\\\x9d\t\x8f\x1b\x94(\x81\xbdZ\xca\u0196=\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xb2=8\v\x82\\F\xe098\x99\xa8UVF-\xa0\u1309lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xb4-\xe1p\xdb\xd7#\xf4T\xe8\x8fw\x16E-\x92\x98P\x92\x89\x10F#\xc0v-\xd1\x00\x00\u07d4\x80\xb7\x9f3\x83\x90\u047a\x1b77\xa2\x9a\x02W\xe5\xd9\x1e\a1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x80\xbf\x99^\u063a\x92p\x1d\x10\xfe\u011f\x9e}\x01M\xbe\xe0&\x89\x1f\x047\xca\x1a~\x12\x80\x00\u07d4\x80\xc0N\xfd1\x0fD\x04\x83\xc7?tK[\x9edY\x9c\xe3\xec\x89A\rXj \xa4\xc0\x00\x00\u07d4\x80\u00e9\xf6\x95\xb1m\xb1Yr\x86\u0473\xa8\xb7il9\xfa'\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\xc5>\xe7\xe35\u007f\x94\xce\rxh\x00\x9c \x8bJ\x13\x01%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xcc!\xbd\x99\xf3\x90\x05\u014f\xe4\xa4H\x90\x92 !\x8ff\u02c966\xc9yd6t\x00\x00\u07d4\x80\xd5\xc4\fY\xc7\xf5N\xa3\xa5_\xcf\xd1uG\x1e\xa3P\x99\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x80\xda/\u0762\x9a\x9e'\xf9\xe1\x15\x97^i\xae\x9c\xfb\xf3\xf2~\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x80\xe7\xb3 R0\xa5f\xa1\xf0a\xd9\"\x81\x9b\xb4\xd4\u04a0\xe1\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x80\xea\x1a\xcc\x13n\xcaKh\xc8B\xa9Z\xdfk\u007f\xee~\xb8\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80\xf0z\xc0\x9e{,<\n=\x1e\x94\x13\xa5D\xc7:A\xbe\u02c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x81\r\xb2Vu\xf4^\xa4\xc7\xf3\x17\u007f7\xce)\xe2-g\x99\x9c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81\x13\x9b\xfd\u0326V\xc40 ?r\x95\x8cT;e\x80\xd4\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x81\x14a\xa2\xb0\u0290\xba\xda\xc0j\x9e\xa1nx{3\xb1\x96\u0309\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\x81\x16M\xeb\x10\x81J\xe0\x83\x91\xf3,\bf{bH\xc2}z\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\x81\x18i1\x18A7\xd1\x19*\u020c\xd3\xe1\xe5\xd0\xfd\xb8jt\x89\x9d5\x95\xab$8\xd0\x00\x00\u0794\x81*U\xc4<\xae\xdcYr\x187\x90\x00\xceQ\rT\x886\xfd\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x81.\xa7\xa3\xb2\xc8n\xed2\xffO,sQL\xc6;\xac\xfb\u038965\u026d\xc5\u07a0\x00\x00\u07d4\x814\xdd\x1c\x9d\xf0\xd6\u0225\x81$&\xbbU\xc7a\u0283\x1f\b\x89\x06\xa2\x16\v\xb5|\xcc\x00\x00\u07d4\x81A5\u068f\x98\x11\aW\x83\xbf\x1a\xb6pb\xaf\x8d>\x9f@\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x81I\x8c\xa0{\x0f/\x17\xe8\xbb\xc7\xe6\x1a\u007fJ\xe7\xbef\xb7\x8b\x89\x05\x81\xfb\xb5\xb3;\xb0\x00\x00\u07d4\x81Um\xb2sI\xab\x8b'\x00ID\xedP\xa4n\x94\x1a\x0f_\x89\u063beI\xb0+\xb8\x00\x00\u07d4\x81U\xfalQ\xeb1\xd8\bA-t\x8a\xa0\x86\x10P\x18\x12/\x89e\xea=\xb7UF`\x00\x00\xe0\x94\x81V6\v\xbd7\ta\xce\xcakf\x91\xd7P\x06\xad L\xf2\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x81a\xd9@\xc3v\x01\x00\xb9\b\x05)\xf8\xa6\x03%\x03\x0fn\u0709\x10CV\x1a\x88)0\x00\x00\xe0\x94\x81d\xe7\x83\x14\xae\x16\xb2\x89&\xccU=,\xcb\x16\xf3V'\r\x8a\x01\xca\x13N\x95\xfb2\xc8\x00\x00\u07d4\x81e\u02b0\xea\xfbZ2\x8f\xc4\x1a\xc6M\xaeq[.\xef,e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81h\xed\xce\u007f)a\xcf)[\x9f\xcdZE\xc0l\xde\xdan\xf5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81m\x97r\xcf\x119\x91\x16\xcc\x1er\xc2lgt\xc9\xed\xd79\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81s\xc85djg.\x01R\xbe\x10\xff\xe8Ab\xdd%nL\x89\x1a\xab\xdf!E\xb40\x00\x00\u07d4\x81t\x93\u035b\xc6#p*$\xa5o\x9f\x82\xe3\xfdH\xf3\xcd1\x89\x9eK#\xf1-L\xa0\x00\x00\u07d4\x81y\xc8\tp\x18,\u0177\xd8*M\xf0n\xa9M\xb6:%\xf3\x89'o%\x9d\xe6k\xf4\x00\x00\u07d4\x81z\xc3;\xd8\xf8GVsr\x95\x1fJ\x10\u05e9\x1c\xe3\xf40\x89\n\xd7\xc4\x06\xc6m\xc1\x80\x00\xe0\x94\x81\x8f\xfe'\x1f\u00d75e\xc3\x03\xf2\x13\xf6\xd2\u0689\x89~\xbd\x8a\x016\xe0SB\xfe\u1e40\x00\u07d4\x81\x97\x94\x81!s.c\xd9\xc1H\x19N\xca\xd4n0\xb7I\u0209\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x81\x9a\xf9\xa1\xc2s2\xb1\xc3i\xbb\xda\x1b=\xe1\xc6\xe93\xd6@\x89\x11\t\xe6T\xb9\x8fz\x00\x00\xe0\x94\x81\x9c\u06a506x\xef|\xecY\u050c\x82\x16:\xcc`\xb9R\x8a\x03\x13QT_y\x81l\x00\x00\u07d4\x81\x9e\xb4\x99\vZ\xbaUG\t=\xa1+k<\x10\x93\xdfmF\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81\xa8\x81\x96\xfa\xc5\xf2<>\x12\xa6\x9d\xecK\x88\x0e\xb7\xd9s\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x81\xbc\xcb\xff\x8fD4~\xb7\xfc\xa9['\xce|\x95$\x92\xaa\xad\x89\b@\xc1!e\xddx\x00\x00\u07d4\x81\xbdu\xab\xd8e\xe0\xc3\xf0J\vO\xdb\xcbt\xd3@\x82\xfb\xb7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x81\xc1\x8c*#\x8d\xdcL\xba#\n\a-\xd7\xdc\x10\x1eb\x02s\x89Hz\x9a0E9D\x00\x00\u07d4\x81\xc9\xe1\xae\xe2\xd36]S\xbc\xfd\u0356\xc7\xc58\xb0\xfd~\xec\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x81\u03edv\t\x13\xd3\xc3\"\xfc\xc7{I\u00ae9\a\xe7On\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x81\xd6\x19\xffW&\xf2@_\x12\x90Lr\xeb\x1e$\xa0\xaa\xeeO\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x81\xef\u25aev\xc8`\xd1\xc5\xfb\xd3=G\xe8\u0399\x96\xd1W\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81\xf8\xde,(=_\u052f\xbd\xa8]\xed\xf9v\x0e\xab\xbb\xb5r\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x82\f\x19)\x11\x96P[e\x05\x9d\x99\x14\xb7\t\v\xe1\u06c7\u0789\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x82\x1c\xb5\xcd\x05\xc7\uf41f\xe1\xbe`s=\x89c\xd7`\xdcA\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x82\x1dy\x8a\xf1\x99\x89\u00ee[\x84\xa7\xa7(<\xd7\xfd\xa1\xfa\xbe\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x82\x1e\xb9\t\x94\xa2\xfb\xf9K\xdc23\x91\x02\x96\xf7o\x9b\xf6\xe7\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x82$\x9f\xe7\x0fa\u01b1o\x19\xa3$\x84\x0f\xdc\x02\x021\xbb\x02\x8a\x02\x036\xb0\x8a\x93c[\x00\x00\u07d4\x82(\xeb\xc0\x87H\x0f\xd6EG\xca(\x1f^\xac\xe3\x04\x14S\xb9\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\x82)\u03b9\xf0\xd7\b9I\x8dD\xe6\xab\xed\x93\xc5\xca\x05\x9f]\x8a\x1a\x1c\x1b<\x98\x9a \x10\x00\x00\u07d4\x82.\xdf\xf66V:a\x06\xe5.\x9a%\x98\xf7\xe6\xd0\xef'\x82\x89\x01\xf4\xf9i=B\u04c0\x00\u07d4\x822\x19\xa2Yv\xbb*\xa4\xaf\x8b\xadA\xac5&\xb4\x936\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x822\xd1\xf9t.\u07cd\xd9'\xda5;*\xe7\xb4\xcb\xceu\x92\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\x824\xf4c\u0444\x85P\x1f\x8f\x85\xac\xe4\x97,\x9bc-\xbc\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x827htg7\xcem\xa3\x12\xd5>TSN\x10o\x96|\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82;\xa7dr8\xd1\x13\xbc\xe9\x96JC\u0420\x98\x11\x8b\xfeM\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x82@t1(\x06\xdaGHCBf\xee\x00!@\u305a\u0089Q\xb1\u04c3\x92a\xac\x00\x00\u07d4\x82C\x8f\u04b3*\x9b\xddgKI\xd8\xcc_\xa2\xef\xf9x\x18G\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82HW(\xd0\xe2\x81V7X\xc7Z\xb2~\xd9\u80a0\x00-\x89\a\xf8\b\xe9)\x1el\x00\x00\u07d4\x82K<<D>\x19)]~\xf6\xfa\xa7\xf3t\xa4y\x84\x86\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82Q5\x8c\xa4\xe0`\u0775Y\xcaX\xbc\v\u077e\xb4\a\x02\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82Q5\xb1\xa7\xfc\x16\x05aL\x8a\xa4\u042cm\xba\u040fH\x0e\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\x82S\t\xa7\xd4]\x18\x12\xf5\x1en\x8d\xf5\xa7\xb9ol\x90\x88\x87\x89\x804\xf7\u0671f\xd4\x00\x00\u07d4\x82Z\u007fN\x10\x94\x9c\xb6\xf8\x96Bh\xf1\xfa_W\xe7\x12\xb4\u0109\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82a\xfa#\f\x90\x1dC\xffW\x9fG\x80\u04d9\xf3\x1e`v\xbc\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82b\x16\x9baXp\x13N\xb4\xacl_G\x1ck\xf2\xf7\x89\xfc\x89\x19\x12z\x13\x91\xea*\x00\x00\u07d4\x82c\xec\xe5\xd7\t\xe0\u05eeq\u0328h\xed7\xcd/\xef\x80{\x895\xab\x02\x8a\xc1T\xb8\x00\x00\xe0\x94\x82l\xe5y\x052\xe0T\x8ca\x02\xa3\r>\xac\x83k\xd68\x8f\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4\x82n\xb7\xcds\x19\xb8-\xd0z\x1f;@\x90q\xd9n9g\u007f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x82u1\xa6\u0141z\xe3_\x82\xb0\v\x97T\xfc\xf7LU\xe22\x89\xc3(\t>a\xee@\x00\x00\u0794\x82u\xcdhL6y\u0548}\x03fN3\x83E\xdc<\xdd\xe1\x88\xdbD\xe0I\xbb,\x00\x00\u07d4\x82\x84\x92;b\u62ff|+\x9f4\x14\xd1>\xf6\xc8\x12\xa9\x04\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\x82\x8b\xa6Q\u02d3\x0e\xd9xqV)\x9a=\xe4L\u040br\x12\x89Hz\x9a0E9D\x00\x00\u07d4\x82\xa1\\\xef\x1dl\x82`\xea\xf1Y\xea?\x01\x80\xd8g}\xce\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82\xa8\xb9kl\x9e\x13\xeb\xec\x1e\x9f\x18\xac\x02\xa6\x0e\xa8\x8aH\xff\x89lk\x8c@\x8es\xb3\x00\x00\u07d4\x82\xa8\u02ff\xdf\xf0+.8\xaeK\xbf\xca\x15\xf1\xf0\xe8;\x1a\xea\x89\x04\x9b\x99\x1c'\xefm\x80\x00\u07d4\x82\xe4F\x1e\xb9\xd8I\xf0\x04\x1c\x14\x04!\x9eBr\u0110\n\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82\xe5w\xb5\x15\xcb+\b`\xaa\xfe\x1c\xe0\x9aY\xe0\x9f\xe7\xd0@\x89 \x86\xac5\x10R`\x00\x00\u07d4\x82\xea\x01\xe3\xbf.\x83\x83nqpN\"\xa2q\x93w\xef\xd9\u00c9\xa4\xccy\x95c\u00c0\x00\x00\u07d4\x82\xf2\xe9\x91\xfd2L_]\x17v\x8e\x9fa3]\xb61\x9dl\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x82\xf3\x9b'X\xaeB'{\x86\u059fu\xe6(\xd9X\xeb\u02b0\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\x82\xf8T\xc9\xc2\xf0\x87\xdf\xfa\x98Z\xc8 \x1ebl\xa5Fv\x86\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x82\xffqo\xdf\x03>\xc7\xe9B\xc9\t\u0643\x18g\xb8\xb6\xe2\xef\x89a\t=|,m8\x00\x00\u07d4\x83\b\xed\n\xf7\xf8\xa3\xc1u\x1f\xaf\xc8w\xb5\xa4*\xf7\xd3X\x82\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x83\x1cD\xb3\b@G\x18K*\xd2\x18h\x06@\x907P\xc4]\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x83!\x05\x83\xc1jN\x1e\x1d\xac\x84\xeb\xd3~=\x0f|W\ub909lk\x93[\x8b\xbd@\x00\x00\u07d4\x83,T\x17k\xdfC\xd2\u027c\u05f8\b\xb8\x95V\xb8\x9c\xbf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x833\x16\x98]Gt+\xfe\xd4\x10`J\x91\x95<\x05\xfb\x12\xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x834vK{9zNW\x8fP6M`\xceD\x89\x9b\xff\x94\x89\x05\x03\xb2\x03\xe9\xfb\xa2\x00\x00\xe0\x94\x83;j\x8e\xc8\xda@\x81\x86\xac\x8a}*m\xd6\x15#\xe7\u0384\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x83=?\xaeT*\xd5\xf8\xb5\f\xe1\x9b\xde+\xecW\x91\x80\u020c\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\x83=\xb4,\x14\x16<{\xe4\u02b8j\u0153\xe0bf\u0599\u054a$\xe4\r+iC\xef\x90\x00\x00\xe0\x94\x83V;\xc3d\ud060\xc6\xda;V\xffI\xbb\xf2g\x82z\x9c\x8a\x03\xab\x91\xd1{ \xdeP\x00\x00\u07d4\x83zd]\xc9\\IT\x9f\x89\x9cN\x8b\u03c7S$\xb2\xf5|\x89 \x8c9J\xf1\u0208\x00\x00\u07d4\x83\x8b\xd5e\xf9\x9f\xdeH\x05?y\x17\xfe3<\xf8J\xd5H\xab\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x83\x90\x8a\xa7G\x8am\x1c\x9b\x9b\x02\x81\x14\x8f\x8f\x9f$+\x9f\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\x83\x92\xe57vq5x\x01[\xffI@\xcfC\x84\x9d}\u02e1\x89\bM\xf05]V\x17\x00\x00\xe0\x94\x83\x97\xa1\xbcG\xac\xd6GA\x81Y\xb9\x9c\xeaW\xe1\xe6S-n\x8a\x01\xf1\x0f\xa8'\xb5P\xb4\x00\x00\u07d4\x83\x98\xe0~\xbc\xb4\xf7_\xf2\x11m\xe7|\x1c*\x99\xf3\x03\xa4\u03c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xa3\x14\x883\xd9dI\x84\xf7\xc4u\xa7\x85\a\x16\ufd00\xff\x89\xb8Pz\x82\a( \x00\x00\u07d4\x83\xa4\x02C\x8e\x05\x19w=TH2k\xfba\xf8\xb2\f\xf5-\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\x83\xa9;[\xa4\x1b\xf8\x87 \xe4\x15y\f\xdc\vg\xb4\xaf4\u0109\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x83\xc2=\x8aP!$\xee\x15\x0f\b\xd7\x1d\xc6rt\x10\xa0\xf9\x01\x8a\a3\x1f;\xfef\x1b\x18\x00\x00\u07d4\x83\u0217\xa8Ki^\xeb\xe4fy\xf7\xda\x19\xd7vb\x1c&\x94\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xd52\u04cdm\xee?`\xad\u018b\x93a3\u01e2\xa1\xb0\u0749\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xdb\xf8\xa1(S\xb4\n\xc6\x19\x96\xf8\xbf\x1d\xc8\xfd\xba\xdd\xd3)\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x83\xdb\xfd\x8e\xda\x01\xd0\u078e\x15\x8b\x16\u0413_\xc28\n]\u01c9 \x86\xac5\x10R`\x00\x00\u07d4\x83\xe4\x80U2|(\xb5\x93o\xd9\xf4D~s\xbd\xb2\xdd3v\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\x83\xfeZ\x1b2\x8b\xaeD\a\x11\xbe\xafj\xad`&\xed\xa6\xd2 \x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x84\x00\x8ar\xf8\x03o?\xeb\xa5B\xe3Px\xc0W\xf3*\x88%\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x84\x0e\xc8>\xa96!\xf04\xe7\xbb7b\xbb\x8e)\xde\xd4\xc4y\x89\x87\x86x2n\xac\x90\x00\x00\xe0\x94\x84\x11E\xb4H@\xc9F\xe2\x1d\xbc\x19\x02d\xb8\xe0\xd5\x02\x93i\x8a?\x87\bW\xa3\xe0\xe3\x80\x00\x00\u07d4\x84#!\a\x93+\x12\xe01\x86X5%\xce\x02:p>\xf8\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\x84$O\xc9ZiW\xed|\x15\x04\xe4\x9f0\xb8\xc3^\xcaKy\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x841'}{\xdd\x10E}\xc0\x17@\x8c\x8d\xbb\xbdAJ\x8d\xf3\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4\x847Z\xfb\xf5\x9b:\x1da\xa1\xbe2\xd0u\xe0\xe1ZO\xbc\xa5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x84;\xd3P/E\xf8\xbcM\xa3p\xb3#\xbd\xac?\xcf_\x19\xa6\x89P\x03\x9dc\xd1\x1c\x90\x00\x00\u07d4\x84P34c\rw\xf7AG\xf6\x8b.\bf\x13\xc8\xf1\xad\xe9\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x84R\x03u\x0fqH\xa9\xaa&)!\xe8mC\xbfd\x19t\xfd\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x84a\xec\u0126\xa4^\xb1\xa5\xb9G\xfb\x86\xb8\x80i\xb9\x1f\xcdo\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84g^\x91wrmE\xea\xa4k9\x92\xa3@\xba\u007fq\f\x95\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x84hl{\xadv,T\xb6g\u055f\x90\x94<\xd1M\x11z&\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\x89\xf6\xad\x1d\x9a\x94\xa2\x97x\x91V\x89\x9d\xb6AT\xf1\u06f5\x89\x13t\a\xc0<\x8c&\x80\x00\u07d4\x84\x8c\x99Jy\x00?\xe7\xb7\xc2l\xc62\x12\xe1\xfc/\x9c\x19\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\x8f\xbd)\xd6|\xf4\xa0\x13\xcb\x02\xa4\xb1v\xef$N\x9e\u6349\x01\x17*ck\xbd\xc2\x00\x00\u07d4\x84\x94\x9d\xbaU\x9ac\xbf\xc8E\xde\xd0n\x9f-\x9b\u007f\x11\xef$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\x9a\xb8\a\x90\xb2\x8f\xf1\xff\u05ba9N\xfctc\x10\\6\xf7\x89\x01\xe0+\xe4\xael\x84\x00\x00\u07d4\x84\x9b\x11oYc\x01\xc5\u063bb\xe0\xe9z\x82H\x12n9\xf3\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x84\xa7L\xee\xcf\xf6\\\xb9;/\x94\x9dw>\xf1\xad\u007f\xb4\xa2E\x89\x05\n\x9bDF\x85\xc7\x00\x00\u07d4\x84\xaa\xc7\xfa\x19\u007f\xf8\\0\xe0;zS\x82\xb9W\xf4\x1f:\xfb\x89\b\x8b#\xac\xff\u0650\x00\x00\u07d4\x84\xaf\x1b\x15sB\xd5Ch&\r\x17\x87b0\xa54\xb5K\x0e\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x84\xb0\xeek\xb87\u04e4\xc4\xc5\x01\x1c:\"\x8c\x0e\u06b4cJ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\xb4\xb7Nf#\xba\x9d\x15\x83\xe0\u03feId?\x168AI\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\xb6\xb6\xad\xbe/[>-h,f\xaf\x1b\u0110S@\xc3\xed\x89!\x92\xf8\xd2\"\x15\x00\x80\x00\xe0\x94\x84\xb9\x1e.)\x02\xd0^+Y\x1bA\b;\u05fe\xb2\xd5,t\x8a\x02\x15\xe5\x12\x8bE\x04d\x80\x00\u07d4\x84\xbc\xbf\"\xc0\x96\a\xac\x844\x1d.\xdb\xc0;\xfb\x179\xd7D\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x84\xbf\xce\xf0I\x1a\n\xe0iK7\u03ac\x02E\x84\xf2\xaa\x04g\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x84\xcb}\xa0P-\xf4\\\xf5a\x81{\xbd#b\xf4Q\xbe\x02\u0689Hz\x9a0E9D\x00\x00\u07d4\x84\xccxx\xda`_\xdb\x01\x9f\xab\x9bL\xcf\xc1Wp\x9c\u0765\x89Hy\x85\x13\xaf\x04\xc9\x00\x00\u07d4\x84\xdb\x14Y\xbb\x00\x81.\xa6~\xcb=\xc1\x89\xb7!\x87\xd9\xc5\x01\x89\b\x11\xb8\xfb\u0685\xab\x80\x00\u07d4\x84\u9516\x80\xbe\xcehA\xb9\xa7\xe5%\r\b\xac\xd8}\x16\u0349\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x84\xe9\u03c1f\xc3j\xbf\xa4\x90S\xb7\xa1\xad@6 &\x81\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\xec\x06\xf2G\x00\xfeBAL\xb9\x89|\x15L\x88\xde/a2\x89Hz\x9a0E9D\x00\x00\xe0\x94\x84\xf5\"\xf0R\x0e\xbaR\xdd\x18\xad!\xfaK\x82\x9f+\x89\u02d7\x8a\x01\fQ\x06\xd5\x13O\x13\x00\x00\u07d4\x85\v\x9d\xb1\x8f\xf8K\xf0\xc7\xdaI\xea7\x81\xd9 \x90\xad~d\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\x85\x10\xee\x93O\f\xbc\x90\x0e\x10\a\xeb8\xa2\x1e*Q\x01\xb8\xb2\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4\x85\x16\xfc\xafw\u0213\x97\x0f\xcd\x1a\x95\x8b\xa9\xa0\x0eI\x04@\x19\x89\n\xa3\xeb\x16\x91\xbc\xe5\x80\x00\u07d4\x85\x1a\xa9\x1c\x82\xf4/\xad]\xd8\xe8\xbb^\xa6\x9c\x8f:Yw\u0449\b\x0eV\x1f%xy\x80\x00\u07d4\x85\x1c\rb\xbeF5\xd4w~\x805\xe3~K\xa8Q|a2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x85\x1d\u00ca\xdbE\x93r\x9av\xf3:\x86\x16\u06b6\xf5\xf5\x9aw\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x852I\b\x97\xbb\xb4\u038b\u007fk\x83~L\xba\x84\x8f\xbe\x99v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x85>j\xba\xf4Di\xc7/\x15\x1dN\"8\x19\xac\xedN7(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85F\x91\xceqO2\\\xedU\xceY(\u039b\xa1/\xac\u0478\x89\xedp\xb5\xe9\xc3\xf2\xf0\x00\x00\u07d4\x85L\fF\x9c$k\x83\xb5\u0473\xec\xa4C\xb3\x9a\xf5\xee\x12\x8a\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x85]\x9a\xef,9\xc6#\r\t\u025e\xf6II\x89\xab\u61c5\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\x85c\u0113a\xb6%\xe7hw\x1c\x96\x15\x1d\xbf\xbd\x1c\x90iv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85fa\t\x01\xaa\xce8\xb82D\xf3\xa9\xc810jg\xb9\u0709\xb0\x82\x13\xbc\xf8\xff\xe0\x00\x00\xe0\x94\x85j\xa2<\x82\xd7![\xec\x8dW\xf6\n\xd7^\xf1O\xa3_D\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x85nZ\xb3\xf6L\x9a\xb5k\x00\x93\x93\xb0\x16d\xfc\x03$\x05\x0e\x89a\t=|,m8\x00\x00\u07d4\x85n\xb2\x04$\x1a\x87\x83\x0f\xb2)\x03\x13C\xdc0\x85OX\x1a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x85s,\x06\\\xbdd\x11\x99A\xae\xd40\xacYg\vlQ\u0109'\xa5sb\xab\n\x0e\x80\x00\xe0\x94\x85x\xe1\x02\x12\xca\x14\xff\a2\xa8$\x1e7F}\xb8V2\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x85y\xda\xdf\x1a9Z4q\xe2\vov=\x9a\x0f\xf1\x9a?o\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x85\u007f\x10\v\x1aY0\"^\xfc~\x90 \u05c3'\xb4\x1c\x02\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x85\x94mV\xa4\xd3q\xa93hS\x96\x90\xb6\x0e\xc8%\x10tT\x89]\u0212\xaa\x111\xc8\x00\x00\xe0\x94\x85\x99\xcb\u0566\xa9\xdc\u0539f\xbe8}iw]\xa5\xe3<o\x8a\fQ\xf1\xb1\xd5&\"\x90\x00\x00\u07d4\x85\x9c`\f\xf1=\x1d\x02s\xd5\xd1\xda<\u05c9\u4549\x9f'\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\x85\xa2\xf6\xea\x94\xd0^\x8c\x1d\x9a\xe2\xf4\x91\x038\xa3X\xe9\x8d\xed\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85\xb1o\v\x8b4\xdf\xf3\x80Oi\xe2\x16\x8aO{$\xd1\x04+\x89\x11/B<vF\xd4\x00\x00\xe0\x94\x85\xb2\x99\x8d\fs0,\xb2\xba\x13\xf4\x8913\x01\xe0S\xbe\x15\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x85\xbbQ\xbc;\xfe\x9a\x1b*/k\x1c\u0695\xbc\xa8\xb3\x8c\x8d^\x89\x11q-\xa0K\xa1\xef\x00\x00\u07d4\x85\xc8\xf3\xccz5O\xea\u025a^{\xfe|\u07e3Q\xcf\xe3U\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\x85\xca\x1er~\x9d\x1a\x87\x99\x1c\xc2\xc4\x18@\xeb\xb9\xed\xf2\x1d\x1b\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x85\u028b\xc6\xda(\x03\xd0r_^\x1aEl\x89\xf9\xbcwN/\x89 \x86\xac5\x10R`\x00\x00\u07d4\x85\xd0\u0607T\xac\x84\xb8\xb2\x1b\xa9=\u04bf\xecrbo\xab\xa8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x85\xeb%kQ\xc8\x19\xd6\x0e\xa6\x1a\x82\xd1,\x93X\u055c\x1c\xae\x89\x18\xef\xc8J\xd0\u01f0\x00\x00\u07d4\x85\xf0\xe7\xc1\xe3\xaf\xf8\x05\xa6'\xa2\xaa\xf2\xcf\xf6\xb4\xc0\xdb\xe9\u02c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x86\x02l\xad?\xe4\xea\x1c\xe7\xfc\xa2`\xd3\xd4^\xb0\x9e\xa6\xa3d\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\x0f_\xfc\x10\xdev}\xed\x80\u007fq\xe8a\xd6G\xdf\xd2\x19\xb1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\x150c\xa1\xae\u007f\x02\xf1\xa8\x816\xd4\u059c|^>C'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86$_Yf\x91\t>\xce?=<\xa2&>\xac\xe8\x19A\u0649\n1\x06+\xee\xedp\x00\x00\u07d4\x86%i!\x1e\x8cc'\xb5A^:g\xe5s\x8b\x15\xba\xafn\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x86)}s\x0f\xe0\xf7\xa9\xee$\xe0\x8f\xb1\b{1\xad\xb3\x06\xa7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86D\xcc(\x1b\xe32\xcc\xce\xd3m\xa4\x83\xfb*\aF\u067a.\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x86I\x9a\x12(\xff-~\xe3\au\x93dPo\x8e\x8c\x83\a\xa5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x86K\xecPi\xf8U\xa4\xfdX\x92\xa6\xc4I\x1d\xb0|\x88\xff|\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86W\n\xb2Y\u0271\xc3,\x97) /w\xf5\x90\xc0}\xd6\x12\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86c\xa2A\xa0\xa8\x9ep\xe1\x82\xc8E\xe2\x10\\\x8a\xd7&K\u03ca\x03#\xb1=\x83\x98\xf3#\x80\x00\u07d4\x86g\xfa\x11U\xfe\xd72\u03f8\u0725\xa0\xd7e\xce\r\a\x05\xed\x89\x04n\xc9e\u00d3\xb1\x00\x00\u07d4\x86h\xaf\x86\x8a\x1e\x98\x88_\x93\u007f&\x15\xde\xd6u\x18\x04\xeb-\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x86t\nFd\x8e\x84Z]\x96F\x1b\x18\t\x1f\xf5{\xe8\xa1o\x8a\x14\xc0\x974\x85\xbf9@\x00\x00\xe0\x94\x86~\xbaVt\x8aY\x045\r,\xa2\xa5\u039c\xa0\vg\n\x9b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x86\x80dt\xc3X\x04}\x94\x06\xe6\xa0\u007f@\x94[\xc82\x8eg\x8a\x01u.\xb0\xf7\x01=\x10\x00\x00\u07d4\x86\x88=T\xcd9\x15\xe5I\tU0\xf9\xab\x18\x05\xe8\xc5C-\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x86\x8c#\xbe\x874f\xd4\xc7L\"\n\x19\xb2E\xd1x~\x80\u007f\x89J\x13\xbb\xbd\x92\u020e\x80\x00\xe0\x94\x86\x92O\xb2\x11\xaa\xd2<\xf5\xce`\x0e\n\xae\x80c\x96D@\x87\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\x93\u9e3e\x94B^\xefyi\xbci\xf9\xd4/|\xadg\x1e\x8967\tlK\xcci\x00\x00\xe0\x94\x86\x9f\x1a\xa3\x0eDU\xbe\xb1\x82 \x91\xde\\\xad\xecy\xa8\xf9F\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x86\xa1\xea\xde\xeb0F\x13E\xd9\xefk\xd0R\x16\xfa$|\r\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86\xa5\xf8%\x9e\u0570\x9e\x18\x8c\xe3F\xee\x92\xd3J\xa5\u0753\xfa\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xb7\xbdV<\uad86\xf9bD\xf9\xdd\xc0*\u05f0\xb1K\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\u008bVx\xaf7\xd7'\xec\x05\xe4Dw\x90\xf1_q\xf2\xea\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xc4\xce\x06\u066c\x18[\xb1H\xd9o{z\xbes\xf4A\x00m\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x86\xc8\xd0\u0642\xb59\xf4\x8f\x980\xf9\x89\x1f\x9d`z\x94&Y\x8a\x02\xce\xd3wa\x82O\xb0\x00\x00\u07d4\x86\xc94\xe3\x8eS\xbe;3\xf2t\xd0S\x9c\xfc\xa1Y\xa4\xd0\u04494\x95tD\xb8@\xe8\x00\x00\xe0\x94\x86\xca\x01E\x95~k\r\xfe6\x87_\xbez\r\xecU\xe1z(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x86\u02af\xac\xf3*\xa01|\x03*\xc3k\xab\xed\x97G\x91\xdc\x03\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x86\u0377\xe5\x1a\xc4Gr\xbe6\x90\xf6\x1d\x0eYvn\x8b\xfc\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x86\xdfs\xbd7\u007f,\t\xdec\xc4]g\xf2\x83\xea\xef\xa0\xf4\xab\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86\xe3\xfe\x86\xe9=\xa4\x86\xb1Bf\xea\xdf\x05l\xbf\xa4\xd9\x14C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86\xe8g\x0e'Y\x8e\xa0\x9c8\x99\xabw\x11\u04f9\xfe\x90\x1c\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xefd&!\x19I\xcc7\xf4\xc7^xP6\x9d\f\xf5\xf4y\x8a\x02\xd6_2\xea\x04Z\xf6\x00\x00\u07d4\x86\xf0]\x19\x06>\x93i\xc6\x00N\xb3\xf1#\x94:|\xffN\xab\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x86\xf2>\x9c\n\xaf\u01cb\x9c@M\xcd`3\x9a\x92[\xff\xa2f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x86\xf4\xf4\n\u0644\xfb\xb8\t3\xaebn\x0eB\xf93?\xddA\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x86\xf9\\[\x11\xa2\x93\x94\x0e5\xc0\xb8\x98\u0637_\b\xaa\xb0m\x8a\x06D\xe3\xe8u\xfc\xcft\x00\x00\u07d4\x86\xff\xf2 \xe5\x93\x05\xc0\x9fH8`\xd6\xf9N\x96\xfb\xe3/W\x89\x02S[j\xb4\xc0B\x00\x00\u07d4\x87\a\x96\xab\xc0\u06c4\xaf\x82\xdaR\xa0\xedhsM\xe7\xe66\xf5\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x87\x0f\x15\xe5\u07cb\x0e\xab\xd0%iSz\x8e\xf9;Vx\\B\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\x87\x181`\xd1r\xd2\xe0\x84\xd3'\xb8k\xcb|\x1d\x8eg\x84\xef\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94\x87\x1b\x8a\x8bQ\u07a1\x98\x9aY!\xf1>\xc1\xa9U\xa5\x15\xadG\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x87%\xe8\xc7S\xb3\xac\xbf\u0725_<b\xdf\u1954T\x96\x8a\x8967\tlK\xcci\x00\x00\u07d4\x877\xda\xe6q\x82:\x8dY\x17\xe0\x15z\u039cCF\x8d\x94k\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x87;\u007fxm<\x99\xff\x01,J|\xae&w'\x02@\xb9\u0149]\u0212\xaa\x111\xc8\x00\x00\u07d4\x87<op\ufdb1\xd0\xf2\xbb\xc5~\xeb\xcdpa|l\xe6b\x896\xf0\xd5']\tW\x00\x00\u07d4\x87>I\x13\\3\x91\x99\x10`)\n\xa7\xf6\u0338\xf8Zx\u06c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x87Pa\xee\x12\xe8 \x04\x1a\x01\x94,\xb0\xe6[\xb4'\xb0\x00`\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\x87XJ?a;\xd4\xfa\xc7L\x1ex\v\x86\xd6\xca\xeb\x89\f\xb2\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x87d\xd0'\"\x00\t\x96\xec\xd4u\xb43)\x8e\x9fT\v\x05\xbf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x87l?!\x8bGv\xdf<\xa9\xdb\xfb'\r\xe1R\xd9N\xd2R\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x87u\xa6\x10\xc5\x02\xb9\xf1\xe6\xadL\xda\u06cc\xe2\x9b\xffu\xf6\xe4\x89 \x86\xac5\x10R`\x00\x00\u07d4\x87vN6w\xee\xf6\x04\xcb\u015a\xed$\xab\xdcVk\t\xfc%\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\x87\x87\xd1&w\xa5\xec)\x1eW\xe3\x1f\xfb\xfa\xd1\x05\xc32K\x87\x8a\x02\xa2N\xb52\b\xf3\x12\x80\x00\u07d4\x87\x94\xbfG\xd5E@\xec\xe5\xc7\"7\xa1\xff\xb5\x11\u0777Gb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x87\xa5>\xa3\x9fY\xa3[\xad\xa85%!dU\x94\xa1\xa7\x14\u02c9g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x87\xa7\xc5\b\xefqX-\u0665Cr\xf8\x9c\xb0\x1f%/\xb1\x80\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x87\xaf%\xd3\xf6\xf8\xee\xa1S\x13\xd5\xfeEW\xe8\x10\xc5$\xc0\x83\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x87\xb1\x0f\x9c(\x00\x98\x17\x9a+v\xe9\u0390\xbea\xfc\x84M\r\x89Hz\x9a0E9D\x00\x00\u07d4\x87\xbf|\xd5\u0629)\xe1\u01c5\xf9\xe5D\x91\x06\xac#$c\u0249\x047\xb1\x1f\xccEd\x00\x00\u07d4\x87\u0118\x17\t4\xb8#=\x1a\xd1\xe7i1}\\G_/@\x897\b\xba\xed=h\x90\x00\x00\u07d4\x87\xcf6\xad\x03\xc9\xea\xe9\x05:\xbbRB\u0791\x17\xbb\x0f*\v\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x87\u05ec\x06S\xcc\xc6z\xa9\xc3F\x9e\xefCR\x19?}\xbb\x86\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94\x87\xe3\x06+#!\xe9\u07f0\x87\\\u311c\x9b.5\"\xd5\n\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x87\xe6\x03N\xcf#\xf8\xb5c\x9d_\x0e\xa7\n\"S\x8a\x92\x04#\x89\x11\xc7\xea\x16.x \x00\x00\u07d4\x87\xefm\x8bj|\xbf\x9b\\\x8c\x97\xf6~\xe2\xad\u00a7;?w\x89\n\xdd\x1b\xd2<<H\x00\x00\u07d4\x87\xfb&\xc3\x1eHdMi14 \\\xaeC\xb2\x1f\x18aK\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\x87\xfcF5&9D\xce\x14\xa4lu\xfaJ\x82\x1f9\xce\u007fr\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x87\xfc\xbe|A\x93\xff\xcb\b\x147y\u027e\xc8?\xe7\xfd\xa9\xfc\x89\x05o\x98]8dK\x80\x00\xe0\x94\x88\x01]r\x03\xc5\xe0\"J\xed\xa2\x86\xed\x12\xf1\xa5\x1bx\x933\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\x88\x10l'\xd2\vt\xb4\xb9\x8c\xa6+#+\xd5\xc9t\x11\x17\x1f\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x88\x120\x04|!\x1d-[\x00\xd8\xdeLQ9\xde^2'\u01ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x88*\xa7\x98\xbfA\xdf\x17\x9f\x85R\x010\xf1\\\xcd\xf5\x9b^X\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x88+\u04e2\xe9\xd7A\x10\xb2Ia\xc57w\xf2/\x1fF\xdc]\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\xe0\x94\x88,\x8f\x81\x87,y\xfe\xd5!\xcb_\x95\r\x8b\x03#\"\xeai\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x88/up\x83\x86e<\x80\x17\x1d\x06c\xbf\xe3\v\x01~\u042d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x884I\tdLz\u0513\x0f\xd8s\xca\x1c\r\xa2\xd44\xc0\u007f\x89\a's\x9f\xcb\x00M\x00\x00\u07d4\x884\xb2E4q\xf3$\xfb&\xbe[%\x16k[W&\x02]\x89\x1f\x0f\xf8\xf0\x1d\xaa\xd4\x00\x00\u07d4\x88:x\xae\xab\xaaP\xd8\xdd\xd8W\v\xcd4&_\x14\xb1\x93c\x89\xd2U\"\xfd\xa3y\xa1\x80\x00\u07d4\x88E\xe9\xf9\x0e\x963k\xac<ak\xe9\u0604\x02h>\x00L\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x88F\x92\x8dh2\x89\xa2\xd1\x1d\xf8\xdbz\x94t\x98\x8e\xf0\x13H\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x88I\x80\xebEe\xc1\x04\x83\x17\xa8\xf4\u007f\u06f4a\x96[\u4049\xd8\xd6\x11\x9a\x81F\x05\x00\x00\xe0\x94\x88Jz9\u0411n\x05\xf1\xc2B\xdfU`\u007f7\u07cc_\u068a\x04\xf4\x84<\x15|\x8c\xa0\x00\x00\u07d4\x88T\x93\xbd\xa3j\x042\x97eF\xc1\xdd\xceq\xc3\xf4W\x00!\x89\v\xbfQ\r\xdf\xcb&\x00\x00\xe0\x94\x88`\x9e\nF[n\x99\xfc\xe9\a\x16mW\xe9\xda\b\x14\xf5\u020a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x88m\n\x9e\x17\xc9\xc0\x95\xaf.\xa25\x8b\x89\xecpR\x12\ue509\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\x88y~Xg^\xd5\xccL\x19\x98\a\x83\xdb\xd0\xc9V\bQS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x88|\xacA\xcdpo3E\xf2\xd3J\xc3N\x01u*nY\t\x89 F\\\ue7617\x00\x00\u07d4\x88\x88\x8aW\xbd\x96\x87\xcb\xf9P\xae\xea\u03d7@\xdc\xc4\xd1\xefY\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u0794\x88\x89D\x83\x16\xcc\xf1N\xd8m\xf8\xe2\xf4x\xdcc\xc43\x83@\x88\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x88\x8c\x16\x14I3\x19|\xac&PM\xd7n\x06\xfdf\x00\u01c9\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x88\x8e\x94\x91p\x83\xd1R +S\x1699\x86\x9d'\x11u\xb4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x88\x90\x87\xf6o\xf2\x84\xf8\xb5\xef\xbd)I;pg3\xab\x14G\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\x88\x95\xebrb&\xed\xc3\xf7\x8c\u01a5\x15\a{2\x96\xfd\xb9^\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x88\x97Z_\x1e\xf2R\x8c0\v\x83\xc0\xc6\a\xb8\xe8}\u0593\x15\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\u07d4\x88\x9d\xa4\x0f\xb1\xb6\x0f\x9e\xa9\xbdzE>XL\xf7\xb1\xb4\xd9\xf7\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x88\x9d\xa6b\xebJ\n*\x06\x9d+\xc2K\x05\xb4\xee.\x92\xc4\x1b\x89Z,\x8cTV\xc9\xf2\x80\x00\u07d4\x88\xa1\"\xa28,R91\xfbQ\xa0\u032d;\xeb[rY\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x88\xa2\x15D0\xc0\xe4\x11G\xd3\xc1\xfe\u3cf0\x06\xf8Q\xed\xbd\x8965f3\xeb\xd8\xea\x00\x00\u07d4\x88\xb2\x17\u0337\x86\xa2T\xcfM\xc5\u007f]\x9a\xc3\xc4U\xa3\x04\x83\x892$\xf4'#\xd4T\x00\x00\xe0\x94\x88\xbcC\x01.\xdb\x0e\xa9\xf0b\xacCxC%\n9\xb7\x8f\xbb\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x88\xc2Qj|\xdb\t\xa6'mr\x97\xd3\x0fZM\xb1\xe8K\x86\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x88\xc3ad\rki7;\b\x1c\xe0\xc43\xbdY\x02\x87\xd5\xec\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x88\xd5A\xc8@\xceC\xce\xfb\xafm\x19\xafk\x98Y\xb5s\xc1E\x89\t79SM(h\x00\x00\u07d4\x88\xde\x13\xb0\x991\x87|\x91\rY1e\xc3d\u0221d\x1b\u04c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x88\xde\u017d?N\xba-\x18\xb8\xaa\xce\xfa{r\x15H\xc3\x19\xba\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\x88\xe6\xf9\xb2G\xf9\x88\xf6\xc0\xfc\x14\xc5o\x1d\xe5>\u019dC\u0309\x05k\xc7^-c\x10\x00\x00\u07d4\x88\xee\u007f\x0e\xfc\x8fw\x8ckh~\xc3+\xe9\xe7\xd6\xf0 \xb6t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x88\xf1\x04_\x19\xf2\xd3\x19\x18\x16\xb1\xdf\x18\xbbn\x145\xad\x1b8\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x89\x00\x9e<d\x88\xbd^W\r\x1d\xa3N\xab\xe2\x8e\xd0$\xde\x1b\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x89\x05D0\xdc\xdc(\xac\x15\xfac^\xf8|\x10^`+\xf7\f\x89\x05\xda\xcd\x13\u029e0\x00\x00\xe0\x94\x89\bv\f\u04db\x9c\x1e\x81\x84\xe6\xa7R\ue20e?\vpE\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x89\x0f\xe1\x1f<$\u06c72\xd6\xc2\xe7r\xe2)|~e\xf19\x8a\rV'\x13}\xa8\xb5\x90\x00\x00\u07d4\x89\x14\xa6\x80\xa5\xae\xc5\"mK\xaa\xec.UR\xb4M\xd7\xc8t\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\x89\x1c\xb8#\x8c\x88\xe9:\x1b\xcfa\xdbI\xbd\x82\xb4z\u007fO\x84\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\x89%\xdaEI\xe1QU\xe5zb\x85\"\u03a9\xdd\xdfb}\x81\x8966\xc2^f\xec\xe7\x00\x00\u07d4\x890\x17\xff\x1a\xda\u0519\xaa\x06T\x01\xb4#l\xe6\xe9+bZ\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x893I\x17`\xc8\xf0\xb4\u07cc\xaa\u01ce\xd85\xca\xee!\x04m\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x896\bu\x1dh\xd0F\xe8X\x02\x92fs\xcd\xf2\xf5\u007f|\xb8\x89\x01\x11du\x9f\xfb2\x00\x00\u0794\x898\u0474\xda\xeeU\xa5Ms\x8c\xf1~Dw\xf6yNF\xf7\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x89:l.\xb8\xb4\n\xb0\x96\xb4\xf6~t\xa8\x97\xb8@tn\x86\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x89<\xdd\xdfSw\xf3\xc7Q\xbf.T\x11 \x04ZG\u02e1\x01\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x89V\x13#o5\x84!j\xd7\\]>\a\xe3\xfahc\xa7x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x89Wr~r\xcfb\x90 \xf4\xe0^\xdfy\x9a\xa7E\x80b\u0409wC\"\x17\xe6\x83`\x00\x00\u07d4\x89]iN\x88\v\x13\xcc\u0404\x8a\x86\xc5\xceA\x1f\x88Gk\xbf\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x89^\xc5TVD\u0dc30\xff\xfa\xb8\xdd\xea\xc9\xe83\x15l\x89 \x86\xac5\x10R`\x00\x00\u07d4\x89`\tRj,{\f\t\xa6\xf6:\x80\xbd\U0009d707\u079c\x89\xbb\xb8k\x82#\xed\xeb\x00\x00\u07d4\x89g\u05f9\xbd\xb7\xb4\xae\xd2.e\xa1]\xc8\x03\xcbz!?\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x89n3\\\xa4z\xf5yb\xfa\x0fM\xbf>E\xe6\x88\u02e5\x84\x89J/\xc0\xab`R\x12\x00\x00\u07d4\x89s\xae\xfd^\xfa\xee\x96\t]\x9e(\x8fj\x04l\x977KC\x89\a\xa4\u0120\xf32\x14\x00\x00\u07d4\x89\x8cr\xddseX\xef\x9eK\xe9\xfd\xc3O\xefT\xd7\xfc~\b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x89\x9b<$\x9f\fK\x81\xdfu\xd2\x12\x00M=m\x95/\xd2#\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x89\xab\x13\xee&mw\x9c5\xe8\xbb\x04\u034a\x90\xcc!\x03\xa9[\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\x89\xc43\xd6\x01\xfa\xd7\x14\xdaci0\x8f\xd2l\x1d\u0254+\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x89\xd7[\x8e\b1\xe4o\x80\xbc\x17A\x88\x18N\x00o\xde\x0e\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x89\u3d5a\x15\x86G7\u0513\xc1\xd2<\xc5=\xbf\x8d\xcb\x13b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x89\xfc\x8eM8k\r\v\xb4\xa7\a\xed\xf3\xbdV\r\xf1\xad\x8fN\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\x89\xfe\xe3\r\x17(\xd9l\xec\xc1\u06b3\xda.w\x1a\xfb\u03eaA\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x8a\x1c\u016c\x11\x1cI\xbf\xcf\xd8H\xf3}\xd7h\xaae\u0208\x02\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x8a \xe5\xb5\xce\xe7\xcd\x1fU\x15\xba\xce;\xf4\xf7\u007f\xfd\xe5\xcc\a\x89\x04V9\x18$O@\x00\x00\xe0\x94\x8a!}\xb3\x8b\xc3_!_\xd9)\x06\xbeBCo\xe7\xe6\xed\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x8a$:\n\x9f\xeaI\xb89TwE\xff-\x11\xaf?K\x05\"\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x8a$}\x18e\x10\x80\x9fq\xcf\xfcEYG\x1c9\x10\x85\x81!\x89a\t=|,m8\x00\x00\u07d4\x8a4p(-^**\xef\u05e7P\x94\xc8\"\xc4\xf5\xae\uf289\r(\xbc`dx\xa5\x80\x00\u07d4\x8a6\x86\x9a\xd4x\x99|\xbfm\x89$\xd2\n<\x80\x18\xe9\x85[\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8aC\x14\xfba\u0353\x8f\xc3>\x15\xe8\x16\xb1\x13\U000ac267\xfb\x89\x17vNz\xede\x10\x00\x00\u07d4\x8aOJ\u007fR\xa3U\xba\x10_\xca r\xd3\x06_\xc8\xf7\x94K\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8aX1(,\xe1Jezs\r\xc1\x88&\xf7\xf9\xb9\x9d\xb9h\x89\uaf8a[A\xc16\x00\x00\u07d4\x8a_\xb7W\x93\xd0C\xf1\xbc\xd48\x85\xe07\xbd0\xa5(\xc9'\x89\x13Snm.\x9a\xc2\x00\x00\u07d4\x8af\xab\xbc-0\xce!\xa83\xb0\u06ceV\x1dQ\x05\xe0\xa7,\x89%\xf1\xde\\v\xac\xdf\x00\x00\u07d4\x8atl]g\x06G\x11\xbf\xcah[\x95\xa4\xfe)\x1a'\x02\x8e\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8ax\n\xb8z\x91E\xfe\x10\xed`\xfaGjt\n\xf4\u02b1\u0489\x12\x1b.^ddx\x00\x00\u07d4\x8az\x06\xbe\x19\x9a:X\x01\x9d\x84j\xc9\xcb\xd4\xd9]\xd7W\u0789\xa2\xa4#\x94BV\xf4\x00\x00\u07d4\x8a\x81\x01\x14\xb2\x02]\xb9\xfb\xb5\x00\x99\xa6\xe0\u02de.\xfak\u0709g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x8a\x86\xe4\xa5\x1c\x01;\x1f\xb4\xc7k\xcf0f|x\xd5.\xed\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\x9e\u029cZ\xba\x8e\x13\x9f\x80\x03\xed\xf1\x16:\xfbp\xaa:\xa9\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\x8a\xb89\xae\xaf*\xd3|\xb7\x8b\xac\xbb\xb63\xbc\xc5\xc0\x99\xdcF\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\u021b\u06780\x1ek\x06w\xfa%\xfc\xf0\xf5\x8f\f\u01f6\x11\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x8a\xdcS\xef\x8c\x18\xed0Qx]\x88\xe9\x96\xf3\xe4\xb2\x0e\xcdQ\x8a\b\xe4\xd3\x16\x82v\x86@\x00\x00\u07d4\x8a\xe6\xf8\vp\xe1\xf2<\x91\xfb\u0569f\xb0\xe4\x99\xd9]\xf82\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x8a\xe9\uf30a\x8a\u07e6\xaby\x8a\xb2\xcd\xc4\x05\b*\x1b\xbbp\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\xf6&\xa5\xf3'\xd7Pe\x89\xee\xb7\x01\x0f\xf9\xc9D` \u0489K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\x8b\x01\xda4\xd4p\xc1\xd1\x15\xac\xf4\xd8\x11<M\u0628\xc38\xe4\x8a\x05W-\xce\xfa\xb6\x97\x90\x00\x00\u07d4\x8b\a\xd0PuM\u027a#\r\xb0\x1c1\n\xfd\xb59Z\xa1\xb3\x89\x06f\xb0nb\xa6 \x00\x00\u07d4\x8b \xad;\x94em\xbd\xc0\xdd!\xa3\x93\u0627\xd9\xe0!8\u02c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x8b'9\"\x06\xb9X\xcd7]~\xf8\xaf,\xf8\xef\x05\x98\xc0\xbc\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x8b0\xc0@\x98\u05e7\xe6B\f5~\xa7\xbf\xa4\x9b\xac\x9a\x8a\x18\x8a\x01\xb1\xb1\x13\xf9\x1f\xb0\x14\x00\x00\u07d4\x8b3\x84\x11\xf2l\xcf7e\x8c\xc7U!\xd7v)\t\x9eF}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8b6\"LsV\xe7Q\xf0\xc0f\xc3^;D\x86\x03d\xbf\u00896'\xba\u01e3\xd9'\x80\x00\xe0\x94\x8b6\x96\xf3\xc6\r\xe3$2\xa2\xe4\u00d5\xef\x03\x03\xb7\xe8\x1eu\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\x8b9?\xb0\x81>\xe1\x01\xdb\x1e\x14\xec\xc7\xd3\"\xc7+\x8c\x04s\x89\x18\xb2j1>\x8a\xe9\x00\x00\xe0\x94\x8bH\xe1\x9d9\xdd5\xb6nn\x1b\xb6\xb9\xc6W\xcb,\xf5\x9d\x04\x8a\x03\xc7U\xac\x9c\x02J\x01\x80\x00\xe0\x94\x8bP^(q\xf7\u07b7\xa68\x95 \x8e\x82'\u072a\x1b\xff\x05\x8a\f\xf6\x8e\xfc0\x8dy\xbc\x00\x00\u07d4\x8bW\xb2\xbc\x83\u030dM\xe31 N\x89?/;\x1d\xb1\a\x9a\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8b\\\x91K\x12\x8b\xf1i\\\b\x89#\xfaF~y\x11\xf3Q\xfa\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\xe0\x94\x8b_)\xcc/\xaa&,\xde\xf3\x0e\xf5T\xf5\x0e\xb4\x88\x14n\xac\x8a\x01;hp\\\x97 \x81\x00\x00\u07d4\x8bpV\xf6\xab\xf3\xb1\x18\xd0&\xe9D\xd5\xc0sC<\xa4Q\u05c965\xc6 G9\u0640\x00\u07d4\x8bqE\"\xfa(9b\x04p\xed\xcf\fD\x01\xb7\x13f=\xf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x8bt\xa7\xcb\x1b\xb8\u014f\xce&tf\xa3\x03X\xad\xafR\u007fa\x8a\x02\xe2WxN%\xb4P\x00\x00\u07d4\x8b~\x9fo\x05\xf7\xe3dv\xa1n>q\x00\xc9\x03\x1c\xf4\x04\xaf\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8b\x81\x15ni\x869\x94<\x01\xa7Rr\xad=5\x85\x1a\xb2\x82\x89\x12\xb3\x16_e\xd3\xe5\x00\x00\u07d4\x8b\x95w\x92\x00S\xb1\xa0\x01\x890M\x88\x80\x10\xd9\xef,\xb4\xbf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8b\x98A\x86.w\xfb\xbe\x91\x94p\x93U\x83\xa9<\xf0'\xe4P\x89llS4B\u007f\x1f\x00\x00\u07d4\x8b\x99}\xbc\a\x8a\xd0)a5]\xa0\xa1Y\xf2\x92~\xd4=d\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x8b\x9f\xda}\x98\x1f\xe9\xd6B\x87\xf8\\\x94\xd8?\x90t\x84\x9f\u030a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x8b\xb0!/2\x95\xe0)\u02b1\xd9a\xb0A3\xa1\x80\x9e{\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8b\xbe\xac\xfc)\xcf\xe94\x02\xdb<A\u065a\xb7Yf.s\xec\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x8b\xc1\xff\x87\x14\x82\x8b\xf2\x86\xff~\x8aw\t\x10eH\xed\x1b\x18\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x8b\u0436ZP\xef\\\xef\x84\xfe\xc4 \xbe{\x89\xed\x14p\u03b9\x8a\x02\x8aw\x93n\x92\xc8\x1c\x00\x00\u07d4\x8b\u05b1\xc6\xd7M\x01\r\x10\b\u06e6\xef\x83]D0\xb3\\2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x8b\xd8\xd4\xc4\xe9C\xf6\xc8\a9!\xdc\x17\xe3\xe8\u05e0v\x16'\x89\x9f\x04!\x9d\x8d4\x95\x00\x00\u07d4\x8b\xdf\xdal!W \xed\xa2\x13o\x91\x05#!\xafN\x93l\x1f\x8965\xe6\x19\xbb\x04\xd4\x00\x00\u07d4\x8b\xea@7\x93G\xa5\u0211\u055acc1V@\xf5\xa7\xe0z\x89lkv\uf597\f\x00\x00\xe0\x94\x8b\xf0+\xd7Hi\x0e\x1f\xd1\xc7m'\b3\x04\x8bf\xb2_\u04ca\x02\u007f\xad\xe5h\xeb\xa9`\x00\x00\u07d4\x8b\xf2\x97\xf8\xf4SR>\xd6j\x1a\xcbvv\x85c7\xb9;\xf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8b\xf3s\xd0v\x81L\xbcW\xe1\xc6\xd1j\x82\u017e\x13\xc7=7\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x8c\x10#\xfd\xe1WM\xb8\xbbT\xf1s\x96p\x15|\xa4}\xa6R\x8a\x01y\u03da\u00e1\xb1w\x00\x00\u07d4\x8c\x1f\xbe_\n\xea5\x9cZ\xa1\xfa\b\u0209T\x12\u028e\x05\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\"B`U\xb7o\x11\xf0\xa2\xde\x1a\u007f\x81\x9aa\x96\x85\xfe`\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4\x8c+}\x8b`\x8d(\xb7\u007f\\\xaa\x9c\xd6E$*\x82>L\u0649b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x8c/\xbe\ue3ac\xc5\xc5\xd7|\x16\xab\xd4b\ue701E\xf3K\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x8c:\x9e\xe7\x1fr\x9f#l\xba8g\xb4\u05dd\x8c\xee\xe2]\xbc\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8cP\xaa*\x92\x12\xbc\xdeVA\x8a\xe2a\xf0\xb3^z\x9d\xbb\x82\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8cT\xc7\xf8\xb9\x89nu\xd7\xd5\xf5\xc7`%\x86\x99\x95qB\xad\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8c]\x16\xede\xe3\xed~\x8b\x96\u0297+\xc8as\xe3P\v\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8cj\xa8\x82\xee2,\xa8HW\x8c\x06\xcb\x0f\xa9\x11\xd3`\x83\x05\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\x8cj\xe7\xa0Z\x1d\xe5u\x82\xae'h Bv\xc0\xffG\xed\x03\x8a,\v\xb3\xdd0\xc4\xe2\x00\x00\x00\u07d4\x8co\x9fN[z\xe2v\xbfXI{\u05ff*}%$_d\x89\x93\xfe\\W\xd7\x10h\x00\x00\u07d4\x8cu\x95n\x8f\xedP\xf5\xa7\xdd|\xfd'\xda \x0fgF\xae\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c|\xb4\xe4\x8b%\x03\x1a\xa1\xc4\xf9)%\xd61\xa8\xc3\xed\xc7a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\u007f\xa5\xca\xe8/\xed\xb6\x9a\xb1\x89\xd3\xff'\xae \x92\x93\xfb\x93\x89\x15\xaf\x88\r\x8c\u06c3\x00\x00\xe0\x94\x8c\x81A\x0e\xa85L\xc5\xc6\\A\xbe\x8b\xd5\xdes<\v\x11\x1d\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\x8c\x83\xd4$\xa3\xcf$\xd5\x1f\x01\x92=\xd5J\x18\u05b6\xfe\xde{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8c\x90\n\x826\xb0\x8c+e@]9\xd7_ \x06*ua\xfd\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\x8c\x93\xc3\xc6\u06dd7q}\xe1e\u00e1\xb4\xfeQ\x95,\b\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8c\x99\x95\x91\xfdr\xefq\x11\xef\xcaz\x9e\x97\xa25k;\x00\n\x89\xddd\xe2\xaa\ngP\x00\x00\u07d4\x8c\xa6\x98\x97F\xb0n2\xe2Hta\xb1\u0399j':\xcf\u05c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8c\xb3\xaa?\xcd!(T\xd7W\x8f\xcc0\xfd\xed\xe6t*1*\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x8c\xc0\xd7\xc0\x16\xfaz\xa9P\x11J\xa1\xdb\tH\x82\xed\xa2t\xea\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4\x8c\xc6R\xdd\x13\xe7\xfe\x14\u06bb\xb3m]2\r\xb9\xff\xee\x8aT\x89a\t=|,m8\x00\x00\u07d4\x8c\u02bf%\a\u007f:\xa4\x15E4MS\xbe\x1b+\x9c3\x90\x00\x89[\xe8f\xc5b\xc5D\x00\x00\u07d4\x8c\xcf:\xa2\x1a\xb7BWj\xd8\xc4\"\xf7\x1b\xb1\x88Y\x1d\ua28965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\xd0\xcd\"\xe6 \xed\xa7\x9c\x04a\xe8\x96\xc9<D\x83~)h\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8c\u078bs.`#\x87\x8e\xb2>\xd1b)\x12K_z\xfb\xec\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x8c\xe2/\x9f\xa3rD\x9aB\x06\x10\xb4z\xe0\xc8\xd5eH\x122\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x8c\u451d\x8a\x16T-B<\x17\x98Ng9\xfar\u03b1w\x8a\x05K@Y&\xf4\xa6=\x80\x00\u07d4\x8c\xe5\xe3\xb5\xf5\x91\xd5\uc8ca\xbf\"\x8f.<5\x13K\xda\xc0\x89}\xc3[\x84\x89|8\x00\x00\xe0\x94\x8c\xee8\xd6YW\x88\xa5n?\xb9F4\xb3\xff\xe1\xfb\xdb&\u058a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x8c\xee\xa1^\xec;\xda\xd8\x02?\x98\xec\xf2[+\x8f\xef'\xdb)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8c\xf3To\xd1\u0363=X\x84_\xc8\xfc\xfe\u02bc\xa7\xc5d*\x89\x1f\x1e9\x93,\xb3'\x80\x00\u07d4\x8c\xf6\xda\x02\x04\xdb\u0106\vF\xad\x97?\xc1\x11\x00\x8d\x9e\fF\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x8c\xfe\xde\xf1\x98\xdb\n\x91C\xf0\x91)\xb3\xfdd\u073b\x9bIV\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x04\xa5\xeb\xfb]\xb4\t\xdb\x06\x17\xc9\xfaV1\xc1\x92\x86\x1fJ\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x8d\x06\xe4d$\\\xadaI9\xe0\xaf\bE\xe6\xd70\xe2\x03t\x89\n\u070a(\xf3\xd8}\x80\x00\u07d4\x8d\a\xd4-\x83\x1c-|\x83\x8a\xa1\x87+:\xd5\xd2w\x17h#\x89\x12\xee\x1f\x9d\xdb\xeeh\x00\x00\u07d4\x8d\v\x9e\xa5?\xd2cA^\xac\x119\x1f|\xe9\x12<Dpb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x17\x94\xdaP\x9c\xb2\x97\x056a\xa1J\xa8\x92321\xe3\xc1\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4\x8d\x1a\xbd\x89}\xac\xd41.\x18\b\f\x88\xfb\x96G\xea\xb4@R\x89\v\xb5\x9a'\x95<`\x00\x00\u07d4\x8d#\x034\x1e\x1e\x1e\xb5\xe8\x18\x9b\xde\x03\xf7:`\xa2\xa5Ha\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8d#\x8e\x03e\x96\x98vC\xd71s\xc3{\n\xd0`U\xb9l\x89qH\xbf\n*\xf0f\x00\x00\xe0\x94\x8d.1\xb0\x88\x03\xb2\xc5\xf1=9\x8e\xca\u0605( \x9f`W\x8a\x02\x1d\xb8\xbb\xca\xd1\x1e\x84\x00\x00\u07d4\x8d7\x8f\x0e\xdc\v\xb0\xf0hmj \xbejv\x92\xc4\xfa$\xb8\x89\x05k\xc7^-c\x10\x00\x00\u0794\x8dK`<]\xd4W\f4f\x95\x15\xfd\xccfX\x90\x84\fw\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x8dQ\xa4\xccb\x01\x13\"\u0196\xfdr[\x9f\xb8\xf5?\xea\xaa\a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8dTL2\xc0\u007f\u0404,v\x1dS\xa8\x97\xd6\xc9P\xbbu\x99\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x8d^\xf1r\xbfw1^\xa6N\x85\xd0\x06\x19\x86\u01d4\xc6\xf5\x19\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x8dak\x1e\xeew\xee\xf6\xf1v\xe0i\x8d\xb3\xc0\xc1A\xb2\xfc\x8f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8dap\xfff\x97\x8ew;\xb6!\xbfr\xb1\xba{\xe3\xa7\xf8~\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x8db\v\xde\x17\"\x8fl\xbb\xa7M\xf6\xbe\x87&M\x98\\\xc1y\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8db\x9c `\x815I\x1bP\x13\xf1\x00%\x86\xa0810\xe5\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\x8dfW\xf5\x97\x11\xb1\xf8\x03\xc6\xeb\xefh/\x91[b\xf9-\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\x8dfv7\xe2\x9e\xca\x05\xb6\xbf\xbe\xf1\xf9mF\x0e\uffd9\x84\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8dm\xf2\tHM{\x94p+\x03\xa5>V\xb9\xfb\x06`\xf6\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8dy\\_JV\x89\xadb\u0696\x16q\xf0(\x06R\x86\xd5T\x89o\x05\xb5\x9d; \x00\x00\x00\u07d4\x8d\u007f>a)\x9c-\xb9\xb9\xc0H|\xf6'Q\x9e\xd0\n\x91#\x89^t\xa8P^\x80\xa0\x00\x00\xe0\x94\x8d\x89\x17\v\x92\xb2\xbe,\b\xd5|H\xa7\xb1\x90\xa2\xf1Fr\x0f\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x8d\x93\xda\u01c5\xf8\x8f\x1a\x84\xbf\x92}Se+E\xa1T\xcc\u0749\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x8d\x99R\u043bN\xbf\xa0\xef\xd0\x1a:\xa9\xe8\xe8\u007f\x05%t.\x89\xbb\x91%T\"c\x90\x00\x00\u07d4\x8d\x9a\fp\xd2& B\xdf\x10\x17\xd6\xc3\x03\x13 $w'\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x9e\xd7\xf4U0X\xc2ox6\xa3\x80-0d\xeb\x1b6=\x89\x04\xe1\x00;(\xd9(\x00\x00\u07d4\x8d\xa1\x17\x8fU\xd9wr\xbb\x1d$\x11\x1a@JO\x87\x15\xb9]\x89/\x9a\xc3\xf6\xde\x00\x80\x80\x00\u07d4\x8d\xa1\xd3Y\xbal\xb4\xbc\xc5}zCw \xd5]\xb2\xf0\x1cr\x89\x04V9\x18$O@\x00\x00\u07d4\x8d\xab\x94\x8a\xe8\x1d\xa3\x01\xd9r\xe3\xf6\x17\xa9\x12\xe5\xa7Sq.\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8d\xad\xdfR\xef\xbdt\u0695\xb9i\xa5GoO\xbb\xb5c\xbf\u0489-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4\x8d\xb1\x85\xfe\x1bp\xa9Jj\b\x0e~#\xa8\xbe\xdcJ\xcb\xf3K\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x8d\xb5\x8e@n -\xf9\xbcp<H\v\xd8\xed$\x8dR\xa02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\xbc>l\xb43\xe1\x94\xf4\x0f\x82\xb4\x0f\xaa\xdb\x1f\x8b\x85a\x16\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x8d\xc1\xd5\x11\x1d\t\xaf%\xfd\xfc\xacE\\|\xec(>mgu\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\u0504\xff\x8a0sd\xebf\xc5%\xa5q\xaa\xc7\x01\xc5\xc3\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\u05a9\xba\xe5\u007fQ\x85I\xad\xa6wFo\ua2b0O\u0674\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\xde<\xb8\x11\x85h\xefE\x03\xfe\x99\x8c\xcd\xf56\xbf\x19\xa0\x98\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\xde`\xeb\b\xa0\x99\xd7\u06a3V\u06aa\xb2G\r{\x02Zk\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x8d\xf39!Kj\u0472Fc\xceq`4t\x9dn\xf88\u064a\x02TO\xaaw\x80\x90\xe0\x00\x00\xe0\x94\x8d\xf5=\x96\x19\x14q\xe0Y\xdeQ\xc7\x18\xb9\x83\xe4\xa5\x1d*\xfd\x8a\x06\u01b95\xb8\xbb\xd4\x00\x00\x00\u07d4\x8d\xfb\xaf\xbc\x0e[\\\x86\xcd\x1a\u0597\xfe\xea\x04\xf41\x88\u0796\x89\x15%+\u007f_\xa0\xde\x00\x00\u07d4\x8e\a;\xad%\xe4\"\x18a_J\x0ek.\xa8\xf8\xde\"0\xc0\x89\x82=b\x9d\x02k\xfa\x00\x00\u07d4\x8e\x0f\xee8hZ\x94\xaa\xbc\xd7\u0385{k\x14\t\x82Ou\xb8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e#\xfa\xcd\x12\xc7e\xc3j\xb8\x1am\xd3M\x8a\xa9\xe6\x89\x18\xae\x89\t\x11\u418d\xba\x9b\x00\x00\xe0\x94\x8e/\x904\xc9%G\x19\u00ceP\u026ad0^\u0596\xdf\x1e\x8a\x01\x00N.E\xfb~\xe0\x00\x00\u07d4\x8e2@\xb0\x81\x0e\x1c\xf4\a\xa5\x00\x80G@\u03cdad2\xa4\x89\x02/fU\xef\v8\x80\x00\u07d4\x8eHj\x04B\xd1q\xc8`[\xe3H\xfe\xe5~\xb5\b^\xff\r\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8eaV3k\xe2\u037e2\x14\r\xf0\x8a+\xa5_\u0425\x84c\x89\x04\t\x9e\x1dcW\x18\x00\x00\u07d4\x8eg\b\x15\xfbg\xae\xae\xa5{\x86SN\xdc\x00\xcd\xf5d\xfe\u5272\xe4\xb3#\xd9\xc5\x10\x00\x00\u07d4\x8emt\x85\xcb\u942c\xc1\xad\x0e\xe9\xe8\xcc\xf3\x9c\f\x93D\x0e\x893\xc5I\x901r\f\x00\x00\xe0\x94\x8et\xe0\u0477~\xbc\x82:\xca\x03\xf1\x19\x85L\xb1 '\xf6\u05ca\x16\xb3R\xda^\x0e\xd3\x00\x00\x00\u07d4\x8ex\xf3QE}\x01oJ\xd2u^\xc7BN\\!\xbamQ\x89\a\xea(2uw\b\x00\x00\u07d4\x8ey6\u0552\x00\x8f\xdcz\xa0N\xde\xebuZ\xb5\x13\u06f8\x9d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8e\u007f\xd28H\xf4\xdb\a\x90j}\x10\xc0K!\x80;\xb0\x82'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8e\x92\xab\xa3\x8er\xa0\x98\x17\v\x92\x95\x92FSz.UV\xc0\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x8e\x98ve$\xb0\xcf'G\xc5\r\xd4;\x95gYM\x971\u0789lD\xb7\xc2a\x82(\x00\x00\u07d4\x8e\x9b5\xadJ\n\x86\xf7XDo\xff\xde4&\x9d\x94\f\xea\u0349\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8e\x9c\b\xf78f\x1f\x96v#n\xff\x82\xbaba\xdd?H\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8e\x9cB\x92f\xdf\x05~\xfax\xdd\x1d_w\xfc@t*\xd4f\x89\x10D.\u0475l|\x80\x00\u07d4\x8e\xa6V\xe7\x1e\xc6Q\xbf\xa1|ZWY\xd8`1\xcc5\x99w\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8e\xae)CU\x98\xba\x8f\x1c\x93B\x8c\xdb>+M1\a\x8e\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\xb1\xfb\xe4\xe5\xd3\x01\x9c\xd7\xd3\r\xae\x9c\r[Lv\xfbc1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\xb5\x17t\xaf k\x96k\x89\t\xc4Z\xa6r'H\x80,\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e\xb8\xc7\x19\x82\xa0\x0f\xb8Bu)2S\xf8\x04ED\xb6kI\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x8e\xcb\u03ec\xbf\xaf\xe9\xf0\f9\"\xa2N,\xf0\x02gV\xca \x8a\x011\xbe\xb9%\xff\xd3 \x00\x00\u07d4\x8e\u03b2\xe1$Sl[_\xfcd\x0e\xd1O\xf1^\u0668\xcbq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\u042f\x11\xff(p\xda\x06\x81\x00J\xfe\x18\xb0\x13\xf7\xbd8\x82\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\x8e\xd1Cp\x1f/r(\x0f\xd0J{Ad(\x19y\xea\x87\u0248\xc2I\xfd\xd3'x\x00\x00\u07d4\x8e\xd1R\x8bD~\xd4)y\x02\xf69\xc5\x14\u0414J\x88\xf8\u0209\n\xc6\xe7z\xb6c\xa8\x00\x00\u07d4\x8e\xd4(L\x0fGD\x9c\x15\xb8\u0673$]\u8fb6\u0380\xbf\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\x8e\xde~=\xc5\aI\xc6\xc5\x0e.(\x16\x84x\xc3M\xb8\x19F\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x8e\xe5\x843}\xdb\xc8\x0f\x9e4\x98\xdfU\xf0\xa2\x1e\xac\xb5\u007f\xb1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8e\xeb\xec\x1ab\xc0\x8b\x05\xa7\xd1\u0551\x80\xaf\x9f\xf0\u044e?6\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e\xf4\u0622\xc2<Ry\x18{d\xe9ot\x14\x04\bS\x85\xf3\x89\x10=\xc1\xe9\xa9i{\x00\x00\u07d4\x8e\xf7\x11\xe4:\x13\x91\x8f\x13\x03\xe8\x1d\x0e\xa7\x8c\x9e\xef\xd6~\xb2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8e\xfe\xc0X\xccTaWvjc'u@J3J\xaa\u0687\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\x8f\x02\xbd\xa6\xc3i\"\xa6\xbejP\x9b\xe5\x19\x06\u04d3\xf7\xb9\x9b\x897I\r\xc1.\xbe\u007f\x80\x00\u07d4\x8f\x058\xedq\xda\x11U\xe0\xf3\xbd\xe5f|\xeb\x841\x8a\x1a\x87\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\x8f\x06||\x1b\xbdWx\v{\x9e\xeb\x9e\xc0\x03/\x90\xd0\xdc\xf9\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x8f\n\xb8\x94\xbd?Ni}\xbc\xfb\x85\x9dIz\x9b\xa1\x95\x99J\x8a\b]c\x8beG*\xa2\x00\x00\u07d4\x8f\n\xf3uf\xd1R\x80/\x1a\xe8\xf9(\xb2Z\xf9\xb19\xb4H\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x8f\x19R\xee\xd1\xc5H\xd9\ue6d7\xd0\x16\x9a\a\x93;\xe6\x9fc\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x8f\x1f\xcc<Q\xe2R\xb6\x93\xbc[\x0e\xc3\xf65)\xfei(\x1e\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x8f\"`\x96\xc1\x84\xeb\xb4\x01\x05\xe0\x8d\xacM\"\xe1\xc2\xd5M0\x89\x10\x9eC{\xd1a\x8c\x00\x00\xe0\x94\x8f)\xa1J\x84Z\xd4X\xf2\xd1\b\xb5h\xd8\x13\x16k\xcd\xf4w\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x8f1\xc7\x00Q\x97\xec\x99z\x87\xe6\x9b\xecHd\x9a\xb9K\xb2\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fA\xb1\xfb\xf5B\x98\xf5\u043c-\x12/N\xb9]\xa4\xe5\xcd=\x89\x133\x83/^3\\\x00\x00\u07d4\x8fG2\x8e\xe02\x01\xc9\xd3^\u04b5A+%\xde\u0305\x93b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8fG=\n\xb8v\u076a\x15`\x86!\xd7\x01>o\xf7\x14\xb6u\x89\x19\x80\x1c\x83\xb6\xc7\xc0\x00\x00\u07d4\x8fM\x1dAi>F,\xf9\x82\xfd\x81\u042ap\x1d:St\u0249\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fM\x1e~Ea(J4\xfe\xf9g<\r4\xe1*\xf4\xaa\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8fO\xb1\xae\xa7\xcd\x0fW\x0e\xa5\xe6\x1b@\xa4\xf4Q\vbd\xe4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fV\x1bA\xb2\t\xf2H\u0229\x9f\x85\x87\x887bP`\x9c\xf3\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94\x8fX\xd84\x8f\xc1\xdcN\r\xd84;eC\xc8W\x04^\xe9@\x8a\x02\xe3\x03\x8d\xf4s\x03(\x00\x00\u07d4\x8f`\x89_\xbe\xbb\xb5\x01\u007f\xcb\xff<\u0763\x97)+\xf2[\xa6\x89\x17D\x06\xff\x9fo\u0480\x00\u07d4\x8fd\xb9\xc1$m\x85x1d1\a\xd3U\xb5\xc7_\xef]O\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x8ff\x0f\x8b.L|\u00b4\xac\x9cG\xed(P\x8d_\x8f\x86P\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x8fi\xea\xfd\x023\xca\xdb@Y\xabw\x9cF\xed\xf2\xa0PnH\x89`\xf0f \xa8IE\x00\x00\xe0\x94\x8fq~\xc1U/LD\x00\x84\xfb\xa1\x15J\x81\xdc\x00>\xbd\xc0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x8f\x8a\xcb\x10v\a8\x84y\xf6K\xaa\xab\xea\x8f\xf0\a\xad\xa9}\x8a\x05\xc6\xf3\b\n\xd4#\xf4\x00\x00\u07d4\x8f\x8c\xd2n\x82\xe7\xc6\xde\xfd\x02\u07ed\a\x97\x90!\xcb\xf7\x15\f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x8f\x8f7\u042d\x8f3]*q\x01\xb4\x11V\xb6\x88\xa8\x1a\x9c\xbe\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\x8f\x92\x84O(*\x92\x99\x9e\u5d28\xd7s\xd0kiM\xbd\x9f\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x8f\xact\x8fxJ\x0f\xedh\u06e43\x19\xb4*u\xb4d\x9cn\x891T\xc9r\x9d\x05x\x00\x00\u07d4\x8f\u0665\xc3:}\x9e\xdc\xe0\x99{\xdfw\xab0d$\xa1\x1e\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8f\xef\xfa\xdb8z\x15G\xfb(M\xa9\xb8\x14\u007f>|m\xc6\u0689-b{\xe4S\x05\b\x00\x00\u07d4\x8f\xf4`Ehw#\xdc3\xe4\u0419\xa0i\x04\xf1\ubd44\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\x8f\xfa\x06!\"\xac0t\x18\x82\x1a\u06d3\x11\aZ7\x03\xbf\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8f\xfe2)\x97\xb8\xe4\x04B-\x19\xc5J\xad\xb1\x8f[\xc8\u9dc9\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x90\x01\x94\u0131\aC\x05\u045d\xe4\x05\xb0\xacx(\x0e\xca\xf9g\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x90\x03\xd2p\x89\x1b\xa2\xdfd=\xa84\x15\x83\x195E\xe3\xe0\x00\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x90\x05z\xf9\xaaf0~\xc9\xf03\xb2\x97$\u04f2\xf4\x1e\xb6\xf9\x8a\x19\xd1\u05aa\xdb,R\xe8\x00\x00\u07d4\x90\x0f\v\x8e5\xb6h\xf8\x1e\xf2R\xb18U\xaaP\a\xd0\x12\xe7\x89\x17\n\x0fP@\xe5\x04\x00\x00\u07d4\x90\x18\xcc\x1fH\xd20\x8e%*\xb6\b\x9f\xb9\x9a|\x1dV\x94\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x90\x1d\x99\xb6\x99\xe5\u0191\x15\x19\xcb v\xb4\xc7c0\xc5M\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x90-t\xa1W\xf7\u04b9\xa37\x8b\x1fVp70\xe0:\x17\x19\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x904\x13\x87\x8a\xea;\xc1\bc\t\xa3\xfev\x8beU\x9e\x8c\xab\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x90If\xcc\"\x13\xb5\xb8\xcb[\xd6\b\x9e\xf9\xcd\xdb\xef~\xdf\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x90L\xaaB\x9ca\x9d\x94\x0f\x8egA\x82j\r\xb6\x92\xb1\x97(\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x90R\xf2\xe4\xa3\xe3\xc1-\xd1\xc7\x1b\xf7\x8aN\xc3\x04=\u020b~\x89\x0e~\xeb\xa3A\vt\x00\x00\u0794\x90U&V\x8a\xc1#\xaf\xc0\xe8J\xa7\x15\x12O\xeb\xe8=\xc8|\x88\xf8i\x93)g~\x00\x00\u07d4\x90\x92\x91\x87\a\xc6!\xfd\xbd\x1d\x90\xfb\x80\xebx\u007f\xd2osP\x89\x85[[\xa6\\\x84\xf0\x00\x00\u07d4\x90\x9b^v:9\xdc\u01d5\"=s\xa1\u06f7\xd9L\xa7Z\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\x90\xac\xce\xd7\xe4\x8c\b\u01b94dm\xfa\n\xdf)\u0714\aO\x89\x03\vK\x15{\xbdI\x00\x00\u07d4\x90\xb1\xf3p\xf9\xc1\xeb\v\xe0\xfb\x8e+\x8a\xd9jAcq\u074a\x890\xca\x02O\x98{\x90\x00\x00\u07d4\x90\xb6/\x13\x1a_)\xb4UqQ>\xe7\xa7J\x8f\v#\"\x02\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x90\xbdb\xa0P\x84Ra\xfaJ\x9f|\xf2A\xeac\v\x05\ufe09\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x90\xc4\x1e\xba\x00\x8e \xcb\xe9'\xf3F`?\u0206\x98\x12Yi\x89\x02F\xdd\xf9yvh\x00\x00\u07d4\x90\u0480\x9a\xe1\xd1\xff\xd8\xf6>\xda\x01\xdeI\xddU-\xf3\u047c\x89\u063beI\xb0+\xb8\x00\x00\u07d4\x90\xdc\t\xf7\x17\xfc*[i\xfd`\xba\b\xeb\xf4\v\xf4\xe8$l\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x90\xe3\x00\xacqE\x1e@\x1f\x88\u007fnw(\x85\x16G\xa8\x0e\a\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x90\xe3Z\xab\xb2\xde\xef@\x8b\xb9\xb5\xac\xefqDW\xdf\xdebr\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\x90\xe7\a\x0fM\x03?\xe6\x91\f\x9e\xfeZ'\x8e\x1f\xc6#M\xef\x89\x05q8\b\x19\xb3\x04\x00\x00\u07d4\x90\xe9>M\xc1q!HyR36\x14\x00+\xe4#VI\x8e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x90\u9a68.\u06a8\x14\u0084\xd22\xb6\u9e90p\x1dIR\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\x90\xf7t\xc9\x14}\u0790\x85=\xdcC\xf0\x8f\x16\xd4U\x17\x8b\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x90\xfcS{!\x06Xf\n\x83\xba\xa9\xacJ\x84\x02\xf6WF\xa8\x89e\xea=\xb7UF`\x00\x00\u07d4\x91\x05\n\\\xff\xad\xed\xb4\xbbn\xaa\xfb\xc9\xe5\x014(\xe9l\x80\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x91\x05\x17d\xafk\x80\x8eB\x12\xc7~0\xa5W.\xaa1pp\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x91\v}Wz~9\xaa#\xac\xf6*\xd7\xf1\xef4)4\xb9h\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x91\x0e\x99eC4Lh\x15\xfb\x97\u0367\xafK\x86\x98vZ[\x89\x05\x9a\xf6\x98)\xcfd\x00\x00\u07d4\x91\x1f\xee\xa6\x1f\xe0\xedP\u0179\xe5\xa0\xd6`q9\x9d(\xbd\u0189\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x91\x1f\xf23\xe1\xa2\x11\xc0\x17,\x92\xb4l\xf9\x97\x03\x05\x82\xc8:\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x91 \xe7\x11s\xe1\xba\x19\xba\x8f\x9fO\xdb\u072a4\xe1\u05bbx\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91!\x17\x12q\x9f+\bM;8u\xa8Pi\xf4f61A\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x91#\x04\x11\x8b\x80G=\x9e\x9f\xe3\xeeE\x8f\xbea\x0f\xfd\xa2\xbb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x91Tky\xec\xf6\x9f\x93kZV\x15\b\xb0\xd7\xe5\f\u0159/\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x91V\u0440)5\x0eG\x04\b\xf1_\x1a\xa3\xbe\x9f\x04\ng\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x91b\x0f>\xb3\x04\xe8\x13\u048b\x02\x97Ume\xdcN]\u5a89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\x91k\xf7\xe3\xc5E\x92\x1d2\x06\xd9\x00\xc2O\x14\x12|\xbd^p\x8a\x03\xd0\u077c}\xf2\xbb\x10\x00\x00\u0794\x91l\xf1}qA(\x05\xf4\xaf\xc3DJ\v\x8d\xd1\xd93\x9d\x16\x88\xc6s\xce<@\x16\x00\x00\u07d4\x91{\x8f\x9f:\x8d\t\xe9 ,R\u009erA\x96\xb8\x97\xd3^\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\x91\x89g\x91\x8c\u0617\xdd\x00\x05\xe3m\xc6\u0203\xefC\x8f\xc8\u01c9\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x91\x89\x8e\xab\x8c\x05\xc0\"(\x83\xcdM\xb2;w\x95\xe1\xa2J\u05c9lk\x93[\x8b\xbd@\x00\x00\u0794\x91\x91\xf9F\x98!\x05\x16\xcfc!\xa1B\a\x0e Yvt\xed\x88\xee\x9d[\xe6\xfc\x11\x00\x00\u07d4\x91\xa4\x14\x9a,{\x1b:g\xea(\xaf\xf3G%\u0fcdu$\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x91\xa7\x87\xbcQ\x96\xf3HW\xfe\f7/M\xf3v\xaa\xa7f\x13\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91\xa8\xba\xae\xd0\x12\xea.c\x80;Y=\r\f*\xabL[\n\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x91\xac\\\xfeg\xc5J\xa7\xeb\xfb\xa4HflF\x1a;\x1f\xe2\xe1\x89\x15\xc94\x92\xbf\x9d\xfc\x00\x00\u07d4\x91\xbb?y\x02+\xf3\xc4S\xf4\xff%n&\x9b\x15\xcf,\x9c\xbd\x89RX\\\x13\xfe:\\\x00\x00\u07d4\x91\xc7^<\xb4\xaa\x89\xf3F\x19\xa1d\xe2\xa4x\x98\xf5gM\x9c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91\xc8\f\xaa\b\x1b85\x1d*\x0e\x0e\x00\xf8\n4\xe5dt\xc1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x91\xccF\xaa7\x9f\x85jf@\xdc\xcdZd\x8ay\x02\xf8I\u0649\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x91\u04a9\xee\x1am\xb2\x0fS\x17\u0327\xfb\xe218\x95\u06ce\xf8\x8a\x01\xcc\u00e5/0n(\x00\x00\u07d4\x91\xd6n\xa6(\x8f\xaaK=`l*\xa4\\{k\x8a%'9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x91\u06f6\xaa\xad\x14\x95\x85\xbeG7\\]m\xe5\xff\t\x19\x15\x18\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x91\xe8\x81\x06R\xe8\xe6\x16\x15%\xd6;\xb7u\x1d\xc2\x0fg`v\x89'Mej\xc9\x0e4\x00\x00\u07d4\x91\xf5\x16\x14l\xda (\x17\x19\x97\x80`\u01beAI\x06|\x88\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x91\xf6$\xb2J\x1f\xa5\xa0V\xfeW\x12)\xe77\x9d\xb1K\x9a\x1e\x8a\x02\x8a\x85\x17\xc6i\xb3W\x00\x00\xe0\x94\x91\xfe\x8aLad\u07cf\xa6\x06\x99]k\xa7\xad\xca\xf1\u0213\u038a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4\x92\x1fRa\xf4\xf6\x12v\a\x06\x89&%\xc7^{\u0396\xb7\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92!\xc9\xce\x01#&et\x10\x96\xac\a#Y\x03\xad\x1f\xe2\xfc\x89\x06\xdbc3U\"b\x80\x00\u07d4\x92%\x988`\xa1\xcbF#\xc7$\x80\xac\x16'+\f\x95\xe5\xf5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x92%\xd4jZ\x80\x949$\xa3\x9e[\x84\xb9m\xa0\xacE\x05\x81\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x92* \u01da\x1d:&\xdd8)g{\xf1\xd4\\\x8fg+\xb6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x92C\x8eR\x03\xb64o\xf8\x86\xd7\xc3b\x88\xaa\xcc\xccx\xce\u028965\u026d\xc5\u07a0\x00\x00\u07d4\x92C\xd7v-w({\x12c\x86\x88\xb9\x85N\x88\xa7i\xb2q\x8965\u026d\xc5\u07a0\x00\x00\u0794\x92K\xcez\x85<\x97\v\xb5\xec{\xb7Y\xba\xeb\x9ct\x10\x85{\x88\xbe -j\x0e\xda\x00\x00\u07d4\x92N\xfam\xb5\x95\xb7\x93\x13'~\x881\x96%\akX\n\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92U\x82&\xb3\x84bl\xadH\xe0\x9d\x96k\xf19^\xe7\xea]\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4\x92`\x82\xcb~\xedK\x19\x93\xad$ZGrg\xe1\xc3<\xd5h\x89\x14Jt\xba\u07e4\xb6\x00\x00\u07d4\x92b\t\xb7\xfd\xa5N\x8d\u06dd\x9eM=\x19\xeb\u070e\x88\u009f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92h\xd6&FV6\x11\xdc;\x83*0\xaa#\x94\xc6F\x13\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92i\x8e4Sx\xc6-\x8e\xda\x18M\x946j\x14K\f\x10[\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x92y:\u0173rhwJq0\xde+\xbd3\x04\x05f\x17s\x89\x02,\xa3X|\xf4\xeb\x00\x00\xe0\x94\x92y\xb2\"\x8c\xec\x8f{M\xda?2\x0e\x9a\x04f\xc2\xf5\x85\u028a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x92|\xb7\xdc\x18p6\xb5B{\xc7\xe2\x00\xc5\xecE\f\x1d'\u0509\v\xb5\x9a'\x95<`\x00\x00\u07d4\x92|\u00bf\xda\x0e\b\x8d\x02\xef\xf7\v8\xb0\x8a\xa5<\xc3\tA\x89do`\xa1\xf9\x866\x00\x00\xe0\x94\x92\x84\xf9m\xdbG\xb5\x18n\xe5X\xaa12M\xf56\x1c\x0fs\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x92\x9d6\x8e\xb4j-\x1f\xbd\xc8\xff\xa0`~\xdeK\xa8\x8fY\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xa7\u0166Cb\xe9\xf8B\xa2=\xec\xa2\x105\x85\u007f\x88\x98\x00\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x92\xa8\x98\xd4o\x19q\x9c8\x12j\x8a<'\x86z\xe2\xce\u5589lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xa9q\xa79y\x9f\x8c\xb4\x8e\xa8G]r\xb2\xd2GAr\xe6\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x92\xaa\xe5\x97h\xed\xdf\xf8<\xfe`\xbbQ.s\n\x05\xa1a\u05c9\\\x97xA\fv\u0440\x00\u07d4\x92\xad\x1b=u\xfb\xa6}Tf=\xa9\xfc\x84\x8a\x8a\xde\x10\xfag\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xae[|~\xb4\x92\xff\x1f\xfa\x16\xddB\xad\x9c\xad@\xb7\xf8\u0709.\xe4IU\b\x98\xe4\x00\x00\u07d4\x92\xc0\xf5s\xec\xcfb\xc5H\x10\xeek\xa8\xd1\xf1\x13T+0\x1b\x89\xb7ro\x16\u0331\xe0\x00\x00\u07d4\x92\xc1?\xe0\xd6\u0387\xfdP\xe0=\uf7e6@\x05\t\xbdps\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x92\xc9L( \xdf\xcfqV\xe6\xf10\x88\xec\u754b6v\xfd\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4\x92\xcf\xd6\x01\x88\xef\u07f2\xf8\xc2\xe7\xb1i\x8a\xbb\x95&\xc1Q\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\u062d\x9aMah;\x80\u0526g.\x84\xc2\rbB\x1e\x80\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x92\u0725\xe1\x02\xb3\xb8\x1b`\xf1\xa5\x04cIG\xc3t\xa8\x8c\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xe454\x0e\x9d%<\x00%c\x89\xf5+\x06}U\x97Nv\x89\x0e\x87?D\x13<\xb0\x00\x00\xe0\x94\x92\xe49(\x16\xe5\xf2\xef_\xb6X7\xce\xc2\xc22\\\xc6I\"\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x92\xe6X\x1e\x1d\xa1\xf9\xb8F\xe0\x93G3=\xc8\x18\xe2\u04acf\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\x93\x1d\xf3M\x12%\xbc\xd4\"Nch\r\\L\t\xbc\xe75\xa6\x89\x03\xaf\xb0\x87\xb8v\x90\x00\x00\u07d4\x93\x1f\xe7\x12\xf6B\a\xa2\xfdP\"r\x88CT\x8b\xfb\x8c\xbb\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x93#_4\r(c\xe1\x8d/LR\x99e\x16\x13\x8d\"\x02g\x89\x04\x00.D\xfd\xa7\xd4\x00\x00\u07d4\x93%\x82U\xb3|\u007fX\xf4\xb1\x06s\xa92\xdd:\xfd\x90\xf4\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x93(\xd5\\\xcb?\xceS\x1f\x19\x93\x823\x9f\x0eWn\xe8@\xa3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x93)\xff\xdc&\x8b\xab\u0788t\xb3f@l\x81D[\x9b-5\x89\x16\xe6/\x8cs\f\xa1\x80\x00\u07d4\x93+\x9c\x04\xd4\r*\xc80\x83\xd9B\x98\x16\x9d\xae\x81\xab.\u0409lk\x93[\x8b\xbd@\x00\x00\u07d4\x9346\xc8G&U\xf6L:\xfa\xaf|Lb\x1c\x83\xa6+8\x8965\u026d\xc5\u07a0\x00\x00\u0794\x93;\xf3?\x82\x99p+:\x90&B\xc3>\v\xfa\xea\\\x1c\xa3\x88\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x93@4\\\xa6\xa3\uaf77sc\xf2X`C\xf2\x948\xce\v\x89\x1c\xc8\x05\xda\r\xff\xf1\x00\x00\xe0\x94\x93@\xb5\xf6x\xe4^\xe0^\xb7\b\xbbz\xbbn\xc8\xf0\x8f\x1bk\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x93J\xf2\x1b~\xbf\xa4g\xe2\xce\xd6Z\xa3N\xdd:\x0e\xc7\x132\x8a\a\x80\x1f>\x80\xcc\x0f\xf0\x00\x00\xe0\x94\x93PiDJj\x98M\xe2\bNFi*\xb9\x9fg\x1f\xc7'\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\xe0\x94\x93P~\x9e\x81\x19\xcb\xce\u068a\xb0\x87\xe7\xec\xb0q8=i\x81\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x93g\x8a<W\x15\x1a\xebh\xef\xdcC\xefM6\xcbY\xa0\t\xf3\x89\x01\xa1*\x92\xbc<>\x00\x00\xe0\x94\x93m\xcf\x00\x01\x94\xe3\xbf\xf5\n\u0174$:;\xa0\x14\xd6a\u060a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x93o8\x13\xf5\xf6\xa1;\x8eO\xfe\xc8?\xe7\xf8&\x18jq\u0349\x1c0s\x1c\xec\x03 \x00\x00\u07d4\x93t\x86\x9dJ\x99\x11\xee\x1e\xafU\x8b\xc4\u00b6>\xc6:\xcf\u074965\u026d\xc5\u07a0\x00\x00\u07d4\x93uc\u0628\x0f\u05657\xb0\xe6m \xa0%%\xd5\u0606`\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\x93v\xdc\xe2\xaf.\xc8\xdc\xdat\x1b~sEfF\x81\xd96h\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x93\x86\x8d\xdb*yM\x02\xeb\xda/\xa4\x80|v\xe3`\x98X\u0709m\xee\x15\xfc|$\xa7\x80\x00\xe0\x94\x93\x9cC\x13\xd2(\x0e\xdf^\a\x1b\xce\xd8F\x06?\n\x97]T\x8a\x19i6\x89t\xc0[\x00\x00\x00\xe0\x94\x93\xa6\xb3\xabB0\x10\xf9\x81\xa7H\x9dJ\xad%\xe2b\\WA\x8a\x04F\x80\xfej\x1e\xdeN\x80\x00\u07d4\x93\xaa\x8f\x92\xeb\xff\xf9\x91\xfc\x05^\x90ne\x1a\xc7h\xd3+\u02092\xf5\x1e\u06ea\xa30\x00\x00\u07d4\x93\xb4\xbf?\xdf\xf6\xde?NV\xbamw\x99\xdcK\x93\xa6T\x8f\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4\x93\xbc}\x9aJ\xbdD\u023b\xb8\xfe\x8b\xa8\x04\xc6\x1a\xd8\xd6Wl\x89\xd8\xd6\x11\x9a\x81F\x05\x00\x00\u07d4\x93\xc2\xe6N]\xe5X\x9e\xd2P\x06\xe8C\x19n\xe9\xb1\xcf\v>\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\x93\u020e-\x88b\x1e0\xf5\x8a\x95\x86\xbe\xd4\t\x89\x99\xebg\u074a\x06\x9bZ\xfa\xc7P\xbb\x80\x00\x00\u07d4\x93\xe0\xf3~\xcd\xfb\x00\x86\xe3\xe8b\xa9p4D{\x1eM\xec\x1a\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\xe0\x94\x93\xe3\x03A\x1a\xfa\xf6\xc1\a\xa4A\x01\u026c[6\xe9\xd6S\x8b\x8a\r\xf9\xdd\xfe\xcd\x03e@\x00\x00\u07d4\x93\xf1\x8c\xd2R`@v\x14\x88\xc5\x13\x17M\x1eycv\x8b,\x89\x82\xff\xac\x9a\u0553r\x00\x00\u07d4\x94\x0fqQ@P\x9f\xfa\xbf\x97EF\xfa\xb3\x90\"\xa4\x19R\u0489K\xe4\xe7&{j\xe0\x00\x00\u07d4\x94,k\x8c\x95[\xc0\u0608\x12g\x8a#g%\xb3'9\xd9G\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\x94=7\x86JJS}5\xc8\u0657#\xcdd\x06\xce%b\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94C\x9c\xa9\xcc\x16\x9ay\u0520\x9c\xae^gvJo\x87\x1a!\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x94D\x9c\x01\xb3*\u007f\xa5Z\xf8\x10OB\xcd\xd8D\xaa\x8c\xbc@\x8a\x03\x81\x11\xa1\xf4\xf0<\x10\x00\x00\xe0\x94\x94E\xba\\0\xe9\x89a\xb8`$a\xd08]@\xfb\xd8\x03\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x94O\a\xb9o\x90\xc5\xf0\xd7\xc0\u0140S1I\xf3\xf5\x85\xa0x\x89\x04\x02\xf4\xcf\xeeb\xe8\x00\x00\u07d4\x94T\xb3\xa8\xbf\xf9p\x9f\xd0\u1407~l\xb6\u0219t\xdb\u0589\x90\xf54`\x8ar\x88\x00\x00\u07d4\x94]\x96\xeaW>\x8d\xf7&+\xbf\xa5r\"\x9bK\x16\x01k\x0f\x89\vX\x9e\xf9\x14\xc1B\x00\x00\u07d4\x94^\x18v\x9d~\xe7'\xc7\x01?\x92\xde$\xd1\x17\x96\u007f\xf3\x17\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94a'\x81\x03;W\xb1F\xeet\xe7S\xc6r\x01\u007fS\x85\xe4\x89\xc3(\t>a\xee@\x00\x00\xe0\x94\x94dJ\xd1\x16\xa4\x1c\xe2\xca\u007f\xbe\xc6\t\xbd\xefs\x8a*\xc7\u01ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x94p\xcc6YE\x86\x82\x18!\xc5\u0256\xb6\xed\xc8;mZ2\x89\x01M\x11 \u05f1`\x00\x00\xe0\x94\x94u\xc5\x10\xec\x9a&\x97\x92GtL=\x8c;\x0e\v_D\u04ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x94~\x11\xe5\xea)\ro\u00f3\x80H\x97\x9e\f\xd4N\xc7\xc1\u007f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94\x83\u064f\x14\xa3?\xdc\x11\x8d@9U\u00995\xed\xfc_p\x89\x18\xea;4\xefQ\x88\x00\x00\u07d4\x94\x911\xf2\x89C\x92\\\xfc\x97\xd4\x1e\f\xea\v&)s\xa70\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\x94\x9f\x84\xf0\xb1\xd7\u0127\xcfI\xee\u007f\x8b,J\x13M\xe3(x\x89%\"H\u07b6\xe6\x94\x00\x00\u07d4\x94\x9f\x8c\x10{\xc7\xf0\xac\xea\xa0\xf1pR\xaa\xdb\xd2\xf9s+.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94\xa7\u0368\xf4\x81\xf9\u061dB\xc3\x03\xae\x162\xb3\xb7\t\xdb\x1d\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x94\xa9\xa7\x16\x911| d'\x1bQ\xc95?\xbd\xed5\x01\xa8\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x94\xadK\xad\x82K\xd0\ub7a4\x9cX\u03bc\xc0\xff^\b4k\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x94\xbb\xc6}\x13\xf8\x9e\xbc\xa5\x94\xbe\x94\xbcQp\x92\f0\xd9\xf3\x89\x04X\xff\xa3\x15\nT\x00\x00\u07d4\x94\xbe:\xe5Ob\xd6c\xb0\xd4\u031e\x1e\xa8\xfe\x95V\ua7bf\x89\x01C\x13,\xa8C\x18\x00\x00\xe0\x94\x94\xc0U\xe8X5z\xaa0\xcf A\xfa\x90Y\xce\x16J\x1f\x91\x8a\x04<%\xe0\xdc\xc1\xbd\x1c\x00\x00\xe0\x94\x94\xc7B\xfdz\x8by\x06\xb3\xbf\xe4\xf8\x90O\xc0\xbe\\v\x803\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x94\xcaV\xdew\u007f\xd4S\x17\u007f^\x06\x94\xc4x\xe6j\xff\x8a\x84\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x94\xd8\x10t\xdbZ\xe1\x97\u04bb\x13s\xab\x80\xa8}\x12\x1cK\u04ca\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x94\u06c0xs\x86\n\xac=Z\xea\x1e\x88^R\xbf\xf2\x86\x99T\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4\x94\xe1\xf5\u02db\x8a\xba\xce\x03\xa1\xa6B\x82VU;i\f#U\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x94\xef\x8b\xe4Pw\xc7\xd4\xc5e'@\u0794jbbOq?\x89\x05l\xf5Y:\x18\xf8\x80\x00\u07d4\x94\xf1?\x9f\b6\xa3\xee$7\xa8I\"\u0498M\xc0\xf7\xd5;\x89\xa2\xa02\x9b\u00ca\xbe\x00\x00\u07d4\x94\xf8\xf0W\xdb~`\xe6u\xad\x94\x0f\x15X\x85\u0464w4\x8e\x89\x15\xbeat\xe1\x91.\x00\x00\xe0\x94\x94\xfc\u03ad\xfe\\\x10\x9c^\xae\xafF-C\x871B\u020e\"\x8a\x01\x045a\xa8\x82\x93\x00\x00\x00\u07d4\x95\x03N\x16!\x86Q7\xcdG9\xb3F\xdc\x17\xda:'\xc3N\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\x95\fh\xa4\t\x88\x15M#\x93\xff\xf8\xda|\u0369\x96\x14\xf7,\x89\xf9AF\xfd\x8d\xcd\xe5\x80\x00\xe0\x94\x95\x0f\xe9\xc6\xca\xd5\f\x18\xf1\x1a\x9e\xd9\xc4W@\xa6\x18\x06\x12\u040a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x95!\x83\xcf\u04ce5.W\x9d6\xde\xce\u0171\x84P\xf7\xfb\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95'\x8b\b\xde\xe7\xc0\xf2\xc8\xc0\xf7\"\xf9\xfc\xbb\xb9\xa5$\x1f\u0689\x82\x93\t\xf6O\r\xb0\x00\x00\u07d4\x95,W\xd2\xfb\x19Q\a\xd4\xcd\\\xa3\x00wA\x19\u07ed/x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x955r\xf0\xeam\xf9\xb1\x97\xca\xe4\x0eK\x8e\xcc\x05lCq\u014965\u026d\xc5\u07a0\x00\x00\u07d4\x95>\xf6R\xe7\xb7i\xf5=nxjX\x95/\xa9>\xe6\xab\u725b\ny\x1f\x12\x110\x00\x00\u07d4\x95DpF1;/:^\x19\xb9H\xfd;\x8b\xed\xc8,q|\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x95]\xb3\xb7C`\xb9\xa2hg~s\u03a8!f\x8a\xf6\xfa\u038a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\x95`\xe8\xacg\x18\xa6\xa1\xcd\xcf\xf1\x89\xd6\x03\xc9\x06>A=\xa6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x95g\xa0\u0781\x1d\xe6\xff\t[~\xe6N\u007f\x1b\x83\xc2a[\x80\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x95h\x1c\xda\xe6\x9b I\xce\x10\x1e2\\u\x98\x92\xca\xc3\xf8\x11\x89\x9a\xe9*\x9b\xc9L@\x00\x00\xe0\x94\x95h\xb7\xdeuV(\xaf5\x9a\x84T=\xe25\x04\xe1^A\xe6\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x95i\xc6:\x92\x84\xa8\x05bm\xb3\xa3.\x9d#c\x93GaQ\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x95\x80\x9e\x8d\xa3\xfb\xe4\xb7\xf2\x81\xf0\xb8\xb1q_B\x0f}}c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95\x9fW\xfd\xedj\xe3y\x13\xd9\x00\xb8\x1e_H\xa7\x93\"\xc6'\x89\r\xdb&\x10GI\x11\x80\x00\u07d4\x95\x9f\xf1\u007f\x1dQ\xb4s\xb4@\x10\x05'U\xa7\xfa\x8cu\xbdT\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x95\xa5w\xdc.\xb3\xael\xb9\xdf\xc7z\xf6\x97\xd7\xef\xdf\xe8\x9a\x01\x89\a_a\x0fp\xed \x00\x00\u07d4\x95\xcbm\x8acy\xf9J\xba\x8b\x88ViV,MD\x8eV\xa7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95\xd5PB{ZQLu\x1ds\xa0\xf6\u049f\xb6]\"\xed\x10\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x95\u064d\f\x10i\x90\x8f\x06zR\xac\xac+\x8bSM\xa3z\xfd\x89oY\xb60\xa9)p\x80\x00\xe0\x94\x95\xdfN4E\xd7f&$\u010e\xbat\u03de\nS\xe9\xf72\x8a\v\xdb\xc4\x1e\x03H\xb3\x00\x00\x00\u07d4\x95\xe6\xa5K-_g\xa2JHu\xafu\x10|\xa7\xea\x9f\xd2\xfa\x89Hz\x9a0E9D\x00\x00\xe0\x94\x95\xe6\xf9=\xac\"\x8b\xc7XZ%sZ\xc2\xd0v\xcc:@\x17\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x95\xe7ad$\xcd\ta\xa7\x17'$t7\xf0\x06\x92r(\x0e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x95\xe8\n\x82\xc2\f\xbe= `$,\xb9-sX\x10\xd04\xa2\x89\x01\xc3.F?\u0539\x80\x00\u07d4\x95\xf6-\x02C\xed\xe6\x1d\xad\x9a1e\xf59\x05'\rT\xe2B\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\x95\xfbZ\xfb\x14\xc1\uf6b7\xd1y\xc5\xc3\x00P?\xd6j^\xe2\x89\x01\xda\xf7\xa0+\r\xbe\x80\x00\u07d4\x96\x10Y\"\x02\u0082\xab\x9b\u0628\x84Q\x8b>\v\xd4u\x817\x89\x0e\x87?D\x13<\xb0\x00\x00\xe0\x94\x96\x1cY\xad\xc7E\x05\u0446M\x1e\xcf\u02ca\xfa\x04\x12Y<\x93\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x96,\r\xec\x8a=FK\xf3\x9b\x12\x15\xea\xfd&H\n\xe4\x90\u0349l\x82\xe3\xea\xa5\x13\xe8\x00\x00\u07d4\x96,\xd2*\x8e\xdf\x1eONU\xb4\xb1]\xdb\xfb]\x9dT\x19q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x963K\xfe\x04\xff\xfaY\x02\x13\xea\xb3e\x14\xf38\xb8d\xb76\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x967\xdc\x12r=\x9cxX\x85B\uac02fO?\x03\x8d\x9d\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x96N\xabK'kL\u0618>\x15\xcar\xb1\x06\x90\x0f\xe4\x1f\u0389\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x96b\xee\x02\x19&h+1\xc5\xf2\x00\xceEz\xbe\xa7ll\xe9\x89$Y\x0e\x85\x89\xebj\x00\x00\xe0\x94\x96l\x04x\x1c\xb5\xe6}\xde25\xd7\xf8b\x0e\x1a\xb6c\xa9\xa5\x8a\x10\r P\xdacQ`\x00\x00\u07d4\x96pv\xa8w\xb1\x8e\xc1ZA[\xb1\x16\xf0n\xf3&E\u06e3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96{\xfa\xf7bC\u0379@<g\xd2\xce\xef\xde\xe9\n?\xebs\x894\x9d\x87\xf2\xa2\xdc/\x00\x00\u07d4\x96}AB\xafw\x05\x15\xddpb\xaf\x93I\x8d\xbf\xdf\xf2\x9f \x89\x01\x18T\xd0\xf9\xce\xe4\x00\x00\u07d4\x96\x8b\x14d\x8f\x01\x833h|\xd2\x13\xfad\n\xec\x04\xcec#\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x96\x8d\xea`\xdf>\t\xae<\x8d5\x05\xe9\xc0\x80EK\xe0\xe8\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x96\x92A\x91\xb7\xdfe[3\x19\xdcma7\xf4\x81\xa7:\x0f\xf3\x89\xd9\xec\xb4\xfd \x8eP\x00\x00\u07d4\x96\x96\x05!83\x8cr/\x11@\x81\\\xf7t\x9d\r;:t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x96\xa5_\x00\xdf\xf4\x05\xdcM\xe5\xe5\x8cW\xf6\xf6\xf0\xca\xc5]/\x89jf\x167\x9c\x87\xb5\x80\x00\u07d4\x96\xaaW?\xed/#4\x10\u06eeQ\x80\x14[#\xc3\x1a\x02\xf0\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x96\xadW\x9b\xbf\xa8\u06ce\xbe\xc9\u0486\xa7.Fa\xee\xd8\xe3V\x89:\v\xa4+\xeca\x83\x00\x00\u07d4\x96\xb44\xfe\x06W\xe4*\u0302\x12\xb6\x86Q9\xde\xde\x15\x97\x9c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x96\xb9\x06\xear\x9fFU\xaf\xe3\xe5}5'|\x96}\xfa\x15w\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x96\xd6-\xfdF\b\u007fb@\x9d\x93\xdd`a\x88\xe7\x0e8\x12W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96\xd9\u0328\xf5^\xea\x00@\xecn\xb3H\xa1wK\x95\xd9>\xf4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x96\xe7\xc0\xc9\u057f\x10\x82\x1b\xf1@\xc5X\xa1E\xb7\xca\xc2\x13\x97\x899>\xf1\xa5\x12|\x80\x00\x00\u07d4\x96\xeaj\u021a+\xac\x954{Q\u06e6=\x8b\xd5\xeb\xde\xdc\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96\xea\xfb\xf2\xfboM\xb9\xa46\xa7LE\xb5eDR\xe28\x19\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x96\xebR>\x83/P\n\x01}\xe1>\xc2\u007f]6lV\x0e\xff\x89\x10\xac\u03baC\xee(\x00\x00\u07d4\x96\xf0F*\xe6\xf8\xb9`\x88\xf7\xe9\u018ct\xb9\u062d4\xb3G\x89a\t=|,m8\x00\x00\u07d4\x96\xf8 P\vp\xf4\xa3\xe3#\x9da\x9c\xff\x8f\" u\xb15\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x96\xfeY\xc3\u06f3\xaa|\xc8\xcbbH\fe\xe5nb\x04\xa7\xe2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x96\xffoP\x99h\xf3l\xb4,\xbaH\xdb2\xf2\x1fVv\xab\xf8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x97\t8R*\xfb^\x8f\x99Hs\xc9\xfb\xdc&\xe3\xb3~1L\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x97\n\xbdS\xa5O\xcaJd) |\x18-MW\xbb9\u0520\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97\r\x8b\x8a\x00\x16\xd1C\x05O\x14\x9f\xb3\xb8\xe5P\xdc\a\x97\u01c965\u026d\xc5\u07a0\x00\x00\u07d4\x97,/\x96\xaa\x00\u03ca/ Z\xbc\xf8\x93|\fu\xf5\xd8\u0649\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97?N6\x1f\xe5\xde\u0358\x9dL\x8f}|\xc9y\x908]\xaf\x89\x15\x0f\x85C\xa3\x87B\x00\x00\u07d4\x97M\x05A\xabJG\xec\u007fu6\x9c\x00i\xb6J\x1b\x81w\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\x97M/\x17\x89_)\x02\x04\x9d\xea\xae\xcf\t\xc3\x04e\a@-\x88\xcc\x19\u00947\xab\x80\x00\u07d4\x97R\xd1O^\x10\x93\xf0qq\x1c\x1a\xdb\xc4\xe3\xeb\x1e\\W\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97V\xe1v\xc9\xefi>\xe1\xee\u01b9\xf8\xb1Q\xd3\x13\xbe\xb0\x99\x89A\rXj \xa4\xc0\x00\x00\u07d4\x97_7d\xe9{\xbc\xcfv|\xbd;y[\xa8m\x8b\xa9\x84\x0e\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\x97j\x18Sj\xf4\x18tBc\b\x87\x1b\xcd\x15\x12\xa7u\xc9\xf8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x97n<\xea\xf3\xf1\xafQ\xf8\u009a\xff]\u007f\xa2\x1f\x03\x86\xd8\xee\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x97w\xcca\xcfuk\xe3\xb3\xc2\f\xd4I\x1ci\xd2u\xe7\xa1 \x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x97\x81\v\xaf\xc3~\x840c2\xaa\xcb5\xe9*\xd9\x11\xd2=$\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x97\x8cC\f\xe45\x9b\x06\xbc,\xdf\\)\x85\xfc\x95\x0eP\xd5\u0209\x1a\x05V\x90\xd9\u06c0\x00\x00\u07d4\x97\x95\xf6C\x19\xfc\x17\xdd\x0f\x82a\xf9\xd2\x06\xfbf\xb6L\xd0\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97\x99\xca!\xdb\xcfi\xbf\xa1\xb3\xf7+\xacQ\xb9\xe3\xcaX|\xf9\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x97\x9c\xbf!\xdf\xec\x8a\xce?\x1c\x19m\x82\u07d6%4\xdf9O\x89\x99\x91\xd4x\xddM\x16\x00\x00\u07d4\x97\x9dh\x1ca}\xa1o!\xbc\xac\xa1\x01\xed\x16\xed\x01Z\xb6\x96\x89e\xea=\xb7UF`\x00\x00\u07d4\x97\x9f0\x15\x8bWK\x99\x9a\xab4\x81\a\xb9\xee\xd8[\x1f\xf8\xc1\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x97\xa8o\x01\xce?|\xfdDA3\x0e\x1c\x9b\x19\xe1\xb1\x06\x06\xef\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x97\xb9\x1e\xfesP\xc2\xd5~~@k\xab\x18\xf3a{\xcd\xe1J\x8a\x02\x1e\x19\x99\xbb\xd5\u04be\x00\x00\u07d4\x97\xd0\xd9r^;p\xe6u\x841s\x93\x8e\xd3q\xb6,\u007f\xac\x89\t79SM(h\x00\x00\u07d4\x97\xd9\xe4jv\x04\u05f5\xa4\xeaN\xe6\x1aB\xb3\xd25\x0f\xc3\xed\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97\xdc&\xecg\n1\xe0\"\x1d*u\xbc]\xc9\xf9\f\x1fo\u0509\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x97\xde!\xe4!\xc3\u007f\xe4\xb8\x02_\x9aQ\xb7\xb3\x90\xb5\xdfx\x04\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\x97\xe2\x89s\xb8`\xc5g@(\x00\xfb\xb6<\xe3\x9a\x04\x8a=y\x89\x05B%:\x12l\xe4\x00\x00\u07d4\x97\xe5\xcca'\xc4\xf8\x85\xbe\x02\xf4KB\xd1\u0230\xac\x91\u44c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97\xf1\xfeL\x80\x83\xe5\x96!*\x18w(\xdd\\\xf8\n1\xbe\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x97\xf7v\x06W\xc1\xe2\x02u\x90\x86\x96>\xb4!\x1c_\x819\xb9\x8a\n\x8a\t\u007f\xcb=\x17h\x00\x00\xe0\x94\x97\xf9\x9bk\xa3\x13F\u0358\xa9\xfeL0\x8f\x87\u0165\x8cQQ\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x98\n\x84\xb6\x86\xfc1\xbd\xc8<\"\x10XTjq\xb1\x1f\x83\x8a\x89*AUH\xaf\x86\x81\x80\x00\u07d4\x98\x10\xe3J\x94\xdbn\xd1V\xd08\x9a\x0e+\x80\xf4\xfdk\n\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\x1d\xdf\x04\x04\xe4\xd2-\xdaUj\a&\xf0\v-\x98\xab\x95i\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94\x98\x1fq'u\xc0\xda\xd9u\x18\xff\xed\xcbG\xb9\xad\x1dl'b\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\x984h!\x80\xb9\x82\xd1f\xba\u06dd\x9d\x1d\x9b\xbf\x01m\x87\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x986\xb4\xd3\x04sd\x1a\xb5j\xee\xe1\x92Bv\x1drrQx\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x989sB\xec_=L\xb8w\xe5N\xf5\xd6\xf1\xd3fs\x1b\u050a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\x98Fd\x886\xa3\a\xa0W\x18O\xd5\x1fb\x8a_\x8c\x12B|\x8a\x04\vi\xbfC\xdc\xe8\xf0\x00\x00\xe0\x94\x98Jy\x85\xe3\xcc~\xb5\xc96\x91\xf6\xf8\xcc{\x8f$]\x01\xb2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x98]p\xd2\a\x89+\xed9\x85\x90\x02N$!\xb1\xcc\x11\x93Y\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x98m\xf4~v\xe4\u05e7\x89\xcd\xee\x91<\u0243\x16P\x93l\x9d\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x98t\x80?\xe1\xf3\xa06^y\"\xb1Bp\xea\xeb\x03,\xc1\xb5\x89<\xf5\x92\x88$\xc6\xc2\x00\x00\u07d4\x98ub4\x95\xa4l\xdb\xf2YS\x0f\xf88\xa1y\x9e\u00c9\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98v\x18\xc8VV |{\xac\x15\a\xc0\xff\xef\xa2\xfbd\xb0\x92\x89\x03}\xfeC1\x89\xe3\x80\x00\u07d4\x98|\x9b\xcdn?9\x90\xa5+\xe3\xed\xa4q\f'Q\x8fOr\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x98\x82\x96|\xeeh\u04a89\xfa\u062bJ|=\xdd\xf6\xc0\xad\u0209Hx\xbe\x1f\xfa\xf9]\x00\x00\u07d4\x98\x85\\}\xfb\xee3SD\x90J\x12\xc4\fs\x17\x95\xb1:T\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4\x98\x9c\f\xcf\xf6T\xda\x03\xae\xb1\x1a\xf7\x01\x05Ea\xd6)~\x1d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x98\xa0\xe5Lm\x9d\u023e\x96'l\xeb\xf4\xfe\xc4`\xf6#]\x85\x89j\u0202\x10\tR\u01c0\x00\u07d4\x98\xb7i\xcc0\\\xec\xfbb\x9a\x00\xc9\a\x06\x9d~\xf9\xbc:\x12\x89\x01h\u048e?\x00(\x00\x00\xe0\x94\x98\xbaN\x9c\xa7/\xdd\xc2\fi\xb49ov\xf8\x18?z*N\x8a\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\x00\u07d4\x98\xbeimQ\xe3\x90\xff\x1cP\x1b\x8a\x0fc1\xb6(\xdd\u016d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\xbe\u04e7.\xcc\xfb\xaf\xb9#H\x92\x93\xe4)\xe7\x03\xc7\xe2[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\xbfJ\xf3\x81\v\x84#\x87\xdbp\xc1MF\t\x96&\x00=\x10\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x98\xc1\x0e\xbf,O\x97\u02e5\xa1\xab?*\xaf\xe1\xca\xc4#\xf8\u02c9\x10CV\x1a\x88)0\x00\x00\u07d4\x98\xc1\x9d\xba\x81\v\xa6\x11\xe6\x8f/\x83\xee\x16\xf6\xe7tO\f\x1f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x98\xc5IJ\x03\xac\x91\xa7h\xdf\xfc\x0e\xa1\xdd\u0b3f\x88\x90\x19\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\x98\xd2\x04\xf9\b_\x8c\x8e}\xe2>X\x9bd\xc6\xef\xf6\x92\xccc\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x98\xd3s\x19\x92\xd1\xd4\x0e\x12\x11\xc7\xf75\xf2\x18\x9a\xfa\a\x02\xe0\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x98\xe2\xb6\xd6\x06\xfd-i\x91\xc9\xd6\xd4\a\u007f\xdf?\xddE\x85\u06890\xdf\x1ao\x8a\xd6(\x00\x00\u07d4\x98\xe3\xe9\v(\xfc\xca\ue087y\xb8\xd4\nUh\xc4\x11n!\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x98\xe6\xf5G\u06c8\xe7_\x1f\x9c\x8a\xc2\xc5\xcf\x16'\xbaX\v>\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x98\xf4\xaf:\xf0\xae\xde_\xaf\xdcB\xa0\x81\xec\xc1\xf8\x9e<\xcf \x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x98\xf6\xb8\xe6!=\xbc\x9aU\x81\xf4\xcc\xe6e_\x95%+\xdb\a\x89\x11Xr\xb0\xbc\xa40\x00\x00\u07d4\x99\te\r\u05719{\x8b\x8b\x0e\xb6\x94\x99\xb2\x91\xb0\xad\x12\x13\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x99\x11s`\x19G\xc2\bJb\xd69R~\x96\x15\x12W\x9a\xf9\x89 \x86\xac5\x10R`\x00\x00\u07d4\x99\x12\x9d[<\f\xdeG\xea\r\xefM\xfc\a\r\x1fJY\x95'\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x99\x17\u058dJ\xf3A\xd6Q\xe7\xf0\a\\m\xe6\xd7\x14Nt\t\x8a\x012\xd4Gl\b\xe6\xf0\x00\x00\u07d4\x99\x1a\xc7\xcap\x97\x11_& ^\xee\x0e\xf7\xd4\x1e\xb4\xe3\x11\xae\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\x99#e\xd7d\xc5\xce5@9\xdd\xfc\x91.\x02:u\xb8\xe1h\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x99&F\xac\x1a\u02ab\xf5\u076b\xa8\xf9B\x9a\xa6\xa9Nt\x96\xa7\x8967Pz0\xab\xeb\x00\x00\u07d4\x99&\x83'\xc3s3.\x06\xc3\xf6\x16B\x87\xd4U\xb9\xd5\xfaK\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99(\xffqZ\xfc:+`\xf8\xebL\u013aN\xe8\u06b6\u5749\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4\x992\xef\x1c\x85\xb7Z\x9b*\x80\x05}P\x874\xc5\x10\x85\xbe\u0309\x02\xb8?\xa50\x1dY\x00\x00\xe0\x94\x99?\x14ax`^f\xd5\x17\xbex.\xf0\xb3\xc6\x1aN\x19%\x8a\x01|\x1f\x055\u05e5\x83\x00\x00\xe0\x94\x99A7\x04\xb1\xa3.p\xf3\xbc\ri\u0748\x1c8VkT\u02ca\x05\xcckiF1\xf7\x12\x00\x00\u07d4\x99AR\xfc\x95\xd5\xc1\u028b\x88\x11:\xbb\xadMq\x0e@\xde\xf6\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x99D\xfe\xe9\xd3JJ\x88\x00#\u01c92\xc0\vY\xd5\xc8*\x82\x89(\xa8\xa5k6\x90\a\x00\x00\u07d4\x99L\u00b5\"~\xc3\xcf\x04\x85\x12F|A\xb7\xb7\xb7H\x90\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99q\xdf`\xf0\xaef\xdc\xe9\xe8\xc8N\x17\x14\x9f\t\xf9\xc5/d\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x99v\x94~\xff_j\xe5\xda\b\xddT\x11\x92\xf3x\xb4(\xff\x94\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x99}e\x92\xa3\x15\x89\xac\xc3\x1b\x99\x01\xfb\xeb<\xc3\xd6[2\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\x82\xa5\x89\x0f\xfbT\x06\u04ec\xa8\u04bf\xc1\xddp\xaa\xa8\n\xe0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\x87\x8f\x9dn\n~\u066e\u01c2\x97\xb78y\xa8\x01\x95\xaf\xe0\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d4\x99\x8c\x1f\x93\xbc\xdbo\xf2<\x10\xd0\u0712G(\xb7;\xe2\xff\x9f\x896[\xf3\xa43\xea\xf3\x00\x00\u07d4\x99\x91aL[\xaaG\xddl\x96\x87FE\xf9z\xdd,=\x83\x80\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x99\x92J\x98\x16\xbb}\xdf?\xec\x18D\x82\x8e\x9a\xd7\xd0k\xf4\xe6\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x99\x99vh\xf7\xc1\xa4\xff\x9e1\xf9\x97z\xe3\"K\u02c8z\x85\x89\x0f\xc969(\x01\xc0\x00\x00\u07d4\x99\x9cI\xc1t\xca\x13\xbc\x83l\x1e\n\x92\xbf\xf4\x8b'\x15C\u0289\xb1\xcf$\xdd\u0431@\x00\x00\u07d4\x99\xa4\xde\x19\xde\u05d0\b\xcf\xdc\xd4]\x01M.XK\x89\x14\xa8\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x99\xa9k\xf2$.\xa1\xb3\x9e\xceo\xcc\r\x18\xae\xd0\f\x01y\xf3\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x99\xb0\x18\x93+\xca\xd3U\xb6y+%]\xb6p-\xec\x8c\xe5\u0749\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x99\xb7C\xd1\xd9\xef\xf9\r\x9a\x194\xb4\xdb!\xd5\x19\u061bJ8\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x99\xb8\xc8$\x86\x9d\xe9\xed$\xf3\xbf\xf6\x85L\xb6\xddE\xcc?\x9f\x89e\xea=\xb7UF`\x00\x00\u07d4\x99\xc0\x17L\xf8N\a\x83\xc2 \xb4\xebj\xe1\x8f\xe7\x03\x85J\u04c9py\xa2W=\fx\x00\x00\u07d4\x99\xc1\xd9\xf4\fj\xb7\xf8\xa9/\xce/\xdc\xe4zT\xa5\x86\xc5?\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x99\xc26\x14\x1d\xae\xc87\xec\xe0O\xda\xee\x1d\x90\u03cb\xbd\xc1\x04\x89ve\x16\xac\xac\r \x00\x00\u07d4\x99\xc3\x1f\xe7HX7\x87\xcd\xd3\xe5%\xb2\x81\xb2\x18\x96\x179\xe3\x897\b\xba\xed=h\x90\x00\x00\u07d4\x99\xc4u\xbf\x02\xe8\xb9!J\xda_\xad\x02\xfd\xfd\x15\xba6\\\f\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\x99\u0203%\x85F\xcc~N\x97\x1fR.8\x99\x18\xda^\xa6:\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x99\xc9\xf9>E\xfe<\x14\x18\xc3S\xe4\u016c8\x94\xee\xf8\x12\x1e\x89\x05\x85\xba\xf1E\x05\v\x00\x00\xe0\x94\x99\xd1W\x9c\xd4&\x82\xb7dN\x1dOq(D\x1e\xef\xfe3\x9d\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x99\u0475\x85\x96_@jB\xa4\x9a\x1c\xa7\x0fv\x9evZ?\x98\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\x99\xdf\xd0PL\x06\xc7C\xe4e4\xfd{U\xf1\xf9\xc7\xec3)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\xf4\x14|\xcck\u02c0\u0304.i\xf6\xd0\x0e0\xfaA3\u0649\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x99\xf7\u007f\x99\x8b \xe0\xbc\xdc\xd9\xfc\x83\x86ARl\xf2Y\x18\xef\x89a\t=|,m8\x00\x00\u07d4\x99\xfa\xd5\x008\xd0\xd9\xd4\xc3\xfb\xb4\xbc\xe0V\x06\xec\xad\xcdQ!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\xfe\r \x12(\xa7S\x14VU\xd4(\xeb\x9f\xd9I\x85\xd3m\x89i \xbf\xf3QZ:\x00\x00\u07d4\x9a\a\x9c\x92\xa6)\xca\x15\xc8\xca\xfa.\xb2\x8d[\xc1z\xf8(\x11\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9a\r<\xee=\x98\x92\xea;7\x00\xa2\u007f\xf8A@\xd9\x02T\x93\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x9a$\u038dH\\\xc4\xc8nI\u07b3\x90\"\xf9,t0\xe6~\x89Fy\x1f\xc8N\a\xd0\x00\x00\u07d4\x9a,\xe4;]\x89\u0593k\x8e\x8c5G\x91\xb8\xaf\xff\x96$%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9a9\x01bS^9\x88w\xe4\x16x}b9\xe0uN\x93|\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9a=\xa6P#\xa10 \xd2!E\xcf\xc1\x8b\xab\x10\xbd\x19\xceN\x89\x18\xbfn\xa3FJ:\x00\x00\xe0\x94\x9a>+\x1b\xf3F\xdd\a\v\x02sW\xfe\xacD\xa4\xb2\xc9}\xb8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9aL\xa8\xb8!\x17\x89NC\xdbr\xb9\xfax\xf0\xb9\xb9:\xce\t\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x9aR.R\xc1\x95\xbf\xb7\xcf_\xfa\xae\u06d1\xa3\xbath\x16\x1d\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9aZ\xf3\x1c~\x063\x9a\u0234b\x8d|M\xb0\xce\x0fE\u0224\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\x9ac?\xcd\x11,\xce\xebv_\xe0A\x81ps*\x97\x05\u7708\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x9ac\u0445\xa7\x91)\xfd\xab\x19\xb5\x8b\xb61\xea6\xa4 TN\x89\x02F\xdd\xf9yvh\x00\x00\u07d4\x9ag\b\u0778\x90<(\x9f\x83\xfe\x88\x9c\x1e\xdc\xd6\x1f\x85D#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9ao\xf5\xf6\xa7\xaf{z\xe0\xed\x9c \xec\xecP#\u0481\xb7\x86\x89\x8a\x12\xb9\xbdjg\xec\x00\x00\xe0\x94\x9a\x82\x82m<)H\x1d\xcc+\u0495\x00G\xe8\xb6\x04\x86\xc38\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9a\x8e\xcaA\x89\xffJ\xa8\xff~\u0536\xb7\x03\x9f\t\x02!\x9b\x15\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9a\x95;[\xccp\x93y\xfc\xb5Y\u05f9\x16\xaf\u06a5\f\xad\u0309\x05k\xc7^-c\x10\x00\x00\u07d4\x9a\x99\v\x8a\xebX\x8d~\xe7\xec.\xd8\xc2\xe6Os\x82\xa9\xfe\xe2\x89\x01\xd1'\xdbi\xfd\x8b\x00\x00\u07d4\x9a\x9d\x1d\xc0\xba\xa7}n \xc3\xd8I\u01c8b\xdd\x1c\x05L\x87\x89/\xb4t\t\x8fg\xc0\x00\x00\xe0\x94\x9a\xa4\x8cf\xe4\xfbJ\u0419\x93N2\x02.\x82t'\xf2w\xba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9a\xa80\x8fB\x91\x0eZ\xde\t\xc1\xa5\xe2\x82\xd6\xd9\x17\x10\xbd\xbf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9a\xaa\xfa\x00gd~\u0659\x06kzL\xa5\xb4\xb3\xf3\xfe\xaao\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9a\xb9\x88\xb5\x05\xcf\xee\x1d\xbe\x9c\u044e\x9bTs\xb9\xa2\xd4\xf56\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\x9a\xb9\x8dm\xbb\x1e\xaa\xe1mE\xa0EhT\x1a\xd3\xd8\xfe\x06\u0309\x0e\xc5\x04d\xfe#\xf3\x80\x00\xe0\x94\x9a\xba+^'\xffx\xba\xaa\xb5\xcd\u0248\xb7\xbe\x85\\\xeb\xbd\u038a\x02\x1e\f\x00\x13\a\n\xdc\x00\x00\u07d4\x9a\xc4\xdaQ\xd2x\"\xd1\xe2\b\xc9n\xa6J\x1e[U)\x97#\x89\x05lUy\xf7\"\x14\x00\x00\u0794\x9a\xc8S\x97y*i\u05cf(k\x86C*\a\xae\u03b6\x0ed\x88\xc6s\xce<@\x16\x00\x00\xe0\x94\x9a\xc9\a\xee\x85\xe6\xf3\xe2#E\x99\x92\xe2V\xa4?\xa0\x8f\xa8\xb2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9a\xd4\u007f\xdc\xf9\u0354-(\xef\xfd[\x84\x11[1\xa6X\xa1>\x89\xb2Y\xec\x00\xd5;(\x00\x00\u07d4\x9a\xdb\u04fc{\n\xfc\x05\xd1\xd2\xed\xa4\x9f\xf8c\x93\x9cH\xdbF\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x9a\xdfE\x8b\xff5\x99\xee\xe1\xa2c\x98\x85<W[\u00ccc\x13\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\x9a\xe1;\u0602\xf2Weu\x92\x1a\x94\x97L\xbe\xa8a\xba\r5\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\x9a\xe9Gk\xfe\xcd5\x91\x96M\xd3%\u03cc*$\xfa\xed\x82\xc1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9a\xf1\x00\xcc=\xae\x83\xa34\x02\x05\x1c\xe4Ik\x16aT\x83\xf6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9a\xf1\x13\x99Q\x1c!1\x81\xbf\xda:\x8b&L\x05\xfc\x81\xb3\u038a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u0794\x9a\xf5\u0249L3\xe4,,Q\x8e:\xc6p\xea\x95\x05\u0475>\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x9a\xf9\xdb\xe4t\"\xd1w\xf9E\xbd\xea\xd7\xe6\xd8)05b0\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x9a\xfaSkLf\xbc8\xd8u\u0133\x00\x99\xd9&\x1f\xdb8\xeb\x89\v*\x8f\x84*w\xbc\x80\x00\u07d4\x9b\x06\xad\x84\x1d\xff\xbeL\xcfF\xf1\x03\x9f\u00c6\xf3\xc3!Dn\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\x11h\u078a\xb6KGU/3\x89\x80\n\x9c\xc0\x8bFf\u03c9]\u0212\xaa\x111\xc8\x00\x00\u07d4\x9b\x18\x11\xc3\x05\x1fF\xe6d\xaeK\xc9\xc8$\u0445\x92\xc4WJ\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x9b\x18G\x86U\xa4\x85\x1c\xc9\x06\xe6`\xfe\xaca\xf7\xf4\u023f\xfc\x89\xe2G\x8d8\x90}\x84\x00\x00\u07d4\x9b\"\xa8\r\\{3t\xa0[D`\x81\xf9}\n4\a\x9e\u007f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x9b+\xe7\xf5gT\xf5\x05\xe3D\x1a\x10\xf7\xf0\xe2\x0f\xd3\xdd\xf8I\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x9b2\xcfOQ\x15\xf4\xb3J\x00\xa6La}\xe0c\x875C#\x89\x05\xb8\x1e\u0608 |\x80\x00\u07d4\x9bC\u0739_\xde1\x80u\xa5g\xf1\xe6\xb5v\x17\x05^\xf9\xe8\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x9bDO\xd37\xe5\xd7R\x93\xad\xcf\xffp\xe1\xea\x01\xdb\x022\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9bH$\xff\x9f\xb2\xab\xdaUM\xeeO\xb8\xcfT\x91eW\x061\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9bL'\x15x\f\xa4\xe9\x9e`\xeb\xf2\x19\xf1Y\f\x8c\xadP\n\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x9bY\xeb!;\x1eue\xe4PG\xe0N\xa07O\x10v-\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\\9\xf7\xe0\xac\x16\x8c\x8e\xd0\xed4\x04w\x11}\x1bh.\xe9\x89\x05P\x05\xf0\xc6\x14H\x00\x00\u07d4\x9b^\xc1\x8e\x83\x13\x88}\xf4a\u0490.\x81\xe6z\x8f\x11;\xb1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9bd\xd3\u034d+s\xf6hA\xb5\xc4k\xb6\x95\xb8\x8a\x9a\xb7]\x89\x01 :Ov\f\x16\x80\x00\u07d4\x9be\x8f\xb3a\xe0F\xd4\xfc\xaa\x8a\xefm\x02\xa9\x91\x11\"6%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9bfA\xb1>\x17/\xc0r\xcaK\x83'\xa3\xbc(\xa1[f\xa9\x89\x06\x81U\xa46v\xe0\x00\x00\xe0\x94\x9bh\xf6t\x16\xa6;\xf4E\x1a1\x16L\x92\xf6r\xa6\x87Y\xe9\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\x9bw6i\xe8}v\x01\x8c\t\x0f\x82U\xe5D\t\xb9\u0728\xb2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9bw\xeb\xce\xd7\xe2\x15\xf0\x92\x0e\x8c+\x87\x00$\xf6\xec\xb2\xff1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9b|\x88\x10\xcc|\u021e\x80Nm>8\x12\x18PG(w\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\xa5=\xc8\xc9^\x9aG/\xeb\xa2\xc4\xe3,\x1d\xc4\xdd{\xabF\x89Hz\x9a0E9D\x00\x00\xe0\x94\x9b\xac\xd3\xd4\x0f;\x82\xac\x91\xa2d\xd9\u060d\x90\x8e\xac\x86d\xb9\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9b\xb7`\xd5\u0089\xa3\xe1\xdb\x18\xdb\tSE\xcaA;\x9aC\u0089\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x9b\xb7b\x04\x18j\xf2\xf6;\xe7\x91h`\x16\x87\xfc\x9b\xadf\x1f\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9b\xb9\xb0*&\xbf\xe1\xcc\xc3\xf0\xc6!\x9e&\x1c9\u007f\xc5\xcax\x89Hz\x9a0E9D\x00\x00\u07d4\x9b\xc5s\xbc\xda#\xb8\xb2o\x90s\xd9\f#\x0e\x8eq\xe0'\v\x896/u\xa40]\f\x00\x00\u07d4\x9b\xd7\u00caB\x100JMe>\xde\xff\x1b<\xe4_\xcexC\x89\x0fI\x89A\xe6d(\x00\x00\xe0\x94\x9b\u0600h\xe10u\xf3\xa8\xca\xc4d\xa5\xf9I\xd6\xd8\x18\xc0\xf6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9b\xd9\x05\xf1q\x9f\u01ec\xd0\x15\x9dM\xc1\xf8\xdb/!G#8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9b\xdb\u071b\x9741\xd1<\x89\xa3\xf9u~\x9b;bu\xbf\u01c9\x1b\x1a}\u03caD\u04c0\x00\u07d4\x9b\xe3\xc3)\xb6*(\xb8\xb0\x88l\xbd\x8b\x99\xf8\xbc\x93\f\xe3\xe6\x89\x04\t\xe5+H6\x9a\x00\x00\xe0\x94\x9b\xf5\x8e\xfb\xea\a\x84\xeb\x06\x8a\xde\u03e0\xbb!P\x84\xc7:5\x8a\x01:k+VHq\xa0\x00\x00\u07d4\x9b\xf6r\xd9y\xb3fR\xfcR\x82Tzjk\xc2\x12\xaeCh\x89#\x8f\xd4,\\\xf0@\x00\x00\xe0\x94\x9b\xf7\x03\xb4\x1c6$\xe1_@T\x96#\x90\xbc\xba0R\xf0\xfd\x8a\x01H>\x01S<.<\x00\x00\u07d4\x9b\xf7\x1f\u007f\xb57\xacT\xf4\xe5\x14\x94\u007f\xa7\xffg(\xf1m/\x89\x01\u03c4\xa3\n\n\f\x00\x00\u07d4\x9b\xf9\xb3\xb2\xf2<\xf4a\xebY\x1f(4\v\xc7\x19\x93\x1c\x83d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x9b\xfce\x9c\x9c`\x1e\xa4*k!\xb8\xf1p\x84\xec\x87\xd7\x02\x12\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9b\xff\xf5\r\xb3jxUU\xf0vR\xa1S\xb0\xc4+\x1b\x8bv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\x05\xe9\xd0\xf0u\x8eyS\x03q~1\xda!<\xa1W\u618965\u026d\xc5\u07a0\x00\x00\u07d4\x9c\x1bw\x1f\t\xaf\x88*\xf0d0\x83\xde*\xa7\x9d\xc0\x97\xc4\x0e\x89\x86p\xe9\xece\x98\xc0\x00\x00\u07d4\x9c(\xa2\xc4\b`\x91\xcb]\xa2&\xa6W\xce2H\xe8\xea{o\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\x9c/\xd5@\x89\xaff]\xf5\x97\x1ds\xb8\x04a`9dsu\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c4@\x98\xbaaZ9\x8f\x11\xd0\t\x90[\x17|D\xa7\xb6\x02\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c=\x06\x92\xce\xee\xf8\n\xa4\x96\\\xee\xd2b\xff\xc7\xf0i\xf2\u0709\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9c@\\\xf6\x97\x95a8\x06^\x11\xc5\xf7U\x9eg$[\u0465\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9cE *%\xf6\xad\x00\x11\xf1\x15\xa5\xa7\"\x04\xf2\xf2\x19\x88f\x8a\x01\x0f\xcf:b\xb0\x80\x98\x00\x00\xe0\x94\x9cI\xde\xffG\b_\xc0\x97\x04\u02a2\u0728\u0087\xa9\xa17\u068a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x9cK\xbc\xd5\xf1dJo\aX$\xdd\xfe\x85\xc5q\u05ab\xf6\x9c\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4\x9cRj\x14\x06\x83\xed\xf1C\x1c\xfa\xa1(\xa95\xe2\xb6\x14\u060b\x89\x06\x04o7\xe5\x94\\\x00\x00\xe0\x94\x9cT\xe4\xedG\x9a\x85h)\u01bbB\u069f\vi*u\xf7(\x8a\x01\x97\xa8\xf6\xddU\x19\x80\x00\x00\xe0\x94\x9cX\x1a`\xb6\x10(\xd94\x16y)\xb2-p\xb3\x13\xc3O\u040a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x9c\\\xc1\x11\t,\x12!\x16\xf1\xa8_N\xe3\x14\bt\x1a}/\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\x9ck\u0264k\x03\xaeT\x04\xf0C\xdf\xcf!\x88>A\x10\xcc3\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9cx\x96?\xbc&<\t\xbdr\xe4\xf8\xde\xf7J\x94u\xf7\x05\\\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4\x9cx\xfb\xb4\xdfv\x9c\xe2\xc1V\x92\f\xfe\xdf\xda\x03:\x0e%J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x9c{m\xc5\x19\x0f\xe2\x91)c\xfc\xd5yh>\xc79Q\x16\xb0\x89*\x11)\u0413g \x00\x00\u07d4\x9c\x80\xbc\x18\xe9\xf8\u0516\x8b\x18]\xa8\u01df\xa6\xe1\x1f\xfc>#\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x9c\x98\xfd\xf1\xfd\u034b\xa8\xf4\u0170L:\xe8X~\xfd\xf0\xf6\xe6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x9c\x99\xa1\u0691\u0552\v\xc1N\f\xb9\x14\xfd\xf6+\x94\u02c3X\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9c\x99\xb6&\x06(\x1b\\\xef\xab\xf3aV\xc8\xfeb\x83\x9e\xf5\xf3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x9c\x9a\a\xa8\xe5|1r\xa9\x19\xefdx\x94tI\x0f\r\x9fQ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9c\x9d\xe4G$\xa4\x05M\xa0\xea\xa6\x05\xab\u0300&hw\x8b\xea\x89\n\xd7\xd5\xca?\xa5\xa2\x00\x00\u07d4\x9c\x9f;\x8a\x81\x1b!\xf3\xff?\xe2\x0f\xe9p\x05\x1c\xe6j\x82O\x89>\xc2\u07bc\a\u053e\x00\x00\xe0\x94\x9c\x9f\x89\xa3\x91\x0fj*\xe8\xa9\x10G\xa1z\xb7\x88\xbd\xde\xc1p\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9c\xa0B\x9f\x87O\x8d\xce\xe2\xe9\xc0b\xa9\x02\n\x84*Xz\xb9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\xa4.\u7838\x98\xf6\xa5\xcc`\xb5\xa5\u05f1\xbf\xa3\xc321\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\xb2\x8a\xc1\xa2\n\x10o\u007f76\x92\xc5\xceLs\xf172\xa1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c\xcd\u0732\xcf\u00b2[\br\x9a\n\x98\xd9\xe6\xf0 .\xa2\xc1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9c\xe2\u007f$^\x02\xd1\xc3\x12\xc1\xd5\x00x\x8c\x9d\xefv\x90E;\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9c\xe56;\x13\xe8#\x8a\xa4\xdd\x15\xac\u0432\xe8\xaf\xe0\x872G\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9c\xf2\x92\x8b\xee\xf0\x9a@\xf9\xbf\xc9S\xbe\x06\xa2Q\x11a\x82\xfb\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9d\x06\x91\x97\xd1\xdeP\x04Z\x18o^\xc7D\xac@\u8bd1\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\x9d\x0e}\x92\xfb0XS\u05d8&;\xf1^\x97\xc7+\xf9\xd7\xe0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9d\x0f4~\x82k}\u03aa\xd2y\x06\n5\xc0\x06\x1e\xcf3K\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d u\x17B,\xc0\xd6\r\xe7\xc27\tzMO\xce \x94\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x9d%\n\xe4\xf1\x10\xd7\x1c\xaf\u01f0\xad\xb5.\x8d\x9a\xcbfy\xb8\x8a\x02\x15mn\x99r\x13\xc0\x00\x00\xe0\x94\x9d+\xfc6\x10o\x03\x82P\xc0\x18\x01hW\x85\xb1l\x86\xc6\r\x8aPw\xd7]\xf1\xb6u\x80\x00\x00\xe0\x94\x9d0\xcb#{\xc0\x96\xf1p6\xfc\x80\xdd!\xcah\x99,\xa2\u064a\x06n\xe71\x8f\u070f0\x00\x00\u07d4\x9d2\x96.\xa9\x97\x00\xd92(\xe9\xdb\xda\xd2\xcc7\xbb\x99\xf0~\x89\xb4c+\xed\xd4\xde\xd4\x00\x00\u07d4\x9d4\xda\xc2[\xd1X(\xfa\xef\xaa\xf2\x8fq\aS\xb3\x9e\x89\u0709;\x1cV\xfe\xd0-\xf0\x00\x00\u07d4\x9d6\x91e\xfbp\xb8\x1a:v_\x18\x8f\xd6\f\xbe^{\th\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9d@\xe0\x12\xf6\x04%\xa3@\xd8-\x03\xa1\xc7W\xbf\xab\xc7\x06\xfb\x89\t4o:\xdd\u020d\x80\x00\u07d4\x9dAt\xaaj\xf2\x84v\xe2)\xda\xdbF\x18\b\b\xc6u\x05\xc1\x89B\x1a\xfd\xa4.\u0597\x00\x00\u07d4\x9dB\x133\x9a\x01U\x18avL\x87\xa9<\xe8\xf8_\x87\x95\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9dF\f\x1b7\x9d\xdb\x19\xa8\xc8[LgG\x05\r\xdf\x17\xa8u\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x9dG\xba[L\x85\x05\xad\x8d\xa4)4(\va\xa0\xe1\xe8\xb9q\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9dM2\x11w%n\xbd\x9a\xfb\xda0A5\xd5\x17\xc3\xdcV\x93\x89!d\xb7\xa0J\u0220\x00\x00\u07d4\x9dO\xf9\x89\xb7\xbe\u066b\x10\x9d\x10\xc8\xc7\xe5_\x02\xd7g4\xad\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x9dQ\x15C\xb3\xd9\xdc`\xd4\u007f\t\u051d\x01\xb6\u0118\xd8 x\x8a\x02a\x97\xb9Qo\u00d4\x00\x00\u07d4\x9dn\u03e0:\xf2\xc6\xe1D\xb7\xc4i*\x86\x95\x1e\x90.\x9e\x1f\x89\xa2\xa5\xaa`\xad$?\x00\x00\u07d4\x9dvU\xe9\xf3\xe5\xba]n\x87\xe4\x12\xae\xbe\x9e\xe0\u0512G\ue24e\t1\x1c\x1d\x80\xfa\x00\x00\u07d4\x9dx1\xe84\xc2\v\x1b\xaaiz\xf1\xd8\xe0\xc6!\u016f\xff\x9a\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4\x9dx\xa9u\xb7\xdb^M\x8e(\x84\\\xfb\xe7\xe3\x14\x01\xbe\r\u0649H\xa4<T`/p\x00\x00\u07d4\x9dy\x9e\x94>0k\xa2\u5e5c\x8ahX\u02f5,\f\xf75\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\x9d\u007f\xdapp\xbf>\xe9\xbb\u0664\x1fU\xca\u0505J\xe6\xc2,\x8a\x02U\u02e3\xc4o\xcf\x12\x00\x00\u07d4\x9d\x81\xae\xa6\x9a\xedj\xd0p\x89\xd6\x14E4\x8c\x17\xf3K\xfc[\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9d\x91\x1f6\x82\xf3/\xe0y.\x9f\xb6\xff<\xfcG\xf5\x89\xfc\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\x91;]3\x9c\x95\xd8wEV%c\xfe\xa9\x8b#\xc6\f\u0109\tA0,\u007fM#\x00\x00\u07d4\x9d\x93\xfa\xb6\xe2(E\xf8\xf4Z\aIo\x11\xdeqS\r\xeb\u01c9lO\xd1\xee$nx\x00\x00\u07d4\x9d\x99\xb1\x89\xbb\u0664\x8f\xc2\xe1n\x8f\u0363;\xb9\x9a1{\xbb\x89=\x16\xe1\vm\x8b\xb2\x00\x00\u07d4\x9d\x9cN\xfe\x9fC9\x89\xe2;\xe9@I!S)\xfaU\xb4\u02c9\r\u3c89\x03\u01b5\x80\x00\u07d4\x9d\x9eW\xfd\xe3\x0ePh\xc0>I\x84\x8e\xdc\xe3C\xb7\x02\x83X\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x9d\xa30\"@\xaf\x05\x11\xc6\xfd\x18W\xe6\u07779Ow\xabk\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\x9d\xa4\xec@pw\xf4\xb9p{-\x9d.\xde^\xa5(+\xf1\u07c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\xa6\t\xfa:~l\xf2\xcc\x0ep\u036b\xe7\x8d\xc4\xe3\x82\xe1\x1e\x89A\rXj \xa4\xc0\x00\x00\xe0\x94\x9d\xa6\x1c\xcdb\xbf\x86\x06V\xe02]qW\xe2\xf1`\xd9;\xb5\x8a\x01\x0f\f\xa9V\xf8y\x9e\x00\x00\xe0\x94\x9d\xa6\xe0u\x98\x9ct\x19\tL\xc9\xf6\xd2\u44d3\xbb\x19\x96\x88\x8a\x02Y\xbbq\u056d\xf3\xf0\x00\x00\u07d4\x9d\xa8\xe2,\xa1\x0eg\xfe\xa4NR^GQ\xee\xac6\xa3\x11\x94\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\x9d\xb2\xe1\\\xa6\x81\xf4\xc6`H\xf6\xf9\xb7\x94\x1e\u040b\x1f\xf5\x06\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\xc1\x0f\xa3\x8f\x9f\xb0h\x10\xe1\x1f`\x17>\xc3\xd2\xfdju\x1e\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x9d\xd2\x19f$\xa1\xdd\xf1J\x9d7^_\a\x15+\xaf\"\xaf\xa2\x89A\xb0^$c\xa5C\x80\x00\u07d4\x9d\xd4k\x1cm?\x05\u279co\x03~\xed\x9aYZ\xf4\xa9\xaa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9d\xdd5^cN\xe9\x92~K\u007fl\x97\xe7\xbf:/\x1ehz\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x9d\xe2\n\xe7j\xa0\x82c\xb2\x05\xd5\x14$a\x96\x1e$\b\xd2f\x89\r\xa93\xd8\xd8\xc6p\x00\x00\u07d4\x9d\xe2\v\xc3~\u007fH\xa8\x0f\xfdz\xd8O\xfb\xf1\xa1\xab\xe1s\x8c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9d\xe78m\xde@\x1c\xe4\xc6{q\xb6U?\x8a\xa3N\xa5\xa1}\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x9d\xeb9\x02z\xf8w\x99+\x89\xf2\xecJ\x1f\x82.\xcd\xf1&\x93\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9d\xef\xe5j\x0f\xf1\xa1\x94}\xba\t#\xf7\xdd%\x8d\x8f\x12\xfaE\x8a\x05\xb1*\ufbe8\x04\x00\x00\x00\u07d4\x9d\xf0W\xcd\x03\xa4\xe2~\x8e\x03/\x85y\x85\xfd\u007f\x01\xad\xc8\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9d\xf3*P\x1c\vx\x1c\x02\x81\x02/B\xa1)?\xfd{\x89*\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\x9e\x01vZ\xff\b\xbc\"\x05P\xac\xa5\xea.\x1c\xe8\u5c19#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9e \xe5\xfd6\x1e\xab\xcfc\x89\x1f[\x87\xb0\x92h\xb8\xeb7\x93\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9e#,\b\xc1M\xc1\xa6\xed\v\x8a;(h\x97{\xa5\xc1}\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e#\xc5\u4dc2\xb0\n_\xad\U0006eb47\xda\xcf[\x03g\xa1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e59\x90q\xa4\xa1\x01\xe9\x19M\xaa?\t\xf0J\v_\x98p\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9e>\xb5\t'\x8f\xe0\xdc\xd8\xe0\xbb\xe7\x8a\x19N\x06\xb6\x809C\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\x9eBrrQk>g\xd4\xfc\xbf\x82\xf5\x93\x90\xd0L\x8e(\xe5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x9eL\xec5:\xc3\u3043^<\t\x91\xf8\xfa\xa5\xb7\u0428\xe6\x8a\x02\x1e\x18\xb9\xe9\xabE\xe4\x80\x00\u07d4\x9eX\x11\xb4\v\xe1\xe2\xa1\xe1\u048c;\at\xac\xde\n\t`=\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x9eZ1\x1d\x9fi\x89\x8a|j\x9dc`h\x048\xe6z{/\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x9e| P\xa2'\xbb\xfd`\x93~&\x8c\xea>h\xfe\xa8\xd1\xfe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9e\u007fe\xa9\x0e\x85\b\x86{\xcc\xc9\x14%j\x1e\xa5t\xcf\a\xe3\x89C8t\xf62\xcc`\x00\x00\xe0\x94\x9e\x81D\xe0\x8e\x89dx\x11\xfekr\xd4E\u05a5\xf8\n\xd2D\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9e\x8fd\xdd\xcd\u9e34Q\xba\xfa\xa25\xa9\xbfQ\x1a%\xac\x91\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\x9e\x95\x1fm\xc5\xe3R\xaf\xb8\xd0B\x99\xd2G\x8aE\x12Y\xbfV\x89\x03\xe7A\x98\x81\xa7:\x00\x00\u07d4\x9e\x96\r\xcd\x03\u057a\x99\xcb\x11]\x17\xffL\t$\x8a\xd4\u043e\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9e\xafj2\x8a@v\x02N\xfakg\xb4\x8b!\xee\xdc\xc0\xf0\xb8\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x9e\xb1\xffqy\x8f(\xd6\xe9\x89\xfa\x1e\xa0X\x8e'\xba\x86\xcb}\x89\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\x9e\xb2\x81\xc3'\x19\xc4\x0f\xdb>!m\xb0\xf3\u007f\xbcs\xa0&\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e\xb3\xa7\xcb^g&Bz:6\x1c\xfa\x8dad\xdb\u043a\x16\x89+\x95\xbd\xcc9\xb6\x10\x00\x00\u07d4\x9e\xb7\x83N\x17\x1dA\xe0i\xa7yG\xfc\xa8v\"\xf0\xbaNH\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x9e\xc0>\x02\u51f7v\x9d\xefS\x84\x13\xe9\u007f~U\xbeq\u060a\x04+\xf0kx\xed;P\x00\x00\u07d4\x9e\u02eb\xb0\xb2'\x82\xb3uD)\xe1uz\xab\xa0K\x81\x18\x9f\x89,\xa7\xbb\x06\x1f^\x99\x80\x00\u07d4\x9e\xce\x14\x00\x80\t6\xc7\xc6H_\xcd\xd3b`\x17\u041a\xfb\xf6\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\x9e\xd4\xe6?ReB\xd4O\xdd\xd3MY\xcd%8\x8f\xfdk\u0689\u049b4\xa4cH\x94\x00\x00\u07d4\x9e\xd8\x0e\xda\u007fU\x05M\xb9\xfbR\x82E\x16\x88\xf2k\xb3t\xc1\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9e\u0710\xf4\xbe!\be!J\xb5\xb3^Z\x8d\xd7t\x15'\x9d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9e\u07acL\x02k\x93\x05M\u0171\xd6a\fo9`\xf2\xads\x89A\rXj \xa4\xc0\x00\x00\u07d4\x9e\xe9?3\x9eg&\xece\xee\xa4O\x8aK\xfe\x10\xda=2\x82\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9e\xe9v\f\xc2s\xd4pj\xa0\x83u\xc3\xe4o\xa20\xaf\xf3\u054a\x01\xe5.3l\xde\"\x18\x00\x00\u07d4\x9e\xeb\a\xbd+x\x90\x19^}F\xbd\xf2\a\x1bf\x17QM\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x9e\xefD-)\x1aD}t\xc5\xd2S\u011e\xf3$\xea\xc1\xd8\xf0\x89\xb9f\b\xc8\x10;\xf0\x00\x00\u07d4\x9e\xf1\x89k\x00|2\xa1Q\x14\xfb\x89\xd7=\xbdG\xf9\x12+i\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9f\x01w\x06\xb80\xfb\x9c0\ufc20\x9fPk\x91WEu4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x10\xf2\xa0F;e\xae0\xb0p\xb3\xdf\x18\xcfF\xf5\x1e\x89\xbd\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x9f\x19\xfa\u0223$7\xd8\n\u0183z\v\xb7\x84\x17)\xf4\x97.\x89#=\xf3)\x9far\x00\x00\u07d4\x9f\x1a\xa8\xfc\xfc\x89\xa1\xa52\x8c\xbdcD\xb7\x1f'\x8a,\xa4\xa0\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9f!0,\xa5\tk\xeat\x02\xb9\x1b\x0f\xd5\x06%O\x99\x9a=\x89C\x97E\x1a\x00=\xd8\x00\x00\u07d4\x9f'\x1d(U\x00\xd78F\xb1\x8fs>%\u074bO]J\x8b\x89'#\xc3F\xae\x18\b\x00\x00\u07d4\x9f4\x97\xf5\xef_\xe60\x95\x83l\x00N\xb9\xce\x02\xe9\x01;K\x89\"V\x86\x1b\xf9\xcf\b\x00\x00\xe0\x94\x9f:t\xfd^~\xdc\xc1\x16)\x93\x17\x13\x81\u02f62\xb7\xcf\xf0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9fF\xe7\xc1\xe9\a\x8c\xae\x860Z\xc7\x06\v\x01F}f\x85\xee\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\x9fIl\xb2\x06\x95c\x14M\b\x11g{\xa0\xe4q:\nAC\x89<\xd2\xe0\xbfc\xa4H\x00\x00\u07d4\x9fJq\x95\xac|\x15\x1c\xa2X\xca\xfd\xa0\u02b0\x83\xe0I\xc6\x02\x89SS\x8c2\x18\\\xee\x00\x00\u07d4\x9fJ\xc9\xc9\xe7\xe2L\xb2DJ\x04T\xfa[\x9a\xd9\xd9-8S\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4\x9f_D\x02kWjJ\xdbA\xe9YaV\x1dA\x03\x9c\xa3\x91\x89\r\x8drkqw\xa8\x00\x00\u07d4\x9f`{?\x12F\x9fDa!\u03bf4u5kq\xb42\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9fa\xbe\xb4o^\x85=\n\x85!\xc7Dnh\xe3L}\ts\x89\x1e[\x8f\xa8\xfe*\xc0\x00\x00\u07d4\x9fd\xa8\xe8\xda\xcfJ\xde0\xd1\x0fMY\xb0\xa3\u056b\xfd\xbft\x8966\x9e\xd7t}&\x00\x00\u07d4\x9ff.\x95'A!\xf1wVncm#\x96L\xf1\xfdho\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9fj2*mF\x99\x81Bj\xe8D\x86]~\xe0\xbb\x15\u01f3\x89\x02\xb5\xeeW\x92\x9f\u06c0\x00\u07d4\x9fy\x86\x92J\xeb\x02h|\xd6A\x89\x18\x9f\xb1g\xde\xd2\xdd\\\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x9fz\x03\x92\xf8Ws.0\x04\xa3u\xe6\xb1\x06\x8dI\xd801\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x82E\u00eb}\x171d\x86\x1c\u04d9\x1b\x94\xf1\xba@\xa9:\x89\x9b\ny\x1f\x12\x110\x00\x00\u07d4\x9f\x83\xa2\x93\xc3$\xd4\x10l\x18\xfa\xa8\x88\x8fd\u0499\x05L\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9f\x86\xa0f\xed\xb6\x1f\xcbXV\u0793\xb7\\\x8cy\x18d\xb9{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x98\xeb4\xd4iy\xb0\xa6\u078b\x05\xaaS:\x89\xb8%\xdc\xf1\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\xe0\x94\x9f\x9f\xe0\xc9_\x10\xfe\xe8z\xf1\xaf r6\xc8\xf3aN\xf0/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9f\xae\xa1<s4\x12\xdcKI\x04\x02\xbf\xef'\xa09z\x9b\u00c9\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\x9f\xbe\x06m\xe5r6\u0703\a%\xd3*\x02\xae\xf9$ll^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\xd1\x05*`Pk\u0469\xef\x00:\xfd\x9d\x03<&}\x8e\x99\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9f\xd6Cs\xf2\xfb\u035c\x0f\xac\xa6\x05G\xca\xd6.&\u0645\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9f\xe5\x01\xaaW\xea\u05d2x\x93|\xd60\x8c\\\xfazV)\xfe\x89\x02\xb5\xeeW\x92\x9f\u06c0\x00\u07d4\x9f\xfc_\xe0o3\xf5\xa4\x80\xb7Z\xa9N\xb8Um\x99z\x16\xc0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9f\xfc\xf5\xefF\xd93\xa5\x19\xd1\xd1lk\xa3\x18\x9b'Ib$\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9f\xfe\xdc\xc3k|\xc3\x12\xad*\x9e\xdeC\x1aQO\u0334\x9b\xa3\x89$OW\x9f?\\\xa4\x00\x00\u07d4\xa0\x06&\x84Fd>\xc5\xe8\x1ez\xcb?\x17\xf1\xc3Q\xee.\u0649\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa0\b\x01\x98c\xc1\xa7|\x14\x99\xeb9\xbb\u05ff-\u05e3\x1c\xb9\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xa0\t\xbf\ao\x1b\xa3\xfaW\u04a7!r\x18\xbe\xd5VZzz\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa0\x1e\x94v\u07c4C\x18%\xc86\xe8\x80:\x97\xe2/\xa5\xa0\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\xa0\x1f\x12\xd7\x0fD\xaa{\x11;(\\\"\xdc\xdbE\x874T\xa7\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xa0\x1f\u0450j\x90\x85\x06\xde\xda\xe1\xe2\b\x12\x88r\xb5n\u7489\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa0\"\x82@\xf9\x9e\x1d\xe9\xcb2\xd8,\x0f/\xa9\xa3\xd4K\v\xf3\x89V\xbcu\xe2\xd61\x00\x00\x00\xe0\x94\xa0+\xdedahn\x19\xace\f\x97\r\x06r\xe7m\xcbO\u008a\x01\xe0\x92\x96\xc37\x8d\xe4\x00\x00\u07d4\xa0,\x1e4\x06O\x04u\xf7\xfa\x83\x1c\xcb%\x01L:\xa3\x1c\xa2\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xa0-\u01aa2\x8b\x88\r\u97acTh#\xfc\xcfw@G\xfb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa0.?\x8fYY\xa7\xaa\xb7A\x86\x12\x12\x9bp\x1c\xa1\xb8\x00\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa04\u007f\n\x98wc\x90\x16\\\x16m2\x96;\xf7M\xcd\n/\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa05\xa3e$x\xf8-\xbdm\x11_\xaa\x8c\xa9F\xec\x9eh\x1d\x89\x05\xf4\xe4-\u052f\xec\x00\x00\u07d4\xa0:=\xc7\xc53\xd1tB\x95\xbe\x95]a\xaf?R\xb5\x1a\xf5\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa0E\x9e\xf3i:\xac\xd1d|\xd5\u0612\x989 L\xefS\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa0O*\xe0*\xdd\x14\xc1/\xafe\xcb%\x90\"\u0403\n\x8e&\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xa0l\xd1\xf3\x969l\ndFFQ\xd7\xc2\x05\xef\xaf8|\xa3\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xa0ri\x1c\x8d\xd7\xcdB7\xffr\xa7\\\x1a\x95\x06\xd0\xce[\x9e\x89\x14\x0e\xc8\x0f\xa7\xee\x88\x00\x00\u07d4\xa0r\u03beb\xa9\xe9\xf6\x1c\xc3\xfb\xf8\x8a\x9e\xfb\xfe>\x9a\x8dp\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa0v\x82\x00\v\x1b\xcf0\x02\xf8\\\x80\xc0\xfa)I\xbd\x1e\x82\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa0z\xa1mt\xae\u8a63(\x8dR\xdb\x15Q\u0553\x882\x97\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa0\x8d![[j\xacHa\xa2\x81\xac~@\vx\xfe\xf0L\xbf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa0\x95\x19p\xdf\u0403/\xb8;\xda\x12\xc25E\xe7\x90Aul\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa0\x9fM^\xaae\xa2\xf4\xcbu\nI\x924\x01\xda\u5410\xaf\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\xe0\x94\xa0\xa0\xe6R\x04T\x1f\u029b/\xb2\x82\u0355\x13\x8f\xae\x16\xf8\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa0\xaa_\x02\x01\xf0M;\xbe\xb8\x98\x13/|\x11g\x94f\xd9\x01\x89\x01\xfb\xedR\x15\xbbL\x00\x00\u07d4\xa0\xaa\xdb\xd9P\x97\"p_m#X\xa5\u01df7\x97\x0f\x00\xf6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa0\xb7q\x95\x1c\xe1\xde\xee6:\xe2\xb7q\xb7>\a\u0135\xe8\x00\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa0\xde\\`\x1eif5\u0198\xb7\xae\x9c\xa4S\x9f\u01f9A\xec\x89\x12\xc3\xcb\xd7\x04\xc9w\x00\x00\u07d4\xa0\xe8\xbaf\x1bH\x15L\xf8C\xd4\u00a5\xc0\xf7\x92\xd5(\xee)\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa0\xfc~S\xc5\xeb\xd2z*\xbd\xacE&\x1f\x84\xab;Q\xae\xfb\x89\xa3\x13\xda\xec\x9b\xc0\xd9\x00\x00\xe0\x94\xa0\xff[L\xf0\x16\x02~\x83#I}D(\xd3\xe5\xa8;\x87\x95\x8a\x01e\x98\xd3\xc8>\xc0B\x00\x00\u07d4\xa1\x06F[\xbd\x19\u1dbc\xe5\r\x1b\x11W\xdcY\tZ60\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa1\x06\xe6\x92>\xddS\u028e\xd6P\x96\x8a\x91\b\xd6\xcc\xfd\x96p\x8a\x02\x02\xfe\x15\x05\xaf\uc240\x00\u07d4\xa1\t\u12f0\xa3\x9c\x9e\xf8/\xa1\x95\x97\xfc^\xd8\xe9\xebmX\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\xa1\x1a\x03\u013b&\xd2\x1e\xffg}]U\\\x80\xb2TS\xeez\x89\x03\xcb'Y\xbcA\x0f\x80\x00\u07d4\xa1\x1e\xff\xabl\xf0\xf5\x97,\xff\xe4\xd5e\x96\xe9\x89h\x14J\x8f\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xa1 M\xad_V\a(\xa3\\\r\x8f\u01d4\x81\x05{\xf7s\x86\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa1&#\xe6)\u07d3\tg\x04\xb1`\x84\xbe,\u061dV-\xa4\x8a\x01\xcc\xc92E\x11\xe4P\x00\x00\xe0\x94\xa1*l-\x98]\xaf\x0eO_ z\xe8Q\xaa\xf7)\xb32\u034a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa13m\xfb\x96\xb6\xbc\xbeK>\xdf2\x05\xbeW#\xc9\x0f\xadR\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa1;\x9d\x82\xa9\x9b<\x9b\xbaZ\xe7.\xf2\x19\x9e\xdc};\xb3l\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xa1<\xfe\x82mm\x18A\u072eD;\xe8\u00c7Q\x816\xb5\xe8\x8a\x1d\xa5jK\b5\xbf\x80\x00\x00\xe0\x94\xa1C.\xd2\u01b7wz\x88\xe8\xd4m8\x8epG\u007f \x8c\xa5\x8a\x01\xb1\xa7\xe4\x13\xa1\x96\xc5\x00\x00\u07d4\xa1D\xf6\xb6\x0fr\xd6J!\xe30\xda\xdbb\u0619\n\xde+\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa1P%\xf5\x95\xac\xdb\xf3\x11\x0fw\u017f$G~eH\xf9\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1X\x14\x8a.\x0f>\x92\xdc,\xe3\x8f\xeb\xc2\x01\a\xe3%<\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1a`\x85\x1d+\x9c4\x9b\x92\xe4o\x82\x9a\xbf\xb2\x10\x945\x95\x89a\t=|,m8\x00\x00\u07d4\xa1f\xf9\x11\xc6D\xac2\x13\u049e\x0e\x1a\xe0\x10\xf7\x94\u056d&\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1m\x9e=c\x98aY\xa8\x00\xb4h7\xf4^\x8b\xb9\x80\xee\v\x89n\x11u\xdaz\xd1 \x00\x00\u07d4\xa1pp\xc2\xe9\u0169@\xa4\xec\x0eIT\xc4\xd7\xd6C\xbe\x8fI\x89lk\x17\x03;6\x1c\x80\x00\u07d4\xa1|\x9eC#\x06\x95\x18\x18\x9dR\a\xa0r\x8d\u02d20j?\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa1\x83`\xe9\x85\xf2\x06.\x8f\x8e\xfe\x02\xad,\xbc\x91\xad\x9aZ\xad\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa1\x91\x14\x05\xcfn\x99\x9e\xd0\x11\xf0\xdd\xcd*O\xf7\u008f%&\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa1\x92i\x80\a\xcc\x11\xaa`=\"\x1d_\xee\xa0v\xbc\xf7\xc3\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\x92\xf0j\xb0R\xd5\xfd\u007f\x94\xee\xa81\x8e\x82x\x15\xfegz\x89\a\x1f\x8a\x93\xd0\x1eT\x00\x00\u07d4\xa1\x99\x81D\x96\x8a\\p\xa6AUT\xce\xfe\u0082F\x90\u0125\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa1\xa1\xf0\xfam \xb5\nyO\x02\xefR\b\\\x9d\x03j\xa6\u028965\u026d\xc5\u07a0\x00\x00\u07d4\xa1\xae\x8dE@\xd4\xdbo\xdd\xe7\x14oA[C\x1e\xb5\\y\x83\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xa1\xb4|M\x0e\xd6\x01\x88B\xe6\xcf\xc8c\n\u00e3\x14.^k\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xa1\xc4\xf4Z\x82\xe1\xc4x\xd8E\b.\xb1\x88u\xc4\xeae9\xab\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xa1\xdc\xd0\xe5\xb0Z\x97|\x96#\xe5\xae/Y\xb9\xad\xa2\xf3>1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa1\xe48\n;\x1ft\x96s\xe2p\"\x99\x93\xeeU\xf3Vc\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\xf1\x93\xa0Y/\x1f\xeb\x9f\xdf\xc9\n\xa8\x13xN\xb8\x04q\u0249K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa1\xf2\x85@P\xf8re\x8e\xd8.R\xb0\xad{\xbc\x1c\xb9!\xf6\x89m\x03\x17\xe2\xb3&\xf7\x00\x00\u07d4\xa1\xf5\xb8@\x14\rZ\x9a\xce\xf4\x02\xac<\u00c8jh\xca\xd2H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\xf7e\xc4O\xe4_y\x06w\x94HD\xbeO-B\x16_\xbd\x89\xc7\xe9\xcf\xdev\x8e\xc7\x00\x00\u07d4\xa1\xf7\xdd\xe1\xd78\xd8\xcdg\x9e\xa1\xee\x96[\xee\"K\xe7\xd0M\x89=\x18DP\xe5\xe9<\x00\x00\u07d4\xa1\xf8\u063c\xf9\x0ew\u007f\x19\xb3\xa6Iu\x9a\xd9P'\xab\xdf\u00c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\x02TrB\x80onp\xe7@X\xd6\xe5)-\xef\xc8\xc8\u0509l\x87T\xc8\xf3\f\b\x00\x00\u07d4\xa2\r\a\x1b\x1b\x000cI}y\x90\xe1$\x9d\xab\xf3l5\xf7\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa2\r\x8f\xf6\f\xaa\xe3\x1d\x02\xe0\xb6e\xfaC]v\xf7|\x94B\x89\x1a\x8a\x90\x9d\xfc\xef@\x00\x00\u07d4\xa2\x11\xda\x03\xcc\x0e1\xec\xceS\t\x99\x87\x18QU(\xa0\x90\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\x14B\xab\x054\n\xdeh\xc9\x15\xf3\xc39\x9b\x99U\xf3\xf7\xeb\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xa2\"\"Y\u075c>=\xed\x12p\x84\xf8\b\xe9*\x18\x870,\x89\b\xc83\x9d\xaf\xedH\x00\x00\u07d4\xa2*\xde\r\xdb\\n\xf8\xd0\u034d\xe9M\x82\xb1\x10\x82\xcb.\x91\x897KW\xf3\xce\xf2p\x00\x00\u07d4\xa2L:\xb6!\x81\xe9\xa1[x\xc4b\x1eL|X\x81'\xbe&\x89\b\xcd\xe4:\x83\xd31\x00\x00\u07d4\xa2W\xadYK\u0603(\xa7\xd9\x0f\xc0\xa9\a\u07d5\xee\xca\xe3\x16\x89\x1c7\x86\xff8F\x93\x00\x00\u07d4\xa2[\bd7\xfd!\x92\u0420\xf6On\xd0D\xf3\x8e\xf3\xda2\x89\x12)\x0f\x15\x18\v\xdc\x00\x00\u07d4\xa2v\xb0X\u02d8\u060b\xee\xdbg\xe5CPl\x9a\r\x94p\u0609\x90\xaa\xfcv\xe0/\xbe\x00\x00\u07d4\xa2\x82\xe9i\xca\xc9\xf7\xa0\xe1\xc0\u0350\xf5\xd0\xc48\xacW\r\xa3\x89\"\a\xeb\x89\xfc'8\x00\x00\xe0\x94\xa2\x91\xe9\u01d9\rU-\u046e\x16\u03bc?\xca4,\xba\xf1\u044a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa2\x93\x19\xe8\x10i\xe5\xd6\r\xf0\x0f=\xe5\xad\xee5\x05\xec\xd5\xfb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa2\x96\x8f\xc1\xc6K\xac\vz\xe0\u058b\xa9I\x87Mm\xb2S\xf4\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xa2\x9d[\xdat\xe0\x03GHr\xbdX\x94\xb8\x853\xffd\u00b5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa2\x9df\x1acv\xf6m\vt\xe2\xfe\x9d\x8f&\xc0$~\xc8L\x89\xdf3\x04\a\x9c\x13\xd2\x00\x00\u07d4\xa2\xa45\xdeD\xa0\x1b\xd0\ucc9eD\xe4vD\xe4j\f\xdf\xfb\x89\x1b\x1dDZz\xff\xe7\x80\x00\u07d4\xa2\xac\xe4\u0253\xbb\x1eS\x83\xf8\xact\xe1y\x06n\x81O\x05\x91\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa2\xb7\x01\xf9\xf5\xcd\u041eK\xa6+\xae\xba\u3a02W\x10X\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa2\u0145O\xf1Y\x9f\x98\x89,W%\xd2b\xbe\x1d\xa9\x8a\xad\xac\x89\x11\t\xff30\x10\xe7\x80\x00\u07d4\xa2\xc7\xea\xff\xdc,\x9d\x93sE l\x90\x9aR\u07f1LG\x8f\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xa2\u04aabk\t\xd6\xd4\xe4\xb1?\u007f\xfcZ\x88\xbdz\xd3gB\x89\xfb\x80xPuS\x83\x00\x00\u07d4\xa2\u04cd\xe1\xc79\x06\xf6\xa7\xcan\xfe\xb9|\xf6\xf6\x9c\xc4!\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa2\xdce\xee%kY\xa5\xbdy)wO\x90K5\x8d\U000ed84a\x04\x83\xbc\xe2\x8b\xeb\t\xf8\x00\x00\u07d4\xa2\xe0h:\x80]\xe6\xa0^\xdb/\xfb\xb5\xe9o\x05p\xb67\u00c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa2\u1e2a\x90\x0e\x9c\x13\x9b?\xa1\"5OaV\xd9*\x18\xb1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa2\u2d54\x1e\f\x01\x94K\xfe\x1d_\xb4\xe8\xa3K\x92,\u03f1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\xe4`\xa9\x89\xcb\x15V_\x9e\u0327\xd1!\xa1\x8eN\xb4\x05\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa2\xec\xce,I\xf7*\t\x95\xa0\xbd\xa5z\xac\xf1\xe9\xf0\x01\xe2*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xa2\xf4r\xfeO\"\xb7}\xb4\x89!\x9e\xa4\x02=\x11X*\x93)\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xa2\xf7\x98\xe0w\xb0}\x86\x12N\x14\a\xdf2\x89\r\xbbKcy\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\xf8k\xc0a\x88N\x9e\xef\x05d\x0e\xddQ\xa2\xf7\xc0Yli\x89llD\xfeG\xec\x05\x00\x00\u07d4\xa2\xfa\x17\xc0\xfbPl\xe4\x94\x00\x8b\x95W\x84\x1c?d\x1b\x8c\xae\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa3\x04X\x8f\r\x85\f\xd8\u04cfv\xe9\xe8<\x1b\xf6>3>\u0789\x02(V\x01!l\x8c\x00\x00\u07d4\xa3\x05\x8cQszN\x96\xc5_.\xf6\xbd{\xb3X\x16~\u00a7\x89 \xdb:\xe4H\x1a\u0500\x00\u07d4\xa3\t\xdfT\u02bc\xe7\f\x95\xec03\x14\x9c\xd6g\x8ao\xd4\u03c9\f\x1f\x12\xc7Q\x01X\x00\x00\u07d4\xa3\nER\x0eR\x06\xd9\x00@p\xe6\xaf>{\xb2\xe8\xddS\x13\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa3\x0e\n\xcbSL\x9b0\x84\xe8P\x1d\xa0\x90\xb4\xeb\x16\xa2\xc0\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3 0\x95\xed\xb7\x02\x8ehq\xce\n\x84\xf5HE\x9f\x830\n\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa3!\t\x1d0\x18\x06By\xdb9\x9d+*\x88\xa6\xf4@\xae$\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4\xa3#-\x06\x8dP\x06I\x03\xc9\xeb\xc5c\xb5\x15\xac\u0237\xb0\x97\x89l\x87T\xc8\xf3\f\b\x00\x00\xe0\x94\xa3$\x1d\x89\n\x92\xba\xf5)\b\xdcJ\xa0Irk\xe4&\xeb\u04ca\x04<-\xa6a\xca/T\x00\x00\u07d4\xa3)F&\xec)\x84\xc4;C\xdaM]\x8eFi\xb1\x1dKY\x896\xa4\xcfcc\x19\xc0\x00\x00\u07d4\xa3,\xf7\xdd\xe2\f=\xd5g\x9f\xf5\xe3%\x84\\p\u0156&b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa39\xa3\xd8\xca(\x0e'\xd2A[&\xd1\xfcy2(\xb6`C\x896\U00086577\x8f\xf0\x00\x00\u07d4\xa3<\xb4P\xf9[\xb4n%\xaf\xb5\x0f\xe0_\xee\xe6\xfb\x8c\xc8\xea\x89*\x11)\u0413g \x00\x00\u07d4\xa3?p\xdaru\xef\x05q\x04\u07e7\xdbd\xf4r\xe9\xf5\xd5S\x89\x04YF\xb0\xf9\xe9\xd6\x00\x00\u07d4\xa3@v\xf8K\xd9\x17\xf2\x0f\x83B\u024b\xa7\x9eo\xb0\x8e\xcd1\x89\u3bb5sr@\xa0\x00\x00\u07d4\xa3C\x0e\x1fd\u007f2\x1e\xd3G9V##\xc7\xd6#A\vV\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xa3O\x9dV\x8b\xf7\xaf\xd9L*[\x8a_\xf5\\f\xc4\by\x99\x89\x84}P;\"\x0e\xb0\x00\x00\u07d4\xa3V\x06\xd5\x12 \xee\u007f!F\xd4\x11X.\xe4\xeeJEYn\x89\u062a\xbe\b\v\xc9@\x00\x00\u07d4\xa3VU\x1b\xb7}OE\xa6\xd7\xe0\x9f\n\b\x9ey\u0322I\u02c9\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xa3\\\x19\x13,\xac\x195Wj\xbf\xedl\x04\x95\xfb\a\x88\x1b\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3e\x91\x8b\xfe?&'\xb9\xf3\xa8gu\xd8un\x0f\u0629K\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa3n\r\x94\xb9Sd\xa8&q\xb6\b\xcb-72Ea)\t\x89\b!\xd2!\xb5)\x1f\x80\x00\u07d4\xa3u\xb4\xbc$\xa2N\x1fyu\x93\xcc0+/3\x10c\xfa\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa3v\"\xac\x9b\xbd\xc4\xd8+u\x01]t[\x9f\x8d\xe6Z(\uc25d\xc0\\\xce(\u00b8\x00\x00\xe0\x94\xa3y\xa5\a\fP=/\xac\x89\xb8\xb3\xaf\xa0\x80\xfdE\xedK\xec\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xa3\x80-\x8ae\x9e\x89\xa2\xc4~\x90T0\xb2\xa8'\x97\x89P\xa7\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa3\x83\x06\xcbp\xba\xa8\u4446\xbdh\xaap\xa8=$/)\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\x84vi\x1d4\x94.\xeak/v\x88\x92#\x04}\xb4az\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\x87\xceN\x96\x1axG\xf5`\a\\d\xe1YkVA\xd2\x1c\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xa3\x87\xec\xde\x0e\xe4\xc8\a\x94\x99\xfd\x8e\x03G;\u060a\xd7R*\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa3\x88:$\xf7\xf1f _\x1aj\x99I\al&\xa7nqx\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xa3\x8b[\xd8\x1a\x9d\xb9\u04b2\x1d^\xc7\xc6\x05R\xcd\x02\xedV\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa3\x90\xca\x12+\x85\x01\xee>^\a\xa8\xcaKA\x9f~M\xae\x15\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xa3\x93*1\xd6\xffu\xfb;\x12q\xac\xe7\u02a7\xd5\xe1\xff\x10Q\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xa3\x94\xadO\xd9\xe6S\x0eo\\S\xfa\xec\xbe\u0781\xcb\x17-\xa1\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xa3\x97\x9a\x92v\n\x13Z\xdfi\xd7/u\xe1gu_\x1c\xb8\u00c9\x05k\xc7^-c\x10\x00\x00\xe0\x94\xa3\x9b\xfe\xe4\xae\u027du\xbd\"\u01b6r\x89\x8c\xa9\xa1\xe9]2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa3\xa2b\xaf\u0493h\x19#\b\x92\xfd\xe8O-ZYJ\xb2\x83\x89e\xea=\xb7UF`\x00\x00\u07d4\xa3\xa2\xe3\x19\xe7\u04e1D\x8bZ\xa2F\x89S\x16\f-\xbc\xbaq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\xa5{\a\x16\x13(\x04\xd6\n\xac(\x11\x97\xff+=#{\x01\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa3\xa9>\xf9\xdb\xea&6&=\x06\xd8I/jA\u0790|\"\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\xa3\xae\x18y\x00}\x80\x1c\xb5\xf3RqjM\u063a'!\xde=\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xa3\xba\r:6\x17\xb1\xe3\x1bNB,\xe2i\xe8s\x82\x8d]i\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\xa3\xbc\x97\x9bp\x80\t/\xa1\xf9/n\x0f\xb3G\u2359PE\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xa3\xbf\xf1\u07e9\x97\x16h6\f\r\x82\x82\x842\xe2{\xf5Ng\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa3\xc1J\xce(\xb1\x92\u02f0b\x14_\u02fdXi\xc6rq\xf6\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa3\xc3:\xfc\x8c\xb4pN#\x15=\xe2\x04\x9d5\xaeq3$r\x89+X\xad\u06c9\xa2X\x00\x00\u07d4\xa3\u0430<\xff\xbb&\x9fyj\u009d\x80\xbf\xb0}\xc7\u01ad\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\u0543\xa7\xb6[#\xf6\vy\x05\xf3\xe4\xaab\xaa\xc8\u007fB'\x898\xbe\xfa\x12mZ\x9f\x80\x00\u07d4\xa3\xdb6J3-\x88K\xa9;&\x17\xaeM\x85\xa1H\x9b\xeaG\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xa3\xe0Q\xfbtJ\xa3A\f;\x88\xf8\x99\xf5\xd5\u007f\x16\x8d\xf1-\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\xa3\xe3\xa6\xeaP\x95s\xe2\x1b\xd0#\x9e\xce\x05#\xa7\xb7\u061b/\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa3\xf4\xad\x14\xe0\xbbD\xe2\xce,\x145\x9cu\xb8\xe72\xd3pT\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa3\xfa\xccP\x19\\\vI3\xc8X\x97\xfe\xcc[\xbd\x99\\4\xb8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\x03Z\xb1\xe5\x18\b!\xf0\xf3\x80\xf1\x13\x1bs\x87\xc8\u0641\u0349\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\n\xa2\xbb\xce\fr\xb4\xd0\xdf\xff\xccBq[+T\xb0\x1b\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4\x19\xa9\x84\x14#c&uuV`\x894\x0e\xea\x0e\xa2\b\x19\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xa4!\u06f8\x9b:\aA\x90\x84\xad\x10\xc3\xc1]\xfe\x9b2\xd0\u008a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa4\"\xe4\xbf\v\xf7AG\u0309[\xed\x8f\x16\xd3\xce\xf3BaT\x89\x12\xef?b\xee\x116\x80\x00\u07d4\xa4%\x9f\x83E\xf7\u3a37+\x0f\xec,\xf7^2\x1f\xdaM\u0089g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xa4)\b\xe7\xfeS\x98\n\x9a\xbf@D\xe9W\xa5Kp\u973e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4)\xfa\x88s\x1f\xdd5\x0e\x8e\xcdn\xa5B\x96\xb6HO\u6549j\xc5\xc6-\x94\x86\a\x00\x00\xe0\x94\xa40\x99]\xdb\x18[\x98e\xdb\xe6%9\xad\x90\xd2.Ks\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa46\xc7TS\xcc\xcaJ\x1f\x1bb\xe5\u0123\r\x86\xdd\xe4\xbeh\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa47\xfen\xc1\x03\u028d\x15\x8fc\xb34\"N\u032c[>\xa3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4;m\xa6\xcbz\xacW\x1d\xff'\xf0\x9d9\xf8F\xf57i\xb1\x89\x14\x99\x8f2\xacxp\x00\x00\u07d4\xa4;\x81\xf9\x93V\xc0\xaf\x14\x1a\x03\x01\rw\xbd\x04,q\xc1\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4>\x19G\xa9$+5Ua\xc3\n\x82\x9d\xfe\uc881Z\xf8\x89\xd2=\x99\x96\x9f\u0591\x80\x00\u07d4\xa4H\x9aP\xea\xd5\xd5DZ{\xeeM-U6\u00a7lA\xf8\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa4O\xe8\x00\xd9o\xca\xd7;qp\xd0\xf6\x10\u02cc\x06\x82\xd6\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa4T2\xa6\xf2\xac\x9dVW{\x93\x8a7\xfa\xba\xc8\xcc|F\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4f\xd7p\u0618\xd8\xc9\xd4\x05\xe4\xa0\xe5Q\xef\xaf\xcd\xe5<\xf9\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\xe0\x94\xa4g\a1\x17X\x93\xbb\xcf\xf4\xfa\x85\u0397\xd9O\xc5\x1cK\xa8\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4kC\x87\xfbM\xcc\xe0\x11\xe7nMsT}D\x81\xe0\x9b\xe5\x89Hz\x9a0E9D\x00\x00\u07d4\xa4l\xd27\xb6>\xeaC\x8c\x8e;e\x85\xf6y\xe4\x86\b2\xac\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4wy\u063c\x1c{\xce\x0f\x01\x1c\xcb9\xefh\xb8T\xf8\u078f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4\x82kl8\x82\xfa\xd0\xed\\\x8f\xbb%\xcc@\xccO3u\x9f\x89p\x1bC\xe3D3\xd0\x00\x00\u07d4\xa4\x87Y(E\x8e\xc2\x00]\xbbW\x8c\\\xd35\x80\xf0\xcf\x14R\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4\x9fR:\xa5\x13d\xcb\xc7\u0655\x16=4\xebY\r\xed/\b\x89\x90'B\x1b*\x9f\xbc\x00\x00\u07d4\xa4\xa4\x9f\v\xc8h\x8c\xc9\xe6\xdc\x04\xe1\xe0\x8dR\x10&\xe6Ut\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa4\xa7\xd3\x06\xf5\x10\xcdX5\x94(\xc0\xd2\xf7\xc3`\x9dVt\u05c9\xb5\x8c\xb6\x1c<\xcf4\x00\x00\u07d4\xa4\xa8:\a8y\x9b\x97\x1b\xf2\xdep\x8c.\xbf\x91\x1c\xa7\x9e\xb2\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa4\xb0\x9d\xe6\xe7\x13\xdciTnv\xef\n\xcf@\xb9O\x02A\xe6\x89\x11}\xc0b~\xc8p\x00\x00\xe0\x94\xa4\u04b4)\xf1\xadSI\xe3\x17\x04\x96\x9e\xdc_%\xee\x8a\xca\x10\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xa4\xd6\xc8.\u076eYG\xfb\xe9\xcd\xfb\xd5H\xae3\xd9\x1aq\x91\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4\xda4E\r\"\xec\x0f\xfc\xed\xe0\x00K\x02\xf7\x87.\xe0\xb7:\x89\x05\x0fafs\xf0\x83\x00\x00\xe0\x94\xa4\xddY\xab^Q}9\x8eI\xfaS\u007f\x89\x9f\xedL\x15\xe9]\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa4\xe6#E\x1e~\x94\xe7\u86e5\xed\x95\u0228:b\xff\xc4\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\xed\x11\xb0r\u061f\xb16u\x9f\u019bB\x8cH\xaa]L\xed\x89\x0e?\x15'\xa0<\xa8\x00\x00\u07d4\xa4\xfb\x14@\x9ag\xb4V\x88\xa8Y>\\\xc2\xcfYl\xedo\x11\x89a\t=|,m8\x00\x00\xe0\x94\xa5\x14\xd0\x0e\xddq\b\xa6\xbe\x83\x9ac\x8d\xb2AT\x18\x17A\x96\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x94\xa5\"\xde~\xb6\xae\x12PR*Q13\xa9;\xd4(IG\\\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa5$\xa8\xcc\xccIQ\x8d\x17\n2\x82p\xa2\xf8\x813\xfb\xaf]\x89\x0f\xf7\x02-\xac\x10\x8a\x00\x00\u07d4\xa59\xb4\xa4\x01\xb5\x84\xdf\xe0\xf3D\xb1\xb4\"\xc6UC\x16~.\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa5>\xadT\xf7\x85\n\xf2\x148\xcb\xe0z\xf6\x86'\x9a1[\x86\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa5C\xa0f\xfb2\xa8f\x8a\xa0sj\f\x9c\xd4\rx\t\x87'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa5gw\vj\xe3 \xbd\xdeP\xf9\x04\xd6c\xe7F\xa6\x1d\xac\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5h\xdbMW\xe4\xd6tb\xd73\u019a\x9e\x0f\xe2n!\x83'\x89;k\xff\x92f\xc0\xae\x00\x00\u07d4\xa5i\x8059\x1eg\xa4\x90\x13\xc0\x00 yY1\x14\xfe\xb3S\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4\xa5p\":\xe3\u02a8QA\x8a\x98C\xa1\xacU\xdbH$\xf4\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa5s`\xf0\x02\xe0\xd6M-tE}\x8c\xa4\x85~\xe0\v\xcd\u07c9\x123\xe22\xf6\x18\xaa\x00\x00\u07d4\xa5u\xf2\x89\x1d\xcf\u0368<\\\xf0\x14t\xaf\x11\xee\x01\xb7-\u0089\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\xa5x;\xf342\xff\x82\xacI\x89\x85\xd7\xd4`\xaeg\xec6s\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xa5\x87MuF5\xa7b\xb3\x81\xa5\xc4\u01d2H:\xf8\xf2=\x1d\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\xa5\xa4\"\u007fl\xf9\x88%\xc0\u057a\xffS\x15u,\xcc\x1a\x13\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa5\xabK\xd3X\x8fF\xcb'.V\xe9=\xee\u04c6\xba\x8bu=\x89HB\xf0A\x05\x87,\x80\x00\xe0\x94\xa5\xba\xd8e\t\xfb\xe0\xe0\xe3\xc0\xe9?m8\x1f\x1a\xf6\xe9\u0501\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa5\xc36\b;\x04\xf9G\x1b\x8cn\xd76y\xb7Mf\xc3c\uc263e\nL\x9d \xe2\x00\x00\u07d4\xa5\xcd\x129\x92\x19K4\xc4x\x13\x140;\x03\xc5IH\xf4\xb9\x89l\xfc\xc3\xd9\x1d\xa5c\x00\x00\u07d4\xa5\u0578\xb6-\x00-\xef\x92A7\x10\xd1;o\xf8\xd4\xfc}\u04c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xa5\xd9ni}F5\x8d\x11\x9a\xf7\x81\x9d\xc7\b\u007fj\xe4\u007f\xef\x8a\x03\x17\xbe\xe8\xaf3\x15\xa7\x80\x00\u07d4\xa5\xde^CO\xdc\xddh\x8f\x1c1\xb6\xfbQ,\xb1\x96rG\x01\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xa5\xe0\xfc<:\xff\xed=\xb6q\tG\xd1\xd6\xfb\x01\u007f>'m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5\xe9;I\xea|P\x9d\xe7\xc4Ml\xfe\xdd\xefY\x10\u07aa\xf2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5\xe9\xcdKt%]\"\xb7\u0672z\xe8\xddC\xedn\xd0%+\x89)\x8d\xb2\xf5D\x11\u0640\x00\xe0\x94\xa5\xf0\a{5\x1flP\\\xd5\x15\u07e6\xd2\xfa\u007f\\L\u0487\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xa5\xf0u\xfd@\x135W{f\x83\u0081\xe6\xd1\x01C-\xc6\xe0\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xa5\xfe,\xe9\u007f\x0e\x8c8V\xbe\r\xe5\xf4\u0732\xce]8\x9a\x16\x89\x01=\xb0\xb8\xb6\x86>\x00\x00\u07d4\xa5\xffb\"-\x80\xc0\x13\xce\xc1\xa0\xe8\x85\x0e\xd4\xd3T\xda\xc1m\x89\vA\a\\\x16\x8b\x18\x00\x00\u07d4\xa6\t\xc2m\xd3P\xc25\xe4K+\x9c\x1d\xdd\xcc\u0429\xd9\xf87\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa6\f\x12\tuO]\x87\xb1\x81\xdaO\b\x17\xa8\x18Y\xef\x9f\u0609\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\xa6\x10\x1c\x96\x1e\x8e\x1c\x15y\x8f\xfc\xd0\xe3 \x1dw\x86\xec7:\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xa6\x13Ei\x96@\x8a\xf1\xc2\xe9>\x17w\x88\xabU\x89^+2\x8a\x01Y\x19\xffG|\x88\xb8\x00\x00\u07d4\xa6\x18\x87\x81\x8f\x91J \xe3\x10w)\v\x83qZk-n\xf9\x89e\xea=\xb7UF`\x00\x00\u07d4\xa6\x1aT\xdfxJD\xd7\x1bw\x1b\x871u\t!\x13\x81\xf2\x00\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa6\x1c\u06ed\xf0K\x1eT\u0203\xde`\x05\xfc\xdf\x16\xbe\xb8\xeb/\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa69\xac\xd9k1\xbaS\xb0\u0407c\"\x9e\x1f\x06\xfd\x10^\x9d\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa6BP\x10\x04\xc9\x0e\xa9\xc9\xed\x19\x98\xba\x14\nL\xd6,o_\x89\r\x94\xfb\x8b\x10\xf8\xb1\x80\x00\u07d4\xa6D\xed\x92,\xc27\xa3\xe5\u0117\x9a\x99Tw\xf3nP\xbcb\x89\x1f\xa7=\x84]~\x96\x00\x00\u07d4\xa6F\xa9\\moY\xf1\x04\xc6T\x1dw`uz\xb3\x92\xb0\x8c\x89\u3bb5sr@\xa0\x00\x00\xe0\x94\xa6HL\u0184\xc4\xc9\x1d\xb5>\xb6\x8aM\xa4Zjk\xda0g\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa6N_\xfbpL,\x919\xd7~\xf6\x1d\x8c\u07e3\x1dz\x88\xe9\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94\xa6T&\xcf\xf3x\xed#%5\x13\xb1\x9fIm\xe4_\xa7\u13ca\x01\x86P\x12|\xc3\u0700\x00\x00\u07d4\xa6jIc\xb2\u007f\x1e\xe1\x93+\x17+\xe5\x96N\r:\xe5KQ\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4\xa6\u007f8\x81\x95eB:\xa8_>:\xb6\x1b\xc7c\u02eb\x89\u0749sw\xb0\"\u01be\b\x00\x00\u07d4\xa6\x8c14E\xc2-\x91\x9e\xe4l\xc2\xd0\xcd\xff\x04:uX%\x89\x04\x13t\xfd!\xb0\u0600\x00\u07d4\xa6\x8e\f0\u02e3\xbcZ\x88>T\x03 \xf9\x99\xc7\xcdU\x8e\\\x89a\x9237b\xa5\x8c\x80\x00\u07d4\xa6\x90\xf1\xa4\xb2\n\xb7\xba4b\x86 \u079c\xa0@\xc4<\x19c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa6\x9d|\xd1}HB\xfe\x03\xf6*\x90\xb2\xfb\xf8\xf6\xaf{\xb3\x80\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa6\xa0\x82R\xc8YQw\xcc.`\xfc'Y>#y\xc8\x1f\xb1\x89\x01\x16Q\xac>zu\x80\x00\u07d4\xa6\xa0\xdeB\x1a\xe5Om\x17(\x13\b\xf5dm/9\xf7w]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa6\xb2\xd5s)s`\x10,\a\xa1\x8f\xc2\x1d\xf2\xe7I\x9f\xf4\xeb\x89\xd9o\u0390\u03eb\xcc\x00\x00\xe0\x94\xa6\xc9\x10\xceMIJ\x91\x9c\u036a\xa1\xfc;\x82\xaat\xba\x06\u03ca\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa6\u3ea3\x8e\x10J\x1e'\xa4\xd8(i\xaf\xb1\xc0\xaen\xff\x8d\x89\x01\x11@\ueb4bq\x00\x00\u07d4\xa6\xee\xbb\xe4d\u04d1\x87\xbf\x80\u029c\x13\xd7 '\xec[\xa8\xbe\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa6\xf6+\x8a=\u007f\x11\"\a\x01\xab\x9f\xff\xfc\xb3'\x95\x9a'\x85\x89\x1bn)\x1f\x18\u06e8\x00\x00\u07d4\xa6\xf93\a\xf8\xbc\xe01\x95\xfe\u0387 C\xe8\xa0?{\xd1\x1a\x89\x9csK\xadQ\x11X\x00\x00\u07d4\xa7\x01\xdfy\xf5\x94\x90\x1a\xfe\x14DH^k \u00fd\xa2\xb9\xb3\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa7\x02L\xfdt,\x1e\xc1<\x01\xfe\xa1\x8d0B\xe6_\x1d]\xee\x8a\x02c\x11\x9a(\xab\u0430\x80\x00\u07d4\xa7\x18\xaa\xadY\xbf9\\\xba+#\xe0\x9b\x02\xfe\f\x89\x81bG\x8960<\x97\xe4hx\x00\x00\u07d4\xa7$|S\xd0Y\xeb|\x93\x10\xf6(\xd7\xfclj\nw?\b\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xa7%7c\xcfJu\u07d2\xca\x1evm\xc4\xee\x8a'E\x14{\x8a\x02F7p\xe9\n\x8fP\x00\x00\u07d4\xa7.\xe6f\u0133^\x82\xa5\x06\x80\x8bD<\xeb\xd5\xc62\xc7\u0749+^:\xf1k\x18\x80\x00\x00\u07d4\xa7DD\xf9\x0f\xbbT\xe5o:\u0276\xcf\u032aH\x19\xe4aJ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa7GC\x9a\xd0\u04d3\xb5\xa08a\xd7r\x962m\u8edd\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa7`{BW;\xb6\xf6\xb4\xd4\xf2<~*&\xb3\xa0\xf6\xb6\xf0\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\xa7i)\x89\n{G\xfb\x85\x91\x96\x01lo\u0742\x89\u03b7U\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u0794\xa7kt?\x98\x1bi0r\xa11\xb2+\xa5\x10\x96\\/\xef\u05c8\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xa7m?\x15bQ\xb7,\f\xcfKG\xa39<\xbdoI\xa9\u0149Hz\x9a0E9D\x00\x00\u07d4\xa7t(\xbc\xb2\xa0\xdbv\xfc\x8e\xf1\xe2\x0eF\x1a\n2\u016c\x15\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xa7u\x8c\xec\xb6\x0e\x8faL\u0396\x13~\xf7+O\xbd\awJ\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa7w^J\xf6\xa2:\xfa \x1f\xb7\x8b\x91^Q\xa5\x15\xb7\xa7(\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\xa7\u007f>\u1793\x88\xbb\xbb\"\x15\xc6#\x97\xb9e`\x13#`\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa7\x85\x9f\xc0\u007fun\xa7\xdc\xeb\xbc\xcdB\xf0X\x17X-\x97?\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa7\x96lH\x9fLt\x8az\u902a'\xa5t%\x17g\xca\xf9\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa7\xa3\xbba9\xb0\xad\xa0\f\x1f\u007f\x1f\x9fV\u0654\xbaM\x1f\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa7\xa3\xf1S\xcd\u00c8!\xc2\f]\x8c\x82A\xb2\x94\xa3\xf8+$\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa7\xa5\x17\u05ed5\x82\v\t\u0517\xfa~U@\xcd\xe9IXS\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa7\xc9\u04c8\xeb\xd8s\xe6k\x17\x13D\x83\x97\xd0\xf3\u007f\x8b\u04e8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa7\u073b\xa9\xb9\xbfgb\xc1EAlPjq\u3d17 \x9c\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xa7\xe7O\v\xdb'\x8f\xf0\xa8\x05\xa6Ha\x8e\xc5+\x16o\xf1\xbe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa7\xe87r\xbc \x0f\x90\x06\xaa*&\r\xba\xa8H=\xc5+0\x89\vB\xd56f7\xe5\x00\x00\xe0\x94\xa7\xef5\u0387\xed\xa6\u008d\xf2HxX\x15\x05>\xc9zPE\x8a\x01\x0f\f\xe9I\xe0\x0f\x93\x00\x00\u07d4\xa7\xf9\"\f\x80G\x82k\xd5\xd5\x18?Ngjmw\xbf\xed6\x89\bPh\x97k\xe8\x1c\x00\x00\u07d4\xa8\a\x10O'\x03\xd6y\xf8\u07af\xc4B\xbe\xfe\x84\x9eB\x95\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\f\xb1s\x8b\xac\b\xd4\xf9\xc0\x8bM\xef\xf5\x15T_\xa8XO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa8\x19\xd2\xec\xe1\"\xe0(\xc8\xe8\xa0J\x06M\x02\xb9\x02\x9b\b\xb9\x8965\u026d\xc5\u07a0\x00\x00\u0794\xa8%\xfdZ\xbby&\xa6|\xf3k\xa2F\xa2K\xd2{\xe6\xf6\xed\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4\xa8(U9\x86\x9d\x88\xf8\xa9aS7Uq}~\xb6Uv\xae\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa83\x82\xb6\xe1Rg\x97J\x85P\xb9\x8fqv\xc1\xa3S\xf9\xbe\x89\xbf\xfd\xaf/\xc1\xb1\xa4\x00\x00\xe0\x94\xa8DlG\x81\xa77\xacC(\xb1\xe1[\x8a\v?\xbb\x0f\xd6h\x8a\x04\x87\x94\xd1\xf2F\x19*\x00\x00\u07d4\xa8E[A\x17e\u0590\x1e1\x1erd\x03\t\x1eB\xc5f\x83\x89\xb7:\xec;\xfe\x14P\x00\x00\xe0\x94\xa8f\x13\xe6\u0124\xc9\xc5_\\\x10\xbc\xda2\x17]\u02f4\xaf`\x8a\x02C\xd6\xc2\xe3k\xe6\xae\x00\x00\u07d4\xa8m\xb0}\x9f\x81/G\x96b-@\xe0=\x13Xt\xa8\x8at\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa8\u007fz\xbdo\xa3\x11\x94(\x96x\xef\xb6<\xf5\x84\xee^*a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xa8\x80\u2a3f\x88\xa1\xa8&H\xb4\x01<I\xc4YLC<\u020a\x01\x00N.E\xfb~\xe0\x00\x00\u07d4\xa8\x85w\xa0s\xfb\xaf3\xc4\xcd .\x00\xeap\xefq\x1b@\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\x91L\x95\xb5`\xec\x13\xf1@Ws8\xc3+\u02f7}:z\x89\t\xc2\x00vQ\xb2P\x00\x00\xe0\x94\xa8\x9a\xc9;#7\x04r\u06ac3~\x9a\xfd\xf6BT?>W\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa8\x9d\xf3HY\xed\xd7\xc8 \u06c8w@\xd8\xff\x9e\x15\x15|{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\xa4<\x00\x91\x00al\xb4\xaeN\x03?\x1f\xc5\xd7\xe0\xb6\xf1R\x89\u0548\xd0x\xb4?M\x80\x00\u07d4\xa8\xa7\b\xe8O\x82\u06c6\xa3U\x02\x19;Ln\xe9\xa7n\xbe\x8f\x897\b\xba\xed=h\x90\x00\x00\xe0\x94\xa8\xa7\xb6\x8a\u06b4\xe3\xea\xdf\xf1\x9f\xfaX\xe3J?\xce\xc0\xd9j\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xa8\xa8\xdb\xdd\x1a\x85\u047e\xee%i\xe9\x1c\xccM\t\xae\u007fn\xa1\x8a\x01:k+VHq\xa0\x00\x00\u07d4\xa8\xac\xa7H\xf9\xd3\x12\xect\u007f\x8bex\x14&\x94\xc7\xe9\xf3\x99\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\xb6[\xa3\x17\x1a?w\xa65\v\x9d\xaf\x1f\x8dU\xb4\xd2\x01\xeb\x89(b\xf3\xb0\xd2\"\x04\x00\x00\u07d4\xa8\xbe\xb9\x1c+\x99\u0216J\xa9[kJ\x18K\x12i\xfc4\x83\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\xa8\xc0\xb0/\xaf\x02\xcbU\x19\u0768\x84\xde{\xbc\x8c\x88\xa2\u0681\x88\xe7\xc2Q\x85\x05\x06\x00\x00\u07d4\xa8\xc1\u05aaA\xfe=e\xf6{\xd0\x1d\xe2\xa8f\xed\x1e\u066eR\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\xa8\xca\xfa\xc3\"\x80\xd0!\x02\v\xf6\xf2\xa9x(\x83\u05ea\xbe\x12\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa8\xdb\v\x9b \x14S3<u\u007fj\u067c\xb5U\xc0-\xa9;\x89wB\xb7\x83\x0f4\x1d\x00\x00\u07d4\xa8\xe4*N3\xd7Rl\xca\x19\u0663m\xcdn\x80@\xd0\xeas\x89:\x8c\x02\xc5\xea-\xe0\x00\x00\u07d4\xa8\xe7 \x1f\xf6\x19\xfa\xff\xc32\xe6\xad7\xedA\xe3\x01\xbf\x01J\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa8\xee\x1d\xf5\xd4K\x12\x84i\xe9\x13V\x9e\xf6\xac\x81\xee\xdaO\u0209\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xa8\xef\x9a\xd2tC`B\x90>A<;\fb\xf5\xf5.\u0544\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa8\xf3\u007f\n\xb3\xa1\xd4H\xa9\xe3\xce@\x96_\x97\xa6F\b:4\x89\x11\xe0\xe4\xf8\xa5\v\xd4\x00\x00\u07d4\xa8\xf8\x9d\xd5\xccnd\u05f1\xee\xac\xe0\a\x02\x02,\xd7\xd2\xf0=\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\xa9\x04v\xe2\xef\xdf\xeeO8{\x0f2\xa5\x06x\xb0\xef\xb5s\xb5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa9\x14PF\xfa6(\xcf_\xd4\xc6\x13\x92{\xe51\xe6\xdb\x1f\u0749\x06\x12O\xee\x99;\xc0\x00\x00\u07d4\xa9\x14\u0375q\xbf\xd9=d\xdaf\xa4\xe1\b\xea\x13NP\xd0\x00\x89M\x878\x99G\x13y\x80\x00\xe0\x94\xa9\x1aZ{4\x1f\x99\xc55\x14N \xbe\x9ck;\xb4\u008eM\x8a\x01&u:\xa2$\xa7\v\x00\x00\u07d4\xa9%%Q\xa6$\xaeQ7\x19\u06beR\a\xfb\xef\xb2\xfdwI\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa9'\u050b\xb6\u02c1K\xc6\t\xcb\u02a9\x15\x1f]E\x9a'\xe1\x89\x0e\xb95\t\x00d\x18\x00\x00\u07d4\xa9)\u023dq\xdb\f0\x8d\xac\x06\b\n\x17G\xf2\x1b\x14e\xaa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa9K\xbb\x82\x14\u03cd\xa0\xc2\xf6h\xa2\xacs\xe8bHR\x8dK\x894\n\xad!\xb3\xb7\x00\x00\x00\u07d4\xa9Q\xb2D\xffP\u03eeY\x1d^\x1a\x14\x8d\xf6\xa98\xef*\x1a\x89^\x00\x15\x84\xdf\xcfX\x00\x00\u07d4\xa9`\xb1\xca\xdd;\\\x1a\x8el\xb3\xab\xca\xf5.\xe7\xc3\xd9\xfa\x88\x89R\x8b\xc3T^Rh\x00\x00\u07d4\xa9a\x17\x1fSB\xb1s\xddp\xe7\xbf\xe5\xb5\xca#\x8b\x13\xbc\u0749\xb8'\x94\xa9$O\f\x80\x00\u07d4\xa9u\xb0w\xfc\xb4\u030e\xfc\xbf\x83\x84Y\xb6\xfa$:AY\u0589\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xa9{\xeb:H\xc4_\x15((L\xb6\xa9_}\xe4S5\x8e\u018a\x06\x90\x83l\n\xf5\xf5`\x00\x00\u07d4\xa9~\a!DI\x9f\xe5\xeb\xbd5J\xcc~~\xfbX\x98]\b\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xa9\x86v/zO)O.\v\x172y\xad,\x81\xa2\"4X\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa9\x8f\x10\x985\xf5\xea\xcd\x05Cd|4\xa6\xb2i\xe3\x80/\xac\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa9\x97\xdf\u01d8j'\x05\bH\xfa\x1cd\u05e7\xd6\xe0z\u0322\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xa9\x99\x91\u03bd\x98\xd9\xc88\xc2_zt\x16\xd9\xe2D\xca%\r\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa9\xa1\xcd\xc3;\xfd7o\x1c\rv\xfbl\x84\xb6\xb4\xac'Mh\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa9\xa8\xec\xa1\x1a#\xd6F\x89\xa2\xaa>A}\xbb=3k\xb5\x9a\x89\x0e4S\xcd;g\xba\x80\x00\u07d4\xa9\xac\xf6\x00\b\x1b\xb5[\xb6\xbf\xba\xb1\x81_\xfcN\x17\xe8Z\x95\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa9\xad\x19&\xbcf\xbd\xb31X\x8e\xa8\x197\x88SM\x98,\x98\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xa9\xaf!\xac\xbeH/\x811\x89j\"\x806\xbaQ\xb1\x94S\u00c9\x02\xb5\xe0!\x98\f\xc1\x80\x00\xe0\x94\xa9\xb2\xd2\xe0IN\xab\x18\xe0}7\xbb\xb8V\xd8\x0e\x80\xf8L\u04ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa9\xbaoA;\x82\xfc\xdd\xf3\xaf\xfb\xbd\u0412\x87\xdc\xf5\x04\x15\u0289\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa9\xbe\x88\xad\x1eQ\x8b\v\xbb\x02J\xb1\xd8\xf0\xe7?y\x0e\fv\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xa9\xbf\xc4\x10\xdd\xdb q\x1eE\xc0s\x87\xea\xb3\n\x05N\x19\xac\x89>\x99`\x1e\xdfNS\x00\x00\u07d4\xa9\u0522\xbc\xbe[\x9e\bi\xd7\x0f\x0f\xe2\xe1\u05aa\xcdE\xed\u0149\n\xc6\xe7z\xb6c\xa8\x00\x00\xe0\x94\xa9\xd6KO;\xb7\x85\a\"\xb5\x8bG\x8b\xa6\x917^\"NB\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa9\xd6\xf8q\xcax\x1au\x9a \xac:\u06d7,\xf1()\xa2\b\x892$\xf4'#\xd4T\x00\x00\xe0\x94\xa9\xdc\x04$\u0196\x9dy\x83X\xb3\x93\xb1\x93:\x1fQ\xbe\xe0\n\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa9\xe1\x94f\x1a\xacpN\xe9\u07a0C\x97N\x96\x92\xde\xd8J]\x89\x1a&\xa5\x14\"\xa0p\x00\x00\u07d4\xa9\xe2\x837\xe65q\x93\xd9\xe2\xcb#k\x01\xbeD\xb8\x14'\u07c9wC\"\x17\xe6\x83`\x00\x00\u07d4\xa9\xe6\xe2^ekv%Xa\x9f\x14z!\x98[\x88t\xed\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa9\xe9\xdb\xcez,\xb06\x94y\x98\x97\xbe\xd7\xc5M\x15_\u06a8\x89\n\xb5\xae\x8f\u025de\x80\x00\u07d4\xa9\xed7{}n\xc2Yq\xc1\xa5\x97\xa3\xb0\xf3\xbe\xadW\u024f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xaa\x02\x00\xf1\xd1~\x9cT\xda\x06G\xbb\x969]W\xa7\x858\u06099>\xf1\xa5\x12|\x80\x00\x00\u07d4\xaa\f\xa3ss7\x17\x8a\f\xaa\xc3\t\x9cXK\x05lV0\x1c\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xaa\x13kG\x96+\xb8\xb4\xfbT\r\xb4\xcc\xf5\xfd\xd0B\xff\xb8\u03c9\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\xaa\x14B-o\n\xe5\xa7X\x19N\xd1W\x80\xc88\xd6\u007f\x1e\xe1\x8a\x06\t2\x05lD\x9d\xe8\x00\x00\u07d4\xaa\x16&\x9a\xac\x9c\r\x800h\xd8/\u01d1Q\xda\xdd3Kf\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xaa\x16p&\u04da\xb7\xa8V5\x94N\xd9\xed\xb2\xbf\xeb\xa1\x18P\x8a\x01\xc1\xd5\xe2\x1bO\xcfh\x00\x00\u07d4\xaa\x1b7h\xc1m\x82\x1fX\x0ev\xc8\xe4\xc8\xe8m}\u01c8S\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xaa\x1d\xf9.Q\xdf\xf7\v\x19s\xe0\xe9$\xc6b\x87\xb4\x94\xa1x\x89\x1c\xf8J0\xa0\xa0\xc0\x00\x00\u07d4\xaa,g\x00\x96\xd3\xf990S%B~\xb9U\xa8\xa6\r\xb3\u0149l\x95Y\x06\x99#-\x00\x00\u07d4\xaa15\xcbT\xf1\x02\xcb\xef\xe0\x9e\x96\x10:\x1ayg\x18\xffT\x89\x03\"\"\xd9\xc31\x94\x00\x00\u07d4\xaa2\x1f\xdb\xd4I\x18\r\xb8\xdd\xd3O\x0f\xe9\x06\xec\x18\xee\t\x14\x89%\"H\u07b6\xe6\x94\x00\x00\xe0\x94\xaa9%\xdc\"\v\xb4\xae!w\xb2\x880x\xb6\xdc4l\xa1\xb2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xaa?)`\x1a\x131t^\x05\xc4(0\xa1^q\x93\x8ab7\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94\xaaG\xa4\xff\xc9y622\u025b\x99\xfa\xda\x0f'4\xb0\xae\xee\x8a\x01\xb8H\x9d\xf4\xdb\xff\x94\x00\x00\u07d4\xaaI=?O\xb8fI\x1c\xf8\xf8\x00\xef\xb7\xe22N\xd7\xcf\xe5\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xaaV\xa6]\u012b\xb7/\x11\xba\xe3+o\xbb\aDG\x91\xd5\u0249(\x94\xe9u\xbfIl\x00\x00\xe0\x94\xaaZ\xfc\xfd\x83\t\xc2\u07dd\x15\xbe^jPN}pf$\u014a\x01<\xf4\"\xe3\x05\xa17\x80\x00\u07d4\xaa\x8e\xb0\x82;\a\xb0\xe6\xd2\n\xad\xda\x0e\x95\xcf85\xbe\x19.\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xaa\x91#~t\r%\xa9/\u007f\xa1F\xfa\xa1\x8c\xe5m\xc6\xe1\xf3\x892$\xf4'#\xd4T\x00\x00\u07d4\xaa\x96\x0e\x10\xc5#\x91\xc5N\x158|\xc6z\xf8'\xb51m\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\x9b\xd4X\x955\xdb'\xfa+\xc9\x03\xca\x17\xd6y\xddeH\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\xa8\xde\xfe\x11\xe3a?\x11\x06\u007f\xb9\x83bZ\b\x99Z\x8d\xfc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaa\xaa\xe6\x8b2\x14\x02\xc8\xeb\xc14h\xf3A\xc6<\f\xf0?\u0389Rf<\u02b1\xe1\xc0\x00\x00\u07d4\xaa\xad\x1b\xaa\xdeZ\xf0N+\x17C\x9e\x93Y\x87\xbf\x8c+\xb4\xb9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xaa\xb0\n\xbfX(\xd7\xeb\xf2kG\u03ac\u0378\xba\x032Qf\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xaa\xbd\xb3\\\x15\x14\x98J\x03\x92\x13y?3E\xa1h\xe8\x1f\xf1\x89\x10\xca\u0216\xd29\x00\x00\x00\u07d4\xaa\xca`\xd9\xd7\x00\u7156\xbb\xbb\xb1\xf1\xe2\xf7\x0fF'\xf9\u060965\xbbw\xcbK\x86\x00\x00\u07d4\xaa\xce\u0629V;\x1b\xc3\x11\xdb\xdf\xfc\x1a\xe7\xf5u\x19\xc4D\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\u04b7\xf8\x10f\x95\a\x8el\x13\x8e\xc8\x1at\x86\xaa\xca\x1e\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaa\xe6\x1eC\xcb\r\f\x96\xb3\x06\x99\xf7~\x00\xd7\x11\u0423\x97\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaa\xe72\xed\xa6Y\x88\u00e0\f\u007fG/5\x1cF;\x1c\x96\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\xf0#\xfe\U0009091b\xb7\x8b\xb7\xab\xc9]f\x9cP\xd5(\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xaa\xf5\xb2\a\xb8\x8b\r\xe4\xac@\xd7G\xce\xe0n\x17-\xf6\xe7E\x8a\x06\xa7\xb7\x1d\u007fQ\u0410\x00\x00\u07d4\xaa\xf9\xeeK\x88lm\x1e\x95Io\xd2t#[\xf4\xec\xfc\xb0}\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xaa\xfb{\x01:\xa1\xf8T\x1c~2{\xf6P\xad\xbd\x19L \x8f\x89I\x9e\t-\x01\xf4x\x00\x00\xe0\x94\xab\t\x863\xee\xee\f\xce\xfd\xf62\xf9WTV\xf6\u0740\xfc\x86\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xab\f\xedv.\x16a\xfa\xe1\xa9*\xfb\x14\b\x88\x94\x13yH%\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xab\x14\xd2!\xe3=TF)\x19\x8c\u0416\xedc\u07e2\x8d\x9fG\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xab \x9f\u0729y\u0426G\x01\n\xf9\xa8\xb5/\xc7\xd2\r\x8c\u044a\x01\xee\xe2S,|-\x04\x00\x00\u07d4\xab'\xbax\xc8\xe5\xe3\xda\xef1\xad\x05\xae\xf0\xff\x03%r\x1e\b\x89\x19^\xce\x00n\x02\xd0\x00\x00\u07d4\xab(q\xe5\a\u01fe9eI\x8e\x8f\xb4b\x02Z\x1a\x1cBd\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xab8a\"o\xfe\xc1(\x91\x87\xfb\x84\xa0\x8e\xc3\xed\x042d\xe8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab=\x86\xbc\x82\x92~\f\xd4!\xd1F\xe0\u007f\x91\x93'\xcd\xf6\xf9\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xab>b\xe7z\x8b\"^A\x15\x92\xb1\xaf0\aR\xfeA$c\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xab>x)K\xa8\x86\xa0\xcf\xd5\xd3H\u007f\xb3\xa3\a\x8d3\x8dn\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xab@\x04\xc0@?~\xab\xb0\xeaXo!!V\xc4 =g\xf1\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xabAo\xe3\rX\xaf\xe5\xd9EL\u007f\xce\u007f\x83\v\xccu\x03V\x89\x0657\x01\xc6\x05\u06c0\x00\u07d4\xabEr\xfb\xb1\xd7+W]i\xecj\xd1s3\x87>\x85R\xfc\x89lj\xc5L\xdahG\x00\x00\u07d4\xabZy\x01av2\ts\xe8\xcd8\xf67U0\x02%1\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab]\xfc\x1e\xa2\x1a\xdcB\u03cc?n6\x1e$?\xd0\xdaa\xe5\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xabke\xea\xb8\xdf\xc9\x17\xec\x02Q\xb9\xdb\x0e\u03e0\xfa\x03(I\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xabp\x91\x93.K\u00dd\xbbU#\x80\u0293O\xd7\x16m\x1en\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\xabt\x16\xff2%IQ\u02fcbN\xc7\xfbE\xfc~\u02a8r\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xab|B\xc5\xe5-d\x1a\a\xadu\t\x9cb\x92\x8b\u007f\x86b/\x89\x126\x1a\xa2\x1d\x14\xba\x00\x00\u07d4\xab}T\xc7\xc6W\x0e\xfc\xa5\xb4\xb8\xcep\xf5*Ws\xe5\xd5;\x89\x0f(:\xbe\x9d\x9f8\x00\x00\u07d4\xab~\v\x83\xed\x9aBLm\x1ejo\x87\xa4\xdb\xf0d\t\xc7\u0589\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xab\x84\xa0\xf1G\xad&T\x00\x00+\x85\x02\x9aA\xfc\x9c\xe5\u007f\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab\x93\xb2n\xce\n\n\xa2\x13e\xaf\xed\x1f\xa9\xae\xa3\x1c\xd5Dh\x89W+{\x98sl \x00\x00\u07d4\xab\x94\x8aJ\xe3y\\\xbc\xa11&\xe1\x92S\xbd\xc2\x1d:\x85\x14\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xab\x9a\xd3n\\t\xce.\x969\x9fW\x83\x941\xd0\u77d6\xab\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\xab\xb2\xe6\xa7*@\xban\xd9\b\u037c\xec<V\x12X12\xfe\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4\xab\xc0h\xb4\x97\x9b\x0e\xa6Jb\u04f7\xaa\x89}s\x81\r\xc53\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xab\xc4_\x84\xdbs\x82\xdd\xe5L_}\x898\xc4/O:;\u0109\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xab\xc4\xca\xebGMF'\xcbn\xb4V\xec\xba\x0e\xcd\b\xed\x8a\xe1\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xab\xc7G\x06\x96I`\xdf\xe0\u0723\u0727\x9e\x92\x16\x05o\x1c\xf4\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\xab\u0269\x9e\x8a!H\xa5Zm\x82\xbdQ\xb9\x8e\xb59\x1f\u06ef\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xab\u037c\x8f\x1d\xd1:\xf5x\u0524wJb\x18+\xed\xf9\xf9\xbe\x89\x01\xfc\xc2{\xc4Y\xd2\x00\x00\u07d4\xab\xd1T\x905\x13\xb8\xdaO\x01\x9fh(K\x06V\xa1\xd0\x16\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab\xd2\x1e\xff\x95O\u01a7\xde&\x91*|\xbb0:f\a\x80N\x89R<\x9a\xa6\x96\xeb\x94\x00\x00\u07d4\xab\xd4\xd6\xc1fcX\xc0@o\xdf:\xf2H\xf7\x8e\u0383\x01\x04\x89r}\xe3J$\xf9\x00\x00\x00\u07d4\xab\xd9`[>\x91\xac\xfdwx0\xd1dcG\x8a\xe0\xfcw \x89\a?u\u0460\x85\xba\x00\x00\xe0\x94\xab\u071f\x1b\xcfM\x19\xee\x96Y\x100\xe7r\xc340/}\x83\x8a\b~^\x11\xa8\x1c\xb5\xf8\x00\x00\u07d4\xab\xde\x14{*\xf7\x89\ua946T~f\xc4\xfa&d\xd3(\xa4\x89\rk`\x81\xf3L\x12\x80\x00\xe0\x94\xab\xe0|\xedj\xc5\xdd\xf9\x91\xef\xf6\xc3\xda\"jt\x1b\xd2C\xfe\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xab\xf1/\xa1\x9e\x82\xf7lq\x8f\x01\xbd\xca\x00\x03gE#\xef0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xab\xf7(\u03d3\x12\xf2!(\x02NpF\xc2Q\xf5\xdcY\x01\xed\x8a\x06A\xe8\xa15c\xd8\xf8\x00\x00\u07d4\xab\xf8\xff\xe0p\x8a\x99\xb5(\xcc\x1e\xd4\xe9\xceK\r\x060\xbe\x8c\x89z\xb5\u00ae\xee\xe68\x00\x00\u07d4\xab\xfc\xf5\xf2P\x91\xceW\x87_\xc6t\xdc\xf1\x04\xe2\xa7=\xd2\xf2\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xab\xfe\x93d%\xdc\u01f7K\x95P\x82\xbb\xaa\xf2\xa1\x1dx\xbc\x05\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xac\x02OYO\x95X\xf0ICa\x8e\xb0\xe6\xb2\xeeP\x1d\xc2r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\x12*\x03\xcd\x05\x8c\x12._\xe1{\x87/Hw\xf9\u07d5r\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xac\x14.\xda\x11W\xb9\xa9\xa6C\x90\xdf~j\xe6\x94\xfa\u0249\x05\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\x1d\xfc\x98Kq\xa1\x99)\xa8\x1d\x81\xf0J|\xbb\x14\a7\x03\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xac!\xc1\xe5\xa3\xd7\xe0\xb5\x06\x81g\x9d\xd6\u01d2\xdb\u0287\xde\u02ca\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xac(\x89\xb5\x96o\f\u007f\x9e\xdbB\x89\\\xb6\x9d\x1c\x04\xf9#\xa2\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xac(\xb5\xed\xea\x05\xb7o\x8c_\x97\bEA'|\x96ijL\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xac,\x8e\t\xd0d\x93\xa68XC{\xd2\v\xe0\x19bE\x03e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xac.vm\xac?d\x8fcz\xc6q?\u0770h\xe4\xa4\xf0M\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\xac9\x00)\x8d\xd1M|\xc9mJ\xbbB\x8d\xa1\xba\xe2\x13\xff\xed\x8a\x05<\xa1)t\x85\x1c\x01\x00\x00\u07d4\xac=\xa5&\xcf\u0388)s\x02\xf3LI\xcaR\r\xc2q\xf9\xb2\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xacD`\xa7nm\xb2\xb9\xfc\xd1R\xd9\xc7q\x8d\x9a\xc6\xed\x8co\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xacJ\xcf\xc3n\xd6\tJ'\xe1\x18\xec\xc9\x11\xcdG>\x8f\xb9\x1f\x89a\x91>\x14@<\f\x00\x00\u07d4\xacL\xc2V\xaet\xd6$\xac\xe8\r\xb0x\xb2 \u007fW\x19\x8fk\x89lyt\x12?d\xa4\x00\x00\u07d4\xacN\xe9\xd5\x02\xe7\xd2\xd2\xe9\x9eY\xd8\xca}_\x00\xc9KM\u058965\u026d\xc5\u07a0\x00\x00\u07d4\xacR\xb7~\x15fH\x14\xf3\x9eO'\x1b\xe6A0\x8d\x91\xd6\u0309\v\xed\x1d\x02c\xd9\xf0\x00\x00\u07d4\xacY\x99\xa8\x9d-\u0486\u0568\fm\xee~\x86\xaa\xd4\x0f\x9e\x12\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xac_br1H\r\r\x950.m\x89\xfc2\xcb\x1dO\xe7\xe3\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xac`\x8e+\xac\x9d\xd2\a(\u0494~\xff\xbb\xbf\x90\n\x9c\xe9K\x8a\x01EK\r\xb3uh\xfc\x00\x00\u07d4\xacm\x02\xe9\xa4k7\x9f\xacJ\u0271\u05f5\xd4{\xc8P\xce\x16\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xacoh\xe87\xcf\x19a\xcb\x14\xabGDm\xa1h\xa1m\u0789\x89Hz\x9a0E9D\x00\x00\u07d4\xacw\xbd\xf0\x0f\u0558[]\xb1+\xbe\xf8\x008\n\xbc*\x06w\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xac~\x03p'#\xcb\x16\xee'\xe2-\u0438\x15\xdc-\\\xae\x9f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xac\x8bP\x9a\xef\xea\x1d\xbf\xaf+\xb35\x00\xd6W\vo\xd9mQ\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xac\x8e\x87\xdd\xda^x\xfc\xbc\xb9\xfa\u007f\xc3\xce\x03\x8f\x9f}.4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xac\x9f\xffh\xc6\x1b\x01\x1e\xfb\xec\xf08\xedr\u06d7\xbb\x9er\x81\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xac\xa1\xe6\xbcd\xcc1\x80\xf6 \xe9M\u0171\xbc\xfd\x81X\xe4]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\xa2\xa883\v\x170-\xa71\xd3\r\xb4\x8a\x04\xf0\xf2\a\xc1\x89Hz\x9a0E9D\x00\x00\u07d4\xac\xaa\xdd\xcb\xf2\x86\xcb\x0e!]\xdaUY\x8f\u007f\xf0\xf4\xad\xa5\u018965\u026d\xc5\u07a0\x00\x00\u07d4\xac\xb9C8UK\u0108\u0308\xae-\x9d\x94\b\rk\u07c4\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xac\xbc-\x19\xe0l;\xab\xbb[o\x05+k\xf7\xfc7\xe0r)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\xbd\x18U\x89\xf7\xa6\x8ag\xaaK\x1b\xd6Pw\xf8\xc6NN!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\xc0bp,Ya]4D\xefb\x14\xb8\x86+\x00\x9a\x02\xed\x89QO\xcb$\xff\x9cP\x00\x00\u07d4\xac\xc0\x90\x9f\xda.\xa6\xb7\xb7\xa8\x8d\xb7\xa0\xaa\xc8h\t\x1d\xdb\xf6\x89\x013v_\x1e&\u01c0\x00\u07d4\xac\xc1\u01c7\x86\xabM+;'q5\xb5\xba\x12>\x04\x00Hk\x89\x04E\x91\xd6\u007f\xec\xc8\x00\x00\u07d4\xac\xc4j*U\\t\xde\u0522\xbd\tN\x82\x1b\x97\x84;@\xc0\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xac\u015f;0\xce\xff\xc5da\xcc[\x8d\xf4\x89\x02$\x0e\x0e{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\xce\x01\xe0\xa7\x06\x10\xdcp\xbb\x91\xe9\x92o\xa9\x95\u007f7/\xba\x89\x1d\x1c_>\xda \xc4\x00\x00\u07d4\xac\xd8\u0751\xf7\x14vLEg|c\xd8R\xe5n\xb9\xee\xce.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\u2af6;\x06\x04@\x9f\xbd\xe3\xe7\x16\u0487mD\xe8\xe5\u0749\b=lz\xabc`\x00\x00\xe0\x94\xac\xec\x91\xefiA\xcfc\v\xa9\xa3\u71e0\x12\xf4\xa2\xd9\x1d\u050a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xad\nJ\xe4x\xe9cn\x88\xc6\x04\xf2B\xcfT9\xc6\xd4V9\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4\xad\x17\x99\xaa\xd7`+E@\u0343/\x9d\xb5\xf1\x11P\xf1hz\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xad\x1dh\xa08\xfd%\x86\x06~\xf6\xd15\xd9b\x8ey\xc2\xc9$\x89\xfe\t\xa5'\x9e*\xbc\x00\x00\u07d4\xad*\\\x00\xf9#\xaa\xf2\x1a\xb9\xf3\xfb\x06n\xfa\n\x03\xde/\xb2\x8965\xbbw\xcbK\x86\x00\x00\u07d4\xad5e\xd5+h\x8a\xdd\xed\b\x16\x8b-8r\xd1}\n&\xae\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xad7|\xd2^\xb5>\x83\xae\t\x1a\n\x1d+E\x16\xf4\x84\xaf\u0789i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xadAM)\xcb~\xe9s\xfe\xc5N\"\xa3\x88I\x17\x86\xcfT\x02\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xadD5~\x01~$OGi1\u01f8\x18\x9e\xfe\xe8\n]\n\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xadW\xaa\x9d\x00\xd1\fC\x9b5\xef\xcc\v\xec\xac.9U\xc3\x13\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xadY\xa7\x8e\xb9\xa7J\u007f\xbd\xae\xfa\xfa\x82\xea\u0684u\xf0\u007f\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xadZ\x8d<dx\xb6\x9fe}\xb3\x83z%u\xef\x8e\x1d\xf91\x89\x02\x01V\xe1\x04\xc1\xb3\x00\x00\u07d4\xadf\r\xec\x82U\"\xa9\xf6/\xce\xc3\u01771\x98\r\u0086\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xadf(5.\xd39\v\xaf\xa8m\x92>V\x01L\xfc\xb3`\xf4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xadr\x81!\x87?\x04V\xd0Q\x8b\x80\xabe\x80\xa2\x03pe\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xads,\x97e\x93\xee\xc4x;N.\xcdy9yx\v\xfe\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xad}\xd0S\x85\x9e\xdf\xf1\xcbo\x9d*\xcb\xedm\xd5\xe32Bo\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xad\x80\xd8e\xb8\\4\xd2\xe6IK.z\xef\xeak\x9a\xf1\x84\u06c9\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xad\x8b\xfe\xf8\u018aH\x16\xb3\x91o5\xcb{\xfc\xd7\xd3\x04\tv\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xad\x8eH\xa3wi]\xe0\x146:R:(\xb1\xa4\fx\xf2\b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xad\x91\n#\u0585\x06\x13eJ\xf7\x863z\u04a7\bh\xacm\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xad\x92~\x03\xd1Y\x9ax\xca+\xf0\xca\u04a1\x83\xdc\xebq\xea\xc0\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xad\x92\xca\x06n\xdb|q\x1d\xfc[\x16a\x92\xd1\xed\xf8\xe7q\x85\x8a\a\x9f\x90\\o\xd3N\x80\x00\x00\u07d4\xad\x94#_\u00f3\xf4z$\x13\xaf1\u8111I\b\xef\fE\x89\x1b\x1b\x01B\xd8\x15\x84\x00\x00\u07d4\xad\x9e\x97\xa0H/5:\x05\xc0\xf7\x92\xb9w\xb6\xc7\xe8\x11\xfa_\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xad\x9fL\x89\n;Q\x1c\xeeQ\xdf\xe6\xcf\xd7\xf1\t;vA,\x89\x1bv|\xbf\xeb\f\xe4\x00\x00\u07d4\xad\xaa\x0eT\x8c\x03Z\xff\xedd\xcag\x8a\x96?\xab\xe9\xa2k\xfd\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\xad\xb9H\xb1\xb6\xfe\xfe }\xe6^\x9b\xbc-\xe9\x8e`]\vW\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xad\xc1\x9e\xc85\xaf\xe3\u5347\u0713\xa8\xa9!<\x90E\x13&\x89j\xdb\xe54\"\x82\x00\x00\x00\u07d4\xad\xc8\"\x8e\xf9(\xe1\x8b*\x80}\x00\xfb<ly\xcd\x1d\x9e\x96\x89\x01<i\xdf3N\xe8\x00\x00\u07d4\xad\xdb&1r'\xf4\\\x87\xa2\u02d0\xdcL\xfd\x02\xfb#\xca\xf8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xad\xe6\xf8\x16;\xf7\u01fbJ\xbe\x8e\x98\x93\xbd\f\xc1\x12\xfe\x88r\x89\x11\xc2]\x00M\x01\xf8\x00\x00\u07d4\xad\xeb J\xa0\u00ce\x17\x9e\x81\xa9N\u0633\xe7\xd50G\xc2k\x89 \xf5\xb1\uab4d\x80\x00\x00\u07d4\xad\xebR\xb6\x04\xe5\xf7\u007f\xaa\xac\x88'[\x8dkI\xe9\xf9\xf9\u007f\x89qBk\x00\x95n\xd2\x00\x00\xe0\x94\xad\xf1\xac\xfe\x99\xbc\x8c\x14\xb3\x04\xc8\xd9\x05\xba'e{\x8a{\u010a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xad\xf8R\x03\xc87j_\u0798\x158J5\f8y\xc4\u02d3\x89>1\xfcgX\x15\xaa\x00\x00\u07d4\xad\xff\r\x1d\v\x97G\x1ev\u05c9\xd2\u470at\xf9\xbdT\xff\x89e\xea=\xb7UF`\x00\x00\u07d4\xae\x06,D\x86\x18d0u\xdez\x0004-\xce\xd6=\xba\u05c9,\xc6\u034c\u0082\xb3\x00\x00\xe0\x94\xae\x10\xe2z\x01O\r0k\xaf&mH\x97\u021a\xee\xe2\xe9t\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xae\x12k8,\xf2W\xfa\xd7\xf0\xbc}\x16)~T\xccrg\u0689\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x13\xa0\x85\x11\x11\x0f2\xe5;\xe4\x12xE\xc8C\xa1\xa5|{\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xae\x17\x9aF\r\xb6c&t=$\xe6u#\xa5{$m\xaf\u007f\x8a\x01\x00\a\xae|\xe5\xbb\xe4\x00\x00\u07d4\xae\"(ey\x90y\xaa\xf4\xf0gJ\f\u06ab\x02\xa6\xd5p\xff\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xae#\x9a\xcf\xfdN\xbe.\x1b\xa5\xb4\x17\x05r\xdcy\xcce3\xec\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xae/\x9c\x19\xacv\x13e\x94C#\x93\xb0G\x1d\b\x90!d\u04c9%\xdf\x05\u01a8\x97\xe4\x00\x00\u07d4\xae4\x86\x1d4\"S\x19O\xfcfR\xdf\xdeQ\xabD\xca\xd3\xfe\x89\x19F\bhc\x16\xbd\x80\x00\u07d4\xae6\xf7E!!\x91>\x80\x0e\x0f\xcd\x1ae\xa5G\x1c#\x84o\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\xae?\x98\xa4C\xef\xe0\x0f>q\x1dR]\x98\x94\u071aa\x15{\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xaeG\xe2`\x9c\xfa\xfe6\x9df\xd4\x15\xd99\xde\x05\b\x1a\x98r\x8a\x05\xba\xec\xf0%\xf9\xb6P\x00\x00\u07d4\xaeO\x12.5\xc0\xb1\xd1\xe4\x06\x92\x91E|\x83\xc0\u007f\x96_\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaePU\x81L\xb8\xbe\f\x11{\xb8\xb1\xc8\u04b6;F\x98\xb7(\x89\x01\xbc\x93.\xc5s\xa3\x80\x00\u07d4\xaeS\x8cs\u0173\x8d\x8dXM~\xbd\xad\xef\xb1\\\xab\xe4\x83W\x896'\xe8\xf7\x127<\x00\x00\u07d4\xaeW\xcc\x12\x9a\x96\xa8\x99\x81\xda\xc6\r/\xfb\x87}]\xc5\xe42\x89<:#\x94\xb3\x96U\x00\x00\u07d4\xaeZ\xa1\xe6\u00b6\x0fo\xd3\xef\xe7!\xbbJq\x9c\xbe=o]\x89+$\u01b5Z^b\x00\x00\u07d4\xae\\\x9b\xda\xd3\xc5\u0221\"\x04D\xae\xa5\xc2)\xc1\x83\x9f\x1dd\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xae\\\xe35Z{\xa9\xb32v\f\tP\u00bcE\xa8_\xa9\xa0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xae]\"\x1a\xfc\xd3\u0493U\xf5\b\xea\xdf\xca@\x8c\xe3<\xa9\x03\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xaec[\xf781\x11\x9d-)\xc0\xd0O\xf8\xf8\xd8\u0425zF\x89Hz\x9a0E9D\x00\x00\xe0\x94\xaed\x81U\xa6X7\x0f\x92\x9b\xe3\x84\xf7\xe0\x01\x04~I\xddF\x8a\x02\xdf$\xae2\xbe D\x00\x00\xe0\x94\xaeo\fs\xfd\xd7|H\x97'Q!t\u0675\x02\x96a\x1cL\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xaep\xe6\x9d,J\n\xf8\x18\x80{\x1a'\x05\xf7\x9f\u0435\xdb\u01095e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xaew9\x12N\xd1S\x05%\x03\xfc\x10\x14\x10\xd1\xff\xd8\xcd\x13\xb7\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xaex\xbb\x84\x919\xa6\xba8\xae\x92\xa0\x9ai`\x1c\xc4\xcbb\u0449\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xae\x84\"\x10\xf4M\x14\u0124\u06d1\xfc\x9d;;P\x01O{\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xae\x84.\x81\x85\x8e\xcf\xed\xf6Plhm\xc2\x04\xac\x15\xbf\x8b$\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xae\x89T\xf8\xd6\x16m\xe5\a\xcfa)}\x0f\xc7\xcak\x9eq(\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x9e\xcdk\u0755.\xf4\x97\xc0\x05\n\u0aca\x82\xa9\x18\x98\u0389\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\xae\x9f\\?\xbb\xe0\u027c\xbf\x1a\xf8\xfft\xea(\v:]\x8b\b\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xae\xad\x88\u0589Ak\x1c\x91\xf26D!7[}<p\xfb.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xae\xad\xfc\u0417\x8e\xda\xd7J2\xbd\x01\xa0\xa5\x1d7\xf2F\xe6a\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xae\xb9\x16\xeb\xf4\x9d\x0f\x86\xc1?s1\xce\xf1\x9e\x12\x997Q-\x89 \x85e[\x8d\x1b\n\x00\x00\u07d4\xae\xbdO ]\u7676K5d\xb2V\xd4*q\x1d7\uf649?\u03cbEt\xf8N\x00\x00\xe0\x94\xae\xc2|\xe2\x13>\x82\xd0RR\n\xfb\\Wm\x9f~\xb9>\u048a\r\xd0A \xba\t\xcf\xe6\x00\x00\u07d4\xae\xc2\u007f\xf5\xd7\xf9\xdd\u0691\x18?F\xf9\xd5%C\xb6\xcd+/\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xae\xe4\x9dh\xad\xed\xb0\x81\xfdCpZ_x\xc7x\xfb\x90\xdeH\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xae\xf5\xb1\"X\xa1\x8d\xec\a\xd5\xec.1et\x91\x9dy\xd6\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xae\xfc\xfe\x88\xc8&\xcc\xf11\xd5N\xb4\ua7b8\x0ea\xe1\xee%\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xaf\x06\xf5\xfam\x12\x14\xecC\x96}\x1b\xd4\xdd\xe7J\xb8\x14\xa98\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4\xaf\x11H\xefl\x8e\x10=u0\xef\xc9\x16y\u026c'\x00\t\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaf >\"\x9d~mA\x9d\xf47\x8e\xa9\x87\x15Q_c\x14\x85\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xaf X\xc7(,\xf6|\x8c<\xf90\x13<\x89a|\xe7])\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\xaf&\xf7\u01bfE> x\xf0\x89S\u4c80\x04\xa2\xc1\xe2\t\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xaf0\x87\xe6.\x04\xbf\x90\rZT\xdc>\x94bt\u0692B;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf6\x14\u0736\x8a6\xe4ZN\x91\x1ebybG\"-Y[\x89z\x81\x06_\x11\x03\xbc\x00\x00\u07d4\xaf6\x15\u01c9\u0431\x15*\xd4\xdb%\xfe]\xcf\"(\x04\xcfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaf<\xb5\x96Y3\xe7\xda\u0603i;\x9c>\x15\xbe\xb6\x8aHs\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xafD\x93\xe8R\x1c\xa8\x9d\x95\xf5&|\x1a\xb6?\x9fEA\x1e\x1b\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xafL\xf4\x17\x85\x16\x1fW\x1d\f\xa6\x9c\x94\xf8\x02\x1fA)N\u028a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xafR\x9b\xdbE\x9c\xc1\x85\xbe\xe5\xa1\u014b\xf7\xe8\xcc\xe2\\\x15\r\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xafg\xfd>\x12\u007f\xd9\xdc6\xeb?\xcdj\x80\u01feOu2\xb2\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xafw\x1094Z40\x01\xbc\x0f\x8aY#\xb1&\xb6\rP\x9c\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xaf\u007fy\xcbAZ\x1f\xb8\u06fd\tF\a\xee\x8dA\xfb|Z;\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xaf\x87\xd27\x1e\xf3x\x95\u007f\xbd\x05\xba/\x1df\x93\x1b\x01\u2e09%\xf2s\x93=\xb5p\x00\x00\u07d4\xaf\x88\x0f\xc7V}U\x95\xca\xcc\xe1\\?\xc1L\x87B\xc2l\x9e\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xaf\x8e\x1d\xcb1L\x95\r6\x87CM0\x98X\xe1\xa8s\x9c\u0509\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xaf\x99-\xd6i\xc0\x88>U\x15\xd3\xf3\x11*\x13\xf6\x17\xa4\xc3g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xa1\u056d8\xfe\xd4GY\xc0[\x89\x93\xc1\xaa\r\xac\xe1\x9f@\x89\x04V9\x18$O@\x00\x00\xe0\x94\xaf\xa59XnG\x19\x17J;F\xb9\xb3\xe6c\xa7\u0475\xb9\x87\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xa6\x94n\xff\xd5\xffS\x15O\x82\x01\x02S\xdfG\xae(\f\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xaf\xc8\xeb\u860b\xd4\x10Z\xccL\x01\x8eTj\x1e\x8f\x9cx\x88\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xaf\xcc}\xbb\x83V\xd8B\xd4:\xe7\xe2<\x84\"\xb0\"\xa3\b\x03\x8a\x06o\xfc\xbf\xd5\xe5\xa3\x00\x00\x00\u07d4\xaf\xd0\x19\xff6\xa0\x91U4ki\x97H\x15\xa1\xc9\x12\xc9\n\xa4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xda\xc5\xc1\xcbV\xe2E\xbfp3\x00f\xa8\x17\uabecL\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xdd\x1bxab\xb81~ \xf0\xe9y\xf4\xb2\xceHmv]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xf1\x04Z\xdf'\xa1\xaa2\x94a\xb2M\xe1\xba\u950ai\x8b\x89\x01\u03c4\xa3\n\n\f\x00\x00\u07d4\xaf\xf1\a\x96\v~\xc3N\u0590\xb6e\x02M`\x83\x8c\x19\x0fp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xaf\xf1\x1c\xcfi\x93\x04\xd5\xf5\x86*\xf8`\x83E\x1c&\xe7\x9a\xe5\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xaf\xf1at\nm\x90\x9f\xe9\x9cY\xa9\xb7yE\xc9\x1c\xc9\x14H\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\xaf\xfc\x99\xd5\ubd28O\xe7x\x8d\x97\xdc\xe2t\xb08$\x048\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xfe\xa0G7\"\xcb\u007f\x0e\x0e\x86\xb9\xe1\x18\x83\xbfB\x8d\x8dT\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xb0\t\x96\xb0Vn\xcb>rC\xb8\"y\x88\u0733R\xc2\x18\x99\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xb0\x1e8\x9b(\xa3\x1d\x8eI\x95\xbd\xd7\xd7\xc8\x1b\xee\xab\x1eA\x19\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0-\x06(s3EE\u03a2\x92\x18\xe4\x05w`Y\x0ft#\x89\xac\xb6\xa1\xc7\xd9:\x88\x00\x00\u07d4\xb0/\xa2\x93\x87\xec\x12\xe3\u007fi\"\xacL\xe9\x8c[\t\xe0\xb0\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb06\x91k\xda\u03d4\xb6\x9eZ\x8ae`)u\xeb\x02a\x04\u0749\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb0A1\x0f\xe9\xee\u0586L\xed\u053e\xe5\x8d\xf8\x8e\xb4\xed<\xac\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb0U\xafL\xad\xfc\xfd\xb4%\xcfe\xbad1\a\x8f\a\xec\u056b\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0W\x11S\xdb\x1cN\u05ec\xae\xfe\x13\xec\xdf\xdbr\xe7\xe4\xf0j\x8a\x11\f\xffyj\xc1\x95 \x00\x00\u07d4\xb0n\xab\t\xa6\x10\u01a5=V\xa9F\xb2\xc44\x87\xac\x1d[-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0rI\xe0U\x04J\x91U5\x9a@)7\xbb\xd9T\xfeH\xb6\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0v\x182\x8a\x90\x13\a\xa1\xb7\xa0\xd0X\xfc\xd5xn\x9er\xfe\x8a\x06gI]JC0\xce\x00\x00\u07d4\xb0y\xbbM\x98f\x14:m\xa7*\xe7\xac\x00\"\x06)\x811\\\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4\xb0{\xcc\bZ\xb3\xf7)\xf2D\x00Ah7\xb6\x996\xba\x88s\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xb0{\xcf\x1c\xc5\xd4F.Q$\xc9e\xec\xf0\xd7\r\xc2z\xcau\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xb0|\xb9\xc1$\x05\xb7\x11\x80uC\u0113De\xf8\u007f\x98\xbd-\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb0\u007f\u07af\xf9\x1dD`\xfel\xd0\u8870\xbd\x8d\"\xa6.\x87\x8a\x01\x1d%)\xf3SZ\xb0\x00\x00\xe0\x94\xb0\x9f\xe6\xd44\x9b\x99\xbc7\x93\x80T\x02-T\xfc\xa3f\xf7\xaf\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94\xb0\xaa\x00\x95\f\x0e\x81\xfa2\x10\x17>r\x9a\xaf\x16:'\xcdq\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb0\xacN\xfff\x80\xee\x14\x16\x9c\xda\xdb\xff\xdb0\x80Om%\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb0\xb3j\xf9\xae\xee\u07d7\xb6\xb0\"\x80\xf1\x14\xf19\x84\xea2`\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xb0\xb7y\xb9K\xfa<.\x1fX{\u031c~!x\x92\"7\x8f\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xb0\xba\xeb0\xe3\x13wlLm$t\x02\xbaAg\xaf\u0361\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb0\xbb)\xa8a\xea\x1dBME\xac\u053f\u0112\xfb\x8e\xd8\t\xb7\x89\x04V9\x18$O@\x00\x00\xe0\x94\xb0\xc1\xb1w\xa2 \xe4\x1f|t\xd0|\u0785i\xc2\x1cu\xc2\xf9\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xb0\xc7\xceL\r\xc3\u00bb\xb9\x9c\xc1\x85{\x8aE_a\x17\x11\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb0\xce\xf8\xe8\xfb\x89\x84\xa6\x01\x9f\x01\xc6y\xf2r\xbb\xe6\x8f\\w\x89\b=lz\xabc`\x00\x00\xe0\x94\xb0\xd3+\xd7\xe4\u6577\xb0\x1a\xa3\xd0Ao\x80U}\xba\x99\x03\x8a\x03s\x9f\xf0\xf6\xe6\x130\x00\x00\xe0\x94\xb0\xd3\u0247+\x85\x05n\xa0\xc0\xe6\xd1\xec\xf7\xa7~<\u6ac5\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xb0\xe4i\u0206Y8\x15\xb3IV8Y]\xae\xf0f_\xaeb\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xb0\xe7`\xbb\a\xc0\x81wsE\xe0W\x8e\x8b\u0218\"mN;\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\x040\x04\xec\x19A\xa8\xcfO+\x00\xb1W\x00\u076co\xf1~\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\x05\xdd=\x98|\xff\xd8\x13\xe9\xc8P\n\x80\xa1\xad%}V\u0189lj\xccg\u05f1\xd4\x00\x00\u07d4\xb1\x0f\u04a6G\x10/\x88\x1ft\xc9\xfb\xc3}\xa62\x94\x9f#u\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb1\x15\xee:\xb7d\x1e\x1a\xa6\xd0\x00\xe4\x1b\xfc\x1e\xc7!\f/2\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xb1\x17\x8a\xd4s\x83\xc3\x1c\x814\xa1\x94\x1c\xbc\xd4t\xd0bD\xe2\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb1\x17\x95\x89\u1779\xd4\x15W\xbb\xec\x1c\xb2L\xcc-\xec\x1c\u007f\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb1\x19\u76a9\xb9\x16Re\x81\xcb\xf5!\xefGJ\xe8M\xcf\xf4\x89O\xba\x10\x01\xe5\xbe\xfe\x00\x00\u07d4\xb1\x1f\xa7\xfb'\n\xbc\xdfZ.\xab\x95\xaa0\u013566\uffc9+^:\xf1k\x18\x80\x00\x00\u07d4\xb1$\xbc\xb6\xff\xa40\xfc\xae.\x86\xb4_'\xe3\xf2\x1e\x81\xee\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1)\xa5\xcbq\x05\xfe\x81\v\u0615\xdcr\x06\xa9\x91\xa4TT\x88\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\xe0\x94\xb1.\xd0{\x8a8\xadU\x066?\xc0z\vmy\x996\xbd\xaf\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb14\xc0\x049\x1a\xb4\x99(x3zQ\xec$/B(WB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1?\x93\xaf0\xe8\xd7fs\x81\xb2\xb9[\xc1\xa6\x99\xd5\xe3\xe1)\x89\x16\u012b\xbe\xbe\xa0\x10\x00\x00\u07d4\xb1E\x92\x85\x86>\xa2\xdb7Y\xe5F\u03b3\xfb7a\xf5\x90\x9c\x89<\xd7*\x89@\x87\xe0\x80\x00\u07d4\xb1F\xa0\xb9%U<\xf0o\xca\xf5J\x1bM\xfe\xa6!)\aW\x89lnY\xe6|xT\x00\x00\xe0\x94\xb1Jz\xaa\x8fI\xf2\xfb\x9a\x81\x02\u05bb\xe4\u010a\xe7\xc0o\xb2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1K\xbe\xffpr\tu\xdca\x91\xb2\xa4O\xf4\x9f&r\x87<\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94\xb1L\xc8\xde3\xd63\x826S\x9aH\x90 \xceFU\xa3+\u018a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1M\xdb\x03\x86\xfb`c\x98\xb8\xccGVZ\xfa\xe0\x0f\xf1\xd6j\x89\xa1*\xff\b>f\xf0\x00\x00\u07d4\xb1S\xf8(\xdd\amJ|\x1c%t\xbb-\xee\x1aD\xa3\x18\xa8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1T\x0e\x94\xcf\xf3F\\\xc3\u0447\xe7\xc8\u3f6f\x98FY\u2262\x15\xe4C\x90\xe33\x00\x00\u07d4\xb1X\xdbC\xfab\xd3\x0ee\xf3\u041b\xf7\x81\u01f6sr\uba89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1ar_\xdc\xed\xd1yR\xd5{#\xef([~K\x11i\xe8\x89\x02\xb6\xdf\xed6d\x95\x80\x00\u07d4\xb1dy\xba\x8e}\xf8\xf6>\x1b\x95\xd1I\u0345)\xd75\xc2\u0689-\xe3:j\xac2T\x80\x00\u07d4\xb1f\xe3}.P\x1a\xe7<\x84\x14+_\xfbZ\xa6U\xddZ\x99\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1\x83\xeb\xeeO\xcbB\xc2 \xe4wt\xf5\x9dlT\xd5\xe3*\xb1\x89V\xf7\xa9\xc3<\x04\xd1\x00\x00\u07d4\xb1\x88\a\x84D\x02~8g\x98\xa8\xaehi\x89\x19\xd5\xcc#\r\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xb1\x89j7\xe5\u0602Z-\x01vZ\xe5\xdeb\x99w\u0783R\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb1\x8eg\xa5\x05\n\x1d\xc9\xfb\x19\t\x19\xa3=\xa88\xefDP\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb1\xa2\xb4:t3\xdd\x15\v\xb8\"'\xedQ\x9c\u05b1B\u04c2\x89\x94mb\rtK\x88\x00\x00\u07d4\xb1\xc0\u040b6\xe1\x84\xf9\x95*@7\xe3\xe5:f}\a\nN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\xc3(\xfb\x98\xf2\xf1\x9a\xb6do\n|\x8cVo\xdaZ\x85@\x89\x87\x86x2n\xac\x90\x00\x00\xe0\x94\xb1\xc7Qxi9\xbb\xa0\xd6q\xa6w\xa1X\u01ab\xe7&^F\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xb1\xcdK\xdf\xd1\x04H\x9a\x02n\u025dYs\a\xa0By\xf1s\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb1\u03d4\xf8\t\x15\x05\x05_\x01\n\xb4\xba\u0196\xe0\xca\x0fg\xa1\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xb1\u05b0\x1b\x94\xd8T\xfe\x8b7J\xa6^\x89\\\xf2*\xa2V\x0e\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94\xb1\u06e5%\v\xa9bWU$n\x06yg\xf2\xad/\a\x91\u078a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xb1\xe2\u0755\xe3\x9a\xe9w\\U\xae\xb1?\x12\xc2\xfa#0S\xba\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\xe6\xe8\x10\xc2J\xb0H\x8d\xe9\xe0\x1eWH7\x82\x9f|w\u0409\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1\xe9\xc5\xf1\xd2\x1eauzk.\xe7Y\x13\xfcZ\x1aA\x01\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\x03\u049elV\xb9&\x99\u0139-\x1fo\x84d\x8d\xc4\u03fc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x16\xdcY\xe2|=ry\xf5\xcd[\xb2\xbe\u03f2`n\x14\u0649\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x1byy\xbf|\\\xa0\x1f\xa8-\xd6@\xb4\x1c9\xe6\u01bcu\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb2#\xbf\x1f\xbf\x80H\\\xa2\xb5V}\x98\xdb{\xc3SM\xd6i\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2-PU\xd9b15\x96\x1ej\xbd'<\x90\xde\xea\x16\xa3\xe7\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb2-\xad\xd7\xe1\xe0R2\xa927\xba\xed\x98\xe0\u07d2\xb1\x86\x9e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb24\x03_uDF<\xe1\xe2+\xc5S\x06F\x84\xc5\x13\xcdQ\x89\r\x89\xfa=\u010d\xcf\x00\x00\u07d4\xb2G\u03dcr\xecH*\xf3\xea\xa7Ye\x8fy=g\nW\f\x891p\x8a\xe0\x04T@\x00\x00\u07d4\xb2ghA\xee\x9f-1\xc1r\xe8#\x03\xb0\xfe\x9b\xbf\x9f\x1e\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb2y\xc7\xd3U\u0088\x03\x92\xaa\u046a!\xee\x86|;5\a\u07c9D[\xe3\xf2\uf1d4\x00\x00\u07d4\xb2|\x1a$ L\x1e\x11\x8du\x14\x9d\xd1\t1\x1e\a\xc0s\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb2\x81\x81\xa4X\xa4@\xf1\u01bb\x1d\xe8@\x02\x81\xa3\x14\x8fL5\x89\x14b\fW\xdd\xda\xe0\x00\x00\xe0\x94\xb2\x82E\x03|\xb1\x92\xf7W\x85\u02c6\xcb\xfe|\x93\r\xa2X\xb0\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xb2\x87\xf7\xf8\xd8\u00c7,\x1bXk\xcd}\n\xed\xbf~s'2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb2\x8b\xb3\x9f4fQ|\xd4o\x97\x9c\xf5\x96S\xee}\x8f\x15.\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xb2\x8d\xbf\xc6I\x98\x94\xf7:q\xfa\xa0\n\xbe\x0fK\xc9\u045f*\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\x96\x8f}5\xf2\b\x87\x161\xc6h{?=\xae\xab\xc6al\x89\bu\xc4\u007f(\x9fv\x00\x00\u07d4\xb2\x9f[|\x190\xd9\xf9z\x11^\x06pf\xf0\xb5M\xb4K;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb2\xa1D\xb1\xeag\xb9Q\x0f\"g\xf9\xda9\xd3\xf9=\xe2fB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xa2\xc2\x11\x16\x12\xfb\x8b\xbb\x8e}\xd97\x8dg\xf1\xa3\x84\xf0P\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb2\xa4\x98\xf0;\xd7\x17\x8b\u0627\x89\xa0\x0fR7\xafy\xa3\xe3\xf8\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\xb2\xaa/\x1f\x8e\x93\xe7\x97\x13\xd9,\xea\x9f\xfc\xe9\xa4\n\xf9\xc8-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xb5\x16\xfd\u045e\u007f8d\xb6\xd2\xcf\x1b%*AV\xf1\xb0;\x89\x02\xe9\x83\xc7a\x15\xfc\x00\x00\u07d4\xb2\xb7\u0374\xffKa\u0577\xce\v\"p\xbb\xb5&\x97C\xec\x04\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xbd\xbe\u07d5\x90\x84v\xd7\x14\x8a7\f\u0193t6(\x05\u007f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2\xbf\xaaX\xb5\x19l\\\xb7\xf8\x9d\xe1_G\x9d\x188\xdeq=\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xb2\xc5>\xfa3\xfeJ:\x1a\x80 \\s\xec;\x1d\xbc\xad\x06\x02\x89h\x01\u06b3Y\x18\x93\x80\x00\xe0\x94\xb2\xd06\x05\x15\xf1}\xab\xa9\x0f\u02ec\x82\x05\xd5i\xb9\x15\u05ac\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xb2\xd1\xe9\x9a\xf9\x121\x85\x8epe\xdd\x19\x183\r\xc4\xc7G\u054a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xb2\u066b\x96d\xbc\xf6\xdf <4o\u0192\xfd\x9c\xba\xb9 ^\x89\x17\xbex\x97`e\x18\x00\x00\u07d4\xb2\u0777\x86\xd3yN'\x01\x87\xd0E\x1a\xd6\u0237\x9e\x0e\x87E\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\xe0\x85\xfd\xdd\x14h\xba\aA['NsN\x11#\u007f\xb2\xa9\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\xe9\xd7k\xf5\x0f\xc3k\xf7\u04d4Kc\xe9\u0288\x9bi\x99h\x89\x902\xeab\xb7K\x10\x00\x00\xe0\x94\xb2\xf9\xc9r\xc1\xe9swU\xb3\xff\x1b0\x88s\x83\x969[&\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xb2\xfc\x84\xa3\xe5\nP\xaf\x02\xf9M\xa08>\u055fq\xff\x01\u05ca\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xb3\x05\v\xef\xf9\xde3\xc8\x0e\x1f\xa1R%\xe2\x8f,A:\xe3\x13\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xb3\x11\x96qJH\xdf\xf7&\xea\x943\xcd)\x12\xf1\xa4\x14\xb3\xb3\x89\x91Hx\xa8\xc0^\xe0\x00\x00\xe0\x94\xb3\x14[tPm\x1a\x8d\x04|\xdc\xdcU9*{SPy\x9a\x8a\x1bb)t\x1c\r=]\x80\x00\u07d4\xb3 \x83H6\xd1\xdb\xfd\xa9\xe7\xa3\x18M\x1a\xd1\xfdC \xcc\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3#\u073f.\xdd\xc58.\u4efb \x1c\xa3\x93\x1b\xe8\xb48\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb3$\x00\xfd\x13\xc5P\t\x17\xcb\x03{)\xfe\"\xe7\xd5\"\x8f-\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb3%gL\x01\xe3\xf7)\rR&3\x9f\xbe\xacg\xd2!'\x9f\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xb3(%\xd5\xf3\xdb$\x9e\xf4\xe8\\\xc4\xf31S\x95\x89v\u8f09\x1b-\xf9\xd2\x19\xf5y\x80\x00\u07d4\xb3*\xf3\xd3\xe8\xd0u4I&To.2\x88{\xf9;\x16\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3/\x1c&\x89\xa5\xcey\xf1\xbc\x97\v1XO\x1b\xcf\"\x83\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3<\x03#\xfb\xf9\xc2l\x1d\x8a\xc4N\xf7C\x91\u0400F\x96\u0689\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb3O\x04\xb8\xdbe\xbb\xa9\xc2n\xfcL\xe6\xef\xc5\x04\x81\xf3\xd6]\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3U}9\xb5A\x1b\x84D__T\xf3\x8fb\xd2qM\x00\x87\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xb3X\xe9|p\xb6\x05\xb1\xd7\xd7)\u07f6@\xb4<^\xaf\xd1\xe7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xb3^\x8a\x1c\r\xac~\x0ef\u06ecsjY*\xbdD\x01%a\x88\xcf\xceU\xaa\x12\xb3\x00\x00\xe0\x94\xb3fx\x94\xb7\x86<\x06\x8a\xd3D\x87?\xcf\xf4\xb5g\x1e\x06\x89\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3qw1\xda\xd6Q2\xday-\x87`0\xe4j\xc2'\xbb\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3s\x1b\x04l\x8a\u0195\xa1'\xfdy\u0425\xd5\xfaj\xe6\xd1.\x89lO\xd1\xee$nx\x00\x00\u07d4\xb3|+\x9fPc{\xec\xe0\u0295\x92\b\xae\xfe\xe6F;\xa7 \x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb3\x88\xb5\xdf\xec\xd2\xc5\u4d56W|d%V\xdb\xfe'xU\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3\x8cNS{]\xf90\xd6Zt\xd0C\x83\x1dkH[\xbd\xe4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb3\x919Wa\x94\xa0\x86a\x95\x15\x1f3\xf2\x14\n\xd1\u0306\u03ca\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb3\x9fL\x00\xb2c\f\xab}\xb7)^\xf4=G\xd5\x01\xe1\u007f\u05c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb3\xa6K\x11vrOT\t\xe1AJ5#f\x1b\xae\xe7KJ\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xb3\xa6\xbdA\xf9\xd9\xc3 \x1e\x05\v\x87\x19\x8f\xbd\xa3\x994\"\x10\x89\xc4a\xe1\xdd\x10)\xb5\x80\x00\u07d4\xb3\xa8\xc2\xcb}5\x8eW9\x94\x1d\x94[\xa9\x04Z\x02:\x8b\xbb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xaeT\xfb\xa0\x9d>\xe1\u05bd\xd1\xe9W\x929\x19\x02L5\xfa\x89\x03\x8d,\xeee\xb2*\x80\x00\u07d4\xb3\xb7\xf4\x93\xb4J,\x8d\x80\xecx\xb1\xcd\xc7Ze+s\xb0l\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb3\xc2(s\x1d\x18m-\xed[_\xbe\x00Lfl\x8eF\x9b\x86\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4\xb3\xc2``\x9b\x9d\xf4\t^l]\xff9\x8e\xeb^-\xf4\x99\x85\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xb3\xc6[\x84Z\xbal\xd8\x16\xfb\xaa\xe9\x83\xe0\xe4l\x82\xaa\x86\"\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xc9H\x11\xe7\x17[\x14\x8b(\x1c\x1a\x84[\xfc\x9b\xb6\xfb\xc1\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3\xe2\x0e\xb4\xde\x18\xbd\x06\x02!h\x98\x94\xbe\u5bb2SQ\xee\x89\x03\xfc\x80\xcc\xe5\x16Y\x80\x00\u07d4\xb3\xe3\xc49\x06\x98\x80\x15f\x00\u0089.D\x8dA6\xc9-\x9b\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xb3\xf8*\x87\xe5\x9a9\xd0\u0480\x8f\aQ\xebr\xc22\x9c\xdc\u014a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb3\xfc\x1dh\x81\xab\xfc\xb8\xbe\xcc\v\xb0!\xb8\xb7;r3\u0751\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xb4\x05\x94\xc4\xf3fN\xf8I\u0326\"{\x8a%\xaai\t%\xee\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb4\x1e\xaf]Q\xa5\xba\x1b\xa3\x9b\xb4\x18\u06f5O\xabu\x0e\xfb\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4$\u058d\x9d\r\x00\xce\xc1\x93\x8c\x85N\x15\xff\xb8\x80\xba\x01p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4%bs\x96+\xf61\xd0\x14U\\\xc1\xda\r\xcc1akI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb40g\xfep\u0675Ys\xbaX\xdcd\xdd\u007f1\x1eUBY\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb46W\xa5\x0e\xec\xbc0w\xe0\x05\xd8\xf8\xd9O7xv\xba\u0509\x01\xec\x1b:\x1f\xf7Z\x00\x00\u07d4\xb4<'\xf7\xa0\xa1\"\bK\x98\xf4\x83\x92%A\u0203l\xee,\x89&\u009eG\u0104L\x00\x00\xe0\x94\xb4A5v\x86\x9c\b\xf9Q*\xd3\x11\xfe\x92Y\x88\xa5-4\x14\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb4F\x05U$q\xa6\xee\xe4\u06abq\xff;\xb4\x13&\xd4s\xe0\x89-~=Q\xbaS\xd0\x00\x00\u07d4\xb4GW\x1d\xac\xbb>\u02f6\xd1\xcf\v\f\x8f88\xe5#$\xe2\x89\x01\xa3\x18f\u007f\xb4\x05\x80\x00\u07d4\xb4G\x83\xc8\xe5{H\a\x93\xcb\u059aE\xd9\f{O\fH\xac\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb4H\x15\xa0\xf2\x8eV\x9d\x0e\x92\x1aJ\u078f\xb2d%&Iz\x89\x03\x027\x9b\xf2\xca.\x00\x00\u07d4\xb4Im\xdb'y\x9a\"$W\xd79y\x11g(\u8844[\x89\x8d\x81\x9e\xa6_\xa6/\x80\x00\xe0\x94\xb4RL\x95\xa7\x86\x0e!\x84\x02\x96\xa6\x16$@\x19B\x1cJ\xba\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb4\\\xca\r6\x82fbh<\xf7\u0432\xfd\xach\u007f\x02\xd0\u010965\u026d\xc5\u07a0\x00\x00\u0794\xb4d@\u01d7\xa5V\xe0L}\x91\x04f\x04\x91\xf9k\xb0v\xbf\x88\xce\xc7o\x0eqR\x00\x00\u07d4\xb4j\u0386^,P\xeaF\x98\xd2\x16\xabE]\xffZ\x11\xcdr\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4m\x11\x82\xe5\xaa\xca\xff\r&\xb2\xfc\xf7/<\x9f\xfb\xcd\xd9}\x89\xaa*`<\xdd\u007f,\x00\x00\u07d4\xb4\x89!\xc9h}U\x10tE\x84\x93n\x88\x86\xbd\xbf-\xf6\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\x98\xbb\x0fR\x00\x05\xb6!jD%\xb7Z\xa9\xad\xc5-b+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb4\xb1\x1d\x10\x9f`\x8f\xa8\xed\xd3\xfe\xa9\xf8\xc3\x15d\x9a\xeb=\x11\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb4\xb1K\xf4TU\u042b\b\x035\x8bu$\xa7+\xe1\xa2\x04[\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb4\xb1\x85\xd9C\xee+Xc\x1e3\xdf\xf5\xafhT\xc1y\x93\xac\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xbf$\u02c3hk\xc4i\x86\x9f\xef\xb0D\xb9\tqi\x93\xe2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb4\xc2\x00@\xcc\u0661\xa3(=\xa4\u0522\xf3e\x82\bC\xd7\xe2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xc8\x17\x0f{*\xb56\xd1\u0662[\xdd :\xe1(\x8d\xc3\u0549\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4\xd8/.i\x94?}\xe0\xf5\xf7t8y@o\xac.\x9c\xec\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb4\xddF\f\xd0\x16rZd\xb2.\xa4\xf8\xe0n\x06gN\x03>\x8a\x01#\x1b\xb8t\x85G\xa8\x00\x00\u07d4\xb4\xddT\x99\xda\xeb%\a\xfb-\xe1\"\x97s\x1dLr\xb1k\xb0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb5\x04l\xb3\xdc\x1d\xed\xbd6E\x14\xa2\x84\x8eD\xc1\xdeN\xd1G\x8a\x03{}\x9b\xb8 @^\x00\x00\xe0\x94\xb5\b\xf9\x87\xb2\xde4\xaeL\xf1\x93\u0785\xbf\xf6\x13\x89b\x1f\x88\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb5\tU\xaan4\x15q\x98f\b\xbd\u0211\xc2\x13\x9fT\f\u07c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5\f\x14\x9a\x19\x06\xfa\xd2xo\xfb\x13Z\xabP\x177\xe9\xe5o\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\xb5\f\x9fW\x89\xaeD\xe2\xdc\xe0\x17\xc7\x14\xca\xf0\f\x83\x00\x84\u0089\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xb5\x14\x88,\x97\x9b\xb6B\xa8\r\u04c7T\u0578\xc8)m\x9a\a\x893\xc5I\x901r\f\x00\x00\u07d4\xb5\x1d\u0734\xddN\x8a\xe6\xbe3m\xd9eIq\xd9\xfe\xc8kA\x89\x16\xd4d\xf8=\u2500\x00\u07d4\xb5\x1eU\x8e\xb5Q/\xbc\xfa\x81\xf8\u043d\x93\x8cy\xeb\xb5$+\x89&\u009eG\u0104L\x00\x00\u07d4\xb5#\xff\xf9t\x98q\xb3S\x88C\x887\xf7\xe6\xe0\u07a9\xcbk\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb5-\xfbE\xde]t\xe3\xdf \x832\xbcW\x1c\x80\x9b\x8d\xcf2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb55\xf8\u06c7\x9f\xc6\u007f\xecX\x82J\\\xbenT\x98\xab\xa6\x92\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xb57\xd3jp\xee\xb8\xd3\xe5\xc8\r\xe8\x15\"\\\x11X\u02d2\u0109QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xb5;\xcb\x17L%\x184\x8b\x81\x8a\xec\xe0 6E\x96Fk\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5I>\xf1srDE\xcf4\\\x03]'\x9b\xa7Y\xf2\x8dQ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb5S\xd2]kT!\xe8\x1c*\xd0^\v\x8b\xa7Q\xf8\xf0\x10\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5Tt\xbaX\xf0\xf2\xf4\x0el\xba\xbe\xd4\xea\x17n\x01\x1f\xca\u0589j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5U\xd0\x0f\x91\x90\xcc6w\xae\xf3\x14\xac\xd7?\xdc99\x92Y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5W\xab\x949\xefP\xd27\xb5S\xf0%\b6JFj\\\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5jx\x00(\x03\x9c\x81\xca\xf3{gu\xc6 \u7195Gd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5j\u04ae\xc6\xc8\xc3\xf1\x9e\x15\x15\xbb\xb7\u0751(RV\xb69\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5t\x13\x06\n\xf3\xf1N\xb4y\x06_\x1e\x9d\x19\xb3uz\xe8\u0309\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xb5uI\xbf\xbc\x9b\xdd\x18\xf76\xb2&P\xe4\x8as`\x1f\xa6\\\x89\x18-~L\xfd\xa08\x00\x00\xe0\x94\xb5w\xb6\xbe\xfa\x05N\x9c\x04\x04a\x85P\x94\xb0\x02\xd7\xf5{\u05ca\x18#\xf3\xcfb\x1d#@\x00\x00\u07d4\xb5{\x04\xfa#\xd1 ?\xae\x06\x1e\xacEB\xcb`\xf3\xa5v7\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xb5\x87\f\xe3B\xd43C36s\x03\x8bGd\xa4n\x92_>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\x87\xb4J,\xa7\x9eK\xc1\u074b\xfd\xd4: qP\xf2\xe7\xe0\x89\",\x8e\xb3\xfff@\x00\x00\u07d4\xb5\x89gm\x15\xa0DH4B0\xd4\xff'\xc9^\xdf\x12,I\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb5\x8bR\x86^\xa5]\x806\xf2\xfa\xb2`\x98\xb3R\u0283~\x18\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xb5\x90k\n\u9881X\xe8\xacU\x0e9\xda\bn\xe3\x15v#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xa4g\x96\x85\xfa\x14\x19l.\x920\xc8\xc4\xe3;\xff\xbc\x10\xe2\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb5\xa5\x89\u075f@q\u06f6\xfb\xa8\x9b?]]\xae}\x96\xc1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xa6\x06\xf4\xdd\u02f9G\x1e\xc6\u007fe\x8c\xaf+\x00\xees\x02^\x89\xeaun\xa9*\xfct\x00\x00\u07d4\xb5\xadQW\u0769!\xe6\xba\xfa\u0350\x86\xaes\xae\x1fa\x1d?\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xad\xd1\u701f}\x03\x06\x9b\xfe\x88;\n\x93\"\x10\xbe\x87\x12\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\xba)\x91|x\xa1\xd9\xe5\xc5\xc7\x13fl\x1eA\x1d\u007fi:\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb5\xc8\x16\xa8(<\xa4\xdfh\xa1\xa7=c\xbd\x80&\x04\x88\xdf\b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xca\xc5\xed\x03G}9\v\xb2g\xd4\xeb\xd4a\x01\xfb\xc2\xc3\u0689\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xb5\u037cA\x15@oR\u5a85\xd0\xfe\xa1p\u0497\x9c\u01fa\x89Hz\x9a0E9D\x00\x00\u0794\xb5\u0653M{)+\xcf`;(\x80t\x1e\xb7`(\x83\x83\xa0\x88\xe7\xc2Q\x85\x05\x06\x00\x00\u07d4\xb5\xddP\xa1]\xa3Ih\x89\nS\xb4\xf1?\xe1\xaf\b\x1b\xaa\xaa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb5\xfa\x81\x84\xe4>\xd3\u0e2b\x91!da\xb3R\x8d\x84\xfd\t\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb5\xfb~\xa2\xdd\xc1Y\x8bfz\x9dW\xdd9\xe8Z8\xf3]V\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb6\x00B\x97R\xf3\x99\xc8\r\a4tK\xae\n\x02.\xcag\u0189\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x00\xfe\xabJ\xa9lSu\x04\xd9`W\"1Ai,\x19:\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb6\x04|\u07d3-\xb3\xe4\x04_Iv\x12#AS~\u0556\x1e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x15\xe9@\x14>\xb5\u007f\x87X\x93\xbc\x98\xa6\x1b=a\x8c\x1e\x8c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb6\x1c4\xfc\xac\xdap\x1aZ\xa8p$Y\u07b0\u4b83\x8d\xf8\x8a\aiZ\x92\xc2\ro\xe0\x00\x00\xe0\x94\xb60d\xbd3U\xe6\xe0~-7p$\x12Z3wlJ\xfa\x8a\b7Z*\xbc\xca$@\x00\x00\u07d4\xb65\xa4\xbcq\xfb(\xfd\xd5\xd2\xc3\"\x98:V\u0084Bni\x89\t79SM(h\x00\x00\u07d4\xb6F\u07d8\xb4\x94BtkaR\\\x81\xa3\xb0K\xa3\x10bP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb6YA\xd4LP\xd2Ffg\r6Gf\xe9\x91\xc0.\x11\u0089 \x86\xac5\x10R`\x00\x00\xe0\x94\xb6[\u05c0\xc7CA\x15\x16 'VR#\xf4NT\x98\xff\x8c\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xb6d\x11\xe3\xa0-\xed\xb7&\xfay\x10}\xc9\v\xc1\xca\xe6MH\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb6fu\x14.1\x11\xa1\xc2\xea\x1e\xb2A\x9c\xfaB\xaa\xf7\xa24\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb6o\x92\x12K^c\x03XY\xe3\x90b\x88i\xdb\u07a9H^\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xb6rsJ\xfc\xc2$\xe2\xe6\t\xfcQ\xd4\xf0Ys'D\xc9H\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xb6w\x1b\v\xf3B\u007f\x9a\xe7\xa9>|.a\xeec\x94\x1f\xdb\b\x8a\x03\xfb&i)T\xbf\xc0\x00\x00\u07d4\xb6z\x80\xf1p\x19}\x96\xcd\xccJ\xb6\u02e6'\xb4\xaf\xa6\xe1,\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xb6\x88\x99\xe7a\rL\x93\xa255\xbc\xc4H\x94[\xa1fo\x1c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb6\xa8)3\xc9\xea\u06bd\x98\x1e]m`\xa6\x81\x8f\xf8\x06\xe3k\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb6\xaa\u02cc\xb3\v\xab*\xe4\xa2BF&\xe6\xe1+\x02\xd0F\x05\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb6\xb3J&?\x10\xc3\xd2\xec\xeb\n\xccU\x9a{*\xb8\\\xe5e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xbf\xe1\xc3\xef\x94\xe1\x84o\xb9\xe3\xac\xfe\x9bP\xc3\xe9\x06\x923\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb6\xcdt2\xd5\x16\x1b\xe7\x97h\xadE\xde>Dz\a\x98 c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xceM\xc5`\xfcs\xdci\xfbzb\xe3\x88\xdb~r\xeavO\x894]\xf1i\xe9\xa3X\x00\x00\u07d4\xb6\xde\u03c2\x96\x98\x19\xba\x02\xde)\xb9\xb5\x93\xf2\x1bd\xee\xda\x0f\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\xe0\x94\xb6\xe6\xc3\"+ko\x9b\xe2\x87]*\x89\xf1'\xfbd\x10\x0f\xe2\x8a\x01\xb2\x1dS#\xcc0 \x00\x00\u07d4\xb6\xe8\xaf\xd9=\xfa\x9a\xf2\u007f9\xb4\xdf\x06\ag\x10\xbe\xe3\u07eb\x89\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xb6\xf7\x8d\xa4\xf4\xd0A\xb3\xbc\x14\xbc[\xa5\x19\xa5\xba\f2\xf1(\x8a$}\xd3,?\xe1\x95\x04\x80\x00\xe0\x94\xb6\xfb9xbP\b\x14&\xa3B\xc7\rG\xeeR\x1e[\xc5c\x8a\x03-&\xd1.\x98\v`\x00\x00\u07d4\xb7\r\xba\x93\x91h+J6Nw\xfe\x99%c\x01\xa6\xc0\xbf\x1f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x16#\xf3Q\a\xcft1\xa8?\xb3\xd2\x04\xb2\x9e\u0c67\xf4\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xb7\x1a\x13\xba\x8e\x95\x16{\x803\x1bR\u059e7\x05O\xe7\xa8&\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x1bb\xf4\xb4H\xc0+\x12\x01\xcb^9J\xe6'\xb0\xa5`\xee\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb7\" \xad\xe3d\xd06\x9f--\xa7\x83\xcaGM{\x9b4\u0389\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\xe0\x94\xb7#\r\x1d\x1f\xf2\xac\xa3f\x969\x14\xa7\x9d\xf9\xf7\xc5\xea,\x98\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xb7$\n\U000af433<\b\xae\x97d\x10>5\xdc\xe3c\x84(\x8a\x01\xca\xdd/\xe9hnc\x80\x00\u07d4\xb7'\xa9\xfc\x82\xe1\xcf\xfc\\\x17_\xa1HZ\x9b\xef\xa2\u037d\u04496'\xe8\xf7\x127<\x00\x00\u07d4\xb7,*\x01\x1c\r\xf5\x0f\xbbn(\xb2\n\xe1\xaa\xd2\x17\x88g\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb78-7\xdb\x03\x98\xacrA\f\xf9\x81=\xe9\xf8\xe1\uc36d\x8966\xc2^f\xec\xe7\x00\x00\u07d4\xb7;O\xf9\x9e\xb8\x8f\u061b\vmW\xa9\xbc3\x8e\x88o\xa0j\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xb7=jwU\x9c\x86\xcfet$)\x039K\xac\xf9n5p\x89\x04\xf1\xa7|\xcd;\xa0\x00\x00\u07d4\xb7Cr\xdb\xfa\x18\x1d\xc9$/9\xbf\x1d71\xdf\xfe+\xda\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7G\x9d\xabP\"\xc4\xd5\u06ea\xf8\xde\x17\x1bN\x95\x1d\u0464W\x89\x04V9\x18$O@\x00\x00\u07d4\xb7I\xb5N\x04\u0571\x9b\xdc\xed\xfb\x84\xdaw\x01\xabG\x8c'\xae\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb7N\xd2f`\x01\xc1c3\xcfz\xf5\x9eJ=H`6;\x9c\x89\n~\xbd^Cc\xa0\x00\x00\u07d4\xb7QI\xe1\x85\xf6\xe3\x92pWs\x90s\xa1\x82*\xe1\xcf\r\xf2\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xb7S\xa7_\x9e\xd1\v!d:\n=\xc0Qz\xc9k\x1a@h\x89\x15\xc8\x18[,\x1f\xf4\x00\x00\xe0\x94\xb7V\xadR\xf3\xbft\xa7\xd2LgG\x1e\b\x87Ci6PL\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb7Wn\x9d1M\xf4\x1e\xc5Pd\x94):\xfb\x1b\xd5\xd3\xf6]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb7X\x89o\x1b\xaa\x86O\x17\xeb\xed\x16\xd9S\x88o\xeeh\xaa\xe6\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb7h\xb5#N\xba:\x99h\xb3Mm\xdbH\x1c\x84\x19\xb3e]\x88\xcf\xceU\xaa\x12\xb3\x00\x00\u07d4\xb7\x82\xbf\xd1\xe2\xdep\xf4gdo\x9b\xc0\x9e\xa5\xb1\xfc\xf4P\xaf\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xb7\xa2\xc1\x03r\x8bs\x05\xb5\xaen\x96\x1c\x94\xee\x99\xc9\xfe\x8e+\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xb7\xa3\x1a|8\xf3\xdb\t2.\xae\x11\xd2'!A\xea\"\x99\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xa6y\x1c\x16\xebN!b\xf1Ke7\xa0+=c\xbf\xc6\x02\x89*Rc\x91\xac\x93v\x00\x00\u07d4\xb7\xa7\xf7|4\x8f\x92\xa9\xf1\x10\fk\xd8)\xa8\xacm\u007f\u03d1\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xb7\xc0w\x94ft\xba\x93A\xfbLtz]P\xf5\xd2\xdad\x15\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb7\xc0\xd0\xcc\vM4-@b\xba\xc6$\xcc\xc3\xc7\f\xc6\xda?\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb7\xc9\xf1+\x03\x8esCm\x17\xe1\xc1/\xfe\x1a\xec\u0373\xf5\x8c\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xb7\xcck\x1a\xcc2\u0632\x95\xdfh\xed\x9d^`\xb8\xf6L\xb6{\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xb7\xcehK\t\xab\xdaS8\x9a\x87Si\xf7\x19X\xae\xac;\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xd1.\x84\xa2\xe4\u01264Z\xf1\xdd\x1d\xa9\xf2PJ*\x99n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\xd2R\xee\x94\x02\xb0\xee\xf1D)_\x0ei\xf0\xdbXl\bq\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\xb7\u0541\xfe\n\xf1\xec8?;<Ag\x83\xf3\x85\x14jv\x12\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb7\xf6s\x14\u02c3.2\xe6;\x15\xa4\f\xe0\xd7\xff\xbd\xb2i\x85\x899\x82y&J\x81\x8d\x00\x00\xe0\x94\xb8\x04\x056\x95\x8dY\x98\xceK\xec\f\xfc\x9c\"\x04\x98\x98H\xe9\x8a\x05.\xa7\rI\x8f\xd5\n\x00\x00\u07d4\xb81\n\x16\xccj\xbcFP\aiK\x93\x0f\x97\x8e\xce\x190\xbd\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\xe0\x94\xb84\xac\xf3\x01S\"\u0143\x82\uecb7\x968\x90n\x88\xb6\u078a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\xe0\x94\xb8KS\u043b\x12VV\xcd\xdcR\xeb\x85*\xb7\x1drY\xf3\u054a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xb8L\x8b\x9f\xd3>\xce\x00\xaf\x91\x99\xf3\xcf_\xe0\xcc\xe2\x8c\xd1J\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xb8R\x18\xf3B\xf8\x01.\u069f'Nc\xce!R\xb2\xdc\xfd\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb8UP\x10wn<\\\xb3\x11\xa5\xad\xee\xfe\x9e\x92\xbb\x9ad\xb9\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb8_&\xdd\x0er\xd9\u009e\xba\xf6\x97\xa8\xafwG,+X\xb5\x8a\x02\x85\x19\xac\xc7\x19\fp\x00\x00\u07d4\xb8_\xf0>{_\xc4\"\x98\x1f\xae^\x99A\xda\xcb\u06bau\x84\x89Hz\x9a0E9D\x00\x00\xe0\x94\xb8f\a\x02\x1bb\xd3@\xcf&R\xf3\xf9_\xd2\xdcgi\x8b\u07ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb8}\xe1\xbc\u0492i\xd5!\xb8v\x1c\u00dc\xfbC\x19\xd2\xea\u054965\u026d\xc5\u07a0\x00\x00\u07d4\xb8\u007fSv\xc2\xde\vl\xc3\xc1y\xc0`\x87\xaaG=kFt\x89Hz\x9a0E9D\x00\x00\u07d4\xb8\x84\xad\u060d\x83\xdcVJ\xb8\xe0\xe0,\xbd\xb69\x19\xae\xa8D\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x8a7\xc2\u007fx\xa6\x17\xd5\xc0\x91\xb7\u0577:7a\xe6_*\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x94x\"\u056c\u79ad\x83&\xe9T\x96\"\x1e\v\xe6\xb7=\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\x9c\x03n\xd7\u0112\x87\x99!\xbeA\xe1\f\xa1i\x81\x98\xa7L\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xb8\x9fF2\xdfY\t\xe5\x8b*\x99d\xf7O\xeb\x9a;\x01\xe0\u014a\x04\x88u\xbc\xc6\xe7\xcb\xeb\x80\x00\u07d4\xb8\xa7\x9c\x84\x94^G\xa9\xc3C\x86\x83\u05b5\x84,\xffv\x84\xb1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\xa9y5'Y\xba\t\xe3Z\xa5\x93]\xf1u\xbf\xf6x\xa1\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\xab9\x80[\xd8!\x18Ol\xbd=$s4{\x12\xbf\x17\\\x89\x06hZ\xc1\xbf\xe3,\x00\x00\xe0\x94\xb8\xac\x11}\x9f\r\xba\x80\x90\x14E\x82<L\x9dO\xa3\xfe\xdcn\x8a\x03VLD'\xa8\xfc}\x80\x00\u07d4\xb8\xbc\x9b\xca\u007fq\xb4\xed\x12\xe6 C\x8db\x0fS\xc1\x144/\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb8\xbe\xddWjKL '\xdasZ[\xc3\xf53%*\x18\b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb8\xc2p=\x8c?/D\u0144\xbc\x10\xe7\xc0\xa6\xb6L\x1c\t~\x8a\x01,\u0778\xea\xd6\xf9\xf8\x00\x00\xe0\x94\xb8\xcc\x0f\x06\n\xad\x92\xd4\xeb\x8b6\xb3\xb9\\\xe9\xe9\x0e\xb3\x83\u05ca\x1f\u00c4+\xd1\xf0q\xc0\x00\x00\u07d4\xb8\xd2\xdd\xc6o0\x8c\x01X\xae<\xcb{\x86\x9f}\x19\x9d{2\x89-\xcb\xf4\x84\x0e\xca\x00\x00\x00\u07d4\xb8\u04c9\xe6$\xa3\xa7\xae\xbc\xe4\xd3\xe5\xdb\xdfl\xdc)\x93*\xed\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb8\xd51\xa9d\xbc\xea\x13\x82\x96 \xc0\xce\xd7$\"\xda\xdbL\u0289\t7\x15\xccZ\xb8\xa7\x00\x00\xe0\x94\xb8\xd5\xc3$\xa8 \x9d|\x80I\xd0\u052e\xde\x02\xba\x80\xabW\x8b\x8a\x03\x93\x92\x86)\xff\xf7^\x80\x00\xe0\x94\xb8\xf2\x00\x05\xb6\x13R\xff\xa7i\x9a\x1bR\xf0\x1fZ\xb3\x91g\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb8\xf3\aX\xfa\xa8\b\xdb\xc9\x19\xaa{B^\xc9\"\xb9;\x81)\x8966\u05ef^\u024e\x00\x00\u07d4\xb9\x01<Q\xbd\a\x8a\t\x8f\xae\x05\xbf*\xce\bI\u01be\x17\xa5\x89\x04V9\x18$O@\x00\x00\u07d4\xb9\x14Kg|-\xc6\x14\xce\xef\xdfP\x98_\x11\x83 \x8e\xa6L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb9\x16\xb1\xa0\x1c\xdcNV\xe7ew\x15\xea7\xe2\xa0\xf0\x87\xd1\x06\x89\x82n1\x81\xe0'\x06\x80\x00\u07d4\xb9\x1d\x9e\x91l\xd4\r\x19=\xb6\x0ey 'x\xa0\bw\x16\xfc\x89\x15\xf1\xba\u007fG\x16 \x00\x00\xe0\x94\xb9#\x1e\xb2n_\x9eKM(\x8f\x03\x90g\x04\xfa\xb9l\x87\u058a\x04+\xf0kx\xed;P\x00\x00\u07d4\xb9$'\xadux\xb4\xbf\xe2\n\x9fc\xa7\xc5Pm\\\xa1-\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\xb9'\xab\xd2\u048a\xaa\xa2M\xb3\x17x\xd2t\x19\u07ce\x1b\x04\xbb\x89\x01~\x11\u00a2oG\x80\x00\u07d4\xb9MG\xb3\xc0R\xa5\xe5\x0eBa\xae\x06\xa2\x0fE\xd8\xee\u25c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb9S\x96\u06aaI\r\xf2V\x93$\xfc\xc6b;\xe0R\xf12\u0289lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb9Y\xdc\xe0.\x91\xd9\xdb\x02\xb1\xbd\x8b}\x17\xa9\xc4\x1a\x97\xaf\t\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb9\\\x9b\x10\xaa\x98\x1c\xf4\xa6zq\xccR\xc5\x04\xde\xe8\xcfX\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb9\\\xfd\xa8F[\xa9\xc2f\x1b$\x9f\u00ebf\x1b\u07e3_\xf0\x89\x11JNy\xa2\xc2\x10\x80\x00\xe0\x94\xb9hA\u02bb\xc7\xdb\u059e\xf0\u03cf\x81\xdf\xf3\u0225\xe2\x15p\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xb9zg3\xcd_\xe9\x98d\xb3\xb34`\xd1g$4\xd5\xca\xfd\x89le\xbb\xaaF\xc2\u03c0\x00\xe0\x94\xb9\x81\xad^kw\x93\xa2?\xc6\xc1\xe8i.\xb2\x96]\x18\xd0\u068a\x02\x1e\x18\xd2\xc8!\xc7R\x00\x00\u07d4\xb9\x8c\xa3\x17\x85\xef\x06\xbeI\xa1\xe4~\x86O`\xd0v\xcaG.\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb9\x92\x0f\xd0\xe2\xc75\xc2VF<\xaa$\x0f\xb7\xac\x86\xa9=\xfa\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xb9\x92\xa9g0\x8c\x02\xb9\x8a\xf9\x1e\xe7`\xfd;kH$\xab\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb9\xa9\x85P\x1e\xe9P\x82\x9b\x17\xfa\xe1\xc9\xcf4\x8c1VT,\x89\x0f\xf1u\x17\u029ab\x00\x00\xe0\x94\xb9\xb0\xa3!\x9a2\x88\u0673[\t\x1b\x14e\v\x8f\xe2=\xce+\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xb9\xcfq\xb2&X>:\x92\x11\x03\xa51o\x85Zew\x9d\x1b\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xb9\xe9\f\x11\x92\xb3\xd5\xd3\xe3\xab\a\x00\xf1\xbfe_]\xd44z\x89\x1b\x19\xe5\vD\x97|\x00\x00\u07d4\xb9\xfd83\xe8\x8e|\xf1\xfa\x98y\xbd\xf5Z\xf4\xb9\x9c\xd5\xce?\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xba\x02I\xe0\x1d\x94[\xef\x93\xee^\xc6\x19%\xe0<\\\xa5\t\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\x0f9\x02;\xdb)\xeb\x18b\xa9\xf9\x05\x9c\xab]0nf/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x10\xf2vB\x90\xf8uCCr\xf7\x9d\xbfq8\x01\u02ac\x01\x893\xc5I\x901r\f\x00\x00\u07d4\xba\x151\xfb\x9ey\x18\x96\xbc\xf3\xa8\x05X\xa3Y\xf6\xe7\xc1D\xbd\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xba\x17m\xbe2I\xe3E\xcdO\xa9g\xc0\xed\x13\xb2LG\u5189\x15\xae\xf9\xf1\xc3\x1c\u007f\x00\x00\xe0\x94\xba\x1f\x0e\x03\u02da\xa0!\xf4\xdc\xeb\xfa\x94\xe5\u0209\xc9\u01fc\x9e\x8a\x06\u0450\xc4u\x16\x9a \x00\x00\u07d4\xba\x1f\xca\xf2#\x93~\xf8\x9e\x85gU\x03\xbd\xb7\xcaj\x92\x8bx\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\xe0\x94\xba$\xfcCgS\xa79\xdb,\x8d@\xe6\xd4\xd0LR\x8e\x86\xfa\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xbaB\xf9\xaa\xceL\x18E\x04\xab\xf5BWb\xac\xa2oq\xfb\u0709\x02\a\a}\u0627\x9c\x00\x00\u07d4\xbaF\x9a\xa5\u00c6\xb1\x92\x95\u0521\xb5G;T\x03S9\f\x85\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbad@\xae\xb3s{\x8e\xf0\xf1\xaf\x9b\f\x15\xf4\xc2\x14\xff\xc7\u03c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbam1\xb9\xa2a\xd6@\xb5\u07a5\x1e\xf2\x16,1\t\xf1\uba0a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xbap\xe8\xb4u\x9c\f<\x82\xcc\x00\xacN\x9a\x94\xdd[\xaf\xb2\xb8\x890C\xfa3\xc4\x12\xd7\x00\x00\u07d4\xba\x8ac\xf3\xf4\r\u4a03\x88\xbcP!/\xea\x8e\x06O\xbb\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x8eF\u059d.#C\xd8l`\xd8,\xf4, A\xa0\xc1\u0089\x05k\xc7^-c\x10\x00\x00\u07d4\xba\xa4\xb6L+\x15\xb7\x9f_ BF\xfdp\xbc\xbd\x86\xe4\xa9*\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xba\u0212,J\xcc},\xb6\xfdY\xa1N\xb4\\\xf3\xe7\x02!K\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xba\xd25\xd5\b]\u01f0h\xa6|A&w\xb0>\x186\x88L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\xd4B^\x17\x1c>r\x97^\xb4j\xc0\xa0\x15\xdb1Z]\x8f\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xba\xdc*\xef\x9fYQ\xa8\u05cak5\xc3\u0433\xa4\xe6\xe2\xe79\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xba\xdeCY\x9e\x02\xf8OL0\x14W\x1c\x97k\x13\xa3le\xab\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\xe9\xb8/r\x99c\x14\be\x9d\xd7N\x89\x1c\xb8\xf3\x86\x0f\xe5\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xbb\x03f\xa7\u03fd4E\xa7\r\xb7\xfeZ\xe3H\x85uO\xd4h\x8a\x01M\xef,B\xeb\xd6@\x00\x00\u07d4\xbb\aj\xac\x92 \x80i\xea1\x8a1\xff\x8e\xeb\x14\xb7\xe9\x96\xe3\x89\b\x13\xcaV\x90m4\x00\x00\u07d4\xbb\bW\xf1\xc9\x11\xb2K\x86\u0227\x06\x81G?\u6aa1\xcc\xe2\x89\x05k\xc7^-c\x10\x00\x00\u0794\xbb\x19\xbf\x91\u02edt\xcc\xeb_\x81\x1d\xb2~A\x1b\xc2\xea\x06V\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xbb'\u01a7\xf9\x10uGZ\xb2)a\x90@\xf8\x04\xc8\xeczj\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xbb7\x1cr\xc9\xf01l\xea+\xd9\xc6\xfb\xb4\a\x9ewT)\xef\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94\xbb;\x01\v\x18\xe6\xe2\xbe\x115\x87\x10&\xb7\xba\x15\xea\x0f\xde$\x8a\x02 |\x800\x9bwp\x00\x00\xe0\x94\xbb;\x90\x05\xf4o\xd2\xca;0\x16%\x99\x92\x8cw\xd9\xf6\xb6\x01\x8a\x01\xb1\xae\u007f+\x1b\xf7\xdb\x00\x00\u07d4\xbb?\xc0\xa2\x9c\x03Mq\b\x12\xdc\xc7u\xc8\u02b9\u048diu\x899\xd4\xe8D\xd1\xcf_\x00\x00\u07d4\xbbH\xea\xf5\x16\xce-\xec>A\xfe\xb4\xc6y\xe4\x95vA\x16O\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xbbKJKT\x80p\xffAC,\x9e\b\xa0\xcao\xa7\xbc\x9fv\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\xbbV\xa4\x04r<\xff \xd0hT\x88\xb0Z\x02\xcd\xc3Z\xac\xaa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbba\x8e%\"\x1a\u0667@\xb2\x99\xed\x14\x06\xbc94\xb0\xb1m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbba\xa0K\xff\xd5|\x10G\rE\u00d1\x03\xf6FP4v\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbbh#\xa1\xbd\x81\x9f\x13QU8&J-\xe0R\xb4D\"\b\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xbbl(J\xac\x8ai\xb7\\\u0770\x0f(\xe1EX;V\xbe\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xbbu\xcbPQ\xa0\xb0\x94KFs\xcau*\x97\x03\u007f|\x8c\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbb\x99;\x96\xee\x92Z\xda}\x99\u05c6W=?\x89\x18\f\u3a89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa3\u0180\x04$\x8eH\x95s\xab\xb2t6w\x06k$\u0227\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa4\xfa\xc3\xc4 9\xd8(\xe7B\xcd\xe0\xef\xff\xe7t\x94\x1b9\x89lj\u04c2\xd4\xfba\x00\x00\u07d4\xbb\xa8\xab\"\xd2\xfe\xdb\xcf\xc6?hL\b\xaf\xdf\x1c\x17P\x90\xb5\x89\x05_)\xf3~N;\x80\x00\u07d4\xbb\xa9v\xf1\xa1!_u\x12\x87\x18\x92\xd4_pH\xac\xd3V\u0209lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbb\xab\x00\v\x04\b\xed\x01Z7\xc0GG\xbcF\x1a\xb1N\x15\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xbb\xab\xf6d;\xebK\xd0\x1c\x12\v\xd0Y\x8a\t\x87\xd8)g\u0449\xb52\x81x\xad\x0f*\x00\x00\u07d4\xbb\xb4\xee\x1d\x82\xf2\xe1VD,\xc938\xa2\xfc(o\xa2\x88d\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\xbb\xb5\xa0\xf4\x80,\x86H\x00\x9e\x8ai\x98\xaf5,\u0787TO\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4\xbb\xb6C\xd2\x18{6J\xfc\x10\xa6\xfd6\x8d}U\xf5\r\x1a<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbb\xb8\xff\xe4?\x98\u078e\xae\x18F#\xaeRd\xe4$\u0438\u05c9\x05\xd5?\xfd\xe9(\b\x00\x00\u07d4\xbb\xbdn\u02f5u(\x91\xb4\u03b3\xcc\xe7:\x8fGpY7o\x89\x01\xf3\x99\xb1C\x8a\x10\x00\x00\u07d4\xbb\xbf9\xb1\xb6y\x95\xa4\"APO\x97\x03\u04a1JQV\x96\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbb\xc8\xea\xffc~\x94\xfc\u014d\x91<wp\u020f\x9bG\x92w\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbb\xc9\xd8\x11.[\xeb\x02\xdd)\xa2%{\x1f\xe6\x9b56\xa9E\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xcae\xb3&n\xa2\xfbs\xa0?\x92\x165\xf9\x12\u01fe\xde\x00\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbb\xf8B\x92\xd9T\xac\xd9\xe4\a/\xb8`\xb1PA\x06\xe0w\xae\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xbb\xf8Z\xaa\xa6\x83s\x8f\a;\xae\xf4J\xc9\xdc4\xc4\xc7y\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xf8am\x97rJ\xf3\xde\xf1e\xd0\xe2\x8c\u0689\xb8\x00\x00\x9a\x89\x06.\xf1.+\x17a\x80\x00\xe0\x94\xbb\xfe\n\x83\f\xac\xe8{r\x93\x99:~\x94\x96\xced\xf8\u350a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xbc\f\xa4\xf2\x17\xe0Ru6\x14\u05b0\x19\x94\x88$\xd0\xd8h\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xbc\x0e\x87E\u00e5ID\\+\xe9\x00\xf5#\x00\x80J\xb5b\x89\x8a\a\x02\x9b\xf5\xddLS\xb2\x80\x00\u07d4\xbc\x0f\x98Y\x8f\x88\x05j&3\x96 \x92;\x8f\x1e\xb0t\xa9\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbc\x16\t\u0585\xb7kH\uc41a\xa0\x99!\x90\"\xf8\x9b,\u0349@\x13\x8b\x91~\u07f8\x00\x00\xe0\x94\xbc\x17\x1eS\xd1z\u0276\x12A\xaeCm\xee\u01efE.t\x96\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xbc\x1b\x02\x1ax\xfd\xe4-\x9bR&\xd6\xec&\xe0j\xa3g\x00\x90\x89\x04V9\x18$O@\x00\x00\u07d4\xbc\x1e\x80\xc1\x81acB\xeb\xb3\xfb9\x92\a/\x1b(\xb8\x02\u0189\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbc#qH\xd3\f\x13\x83o\xfa,\xadR\x0e\xe4\xd2\xe5\xc4\xee\xff\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xbcF\xd57\xcf.\xdd@5e\xbd\xe73\xb2\xe3K!P\x01\xbd\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xbcNG\x15`\u025c\x8a*K\x1b\x1a\xd0\xc3j\xa6P+|K\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xbcb\xb3\tj\x91\xe7\xdc\x11\xa1Y*)=\xd2T!P\xd7Q\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbci\xa0\u04a3\x1c=\xbfz\x91\"\x11i\x01\xb2\xbd\xfe\x98\x02\xa0\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xbckX6K\xf7\xf1\x95\x1c0\x9e\f\xba\x05\x95 \x1c\xd7?\x9a\x89b@\x1aE~E\xf8\x00\x00\u07d4\xbcs\xf7\xb1\xca;w;4$\x9a\xda.,\x8a\x92t\xcc\x17\u0089lk\x93[\x8b\xbd@\x00\x00\u07d4\xbcz\xfc\x84wA\"t\xfc&]\xf1<\x05DsB}C\u0189\a\f\x95\x92\f\xe3%\x00\x00\xe0\x94\xbc\x96\u007f\xe4A\x8c\x18\xb9\x98X\x96m\x87\x06x\u0722\xb8\x88y\x8a\x01\xd9\xcb\u074d~\xd2\x10\x00\x00\u07d4\xbc\x99\x9e8\\Z\xeb\xca\xc8\xd6\xf3\xf0\xd6\rZ\xa7%3m\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbc\x9c\x95\u07eb\x97\xa5t\u03a2\xaa\x80;\\\xaa\x19|\xef\f\xff\x89\x16\u012b\xbe\xbe\xa0\x10\x00\x00\u07d4\xbc\x9e\x0e\xc6x\x8f}\xf4\xc7\xfc!\n\xac\xd2 \xc2~E\xc9\x10\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbc\xa3\xff\xd4h?\xba\n\u04fb\xc9\a4\xb6\x11\u069c\xfbE~\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbc\xae\u042c\xb6\xa7o\x11?|a5U\xa2\u00f0\xf5\xbf4\xa5\x89\n~\xbd^Cc\xa0\x00\x00\u07d4\xbc\xaf4y\x18\xef\xb2\xd6=\xde\x03\xe3\x92u\xbb\xe9}&\xdfP\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbc\xb4\"\xdcM\u04aa\xe9J\xba\xe9^\xa4]\xd1s\x1b\xb6\xb0\xba\x89\x18BO_\v\x1bN\x00\x00\u07d4\xbc\xbd1%.\u0088\xf9\x1e)\x8c\xd8\x12\xc9!`\xe783\x1a\x89k\x1b\xc2\xca\xc0\x9aY\x00\x00\u07d4\xbc\xbfk\xa1f\xe24\r\xb0R\xea#\u0480)\xb0\xdej\xa3\x80\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xbc\xc8E\x97\xb9\x1es\xd5\u0174\u059c\x80\xec\xf1F\x86\x0fw\x9a\x89\xedp\xb5\xe9\xc3\xf2\xf0\x00\x00\u07d4\xbc\xc9Y;-\xa6\xdfj4\xd7\x1b\x1a\xa3\x8d\xac\xf8v\xf9[\x88\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbc\xd9^\xf9bF+n\u07e1\x0f\u0687\xd7\"B\xfe>\xdb\\\x89\x12\x1d\x06\xe1/\xff\x98\x80\x00\u07d4\xbc\u065e\xdc!`\xf2\x10\xa0^:\x1f\xa0\xb0CL\xed\x00C\x9b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbc\u07ec\xb9\xd9\x02<4\x17\x18.\x91\x00\xe8\xea\x1d73\x93\xa3\x89\x034-`\xdf\xf1\x96\x00\x00\u07d4\xbc\xe1>\"2*\u03f3U\xcd!\xfd\r\xf6\f\xf9:\xdd&\u0189\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xbc\xe4\x04u\xd3E\xb0q-\xeep=\x87\xcdvW\xfc\u007f;b\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\xbc\xed\xc4&|\u02c9\xb3\x1b\xb7d\xd7!\x11q\x00\x8d\x94\xd4M\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbc\xfc\x98\xe5\xc8+j\xdb\x18\n?\xcb\x12\v\x9av\x90\xc8j?\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\x04;g\xc6>`\xf8A\xcc\xca\x15\xb1)\xcd\xfee\x90\xc8\xe3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd\x04\u007f\xf1\xe6\x9c\u01b2\x9a\xd2d\x97\xa9\xa6\xf2z\x90?\xc4\u0749.\xe4IU\b\x98\xe4\x00\x00\u07d4\xbd\b\xe0\xcd\xde\xc0\x97\xdby\x01\ua05a=\x1f\xd9\u0789Q\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbd\t\x12l\x89\x1cJ\x83\x06\x80Y\xfe\x0e\x15ylFa\xa9\xf4\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xbd\f\\\u05d9\xeb\u0106B\xef\x97\xd7N\x8eB\x90d\xfe\u4489\x11\xac(\xa8\xc7)X\x00\x00\u07d4\xbd\x17\xee\xd8+\x9a%\x92\x01\x9a\x1b\x1b<\x0f\xba\xd4\\@\x8d\"\x89\r\x8drkqw\xa8\x00\x00\u07d4\xbd\x18\x037\v\u0771)\xd29\xfd\x16\xea\x85&\xa6\x18\x8a\u5389\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbd+p\xfe\xcc7d\x0fiQO\xc7\xf3@IF\xaa\xd8k\x11\x89A\rXj \xa4\xc0\x00\x00\u07d4\xbd0\x97\xa7\x9b<\r.\xbf\xf0\xe6\xe8j\xb0\xed\xad\xbe\xd4p\x96\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xbd2]@)\xe0\xd8r\x9fm9\x9cG\x82$\xae\x9ez\xe4\x1e\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xbdC*9\x16$\x9bG$):\xf9\x14nI\xb8(\n\u007f*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbdG\xf5\xf7n;\x93\x0f\xd9HR\t\xef\xa0\xd4v=\xa0uh\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbdK`\xfa\xect\n!\xe3\a\x13\x91\xf9j\xa54\xf7\xc1\xf4N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xbdK\u0571\"\xd8\xef{|\x8f\x06gE\x03 \xdb!\x16\x14.\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbdQ\xee.\xa1C\u05f1\u05b7~~D\xbd\xd7\xda\x12\U00105b09G~\x06\u0332\xb9(\x00\x00\u07d4\xbdY\tN\aO\x8dy\x14*\xb1H\x9f\x14\x8e2\x15\x1f \x89\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbdZ\x8c\x94\xbd\x8b\xe6G\x06D\xf7\f\x8f\x8a3\xa8\xa5\\cA\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd^G:\xbc\xe8\xf9zi2\xf7|/\xac\xaf\x9c\xc0\xa0\x05\x14\x89<\x92X\xa1\x06\xa6\xb7\x00\x00\u07d4\xbd_F\u02ab,=K(\x93\x96\xbb\xb0\u007f <M\xa8%0\x89\x04V9\x18$O@\x00\x00\u07d4\xbdf\xff\xed\xb50\xea\v.\x85m\xd1*\xc2)l1\xfe)\xe0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbdg\xd2\xe2\xf8-\xa8\x86\x13A\xbc\x96\xa2\xc0y\x1f\xdd\xf3\x9e@\x89\n\xd7\xc0yG\xc8\xfb\x00\x00\u07d4\xbdjGMf4[\xcd\xd7\aYJ\xdbc\xb3\fx\"\xafT\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xbdr;(\x9asg\xb6\xec\xe2E^\xd6\x1e\xdbIg\n\xb9\u010a\x01\x0f\f\u07a1d!?\x80\x00\u07d4\xbds\xc3\xcb\xc2j\x17Pb\xea\x03 \u0744\xb2S\xbc\xe6CX\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94\xbdt\x19\xdc*\t\nF\xe2\x87=}\xe6\xea\xaa\u055e\x19\xc4y\x8a\x01p\xbc\xb6qu\x9f\b\x00\x00\u07d4\xbd\x87e\xf4\x12\x99\xc7\xf4y\x92<O\u044f\x12mr)\x04}\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbd\x93\xe5P@>*\x06\x11>\xd4\xc3\xfb\xa1\xa8\x91;\x19@~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\x9eV\xe9\x02\xf4\xbe\x1f\xc8v\x8d\x808\xba\xc6>*\u02ff\x8e\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xbd\xa4\xbe1~~K\xed\x84\xc0I^\xee2\xd6\a\xec8\xcaR\x89}2'yx\xefN\x80\x00\u07d4\xbd\xb6\v\x82:\x11s\xd4Z\a\x92$_\xb4\x96\xf1\xfd3\x01\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xba\xf6CM@\xd65[\x1e\x80\xe4\f\u012b\x9ch\xd9a\x16\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbd\xc0,\xd43\f\x93\xd6\xfb\xdaOm\xb2\xa8]\xf2/C\xc23\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc4aF+c\"\xb4b\xbd\xb3?\"y\x9e\x81\b\xe2A}\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xbd\xc79\xa6\x99p\v.\x8e,JL{\x05\x8a\x0eQ=\u07be\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc7Hs\xaf\x92+\x9d\xf4t\x85;\x0f\xa7\xff\v\xf8\xc8&\x95\x89\xd8\xc9F\x00c\xd3\x1c\x00\x00\u07d4\xbd\xca*\x0f\xf3E\x88\xafb_\xa8\xe2\x8f\xc3\x01Z\xb5\xa3\xaa\x00\x89~\xd7?w5R\xfc\x00\x00\u07d4\xbd\xd3%N\x1b:m\xc6\xcc,i}Eq\x1a\xca!\xd5\x16\xb2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbd\u07e3M\x0e\xbf\x1b\x04\xafS\xb9\x9b\x82IJ\x9e=\x8a\xa1\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xbd\xe4\xc7?\x96\x9b\x89\xe9\u03aef\xa2\xb5\x18DH\x0e\x03\x8e\x9a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbd\xe9xj\x84\xe7[H\xf1\x8erm\u05cdp\xe4\xaf>\xd8\x02\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xbd\xed\x11a/\xb5\xc6\u0699\xd1\xe3\x0e2\v\xc0\x99Tf\x14\x1e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xbd\xed~\a\xd0q\x1ehM\xe6Z\u0232\xabW\xc5\\\x1a\x86E\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xbd\xf6\x93\xf83\xc3\xfeG\x17S\x18G\x88\xebK\xfeJ\xdc?\x96\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\xf6\xe6\x8c\f\xd7X@\x80\xe8G\xd7,\xbb#\xaa\xd4j\xeb\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbe\n/8_\t\xdb\xfc\xe9g2\xe1+\xb4\n\xc3I\x87\x1b\xa8\x89WL\x11^\x02\xb8\xbe\x00\x00\u07d4\xbe\f*\x80\xb9\xde\bK\x17(\x94\xa7l\xf4szOR\x9e\x1a\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\x1c\xd7\xf4\xc4r\a\th\xf3\xbd\xe2h6k!\xee\xea\x83!\x89\xe9\x1a|\u045f\xa3\xb0\x00\x00\u07d4\xbe#F\xa2\u007f\xf9\xb7\x02\x04OP\r\xef\xf2\xe7\xff\xe6\x82EA\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbe$q\xa6\u007f`G\x91\x87r\xd0\xe3h9%^\xd9\u0591\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbe+\"\x80R7h\xea\x8a\xc3\\\xd9\xe8\x88\xd6\nq\x93\x00\u0509lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe+2nx\xed\x10\xe5P\xfe\xe8\xef\xa8\xf8\a\x03\x96R/Z\x8a\bW\xe0\xd6\xf1\xdav\xa0\x00\x00\xe0\x94\xbe0Zyn3\xbb\xf7\xf9\xae\xaee\x12\x95\x90f\xef\xda\x10\x10\x8a\x02M\xceT\xd3J\x1a\x00\x00\x00\u07d4\xbeG\x8e\x8e=\xdek\xd4\x03\xbb-\x1ce|C\x10\xee\x19'#\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\xbeN}\x98?.*ck\x11\x02\xecp9\xef\xeb\xc8B\u9349\x03\x93\xef\x1aQ'\xc8\x00\x00\u07d4\xbeO\xd0sap\"\xb6\u007f\\\x13I\x9b\x82\u007fv69\xe4\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbeRZ3\xea\x91aw\xf1r\x83\xfc\xa2\x9e\x8b5\v\u007fS\v\x89\x8f\x01\x9a\xafF\xe8x\x00\x00\u07d4\xbeS2/C\xfb\xb5\x84\x94\xd7\xcc\xe1\x9d\xda'+$P\xe8'\x89\n\xd7\u03afB\\\x15\x00\x00\u07d4\xbeS\x82F\xddNo\f \xbfZ\xd17<;F:\x13\x1e\x86\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbeZ`h\x99\x98c\x9a\xd7[\xc1\x05\xa3qt>\xef\x0fy@\x89\x1b2|s\xe1%z\x00\x00\u07d4\xbe\\\xba\x8d7By\x86\xe8\xca&\x00\xe8X\xbb\x03\xc3YR\x0f\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\xbe`\x03~\x90qJK\x91~a\xf1\x93\xd84\x90g\x03\xb1:\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbec:77\xf6\x849\xba\xc7\xc9\nR\x14 X\ue38ao\x894\n\xad!\xb3\xb7\x00\x00\x00\xe0\x94\xbee\x9d\x85\xe7\xc3O\x883\xea\u007fH\x8d\xe1\xfb\xb5\xd4\x14\x9b\xef\x8a\x01\xeb\xd2:\xd9\u057br\x00\x00\u07d4\xbes'M\x8cZ\xa4J<\xbe\xfc\x82c\xc3{\xa1!\xb2\n\u04c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbe\x86\u0430C\x84\x19\u03b1\xa081\x927\xbaR\x06\xd7.F\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xbe\x8d\u007f\x18\xad\xfe]l\xc7u9I\x89\xe1\x93\f\x97\x9d\x00}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\x91\x86\xc3JRQJ\xbb\x91\a\x86\x0fgO\x97\xb8!\xbd[\x89\x1b\xa0\x1e\xe4\x06\x03\x10\x00\x00\u07d4\xbe\x93W\x93\xf4[p\xd8\x04]&T\xd8\xdd:\xd2K[a7\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xbe\x98\xa7\u007f\xd4\x10\x97\xb3OY\xd7X\x9b\xaa\xd0!e\x9f\xf7\x12\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xbe\x9b\x8c4\xb7\x8e\xe9G\xff\x81G.\xdaz\xf9\xd2\x04\xbc\x84f\x89\b!\xab\rD\x14\x98\x00\x00\u07d4\xbe\xa0\r\xf1pg\xa4:\x82\xbc\x1d\xae\xca\xfbl\x140\x0e\x89\xe6\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xbe\xa0\xaf\xc9:\xae!\b\xa3\xfa\xc0Yb;\xf8o\xa5\x82\xa7^\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbe\xb35\x8cP\u03dfu\xff\xc7mD<,\u007fU\aZ\x05\x89\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xbe\xb4\xfd1UYC`E\u0739\x9dI\xdc\xec\x03\xf4\fB\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xc2\xe6\xde9\xc0|+\xaeUj\u03fe\xe2\xc4r\x8b\x99\x82\xe3\x89\x1f\x0f\xf8\xf0\x1d\xaa\xd4\x00\x00\u07d4\xbe\xc6d\x0fI\t\xb5\x8c\xbf\x1e\x80cB\x96\x1d`u\x95\tl\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\xc8\xca\xf7\xeeIF\x8f\xeeU.\xff:\xc5#N\xb9\xb1}B\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xce\xf6\x1c\x1cD+\xef|\xe0Ks\xad\xb2I\xa8\xba\x04~\x00\x896;V\u00e7T\xc8\x00\x00\u0794\xbe\xd4d\x9d\xf6F\u2052)\x03-\x88hUo\xe1\xe0S\u04c8\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xbe\xd4\xc8\xf0\x06\xa2|\x1e_|\xe2\x05\xdeu\xf5\x16\xbf\xb9\xf7d\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xbe\xe8\u0430\bB\x19T\xf9-\x00\r9\x0f\xb8\xf8\xe6X\xea\xee\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\xec\u05af\x90\f\x8b\x06J\xfc\xc6\a?-\x85\u055a\xf1\x19V\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe\xef\x94!8y\xe0&\"\x14+\xeaa)\tx\x93\x9a`\u05ca\x016\x85{2\xad\x86\x04\x80\x00\xe0\x94\xbe\xf0}\x97\xc3H\x1f\x9dj\xee\x1c\x98\xf9\xd9\x1a\x18\n2D+\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xbe\xfbD\x8c\f_h?\xb6~\xe5p\xba\xf0\xdbV\x86Y\x97Q\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbf\x05\a\f,4!\x93\x11\xc4T\x8b&\x14\xa48\x81\r\xedm\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbf\x05\xff^\xcf\r\xf2\u07c8wY\xfb\x82t\xd928\xac&}\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\xbf\t\xd7pH\xe2p\xb6b3\x0e\x94\x86\xb3\x8bC\xcdx\x14\x95\x8a\\S\x9b{\xf4\xff(\x80\x00\x00\u07d4\xbf\x17\xf3\x97\xf8\xf4o\x1b\xaeE\u0447\x14\x8c\x06\xee\xb9Y\xfaM\x896I\u0156$\xbb0\x00\x00\u07d4\xbf\x186A\xed\xb8\x86\xce`\xb8\x19\x02a\xe1OB\xd9<\xce\x01\x89\x01[5W\xf1\x93\u007f\x80\x00\u07d4\xbf*\xeaZ\x1d\xcfn\u04f5\xe829D\xe9\x83\xfe\xdf\u046c\xfb\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbf@\x96\xbcT}\xbf\xc4\xe7H\t\xa3\x1c\x03\x9e{8\x9d^\x17\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xbfI\xc1H\x981eg\u0637\t\xc2\xe5\x05\x94\xb3f\xc6\u04cc\x89'\xbf8\xc6TM\xf5\x00\x00\u07d4\xbfLs\xa7\xed\xe7\xb1d\xfe\a!\x14\x846T\xe4\xd8x\x1d\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfP\xce.&K\x9f\xe2\xb0h0az\xed\xf5\x02\xb25\x1bE\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfY\xae\xe2\x81\xfaC\xfe\x97\x19CQ\xa9\x85~\x01\xa3\xb8\x97\xb2\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbfh\u048a\xaf\x1e\xee\xfe\xf6F\xb6^\x8c\xc8\u0450\xf6\xc6\u069c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfi%\xc0\aQ\x00\x84@\xa6s\x9a\x02\xbf+l\u06ab^:\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfw\x01\xfcb%\u0561x\x15C\x8a\x89A\xd2\x1e\xbc]\x05\x9d\x89e\xea=\xb7UF`\x00\x00\u07d4\xbf\x8b\x80\x05\xd66\xa4\x96d\xf7Bu\xefBC\x8a\xcde\xac\x91\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\x92A\x8a\fl1$M\"\x02`\xcb>\x86}\u05f4\xefI\x89\x05i\x00\xd3<\xa7\xfc\x00\x00\u07d4\xbf\x9a\xcdDE\xd9\xc9UF\x89\u02bb\xba\xb1\x88\x00\xff\x17A\u008965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\x9f'\x1fz~\x12\xe3m\xd2\xfe\x9f\xac\xeb\xf3\x85\xfeaB\xbd\x89\x03f\xf8O{\xb7\x84\x00\x00\u07d4\xbf\xa8\xc8X\xdf\x10,\xb1$!\x00\x8b\n1\xc4\xc7\x19\n\xd5`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\xae\xb9\x10ga}\u03cbD\x17+\x02\xafaVt\x83]\xba\x89\b\xb5\x9e\x88H\x13\b\x80\x00\xe0\x94\xbf\xb0\xea\x02\xfe\xb6\x1d\xec\x9e\"\xa5\a\tY3\x02\x99\xc40r\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xbf\xbc\xa4\x18\xd3R\x9c\xb3\x93\b\x10b\x03*n\x11\x83\u01b2\u070a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xbf\xbe\x05\u831c\xbb\xcc\x0e\x92\xa4\x05\xfa\xc1\xd8]\xe2H\xee$\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xbf\xbf\xbc\xb6V\u0099+\xe8\xfc\u0782\x19\xfb\xc5J\xad\u055f)\x8a\x02\x1e\x18\xd2\xc8!\xc7R\x00\x00\u07d4\xbf\xc5z\xa6f\xfa\u239f\x10zI\xcbP\x89\xa4\xe2!Q\u074965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\u02d70$c\x04p\r\xa9\vAS\xe7\x11Ab.\x1cA\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbf\xd9<\x90\u009c\a\xbc_\xb5\xfcI\xae\xeaU\xa4\x0e\x13O5\x8a\x05\xed\xe2\x0f\x01\xa4Y\x80\x00\x00\xe0\x94\xbf\xe3\xa1\xfcn$\xc8\xf7\xb3%\x05`\x99\x1f\x93\u02e2\u03c0G\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xbf\u6f30\xf0\xc0xRd3$\xaa]\xf5\xfdb%\xab\xc3\u0289\x04\t\xe5+H6\x9a\x00\x00\u07d4\xbf\xf5\xdfv\x994\xb8\x94<\xa9\x13}\x0e\xfe\xf2\xfen\xbb\xb3N\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbf\xfbi)$\x1fx\x86\x93'>p\"\xe6\x0e>\xab\x1f\xe8O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x06O\x1d\x94t\xab\x91]V\x90l\x9f\xb3 \xa2\xc7\t\x8c\x9b\x89\x13h?\u007f<\x15\xd8\x00\x00\u07d4\xc0\a\xf0\xbd\xb6\xe7\x00\x92\x02\xb7\xaf>\xa9\t\x02i|r\x14\x13\x89\xa2\xa0\xe4>\u007f\xb9\x83\x00\x00\u07d4\xc0\n\xb0\x80\xb6C\xe1\u00ba\xe3c\xe0\u0455\xde.\xff\xfc\x1cD\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\xc0 wD\x9a\x13Jz\xd1\xef~M\x92z\xff\xec\ueb75\xae\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc0$q\xe3\xfc.\xa0S&\x15\xa7W\x1dI2\x89\xc1<6\xef\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0-n\xad\xea\xcf\x1bx\xb3\u0285\x03\\c{\xb1\xce\x01\xf4\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc03\xb12Z\n\xf4Tr\xc2U'\x85;\x1f\x1c!\xfa5\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc03\xbe\x10\xcbHa;\xd5\xeb\xcb3\xedI\x02\xf3\x8bX0\x03\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc04[3\xf4\x9c\xe2\u007f\xe8,\xf7\xc8M\x14\x1ch\xf5\x90\xcev\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0=\xe4*\x10\x9bezd\xe9\"$\xc0\x8d\xc1'^\x80\u0672\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0@i\u07f1\x8b\tlxg\xf8\xbe\xe7zm\xc7Gz\xd0b\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xc0A?Z|-\x9aK\x81\b(\x9e\xf6\xec\xd2qx\x15$\xf4\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xc0C\xf2E-\u02d6\x02\xefb\xbd6\x0e\x03=\xd29q\xfe\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0OK\xd4\x04\x9f\x04F\x85\xb8\x83\xb6)Y\xaec\x1df~5\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0V\u053dk\xf3\u02ec\xace\xf8\xf5\xa0\xe3\x98\v\x85'@\xae\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0[t\x06 \xf1s\xf1nRG\x1d\u00cb\x9cQJ\v\x15&\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\xc0i\xef\x0e\xb3B\x99\xab\xd2\xe3-\xab\xc4yD\xb2r3H$\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\xc0l\xeb\xbb\xf7\xf5\x14\x9af\xf7\xeb\x97k>G\xd5e\x16\xda/\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0r^\u00bd\xc3:\x1d\x82`q\u07a2\x9db\xd48Z\x8c%\x8a\b\xa0\x85\x13F:\xa6\x10\x00\x00\u07d4\xc0~8g\xad\xa0\x96\x80z\x05\x1al\x9c4\xcc;?J\xd3J\x89`\xf0f \xa8IE\x00\x00\u07d4\xc0\x89^\xfd\x05m\x9a:\x81\xc3\xdaW\x8a\xda1\x1b\xfb\x93V\u03c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x90\xfe#\xdc\xd8k5\x8c2\xe4\x8d*\xf9\x10$%\x9fef\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x9af\x17*\xea7\r\x9ac\xda\x04\xffq\xff\xbb\xfc\xff\u007f\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x9e<\xfc\x19\xf6\x05\xff>\xc9\xc9\xc7\x0e%@\xd7\xee\x97Cf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc0\xa0*\xb9N\xbeV\xd0E\xb4\x1bb\x9b\x98F.:\x02J\x93\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0\xa3\x93\b\xa8\x0e\x9e\x84\xaa\xaf\x16\xac\x01\xe3\xb0\x1dt\xbdk-\x89\afM\xddL\x1c\v\x80\x00\u07d4\xc0\xa6\u02edwi*=\x88\xd1A\xefv\x9a\x99\xbb\x9e<\x99Q\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xc0\xa7\xe8C]\xff\x14\xc2Uws\x9d\xb5\\$\u057fW\xa3\u064a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xc0\xae\x14\xd7$\x83./\xce'x\xde\u007f{\x8d\xaf{\x12\xa9>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0\xaf\xb7\u0637\x93p\xcf\xd6c\u018c\u01b9p*7\u035e\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0\xb0\xb7\xa8\xa6\xe1\xac\xdd\x05\xe4\u007f\x94\xc0\x96\x88\xaa\x16\u01ed\x8d\x89\x03{m\x02\xacvq\x00\x00\xe0\x94\xc0\xb3\xf2D\xbc\xa7\xb7\xde[H\xa5>\u06dc\xbe\xab\vm\x88\xc0\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0\xc0M\x01\x06\x81\x0e>\xc0\xe5J\x19\U000ab157\xe6\x9aW=\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xc0\xca2w\x94.tE\x87K\xe3\x1c\xeb\x90)rqO\x18#\x89\r\x8drkqw\xa8\x00\x00\u07d4\xc0\u02ed<\xcd\xf6T\xda\"\xcb\xcf\\xe\x97\xca\x19U\xc1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\xcb\xf6\x03/\xa3\x9e|F\xffw\x8a\x94\xf7\xd4E\xfe\"\xcf0\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\xc0\xe0\xb9\x03\b\x8e\fc\xf5=\xd0iWTR\xaf\xf5$\x10\u00c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc0\xe4W\xbdV\xec6\xa1$k\xfa20\xff\xf3\x8eY&\xef\"\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xc0\xed\rJ\xd1\r\xe045\xb1S\xa0\xfc%\xde;\x93\xf4R\x04\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xc0\xf2\x9e\xd0\af\x11\xb5\xe5^\x13\x05G\xe6\x8aH\xe2m\xf5\u4262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc1\x13(x#\\]\u06e5\xd9\xf3\"\x8bR6\xe4p \xdco\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1\x17\r\xba\xad\xb3\xde\xe6\x19\x8e\xa5D\xba\xec\x93%\x18`\xfd\xa5\x89A\rXj \xa4\xc0\x00\x00\xe0\x94\xc1&W=\x87\xb0\x17ZR\x95\xf1\xdd\a\xc5u\u03cc\xfa\x15\xf2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc1'\xaa\xb5\x90e\xa2\x86D\xa5k\xa3\xf1^.\xac\x13\xda)\x95\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xc1+\u007f@\u07da/{\xf9\x83f\x14\"\xab\x84\xc9\xc1\xf5\bX\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc1,\xfb{=\xf7\x0f\xce\xca\x0e\xde&5\x00\xe2xs\xf8\xed\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1/\x88\x1f\xa1\x12\xb8\x19\x9e\xcb\xc7>\xc4\x18W\x90\xe6\x14\xa2\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc18Lnq~\xbeK#\x01NQ\xf3\x1c\x9d\xf7\xe4\xe2[1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1C\x8c\x99\xddQ\xef\x1c\xa88j\xf0\xa3\x17\xe9\xb0AEx\x88\x89\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xc1c\x12(\xef\xbf*.:@\x92\xee\x89\x00\xc69\xed4\xfb\u02093\xc5I\x901r\f\x00\x00\u07d4\xc1u\xbe1\x94\xe6iB-\x15\xfe\xe8\x1e\xb9\xf2\xc5lg\xd9\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\x82v\x86\xc0\x16\x94\x85\xec\x15\xb3\xa7\xc8\xc0\x15\x17\xa2\x87M\xe1\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xc1\x8a\xb4g\xfe\xb5\xa0\xaa\xdf\xff\x91#\x0f\xf0VFMx\xd8\x00\x89lk\x93[\x8b\xbd@\x00\x00\u0794\xc1\x95\x05CUM\x8aq0\x03\xf6b\xbba,\x10\xadL\xdf!\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc1\xa4\x1aZ'\x19\x92&\xe4\xc7\xeb\x19\x8b\x03\x1bY\x19o\x98B\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xc1\xb2\xa0\xfb\x9c\xadE\xcdi\x91\x92\xcd'T\v\x88\xd38By\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1\xb2\xaa\x8c\xb2\xbfb\xcd\xc1:G\xec\xc4e\u007f\xac\xaa\x99_\x98\x8967\x93\xfa\x96\u6980\x00\u07d4\xc1\xb5\x00\x01\x1c\xfb\xa9]|\xd66\xe9^l\xbfagFK%\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\xb9\xa5pM5\x1c\xfe\x98?y\xab\xee\xc3\u06fb\xae;\xb6)\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xcb\xd2\xe23*RL\xf2\x19\xb1\r\x87\x1c\xcc \xaf\x1f\xb0\xfa\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xc1\xcd\xc6\x01\xf8\x9c\x04(\xb3\x13\x02\u0447\xe0\xdc\b\xad}\x1cW\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xc1\u052f8\xe9\xbay\x90@\x89HI\xb8\xa8!\x93u\xf1\xacx\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc1\xe1@\x9c\xa5,%CQ4\xd0\x06\u00a6\xa8T-\xfbrs\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xc1\xeb\xa5hJ\xa1\xb2L\xbac\x15\x02c\xb7\xa9\x13\x1a\xee\u008d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xec\x81\xdd\x12=K|-\u0674\xd48\xa7\a,\x11\u0707L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc1\xf3\x9b\xd3]\xd9\xce\xc37\xb9oG\xc6w\x81\x81`\xdf7\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xc1\xff\xad\a\u06d6\x13\x8cK*S\x0e\xc1\xc7\xde)\xb8\xa0Y,\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xc2\x1f\xa6d:\x1f\x14\xc0)\x96\xadqD\xb7Y&\xe8~\xcbK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc24\nL\xa9L\x96x\xb7IL<\x85%(\xed\xe5\xeeR\x9f\x89\x02\xa3k\x05\xa3\xfd|\x80\x00\u07d4\xc29\xab\u07ee>\x9a\xf5E\u007fR\xed+\x91\xfd\n\xb4\xd9\xc7\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2;/\x92\x1c\xe4\xa3z%\x9e\u4b4b!X\xd1]fOY\x89\x01`\x89\x95\xe8\xbd?\x80\x00\u07d4\xc2C\x99\xb4\xbf\x86\xf73\x8f\xbfd^;\"\xb0\u0dd79\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2L\u03bc#D\xcc\xe5d\x17\xfbhL\xf8\x16\x13\xf0\xf4\xb9\xbd\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc2Rf\xc7gf2\xf1>\xf2\x9b\xe4U\ud50a\xddVw\x92\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\\\xf8&U\f\x8e\xaf\x10\xaf\"4\xfe\xf9\x04\u0779R\x13\xbe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc2f?\x81E\xdb\xfe\xc6\xc6F\xfc\\I\x96\x13E\xde\x1c\x9f\x11\x89%g\xacp9+\x88\x00\x00\u07d4\xc2pEh\x854+d\vL\xfc\x1bR\x0e\x1aTN\xe0\xd5q\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc2sv\xf4]!\xe1^\xde;&\xf2e_\xce\xe0,\xcc\x0f*\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc2w\x97q\xf0Smy\xa8p\x8fi1\xab\xc4K05\u964a\x04<O\x83\x00\u0733H\x00\x00\u07d4\xc2\u007fN\b\t\x9d\x8c\xf3\x9e\xe1\x16\x01\x83\x8e\xf9\xfc\x06\xd7\xfcA\x89a\t=|,m8\x00\x00\u07d4\u0082\xe6\x99?\xbez\x91.\xa0G\x15?\xfd\x92t'\x0e([\x89\a\x96\v3\x12Gc\x80\x00\u07d4\u0083a\x88\u0662\x92S\xe0\xcb\xdaeq\xb0X\u0089\xa0\xbb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u00aat\x84~\x86\xed\xfd\xd3\xf3\xdb\"\xf8\xa2\x15/\xee\xe5\xb7\xf7\x89o\x11\x88\x86\xb7\x84\xa2\x00\x00\xe0\x94\u00b2\xcb\xe6[\xc6\xc2\xeez<u\xb2\xe4|\x18\x9c\x06.\x8d\x8b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\u00ba\xe4\xa23\xc2\xd8W$\xf0\u06be\xbd\xa0$\x9d\x83>7\u04ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc2\xc1>r\xd2h\xe7\x15\r\u01d9\xe7\xc6\xcf\x03\u0209T\xce\u05c9%\xf2s\x93=\xb5p\x00\x00\u07d4\xc2\xcb\x1a\xda]\xa9\xa0B8s\x81G\x93\xf1aD\xef6\xb2\xf3\x89HU~;p\x17\xdf\x00\x00\u07d4\xc2\xd1w\x8e\xf6\xee_\xe4\x88\xc1E\xf3Xkn\xbb\xe3\xfb\xb4E\x89>\x1f\xf1\xe0;U\xa8\x00\x00\xe0\x94\xc2\xd9\xee\xdb\xc9\x01\x92c\xd9\xd1l\u016e\a-\x1d=\xd9\xdb\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc2\xe0XJq4\x8c\xc3\x14\xb7; )\xb6#\v\x92\u06f1\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2\xe2\u0518\xf7\r\xcd\bY\xe5\v\x02:q\nmK!3\xbd\x8989\x11\xf0\f\xbc\xe1\x00\x00\u07d4\xc2\xed_\xfd\u046d\xd8U\xa2i/\xe0b\xb5\xd6\x18t#`\u0509A\rXj \xa4\xc0\x00\x00\u07d4\xc2\xee\x91\xd3\xefX\xc9\u0465\x89\x84N\xa1\xae1%\xd6\u017ai\x894\x95tD\xb8@\xe8\x00\x00\u07d4\xc2\xfa\xfd\xd3\n\xcbmg\x06\xe9)<\xb0&A\xf9\xed\xbe\a\xb5\x89Q\x00\x86\vC\x0fH\x00\x00\u07d4\xc2\xfd\v\xf7\xc7%\xef>\x04~Z\xe1\u009f\xe1\x8f\x12\xa7)\x9c\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\xfe}us\x1fcm\xcd\t\xdb\xda\x06q9;\xa0\xc8*}\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\xc3\x10z\x9a\xf32-R8\xdf\x012A\x911b\x959W}\x89\x1a\xb4\xe4d\xd4\x141\x00\x00\xe0\x94\xc3\x11\v\xe0\x1d\xc9sL\xfcn\x1c\xe0\u007f\x87\xd7}\x13E\xb7\xe1\x8a\x01\x0f\f\xe9I\xe0\x0f\x93\x00\x00\u07d4\xc3 8\xcaR\xae\xe1\x97E\xbe\\1\xfc\xdcT\x14\x8b\xb2\xc4\u0409\x02\xb5\xaa\xd7,e \x00\x00\u07d4\xc3%\xc3R\x80\x1b\xa8\x83\xb3\"l_\xeb\r\xf9\xea\xe2\xd6\xe6S\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xc3.\xc7\xe4*\xd1l\xe3\xe2UZ\xd4\xc5C\x06\xed\xa0\xb2gX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc32\xdfP\xb1<\x014\x90\xa5\xd7\xc7]\xbf\xa3f\u0687\xb6\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc3:\u0373\xba\x1a\xab'P{\x86\xb1]g\xfa\xf9\x1e\xcfb\x93\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3>\u0393Z\x8fN\xf98\xea~\x1b\xac\x87\u02d2]\x84\x90\u028a\a\x03\x8c\x16x\x1fxH\x00\x00\u07d4\xc3@\xf9\xb9\x1c&r\x8c1\xd1!\xd5\xd6\xfc;\xb5m=\x86$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3F\xcb\x1f\xbc\xe2\xab(]\x8eT\x01\xf4-\xd7#M7\xe8m\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\xe0\x94\xc3H=n\x88\xac\x1fJ\xe7<\xc4@\x8dl\x03\xab\xe0\xe4\x9d\u028a\x03\x99\x92d\x8a#\u0220\x00\x00\xe0\x94\xc3H\xfcZF\x13#\xb5{\xe3\x03\u02c96\x1b\x99\x19\x13\xdf(\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc3N;\xa12.\xd0W\x11\x83\xa2O\x94 N\xe4\x9c\x18fA\x89\x03'\xaf\uf927\xbc\x00\x00\xe0\x94\xc3[\x95\xa2\xa3s|\xb8\xf0\xf5\x96\xb3E$\x87+\xd3\r\xa24\x8a\x01\x98\xbe\x85#^-P\x00\x00\xe0\x94\xc3c\x1cv\x98\xb6\xc5\x11\x19\x89\xbfE''\xb3\xf99Zm\xea\x8a\x02C'X\x96d\x1d\xbe\x00\x00\u07d4\xc3l\vc\xbf\xd7\\/\x8e\xfb\x06\b\x83\xd8h\xcc\xcdl\xbd\xb4\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xc3uk\xcd\xcc~\xect\xed\x89j\xdf\xc35'Y0&n\b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u00c4\xacn\xe2|9\xe2\xf2x\xc2 \xbd\xfa[\xae\xd6&\xd9\u04c9 \x86\xac5\x10R`\x00\x00\u07d4\u00e0F\xe3\u04b2\xbfh\x14\x88\x82n2\xd9\xc0aQ\x8c\xfe\x8c\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\u00e9\"j\xe2u\xdf,\xab1+\x91\x10@cJ\x9c\x9c\x9e\xf6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u00f9(\xa7o\xadex\xf0O\x05U\xe69R\xcd!\xd1R\n\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xc2)s)\xa6\xfd\x99\x11~T\xfcj\xf3y\xb4\xd5VT~\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc3\xc3\xc2Q\rg\x80 HZcs]\x13\a\xecL\xa60+\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc3\xcbk6\xafD?,n%\x8bJ9U:\x81\x87G\x81\x1f\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\xc3\xdbVW\xbbr\xf1\rX\xf21\xfd\xdf\x11\x98\n\xffg\x86\x93\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xc3\u06df\xb6\xf4lH\n\xf3De\u05d7S\xb4\xe2\xb7Jg\u038a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc3\xddX\x908\x860;\x92\x86%%z\xe1\xa0\x13\xd7\x1a\xe2\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe0G\x1cd\xff5\xfaR2\xcc1!\xd1\u04cd\x1a\x0f\xb7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe2\f\x96\u07cdN8\xf5\v&Z\x98\xa9\x06\xd6\x1b\xc5\x1aq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\u31f0<\xe9\\\xcf\xd7\xfaQ\u0744\x01\x83\xbcCS(\t\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xf8\xf6r\x95\xa5\xcd\x04\x93d\xd0]#P&#\xa3\xe5.\x84\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc4\x01\xc4'\xcc\xcf\xf1\r\xec\xb8d /6\xf5\x80\x83\"\xa0\xa8\x89\xb4{Q\xa6\x9c\xd4\x02\x00\x00\u07d4\xc4\b\x8c\x02_>\x85\x01?T9\xfb4@\xa1s\x01\xe5D\xfe\x89~\t\xdbM\x9f?4\x00\x00\u07d4\xc4\x14a\xa3\u03fd2\u0246UU\xa4\x8117\xc0v1#`\x8965\xc6 G9\u0640\x00\u07d4\xc4 8\x8f\xbe\xe8J\xd6V\xddh\xcd\xc1\xfb\xaa\x93\x92x\v4\x89\n-\xcac\xaa\xf4\u0140\x00\u07d4\xc4\"P\xb0\xfeB\xe6\xb7\xdc\xd5\u0210\xa6\xf0\u020f__\xb5t\x89\b\x1e\xe4\x82SY\x84\x00\x00\u07d4\xc4-j\xebq\x0e:P\xbf\xb4Ml1\t)i\xa1\x1a\xa7\xf3\x89\b\"c\xca\xfd\x8c\xea\x00\x00\xe0\x94\xc4@\xc7\xca/\x96Kir\xeffJ\"a\xdd\xe8\x92a\x9d\x9c\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xc4K\xde\xc8\xc3l\\h\xba\xa2\xdd\xf1\xd41i2)rlC\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc4OJ\xb5\xbc`9|s~\xb0h3\x91\xb63\xf8<H\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc4R\xe0\xe4\xb3\u05ae\x06\xb86\xf02\xca\t\xdb@\x9d\xdf\xe0\xfb\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xc4Z\x1c\xa1\x03k\x95\x00A\x87\u036cD\xa3n3\xa9J\xb5\u00c9\r\xd0\x0fr\x03\x01\x88\x00\x00\u07d4\xc4]G\xab\f\x9a\xa9\x8a[\xd6-\x16\">\xa2G\x1b\x12\x1c\xa4\x89 .h\xf2\u00ae\xe4\x00\x00\u07d4\xc4h\x1es\xbb\x0e2\xf6\xb7& H1\xffi\xba\xa4\x87~2\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc4k\xbd\xefv\xd4\xca`\xd3\x16\xc0\u007f]\x1ax\x0e;\x16_~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc4}a\v9\x92P\xf7\x0e\xcf\x13\x89\xba\xb6),\x91&O#\x89\x0f\xa7\xe7\xb5\xdf<\xd0\x00\x00\u07d4\u0100;\xb4\a\xc7b\xf9\vu\x96\xe6\xfd\u1513\x1ev\x95\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0106Q\xc1\xd9\xc1k\xffL\x95T\x88l??&C\x1foh\x89#\xab\x95\x99\xc4?\b\x00\x00\u07d4\u0109\xc8?\xfb\xb0%*\xc0\xdb\xe3R\x12\x17c\x0e\x0fI\x1f\x14\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u010bi<\xac\xef\xdb\xd6\xcb]x\x95\xa4.1\x962~&\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0113H\x9eV\u00fd\xd8)\x00}\xc2\xf9VA)\x06\xf7k\xfa\x89\x02\xa7\x91H\x8eqT\x00\x00\u07d4\u0116\u02f0E\x9aj\x01`\x0f\u0149\xa5Z2\xb4T!\u007f\x9d\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4\u011c\xfa\xa9g\xf3\xaf\xbfU\x03\x10a\xfcL\xef\x88\xf8]\xa5\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0136\xe5\xf0\x9c\xc1\xb9\r\xf0x\x03\xce=M\x13vj\x9cF\xf4\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\u013e\xc9c\b\xa2\x0f\x90\u02b1\x83\x99\u0113\xfd=\x06Z\xbfE\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xc4\xc0\x1a\xfc>\x0f\x04R!\xda\x12\x84\u05c7\x85tD/\xb9\xac\x8a\x01\x92<h\x8bs\xab\x04\x00\x00\u07d4\xc4\xc1S\x18\xd3p\xc73\x18\xcc\x18\xbd\xd4f\u06eaLf\x03\xbf\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xc4\xc6\xcbr=\u05ef\xa7\xebSV\x15\xe5?<\xef\x14\xf1\x81\x18\x89lk\x8f\xce\r\x18y\x80\x00\u07d4\xc4\xccE\xa2\xb6<'\xc0\xb4B\x9eX\xcdB\xdaY\xbes\x9b\u058965\u026d\xc5\u07a0\x00\x00\u07d4\xc4\u03d3\x0e]\x11j\xb8\xd1;\x9f\x9a~\u012bP\x03\xa6\xab\u0789\x11X\xe4`\x91=\x00\x00\x00\u07d4\xc4\xd9\x16WNh\u011f~\xf9\xd3\xd8-\x168\xb2\xb7\xee\t\x85\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94\xc4\xda\u0168\xa0&O\xbc\x10U9\x1cP\x9c\xc3\xee!\xa6\xe0L\x8a\x01`k\u007f\xa09\xcet\x00\x00\u07d4\xc4\xdd\x04\x8b\xfb\x84\x0e+\xc8\\\xb5?\xcbu\xab\xc4C\xc7\xe9\x0f\x89\xc9q\xdc\a\xc9\u01d0\x00\x00\u07d4\xc4\xf2\x91;&\\C\x0f\xa1\xab\x8a\xdf&\xc33\xfc\x1d\x9bf\xf2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc4\xf7\xb1:\xc6\xd4\xebM\xb3\xd4\xe6\xa2R\xaf\x8a\a\xbdYW\u0689\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc4\xf7\xd2\xe2\xe2 \x84\xc4Op\xfe\xaa\xb6\xc3!\x05\xf3\xda7o\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xc4\xffo\xbb\x1f\t\xbd\x9e\x10+\xa03\xd66\xac\x1cL\x0fS\x04\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc4\xff\xad\xaa\xf2\x82?\xbe\xa7\xbf\xf7\x02\x02\x1b\xff\u0105>\xb5\u0249\x02J\x19\xc1\xbdo\x12\x80\x00\xe0\x94\xc5\x00\xb7 sN\xd2)8\u05cc^H\xb2\xba\x93g\xa5u\xba\x8a\a\x12\x9e\x1c\xdf7>\xe0\x00\x00\u07d4\xc5\x0f\xe4\x15\xa6A\xb0\x85lNu\xbf\x96\x05\x15D\x1a\xfa5\x8d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\x13L\xfb\xb1\xdfz \xb0\xedpWb.\xee\u0480\x94}\xad\x89\xcd\xff\x97\xfa\xbc\xb4`\x00\x00\xe0\x94\xc5\x17\xd01\\\x87\x88\x13\xc7\x17\u132f\xa1\xea\xb2eN\x01\u068a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc5\x18y\x9aY%Wb\x13\xe2\x18\x96\xe0S\x9a\xbb\x85\xb0Z\xe3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\"\xe2\x0f\xbf\x04\xed\u007fk\x05\xa3{G\x18\xd6\xfc\xe0\x14.\x1a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc5$\bmF\xc8\x11+\x12\x8b/\xafo|}\x81`\xa88l\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc5-\x1a\fs\u00a1\xbe\x84\x91Q\x85\xf8\xb3O\xaa\n\xdf\x1d\xe3\x89K\xe4\xea\xb3\xfa\x0f\xa6\x80\x00\xe0\x94\xc55\x94\xc7\u03f2\xa0\x8f(L\xc9\u05e6;\xbd\xfc\v1\x972\x8a\nk#(\xff:b\xc0\x00\x00\u07d4\xc57I(\xcd\xf1\x93pTC\xb1L\xc2\r\xa4#G<\xd9\u03c9\a}\x10P\x9b\xb3\xaf\x80\x00\u07d4\xc58\xa0\xff(*\xaa_Ku\u03f6,p\x03~\xe6}O\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5;P\xfd;+r\xbclC\v\xaf\x19JQU\x85\u04d8m\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc5=y\xf7\u02dbp\x95/\xd3\x0f\xceX\xd5K\x9f\vY\xf6G\x8a\x01\x13\xe2\xd6tCE\xf8\x00\x00\u07d4\xc5I\u07c3\xc6\xf6^\xec\x0f\x1d\u0260\x93J\\_:P\xfd\x88\x89\x9d\xc0\\\xce(\u00b8\x00\x00\u07d4\xc5P\x05\xa6\xc3~\x8c\xa7\xe5C\xce%\x99s\xa3\xca\u0396\x1aJ\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5U\xb91V\xf0\x91\x01#<o|\xf6\xeb<O\x19m3F\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc5ZkGa\xfd\x11\xe8\xc8_\x15\x17Mtv|\u063d\x9ah\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xc5nkb\xban@\xe5*\xab\x16}!\xdf\x02]\x00UuK\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5s\xe8A\xfa\b\x17J \x8b\x06\f\xcb{L\rv\x97\x12\u007f\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xc5v\x12\u0791\x11\fH.oP[\xcd#\xf3\xc5\x04}\x1da\x89\xc2\x12z\xf8X\xdap\x00\x00\u07d4\u01443\x99\xd1P\x06k\xf7\x97\x9c4\xba)F 6\x8a\xd7\xc0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u014b\x9c\xc6\x1d\xed\xbb\x98\xc3?\"M'\x1f\x0e\"\x8bX43\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\u014fb\xfe\xe9q\x1ej\x05\xdc\t\x10\xb6\x18B\n\xa1'\xf2\x88\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d4\u0153\xb5F\xb7i\x87\x10\xa2\x05\xadF\x8b,\x13\x15\"\x19\xa3B\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94\u0153\xd6\xe3}\x14\xb5fd:\xc4\x13_$<\xaa\a\x87\xc1\x82\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94\u0163\xb9\x8eE\x93\xfe\xa0\xb3\x8cOEZPe\xf0Q\xa2\xf8\x15\x8a\x04L\xf4h\xaf%\xbfw\x00\x00\u07d4\u0164\x8a\x85\x00\xf9\xb4\xe2/\x0e\xb1loFIhvt&}\x89,\x0e\xc5\x03\x85\x04>\x80\x00\xe0\x94\u0166)\xa3\x96%R\u02ce\xde\u0609cj\xaf\xbd\f\x18\xcee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u016e\x86\xb0\xc6\xc7\xe3\x90\x0f\x13h\x10\\VS\u007f\xaf\x8dt>\x89\n1\x06+\xee\xedp\x00\x00\u07d4\u0170\t\xba\xea\xf7\x88\xa2v\xbd5\x81:\xd6[@\v\x84\x9f;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0175l\xd24&|(\xe8\x9cok\"f\xb0\x86\xa1/\x97\f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xc5\u01a4\x99\x8a3\xfe\xb7dCz\x8b\xe9)\xa7;\xa3J\ad\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\xe0\x94\xc5\xc7=a\xcc\xe7\xc8\xfeL\x8f\xce)\xf3\x90\x92\xcd\x19>\x0f\xff\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc5\xc7Y\vV!\xec\xf85\x85\x88\u079bh\x90\xf2baC\U000498a1]\tQ\x9b\xe0\x00\x00\u07d4\xc5\xcd\xce\xe0\xe8]\x11}\xab\xbfSj?@i\xbfD?T\xe7\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xc5\u050c\xa2\xdb/\x85\xd8\xc5U\xcb\x0e\x9c\xfe\x82i6x?\x9e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc5\xde\x12\x03\xd3\xcc,\xea1\xc8.\xe2\xdeY\x16\x88\a\x99\xea\xfd\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc5\xe4\x88\xcf+Vw\x939q\xf6L\xb8 -\xd0WR\xa2\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\xe8\x12\xf7o\x15\xf2\xe1\xf2\xf9\xbcH#H<\x88\x04cog\x89\x03\xf5\x14\x19:\xbb\x84\x00\x00\u07d4\xc5\u94d34\xf1%.\u04ba&\x81D\x87\xdf\u0498+1(\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\xc5\xebB)^\x9c\xad\xea\xf2\xaf\x12\xde\u078a\x8dS\xc5y\xc4i\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xc5\xed\xbb\xd2\xca\x03WeJ\xd0\xeaG\x93\xf8\xc5\xce\xcd0\xe2T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc5\xf6K\xab\xb7\x031B\xf2\x0eF\u05eab\x01\xed\x86\xf6q\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\xf6\x87qrF\u068a \r \xe5\u9f2c`\xb6\u007f8a\x89\x01\x8d\x99?4\xae\xf1\x00\x00\u07d4\xc6\x04[<5\vL\xe9\xca\fkuO\xb4\x1ai\xb9~\x99\x00\x892$\xf4'#\xd4T\x00\x00\u07d4\xc6\v\x04eN\x00;F\x83\x04\x1f\x1c\xbdk\u00cf\xda|\xdb\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x14F\xb7T\xc2N;\x16B\xd9\xe5\x17e\xb4\xd3\xe4k4\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x18R\x13!\xab\xaf[&Q:J\x95(\bo\"\n\xdco\x89\x01v\xb3D\xf2\xa7\x8c\x00\x00\u07d4\xc6#FW\xa8\a8A&\xf8\x96\x8c\xa1p\x8b\xb0{\xaaI<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6%\xf8\u024d'\xa0\x9a\x1b\u02bdQ(\xb1\u00a9HV\xaf0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc65^\xc4v\x8cp\xa4\x9a\xf6\x95\x13\u0343\xa5\xbc\xa7\xe3\xb9\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc6:\xc4\x17\x99.\x9f\x9b`8n\xd9S\xe6\xd7\xdf\xf2\xb0\x90\xe8\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xc6<\u05c8!\x18\xb8\xa9\x1e\aML\x8fK\xa9\x18Q0;\x9a\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xc6R\x87\x1d\x19$\"\u01bc#_\xa0c\xb4J~\x1dC\u3149\bg\x0e\x9e\xc6Y\x8c\x00\x00\xe0\x94\xc6gD\x1e\u007f)y\x9a\xbaadQ\xd5;?H\x9f\x9e\x0fH\x8a\x02\xf2\x9a\xceh\xad\u0740\x00\x00\u07d4\xc6j\xe4\xce\xe8\u007f\xb352\x19\xf7\u007f\x1dd\x86\u0140(\x032\x89\x01\x9a\x16\xb0o\xf8\xcb\x00\x00\u07d4\xc6t\xf2\x8c\x8a\xfd\a?\x8by\x96\x91\xb2\xf0XM\xf9B\xe8D\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0197\xb7\x04w\u02b4.+\x8b&f\x81\xf4\xaesu\xbb%A\x8a\x01.W2\xba\xba\\\x98\x00\x00\u07d4\u019b\x85U9\xce\x1b\x04qG(\xee\xc2Z7\xf3g\x95\x1d\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u019b\xe4@\x13Mb\x80\x98\x01D\xa9\xf6M\x84t\x8a7\xf3I\x89&\u009eG\u0104L\x00\x00\u07d4\u019df<\x8d`\x90\x83\x91\xc8\xd26\x19\x153\xfd\xf7wV\x13\x89\x1aJ\xba\"\\ t\x00\x00\u0794\u01a2\x86\xe0e\xc8_:\xf7H\x12\xed\x8b\u04e8\xce]%\xe2\x1d\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\u01a3\x0e\xf5\xbb3 \xf4\r\xc5\xe9\x81#\rR\xae:\xc1\x93\"\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\u01ae(}\xdb\xe1\x14\x9b\xa1m\xdc\xcaO\xe0j\xa2\uaa48\xa9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xc7\xc1\x917\x98\x97\u075c\x9d\x9a3\x83\x9cJ_b\xc0\x89\r\x89\xd8\xd8T\xb2$0h\x80\x00\xe0\x94\xc6\xcdh\xec56,Z\xd8L\x82\xadN\xdc#!%\x91-\x99\x8a\x05\xe0T\x9c\x962\xe1\xd8\x00\x00\u07d4\xc6\u0615N\x8f?\xc53\xd2\xd20\xff\x02\\\xb4\xdc\xe1O4&\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xdb\u06de\xfd^\xc1\xb3xn\x06q\xeb\"y\xb2S\xf2\x15\xed\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc6\xdf u\xeb\xd2@\xd4Hi\u00bek\u07c2\xe6=N\xf1\xf5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6\xe2\xf5\xaf\x97\x9a\x03\xfdr:\x1bn\xfar\x83\x18\u03dc\x18\x00\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xc6\xe3$\xbe\xeb[6v^\xcdFB`\xf7\xf2`\x06\xc5\xc6.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\xe4\xcc\fr\x83\xfc\x1c\x85\xbcH\x13\xef\xfa\xafr\xb4\x98#\xc0\x89\x0f\x03\x1e\xc9\xc8}\xd3\x00\x00\xe0\x94\xc6\xee5\x93B)i5)\xdcA\u067bq\xa2IfX\xb8\x8e\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xc6\xfb\x1e\xe3t\x17\u0400\xa0\xd0H\x92;\u06ba\xb0\x95\xd0w\u0189\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc7\x05'\xd4D\u0110\xe9\xfc?\\\xc4Nf\xebO0k8\x0f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\r\x85mb\x1e\xc1E0<\nd\x00\xcd\x17\xbb\xd6\xf5\xea\xf7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc7\x0f\xa4Uv\xbf\x9c\x86_\x988\x93\x00,AI&\xf6\x10)\x89\x15\xb4\xaa\x8e\x97\x02h\x00\x00\u07d4\xc7\x11E\xe5)\u01e7\x14\xe6y\x03\xeeb\x06\xe4\xc3\x04+g'\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xc7\x1b*=q5\u04a8_\xb5\xa5q\u073ei^\x13\xfcC\u034965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\x1f\x1du\x87?3\u0732\xddK9\x87\xa1-\a\x91\xa5\xce'\x897\b\xba\xed=h\x90\x00\x00\u07d4\xc7\x1f\x92\xa3\xa5J{\x8c/^\xa4C\x05\xfc\u02c4\xee\xe21H\x89\x02\xb5\x9c\xa11\xd2\x06\x00\x00\u07d4\xc7!\xb2\xa7\xaaD\xc2\x12\x98\xe8P9\xd0\x0e.F\x0eg\v\x9c\x89\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xc7,\xb3\x01%\x8e\x91\xbc\b\x99\x8a\x80]\u0452\xf2\\/\x9a5\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc76\x8b\x97\t\xa5\xc1\xb5\x1c\n\xdf\x18ze\xdf\x14\xe1+}\xba\x8a\x02\x02o\xc7\u007f\x03\u5b80\x00\u07d4\xc79%\x9e\u007f\x85\xf2e\x9b\xef_`\x9e\xd8k=Yl \x1e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc7>!\x12(\"\x15\xdc\ab\xf3+~\x80}\xcd\x1az\xae>\x8a\x01v\f\xbcb;\xb3P\x00\x00\xe0\x94\xc7If\x80B\xe7\x11#\xa6H\x97^\b\xedc\x82\xf8>\x05\xe2\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xc7J9\x95\xf8\a\xde\x1d\xb0\x1a.\xb9\xc6.\x97\xd0T\x8fio\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7Pl\x10\x19\x12\x1f\xf0\x8a,\x8c\x15\x91\xa6^\xb4\xbd\xfbJ?\x89 \x86\xac5\x10R`\x00\x00\u07d4\xc7\\7\xce-\xa0k\xbc@\b\x11Y\u01ba\x0f\x97n9\x93\xb1\x89:y#\x15\x1e\xcfX\x00\x00\u07d4\xc7]\"Y0j\xec}\xf0\"v\x8ci\x89\x9ae!\x85\xdb\u0109\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7`\x97\x1b\xbc\x18\x1cj|\xf7tA\xf2BG\u045c\xe9\xb4\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc7a0\xc7<\xb9!\x028\x02\\\x9d\xf9]\v\xe5J\xc6\u007f\xbe\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xc7e\xe0\x04v\x81\tG\x81j\xf1B\xd4m.\u7f28\xccO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc7g^VG\xb9\xd8\xda\xf4\xd3\xdf\xf1\xe5R\xf6\xb0qT\xac8\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xc7{\x01\xa6\xe9\x11\xfa\x98\x8d\x01\xa3\xab3dk\xee\xf9\xc18\xf3\x89'\x1bo\xa5\xdb\xe6\xcc\x00\x00\u07d4\u01c3z\u0420\xbf\x14\x18i7\xac\xe0lUF\xa3j\xa5OF\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01d8\x06\x03+\xc7\xd8(\xf1\x9a\u01a6@\u018e=\x82\x0f\xa4B\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u01d9\xe3N\x88\xff\x88\xbe}\xe2\x8e\x15\xe4\xf2\xa6=\v3\xc4\u02c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\u01ddPb\u01d6\xddwa\xf1\xf1>U\x8ds\xa5\x9f\x82\xf3\x8b\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\u01e0\x18\xf0\x96\x8aQ\xd1\xf6`<\\I\xdcT[\xcb\x0f\xf2\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01ef\xf9\x19)yt\x89UZ/\xf1\xd1M\\iZ\x10\x83U\x8965\u026d\xc5\u07a0\x00\x00\u0794\u01f1\xc8>c ?\x95G&>\xf6(.}\xa3;n\xd6Y\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\u01f3\x9b\x06\x04Q\x00\f\xa1\x04\x9b\xa1T\xbc\xfa\x00\xff\x8a\xf2b\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\u01ff\x17\xc4\xc1\x1f\x98\x94\x1fP~w\bO\xff\xbd-\xbd=\xb5\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u01ff.\xd1\xed1)@\xeej\xde\xd1Qn&\x8eJ`HV\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc7\xd4O\xe3,\u007f\x8c\xd5\xf1\xa9t'\xb6\xcd:\xfc\x9eE\x02>\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xc7\xd5\xc7\x05@\x81\xe9\x18\xech{Z\xb3n\x97=\x18\x13)5\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xc7\xde^\x8e\xaf\xb5\xf6+\x1a\n\xf2\x19\\\xf7\x93\u01c9L\x92h\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\xe30\xcd\f\x89\n\u025f\xe7q\xfc\xc7\xe7\xb0\t\xb7A=\x8a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\xea\xc3\x1a\xbc\xe6\xd5\xf1\u07a4\"\x02\xb6\xa6t\x15=\xb4z)\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc7\xecb\xb8\x04\xb1\xf6\x9b\x1e0p\xb5\xd3b\xc6/\xb3\t\xb0p\x8a\x02\xc4k\xf5A`f\x11\x00\x00\u07d4\xc7\xf7+\xb7X\x01k7G\x14\u0509\x9b\xce\"\xb4\xae\xc7\n1\x89:&\xc9G\x8f^-\x00\x00\u0794\xc8\v6\u047e\xaf\xba_\xccdM`\xacnF\xed)'\xe7\u0708\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xc8\x11\xc2\xe9\xaa\x1a\xc3F.\xba^\x88\xfc\xb5\x12\x0e\x9fn,\xa2\x89K\xe6\u0607\xbd\x87n\x00\x00\u07d4\xc8\x17\xdf\x1b\x91\xfa\xf3\x0f\xe3%\x15qr|\x97\x11\xb4]\x8f\x06\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xc8\x1f\xb7\xd2\x0f\u0480\x01\x92\xf0\xaa\xc1\x98\xd6\u05a3}?\xcb}\x89\x0e\x11I3\x1c-\xde\x00\x00\u07d4\xc8 \xc7\x11\xf0w\x05'8\a\xaa\xaam\xe4M\x0eKH\xbe.\x89\bg\x0e\x9e\xc6Y\x8c\x00\x00\u07d4\xc8#\x1b\xa5\xa4\x11\xa1>\"+)\xbf\xc1\b?v1X\xf2&\x8967\tlK\xcci\x00\x00\u07d4\xc86\xe2Jo\xcf)\x94;6\b\xe6b)\n!_e)\xea\x89\x0f\xd4Pd\xea\xee\x10\x00\x00\xe0\x94\xc8;\xa6\u0755I\xbe\x1d2\x87\xa5\xa6T\xd1\x06\xc3Lk]\xa2\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc8>\x9djX%;\uefb7\x93\xe6\xf2\x8b\x05JXI\x1bt\x89\x0fF\u00b6\xf5\xa9\x14\x00\x00\u07d4\xc8A\x88O\xa4x_\xb7s\xb2\x8e\x97\x15\xfa\xe9\x9aQ40]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8M\x9b\xea\n{\x9f\x14\x02 \xfd\x8b\x90\x97\u03ff\xd5\xed\xf5d\x89\x06\xab\x9e\u0091\xad}\x80\x00\u07d4\xc8RB\x8d+Xd\x97\xac\xd3\fV\xaa\x13\xfbU\x82\xf8D\x02\x893B\xd6\r\xff\x19`\x00\x00\u07d4\xc8S![\x9b\x9f-,\xd0t\x1eX^\x98{_\xb8\f!.\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc8S%\uaca5\x9b>\xd8c\xc8j_)\x06\xa0B)\xff\xa9\x89\x19=\u007f}%=\xe0\x00\x00\u07d4\xc8^\xf2}\x82\x04\x03\x80_\xc9\xed%\x9f\xffd\xac\xb8\xd64j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8akN\xc0\x91(\xcd\xff9\xd6\u4e6c\x86\xee\xc4q\xd5\xf2\x89\x01\r:\xa56\xe2\x94\x00\x00\xe0\x94\xc8a\x90\x90K\x8d\a\x9e\xc0\x10\xe4b\xcb\xff\xc9\b4\xff\xaa\\\x8a\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4\xc8q\r~\x8bZ;\u059aB\xfe\x0f\xa8\xb8|5\u007f\xdd\xcd\u0209\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc8sR\u06e5\x82\xee f\xb9\xc0\x02\xa9b\xe0\x03\x13Ox\xb1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xc8|w\xe3\xc2J\xde\xcd\xcd\x108\xa3\x8bV\xe1\x8d\xea\u04f7\x02\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xc8}:\xe3\u0607\x04\u066b\x00\t\xdc\xc1\xa0\x06q1\xf8\xba<\x89j\xc5\xc6-\x94\x86\a\x00\x00\xe0\x94\u0201N4R>8\xe1\xf9'\xa7\xdc\xe8FjDz\t6\x03\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0202U\xed\xdc\xf5!\xc6\xf8\x1d\x97\xf5\xa4!\x81\xc9\a=N\xf1\x89\x0f\u00d0D\xd0\n*\x80\x00\u07d4\u0205\xa1\x8a\xab\xf4T\x1b{{~\xcd0\xf6\xfa\u619d\x95i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u020c\xa1\xe6\xe5\xf4\xd5X\xd17\x80\xf4\x88\xf1\rJ\xd3\x13\r4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u020e\xecT\xd3\x05\xc9(\xcc(H\xc2\xfe\xe251\xac\xb9mI\x89lj\u04c2\xd4\xfba\x00\x00\xe0\x94\u021c\xf5\x04\xb9\xf3\xf85\x18\x1f\xd8BO\\\xcb\xc8\xe1\xbd\xdf}\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0222\xc4\xe5\x9e\x1c\u007f\xc5H\x05X\x048\xae\xd3\xe4J\xfd\xf0\x0e\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\u022aI\u301f\b\x99\xf2\x8a\xb5~gCp\x9dXA\x903\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\u022b\x1a<\xf4l\xb8\xb0d\xdf.\"-9`s\x94 2w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0231\x85\x05%\xd9F\xf2\xae\x84\xf3\x17\xb1Q\x88\xc56\xa5\u0706\x89\x91\x8d\xdc:B\xa3\xd4\x00\x00\u07d4\xc8\xd4\xe1Y\x9d\x03\xb7\x98\t\xe0\x13\n\x8d\u00c4\b\xf0^\x8c\u04c9\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xc8\xdd'\xf1k\xf2$P\xf5w\x1b\x9f\xe4\xedO\xfc\xb3\t6\xf4\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xc8\xdezVL\u007f@\x12\xa6\xf6\xd1\x0f\u040fG\x89\x0f\xbf\a\u0509\x10CV\x1a\x88)0\x00\x00\u07d4\xc8\xe2\xad\xebT^I\x9d\x98,\f\x11sc\u03b4\x89\u0171\x1f\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xc8\xe5X\xa3\xc5i~o\xb2:%\x94\u0200\xb7\xa1\xb6\x8f\x98`\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc8\xf2\xb3 \xe6\xdf\xd7\t\x06\u0157\xba\xd2\xf9P\x13\x12\u01c2Y\x89Q\x93K\x8b:W\xd0\x00\x00\u07d4\xc9\x03\x00\xcb\x1d@w\xe6\xa6\xd7\xe1i\xa4`F\x8c\xf4\xa4\x92\u05c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\f7e\x15k\u028eH\x97\xab\x80$\x19\x15<\xbeR%\xa9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc9\x10\xa9pUl\x97\x16\xeaS\xaff\xdd\xef\x93\x141$\x91=\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94\xc9\x12{\u007ff)\xee\x13\xfc?`\xbc/Dg\xa2\aE\xa7b\x8a\x03|\x9a\xa4\xe7\xceB\x1d\x80\x00\u07d4\xc9\x1b\xb5b\xe4+\xd4a0\xe2\u04eeFR\xb6\xa4\ub1bc\x0f\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\xe0\x94\xc90\x88y\x05m\xfe\x13\x8e\xf8 \x8fy\xa9\x15\u01bc~p\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xc94\xbe\xca\xf7\x1f\"_\x8bJK\xf7\xb1\x97\xf4\xac\x9604\\\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9?\xbd\xe8\xd4m+\xcc\x0f\xa9\xb3;\u063a\u007f\x80B\x12Ue\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xc9@\x89U:\xe4\xc2,\xa0\x9f\xbc\x98\xf5pu\xcf.\u0155\x04\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc9A\x10\xe7\x1a\xfeW\x8a\xa2\x18\xe4\xfc(d\x03\xb03\n\u038d\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc9F\u056c\xc14n\xba\nry\xa0\xac\x1dF\\\x99m\x82~\x8a\x03x=T_\xdf\n\xa4\x00\x00\u07d4\xc9J(\xfb20\xa9\xdd\xfa\x96Nw\x0f,\xe3\xc2S\xa7\xbeO\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc9JXR\x03\xda{\xba\xfd\x93\xe1X\x84\xe6`\u0531\xea\xd8T\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc9O|5\xc0'\xd4}\xf8\xefO\x9d\xf8Z\x92H\xa1}\xd2;\x89\x01\x9f\x8euY\x92L\x00\x00\u07d4\xc9Q\x90\f4\x1a\xbb\xb3\xba\xfb\xf7\xee )7pq\xdb\xc3j\x89\x11\xc2]\x00M\x01\xf8\x00\x00\u07d4\xc9S\xf94\xc0\xeb-\x0f\x14K\u06b0\x04\x83\xfd\x81\x94\x86\\\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9f&r\x8a\xaaLO\xb3\xd3\x1c&\xdf:\xf3\x10\b\x17\x10\u0449\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\xc9gQel\n\x8e\xf45{sD2!4\xb9\x83PJ\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\u0240Hh\u007f+\xfc\u027d\x90\xed\x18slW\xed\xd3R\xb6]\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0241\xd3\x12\u0487\xd5X\x87\x1e\u0757:\xbbv\xb9y\xe5\xc3^\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0242Xmc\xb0\xd7L \x1b\x1a\xf8A\x83r\xe3\fv\x16\xbe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u0249CO\x82Z\xaf\x9cU/h^\xba|\x11\xdbJ_\xc7:\x89\x1b(\u014d\x96\x96\xb4\x00\x00\u07d4\u0249\xee\xc3\a\u80db\x9dr7\xcf\xda\b\x82)b\xab\u41c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u0252\xbeY\xc6r\x1c\xafN\x02\x8f\x9e\x8f\x05\xc2\\UQ[\u0509\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0255{\xa9L\x1b)\xe5'~\xc3f\"pI\x04\xc6=\xc0#\x89h>\xfcg\x82d,\x00\x00\xe0\x94\u025a\x9c\xd6\xc9\xc1\xbe54\xee\u0352\xec\xc2/\\8\xe9Q[\x8a\x01\x05Y;:\x16\x9dw\x00\x00\xe0\x94\u026c\x01\xc3\xfb\t)\x03?\f\xcc~\x1a\xcf\uaae7\x94]G\x8a\x02\xa3j\x9e\x9c\xa4\xd2\x03\x80\x00\u07d4\u0276\x98\xe8\x98\xd2\rMO@\x8eNM\x06\x19\"\xaa\x85c\a\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u0276\xb6\x86\x11\x16\x91\xeej\xa1\x97\xc7#\x1a\x88\xdc`\xbd)]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\u01ec\v\u0753B\xb5\xea\xd46\t#\xf6\x8cr\xa6\xbac:\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\xc8\r\xc1.{\xab\x86\xe9I\xd0\x1eL>\xd3_+\x9b\xba_\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\xd7dF\u056a\xdf\xf8\vh\xb9\x1b\b\u035b\xc8\xf5U\x1a\xc1\x89&\xb4\xbd\x91\x10\xdc\xe8\x00\x00\xe0\x94\xc9\u073b\x05oM\xb7\xd9\xda9\x93b\x02\u017d\x820\xb3\xb4w\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9\xe0&\b\x06h(\x84\x8a\xeb(\xc76r\xa1)%\x18\x1fM\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xca\x042\xcb\x15{Qy\xf0.\xbb\xa5\xc9\u0475O\xecM\x88\u028965\u026d\xc5\u07a0\x00\x00\u07d4\xca\x12,\xf0\U00094216\xb7HC\xf4\x9a\xfe\u043a\x16\x18\xee\u05c9\x1e[\x8f\xa8\xfe*\xc0\x00\x00\xe0\x94\xca\"\u0363`m\xa5\xca\xd0\x13\xb8\aG\x06\xd7\xe9\xe7!\xa5\f\x8a\x01q\x81\xc6\xfa9\x81\x94\x00\x00\u07d4\xca#\xf6-\xff\rd`\x03lb\xe8@\xae\xc5W~\v\xef\u0489\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xca%\xff4\x93L\x19B\xe2*N{\xd5o\x14\x02\x1a\x1a\xf0\x88\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xca7?\xe3\xc9\x06\xb8\xc6U\x9e\xe4\x9c\xcd\a\xf3|\xd4\xfbRf\x89a\t=|,m8\x00\x00\u07d4\xcaA\u032c0\x17 R\xd5\"\xcd//\x95}$\x81S@\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xcaB\x88\x01N\xdd\xc5c/_\xac\xb5\xe3\x85\x17\xa8\xf8\xbc]\x98\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xcaB\x88c\xa5\xca06\x98\x92\xd6\x12\x18>\xf9\xfb\x1a\x04\xbc\xea\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\xcaI\xa5\xf5\x8a\xdb\xef\xae#\xeeY\xee\xa2A\xcf\x04\x82b.\xaa\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcaL\xa9\xe4w\x9dS\x0e\u02ec\xd4~j\x80X\xcf\xdee\u064f\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xcae~\xc0o\xe5\xbc\t\xcf#\xe5*\xf7\xf8\f\xc3h\x9en\u07890\xca\x02O\x98{\x90\x00\x00\u07d4\xcaf\xb2(\x0f\xa2\x82\u0176v1\xceU+b\xeeU\xad\x84t\x89j\xc4\"\xf54\x92\x88\x00\x00\xe0\x94\xcal\x81\x8b\xef\xd2Q6\x1e\x02t@h\xbe\x99\u062a`\xb8J\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcap\xf4\u077f\x06\x9d!C\xbdk\xbc\u007fikRx\x9b2\u7262\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xcatuvDjL\x8f0\xb0\x83@\xfe\xe1\x98\xdec\xec\x92\u03ca\x01|\x8e\x12\x06r*0\x00\x00\u07d4\xca{\xa3\xffSl~_\x0e\x158\x00\xbd8=\xb81)\x98\xe0\x89\t1\xac=k\xb2@\x00\x00\xe0\x94\u0282v\xc4w\xb4\xa0{\x80\x10{\x845\x94\x18\x96\a\xb5;\xec\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u0284\t\b>\x01\xb3\x97\xcf\x12\x92\x8a\x05\xb6\x84U\xceb\x01\u07c9V\xbcu\xe2\xd61\x00\x00\x00\u07d4\u0298\u01d8\x8e\xfa\b\xe9%\uf719ER\x03&\xe9\xf4;\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u029a\x04*j\x80o\xfc\x92\x17\x95\x00\xd2D)\xe8\xabR\x81\x17\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\u029d\xec\x02\x84\x1a\xdf\\\xc9 WjQ\x87\xed\u04bdCJ\x18\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u029f\xaa\x17T/\xaf\xbb8\x8e\xab!\xbcL\x94\u89f3G\x88\x89lk\x8f\xce\r\x18y\x80\x00\xe0\x94\u02aah\xeel\xdf\r4EJv\x9b\r\xa1H\xa1\xfa\xaa\x18e\x8a\x01\x87.\x1d\xe7\xfeR\xc0\x00\x00\u07d4\u02ad\x9d\xc2\rX\x9c\xe4(\xd8\xfd\xa3\xa9\xd5:`{y\x88\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u02b0\xd3,\xf3v\u007f\xa6\xb3S|\x842\x8b\xaa\x9fPE\x816\x8a\x01\xe5\xb8\xfa\x8f\xe2\xac\x00\x00\x00\u07d4\u02b9\xa3\x01\xe6\xbdF\xe9@5P(\xec\xcd@\xceMZ\x1a\u00c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u02b9\xa9z\xda\x06\\\x87\x81nh`\xa8\xf1Bo\xe6\xb3\xd7u\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u02ba\xb6'N\xd1P\x89s~({\xe8x\xb7W\x93Hd\xe2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\u02bd\xaf5OG \xa4f\xa7d\xa5(\xd6\x0e:H*9<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xca\xcbg^\t\x96#T\x04\ufbfb.\u02c1R'\x1bU\xe0\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xca\xd1O\x9e\xbb\xa7f\x80\xeb\x83k\a\x9c\u007f{\xaa\xf4\x81\xedm\x89\f\xef={\xd7\xd04\x00\x00\xe0\x94\xca\xe3\xa2S\xbc\xb2\xcfN\x13\xba\x80\u0098\xab\x04\x02\xda|*\xa0\x8a\x01$\xbc\r\u0752\xe5`\x00\x00\u07d4\xca\xef\x02{\x1a\xb5\x04\xc7?A\xf2\xa1\ty\xb4t\xf9~0\x9f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xca\xf4H\x1d\x9d\xb7\x8d\xc4\xf2_{J\u023d;\x1c\xa0\x10k1\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xca\xfd\xe8U\x86L%\x98\xda<\xaf\xc0Z\u064d\U00089380H\x8a\x03\x00\xa8\xed\x96\xffJ\x94\x00\x00\xe0\x94\xcb\r\xd7\xcfN]\x86a\xf6\x02\x89C\xa4\xb9\xb7\\\x91D6\xa7\x8a\x19i6\x89t\xc0[\x00\x00\x00\u07d4\xcb\x1b\xb6\xf1\xda^\xb1\rH\x99\xf7\xe6\x1d\x06\xc1\xb0\x0f\u07f5-\x898E$\xccp\xb7x\x00\x00\u07d4\xcb=vl\x98?\x19+\xce\xca\xc7\x0fN\xe0=\xd9\xffqMQ\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcbB\xb4N\xb5\xfd`\xb5\x83~O\x9e\xb4rgR=\x1a\"\x9c\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xcbG\xbd0\u03e8\xecTh\xaa\xa6\xa9FB\xce\xd9\xc8\x19\xc8\u0509\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcbH\xfe\x82e\u066fU\xebp\x06\xbc3VE\xb0\xa3\xa1\x83\xbe\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xcbJ\x91M+\xb0)\xf3._\xef\\#LO\xec--\xd5w\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcbJ\xbf\u0082\xae\xd7n]W\xaf\xfd\xa5B\xc1\xf3\x82\xfc\xac\xf4\x8a\x01\xb9\x0f\x11\xc3\x18?\xaa\x00\x00\u07d4\xcbJ\xd0\xc7#\xdaF\xabV\xd5&\xda\f\x1d%\xc7=\xaf\xf1\n\x89\x1b\xa5\xab\xf9\xe7y8\x00\x00\u07d4\xcbK\xb1\xc6#\xba(\xdcB\xbd\xaa\xa6\xe7N\x1d*\xa1%l*\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xcbPXt\x12\x82#\x04\xeb\u02e0}\xab:\x0f\t\xff\xfe\u4189JD\x91\xbdm\xcd(\x00\x00\u07d4\xcbX\x99\v\u0350\u03ffm\x8f\t\x86\xf6\xfa`\x02v\xb9N-\x8964\xbf9\xab\x98x\x80\x00\u07d4\xcbh\xaeZ\xbe\x02\xdc\xf8\xcb\u016aq\x9c%\x81FQ\xaf\x8b\x85\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xcbty\x10\x9bC\xb2fW\xf4F_M\x18\xc6\xf9t\xbe_B\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xcb}+\x80\x89\xe91,\u026e\xaa's\xf3S\b\xecl*{\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u02c6\xed\xbc\x8b\xbb\x1f\x911\x02+\xe6IV^\xbd\xb0\x9e2\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u02d3\x19\x9b\x9c\x90\xbcI\x15\xbd\x85\x9e=B\x86m\xc8\xc1\x87I\x89\f\x90\xdf\a\xde\xf7\x8c\x00\x00\u07d4\u02d4\xe7o\xeb\xe2\b\x11g3\xe7n\x80]H\xd1\x12\xec\x9f\u028965\u026d\xc5\u07a0\x00\x00\u07d4\u02dbQ\x03\xe4\u0389\xafOd\x91aP\xbf\xf9\xee\u02df\xaa\\\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\\zP<\xc8\xe0\xd0Iq\xca\x05\xc7b\xf9\xb7b\xb4\x8b\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\x88\xcd<\x1e\xb4\u055d\xdb\x06\xa6B\x1c\x14\xc3E\xa4{$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u02f3\x18\x9eK\xd7\xf4_\x17\x8b\x1c0\xc7n&1MJK\n\x89\x0f\xfe\vg|e\xa9\x80\x00\xe0\x94\u02f7\xbe\x17\x95?,\u0313\u1f19\x80[\xf4U\x11CNL\x8a\n\xae[\x9d\xf5m/ \x00\x00\xe0\x94\xcb\xc0KM\x8b\x82\xca\xf6p\x99o\x16\f6)@\xd6o\xcf\x1a\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcb\u07974\xb8\xe6\xaaS\x8c)\x1dm\u007f\xac\xed\xb0\xf38\xf8W\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcb\xe1\xb9H\x86M\x84t\xe7e\x14XX\xfc\xa4U\x0fxK\x92\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xe5/\xc53\xd7\xdd`\x8c\x92\xa2`\xb3|?E\u07b4\xeb3\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcb\xe8\x10\xfe\x0f\xec\xc9dGJ\x1d\xb9w(\xbc\x87\xe9s\xfc\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xf1j\x0f\xe2tRX\xcdR\xdb+\xf2\x19T\xc9u\xfcj\x15\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\xcb\xf3\u007f\xf8T\xa2\xf1\xceS\x93D\x94wx\x92\xd3\xeceW\x82\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xfaj\xf6\u0083\xb0F\xe2w,`c\xb0\xb2\x15S\xc4\x01\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcb\xfav\xdb\x04\xce8\xfb ]7\xb8\xd3w\xcf\x13\x80\xda\x03\x17\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcc\x03I\x85\xd3\xf2\x8c-9\xb1\xa3K\xce\xd4\u04f2\xb6\xca#N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xcc\x04<C\x88\xd3E\xf8\x84\u0185^q\x14*\x9fA\xfdi5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xcc\x1dn\xad\x01\xaa\xda>\x8d\u01f9]\xca%\xdf&\xee\xfac\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc+_D\x8f5(\xd3\xfeA\xcc}\x1f\xa9\xc0\xdcv\xf1\xb7v\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc-\x04\xf0\xa4\x01q\x89\xb3@\xcaw\x19\x86A\xdc\xf6Ek\x91\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xccA\x9f\u0651+\x85\x13VY\xe7z\x93\xbc=\xf1\x82\xd4Q\x15\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccE\xfb:U[\xad\x80{8\x8a\x03W\xc8U _|u\xe8\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xccHAM*\xc4\xd4*Yb\xf2\x9e\xeeD\x97\t/C\x13R\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\xccJ/,\xf8l\xf3\xe43u\xf3`\xa4sF\x91\x19_\x14\x90\x89I\x15\x05;\xd1)\t\x80\x00\u07d4\xccO\x0f\xf2\xae\xb6}T\xce;\xc8\xc6Q\v\x9a\xe8>\x9d2\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xccO\xaa\xc0\v\xe6b\x8f\x92\xefk\x8c\xb1\xb1\xe7j\xac\x81\xfa\x18\x89\v\"\xa2\xea\xb0\xf0\xfd\x00\x00\xe0\x94\xccO\xebr\u07d8\xff5\xa18\xe0\x17a\xd1 ?\x9b~\xdf\n\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xcc`oQ\x13\x97\xa3\x8f\u01c7+\u04f0\xbd\x03\xc7\x1b\xbdv\x8b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcc`\xf86\xac\xde\xf3T\x8a\x1f\xef\u0321>\u01a97\xdbD\xa0\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4\xccl\x03\xbd`>\t\xdeT\xe9\xc4\u056cmA\xcb\xceqW$\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xccl-\xf0\x0e\x86\xec\xa4\x0f!\xff\xda\x1ag\xa1i\x0fG|e\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\xccm{\x12\x06\x1b\xc9m\x10M`me\xff\xa3+\x006\xeb\a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccs\xdd5kIy\xb5y\xb4\x01\xd4\xccz1\xa2h\xdd\xceZ\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xccu\x8d\a\x1d%\xa62\n\xf6\x8c]\xc9\xc4\xf6\x95[\xa9E \x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcc{\x04\x81\xcc2\xe6\xfa\xef#\x86\xa0p\"\xbc\xb6\xd2\u00f4\xfc\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\u0314;\xe1\",\xd1@\n#\x99\xdd\x1bE\x94E\xcfmT\xa9\x8a\x02\xa7@\xaee6\xfc\x88\x00\x00\u07d4\u0315\x19\xd1\xf3\x98_k%^\xad\xed\x12\xd5bJ\x97'!\xe1\x8965\u026d\xc5\u07a0\x00\x00\u0794\u031a\xc7\x15\xcdo&\x10\xc5+XgdV\x88B\x97\x01\x8b)\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\u0320{\xb7\x94W\x1dJ\xcf\x04\x1d\xad\x87\xf0\xd1\xef1\x85\xb3\x19\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u032b\xc6\x04\x8aSFD$\xfc\xf7n\xeb\x9en\x18\x01\xfa#\u0509\x02\xab{&\x0f\xf3\xfd\x00\x00\u07d4\u032e\r=\x85*}\xa3\x86\x0f\x066\x15L\nl\xa3\x16(\u0509\x05\xc6\xd1+k\xc1\xa0\x00\x00\u07d4\xcc\xca$\xd8\xc5mn,\a\xdb\bn\xc0~X[\xe2g\xac\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcc\xd5!\x13-\x98l\xb9hi\x84&\"\xa7\u0762l>\xd0W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc\xf49u\xb7k\xfes_\xec<\xb7\xd4\xdd$\xf8\x05\xba\tb\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc\xf6*f?\x13S\xba.\xf8\xe6R\x1d\xc1\xec\xb6s\xec\x8e\xf7\x89\b=lz\xabc`\x00\x00\u07d4\xcc\xf7\x11\r\x1b\u0667K\xfd\x1d}}-\x9dU`~{\x83}\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xcc\xfdrW`\xa6\x88#\xff\x1e\x06/L\xc9~\x13`\xe8\u0657\x89\x15\xacV\xed\xc4\xd1,\x00\x00\u07d4\xcd\x02\x0f\x8e\xdf\xcfRG\x98\xa9\xb7:d\x034\xbb\xf7/\x80\xa5\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xcd\x06\xf8\xc1\xb5\u037d(\xe2\xd9kcF\xc3\xe8Z\x04\x83\xba$\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcd\a.n\x183\x13y\x95\x19m{\xb1r_\xef\x87a\xf6U\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcd\n\x16\x1b\xc3g\xae\t'\xa9*\xac\x9c\xf6\xe5\bg\x14\xef\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\n\xf3GN\"\xf0i\xec4\a\x87\r\xd7pD=[\x12\xb0\x89\x8e^\xb4\xeew\xb2\xef\x00\x00\u07d4\xcd\v\x02W\u70e3\xd2\xc2\u3e9dny\xb7^\xf9\x80$\u0509\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xcd\x10,\xd6\xdb=\xf1J\u05af\x0f\x87\xc7$y\x86\x1b\xfc=$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\x1ef\xedS\x9d\xd9/\xc4\v\xba\xa1\xfa\x16\u078c\x02\xc1ME\x89\fw\xe4%hc\xd8\x00\x00\u07d4\xcd\x1e\xd2c\xfb\xf6\xf6\xf7\xb4\x8a\xef\x8fs=2\x9dC\x82\xc7\u01c9\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4\xcd*6\xd7S\xe9\xe0\xed\x01*XMqh\aX{A\xd5j\x89\x0e+\xa7[\v\x1f\x1c\x00\x00\u07d4\xcd2\xa4\xa8\xa2\u007f\x1c\xc69T\xaacOxW\x05s4\u01e3\x89:\xd1fWlr\xd4\x00\x00\u07d4\xcd5\xff\x01\x0e\xc5\x01\xa7!\xa1\xb2\xf0z\x9c\xa5\x87}\xfc\xf9Z\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xcdC\x06\xd7\xf6\x94z\xc1tMN\x13\xb8\xef2\xcbe~\x1c\x00\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4\xcdC%\x8bs\x92\xa90\x83\x9aQ\xb2\xef\x8a\xd24\x12\xf7Z\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcdI\xbf\x18^p\xd0E\a\x99\x9f\x92\xa4\xdeDU1('\u040965\u026d\xc5\u07a0\x00\x00\u07d4\xcdU\x10\xa2B\u07f0\x18=\xe9%\xfb\xa8f\xe3\x12\xfa\xbc\x16W\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xcdVj\u05f8\x83\xf0\x1f\u04d9\x8a\x9aX\xa9\xde\xe4rM\u0725\x89\x030\xae\x185\xbe0\x00\x00\xe0\x94\xcdY\xf3\xdd\xe7~\t\x94\v\xef\xb6\xeeX\x03\x19e\xca\xe7\xa36\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcdr]p\xbe\x97\xe6w\xe3\xc8\xe8\\\v&\xef1\xe9\x95PE\x89Hz\x9a0E9D\x00\x00\xe0\x94\xcd~G\x90\x94d\xd8q\xb9\xa6\xdcv\xa8\xe9\x19]\xb3H^z\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xcd~\xce\bkKa\x9b;6\x93R\xee8\xb7\x1d\xdb\x06C\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcd\u007f\t\xd7\xedf\xd0\u00cb\u016dN2\xb7\xf2\xb0\x8d\xc1\xb3\r\x89>;\xb3M\xa2\xa4p\x00\x00\u07d4\u0355)I+\\)\xe4u\xac\xb9A@+=;\xa5\x06\x86\xb0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0355\xfaB=o\xc1 'J\xac\xde\x19\xf4\xee\xb7f\xf1\x04 \x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u035bL\xefs9\f\x83\xa8\xfdq\u05f5@\xa7\xf9\u03cb\x8c\x92\x89\x04\xe1\x00;(\xd9(\x00\x00\u07d4\u0361t\x11\t\xc0&[?\xb2\xbf\x8d^\xc9\u00b8\xa34kc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0361\xb8\x86\u39d5\u027aw\x91N\n/\xe5go\x0f\\\u03c9\x05\xbf`\xeaB\xc2\x04\x00\x00\u07d4\u0364S\x0fK\x9b\xc5\t\x05\xb7\x9d\x17\u008f\xc4o\x954\x9b\u07c93\x10\xe0I\x11\xf1\xf8\x00\x00\u07d4\u036bF\xa5\x90 \x80do\xbf\x95B\x04 J\xe8\x84\x04\x82+\x89\x1d\x8a\x96\xe5\xc6\x06\xeb\x00\x00\u07d4\u0375\x97)\x900\x18?n-#\x853\xf4d*\xa5\x87T\xb6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xcd\xd5\u0601\xa76,\x90p\a;\u07fcu\xe7$S\xacQ\x0e\x89-\xa5\x18\xea\xe4\x8e\xe8\x00\x00\u07d4\xcd\xd6\rs\xef\xaa\xd8s\u027b\xfb\x17\x8c\xa1\xb7\x10Z\x81\xa6\x81\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xcd\xd9\xef\xacMm`\xbdq\xd9U\x85\xdc\xe5\u0557\x05\xc15d\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcd\xe3m\x81\xd1(\u015d\xa1Ee!\x93\xee\u00bf\xd9e\x86\xef\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcd\xea8o\x9d\x0f\xd8\x04\xd0(\x18\xf27\xb7\xd9\xfavF\xd3^\x89\xa3I\xd3m\x80\xecW\x80\x00\u07d4\xcd\xec\xf5gT3\u0370\xc2\xe5Zh\xdb]\x8b\xbexA\x9d\u0489\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xcd\xfd\x82\x173\x97%\xd7\xeb\xac\x11\xa66U\xf2e\xef\xf1\xcc=\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce\a\x9fQ\x88wt\xd8\x02\x1c\xb3\xb5u\xf5\x8f\x18\xe9\xac\xf9\x84\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xce\x18\x84\u077b\xb8\xe1\x0eM\xbanD\xfe\xee\u00a7\xe5\xf9/\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\x1b\f\xb4j\xae\xcf\u05db\x88\f\xad\x0f-\u068a\x8d\xed\u0431\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xce&\xf9\xa50_\x83\x81\tCT\xdb\xfc\x92fN\x84\xf9\x02\xb5\x89\fz\xaa\xb0Y\x1e\xec\x00\x00\u07d4\xce-\xea\xb5\x1c\n\x9a\xe0\x9c\xd2\x12\xc4\xfaL\xc5+S\xcc\r\xec\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xce.\r\xa8\x93F\x99\xbb\x1aU>U\xa0\xb8\\\x16\x945\xbe\xa3\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce:a\xf0F\x1b\x00\x93^\x85\xfa\x1e\xad\x82\xc4^Zd\u0508\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceK\x06]\xbc\xb20G 2b\xfbH\xc1\x18\x83d\x97tp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceS\xc8\xcd\xd7B\x96\xac\xa9\x87\xb2\xbc\x19\u00b8u\xa4\x87I\u0409\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xce^\x04\xf0\x18Ci\xbc\xfa\x06\xac\xa6o\xfa\x91\xbfY\xfa\x0f\xb9\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xce^\xb6:{\xf4\xfb\xc2\xf6\u4ea0\u018a\xb1\xcbL\xf9\x8f\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xceb\x12Z\xde\xc37\n\xc5!\x10\x95:Nv\v\xe9E\x1e;\x89\b=lz\xabc`\x00\x00\xe0\x94\xceq\bmL`%T\xb8-\xcb\xfc\xe8\x8d cMS\xccM\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4\u038akmP3\xb1I\x8b\x1f\xfe\xb4\x1aAU\x04\x05\xfa\x03\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0397\x86\xd3q/\xa2\x00\xe9\xf6\x857\xee\xaa\x1a\x06\xa6\xf4ZK\x89a\t=|,m8\x00\x00\u07d4\u039d!\u0192\xcd<\x01\xf2\x01\x1fP_\x87\x006\xfa\x8fl\u0489\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u03a2\x89f#\xf4\x91\x02\x87\xa2\xbd\u017e\x83\xae\xa3\xf2\xe6\xde\b\x8a\x01\xfbZ7Q\xe4\x90\xdc\x00\x00\u07d4\u03a3JM\xd9=\u066e\xfd9\x90\x02\xa9}\x99z\x1bK\x89\u0349QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\u03a4?pu\x81k`\xbb\xfc\u62d9:\xf0\x88\x12p\xf6\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\u03a8t3AS<\xb2\xf0\xb9\xc6\xef\xb8\xfd\xa8\rw\x16(%\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u03b0\x89\xec\x8ax3~\x8e\xf8\x8d\xe1\x1bI\xe3\u0751\x0ft\x8f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u03b3=x\xe7Tz\x9d\xa2\xe8}Q\xae\xc5\xf3D\x1c\x87\x92:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u03b3\x898\x1dH\xa8\xaeO\xfcH:\u043b^ L\xfd\xb1\xec\x89('\xe6\xe4\xddb\xba\x80\x00\u07d4\xce\xc6\xfce\x85?\x9c\xce_\x8e\x84Fv6.\x15y\x01_\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xce\xd3\u01fe\x8d\xe7XQ@\x95*\xebP\x1d\xc1\xf8v\ucbf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\xd8\x1e\xc3S?\xf1\xbf\xeb\xf3\xe3\x84>\xe7@\xad\x11u\x8d>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xce\u0733\xa1\u0584?\xb6\xbe\xf6Ca}\xea\U000cf398\xdd_\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xce\xe6\x99\xc0pzx6%+)/\x04|\xe8\xad(\x9b/U\x89\x11\x9a\x1e!\xaaiV\x00\x00\u07d4\xce\xedG\xca[\x89\x9f\xd1b?!\xe9\xbdM\xb6Z\x10\u5c1d\x89\a8w@L\x1e\xee\x00\x00\u07d4\xce\xf7tQ\u07e2\xc6C\xe0\v\x15mlo\xf8N#s\xebf\x89\n1\x06+\xee\xedp\x00\x00\u07d4\xcf\x11i\x04\x1c\x17E\xe4[\x17$5\xa2\xfc\x99\xb4\x9a\xce+\x00\x89\x01\xbb\x88\xba\xab-|\x00\x00\xe0\x94\xcf\x15v\x12vN\x0f\u0596\xc8\xcb_\xba\x85\xdfL\r\xdc<\xb0\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u0794\xcf\x1b\xdby\x9b.\xa6<\xe14f\x8b\xdc\x19\x8bT\x84\x0f\x18\v\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xcf\"\x88\xefN\xbf\x88\xe8m\xb1=\x8a\x0e\v\xf5*\x05e\x82\u00c9\x89Po\xbf\x97@t\x00\x00\u07d4\xcf&Ni%\x13\t\x06\xc4\xd7\xc1\x85\x91\xaaA\xb2\xa6\u007foX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf&\xb4{\xd04\xbcP\x8elK\xcf\xd6\xc7\xd3\x004\x92Wa\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcf.*\xd65\xe9\x86\x1a\xe9\\\xb9\xba\xfc\xca\x03kR\x81\xf5\u038a\at2!~h6\x00\x00\x00\u07d4\xcf.s@B\xa3U\xd0_\xfb.9\x15\xb1h\x11\xf4Zi^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf4\x8f/\xe4{~A<\az{\xaf:u\xfb\xf8B\x86\x92\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcf?\x91(\xb0r\x03\xa3\xe1\r}WU\xc0\u012b\xc6\xe2\xca\u008a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xcf?\xbf\xa1\xfd2\u05e6\xe0\xe6\xf8\xefN\xabW\xbe4\x02\\L\x899\xa1\xc0\xf7YMH\x00\x00\u07d4\xcfAftn\x1d;\xc1\xf8\xd0qK\x01\xf1~\x8ab\xdf\x14d\x896w\x03n\xdf\n\xf6\x00\x00\u07d4\xcfO\x118\xf1\xbdk\xf5\xb6\u0505\xcc\xe4\xc1\x01\u007f\u02c5\xf0}\x89/\u043cw\xc3+\xff\x00\x00\u07d4\xcfZo\x9d\xf7Uy\xc6D\xf7\x94q\x12\x15\xb3\rw\xa0\xce@\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf^\x0e\xac\u0473\x9d\x06U\xf2\xf7u5\xeff\b\xeb\x95\v\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcfhM\xfb\x83\x04r\x93U\xb5\x83\x15\xe8\x01\x9b\x1a\xa2\xad\x1b\xac\x89\x17r$\xaa\x84Lr\x00\x00\u07d4\xcfi@\x81\xc7m\x18\xc6L\xa7\x13\x82\xbe\\\xd6;<\xb4v\xf8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcfnR\xe6\xb7t\x80\xb1\x86~\xfe\xc6Dm\x9f\xc3\xcc5w\xe8\x89\f\t\x01\xf6\xbd\x98y\x00\x00\u07d4\u03c8: 2\x96g\xea\"j\x1e<v]\xbbk\xab2!\x9f\x89\xa4\xbe5d\xd6\x16f\x00\x00\xe0\x94\u03c8\x825\x9c\x0f\xb23\x87\xf5g@t\u0631z\xdeQ/\x98\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u03c9\xf7F\v\xa3\xdf\xe8<Z\x1d:\x01\x9e\xe1%\x0f$/\x0f\x895h\x13\xcd\xce\xfd\x02\x80\x00\u07d4\u03d2:]\x8f\xbc=\x01\xaa\a\x9d\x1c\xfeKC\xce\a\x1b\x16\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u03db\u9e6b\x86\xc6kY\x96\x8eg\xb8\xd4\xdc\xffF\xb1\x81J\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\u03e8\xb3q'\x14\x9b\xdb\xfe\xe2\\4\xd8xQ\tQ\xea\x10\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u03ec.\x1b\xf32\x05\xb0U3i\x1a\x02&~\xe1\x9c\xd8\x186\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u03fb2\xb7\xd0$5\x0e3!\xfa \u0269\x14\x03Sr\xff\u0189\x15\xbeat\xe1\x91.\x00\x00\u07d4\xcf\xc4\xe6\xf7\xf8\xb0\x11AK\xfb\xa4/#\xad\xfa\xa7\x8dN\xcc^\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xcf\xd2r\x8d\xfb\x8b\xdb\xf3\xbfsY\x8an\x13\xea\xf40R\xea+\x89\t79SM(h\x00\x00\u07d4\xcf\xd4t\x93\xc9\xf8\x9f\u603d\xa5uM\xd7\xc9\xcf\xe7\xcb[\xbe\x89\x02\xf4sQ4H\xfe\x00\x00\u07d4\xcf\xde\x0f\xc7]o\x16\xc4C\xc3\x03\x82\x177-\x99\xf5\xd9\a\xf7\x89\x83\"^c\x96\xb5\xec\x00\x00\xe0\x94\xcf\xe2\u02af<\xec\x97\x06\x1d\t9t\x879\xbf\xfehJ\xe9\x1f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcf\xea\u02ae\xd5r\x85\xe0\xacrh\xcejN5\xec\xfd\xb2B\u05c9:\xe4\xd4$\x01\x90`\x00\x00\u07d4\xcf\ucfa0|'\x00/e\xfeSK\xb8\x84-\t%\u01c4\x02\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcf\xee\x05\u019d\x1f)\xe7qF\x84\u020d\xe5\xa1`\x98\xe9\x13\x99\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xcf\xf6\xa6\xfe>\x9a\x92*\x12\xf2\x1f\xaa\x03\x81V\x91\x8cO\u02dc\x89\x04E\x91\xd6\u007f\xec\xc8\x00\x00\u07d4\xcf\xf7\xf8\x9aMB\x19\xa3\x82\x95%\x131V\x82\x10\xff\xc1\xc14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xcf\xf8\xd0k\x00\xe3\xf5\f\x19\x10\x99\xadV\xbaj\xe2eq\u0348\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcf\xfcI\xc1x~\ubcb5l\xab\xe9$\x04\xb66\x14}EX\x8a\x013\xe00\x8f@\xa3\u0680\x00\u07d4\xd0\bQ;'`J\x89\xba\x17c\xb6\xf8L\u6233F\x94[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x0f\x06r\x86\xc0\xfb\u0402\xf9\xf4\xa6\x10\x83\xecv\u07b3\xce\xe6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x15\xf6\xfc\xb8M\xf7\xbbA\x0e\x8c\x8f\x04\x89J\x88\x1d\xca\xc27\x898E$\xccp\xb7x\x00\x00\u07d4\xd0\x1a\xf9\x13O\xafRW\x17N\x8by\x18oB\xee5Nd-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0!\b\u04ae<\xab\x10\xcb\xcf\x16W\xaf\">\x02|\x82\x10\xf6\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xd0*\xfe\u03ce.\u00b6*\u022d Aa\xfd\x1f\xaew\x1d\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd01\x919\xfb\xab.\x8e*\xcc\xc1\xd9$\u0531\x1d\xf6ilZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd07\xd2\x15\xd1\x1d\x1d\xf3\xd5O\xbd2\x1c\u0495\xc5F^';\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xd0:-\xa4\x1e\x86\x8e\xd3\xfe\xf5t[\x96\xf5\xec\xa4b\xffo\u0689\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xd0?\xc1eWj\xae\xd5%\xe5P,\x8e\x14\x0f\x8b.\x86\x969\x8a\x01sV\u0633%\x01\xc8\x00\x00\u07d4\xd0C\xa0\x11\xecBp\xee~\u0239hsu\x15\xe5\x03\xf80(\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd0K\x86\x1b=\x9a\xccV:\x90\x16\x89\x94\x1a\xb1\xe1\x86\x11a\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd0ZD|\x91\x1d\xbb'[\xfb.Z7\xe5\xa7\x03\xa5o\x99\x97\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd0_\xfb+t\xf8g O\xe51e;\x02H\xe2\x1c\x13TN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0bX\x81q\u03d9\xbb\xebX\xf1&\xb8p\xf9\xa3r\x8da\xec\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\u07d4\xd0c\x8e\xa5q\x89\xa6\xa6\x99\x02J\u05ccq\xd99\xc1\xc2\xff\x8c\x89\x8e\xaeVg\x10\xfc \x00\x00\xe0\x94\xd0d\x8aX\x1b5\b\xe15\xa2\x93]\x12\xc9epE\xd8q\u028a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xd0q\x19)f\xebi\xc3R\x0f\xca:\xa4\xdd\x04)~\xa0KN\x89\x05\xf6\x8e\x811\xec\xf8\x00\x00\u07d4\xd0q\x85 \xea\xe0\xa4\xd6-p\xde\x1b\xe0\xcaC\x1c^\xea$\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd0w]\xba*\xf4\xc3\n:x6Y9\xcdq\xc2\xf9\u0795\u0489i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd0{\xe0\xf9\t\x97\xca\xf9\x03\u022c\x1dS\xcd\xe9\x04\xfb\x19\aA\x8968\x908\xb6\x99\xb4\x00\x00\u07d4\xd0~Q\x18d\xb1\u03d9i\xe3V\x06\x02\x82\x9e2\xfcNq\xf5\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u0400\x94\x98\xc5H\x04z\x1e**\xa6\xa2\x9c\xd6\x1a\x0e\xe2h\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0402'_tZ,\xac\x02v\xfb\xdb\x02\u0532\xa3\xab\x17\x11\xfe\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\u040f\xc0\x9a\x000\xfd\t(\xcd2\x11\x98X\x01\x82\xa7j\xae\x9f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0413\xe8)\x81\x9f\xd2\xe2[\x978\x00\xbb=XA\xdd\x15-\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0414J\xa1\x85\xa13pa\xae \u071d\xd9l\x83\xb2\xbaF\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u0416V[|t\a\xd0e6X\x03U\xfd\xd6\xd29\x14J\xa1\x89\r\x8drkqw\xa8\x00\x00\u07d4\u041c\xb2\xe6\b-i:\x13\xe8\xd2\xf6\x8d\xd1\u0744a\xf5X@\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0426\xc6\xf9\xe9\u0133\x83\xd7\x16\xb3\x1d\xe7\x8dVAM\xe8\xfa\x91\x89\x10CV\x1a\x88)0\x00\x00\u07d4\u0427 \x9b\x80\xcf`\xdbb\xf5}\n]}R\x1ai`fU\x89\b\xacr0H\x9e\x80\x00\x00\xe0\x94\u0428\xab\xd8\n\x19\x9bT\xb0\x8be\xf0\x1d \x9c'\xfe\xf0\x11[\x8a\x01a\xc6&\xdca\xa2\xef\x80\x00\xe0\x94\u042b\xccp\xc0B\x0e\x0e\x17/\x97\xd4;\x87\xd5\xe8\f3n\xa9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u042es]\x91^\x94hf\xe1\xfe\xa7~^\xa4f\xb5\xca\xdd\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0431\x1do+\u0394^\fjP \u00f5'S\xf8\x03\xf9\u0449\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xd0\xc1\x01\xfd\x1f\x01\xc6?k\x1d\x19\xbc\x92\r\x9f\x93#\x14\xb16\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd0\xc5Z\xbf\x97o\xdc=\xb2\xaf\u9f99\u0519HMWl\x02\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\u0422\xadE\xf5\x9a\x9d\xcc\u0195\xd8_%\xcaF\xed1\xa5\xa3\x89-\x89W}}@ \x00\x00\u07d4\xd0\xd6,G\xea`\xfb\x90\xa3c\x92\t\xbb\xfd\xd4\xd93\x99\x1c\u0189\n\x84Jt$\xd9\xc8\x00\x00\u07d4\xd0\xdbEax o\\D0\xfe\x00Pc\x90<=zI\xa7\x89&I\x1eE\xa7S\xc0\x80\x00\u07d4\xd0\xe1\x94\xf3K\x1d\xb6\t(\x85\t\xcc\xd2\xe7;a1\xa2S\x8b\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xd0\xe3^\x04vF\xe7Y\xf4Qp\x93\xd6@\x86BQ\u007f\bM\x89\u054f\xa4h\x18\xec\u02c0\x00\u07d4\xd0\xeeM\x02\xcf$8,0\x90\xd3\xe9\x95`\xde6xs\\\u07c9\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xd0\xf0OR\x10\x9a\xeb\xec\x9a{\x1e\x932v\x1e\x9f\xe2\xb9{\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd0\xf9Yx\x11\xb0\xb9\x92\xbb}7W\xaa%\xb4\xc2V\x1d2\xe2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\x03\x02\xfa\xa1\x92\x9a2i\x04\xd3v\xbf\v\x8d\xc9:\xd0LL\x89a\t=|,m8\x00\x00\xe0\x94\xd1\x10\r\xd0\x0f\xe2\xdd\xf1\x81c\xad\x96M\vi\xf1\xf2\xe9e\x8a\x8a\x01C\x12\tU\xb2Pk\x00\x00\u07d4\xd1\x16\xf3\xdc\xd5\xdbtK\xd0\b\x88v\x87\xaa\x0e\xc9\xfdr\x92\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\x19A|Fs,\xf3M\x1a\x1a\xfby\xc3\xe7\xe2\u034e\xec\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1-w\xae\x01\xa9-5\x11{\xacpZ\xac\u0642\xd0.t\xc1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd15yK\x14\x9a\x18\xe1G\xd1nb\x1ai1\xf0\xa4\n\x96\x9a\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xd1C%8\xe3[vd\x95j\u4563*\xbd\xf0A\xa7\xa2\x1c\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xd1C\x82g#\x17\x04\xfcr\x80\xd5c\xad\xf4v8D\xa8\a\"\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd1S\x8e\x9a\x87\u5729\xec\x8eX&\xa5\xb7\x93\xf9\x9f\x96\xc4\u00c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd1d\x85\x03\xb1\xcc\u0178\xbe\x03\xfa\x1e\xc4\xf3\xee&~j\xdf{\x8a\x01;\xef\xbfQ\xee\xc0\x90\x00\x00\xe0\x94\xd1h,!Y\x01\x8d\xc3\xd0\u007f\b$\n\x8c`m\xafe\xf8\xe1\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xd1q\xc3\xf2%\x8a\xef5\xe5\x99\xc7\xda\x1a\xa0s\x00#M\xa9\xa6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1w\x8c\x13\xfb\xd9h\xbc\b<\xb7\xd1\x02O\xfe\x1fI\xd0,\xaa\x89\xd9\xec\xb4\xfd \x8eP\x00\x00\u07d4\xd1\u007f\xbe\"\xd9\x04b\xed7(\x06p\xa2\xea\v0\x86\xa0\xd6\u0589\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\u0441\x1cU\x97i\x80\xf0\x83\x90\x1d\x8a\r\xb2i\"-\xfb\\\xfe\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u044e\xb9\xe1\u0485\u06be\x93\xe5\u053a\xe7k\xee\xfeC\xb5!\xe8\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\u0453\xe5\x83\xd6\a\x05c\xe7\xb8b\xb9aJG\u9509\xf3\xe5\x8965f3\xeb\xd8\xea\x00\x00\u07d4\u0457\x8f.4@\u007f\xab\x1d\xc2\x18=\x95\xcf\xdab`\xb3Y\x82\x89*\xb7\xb2`\xff?\xd0\x00\x00\u07d4\u045c\xaf9\xbb7\u007f\xdf,\xf1\x9b\xd4\xfbRY\x1c&1\xa6<\x8965\u026d\xc5\u07a0\x00\x00\u0794\u0463\x96\xdc\u06b2\xc7IA0\xb3\xfd0x 4\r\xfd\x8c\x1f\x88\xf9\"P\xe2\xdf\xd0\x00\x00\xe0\x94\u0467\x1b-\bX\xe82p\b]\x95\xa3\xb1T\x96P\x03^#\x8a\x03'\xbb\t\xd0j\xa8P\x00\x00\u07d4\u046c\xb5\xad\xc1\x189s%\x8dk\x85$\xff\xa2\x8f\xfe\xb2=\xe3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0473\u007f\x03\xcb\x10t$\xe9\xc4\xddW\\\xcdOL\xeeW\xe6\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\u0475\xa4T\xac4\x05\xbbAy \x8cl\x84\xde\x00k\u02db\xe9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xc4YT\xa6+\x91\x1a\xd7\x01\xff.\x90\x13\x1e\x8c\xeb\x89\xc9\\\x89K\x91\xa2\xdeE~\x88\x00\x00\u07d4\xd1\xc9np\xf0Z\xe0\xe6\xcd`!\xb2\b7P\xa7q|\xdeV\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\u0571\u007f\xfe-{\xbby\xcc}y0\xbc\xb2\xe5\x18\xfb\x1b\xbf\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd1\xda\f\x8f\xb7\xc2\x10\xe0\xf2\xeca\x8f\x85\xbd\xae}>sK\x1c\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd1\xddy\xfb\x15\x81`\xe5\xb4\xe8\xe2?1.j\x90\u007f\xbcMN\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xdeZ\xad:_\xd8\x03\U00071bb6\x10<\xb8\xe1O\xe7#\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd1\xe1\xf2\xb9\xc1l0\x98t\xde\xe7\xfa\xc3&u\xaf\xf1)\u00d8\x89\x03\xf2M\x8eJ\x00p\x00\x00\xe0\x94\xd1\xe5\xe24\xa9\xf4Bf\xa4\xa6$\x1a\x84\u05e1\xa5Z\u0567\xfe\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd1\xeaMr\xa6{[>\x0f1UY\xf5+\xd0aMq0i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1\xee\x90YW\xfe|\xc7\x0e\xc8\xf2\x86\x8bC\xfeG\xb1?\xeb\xff\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\xd1\xf1iM\"g\x1bZ\xadj\x94\x99\\6\x9f\xbea3go\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\xf4\xdc\x1d\u06ca\xbb\x88H\xa8\xb1N%\xf3\xb5Z\x85\x91\xc2f\x89\r\x8drkqw\xa8\x00\x00\u07d4\xd1\xfe\u042e\xe6\xf5\xdf\xd7\xe2Wi%L<\xfa\xd1Z\xde\u032a\x89'\x92\xc8\xfcKS(\x00\x00\u07d4\xd2\x05\x1c\xb3\xcbg\x04\xf0T\x8c\u0210\xab\n\x19\xdb4\x15\xb4*\x89\x12\x1b.^ddx\x00\x00\u07d4\xd2\x06\xaa\u07736\xd4^yr\xe9<\xb0uG\x1d\x15\x89{]\x89 \x86\xac5\x10R`\x00\x00\u07d4\xd2\tH+\xb5I\xab\xc4w{\xeam\u007fe\x00b\xc9\xc5z\x1c\x89\x11e\x1a\xc3\xe7\xa7X\x00\x00\u07d4\xd2\r\xcb\vxh+\x94\xbc0\x00(\x14H\xd5W\xa2\v\xfc\x83\x890\x84\x9e\xbe\x166\x9c\x00\x00\u07d4\xd2\x10{57&\u00e2\xb4ef\xea\xa7\xd9\xf8\v]!\xdb\xe3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\x11\xb2\x1f\x1b\x12\xb5\ta\x81Y\r\xe0~\xf8\x1a\x89S~\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd2\x18\xef\xb4\u06d8\x1c\xddjy\u007fK\u050c|&)<\xeb@\x89\xa1Fk1\xc6C\x1c\x00\x00\xe0\x94\xd2\x1asA\xeb\x84\xfd\x15\x10T\xe5\u31fb%\xd3nI\x9c\t\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2$\xf8\x80\xf9G\x9a\x89\xd3/\t\xe5+\u9432\x88\x13\\\xef\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xd2/\f\xa4\xcdG\x9ef\x17u\x05;\xccI\xe3\x90\xf6p\u074a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd21\x92\x975\x13!\x02G\x1b\xa5\x90\a\xb6dL\xc0\xc1\xde>\x8967\tlK\xcci\x00\x00\u07d4\xd25\xd1\\\xb5\xec\xee\xbba)\x9e\x0e\x82\u007f\xa8'H\x91\x1d\x89\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2:$\xd7\xf9F\x83C\xc1C\xa4\x1ds\xb8\x8f|\xbec\xbe^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2=z\xff\xac\xdc>\x9f=\xaez\xfc\xb4\x00oX\xf8\xa4F\x00\x89\xc3(\t>a\xee@\x00\x00\u07d4\xd2C\x18L\x80\x1e]y\xd2\x06?5x\u06ee\x81\u7ce9\u02c9k\u0722h\x1e\x1a\xba\x00\x00\u07d4\xd2KfD\xf49\xc8\x05\x1d\xfcd\u04c1\xb8\xc8lu\xc1u8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2K\xf1--\xdfE}\xec\xb1xt\xef\xde R\xb6\\\xbbI\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2Q\xf9\x03\xae\x18rrY\xee\xe8A\xa1\x89\xa1\xf5i\xa5\xfdv\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd2R\x96\v\v\xf6\xb2\x84\x8f\u07ad\x80\x13m\xb5\xf5\a\xf8\xbe\x02\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2X\x1aU\xce#\xab\x10\u062d\x8cD7\x8fY\a\x9b\xd6\xf6X\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xd2Z\xec\xd7\xeb\x8b\xd64[\x06;]\xbd'\x1cw\xd3QD\x94\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xd2|#O\xf7\xac\xca\xce=\x99g\b\xf8\xf9\xb0Ip\xf9}6\x89Hz\x9a0E9D\x00\x00\u07d4\u0482\x98RM\xf5\xecK$\xb0\xff\xb9\u07c5\x17\n\x14Z\x9e\xb5\x89\x0f\x98\xa3\xb9\xb37\xe2\x00\x00\xe0\x94\u0483\xb8\xed\xb1\n%R\x8aD\x04\xde\x1ce\xe7A\r\xbc\xaag\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u0484\xa5\x03\x82\xf8:am9\xb8\xa9\xc0\xf3\x96\xe0\ubfe9]\x8966\xc2^f\xec\xe7\x00\x00\u07d4\u0488\xe7\xcb{\xa9\xf6 \xab\x0ftR\xe5\bc=\x1cZ\xa2v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u049d\xc0\x8e\xfb\xb3\xd7.&?x\xabv\x10\xd0\"m\xe7k\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u04a00\xac\x89R2_\x9e\x1d\xb3x\xa7\x14\x85\xa2N\x1b\a\xb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u04a4y@CG\xc5T:\xab)*\xe1\xbbJo\x15\x83W\xfa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u04a5\xa0$#\nW\xcc\xc6fv\v\x89\xb0\xe2l\xaf\u0449\u01ca\n\x96YZ\\n\x8a?\x80\x00\u07d4\u04a8\x03'\xcb\xe5\\L{\xd5\x1f\xf9\xdd\xe4\xcad\x8f\x9e\xb3\xf8\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u04a8Oug\\b\xd8\f\x88ulB\x8e\xee+\xcb\x18T!\x89A\rXj \xa4\xc0\x00\x00\u07d4\u04ab\xd8J\x18\x10\x93\xe5\xe2)\x13oB\xd85\xe8#]\xe1\t\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\u04ac\r:X`^\x1d\x0f\x0e\xb3\xde%\xb2\xca\xd1)\xed`X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u04bfg\xa7\xf3\xc6\xceV\xb7\xbeAg]\xbb\xad\xfe~\xa9:3\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd2\xdb\xeb\xe8\x9b\x03W\xae\xa9\x8b\xbe\x8eIc8\u07bb(\xe8\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2\xe2\x1e\xd5hh\xfa\xb2\x8e\tG\x92z\xda\xf2\x9f#\xeb\xadl\x89l\x18O\x13U\xd0\xe8\x00\x00\u07d4\xd2\xe8\x17s\x8a\xbf\x1f\xb4\x86X?\x80\xc3P1\x8b\xed\x86\f\x80\x89\r\x02\xce\xcf_]\x81\x00\x00\u07d4\xd2\xed\xd1\xdd\xd6\xd8m\xc0\x05\xba\xebT\x1d\"\xb6@\xd5\xc7\xca\xe5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\xf1\x99\x8e\x1c\xb1X\f\xecOl\x04}\xcd=\xce\xc5L\xf7<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2\xf2A%]\xd7\xc3\xf7<\a\x040q\xec\b\xdd\xd9\xc5\xcd\xe5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd2\xffg \x16\xf6;/\x859\x8fJo\xed\xbb`\xa5\r<\u0389\x12\x91$o[sJ\x00\x00\u07d4\xd3\rLC\xad\xcfU\xb2\xcbS\u0583#&A4I\x8d\x89\u038965\u026d\xc5\u07a0\x00\x00\u07d4\xd3\x0e\xe9\xa1+Mh\xab\xac\xe6\xba\u029a\u05ff\\\xd1\xfa\xf9\x1c\x89QO\xcb$\xff\x9cP\x00\x00\u07d4\xd3\x11\x8e\xa3\xc85\x05\xa9\u0613\xbbg\xe2\xde\x14-Sz>\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd3\x11\xbc\u05eaN\x9bO8?\xf3\xd0\u05b6\xe0~!\xe3p]\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd3\x15\xde\xea\x1d\x8c\x12q\xf9\xd11\x12c\xabG\xc0\a\xaf\xb6\xf5\x89\x03\xc8\x1dNeK@\x00\x00\u07d4\xd3+,y\xc3dx\xc5C\x19\x01\xf6\xd7\x00\xb0M\xbe\x9b\x88\x10\x89\x15w\x9a\x9d\xe6\xee\xb0\x00\x00\u07d4\xd3+EVF\x14Ql\x91\xb0\u007f\xa9\xf7-\xcfx|\xceN\x1c\x89\x0f\xc6o\xae7F\xac\x00\x00\u07d4\xd30r\x811\xfe\x8e:\x15Hz4W<\x93E~*\xfe\x95\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd31\xc8#\x82Z\x9eRc\xd0R\u0611]M\xcd\xe0z\\7\x89\x1e\x93\x12\x83\xcc\xc8P\x00\x00\u07d4\xd33btE\xf2\u05c7\x90\x1e\xf3;\xb2\xa8\xa3g^'\xff\xec\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3<\xf8+\xf1LY&@\xa0\x86\b\x91L#py\u057e4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3Mp\x8ds\x98\x02E3\xa5\xa2\xb20\x9b\x19\xd3\xc5Qq\xbb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3N\x03\xd3j+\xd4\u045a_\xa1b\x18\xd1\xd6\x1e?\xfa\v\x15\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\xd3Pu\xcaa\xfeY\xd1#\x96\x9c6\xa8-\x1a\xb2\xd9\x18\xaa8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xd3g\x00\x9a\xb6X&;b\xc23:\x1c\x9eA@I\x8e\x13\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3g\x9aG\xdf-\x99\xa4\x9b\x01\u024d\x1c>\f\x98|\xe1\xe1X\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94\u04cf\xa2\xc4\xcc\x14z\xd0j\u0562\xf7Uy(\x1f\"\xa7\xcc\x1f\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\u04da]\xa4`9+\x94\v<i\xbc\x03u{\xf3\xf2\xe8$\x89\x8a\x01|\x83\xa9}kl\xa5\x00\x00\u07d4\u04db|\xbc\x94\x00?\xc9H\xf0\xcd\xe2{\x10\r\xb8\xcc\xd6\xe0c\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u04e1\x0e\u01e5\xc92I\x99\u075e\x9bk\xde|\x91\x1eXK\u0689 \x86\xac5\x10R`\x00\x00\u07d4\u04e9A\xc9a\xe8\u028b\x10p\xf2<mm\r*u\x8aDD\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u04fbY\xfa1%\x8b\xe6/\x8e\xd22\xf1\xa7\xd4{J\vA\xee\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u04fcs\t7\xfau\xd8E&\x16\xad\x1e\xf1\xfe\u007f\xff\xe0\xd0\xe7\x89\x04\x84\xe4\xde\xd2\xea\xe3\x80\x00\u07d4\xd3\xc2MK:^\x0f\xf8\xa4b-Q\x8e\xdds\xf1j\xb2\x86\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd3\xc6\xf1\xe0\xf5\x0e\xc3\u04a6~k\xcd\x19>\u01ee8\xf1e\u007f\x8a\x01f\xc5H\b\x89\xdbw\x00\x00\xe0\x94\xd3\xd6\xe9\xfb\x82T/\u049e\xd9\xea6\t\x89\x1e\x15\x13\x96\xb6\xf7\x8a\voX\x8a\xa7\xbc\xf5\xc0\x00\x00\xe0\x94\xd3\xda\u0476\u040dE\x81\u032ee\xa8s-\xb6\xaci\xf0\u019e\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xd3\xdf;S\xcb;GU\xdeT\xe1\x80E\x1c\xc4L\x9e\x8a\u0a89#\u0114\t\xb9w\x82\x80\x00\u07d4\xd3\xf8s\xbd\x99V\x13W\x89\xab\x00\xeb\xc1\x95\xb9\"\xe9K%\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\x02\xb4\xf6\xa0\x99\xeb\xe7\x16\xcb\x14\xdfOy\xc0\xcd\x01\xc6\a\x1b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd4\r\x00U\xfd\x9a8H\x8a\xff\x92?\xd0=5\xecF\xd7\x11\xb3\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xd4\x0e\xd6j\xb3\xce\xff$\xca\x05\xec\xd4q\ufd12\xc1__\xfa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4\x18\x87\v\xc2\xe4\xfa{\x8aa!\xae\br\xd5RG\xb6%\x01\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xd4\x1d\u007f\xb4\x9f\xe7\x01\xba\xac%qpBl\u0273\x8c\xa3\xa9\xb2\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xd4 U\x92\x84@U\xb3\u01e1\xf8\f\xef\xe3\xb8\xebP\x9b\xcd\xe7\x89\t\xb3\xbf\xd3B\xa9\xfc\x80\x00\u07d4\xd4+ \xbd\x03\x11`\x8bf\xf8\xa6\xd1[*\x95\xe6\xde'\u017f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd44O}\\\xade\xd1~\\-\x0es#\x94=ob\xfe\x92\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xd4>\xe48\xd8=\xe9\xa3ub\xbbN(l\xb1\xbd\x19\xf4\x96M\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd4C4\xb4\xe2:\x16\x9a\f\x16\xbd!\xe8f\xbb\xa5-\x97\x05\x87\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\xe0\x94\xd4M\x81\xe1\x8fF\xe2\u03f5\xc1\xfc\xf5\x04\x1b\xc8V\x97g\xd1\x00\x8a\a\xb4B\xe6\x84\xf6Z\xa4\x00\x00\u07d4\xd4OJ\xc5\xfa\xd7k\xdc\x157\xa3\xb3\xafdr1\x9bA\r\x9d\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xd4O^\xdf+\xcf$3\xf2\x11\xda\xdd\f\xc4P\xdb\x1b\x00\x8e\x14\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xd4Oj\u00d2;_\xd71\xa4\xc4YD\xecO~\xc5*j\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd4[3A\xe8\xf1\\\x802\x93 \u00d7~;\x90\xe7\x82j~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4]]\xaa\x13\x8d\xd1\xd3t\xc7\x1b\x90\x19\x91h\x11\xf4\xb2\nN\x89\x1f9\x9b\x148\xa1\x00\x00\x00\u07d4\xd4`\xa4\xb9\b\xdd+\x05gY\xb4\x88\x85\vf\xa88\xfcw\xa8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd4g\xcf\x06L\bq\x98\x9b\x90\u0632\xeb\x14\xcc\xc6;6\b#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd4k\xaea\xb0'\xe5\xbbB.\x83\xa3\xf9\xc9?<\x8f\xc7}'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4o\x82#E)\x82\xa1\xee\xa0\x19\xa8\x81n\xfc-o\xc0\ah\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xd4uG\u007f\xa5c\x90\xd30\x17Q\x8dg\x11\x02\u007f\x05\U0008dfc9k\x11\x133\xd4\xfdL\x00\x00\u07d4\xd4|$.\xdf\xfe\xa0\x91\xbcT\xd5}\xf5\xd1\xfd\xb91\x01Gl\x89\x9d\xf7\u07e8\xf7`H\x00\x00\u07d4\xd4}\x86\x85\xfa\xee\x14|R\x0f\u0646p\x91u\xbf/\x88k\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\u007fP\u07c9\xa1\xcf\xf9e\x13\xbe\xf1\xb2\xae:)q\xac\xcf,\x89-\x89W}}@ \x00\x00\u07d4\u0502\xe7\xf6\x8eA\xf28\xfeQx)\xde\x15G\u007f\xe0\xf6\xdd\x1d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u0507\x9f\xd1+\x1f:'\xf7\xe1\tv\x1b#\xca4<H\xe3\u0609$\x1a\x9bOaz(\x00\x00\u07d4\u050e?\x93W\xe3\x03Q8A\xb3\xf8K\u0683\xfc\x89ru\x87\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u051au\xbb\x93?\xca\x1f\u029a\xa10:d\xb6\xcbD\xea0\xe1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0530\x85\xfb\bo=\rh\xbf\x12\x92k\x1c\xc3\x14,\xae\x87p\x89\u0213\u041c\x8fQP\x00\x00\u07d4\u0532\xff;\xae\x19\x93\xff\xeaM;\x18\x021\xdaC\x9fu\x02\xa2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0533\x8a_\xdbc\xe0\x17\x14\xe9\x80\x1d\xb4{\u0250\xbdP\x91\x83\x8a\x01E4\xd9[\xef\x90\\\x00\x00\u07d4\u0538\xbd\xf3\u07daQ\xb0\xb9\x1d\x16\xab\xbe\xa0[\xb4x<\x86a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd4\xc4\u0467\xc3\xc7I\x84\xf6\x85{/_\a\xe8\xfa\xceh\x05m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\u01act.|\x85}J\x05\xa0L3\xd4\xd0\\\x14gW\x1d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd4\xcb!\xe5\x90\u0160\xe0h\x016j\xff4,}}\xb1d$\x89\x1a\u01e0\x8e\xad\x02\xf8\x00\x00\u07d4\xd4\xd9,b\xb2\x80\xe0\x0fbm\x86W\xf1\xb8af\xcb\x1ft\x0f\x89\n\xd7\xf264\xcb\xd6\x00\x00\u07d4\xd4\ubc52\x9a#\x87\x1c\xf7\u007f\xe0I\xab\x96\x02\xbe\b\xbe\ns\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xd4\xeeI\x19\xfb7\xf2\xbb\x97\f?\xffT\xaa\xf1\xf3\u0766\xc0?\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xd4\xfe\xed\x99\xe8\x91|\\TXc_6\x03\xec\xb7\xe8\x17\xa7\u0409\x10C\xc4<\xde\x1d9\x80\x00\u07d4\xd4\xffF >\xfa#\x06K\x1c\xaf\x00Qn(pJ\x82\xa4\xf8\x89Hz\x9a0E9D\x00\x00\u07d4\xd5\x00\xe4\xd1\u0242K\xa9\xf5\xb65\u03e3\xa8\xc2\u00cb\xbdL\xed\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5\b\u04dcp\x91oj\xbcL\xc7\xf9\x99\xf0\x11\xf0w\x10X\x02\x89\x05rM$\xaf\xe7\u007f\x00\x00\u07d4\xd5\x0f\u007f\xa0>8\x98v\u04d0\x8b`\xa57\xa6pc\x04\xfbV\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd5\x13\xa4P\x80\xff/\xeb\xe6,\u0545J\xbe)\xeeDg\xf9\x96\x89\bN\x13\xbcO\xc5\xd8\x00\x00\u07d4\xd5'o\f\xd5\xff\xd5\xff\xb6?\x98\xb5p=U\x94\xed\xe0\x83\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5)KfbB0;m\xf0\xb1\u020d7B\x9b\xc8\xc9e\xaa\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xd5*\xec\xc6I98\xa2\x8c\xa1\xc3g\xb7\x01\xc2\x15\x98\xb6\xa0.\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xd5<V\u007f\f?\xf2\xe0\x8b}Y\xe2\xb5\xc74\x85C\u007f\u014d\x89 \x86\xac5\x10R`\x00\x00\u07d4\xd5A\xac\x18z\xd7\xe0\x90R-\xe6\xda2\x13\xe9\xa7\xf4C\x96s\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd5K\xa2\xd8V\x81\xdc\x13\x0e[\x9b\x02\xc4\xe8\xc8Q9\x1f\u0679\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xd5U\b\xad\xbb\xbe\x9b\xe8\x1b\x80\xf9zn\xa8\x9a\xddh\xdagO\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd5U\f\xaa\xf7C\xb07\xc5o\xd2U\x8a\x1c\x8e\xd25\x13\aP\x8a\x01!\xe4\u05106%[\x00\x00\u07d4\xd5Xm\xa4\u5543\xc8\xd8l\xcc\xf7\x1a\x86\x19\u007f\x17\x99gI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd5\\\x1c\x8d\xfb\xe1\xe0,\xac\xbc\xa6\x0f\xdb\xdd@[\t\xf0\xb7_\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd5a\u02fc\x05Q]\xe7:\xb8\u03de\xae\x13W4\x1e}\xfd\xf4\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xd5j\x14Mz\xf0\xae\x8d\xf6I\xab\xaeSZ\x15\x98:\xa0M\x02\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xd5r0\x91i\xb1@.\xc8\x13\x1a\x17\xa6\xaa\xc3\"/\x89\xe6\xeb\x8a\x02\xec\x19x\xc4wf\xa0\x00\x00\u07d4\xd5xvh\xc2\xc5\x17[\x01\xa8\xee\x1a\xc3\xec\xc9\u0232\xab\xa9Z\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\u0548\u00e5\xdf\"\x81\x85\u064e\xe7\xe6\aH%\\\u07a6\x8b\x01\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u054aR\xe0x\xa8\x05Yk\rV\xeaJ\xe13Z\xf0\x1cf\xeb\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\u0550>\x99x\xee \xa3\x8c?I\x8dc\xd5\u007f1\xa3\x9fj\x06\x8a\x022\xb3o\xfcg*\xb0\x00\x00\u07d4\u05568\xd3\xc5\xfa\xa7q\x1b\xf0\x85t_\x9d[\xdc#\u0518\u0609lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u055d\x92\xd2\xc8p\x19\x80\xcc\a<7]r\n\xf0dt<\f\x8a\x04\x05\xfd\xf7\u5bc5\xe0\x00\x00\u07d4\u0567\xbe\xc32\xad\xde\x18\xb3\x10KW\x92Tj\xa5\x9b\x87\x9bR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0571\x17\xec\x11n\xb8FA\x89a\xeb~\xdbb\x9c\xd0\xddi\u007f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u0572\x84\x04\x010\xab\xf7\xc1\xd1cq#q\xcc~(\xadf\u0689j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0579\xd2w\u062a\xd2\x06\x97\xa5\x1fv\xe2\tx\x99k\xff\xe0U\x89\a\xc3\xfe<\aj\xb5\x00\x00\u07d4\u057d^\x84U\xc10\x16\x93W\xc4q\xe3\u6077\x99jrv\x89-\x9e(\x8f\x8a\xbb6\x00\x00\u07d4\xd5\u02e5\xb2k\xea]s\xfa\xbb\x1a\xba\xfa\xcd\xef\x85\xde\xf3h\u0309\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd5\xceU\u0476/YC<!&\xbc\xec\t\xba\xfc\x9d\xfa\xa5\x14\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xd5\xe5Q\x00\xfb\u0455k\xbe\xd2\xcaQ\x8dK\x1f\xa3v\x03+\v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd5\xe5\xc15\xd0\xc4\xc3094q\x19\x93\xd0\xd1o\xf9\u7ea0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd5\xe6V\xa1\xb9\x16\xf9\xbfE\xaf\xb0}\u062f\xafs\xb4\xc5oA\x89\x05B%:\x12l\xe4\x00\x00\xe0\x94\xd5\xeaG,\xb9F`\x18\x11\n\xf0\f7I[\\,q1\x12\x8a\x01\x0e\xeehl\x85OD\x00\x00\u07d4\xd5\xf0uR\xb5\u0193\xc2\x00g\xb3x\xb8\t\xce\xe8S\xb8\xf16\x89\x1bg\xc6\u07c8\xc6\xfa\x00\x00\u07d4\xd5\xf7\xc4\x1e\ar\x9d\xfam\xfcd\xc4B1`\xa2,`\x9f\u04c9a\t=|,m8\x00\x00\u07d4\xd6\x04\xab\xceC0\x84.=9l\xa7=\xdbU\x19\xed>\xc0?\x89\b\xe3\x1f\xe1h\x9d\x8a\x00\x00\u07d4\xd6\x06Q\xe3\x93x4#\xe5\xcc\x1b\xc5\xf8\x89\xe4N\xf7\xea$>\x89\x15\x9ev7\x11)\xc8\x00\x00\u07d4\xd6\t\xbfO\x14n\xeak\r\xc8\xe0m\xdc\xf4D\x8a\x1f\xcc\xc9\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\t\xec\v\xe7\r\n\xd2ong\xc9\xd4v+R\xeeQ\x12,\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6\nRX\a(R\r\xf7Tk\xc1\xe2\x83)\x17\x88\u06ee\f\x8964\x89\xef?\xf0\xd7\x00\x00\u07d4\xd6\v$s!\xa3*Z\xff\xb9k\x1e'\x99'\xccXM\xe9C\x89z\xd0 \xd6\xdd\xd7v\x00\x00\u07d4\xd6\x11\x02v\xcf\xe3\x1eB\x82ZW\u007fkC]\xbc\xc1\f\xf7d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd6\x12Y{\xc3\x17C\u01c63\xf63\xf29\xb1\xe9Bk\xd9%\x8a\x10\x17\xf7\u07d6\xbe\x17\x80\x00\x00\u07d4\xd6#J\xafE\xc6\xf2.f\xa2%\xff\xb9:\xddb\x9bN\xf8\x0f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6.\u06d6\xfc\u259a\xaflT^\x96|\xf1\xc0\xbc\x80R\x05\x89\x04\xa5eSjZ\u0680\x00\u07d4\xd60\v2\x15\xb1\x1d\xe7b\xec\xdeKp\xb7\x92}\x01)\x15\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd69]\xb5\xa4\xbbf\xe6\x0fL\xfb\xcd\xf0\x05{\xb4\xd9xb\xe2\x891T\xc9r\x9d\x05x\x00\x00\xe0\x94\xd6J-P\xf8\x85\x857\x18\x8a$\xe0\xf5\r\xf1h\x1a\xb0~\u05ca\b7Z*\xbc\xca$@\x00\x00\u07d4\xd6X\n\xb5\xedL}\xfaPo\xa6\xfed\xad\\\xe1)pw2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6Y\x8b\x13\x86\xe9<\\\u02d6\x02\xffK\xbb\xec\xdb\xd3p\x1d\u0109\f%\xf4\xec\xb0A\xf0\x00\x00\u07d4\xd6dM@\xe9\v\xc9\u007f\xe7\xdf\xe7\u02bd2i\xfdW\x9b\xa4\xb3\x89\b\x9e\x91y\x94\xf7\x1c\x00\x00\xe0\x94\xd6g\f\x03m\xf7T\xbeC\xda\u074fP\xfe\xea(\x9d\x06\x1f\u058a\x01D\xa2\x904H\xce\xf7\x80\x00\u07d4\xd6hR:\x90\xf0)=e\xc58\xd2\xddlWg7\x10\x19n\x89\x02$,0\xb8S\xee\x00\x00\u07d4\xd6j\xb7\x92\x94\aL\x8bb}\x84-\xabA\xe1}\xd7\f]\xe5\x8965\u026d\xc5\u07a0\x00\x00\u0794\xd6j\xcc\r\x11\xb6\x89\u03a6\xd9\xea_\xf4\x01L\"J]\xc7\u0108\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xd6m\xdf\x11Y\xcf\"\xfd\x8czK\xc8\u0540wV\xd43\xc4>\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\u0587\xce\xc0\x05\x90\x87\xfd\xc7\x13\xd4\xd2\xd6^w\xda\xef\xed\xc1_\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\u0588\xe7\x85\u024f\x00\xf8K:\xa1S3U\u01e2X\xe8yH\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u05a2.Y\x8d\xab\u04ce\xa6\xe9X\xbdy\u050d\u0756\x04\xf4\u07c965\u026d\xc5\u07a0\x00\x00\u07d4\u05a7\xacM\xe7\xb5\x10\xf0\xe8\xdeQ\x9d\x97?\xa4\xc0\x1b\xa84\x00\x89e\xea=\xb7UF`\x00\x00\u07d4\u05ac\xc2 \xba.Q\xdf\xcf!\xd4C6\x1e\xeav\\\xbd5\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05ac\xff\u043f\u065c8.{\xd5o\xf0\xe6\x14J\x9eR\xb0\x8e\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xd6\xc0\u043c\x93\xa6.%qtp\x0e\x10\xf0$\u0232?\x1f\x87\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd6\xcf\\\x1b\u03dd\xa6b\xbc\xea\"U\x90P\x99\xf9\xd6\xe8M\u030a\x01\u011eB\x01W\xd9\xc2\x00\x00\u07d4\xd6\xd05r\xa4RE\xdb\xd46\x8cO\x82\xc9W\x14\xbd!g\xe2\x89?\x00\xc3\xd6f\x86\xfc\x00\x00\u07d4\xd6\xd6wiX\xee#\x14:\x81\xad\xad\xeb\b8 \t\xe9\x96\u0089\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd6\xd9\xe3\x0f\bB\x01*qv\xa9\x17\xd9\xd2\x04\x8c\xa0s\x87Y\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6\xe0\x9e\x98\xfe\x13\x003!\x04\xc1\xca4\xfb\xfa\xc5T6N\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\xe8\xe9z\u90db\x9e\xe5\a\xee\xdb(\xed\xfbtw\x03\x149\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\uea18\u052e+q\x80'\xa1\x9c\xe9\xa5\xebs\x00\xab\xe3\u0289\x01}J\xce\xeec\u06c0\x00\xe0\x94\xd6\xf1\xe5[\x16\x94\b\x9e\xbc\xb4\xfe}x\x82\xaaf\u0217av\x8a\x04<#\xbd\xbe\x92\x9d\xb3\x00\x00\u07d4\xd6\xf4\xa7\xd0N\x8f\xaf \xe8\xc6\ub15c\xf7\xf7\x8d\xd2=z\x15\x89\a$\xde\xd1\xc7H\x14\x00\x00\u07d4\xd6\xfc\x04F\u01a8\xd4\n\xe3U\x1d\xb7\xe7\x01\xd1\xfa\x87nJI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x03\u01a4\xf1\x1d`\x19Ey\u054c'f\xa7\xef\x16\xc3\n)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x05%\x19uj\xf4%\x90\xf1S\x91\xb7#\xa0?\xa5d\xa9Q\x89\xfa61H\r\x01\xfd\x80\x00\u07d4\xd7\na+\xd6\u0769\xea\xb0\xdd\xdc\xffJ\xafA\"\u04cf\xea\xe4\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xd7\n\xd2\xc4\xe9\uefe67\xefV\xbdHj\u04a1\xe5\xbc\xe0\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x14\f\x8eZC\a\xfa\xb0\xcc'\xba\u0752\x95\x01\x8b\xf8yp\x89\x05\xf1\x01kPv\xd0\x00\x00\u07d4\xd7\x16J\xa2a\xc0\x9a\u0672\xb5\x06\x8dE>\xd8\xebj\xa10\x83\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\x1eC\xa4Qw\xadQ\xcb\xe0\xf7!\x84\xa5\xcbP9\x17(Z\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x1f\xb10\xf0\x15\fVRi\xe0\x0e\xfbC\x90+R\xa4U\xa6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\"W8\xdc\xf3W\x848\xf8\xe7\u0233\x83~B\xe0J&/\x89\x18+\x8c\ubec3\xaa\x00\x00\u07d4\xd7'MP\x80M\x9cw\u0693\xfaH\x01V\xef\xe5{\xa5\x01\u0789i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd71\xbbk_<79^\t\u03ac\xcd\x14\xa9\x18\xa6\x06\a\x89\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xd7>\xd2\u0645\xb5\xf2\x1bU\xb2td;\xc6\xda\x03\x1d\x8e\u074d\x8a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xd7D\xac~S\x10\xbeijc\xb0\x03\xc4\v\xd097\x05a\u0189Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\xd7Jn\x8dj\xab4\u0385\x97h\x14\xc12{\xd6\xea\a\x84\u048a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xd7ZP*[gr\x87G\x0fe\u016aQ\xb8|\x10\x15\x05r\x8910\xb4dc\x85t\x00\x00\u07d4\xd7m\xba\xeb\xc3\rN\xf6{\x03\xe6\xe6\xec\xc6\xd8N\x00MP-\x89mv\xb9\x18\x8e\x13\x85\x00\x00\u07d4\xd7q\xd9\xe0\u028a\b\xa1\x13wW1CN\xb3'\x05\x99\xc4\r\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7x\x8e\xf2\x86X\xaa\x06\xccS\xe1\xf3\xf0\xdeX\xe5\xc3q\xbex\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xd7x\x92\xe2';#]v\x89\xe40\xe7\xae\ud73c\xe8\xa1\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u05c1\xf7\xfc\t\x18F\x11V\x85p\xb4\x98n,r\x87+~\u0409\x01\x15\x95a\x06]]\x00\x00\u07d4\u05c5\xa8\xf1\x8c8\xb9\xbcO\xfb\x9b\x8f\xa8\xc7r{\xd6B\xee\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u05ce\xcd%\xad\xc8k\xc2\x05\x1d\x96\xf6Sd\x86kB\xa4&\xb7\x89\xd20X\xbf/&\x12\x00\x00\xe0\x94\u05cf\x84\xe3\x89D\xa0\xe0%_\xae\xceH\xbaIP\u053d9\u048a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\u05d4\x83\xf6\xa8DO%I\xd6\x11\xaf\xe0,C-\x15\xe1\x10Q\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05d85\xe4\x04\xfb\x86\xbf\x84_\xba\t\rk\xa2^\f\x88f\xa6\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\u05da\xff\x13\xba-\xa7]F$\f\xac\n$g\xc6V\x94\x98#\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\u05dd\xb5\xabCb\x1az=\xa7\x95\xe5\x89)\xf3\xdd%\xafg\u0649lj\xccg\u05f1\xd4\x00\x00\u07d4\u05e1C\x1e\xe4S\xd1\xe4\x9a\x05P\xd1%hy\xb4\xf5\xd1\x02\x01\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\u05ed\t\xc6\xd3&WhSU\xb5\xc6\uc39fW\xb4\ube42\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u05f7@\xdf\xf8\xc4Wf\x8f\xdft\xf6\xa2f\xbf\xc1\u0737#\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7\u0080>\u05f0\xe0\x83sQA\x1a\x8ef7\xd1h\xbc[\x05\x8a\x06A\xda\xf5\xc9\x1b\xd95\x80\x00\u07d4\xd7\xc6&]\xea\x11\x87l\x90;q\x8eL\u062b$\xfe&[\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xca\u007f\xdc\xfe\xbeE\x88\xef\xf5B\x1d\x15\"\xb6\x13(\xdf{\xf3\x89\xd8\xe6\x00\x1el0+\x00\x00\u07d4\xd7\u037dA\xff\xf2\r\xf7'\xc7\vbU\xc1\xbav\x06\x05Th\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xd1W\xe4\xc0\xa9d7\xa6\u0485t\x1d\xd2>\xc46\x1f\xa3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xd2\xc6\xfc\xa8\xad\x1fu9R\x10\xb5}\xe5\xdf\xd6s\x939\t\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94\xd7\xd3\xc7Y Y\x048\xb8,>\x95\x15\xbe.\xb6\xedz\x8b\x1a\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\xd7\xd7\xf2\u02a4b\xa4\x1b;0\xa3J\xeb;\xa6\x10\x10\xe2bo\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xe7J\xfd\xba\xd5^\x96\u03bcZ7O,\x8bv\x86\x80\xf2\xb0\x89\x05]\xe6\xa7y\xbb\xac\x00\x00\xe0\x94\xd7\xeb\x901b'\x1c\x1a\xfa5\xfei\xe3s\"\u0224\u049b\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd7\xeb\u0779\xf99\x87w\x9bh\x01U7T8\xdbe\xaf\xcbj\x89\x05t\x1a\xfe\xff\x94L\x00\x00\u07d4\xd7\xef4\x0ef\xb0\u05ef\xcc\xe2\n\x19\xcb{\xfc\x81\xda3\xd9N\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\xf3p\u053e\xd9\xd5|oI\u0259\xder\x9e\xe5i\xd3\xf4\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xfa_\xfb`H\xf9o\xb1\xab\xa0\x9e\xf8{\x1c\x11\xddp\x05\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\x06\x9f\x84\xb5!I?G\x15\x03\u007f2&\xb2_3\xb6\x05\x86\x89g\x8a\x93 b\xe4\x18\x00\x00\u0794\xd8\x15\xe1\xd9\xf4\xe2\xb5\xe5~4\x82k|\xfd\x88\x81\xb8Th\x90\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xd8\x1b\xd5K\xa2\xc4Jok\xeb\x15a\u058b\x80\xb5DNm\u0189?\x17\r~\xe4<C\x00\x00\xe0\x94\xd8\"QEm\xc18\x0f\x8fV\x92\xf9b\x82\x86@\xab\x9f*\x03\x8a\x01\b\x8bS\xb2\xc2\x02\xbe\x00\x00\u07d4\xd8,o\xed\xbd\xac\x98\xaf.\xed\x10\xb0\x0f2\xb0\x00V\xcaZm\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd8/\xd9\xfd\xf6\x99k\xed\xad(C\x15\x9c\x06\xf3~\t$3}\x89[\x8c\xce\xdcZ\xa7\xb0\x00\x00\u07d4\xd8:\xd2`\xe9\xa6\xf42\xfbn\xa2\x87C)\x9bJ\t\xade\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u0794\xd8C\xee\bc\u0393>\"\xf8\x9c\x80-1({\x96q\xe8\x1c\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xd8K\x92/xA\xfcWt\xf0\x0e\x14`J\xe0\xdfB\xc8U\x1e\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xd8U\xb0<\xcb\x02\x9awG\xb1\xf0s\x03\xe0\xa6dy59\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8_\u07af*a\xf9]\xb9\x02\xf9\xb5\xa5<\x9b\x8f\x92f\u00ec\x89l\xf6Z~\x90G(\x00\x00\u07d4\xd8q^\xf9\x17o\x85\v.0\xeb\x8e8'\a\xf7w\xa6\xfb\xe9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8t\xb9\u07eeEj\x92\x9b\xa3\xb1\xa2~W,\x9b,\xec\u07f3\x89\t79SM(h\x00\x00\u07d4\u0613\n9\xc7sW\xc3\n\u04e0`\xf0\v\x06\x04c1\xfdb\x89,s\xc97t,P\x00\x00\u07d4\u061b\xc2q\xb2{\xa3\xabib\xc9JU\x90\x06\xae8\xd5\xf5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0637}\xb9\xb8\x1b\xbe\x90B{b\xf7\x02\xb2\x01\xff\u009f\xf6\x18\x892m\x1eC\x96\xd4\\\x00\x00\u07d4\xd8\xcdd\xe0(N\xecS\xaaF9\xaf\xc4u\b\x10\xb9\u007f\xabV\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xd6C\x84$\x9bwg\x94\x06;V\x98x\xd5\xe3\xb50\xa4\xb2\x89\t\xa0C\u0432\xf9V\x80\x00\u07d4\xd8\xd6T \xc1\x8c#'\xccZ\xf9t%\xf8W\xe4\xa9\xfdQ\xb3\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xd8\xe5\xc9g^\xf4\xde\xed&k\x86\x95o\xc4Y\x0e\xa7\u0522}\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd8\xe8GB\x92\xe7\xa0Q`L\xa1d\xc0pw\x83\xbb(\x85\xe8\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\u07d4\xd8\xebxP>\xc3\x1aT\xa9\x016x\x1a\xe1\t\x00Lt2W\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\xee\xf4\xcfK\xeb\x01\xee \xd1\x11t\x8ba\xcbM?d\x1a\x01\x89\x94\x89#z\u06daP\x00\x00\u07d4\xd8\xf4\xba\xe6\xf8M\x91\rm}Z\xc9\x14\xb1\xe6\x83r\xf9A5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd8\xf6 6\xf0;v5\xb8X\xf1\x10?\x8a\x1d\x90\x19\xa8\x92\xb6\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xd8\xf6e\xfd\x8c\xd5\u00bc\xc6\xdd\xc0\xa8\xaeR\x1eM\u01aa``\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xd8\xf9$\fU\xcf\xf05R<m[\xd3\x00\xd3p\u070f\f\x95\x89\x0fs+f\x01ZT\x00\x00\xe0\x94\xd8\xf9EyIg%\xb5\xcbS\u05d8\\\x98\x97I\xaf\xf8I\xc0\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4\xd8\xfd\xf5FgG8\u0244\xd8\xfa\xb8W\x88\v>B\x80\xc0\x9e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xfe\b\x8f\xff\u0394\x8fQ7\xee#\xb0\x1d\x95\x9e\x84\xacB#\x89\f[T\xa9O\xc0\x17\x00\x00\u07d4\xd9\x0f0\t\xdbC~N\x11\u01c0\xbe\u0209os\x8de\xef\r\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\x10;\xb6\xb6zU\xa7\xfe\xce-\x1a\xf6-E|!x\x94m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\x13\xf0w\x19Iu<G&\xac\xaa+\xd3a\x9c\\ \xffw\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd9\x1d\x88\x91dG\x9c\xe46\xec\xe5\x17c\xe2,\xda\x19\xb2-k\x89\xb6m\x88\x12h\x00\x88\x00\x00\u07d4\xd9)\xc6]i\u057b\xae\xa5\x97bf.\xf4\x18\xbc!\xad\x92J\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd90\xb2zx\x87d\x85\xd0\xf4\x8bp\xddS6T\x96y\u028f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xd91\xac&h\xbaj\x84H\x1a\xb19sZ\xec\x14\xb7\xbf\xba\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd98=Km\x17\xb3\xf9\xcdBn\x10\xfb\x94@\x15\xc0\xd4K\xfb\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\xd9B\xdeG\x84\xf7\xa4\x87\x16\xc0\xfdK\x9dT\xa6\xe5L_/>\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd9D\u0226\x9f\xf2\xca\x12Ii\f\x12)\xc7\x19/6%\x10b\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd9JW\x88*Rs\x9b\xbe*\x06G\xc8\f$\xf5\x8a+O\x1c\x89H\xb5N*\xdb\xe1+\x00\x00\xe0\x94\xd9SB\x95<\x8a!\xe8\xb65\xee\xfa\u01c1\x9b\xea0\xf1pG\x8a\x13\xf0l\u007f\xfe\xf0]@\x00\x00\u07d4\xd9\\\x90\xff\xbeT\x84\x86G\x80\xb8gIJ\x83\u0212V\xd6\xe4\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\xd9g\x11T\x0e.\x99\x83C\xd4\xf5\x90\xb6\xfc\x8f\xac;\xb8\xb3\x1d\x89_Z@h\xb7\x1c\xb0\x00\x00\u07d4\xd9j\xc2Pt\t\u01e3\x83\xab.\xee\x18\"\xa5\xd78\xb3kV\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd9m\xb3;{Z\x95\f>\xfa-\xc3\x1b\x10\xba\x10\xa52\uf1c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd9wYe\xb7\x16Gfu\xa8\xd5\x13\xeb\x14\xbb\xf7\xb0|\xd1J\x8a\x01\x13.m-#\xc5\xe4\x00\x00\u07d4\xd9{\xc8J\xbdG\xc0[\xbfE{.\xf6Y\xd6\x1c\xa5\xe5\u43c9\x06\x9d\x17\x11\x9d\u0168\x00\x00\u07d4\xd9\u007fE&\u07a9\xb1c\xf8\xe8\xe3:k\u03d2\xfb\x90}\xe6\xec\x89\x0feJ\xafM\xb2\xf0\x00\x00\u07d4\xd9\u007f\xe6\xf5?*X\xf6\xd7mu*\xdft\xa8\xa2\xc1\x8e\x90t\x89\x10\xcd\xf9\xb6\x9aCW\x00\x00\u07d4\u0659\x99\xa2I\r\x94\x94\xa50\xca\xe4\xda\xf3\x85T\xf4\xddc>\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\u065d\xf7B\x1b\x93\x82\xe4,\x89\xb0\x06\xc7\xf0\x87p*\aW\xc0\x89\x1a\x05V\x90\xd9\u06c0\x00\x00\xe0\x94\u0677\x83\xd3\x1d2\xad\xc5\x0f\xa3\xea\u02a1]\x92\xb5h\xea\xebG\x8a\a3\xaf\x907L\x1b(\x00\x00\u07d4\xd9\xd3p\xfe\xc65v\xab\x15\xb3\x18\xbf\x9eX6M\u00a3U*\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xd9\xd4/\xd1>\xbdK\xf6\x9c\xac^\x9c~\x82H:\xb4m\xd7\xe9\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xd9\xe2~\xb0}\xfcq\xa7\x06\x06\f\u007f\a\x928\u0293\xe8\x859\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\xe3\x85~\xfd\x1e *D\x17p\xa7w\xa4\x9d\xccE\xe2\xe0\u04c9\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xd9\xec.\xfe\x99\xff\\\xf0\r\x03\xa81{\x92\xa2J\xefD\x1f~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd9\xec\x8f\xe6\x9bw\x16\xc0\x86Z\xf8\x88\xa1\x1b+\x12\xf7 \xed3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\xf1\xb2d\b\xf0\xecg\xad\x1d\ro\xe2.\x85\x15\xe1t\x06$\x89\x01M\x11 \u05f1`\x00\x00\u07d4\xd9\xf5G\xf2\xc1\xde\x0e\u064aS\xd1a\xdfWc]\xd2\x1a\x00\xbd\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xd9\xff\x11]\x01&l\x9fs\xb0c\xc1\xc28\xef5e\xe6;6\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xda\x06\x04N)<e,F\u007f\xe7AF\xbf\x18[!3\x8a\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\vH\xe4\x89\xd3\x02\xb4\xb7\xbf O\x95|\x1c\x9b\u30f0\u07c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xda\rK~\xf9\x1f\xb5Z\xd2e\xf2Q\x14 g\xf1\x03v\xce\u058a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xda\x10\x97\x8a9\xa4o\U0003b10c\xf6]\xd9\xc7u\t\xa6\xd7\x0e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xda\x16\xdd\\=\x1a'\x145\x8f\xe3u,\xaeS\u06eb+\u930a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\xda!L\x02>#&\xffil\x0091h\xceF\xff\xac9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda*\x14\xf9r@\x15\u05d0\x14\xed\x8eY\th\x1dYaH\xf1\x89\x02\xa1\x0f\x0f\x8a\x91\xab\x80\x00\u07d4\xda*\u054ew\xde\xdd\xed\xe2\x18vF\xc4e\x94Z\x8d\xc3\xf6A\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\xda0\x17\xc1P\xdd\r\xce\u007f\u03c8\x1b\nH\xd0\xd1\xc7V\xc4\u01c9\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d4\xda4\xb2\xea\xe3\v\xaf\xe8\xda\xec\xcd\xe8\x19\xa7\x94\u0349\xe0\x95I\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdaJ_U\u007f;\xab9\n\x92\xf4\x9b\x9b\x90\n\xf3\fF\xae\x80\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdaPU7S\u007f\xfb3\xc4\x15\xfe\xc6Ni\xba\xe0\x90\xc5\xf6\x0f\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdai\x8dd\xc6\\\u007f+,rS\x05\x9c\xd3\u0441\u0619\xb6\xb7\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\u07d4\xdaw2\xf0/.'.\xaf(\u07d7.\xcc\r\xde\xed\x9c\xf4\x98\x89\v \xbf\xbfig\x89\x00\x00\u07d4\xdaz\xd0%\xeb\xde%\xd2\"C\u02c3\x0e\xa1\xd3\xf6JVc#\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\u0685]SG\u007fP^\xc4\xc8\xd5\u8ed1\x80\u04c6\x81\x11\x9c\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\u0687^N/<\xab\xe4\xf3~\x0e\xae\xd7\xd1\xf6\xdc\xc6\xff\xefC\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u068b\xbe\xe1\x82\xe4U\xd2\t\x8a\xcb3\x8amE\xb4\xb1~\u0636\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0698.\x96C\xff\xec\xe7#\aZ@\xfewnZ\xce\x04\xb2\x9b\x89\b\xb8\xb6\u0259\x9b\xf2\x00\x00\u07d4\u069fUF\tF\u05ff\xb5p\xdd\xecu|\xa5w;XB\x9a\x89\x1b\x84]v\x9e\xb4H\x00\x00\u07d4\u06a1\xbdz\x91H\xfb\x86\\\xd6\x12\xdd5\xf1b\x86\x1d\x0f;\u0709\xa68\xabr\xd9,\x13\x80\x00\xe0\x94\u06a6<\xbd\xa4]\u0507\xa3\xf1\xcdJtj\x01\xbb^\x06\v\x90\x8a\x01\x04\x16\u0670*\x89$\x00\x00\u07d4\u06a7v\xa6uDi\u05f9&z\x89\xb8g%\xe7@\xda\x0f\xa0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u06ac\x91\xc1\xe8Y\xd5\xe5~\xd3\bKP \x0f\x97f\xe2\xc5+\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u06ac\xda\xf4\"&\xd1\\\xb1\u03d8\xfa\x15\x04\x8c\u007fL\xee\xfei\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\u06b6\xbc\u06c3\xcf$\xa0\xae\x1c\xb2\x1b;[\x83\xc2\xf3\x82I'\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\u06bb\b\x89\xfc\x04)&\xb0^\xf5{% \x91\n\xbcKAI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06bc\"PB\xa6Y,\xfa\x13\xeb\xe5N\xfaA\x04\bx\xa5\xa2\x89\x0e\x11\xfa\xd5\xd8\\\xa3\x00\x00\u07d4\xda\xc0\xc1w\xf1\x1c\\>>x\xf2\xef\xd6c\xd12!H\x85t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xd16\xb8\x81x\xb4\x83zlx\x0f\xeb\xa2&\xb9\x85i\xa9L\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xda\xdb\xfa\xfd\x8bb\xb9*$\xef\xd7RV\u0743\xab\xdb\u05fb\u06c9\x01\x11du\x9f\xfb2\x00\x00\u07d4\xda\xdc\x00\xaby'`</\xcf1\xce\xe3R\xf8\x0elMcQ\x89lf\xe9\xa5Sx\xb8\x00\x00\u07d4\xda\xe0\xd3>\xaa4\x15i\xfa\x9f\xf5\x98&\x84\x85JJ2\x8an\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xe7 \x1e\xab\x8c\x063\x02\x93\ri9)\xd0\u007f\x95\xe7\x19b\x89\x91\xae\xc0(\xb4\x19\x81\x00\x00\u07d4\xda\xed\u052d\x10{'\x1e\x89Hl\xbf\x80\xeb\xd6!\u0757Ex\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x04\xfa\xd9\u011f\x9e\x88\v\xeb\x8f\xcf\x1d:8\x90\u4cc4o\x89CZ\xe6\xcc\fX\xe5\x00\x00\u07d4\xdb\f\u01cft\u0642{\u070ads'n\xb8O\u0717b\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x12\x93\xa5\x06\xe9\f\xad*Y\xe1\xb8V\x1f^f\x96\x1ag\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x19\xa3\x98\"06\x8f\x01w!\x9c\xb1\f\xb2Y\u0372%|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb#\xa6\xfe\xf1\xaf{X\x1ew,\xf9\x18\x82\u07b2Qo\xc0\xa7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb$O\x97\xd9\xc4K\x15\x8a@\xed\x96\x06\xd9\xf7\xbd8\x9131\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xdb(\x8f\x80\xff\xe22\u00baG\u0314\xc7c\xcfo\u0278+\r\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xdb*\f\x9a\xb6M\xf5\x8d\u07f1\u06ec\xf8\xba\r\x89\xc8[1\xb4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdb4t^\u0785v\xb4\x99\xdb\x01\xbe\xb7\xc1\xec\u0685\xcfJ\xbe\x89\x04V9\x18$O@\x00\x00\u07d4\xdb?%\x8a\xb2\xa3\xc2\xcf3\x9cD\x99\xf7ZK\xd1\xd3G.\x9e\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xdbK\xc8;\x0ek\xaa\xdb\x11V\xc5\xcf\x06\xe0\xf7!\x80\x8cR\u01c9/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xdbc\x12-\xe7\x03}\xa4\x97\x151\xfa\u9bc5\x86x\x86\u0192\x89\x0f\x04%\xb0d\x1f4\x00\x00\u07d4\xdbl*s\xda\xc7BJ\xb0\xd01\xb6ga\x12%f\xc0\x10C\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xdbnV\f\x9b\xc6 \u053e\xa3\xa9MG\xf7\x88\v\xf4\u007f-_\x89\x04\xda\x0f\xdf\xcf\x05v\x00\x00\u07d4\xdbo\xf7\x1b=\xb0\x92\x8f\x83\x9e\x05\xa72;\xfbW\u049c\x87\xaa\x891T\xc9r\x9d\x05x\x00\x00\u07d4\xdbsF\vY\xd8\xe8PE\xd5\xe7R\xe6%Y\x87^BP.\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xdbw\xb8\x8d\xcbq/\xd1~\xe9\x1a[\x94t\x8dr\f\x90\xa9\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb}@7\b\x1fle\xf9Gk\x06\x87\xd9\u007f\x1e\x04M\n\x1d\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\u06c8.\xac\xed\xd0\xef\xf2cQ\x1b1*\u06fcY\u01b8\xb2[\x8a\x01\xedO\xdez\"6\xb0\x00\x00\u07d4\u06d3q\xb3\fL\x84NY\xe0>\x92K\xe6\x06\xa98\xd1\xd3\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06e4ym\f\xebM:\x83k\x84\xc9o\x91\n\xfc\x10?[\xa0\x89\t\b\xf4\x93\xf77A\x00\x00\u07d4\u06ed\xc6\x1e\xd5\xf0F\n\u007f\x18\xe5\x1b/\xb2aM\x92d\xa0\xe0\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u06f6\xacH@'\x04\x16B\xbb\xfd\x8d\x80\xf9\xd0\xc1\xcf3\xc1\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06fc\xbby\xbfG\x9aB\xadq\xdb\u02b7{Z\u07ea\x87,X\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xdb\xc1\xce\x0eI\xb1\xa7\x05\xd2. 7\xae\xc8x\xee\ru\xc7\x03\x89\r\x8drkqw\xa8\x00\x00\u07d4\xdb\xc1\xd0\xee+\xabS\x11@\xde\x13w\"\xcd6\xbd\xb4\xe4q\x94\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\u015e\u0609s\u07ad1\b\x84\":\xf4\x97c\xc0P0\xf1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xdb\xc6ie\xe4&\xff\x1a\xc8z\xd6\xebx\xc1\xd9Rq\x15\x8f\x9f\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xdb\xcb\xcdzW\ua7724\x9b\x87\x8a\xf3K\x1a\xd6B\xa7\xf1\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\xd5\x1c\xdf,;\xfa\xcd\xff\x10b!\xde.\x19\xadmB\x04\x14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdb\xd7\x1e\xfaK\x93\u0209\xe7e\x93\xde`\x9c;\x04\u02ef\xbe\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xdb\xf5\xf0a\xa0\xf4\x8e^ia\x879\xa7}.\xc1\x97h\xd2\x01\x89\b=lz\xabc`\x00\x00\u07d4\xdb\xf8\xb19g\xf5Q%'-\xe0V%6\xc4P\xbaVU\xa0\x89n\xf5x\xf0n\f\xcb\x00\x00\u07d4\xdb\xfb\x1b\xb4d\xb8\xa5\x8eP\r.\xd8\u0797,E\xf5\xf1\xc0\xfb\x89V\xbcu\xe2\xd61\x00\x00\x00\xe0\x94\xdc\x06~\xd3\xe1-q\x1e\xd4u\xf5\x15n\xf7\xe7\x1a\x80\xd94\xb9\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xdc\b\u007f\x93\x90\xfb\x9e\x97j\xc2:\xb6\x89TJ\tB\xec !\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xdc\x1e\xb9\xb6\xe6CQ\xf5d$P\x96E\xf8>y\xee\xe7l\xf4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\x1f\x19ya_\b!@\xb8\xbbx\xc6{'\xa1\x94'\x13\xb1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xdc#\xb2`\xfc\xc2n}\x10\xf4\xbd\x04J\xf7\x94W\x94`\xd9\u0689\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xdc)\x11\x97E\xd23s \xdaQ\xe1\x91\x00\xc9H\u0640\xb9\x15\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdc-\x15\xa6\x9fk\xb3;$j\xef@E\aQ\xc2\xf6uj\u0489l4\x10\x80\xbd\x1f\xb0\x00\x00\u07d4\xdc=\xaeY\xed\x0f\xe1\x8bXQ\x1eo\xe2\xfbi\xb2\x19h\x94#\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xdc?\x0evr\xf7\x1f\xe7R[\xa3\v\x97U\x18: \xb9\x16j\x8a\x02\b\x9c\xf5{[>\x96\x80\x00\xe0\x94\xdcCE\u0581.\x87\n\xe9\fV\x8cg\xd2\xc5g\u03f4\xf0<\x8a\x01k5-\xa5\xe0\xed0\x00\x00\u07d4\xdcD'[\x17\x15\xba\xea\x1b\x03EsZ)\xacB\xc9\xf5\x1bO\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xdcF\xc13%\u034e\xdf\x020\xd0h\x89d\x86\xf0\a\xbfN\xf1\x89Hz\x9a0E9D\x00\x00\u07d4\xdcQ\xb2\u071d$z\x1d\x0e[\xc3l\xa3\x15oz\xf2\x1f\xf9\xf6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdcS\x05\xb4\x02\n\x06\xb4\x9de||\xa3L5\xc9\x1c_,V\x8a\x01}\xf6\xc1\r\xbe\xba\x97\x00\x00\u07d4\xdcW4[8\xe0\xf0g\u0263\x1d\x9d\xea\xc5'Z\x10\x94\x93!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdcWG}\xaf\xa4/p\\\u007f\xe4\x0e\xae\x9c\x81un\x02%\xf1\x89\x1b\x1b\x81(\xa7An\x00\x00\u07d4\xdc_Z\xd6c\xa6\xf2c2}d\xca\xc9\xcb\x13=,\x96\x05\x97\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdcp:_7\x94\xc8Ml\xb3TI\x18\xca\xe1J5\u00fdO\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94\xdcs\x8f\xb2\x17\u03ad/iYL\b\x17\r\xe1\xaf\x10\xc4\x19\xe3\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xdcv\xe8[\xa5\v\x9b1\xec\x1e& \xbc\xe6\xe7\xc8\x05\x8c\x0e\xaf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0703\xb6\xfd\rQ!1 G\a\xea\xf7.\xa0\xc8\u027e\xf9v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u070c)\x12\xf0\x84\xa6\u0444\xaasc\x85\x13\u033c2n\x01\x02\x89F3\xbc6\xcb\xc2\xdc\x00\x00\u07d4\u0711\x1c\xf7\xdc]\u04016Vg\x05(\xe93\x8eg\x03G\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0730;\xfal\x111#NV\xb7\xea|Or\x14\x87Tkz\x89Hz\x9a0E9D\x00\x00\xe0\x94\u0736M\xf47X\xc7\u03d7O\xa6`HO\xbbq\x8f\x8cg\xc1\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdc\xc5-\x8f\x8d\x9f\xc7B\xa8\xb8'g\xf0US\x87\xc5c\xef\xff\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xdc\xcb7\x0e\u058a\xa9\"(0C\xef|\xad\x1b\x9d@?\xc3J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\u0324 E\xec>\x16P\x8b`?\xd96\xe7\xfd}\xe5\xf3j\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xdc\xd1\fU\xbb\x85OuD4\xf1!\x9c,\x9a\x98\xac\xe7\x9f\x03\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xdc\u057c\xa2\x00S\x95\xb6u\xfd\xe5\x03VY\xb2k\xfe\xfcI\xee\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xdc\u06fdN&\x04\xe4\x0e\x17\x10\xccg0(\x9d\xcc\xfa\u04c9-\x89\xf9]\xd2\xec'\xcc\xe0\x00\x00\u07d4\xdc\xe3\f1\xf3\xcafr\x1e\xcb!<\x80\x9a\xabV\x1d\x9bR\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdc\xf39eS\x13\x80\x161h\xfc\x11\xf6~\x89\xc6\xf1\xbc\x17\x8a\x89\x12'v\x854\x06\xb0\x80\x00\u07d4\xdc\xf6\xb6W&n\x91\xa4\xda\xe6\x03=\xda\xc1S2\u074d+4\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdc\xf9q\x9b\xe8|oFum\xb4\x89\x1d\xb9\xb6\x11\xd2F\x9cP\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdc\xff\xf3\xe8\xd2<*4\xb5k\u0473\xbdE\u01d3tC\"9\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xdd\x04\xee\xe7N\v\xf3\f?\x8dl,\u007fR\xe0Q\x92\x10\u07d3\x89\x04V9\x18$O@\x00\x00\xe0\x94\xdd&\xb4)\xfdC\xd8N\xc1y\x82S$\xba\u057f\xb9\x16\xb3`\x8a\x01\x16\xbf\x95\xbc\x842\x98\x00\x00\u07d4\xdd*#:\xde\xdef\xfe\x11&\xd6\xc1h#\xb6*\x02\x1f\xed\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd+\u07e9\x17\xc1\xf3\x10\xe6\xfa5\xaa\x8a\xf1i9\xc23\xcd}\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xdd5\xcf\xdb\u02d93\x95Sz\xec\xc9\xf5\x90\x85\xa8\xd5\u0776\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xddG\x18\x9a>d9qg\xf0b\x0eHEe\xb7b\xbf\xbb\xf4\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xddM\xd6\xd3`3\xb0co\u030d\t8`\x9fM\xd6OJ\x86\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xddO_\xa2\x11\x1d\xb6\x8fk\xde5\x89\xb60)9[i\xa9-\x89\b\x96=\xd8\xc2\xc5\xe0\x00\x00\xe0\x94\xddc\x04/%\xed2\x88J\xd2n:\xd9Y\xeb\x94\xea6\xbfg\x8a\x04\x84\xd7\xfd\xe7\u0553\xf0\x00\x00\u07d4\xdde\xf6\xe1qc\xb5\xd2\x03d\x1fQ\xcc{$\xb0\x0f\x02\xc8\xfb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xddl\x06!\x93\xea\xc2=/\xdb\xf9\x97\xd5\x06:4k\xb3\xb4p\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdd{\u0366Y$\xaa\xa4\x9b\x80\x98J\xe1su\x02X\xb9(G\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdd\u007f\xf4A\xbao\xfe6q\xf3\xc0\u06bb\xff\x18#\xa5\x043p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0742T\x12\x1an\x94/\xc9\b(\xf2C\x1fQ\x1d\xad\u007f2\u6263\x9b)\xe1\xf3`\xe8\x00\x00\xe0\x94\u074a\xf9\xe7vR#\xf4DoD\xd3\xd5\t\x81\x9a==\xb4\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\u0755\xdb\xe3\x0f\x1f\x18w\xc5\xddv\x84\xae\xef0*\xb6\x88Q\x92\x8a\x01\xc5\xd8\xd6\xeb>2P\x00\x00\xe0\x94\u0756|L_\x8a\xe4~&o\xb4\x16\xaa\u0456N\xe3\xe7\xe8\u00ca\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u075bHZ;\x1c\xd3:j\x9cb\xf1\xe5\xbe\xe9'\x01\x85m%\x89\f3\x83\xed\x03\x1b~\x80\x00\xe0\x94\u0763q\xe6\x00\xd3\x06\x88\xd4q\x0e\b\x8e\x02\xfd\xf2\xb9RM_\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\u0764\xed*X\xa8\xdd \xa72u4{X\rq\xb9[\xf9\x9a\x89\x15\xa1<\xc2\x01\xe4\xdc\x00\x00\xe0\x94\u0764\xff}\xe4\x91\u0187\xdfEt\xdd\x1b\x17\xff\x8f$k\xa3\u044a\x04&\x84\xa4\x1a\xbf\xd8@\x00\x00\u07d4\u076bkQ\xa9\x03\v@\xfb\x95\xcf\vt\x8a\x05\x9c$\x17\xbe\u01c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u076bu\xfb/\xf9\xfe\u02c8\xf8\x94vh\x8e+\x00\xe3g\xeb\xf9\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\xe0\x94\u076b\xf1<<\x8e\xa4\xe3\xd7=x\xecqz\xfa\xfaC\x0eTy\x8a\b\xcf#\xf9\t\xc0\xfa\x00\x00\x00\u07d4\u076c1*\x96UBj\x9c\f\x9e\xfa?\xd8%Y\xefE\x05\xbf\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\u076ck\xf4\xbb\xdd}Y}\x9chm\x06\x95Y;\xed\xcc\xc7\xfa\x89.\xe4IU\b\x98\xe4\x00\x00\xe0\x94\u077d+\x93,v;\xa5\xb1\xb7\xae;6.\xac>\x8d@\x12\x1a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u077d\xdd\x1b\xbd8\xff\xad\xe00]0\xf0 (\xd9.\x9f:\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u077e\xe6\xf0\x94\xea\xe64 \xb0\x03\xfbGW\x14*\xeal\xd0\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd\u059c[\x9b\xf5\xebZ9\xce\xe7\xc34\x1a\x12\r\x97?\xdb4\x89k\xc1K\x8f\x8e\x1b5\x00\x00\xe0\x94\xdd\xdd{\x9en\xab@\x9b\x92&:\xc2r\u0680\x1bfO\x8aW\x8ai\xe1\r\xe7fv\u0400\x00\x00\u07d4\xdd\xe6p\xd0\x169fuv\xa2-\xd0]2F\xd6\x1f\x06\xe0\x83\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94\xdd\xe7zG@\xba\b\xe7\xf7?\xbe:\x16t\x91)1t.\xeb\x8a\x044\xfeMC\x82\xf1\u0500\x00\u07d4\xdd\xe8\xf0\xc3\x1bt\x15Q\x1d\xce\xd1\xcd}F2>K\xd1\"2\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xdd\xe9i\xae\xf3N\xa8z\u0099\xb7Y~)+J\x01U\u030a\x89\x102\xf2YJ\x01s\x80\x00\u07d4\xdd\xf0\xcc\xe1\xfe\x99m\x91v5\xf0\a\x12\xf4\x05 \x91\xdf\xf9\xea\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdd\xf3\xadv58\x10\xbej\x89\xd71\xb7\x87\xf6\xf1q\x88a+\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xdd\xf5\x81\n\x0e\xb2\xfb.22;\xb2\u0255\t\xab2\x0f$\xac\x8a\x03\xca\\f\u067cD0\x00\x00\xe0\x94\xdd\xf9\\\x1e\x99\xce/\x9fV\x98\x05|\x19\xd5\xc9@'\xeeJn\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\xdd\xfa\xfd\xbc|\x90\xf12\x0eT\xb9\x8f7F\x17\xfb\xd0\x1d\x10\x9f\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xdd\xfc\xca\x13\xf94\xf0\u03fe#\x1d\xa109\xd7\x04u\xe6\xa1\u040968\"\x16`\xa5\xaa\x80\x00\u07d4\xde\x02~\xfb\xb3\x85\x03\"n\xd8q\t\x9c\xb3\v\xdb\x02\xaf\x135\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xde\x06\xd5\xeawzN\xb1G^`]\xbc\xbfCDN\x807\xea\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xde\a\xfb[zFN;\xa7\xfb\xe0\x9e\x9a\xcb'\x1a\xf53\x8cX\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xde\x11!\x82\x9c\x9a\b(@\x87\xa4?\xbd/\xc1\x14*23\xb4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xde\x17kR\x84\xbc\xee:\x83\x8b\xa2Og\xfc|\xbfg\u05ce\xf6\x89\x02\t\xce\b\xc9b\xb0\x00\x00\u07d4\xde!\"\x93\xf8\xf1\xd21\xfa\x10\xe6\tG\rQ,\xb8\xff\xc5\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde0\xe4\x9eZ\xb3\x13!M/\x01\u072b\u0389@\xb8\x1b\x1cv\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xde3\xd7\b\xa3\xb8\x9e\x90\x9e\xafe;0\xfd\u00e5\xd5\u0334\xb3\x89\t\x9c\x88\"\x9f\xd4\xc2\x00\x00\u07d4\xde7B\x99\xc1\xd0}ySs\x85\x19\x0fD.\xf9\xca$\x06\x1f\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xdeB\xfc\xd2L\xe4#\x93\x830CgY_\x06\x8f\fa\a@\x89\x02r*p\xf1\xa9\xa0\x00\x00\u07d4\xdeP\x86\x8e\xb7\xe3\xc7\x197\xecs\xfa\x89\u074b\x9e\xe1\rE\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdeU\xde\x04X\xf8P\xb3~Mx\xa6A\xdd.\xb2\u074f8\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde[\x00_\xe8\u06ae\x8d\x1f\x05\xde>\xda\x04 f\xc6\xc4i\x1c\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xdea-\a$\xe8N\xa4\xa7\xfe\xaa=!B\xbd^\xe8-2\x01\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdem61\x06\xccb8\xd2\xf0\x92\xf0\xf07!6\xd1\xcdP\u018a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xde}\xee\"\x0f\x04W\xa7\x18}V\xc1\xc4\x1f.\xb0\n\xc5`!\x89\"%\xf3\x9c\x85\x05*\x00\x00\u07d4\u0782\u030dJ\x1b\xb1\xd9CC\x92\x96[>\x80\xba\xd3\xc0=O\x89P\x18nu\u0797\xa6\x00\x00\u07d4\u0797\xf43\a\x00\xb4\x8cImC|\x91\xca\x1d\xe9\u0130\x1b\xa4\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\u07d4\u079e\xffLy\x88\x11\xd9h\xdc\xcbF\r\x9b\x06\x9c\xf3\x02x\xe0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u07b1\xbc4\xd8mJM\xde%\x80\u063e\xaf\aN\xb0\xe1\xa2D\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\u07b2I]j\xca{*j-\x13\x8bn\x1aB\xe2\xdc1\x1f\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\u07b9rTGL\r/Zyp\xdc\xdb/R\xfb\x10\x98\xb8\x96\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07b9\xa4\x9aC\x870 \xf0u\x91\x85\xe2\v\xbbL\U000c1ecf\x89\vx\xed\xb0\xbf.^\x00\x00\u07d4\u07bb\u0743\x1e\x0f \xaen7\x82R\xde\xcd\xf9/|\xf0\xc6X\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xc3\xee\xc2d\nu,Fn+~~\u616f\xe9\xacA\xf4\x89G\u0257SYk(\x80\x00\u07d4\xde\xc8#s\xad\xe8\xeb\xcf*\xcbo\x8b\xc2AM\u05eb\xb7\rw\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xde\u0221\xa8\x98\xf1\xb8\x95\xd80\x1f\xe6J\xb3\xad]\xe9A\xf6\x89\x89*\xb4\xf6~\x8as\x0f\x80\x00\u07d4\xde\u025e\x97/\xcaqwP\x8c\x8e\x1aG\xac\"\xd7h\xac\xab|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xd8w7\x84\a\xb9Nx\x1cN\xf4\xaf|\xfc[\xc2 \xb5\x16\x89\x141y\xd8i\x11\x02\x00\x00\u07d4\xde\xe9B\xd5\xca\xf5\xfa\xc1\x14!\xd8k\x01\vE\x8e\\9)\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde\xee&\x89\xfa\x90\x06\xb5\x9c\xf2\x85#}\xe5;:\u007f\xd0\x148\x89\x18ey\xf2\x9e %\x00\x00\u07d4\xde\xfd\xdf\u055b\x8d,\x15N\xec\xf5\xc7\xc1g\xbf\v\xa2\x90]>\x89\x05\x12\xcb^&GB\x00\x00\u07d4\xde\xfe\x91A\xf4pE\x99\x15\x9d{\"=\xe4+\xff\xd8\x04\x96\xb3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xdf\t\x8f^N=\xff\xa5\x1a\xf27\xbd\xa8e,Os\ud726\x89\x1b6\xa6DJ>\x18\x00\x00\xe0\x94\xdf\r\ba{\xd2R\xa9\x11\u07cb\xd4\x1a9\xb8=\u07c0\x96s\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdf\x0f\xf1\xf3\xd2z\x8e\xc9\xfb\x8fk\f\xb2T\xa6;\xba\x82$\xa5\x89\xec\xc5 )E\xd0\x02\x00\x00\u07d4\xdf\x1f\xa2\xe2\x0e1\x98^\xbe,\x0f\f\x93\xb5L\x0f\xb6z&K\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdf!\x1c\xd2\x12\x88\xd6\xc5o\xaef\xc3\xffTb]\u0531T'\x89\x87\x86\xcdvN\x1f,\x00\x00\u07d4\xdf#k\xf6\xab\xf4\xf3)7\x95\xbf\f(q\x8f\x93\u3c73k\x89Hz\x9a0E9D\x00\x00\u07d4\xdf1\x02_VI\xd2\xc6\xee\xa4\x1e\u04fd\xd3G\x1ay\x0fu\x9a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdf7\xc2.`:\xed\xb6\nbrS\xc4}\x8b\xa8f\xf6\xd9r\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xdf;r\u017dq\u0501N\x88\xa6#!\xa9=@\x11\xe3W\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf?W\xb8\xeed4\xd0G\"=\xeft\xb2\x0fc\xf9\xe4\xf9U\x89\r\x94b\xc6\xcbKZ\x00\x00\u07d4\xdfD\xc4\u007f\xc3\x03\xacv\xe7O\x97\x19L\xcag\xb5\xbb<\x02?\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xdfG\xa6\x1brSQ\x93\xc5a\xcc\xccu\xc3\xf3\xce\b\x04\xa2\x0e\x89\x15\x93\\\vN=x\x00\x00\u07d4\xdfG\xa8\xef\x95\xf2\xf4\x9f\x8eoX\x18AT\x14]\x11\xf7'\x97\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xdfS\x003F\xd6\\^zdk\xc04\xf2\xb7\xd3/\xcb\xe5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfW5:\xaf\xf2\xaa\xdb\n\x04\xf9\x01N\x8d\xa7\x88N\x86X\x9c\x89\bH\x86\xa6nO\xb0\x00\x00\u07d4\xdf`\xf1\x8c\x81*\x11\xedN'v\xe7\xa8\x0e\xcf^S\x05\xb3\u05890\xca\x02O\x98{\x90\x00\x00\u07d4\xdfd\x85\xc4)z\xc1R\xb2\x89\xb1\x9d\xde2\xc7~\xc4\x17\xf4}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdff\n\x91\u06b9\xf70\xf6\x19\rP\xc89\x05aP\aV\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfn\xd6\x00jj\xbe\x88n\xd3=\x95\xa4\xde(\xfc\x12\x189'\x891T\xc9r\x9d\x05x\x00\x00\u07d4\u07c5\x10y>\xee\x81\x1c-\xab\x1c\x93\xc6\xf4G?0\xfb\xef[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07cdH\xb1\xeb\a\xb3\xc2\x17y\x0el-\xf0M\xc3\x19\xe7\xe8H\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u07e6\xb8\xb8\xad1\x84\xe3W\xda()Q\u05d1a\u03f0\x89\xbc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u07ef1\xe6\"\xc0=\x9e\x18\xa0\u0778\xbe`\xfb\xe3\xe6a\xbe\n\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\u07f1bn\xf4\x8a\x1d}uR\xa5\xe0)\x8f\x1f\xc2:;H-\x89\\\xe8\x95\u0754\x9e\xfa\x00\x00\xe0\x94\u07f4\u052d\xe5/\u0301\x8a\xccz,k\xb2\xb0\x02$e\x8fx\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u07fdB2\xc1|@z\x98\r\xb8\u007f\xfb\u036060\xe5\xc4Y\x89\x1d\xfc\u007f\x92I#S\x00\x00\u07d4\xdf\xcb\xdf\tEN\x1a^J@\xd3\xee\xf7\xc5\xcf\x1c\xd3\u0794\x86\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf\xdb\xce\xc1\x01K\x96\xda!X\xcaQ>\x9c\x8d;\x9a\xf1\xc3\u0409lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdf\xde\xd2WK'\xd1a:}\x98\xb7\x15\x15\x9b\r\x00\xba\xab(\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdf\xdfC9<d\x9c\xae\xbe\x1b\xb1\x80Y\xde\xcb9\xf0\x9f\xb4\xe8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xdf\xe3\xc5*\x92\xc3\x03\x96\xa4\xe3:P\x17\r\xc9\x00\xfc\xf8\xc9\u03c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xdf\xe5I\xfe\x840\xe5R\xc6\xd0|\u00f9,\xcdC\xb1/\xb5\x0f\x89\x04\x88u\xea\xf6V*\x00\x00\u07d4\xdf\xe9)\xa6\x1c\x1b8\xed\xdb\xe8,%\xc2\xd6u<\xb1\xe1-h\x89\x15\xd1\xcfAv\xae\xba\x00\x00\u07d4\xdf\xf1\xb2 \xde=\x8e\x9c\xa4\xc1\xb5\xbe4\xa7\x99\xbc\xde\xd4\xf6\x1c\x89\x14\xe4\xe3S\xea9B\x00\x00\xe0\x94\xdf\xf4\x00y1xe\x93\xb2)\xef\u555f:N!\x9eQ\xaf\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4\xdf\xfc\xeaT!\xec\x15\x90\fn\xcf\xc7w\x18N\x14\x0e \x9e$\x89\x01\x15G8$4N\x00\x00\u07d4\xe0\x01\xab\xa7|\x02\xe1r\bl\x19P\xff\xfb\u02a3\v\x83H\x8f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe0\x04\x84x\x8d\xb5\x0f\u01a4\x8e7\x9d\x12>P\x8b\x0fnZ\xb1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe0\x06\x04b\xc4\u007f\xf9g\x9b\xae\xf0qY\xca\xe0\x8c)\xf2t\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\r\x15;\x106\x91C\xf9\u007fT\xb8\xd4\xca\"\x9e\xb3\xe8\xf3$\x89\b=lz\xabc`\x00\x00\u07d4\xe0\x12\xdbE8'\xa5\x8e\x16\xc16V\b\xd3n\xd6Xr\x05\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x15G\xbaB\xfc\xaf\xaf\x93\x93\x8b\xec\xf7i\x9ft)\n\xf7O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x16\xdc\x13\x8e%\x81[\x90\xbe?\xe9\xee\xe8\xff\xb2\xe1\x05bO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\x18Y\xf2B\xf1\xa0\xec`/\xa8\xa3\xb0\xb5v@\xec\x89\a^\x89\x1e\x16,\x17{\xe5\xcc\x00\x00\xe0\x94\xe0 \xe8cb\xb4\x87u(6\xa6\xde\v\xc0,\xd8\u061a\x8bj\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xe0#\xf0\x9b(\x87a,|\x9c\xf1\x98\x8e::`+3\x94\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0'\"\x13\xe8\xd2\xfd>\x96\xbdb\x17\xb2KK\xa0\x1bapy\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0+t\xa4v(\xbe1[\x1fv\xb3\x15\x05J\xd4J\xe9qo\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe02 \u0197\xbc\u048f&\xef\vt@J\x8b\xeb\x06\xb2\xba{\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe05/\u07c1\x9b\xa2e\xf1L\x06\xa61\\J\xc1\xfe\x13\x1b.\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe08\x8a\xed\xdd?\xe2\xadV\xf8WH\xe8\x0eq\n4\xb7\xc9.\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0<\x00\xd0\x03\x88\xec\xbfO&=\n\xc7x\xbbA\xa5z@\u064966\xc9yd6t\x00\x00\u07d4\xe0I \xdcn\xcc\x1dn\xcc\bO\x88\xaa\n\xf5\u06d7\xbf\x89:\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xe0Ir\xa8<\xa4\x11+\xc8q\xc7-J\xe1al/\a(\u06c9\x0e\x81\xc7\u007f)\xa3/\x00\x00\u07d4\xe0O\xf5\xe5\xa7\u2bd9]\x88W\xce\x02\x90\xb5:+\x0e\xda]\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xe0P)\xac\xeb\axg[\xef\x17A\xab,\u0493\x1e\xf7\xc8K\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0V\xbf?\xf4\x1c&%o\xefQqf\x12\xb9\u04da\u0799\x9c\x89\x05k\xe7W\xa1.\n\x80\x00\u07d4\xe0a\xa4\xf2\xfcw\xb2\x96\u045a\xda#\x8eI\xa5\u02ce\xcb\xfap\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0f>\x8c\xd6g\x92\xa6A\xf5nP\x03f\x01G\x88\x0f\x01\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0f\x8f\xa8,\x14\xd6\xe8\xd9:S\x11>\xf2\x86/\xa8\x15\x81\xbc\x89//9\xfclT\x00\x00\x00\u07d4\xe0i\xc0\x173R\xb1\v\xf6\x83G\x19\xdb[\xed\x01\xad\xf9{\xbc\x89\x01\x064\xf8\xe52;\x00\x00\u07d4\xe0l)\xa8\x15\x17\xe0\u0507\xb6\u007f\xb0\xb6\xaa\xbcOW6\x83\x88\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xe0l\xb6)G\x04\xee\xa7C|/\xc3\xd3\as\xb7\xbf8\x88\x9a\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\u07d4\xe0q7\xae\r\x11m\x0353\xc4\uad16\xf8\xa9\xfb\tV\x9c\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe0v\xdb0\xabHoy\x19N\xbb\xc4]\x8f\xab\x9a\x92B\xf6T\x8a\x01\x06`~4\x94\xba\xa0\x00\x00\u07d4\xe0~\xbb\xc7\xf4\xdaAnB\xc8\xd4\xf8B\xab\xa1b3\xc1%\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x81\xca\x1fH\x82\xdb`C\u0569\x19\a\x03\xfd\xe0\xab;\xf5m\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x83\xd3Hc\xe0\xe1\u007f\x92ky(\xed\xff1~\x99\x8e\x9cK\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x8b\x9a\xbak\xd9\u048b\xc2\x05gy\xd2\xfb\xf0\xf2\x85Z=\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x8b\u009c+H\xb1i\xff+\xdc\x16qLXnl\xb8\\\u03c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0\x8c`11\x06\xe3\xf93O\xe6\xf7\xe7bM!\x110\xc0w\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xe0\x9ch\xe6\x19\x98\xd9\xc8\x1b\x14\xe4\xee\x80+\xa7\xad\xf6\xd7L\u06c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0\x9f\xeauZ\xee\x1aD\xc0\xa8\x9f\x03\xb5\u07b7b\xba3\x00o\x89;\xa2\x89\xbc\x94O\xf7\x00\x00\xe0\x94\xe0\xa2T\xac\t\xb9r[\xeb\xc8\xe4`C\x1d\xd0s.\xbc\xab\xbf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe0\xaai6UU\xb7?(#3\xd1\xe3\f\x1b\xbd\a(T\xe8\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xe0\xba\u064e\ue598\xdb\xf6\xd7`\x85\xb7\x92=\xe5uN\x90m\x89\t\r\x97/22<\x00\x00\u07d4\xe0\u012b\x90r\xb4\xe6\xe3eJI\xf8\xa8\xdb\x02jK3\x86\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\u0380\xa4a\xb6H\xa5\x01\xfd\v\x82F\x90\u0206\x8b\x0eM\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\xcfi\x8a\x053'\xeb\xd1k}w\x00\t/\xe2\xe8T$F\x89\x05*4\u02f6\x1fW\x80\x00\xe0\x94\xe0\xd21\xe1D\xec\x91\a8l|\x9b\x02\xf1p,\xea\xa4\xf7\x00\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0\xd7kqf\xb1\xf3\xa1+@\x91\xee+)\u078c\xaa}\a\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\xe0\xb2\xe2\x9d\xdes\xafu\x98~\xe4Dl\x82\x9a\x18\x9c\x95\xbc\x89\b\x13\xcaV\x90m4\x00\x00\xe0\x94\xe0\xe9xu=\x98/\u007f\x9d\x1d#\x8a\x18\xbdH\x89\xae\xfeE\x1b\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4\xe0\xf3r4|\x96\xb5_}C\x06\x03K\xeb\x83&o\xd9\tf\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\xf9\x03\xc1\xe4\x8a\xc4!\xabHR\x8f=J&H\b\x0f\xe0C\x897\b\xba\xed=h\x90\x00\x00\u07d4\xe0\xff\v\xd9\x15D9\u0125\xb7#>)\x1d}\x86\x8a\xf5?3\x89\x15y!jQ\xbb\xfb\x00\x00\xe0\x94\xe1\n\xc1\x9cTo\xc2T|a\xc19\xf5\xd1\xf4Zff\u0570\x8a\x01\x02\xdao\xd0\xf7:<\x00\x00\xe0\x94\xe1\fT\x00\x88\x11?\xa6\xec\x00\xb4\xb2\u0202O\x87\x96\xe9n\u010a2\x0fE\t\xab\x1e\xc7\xc0\x00\x00\xe0\x94\xe1\x17:$})\xd8#\x8d\xf0\x92/M\xf2Z\x05\xf2\xafw\u00ca\bx\xc9]V\x0f0G\x80\x00\xe0\x94\xe1 >\xb3\xa7#\xe9\x9c\" \x11|\xa6\xaf\xebf\xfaBOa\x8a\x02\x00\uf49e2V\xfe\x00\x00\xe0\x94\xe11\xf8~\xfc^\xf0~C\xf0\xf2\xf4\xa7G\xb5Q\xd7P\xd9\xe6\x8a\x04<%\xe0\xdc\xc1\xbd\x1c\x00\x00\u07d4\xe13N\x99\x83y\xdf\xe9\x83\x17pby\x1b\x90\xf8\x0e\xe2-\x8d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe15@\xec\xee\x11\xb2\x12\xe8\xb7u\u070eq\xf3t\xaa\xe9\xb3\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1;=+\xbf\u073c\x87r\xa23\x15rL\x14%\x16|V\x88\x897\xf3y\x14\x1e\xd0K\x80\x00\u07d4\xe1D=\xbd\x95\xccA#\u007fa:HEi\x88\xa0Oh2\x82\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xe1F\x17\xf6\x02%\x01\xe9~{>-\x886\xaaa\xf0\xff-\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe1I\xb5rl\xafm^\xb5\xbf*\xccA\xd4\xe2\xdc2\x8d\u1089i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xe1T\xda\xea\xdbTX8\xcb\u01aa\fUu\x19\x02\xf5(h*\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4\xe1l\xe3Ya\xcdt\xbdY\r\x04\u012dJ\x19\x89\xe0V\x91\u0189\a\xea(2uw\b\x00\x00\u07d4\xe1r\xdf\xc8\xf8\f\xd1\xf8\u03459\xdc&\b \x14\xf5\xa8\xe3\u8262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xe1w\xe0\xc2\x01\xd35\xba9V\x92\x9cW\x15\x88\xb5\x1cR#\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1x\x12\xf6l^e\x94\x1e\x18lF\x92+n{/\x0e\xebF\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe1\x80\u079e\x86\xf5{\xaf\xac\u05d0O\x98&\xb6\xb4\xb2c7\xa3\x89-\x04\x1dpZ,`\x00\x00\xe0\x94\xe1\x92H\x9b\x85\xa9\x82\xc1\x882F\xd9\x15\xb2)\xcb\x13 \u007f8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xe1\x95<n\x97X\x14\xc5q1\x1c4\xc0\xf6\xa9\x9c\xdfH\xab\x82\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xe1\xae\x02\x9b\x17\xe3s\xcd\xe3\xdeZ\x91R \x1a\x14\xca\xc4\xe1\x19\x89\x05kU\xaeX\xca@\x00\x00\u07d4\u1cac\xa1T\xb8\xe0vlN\xba0\xbc\x10\xc7\xf3P6\xf3h\x89\x01\x15G8$4N\x00\x00\u07d4\u1cdb\x88\u0650\r\xbcJl\xdcH\x1e\x10`\b\n\x8a\xec<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe1\xb62\x01\xfa\xe1\xf1)\xf9\\z\x11k\xd9\xdd\xe5\x15\x9cl\u068a\x04\xd6\x05s\xa2\xf0\xc9\xef\x00\x00\u07d4\u1feaZE\xc5\x04B\x89#\u0126\x11\x92\xa5[\x14\x00\xb4]\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xe1\xc6\a\xc0\xa8\xa0`\u068f\x02\xa8\xeb8\xa0\x13\xea\x8c\xda[\x8c\x89+\xa3\x9e\x82\xed]t\x00\x00\xe0\x94\xe1\u02c3\xec^\xb6\xf1\xee\xb8^\x99\xb2\xfcc\x81/\u0795q\x84\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xe1\xd9\x1b\tT\xce\xde\"\x1do$\u01d8_\u0159e\xfb\x98\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1\u07f5\u0309\x0e\u8c87~\x88]&|%a\x87\xd0\x19\xe6\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe1\xe8\xc5\v\x80\xa3R\xb2@\xcesB\xbb\xfd\xf5i\f\xc8\xcb\x14\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xe1\xf6>\xbb\xc6,{tD\x04\x0e\xb9\x96#\x96Ovg\xb3v\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\x06\xfbs$\xe9\u07b7\x9e\x19\x904\x96\u0596\x1b\x9b\xe5f\x03\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe2\aW\x8e\x1fM\u06cf\xf6\u0546{9X-q\xb9\x81*\u0149\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xe2\b\x81*h@\x98\xf3\xdaN\xfej\xba%bV\xad\xfe?\xe6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2\tT\xd0\xf4\x10\x8c\x82\xd4\u0732\x14\x8d&\xbb\xd9$\xf6\xdd$\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe2\v\xb9\xf3\x96d\x19\xe1K\xbb\xaa\xaag\x89\xe9$\x96\u03e4y\x89\xbb\xd8%\x03\aRv\x00\x00\u07d4\xe2\r\x1b\xcbq(m\xc7\x12\x8a\x9f\xc7\xc6\xed\u007fs8\x92\xee\xf5\x896d\xf8\xe7\xc2J\xf4\x00\x00\u0794\xe2\x19\x12\x15\x98?3\xfd3\xe2,\u0522I\x00T\xdaS\xfd\u0708\xdbD\xe0I\xbb,\x00\x00\u07d4\xe2\x19\x8c\x8c\xa1\xb3\x99\xf7R\x15a\xfdS\x84\xa7\x13/\xbaHk\x897\b\xba\xed=h\x90\x00\x00\xe0\x94\xe2\x1cw\x8e\xf2\xa0\xd7\xf7Q\xea\x8c\aM\x1f\x81\"C\x86>N\x8a\x01\x1f\xc7\x0e,\x8c\x8a\xe1\x80\x00\xe0\x94\xe2)\xe7F\xa8?,\xe2S\xb0\xb0>\xb1G$\x11\xb5~W\x00\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xe2+ \xc7x\x94F;\xafwL\xc2V\u057d\u06ff}\xdd\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe20\xfe\x1b\xff\x03\x18m\x02\x19\xf1]LH\x1b}Y\xbe(j\x89\x01\xfdt\x1e\x80\x88\x97\x00\x00\u07d4\xe27\xba\xa4\xdb\u0252n2\xa3\xd8]\x12d@-T\xdb\x01/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2A\t\xbe/Q=\x87I\x8e\x92j(d\x99uO\x9e\u051e\x890\x0e\xa8\xad\x1f'\xca\x00\x00\u07d4\xe2Fh<\u025d\xb7\u0125+\u02ec\xaa\xb0\xb3/k\xfc\x93\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2Z\x16{\x03\x1e\x84am\x0f\x01?1\xbd\xa9]\xcccP\xb9\x8a\x02<upr\xb8\xdd\x00\x00\x00\u07d4\xe2[\x9fv\xb8\xad\x02?\x05~\xb1\x1a\xd9BW\xa0\x86.N\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2fW\xf0\xed \x1e\xa29,\x92\"\xb8\np\x03`\x8d\xdf0\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xe2k\xf3\"wN\x18(\x87i\xd6~1\a\u07b7Dw\a\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2r\x8a>\x8c*\xaa\u0243\xd0]\u0187st\xa8\xf4F\xee\xe9\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xe2\x8b\x06\"Y\xe9n\xeb<\x8dA\x04\x94?\x9e\xb3%\x89<\xf5\x89Hz\x9a0E9D\x00\x00\xe0\x94\u237c\x8e\xfd^Ajv.\xc0\xe0\x18\x86K\xb9\xaa\x83({\x8a\x051\xf2\x00\xab>\x03\n\x80\x00\u07d4\xe2\x90K\x1a\xef\xa0V9\x8bb4\xcb5\x81\x12\x88\xd76\xdbg\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u274a\xe4R\xdc\xf3\xb6\xacd^c\x04\t8UQ\xfa\xae\n\x89\x04Z\r\xa4\xad\xf5B\x00\x00\u07d4\xe2\xbb\xf8FA\xe3T\x1fl3\xe6\xedh:cZp\xbd\xe2\xec\x89\x1bA<\xfc\xbfY\xb7\x80\x00\u07d4\xe2\xcf6\n\xa22\x9e\xb7\x9d+\xf7\xca\x04\xa2z\x17\xc52\xe4\u0609\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xe2\xdf#\xf6\xea\x04\xbe\xcfJ\xb7\x01t\x8d\xc0\x961\x84U\\\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2\xe1\\`\xdd8\x1e:K\xe2Pq\xab$\x9aL\\Rd\u0689\u007fk\u011b\x81\xb57\x00\x00\u07d4\xe2\xe2nN\x1d\xcf0\xd0H\xccn\u03ddQ\xec\x12\x05\xa4\xe9&\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe2\xeei\x1f#~\xe6R\x9beW\xf2\xfc\xdd=\xcf\fY\xecc\x8a\x01'r\x9c\x14h| \x00\x00\u07d4\xe2\xef\xa5\xfc\xa7\x958\xce`h\xbf1\xd2\xc5\x16\xd4\xd5<\b\xe5\x89\a\x1c\xc4\b\xdfc@\x00\x00\xe0\x94\xe2\xef\u0429\xbc@~\xce\x03\xd6~\x8e\xc8\xe9\u0483\xf4\x8d*I\x8a\x02\x99\xb3;\xf9\u0144\xe0\x00\x00\u07d4\xe2\xf4\r5\x8f^?\xe7F>\xc7\x04\x80\xbd.\u04d8\xa7\x06;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\xf98=X\x10\xea{C\x18+\x87\x04\xb6+'\xf5\x92]9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe2\xff\x9e\xe4\xb6\xec\xc1AA\xcct\xcaR\xa9\xe7\xa2\xee\x14\xd9\b\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe3\x02\x12\xb2\x01\x1b\xb5k\xdb\xf1\xbc5i\x0f:N\x0f\xd9\x05\xea\x8a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xe3\x03\x16\u007f=I`\xfe\x88\x1b2\x80\n+J\xef\xf1\xb0\x88\u0509lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\x04\xa3/\x05\xa87btJ\x95B\x97o\xf9\xb7#\xfa1\xea\x89Ur\xf2@\xa3F \x00\x00\u07d4\xe3\bCR\x04y7d\xf5\xfc\xbee\xebQ\x0fZtJeZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3\t\x97L\xe3\x9d`\xaa\xdf.ig2Q\xbf\x0e\x04v\n\x10\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xe3\x1bN\xef\x18L$\xab\t\x8e6\xc8\x02qK\xd4t=\xd0\u0509\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3!\xbbJ\x94j\xda\xfd\xad\xe4W\x1f\xb1\\\x00C\u04de\xe3_\x89Udu8+L\x9e\x00\x00\u07d4\xe3&<\xe8\xafm\xb3\xe4gXE\x02\xedq\t\x12^\xae\"\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3+\x1cG%\xa1\x87TI\u93d7\x0e\xb3\xe5@b\xd1X\x00\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3/\x95vmW\xb5\xcdK\x172\x89\u0587o\x9edU\x81\x94\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe38@\u063c\xa7\u0698\xa6\xf3\u0416\xd8=\xe7\x8bp\xb7\x1e\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe38\xe8Y\xfe.\x8c\x15UHH\xb7\\\xae\u0368w\xa0\xe82\x89a\xac\xff\x81\xa7\x8a\xd4\x00\x00\u07d4\xe3=\x98\x02 \xfa\xb2Y\xafj\x1fK8\xcf\x0e\xf3\xc6\xe2\xea\x1a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe3=\xf4\u0380\u0336*v\xb1+\xcd\xfc\xec\xc4b\x89\x97:\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe3?\xf9\x87T\x1d\xde\\\xde\u0a29m\xcc?3\xc3\xf2L\u008a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xe3A\v\xb7U|\xf9\x1dy\xfai\xd0\xdf\xea\n\xa0u@&Q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3Ad-@\u04af\xce.\x91\a\xc6py\xacz&`\bl\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe3TS\xee\xf2\xcc<z\x04M\n\xc14\xbaaY\b\xfa\x82\xee\x89\a\xff\x1c\xcbua\xdf\x00\x00\u07d4\xe3j\x8e\xa8\u007f\x1e\x99\xe8\xa2\xdc\x1b&\b\xd1ff|\x9d\xfa\x01\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe3q'\x01a\x9c\xa7b<U\xdb:\n\xd3\x0e\x86}\xb0\x16\x8b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xe3\u007f_\xdcn\xc9}/\x86j\x1c\xfd\r:M\xa48{\"\xb5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u31cf\x91\u0286\x05?\xce\xd5DF\x86\xa30\xe0\x9c\u00c8\xfb\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4\u32d1\xb3Q\x90\xb6\xd9\xde\xed\x02\x1c0\xaf\tK\x95?\u072a\x89\x01\u03afy[k\x86\x00\x00\u07d4\xe3\x8e\xf2\x8a^\u0644\xa7\xdb$\xa1\xaex-\xfb\x87\xf3\x97\xdf\u0189\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xe3\x92U\t\xc8\u0432\xa6s\x8c_jr\xf3S\x14I\x12H\u03896\xe9\xa8f\x9aDv\x80\x00\xe0\x94\xe3\x93=a\xb7}\xcd\xc7\x16@\u007f\x82P\xbc\x91\xe4\xff\xae\xb0\x9d\x8a\x12V\x98l\x95\x89\x1c \x00\x00\u07d4\xe3\x95\x1d\xe5\xae\xfa\xf0E\x87h\xd7t\xc2T\xf7\x15w5\xe5\x05\x89V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4\xe3\x99\xc8\x1a\x1dp\x1bD\xf0\xb6o3\x99\xe6k'Z\xaa\xf8\xc1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe3\x9b\x11\xa8\xab\x1f\xf5\xe2.Z\xe6Qr\x14\xf7<[\x9bU\u0709lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe3\x9eF\xe1]\"\xceV\xe0\xc3/\x18w\xb7\u0462d\u03d4\xf3\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xe3\xa4b\x1bf\x00E\x88\xe3\x12\x06\xf7\x18\xcb\x00\xa3\x19\x88\x9c\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\xa4\xf8<9\xf8Z\xf9\u0231\xb3\x12\xbf\xe5\xfc4#\xaf\xa64\x89\x01\x8d\x99?4\xae\xf1\x00\x00\u07d4\u3a1a\x19'\xccN-C\xfb\u0361\xe4\x14\xd3$\xa7\xd9\xe0W\x89\v#\xe2\xa96\xde\xc6\x00\x00\u07d4\xe3\xab<\xa9\xb8p\xe3\xf5HQs\x06\xbb\xa4\xde%\x91\xaf\xaf\u0089A\x0e4\xae\u030c\xd3\x00\x00\u07d4\xe3\xb3\xd2\u027fW\v\xe6\xa2\xf7*\u0721\x86,1\t6\xa4<\x89\x05m*\xa3\xa5\xc0\x9a\x00\x00\u07d4\xe3\xc0\xc1(2z\x9a\xd8\x01H\x13\x9e&\x97sB\x8ec\x8c\xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\xc8\x12sz\xc6\x06\xba\xf7R*\xd8\x17B\x8a6\x05\x0ez4\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xe3\xcf\xfe#\x9cd\xe7\xe2\x03\x88\xe6\"\x11s\x910\x1b)\x86\x96\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe3\xd3\ua899\x88xeV\x9e\x88\xbe!\x9b\xe5\a\x18\x9b\xe1\u0249\x18\xbao\xa9.\x93\x16\x00\x00\u07d4\xe3\u063fN\xfe\x84\xb1am\x1b\x89\xe4'\xdd\xc6\u0203\x06\x85\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\xd9\x15\xed\xa3\xb8%\xd6\xeeJ\xf92\x8d2\xac\x18\xad\xa3T\x97\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xe3\xdaO2@\x84L\x9bc#\xb4\x99i! q\"EC\x99\x8a\x02q\x90\xa9R\xdfK\xe5\x80\x00\u07d4\xe3\xeb,\n\x13*ROr\xcc\xc0\xd6\x0f\xee\x8bAh]9\xe2\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe3\xec\x18\xa7N\xd48U@\x9a&\xad\xe7\x83\r\xe8\xe4&\x85\xef\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xe3\xec\xe1\xf62q\x1d\x13\xbf\xff\xa1\xf8\xf6\x84\bq\xeeX\xfb'\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe3\xf8\v@\xfb\x83\xfb\x97\xbb\rR0\xafOn\u055b\x1c|\u0209Hz\x9a0E9D\x00\x00\u07d4\xe3\xff\xb0,\xb7\xd9\xeaRCp\x16\x89\xaf\xd5\xd4\x17\xd7\xed.\u0389\x04:w\xaa\xbd\x00x\x00\x00\u07d4\xe4\x00\xd6Q\xbb?-#\xd5\xf8I\xe6\xf9-\x9cW\x95\xc4:\x8a\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xe4\x06\xf5\xddr\u02b6m\x8an\xcb\u05bf\xb4\x94\xa7\xb6\xb0\x9a\xfe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe4\b\xaa\x99\x83S\a\ue926\xc5\xeb\x80\x1f\xe6\x94\x11\u007fp}\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xe4\b\xfc\ua879\x8f<d\x0fH\xfc\xba9\xf0V\x06mc\b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe4\n|\x82\xe1WT\n\v\x00\x90\x1d\xbb\x86\xc7\x16\xe1\xa0b\u0689\x02\xb3\x1d$%\xf6t\x00\x00\u07d4\xe4\x1a\xea%\v\x87}B:c\xba+\xce/:a\xc0$\x8dV\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xe40\xc0\x02O\xdb\xf7:\x82\xe2\x1f\xcc\xf8\xcb\u04118B\x1c!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe42I\x12\xd6N\xa3\xae\xf7k</\xf9\u07c2\xc7\xe1:\u9449lk\x93[\x8b\xbd@\x00\x00\u07d4\xe46\x8b\xc1B\v5\xef\u0695\xfa\xfb\xc70\x90R\x19\x16\xaa4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe47\xac\xbe\x0fb'\xb0\xe3o6\xe4\xbc\xf7\xcfa35\xfbh\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xe4Krd\u0743k\ue387\x97\x03@\xed+\x9a\xed\x8e\u0425\x8a\x018\xe7\xfa\xa0\x1a\x80:\x00\x00\u07d4\xe4N\xa5\x10c@QT\xaa\xe76\xbe+\xf1\xee;\x9b\xe69\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe4bU\x01\xf5+z\xf5+\x19\xeda.\x9dT\xfd\xd0\x06\xb4\x92\x89\vZ\x90ZV\xdd\xd0\x00\x00\u07d4\xe4qYV\xf5/\x150n\xe9Pk\xf8+\xcc\xc4\x06\xb3\x89^\x89\x0e\xe7\x9dOH\xc5\x00\x00\x00\u07d4\xe4\u007f\xba\xed\x99\xfc \x99b`N\xbd \xe2@\xf7OE\x91\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe4\x82\xd2U\xed\xe5k\x04\xc3\xe8\xdf\x15\x1fV\xe9\xcab\xaa\xa8\u0089\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe4\x8ee\x12T!\x88\rB\xbd\xf1\x01\x8a\xb9w\x8d\x96\x92\x8f?\x89\u3bb5sr@\xa0\x00\x00\u07d4\u4481\x8a\xa6\x84\xe5\xa6vV\x1br]B\xf3\xccV\xaeQ\x98\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xe4\x996\xa9*\x8c\xcfq\x0e\xaa\xc3B\xbcEK\x9b\x14\xeb\ucc49lk\x93[\x8b\xbd@\x00\x00\u07d4\xe4\x9a\xf4\xf3J\u06a23\v\x0eI\xdct\xec\x18\xab/\x92\xf8'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u46e0\u0356\x81lF\aw<\xf8\xa5\x97\v\xb5\xbc\x16\xa1\xe6\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xe4\xa4~93$l?\xd6)y\xa1\xea\x19\xff\u07ccr\xef7\x89\b\t\xb3\x83\xea}~\x80\x00\u07d4\u4dae\"\xc7s_[\x89\xf3M\xd7z\u0417_\n\u0311\x81\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe4\xca\nR8VM\xfc\x91\xe8\xbf\"\xba\xde)\x01a\x9a\x1c\u050965\u026d\xc5\u07a0\x00\x00\u07d4\xe4\xca\xfbr\u007f\xb5\u01b7\v\xb2u3\xb8\xa9\xcc\xc9\xefh\x88\xe1\x89\x10I{\xf4\xafL\xaf\x80\x00\u07d4\xe4\xdc\"\xedY[\xf0\xa37\xc0\x1e\x03\xcck\xe7D%_\xc9\xe8\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xe4\xfb&\xd1\xca\x1e\xec\xba=\x82\x98\xd9\xd1H\x11\x9a\u00bb\xf5\x80\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe4\xfc\x13\xcf\u02ec\x1b\x17\xcew\x83\xac\xd4#\xa8E\x94?k:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe5\vFJ\xc9\xde5\xa5a\x8b|\xbf%Ft\x18+\x81\xb9~\x89\xdeB\xee\x15D\u0750\x00\x00\u07d4\xe5\x10,;q\x1b\x81\x03D\x19t\x19\xb1\u034apY\xf1>2\x89\x10CR\x8d\t\x84i\x80\x00\xe0\x94\xe5\x10\xd6y\u007f\xba=f\x93\x83Z\x84N\xa2\xadT\x06\x91\x97\x1b\x8a\x03\xae9\xd4s\x83\xe8t\x00\x00\u07d4\xe5\x14!\xf8\xee\"\x10\xc7\x1e\xd8p\xfea\x82v\u0215J\xfb\xe9\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\x1e\xb8~\u007f\xb71\x1fR(\xc4y\xb4\x8e\u0247\x881\xacL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5!V1\xb1BH\xd4Z%R\x96\xbe\xd1\xfb\xfa\x030\xff5\x89G\x03\xe6\xebR\x91\xb8\x00\x00\xe0\x94\xe5(\xa0\xe5\xa2g\xd6g\xe99:e\x84\xe1\x9b4\u071b\xe9s\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xe54%\xd8\xdf\x1f\x11\xc3A\xffX\xae_\x148\xab\xf1\xcaS\u03c9\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\u07d4\xe5<hyb\x12\x03>No\x9c\xffV\xe1\x9cF\x1e\xb4T\xf9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5A\x02SM\xe8\xf2>\xff\xb0\x93\xb3\x12B\xad;#?\xac\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe5E\xee\x84\xeaH\xe5d\x16\x1e\x94\x82\u055b\xcf@j`,\xa2\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xe5H\x1a\u007f\xedB\xb9\x01\xbb\xed x\x9b\u052d\xe5\r_\x83\xb9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5Y\xb5\xfd3{\x9cUr\xa9\xbf\x9e\x0f%!\xf7\xd4F\xdb\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\\\x80R\n\x1b\x0fu[\x9a,\xd3\xce!Ov%e>\x8a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe5mC\x13$\xc9)\x11\xa1t\x9d\xf2\x92p\x9c\x14\xb7ze\u034a\x01\xbc\x85\xdc*\x89\xbb \x00\x00\u07d4\xe5})\x95\xb0\xeb\xdf?<\xa6\xc0\x15\xeb\x04&\r\xbb\x98\xb7\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\u51f1j\xbc\x8at\b\x1e6\x13\xe1CB\xc03u\xbf\bG\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\x89\xfav\x98M\xb5\xec@\x04\xb4n\u8954\x92\xc3\aD\u0389\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xe5\x8d\xd228\xeen\xa7\xc2\x13\x8d8]\xf5\x00\xc3%\xf3v\xbe\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe5\x95?\xeaIq\x04\xef\x9a\xd2\xd4\xe5\x84\x1c'\x1f\a5\x19\u0089&)\xf6n\fS\x00\x00\x00\xe0\x94\u5587\x97F\x8e\xf7g\x10\x1bv\x1dC\x1f\xce\x14\xab\xff\u06f4\x8a\x01\xb3\xd9i\xfaA\x1c\xa0\x00\x00\u07d4\xe5\x97\xf0\x83\xa4i\xc4Y\x1c=+\x1d,w'\x87\xbe\xfe'\xb2\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe5\x9b;\xd3\x00\x89?\x97#>\xf9G\xc4or\x17\xe3\x92\xf7\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xa3e4<\xc4\xeb\x1ew\x03h\xe1\xf1\x14Jw\xb82\xd7\xe0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe5\xa3\xd7\xeb\x13\xb1\\\x10\x01w#m\x1b\xeb0\xd1~\xe1T \x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xaa\v\x83;\xb9\x16\xdc\x19\xa8\xddh?\x0e\xde$\x1d\x98\x8e\xba\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u5def\x14i\x86\xc0\xff\x8f\x85\xd2.l\xc34\a}\x84\xe8$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xb8&\x19l\x0e\x1b\xc1\x11\x9b\x02\x1c\xf6\xd2Y\xa6\x10\u0256p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\xb9o\u026c\x03\xd4H\xc1a:\xc9\x1d\x15\x97\x81E\xdb\xdf\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u5e40\u048e\xec\xe2\xc0o\xcal\x94s\x06\x8b7\u0526\xd6\xe9\x89%\xaf\u058c\xac+\x90\x00\x00\u07d4\u5eb4\xf0\xaf\u0629\u0463\x81\xb4Wa\xaa\x18\xf3\xd3\xcc\xe1\x05\x89Q\xbf\xd7\xc18x\xd1\x00\x00\u07d4\xe5\xbc\u020c;%on\xd5\xfeU\x0eJ\x18\x19\x8b\x943V\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xbd\xf3OL\xccH>L\xa50\xcc|\xf2\xbb\x18\xfe\xbe\x92\xb3\x89\x06\xd85\xa1\v\xbc\xd2\x00\x00\u07d4\xe5\u0713I\xcbR\xe1a\x19a\"\u03c7\xa3\x896\xe2\xc5\u007f4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xe38\x00\xa1\xb2\xe9k\xde\x101c\n\x95\x9a\xa0\a\xf2nQ\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\xe3~\x19@\x8f,\xfb\xec\x834\x9d\u0501S\xa4\xa7\x95\xa0\x8f\x89\u3bb5sr@\xa0\x00\x00\u07d4\xe5\xed\xc7>bo]4A\xa4U9\xb5\xf7\xa3\x98\u0153\xed\xf6\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xe5\xed\xf8\x12?$\x03\xce\x1a\x02\x99\xbe\xcfz\xactM\a_#\x89\n\xdaUGK\x814\x00\x00\u07d4\xe5\xf8\xefm\x97\x066\xb0\u072aO \x0f\xfd\xc9\xe7Z\xf1t\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xfb1\xa5\xca\xeej\x96\xde9;\xdb\xf8\x9f\xbee\xfe\x12[\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xfb\xe3I\x84\xb67\x19o3\x1cg\x9d\f\fG\xd84\x10\xe1\x89llD\xfeG\xec\x05\x00\x00\u07d4\xe6\tU\xdc\v\xc1V\xf6\xc4\x18I\xf6\xbdwk\xa4K\x0e\xf0\xa1\x89\x10C\x16'\xa0\x93;\x00\x00\u07d4\xe6\nU\xf2\u07d9m\u00ee\xdbil\b\xdd\xe09\xb2d\x1d\xe8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6\x11[\x13\xf9y_~\x95e\x02\xd5\aEg\u06b9E\xcek\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xe6\x1f(\t\x15\xc7t\xa3\x1d\"<\xf8\f\x06\x92f\xe5\xad\xf1\x9b\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xe6/\x98e\a\x12\xeb\x15\x87S\xd8)r\xb8\u9723\xf6\x18w\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6/\x9d|d\xe8\xe2cZ\xeb\x88=\xd7;\xa6\x84\xee|\x10y\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe6>xt\x14\xb9\x04\x84x\xa5\a35\x9e\xcd\xd7\xe3dz\xa6\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe6FfXr\xe4\v\rz\xa2\xff\x82r\x9c\xaa\xba[\xc3\u8789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe6N\xf0\x12e\x8dT\xf8\xe8`\x9cN\x90#\xc0\x9f\xe8e\xc8;\x89\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\xe6On\x1dd\x01\xb5l\akd\xa1\xb0\x86}\v/1\rN\x89\x02\u02edq\xc5:\xe5\x00\x00\u07d4\xe6g\xf6R\xf9W\u008c\x0ef\u04364\x17\xc8\f\x8c\x9d\xb8x\x89 \x9d\x92/RY\xc5\x00\x00\xe0\x94\xe6w\xc3\x1f\xd9\xcbr\x00u\u0724\x9f\x1a\xbc\xcdY\xec3\xf74\x8a\x01\xa6\u05be\xb1\xd4.\xe0\x00\x00\u07d4\xe6|,\x16e\u02038h\x81\x87b\x9fI\xe9\x9b`\xb2\u04fa\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xe6\x9al\xdb:\x8a}\xb8\xe1\xf3\f\x8b\x84\xcds\xba\xe0+\xc0\xf8\x8a\x03\x94\xfd\xc2\xe4R\xf6q\x80\x00\u07d4\xe6\x9d\x1c7\x8bw\x1e\x0f\xef\xf0Q\xdbi\xd9f\xacgy\xf4\xed\x89\x1d\xfaj\xaa\x14\x97\x04\x00\x00\u07d4\xe6\x9f\xcc&\xed\"_{.7\x984\xc5$\xd7\f\x175\u5f09lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\xa3\x01\x0f\x02\x01\xbc\x94\xffg\xa2\xf6\x99\xdf\xc2\x06\xf9\xe7gB\x89/\xa7\xcb\xf6dd\x98\x00\x00\u07d4\xe6\xa6\xf6\xddop\xa4V\xf4\xec\x15\xefz\xd5\xe5\u06f6\x8b\xd7\u0709\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe6\xb2\x0f\x98\n\xd8S\xad\x04\xcb\xfc\x88|\xe6`\x1ck\xe0\xb2L\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u6cec?]M\xa5\xa8\x85}\v?0\xfcK+i+w\u05c9O%\x91\xf8\x96\xa6P\x00\x00\u07d4\xe6\xb9T_~\u0406\xe5R\x92F9\xf9\xa9\xed\xbb\xd5T\v>\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\xe0\x94\xe6\xbc\xd3\n\x8f\xa18\xc5\xd9\xe5\xf6\xc7\xd2\u0680i\x92\x81-\u034a7\x0e\xa0\xd4|\xf6\x1a\x80\x00\x00\u07d4\xe6\xc8\x1f\xfc\xec\xb4~\xcd\xc5\\\vq\xe4\x85_>^\x97\xfc\x1e\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4\xe6\xcb&\vqmL\n\xb7&\xee\xeb\a\xc8pr\x04\xe2v\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe6\xcb?1$\xc9\xc9\xcc84\xb1'K\xc33dV\xa3\x8b\xac\x89\x17+\x1d\xe0\xa2\x13\xff\x00\x00\xe0\x94\xe6\xd2\"\t\xff\u0438u\t\xad\xe3\xa8\xe2\xefB\x98y\u02c9\xb5\x8a\x03\xa7\xaa\x9e\x18\x99\xca0\x00\x00\u07d4\xe6\u051f\x86\xc2(\xf4sg\xa3^\x88l\xaa\xcb'\x1eS\x94)\x89\x16^\xc0\x9d\xa7\xa1\x98\x00\x00\u07d4\xe6\xe6!\xea\xab\x01\xf2\x0e\xf0\x83k|\xadGFL\xb5\xfd<\x96\x89\x11!\x93B\xaf\xa2K\x00\x00\u07d4\xe6\xe8\x861{jf\xa5\xb4\xf8\x1b\xf1d\xc58\xc2d5\x17e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\u98ddu\x0f\xe9\x949N\xb6\x82\x86\xe5\xeab\xa6\x99x\x82\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xe6\xec\\\xf0\u011b\x9c1~\x1epc\x15\uf7b7\xc0\xbf\x11\xa7\x8a\x03\xa4i\xf3F~\x8e\xc0\x00\x00\u07d4\xe6\xf5\xebd\x9a\xfb\x99Y\x9cAK'\xa9\xc9\xc8U5\u007f\xa8x\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xe6\xfe\n\xfb\x9d\xce\xdd7\xb2\xe2,E\x1b\xa6\xfe\xabg4\x803\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe7\x10\xdc\u041b\x81\x01\xf9C{\xd9}\xb9\ns\xef\x99=\v\xf4\x89\x14\xee6\xc0Z\xc2R\x00\x00\u07d4\xe7'\xe6~\xf9\x11\xb8\x1fl\xf9\xc7?\xcb\xfe\xbc+\x02\xb5\xbf\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7.\x1d3\\\u009a\x96\xb9\xb1\xc0/\x00:\x16\xd9q\xe9\v\x9d\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe71\x1c\x953\xf0\t,rH\xc9s\x9b[,\x86J4\xb1\u0389\x97\xf9}l\xc2m\xfe\x00\x00\u07d4\xe7;\xfe\xad\xa6\xf0\xfd\x01o\xbc\x84>\xbc\xf6\xe3p\xa6[\xe7\f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe7<\xcfCg%\xc1Q\xe2U\xcc\xf5!\f\xfc\xe5\xa4?\x13\xe3\x89\x01\x15NS!}\xdb\x00\x00\u07d4\xe7B\xb1\xe6\x06\x9a\x8f\xfc<Gg#]\xef\xb0\u051c\xbe\xd2\"\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xe7F\b\xf5\x06\x86j\xdak\xfb\xfd\xf2\x0f\xeaD\v\xe7i\x89\xef\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xe7S>'\f\xc6\x1f\xa1d\xac\x15SE\\\x10]\x04\x88~\x14\x89\x06\x96\xd8Y\x00 \xbb\x00\x00\u07d4\xe7\\\x1f\xb1w\b\x9f>X\xb1\x06y5\xa6Yn\xf1s\u007f\xb5\x89\x05j\x87\x9f\xa7uG\x00\x00\u07d4\xe7\\;8\xa5\x8a?3\xd5V\x90\xa5\xa5\x97f\xbe\x18^\x02\x84\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7a\xd2\u007f\xa3P,\xc7k\xb1\xa6\bt\x0e\x14\x03\u03dd\xfci\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe7f\xf3O\xf1o<\xfc\xc9s!r\x1fC\xdd\xf5\xa3\x8b\f\xf4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xe7m\x94Z\xa8\x9d\xf1\xe4W\xaa4+1\x02\x8a^\x910\xb2\u03897\b\xba\xed=h\x90\x00\x00\u07d4\xe7s^\xc7e\x18\xfcj\xa9-\xa8qZ\x9e\xe3\xf6%x\x8f\x13\x89lM\x16\v\xaf\xa1\xb7\x80\x00\xe0\x94\xe7z\x89\xbdE\xdc\x04\xee\xb4\xe4\x1d{Ykp~nQ\xe7L\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xe7}}\uac96\u0234\xfa\a\xca;\xe1\x84\x16=Zm`l\x89\x05\x049\x04\xb6q\x19\x00\x00\u07d4\xe7\u007f\xeb\xab\xdf\b\x0f\x0f]\xca\x1d?Wf\xf2\xa7\x9c\x0f\xfa|\x89K\"\x9d(\xa8Ch\x00\x00\xe0\x94\u7025c\x06\xba\x1ek\xb31\x95,\"S\x9b\x85\x8a\xf9\xf7}\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xe7\x81\xecs-@\x12\x02\xbb\x9b\xd18`\x91\r\xd6\u009a\xc0\xb6\x89C8t\xf62\xcc`\x00\x00\u07d4\xe7\x84\xdc\xc8s\xaa\x8c\x15\x13\xec&\xff6\xbc\x92\xea\xc6\xd4\xc9h\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\x91-L\xf4V,W=\xdc[q\xe3s\x10\xe3x\xef\x86\u0249\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xe7\x91\u0545\xb8\x996\xb2])\x8f\x9d5\xf9\xf9\xed\xc2Z)2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\x924\x9c\xe9\xf6\xf1O\x81\xd0g@\x96\xbe\xfa\x1f\x92!\xcd\xea\x89[]#J\r\xb48\x80\x00\u07d4\xe7\x96\xfdN\x83\x9bL\x95\xd7Q\x0f\xb7\xc5\xc7+\x83\xc6\xc3\xe3\u01c9\x1b\xc43\xf2?\x83\x14\x00\x00\xe0\x94\xe7\xa4/Y\xfe\xe0t\xe4\xfb\x13\xea\x9eW\xec\xf1\xccH(\"I\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xe7\xa4V\f\x84\xb2\x0e\x0f\xb5LIg\f)\x03\xb0\xa9lB\xa4\x89 j\xea\u01e9\x03\x98\x00\x00\u07d4\xe7\xa8\xe4q\xea\xfby\x8fET\xccnRg0\xfdV\xe6,}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u7f82\xc6Y<\x1e\xed\xdd*\xe0\xb1P\x01\xff \x1a\xb5{/\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4\xe7\u01b5\xfc\x05\xfct\x8e[C\x81rdI\xa1\xc0\xad\x0f\xb0\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\xd1u$\xd0\v\xad\x82I|\x0f'\x15jd\u007f\xf5\x1d'\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe7\xd2\x13\x94\u007f\u02d0J\xd78H\v\x1e\xed/\\2\x9f'\xe8\x89\x01\x03\u00f1\xd3\xe9\xc3\x00\x00\u07d4\xe7\xd6$\x06 \xf4,^\u06f2\xed\xe6\xae\xc4=\xa4\xed\x9bWW\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xda`\x9d@\xcd\xe8\x0f\x00\xce[O\xfbj\xa9\u04304\x94\xfc\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xf0oi\x9b\xe3\x1cD\vC\xb4\xdb\x05\x01\xec\x0e%&\x16D\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7\xf4\xd7\xfeoV\x1f\u007f\xa1\xda0\x05\xfd6TQ\xad\x89\u07c9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\xfd\x8f\xd9Y\xae\xd2v~\xa7\xfa\x96\f\xe1\xdbS\xaf\x80%s\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe8\x0e\u007f\xef\x18\xa5\xdb\x15\xb0\x14s\xf3\xadkx\xb2\xa2\xf8\xac\u0649\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe8\x13\u007f\xc1\xb2\xec|\xc7\x10:\xf9!\x89\x9bJ9\xe1\xd9Y\xa1\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xe8\x1c-4l\n\xdfL\xc5g\b\xf69K\xa6\xc8\u0226J\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8,X\xc5yC\x1bg5F\xb5:\x86E\x9a\xca\xf1\u079b\x93\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe84\xc6C\x18 \\\xa7\xddJ!\xab\xcb\b&l\xb2\x1f\xf0,\x8965\xc6 G9\u0640\x00\u07d4\xe86\x04\xe4\xffk\xe7\xf9o`\x18\xd3\xec0r\xecR]\xffk\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\xe0\x94\xe8E\xe3\x87\xc4\xcb\u07d8\"\x80\xf6\xaa\x01\xc4\x0eK\xe9X\u0772\x8a\x05K@\xb1\xf8R\xbd\xa0\x00\x00\u07d4\xe8H\xca~\xbf\xf5\xc2O\x9b\x9c1g\x97\xa4;\xf7\xc3V)-\x89\x06.\x11\\\x00\x8a\x88\x00\x00\u07d4\xe8KU\xb5%\xf1\x03\x9etK\x91\x8c\xb33$\x92\xe4^\xcaz\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe8O\x80v\xa0\xf2\x96\x9e\xcd3>\xef\x8d\xe4\x10B\x98b\x91\xf2\x89\x17k4O*x\xc0\x00\x00\u07d4\xe8d\xfe\xc0~\xd1!Je1\x1e\x11\xe3)\xde\x04\r\x04\xf0\xfd\x89Y\u0283\xf5\xc4\x04\x96\x80\x00\u07d4\xe8}\xba\xc66\xa3w!\xdfT\xb0\x8a2\xefIY\xb5\xe4\xff\x82\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe8~\x9b\xbf\xbb\xb7\x1c\x1at\ft\xc7#Bm\xf5]\x06=\u064a\x01\xb1\x92\x8c\x00\u01e68\x00\x00\u07d4\xe8~\xacm`+A\t\xc9g\x1b\xf5{\x95\f,\xfd\xb9\x9dU\x89\x02\xb4\xf2\x19r\xec\xce\x00\x00\xe0\x94\u807b\xbeir-\x81\xef\xec\xaaH\u0455*\x10\xa2\xbf\xac\x8f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xe8\x92Is\x8b~\xce\xd7\xcbfjf<I\xcb\xf6\u0783C\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\x9c\"\xf1\xa4\xe1\xd4tn\u03eaY\xed8o\xee\x12\xd5\x1e7\x89\x02o\x8e\x87\xf0\xa7\xda\x00\x00\u07d4\u8769n\x06\xbe\xafk\u0600\xb3x\xf0h\fC\xfd.\x9d0\x89 \x9a\x1a\x01\xa5o\xec\x00\x00\u07d4\xe8\xa9\x1d\xa6\xcf\x1b\x9de\xc7J\x02\xec\x1f\x96\xee\xcbm\xd2A\xf3\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\u8a64\x17@\xf4OT\xc3h\x8bS\xe1\xdd\xd4.C\xc9\xfe\x94\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u8c8a\u0369qrWi\u06cfV=(fmA\u076bl\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe8\xbe$\xf2\x89D>\xe4s\xbcv\x82/U\t\x8d\x89\xb9\x1c\u0149lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xc3\u04f0\xe1\u007f\x97\xd1\xe7V\xe6\x84\xf9N\x14p\xf9\x9c\x95\xa1\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xe8\xc3\xf0E\xbb}8\xc9\xd2\U000d5c3a\x84\x92\xb2S#\t\x01\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\xe8\xccC\xbcO\x8a\xcf9\xbf\xf0N\xbf\xbfB\xaa\xc0j2\x84p\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe8\xd9B\xd8/\x17^\xcb\x1c\x16\xa4\x05\xb1\x01C\xb3\xf4k\x96:\x89\x1e\xd2\xe8\xffm\x97\x1c\x00\x00\u07d4\xe8\u077e\xd72\xeb\xfeu@\x96\xfd\xe9\bk\x8e\xa4\xa4\xcd\xc6\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xder^\xca]\xef\x80_\xf7\x94\x1d1\xac\x1c.4-\xfe\x95\x89\x85~\ro\x1d\xa7j\x00\x00\u07d4\xe8\xe9\x85\x05\x86\xe9OR\x99\xabIK\xb8!\xa5\xf4\f\x00\xbd\x04\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xe8\xea\u047b\x90\xcc\u00ee\xa2\xb0\xdc\u0175\x80VUFU\xd1\u054a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\u07d4\xe8\xea\xf1)D\t-\xc3Y\x9b9S\xfa|\xb1\xc9v\x1c\xc2F\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xe8\xedQ\xbb\xb3\xac\xe6\x9e\x06\x02K3\xf8hD\xc4sH\u06de\x8a\"\xf9\xea\x89\xf4\xa7\xd6\xc4\x00\x00\u07d4\xe8\xef\x10\r|\xe0\x89X2\xf2g\x8d\xf7-J\u03cc(\xb8\xe3\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xe8\xf2\x99i\xe7\\e\xe0\x1c\xe3\xd8aT }\n\x9e|v\xf2\x89\xa2/\xa9\xa7:'\x19\x80\x00\u07d4\xe8\xfc6\xb0\x13\x1e\xc1 \xac\x9e\x85\xaf\xc1\f\xe7\vV\u0636\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9\n5L\xec\x04\u059e]\x96\xdd\xc0\xc5\x13\x8d=3\x15\n\xa0\x89\x1b\x1a}\u03caD\u04c0\x00\xe0\x94\xe9\x13>}1\x84]_+f\xa2a\x87\x92\xe8i1\x1a\xcff\x8a\x05\x17\xc0\xcb\xf9\xa3\x90\x88\x00\x00\u07d4\xe9\x1d\xac\x01\x95\xb1\x9e7\xb5\x9bS\xf7\xc0\x17\xc0\xb29[\xa4L\x89e\xea=\xb7UF`\x00\x00\u07d4\xe9\x1f\xa0\xba\xda\u0779\xa9~\x88\xd3\xf4\xdb|U\u05bbt0\xfe\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xe9#\xc0aw\xb3B~\xa4H\xc0\xa6\xff\x01\x9bT\xccT\x8d\x95\x89\x01\xf7\x80\x01Fg\xf2\x80\x00\xe0\x94\xe9=G\xa8\u0288]T\fNRo%\xd5\xc6\xf2\xc1\b\u0138\x8a\x17\xda:\x04\u01f3\xe0\x00\x00\x00\u07d4\xe9E\x8fh\xbb',\xb5g:\x04\xf7\x81\xb4\x03Uo\u04e3\x87\x89\x03N\x8b\x88\xce\xe2\xd4\x00\x00\u07d4\xe9IA\xb6\x03`\x19\xb4\x01j0\xc1\x03}Zi\x03\xba\xba\xad\x89*H\xac\xabb\x04\xb0\x00\x00\u07d4\xe9I[\xa5\x84'(\xc0\ud5fe7\xd0\xe4\"\xb9\x8di ,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe9M\xed\x99\u0735r\xb9\xbb\x1d\u02e3/m\xee\x91\xe0W\x98N\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94\xe9QyR}\uc951l\xa9\xa3\x8f!\\\x1e\x9c\xe77\xb4\u024a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xe9U\x91\x85\xf1f\xfc\x95\x13\xccq\x11aD\xce-\xeb\x0f\x1dK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xe9^\x92\xbb\xc6\xde\a\xbf:f\x0e\xbf_\xeb\x1c\x8a5'\xe1\u0148\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xe9e\u06a3@9\xf7\xf0\xdfb7Z7\u5acar\xb3\x01\xe7\x8a\x01\x03\xfd\xde\u0373\xf5p\x00\x00\u07d4\xe9i\xea\x15\x95\xed\xc5\u0127\a\xcf\xde8\t)c2Q\xa2\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9k\x18N\x1f\x0fT\x92J\xc8t\xf6\v\xbfDptF\xb7+\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\xe0\x94\xe9m}L\xdd\x15U:NM1mmd\x80\xca<\xea\x1e8\x8a\x02\x95]\x02\xe1\xa15\xa0\x00\x00\u07d4\xe9n-8\x13\xef\xd1\x16_\x12\xf6\x02\xf9\u007fJb\x90\x9d<f\x89|\xae\xe9v\x13\xe6p\x00\x00\u07d4\xe9\u007f\xde\vgqc%\xcf\x0e\xcc\u8851\xa3v\x1b,y\x1d\x896w\x03n\xdf\n\xf6\x00\x00\u07d4\xe9\x82\xe6\xf2\x8cT\x8f_\x96\xf4^c\xf7\xabp\x87$\xf5?\xa1\x89\x15z\xe8)\xa4\x1f;\x00\x00\u07d4\xe9\x86L\x1a\xfc\x8e\xaa\xd3\u007f;\xa5o\xcbtw\xccb \t\xb7\x89\x04HXap\xa7\xdc\x00\x00\u07d4\xe9\x87\xe6\x13\x9eaF\xa7\x17\xfe\xf9k\xc2I4\xa5D\u007f\xe0]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe9\x89s<\xa1\u054d\x9e{P)\xba]DHX\xbe\xc01r\x89\x1f\x87@\x83\x13\xdfO\x80\x00\u07d4\u9311\xca\u0752L\x92W\x9e\x11\xb4\x12\x17\xb2\x82\x95l\u06a1\x89\a\\\x9a\x84\x802\f\x00\x00\u07d4\xe9\x9a\xec\xe9\x05A\xca\xe2$\xb8}\xa6s\x96^\n\xeb)j\xfd\x891\u07d0\x95\xa1\x8f`\x00\x00\u07d4\xe9\x9d\xe2X\xa4\x17<\xe9\xac8\xed\xe2l\v;\xea<\ts\u0549Y\u0438\x05\xe5\xbb0\x00\x00\u07d4\u98b4\x91N\x85S\xbf\r|\x00\xcaS#i\xb8y\xf91\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u98da\x8b\xac\x0f\x01\xc3I\xc6L\xed\xb6\x98\x97\xf63#N\u0489\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d4\u996e<\x9e\x05\x97}\xd1\x06\x9e\x9f\xd9\u04ee\xfb\xae\x04\xb8\u07c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe9\xac67n\xfa\x06\x10\x9d@rc\a\xdd\x1aW\xe2\x13\uaa49\n\x84Jt$\xd9\xc8\x00\x00\u07d4\xe9\xb1\xf1\xfc\xa3\xfaG&\x9f!\xb0a\xc3S\xb7\xf5\xe9m\x90Z\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xe9\xb3o\xe9\xb5\x14\x12\xdd\xca\x1aR\x1dn\x94\xbc\x90\x12\x13\u0768\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u9d24\x855w\xa9\xdb\xcc.y[\xe01\r\x1b\xed(d\x1a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u9da7\x90\x00\x9b\xc1fB\xc8\xd8 \xb7\xcd\xe0\xe9\xfd\x16\xd8\xf5\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\u9e62tu\x10\xe3\x10$\x1d.\u0398\xf5k3\x01\xd7W\xe0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe9\xc3\\\x91<\xa1\xfc\xea\xb4aX/\u1941Qd\xb4\xfd!\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe9\xc6\u07ee\x97\xf7\t\x9f\xc5\xf4\xe9KxM\xb8\x02\x92:\x14\x19\x89\x02\xa5<mrO\x10\x00\x00\u07d4\xe9\xc7X\xf8\xdaA\xe34nCP\xe5\xac9v4\\l\x10\x82\x89h\xa0\xd3\t(&\xad\x00\x00\u07d4\xe9\xca\xf8'\xbe\x9d`y\x15\xb3e\xc8?\r;~\xa8\u01dbP\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xe9\xca\xfeA\xa5\xe8\xbb\xd9\v\xa0-\x9e\x06X[N\xb5F\xc5\u007f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe9\u0559Ek%C\xe6\u06c0\xea\x9b!\x0e\x90\x80&\xe2\x14n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9\xe1\xf7\xcb\x00\xa1\x10\xed\xd0\xeb\xf8\xb3w\xef\x8a{\xb8V\x11\u007f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xea\x14\xbf\xda\nnvf\x8f\x87\x882\x1f\a\xdf7\x82N\xc5\u07ca*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xea\x1e\xa0\u0159\xaf\xb9\xcd6\u02ac\xbb\xb5+[\xbb\x97Ysw\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4\xea\x1e\xfb<\u727e\xde\xc3\xd6|>\x1b;\xc0\xe9\xaa\"\u007f\x90\x89'\xcaK\xd7\x19\xf0\xb8\x00\x00\u07d4\xea,\x19}&\xe9\x8b\r\xa8>\x1br\u01c7a\x8c\x97\x9d=\xb0\x89\x01\x11du\x9f\xfb2\x00\x00\xe0\x94\xea7y\xd1J\x13\xf6\u01c5f\xbc\xde@5\x91A:b9\u06ca)\xb7d2\xb9DQ \x00\x00\u07d4\xeaN\x80\x9e&j\xe5\xf1<\xdb\u33dd\x04V\xe68m\x12t\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\xe0\x94\xeaS\xc9T\xf4\xed\x97\xfdH\x10\x11\x1b\u06b6\x9e\xf9\x81\xef%\xb9\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xeaS\xd2ed\x85\x9d\x9e\x90\xbb\x0eS\xb7\xab\xf5`\xe0\x16,8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xea`Ci\x12\xdek\xf1\x87\u04e4r\xff\x8fS3\xa0\xf7\xed\x06\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xea`T\x9e\xc7U?Q\x1d!I\xf2\xd4fl\xbd\x92C\xd9<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeaf\xe7\xb8M\u037f6\xee\xa3\xe7[\x858*u\xf1\xa1]\x96\x89]\xbc\x91\x91&o\x11\x80\x00\u07d4\xeahlPW\t<\x17\x1cf\u06d9\xe0\x1b\x0e\xce\xcb0\x86\x83\x89\x14\u0768],\xe1G\x80\x00\u07d4\xeaj\xfe,\xc9(\xac\x83\x91\xeb\x1e\x16_\xc4\x00@\xe3t!\u7262\u007f\xa0c\xb2\xe2\xe6\x80\x00\u07d4\xeay\x05}\xab\xef^d\xe7\xb4O\u007f\x18d\x8e~S7\x18\u0489\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xea|Mm\xc7)\xcdk\x15|\x03\xad#|\xa1\x9a \x93F\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\x81h\xfb\xf2%\xe7\x86E\x9c\xa6\xbb\x18\xd9c\xd2kPS\t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xea\x81\u02868T\f\xd9\xd4\xd7=\x06\x0f,\xeb\xf2$\x1f\xfc>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xea\x83\x17\x19yYB@A\xd9\xd7\xc6z>\xce\x1d\xbbx\xbbU\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xea\x85'\xfe\xbf\xa1\xad\xe2\x9e&A\x93)\u04d3\xb9@\xbb\xb7\u0709lj\xccg\u05f1\xd4\x00\x00\u07d4\xea\x8f0\xb6\xe4\xc5\xe6R\x90\xfb\x98d%\x9b\u0159\x0f\xa8\ue289\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\x94\xf3(\b\xa2\uf29b\xf0\x86\x1d\x1d$\x04\xf7\xb7\xbe%\x8a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\xa4\\\xea\x02\xd8},\xc8\xfd\xa9CN-\x98[\xd4\x03\x15\x84\x89h\x1f\xc2\xccn+\x8b\x00\x00\xe0\x94\uac3d\x14\x83\t\x18l\xf8\xcb\xd1;r2\xd8\tZ\u02c3:\x8a\x02C\x9a\x88\x1cjq|\x00\x00\u07d4\uaed0\xd3y\x89\xaa\xb3\x1f\xea\xe5G\xe0\xe6\xf3\x99\x9c\xe6\xa3]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xc0\x82~\xff\fn?\xf2\x8a}JT\xf6\\\xb7h\x9d{\x99\x89\x9a\xd9\u67ddGR\x00\x00\u07d4\xea\xc1H(&\xac\xb6\x11\x1e\x19\xd3@\xa4_\xb8QWk\xed`\x89\x01\xbe\x8b\xab\x04\u067e\x80\x00\xe0\x94\xea\xc1{\x81\xedQ\x91\xfb\b\x02\xaaT3s\x13\x83A\a\xaa\xa4\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xea\u00efW\x84\x92\u007f\u9958\xfcN\xec8\xb8\x10/7\xbcX\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xea\u01b9\x88BT.\xa1\v\xb7O&\xd7\xc7H\x8fi\x8bdR\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xea\xc7h\xbf\x14\xb8\xf9C.i\xea\xa8*\x99\xfb\xeb\x94\xcd\f\x9c\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xea\xd2\x1c\x1d\xec\u03ff\x1c\\\xd9f\x88\xa2Gki\xba\a\xceJ\x89\x03\xf2M\x8eJ\x00p\x00\x00\u07d4\xea\xd4\xd2\xee\xfbv\xab\xaeU3\x96\x1e\xdd\x11@\x04\x06\xb2\x98\xfc\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xea\xd6Rb\xed]\x12-\xf2\xb2u\x14\x10\xf9\x8c2\xd1#\x8fQ\x89\x05\x83\x17\xedF\xb9\xb8\x00\x00\u07d4\xea\xd7P\x16\u3801Pr\xb6\xb1\b\xbc\xc1\xb7\x99\xac\xf08>\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xea#\xaa\x05r\x00\xe7\xc9\xc1^\x8f\xf1\x90\xd0\xe6l\f\x0e\x83\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xed\x16\xea\xf5\u06ab[\xf0)^^\a\u007fY\xfb\x82U\x90\v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xea\xed\xcck\x8bib\xd5\xd9(\x8c\x15lW\x9dG\xc0\xa9\xfc\xff\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xea\xf5#\x88Tn\xc3Z\xcaolc\x93\xd8\xd6\t\xde:K\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xeb\x10E\x8d\xac\xa7\x9eJk$\xb2\x9a\x8a\x8a\xdaq\x1b\u007f.\xb6\x89\u063beI\xb0+\xb8\x00\x00\u07d4\xeb\x1c\xea{E\u047dM\x0e*\x00{\u04ff\xb3Tu\x9e,\x16\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xeb%H\x1f\u035c\"\x1f\x1a\xc7\xe5\xfd\x1e\u0353\a\xa1b\x15\xb8\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\xeb.\xf3\u04cf\xe6R@<\xd4\xc9\xd8^\xd7\xf0h,\xd7\xc2\u078a\t\x0fSF\b\xa7(\x80\x00\x00\xe0\x94\xeb;\xddY\xdc\u0765\xa9\xbb*\xc1d\x1f\xd0!\x80\xf5\xf3e`\x8a\x01e\xc9fG\xb3\x8a \x00\x00\u07d4\xeb<\xe7\xfc8\x1cQ\xdb}_\xbdi/\x8f\x9e\x05\x8aLp=\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xebE?Z:\xdd\u074a\xb5gP\xfa\xdb\x0f\xe7\xf9M\x9c\x89\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xebO\x00\xe2\x836\xea\t\x94%\x88\ueb12\x18\x11\xc5\"\x14<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebR\xab\x10U4\x922\x9c\x1cT\x83:\xe6\x10\xf3\x98\xa6[\x9d\x89\b=lz\xabc`\x00\x00\u07d4\xebW\r\xba\x97R'\xb1\xc4-n\x8d\xea,V\u026d\x96\x06p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebc\x94\xa7\xbf\xa4\u0489\x11\u0565\xb2>\x93\xf3^4\f\"\x94\x89\x04:w\xaa\xbd\x00x\x00\x00\u07d4\xebh\x10i\x1d\x1a\xe0\u045eG\xbd\"\u03be\u0cfa'\xf8\x8a\x89\x87\x85c\x15\xd8x\x15\x00\x00\u07d4\xebvBL\x0f\u0557\xd3\xe3A\xa9d*\xd1\xee\x11\x8b+W\x9d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb| +F+|\u0145]t\x84u_n&\xefC\xa1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\x83\\\x1a\x91\x18\x17\x87\x8a3\xd1gV\x9e\xa3\xcd\u04c7\xf3(\x8965\u026d\xc5\u07a0\x00\x00\u07d4\ub268\x82g\t\t\xcf7~\x9ex(n\xe9{\xa7\x8dF\u0089+|\xc2\xe9\xc3\"\\\x00\x00\xe0\x94\xeb\x90\u01d3\xb3S\x97a\xe1\xc8\x14\xa2\x96q\x14\x86\x92\x19>\xb4\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xeb\x9c\xc9\xfe\bi\xd2\u06b5,\u01ea\xe8\xfdW\xad\xb3_\x9f\xeb\x89j\x93\xbb\x17\xaf\x81\xf8\x00\x00\xe0\x94\ub8c8\xb0\xda'\xc8{\x1c\xc0\xea\xc6\xc5{,Z\vE\x9c\x1a\x8a\x01p\xa0\xf5\x04\x0eP@\x00\x00\u07d4\xeb\xaa!m\xe9\xccZC\x03\x17\a\xd3o\xe6\u057e\xdc\x05\xbd\xf0\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xeb\xac+D\b\xefT1\xa1;\x85\b\xe8bP\x98!\x14\xe1E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb\xb6,\xf8\xe2,\x88K\x1b(\xc6\xfa\x88\xfb\xbc\x17\x93\x8a\xa7\x87\x89+By\x84\x03\u0278\x00\x00\u07d4\xeb\xb7\xd2\xe1\x1b\u01b5\x8f\n\x8dE\xc2\xf6\xde0\x10W\n\u0211\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4\xeb\xbbO,=\xa8\xbe>\xb6-\x1f\xfb\x1f\x95\x02a\u03d8\xec\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\xbdM\xb9\x01\x99R\u058b\x1b\x0fm\x8c\xf0h<\x008{\xb5\x89\x12\x04\x01V=}\x91\x00\x00\u07d4\xeb\xbe\xeb%\x91\x84\xa6\xe0\x1c\xcc\xfc\"\a\xbb\u0603xZ\xc9\n\x89!\x9b\xc1\xb0G\x83\xd3\x00\x00\u07d4\xeb\xd3V\x15j81#4=H\x84;\xff\xeda\x03\xe8f\xb3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xeb\xd3{%ec\xe3\fo\x92\x89\xa8\xe2p/\bR\x88\b3\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xeb\xe4l\xc3\xc3L2\xf5\xad\xd6\xc3\x19[\xb4\x86\xc4q>\xb9\x18\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xeb\xff\x84\xbb\xefB0q\xe6\x04\xc3a\xbb\xa6w\xf5Y=\xefN\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xec\t'\xba\xc7\xdc6f\x9c(5J\xb1\xbe\x83\xd7\xee\xc3\t4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\x0e\x18\xa0\x1d\xc4\xdc]\xaa\xe5g\xc3\xfaL\u007f\x8f\x9bY\x02\x05\x89\x11\x1f\xfe@JA\xe6\x00\x00\xe0\x94\xec\x116,\xec\x81\t\x85\xd0\xeb\xbd{sE\x14D\x98[6\x9f\x8a\x06ZNIWpW1\x80\x00\u07d4\xec,\xb8\xb97\x8d\xff1\xae\xc3\xc2.\x0em\xad\xff1J\xb5\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xec0\xad\u0749[\x82\xee1\x9eT\xfb\x04\xcb+\xb09q\xf3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec;\x8bX\xa1'\x03\xe5\x81\xce_\xfd~!\xc5}\x1e\\f?\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xecHg\xd2\x17Z\xb5\xb9F\x93aYUFUF\x84\u0364`\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xecM\b\xaa.GIm\u0287\"]\xe3?+@\xa8\xa5\xb3o\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\xecX\xbc\r\f \xd8\xf4\x94efAS\xc5\xc1\x96\xfeY\u6f89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xec[\x19\x8a\x00\u03f5Z\x97\xb5\xd56D\xcf\xfa\x8a\x04\u04abE\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec]\xf2'\xbf\xa8]z\xd7kBn\x1c\xee\x96;\xc7\xf5\x19\u074965\u026d\xc5\u07a0\x00\x00\xe0\x94\xec_\xea\xfe!\f\x12\xbf\u0265\xd0Y%\xa1#\xf1\xe7?\xbe\xf8\x8a`\x8f\xcf=\x88t\x8d\x00\x00\x00\u07d4\xeci\x04\xba\xe1\xf6\x97\x90Y\x17\t\xb0`\x97\x83s?%s\xe3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xecs\x11L^@o\u06fe\t\xb4\xfab\x1b\xd7\x0e\xd5N\xa1\xef\x8a\x050%\xcd!o\xceP\x00\x00\u07d4\xecs\x83=\xe4\xb8\x10\xbb\x02x\x10\xfc\x8fi\xf5D\xe8<\x12\u044965\u026d\xc5\u07a0\x00\x00\u07d4\xecu\xb4\xa4u\x13\x12\v\xa5\xf8`9\x81O\x19\x98\xe3\x81z\u00c9\t\xb0\xbc\xe2\xe8\xfd\xba\x00\x00\u07d4\xecv\xf1.W\xa6U\x04\x03?,\v\xceo\xc0;\xd7\xfa\n\u0109\xc2\x12z\xf8X\xdap\x00\x00\u0794\xec\x80\x14\xef\xc7\xcb\xe5\xb0\xceP\xf3V,\xf4\xe6\u007f\x85\x93\xcd2\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xec\x82\xf5\r\x06G_hM\xf1\xb3\x92\xe0\r\xa3A\xaa\x14TD\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xec\x83\xe7\x98\u00d6\xb7\xa5^*\"$\xab\u0343K'\xeaE\x9c\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\x89\xf2\xb6x\xa1\xa1[\x914\xec^\xb7\fjb\a\x1f\xba\xf9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\x8c\x1d{j\xac\xcdB\x9d\xb3\xa9\x1e\xe4\xc9\xeb\x1c\xa4\xf6\xf7<\x89\xe6d\x99\"\x88\xf2(\x00\x00\xe0\x94\xec\x98Q\xbd\x91rpa\x02g\xd6\x05\x18\xb5M<\xa2\xb3[\x17\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xec\x99\xe9]\xec\xe4o\xff\xfb\x17^\xb6@\x0f\xbe\xbb\b\ue6d5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xec\xa5\xf5\x87\x92\xb8\xc6-*\xf5Vq~\xe3\xee0(\xbeM\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xabZ\xba[\x82\x8d\xe1pS\x81\xf3\x8b\xc7D\xb3+\xa1\xb47\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\xec\xaf3P\xb7\xce\x14M\x06\x8b\x18`\x10\x85,\x84\xdd\f\xe0\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xb9LV\x8b\xfeY\xad\xe6Pd_O&0lsl\xac\xe4\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xec\xbeB^g\r9\tN \xfbVC\xa9\xd8\x18\xee\xd26\u078a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xec\xbe^\x1c\x9a\u04b1\xdc\xcf\n0_\xc9R/Fi\xdd:\xe7\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xec\xcfz\x04W\xb5f\xb3F\xcag:\x18\x0fDA0!j\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4\xec\u0466(\x025\x1aAV\x8d#\x030\x04\xac\xc6\xc0\x05\xa5\u04c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xec\xd2v\xafd\u01dd\x1b\u0669+\x86\xb5\u835a\x95\xeb\x88\xf8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xec\u0506\xfc\x19g\x91\xb9,\xf6\x12\xd3HaO\x91VH\x8b~\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\xda\xf92)\xb4^\xe6r\xf6]\xb5\x06\xfb^\xca\x00\xf7\xfc\xe6\x89W\x01\xf9m\xcc@\xee\x80\x00\u07d4\xec\xe1\x11g\vV<\u037e\xbc\xa5#\x84)\x0e\xcdh\xfe\\\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xec\xe1\x15&\x82\xb7Y\x8f\xe2\xd1\xe2\x1e\xc1U3\x88T5\xac\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xec\xe1)\bw\xb5\x83\xe3a\xa2\xd4\x1b\x00\x93F\xe6'N%8\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xec\xf0]\a\xea\x02n~\xbfIA\x00#5\xba\xf2\xfe\xd0\xf0\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\xf2L\xdd|\"\x92\x8cD\x1eiM\xe4\xaa1\xb0\xfa\xb5\x97x\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xec\xfd\x00M\x02\xf3l\xd4\u0634\xa8\xc1\xa9S;j\xf8\\\xd7\x16\x8a\x01\x0fA\xac\xb4\xbb;\x9c\x00\x00\xe0\x94\xed\x02\x06\xcb#1Q(\xf8\xca\xff&\xf6\xa3\v\x98Tg\xd0\"\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xed\x10e\xdb\u03dds\xc0O\xfcy\b\x87\r\x88\x14h\xc1\xe12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xed\x12vQ;o\u0186(\xa7A\x85\xc2\xe2\f\xbb\xcax\x17\xbf\x89\nZ\xa8P\t\xe3\x9c\x00\x00\xe0\x94\xed\x12\xa1\xba\x1f\xb8\xad\xfc\xb2\r\xfa\x19X.RZ\xa3\xb7E$\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xed\x16\xce9\xfe\xef;\xd7\xf5\xd1b\x04^\x0fg\xc0\xf0\x00F\xbb\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xed\x1a\\C\xc5t\xd4\xe94)\x9b$\xf1G,\u071f\xd6\xf0\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xed\x1b$\xb6\x91-Q\xb34\xac\r\xe6\xe7q\xc7\xc0EF\x95\xea\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xed\x1f\x1e\x11Z\r`\xce\x02\xfb%\xdf\x01M(\x9e:\f\xbe}\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xed10\\1\x9f\x92s\u04d3m\x8f[/q\u9c72)c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xed2z\x14\xd5\u03ed\u0641\x03\xfc\t\x99q\x8d~\xd7\x05(\xea\x89N\x10\x03\xb2\x8d\x92\x80\x00\x00\u07d4\xed<\xbc7\x82\u03bdg\x98\x9b0\\A3\xb2\xcd\xe3\"\x11\xeb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xed@\x14S\x8c\xeefJ/\xbc\xb6\xdcf\x9fz\xb1m\v\xa5|\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedA\u188f\\\xaa\x848\x80\xefN\x8b\b\xbdl3\x14\x1e\u07c9*\xd5\xdd\xfaz\x8d\x83\x00\x00\xe0\x94\xedK\xe0J\x05-z\u0333\xdc\u03901\x9d\xba@ \xab,h\x8a\a\xf3zp\xea\xf3b\x17\x80\x00\xe0\x94\xedR\xa2\xcc\bi\u071e\x9f\x84+\u0415|G\xa8\xe9\xb0\xc9\xff\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xed[LA\xe7b\xd9B@Cs\xca\xf2\x1e\xd4a]%\xe6\xc1\x89m-O=\x95%\xb4\x00\x00\u07d4\xed`\u012bnT\x02\x061~5\x94zc\xa9\xcak\x03\xe2\u02c9\x03\x1a\u066d\vF\u007f\x80\x00\u07d4\xedd\x1e\x066\x8f\xb0\xef\xaa\x17\x03\xe0\x1f\xe4\x8fJhS\t\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedfC\xc0\xe8\x88K-2\x11\x857\x85\xa0\x8b\xf8\xf3>\u049f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xedp\xa3|\xdd\x1c\xbd\xa9tm\x93\x96X\xae*a\x81(\x85x\x8a\x02\bj\xc3Q\x05&\x00\x00\x00\u07d4\xedsFvn\x1agm\r\x06\xec\x82\x18g\xa2v\xa0\x83\xbf1\x89\u064a\t1\xcc-I\x00\x00\u07d4\xed\x86&\x16\xfc\xbf\xb3\xbe\xcbt\x06\xf7<\\\xbf\xf0\f\x94\aU\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xed\x9e\x03\f\xa7\\\xb1\u049e\xa0\x1d\rL\xdf\xdc\xcd8D\xb6\xe4\x89\x01\xac\xc1\x16\u03ef\xb1\x80\x00\xe0\x94\ud7bc\u02e4/\x98\x15\xe7\x823&m\xd6\xe85\xb6\xaf\xc3\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\ud7f1\xf5\xaf/\xbf\u007f\xfcP)\xce\xe4+p\xff\\'[\xf5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xed\xa4\xb2\xfaY\u0584\xb2z\x81\r\xf8\x97\x8as\xdf0\x8ac\u0089\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xed\xb4s59y\xa2\x06\x87\x9d\xe1D\xc1\n<Q\xd7\xd7\b\x1a\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xed\xb7\x1e\xc4\x1b\xda}\u0386\xe7f\xe6\xe8\xc3\xe9\x90w#\xa6\x9b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xed\xba\xc9R{T\xd6\xdfz\xe2\xe0\x00\u0323a;\xa0\x15\xca\xe3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xed\xc2/\xb9,c\x8e\x1e!\xff\\\xf09\u06a6\xe74\xda\xfb)\x89\x10'\x94\xad \xdah\x00\x00\xe0\x94\xed\xda\u0354\uc262\uf58f\u03d7z\b\xf1\xfa\xe2uxi\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xed\u06ea\xfb\xc2\x1b\xe8\xf2Ub\xf1\xedm\x05\u05af\xb5\x8f\x02\u0089lk\x93[\x8b\xbd@\x00\x00\u07d4\xed\xe0\x14~\xc02\xc3a\x83\x10\xc1\xff%i\v\xf1r\x19=\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xed\xe5\xde|\u007f\xb7\xee\xe0\xf3ndS\nAD\x0e\u07fe\xfa\u03c9!u^\xe1\xef+\x18\x00\x00\u07d4\xed\xe7\x9a\xe1\xffO\x16\x06\u0552p!o\xa4j\xb2\xdd\xd4\uca89\a\xea(2uw\b\x00\x00\xe0\x94\xed\xe8\xc2\u02c7o\xbe\x8aL\u0282\x906\x1a~\xa0\x1ai\xfd\xf8\x8a\x01\xa7\x8ckD\xf8A\x83\x80\x00\u07d4\xed\xebH\x94\xaa\xdd\x00\x81\xbb\xdd\xd3\xe8\x84h\x04\xb5\x83\u045f'\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xed\xf6\x03\x89\x02(\xd7\xd5\u0793\t\x94+\\\xadB\x19\xef\x9a\u05ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xed\xf8\xa3\xe1\xd4\x0f\x13\xb7\x9e\xc8\xe3\xe1\xec\xf2b\xfd\x92\x11bc\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\xed\xfd\xa2\xd5\u06d8\xf98\a\x14fMT\xb4\xee\x97\x1a\x1c\xae\x03\x89\x02+\xb8\xdd\xd6y\xbe\x00\x00\u07d4\xee\x00\a\xb0\x96\r\x00\x90\x8a\x94C*suW\x87j\xac|1\x89\x02\xe0B\x1ei\xc4\u0300\x00\u07d4\xee\x04\x9a\xf0\x05\x97M\xd1\u01f3\xa9\u028d\x9a\xa7qu\xbaS\xaa\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4\xee%\xb9\xa7\x03&y\xb1\x13X\x8e\xd5,\x13}\x1a\x05:\x1e\x94\x89\n\xd5\x0f?N\xea\x8e\x00\x00\u07d4\xee1\x16\u007f\x9c\xc9;<de`\x9dy\xdb\f\u0790\xe8HL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xee4\xc7\xe7\x99]\xb9\xf1\x87\xcf\xf1V\x91\x8c\xfbo\x13\xf6\xe0\x03\x89j@v\xcfy\x95\xa0\x00\x00\u07d4\xee5d\xf5\xf1\xba\x0f\x94\xec{\xac\x16K\u077f1\u0188\x8bU\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xeeX\xfb=\xb2\x90p\xd0\x13\x01\x88\xceG+\xe0\xa1r\xb8\x90U\x8a\x02\x1fB\xdc\xdcX\xe3\x9c\x00\x00\u07d4\xeee[\xb4\xee\x0e\x8dTxRo\xb9\xf1^@d\xe0\x9f\xf3\u0749\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xeeiY\xde+g\x96{q\x94\x8c\x89\x1a\xb0\r\x8c\x8f8\xc7\u0709\x06hZ\xc1\xbf\xe3,\x00\x00\u07d4\xeel\x03B\x99i\xca\x12b\xcb?\nJT\xaf\xa7\xd3H\xd7\xf5\x89\r\xe2\x19\xf9\x1f\xc1\x8a\x00\x00\u07d4\xeeqy>:\xcf\x12\xa7'OV9a\xf57R\x9d\x89\xc7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xeer\x88\xd9\x10\x86\xd9\xe2\xeb\x91\x00\x14\u066b\x90\xa0-x\u00a0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xee|=\xed|(\xf4Y\xc9/\xe1;M\x95\xba\xfb\xab\x026}\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\xee\x86} \x91k\xd2\xe9\xc9\xec\xe0\x8a\xa0C\x85\xdbf|\x91.\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\ue25b\x02\xcb\xcb99\xcda\xde\x13B\xd5\x04\x82\xab\xb6\x852\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xee\x90m}_\x17H%\x81t\xbeL\xbc8\x93\x03\x02\xab{B\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\ue5ea\x8a\u019e\xdfz\x98}mp\x97\x9f\x8e\xc1\xfb\xcaz\x94\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xee\xa1\xe9y\x88\xdeu\xd8!\xcd(\xadh\"\xb2,\u0398\x8b1\x89\x1c0s\x1c\xec\x03 \x00\x00\xe0\x94\xee\u048c?\x06\x8e\tJ0K\x85<\x95\nh\t\xeb\xcb\x03\xe0\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xee\u04c4\xef-A\xd9\xd2\x03\x97NW\xc1#(\xeav\x0e\b\xea\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xdflB\x80\xe6\xeb\x05\xb94\xac\xe4(\xe1\x1dB1\xb5\x90[\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xe7a\x84~3\xfda\u0653\x87\xee\x14b\x86\x94\u047f\xd5%\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xee\xe9\xd0Rn\xda\x01\xe41\x16\xa3\x952-\u0689pW\x8f9\x8a\x02\x1e\x19\x99\xbb\xd5\u04be\x00\x00\u07d4\xee\xf1\xbb\xb1\xe5\xa8?\u0782H\xf8\x8e\xe3\x01\x8a\xfa-\x132\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xfb\xa1-\xfc\x99gB\xdby\x04d\xca}';\xe6\xe8\x1b>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xfd\x05\xb0\xe3\xc4\x17\xd5[3C\x06\x04\x86\xcd\xd5\xe9*\xa7\xa6\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xef\r\xc7\xddzS\xd6\x12r\x8b\xcb\u04b2|\x19\xddM}fo\x89&A\x1c[5\xf0Z\x00\x00\u07d4\xef\x11RR\xb1\xb8E\u0345\u007f\x00-c\x0f\x1bo\xa3zNP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xef\x1c\x04w\xf1\x18M`\xac\u02b3t\xd3tUz\n>\x10\xf3\x89\b=lz\xabc`\x00\x00\u07d4\xef,4\xbbH}7b\xc3\u0327\x82\xcc\xddz\x8f\xbb\n\x991\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xef5\xf6\u0531\a^j\xa19\x15\x1c\x97K/FX\xf7\x058\x89<;\xc3?\x94\xe5\r\x80\x00\u07d4\xef9\u0291s\xdf\x15S\x1ds\xe6\xb7*hKQ\xba\x0f+\xb4\x89V\xa0\xb4un\xe28\x00\x00\u07d4\xefF<&y\xfb'\x91d\xe2\f=&\x915\x87s\xa0\xad\x95\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xefG\xcf\a>6\xf2q\xd5\"\xd7\xfaNq \xadP\a\xa0\xbc\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\xefa\x15[\xa0\t\xdc\u07be\xf1\v(\xd9\xda=\x1b\xc6\xc9\xce\u0509\x034-`\xdf\xf1\x96\x00\x00\u0794\xefix\x1f2\xff\xce34o,\x9a\xe3\xf0\x84\x93\xf3\xe8/\x89\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xefv\xa4\u034f\xeb\xcb\u0278\x18\xf1x(\xf8\xd94s\xf3\xf3\u02c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\uf4c1\x8fhM\xb0\xc3g^\xc8\x132\xb3\x18>\xcc(\xa4\x95\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94\xef\x9fY\xae\xdaA\x8c\x14\x94h-\x94\x1a\xabI$\xb5\xf4\x92\x9a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\uf9b1\xf0\xdb`57\x82h\x91\xb8\xb4\xbc\x169\x84\xbb@\u03495e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xef\xbdR\xf9}\xa5\xfd:g:F\xcb\xf30D{~\x8a\xad\\\x89\x05l<\x9b\x80\xa0\xa6\x80\x00\xe0\x94\xef\xc8\xcf\x19c\u0269Rg\xb2(\xc0\x86#\x98\x89\xf4\xdf\xd4g\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xef\u02ae\x9f\xf6M,\xd9[RI\xdc\xff\xe7\xfa\xa0\xa0\xc0\xe4M\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xef\xcc\xe0k\xd6\b\x9d\x0eE\x8e\xf5a\xf5\xa6\x89H\n\xfep\x00\x89 \x86\xac5\x10R`\x00\x00\u07d4\xef\xe0g]\xa9\x8a]\xdap\u0356\x19k\x87\xf4\xe7&\xb43H\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xef\xe8\xff\x87\xfc&\x0e\agc\x8d\xd5\xd0/\xc4g.\x0e\xc0m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xeb\x19\x97\xaa\xd2w\xcc3C\x0ea\x11\xed\tCY@H\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xee\xa0\x10uo\x81\xdaK\xa2[r\x17\x87\xf0X\x17\v\uff49\x01\u009c\x9c\xf7p\xef\x00\x00\u07d4\xef\xf5\x1dr\xad\xfa\xe1C\xed\xf3\xa4+\x1a\xecU\xa2\xcc\xdd\v\x90\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xef\xf8kQ#\xbc\xdc\x17\xedL\xe8\xe0[~\x12\xe5\x13\x93\xa1\xf7\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xef\xfc\x15\u41f1\xbe\xda\n\x8d\x13%\xbd\xb4\x17\"@\xdcT\n\x89\x03\x8599\xee\xe1\xde\x00\x00\xe0\x94\xf0\x11\x95\xd6W\xef<\x94.l\xb89I\xe5\xa2\v\\\xfa\x8b\x1e\x8a\x05ts\xd0]\xab\xae\x80\x00\x00\u07d4\xf0'\x96)Q\x01gB\x88\xc1\xd94g\x05=\x04\"\x19\xb7\x94\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\u07d4\xf09h={=\"[\xc7\xd8\u07ed\xefc\x164A\xbeA\xe2\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xf0Jj7\x97\b\xb9B\x8dr*\xa2\xb0kw\xe8\x895\u03c9\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf0M,\x91\xef\xb6\xe9\xc4_\xfb\xe7KCL\x8c_+\x02\x8f\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf0W\xaaf\xcav~\xde\x12J\x1c[\x9c\xc5\xfc\x94\xef\v\x017\x89p\xa2K\u02b6\xf4]\x00\x00\u07d4\xf0[\xa8\u05f6\x859\xd930\v\xc9(\x9c=\x94t\xd0A\x9e\x89\x06\xda'\x02M\xd9`\x00\x00\u07d4\xf0\\\xee\xabeA\x05dp\x99Qw<\x84E\xad\x9fN\u01d7\x89\x10C\x16'\xa0\x93;\x00\x00\xe0\x94\xf0_\xcdL\rs\xaa\x16~US\xc8\xc0\xd6\xd4\xf2\xfa\xa3\x97W\x8a\x02\xd2\xd6l1p\xb2\x98\x00\x00\u07d4\xf0g\xe1\xf1\u0583UjL\xc4\xfd\f\x03\x13#\x9f2\xc4\xcf\u060965\u026d\xc5\u07a0\x00\x00\u07d4\xf0g\xfb\x10\u07f2\x93\u962b\xe5d\xc0U\xe34\x8f\x9f\xbf\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0h\xdf\xe9]\x15\xcd:\u007f\x98\xff\xa6\x88\xb44hB\xbe&\x90\x89D\n\xd8\x19\xe0\x97L\x00\x00\xe0\x94\xf0j\x85J<]\xc3m\x1cI\xf4\xc8}m\xb33\xb5~J\u074a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf0y\xe1\xb1&_P\xe8\u0229\x8e\xc0\u01c1^\xb3\xae\xac\x9e\xb4\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\xe0\x94\xf0{\xd0\xe5\xc2\xcei\xc7\u0127$\xbd&\xbb\xfa\x9d*\x17\xca\x03\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xf0\x83*k\xb2U\x03\xee\xcaC[\xe3\x1b\v\xf9\x05\xca\x1f\xcfW\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf0\x9b>\x87\xf9\x13\xdd\xfdW\xae\x80I\xc71\u06e9\xb66\xdf\u00c9 \xf5\xb1\uab4d\x80\x00\x00\u07d4\xf0\xb14\v\x99oo\v\xf0\xd9V\x1c\x84\x9c\xaf\u007fD0\xbe\xfa\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xb1\xf9\xe2x2\xc6\xdei\x14\xd7\n\xfc#\x8ct\x99\x95\xac\xe4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf0\xb4i\xea\xe8\x9d@\f\xe7\xd5\xd6j\x96\x95\x03p6\xb8\x89\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf0\xb9\u0583\u03a1+\xa6\x00\xba\xac\xe2\x19\xb0\xb3\xc9~\x8c\x00\xe4\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xbe\x0f\xafMy#\xfcDF\"\u0458\f\xf2\u0650\xaa\xb3\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xc0\x81\xdaR\xa9\xae6d*\xdf^\b _\x05\xc5Ah\xa6\x89\x06\x04o7\xe5\x94\\\x00\x00\u07d4\xf0\xc7\r\rm\xabvc\xaa\x9e\xd9\xce\xeaV~\xe2\u01b0'e\x89qC\x8a\u0167\x91\xa0\x80\x00\u07d4\xf0\xcb\xef\x84\xe1ic\x00\x98\xd4\xe3\x01\xb2\x02\b\xef\x05\x84j\u0249\x0e\v\x83EPkN\x00\x00\u07d4\xf0\xd2\x16c\u0630\x17n\x05\xfd\xe1\xb9\x0e\xf3\x1f\x850\xfd\xa9_\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf0\xd5\xc3\x1c\xcbl\xbe0\xc7\xc9\xea\x19\xf2h\xd1Y\x85\x1f\x8c\x9c\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xf0\xd6L\xf9\xdf\tt\x113\xd1pH_\xd2K\x00P\x11\xd5 \x89\x1b\b\x93A\xe1O\xcc\x00\x00\u07d4\xf0\xd8X\x10^\x1bd\x81\x01\xac?\x85\xa0\xf8\"+\xf4\xf8\x1dj\x89 \x86\xac5\x10R`\x00\x00\u07d4\xf0\xdcC\xf2\x05a\x91'P{+\x1c\x1c\xfd\xf3-(1\t \x89\x10^\xb7\x9b\x94\x17\b\x80\x00\u07d4\xf0\xe1\u07e4*\u07ac/\x17\xf6\xfd\xf5\x84\xc9Hb\xfdV3\x93\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf0\xe2d\x9c~j?,]\xfe3\xbb\xfb\xd9'\xca<5\nX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xe7\xfb\x9eB\nS@\xd56\xf4\x04\b4O\xea\xef\xc0j\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf1\x04b\xe5\x8f\xcc\a\U000d5121\x87c\x94Q\x16~\x85\x92\x01\x89\t4\xdd]3\xbc\x97\x00\x00\xe0\x94\xf1\x06a\xff\x94\x14\x0f >zH%rCy8\xbe\xc9\xc3\xf7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xf1\x14\xff\r\x0f$\xef\xf8\x96\xed\xdeTq\u07a4\x84\x82J\x99\xb3\x88\xbe -j\x0e\xda\x00\x00\u07d4\xf1\x16\xb0\xb4h\x0fS\xabr\xc9h\xba\x80.\x10\xaa\x1b\xe1\x1d\u0209\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xf1\x1c\xf5\xd3cto\xeehd\xd3\xca3m\xd8\x06y\xbb\x87\xae\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf1\x1e\x01\u01e9\xd1$\x99\x00_M\xaew\x16\tZ4\x17bw\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf1;\b0\x93\xbaVN-\xc61V\x8c\xf7T\r\x9a\x0e\xc7\x19\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf1O\x0e\xb8m\xb0\xebhu?\x16\x91\x8e]K\x80t7\xbd>\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf1Qx\xff\xc4:\xa8\a\x0e\xce2~\x93\x0f\x80\x9a\xb1\xa5O\x9d\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xf1V\xdc\v*\x98\x1e[U\xd3\xf2\xf0;\x814\xe31\u06ed\xb7\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf1]\x9dZ!\xb1\x92\x9ey\x03q\xa1\u007f\x16\xd9_\fie\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1^\x18,O\xbb\xady\xbd\x934\"B\xd4\xdc\xcf+\xe5\x89%\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf1bM\x98\ve3o\xea\u0166\xd5A%\x00\\\xfc\xf2\xaa\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1g\xf5\x86\x8d\xcfB3\xa7\x83\x06\th,\xaf-\xf4\xb1\xb8\a\x89\x81\xe5B\xe1\xa78?\x00\x00\u07d4\xf1m\xe1\x89\x1d\x81\x96F\x13\x95\xf9\xb16&[;\x95F\xf6\xef\x89\x01\xb2\x8e\x1f\x98\xbb\u0380\x00\u07d4\xf1z\x92\xe06\x1d\xba\xce\xcd\xc5\xde\r\x18\x94\x95Z\xf6\xa9\xb6\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1z\xdbt\x0fE\u02fd\xe3\tN~\x13qo\x81\x03\xf5c\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x8b\x14\xcb\xf6iC6\xd0\xfe\x12\xac\x1f%\xdf-\xa0\xc0]\xbb\x89\xd8\xd4`,&\xbfl\x00\x00\u07d4\xf1\x9b98\x9dG\xb1\x1b\x8a,?\x1d\xa9\x12M\xec\xff\xbe\xfa\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x9f\x195\b9>M*\x12{ \xb2\x03\x1f9\xc8%\x81\u0189\xbd\xbdz\x83\xbd/l\x00\x00\u07d4\xf1\xa1\xf3 @yd\xfd<\x8f.,\u0224X\r\xa9O\x01\xea\x89ll!wU|D\x00\x00\u07d4\xf1\xb4\xec\xc65%\xf7C,=\x83O\xfe+\x97\x0f\xbe\xb8r\x12\x89\xa2\xa2@h\xfa\u0340\x00\x00\u07d4\U000753ef\xfa\x87\x94\xf5\n\xf8\xe8\x83\t\u01e6&TU\xd5\x1a\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xf1\xc8\u0129A\xb4b\x8c\rl0\xfd\xa5dR\u065c~\x1bd\x89N\x8c\xea\x1e\xdeu\x04\x00\x00\u07d4\xf1\xda@so\x99\xd5\xdf;\x06\x8a]t_\xaf\xc6F?\u0271\x89\x06\x96\xca#\x05\x8d\xa1\x00\x00\u07d4\xf1\u070a\xc8\x10B\xc6z\x9c<g\x92\xb20\xc4j\xc0\x16\xca\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf1\xdfU\xdc\xc3J\x05\x10\x12\xb5u\u02d6\x8b\xc9\xc4X\xea\t\u0249\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xf1\xe9\x80\xc5Y\xa1\xa8\xe5\xe5\nG\xf8\xff\xfd\xc7s\xb7\xe0jT\x8a\x06_\xfb\xcd\xea\x04\xb7H\x00\x00\xe0\x94\xf1\xf3\x91\u0292\x80\x88\x17\xb7U\xa8\xb8\xf4\xe2\xca\b\xd1\xfd\x11\b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf1\xf7f\xb0\xe4ms\xfc\xd4\xd5.zr\xe1\xb9\x19\f\xc62\xb3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xf2\x04\x952\xfdE\x8a\x83\xca\x1b\xff.\ubb36\xd5\xcac\xf4\xa4\x89\u010c\x99\x1d\xc1T\\\x80\x00\u07d4\xf2\x06\xd3(\xe4q\xd0\x11{$m*F\x19\x82w\t\xe9m\U000c98af=\xc0\x05CD\x00\x00\u07d4\xf2\f\x9a\x99\xb7GY\u05c2\xf2\\\x1c\xec\xa8\x02\xa2~\vCl\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\xf2\x12}T\x18\x8f\xed\xef\x0f3\x8a_8\xc7\xffs\xad\x9foB\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf2\x1341\xd1\u0663{\xa2\xf0v+\xc4\fZ\u030a\xa6\x97\x8e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf2\x15I\xbd\xd1Hy\x12\xf9\x00\xa7R=\xb5\xf7ba!\xbb\xa3\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf2\x18\xbd\x84\x8e\xe7\xf9\u04cb\xfd\xd1\xc4\xeb.\xd2Ij\xe40_\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf2$\xeb\x90\v7\xb4I\x0e\xeej\vd \xd8\\\x94}\x873\x894\x95tD\xb8@\xe8\x00\x00\u07d4\xf2)J\u06f6\xf0\xdc\xc7nc.\xbe\U0010ad1f\x12M\xbb\xa4\x89NC96\x00\xa7\xb1\x00\x00\u07d4\xf2/@x\xfe\xbb\xba\xa8\xb0\xe7\x8ed,\x8aB\xf3]C9\x05\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf27\xef\x05&\x1c4\u05dc\xc2+\x86\r\xe0\xf1\u007fy<8`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf2<{\f\xb8\xcdY\xb8+\u0610dJW\xda\xf4\f\x85\xe2x\x89\x02\xb6j\xaf\xe3&\xff\x00\x00\u07d4\xf2=\x01X\x9e\xb1-C\x9ftH\xffT0u)\U0005114d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf2>\\c2!\xa8\xf76>e\x87\f\x9f(t$\u04a9`\x89J\xcfX\xe0rW\x10\x00\x00\u07d4\xf2B\u0684]B\u053fw\x9a\x00\xf2\x95\xb4\aP\xfeI\xea\x13\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2RY\xa5\xc99\xcd%\x96l\x9bc\x03\xd3s\x1cS\u077cL\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf2^Lp\xbcFV2\u021eV%\xa82\xa7r/k\xff\xab\x89\xf3K\x82\xfd\x8e\x91 \x00\x00\u07d4\xf2k\xce\xdc\xe3\xfe\xad\u03a3\xbc>\x96\xeb\x10@\xdf\xd8\xff\u1809*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xf2py%v\xf0]QD\x93\xff\xd1\xf5\xe8K\xecK-\xf8\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2s,\xf2\xc1;\x8b\xb8\xe7I*\x98\x8f_\x89\xe3\x82s\xdd\u0209 \x86\xac5\x10R`\x00\x00\xe0\x94\xf2t.hY\xc5i\xd5\xf2\x10\x83Q\xe0\xbfM\xca5*H\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf2\x81:d\xc5&]\x02\x025\u02dc1\x9bl\x96\xf9\x06\xc4\x1e\x89\x12\xf99\u025e\u06b8\x00\x00\u07d4\xf2\x87\xffR\xf4a\x11z\xdb>\x1d\xaaq\x93-\x14\x93\xc6_.\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\xf2\xab\x11au\x02D\xd0\xec\xd0H\xee\r>Q\xab\xb1C\xa2\xfd\x89B\xfe+\x90ss\xbc\x00\x00\u07d4\xf2\xb4\xab,\x94'\xa9\x01^\xf6\xee\xff\xf5\xed\xb6\x019\xb7\x19\u0449&\u06d9*;\x18\x00\x00\x00\u07d4\xf2\xc0>*8\x99\x8c!d\x87`\xf1\xe5\xae~\xa3\a}\x85\"\x89\x8f?q\x93\xab\a\x9c\x00\x00\u0794\xf2\u0090N\x9f\xa6d\xa1\x1e\xe2VV\xd8\xfd,\xc0\u0665\"\xa0\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xf2\xc3b\xb0\xef\x99\x1b\xc8/\xb3nf\xffu\x93*\xe8\u0742%\x89\x04\x02\xf4\xcf\xeeb\xe8\x00\x00\u07d4\xf2\xd0\xe9\x86\xd8\x14\xea\x13\xc8\xf4f\xa0S\x8cS\u0712&Q\xf0\x89J\xcfX\xe0rW\x10\x00\x00\xe0\x94\xf2\u04775w$\xecL\x03\x18[\x87\x9bc\xf5~&X\x91S\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf2\xd5v<\xe0s\x12~,\xed\xdeo\xab\xa7\x86\xc7<\xa9AA\x8a\x01\xacB\x86\x10\x01\x91\xf0\x00\x00\xe0\x94\xf2\u055c\x89#u\x90s\xd6\xf4\x15\xaa\xf8\xeb\x06_\xf2\U000f614a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4\xf2\xe9\x9f\\\xbb\x83kz\xd3bGW\x1a0,\xbeKH\x1ci\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf2\xed>w%J\u02c3#\x1d\xc0\x86\x0e\x1a\x11$+\xa6'\u06c9kV\x05\x15\x82\xa9p\x00\x00\xe0\x94\xf2\xed\xde7\xf9\xa8\u00dd\u07a2My\xf4\x01WW\xd0k\xf7\x86\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf2\xef\xe9e`\xc9\xd9{r\xbd6DxC\x88\\\x1d\x90\xc21\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf2\xfb\xb6\u0607\xf8\xb8\xcc:\x86\x9a\xba\x84\u007f=\x1fd<S\u0589\xd8\xc9F\x00c\xd3\x1c\x00\x00\u07d4\xf3\x03Cg\xf8}$\xd3\a\u007f\xa9\xa2\u328b\f\xcb\x11\x04\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf3\x03\u0568\x16\xaf\xfd\x97\xe8=\x9eM\xac/y\a+\xb0\t\x8f\x894\n\xad!\xb3\xb7\x00\x00\x00\u07d4\xf3\x15\x98f\u00bc\x86\xbb\xa4\x0f\x9ds\xbb\x99\xf1\xee\xe5{\xb9\u05c965\u026d\xc5\u07a0\x00\x00\u07d4\xf3\x16\xef\x1d\xf2\xffMl\x18\b\u06e6c\uc013iyh\xe0\x89aFMl\u0700\xf0\x00\x00\xe0\x94\xf3-%\xeb\x0e\xa2\xb8\xb3\x02\x8aLz\x15]\xc1\xaa\xe8exM\x8a\x015\x93\xa9)\u007f\xda\xd6\x00\x00\xe0\x94\xf32\xc0\xf3\xe0Z'\xd9\x12o\u0436A\xa8\xc2\xd4\x06\x06\b\xfd\x8a\x01\x0f\x1bb\xc4\xd9dN\x80\x00\xe0\x94\xf38E\x9f2\xa1Y\xb2=\xb3\n\xc35v\x9a\xb25\x1a\xa6<\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xf3>\xfcc\x97\xaae\xfbS\xa8\xf0z\x0f\x89:\xae0\xe8\xbc\xee\x89|\xf28\x1fa\x9f\x15\x00\x00\u07d4\xf3@\x83\xec\xea8P\x17\xaa@\xbd\xd3^\xf7\xef\xfbL\xe7v-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf3F\xd7\u0792t\x1c\b\xfcX\xa6M\xb5[\x06-\xde\x01-\x14\x89\x0f\xffk\x1fv\x1em\x00\x00\xe0\x94\xf3U\xd3\xec\f\xfb\x90}\x8d\xbb\x1b\xf3FNE\x81(\x19\v\xac\x8a\x01\v\x04n\u007f\r\x80\x10\x00\x00\u07d4\xf3m\xf0/\xbd\x89`sG\xaf\xce)i\xb9\xc4#jX\xa5\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3s\xe9\u06ac\f\x86u\xf5;yz\x16\x0fo\xc04\xaek#\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3{BeG\xa1d-\x8032H\x14\xf0\xed\xe3\x11O\xc2\x12\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xf3{\xf7\x8cXu\x15G\x11\xcbd\r7\xeam(\xcf\xcb\x12Y\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf3\x82\xdfX1U\xd8T\x8f?\x93D\f\xd5\xf6\x8c\xb7\x9d`&\x8a8u}\x02\u007f\xc1\xfd\\\x00\x00\xe0\x94\xf3\x82\xe4\xc2\x04\x10\xb9Q\b\x9e\x19\xba\x96\xa2\xfe\xe3\xd9\x1c\xce~\x8a\x01\x11\xfaV\xee\u00a88\x00\x00\xe0\x94\xf3\x8al\xa8\x01hS~\x97M\x14\xe1\xc3\xd19\x90\xa4L,\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xf3\x9a\x9dz\xa3X\x1d\xf0~\xe4'\x9a\xe6\xc3\x12\xef!\x036X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xb6h\xb3\xf1M\x92\x0e\xbc7\x90\x92\u06d8\x03\x1bg\xb2\x19\xb3\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\U000fe679\x10<\xe7U\n\xa7O\xf1\xdb\x18\xe0\x9d\xfe2\xe0\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xc1\xab\u049d\xc5{A\xdc\x19-\x0e8M\x02\x1d\xf0\xb4\xf6\u0509\x97\xae\f\u07cf\x86\xf8\x00\x00\u07d4\xf3\xc4qm\x1e\xe5'\x9a\x86\xd0\x16:\x14a\x81\x81\xe1a6\u01c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf3\u030b\xcbU\x94e\xf8\x1b\xfeX;\u05eb\n#\x06E;\x9e\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf3\u0588\xf0k\xbd\xbfP\xf9\x93,AE\xcb\xe4\x8e\xcd\xf6\x89\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf3\xdb\xcf\x13Z\u02dd\xee\x1aH\x9cY<\x02O\x03\u00bb\xae\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xde_&\xefj\xde\xd6\xf0m;\x91\x13F\xeep@\x1d\xa4\xa0\x89\x13:\xb3}\x9f\x9d\x03\x00\x00\u07d4\xf3\xdfc\xa9q\x99\x93308;>\xd7W\v\x96\u0101#4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xe7OG\f}:?\x003x\x0fv\xa8\x9f>\xf6\x91\xe6\u02c9\xa3\xcf\xe61\xd1Cd\x00\x00\u07d4\xf3\xeb\x19H\xb9Q\xe2-\xf1ax)\xbf;\x8d\x86\x80\xeckh\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xf1\xfa9\x18\xca4\xe2\xcf~\x84g\v\x1fM\x8e\xca\x16\r\xb3\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xf3\xf2O\u009e @?\xc0\xe8\xf5\xeb\xbbU4&\xf7\x82p\xa2\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3\xfar5R\xa5\xd0Q.+b\xf4\x8d\xca{+\x81\x050[\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xf3\xfeQ\xfd\xe3D\x13\xc73\x18\xb9\xc8T7\xfe~\x82\x0fV\x1a\x896b2\\\u044f\xe0\x00\x00\u07d4\xf4\x00\xf9=_\\~?\xc3\x03\x12\x9a\xc8\xfb\f/xd\a\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\v\x13O\xea\"\u01b2\x9c\x84W\xf4\x9f\x00\x0f\x9c\xdax\x9a\u06c9 \x86\xac5\x10R`\x00\x00\u07d4\xf4\x15W\xdf\u07f1\xa1\xbd\xce\xfe\xfe.\xba\x1e!\xfe\nJ\x99B\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\x17z\r\x85\u050b\x0e&B\x11\xce*\xa2\xef\xd3\xf1\xb4\u007f\b\x89\xc2\xcc\xca&\xb7\xe8\x0e\x80\x00\u07d4\xf4/\x90R1\xc7p\xf0\xa4\x06\xf2\xb7h\x87\u007f\xb4\x9e\xee\x0f!\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf42\xb9\u06ef\x11\xbd\xbds\xb6Q\x9f\xc0\xa9\x04\x19\x87q\xaa\u0189\b=lz\xabc`\x00\x00\u07d4\xf4=\xa3\xa4\xe3\xf5\xfa\xb1\x04\u029b\xc1\xa0\xf7\xf3\xbbJV\xf3Q\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf4G\x10\x8b\x98\xdfd\xb5~\x87\x103\x88\\\x1a\xd7\x1d\xb1\xa3\xf9\x8a\x01v\xf4\x9e\xad4\x83P\x80\x00\u07d4\xf4O\x85Q\xac\xe93r\a\x12\xc5\u0111\u0376\xf2\xf9Qsl\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\xf4V\x05Z\x11\xab\x91\xfff\x8e.\xc9\"\x96\x1f*#\xe3\xdb%\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf4V\xa7[\xb9\x96U\xa7A,\xe9}\xa0\x81\x81m\xfd\xb2\xb1\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf4[\x1d\xcb.A\xdc'\xff\xa0$\u06ad\xf6\x19\xc1\x11u\xc0\x87\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xf4c\xa9\f\xb3\xf1>\x1f\x06CB66\xbe\xab\x84\xc1#\xb0m\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf4h\x90n~\xdffJ\xb0\u063e=\x83\xebz\xb3\xf7\xff\xdcx\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xf4i\x80\u3929\u049ajn\x90`E7\xa3\x11K\xcb(\x97\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf4kk\x9c|\xb5R\x82\x9c\x1d=\xfd\x8f\xfb\x11\xaa\xba\xe7\x82\xf6\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xf4v\xe1&\u007f\x86$|\xc9\b\x81o.z\xd58\x8c\x95-\xb0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf4v\xf2\xcbr\b\xa3.\x05\x1f\xd9N\xa8f)\x92c\x82\x87\xa2\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xf4{\xb14\xda0\xa8\x12\xd0\x03\xaf\x8d\u0338\x88\xf4K\xbfW$\x8a\x01\x19Y\xb7\xfe3\x95X\x00\x00\u07d4\xf4\x83\xf6\a\xa2\x1f\xcc(\x10\n\x01\x8cV\x8f\xfb\xe1@8\x04\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf4\x8e\x1f\x13\xf6\xafM\x84\xb3q\xd7\xdeK'=\x03\xa2c'\x8e\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xf4\x9cG\xb3\xef\xd8knj[\xc9A\x8d\x1f\x9f\xec\x81Ki\xef\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xf4\x9fo\x9b\xaa\xbc\x01\x8c\x8f\x8e\x11\x9e\x01\x15\xf4\x91\xfc\x92\xa8\xa4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf4\xa3g\xb1f\u0499\x1a+\xfd\xa9\xf5dc\xa0\x9f%,\x1b\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\xa5\x1f\xceJ\x1d[\x94\xb0q\x83\x89\xbaNx\x14\x13\x9c\xa78\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf4\xa9\xd0\f\xef\xa9{zX\xef\x94\x17\xfcbg\xa5\x06\x909\xee\x89\x01.\x89(\u007f\xa7\x84\x00\x00\u07d4\xf4\xaa\xa3\xa6\x16>7\x06W{I\xc0v~\x94\x8ah\x1e\x16\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\xb1bn$\xf3\v\xca\xd9'<R\u007f\xccqK]\x00{\x8f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf4\xb4\x91\x00uwr\xf3<\x17{\x9av\xba\x95\"l\x8f=\u060a\x01k5-\xa5\xe0\xed0\x00\x00\xe0\x94\xf4\xb6\xcd\xcf\xcb$#\v3}w\r\xf6\x03M\xfb\xd4\xe1P?\x8a\x04\x05\xfd\xf7\u5bc5\xe0\x00\x00\u07d4\xf4\xb7Y\u030a\x1cu\xf8\bI\xeb\xbc\u0687\x8d\xc8\xf0\xd6m\xe4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf4\xbajF\xd5Q@\xc49\xcb\xcf\al\xc6W\x13bb\xf4\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\xd6z\x90D\xb45\xb6n\x89w\xff9\xa2\x8d\u013dSr\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf4\xd9vd\xccN\xec\x9e\xdb\xe7\xfa\t\xf4u\nf;P}y\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xf4\xdc{\xa8T\x80\xbb\xb3\xf55\xc0\x95h\xaa\xa3\xafo7!\u018a\x01\x87\x1f\xb60~5\xe5\x00\x00\xe0\x94\xf4\xeb\xf5\v\xc7\xe5O\x82\u9e7d$\xba\xef)C\x8e%\x9c\xe6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf4\uc397\xa2\n\xa5\xf8\xdd oU ~\x06\xb8\x13\xdf,\xc0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf4\ud10e\xc9as\x9c,~5/C[\xa7\n|\xd5\xdb8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\xfcM9\xbc\f,@h\xa3m\xe5\x0eJ\xb4\xd4\xdb~4\n\x89\x01`7\u07c7\xefj\x00\x00\xe0\x94\xf5\x04\x94:\xaf\x16yn\v4\x1b\xbc\xdf!\xd1\x1c\u0146\xcd\u044a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\xf5\x06\x1e\xe2\xe5\xee&\xb8\x15P6w\x13\x0e\x1d\xe0zR\xdb\a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf5\tU~\x90\x18?\xbf\x0f\x06Q\xa7\x86H{\xccB\x8b\xa1u\x89\n\x84Jt$\xd9\xc8\x00\x00\xe0\x94\xf5\n\xbb\u052aE\xd3\xeb\x88QTe\xa8\xba\v1\x0f\u0675!\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf5\n\xe7\xfa\xb4\u03f5\xa6F\xee\x04\u03ad\xf9\xbf\x9d\u0568\xe5@\x89\xd8\xd6|/X\x95H\x00\x00\u07d4\xf5\f\xba\xfd9~\xddUl\x06x\x98\x8c\xb2\xaf\\&\x17\u0889&\xd0~\xfex+\xb0\x00\x00\xe0\x94\xf5\x1f\xde\xd8\n\xcbP(\x90\xe8sit\x1f7\"QL\xef\xff\x8a\x04<4V\xca<m\x11\x00\x00\xe0\x94\xf5*X\x82\xe8\x92}\x94K5\x9b&6k\xa2\xb9\xca\u03fa\xe8\x8a\x05KA\xce/\xe6;\xa8\x00\x00\xe0\x94\xf5,\nxw4_\xe0\xc23\xbb\x0f\x04\xfdj\xb1\x8bo\x14\xba\x8aT\xcb\xe5Y\x89\xf3\x8d\xe0\x00\x00\u07d4\xf5C~\x15\x80\x90\xb2\xa2\u058f\x82\xb5JXd\xb9]\xd6\xdb\xea\x89\xd9l\x16p;+\xfe\x00\x00\xe0\x94\xf5L\x19\xd9\xef8s\xbf\xd1\xf7\xa6\"\xd0-\x86$\x9a2\x8f\x06\x8a\t`\xae\x12z\xf3/\xb2\x80\x00\u07d4\xf5P\x01x\u02d9\x8f\x12d\x17\x83\x1a\b\xc2\u05eb\xff\xf6\xab_\x89F\xf4\xf4\xa5\x87Z\x9f\x80\x00\u07d4\xf5SH\x15\xdcc^\xfa\\\xc8K*\xc74r>!\xb2\x93r\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xf5U\xa2{\xb1\xe2\xfdN,\u01c4\xca\ue493\x9f\xc0n/\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5X\xa2\xb2\xdd&\u0755\x93\xaa\xe0E1\xfd<<\u00c5Kg\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xf5`H\xdd!\x81\u0523od\xfc\xec\xc6!T\x81\xe4*\xbc\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5dB\xf6\x0e!i\x13\x95\u043f\xfa\xa9\x19M\xca\xff\x12\u2dc9\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xf5yqJE\xeb\x8fR\xc3\xd5{\xbd\xef\xd2\xc1[./\x11\u07c9T\x91YV\xc4\t`\x00\x00\u07d4\xf5\x93\xc6R\x85\xeek\xbdf7\U000fe3c9\xad@\u0509\xf6U\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf5\x98\xdb.\t\xa8\xa5\xee}r\r+\\C\xbb\x12m\x11\xec\u0089\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf5\x9d\xab\x1b\xf8\xdf\x112~a\xf9\xb7\xa1KV:\x96\xec5T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf5\x9f\x9f\x02\xbb\u024e\xfe\t~\xab\xb7\x82\x10\x97\x90!\x89\x8b\xfd\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\xf5\xa5E\x9f\xcd\xd5\xe5\xb2s\x83\r\xf8\x8e\xeaL\xb7}\xda\u07f9\x89\x04\t\xe5+H6\x9a\x00\x00\u07d4\xf5\xa7gj\xd1H\xae\x9c\x1e\xf8\xb6\xf5\xe5\xa0\xc2\xc4s\xbe\x85\v\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5\xb0h\x98\x9d\xf2\x9c%5w\xd0@Z\xden\x0eu(\xf8\x9e\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xf5\xb6\xe9\x06\x1aN\xb0\x96\x16\aw\xe2gb\xcfH\xbd\u0635]\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xf5\xcf\xfb\xbabN~\xb3!\xbc\x83\xc6\f\xa6\x81\x99\xb4\xe3fq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5\xd1ER\xb1\xdc\xe0\xd6\xdc\x1f2\r\xa6\xff\u02231\xcdo\f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xf5\xd6\x1a\xc4\u0295G^[{\xff\xd5\xf2\xf6\x90\xb3\x16u\x96\x15\x8a\x06\x92\xae\x88\x97\b\x1d\x00\x00\x00\u07d4\xf5\xd9\xcf\x00\xd6X\xddEQzH\xa9\xd3\xf5\xf63T\x1aS=\x89\x06O_\xdfIOx\x00\x00\u07d4\xf5\xea\xdc\xd2\u0478ez\x12\x1f3\xc4X\xa8\xb1>v\xb6U&\x89\r\x8b\x0fZZ\xc2J\x00\x00\u07d4\xf6\a\xc2\x15\r>\x1b\x99\xf2O\xa1\xc7\xd5@\xad\xd3\\N\xbe\x1e\x89\xa7\xf1\xaa\a\xfc\x8f\xaa\x00\x00\u07d4\xf6\v\xd75T>k\xfd.\xa6\xf1\x1b\xffbs@\xbc\x03Z#\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6\f\x1bE\xf1d\xb9X\x0e 'Z\\9\xe1\xd7\x1e5\xf8\x91\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf6\x0fb\xd797\x95?\xef5\x16\x9e\x11\xd8r\xd2\xea1~\xec\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xf6\x12\x83\xb4\xbd\x85\x04\x05\x8c\xa3`\u94d9\x9bb\xcb\xc8\xcdg\x89\r\xd2\xd5\xfc\xf3\xbc\x9c\x00\x00\u07d4\xf6\x17\xb9g\xb9\xbdH_v\x95\xd2\xefQ\xfbw\x92\u0618\xf5\x00\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf6\x18\u0671\x04A\x14\x80\xa8c\xe6#\xfcU#-\x1aOH\xaa\x89\x0eh\x9emD\xb1f\x80\x00\u07d4\xf6\"\u5126b>\xaa\xf9\x9f+\xe4\x9eS\x80\xc5\xcb\xcf\\\u0609\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf62\xad\xffI\r\xa4\xb7-\x126\xd0KQ\x0ft\xd2\xfa\xa3\u0349K\xe4\xe7&{j\xe0\x00\x00\u07d4\xf69\xac1\u069fg'\x1b\xd1\x04\x02\xb7eN\\\xe7c\xbdG\x89\x15\xaf\x0fB\xba\xf9&\x00\x00\u07d4\xf6:W\x9b\xc3\xea\u00a9I\x04\x10\x12\x8d\xbc\xeb\xe6\xd9\u0782C\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xf6E\xdd|\x89\x00\x93\xe8\xe4\u022a\x92\xa6\xbb55\"\xd3\u0718\x89\aC\x9f\xa2\t\x9eX\x00\x00\xe0\x94\xf6H\xea\x89\xc2u%q\x01r\x94Ny\xed\xff\x84x\x03\xb7u\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf6JJ\xc8\xd5@\xa9(\x9ch\xd9`\xd5\xfb|\xc4Zw\x83\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6N\xcf!\x17\x93\x1cmSZ1\x1eO\xfe\xae\xf9\u0514\x05\xb8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf6O\xe0\x93\x9a\x8d\x1e\xea*\x0e\u035a\x970\xfdyX\xe31\t\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xf6V\x16\xbe\x9c\x8by~t\x15\"|\x918\xfa\xa0\x89\x17B\u05c9*\xd3s\xcef\x8e\x98\x00\x00\u07d4\xf6W\xfc\xbeh.\xb4\xe8\xdb\x15.\u03c9$V\x00\vQ=\x15\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf6X\x19\xacL\xc1L\x13\u007f\x05\xddyw\xc7\xda\xe0\x8d\x1aJ\xb5\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xf6{\xb8\xe2\x11\x8b\xbc\u0550'fn\xed\xf6\x94>\xc9\xf8\x80\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x84d\xbfd\xf2A\x13V\xe4\xd3%\x0e\xfe\xfe\\P\xa5\xf6[\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf6\x86x[\x89r\va\x14_\ua017\x8dj\u030e\v\xc1\x96\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x8c^3\xfa\x97\x13\x9d\xf5\xb2\xe68\x86\xce4\xeb\xf3\u45dc\x89\xb3\xfaAi\xe2\xd8\xe0\x00\x00\u07d4\xf6\xa8cWW\xc5\xe8\xc14\xd2\r\x02\x8c\xf7x\u03c6\t\xe4j\x89O\x1dw/\xae\xc1|\x00\x00\u07d4\xf6\xb7\x82\xf4\xdc\xd7E\xa6\xc0\xe2\xe00`\x0e\x04\xa2K%\xe5B\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xf6\xbc7\xb1\u04a3x\x8dX\x9bm\xe2\x12\xdc\x17\x13\xb2\xf6\u738a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xf6\xc3\u010a\x1a\xc0\xa3G\x99\xf0M\xb8n\u01e9u\xfewh\xf3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf6\xd2]?=\x84m#\x9fR_\xa8\xca\xc9{\xc45x\u06ec\x890\x92\u007ft\xc9\xde\x00\x00\x00\u07d4\xf6\xea\xacp2\u0512\xef\x17\xfd`\x95\xaf\xc1\x1dcOV\xb3\x82\x89\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\xf6\xea\xd6}\xbf[~\xb13X\xe1\x0f6\x18\x9dS\xe6C\xcf\u03ca\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf6\xf1\xa4C\t\x05\x1ck%\xe4}\xff\x90\x9b\x17\x9b\xb9\xabY\x1c\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf7\x03(\xef\x97b_\xe7E\xfa\xa4\x9e\xe0\xf9\u052a;\r\xfbi\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\n\x99\x8aq{3\x8d\x1d\u0658T@\x9b\x1a3\x8d\ue930\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\rcz\x84\\\x06\xdbl\u0711\xe67\x1c\xe7\xc48\x8ab\x8e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x15R\x13D\x98\x92tK\xc6\x0f.\x04@\a\x88\xbd\x04\x1f\u0749\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\xe0\x94\xf7\x1bE4\xf2\x86\xe40\x93\xb1\xe1^\xfe\xa7I\xe7Y{\x8bW\x8a\x16\x1c\x13\xd34\x1c\x87(\x00\x00\u07d4\xf74\xec\x03rM\xde\xe5\xbbRy\xaa\x1a\xfc\xf6\x1b\f\xb4H\xa1\x89\xe5\xbf,\u0270\x97\x80\x00\x00\u07d4\xf76\u0716v\x00\x128\x8f\xe8\x8bf\xc0n\xfeW\xe0\xd7\xcf\n\x89q\xd7Z\xb9\xb9 P\x00\x00\u07d4\xf7:\xc4l ;\xe1S\x81\x11\xb1Q\xec\x82 \u01c6\xd8AD\x89\x0f\xf77x\x17\xb8+\x80\x00\u07d4\xf7=\xd9\xc1B\xb7\x1b\xce\x11\xd0n0\xe7\xe7\xd02\xf2\uc71e\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf7A\x8a\xa0\xe7\x13\xd2H\"\x87v\xb2\xe7CB\"\xaeu\u3949lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7Nn\x14S\x82\xb4\u06c2\x1f\xe0\xf2\u0643\x88\xf4V\t\u019f\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf7P\f\x16o\x8b\xea/\x824v\x06\xe5\x02K\xe9\xe4\xf4\u0399\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7W\xfc\x87 \xd3\xc4\xfaRw\a^`\xbd\\A\x1a\xeb\xd9w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7[\xb3\x9cy\x97y\xeb\xc0J3m&\r\xa61F\xed\x98\u0409\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xf7h\xf3!\xfdd3\xd9kO5M<\xc1e,\x172\xf5\u007f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf7oi\xce\xe4\xfa\xa0\xa6;0\xae\x1ex\x81\xf4\xf7\x15ep\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf7w6\x1a=\u062bb\xe5\xf1\xb9\xb0GV\x8c\xc0\xb5UpL\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7|{\x84QI\xef\xba\x19\xe2a\xbc|u\x15y\b\xaf\xa9\x90\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\u007f\x95\x87\xffz-r\x95\xf1\xf5q\u0206\xbd3\x92jR|\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xf7\x82X\xc1$\x81\xbc\xdd\u06f7*\x8c\xa0\xc0C\tra\xc6\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x98\xd1m\xa4\xe4`\xc4`\xcdH_\xae\x0f\xa0Y\x97\b\ub08965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\xa1\xad\xe2\xd0\xf5)\x12=\x10U\xf1\x9b\x17\x91\x9fV!Ng\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf7\xac\xff\x93K\x84\xda\ti\xdc7\xa8\xfc\xf6C\xb7\xd7\xfb\xedA\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf7\xb1Q\xcc^W\x1c\x17\xc7e9\xdb\xe9\x96L\xbbo\xe5\xdey\x89tq|\xfbh\x83\x10\x00\x00\u07d4\xf7\xb2\x9b\x82\x19\\\x88-\xabx\x97\u00ae\x95\xe7w\x10\xf5xu\x89w5Aa2\xdb\xfc\x00\x00\u07d4\xf7\xbcLD\x91\rZ\xed\xd6n\xd25U8\xa6\xb1\x93\xc3a\xec\x89\x05A\xde,-\x8db\x00\x00\u07d4\xf7\xc0\f\xdb\x1f\x02\x03\x10\u056c\xab{Ij\xaaD\xb7y\b^\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xf7\xc1\xb4C\x96\x8b\x11{]\u0677UW/\xcd9\xca^\xc0K\x89\x18\xb9h\u0092\xf1\xb5\x00\x00\xe0\x94\xf7\xc5\x0f\x92*\xd1ka\xc6\u047a\xa0E\xed\x81h\x15\xba\u010f\x8a\x02\xa99j\x97\x84\xad}\x00\x00\u07d4\xf7\xc7\b\x01Pq\xd4\xfb\n:*\t\xa4]\x15c\x96\xe34\x9e\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf7\xcb\u06e6\xbel\xfeh\xdb\xc2<+\x0f\xf50\xee\x05\"o\x84\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\xd0\xd3\x10\xac\xea\x18@a8\xba\xaa\xbb\xfe\x05q\xe8\r\xe8_\x89Hz\x9a0E9D\x00\x00\u07d4\xf7\u05ef LV\xf3\x1f\xd9C\x98\xe4\r\xf1\x96K\u063f\x12<\x89\b!\xd2!\xb5)\x1f\x80\x00\u07d4\xf7\xdc%\x11\x96\xfb\u02f7|\x94}|\x19F\xb0\xffe\x02\x1c\xea\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf7\xe4Z\x12\xaaq\x1cp\x9a\xce\xfe\x95\xf3;xa-*\xd2*\x8a\x0e\x06U\xe2\xf2k\xc9\x18\x00\x00\u07d4\xf7\xf4\x89\x8cLRm\x95_!\xf0U\xcbnG\xb9\x15\xe5\x19d\x89|\b`\xe5\xa8\r\xc0\x00\x00\u07d4\xf7\xf9\x1ez\xcb[\x81)\xa3\x06\x87|\xe3\x16\x8eoC\x8bf\xa1\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xf7\xfcE\xab\xf7oP\x88\xe2\u5d68\xd12\xf2\x8aMN\xc1\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\x06:\xf4\xcc\x1d\xd9a\x9a\xb5\u063f\xf3\xfc\xd1\xfa\xa8H\x82!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\bnBf\x1e\xa9)\xd2\u0761\xablt\x8c\xe3\x05]\x11\x1e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf8\bw\x86\xb4-\xa0N\xd6\xd1\xe0\xfe&\xf6\xc0\xee\xfe\x1e\x9fZ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf8\r6\x19p/\xa5\x83\x8cH9\x18Y\xa89\xfb\x9c\xe7\x16\x0f\x89l\a\xa7\u0471np\x00\x00\u07d4\xf8\x14y\x9fm\xdfM\xcb)\xc7\xee\x87\x0eu\xf9\xcc-52m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf8\x15\xc1\n\x03-\x13\xc3K\x89v\xfan;\xd2\xc9\x13\x1a\x8b\xa9\x89Hz\x9a0E9D\x00\x00\u07d4\xf8\x16\"\xe5WW\xda\xeafu\x97]\xd958\xda}\x16\x99\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8$\xee3\x1eJ\xc3\xccXv\x939[W\xec\xf6%\xa6\xc0\u0089V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4\xf8'\xd5n\xd2\xd3' \u052b\xf1\x03\xd6\xd0\xefM;\xcdU\x9b\x89\x01l\x80\x06W\x91\xa2\x80\x00\u07d4\xf8)\x85\x91R>P\xb1\x03\xf0\xb7\x01\xd6#\xcb\xf0\xf7EV\xf6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8H\xfc\xe9\xaba\x1c}\x99 n#\xfa\u019a\u0508\xb9O\xe1\x89\x02\xa1\x12\x9d\t6r\x00\x00\u07d4\xf8O\t\n\xdf?\x8d\xb7\u1533P\xfb\xb7u\x00i\x9ff\xfd\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf8Q\xb0\x10\xf63\xc4\n\xf1\xa8\xf0js\ubeabe\az\xb5\x89\xee\x86D/\xcd\x06\xc0\x00\x00\u07d4\xf8X\x17\x1a\x04\xd3W\xa1;IA\xc1n~U\xdd\u0514\x13)\x89\x02F\xa5!\x8f*\x00\x00\x00\u07d4\xf8[\xab\x1c\xb3q\x0f\xc0_\xa1\x9f\xfa\xc2.gR\x1a\v\xa2\x1d\x89l\x955\u007f\xa6\xb3l\x00\x00\u07d4\xf8j>\xa8\a\x1fp\x95\xc7\u06ca\x05\xaePz\x89)\u06f8v\x89\x126\xef\xcb\u02f3@\x00\x00\u07d4\xf8pL\x16\xd2\xfd[\xa3\xa2\xc0\x1d\x0e\xb2\x04\x84\xe6\xec\xfa1\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8p\x99_\xe1\xe5\"2\x1duC7\xa4\\\f\x9d{8\x95\x1c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf8s\xe5ze\xc9;n\x18\xcbu\xf0\xdc\a}[\x893\xdc\\\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf8ua\x9d\x8a#\xe4]\x89\x98\u0444\u0500\xc0t\x89p\x82*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8{\xb0{(\x9d\xf70\x1eT\xc0\xef\xdaj,\xf2\x91\xe8\x92\x00\x89K\xe4\xe7&{j\xe0\x00\x00\u0794\xf8\x89\x00\xdbsyU\xb1Q\x9b\x1a}\x17\n\x18\x86L\xe5\x90\xeb\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf8\x8bX\xdb7B\vFL\v\xe8\x8bE\xee+\x95)\x0f\x8c\xfa\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf8\x96+u\xdb]$\xc7\xe8\xb7\xce\xf1\x06\x8c>g\u03bb0\xa5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xf8\xa0e\xf2\x87\xd9\x1dw\xcdbj\xf3\x8f\xfa\"\r\x9bU*+\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xf8\xa4\x9c\xa29\f\x1fm\\\x0ebQ;\a\x95qt?|\u0189\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf8\xa5\f\xee.h\x8c\xee\u3b24\u0522\x97%\xd4\a,\u0103\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xacJ9\xb5<\x110x \x97;D\x13e\xcf\xfeYof\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xae\x85{g\xa4\xa2\x89:?\xbe|z\x87\xff\x1c\x01\u01a6\xe7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8\xbf\x9c\x04\x87NZw\xf3\x8fL8R~\x80\xc6v\xf7\xb8\x87\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xc7\xf3J8\xb3\x18\x01\xdaC\x064w\xb1+'\xd0\xf2\x03\xff\x89\x1a\u04ba\xbao\xefH\x00\x00\u07d4\xf8\xca3l\x8e\x91\xbd \xe3\x14\xc2\v-\xd4`\x8b\x9c\x8b\x94Y\x89-\u071b\u0173,x\x00\x00\u07d4\xf8\xd1t$\xc7g\xbe\xa3\x12\x05s\x9a+W\xa7'r\x14\uef89\x02F\xdd\xf9yvh\x00\x00\u07d4\xf8\xd5-\xcc_\x96\xcc(\x00{>\u02f4\t\xf7\xe2*dl\xaa\x89\b\x16\x90\xe1\x81(H\x00\x00\u07d4\xf8\xdc\xe8g\xf0\xa3\x9c[\xef\x9e\xeb\xa6\t\"\x9e\xfa\x02g\x8bl\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xf2&\x14*B\x844\xab\x17\xa1\x86J%\x97\xf6J\xab/\x06\x89\tY\x8b/\xb2\xe9\xf2\x80\x00\u07d4\xf8\xf6d^\r\xeedK=\xad\x81\xd5q\uf6ef\x84\x00!\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x01\xc0\x0f\xc1\u06c8\xb6\x9cK\xc3%+\\\xa7\x0e\xa6\xee\\\xf6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9=[\xcb\x06D\xb0\xcc\xe5\xfc\u0763C\xf5\x16\x8f\xfa\xb2\x87}\x89\vb\a\xb6}&\xf9\x00\x00\u07d4\xf9W\x0e\x92L\x95\u07bbpa6\x97\x92\xcf.\xfe\u00a8-^\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf9d \x86\xb1\xfb\xaea\xa6\x80M\xbe_\xb1^\xc2\u04b57\xf4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9d\x88i\x85\x90\xdc;,UVB\xb8q4\x8d\xfa\x06z\u0549\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9d\u064d(\x170\xba5\xb2\xe3\xa3\x14yn{B\xfe\xdfg\x89S\xb0\x87`\x98\xd8\f\x00\x00\u07d4\xf9e\ri\x89\xf1\x99\xab\x1c\xc4ycm\xed0\xf2A\x02\x1fe\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xf9h\x83X$Y\x90\x8c\x82v'\xe8o(\xe6F\xf9\xc7\xfcz\x8a\x01\u0127\x877\xcd\u03f8\x00\x00\u07d4\xf9kL\x00voSsj\x85t\xf8\"\xe6GL/!\xda-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9r\x9dH(,\x9e\x87\x16m^\xef-\x01\xed\xa9\xdb\xf7\x88!\x89\x05k\x83\xdd\xc7(T\x80\x00\u07d4\xf9v~N\xcbJY\x80Ru\b\u05fe\xc3\xd4^Ld\x9c\x13\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xf9x\xb0%\xb6B3U\\\xc3\xc1\x9a\xda\u007fA\x99\xc94\x8b\xf7\x8aT\xb4\v\x1f\x85+\xda\x00\x00\x00\u07d4\xf9{V\xeb\u0577z\xbc\x9f\xba\u02eb\u0514\xb9\xd2\xc2!\xcd\x03\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf9\x81\x1f\xa1\x9d\xad\xbf\x02\x9f\x8b\xfeV\x9a\xdb\x18\"\x8c\x80H\x1a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf9\x82Ps\fLa\xc5\u007f\x12\x985\xf2h\b\x94yEB\xf3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xf9\x894gr\x99^\xc1\x90o\xaf\xfe\xba*\u007f\xe7\u079ck\xab\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf9\x98\xca4\x11s\nl\xd1\x0etU\xb0A\x0f\xb0\xf6\xd3\xff\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x9a\xeeDKW\x83\xc0\x93\xcf\xff\xd1\xc4c,\xf9<o\x05\f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9\x9e\xee\xce9\xfa~\xf5\am\x85Pa8@\ty,\xf2\xe0\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9\xa5\x9c<\xc5\xff\xac\xbc\xb6{\xe0\xfcrV\xf6L\x9b\x12|\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\xa9K\xd5a\x98\xda$^\xd0\x1d\x1ed0\xb2K'\b\xdc\xc0\x89(\xa7z\xfd\xa8~\xe5\x00\x00\u07d4\xf9\xb3x%\xf00s\xd3\x1e$\x93x\xc3\fy\\3\xf8:\xf2\x89\n\u066a\xbf\x8c\x9b\xfc\x00\x00\u07d4\xf9\xb6\x17\xf7R\xed\xec\xae>\x90\x9f\xbb\x91\x1d/\x81\x92\xf8B\t\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf9\xbf\xb5\x9dS\x8a\xfcHt\xd4\xf5\x94\x1b\b\xc9s\x0e8\xe2K\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf9\xdd#\x90\b\x18/\xb5\x19\xfb0\xee\xdd \x93\xfe\xd1c\x9b\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9\u07ba\xec\xb5\xf39\xbe\xeaH\x94\xe5 K\xfa4\r\x06\u007f%\x89ZB\x84Fs\xb1d\x00\x00\xe0\x94\xf9\xe3tG@lA!\x97\xb2\u2bbc\x00\x1dn0\u024c`\x8a\x01\xc4y\xbbCI\xc0\xee\x00\x00\u07d4\xf9\xe7\"/\xaa\xf0\xf4\xda@\xc1\u0124\x0607:\t\xbe\u05f6\x89\x9bO\u0730\x94V$\x00\x00\u07d4\xf9\xec\xe0\"\xbc\xcd,\x924i\x11\xe7\x9d\xd5\x03\x03\xc0\x1e\x01\x88\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfa\x00\xc3v\xe8\x9c\x05\u81c1z\x9d\xd0t\x8d\x96\xf3A\xaa\x89\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xfa\f\x1a\x98\x8c\x8a\x17\xad5(\xeb(\xb3@\x9d\xaaX\"_&\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa\x10_\x1a\x11\xb6\xe4\xb1\xf5`\x12\xa2y\"\xe2\xac-\xa4\x81/\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfa\x14/\xe4~\u0697\xe6P;8k\x18\xa2\xbe\xdds\u0335\xb1\x89.\x15:\xd8\x15H\x10\x00\x00\u07d4\xfa\x14\xb5f#J\xbe\xe70B\xc3\x1d!qq\x82\u02e1J\xa1\x89\x11\xc7\xea\x16.x \x00\x00\u07d4\xfa\x19\xd6\xf7\xa5\x0fO\a\x98\x93\xd1g\xbf\x14\xe2\x1d\x00s\u0456\x89\x1c\xbb:?\xf0\x8d\b\x00\x00\u07d4\xfa\x1f\x19q\xa7u\xc3PO\xefPy\xf6@\xc2\u013c\xe7\xac\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfa'\x9b\xfd\x87g\xf9V\xbf\u007f\xa0\xbdV`\x16\x8d\xa7V\x86\xbd\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xfa'\xccI\xd0\vl\x98s6\xa8u\xae9\xdaX\xfb\x04\x1b.\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfa(2\x99`=\x87X\xe8\u02b0\x82\x12],\x8f}DT)\x8a\x01[\xca\xcb\x1e\x05\x01\xae\x80\x00\u07d4\xfa+\xbc\xa1]?\u37ca2\x8e\x91\xf9\r\xa1Oz\xc6%=\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa/\u049d\x03\xfe\xe9\xa0x\x93\xdf:&\x9fV\xb7/.\x1ed\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfa3U2\x85\xa9sq\x9a\r_\x95o\xf8a\xb2\u061e\xd3\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\fK\x90?n\xa5.\xa7\xab{\x88c\xb6\xa6\x16\xadfP\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\x1a\xa4H\x8b5\x1a\xa7V\f\xf5\xeec\n/\xd4\\2\"\x89/\xa4~j\xa74\r\x00\x00\u07d4\xfaA\tq\xad\"\x9c06\xf4\x1a\u03c5/*\u0259(\x19P\x89\u0633\x11\xa8\xdd\xfa|\x00\x00\u07d4\xfaD\xa8U\xe4\x04\xc8m\f\xa8\xef3$%\x1d\xfb4\x9cS\x9e\x89T\"S\xa1&\xce@\x00\x00\xe0\x94\xfaR\x01\xfe\x13B\xaf\x110{\x91B\xa0A$<\xa9./\t\x8a 8\x11j:\xc0C\x98\x00\x00\xe0\x94\xfa`\x86\x8a\xaf\xd4\xffL\\W\x91K\x8e\u054bBWs\u07e9\x8a\x01\xcf\xe5\xc8\b\xf3\x9f\xbc\x00\x00\u07d4\xfag\xb6{O7\xa0\x15\t\x15\x11\x0e\xde\a;\x05\xb8S\xbd\xa2\x89#\x19\xba\x94sq\xad\x00\x00\u07d4\xfah\xe0\xcb>\xdfQ\xf0\xa6\xf2\x11\u0272\xcb^\a<\x9b\xff\xe6\x89\x0f\xc969(\x01\xc0\x00\x00\xe0\x94\xfaj7\xf0\x18\xe9yg\x93\u007f\xc5\xe8a{\xa1\u05c6\xdd_w\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xfav\x06C[5l\xee%{\xd2\xfc\xd3\xd9\xea\xcb<\xd1\xc4\xe1\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfaz\xdff\v\x8d\x99\xce\x15\x93=|_\a/<\xbe\xb9\x9d3\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfa\x86\xca'\xbf(T\u0648p\x83\u007f\xb6\xf6\xdf\xe4\xbfdS\xfc\x89\x11u~\x85%\xcf\x14\x80\x00\u07d4\xfa\x8c\xf4\xe6'i\x8c]W\x88\xab\xb7\x88\x04\x17\xe7P#\x13\x99\x89\xe6\x1a6\x96\xee\xf6\x10\x00\x00\u07d4\xfa\x8e;\x1f\x13C9\x00s}\xaa\xf1\xf6)\x9cH\x87\xf8[_\x89&\u009eG\u0104L\x00\x00\u07d4\xfa\x9e\xc8\xef\xe0\x86\x86\xfaX\xc1\x813Xr\xbai\x85`\ucac9lj\xccg\u05f1\xd4\x00\x00\u07d4\xfa\xad\x90]\x84|{#A\x8a\xee\xcb\xe3\xad\u06cd\xd3\xf8\x92J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xfa\xae\xba\x8f\xc0\xbb\xdaU<\xa7.0\xef=s.&\xe8 A\x89H\x8d(*\xaf\xc9\xf6\x80\x00\u07d4\xfa\xb4\x87P\r\xf2\x0f\xb8>\xbe\xd9\x16y\x1dV\x17r\xad\xbe\xbf\x89lkLM\xa6\u077e\x00\x00\u07d4\xfa\xc5\u0294u\x80x\xfb\xfc\xcd\x19\xdb5X\xda~\u8827h\x897(\xa6+\r\xcf\xf6\x00\x00\u07d4\xfa\xd9j\xb6\xacv\x8a\xd5\t\x94R\xacGw\xbd\x1aG\xed\u010f\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfa\xe7g\x19\xd9~\xacA\x87\x04(\xe9@'\x9d\x97\xddW\xb2\xf6\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xfa\u8053pG\x89Zf\f\xf2)v\x0f'\xe6h(\xd6C\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xfa\xe9,\x13p\xe9\u115a]\xf8;V\xd0\xf5\x86\xaa;@L\x89\x05\u0174\xf3\xd8C\x98\x00\x00\xe0\x94\xfa\xf5\xf0\xb7\xb6\xd5X\xf5\t\r\x9e\xa1\xfb-B%\x9cX`x\x8a\x01Z\xff\xb8B\fkd\x00\x00\xe0\x94\xfb\x12o\x0e\xc7i\xf4\x9d\xce\xfc\xa2\xf2\x00(dQX0\x84\xb8\x8a\x01\x0f\xcb\xc25\x03\x96\xbf\x00\x00\xe0\x94\xfb\x13^\xb1Z\x8b\xacr\xb6\x99\x154*`\xbb\xc0k~\a|\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\"<\x1e\"\xea\xc1&\x9b2\xee\x15jS\x85\x92.\xd3o\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb7\xcfkO\x81\xa9\xe2\"\xfb\xa2.\x9b\xd2KP\x98\xb73\u03c9\x02\x1auJm\xc5(\x00\x00\u07d4\xfb8`\xf4\x12\x1cC.\xbd\xc8\xecj\x031\xb1\xb7\ty.\x90\x89 \x8c9J\xf1\u0208\x00\x00\u07d4\xfb9\x18\x9a\xf8v\xe7b\xc7\x1dl>t\x18\x93\xdf\"l\xed\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfb:\v\rkjq\x8fo\xc0)*\x82]\xc9$z\x90\xa5\u0409\n\xd6\xdd\x19\x9e\x97[\x00\x00\xe0\x94\xfb?\xa1\xac\b\xab\xa9\xcc;\xf0\xfe\x9dH8 h\x8fe\xb4\x10\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xfb?\xe0\x9b\xb86\x86\x15)\xd7Q\x8d\xa2v5\xf58PV\x15\x89K\xe3\x92\x16\xfd\xa0p\x00\x00\xe0\x94\xfbQ%\xbf\x0f^\xb0\xb6\xf0 \xe5k\xfc/\xdf=@,\t~\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfbU\x18qL\xef\xc3m\x04\x86]\xe5\x91^\xf0\xffG\xdf\xe7C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb_\xfa\xa0\xf7aW&5x\x91GX\x18\x93\x9d 7\u03d6\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfbh\\\x15\xe49\x96^\xf6&\xbf\r\x83L\u0468\x9f+V\x95\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xfbtK\x95\x1d\tK1\x02b\xc8\xf9\x86\xc8`\u07da\xb1\xdee\x89\x02\xd1\xc5\x15\xf1\xcbJ\x80\x00\u07d4\xfby\xab\u06d2\\U\xb9\xf9\x8e\xfe\xefd\xcf\xc9\xeba\xf5\x1b\xb1\x89a@\xc0V\xfb\n\xc8\x00\x00\u07d4\xfb\x81\x13\xf9M\x91s\xee\xfdZ0s\xf5\x16\x80:\x10\xb2\x86\xae\x89\x04V9\x18$O@\x00\x00\u07d4\xfb\x84,\xa2\xc5\xef\x139\x17\xa26\xa0\u052c@i\x01\x10\xb08\x89\x10\x96\x9ab\xbe\x15\x88\x00\x00\u07d4\xfb\x91\xfb\x1aiUS\xf0\u018e!'m\xec\xf0\xb89\t\xb8m\x89\x05l\x006\x17\xafx\x00\x00\u07d4\xfb\x94s\xcfw\x125\n\x1f\xa09Rs\xfc\x80V\aR\xe4\xfb\x89\x06\xaf!\x98\xba\x85\xaa\x00\x00\xe0\x94\xfb\x94\x9cd\u007f\xdc\xfd%\x14\xc7\u054e1\xf2\x8aS-\x8cX3\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\xa5HmS\xc6\xe2@IBA\xab\xf8~C\xc7`\rA:\x89k\xbfaIIH4\x00\x00\u07d4\xfb\xb1a\xfe\x87_\t)\nK&+\xc6\x01\x10\x84\x8f\r\"&\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb\xbb\xeb\u03fe#^W\xdd#\x06\xad\x1a\x9e\u0141\xc7\xf9\xf4\x8f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfb\xc0\x1d\xb5NG\xcd\xc3\xc48iJ\xb7\x17\xa8V\xc2?\xe6\xe9\x8a\x01\xcaqP\xab\x17OG\x00\x00\xe0\x94\xfb\xcf\xccJ{\x0f&\xcf&\xe9\xf33!2\xe2\xfcj#\af\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfb\xe7\x16\"\xbc\xbd1\xc1\xa3iv\xe7\xe5\xf6p\xc0\u007f\xfe\x16\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfb\xed\xe3,4\x9f3\x00\xefL\xd3;M\xe7\xdc\x18\xe4C\xd3&\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xfb\xf2\x04\xc8\x13\xf86\xd89b\u01c7\fx\b\xca4\u007f\xd3>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfb\xf7Y3\xe0\x1bu\xb1T\xef\x06i\ak\xe8\u007fb\xdf\xfa\xe1\x8a\x10\x84cr\xf2I\xd4\xc0\x00\x00\u07d4\xfc\x00\x96\xb2\x1e\x95\xac\xb8\xd6\x19\xd1v\xa4\xa1\xd8\xd5)\xba\xdb\xef\x89\x14\xd9i;\xcb\xec\x02\x80\x00\xe0\x94\xfc\x00\xa4 \xa3a\a\xdf\xd5\xf4\x95\x12\x8a_\u5af2\xdb\x0f4\x8a\x01C\x17\x9d\x86\x91\x10 \x00\x00\xe0\x94\xfc\x01\x8ai\n\xd6tm\xbe:\u03d7\x12\xdd\xcaR\xb6%\x009\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfc\x02s@3\xe5\u007fpQ~\n\xfc~\xe6$a\xf0o\xad\x8e\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfc\x0e\xe6\xf7\u00b3qJ\xe9\x91lEVf\x05\xb6V\xf3$A\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xfc\x10\xb7\xa6{2h\xd53\x1b\xfbj\x14\xde\xf5\xeaJ\x16,\xa3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfc\x15\u02d9\xa8\xd1\x03\v\x12w\n\xdd\x03:y\xee\r\f\x90\x8c\x89\x12\xfa\x00\xbdR\xe6$\x00\x00\u07d4\xfc)R\xb4\u011f\xed\u043c\x05(\xa3\bI^mj\x1cq\u0589lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc,\x1f\x88\x96\x1d\x01\x9c>\x9e\xa30\t\x15.\x06\x93\xfb\xf8\x8a\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xfc6\x11\x05\u0750\xf9\xed\xe5fI\x9di\xe9\x13\x03\x95\xf1*\u020aS\xa4\xfe/ N\x80\xe0\x00\x00\u07d4\xfc7/\xf6\x92|\xb3\x96\xd9\xcf)\x805\x00\x11\r\xa62\xbcR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc9\xbeA\tK\x19\x97\xd2\x16\x9e\x82d\xc2\u00fa\xa6\u025b\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc=\"k\xb3jX\xf5&V\x88W\xb0\xbb\x12\xd1\t\xec\x93\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfcC\x82\x9a\u01c7\xff\x88\xaa\xf1\x83\xba5*\xad\xbfZ\x15\xb1\x93\x89\u05ac\n+\x05R\xe0\x00\x00\u07d4\xfcI\xc1C\x9aA\u05b3\xcf&\xbbg\xe06R$\xe5\xe3\x8f_\x8966\u05ef^\u024e\x00\x00\u07d4\xfcU\x00\x82Q\x05\xcfq*1\x8a^\x9c;\xfci\u021d\f\x12\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xfcf\xfa\xba'\u007fK]\xe6J\xd4^\xb1\x9c1\xe0\f\xed>\u054a\x011\xbe\xb9%\xff\xd3 \x00\x00\xe0\x94\xfc~\"\xa5\x03\xecZ\xbe\x9b\b\xc5\v\xd1I\x99\xf5 \xfaH\x84\x8a\x01ZG}\xfb\xe1\xea\x14\x80\x00\u07d4\xfc\x82\x15\xa0\xa6\x99\x13\xf6*C\xbf\x1c\x85\x90\xb9\xdd\xcd\r\x8d\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc\x98\x9c\xb4\x87\xbf\x1a}\x17\xe4\xc1\xb7\u0137\xaa\xfd\xdak\n\x8d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfc\x9b4td\xb2\xf9\x92\x9d\x80~\x03\x9d\xaeH\xd3\u064d\xe3y\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xfc\xa4;\xbc#\xa0\xd3!\xba\x9eF\xb9)s\\\xe7\xd8\xef\f\x18\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfc\xa7>\xff\x87q\xc0\x10;\xa3\xcc\x1a\x9c%\x94H\xc7*\xbf\v\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xad\xa3\x00(?k\xcc\x13J\x91Eg`\xb0\xd7}\xe4\x10\xe0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc\xbc\\q\xac\xe7\x97AE\v\x01,\xf6\xb8\xd3\xf1}\xb6\x8ap\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfc\xbd\x85\xfe\xeajuO\xcf4ID\x9e7\xff\x97\x84\xf7w<\x89\xa7J\xdai\xab\xd7x\x00\x00\xe0\x94\xfc\xc9\u0524&.z\x02z\xb7Q\x91\x10\xd8\x02\u0115\xce\xea9\x8a\x01YQ\x82\"K&H\x00\x00\xe0\x94\xfc\xcd\r\x1e\xce\xe2z\xdd\xea\x95\xf6\x85z\xee\xc8\u01e0K(\xee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfc\u0434\x82|\xd2\b\xff\xbf^u\x9d\xba\x8c<\xc6\x1d\x8c,<\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfc\xe0\x89c\\\xe9z\xba\xc0kD\x81\x9b\xe5\xbb\n>.\v7\x89\x05\x03\x92\nv0\xa7\x80\x00\u07d4\xfc\xf1\x99\xf8\xb8T\"/\x18.N\x1d\t\x9dN2>*\xae\x01\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xfc:P\x04\xd6xa?\v6\xa6B&\x9a\u007f7\x1c?j\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x19\x1a5\x15}x\x13s\xfbA\x1b\xf9\xf2R\x90\x04|^\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x1f\xaa4{\x0f\u0300L-\xa8l6\xd5\xf1\u044bp\x87\xbb\x89\x02\xd6\xeb$z\x96\xf6\x00\x00\u07d4\xfd\x1f\xb5\xa8\x9a\x89\xa7!\xb8yph\xfb\xc4\u007f>\x9dR\xe1I\x89\f\u0435\x83\u007f\xc6X\x00\x00\u07d4\xfd OOJ\xba%%\xbar\x8a\xfd\xf7\x87\x92\xcb\u07b75\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd'W\xcc5Q\xa0\x95\x87\x8d\x97\x87V\x15\xfe\fj2\xaa\x8a\x89 m\xb1R\x99\xbe\xac\x00\x00\u07d4\xfd(r\u045eW\x85<\xfa\x16\xef\xfe\x93\u0431\xd4{O\x93\xfb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd))'\x1e\x9d \x95\xa2dv~{\r\xf5.\xa0\xd1\xd4\x00\x89\xa2\xa1\xeb%\x1bZ\xe4\x00\x00\u07d4\xfd7z8Rr\x90\f\xb46\xa3\xbbyb\xcd\xff\xe9?]\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd@$+\xb3Jp\x85^\xf0\xfd\x90\xf3\x80-\xec!6\xb3'\x89h\xa8u\a>)$\x00\x00\xe0\x94\xfdE,9i\xec\xe3\x80\x1cT \xf1\xcd\u02a1\xc7\x1e\xd2=\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xfdKU\x1fo\xdb\u0366\xc5\x11\xb5\xbb7\"P\xa6\xb7\x83\xe54\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xfdK\x98\x95X\xae\x11\xbe\f;6\xe2\xd6\xf2\xa5J\x93C\xca.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfdM\xe8\xe3t\x8a(\x9c\xf7\xd0`Q}\x9d88\x8d\xb0\x1f\xb8\x89\r\x8drkqw\xa8\x00\x00\u07d4\xfdZc\x15\u007f\x91O\u04d8\uac5c\x13}\xd9U\v\xb7q\\\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xfd`\u04b5\xaf=5\xf7\xaa\xf0\u00d3\x05.y\xc4\xd8#\u0645\x89\x03\x0e\xb5\r.\x14\b\x00\x00\u07d4\xfdhm\xe5?\xa9\u007f\x99c\x9e%hT\x97 \xbcX\x8c\x9e\xfc\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xfd~\u078fR@\xa0eA\xebi\x9dx,/\x9a\xfb!p\xf6\x89Hz\x9a0E9D\x00\x00\u07d4\xfd\x81+\u019f\xb1p\xefW\xe22~\x80\xaf\xfd\x14\xf8\xe4\xb6\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\x88\xd1\x14\"\x0f\b\x1c\xb3\xd5\xe1[\xe8\x15*\xb0sfWj\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xfd\x91\x856\xa8\xef\xa6\xf6\xce\xfe\x1f\xa1\x159\x95\xfe\xf5\xe3=;\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfd\x92\x0fr&\x82\xaf\xb5\xafE\x1b\x05D\xd4\xf4\x1b;\x9dWB\x89~R\x05j\x12?<\x00\x00\u07d4\xfd\x95y\xf1\x19\xbb\xc8\x19\xa0+a\u3348\x03\xc9B\xf2M2\x89\x05\xb9~\x90\x81\xd9@\x00\x00\u07d4\xfd\xa0\xce\x153\a\a\xf1\v\xce2\x01\x17- \x18\xb9\xdd\xeat\x89\x02\xd0A\xd7\x05\xa2\xc6\x00\x00\xe0\x94\xfd\xa3\x04(\x19\xaf>f)\x00\xe1\xb9+CX\xed\xa6\xe9%\x90\x8a\x19\a\xa2\x84\u054fc\xe0\x00\x00\u07d4\xfd\xa6\x81\x0e\xa5\xac\x98]o\xfb\xf1\xc5\x11\xf1\xc1B\xed\xcf\xdd\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd\xb39D\xf26\x06\x15\xe5\xbe#\x95w\u0221\x9b\xa5-\x98\x87\x89 \x9d\x92/RY\xc5\x00\x00\u07d4\xfd\xbaSY\xf7\xec;\xc7p\xacI\x97]\x84N\xc9qbV\xf1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xfd\xc4\xd4vZ\x94/[\xf9i1\xa9\xe8\xccz\xb8\xb7W\xffL\x8a\x12lG\x8a\x0e>\xa8`\x00\x00\xe0\x94\xfd\xcd]\x80\xb1\x05\x89zW\xab\xc4xev\x8b)\x00RB\x95\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u0794\xfd\xd1\x19_y}O5q}\x15\xe6\xf9\x81\n\x9a?\xf5T`\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xfd\xd5\x02\xa7N\x81;\u03e3U\xce\xda<\x17ojhq\xaf\u007f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfd\u357c\vm\\\xbbL\x1d\x8f\xea>\vK\xffc^\x9d\xb7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xea\xac*\xcf\x1d\x13\x8e\x19\xf2\xfc?\x9f\xb7E\x92\xe3\ud04a\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xfd\xec\xc8-\xdf\xc5a\x92\xe2oV<=h\xcbTJ\x96\xbf\xed\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4\xfd\xf4#C\x01\x9b\v\fk\xf2`\xb1s\xaf\xab~E\xb9\xd6!\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xfd\xf4I\xf1\b\xc6\xfbOZ+\b\x1e\xed~E\u645eM%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xfda4\xc0J\x8a\xb7\xeb\x16\xf0\x06C\xf8\xfe\xd7\u06aa\ucc89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfe\x00\xbfC\x99\x11\xa5S\x98-\xb68\x03\x92E\xbc\xf02\xdb\u0709\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfe\x01n\xc1~\xc5\xf1\x0e;\xb9\x8f\xf4\xa1\xed\xa0E\x15v\x82\xab\x89\x14_T\x02\xe7\xb2\xe6\x00\x00\u07d4\xfe\x0e0\xe2\x14)\rt=\xd3\x0e\xb0\x82\xf1\xf0\xa5\"Z\xdea\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe!\v\x8f\x04\xdcmOv!j\xcf\xcb\u055b\xa8;\xe9\xb60\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\"\xa0\xb3\x88f\x8d\x1a\xe2d>w\x1d\xac\xf3\x8aCB#\u0309\xd8\xdb^\xbd{&8\x00\x00\u07d4\xfe6&\x88\x84_\xa2D\u0300~K\x110\xeb7A\xa8\x05\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe8'\xd5v0\u03c7a\xd5\x12y{\v\x85\x8eG\x8b\xbd\x12\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfeA\x8bB\x1a\x9cm76\x02y\x04u\xd20>\x11\xa7Y0\x897\b\xba\xed=h\x90\x00\x00\u07d4\xfeBI\x12yP\xe2\xf8\x96\xec\x0e~.=\x05Z\xab\x10U\x0f\x89$=M\x18\"\x9c\xa2\x00\x00\xe0\x94\xfeM\x84\x03!o\xd5qW+\xf1\xbd\xb0\x1d\x00W\x89x\u0588\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xfeS\xb9I\x89\u0619d\xda aS\x95&\xbb\xe9y\xdd.\xa9\x89h\xa8u\a>)$\x00\x00\u07d4\xfeT\x9b\xbf\xe6G@\x18\x98\x92\x93%8\u06afF\u04b6\x1dO\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfea]\x97\\\b\x87\xe0\xc9\x11>\xc7)\x84 \xa7\x93\xaf\x8b\x96\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfee\xc4\x18\x8dy\"Wi\td D\xfd\xc5#\x95V\x01e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfei\u007f\xf2,\xa5G\xbf\xc9^3\xd9`\xda`\\gc\xf3[\x89G\xd4\x11\x9f\xd9`\x94\x00\x00\u07d4\xfej\x89[y\\\xb4\xbf\x85\x90=<\xe0\x9cZ\xa49S\u04ff\x89\xb8Pz\x82\a( \x00\x00\u07d4\xfeo_B\xb6\x19;\x1a\xd1b\x06\u4bf5#\x9dM}\xb4^\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xfep\x11\xb6\x98\xbf3q\x13-tE\xb1\x9e\xb5\xb0\x945j\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x80\xe9#-\xea\xff\x19\xba\xf9\x98i\x88:K\xdf\x00\x04\xe5<\x89.b\xf2\ni\xbe@\x00\x00\u07d4\xfe\x8en6eW\r\xffz\x1b\xdaiz\xa5\x89\xc0\xb4\xe9\x02J\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x8f\x1f\u072b\u007f\xbe\u0266\xa3\xfc\xc5\aa\x96\x00P\\6\xa3\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xfe\x91\xec\xcf+\xd5f\xaf\xa1\x16\x96\xc5\x04\x9f\xa8Lic\nR\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xfe\x96\xc4\xcd8\x15b@\x1a\xa3*\x86\xe6[\x9dR\xfa\x8a\xee'\x89\x8f\x1d\\\x1c\xae7@\x00\x00\u07d4\xfe\x98\xc6d\xc3\xe4G\xa9^i\xbdX!q\xb7\x17n\xa2\xa6\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfe\x9a\xd1.\xf0]m\x90&\x1f\x96\xc84\n\x03\x81\x97M\xf4w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x9c\x0f\xff\xef\xb8\x03\b\x12V\xc0\xcfMfY\xe6\xd3>\xb4\xfb\x89R\xd5B\x80O\x1c\xe0\x00\x00\u07d4\xfe\x9c\xfc;\xb2\x93\u0772\x85\xe6%\xf3X/t\xa6\xb0\xa5\xa6\u0349j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xfe\x9e\x11\x97\u05d7JvH\xdc\u01e01\x12\xa8\x8e\xdb\xc9\x04]\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\xe0\x94\xfe\xac\xa2\xactbK\xf3H\xda\u0258QC\xcf\xd6R\xa4\xbeU\x8a\x05\x89\u007f\u02f0)\x14\b\x80\x00\u07d4\xfe\xad\x18\x03\xe5\xe77\xa6\x8e\x18G-\x9a\xc7\x15\xf0\x99L\u00be\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfe\xb8\xb8\xe2\xafqj\xe4\x1f\xc7\xc0K\xcf)T\x01VF\x1ek\x89TQt\xa5(\xa7z\x00\x00\u07d4\xfe\xb9-0\xbf\x01\xff\x9a\x19\x01flUsS+\xfa\a\xee\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe\xbc1s\xbc\x90r\x13cT\x00+{O\xb3\xbf\xc5?\"\xf1\x89\x14\x0e\xc8\x0f\xa7\xee\x88\x00\x00\u07d4\xfe\xbdH\xd0\xff\xdb\xd5el\xd5\xe6\x866:a\x14R(\xf2y\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xfe\xbd\x9f\x81\xcfx\xbd_\xb6\u0139\xa2K\xd4\x14\xbb\x9b\xfaLN\x89k\xe1\x0f\xb8\xedn\x13\x80\x00\u07d4\xfe\xc0o\xe2{D\u01c4\xb29n\xc9/{\x92:\xd1~\x90w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\xc1NT\x85\xde+>\xef^t\xc4aF\u06ceEN\x035\x89\t\xb4\x1f\xbf\x9e\n\xec\x00\x00\u07d4\xfe\xd8Gm\x10\u0544\xb3\x8b\xfag7`\x0e\xf1\x9d5\xc4\x1e\u0609b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xfe\xef;n\xab\xc9J\xff\xd31\f\x1cM\x0ee7^\x13\x11\x19\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\xf0\x9dp$?9\xed\x8c\xd8\x00\xbf\x96QG\x9e\x8fJ\xca<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe\xf3\xb3\u07ad\x1ai&\u051a\xa3+\x12\xc2*\xf5M\x9f\xf9\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xff\v|\xb7\x1d\xa9\xd4\xc1\xean\xcc(\xeb\xdaPLc\xf8/\u04498\x8a\x88]\xf2\xfcl\x00\x00\u07d4\xff\f<w\x98\xe8s=\xd2f\x81R\x89\x1b\xab\x80\xa8\xbe\x95\\\x89\x04YF\xb0\xf9\xe9\xd6\x00\x00\u07d4\xff\f\xb0lB\xe3\u0609H\xe4[\u05f0\xd4\u246e\xfe\xeaQ\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xff\f\xc8\xda\xc8$\xfa$\xfc<\xaa!i\xe6\xe0W\xcfc\x8a\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xff\x0e/\xec0B\aF~\x1e3\a\xf6L\xbf0\xaf\x8f\xd9\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\xff\x12\x8fK5[\xe1\xdcJo\x94\xfaQ\r\u007f\x15\xd5<*\xff\x89\x93s\x954\u0486\x80\x00\x00\u07d4\xff\x12\u474e\x06\xaa \xf8\x86)<\v\x98\xed~\xffx\x88\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xff s\b\xce\xd28\xa6\xc0\x1a\xd0!<\xa9\xebDe\xd4%\x90\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xff&\x13\x830'M\xf4\xe0\xa3\b\x1em\xf7\u0758>\xc6\u73c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xff'&)AH\xb8lx\xa97$\x97\xe4Y\x89\x8e\xd3\xfe\xe3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xff=\xedz@\u04ef\xf0\u05e8\xc4_\xa6\x13j\xa0C=\xb4W\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xff>\xeeW\xc3Mm\xae\x97\r\x8b1\x11\x17\xc55\x86\xcd5\x02\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xff>\xf6\xba\x15\x1c!\xb5\x99\x86\xaed\xf6\xe8\"\x8b\u0262\xc73\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffA\xd9\xe1\xb4\xef\xfe\x18\u0630\xd1\xf6?\xc4%_\xb4\xe0l=\x89Hz\x9a0E9D\x00\x00\u07d4\xffE\xcb4\xc9(6M\x9c\xc9\u063b\x0074ta\x8f\x06\xf3\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xffI\xa7u\x81N\xc0\x00Q\xa7\x95\xa8u\xde$Y.\xa4\x00\u050a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xffJ@\x8fP\xe9\xe7!F\xa2\x8c\xe4\xfc\x8d\x90'\x1f\x11n\x84\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xffM\x9c\x84\x84\xc4<B\xff,Z\xb7Y\x99d\x98\xd3#\x99M\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xffO\xc6`i\x04lRVX\xc37\xa9\x17\xf2\u05382\xb4\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffQb\xf25M\u0112\xc7_\xd6\xe3\xa1\a&\x86`\xee\xcbG\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xffT[\xbbf\xfb\xd0\x0e\xb5\xe67?\xf4\xe3&\xf5\xfe\xb5\xfe\x12\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xff^~\xe7\xd5\x11H!\xe1Y\u0725\xe8\x1f\x18\xf1\xbf\xff\xbf\xf9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffa\xc9\xc1\xb7\xa3\u0635;\xba \xb3DfTK{!fD\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffeQ\x1c\xad\xa2Y&\f\x1d\xdcA\x97N\xca\xec\xd3-cW\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94\xffxC\xc7\x01\n\xa7\xe6\x15\x19\xb7b\xdf\xe4\x91$\xa7k\x0eN\x8a\u0171y$A+\x9b\xb0\x00\x00\xe0\x94\xffxT\x17V\xab+pn\rp\xb1\x8a\xdbp\x0f\xc4\xf1d=\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4\xff\x83\x85PQ\xee\x8f\xfbp\xb4\x81}\xba2\x11\xed#U\x86\x9d\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xff\x85\x0e;\xe1\xebjMrl\b\xfas\xaa\xd3X\xf3\x97\x06\u0689i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xff\x86\xe5\xe8\xe1[S\x90\x96\x00\xe4\x13\b\u06b7_\x0e$\xe4k\x890\xebP\xd2\xe1@\x80\x00\x00\u07d4\xff\x88\xeb\xac\xc4\x1b6\x87\xf3\x9eKY\xe1YY\x9b\x80\u02e3?\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xff\x8a,\xa5\xa8\x133\xf1\x99\x98%_ 2V\xe1\xa8\x19\xc0\xaa\x89\f$\x9f\xdd2w\x80\x00\x00\u07d4\xff\x8e\xb0}\xe3\u051d\x9dR\xbb\xe8\xe5\xb2m\xbe\x1d\x16\x0f\xa84\x89\xd8\x14\u0739DS\b\x00\x00\xe0\x94\xff\xa4\xaf\xf1\xa3\u007f\x98K\ng'!I':\xe9\xbdA\u3f0a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xff\xa6\x96\xec\xbdx~f\xab\xaeO\xe8{c_\a\xcaW\xd8H\x89Hz\x9a0E9D\x00\x00\u07d4\xff\xac=\xb8y\xa6\xc7\x15\x8e\x8d\xec`;@tc\xba\r1\u03c9j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xff\xad=\xd7N,\x1fyj\xc6@\xdeV\u0719\xb4\u01d2\xa4\x02\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xff\xb0G&\u07e4\x1a\xfd\xc8\x19\x16\x84\x18a\x04r\x97\r{\xfc\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xff\xb3\xbc\xc3\x19j\x8c<\xb84\xce\xc9L4\xfe\xd3[>\x10T\x89H\xa4<T`/p\x00\x00\u07d4\xff\xb9tg3g\xf5\xc0{\xe5\xfd'\r\u0137\x13\x8b\aMW\x89\x85\xeb\u023d\xb9\x06m\x80\x00\u07d4\xff\xb9\xc7!~ft01\xeb7z\xf6\\w\xdbsY\xdc\u0689\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xff\xbc=\xa08\x1e\xc39\xc1\xc0I\xeb\x1e\xd9\xee4\xfd\u03a6\u0289\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xff\xc5\xfcK~\x8a\x02\x93\xff9\xa3\xa0\xf7\xd6\r&F\xd3zt\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xff\xc9\xcc0\x94\xb0A\xad\x0e\ao\x96\x8a\r\xe3\xb1g%Xf\x89\x17p\xc1e\v\xee\xe8\x00\x00\u07d4\xff\xd5\x17\x0f\u0468\x11\x8dU\x8eu\x11\xe3d\xb2I\x06\xc4\xf6\xb3\x89\x03A\xd8\xcd'\xf1X\x80\x00\xe0\x94\xff\xd6\u0695\x8e\xec\xbc\x01k\xab\x91\x05\x84@\u04dbA\u01fe\x83\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xff\xe0\xe9\x97\xf1\x97za_Z1Z\xf4\x13\xfdHi4;\xa0\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\xff\u2375<\x90D\xb4\xec\xd4\x05?\u0474\xb1\rpV\u0188\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xff\xe2\xe2\x8c?\xb7GI\xd7\xe7\x80\u070a]B%8\xe6\xe4Q\x89\r\xbb\x81\xe0[\xc1-\x80\x00\u07d4\xff\xe8\xcb\xc1h\x1e^\x9d\xb7J\x0f\x93\xf8\xed%\x89u\x19\x12\x0f\x89Q\xb1\u04c3\x92a\xac\x00\x00\u07d4\xff\xea\xc00^\xde:\x91R\x95\xec\x8ea\xc7\xf8\x81\x00oDt\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xff\xec\t\x13\xc65\xba\xca/^W\xa3z\xa9\xfb{l\x9bn&\x89+\xa3\x9e\x82\xed]t\x00\x00\xe0\x94\xff\xf3:;\xd3j\xbd\xbdA'\a\xb8\xe3\x10\xd6\x01\x14T\xa7\xae\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xff\xf4\xba\u0556c4y\xa2\xa2\x9f\x9a\x8b?x\xee\xfd\a\xe6\xee\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xff\xf7\xac\x99\xc8\xe4\xfe\xb6\f\x97P\x05K\xdc\x14\xce\x18W\xf1\x81\x8965\u026d\xc5\u07a0\x00\x00" -const ropstenAllocData = "\xf9\x03\xa4\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x80\xc2\v\x80\xc2\f\x80\xc2\r\x80\xc2\x0e\x80\xc2\x0f\x80\xc2\x10\x80\xc2\x11\x80\xc2\x12\x80\xc2\x13\x80\xc2\x14\x80\xc2\x15\x80\xc2\x16\x80\xc2\x17\x80\xc2\x18\x80\xc2\x19\x80\xc2\x1a\x80\xc2\x1b\x80\xc2\x1c\x80\xc2\x1d\x80\xc2\x1e\x80\xc2\x1f\x80\xc2 \x80\xc2!\x80\xc2\"\x80\xc2#\x80\xc2$\x80\xc2%\x80\xc2&\x80\xc2'\x80\xc2(\x80\xc2)\x80\xc2*\x80\xc2+\x80\xc2,\x80\xc2-\x80\xc2.\x80\xc2/\x80\xc20\x80\xc21\x80\xc22\x80\xc23\x80\xc24\x80\xc25\x80\xc26\x80\xc27\x80\xc28\x80\xc29\x80\xc2:\x80\xc2;\x80\xc2<\x80\xc2=\x80\xc2>\x80\xc2?\x80\xc2@\x80\xc2A\x80\xc2B\x80\xc2C\x80\xc2D\x80\xc2E\x80\xc2F\x80\xc2G\x80\xc2H\x80\xc2I\x80\xc2J\x80\xc2K\x80\xc2L\x80\xc2M\x80\xc2N\x80\xc2O\x80\xc2P\x80\xc2Q\x80\xc2R\x80\xc2S\x80\xc2T\x80\xc2U\x80\xc2V\x80\xc2W\x80\xc2X\x80\xc2Y\x80\xc2Z\x80\xc2[\x80\xc2\\\x80\xc2]\x80\xc2^\x80\xc2_\x80\xc2`\x80\xc2a\x80\xc2b\x80\xc2c\x80\xc2d\x80\xc2e\x80\xc2f\x80\xc2g\x80\xc2h\x80\xc2i\x80\xc2j\x80\xc2k\x80\xc2l\x80\xc2m\x80\xc2n\x80\xc2o\x80\xc2p\x80\xc2q\x80\xc2r\x80\xc2s\x80\xc2t\x80\xc2u\x80\xc2v\x80\xc2w\x80\xc2x\x80\xc2y\x80\xc2z\x80\xc2{\x80\xc2|\x80\xc2}\x80\xc2~\x80\xc2\u007f\x80\u00c1\x80\x80\u00c1\x81\x80\u00c1\x82\x80\u00c1\x83\x80\u00c1\x84\x80\u00c1\x85\x80\u00c1\x86\x80\u00c1\x87\x80\u00c1\x88\x80\u00c1\x89\x80\u00c1\x8a\x80\u00c1\x8b\x80\u00c1\x8c\x80\u00c1\x8d\x80\u00c1\x8e\x80\u00c1\x8f\x80\u00c1\x90\x80\u00c1\x91\x80\u00c1\x92\x80\u00c1\x93\x80\u00c1\x94\x80\u00c1\x95\x80\u00c1\x96\x80\u00c1\x97\x80\u00c1\x98\x80\u00c1\x99\x80\u00c1\x9a\x80\u00c1\x9b\x80\u00c1\x9c\x80\u00c1\x9d\x80\u00c1\x9e\x80\u00c1\x9f\x80\u00c1\xa0\x80\u00c1\xa1\x80\u00c1\xa2\x80\u00c1\xa3\x80\u00c1\xa4\x80\u00c1\xa5\x80\u00c1\xa6\x80\u00c1\xa7\x80\u00c1\xa8\x80\u00c1\xa9\x80\u00c1\xaa\x80\u00c1\xab\x80\u00c1\xac\x80\u00c1\xad\x80\u00c1\xae\x80\u00c1\xaf\x80\u00c1\xb0\x80\u00c1\xb1\x80\u00c1\xb2\x80\u00c1\xb3\x80\u00c1\xb4\x80\u00c1\xb5\x80\u00c1\xb6\x80\u00c1\xb7\x80\u00c1\xb8\x80\u00c1\xb9\x80\u00c1\xba\x80\u00c1\xbb\x80\u00c1\xbc\x80\u00c1\xbd\x80\u00c1\xbe\x80\u00c1\xbf\x80\u00c1\xc0\x80\u00c1\xc1\x80\u00c1\u0080\u00c1\u00c0\u00c1\u0100\u00c1\u0140\u00c1\u0180\u00c1\u01c0\u00c1\u0200\u00c1\u0240\u00c1\u0280\u00c1\u02c0\u00c1\u0300\u00c1\u0340\u00c1\u0380\u00c1\u03c0\u00c1\u0400\u00c1\u0440\u00c1\u0480\u00c1\u04c0\u00c1\u0500\u00c1\u0540\u00c1\u0580\u00c1\u05c0\u00c1\u0600\u00c1\u0640\u00c1\u0680\u00c1\u06c0\u00c1\u0700\u00c1\u0740\u00c1\u0780\u00c1\u07c0\u00c1\xe0\x80\u00c1\xe1\x80\u00c1\xe2\x80\u00c1\xe3\x80\u00c1\xe4\x80\u00c1\xe5\x80\u00c1\xe6\x80\u00c1\xe7\x80\u00c1\xe8\x80\u00c1\xe9\x80\u00c1\xea\x80\u00c1\xeb\x80\u00c1\xec\x80\u00c1\xed\x80\u00c1\xee\x80\u00c1\xef\x80\u00c1\xf0\x80\u00c1\xf1\x80\u00c1\xf2\x80\u00c1\xf3\x80\u00c1\xf4\x80\u00c1\xf5\x80\u00c1\xf6\x80\u00c1\xf7\x80\u00c1\xf8\x80\u00c1\xf9\x80\u00c1\xfa\x80\u00c1\xfb\x80\u00c1\xfc\x80\u00c1\xfd\x80\u00c1\xfe\x80\u00c1\xff\x80\u3507KT\xa8\xbd\x15)f\xd6?pk\xae\x1f\xfe\xb0A\x19!\xe5\x8d\f\x9f,\x9c\xd0Ft\xed\xea@\x00\x00\x00" -const rinkebyAllocData = "\xf9\x03\xb7\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x941\xb9\x8d\x14\x00{\xde\xe67)\x80\x86\x98\x8a\v\xbd1\x18E#\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" -const goerliAllocData = "\xf9\x04\x06\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xe0\x94L*\xe4\x82Y5\x05\xf0\x16<\xde\xfc\a>\x81\xc6<\xdaA\a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa8\xe8\xf1G2e\x8eKQ\xe8q\x191\x05:\x8ai\xba\xf2\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe1\x94\u0665\x17\x9f\t\x1d\x85\x05\x1d<\x98'\x85\xef\xd1E\\\uc199\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe1\x94\u08bdBX\xd2v\x887\xba\xa2j(\xfeq\xdc\a\x9f\x84\u01cbJG\xe3\xc1$H\xf4\xad\x00\x00\x00" -const calaverasAllocData = "\xf9\x06\x14\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x94\x0e\x89\xe2\xae\xdb\x1c\xfc\u06d4$\xd4\x1a\x1f!\x8fA2s\x81r\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x10A\xaf\xbc\xb3Y\u0568\xdcX\xc1[/\xf5\x13T\xff\x8a!}\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94#o\xf1\xe9t\x19\xae\x93\xad\x80\xca\xfb\xaa!\"\f]x\xfb}\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94`\xad\xc0\xf8\x9aA\xaf#|\xe75T\xed\xe1p\xd73\xec\x14\xe0\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94y\x9d2\x9e_X4\x19\x16|\xd7\"\x96$\x85\x92n3\x8fJ\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94|\xf5\xb7\x9b\xfe)\x1ag\xab\x02\xb3\x93\xe4V\xcc\xc4\xc2f\xf7S\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x8a\x8e\xaf\xb1\xcfb\xbf\xbe\xb1t\x17i\xda\xe1\xa9\xddG\x99a\x92\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x8b\xa1\xf1\tU\x1b\xd42\x800\x12dZ\xc16\xdd\xd6M\xbar\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\xb0*.\xda\x1b1\u007f\xbd\x16v\x01(\x83k\n\u015bV\x0e\x9d\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\xba\xdc\r\xe9\xe0yK\x04\x9b^\xa6<>\x1ei\x8a4v\xc1r\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\xf00\v\ue24a\xe2r\xeb4~\x83i\xac\fv\xdfB\xc9?\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\xfe;U~\x8f\xb6+\x89\xf4\x91kr\x1b\xe5\\\ub08d\xbds\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +const baklavaAllocJSON = `{ + "fCf982bb4015852e706100B14E21f947a5Bb718E": { + "balance": "200000000000000000000000000" + }, + "0xd71fea6b92d3f21f659152589223385a7329bb11": { + "balance": "1000000000000000000000" + }, + "0x1e477fc9b6a49a561343cd16b2c541930f5da7d2": { + "balance": "1000000000000000000000" + }, + "0x460b3f8d3c203363bb65b1a18d89d4ffb6b0c981": { + "balance": "1000000000000000000000" + }, + "0x3b522230c454ca9720665d66e6335a72327291e8": { + "balance": "1000000000000000000000" + }, + "0x0AFe167600a5542d10912f4A07DFc4EEe0769672": { + "balance": "1000000000000000000000" + }, + "0x412ebe7859e9aa71ff5ce4038596f6878c359c96": { + "balance": "1000000000000000000000" + }, + "0xbbfe73df8b346b3261b19ac91235888aba36d68c": { + "balance": "1000000000000000000000" + }, + "0x02b1d1bea682fcab4448c0820f5db409cce4f702": { + "balance": "1000000000000000000000" + }, + "0xe90f891710f625f18ecbf1e02efb4fd1ab236a10": { + "balance": "1000000000000000000000" + }, + "0x28c52c722df87ed11c5d7665e585e84aa93d7964": { + "balance": "1000000000000000000000" + }, + "0Cc59Ed03B3e763c02d54D695FFE353055f1502D": { + "balance": "103010030000000000000000000" + }, + "3F5084d3D4692cf19b0C98A9b22De614e49e1470": { + "balance": "10011000000000000000000" + }, + "EF0186B8eDA17BE7D1230eeB8389fA85e157E1fb": { + "balance": "10011000000000000000000" + }, + "edDdb60EF5E90Fb09707246DF193a55Df3564c9d": { + "balance": "10011000000000000000000" + }, + "d5e454462b3Fd98b85640977D7a5C783CA162228": { + "balance": "10011000000000000000000" + }, + "a4f1bad7996f346c3E90b90b60a1Ca8B67B51E4B": { + "balance": "10011000000000000000000" + }, + "5B991Cc1Da0b6D54F8befa9De701d8BC85C92324": { + "balance": "10011000000000000000000" + }, + "6dfdAa51D146eCff3B97614EF05629EA83F4997E": { + "balance": "10011000000000000000000" + }, + "D2b16050810600296c9580D947E9D919D0c332ed": { + "balance": "10011000000000000000000" + }, + "Fe144D67068737628efFb701207B3eB30eF93C69": { + "balance": "10011000000000000000000" + }, + "82E64996B355625efeAaD12120710706275b5b9A": { + "balance": "10011000000000000000000" + }, + "241752a3f65890F4AC3eAeC518fF94567954e7b5": { + "balance": "10011000000000000000000" + }, + "1bdDeaF571d5da96ce6a127fEb3CADaDB531f433": { + "balance": "10011000000000000000000" + }, + "F86345e9c9b39aB1cbE82d7aD35854f905B8B835": { + "balance": "10011000000000000000000" + }, + "5c3512b1697302c497B861CBfDA158f8a3c5122C": { + "balance": "10011000000000000000000" + }, + "a02A692d70Fd9A5269397C044aEBDf1085ba090f": { + "balance": "10011000000000000000000" + }, + "aC91f591F12a8B6531Be43E0ccF21cd5fA0E80b0": { + "balance": "10011000000000000000000" + }, + "718A8AC0943a6D3FFa3Ec670086bfB03817ed540": { + "balance": "10011000000000000000000" + }, + "b30980cE21679314E240DE5Cbf437C15ad459EB8": { + "balance": "10011000000000000000000" + }, + "99eCa23623E59C795EceB0edB666eca9eC272339": { + "balance": "10011000000000000000000" + }, + "c030e92d19229c3EfD708cf4B85876543ee1A3F7": { + "balance": "10011000000000000000000" + }, + "5c98A3414Cb6Ff5c24d145F952Cd19F5f1f56643": { + "balance": "10011000000000000000000" + }, + "1979b042Ae2272197f0b74170B3a6F44C3cC5c05": { + "balance": "10011000000000000000000" + }, + "Db871070334b961804A15f3606fBB4fAc7C7f932": { + "balance": "10011000000000000000000" + }, + "C656C97b765D61E0fbCb1197dC1F3a91CC80C2a4": { + "balance": "10011000000000000000000" + }, + "aD95a2f518c197dc9b12eE6381D88bba11F2E0E5": { + "balance": "10011000000000000000000" + }, + "4D4B5bF033E4A7359146C9ddb13B1C821FE1D0d3": { + "balance": "10011000000000000000000" + }, + "9C64dA169d71C57f85B3d7A17DB27C1ce94FBDE4": { + "balance": "10011000000000000000000" + }, + "B5f32e89ccaD3D396f50da32E0a599E43CE87dd7": { + "balance": "10011000000000000000000" + }, + "Ba40Db8ab5325494C9E7e07A4c4720990A39305c": { + "balance": "10011000000000000000000" + }, + "8B7852DA535df3D06D6ADc1906778afd9481588a": { + "balance": "10011000000000000000000" + }, + "a8F41EA062C22dAFFc61e47cF15fc898517b86B1": { + "balance": "10011000000000000000000" + }, + "66a3Fc7E8fd6932568cDB6610F5a67BeD9F5beF8": { + "balance": "10011000000000000000000" + }, + "10301d9389653497F62876f450332467E07eEe1F": { + "balance": "10011000000000000000000" + }, + "6c3ac5fcb13E8DCd908C405Ec6DAcF0EF575D8FC": { + "balance": "10011000000000000000000" + }, + "85226637919D3d47E1A37b3AF989E9aE1a1C4790": { + "balance": "10011000000000000000000" + }, + "43BCa16603c56cb681d1da3636B7a1A225598bfc": { + "balance": "10011000000000000000000" + }, + "E55d8Bc08025BDDF8Da02eEB54882d0586f90700": { + "balance": "10011000000000000000000" + }, + "40E1C73f6228a2c15e10aF2F3e890098b777ED15": { + "balance": "10011000000000000000000" + }, + "DbbF476089a186a406EA13a4c46813f4BccC3660": { + "balance": "10011000000000000000000" + }, + "7baCEA66a75dD974Ad549987768bF8d8908b4917": { + "balance": "10011000000000000000000" + }, + "fbF4C2362a9EB672BAC39A46AFd919B3c12Ce44c": { + "balance": "10011000000000000000000" + }, + "A8dB96136990be5B3d3bfe592e5A5a5223350A7A": { + "balance": "10011000000000000000000" + }, + "1Dd21ED691195EBA816d59B3De7Fab8b3470Ae4B": { + "balance": "10011000000000000000000" + }, + "058A778A6aeEfacc013afba92578A43e38cc012D": { + "balance": "10011000000000000000000" + }, + "13f52Ab66871880DC8F2179d705281a4cf6a15fB": { + "balance": "10011000000000000000000" + }, + "eD1Ed9a71E313d1BCe14aB998E0646F212230a33": { + "balance": "10011000000000000000000" + }, + "c563F264f98e34A409C6a085da7510De8B6FE90B": { + "balance": "10011000000000000000000" + }, + "c6D678fC6Cc1dA9D5eD1c0075cF7c679e7138e02": { + "balance": "10011000000000000000000" + }, + "5179fc80CaB9BB20d5405a50ec0Fb9a36c1B367a": { + "balance": "10011000000000000000000" + }, + "0d473f73AAf1C2bf7EBd2be7196C71dBa6C1724b": { + "balance": "100110000000000000000" + }, + "6958c5b7E3D94B041d0d76Cac2e09378d31201bd": { + "balance": "10011000000000000000000" + }, + "628d4A734d1a2647c67D254209e7B6471a11a5cb": { + "balance": "10011000000000000000000" + }, + "E1601e3172F0ef0100e363B639Bd44420B7E5490": { + "balance": "10011000000000000000000" + }, + "3337F2Cd103976F044b55D3E69aB06d1ebB142Db": { + "balance": "10011000000000000000000" + }, + "8D0D5c57dC232Be15Df4A1a048EF36162C853b94": { + "balance": "10011000000000000000000" + }, + "14800c28F3cF1Dd17AaC55263ef4e173b0e8e3Ef": { + "balance": "10011000000000000000000" + }, + "f3996A0f0f593BfD5E39780059C5430fab7359FD": { + "balance": "10011000000000000000000" + }, + "2217FeBe31Aea6C771AF163dCc453F9f060a4a00": { + "balance": "10011000000000000000000" + }, + "f426CC817400766cd6b44F13Cb63Ca648e323484": { + "balance": "10011000000000000000000" + }, + "B2C4913e257a34445Ec31685E625bb4060FB8e1f": { + "balance": "10011000000000000000000" + }, + "9438dbD05dfC19F049a469185c7599daa82646e8": { + "balance": "10011000000000000000000" + }, + "4BeD66Bf507f3CF524704267908Ea4ee3cDe3053": { + "balance": "10011000000000000000000" + }, + "9a850fe8105e9CCfBD9d1D06D535BB4948f3f6Cf": { + "balance": "10011000000000000000000" + }, + "1277eE554565542A8d0553E1e54006d006db75bd": { + "balance": "10011000000000000000000" + }, + "D7e829bE8E374D3fBbd2F68D9A916cB2f769BA89": { + "balance": "10011000000000000000000" + }, + "3691b847eD14E296afC90Ff3E37D21e518306170": { + "balance": "10011000000000000000000" + }, + "c4C703357B01672cF95bFa0450a5717812Bc7ffb": { + "balance": "10011000000000000000000" + }, + "0c9369077836353A8D92aeD29C72A7DfD300B354": { + "balance": "10011000000000000000000" + }, + "856DF2A3bdBb8086cE406C469dDE94d12C1E3176": { + "balance": "10011000000000000000000" + }, + "E40B3e5c59e2157037b699895329DBe4aA33C039": { + "balance": "10011000000000000000000" + }, + "edb47aF3aC2325735722450D1E7DA082bDDad58c": { + "balance": "10011000000000000000000" + }, + "315D669866E13fA302B76c85481F9181e06304Ce": { + "balance": "10011000000000000000000" + }, + "A5185E3328592428d5989422e0339247dD77e10D": { + "balance": "10011000000000000000000" + }, + "85Fd1d1Cd6655EbB89db7D6cA0a5C9c62F7a4CFf": { + "balance": "10011000000000000000000" + }, + "ACC9E4430EC1011673547395A191C6b152763EA4": { + "balance": "10011000000000000000000" + }, + "3824967C172D52128522dD257FE8f58C9099166B": { + "balance": "10011000000000000000000" + }, + "5542aDEA3092da5541250d70a3Db28Ad9BE7Cfc7": { + "balance": "10011000000000000000000" + }, + "c61Cd4477f0A98BfC97744481181730f7af7c14f": { + "balance": "10011000000000000000000" + }, + "5D7Ffd0fC6DAA67AbF7d48ae69f09dbe53d86983": { + "balance": "10011000000000000000000" + }, + "350914ABD4F095534823C1e8fA1cfD7EF79e7E4c": { + "balance": "10011000000000000000000" + }, + "ECa6f058B718E320c1D45f5D1fb07947367C3D4B": { + "balance": "10011000000000000000000" + }, + "9C577D0795Ed0cA88814d149c2DC61E8Fc48Ad81": { + "balance": "10011000000000000000000" + }, + "72fE8bC8E3Ff1e56543c9c1F9834D6dfC31BEDDC": { + "balance": "10011000000000000000000" + }, + "6Ff2CFa7899073CD029267fd821C9497811b5f7E": { + "balance": "10011000000000000000000" + }, + "4685D123aE928a7912646681ba32035ad6F010a6": { + "balance": "10011000000000000000000" + }, + "4799946c8B21fF5E58A225AeCB6F54ec17a94566": { + "balance": "10011000000000000000000" + }, + "1D7dA5a23a99Fc33e2e94d502E4Fdb564eA0B24C": { + "balance": "10011000000000000000000" + }, + "DFc9719cD9c7982e4A1FFB4B87cC3b861C40E367": { + "balance": "10011000000000000000000" + }, + "0c1F0457ce3e87f5eA8F2C3A007dfe963A6Ff9a7": { + "balance": "10011000000000000000000" + }, + "7dC23b30dFDc326B9a694c6f9723DC889fe16b7d": { + "balance": "10011000000000000000000" + }, + "3F0c4cFDD40D16B7C15878AcCdc91Be9ca4DeE79": { + "balance": "10011000000000000000000" + }, + "B984a83416F560437C7866e26CdDb94bDB821594": { + "balance": "10011000000000000000000" + }, + "138EA4C57F5b3984EFacd944b3b85dfDd5A78Dcc": { + "balance": "10011000000000000000000" + }, + "AD4f16F3435E849505C643714C9E5f40f73c4a5a": { + "balance": "10011000000000000000000" + }, + "6b38E861ec0b65fd288d96d5630711C576362152": { + "balance": "10011000000000000000000" + }, + "AE15D05100CE807d0aC93119f4ada8fa21441Fd2": { + "balance": "10011000000000000000000" + }, + "e0e25c5734bef8b2Add633eAa2518B207DAa0D66": { + "balance": "10011000000000000000000" + }, + "9039Ce107A9cD36Ed116958E50f8BDe090e2406f": { + "balance": "10011000000000000000000" + }, + "089bE2dD42096ebA1d94aad20228b75df2BeeBC7": { + "balance": "10011000000000000000000" + }, + "E3a79AEee437532313015892B52b65f52794F8a2": { + "balance": "10011000000000000000000" + }, + "Cc38EE244819649C9DaB02e268306cED09B20672": { + "balance": "10011000000000000000000" + }, + "eb0357140a1a0A6c1cB9c93Bf9354ef7365C97d9": { + "balance": "10011000000000000000000" + }, + "44370D6b2d010C9eBFa280b6C00010AC99a45660": { + "balance": "10011000000000000000000" + }, + "762438915209d038340C3Af9f8aAb8F93aDc8A9A": { + "balance": "10011000000000000000000" + }, + "9CBa7aD50fa366Ff6fC2CAe468929eC9AD23Ea2B": { + "balance": "10011000000000000000000" + }, + "4f4F159826b2B1eE903A811fCd86E450c9954396": { + "balance": "10011000000000000000000" + }, + "3C132B8465e2D172BB7bab6654D85E398ee7c8AD": { + "balance": "10011000000000000000000" + }, + "0582426C929B7e525c22201Bd4c143E45189C589": { + "balance": "10011000000000000000000" + }, + "fb542740B34dDC0ADE383F2907a1e1E175E0BF5a": { + "balance": "10011000000000000000000" + }, + "184Ca91AfE8F36bC5772b29cE2A76c90fCef34D0": { + "balance": "10011000000000000000000" + }, + "0C6f48B50B166ddcE52CEE051acCAfFB8ecB4976": { + "balance": "10011000000000000000000" + }, + "3aD2bE38fA3DFa7969E79B4053868FD1C368eAb2": { + "balance": "10011000000000000000000" + }, + "a6A690637b088E9A1A89c44c9dC5e14eD4825053": { + "balance": "10011000000000000000000" + }, + "C224B131Ea71e11E7DF38de3774AAAAe7E197BA4": { + "balance": "10011000000000000000000" + }, + "d3C18531f0879B9FB8Ed45830C4ce6b54dC57128": { + "balance": "10011000000000000000000" + }, + "02a272d17E1308beF21E783A93D1658f84F2D414": { + "balance": "10011000000000000000000" + }, + "57A1aC8167d94b899b32C38Ff9D2B2bD0e55C10d": { + "balance": "10011000000000000000000" + }, + "F8fc7D740929E5DD4eBA8fd5a6873Be6a4151087": { + "balance": "10011000000000000000000" + }, + "B2AfC45838b364240dE17D3143AA6096d3340A91": { + "balance": "10011000000000000000000" + }, + "eAf133d1e0Dd325721665B19f67C9b914EE2469F": { + "balance": "10011000000000000000000" + }, + "B7660F1B075e56780e7E026ff66995765f5f1f7F": { + "balance": "10011000000000000000000" + }, + "F25087E27B7a59003bb08d2cAc7A69E7c15a4be8": { + "balance": "10011000000000000000000" + }, + "E65054681206658A845140331459A057C4EB3CA7": { + "balance": "10011000000000000000000" + }, + "e7569A0F93E832a6633d133d23503B5175bEa5Db": { + "balance": "10011000000000000000000" + }, + "a9f6102BCf5351dFdC7fA0CA4Fa0A711e16605c3": { + "balance": "10011000000000000000000" + }, + "1AB9aA0E855DF953CF8d9cC166172799afD12a68": { + "balance": "10011000000000000000000" + }, + "6C04aA35c377E65658EC3600Cab5E8FFa95567D9": { + "balance": "10011000000000000000000" + }, + "6b82AD37e64c91c628305813B2DA82F18f8e2a2B": { + "balance": "10011000000000000000000" + }, + "AD5D1DeD72F0e70a0a5500B26b82B1A2e8A63471": { + "balance": "10011000000000000000000" + }, + "72B3589771Ec8e189a5d9Fe7a214e44085e89054": { + "balance": "10011000000000000000000" + }, + "74F57dA8be3E9AB4463DD70319A06Fb5E3168211": { + "balance": "10011000000000000000000" + }, + "b6f7F57b99DB21027875BEa3b8531d5925c346cE": { + "balance": "10011000000000000000000" + }, + "279d05241d33Dc422d5AEcAc0e089B7f50f879c3": { + "balance": "10011000000000000000000" + }, + "d57FEfe1B634ab451a6815Cd6769182EABA62779": { + "balance": "10011000000000000000000" + }, + "e86C8538Bdfb253E8D6cC29ee24A330905324849": { + "balance": "10011000000000000000000" + }, + "2C58D7f7f9CDF79CF3Cd5F4247761b93428A4E9e": { + "balance": "10011000000000000000000" + }, + "37326cEfAFB1676f7Af1CcDcCD37A846Ec64F19d": { + "balance": "10011000000000000000000" + }, + "f01DCf91d5f74BDB161F520e800c64F686Eb253F": { + "balance": "10011000000000000000000" + }, + "Ba85246bc2A4fdaC1cB2e3C68383Fe79A6466fd9": { + "balance": "10011000000000000000000" + }, + "4A76f81eA26381981a3B740975fb4F605989b585": { + "balance": "10011000000000000000000" + }, + "00ee7168618BaE4F4d2900D5063c62948c6F0566": { + "balance": "10011000000000000000000" + }, + "E1aD0B232B4262E4A279C91070417DAAF202623F": { + "balance": "10011000000000000000000" + }, + "f611173319b22080E0F02eE724781d85f4b39Ae6": { + "balance": "10011000000000000000000" + }, + "158659458dff3a9E5182cA0e8Ba08F53463FA5e7": { + "balance": "10011000000000000000000" + }, + "FEB11610ad367b0c994274A8153E50F4557e473F": { + "balance": "10011000000000000000000" + }, + "e1eB2279f45760Ab9D734782B1a0A8FD3d47D807": { + "balance": "10011000000000000000000" + }, + "8667d005eCF50Eb247890a11FCdCfC321DC1Da9f": { + "balance": "10011000000000000000000" + }, + "5Ce612A664C2f35558Dcab7edb999619e155CD07": { + "balance": "10011000000000000000000" + }, + "aD95f88cCd3aBC12ddd6cD0b9a777B95339b747b": { + "balance": "10011000000000000000000" + }, + "6E5a5A2963F6d0C2EA26682a152fE3ac7CBC1227": { + "balance": "10011000000000000000000" + }, + "000000000000000000000000000000000000ce10": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820959a50d5df76f90bc1825042f47788ee27f1b4725f7ed5d37c5c05c0732ef44f0029", + "storage": { + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x0Cc59Ed03B3e763c02d54D695FFE353055f1502D" + }, + "balance": "0" + } +}` + +const alfajoresAllocJSON = `{ + "456f41406B32c45D59E539e4BBA3D7898c3584dA": { + "balance": "103010030000000000000000000" + }, + "DD1F519F63423045F526b8c83edC0eB4BA6434a4": { + "balance": "10011000000000000000000" + }, + "050f34537F5b2a00B9B9C752Cb8500a3fcE3DA7d": { + "balance": "10011000000000000000000" + }, + "Cda518F6b5a797C3EC45D37c65b83e0b0748eDca": { + "balance": "10011000000000000000000" + }, + "b4e92c94A2712e98c020A81868264bdE52C188Cb": { + "balance": "10011000000000000000000" + }, + "Ae1ec841923811219b98ACeB1db297AADE2F46F3": { + "balance": "10011000000000000000000" + }, + "621843731fe33418007C06ee48CfD71e0ea828d9": { + "balance": "10011000000000000000000" + }, + "2A43f97f8BF959E31F69A894ebD80A88572C8553": { + "balance": "10011000000000000000000" + }, + "AD682035bE6Ab6f06e478D2BDab0EAb6477B460E": { + "balance": "10011000000000000000000" + }, + "30D060F129817c4DE5fBc1366d53e19f43c8c64f": { + "balance": "10011000000000000000000" + }, + "22579CA45eE22E2E16dDF72D955D6cf4c767B0eF": { + "balance": "10011000000000000000000" + }, + "1173C5A50bf025e8356823a068E396ccF2bE696C": { + "balance": "10011000000000000000000" + }, + "40F71B525A96baa8d14Eaa7Bcd19929782659c64": { + "balance": "10011000000000000000000" + }, + "b923626C6f1d237252793FB2aA12BA21328C51BC": { + "balance": "10011000000000000000000" + }, + "B70f9ABf41F36B3ab60cc9aE1a85Ddda3C88D261": { + "balance": "10011000000000000000000" + }, + "d4369DB59eaDc4Cfa089c0a3c1004ceAb1b318D8": { + "balance": "10011000000000000000000" + }, + "2fd430d3a96eadc38cc1B38b6685C5f52Cf7a083": { + "balance": "10011000000000000000000" + }, + "Fecc71C8f33Ca5952534fd346ADdeDC38DBb9cb7": { + "balance": "10011000000000000000000" + }, + "0de78C89e7BF5060f28dd3f820C15C4A6A81AFB5": { + "balance": "10011000000000000000000" + }, + "75411b92fcE120C1e7fd171b1c2bF802f2E3CF48": { + "balance": "10011000000000000000000" + }, + "563433bD8357b06982Fe001df20B2b43393d21d2": { + "balance": "10011000000000000000000" + }, + "79dfB9d2367E7921d4139D7841d24ED82F48907F": { + "balance": "10011000000000000000000" + }, + "5809369FC5121a071eE67659a975e88ae40fBE3b": { + "balance": "10011000000000000000000" + }, + "7517E54a456bcc6c5c695B5d9f97EBc05d29a824": { + "balance": "10011000000000000000000" + }, + "B0a1A5Ffcb34E6Fa278D2b40613f0AE1042d32f8": { + "balance": "10011000000000000000000" + }, + "EeE9f4DDf49976251E84182AbfD3300Ee58D12aa": { + "balance": "10011000000000000000000" + }, + "Eb5Fd57f87a4e1c7bAa53ec1c0d021bb1710B743": { + "balance": "10011000000000000000000" + }, + "B7Dd51bFb73c5753778e5Af56f1D9669BCe6777F": { + "balance": "10011000000000000000000" + }, + "33C222BB13C63295AF32D6C91278AA34b573e776": { + "balance": "10011000000000000000000" + }, + "83c58603bF72DA067D7f6238E7bF390d91B2f531": { + "balance": "10011000000000000000000" + }, + "6651112198C0da05921355642a2B8dF1fA3Ede93": { + "balance": "10011000000000000000000" + }, + "4EE72A98549eA7CF774C3E2E1b39fF166b4b68BE": { + "balance": "10011000000000000000000" + }, + "840b32F30e1a3b2E8b9E6C0972eBa0148E22B847": { + "balance": "100000000000000000000" + }, + "000000000000000000000000000000000000ce10": { + "code": "0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a723058202dbb6037e4381b4ad95015ed99441a23345cc2ae52ef27e2e91d34fb0acd277b0029", + "storage": { + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "456f41406B32c45D59E539e4BBA3D7898c3584dA" + }, + "balance": "0" + } +}` + +const mainnetAllocJSON = "{\"0x11901cf7eEae1E2644995FB2E47Ce46bC7F33246\":{\"balance\":\"120000000000000000000000000\"},\"0xC1cDA18694F5B86cFB80c1B4f8Cc046B0d7E6326\":{\"balance\":\"20000000000000000000000000\"},\"0xa5d40D93b01AfBafec84E20018Aff427628F645E\":{\"balance\":\"20000000000000000000000000\"},\"0x8d485780E84E23437f8F6938D96B964645529127\":{\"balance\":\"20000000000000000000000000\"},\"0x5F857c501b73ddFA804234f1f1418D6f75554076\":{\"balance\":\"20000000000000000000000000\"},\"0xaa9064F57F8d7de4b3e08c35561E21Afd6341390\":{\"balance\":\"20000000000000000000000000\"},\"0x7FA26b50b3e9a2eC8AD1850a4c4FBBF94D806E95\":{\"balance\":\"20000000000000000000000000\"},\"0x08960Ce6b58BE32FBc6aC1489d04364B4f7dC216\":{\"balance\":\"20000000000000000000000000\"},\"0x77B68B2e7091D4F242a8Af89F200Af941433C6d8\":{\"balance\":\"20000000000000000000000000\"},\"0x75Bb69C002C43f5a26a2A620518775795Fd45ecf\":{\"balance\":\"20000000000000000000000000\"},\"0x19992AE48914a178Bf138665CffDD8CD79b99513\":{\"balance\":\"20000000000000000000000000\"},\"0xE23a4c6615669526Ab58E9c37088bee4eD2b2dEE\":{\"balance\":\"20000000000000000000000\"},\"0xDe22679dCA843B424FD0BBd70A22D5F5a4B94fe4\":{\"balance\":\"10200014000000000000000000\"},\"0x743D80810fe10c5C3346D2940997cC9647035B13\":{\"balance\":\"20513322000000000000000000\"},\"0x8e1c4355307F1A59E7eD4Ae057c51368b9338C38\":{\"balance\":\"7291740000000000000000000\"},\"0x417fe63186C388812e342c85FF87187Dc584C630\":{\"balance\":\"20000062000000000000000000\"},\"0xF5720c180a6Fa14ECcE82FB1bB060A39E93A263c\":{\"balance\":\"30000061000000000000000000\"},\"0xB80d1e7F9CEbe4b5E1B1Acf037d3a44871105041\":{\"balance\":\"9581366833333333333333335\"},\"0xf8ed78A113cD2a34dF451Ba3D540FFAE66829AA0\":{\"balance\":\"11218686833333333333333333\"},\"0x9033ff75af27222c8f36a148800c7331581933F3\":{\"balance\":\"11218686833333333333333333\"},\"0x8A07541C2eF161F4e3f8de7c7894718dA26626B2\":{\"balance\":\"11218686833333333333333333\"},\"0xB2fe7AFe178335CEc3564d7671EEbD7634C626B0\":{\"balance\":\"11218686833333333333333333\"},\"0xc471776eA02705004C451959129bF09423B56526\":{\"balance\":\"11218686833333333333333333\"},\"0xeF283eca68DE87E051D427b4be152A7403110647\":{\"balance\":\"14375000000000000000000000\"},\"0x7cf091C954ed7E9304452d31fd59999505Ddcb7a\":{\"balance\":\"14375000000000000000000000\"},\"0xa5d2944C32a8D7b284fF0b84c20fDcc46937Cf64\":{\"balance\":\"14375000000000000000000000\"},\"0xFC89C17525f08F2Bc9bA8cb77BcF05055B1F7059\":{\"balance\":\"14375000000000000000000000\"},\"0x3Fa7C646599F3174380BD9a7B6efCde90b5d129d\":{\"balance\":\"14375000000000000000000000\"},\"0x989e1a3B344A43911e02cCC609D469fbc15AB1F1\":{\"balance\":\"14375000000000000000000000\"},\"0xAe1d640648009DbE0Aa4485d3BfBB68C37710924\":{\"balance\":\"20025000000000000000000000\"},\"0x1B6C64779F42BA6B54C853Ab70171aCd81b072F7\":{\"balance\":\"20025000000000000000000000\"},\"000000000000000000000000000000000000ce10\":{\"code\":\"0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a723058206808dd43e7d765afca53fe439122bc5eac16d708ce7d463451be5042426f101f0029\",\"storage\":{\"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103\":\"0xE23a4c6615669526Ab58E9c37088bee4eD2b2dEE\"},\"balance\":\"0\"}}"
diff --git go-ethereum/core/blockchain_repair_test.go celo/core/blockchain_repair_test.go index 67681b0bdbe89df9ab2ebee42feae5267ff7447f..25977848f81d8376f9dba64dabbfad3eaa742a3b 100644 --- go-ethereum/core/blockchain_repair_test.go +++ celo/core/blockchain_repair_test.go @@ -21,14 +21,14 @@ package core   import ( + // "fmt" "io/ioutil" - "math/big" "os" "testing" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -422,140 +422,6 @@ commitBlock: 0, pivotBlock: uint64ptr(4), expCanonicalBlocks: 8, expSidechainBlocks: 6, - expFrozen: 0, - expHeadHeader: 8, - expHeadFastBlock: 8, - expHeadBlock: 0, - }, snapshots) -} - -// Tests a recovery for a short canonical chain and a longer side chain, where a -// recent block was already committed to disk and then the process crashed. In this -// case we expect the canonical chain to be rolled back to the committed block, but -// the chain data itself left in the database for replaying. -func TestShortReorgedRepair(t *testing.T) { testShortReorgedRepair(t, false) } -func TestShortReorgedRepairWithSnapshots(t *testing.T) { testShortReorgedRepair(t, true) } - -func testShortReorgedRepair(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10 - // - // Frozen: none - // Commit: G, C4 - // Pivot : none - // - // CRASH - // - // ------------------------------ - // - // Expected in leveldb: - // G->C1->C2->C3->C4->C5->C6->C7->C8 - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10 - // - // Expected head header : C8 - // Expected head fast block: C8 - // Expected head block : C4 - testRepair(t, &rewindTest{ - canonicalBlocks: 8, - sidechainBlocks: 10, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: nil, - expCanonicalBlocks: 8, - expSidechainBlocks: 10, - expFrozen: 0, - expHeadHeader: 8, - expHeadFastBlock: 8, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a recovery for a short canonical chain and a longer side chain, where -// the fast sync pivot point was already committed to disk and then the process -// crashed. In this case we expect the canonical chain to be rolled back to the -// committed block, but the chain data itself left in the database for replaying. -func TestShortReorgedFastSyncedRepair(t *testing.T) { - testShortReorgedFastSyncedRepair(t, false) -} -func TestShortReorgedFastSyncedRepairWithSnapshots(t *testing.T) { - testShortReorgedFastSyncedRepair(t, true) -} - -func testShortReorgedFastSyncedRepair(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10 - // - // Frozen: none - // Commit: G, C4 - // Pivot : C4 - // - // CRASH - // - // ------------------------------ - // - // Expected in leveldb: - // G->C1->C2->C3->C4->C5->C6->C7->C8 - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10 - // - // Expected head header : C8 - // Expected head fast block: C8 - // Expected head block : C4 - testRepair(t, &rewindTest{ - canonicalBlocks: 8, - sidechainBlocks: 10, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: uint64ptr(4), - expCanonicalBlocks: 8, - expSidechainBlocks: 10, - expFrozen: 0, - expHeadHeader: 8, - expHeadFastBlock: 8, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a recovery for a short canonical chain and a longer side chain, where -// the fast sync pivot point was not yet committed, but the process crashed. In -// this case we expect the chain to detect that it was fast syncing and not delete -// anything, since we can just pick up directly where we left off. -func TestShortReorgedFastSyncingRepair(t *testing.T) { - testShortReorgedFastSyncingRepair(t, false) -} -func TestShortReorgedFastSyncingRepairWithSnapshots(t *testing.T) { - testShortReorgedFastSyncingRepair(t, true) -} - -func testShortReorgedFastSyncingRepair(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10 - // - // Frozen: none - // Commit: G - // Pivot : C4 - // - // CRASH - // - // ------------------------------ - // - // Expected in leveldb: - // G->C1->C2->C3->C4->C5->C6->C7->C8 - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10 - // - // Expected head header : C8 - // Expected head fast block: C8 - // Expected head block : G - testRepair(t, &rewindTest{ - canonicalBlocks: 8, - sidechainBlocks: 10, - freezeThreshold: 16, - commitBlock: 0, - pivotBlock: uint64ptr(4), - expCanonicalBlocks: 8, - expSidechainBlocks: 10, expFrozen: 0, expHeadHeader: 8, expHeadFastBlock: 8, @@ -1453,303 +1319,6 @@ expHeadBlock: 0, }, snapshots) }   -// Tests a recovery for a long canonical chain with frozen blocks and a longer side -// chain, where a recent block - newer than the ancient limit - was already committed -// to disk and then the process crashed. In this case we expect the chain to be -// rolled back to the committed block, with everything afterwads kept as fast sync -// data. The side chain completely nuked by the freezer. -func TestLongReorgedShallowRepair(t *testing.T) { testLongReorgedShallowRepair(t, false) } -func TestLongReorgedShallowRepairWithSnapshots(t *testing.T) { testLongReorgedShallowRepair(t, true) } - -func testLongReorgedShallowRepair(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2 - // - // Commit: G, C4 - // Pivot : none - // - // CRASH - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2 - // - // Expected in leveldb: - // C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 - // - // Expected head header : C18 - // Expected head fast block: C18 - // Expected head block : C4 - testRepair(t, &rewindTest{ - canonicalBlocks: 18, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: nil, - expCanonicalBlocks: 18, - expSidechainBlocks: 0, - expFrozen: 3, - expHeadHeader: 18, - expHeadFastBlock: 18, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a recovery for a long canonical chain with frozen blocks and a longer side -// chain, where a recent block - older than the ancient limit - was already committed -// to disk and then the process crashed. In this case we expect the canonical chains -// to be rolled back to the committed block, with everything afterwads deleted. The -// side chain completely nuked by the freezer. -func TestLongReorgedDeepRepair(t *testing.T) { testLongReorgedDeepRepair(t, false) } -func TestLongReorgedDeepRepairWithSnapshots(t *testing.T) { testLongReorgedDeepRepair(t, true) } - -func testLongReorgedDeepRepair(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2->C3->C4->C5->C6->C7->C8 - // - // Commit: G, C4 - // Pivot : none - // - // CRASH - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2->C3->C4 - // - // Expected in leveldb: none - // - // Expected head header : C4 - // Expected head fast block: C4 - // Expected head block : C4 - testRepair(t, &rewindTest{ - canonicalBlocks: 24, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: nil, - expCanonicalBlocks: 4, - expSidechainBlocks: 0, - expFrozen: 5, - expHeadHeader: 4, - expHeadFastBlock: 4, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a recovery for a long canonical chain with frozen blocks and a longer -// side chain, where the fast sync pivot point - newer than the ancient limit - -// was already committed to disk and then the process crashed. In this case we -// expect the chain to be rolled back to the committed block, with everything -// afterwads kept as fast sync data. The side chain completely nuked by the -// freezer. -func TestLongReorgedFastSyncedShallowRepair(t *testing.T) { - testLongReorgedFastSyncedShallowRepair(t, false) -} -func TestLongReorgedFastSyncedShallowRepairWithSnapshots(t *testing.T) { - testLongReorgedFastSyncedShallowRepair(t, true) -} - -func testLongReorgedFastSyncedShallowRepair(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2 - // - // Commit: G, C4 - // Pivot : C4 - // - // CRASH - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2 - // - // Expected in leveldb: - // C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 - // - // Expected head header : C18 - // Expected head fast block: C18 - // Expected head block : C4 - testRepair(t, &rewindTest{ - canonicalBlocks: 18, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: uint64ptr(4), - expCanonicalBlocks: 18, - expSidechainBlocks: 0, - expFrozen: 3, - expHeadHeader: 18, - expHeadFastBlock: 18, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a recovery for a long canonical chain with frozen blocks and a longer -// side chain, where the fast sync pivot point - older than the ancient limit - -// was already committed to disk and then the process crashed. In this case we -// expect the canonical chains to be rolled back to the committed block, with -// everything afterwads deleted. The side chain completely nuked by the freezer. -func TestLongReorgedFastSyncedDeepRepair(t *testing.T) { - testLongReorgedFastSyncedDeepRepair(t, false) -} -func TestLongReorgedFastSyncedDeepRepairWithSnapshots(t *testing.T) { - testLongReorgedFastSyncedDeepRepair(t, true) -} - -func testLongReorgedFastSyncedDeepRepair(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2->C3->C4->C5->C6->C7->C8 - // - // Commit: G, C4 - // Pivot : C4 - // - // CRASH - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2->C3->C4 - // - // Expected in leveldb: none - // - // Expected head header : C4 - // Expected head fast block: C4 - // Expected head block : C4 - testRepair(t, &rewindTest{ - canonicalBlocks: 24, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: uint64ptr(4), - expCanonicalBlocks: 4, - expSidechainBlocks: 0, - expFrozen: 5, - expHeadHeader: 4, - expHeadFastBlock: 4, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a recovery for a long canonical chain with frozen blocks and a longer -// side chain, where the fast sync pivot point - newer than the ancient limit - -// was not yet committed, but the process crashed. In this case we expect the -// chain to detect that it was fast syncing and not delete anything, since we -// can just pick up directly where we left off. -func TestLongReorgedFastSyncingShallowRepair(t *testing.T) { - testLongReorgedFastSyncingShallowRepair(t, false) -} -func TestLongReorgedFastSyncingShallowRepairWithSnapshots(t *testing.T) { - testLongReorgedFastSyncingShallowRepair(t, true) -} - -func testLongReorgedFastSyncingShallowRepair(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2 - // - // Commit: G - // Pivot : C4 - // - // CRASH - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2 - // - // Expected in leveldb: - // C2)->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 - // - // Expected head header : C18 - // Expected head fast block: C18 - // Expected head block : G - testRepair(t, &rewindTest{ - canonicalBlocks: 18, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 0, - pivotBlock: uint64ptr(4), - expCanonicalBlocks: 18, - expSidechainBlocks: 0, - expFrozen: 3, - expHeadHeader: 18, - expHeadFastBlock: 18, - expHeadBlock: 0, - }, snapshots) -} - -// Tests a recovery for a long canonical chain with frozen blocks and a longer -// side chain, where the fast sync pivot point - older than the ancient limit - -// was not yet committed, but the process crashed. In this case we expect the -// chain to detect that it was fast syncing and not delete anything, since we -// can just pick up directly where we left off. -func TestLongReorgedFastSyncingDeepRepair(t *testing.T) { - testLongReorgedFastSyncingDeepRepair(t, false) -} -func TestLongReorgedFastSyncingDeepRepairWithSnapshots(t *testing.T) { - testLongReorgedFastSyncingDeepRepair(t, true) -} - -func testLongReorgedFastSyncingDeepRepair(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2->C3->C4->C5->C6->C7->C8 - // - // Commit: G - // Pivot : C4 - // - // CRASH - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2->C3->C4->C5->C6->C7->C8 - // - // Expected in leveldb: - // C8)->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 - // - // Expected head header : C24 - // Expected head fast block: C24 - // Expected head block : G - testRepair(t, &rewindTest{ - canonicalBlocks: 24, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 0, - pivotBlock: uint64ptr(4), - expCanonicalBlocks: 24, - expSidechainBlocks: 0, - expFrozen: 9, - expHeadHeader: 24, - expHeadFastBlock: 24, - expHeadBlock: 0, - }, snapshots) -} - func testRepair(t *testing.T, tt *rewindTest, snapshots bool) { // It's hard to follow the test case, visualize the input //log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) @@ -1770,8 +1339,8 @@ defer db.Close() // Might double close, should be fine   // Initialize a fresh chain var ( - genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) - engine = ethash.NewFullFaker() + genesis = new(Genesis).MustCommit(db) + engine = mockEngine.NewFaker() config = &CacheConfig{ TrieCleanLimit: 256, TrieDirtyLimit: 256, @@ -1783,7 +1352,7 @@ if snapshots { config.SnapshotLimit = 256 config.SnapshotWait = true } - chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(db, config, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to create chain: %v", err) } @@ -1799,7 +1368,7 @@ } } canonblocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0x02}) - b.SetDifficulty(big.NewInt(1000000)) + // b.SetDifficulty(big.NewInt(1000000)) }) if _, err := chain.InsertChain(canonblocks[:tt.commitBlock]); err != nil { t.Fatalf("Failed to import canonical chain start: %v", err) @@ -1829,14 +1398,13 @@ } // Pull the plug on the database, simulating a hard crash db.Close()   - // Start a new blockchain back up and see where the repait leads us + // Start a new blockchain back up and see where the repair leads us db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false) if err != nil { t.Fatalf("Failed to reopen persistent database: %v", err) } defer db.Close() - - chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + chain, err = NewBlockChain(db, nil, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) }
diff --git go-ethereum/core/bloom_indexer.go celo/core/bloom_indexer.go index 4ec6f666419c0e9b0ac95e83c35c41df5d76fb41..cfa04a36c8c030fe4df581d3ab638ca7433dbb5d 100644 --- go-ethereum/core/bloom_indexer.go +++ celo/core/bloom_indexer.go @@ -46,14 +46,14 @@ }   // NewBloomIndexer returns a chain indexer that generates bloom bits data for the // canonical chain for fast logs filtering. -func NewBloomIndexer(db ethdb.Database, size, confirms uint64) *ChainIndexer { +func NewBloomIndexer(db ethdb.Database, size, confirms uint64, fullChainDownloaded bool) *ChainIndexer { backend := &BloomIndexer{ db: db, size: size, } table := rawdb.NewTable(db, string(rawdb.BloomBitsIndexPrefix))   - return NewChainIndexer(db, table, backend, size, confirms, bloomThrottling, "bloombits") + return NewChainIndexer(db, table, backend, size, confirms, bloomThrottling, "bloombits", fullChainDownloaded) }   // Reset implements core.ChainIndexerBackend, starting a new bloombits index
diff --git go-ethereum/core/blockchain_snapshot_test.go celo/core/blockchain_snapshot_test.go index 4981509277e5d7163a496a4bc0614b81168044ce..1a2818a1527367bdbccbfc28c533ad6725cebbc6 100644 --- go-ethereum/core/blockchain_snapshot_test.go +++ celo/core/blockchain_snapshot_test.go @@ -23,14 +23,13 @@ import ( "bytes" "fmt" "io/ioutil" - "math/big" "os" "strings" "testing" "time"   "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -71,8 +70,8 @@ t.Fatalf("Failed to create persistent database: %v", err) } // Initialize a fresh chain var ( - genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) - engine = ethash.NewFullFaker() + genesis = new(Genesis).MustCommit(db) + engine = mockEngine.NewFaker() gendb = rawdb.NewMemoryDatabase()   // Snapshot is enabled, the first snapshot is created from the Genesis. @@ -80,7 +79,7 @@ // The snapshot memory allowance is 256MB, it means no snapshot flush // will happen during the block insertion. cacheConfig = defaultCacheConfig ) - chain, err := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(db, cacheConfig, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to create chain: %v", err) } @@ -223,7 +222,7 @@ chain, blocks := snaptest.prepare(t)   // Restart the chain normally chain.Stop() - newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(snaptest.db, nil, params.IstanbulTestChainConfig, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -259,13 +258,13 @@ // The interesting thing is: instead of starting the blockchain after // the crash, we do restart twice here: one after the crash and one // after the normal stop. It's used to ensure the broken snapshot // can be detected all the time. - newchain, err := NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(newdb, nil, params.IstanbulTestChainConfig, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } newchain.Stop()   - newchain, err = NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err = NewBlockChain(newdb, nil, params.IstanbulTestChainConfig, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -301,7 +300,7 @@ TrieDirtyLimit: 256, TrieTimeLimit: 5 * time.Minute, SnapshotLimit: 0, } - newchain, err := NewBlockChain(snaptest.db, cacheConfig, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(snaptest.db, cacheConfig, params.IstanbulTestChainConfig, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -309,7 +308,7 @@ newchain.InsertChain(gappedBlocks) newchain.Stop()   // Restart the chain with enabling the snapshot - newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err = NewBlockChain(snaptest.db, nil, params.IstanbulTestChainConfig, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -337,7 +336,7 @@ // Rewind the chain if setHead operation is required. chain.SetHead(snaptest.setHead) chain.Stop()   - newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(snaptest.db, nil, params.IstanbulTestChainConfig, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -368,7 +367,7 @@ // Firstly, stop the chain properly, with all snapshot journal // and state committed. chain.Stop()   - newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(snaptest.db, nil, params.IstanbulTestChainConfig, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -385,7 +384,7 @@ // Don't call chain.Stop here, so that no snapshot // journal and latest state will be committed   // Restart the chain after the crash - newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err = NewBlockChain(snaptest.db, nil, params.IstanbulTestChainConfig, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -420,7 +419,7 @@ TrieDirtyLimit: 256, TrieTimeLimit: 5 * time.Minute, SnapshotLimit: 0, } - newchain, err := NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(snaptest.db, config, params.IstanbulTestChainConfig, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -436,13 +435,13 @@ TrieTimeLimit: 5 * time.Minute, SnapshotLimit: 256, SnapshotWait: false, // Don't wait rebuild } - newchain, err = NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err = NewBlockChain(snaptest.db, config, params.IstanbulTestChainConfig, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } // Simulate the blockchain crash.   - newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err = NewBlockChain(snaptest.db, nil, params.IstanbulTestChainConfig, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) }
diff --git go-ethereum/core/sys_context.go celo/core/sys_context.go new file mode 100644 index 0000000000000000000000000000000000000000..6e2fba2f7f780236101708519268e92c88c11054 --- /dev/null +++ celo/core/sys_context.go @@ -0,0 +1,122 @@ +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" + "github.com/ethereum/go-ethereum/contracts/currency" + "github.com/ethereum/go-ethereum/contracts/gasprice_minimum" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" +) + +// SysContractCallCtx acts as a cache holding information obtained through +// system contract calls to be used during block processing. +// Note: This struct should be a read only one to be safe for concurrent use +type SysContractCallCtx struct { + whitelistedCurrencies map[common.Address]struct{} + // The gas required for a non celo (cUSD, cEUR, ...) transfer. + nonCeloCurrencyIntrinsicGas uint64 + // gasPriceMinimums stores values for whitelisted currencies keyed by their contract address + // Note that native token(CELO) is keyed by common.ZeroAddress + gasPriceMinimums GasPriceMinimums +} + +// runnerFactory exists to allow multiple different implementations to be +// used as input to NewSysContractCallCtx. +type runnerFactory interface { + NewEVMRunner(*types.Header, vm.StateDB) vm.EVMRunner +} + +// NewSysContractCallCtx returns a SysContractCallCtx filled with data obtained +// by calling the relevant system contracts. This is a read only operation, no +// state changing operations should be performed here. The provided header and +// state should be for the parent of the block to be processed, in normal +// operation that will be the head block. +// +// Since geth introduced the access list, even read only contract calls modify +// the state (by adding to the access list) as such the provided state is +// copied to ensure that the state provided by the caller is not modified by +// this operation. +func NewSysContractCallCtx(header *types.Header, state *state.StateDB, factory runnerFactory) (sc *SysContractCallCtx) { + vmRunner := factory.NewEVMRunner(header, state.Copy()) + sc = &SysContractCallCtx{ + whitelistedCurrencies: make(map[common.Address]struct{}), + gasPriceMinimums: make(map[common.Address]*big.Int), + } + // intrinsic gas + sc.nonCeloCurrencyIntrinsicGas = blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(vmRunner) + // whitelist + whiteListedArr, err := currency.CurrencyWhitelist(vmRunner) + if err != nil { + whiteListedArr = []common.Address{} + } + for _, feeCurrency := range whiteListedArr { + sc.whitelistedCurrencies[feeCurrency] = struct{}{} + } + // gas price minimum + celoGPM, _ := gasprice_minimum.GetGasPriceMinimum(vmRunner, nil) + sc.gasPriceMinimums[common.ZeroAddress] = celoGPM + + for feeCurrency := range sc.whitelistedCurrencies { + gasPriceMinimum, _ := gasprice_minimum.GetGasPriceMinimum(vmRunner, &feeCurrency) + sc.gasPriceMinimums[feeCurrency] = gasPriceMinimum + } + return sc +} + +// GetIntrinsicGasForAlternativeFeeCurrency retrieves intrinsic gas for non-native fee currencies. +func (sc *SysContractCallCtx) GetIntrinsicGasForAlternativeFeeCurrency() uint64 { + return sc.nonCeloCurrencyIntrinsicGas +} + +// IsWhitelisted indicates if the fee currency is whitelisted, or it's native token(CELO). +func (sc *SysContractCallCtx) IsWhitelisted(feeCurrency *common.Address) bool { + if feeCurrency == nil { + return true + } + _, ok := sc.whitelistedCurrencies[*feeCurrency] + return ok +} + +// GetGasPriceMinimum retrieves gas price minimum for given fee currency address. +// Note that the CELO currency is keyed by the Zero address. +func (sc *SysContractCallCtx) GetGasPriceMinimum(feeCurrency *common.Address) *big.Int { + return sc.gasPriceMinimums.GetGasPriceMinimum(feeCurrency) +} + +// GetCurrentGasPriceMinimumMap returns the gas price minimum map for all whitelisted currencies. +// Note that the CELO currency is keyed by the Zero address. +func (sc *SysContractCallCtx) GetCurrentGasPriceMinimumMap() GasPriceMinimums { + return sc.gasPriceMinimums +} + +type GasPriceMinimums map[common.Address]*big.Int + +func (gpm GasPriceMinimums) valOrDefault(key common.Address) *big.Int { + val, ok := gpm[key] + if !ok { + return gasprice_minimum.FallbackGasPriceMinimum + } + return val +} + +// GetNativeGPM retrieves the gas price minimum for the native currency. +func (gpm GasPriceMinimums) GetNativeGPM() *big.Int { + return gpm.valOrDefault(common.ZeroAddress) +} + +// GetGasPriceMinimum retrieves gas price minimum for given fee currency address, it returns gasprice_minimum.FallbackGasPriceMinimum when there is an error +func (gpm GasPriceMinimums) GetGasPriceMinimum(feeCurrency *common.Address) *big.Int { + // feeCurrency for native token(CELO) is nil, so we bind common.ZeroAddress as key + var key common.Address + if feeCurrency == nil { + key = common.ZeroAddress + } else { + key = *feeCurrency + } + + return gpm.valOrDefault(key) +}
diff --git go-ethereum/core/blockchain_test.go celo/core/blockchain_test.go index 9fa697b163e6ffbc88f9068059f87e41a5a5690f..ed397c39e1c6194a50fe8e6897f2fc708bac4c7a 100644 --- go-ethereum/core/blockchain_test.go +++ celo/core/blockchain_test.go @@ -18,7 +18,6 @@ package core   import ( "errors" - "fmt" "io/ioutil" "math/big" "math/rand" @@ -29,7 +28,8 @@ "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/contracts/testutil" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -52,11 +52,11 @@ // header only chain. func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *BlockChain, error) { var ( db = rawdb.NewMemoryDatabase() - genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) + genesis = new(Genesis).MustCommit(db) )   // Initialize a fresh chain with only a genesis block - blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) // Create and inject the requested chain if n == 0 { return db, blockchain, nil @@ -69,7 +69,7 @@ return db, blockchain, err } // Header-only chain requested headers := makeHeaderChain(genesis.Header(), n, engine, db, canonicalSeed) - _, err := blockchain.InsertHeaderChain(headers, 1) + _, err := blockchain.InsertHeaderChain(headers, 1, true) return db, blockchain, err }   @@ -80,7 +80,7 @@ // Test fork of length N starting from block i func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, comparator func(td1, td2 *big.Int)) { // Copy old chain up to #i into a new db - db, blockchain2, err := newCanonical(ethash.NewFaker(), i, full) + db, blockchain2, err := newCanonical(mockEngine.NewFaker(), i, full) if err != nil { t.Fatal("could not make new canonical in testFork", err) } @@ -104,13 +104,13 @@ blockChainB []*types.Block headerChainB []*types.Header ) if full { - blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, ethash.NewFaker(), db, forkSeed) + blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, mockEngine.NewFaker(), db, forkSeed) if _, err := blockchain2.InsertChain(blockChainB); err != nil { t.Fatalf("failed to insert forking chain: %v", err) } } else { - headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, ethash.NewFaker(), db, forkSeed) - if _, err := blockchain2.InsertHeaderChain(headerChainB, 1); err != nil { + headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, mockEngine.NewFaker(), db, forkSeed) + if _, err := blockchain2.InsertHeaderChain(headerChainB, 1, true); err != nil { t.Fatalf("failed to insert forking chain: %v", err) } } @@ -163,8 +163,8 @@ if err != nil { blockchain.reportBlock(block, receipts, err) return err } - blockchain.chainmu.Lock() - rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash()))) + blockchain.chainmu.MustLock() + rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), block.TotalDifficulty()) rawdb.WriteBlock(blockchain.db, block) statedb.Commit(false) blockchain.chainmu.Unlock() @@ -181,8 +181,8 @@ if err := blockchain.engine.VerifyHeader(blockchain, header, false); err != nil { return err } // Manually insert the header into the database, but don't reorganise (allows subsequent testing) - blockchain.chainmu.Lock() - rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash))) + blockchain.chainmu.MustLock() + rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Number, big.NewInt(1))) rawdb.WriteHeader(blockchain.db, header) blockchain.chainmu.Unlock() } @@ -190,13 +190,13 @@ return nil }   func TestLastBlock(t *testing.T) { - _, blockchain, err := newCanonical(ethash.NewFaker(), 0, true) + _, blockchain, err := newCanonical(mockEngine.NewFaker(), 0, true) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } defer blockchain.Stop()   - blocks := makeBlockChain(blockchain.CurrentBlock(), 1, ethash.NewFullFaker(), blockchain.db, 0) + blocks := makeBlockChain(blockchain.CurrentBlock(), 1, mockEngine.NewFullFaker(), blockchain.db, 0) if _, err := blockchain.InsertChain(blocks); err != nil { t.Fatalf("Failed to insert block: %v", err) } @@ -214,7 +214,7 @@ func testExtendCanonical(t *testing.T, full bool) { length := 5   // Make first chain starting from genesis - _, processor, err := newCanonical(ethash.NewFaker(), length, full) + _, processor, err := newCanonical(mockEngine.NewFaker(), length, full) if err != nil { t.Fatalf("failed to make new canonical chain: %v", err) } @@ -242,7 +242,7 @@ func testShorterFork(t *testing.T, full bool) { length := 10   // Make first chain starting from genesis - _, processor, err := newCanonical(ethash.NewFaker(), length, full) + _, processor, err := newCanonical(mockEngine.NewFaker(), length, full) if err != nil { t.Fatalf("failed to make new canonical chain: %v", err) } @@ -272,7 +272,7 @@ func testLongerFork(t *testing.T, full bool) { length := 10   // Make first chain starting from genesis - _, processor, err := newCanonical(ethash.NewFaker(), length, full) + _, processor, err := newCanonical(mockEngine.NewFaker(), length, full) if err != nil { t.Fatalf("failed to make new canonical chain: %v", err) } @@ -302,7 +302,7 @@ func testEqualFork(t *testing.T, full bool) { length := 10   // Make first chain starting from genesis - _, processor, err := newCanonical(ethash.NewFaker(), length, full) + _, processor, err := newCanonical(mockEngine.NewFaker(), length, full) if err != nil { t.Fatalf("failed to make new canonical chain: %v", err) } @@ -329,7 +329,7 @@ func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) }   func testBrokenChain(t *testing.T, full bool) { // Make chain starting from genesis - db, blockchain, err := newCanonical(ethash.NewFaker(), 10, full) + db, blockchain, err := newCanonical(mockEngine.NewFaker(), 10, full) if err != nil { t.Fatalf("failed to make new canonical chain: %v", err) } @@ -337,12 +337,12 @@ defer blockchain.Stop()   // Create a forked chain, and try to insert with a missing link if full { - chain := makeBlockChain(blockchain.CurrentBlock(), 5, ethash.NewFaker(), db, forkSeed)[1:] + chain := makeBlockChain(blockchain.CurrentBlock(), 5, mockEngine.NewFaker(), db, forkSeed)[1:] if err := testBlockChainImport(chain, blockchain); err == nil { t.Errorf("broken block chain not reported") } } else { - chain := makeHeaderChain(blockchain.CurrentHeader(), 5, ethash.NewFaker(), db, forkSeed)[1:] + chain := makeHeaderChain(blockchain.CurrentHeader(), 5, mockEngine.NewFaker(), db, forkSeed)[1:] if err := testHeaderChainImport(chain, blockchain); err == nil { t.Errorf("broken header chain not reported") } @@ -355,7 +355,7 @@ func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) } func TestReorgLongBlocks(t *testing.T) { testReorgLong(t, true) }   func testReorgLong(t *testing.T, full bool) { - testReorg(t, []int64{0, 0, -9}, []int64{0, 0, 0, -9}, 393280, full) + testReorg(t, []int64{0, 0, -9}, []int64{0, 0, 0, -9}, 5, full) }   // Tests that reorganising a short difficult chain after a long easy one @@ -375,22 +375,22 @@ diff := make([]int64, len(easy)-1) for i := 0; i < len(diff); i++ { diff[i] = -9 } - testReorg(t, easy, diff, 12615120, full) + testReorg(t, easy, diff, 97, full) }   func testReorg(t *testing.T, first, second []int64, td int64, full bool) { // Create a pristine chain and database - db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full) + db, blockchain, err := newCanonical(mockEngine.NewFaker(), 0, full) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } defer blockchain.Stop()   // Insert an easy and a difficult chain afterwards - easyBlocks, _ := GenerateChain(params.TestChainConfig, blockchain.CurrentBlock(), ethash.NewFaker(), db, len(first), func(i int, b *BlockGen) { + easyBlocks, _ := GenerateChain(params.IstanbulTestChainConfig, blockchain.CurrentBlock(), mockEngine.NewFaker(), db, len(first), func(i int, b *BlockGen) { b.OffsetTime(first[i]) }) - diffBlocks, _ := GenerateChain(params.TestChainConfig, blockchain.CurrentBlock(), ethash.NewFaker(), db, len(second), func(i int, b *BlockGen) { + diffBlocks, _ := GenerateChain(params.IstanbulTestChainConfig, blockchain.CurrentBlock(), mockEngine.NewFaker(), db, len(second), func(i int, b *BlockGen) { b.OffsetTime(second[i]) }) if full { @@ -409,10 +409,10 @@ diffHeaders := make([]*types.Header, len(diffBlocks)) for i, block := range diffBlocks { diffHeaders[i] = block.Header() } - if _, err := blockchain.InsertHeaderChain(easyHeaders, 1); err != nil { + if _, err := blockchain.InsertHeaderChain(easyHeaders, 1, true); err != nil { t.Fatalf("failed to insert easy chain: %v", err) } - if _, err := blockchain.InsertHeaderChain(diffHeaders, 1); err != nil { + if _, err := blockchain.InsertHeaderChain(diffHeaders, 1, true); err != nil { t.Fatalf("failed to insert difficult chain: %v", err) } } @@ -433,7 +433,7 @@ } } } // Make sure the chain total difficulty is the correct one - want := new(big.Int).Add(blockchain.genesisBlock.Difficulty(), big.NewInt(td)) + want := big.NewInt(td) if full { if have := blockchain.GetTdByHash(blockchain.CurrentBlock().Hash()); have.Cmp(want) != 0 { t.Errorf("total difficulty mismatch: have %v, want %v", have, want) @@ -451,7 +451,7 @@ func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) }   func testBadHashes(t *testing.T, full bool) { // Create a pristine chain and database - db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full) + db, blockchain, err := newCanonical(mockEngine.NewFaker(), 0, full) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } @@ -459,19 +459,19 @@ defer blockchain.Stop()   // Create a chain, ban a hash and try to import if full { - blocks := makeBlockChain(blockchain.CurrentBlock(), 3, ethash.NewFaker(), db, 10) + blocks := makeBlockChain(blockchain.CurrentBlock(), 3, mockEngine.NewFaker(), db, 10)   BadHashes[blocks[2].Header().Hash()] = true defer func() { delete(BadHashes, blocks[2].Header().Hash()) }()   _, err = blockchain.InsertChain(blocks) } else { - headers := makeHeaderChain(blockchain.CurrentHeader(), 3, ethash.NewFaker(), db, 10) + headers := makeHeaderChain(blockchain.CurrentHeader(), 3, mockEngine.NewFaker(), db, 10)   BadHashes[headers[2].Hash()] = true defer func() { delete(BadHashes, headers[2].Hash()) }()   - _, err = blockchain.InsertHeaderChain(headers, 1) + _, err = blockchain.InsertHeaderChain(headers, 1, true) } if !errors.Is(err, ErrBannedHash) { t.Errorf("error mismatch: have: %v, want: %v", err, ErrBannedHash) @@ -485,13 +485,13 @@ func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) }   func testReorgBadHashes(t *testing.T, full bool) { // Create a pristine chain and database - db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full) + db, blockchain, err := newCanonical(mockEngine.NewFaker(), 0, full) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } // Create a chain, import and ban afterwards - headers := makeHeaderChain(blockchain.CurrentHeader(), 4, ethash.NewFaker(), db, 10) - blocks := makeBlockChain(blockchain.CurrentBlock(), 4, ethash.NewFaker(), db, 10) + headers := makeHeaderChain(blockchain.CurrentHeader(), 4, mockEngine.NewFaker(), db, 10) + blocks := makeBlockChain(blockchain.CurrentBlock(), 4, mockEngine.NewFaker(), db, 10)   if full { if _, err = blockchain.InsertChain(blocks); err != nil { @@ -503,7 +503,7 @@ } BadHashes[blocks[3].Header().Hash()] = true defer func() { delete(BadHashes, blocks[3].Header().Hash()) }() } else { - if _, err = blockchain.InsertHeaderChain(headers, 1); err != nil { + if _, err = blockchain.InsertHeaderChain(headers, 1, true); err != nil { t.Errorf("failed to import headers: %v", err) } if blockchain.CurrentHeader().Hash() != headers[3].Hash() { @@ -515,16 +515,13 @@ } blockchain.Stop()   // Create a new BlockChain and check that it rolled back the state. - ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, mockEngine.NewFaker(), vm.Config{}, nil, nil) if err != nil { t.Fatalf("failed to create new chain manager: %v", err) } if full { if ncm.CurrentBlock().Hash() != blocks[2].Header().Hash() { t.Errorf("last block hash mismatch: have: %x, want %x", ncm.CurrentBlock().Hash(), blocks[2].Header().Hash()) - } - if blocks[2].Header().GasLimit != ncm.GasLimit() { - t.Errorf("last block gasLimit mismatch: have: %d, want %d", ncm.GasLimit(), blocks[2].Header().GasLimit) } } else { if ncm.CurrentHeader().Hash() != headers[2].Hash() { @@ -541,7 +538,7 @@ func testInsertNonceError(t *testing.T, full bool) { for i := 1; i < 25 && !t.Failed(); i++ { // Create a pristine chain and database - db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full) + db, blockchain, err := newCanonical(mockEngine.NewFaker(), 0, full) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } @@ -554,22 +551,22 @@ failRes int failNum uint64 ) if full { - blocks := makeBlockChain(blockchain.CurrentBlock(), i, ethash.NewFaker(), db, 0) + blocks := makeBlockChain(blockchain.CurrentBlock(), i, mockEngine.NewFaker(), db, 0)   failAt = rand.Int() % len(blocks) failNum = blocks[failAt].NumberU64()   - blockchain.engine = ethash.NewFakeFailer(failNum) + blockchain.engine = mockEngine.NewFakeFailer(failNum) failRes, err = blockchain.InsertChain(blocks) } else { - headers := makeHeaderChain(blockchain.CurrentHeader(), i, ethash.NewFaker(), db, 0) + headers := makeHeaderChain(blockchain.CurrentHeader(), i, mockEngine.NewFaker(), db, 0)   failAt = rand.Int() % len(headers) failNum = headers[failAt].Number.Uint64()   - blockchain.engine = ethash.NewFakeFailer(failNum) + blockchain.engine = mockEngine.NewFakeFailer(failNum) blockchain.hc.engine = blockchain.engine - failRes, err = blockchain.InsertHeaderChain(headers, 1) + failRes, err = blockchain.InsertHeaderChain(headers, 1, true) } // Check that the returned error indicates the failure if failRes != failAt { @@ -600,35 +597,30 @@ key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(1000000000000000) gspec = &Genesis{ - Config: params.TestChainConfig, + Config: params.IstanbulTestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}, - BaseFee: big.NewInt(params.InitialBaseFee), } genesis = gspec.MustCommit(gendb) signer = types.LatestSigner(gspec.Config) ) - blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, 1024, func(i int, block *BlockGen) { + blocks, receipts := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), gendb, 1024, func(i int, block *BlockGen) { block.SetCoinbase(common.Address{0x00})   // If the block number is multiple of 3, send a few bonus transactions to the miner if i%3 == 2 { for j := 0; j < i%4+1; j++ { - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key) if err != nil { panic(err) } block.AddTx(tx) } } - // If the block number is a multiple of 5, add a few bonus uncles to the block - if i%5 == 5 { - block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 1).Hash(), Number: big.NewInt(int64(i - 1))}) - } }) // Import the chain as an archive node for the comparison baseline archiveDb := rawdb.NewMemoryDatabase() gspec.MustCommit(archiveDb) - archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer archive.Stop()   if n, err := archive.InsertChain(blocks); err != nil { @@ -637,14 +629,14 @@ } // Fast import the chain as a non-archive node to test fastDb := rawdb.NewMemoryDatabase() gspec.MustCommit(fastDb) - fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + fast, _ := NewBlockChain(fastDb, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer fast.Stop()   headers := make([]*types.Header, len(blocks)) for i, block := range blocks { headers[i] = block.Header() } - if n, err := fast.InsertHeaderChain(headers, 1); err != nil { + if n, err := fast.InsertHeaderChain(headers, 1, true); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) } if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil { @@ -661,10 +653,10 @@ if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } gspec.MustCommit(ancientDb) - ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer ancient.Stop()   - if n, err := ancient.InsertHeaderChain(headers, 1); err != nil { + if n, err := ancient.InsertHeaderChain(headers, 1, true); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) } if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(len(blocks)/2)); err != nil { @@ -691,8 +683,6 @@ if fblock, arblock, anblock := fast.GetBlockByHash(hash), archive.GetBlockByHash(hash), ancient.GetBlockByHash(hash); fblock.Hash() != arblock.Hash() || anblock.Hash() != arblock.Hash() { t.Errorf("block #%d [%x]: block mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock, anblock, arblock) } else if types.DeriveSha(fblock.Transactions(), trie.NewStackTrie(nil)) != types.DeriveSha(arblock.Transactions(), trie.NewStackTrie(nil)) || types.DeriveSha(anblock.Transactions(), trie.NewStackTrie(nil)) != types.DeriveSha(arblock.Transactions(), trie.NewStackTrie(nil)) { t.Errorf("block #%d [%x]: transactions mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Transactions(), anblock.Transactions(), arblock.Transactions()) - } else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) || types.CalcUncleHash(anblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) { - t.Errorf("block #%d [%x]: uncles mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Uncles(), anblock, arblock.Uncles()) }   // Check receipts. @@ -734,16 +724,12 @@ var ( gendb = rawdb.NewMemoryDatabase() key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) - funds = big.NewInt(1000000000000000) - gspec = &Genesis{ - Config: params.TestChainConfig, - Alloc: GenesisAlloc{address: {Balance: funds}}, - BaseFee: big.NewInt(params.InitialBaseFee), - } + funds = big.NewInt(1000000000) + gspec = &Genesis{Config: params.IstanbulTestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}} genesis = gspec.MustCommit(gendb) ) height := uint64(1024) - blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), nil) + blocks, receipts := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), gendb, int(height), nil)   // makeDb creates a db instance for testing. makeDb := func() (ethdb.Database, func()) { @@ -783,7 +769,7 @@ archiveCaching := *defaultCacheConfig archiveCaching.TrieDirtyDisabled = true   - archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) if n, err := archive.InsertChain(blocks); err != nil { t.Fatalf("failed to process block %d: %v", n, err) } @@ -796,14 +782,14 @@ // Import the chain as a non-archive node and ensure all pointers are updated fastDb, delfn := makeDb() defer delfn() - fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + fast, _ := NewBlockChain(fastDb, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer fast.Stop()   headers := make([]*types.Header, len(blocks)) for i, block := range blocks { headers[i] = block.Header() } - if n, err := fast.InsertHeaderChain(headers, 1); err != nil { + if n, err := fast.InsertHeaderChain(headers, 1, true); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) } if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil { @@ -816,10 +802,10 @@ // Import the chain as a ancient-first node and ensure all pointers are updated ancientDb, delfn := makeDb() defer delfn() - ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer ancient.Stop()   - if n, err := ancient.InsertHeaderChain(headers, 1); err != nil { + if n, err := ancient.InsertHeaderChain(headers, 1, true); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) } if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil { @@ -835,8 +821,8 @@ } // Import the chain as a light node and ensure all pointers are updated lightDb, delfn := makeDb() defer delfn() - light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) - if n, err := light.InsertHeaderChain(headers, 1); err != nil { + light, _ := NewBlockChain(lightDb, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) + if n, err := light.InsertHeaderChain(headers, 1, true); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) } defer light.Stop() @@ -857,8 +843,7 @@ addr2 = crypto.PubkeyToAddress(key2.PublicKey) addr3 = crypto.PubkeyToAddress(key3.PublicKey) db = rawdb.NewMemoryDatabase() gspec = &Genesis{ - Config: params.TestChainConfig, - GasLimit: 3141592, + Config: params.IstanbulTestChainConfig, Alloc: GenesisAlloc{ addr1: {Balance: big.NewInt(1000000000000000)}, addr2: {Balance: big.NewInt(1000000000000000)}, @@ -872,8 +857,8 @@ // Create two transactions shared between the chains: // - postponed: transaction included at a later block in the forked chain // - swapped: transaction included at the same block number in the forked chain - postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, key1) - swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, key1) + postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key1) + swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key1)   // Create two transactions that will be dropped by the forked chain: // - pastDrop: transaction dropped retroactively from a past block @@ -886,16 +871,15 @@ // - freshAdd: transaction added at the exact block the reorg is detected // - futureAdd: transaction added after the reorg has already finished var pastAdd, freshAdd, futureAdd *types.Transaction   - chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) { + chain, _ := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), db, 3, func(i int, gen *BlockGen) { switch i { case 0: - pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key2) - + pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key2) gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork   case 2: - freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key2) + freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key2)   gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point gen.AddTx(swapped) // This transaction will be swapped out at the exact height @@ -904,28 +888,28 @@ gen.OffsetTime(9) // Lower the block difficulty to simulate a weaker chain } }) // Import the chain. This runs all block validation rules. - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) if i, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert original chain[%d]: %v", i, err) } defer blockchain.Stop()   // overwrite the old chain - chain, _ = GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) { + chain, _ = GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), db, 5, func(i int, gen *BlockGen) { switch i { case 0: - pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3) + pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key3) gen.AddTx(pastAdd) // This transaction needs to be injected during reorg   case 2: gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain   - freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3) + freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key3) gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time   case 3: - futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3) + futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key3) gen.AddTx(futureAdd) // This transaction will be added after a full reorg } }) @@ -969,19 +953,20 @@ addr1 = crypto.PubkeyToAddress(key1.PublicKey) db = rawdb.NewMemoryDatabase() // this code generates a log code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") - gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}} + gspec = &Genesis{Config: params.IstanbulTestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} genesis = gspec.MustCommit(db) signer = types.LatestSigner(gspec.Config) )   - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer blockchain.Stop()   - rmLogsCh := make(chan RemovedLogsEvent) + rmLogsCh := make(chan RemovedLogsEvent, 100) blockchain.SubscribeRemovedLogsEvent(rmLogsCh) - chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) { + chain, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), db, 2, func(i int, gen *BlockGen) { if i == 1 { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, code), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil, nil, nil, code), signer, key1) + if err != nil { t.Fatalf("failed to create tx: %v", err) } @@ -992,22 +977,17 @@ if _, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert chain: %v", err) }   - chain, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {}) - done := make(chan struct{}) - go func() { - ev := <-rmLogsCh - if len(ev.Logs) == 0 { - t.Error("expected logs") - } - close(done) - }() + chain, _ = GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), db, 3, func(i int, gen *BlockGen) {}) if _, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert forked chain: %v", err) } timeout := time.NewTimer(1 * time.Second) defer timeout.Stop() select { - case <-done: + case ev := <-rmLogsCh: + if len(ev.Logs) == 0 { + t.Error("expected logs") + } case <-timeout.C: t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.") } @@ -1023,10 +1003,10 @@ var ( key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr1 = crypto.PubkeyToAddress(key1.PublicKey) db = rawdb.NewMemoryDatabase() - gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}} + gspec = &Genesis{Config: params.IstanbulTestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} genesis = gspec.MustCommit(db) - signer = types.LatestSigner(gspec.Config) - engine = ethash.NewFaker() + signer = types.NewEIP155Signer(gspec.Config.ChainID) + engine = mockEngine.NewFaker() blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil) )   @@ -1039,9 +1019,9 @@ blockchain.SubscribeLogsEvent(newLogCh) blockchain.SubscribeRemovedLogsEvent(rmLogsCh)   // This chain contains a single log. - chain, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2, func(i int, gen *BlockGen) { + chain, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, engine, db, 2, func(i int, gen *BlockGen) { if i == 1 { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, logCode), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil, nil, nil, logCode), signer, key1) if err != nil { t.Fatalf("failed to create tx: %v", err) } @@ -1055,9 +1035,9 @@ checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)   // Generate long reorg chain containing another log. Inserting the // chain removes one log and adds one. - forkChain, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2, func(i int, gen *BlockGen) { + forkChain, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, engine, db, 3, func(i int, gen *BlockGen) { if i == 1 { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, logCode), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil, nil, nil, logCode), signer, key1) if err != nil { t.Fatalf("failed to create tx: %v", err) } @@ -1073,7 +1053,7 @@ // This chain segment is rooted in the original chain, but doesn't contain any logs. // When inserting it, the canonical chain switches away from forkChain and re-emits // the log event for the old chain, as well as a RemovedLogsEvent for forkChain. - newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-1], engine, db, 1, func(i int, gen *BlockGen) {}) + newBlocks, _ := GenerateChain(params.IstanbulTestChainConfig, chain[len(chain)-1], engine, db, 2, func(i int, gen *BlockGen) {}) if _, err := blockchain.InsertChain(newBlocks); err != nil { t.Fatalf("failed to insert forked chain: %v", err) } @@ -1087,10 +1067,10 @@ var ( key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr1 = crypto.PubkeyToAddress(key1.PublicKey) db = rawdb.NewMemoryDatabase() - gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}} + gspec = &Genesis{Config: params.IstanbulTestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} genesis = gspec.MustCommit(db) - signer = types.LatestSigner(gspec.Config) - blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + signer = types.NewEIP155Signer(gspec.Config.ChainID) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) )   defer blockchain.Stop() @@ -1100,7 +1080,7 @@ rmLogsCh := make(chan RemovedLogsEvent, 10) blockchain.SubscribeLogsEvent(newLogCh) blockchain.SubscribeRemovedLogsEvent(rmLogsCh)   - chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) { + chain, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), db, 3, func(i int, gen *BlockGen) { if i == 1 { gen.OffsetTime(-9) // higher block difficulty   @@ -1112,9 +1092,10 @@ } checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)   // Generate side chain with lower difficulty - sideChain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) { + sideChain, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), db, 2, func(i int, gen *BlockGen) { if i == 1 { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, logCode), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil, nil, nil, logCode), signer, key1) + if err != nil { t.Fatalf("failed to create tx: %v", err) } @@ -1126,8 +1107,8 @@ t.Fatalf("failed to insert forked chain: %v", err) } checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)   - // Generate a new block based on side chain. - newBlocks, _ := GenerateChain(params.TestChainConfig, sideChain[len(sideChain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) + // Generate two new blocks based on side chain, to trigger a reorg + newBlocks, _ := GenerateChain(params.IstanbulTestChainConfig, sideChain[len(sideChain)-1], mockEngine.NewFaker(), db, 2, func(i int, gen *BlockGen) {}) if _, err := blockchain.InsertChain(newBlocks); err != nil { t.Fatalf("failed to insert forked chain: %v", err) } @@ -1158,23 +1139,23 @@ db = rawdb.NewMemoryDatabase() key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr1 = crypto.PubkeyToAddress(key1.PublicKey) gspec = &Genesis{ - Config: params.TestChainConfig, - Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}, + Config: params.IstanbulTestChainConfig, + Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}, } genesis = gspec.MustCommit(db) signer = types.LatestSigner(gspec.Config) )   - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer blockchain.Stop()   - chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {}) + chain, _ := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), db, 3, func(i int, gen *BlockGen) {}) if _, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert chain: %v", err) }   - replacementBlocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, gen *BlockGen) { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, nil), signer, key1) + replacementBlocks, _ := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), db, 4, func(i int, gen *BlockGen) { + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil, nil, nil, nil), signer, key1) if i == 2 { gen.OffsetTime(-9) } @@ -1192,9 +1173,13 @@ // first two block of the secondary chain are for a brief moment considered // side chains because up to that point the first one is considered the // heavier chain. + // the third may or may not be, depending on whether it triggers a reorg (the + // difficulties of the two chains are equal at this time). + // the boolean value indicates whether we are still waiting for that block's event. expectedSideHashes := map[common.Hash]bool{ replacementBlocks[0].Hash(): true, replacementBlocks[1].Hash(): true, + replacementBlocks[2].Hash(): false, // may not be sent (if reorg was on the 3rd block) chain[0].Hash(): true, chain[1].Hash(): true, chain[2].Hash(): true, @@ -1212,9 +1197,16 @@ block := ev.Block if _, ok := expectedSideHashes[block.Hash()]; !ok { t.Errorf("%d: didn't expect %x to be in side chain", i, block.Hash()) } + expectedSideHashes[block.Hash()] = false i++   - if i == len(expectedSideHashes) { + numLeft := 0 + for _, isLeft := range expectedSideHashes { + if isLeft { + numLeft += 1 + } + } + if numLeft == 0 { timeout.Stop()   break done @@ -1237,13 +1229,13 @@ }   // Tests if the canonical block can be fetched from the database during chain insertion. func TestCanonicalBlockRetrieval(t *testing.T) { - _, blockchain, err := newCanonical(ethash.NewFaker(), 0, true) + _, blockchain, err := newCanonical(mockEngine.NewFaker(), 0, true) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } defer blockchain.Stop()   - chain, _ := GenerateChain(blockchain.chainConfig, blockchain.genesisBlock, ethash.NewFaker(), blockchain.db, 10, func(i int, gen *BlockGen) {}) + chain, _ := GenerateChain(blockchain.chainConfig, blockchain.genesisBlock, mockEngine.NewFaker(), blockchain.db, 10, func(i int, gen *BlockGen) {})   var pend sync.WaitGroup pend.Add(len(chain)) @@ -1297,15 +1289,15 @@ } genesis = gspec.MustCommit(db) )   - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer blockchain.Stop()   - blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) { + blocks, _ := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), db, 4, func(i int, block *BlockGen) { var ( tx *types.Transaction err error basicTx = func(signer types.Signer) (*types.Transaction, error) { - return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key) + return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil, nil, nil, nil), signer, key) } ) switch i { @@ -1363,12 +1355,12 @@ }   // generate an invalid chain id transaction config := &params.ChainConfig{ChainID: big.NewInt(2), EIP150Block: big.NewInt(0), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)} - blocks, _ = GenerateChain(config, blocks[len(blocks)-1], ethash.NewFaker(), db, 4, func(i int, block *BlockGen) { + blocks, _ = GenerateChain(config, blocks[len(blocks)-1], mockEngine.NewFaker(), db, 4, func(i int, block *BlockGen) { var ( tx *types.Transaction err error basicTx = func(signer types.Signer) (*types.Transaction, error) { - return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key) + return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil, nil, nil, nil), signer, key) } ) if i == 0 { @@ -1405,10 +1397,10 @@ Alloc: GenesisAlloc{address: {Balance: funds}}, } genesis = gspec.MustCommit(db) ) - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer blockchain.Stop()   - blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) { + blocks, _ := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), db, 3, func(i int, block *BlockGen) { var ( tx *types.Transaction err error @@ -1416,11 +1408,11 @@ signer = types.LatestSigner(gspec.Config) ) switch i { case 0: - tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key) + tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil, nil, nil, nil), signer, key) case 1: - tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key) + tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil, nil, nil, nil), signer, key) case 2: - tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key) + tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil, nil, nil, nil), signer, key) } if err != nil { t.Fatal(err) @@ -1459,11 +1451,11 @@ // // https://github.com/ethereum/go-ethereum/pull/15941 func TestBlockchainHeaderchainReorgConsistency(t *testing.T) { // Generate a canonical chain to act as the main dataset - engine := ethash.NewFaker() + engine := mockEngine.NewFaker()   db := rawdb.NewMemoryDatabase() - genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) + genesis := new(Genesis).MustCommit(db) + blocks, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })   // Generate a bunch of fork blocks, each side forking from the canonical chain forks := make([]*types.Block, len(blocks)) @@ -1472,15 +1464,15 @@ parent := genesis if i > 0 { parent = blocks[i-1] } - fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) + fork, _ := GenerateChain(params.IstanbulTestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) forks[i] = fork[0] } // Import the canonical and fork chain side by side, verifying the current block // and current header consistency diskdb := rawdb.NewMemoryDatabase() - (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) + new(Genesis).MustCommit(diskdb)   - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -1504,11 +1496,11 @@ // Tests that importing small side forks doesn't leave junk in the trie database // cache (which would eventually cause memory issues). func TestTrieForkGC(t *testing.T) { // Generate a canonical chain to act as the main dataset - engine := ethash.NewFaker() + engine := mockEngine.NewFaker()   db := rawdb.NewMemoryDatabase() - genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) + genesis := new(Genesis).MustCommit(db) + blocks, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })   // Generate a bunch of fork blocks, each side forking from the canonical chain forks := make([]*types.Block, len(blocks)) @@ -1517,14 +1509,14 @@ parent := genesis if i > 0 { parent = blocks[i-1] } - fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) + fork, _ := GenerateChain(params.IstanbulTestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) forks[i] = fork[0] } // Import the canonical and fork chain side by side, forcing the trie cache to cache both diskdb := rawdb.NewMemoryDatabase() - (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) + new(Genesis).MustCommit(diskdb)   - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -1550,20 +1542,20 @@ // Tests that doing large reorgs works even if the state associated with the // forking point is not available any more. func TestLargeReorgTrieGC(t *testing.T) { // Generate the original common chain segment and the two competing forks - engine := ethash.NewFaker() + engine := mockEngine.NewFaker()   db := rawdb.NewMemoryDatabase() - genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) + genesis := new(Genesis).MustCommit(db)   - shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) - original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) - competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) }) + shared, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) + original, _ := GenerateChain(params.IstanbulTestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) + competitor, _ := GenerateChain(params.IstanbulTestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })   // Import the shared chain and the original canonical one diskdb := rawdb.NewMemoryDatabase() - (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) + new(Genesis).MustCommit(diskdb)   - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -1606,11 +1598,11 @@ gendb = rawdb.NewMemoryDatabase() key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(1000000000) - gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}} + gspec = &Genesis{Config: params.IstanbulTestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}} genesis = gspec.MustCommit(gendb) ) height := uint64(1024) - blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), nil) + blocks, receipts := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), gendb, int(height), nil)   // Import the chain as a ancient-first node and ensure all pointers are updated frdir, err := ioutil.TempDir("", "") @@ -1624,13 +1616,13 @@ if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } gspec.MustCommit(ancientDb) - ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil)   headers := make([]*types.Header, len(blocks)) for i, block := range blocks { headers[i] = block.Header() } - if n, err := ancient.InsertHeaderChain(headers, 1); err != nil { + if n, err := ancient.InsertHeaderChain(headers, 1, true); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) } if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil { @@ -1644,7 +1636,7 @@ midBlock := blocks[len(blocks)/2] rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash())   // Reopen broken blockchain again - ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer ancient.Stop() if num := ancient.CurrentBlock().NumberU64(); num != 0 { t.Errorf("head block mismatch: have #%v, want #%v", num, 0) @@ -1657,82 +1649,6 @@ t.Errorf("head header mismatch: have #%v, want #%v", num, midBlock.NumberU64()) } }   -// This test checks that InsertReceiptChain will roll back correctly when attempting to insert a side chain. -func TestInsertReceiptChainRollback(t *testing.T) { - // Generate forked chain. The returned BlockChain object is used to process the side chain blocks. - tmpChain, sideblocks, canonblocks, err := getLongAndShortChains() - if err != nil { - t.Fatal(err) - } - defer tmpChain.Stop() - // Get the side chain receipts. - if _, err := tmpChain.InsertChain(sideblocks); err != nil { - t.Fatal("processing side chain failed:", err) - } - t.Log("sidechain head:", tmpChain.CurrentBlock().Number(), tmpChain.CurrentBlock().Hash()) - sidechainReceipts := make([]types.Receipts, len(sideblocks)) - for i, block := range sideblocks { - sidechainReceipts[i] = tmpChain.GetReceiptsByHash(block.Hash()) - } - // Get the canon chain receipts. - if _, err := tmpChain.InsertChain(canonblocks); err != nil { - t.Fatal("processing canon chain failed:", err) - } - t.Log("canon head:", tmpChain.CurrentBlock().Number(), tmpChain.CurrentBlock().Hash()) - canonReceipts := make([]types.Receipts, len(canonblocks)) - for i, block := range canonblocks { - canonReceipts[i] = tmpChain.GetReceiptsByHash(block.Hash()) - } - - // Set up a BlockChain that uses the ancient store. - frdir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatalf("failed to create temp freezer dir: %v", err) - } - defer os.Remove(frdir) - ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false) - if err != nil { - t.Fatalf("failed to create temp freezer db: %v", err) - } - gspec := Genesis{Config: params.AllEthashProtocolChanges} - gspec.MustCommit(ancientDb) - ancientChain, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) - defer ancientChain.Stop() - - // Import the canonical header chain. - canonHeaders := make([]*types.Header, len(canonblocks)) - for i, block := range canonblocks { - canonHeaders[i] = block.Header() - } - if _, err = ancientChain.InsertHeaderChain(canonHeaders, 1); err != nil { - t.Fatal("can't import canon headers:", err) - } - - // Try to insert blocks/receipts of the side chain. - _, err = ancientChain.InsertReceiptChain(sideblocks, sidechainReceipts, uint64(len(sideblocks))) - if err == nil { - t.Fatal("expected error from InsertReceiptChain.") - } - if ancientChain.CurrentFastBlock().NumberU64() != 0 { - t.Fatalf("failed to rollback ancient data, want %d, have %d", 0, ancientChain.CurrentFastBlock().NumberU64()) - } - if frozen, err := ancientChain.db.Ancients(); err != nil || frozen != 1 { - t.Fatalf("failed to truncate ancient data, frozen index is %d", frozen) - } - - // Insert blocks/receipts of the canonical chain. - _, err = ancientChain.InsertReceiptChain(canonblocks, canonReceipts, uint64(len(canonblocks))) - if err != nil { - t.Fatalf("can't import canon chain receipts: %v", err) - } - if ancientChain.CurrentFastBlock().NumberU64() != canonblocks[len(canonblocks)-1].NumberU64() { - t.Fatalf("failed to insert ancient recept chain after rollback") - } - if frozen, _ := ancientChain.db.Ancients(); frozen != uint64(len(canonblocks))+1 { - t.Fatalf("wrong ancients count %d", frozen) - } -} - // Tests that importing a very large side fork, which is larger than the canon chain, // but where the difficulty per block is kept low: this means that it will not // overtake the 'canon' chain until after it's passed canon by about 200 blocks. @@ -1742,22 +1658,22 @@ // - https://github.com/ethereum/go-ethereum/issues/18977 // - https://github.com/ethereum/go-ethereum/pull/18988 func TestLowDiffLongChain(t *testing.T) { // Generate a canonical chain to act as the main dataset - engine := ethash.NewFaker() + engine := mockEngine.NewFaker() db := rawdb.NewMemoryDatabase() - genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) + genesis := new(Genesis).MustCommit(db)   // We must use a pretty long chain to ensure that the fork doesn't overtake us // until after at least 128 blocks post tip - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*TriesInMemory, func(i int, b *BlockGen) { + blocks, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, engine, db, 6*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) b.OffsetTime(-9) })   // Import the canonical chain diskdb := rawdb.NewMemoryDatabase() - (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) + new(Genesis).MustCommit(diskdb)   - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -1766,7 +1682,7 @@ t.Fatalf("block %d: failed to insert into chain: %v", n, err) } // Generate fork chain, starting from an early block parent := blocks[10] - fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*TriesInMemory, func(i int, b *BlockGen) { + fork, _ := GenerateChain(params.IstanbulTestChainConfig, parent, engine, db, 8*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })   @@ -1796,14 +1712,14 @@ // - The sidechain S is prepended with numCanonBlocksInSidechain blocks from the canon chain func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommonAncestorAndPruneblock int) {   // Generate a canonical chain to act as the main dataset - engine := ethash.NewFaker() + engine := mockEngine.NewFaker() db := rawdb.NewMemoryDatabase() - genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) + genesis := new(Genesis).MustCommit(db)   // Generate and import the canonical chain blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil) diskdb := rawdb.NewMemoryDatabase() - (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) + new(Genesis).MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -1875,18 +1791,17 @@ func TestInsertKnownReceiptChain(t *testing.T) { testInsertKnownChainData(t, "receipts") } func TestInsertKnownBlocks(t *testing.T) { testInsertKnownChainData(t, "blocks") }   func testInsertKnownChainData(t *testing.T, typ string) { - engine := ethash.NewFaker() + engine := mockEngine.NewFaker()   db := rawdb.NewMemoryDatabase() - genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) + genesis := new(Genesis).MustCommit(db)   - blocks, receipts := GenerateChain(params.TestChainConfig, genesis, engine, db, 32, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) - // A longer chain but total difficulty is lower. - blocks2, receipts2 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, db, 65, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) - // A shorter chain but total difficulty is higher. - blocks3, receipts3 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, db, 64, func(i int, b *BlockGen) { + blocks, receipts := GenerateChain(params.IstanbulTestChainConfig, genesis, engine, db, 32, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) + blocks2, receipts2 := GenerateChain(params.IstanbulTestChainConfig, blocks[len(blocks)-1], engine, db, 65, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) + // Total difficulty is higher. + blocks3, receipts3 := GenerateChain(params.IstanbulTestChainConfig, blocks[len(blocks)-1], engine, db, 66, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) - b.OffsetTime(-9) // A higher difficulty + b.OffsetTime(-9) }) // Import the shared chain and the original canonical one dir, err := ioutil.TempDir("", "") @@ -1898,10 +1813,10 @@ chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), dir, "", false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } - (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb) + new(Genesis).MustCommit(chaindb) defer os.RemoveAll(dir)   - chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(chaindb, nil, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -1916,7 +1831,7 @@ headers := make([]*types.Header, 0, len(blocks)) for _, block := range blocks { headers = append(headers, block.Header()) } - _, err := chain.InsertHeaderChain(headers, 1) + _, err := chain.InsertHeaderChain(headers, 1, true) return err } asserter = func(t *testing.T, block *types.Block) { @@ -1930,7 +1845,7 @@ headers := make([]*types.Header, 0, len(blocks)) for _, block := range blocks { headers = append(headers, block.Header()) } - _, err := chain.InsertHeaderChain(headers, 1) + _, err := chain.InsertHeaderChain(headers, 1, true) if err != nil { return err } @@ -1995,128 +1910,6 @@ } asserter(t, blocks2[len(blocks2)-1]) }   -// getLongAndShortChains returns two chains: A is longer, B is heavier. -func getLongAndShortChains() (bc *BlockChain, longChain []*types.Block, heavyChain []*types.Block, err error) { - // Generate a canonical chain to act as the main dataset - engine := ethash.NewFaker() - db := rawdb.NewMemoryDatabase() - genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) - - // Generate and import the canonical chain, - // Offset the time, to keep the difficulty low - longChain, _ = GenerateChain(params.TestChainConfig, genesis, engine, db, 80, func(i int, b *BlockGen) { - b.SetCoinbase(common.Address{1}) - }) - diskdb := rawdb.NewMemoryDatabase() - (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) - - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) - if err != nil { - return nil, nil, nil, fmt.Errorf("failed to create tester chain: %v", err) - } - - // Generate fork chain, make it shorter than canon, with common ancestor pretty early - parentIndex := 3 - parent := longChain[parentIndex] - heavyChainExt, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 75, func(i int, b *BlockGen) { - b.SetCoinbase(common.Address{2}) - b.OffsetTime(-9) - }) - heavyChain = append(heavyChain, longChain[:parentIndex+1]...) - heavyChain = append(heavyChain, heavyChainExt...) - - // Verify that the test is sane - var ( - longerTd = new(big.Int) - shorterTd = new(big.Int) - ) - for index, b := range longChain { - longerTd.Add(longerTd, b.Difficulty()) - if index <= parentIndex { - shorterTd.Add(shorterTd, b.Difficulty()) - } - } - for _, b := range heavyChain { - shorterTd.Add(shorterTd, b.Difficulty()) - } - if shorterTd.Cmp(longerTd) <= 0 { - return nil, nil, nil, fmt.Errorf("Test is moot, heavyChain td (%v) must be larger than canon td (%v)", shorterTd, longerTd) - } - longerNum := longChain[len(longChain)-1].NumberU64() - shorterNum := heavyChain[len(heavyChain)-1].NumberU64() - if shorterNum >= longerNum { - return nil, nil, nil, fmt.Errorf("Test is moot, heavyChain num (%v) must be lower than canon num (%v)", shorterNum, longerNum) - } - return chain, longChain, heavyChain, nil -} - -// TestReorgToShorterRemovesCanonMapping tests that if we -// 1. Have a chain [0 ... N .. X] -// 2. Reorg to shorter but heavier chain [0 ... N ... Y] -// 3. Then there should be no canon mapping for the block at height X -func TestReorgToShorterRemovesCanonMapping(t *testing.T) { - chain, canonblocks, sideblocks, err := getLongAndShortChains() - if err != nil { - t.Fatal(err) - } - if n, err := chain.InsertChain(canonblocks); err != nil { - t.Fatalf("block %d: failed to insert into chain: %v", n, err) - } - canonNum := chain.CurrentBlock().NumberU64() - _, err = chain.InsertChain(sideblocks) - if err != nil { - t.Errorf("Got error, %v", err) - } - head := chain.CurrentBlock() - if got := sideblocks[len(sideblocks)-1].Hash(); got != head.Hash() { - t.Fatalf("head wrong, expected %x got %x", head.Hash(), got) - } - // We have now inserted a sidechain. - if blockByNum := chain.GetBlockByNumber(canonNum); blockByNum != nil { - t.Errorf("expected block to be gone: %v", blockByNum.NumberU64()) - } - if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil { - t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64()) - } -} - -// TestReorgToShorterRemovesCanonMappingHeaderChain is the same scenario -// as TestReorgToShorterRemovesCanonMapping, but applied on headerchain -// imports -- that is, for fast sync -func TestReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T) { - chain, canonblocks, sideblocks, err := getLongAndShortChains() - if err != nil { - t.Fatal(err) - } - // Convert into headers - canonHeaders := make([]*types.Header, len(canonblocks)) - for i, block := range canonblocks { - canonHeaders[i] = block.Header() - } - if n, err := chain.InsertHeaderChain(canonHeaders, 0); err != nil { - t.Fatalf("header %d: failed to insert into chain: %v", n, err) - } - canonNum := chain.CurrentHeader().Number.Uint64() - sideHeaders := make([]*types.Header, len(sideblocks)) - for i, block := range sideblocks { - sideHeaders[i] = block.Header() - } - if n, err := chain.InsertHeaderChain(sideHeaders, 0); err != nil { - t.Fatalf("header %d: failed to insert into chain: %v", n, err) - } - head := chain.CurrentHeader() - if got := sideblocks[len(sideblocks)-1].Hash(); got != head.Hash() { - t.Fatalf("head wrong, expected %x got %x", head.Hash(), got) - } - // We have now inserted a sidechain. - if blockByNum := chain.GetBlockByNumber(canonNum); blockByNum != nil { - t.Errorf("expected block to be gone: %v", blockByNum.NumberU64()) - } - if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil { - t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64()) - } -} - func TestTransactionIndices(t *testing.T) { // Configure and generate a sample block chain var ( @@ -2127,20 +1920,20 @@ funds = big.NewInt(100000000000000000) gspec = &Genesis{ Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}, - BaseFee: big.NewInt(params.InitialBaseFee), } genesis = gspec.MustCommit(gendb) signer = types.LatestSigner(gspec.Config) ) height := uint64(128) - blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), func(i int, block *BlockGen) { - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key) + blocks, receipts := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), gendb, int(height), func(i int, block *BlockGen) { + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key) + if err != nil { panic(err) } block.AddTx(tx) }) - blocks2, _ := GenerateChain(gspec.Config, blocks[len(blocks)-1], ethash.NewFaker(), gendb, 10, nil) + blocks2, _ := GenerateChain(gspec.Config, blocks[len(blocks)-1], mockEngine.NewFaker(), gendb, 10, nil)   check := func(tail *uint64, chain *BlockChain) { stored := rawdb.ReadTxIndexTail(chain.db) @@ -2188,7 +1981,7 @@ gspec.MustCommit(ancientDb)   // Import all blocks into ancient db l := uint64(0) - chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l) + chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, mockEngine.NewFaker(), vm.Config{}, nil, &l) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2196,7 +1989,7 @@ headers := make([]*types.Header, len(blocks)) for i, block := range blocks { headers[i] = block.Header() } - if n, err := chain.InsertHeaderChain(headers, 0); err != nil { + if n, err := chain.InsertHeaderChain(headers, 0, true); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) } if n, err := chain.InsertReceiptChain(blocks, receipts, 128); err != nil { @@ -2213,7 +2006,7 @@ if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } gspec.MustCommit(ancientDb) - chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l) + chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, mockEngine.NewFaker(), vm.Config{}, nil, &l) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2237,7 +2030,7 @@ limit = []uint64{0, 64 /* drop stale */, 32 /* shorten history */, 64 /* extend history */, 0 /* restore all */} tails := []uint64{0, 67 /* 130 - 64 + 1 */, 100 /* 131 - 32 + 1 */, 69 /* 132 - 64 + 1 */, 0} for i, l := range limit { - chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l) + chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, mockEngine.NewFaker(), vm.Config{}, nil, &l) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2260,8 +2053,8 @@ genesis = gspec.MustCommit(gendb) signer = types.LatestSigner(gspec.Config) ) height := uint64(128) - blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), func(i int, block *BlockGen) { - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key) + blocks, receipts := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), gendb, int(height), func(i int, block *BlockGen) { + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key) if err != nil { panic(err) } @@ -2315,7 +2108,7 @@ gspec.MustCommit(ancientDb)   // Import all blocks into ancient db, only HEAD-32 indices are kept. l := uint64(32) - chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l) + chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, mockEngine.NewFaker(), vm.Config{}, nil, &l) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2323,7 +2116,7 @@ headers := make([]*types.Header, len(blocks)) for i, block := range blocks { headers[i] = block.Header() } - if n, err := chain.InsertHeaderChain(headers, 0); err != nil { + if n, err := chain.InsertHeaderChain(headers, 0, true); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) } // The indices before ancient-N(32) should be ignored. After that all blocks should be indexed. @@ -2342,7 +2135,7 @@ testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) bankFunds = big.NewInt(100000000000000000) gspec = Genesis{ - Config: params.TestChainConfig, + Config: params.IstanbulTestChainConfig, Alloc: GenesisAlloc{ testBankAddress: {Balance: bankFunds}, common.HexToAddress("0xc0de"): { @@ -2350,11 +2143,10 @@ Code: []byte{0x60, 0x01, 0x50}, Balance: big.NewInt(0), }, // push 1, pop }, - GasLimit: 100e6, // 100 M } ) // Generate the original common chain segment and the two competing forks - engine := ethash.NewFaker() + engine := mockEngine.NewFaker() db := rawdb.NewMemoryDatabase() genesis := gspec.MustCommit(db)   @@ -2363,7 +2155,8 @@ block.SetCoinbase(common.Address{1}) for txi := 0; txi < numTxs; txi++ { uniq := uint64(i*numTxs + txi) recipient := recipientFn(uniq) - tx, err := types.SignTx(types.NewTransaction(uniq, recipient, big.NewInt(1), params.TxGas, big.NewInt(1), nil), signer, testBankKey) + //recipient := common.BigToAddress(big.NewInt(0).SetUint64(1337 + uniq)) + tx, err := types.SignTx(types.NewTransaction(uniq, recipient, big.NewInt(1), params.TxGas, big.NewInt(1), nil, nil, nil, nil), signer, testBankKey) if err != nil { b.Error(err) } @@ -2371,7 +2164,7 @@ block.AddTx(tx) } }   - shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, numBlocks, blockGenerator) + shared, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, engine, db, numBlocks, blockGenerator) b.StopTimer() b.ResetTimer() for i := 0; i < b.N; i++ { @@ -2379,7 +2172,7 @@ // Import the shared chain and the original canonical one diskdb := rawdb.NewMemoryDatabase() gspec.MustCommit(diskdb)   - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { b.Fatalf("failed to create tester chain: %v", err) } @@ -2452,16 +2245,18 @@ // 1. Downloader rollbacks a batch of inserted blocks and exits // 2. Downloader starts to sync again // 3. The blocks fetched are all known and canonical blocks func TestSideImportPrunedBlocks(t *testing.T) { + //t.Skip("disabled temporarily, do not merge.") // Generate a canonical chain to act as the main dataset - engine := ethash.NewFaker() + engine := mockEngine.NewFaker() db := rawdb.NewMemoryDatabase() - genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) + genesis := new(Genesis).MustCommit(db)   // Generate and import the canonical chain - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil) + blocks, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, engine, db, 2*TriesInMemory, nil) diskdb := rawdb.NewMemoryDatabase() - (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + new(Genesis).MustCommit(diskdb) + chain, err := NewBlockChain(diskdb, nil, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) + if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2502,7 +2297,7 @@ var ( aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb") // Generate a canonical chain to act as the main dataset - engine = ethash.NewFaker() + engine = mockEngine.NewFaker() db = rawdb.NewMemoryDatabase()   // A sender who makes transactions, has some funds @@ -2510,7 +2305,7 @@ key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(100000000000000000) gspec = &Genesis{ - Config: params.TestChainConfig, + Config: params.IstanbulTestChainConfig, Alloc: GenesisAlloc{ address: {Balance: funds}, // The address 0xAAAAA selfdestructs if called @@ -2540,22 +2335,22 @@ } genesis = gspec.MustCommit(db) )   - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 1, func(i int, b *BlockGen) { + blocks, _ := GenerateChain(params.IstanbulTestChainConfig, genesis, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) // One transaction to AAAA tx, _ := types.SignTx(types.NewTransaction(0, aa, - big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key) + big.NewInt(0), 50000, big.NewInt(1), nil, nil, nil, nil), types.HomesteadSigner{}, key) b.AddTx(tx) // One transaction to BBBB tx, _ = types.SignTx(types.NewTransaction(1, bb, - big.NewInt(0), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key) + big.NewInt(0), 100000, big.NewInt(1), nil, nil, nil, nil), types.HomesteadSigner{}, key) b.AddTx(tx) }) // Import the canonical chain diskdb := rawdb.NewMemoryDatabase() gspec.MustCommit(diskdb)   - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2574,7 +2369,7 @@ // and then the new slots exist func TestDeleteRecreateSlots(t *testing.T) { var ( // Generate a canonical chain to act as the main dataset - engine = ethash.NewFaker() + engine = mockEngine.NewFaker() db = rawdb.NewMemoryDatabase() // A sender who makes transactions, has some funds key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") @@ -2656,11 +2451,12 @@ blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) // One transaction to AA, to kill it tx, _ := types.SignTx(types.NewTransaction(0, aa, - big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key) + big.NewInt(0), 50000, big.NewInt(1), nil, nil, nil, nil), types.HomesteadSigner{}, key) + b.AddTx(tx) // One transaction to BB, to recreate AA tx, _ = types.SignTx(types.NewTransaction(1, bb, - big.NewInt(0), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key) + big.NewInt(0), 100000, big.NewInt(1), nil, nil, nil, nil), types.HomesteadSigner{}, key) b.AddTx(tx) }) // Import the canonical chain @@ -2701,7 +2497,7 @@ // Expected outcome is that _all_ slots are cleared from A func TestDeleteRecreateAccount(t *testing.T) { var ( // Generate a canonical chain to act as the main dataset - engine = ethash.NewFaker() + engine = mockEngine.NewFaker() db = rawdb.NewMemoryDatabase() // A sender who makes transactions, has some funds key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") @@ -2736,11 +2532,12 @@ blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) // One transaction to AA, to kill it tx, _ := types.SignTx(types.NewTransaction(0, aa, - big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key) + big.NewInt(0), 50000, big.NewInt(1), nil, nil, nil, nil), types.HomesteadSigner{}, key) b.AddTx(tx) // One transaction to AA, to recreate it (but without storage tx, _ = types.SignTx(types.NewTransaction(1, aa, - big.NewInt(1), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key) + big.NewInt(1), 100000, big.NewInt(1), nil, nil, nil, nil), types.HomesteadSigner{}, key) + b.AddTx(tx) }) // Import the canonical chain @@ -2777,7 +2574,7 @@ // and then the new slots exist func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) { var ( // Generate a canonical chain to act as the main dataset - engine = ethash.NewFaker() + engine = mockEngine.NewFaker() db = rawdb.NewMemoryDatabase() // A sender who makes transactions, has some funds key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") @@ -2870,7 +2667,7 @@ } var expectations []*expectation var newDestruct = func(e *expectation, b *BlockGen) *types.Transaction { tx, _ := types.SignTx(types.NewTransaction(nonce, aa, - big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key) + big.NewInt(0), 50000, big.NewInt(1), nil, nil, nil, nil), types.HomesteadSigner{}, key) nonce++ if e.exist { e.exist = false @@ -2881,7 +2678,7 @@ return tx } var newResurrect = func(e *expectation, b *BlockGen) *types.Transaction { tx, _ := types.SignTx(types.NewTransaction(nonce, bb, - big.NewInt(0), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key) + big.NewInt(0), 100000, big.NewInt(1), nil, nil, nil, nil), types.HomesteadSigner{}, key) nonce++ if !e.exist { e.exist = true @@ -2979,7 +2776,7 @@ // func TestInitThenFailCreateContract(t *testing.T) { var ( // Generate a canonical chain to act as the main dataset - engine = ethash.NewFaker() + engine = mockEngine.NewFaker() db = rawdb.NewMemoryDatabase() // A sender who makes transactions, has some funds key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") @@ -3045,7 +2842,7 @@ blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 4, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) // One transaction to BB tx, _ := types.SignTx(types.NewTransaction(nonce, bb, - big.NewInt(0), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key) + big.NewInt(0), 100000, big.NewInt(1), nil, nil, nil, nil), types.HomesteadSigner{}, key) b.AddTx(tx) nonce++ }) @@ -3093,7 +2890,7 @@ var ( aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")   // Generate a canonical chain to act as the main dataset - engine = ethash.NewFaker() + engine = mockEngine.NewFaker() db = rawdb.NewMemoryDatabase()   // A sender who makes transactions, has some funds @@ -3101,7 +2898,7 @@ key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(1000000000000000) gspec = &Genesis{ - Config: params.TestChainConfig, + Config: params.IstanbulEHFTestChainConfig, Alloc: GenesisAlloc{ address: {Balance: funds}, // The address 0xAAAA sloads 0x00 and 0x01 @@ -3117,8 +2914,9 @@ Balance: big.NewInt(0), }, }, } - genesis = gspec.MustCommit(db) ) + gspec.Config.Faker = true + genesis := gspec.MustCommit(db)   blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) @@ -3130,7 +2928,7 @@ ChainID: gspec.Config.ChainID, Nonce: 0, To: &aa, Gas: 30000, - GasPrice: b.header.BaseFee, + GasPrice: MockSysContractCallCtx().GetGasPriceMinimum(nil), AccessList: types.AccessList{{ Address: aa, StorageKeys: []common.Hash{{0}}, @@ -3176,7 +2974,7 @@ var ( aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")   // Generate a canonical chain to act as the main dataset - engine = ethash.NewFaker() + engine = mockEngine.NewFaker() db = rawdb.NewMemoryDatabase()   // A sender who makes transactions, has some funds @@ -3186,7 +2984,7 @@ addr1 = crypto.PubkeyToAddress(key1.PublicKey) addr2 = crypto.PubkeyToAddress(key2.PublicKey) funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) gspec = &Genesis{ - Config: params.AllEthashProtocolChanges, + Config: params.IstanbulEHFTestChainConfig, Alloc: GenesisAlloc{ addr1: {Balance: funds}, addr2: {Balance: funds}, @@ -3201,17 +2999,29 @@ }, Nonce: 0, Balance: big.NewInt(0), }, + common.HexToAddress("0xce10"): { // Registry Proxy + Code: testutil.RegistryProxyOpcodes, + Storage: map[common.Hash]common.Hash{ + common.HexToHash("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"): common.HexToHash("0xce11"), // Registry Implementation + common.HexToHash("0x91646b8507bf2e54d7c3de9155442ba111546b81af1cbdd1f68eeb6926b98d58"): common.HexToHash("0xd023"), // Governance Proxy + }, + Balance: big.NewInt(0), + }, + common.HexToAddress("0xce11"): { // Registry Implementation + Code: testutil.RegistryOpcodes, + Balance: big.NewInt(0), + }, }, } )   - gspec.Config.BerlinBlock = common.Big0 - gspec.Config.LondonBlock = common.Big0 + gspec.Config.Faker = true genesis := gspec.MustCommit(db) signer := types.LatestSigner(gspec.Config) + txFeeRecipient := common.Address{10}   blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) { - b.SetCoinbase(common.Address{1}) + b.SetCoinbase(txFeeRecipient)   // One transaction to 0xAAAA accesses := types.AccessList{types.AccessTuple{ @@ -3239,6 +3049,7 @@ diskdb := rawdb.NewMemoryDatabase() gspec.MustCommit(diskdb)   chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil) + defer chain.Stop() if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -3258,20 +3069,19 @@ state, _ := chain.State()   // 3: Ensure that miner received only the tx's tip. - actual := state.GetBalance(block.Coinbase()) - expected := new(big.Int).Add( - new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()), - ethash.ConstantinopleBlockReward, - ) + actual := state.GetBalance(txFeeRecipient) + expected := new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64() + 1) // 1 is added by accumulateRewards in consensustest.MockEngine, and will break blockchain_repair_test.go if set 0 + if actual.Cmp(expected) != 0 { t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual) }   // 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee). + baseFee := MockSysContractCallCtx().GetGasPriceMinimum(nil).Uint64() actual = new(big.Int).Sub(funds, state.GetBalance(addr1)) - expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64())) + expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + baseFee)) if actual.Cmp(expected) != 0 { - t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) + t.Fatalf("sender paid fee incorrect: expected %d, got %d", expected, actual) }   blocks, _ = GenerateChain(gspec.Config, block, engine, db, 1, func(i int, b *BlockGen) { @@ -3295,22 +3105,19 @@ }   block = chain.GetBlockByNumber(2) state, _ = chain.State() - effectiveTip := block.Transactions()[0].GasTipCap().Uint64() - block.BaseFee().Uint64() + effectiveTip := block.Transactions()[0].GasTipCap().Uint64() - baseFee   // 6+5: Ensure that miner received only the tx's effective tip. actual = state.GetBalance(block.Coinbase()) - expected = new(big.Int).Add( - new(big.Int).SetUint64(block.GasUsed()*effectiveTip), - ethash.ConstantinopleBlockReward, - ) + expected = new(big.Int).SetUint64(block.GasUsed()*effectiveTip + 1) // 1 is added by accumulateRewards in consensustest.MockEngine, and will break blockchain_repair_test.go if set 0 if actual.Cmp(expected) != 0 { t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual) }   // 4: Ensure the tx sender paid for the gasUsed * (effectiveTip + block baseFee). actual = new(big.Int).Sub(funds, state.GetBalance(addr2)) - expected = new(big.Int).SetUint64(block.GasUsed() * (effectiveTip + block.BaseFee().Uint64())) + expected = new(big.Int).SetUint64(block.GasUsed() * (effectiveTip + baseFee)) if actual.Cmp(expected) != 0 { - t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) + t.Fatalf("sender paid fee incorrect: expected %d, got %d", expected, actual) } }
diff --git go-ethereum/core/genesis_test.go celo/core/genesis_test.go index c7762a60a32e97c800caf573b4d12004bc29a9e3..22ca8eef34dc45681632dd3fbc63ea23debb025e 100644 --- go-ethereum/core/genesis_test.go +++ celo/core/genesis_test.go @@ -23,51 +23,39 @@ "testing"   "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/contracts/config" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" )   -func TestDefaultGenesisBlock(t *testing.T) { - block := DefaultGenesisBlock().ToBlock(nil) +func TestMainnetGenesisBlock(t *testing.T) { + block := MainnetGenesisBlock().ToBlock(nil) if block.Hash() != params.MainnetGenesisHash { - t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainnetGenesisHash) - } - block = DefaultRopstenGenesisBlock().ToBlock(nil) - if block.Hash() != params.RopstenGenesisHash { - t.Errorf("wrong ropsten genesis hash, got %v, want %v", block.Hash(), params.RopstenGenesisHash) + t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash().Hex(), params.MainnetGenesisHash.Hex()) } - block = DefaultRinkebyGenesisBlock().ToBlock(nil) - if block.Hash() != params.RinkebyGenesisHash { - t.Errorf("wrong rinkeby genesis hash, got %v, want %v", block.Hash(), params.RinkebyGenesisHash) + block = DefaultBaklavaGenesisBlock().ToBlock(nil) + if block.Hash() != params.BaklavaGenesisHash { + t.Errorf("wrong baklava testnet genesis hash, got %v, want %v", block.Hash().Hex(), params.BaklavaGenesisHash.Hex()) } - block = DefaultGoerliGenesisBlock().ToBlock(nil) - if block.Hash() != params.GoerliGenesisHash { - t.Errorf("wrong goerli genesis hash, got %v, want %v", block.Hash(), params.GoerliGenesisHash) + block = DefaultAlfajoresGenesisBlock().ToBlock(nil) + if block.Hash() != params.AlfajoresGenesisHash { + t.Errorf("wrong alfajores testnet genesis hash, got %v, want %v", block.Hash().Hex(), params.AlfajoresGenesisHash.Hex()) } }   -func TestInvalidCliqueConfig(t *testing.T) { - block := DefaultGoerliGenesisBlock() - block.ExtraData = []byte{} - if _, err := block.Commit(nil); err == nil { - t.Fatal("Expected error on invalid clique config") +func TestSetupGenesis(t *testing.T) { + customghash := common.HexToHash("0xade49833713207ecf7d4807ca34b1246b014ef3992ec231deb1e0ee56289c1c8") + alloc := &GenesisAlloc{} + alloc.UnmarshalJSON([]byte(devAllocJSON)) + customg := Genesis{ + Config: &params.ChainConfig{HomesteadBlock: big.NewInt(3), Istanbul: &params.IstanbulConfig{}}, + Alloc: *alloc, } -} + oldcustomg := customg   -func TestSetupGenesis(t *testing.T) { - var ( - customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50") - customg = Genesis{ - Config: &params.ChainConfig{HomesteadBlock: big.NewInt(3)}, - Alloc: GenesisAlloc{ - {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, - }, - } - oldcustomg = customg - ) oldcustomg.Config = &params.ChainConfig{HomesteadBlock: big.NewInt(2)} tests := []struct { name string @@ -82,7 +70,7 @@ fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { return SetupGenesisBlock(db, new(Genesis)) }, wantErr: errGenesisNoConfig, - wantConfig: params.AllEthashProtocolChanges, + wantConfig: params.MainnetChainConfig, }, { name: "no block in DB, genesis == nil", @@ -95,7 +83,7 @@ }, { name: "mainnet block in DB, genesis == nil", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - DefaultGenesisBlock().MustCommit(db) + MainnetGenesisBlock().MustCommit(db) return SetupGenesisBlock(db, nil) }, wantHash: params.MainnetGenesisHash, @@ -111,14 +99,24 @@ wantHash: customghash, wantConfig: customg.Config, }, { - name: "custom block in DB, genesis == ropsten", + name: "custom block in DB, genesis == baklava", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { customg.MustCommit(db) - return SetupGenesisBlock(db, DefaultRopstenGenesisBlock()) + return SetupGenesisBlock(db, DefaultBaklavaGenesisBlock()) }, - wantErr: &GenesisMismatchError{Stored: customghash, New: params.RopstenGenesisHash}, - wantHash: params.RopstenGenesisHash, - wantConfig: params.RopstenChainConfig, + wantErr: &GenesisMismatchError{Stored: customghash, New: params.BaklavaGenesisHash}, + wantHash: params.BaklavaGenesisHash, + wantConfig: params.BaklavaChainConfig, + }, + { + name: "custom block in DB, genesis == alfajores", + fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + customg.MustCommit(db) + return SetupGenesisBlock(db, DefaultAlfajoresGenesisBlock()) + }, + wantErr: &GenesisMismatchError{Stored: customghash, New: params.AlfajoresGenesisHash}, + wantHash: params.AlfajoresGenesisHash, + wantConfig: params.AlfajoresChainConfig, }, { name: "compatible config in DB", @@ -136,10 +134,10 @@ // Commit the 'old' genesis block with Homestead transition at #2. // Advance to block #4, past the homestead transition block of customg. genesis := oldcustomg.MustCommit(db)   - bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil) + bc, _ := NewBlockChain(db, nil, oldcustomg.Config, mockEngine.NewFullFaker(), vm.Config{}, nil, nil) defer bc.Stop()   - blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil) + blocks, _ := GenerateChain(oldcustomg.Config, genesis, mockEngine.NewFaker(), db, 4, nil) bc.InsertChain(blocks) bc.CurrentBlock() // This should return a compatibility error. @@ -179,6 +177,55 @@ } } }   +// TestRegistryInGenesis tests if the params.RegistrySmartContract that defined in the genesis block sits in the blockchain +func TestRegistryInGenesis(t *testing.T) { + tests := []struct { + name string + genesis func() *Genesis + }{ + { + name: "dev", + genesis: func() *Genesis { + return DeveloperGenesisBlock(1) + }, + }, + { + name: "alfajores", + genesis: DefaultAlfajoresGenesisBlock, + }, + { + name: "baklava", + genesis: DefaultBaklavaGenesisBlock, + }, + { + name: "mainnet", + genesis: MainnetGenesisBlock, + }, + { + name: "emptyAlloc", + genesis: func() *Genesis { + return &Genesis{} + }, + }, + } + + for _, test := range tests { + db := rawdb.NewMemoryDatabase() + test.genesis().MustCommit(db) + chain, _ := NewBlockChain(db, nil, params.IstanbulTestChainConfig, mockEngine.NewFaker(), vm.Config{}, nil, nil) + state, _ := chain.State() + codeSize := state.GetCodeSize(config.RegistrySmartContractAddress) + if test.name == "emptyAlloc" { + if codeSize != 0 { + t.Errorf("%s: Registry code size is %d, want 0", test.name, codeSize) + } + } else if codeSize == 0 { + t.Errorf("%s: Registry code size should not be 0", test.name) + } + chain.Stop() + } +} + // TestGenesisHashes checks the congruity of default genesis data to corresponding hardcoded genesis hash values. func TestGenesisHashes(t *testing.T) { cases := []struct { @@ -186,20 +233,16 @@ genesis *Genesis hash common.Hash }{ { - genesis: DefaultGenesisBlock(), - hash: params.MainnetGenesisHash, + genesis: DefaultAlfajoresGenesisBlock(), + hash: params.AlfajoresGenesisHash, }, { - genesis: DefaultGoerliGenesisBlock(), - hash: params.GoerliGenesisHash, + genesis: DefaultBaklavaGenesisBlock(), + hash: params.BaklavaGenesisHash, }, { - genesis: DefaultRopstenGenesisBlock(), - hash: params.RopstenGenesisHash, - }, - { - genesis: DefaultRinkebyGenesisBlock(), - hash: params.RinkebyGenesisHash, + genesis: MainnetGenesisBlock(), + hash: params.MainnetGenesisHash, }, } for i, c := range cases {
diff --git go-ethereum/core/dao_test.go celo/core/dao_test.go deleted file mode 100644 index c9c765a3832a3f734210268dc445ded1bbafd02d..0000000000000000000000000000000000000000 --- go-ethereum/core/dao_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package core - -import ( - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/params" -) - -// Tests that DAO-fork enabled clients can properly filter out fork-commencing -// blocks based on their extradata fields. -func TestDAOForkRangeExtradata(t *testing.T) { - forkBlock := big.NewInt(32) - - // Generate a common prefix for both pro-forkers and non-forkers - db := rawdb.NewMemoryDatabase() - gspec := &Genesis{BaseFee: big.NewInt(params.InitialBaseFee)} - genesis := gspec.MustCommit(db) - prefix, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {}) - - // Create the concurrent, conflicting two nodes - proDb := rawdb.NewMemoryDatabase() - gspec.MustCommit(proDb) - - proConf := *params.TestChainConfig - proConf.DAOForkBlock = forkBlock - proConf.DAOForkSupport = true - - proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil) - defer proBc.Stop() - - conDb := rawdb.NewMemoryDatabase() - gspec.MustCommit(conDb) - - conConf := *params.TestChainConfig - conConf.DAOForkBlock = forkBlock - conConf.DAOForkSupport = false - - conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil) - defer conBc.Stop() - - if _, err := proBc.InsertChain(prefix); err != nil { - t.Fatalf("pro-fork: failed to import chain prefix: %v", err) - } - if _, err := conBc.InsertChain(prefix); err != nil { - t.Fatalf("con-fork: failed to import chain prefix: %v", err) - } - // Try to expand both pro-fork and non-fork chains iteratively with other camp's blocks - for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ { - // Create a pro-fork block, and try to feed into the no-fork chain - db = rawdb.NewMemoryDatabase() - gspec.MustCommit(db) - bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil) - defer bc.Stop() - - blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) - for j := 0; j < len(blocks)/2; j++ { - blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] - } - if _, err := bc.InsertChain(blocks); err != nil { - t.Fatalf("failed to import contra-fork chain for expansion: %v", err) - } - if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true, nil); err != nil { - t.Fatalf("failed to commit contra-fork head for expansion: %v", err) - } - blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) - if _, err := conBc.InsertChain(blocks); err == nil { - t.Fatalf("contra-fork chain accepted pro-fork block: %v", blocks[0]) - } - // Create a proper no-fork block for the contra-forker - blocks, _ = GenerateChain(&conConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) - if _, err := conBc.InsertChain(blocks); err != nil { - t.Fatalf("contra-fork chain didn't accepted no-fork block: %v", err) - } - // Create a no-fork block, and try to feed into the pro-fork chain - db = rawdb.NewMemoryDatabase() - gspec.MustCommit(db) - bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil) - defer bc.Stop() - - blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) - for j := 0; j < len(blocks)/2; j++ { - blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] - } - if _, err := bc.InsertChain(blocks); err != nil { - t.Fatalf("failed to import pro-fork chain for expansion: %v", err) - } - if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true, nil); err != nil { - t.Fatalf("failed to commit pro-fork head for expansion: %v", err) - } - blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) - if _, err := proBc.InsertChain(blocks); err == nil { - t.Fatalf("pro-fork chain accepted contra-fork block: %v", blocks[0]) - } - // Create a proper pro-fork block for the pro-forker - blocks, _ = GenerateChain(&proConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) - if _, err := proBc.InsertChain(blocks); err != nil { - t.Fatalf("pro-fork chain didn't accepted pro-fork block: %v", err) - } - } - // Verify that contra-forkers accept pro-fork extra-datas after forking finishes - db = rawdb.NewMemoryDatabase() - gspec.MustCommit(db) - bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil) - defer bc.Stop() - - blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) - for j := 0; j < len(blocks)/2; j++ { - blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] - } - if _, err := bc.InsertChain(blocks); err != nil { - t.Fatalf("failed to import contra-fork chain for expansion: %v", err) - } - if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true, nil); err != nil { - t.Fatalf("failed to commit contra-fork head for expansion: %v", err) - } - blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) - if _, err := conBc.InsertChain(blocks); err != nil { - t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err) - } - // Verify that pro-forkers accept contra-fork extra-datas after forking finishes - db = rawdb.NewMemoryDatabase() - gspec.MustCommit(db) - bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil) - defer bc.Stop() - - blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) - for j := 0; j < len(blocks)/2; j++ { - blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] - } - if _, err := bc.InsertChain(blocks); err != nil { - t.Fatalf("failed to import pro-fork chain for expansion: %v", err) - } - if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true, nil); err != nil { - t.Fatalf("failed to commit pro-fork head for expansion: %v", err) - } - blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) - if _, err := proBc.InsertChain(blocks); err != nil { - t.Fatalf("pro-fork chain didn't accept contra-fork block post-fork: %v", err) - } -}
diff --git go-ethereum/core/state_prefetcher.go celo/core/state_prefetcher.go index 5e10209407547b3112eeea33f551fe1b3c60e4f6..5b064cdbb42e840588f3638daddae809fab0fec8 100644 --- go-ethereum/core/state_prefetcher.go +++ celo/core/state_prefetcher.go @@ -17,9 +17,12 @@ package core   import ( + "math/big" "sync/atomic"   + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -50,25 +53,28 @@ // only goal is to pre-cache transaction signatures and state trie nodes. func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *uint32) { var ( header = block.Header() - gaspool = new(GasPool).AddGas(block.GasLimit()) - blockContext = NewEVMBlockContext(header, p.bc, nil) - evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) - signer = types.MakeSigner(p.config, header.Number) + vmRunner = p.bc.NewEVMRunner(header, statedb) + gaspool = new(GasPool).AddGas(blockchain_parameters.GetBlockGasLimitOrDefault(vmRunner)) + baseFee *big.Int + sysCtx *SysContractCallCtx ) // Iterate over and process the individual transactions byzantium := p.config.IsByzantium(block.Number()) + espresso := p.bc.chainConfig.IsEspresso(block.Number()) + if espresso { + sysCtx = NewSysContractCallCtx(header, statedb, p.bc) + } for i, tx := range block.Transactions() { // If block precaching was interrupted, abort if interrupt != nil && atomic.LoadUint32(interrupt) == 1 { return } - // Convert the transaction into an executable message and pre-cache its sender - msg, err := tx.AsMessage(signer, header.BaseFee) - if err != nil { - return // Also invalid block, bail out + // Block precaching permitted to continue, execute the transaction + statedb.Prepare(tx.Hash(), i) + if espresso { + baseFee = sysCtx.GetGasPriceMinimum(tx.FeeCurrency()) } - statedb.Prepare(tx.Hash(), i) - if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil { + if err := precacheTransaction(p.config, p.bc, nil, gaspool, statedb, header, tx, cfg, baseFee); err != nil { return // Ugh, something went horribly wrong, bail out } // If we're pre-byzantium, pre-load trie nodes for the intermediate root @@ -85,10 +91,21 @@ // precacheTransaction attempts to apply a transaction to the given state database // and uses the input parameters for its environment. The goal is not to execute // the transaction successfully, rather to warm up touched data slots. -func precacheTransaction(msg types.Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM) error { - // Update the evm with the new transaction context. - evm.Reset(NewEVMTxContext(msg), statedb) - // Add addresses to access list if applicable - _, err := ApplyMessage(evm, msg, gaspool) +func precacheTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gaspool *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, cfg vm.Config, baseFee *big.Int) error { + // Convert the transaction into an executable message and pre-cache its sender + msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), baseFee) + if err != nil { + return err + } + // Create the EVM and execute the transaction + context := NewEVMBlockContext(header, bc, author) + txContext := NewEVMTxContext(msg) + vm := vm.NewEVM(context, txContext, statedb, config, cfg) + + var sysCtx *SysContractCallCtx + if config.IsEspresso(header.Number) { + sysCtx = NewSysContractCallCtx(header, statedb, bc) + } + _, err = ApplyMessage(vm, msg, gaspool, bc.NewEVMRunner(header, statedb), sysCtx) return err }
diff --git go-ethereum/core/types.go celo/core/types.go index 4c5b74a49865b26d3d9c10ed7b90bf490aec683d..a2800819d6170d9cbd65260591dafff8425bd6a5 100644 --- go-ethereum/core/types.go +++ celo/core/types.go @@ -45,7 +45,7 @@ // Processor is an interface for processing blocks using a given initial state. type Processor interface { // Process processes the state changes according to the Ethereum rules by running - // the transaction messages using the statedb and applying any rewards to both - // the processor (coinbase) and any included uncles. + // the transaction messages using the statedb and applying any rewards to the + // processor (coinbase). Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) }
diff --git go-ethereum/core/rlp_test.go celo/core/rlp_test.go index 01f85786f26c7aae31acb8e0f4fbd9eebcaed4c6..4d70f48f933049345d2bf24066f842b24036c890 100644 --- go-ethereum/core/rlp_test.go +++ celo/core/rlp_test.go @@ -22,7 +22,7 @@ "math/big" "testing"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -31,11 +31,11 @@ "github.com/ethereum/go-ethereum/rlp" "golang.org/x/crypto/sha3" )   -func getBlock(transactions int, uncles int, dataSize int) *types.Block { +func getBlock(transactions int, chainSize, dataSize int) *types.Block { var ( aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") // Generate a canonical chain to act as the main dataset - engine = ethash.NewFaker() + engine = mockEngine.NewFaker() db = rawdb.NewMemoryDatabase() // A sender who makes transactions, has some funds key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") @@ -49,17 +49,14 @@ genesis = gspec.MustCommit(db) )   // We need to generate as many blocks +1 as uncles - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, uncles+1, + blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, chainSize+1, func(n int, b *BlockGen) { - if n == uncles { + if n == chainSize { // Add transactions and stuff on the last block for i := 0; i < transactions; i++ { tx, _ := types.SignTx(types.NewTransaction(uint64(i), aa, - big.NewInt(0), 50000, b.header.BaseFee, make([]byte, dataSize)), types.HomesteadSigner{}, key) + big.NewInt(0), 50000, big.NewInt(1), nil, nil, nil, make([]byte, dataSize)), types.HomesteadSigner{}, key) b.AddTx(tx) - } - for i := 0; i < uncles; i++ { - b.AddUncle(&types.Header{ParentHash: b.PrevBlock(n - 1 - i).Hash(), Number: big.NewInt(int64(n - i))}) } } }) @@ -72,7 +69,7 @@ // from blocks without full unmarshalling/marshalling func TestRlpIterator(t *testing.T) { for _, tt := range []struct { txs int - uncles int + chainSize int datasize int }{ {0, 0, 0}, @@ -81,29 +78,33 @@ {10, 0, 0}, {10, 2, 0}, {10, 2, 50}, } { - testRlpIterator(t, tt.txs, tt.uncles, tt.datasize) + testRlpIterator(t, tt.txs, tt.chainSize, tt.datasize) } }   -func testRlpIterator(t *testing.T, txs, uncles, datasize int) { - desc := fmt.Sprintf("%d txs [%d datasize] and %d uncles", txs, datasize, uncles) - bodyRlp, _ := rlp.EncodeToBytes(getBlock(txs, uncles, datasize).Body()) +func testRlpIterator(t *testing.T, txs, chainSize, datasize int) { + desc := fmt.Sprintf("%d txs [%d datasize]", txs, datasize) + bodyRlp, _ := rlp.EncodeToBytes(getBlock(txs, chainSize, datasize).Body()) it, err := rlp.NewListIterator(bodyRlp) if err != nil { t.Fatal(err) } // Check that txs exist if !it.Next() { - t.Fatal("expected two elems, got zero") + t.Fatal("expected 3 elems, got zero") } txdata := it.Value() - // Check that uncles exist + // Check that randomness exist + if !it.Next() { + t.Fatal("expected 3 elems, got one") + } + // Check that epochSnarkData exist if !it.Next() { - t.Fatal("expected two elems, got one") + t.Fatal("expected 3 elems, got two") } // No more after that if it.Next() { - t.Fatal("expected only two elems, got more") + t.Fatal("expected only 3 elems, got more") } txIt, err := rlp.NewListIterator(txdata) if err != nil {
diff --git go-ethereum/core/error.go celo/core/error.go index 594f3a26e55960c98303d68c5c7e3ab3b151252f..853d4d55e8038c60aef0e5e7d4f15c76fb7b7c70 100644 --- go-ethereum/core/error.go +++ celo/core/error.go @@ -57,11 +57,24 @@ ErrGasLimitReached = errors.New("gas limit reached")   // ErrInsufficientFundsForTransfer is returned if the transaction sender doesn't // have enough funds for transfer(topmost call only). - ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer") + // Note that the check for this is done after buying the gas. + ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer (after fees)")   // ErrInsufficientFunds is returned if the total cost of executing a transaction // is higher than the balance of the user's account. - ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value") + ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value + gatewayFee") + + // ErrGasPriceDoesNotExceedMinimum is returned if the gas price specified doesn't meet the + // minimum specified by the GasPriceMinimum contract. + ErrGasPriceDoesNotExceedMinimum = errors.New("gasprice is less than gas price minimum") + + // ErrGasPriceDoesNotExceedMinimumFloor is returned if the gas price specified doesn't meet the + // minimum floor specified by the GasPriceMinimum contract. + ErrGasPriceDoesNotExceedMinimumFloor = errors.New("gasprice is less than gas price minimum floor") + + // ErrNonWhitelistedFeeCurrency is returned if the currency specified to use for the fees + // isn't one of the currencies whitelisted for that purpose. + ErrNonWhitelistedFeeCurrency = errors.New("non-whitelisted fee currency address")   // ErrGasUintOverflow is returned when calculating gas usage. ErrGasUintOverflow = errors.New("gas uint64 overflow") @@ -69,6 +82,14 @@ // ErrIntrinsicGas is returned if the transaction is specified to use less gas // than required to start the invocation. ErrIntrinsicGas = errors.New("intrinsic gas too low") + + // ErrEthCompatibleTransactionsNotSupported is returned if the transaction omits the 3 Celo-only + // fields (FeeCurrency & co.) but support for this kind of transaction is not enabled. + ErrEthCompatibleTransactionsNotSupported = errors.New("support for eth-compatible transactions is not enabled") + + // ErrUnprotectedTransaction is returned if replay protection is required (post-Donut) but the transaction doesn't + // use it. + ErrUnprotectedTransaction = errors.New("replay protection is required")   // ErrTxTypeNotSupported is returned if a transaction is not supported in the // current network configuration.
diff --git go-ethereum/core/block_validator.go celo/core/block_validator.go index dfcc14d57e1e4c201b5abf9159f00747809a08cf..8c6d205e3e0b8c723d22bddcf2181bed87bc1e8d 100644 --- go-ethereum/core/block_validator.go +++ celo/core/block_validator.go @@ -26,7 +26,7 @@ "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" )   -// BlockValidator is responsible for validating block headers, uncles and +// BlockValidator is responsible for validating block headers and // processed state. // // BlockValidator implements Validator. @@ -46,22 +46,15 @@ } return validator }   -// ValidateBody validates the given block's uncles and verifies the block -// header's transaction and uncle roots. The headers are assumed to be already -// validated at this point. +// ValidateBody verifies the block header's transaction. +// The headers are assumed to be already validated at this point. func (v *BlockValidator) ValidateBody(block *types.Block) error { // Check whether the block's known, and if not, that it's linkable if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) { return ErrKnownBlock } - // Header validity is known at this point, check the uncles and transactions + // Header validity is known at this point, check the transactions header := block.Header() - if err := v.engine.VerifyUncles(v.bc, block); err != nil { - return err - } - if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { - return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash) - } if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash { return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash) } @@ -101,29 +94,3 @@ return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) } return nil } - -// CalcGasLimit computes the gas limit of the next block after parent. It aims -// to keep the baseline gas close to the provided target, and increase it towards -// the target if the baseline gas is lower. -func CalcGasLimit(parentGasLimit, desiredLimit uint64) uint64 { - delta := parentGasLimit/params.GasLimitBoundDivisor - 1 - limit := parentGasLimit - if desiredLimit < params.MinGasLimit { - desiredLimit = params.MinGasLimit - } - // If we're outside our allowed gas range, we try to hone towards them - if limit < desiredLimit { - limit = parentGasLimit + delta - if limit > desiredLimit { - limit = desiredLimit - } - return limit - } - if limit > desiredLimit { - limit = parentGasLimit - delta - if limit < desiredLimit { - limit = desiredLimit - } - } - return limit -}
diff --git go-ethereum/core/headerchain_test.go celo/core/headerchain_test.go index 581d4416d2c4efda7013893c5c9419c98d6b84b9..049961fe81e0686be530020622b108bbe857c360 100644 --- go-ethereum/core/headerchain_test.go +++ celo/core/headerchain_test.go @@ -19,12 +19,11 @@ import ( "errors" "fmt" - "math/big" "testing" "time"   "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" @@ -71,17 +70,17 @@ // This test checks status reporting of InsertHeaderChain. func TestHeaderInsertion(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) + genesis = new(Genesis).MustCommit(db) )   - hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false }) + hc, err := NewHeaderChain(db, params.IstanbulTestChainConfig, mockEngine.NewFaker(), func() bool { return false }) if err != nil { t.Fatal(err) } // chain A: G->A1->A2...A128 - chainA := makeHeaderChain(genesis.Header(), 128, ethash.NewFaker(), db, 10) + chainA := makeHeaderChain(genesis.Header(), 128, mockEngine.NewFaker(), db, 10) // chain B: G->A1->B2...B128 - chainB := makeHeaderChain(chainA[0], 128, ethash.NewFaker(), db, 10) + chainB := makeHeaderChain(chainA[0], 128, mockEngine.NewFaker(), db, 10) log.Root().SetHandler(log.StdoutHandler)   // Inserting 64 headers on an empty chain, expecting
diff --git go-ethereum/core/genesis.go celo/core/genesis.go index 0fcc06fac6f61a2cdf6f8c3d45b650be69fec84c..8b2db60c842219780997467ac8d454c57ae92b41 100644 --- go-ethereum/core/genesis.go +++ celo/core/genesis.go @@ -23,37 +23,33 @@ "encoding/json" "errors" "fmt" "math/big" - "strings"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/contracts/config" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" )   //go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go //go:generate gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go   -var errGenesisNoConfig = errors.New("genesis has no chain configuration") +var ( + errGenesisNoConfig = errors.New("genesis has no chain configuration") +)   // Genesis specifies the header fields, state of a genesis block. It also defines hard // fork switch-over blocks through the chain configuration. type Genesis struct { Config *params.ChainConfig `json:"config"` - Nonce uint64 `json:"nonce"` Timestamp uint64 `json:"timestamp"` ExtraData []byte `json:"extraData"` - GasLimit uint64 `json:"gasLimit" gencodec:"required"` - Difficulty *big.Int `json:"difficulty" gencodec:"required"` - Mixhash common.Hash `json:"mixHash"` Coinbase common.Address `json:"coinbase"` Alloc GenesisAlloc `json:"alloc" gencodec:"required"`   @@ -62,7 +58,6 @@ // in actual genesis blocks. Number uint64 `json:"number"` GasUsed uint64 `json:"gasUsed"` ParentHash common.Hash `json:"parentHash"` - BaseFee *big.Int `json:"baseFeePerGas"` }   // GenesisAlloc specifies the initial state that is part of the genesis block. @@ -91,14 +86,10 @@ }   // field type overrides for gencodec type genesisSpecMarshaling struct { - Nonce math.HexOrDecimal64 Timestamp math.HexOrDecimal64 ExtraData hexutil.Bytes - GasLimit math.HexOrDecimal64 GasUsed math.HexOrDecimal64 Number math.HexOrDecimal64 - Difficulty *math.HexOrDecimal256 - BaseFee *math.HexOrDecimal256 Alloc map[common.UnprefixedAddress]GenesisAccount }   @@ -158,16 +149,16 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) { return SetupGenesisBlockWithOverride(db, genesis, nil) }   -func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideLondon *big.Int) (*params.ChainConfig, common.Hash, error) { - if genesis != nil && genesis.Config == nil { - return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig +func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideEHardfork *big.Int) (*params.ChainConfig, common.Hash, error) { + if genesis != nil && (genesis.Config == nil || genesis.Config.Istanbul == nil) { + return params.MainnetChainConfig, common.Hash{}, errGenesisNoConfig } // Just commit the new block if there is no stored genesis block. stored := rawdb.ReadCanonicalHash(db, 0) if (stored == common.Hash{}) { if genesis == nil { log.Info("Writing default main-net genesis block") - genesis = DefaultGenesisBlock() + genesis = MainnetGenesisBlock() } else { log.Info("Writing custom genesis block") } @@ -180,9 +171,10 @@ } // We have the genesis block in database(perhaps in ancient database) // but the corresponding state is missing. header := rawdb.ReadHeader(db, stored, 0) - if _, err := state.New(header.Root, state.NewDatabaseWithConfig(db, nil), nil); err != nil { + s, err := state.New(header.Root, state.NewDatabaseWithConfig(db, nil), nil) + if err != nil { if genesis == nil { - genesis = DefaultGenesisBlock() + genesis = MainnetGenesisBlock() } // Ensure the stored genesis matches with the given one. hash := genesis.ToBlock(nil).Hash() @@ -202,11 +194,18 @@ if hash != stored { return genesis.Config, hash, &GenesisMismatchError{stored, hash} } } + + // Check if Registry sits in genesis + if s.GetCodeSize(config.RegistrySmartContractAddress) == 0 { + return params.MainnetChainConfig, common.Hash{}, errors.New("no Registry Smart Contract deployed in genesis") + } + // Get the existing chain configuration. newcfg := genesis.configOrDefault(stored) - if overrideLondon != nil { - newcfg.LondonBlock = overrideLondon + if overrideEHardfork != nil { + newcfg.EspressoBlock = overrideEHardfork } + if err := newcfg.CheckConfigForkOrder(); err != nil { return newcfg, common.Hash{}, err } @@ -242,14 +241,12 @@ case g != nil: return g.Config case ghash == params.MainnetGenesisHash: return params.MainnetChainConfig - case ghash == params.RopstenGenesisHash: - return params.RopstenChainConfig - case ghash == params.RinkebyGenesisHash: - return params.RinkebyChainConfig - case ghash == params.GoerliGenesisHash: - return params.GoerliChainConfig + case ghash == params.BaklavaGenesisHash: + return params.BaklavaChainConfig + case ghash == params.AlfajoresGenesisHash: + return params.AlfajoresChainConfig default: - return params.AllEthashProtocolChanges + return params.MainnetChainConfig } }   @@ -274,31 +271,14 @@ } root := statedb.IntermediateRoot(false) head := &types.Header{ Number: new(big.Int).SetUint64(g.Number), - Nonce: types.EncodeNonce(g.Nonce), Time: g.Timestamp, ParentHash: g.ParentHash, Extra: g.ExtraData, - GasLimit: g.GasLimit, GasUsed: g.GasUsed, - BaseFee: g.BaseFee, - Difficulty: g.Difficulty, - MixDigest: g.Mixhash, Coinbase: g.Coinbase, Root: root, } - if g.GasLimit == 0 { - head.GasLimit = params.GenesisGasLimit - } - if g.Difficulty == nil { - head.Difficulty = params.GenesisDifficulty - } - if g.Config != nil && g.Config.IsLondon(common.Big0) { - if g.BaseFee != nil { - head.BaseFee = g.BaseFee - } else { - head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee) - } - } + statedb.Commit(false) statedb.Database().TrieDB().Commit(root, true, nil)   @@ -314,21 +294,19 @@ return nil, errors.New("can't commit genesis block with number > 0") } config := g.Config if config == nil { - config = params.AllEthashProtocolChanges + config = params.MainnetChainConfig } if err := config.CheckConfigForkOrder(); err != nil { return nil, err } - if config.Clique != nil && len(block.Extra()) == 0 { - return nil, errors.New("can't start clique chain without signers") - } - rawdb.WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty) + rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.TotalDifficulty()) rawdb.WriteBlock(db, block) rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil) rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64()) rawdb.WriteHeadBlockHash(db, block.Hash()) rawdb.WriteHeadFastBlockHash(db, block.Hash()) rawdb.WriteHeadHeaderHash(db, block.Hash()) + rawdb.WriteChainConfig(db, block.Hash(), config) return block, nil } @@ -345,100 +323,56 @@ }   // GenesisBlockForTesting creates and writes a block in which addr has the given wei balance. func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { - g := Genesis{ - Alloc: GenesisAlloc{addr: {Balance: balance}}, - BaseFee: big.NewInt(params.InitialBaseFee), - } + g := Genesis{Config: params.BaklavaChainConfig, Alloc: GenesisAlloc{addr: {Balance: balance}}} return g.MustCommit(db) }   -// DefaultGenesisBlock returns the Ethereum main net genesis block. -func DefaultGenesisBlock() *Genesis { +// MainnetGenesisBlock returns the Celo main net genesis block. +func MainnetGenesisBlock() *Genesis { + mainnetAlloc := &GenesisAlloc{} + mainnetAlloc.UnmarshalJSON([]byte(mainnetAllocJSON)) return &Genesis{ Config: params.MainnetChainConfig, - Nonce: 66, - ExtraData: hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), - GasLimit: 5000, - Difficulty: big.NewInt(17179869184), - Alloc: decodePrealloc(mainnetAllocData), + Timestamp: 0x5ea06a00, + ExtraData: hexutil.MustDecode(mainnetExtraData), + Alloc: *mainnetAlloc, } }   -// DefaultRopstenGenesisBlock returns the Ropsten network genesis block. -func DefaultRopstenGenesisBlock() *Genesis { - return &Genesis{ - Config: params.RopstenChainConfig, - Nonce: 66, - ExtraData: hexutil.MustDecode("0x3535353535353535353535353535353535353535353535353535353535353535"), - GasLimit: 16777216, - Difficulty: big.NewInt(1048576), - Alloc: decodePrealloc(ropstenAllocData), - } -} - -// DefaultRinkebyGenesisBlock returns the Rinkeby network genesis block. -func DefaultRinkebyGenesisBlock() *Genesis { +// DefaultBaklavaGenesisBlock returns the Baklava network genesis block. +func DefaultBaklavaGenesisBlock() *Genesis { + baklavaAlloc := &GenesisAlloc{} + baklavaAlloc.UnmarshalJSON([]byte(baklavaAllocJSON)) return &Genesis{ - Config: params.RinkebyChainConfig, - Timestamp: 1492009146, - ExtraData: hexutil.MustDecode("0x52657370656374206d7920617574686f7269746168207e452e436172746d616e42eb768f2244c8811c63729a21a3569731535f067ffc57839b00206d1ad20c69a1981b489f772031b279182d99e65703f0076e4812653aab85fca0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), - GasLimit: 4700000, - Difficulty: big.NewInt(1), - Alloc: decodePrealloc(rinkebyAllocData), + Config: params.BaklavaChainConfig, + Timestamp: 0x5b843511, + ExtraData: hexutil.MustDecode(baklavaExtraData), + Alloc: *baklavaAlloc, } }   -// DefaultGoerliGenesisBlock returns the Görli network genesis block. -func DefaultGoerliGenesisBlock() *Genesis { +func DefaultAlfajoresGenesisBlock() *Genesis { + alfajoresAlloc := &GenesisAlloc{} + alfajoresAlloc.UnmarshalJSON([]byte(alfajoresAllocJSON)) return &Genesis{ - Config: params.GoerliChainConfig, - Timestamp: 1548854791, - ExtraData: hexutil.MustDecode("0x22466c6578692069732061207468696e6722202d204166726900000000000000e0a2bd4258d2768837baa26a28fe71dc079f84c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), - GasLimit: 10485760, - Difficulty: big.NewInt(1), - Alloc: decodePrealloc(goerliAllocData), + Config: params.AlfajoresChainConfig, + Timestamp: 0x5b843511, + ExtraData: hexutil.MustDecode(alfajoresExtraData), + Alloc: *alfajoresAlloc, } }   // DeveloperGenesisBlock returns the 'geth --dev' genesis block. -func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis { +func DeveloperGenesisBlock(period uint64) *Genesis { // Override the default period to the user requested one - config := *params.AllCliqueProtocolChanges - config.Clique = &params.CliqueConfig{ - Period: period, - Epoch: config.Clique.Epoch, - } - + config := *params.DeveloperChainConfig + config.Istanbul.BlockPeriod = period + devAlloc := &GenesisAlloc{} + devAlloc.UnmarshalJSON([]byte(devAllocJSON)) // Assemble and return the genesis with the precompiles and faucet pre-funded return &Genesis{ Config: &config, - ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...), - GasLimit: 11500000, - BaseFee: big.NewInt(params.InitialBaseFee), - Difficulty: big.NewInt(1), - Alloc: map[common.Address]GenesisAccount{ - common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover - common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256 - common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD - common.BytesToAddress([]byte{4}): {Balance: big.NewInt(1)}, // Identity - common.BytesToAddress([]byte{5}): {Balance: big.NewInt(1)}, // ModExp - common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd - common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul - common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing - common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b - faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))}, - }, + ExtraData: hexutil.MustDecode(developerExtraData), + Alloc: *devAlloc, } } - -func decodePrealloc(data string) GenesisAlloc { - var p []struct{ Addr, Balance *big.Int } - if err := rlp.NewStream(strings.NewReader(data), 0).Decode(&p); err != nil { - panic(err) - } - ga := make(GenesisAlloc, len(p)) - for _, account := range p { - ga[common.BigToAddress(account.Addr)] = GenesisAccount{Balance: account.Balance} - } - return ga -}
diff --git go-ethereum/core/tx_list.go celo/core/tx_list.go index 77d5818e0287e0b0a2adb158cf889ad6e2461f72..70e707168daa7a159ef23b038216496f8c31e8d9 100644 --- go-ethereum/core/tx_list.go +++ celo/core/tx_list.go @@ -27,6 +27,7 @@ "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" )   // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for @@ -253,17 +254,26 @@ type txList struct { strict bool // Whether nonces are strictly continuous or not txs *txSortedMap // Heap indexed sorted hash map of the transactions   - costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) + nativecostcap *big.Int // Price of the highest costing transaction paid with native fees (reset only if exceeds balance) + feecaps map[common.Address]*big.Int // Price of the highest costing transaction per fee currency (reset only if exceeds balance) + nativegaspricefloor *big.Int // Lowest gas price minimum in the native currency + gaspricefloors map[common.Address]*big.Int // Lowest gas price minimum per currency (reset only if it is below the gpm) gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit) + + ctx *atomic.Value // transaction pool context }   // newTxList create a new transaction list for maintaining nonce-indexable fast, // gapped, sortable transaction lists. -func newTxList(strict bool) *txList { +func newTxList(strict bool, ctx *atomic.Value) *txList { return &txList{ + ctx: ctx, strict: strict, txs: newTxSortedMap(), - costcap: new(big.Int), + nativecostcap: new(big.Int), + feecaps: make(map[common.Address]*big.Int), + nativegaspricefloor: nil, + gaspricefloors: make(map[common.Address]*big.Int), } }   @@ -273,22 +283,69 @@ func (l *txList) Overlaps(tx *types.Transaction) bool { return l.txs.Get(tx.Nonce()) != nil }   +// FeeCurrencies returns a list of each fee currency used to pay for gas in the txList +func (l *txList) FeeCurrencies() []common.Address { + var feeCurrencies []common.Address + for feeCurrency := range l.feecaps { + feeCurrencies = append(feeCurrencies, feeCurrency) + } + return feeCurrencies +} + +func toCELO(amount *big.Int, feeCurrency *common.Address, txCtx *txPoolContext) (*big.Int, error) { + if feeCurrency == nil { + return amount, nil + } + currency, err := txCtx.GetCurrency(feeCurrency) + if err != nil { + return nil, err + } + return currency.ToCELO(amount), nil +} + // Add tries to insert a new transaction into the list, returning whether the // transaction was accepted, and if yes, any previous transaction it replaced. // -// If the new transaction is accepted into the list, the lists' cost and gas -// thresholds are also potentially updated. +// If the new transaction is accepted into the list, the lists' cost, gas and +// gasPriceMinimum thresholds are also potentially updated. func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) { // If there's an older better transaction, abort old := l.txs.Get(tx.Nonce()) if old != nil { - if old.GasFeeCapCmp(tx) >= 0 || old.GasTipCapCmp(tx) >= 0 { - return false, nil + var oldGasFeeCap, oldGasTipCap *big.Int + var newGasFeeCap, newGasTipCap *big.Int + + // Short circuit conversion if both are the same currency + if old.FeeCurrency() == tx.FeeCurrency() { + if old.GasFeeCapCmp(tx) >= 0 || old.GasTipCapCmp(tx) >= 0 { + return false, nil + } + oldGasFeeCap = old.GasFeeCap() + oldGasTipCap = old.GasTipCap() + newGasFeeCap = tx.GasFeeCap() + newGasTipCap = tx.GasTipCap() + } else { + // Convert old values into tx fee currency + var err error + txCtx := l.ctx.Load().(txPoolContext) + if oldGasFeeCap, err = toCELO(old.GasFeeCap(), old.FeeCurrency(), &txCtx); err != nil { + return false, nil + } + if oldGasTipCap, err = toCELO(old.GasTipCap(), old.FeeCurrency(), &txCtx); err != nil { + return false, nil + } + if newGasFeeCap, err = toCELO(tx.GasFeeCap(), tx.FeeCurrency(), &txCtx); err != nil { + return false, nil + } + if newGasTipCap, err = toCELO(tx.GasTipCap(), tx.FeeCurrency(), &txCtx); err != nil { + return false, nil + } + } // thresholdFeeCap = oldFC * (100 + priceBump) / 100 a := big.NewInt(100 + int64(priceBump)) - aFeeCap := new(big.Int).Mul(a, old.GasFeeCap()) - aTip := a.Mul(a, old.GasTipCap()) + aFeeCap := new(big.Int).Mul(a, oldGasFeeCap) + aTip := a.Mul(a, oldGasTipCap)   // thresholdTip = oldTip * (100 + priceBump) / 100 b := big.NewInt(100) @@ -298,14 +355,31 @@ // Have to ensure that either the new fee cap or tip is higher than the // old ones as well as checking the percentage threshold to ensure that // this is accurate for low (Wei-level) gas price replacements - if tx.GasFeeCapIntCmp(thresholdFeeCap) < 0 || tx.GasTipCapIntCmp(thresholdTip) < 0 { + if newGasFeeCap.Cmp(thresholdFeeCap) < 0 || newGasTipCap.Cmp(thresholdTip) < 0 { return false, nil } } // Otherwise overwrite the old transaction with the current one + // caps can only increase and floors can only decrease in this function l.txs.Put(tx) - if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 { - l.costcap = cost + if feeCurrency := tx.FeeCurrency(); feeCurrency == nil { + if cost := tx.Cost(); l.nativecostcap.Cmp(cost) < 0 { + l.nativecostcap = cost + } + if gasPrice := tx.GasPrice(); l.nativegaspricefloor == nil || l.nativegaspricefloor.Cmp(gasPrice) > 0 { + l.nativegaspricefloor = gasPrice + } + } else { + fee := tx.Fee() + if oldFee, ok := l.feecaps[*feeCurrency]; !ok || oldFee.Cmp(fee) < 0 { + l.feecaps[*feeCurrency] = fee + } + if gasFloor, ok := l.gaspricefloors[*feeCurrency]; !ok || gasFloor.Cmp(tx.GasPrice()) > 0 { + l.gaspricefloors[*feeCurrency] = tx.GasPrice() + } + if value := tx.Value(); l.nativecostcap.Cmp(value) < 0 { + l.nativecostcap = value + } } if gas := tx.Gas(); l.gascap < gas { l.gascap = gas @@ -328,18 +402,87 @@ // // This method uses the cached costcap and gascap to quickly decide if there's even // a point in calculating all the costs or if the balance covers all. If the threshold // is lower than the costgas cap, the caps will be reset to a new high after removing -// the newly invalidated transactions. -func (l *txList) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) { - // If all transactions are below the threshold, short circuit - if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit { +func (l *txList) Filter(nativeCostLimit *big.Int, feeLimits map[common.Address]*big.Int, gasLimit uint64) (types.Transactions, types.Transactions) { + + // check if we can bail & lower caps & raise floors at the same time + canBail := true + // Ensure that the cost cap <= the cost limit + if l.nativecostcap.Cmp(nativeCostLimit) > 0 { + canBail = false + l.nativecostcap = new(big.Int).Set(nativeCostLimit) + } + + // Ensure that the gas cap <= the gas limit + if l.gascap > gasLimit { + canBail = false + l.gascap = gasLimit + } + // Ensure that each cost cap <= the per currency cost limit. + for feeCurrency, feeLimit := range feeLimits { + if l.feecaps[feeCurrency].Cmp(feeLimit) > 0 { + canBail = false + l.feecaps[feeCurrency] = new(big.Int).Set(feeLimit) + } + } + + if canBail { + return nil, nil + } + txCtx := l.ctx.Load().(txPoolContext) + // Filter out all the transactions above the account's funds + removed := l.txs.Filter(func(tx *types.Transaction) bool { + if feeCurrency := tx.FeeCurrency(); feeCurrency == nil { + log.Trace("Transaction Filter", "hash", tx.Hash(), "Fee currency", tx.FeeCurrency(), "Cost", tx.Cost(), "Cost Limit", nativeCostLimit, "Gas", tx.Gas(), "Gas Limit", gasLimit) + return tx.Cost().Cmp(nativeCostLimit) > 0 || tx.Gas() > gasLimit || txCtx.celoGasPriceMinimumFloor.Cmp(tx.GasPrice()) > 0 + } else { + feeLimit := feeLimits[*feeCurrency] + fee := tx.Fee() + log.Trace("Transaction Filter", "hash", tx.Hash(), "Fee currency", tx.FeeCurrency(), "Value", tx.Value(), "Cost Limit", feeLimit, "Gas", tx.Gas(), "Gas Limit", gasLimit) + + // If any of the following is true, the transaction is invalid + // The fees are greater than or equal to the balance in the currency + return fee.Cmp(feeLimit) >= 0 || + // The value of the tx is greater than the native balance of the account + tx.Value().Cmp(nativeCostLimit) > 0 || + // The gas price is smaller that the gasPriceMinimumFloor + txCtx.CmpValues(txCtx.celoGasPriceMinimumFloor, nil, tx.GasPrice(), tx.FeeCurrency()) > 0 || + // The gas used is greater than the gas limit + tx.Gas() > gasLimit + } + }) + + // If the list was strict, filter anything above the lowest nonce + var invalids types.Transactions + + if l.strict && len(removed) > 0 { + lowest := uint64(math.MaxUint64) + for _, tx := range removed { + if nonce := tx.Nonce(); lowest > nonce { + lowest = nonce + } + } + invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest }) + } + return removed, invalids +} + +// FilterOnGasLimit removes all transactions from the list with a gas limit higher +// than the provided thresholds. Every removed transaction is returned for any +// post-removal maintenance. Strict-mode invalidated transactions are also +// returned. +// +// This method uses the cached gascap to quickly decide if there's even +// a point in calculating all the gas used +func (l *txList) FilterOnGasLimit(gasLimit uint64) (types.Transactions, types.Transactions) { + // We can bail if the gas cap <= the gas limit + if l.gascap <= gasLimit { return nil, nil } - l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds l.gascap = gasLimit   // Filter out all the transactions above the account's funds removed := l.txs.Filter(func(tx *types.Transaction) bool { - return tx.Gas() > gasLimit || tx.Cost().Cmp(costLimit) > 0 + return tx.Gas() > gasLimit })   if len(removed) == 0 { @@ -420,6 +563,7 @@ // priceHeap is a heap.Interface implementation over transactions for retrieving // price-sorted transactions to discard when the pool fills up. If baseFee is set // then the heap is sorted based on the effective tip based on the given base fee. // If baseFee is nil then the sorting is based on gasFeeCap. +// The fee currencies for each transaction should be the same. type priceHeap struct { baseFee *big.Int // heap should always be re-sorted after baseFee is changed list []*types.Transaction @@ -485,9 +629,11 @@ // This field is accessed atomically, and must be the first field // to ensure it has correct alignment for atomic.AddInt64. // See https://golang.org/pkg/sync/atomic/#pkg-note-BUG. stales int64 + maxStales int64 // Maximum amount of stale price points allowed before a forced re-heap   + ctx *atomic.Value all *txLookup // Pointer to the map of all transactions - urgent, floating priceHeap // Heaps of prices of all the stored **remote** transactions + urgent, floating multiCurrencyPriceHeap // Heaps of prices of all the stored **remote** transactions reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list }   @@ -498,9 +644,14 @@ floatingRatio = 1 )   // newTxPricedList creates a new price-sorted transaction heap. -func newTxPricedList(all *txLookup) *txPricedList { +func newTxPricedList(all *txLookup, ctx *atomic.Value, maxStales int64) *txPricedList { + txCtx := ctx.Load().(txPoolContext) return &txPricedList{ + ctx: ctx, all: all, + maxStales: maxStales, + urgent: newMultiCurrencyPriceHeap(txCtx.CmpValues, txCtx.SysContractCallCtx.GetCurrentGasPriceMinimumMap()), + floating: newMultiCurrencyPriceHeap(txCtx.CmpValues, txCtx.SysContractCallCtx.GetCurrentGasPriceMinimumMap()), } }   @@ -510,20 +661,26 @@ if local { return } // Insert every new transaction to the urgent heap first; Discard will balance the heaps - heap.Push(&l.urgent, tx) + l.urgent.Push(tx) }   // Removed notifies the prices transaction list that an old transaction dropped // from the pool. The list will just keep a counter of stale objects and update // the heap if a large enough ratio of transactions go stale. func (l *txPricedList) Removed(count int) { - // Bump the stale counter, but exit if still too low (< 25%) + // Bump the stale counter stales := atomic.AddInt64(&l.stales, int64(count)) - if int(stales) <= (len(l.urgent.list)+len(l.floating.list))/4 { - return + urgentSize := l.urgent.Len() + floatingSize := l.floating.Len() + + // Reheap if the ratio of stales is more than 25% of the heaps sizes + overStalesRatio := int(stales) > (urgentSize+floatingSize)/4 + // Reheap if stales exceed the max stales limit + overMaxStales := stales >= l.maxStales + + if overStalesRatio || overMaxStales { + l.Reheap() } - // Seems we've reached a critical number of stale transactions, reheap - l.Reheap() }   // Underpriced checks whether a transaction is cheaper than (or as cheap as) the @@ -531,9 +688,21 @@ // lowest priced (remote) transaction currently being tracked. func (l *txPricedList) Underpriced(tx *types.Transaction) bool { // Note: with two queues, being underpriced is defined as being worse than the worst item // in all non-empty queues if there is any. If both queues are empty then nothing is underpriced. - return (l.underpricedFor(&l.urgent, tx) || len(l.urgent.list) == 0) && - (l.underpricedFor(&l.floating, tx) || len(l.floating.list) == 0) && - (len(l.urgent.list) != 0 || len(l.floating.list) != 0) + urgentUnderpriced := l.underpricedForMulti(&l.urgent, tx) + floatingUnderpriced := l.underpricedForMulti(&l.floating, tx) + return (urgentUnderpriced || l.urgent.Len() == 0) && + (floatingUnderpriced || l.floating.Len() == 0) && + (l.urgent.Len() != 0 || l.floating.Len() != 0) +} + +func (l *txPricedList) underpricedForMulti(h *multiCurrencyPriceHeap, tx *types.Transaction) bool { + underpriced := l.underpricedFor(h.nativeCurrencyHeap, tx) + for _, sh := range h.currencyHeaps { + if l.underpricedFor(sh, tx) { + underpriced = true + } + } + return underpriced }   // underpricedFor checks whether a transaction is cheaper than (or as cheap as) the @@ -565,22 +734,22 @@ // Note local transaction won't be considered for eviction. func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) { drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop for slots > 0 { - if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio || floatingRatio == 0 { + if l.urgent.Len()*floatingRatio > l.floating.Len()*urgentRatio || floatingRatio == 0 { // Discard stale transactions if found during cleanup - tx := heap.Pop(&l.urgent).(*types.Transaction) + tx := l.urgent.Pop() if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated atomic.AddInt64(&l.stales, -1) continue } // Non stale transaction found, move to floating heap - heap.Push(&l.floating, tx) + l.floating.Push(tx) } else { - if len(l.floating.list) == 0 { + if l.floating.Len() == 0 { // Stop if both heaps are empty break } // Discard stale transactions if found during cleanup - tx := heap.Pop(&l.floating).(*types.Transaction) + tx := l.floating.Pop() if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated atomic.AddInt64(&l.stales, -1) continue @@ -593,7 +762,7 @@ } // If we still can't make enough room for the new transaction if slots > 0 && !force { for _, tx := range drop { - heap.Push(&l.urgent, tx) + l.urgent.Push(tx) } return nil, false } @@ -606,30 +775,30 @@ l.reheapMu.Lock() defer l.reheapMu.Unlock() start := time.Now() atomic.StoreInt64(&l.stales, 0) - l.urgent.list = make([]*types.Transaction, 0, l.all.RemoteCount()) + l.urgent.Clear() l.all.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool { - l.urgent.list = append(l.urgent.list, tx) + l.urgent.Add(tx) return true }, false, true) // Only iterate remotes - heap.Init(&l.urgent) + l.urgent.Init()   // balance out the two heaps by moving the worse half of transactions into the // floating heap // Note: Discard would also do this before the first eviction but Reheap can do // is more efficiently. Also, Underpriced would work suboptimally the first time // if the floating queue was empty. - floatingCount := len(l.urgent.list) * floatingRatio / (urgentRatio + floatingRatio) - l.floating.list = make([]*types.Transaction, floatingCount) + floatingCount := l.urgent.Len() * floatingRatio / (urgentRatio + floatingRatio) + l.floating.Clear() for i := 0; i < floatingCount; i++ { - l.floating.list[i] = heap.Pop(&l.urgent).(*types.Transaction) + l.floating.Add(l.urgent.Pop()) } - heap.Init(&l.floating) + l.floating.Init() reheapTimer.Update(time.Since(start)) }   // SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not // necessary to call right before SetBaseFee when processing a new block. -func (l *txPricedList) SetBaseFee(baseFee *big.Int) { - l.urgent.baseFee = baseFee +func (l *txPricedList) SetBaseFee(txCtx *txPoolContext) { + l.urgent.UpdateFeesAndCurrencies(txCtx.CmpValues, txCtx.SysContractCallCtx.GetCurrentGasPriceMinimumMap()) l.Reheap() }
diff --git go-ethereum/core/gen_genesis.go celo/core/gen_genesis.go index 889ae5636cc07645841c51a49330aa4f803b0fc9..8af4d3194724733c6146a02130042291548a69d5 100644 --- go-ethereum/core/gen_genesis.go +++ celo/core/gen_genesis.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" - "math/big"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -19,27 +18,18 @@ // MarshalJSON marshals as JSON. func (g Genesis) MarshalJSON() ([]byte, error) { type Genesis struct { Config *params.ChainConfig `json:"config"` - Nonce math.HexOrDecimal64 `json:"nonce"` Timestamp math.HexOrDecimal64 `json:"timestamp"` ExtraData hexutil.Bytes `json:"extraData"` - GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` - Mixhash common.Hash `json:"mixHash"` Coinbase common.Address `json:"coinbase"` Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"` Number math.HexOrDecimal64 `json:"number"` GasUsed math.HexOrDecimal64 `json:"gasUsed"` ParentHash common.Hash `json:"parentHash"` - BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` } var enc Genesis enc.Config = g.Config - enc.Nonce = math.HexOrDecimal64(g.Nonce) enc.Timestamp = math.HexOrDecimal64(g.Timestamp) enc.ExtraData = g.ExtraData - enc.GasLimit = math.HexOrDecimal64(g.GasLimit) - enc.Difficulty = (*math.HexOrDecimal256)(g.Difficulty) - enc.Mixhash = g.Mixhash enc.Coinbase = g.Coinbase if g.Alloc != nil { enc.Alloc = make(map[common.UnprefixedAddress]GenesisAccount, len(g.Alloc)) @@ -50,7 +40,6 @@ } enc.Number = math.HexOrDecimal64(g.Number) enc.GasUsed = math.HexOrDecimal64(g.GasUsed) enc.ParentHash = g.ParentHash - enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee) return json.Marshal(&enc) }   @@ -58,18 +47,13 @@ // UnmarshalJSON unmarshals from JSON. func (g *Genesis) UnmarshalJSON(input []byte) error { type Genesis struct { Config *params.ChainConfig `json:"config"` - Nonce *math.HexOrDecimal64 `json:"nonce"` Timestamp *math.HexOrDecimal64 `json:"timestamp"` ExtraData *hexutil.Bytes `json:"extraData"` - GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` - Mixhash *common.Hash `json:"mixHash"` Coinbase *common.Address `json:"coinbase"` Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"` Number *math.HexOrDecimal64 `json:"number"` GasUsed *math.HexOrDecimal64 `json:"gasUsed"` ParentHash *common.Hash `json:"parentHash"` - BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` } var dec Genesis if err := json.Unmarshal(input, &dec); err != nil { @@ -78,26 +62,12 @@ } if dec.Config != nil { g.Config = dec.Config } - if dec.Nonce != nil { - g.Nonce = uint64(*dec.Nonce) - } if dec.Timestamp != nil { g.Timestamp = uint64(*dec.Timestamp) } if dec.ExtraData != nil { g.ExtraData = *dec.ExtraData } - if dec.GasLimit == nil { - return errors.New("missing required field 'gasLimit' for Genesis") - } - g.GasLimit = uint64(*dec.GasLimit) - if dec.Difficulty == nil { - return errors.New("missing required field 'difficulty' for Genesis") - } - g.Difficulty = (*big.Int)(dec.Difficulty) - if dec.Mixhash != nil { - g.Mixhash = *dec.Mixhash - } if dec.Coinbase != nil { g.Coinbase = *dec.Coinbase } @@ -116,9 +86,6 @@ g.GasUsed = uint64(*dec.GasUsed) } if dec.ParentHash != nil { g.ParentHash = *dec.ParentHash - } - if dec.BaseFee != nil { - g.BaseFee = (*big.Int)(dec.BaseFee) } return nil }
diff --git go-ethereum/core/bench_test.go celo/core/bench_test.go index b3036a225409c6c28c3fcb347a99c0aecc3c4cac..aa8ada49e8abdc6ee5838b181f32614869efdeb5 100644 --- go-ethereum/core/bench_test.go +++ celo/core/bench_test.go @@ -25,7 +25,7 @@ "testing"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -52,12 +52,6 @@ } func BenchmarkInsertChain_valueTx_100kB_diskdb(b *testing.B) { benchInsertChain(b, true, genValueTx(100*1024)) } -func BenchmarkInsertChain_uncles_memdb(b *testing.B) { - benchInsertChain(b, false, genUncles) -} -func BenchmarkInsertChain_uncles_diskdb(b *testing.B) { - benchInsertChain(b, true, genUncles) -} func BenchmarkInsertChain_ring200_memdb(b *testing.B) { benchInsertChain(b, false, genTxRing(200)) } @@ -85,8 +79,8 @@ func genValueTx(nbytes int) func(int, *BlockGen) { return func(i int, gen *BlockGen) { toaddr := common.Address{} data := make([]byte, nbytes) - gas, _ := IntrinsicGas(data, nil, false, false, false) - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey) + gas, _ := IntrinsicGas(data, nil, false, nil, 0, false) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, nil, nil, nil, data), types.HomesteadSigner{}, benchRootKey) gen.AddTx(tx) } } @@ -111,8 +105,7 @@ // and fills the blocks with many small transactions. func genTxRing(naccounts int) func(int, *BlockGen) { from := 0 return func(i int, gen *BlockGen) { - block := gen.PrevBlock(i - 1) - gas := block.GasLimit() + gas := params.DefaultGasLimit for { gas -= params.TxGas if gas < params.TxGas { @@ -126,6 +119,9 @@ benchRootFunds, params.TxGas, nil, nil, + nil, + nil, + nil, ) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, ringKeys[from]) gen.AddTx(tx) @@ -134,18 +130,6 @@ } } }   -// genUncles generates blocks with two uncle headers. -func genUncles(i int, gen *BlockGen) { - if i >= 6 { - b2 := gen.PrevBlock(i - 6).Header() - b2.Extra = []byte("foo") - gen.AddUncle(b2) - b3 := gen.PrevBlock(i - 6).Header() - b3.Extra = []byte("bar") - gen.AddUncle(b3) - } -} - func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Create the database in memory or in a temporary directory. var db ethdb.Database @@ -167,15 +151,15 @@ // Generate a chain of b.N blocks using the supplied block // generator function. gspec := Genesis{ - Config: params.TestChainConfig, + Config: params.IstanbulTestChainConfig, Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}}, } genesis := gspec.MustCommit(db) - chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, b.N, gen) + chain, _ := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), db, b.N, gen)   // Time the insertion of the new chain. // State and blocks are stored in the same DB. - chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + chainman, _ := NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer chainman.Stop() b.ReportAllocs() b.ResetTimer() @@ -230,8 +214,6 @@ header := &types.Header{ Coinbase: common.Address{}, Number: big.NewInt(int64(n)), ParentHash: hash, - Difficulty: big.NewInt(1), - UncleHash: types.EmptyUncleHash, TxHash: types.EmptyRootHash, ReceiptHash: types.EmptyRootHash, } @@ -287,7 +269,7 @@ db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false) if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } - chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + chain, err := NewBlockChain(db, nil, params.IstanbulTestChainConfig, mockEngine.NewFaker(), vm.Config{}, nil, nil) if err != nil { b.Fatalf("error creating chain: %v", err) }
diff --git go-ethereum/core/block_receipt.go celo/core/block_receipt.go new file mode 100644 index 0000000000000000000000000000000000000000..8db23d1c52c3b109458c6f80f7f72450a838c1cd --- /dev/null +++ celo/core/block_receipt.go @@ -0,0 +1,42 @@ +// Copyright 2021 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" +) + +// AddBlockReceipt checks whether logs were emitted by the core contract calls made as part +// of block processing outside of transactions. If there are any, it creates a receipt for +// them (the so-called "block receipt") and appends it to receipts +func AddBlockReceipt(receipts types.Receipts, statedb *state.StateDB, blockHash common.Hash) types.Receipts { + blockLogs := statedb.GetLogs(common.Hash{}, blockHash) + if len(blockLogs) > 0 { + receipt := types.NewReceipt(nil, false, 0) + receipt.Logs = blockLogs + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + for i := range receipt.Logs { + receipt.Logs[i].TxIndex = uint(len(receipts)) + receipt.Logs[i].TxHash = blockHash + receipt.Logs[i].BlockHash = blockHash + } + receipts = append(receipts, receipt) + } + return receipts +}
diff --git go-ethereum/core/tx_multicurrency_priceheap.go celo/core/tx_multicurrency_priceheap.go new file mode 100644 index 0000000000000000000000000000000000000000..0b9e096bb41feb937b794f9ddbda1c6f26d0bda5 --- /dev/null +++ celo/core/tx_multicurrency_priceheap.go @@ -0,0 +1,132 @@ +package core + +import ( + "container/heap" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +type CurrencyCmpFn func(*big.Int, *common.Address, *big.Int, *common.Address) int + +// IsCheaper returns true if tx1 is cheaper than tx2 (GasPrice with currency comparison) +func (cc CurrencyCmpFn) IsCheaper(tx1, tx2 *types.Transaction) bool { + return cc(tx1.GasPrice(), tx1.FeeCurrency(), tx2.GasPrice(), tx2.FeeCurrency()) < 0 +} + +// multiCurrencyPriceHeap is a heap.Interface implementation over transactions +// with different fee currencies for retrieving price-sorted transactions to discard +// when the pool fills up. If baseFee is set then the heap is sorted based on the +// effective tip based on the given base fee. If baseFee is nil then the sorting +// is based on gasFeeCap. +type multiCurrencyPriceHeap struct { + currencyCmp CurrencyCmpFn + gpm GasPriceMinimums // heap should always be re-sorted after gas price minimums (baseFees) is changed + currencyHeaps map[common.Address]*priceHeap // Heap of prices of all the stored non-nil currency transactions + nativeCurrencyHeap *priceHeap // Heap of prices of all the stored nil currency transactions +} + +func newMultiCurrencyPriceHeap(currencyCmp CurrencyCmpFn, gpm GasPriceMinimums) multiCurrencyPriceHeap { + return multiCurrencyPriceHeap{ + currencyCmp: currencyCmp, + gpm: gpm, + + // inner state + + nativeCurrencyHeap: &priceHeap{}, + currencyHeaps: make(map[common.Address]*priceHeap), + } +} + +// getHeapFor returns the proper heap for the given transaction, and creates it +// if it's not available in the currencyHeaps +func (h *multiCurrencyPriceHeap) getHeapFor(tx *types.Transaction) *priceHeap { + fc := tx.FeeCurrency() + if fc == nil { + return h.nativeCurrencyHeap + } + if _, ok := h.currencyHeaps[*fc]; !ok { + h.currencyHeaps[*fc] = &priceHeap{ + baseFee: h.gpm.GetGasPriceMinimum(fc), + } + } + return h.currencyHeaps[*fc] +} + +// Add to the heap. Must call Init afterwards to retain the heap invariants. +func (h *multiCurrencyPriceHeap) Add(tx *types.Transaction) { + ph := h.getHeapFor(tx) + ph.list = append(ph.list, tx) +} + +// Push to the heap, maintains heap invariants. +func (h *multiCurrencyPriceHeap) Push(tx *types.Transaction) { + ph := h.getHeapFor(tx) + heap.Push(ph, tx) +} + +func (h *multiCurrencyPriceHeap) cheapestTxs() []*types.Transaction { + txs := make([]*types.Transaction, 0, 1+len(h.currencyHeaps)) + if len(h.nativeCurrencyHeap.list) > 0 { + txs = append(txs, h.nativeCurrencyHeap.list[0]) + } + for _, ph := range h.currencyHeaps { + if len(ph.list) > 0 { + txs = append(txs, ph.list[0]) + } + } + return txs +} + +func (h *multiCurrencyPriceHeap) cheapestTx() *types.Transaction { + txs := h.cheapestTxs() + var cheapestTx *types.Transaction + for _, tx := range txs { + if cheapestTx == nil || h.currencyCmp.IsCheaper(tx, cheapestTx) { + cheapestTx = tx + } + } + return cheapestTx +} + +func (h *multiCurrencyPriceHeap) Pop() *types.Transaction { + cheapestTx := h.cheapestTx() + if cheapestTx == nil { + return nil + } + ph := h.getHeapFor(cheapestTx) + return heap.Pop(ph).(*types.Transaction) +} + +func (h *multiCurrencyPriceHeap) Len() int { + r := len(h.nativeCurrencyHeap.list) + for _, priceHeap := range h.currencyHeaps { + r += len(priceHeap.list) + } + return r +} + +func (h *multiCurrencyPriceHeap) Init() { + heap.Init(h.nativeCurrencyHeap) + for _, priceHeap := range h.currencyHeaps { + heap.Init(priceHeap) + } +} + +func (h *multiCurrencyPriceHeap) Clear() { + h.nativeCurrencyHeap.list = nil + for _, priceHeap := range h.currencyHeaps { + priceHeap.list = nil + } +} + +func (h *multiCurrencyPriceHeap) UpdateFeesAndCurrencies(currencyCmpFn CurrencyCmpFn, gpm GasPriceMinimums) { + h.currencyCmp = currencyCmpFn + h.gpm = gpm + h.nativeCurrencyHeap.baseFee = gpm.GetNativeGPM() + for currencyAddr, heap := range h.currencyHeaps { + heap.baseFee = gpm.GetGasPriceMinimum(&currencyAddr) + } + +}
diff --git go-ethereum/core/chain_makers.go celo/core/chain_makers.go index dd33ce929ed88e8e0c9237b981baa827ec3b2ced..3eb033657ee00a0c9058419085fc85be14ab8286 100644 --- go-ethereum/core/chain_makers.go +++ celo/core/chain_makers.go @@ -17,17 +17,20 @@ package core   import ( + "bytes" "fmt" "math/big"   + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/misc" + "github.com/ethereum/go-ethereum/contracts/testutil" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" )   // BlockGen creates blocks for testing. @@ -42,7 +45,6 @@ gasPool *GasPool txs []*types.Transaction receipts []*types.Receipt - uncles []*types.Header   config *params.ChainConfig engine consensus.Engine @@ -50,6 +52,10 @@ }   // SetCoinbase sets the coinbase of the generated block. // It can be called at most once. +// +// Note: This must be called after the parent and statedb +// are set or CalcGasLimit will return the wrong amount +// without throwing an error. func (b *BlockGen) SetCoinbase(addr common.Address) { if b.gasPool != nil { if len(b.txs) > 0 { @@ -58,7 +64,7 @@ } panic("coinbase can only be set once") } b.header.Coinbase = addr - b.gasPool = new(GasPool).AddGas(b.header.GasLimit) + b.gasPool = new(GasPool).AddGas(params.DefaultGasLimit) }   // SetExtra sets the extra data field of the generated block. @@ -66,18 +72,6 @@ func (b *BlockGen) SetExtra(data []byte) { b.header.Extra = data }   -// SetNonce sets the nonce field of the generated block. -func (b *BlockGen) SetNonce(nonce types.BlockNonce) { - b.header.Nonce = nonce -} - -// SetDifficulty sets the difficulty field of the generated block. This method is -// useful for Clique tests where the difficulty does not depend on time. For the -// ethash tests, please use OffsetTime, which implicitly recalculates the diff. -func (b *BlockGen) SetDifficulty(diff *big.Int) { - b.header.Difficulty = diff -} - // AddTx adds a transaction to the generated block. If no coinbase has // been set, the block's coinbase is set to the zero address. // @@ -98,12 +92,15 @@ // the protocol-imposed limitations (gas limit, etc.), there are some // further limitations on the content of transactions that can be // added. If contract code relies on the BLOCKHASH instruction, // the block in chain will be returned. -func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { +func (b *BlockGen) AddTxWithChain(bc ChainContext, tx *types.Transaction) { if b.gasPool == nil { b.SetCoinbase(common.Address{}) } b.statedb.Prepare(tx.Hash(), len(b.txs)) - receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) + + celoMock := testutil.NewCeloMock() + + receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}, celoMock.Runner, MockSysContractCallCtx()) if err != nil { panic(err) } @@ -111,6 +108,14 @@ b.txs = append(b.txs, tx) b.receipts = append(b.receipts, receipt) }   +// MockSysContractCallCtx returns a SysContractCallCtx mock. +func MockSysContractCallCtx() *SysContractCallCtx { + return &SysContractCallCtx{ + // Set common.ZeroAddress to non-zero value to test on proper base fee distribution + gasPriceMinimums: map[common.Address]*big.Int{common.ZeroAddress: common.Big3}, + } +} + // GetBalance returns the balance of the given address at the generated block. func (b *BlockGen) GetBalance(addr common.Address) *big.Int { return b.statedb.GetBalance(addr) @@ -130,11 +135,6 @@ func (b *BlockGen) Number() *big.Int { return new(big.Int).Set(b.header.Number) }   -// BaseFee returns the EIP-1559 base fee of the block being generated. -func (b *BlockGen) BaseFee() *big.Int { - return new(big.Int).Set(b.header.BaseFee) -} - // AddUncheckedReceipt forcefully adds a receipts to the block without a // backing transaction. // @@ -153,11 +153,6 @@ } return b.statedb.GetNonce(addr) }   -// AddUncle adds an uncle header to the generated block. -func (b *BlockGen) AddUncle(h *types.Header) { - b.uncles = append(b.uncles, h) -} - // PrevBlock returns a previously generated block by number. It panics if // num is greater or equal to the number of the block being generated. // For index -1, PrevBlock returns the parent block given to GenerateChain. @@ -171,16 +166,20 @@ } return b.chain[index] }   -// OffsetTime modifies the time instance of a block, implicitly changing its -// associated difficulty. It's useful to test scenarios where forking is not -// tied to chain length directly. +// OffsetTime modifies the time instance of a block. It's useful to test +// scenarios where forking is not tied to chain length directly. +// NOTE: `gen.OffsetTime(int)` is used throughout the code in this test file to adjust the total difficulty. +// This made sense with Ethhash, but is no longer relevant to Istanbul consensus because difficulty is constant. +// These calls can likely be removed, but have not been as a matter of simplicity. func (b *BlockGen) OffsetTime(seconds int64) { b.header.Time += uint64(seconds) if b.header.Time <= b.parent.Header().Time { panic("block time out of range") } - chainreader := &fakeChainReader{config: b.config} - b.header.Difficulty = b.engine.CalcDifficulty(chainreader, b.header.Time, b.parent.Header()) +} + +func (b *BlockGen) Config() *params.ChainConfig { + return b.config }   // GenerateChain creates a chain of n blocks. The first block's @@ -188,13 +187,13 @@ // parent will be the provided parent. db is used to store // intermediate states and should contain the parent's state trie. // // The generator function is called with a new block generator for -// every block. Any transactions and uncles added to the generator +// every block. Any transactions added to the generator // become part of the block. If gen is nil, the blocks will be empty // and their coinbase will be the zero address. // // Blocks created by GenerateChain do not contain valid proof of work -// values. Inserting them into BlockChain requires use of FakePow or -// a similar non-validating proof of work implementation. +// values. Inserting them into BlockChain requires use of +// a non-validating proof of work implementation. func GenerateChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { if config == nil { config = params.TestChainConfig @@ -205,25 +204,13 @@ genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) { b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine} b.header = makeHeader(chainreader, parent, statedb, b.engine)   - // Mutate the state and block according to any hard-fork specs - if daoBlock := config.DAOForkBlock; daoBlock != nil { - limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) - if b.header.Number.Cmp(daoBlock) >= 0 && b.header.Number.Cmp(limit) < 0 { - if config.DAOForkSupport { - b.header.Extra = common.CopyBytes(params.DAOForkBlockExtra) - } - } - } - if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 { - misc.ApplyDAOHardFork(statedb) - } // Execute any user modifications to the block if gen != nil { gen(i, b) } if b.engine != nil { // Finalize and seal the block - block, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts) + block, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.receipts, nil)   // Write state changes to db root, err := statedb.Commit(config.IsEIP158(b.header.Number)) @@ -250,7 +237,27 @@ } return blocks, receipts }   -func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header { +// Modified from 'func writeEmptyIstanbulExtra(header *types.Header) error' from consensus/backend/engine.go +func CreateEmptyIstanbulExtra(vanity []byte) []byte { + extra := types.IstanbulExtra{ + AddedValidators: []common.Address{}, + AddedValidatorsPublicKeys: []blscrypto.SerializedPublicKey{}, + RemovedValidators: big.NewInt(0), + Seal: []byte{}, + AggregatedSeal: types.IstanbulAggregatedSeal{}, + ParentAggregatedSeal: types.IstanbulAggregatedSeal{}, + } + payload, _ := rlp.EncodeToBytes(&extra) + + if len(vanity) < types.IstanbulExtraVanity { + vanity = append(vanity, bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity-len(vanity))...) + } + vanity = append(vanity[:types.IstanbulExtraVanity], payload...) + + return vanity +} + +func makeHeader(chain consensus.ChainHeaderReader, parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header { var time uint64 if parent.Time() == 0 { time = 10 @@ -261,23 +268,11 @@ header := &types.Header{ Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), - Difficulty: engine.CalcDifficulty(chain, time, &types.Header{ - Number: parent.Number(), - Time: time - 10, - Difficulty: parent.Difficulty(), - UncleHash: parent.UncleHash(), - }), - GasLimit: parent.GasLimit(), Number: new(big.Int).Add(parent.Number(), common.Big1), Time: time, } - if chain.Config().IsLondon(header.Number) { - header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header()) - if !chain.Config().IsLondon(parent.Number()) { - parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier - header.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit) - } - } + // Properly set the extra data field + header.Extra = CreateEmptyIstanbulExtra(header.Extra) return header }   @@ -313,3 +308,4 @@ func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *types.Header { return nil } func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *types.Header { return nil } func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { return nil } func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil } +func (cr *fakeChainReader) StateAt(root common.Hash) (*state.StateDB, error) { return nil, nil }
diff --git go-ethereum/core/state_processor_test.go celo/core/state_processor_test.go index e6fd49143410fe3c6ff3757bf66d063c64a8dc2e..a9f96be406fdb980b8bc26cf91f7b2f6c618a47c 100644 --- go-ethereum/core/state_processor_test.go +++ celo/core/state_processor_test.go @@ -22,8 +22,7 @@ "testing"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/consensus/misc" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -49,18 +48,30 @@ ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - Ethash: new(params.EthashConfig), + ChurritoBlock: big.NewInt(0), + DonutBlock: big.NewInt(0), + EspressoBlock: big.NewInt(0), + GForkBlock: big.NewInt(0), + Faker: true, } signer = types.LatestSigner(config) testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") ) - var makeTx = func(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction { - tx, _ := types.SignTx(types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data), signer, testKey) + var makeTx = func(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, feeCurrency *common.Address, gatewayFeeRecipient *common.Address, gatewayFee *big.Int, data []byte) *types.Transaction { + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: nonce, + GasPrice: gasPrice, + Gas: gasLimit, + FeeCurrency: feeCurrency, + GatewayFeeRecipient: gatewayFeeRecipient, + GatewayFee: gatewayFee, + To: &to, + Value: amount, + Data: data, + }), signer, testKey) return tx } + var mkDynamicTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int) *types.Transaction { tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{ Nonce: nonce, @@ -79,88 +90,100 @@ gspec = &Genesis{ Config: config, Alloc: GenesisAlloc{ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ - Balance: big.NewInt(1000000000000000000), // 1 ether + Balance: big.NewInt(params.Ether), Nonce: 0, }, }, } genesis = gspec.MustCommit(db) - blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) ) defer blockchain.Stop() bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) tooBigNumber := new(big.Int).Set(bigNumber) tooBigNumber.Add(tooBigNumber, common.Big1) for i, tt := range []struct { + name string txs []*types.Transaction want string }{ - { // ErrNonceTooLow + { + name: "ErrNonceTooLow", txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil, nil, nil, nil), + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil, nil, nil, nil), }, - want: "could not apply tx 1 [0x0026256b3939ed97e2c4a6f3fce8ecf83bdcfa6d507c47838c308a1fb0436f62]: nonce too low: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1", + want: "could not apply tx 1 [0x007b02f957123ca016999a9e826e6dfd4f7b36d2406f0057526aeb9a6202adfa]: nonce too low: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1", }, - { // ErrNonceTooHigh + { + name: "ErrNonceTooHigh", txs: []*types.Transaction{ - makeTx(100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), + makeTx(100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil, nil, nil, nil), }, - want: "could not apply tx 0 [0xdebad714ca7f363bd0d8121c4518ad48fa469ca81b0a081be3d10c17460f751b]: nonce too high: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0", + want: "could not apply tx 0 [0xa0f823b5e4c81b1b0528531f25f06514b8d8c766de99747ad3137577987ff0b7]: nonce too high: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0", }, - { // ErrGasLimitReached + { + name: "ErrGasLimitReached", txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil), + makeTx(0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil, nil, nil, nil), }, - want: "could not apply tx 0 [0xbd49d8dadfd47fb846986695f7d4da3f7b2c48c8da82dbc211a26eb124883de9]: gas limit reached", + want: "could not apply tx 0 [0xbd4180e51b03a814b5c96016cc47575fdf777288fc165dc5ad3f9dd3e0e8dcee]: gas limit reached", }, - { // ErrInsufficientFundsForTransfer + { + name: "ErrInsufficientFundsForTransfer", txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil), + makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil, nil, nil, nil), }, - want: "could not apply tx 0 [0x98c796b470f7fcab40aaef5c965a602b0238e1034cce6fb73823042dd0638d74]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1000018375000000000", + want: "could not apply tx 0 [0x4610b4dc2b2508d3555499e34eed7315ad3b5215ec8dbc1cb22bda45e1667cf6]: insufficient funds for gas * price + value + gatewayFee: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1000018375000000000", }, - { // ErrInsufficientFunds + { + name: "ErrInsufficientFunds", txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil), + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil, nil, nil, nil), }, - want: "could not apply tx 0 [0x4a69690c4b0cd85e64d0d9ea06302455b01e10a83db964d60281739752003440]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 18900000000000000000000", + want: "could not apply tx 0 [0x99113426d253b063416fc51259f7f78c832d49a9bf09f2748fe50fd3130e7162]: insufficient funds for gas * price + value + gatewayFee: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 18900000000000000000000", }, // ErrGasUintOverflow // One missing 'core' error is ErrGasUintOverflow: "gas uint64 overflow", // In order to trigger that one, we'd have to allocate a _huge_ chunk of data, such that the // multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment - { // ErrIntrinsicGas + { + name: "ErrIntrinsicGas", txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(875000000), nil), + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(875000000), nil, nil, nil, nil), }, - want: "could not apply tx 0 [0xcf3b049a0b516cb4f9274b3e2a264359e2ba53b2fb64b7bda2c634d5c9d01fca]: intrinsic gas too low: have 20000, want 21000", + want: "could not apply tx 0 [0x436f2e580794590a8627d8155b638c4ed1b41375283ba19f13a5a8b76ed51fdc]: intrinsic gas too low: have 20000, want 21000", }, - { // ErrGasLimitReached + { + name: "ErrGasLimitReached", txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil), + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil, nil, nil, nil), }, - want: "could not apply tx 0 [0xbd49d8dadfd47fb846986695f7d4da3f7b2c48c8da82dbc211a26eb124883de9]: gas limit reached", + want: "could not apply tx 0 [0xbd4180e51b03a814b5c96016cc47575fdf777288fc165dc5ad3f9dd3e0e8dcee]: gas limit reached", }, - { // ErrFeeCapTooLow + { + name: "ErrFeeCapTooLow", txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)), }, - want: "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0 baseFee: 875000000", + want: "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0 baseFee: 3", // MockSysContractCallCtx expects baseFee to be 3 }, - { // ErrTipVeryHigh + { + name: "ErrTipVeryHigh", txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, tooBigNumber, big.NewInt(1)), }, want: "could not apply tx 0 [0x15b8391b9981f266b32f3ab7da564bbeb3d6c21628364ea9b32a21139f89f712]: max priority fee per gas higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas bit length: 257", }, - { // ErrFeeCapVeryHigh + { + name: "ErrFeeCapVeryHigh", txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), tooBigNumber), }, want: "could not apply tx 0 [0x48bc299b83fdb345c57478f239e89814bb3063eb4e4b49f3b6057a69255c16bd]: max fee per gas higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas bit length: 257", }, - { // ErrTipAboveFeeCap + { + name: "ErrTipAboveFeeCap", txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(2), big.NewInt(1)), }, @@ -168,29 +191,31 @@ want: "could not apply tx 0 [0xf987a31ff0c71895780a7612f965a0c8b056deb54e020bb44fa478092f14c9b4]: max priority fee per gas higher than max fee per gas: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas: 2, maxFeePerGas: 1", }, { // ErrInsufficientFunds // Available balance: 1000000000000000000 - // Effective cost: 18375000021000 + // Effective cost: 84000 // FeeCap * gas: 1050000000000000000 // This test is designed to have the effective cost be covered by the balance, but // the extended requirement on FeeCap*gas < balance to fail + name: "ErrInsufficientFunds", txs: []*types.Transaction{ - mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(50000000000000)), + mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(50_000_000_000_000)), }, - want: "could not apply tx 0 [0x413603cd096a87f41b1660d3ed3e27d62e1da78eac138961c0a1314ed43bd129]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1050000000000000000", + want: "could not apply tx 0 [0x413603cd096a87f41b1660d3ed3e27d62e1da78eac138961c0a1314ed43bd129]: insufficient funds for gas * price + value + gatewayFee: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1050000000000000000", }, { // Another ErrInsufficientFunds, this one to ensure that feecap/tip of max u256 is allowed + name: "Another ErrInsufficientFunds", txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber), }, - want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000", + want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value + gatewayFee: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000", }, } { - block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config) + block := GenerateBadBlock(genesis, mockEngine.NewFaker(), tt.txs, gspec.Config) _, err := blockchain.InsertChain(types.Blocks{block}) if err == nil { t.Fatal("block imported without errors") } if have, want := err.Error(), tt.want; have != want { - t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want) + t.Errorf("test %d %v:\nhave \"%v\"\nwant \"%v\"\n", i, tt.name, have, want) } } } @@ -210,7 +235,6 @@ ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), }, Alloc: GenesisAlloc{ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ @@ -220,7 +244,7 @@ }, }, } genesis = gspec.MustCommit(db) - blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) ) defer blockchain.Stop() for i, tt := range []struct { @@ -234,7 +258,7 @@ }, want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: transaction type not supported", }, } { - block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config) + block := GenerateBadBlock(genesis, mockEngine.NewFaker(), tt.txs, gspec.Config) _, err := blockchain.InsertChain(types.Blocks{block}) if err == nil { t.Fatal("block imported without errors") @@ -245,45 +269,46 @@ } } }   - // ErrSenderNoEOA, for this we need the sender to have contract code - { - var ( - db = rawdb.NewMemoryDatabase() - gspec = &Genesis{ - Config: config, - Alloc: GenesisAlloc{ - common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ - Balance: big.NewInt(1000000000000000000), // 1 ether - Nonce: 0, - Code: common.FromHex("0xB0B0FACE"), - }, - }, - } - genesis = gspec.MustCommit(db) - blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) - ) - defer blockchain.Stop() - for i, tt := range []struct { - txs []*types.Transaction - want string - }{ - { // ErrSenderNoEOA - txs: []*types.Transaction{ - mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)), - }, - want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1", - }, - } { - block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config) - _, err := blockchain.InsertChain(types.Blocks{block}) - if err == nil { - t.Fatal("block imported without errors") - } - if have, want := err.Error(), tt.want; have != want { - t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want) - } - } - } + // // We do not check for EOAs at this time. + // // ErrSenderNoEOA, for this we need the sender to have contract code + // { + // var ( + // db = rawdb.NewMemoryDatabase() + // gspec = &Genesis{ + // Config: config, + // Alloc: GenesisAlloc{ + // common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ + // Balance: big.NewInt(1000000000000000000), // 1 ether + // Nonce: 0, + // Code: common.FromHex("0xB0B0FACE"), + // }, + // }, + // } + // genesis = gspec.MustCommit(db) + // blockchain, _ = NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) + // ) + // defer blockchain.Stop() + // for i, tt := range []struct { + // txs []*types.Transaction + // want string + // }{ + // { // ErrSenderNoEOA + // txs: []*types.Transaction{ + // mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)), + // }, + // want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1", + // }, + // } { + // block := GenerateBadBlock(genesis, mockEngine.NewFaker(), tt.txs, gspec.Config) + // _, err := blockchain.InsertChain(types.Blocks{block}) + // if err == nil { + // t.Fatal("block imported without errors") + // } + // if have, want := err.Error(), tt.want; have != want { + // t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want) + // } + // } + // } }   // GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be @@ -294,20 +319,12 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block { header := &types.Header{ ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), - Difficulty: engine.CalcDifficulty(&fakeChainReader{config}, parent.Time()+10, &types.Header{ - Number: parent.Number(), - Time: parent.Time(), - Difficulty: parent.Difficulty(), - UncleHash: parent.UncleHash(), - }), - GasLimit: parent.GasLimit(), Number: new(big.Int).Add(parent.Number(), common.Big1), Time: parent.Time() + 10, - UncleHash: types.EmptyUncleHash, - } - if config.IsLondon(header.Number) { - header.BaseFee = misc.CalcBaseFee(config, parent.Header()) + Extra: CreateEmptyIstanbulExtra(nil), } + header.Extra = CreateEmptyIstanbulExtra(header.Extra) + var receipts []*types.Receipt // The post-state result doesn't need to be correct (this is a bad block), but we do need something there // Preferably something unique. So let's use a combo of blocknum + txhash @@ -325,5 +342,6 @@ cumulativeGas += tx.Gas() } header.Root = common.BytesToHash(hasher.Sum(nil)) // Assemble and return the final block for sealing - return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)) + return types.NewBlock(header, txs, receipts, nil, trie.NewStackTrie(nil)) + }
diff --git go-ethereum/core/chain_indexer.go celo/core/chain_indexer.go index 873d8125ea8d07203eccedf13f8fd5d0e1fc5e6f..4ada0ea228fa32527e8cd813aeb110d4bff6006d 100644 --- go-ethereum/core/chain_indexer.go +++ celo/core/chain_indexer.go @@ -95,12 +95,14 @@ throttling time.Duration // Disk throttling to prevent a heavy upgrade from hogging resources   log log.Logger lock sync.Mutex + // True in all sync modes except LightestSync + fullHeaderChainDownloaded bool }   // NewChainIndexer creates a new chain indexer to do background processing on // chain segments of a given size after certain number of confirmations passed. // The throttling parameter might be used to prevent database thrashing. -func NewChainIndexer(chainDb ethdb.Database, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer { +func NewChainIndexer(chainDb ethdb.Database, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string, fullChainDownloaded bool) *ChainIndexer { c := &ChainIndexer{ chainDb: chainDb, indexDb: indexDb, @@ -111,6 +113,7 @@ sectionSize: section, confirmsReq: confirm, throttling: throttling, log: log.New("type", kind), + fullHeaderChainDownloaded: fullChainDownloaded, } // Initialize database dependent fields and start the updater c.loadValidSections() @@ -397,6 +400,9 @@ for number := section * c.sectionSize; number < (section+1)*c.sectionSize; number++ { hash := rawdb.ReadCanonicalHash(c.chainDb, number) if hash == (common.Hash{}) { + if !c.fullHeaderChainDownloaded { + return lastHead, nil + } return common.Hash{}, fmt.Errorf("canonical block #%d unknown", number) } header := rawdb.ReadHeader(c.chainDb, hash, number)
diff --git go-ethereum/core/tx_list_test.go celo/core/tx_list_test.go index 3a5842d2e8e32595b3b9a2707a07f4737d96893a..29c879fd12612b2d767ef2e16ad564520b0c0b42 100644 --- go-ethereum/core/tx_list_test.go +++ celo/core/tx_list_test.go @@ -17,7 +17,6 @@ package core   import ( - "math/big" "math/rand" "testing"   @@ -36,7 +35,7 @@ for i := 0; i < len(txs); i++ { txs[i] = transaction(uint64(i), 0, key) } // Insert the transactions in a random order - list := newTxList(true) + list := newTxList(true, nil) for _, v := range rand.Perm(len(txs)) { list.Add(txs[v], DefaultTxPoolConfig.PriceBump) } @@ -50,21 +49,3 @@ t.Errorf("item %d: transaction mismatch: have %v, want %v", i, list.txs.items[tx.Nonce()], tx) } } } - -func BenchmarkTxListAdd(t *testing.B) { - // Generate a list of transactions to insert - key, _ := crypto.GenerateKey() - - txs := make(types.Transactions, 100000) - for i := 0; i < len(txs); i++ { - txs[i] = transaction(uint64(i), 0, key) - } - // Insert the transactions in a random order - list := newTxList(true) - priceLimit := big.NewInt(int64(DefaultTxPoolConfig.PriceLimit)) - t.ResetTimer() - for _, v := range rand.Perm(len(txs)) { - list.Add(txs[v], DefaultTxPoolConfig.PriceBump) - list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump) - } -}
diff --git go-ethereum/core/block_validator_test.go celo/core/block_validator_test.go index c7f8ed389de57d409f06fd5132c77a9eee5b8364..b54546aeb4be168b47e5442ff032682d769abfce 100644 --- go-ethereum/core/block_validator_test.go +++ celo/core/block_validator_test.go @@ -21,7 +21,7 @@ "runtime" "testing" "time"   - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -33,16 +33,16 @@ func TestHeaderVerification(t *testing.T) { // Create a simple chain to verify var ( testdb = rawdb.NewMemoryDatabase() - gspec = &Genesis{Config: params.TestChainConfig} + gspec = &Genesis{Config: params.IstanbulTestChainConfig} genesis = gspec.MustCommit(testdb) - blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil) + blocks, _ = GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), testdb, 8, nil) ) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { headers[i] = block.Header() } // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces - chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + chain, _ := NewBlockChain(testdb, nil, params.IstanbulTestChainConfig, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer chain.Stop()   for i := 0; i < len(blocks); i++ { @@ -50,10 +50,10 @@ for j, valid := range []bool{true, false} { var results <-chan error   if valid { - engine := ethash.NewFaker() + engine := mockEngine.NewFaker() _, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}) } else { - engine := ethash.NewFakeFailer(headers[i].Number.Uint64()) + engine := mockEngine.NewFakeFailer(headers[i].Number.Uint64()) _, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}) } // Wait for the verification result @@ -85,9 +85,9 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) { // Create a simple chain to verify var ( testdb = rawdb.NewMemoryDatabase() - gspec = &Genesis{Config: params.TestChainConfig} + gspec = &Genesis{Config: params.IstanbulTestChainConfig} genesis = gspec.MustCommit(testdb) - blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil) + blocks, _ = GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), testdb, 8, nil) ) headers := make([]*types.Header, len(blocks)) seals := make([]bool, len(blocks)) @@ -106,11 +106,11 @@ for i, valid := range []bool{true, false} { var results <-chan error   if valid { - chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + chain, _ := NewBlockChain(testdb, nil, params.IstanbulTestChainConfig, mockEngine.NewFaker(), vm.Config{}, nil, nil) _, results = chain.engine.VerifyHeaders(chain, headers, seals) chain.Stop() } else { - chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil) + chain, _ := NewBlockChain(testdb, nil, params.IstanbulTestChainConfig, mockEngine.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil) _, results = chain.engine.VerifyHeaders(chain, headers, seals) chain.Stop() } @@ -157,9 +157,9 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) { // Create a simple chain to verify var ( testdb = rawdb.NewMemoryDatabase() - gspec = &Genesis{Config: params.TestChainConfig} + gspec = &Genesis{Config: params.IstanbulTestChainConfig} genesis = gspec.MustCommit(testdb) - blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 1024, nil) + blocks, _ = GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), testdb, 1024, nil) ) headers := make([]*types.Header, len(blocks)) seals := make([]bool, len(blocks)) @@ -173,7 +173,7 @@ old := runtime.GOMAXPROCS(threads) defer runtime.GOMAXPROCS(old)   // Start the verifications and immediately abort - chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil) + chain, _ := NewBlockChain(testdb, nil, params.IstanbulTestChainConfig, mockEngine.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil) defer chain.Stop()   abort, results := chain.engine.VerifyHeaders(chain, headers, seals) @@ -197,35 +197,3 @@ if verified > 2*threads { t.Errorf("verification count too large: have %d, want below %d", verified, 2*threads) } } - -func TestCalcGasLimit(t *testing.T) { - for i, tc := range []struct { - pGasLimit uint64 - max uint64 - min uint64 - }{ - {20000000, 20019530, 19980470}, - {40000000, 40039061, 39960939}, - } { - // Increase - if have, want := CalcGasLimit(tc.pGasLimit, 2*tc.pGasLimit), tc.max; have != want { - t.Errorf("test %d: have %d want <%d", i, have, want) - } - // Decrease - if have, want := CalcGasLimit(tc.pGasLimit, 0), tc.min; have != want { - t.Errorf("test %d: have %d want >%d", i, have, want) - } - // Small decrease - if have, want := CalcGasLimit(tc.pGasLimit, tc.pGasLimit-1), tc.pGasLimit-1; have != want { - t.Errorf("test %d: have %d want %d", i, have, want) - } - // Small increase - if have, want := CalcGasLimit(tc.pGasLimit, tc.pGasLimit+1), tc.pGasLimit+1; have != want { - t.Errorf("test %d: have %d want %d", i, have, want) - } - // No change - if have, want := CalcGasLimit(tc.pGasLimit, tc.pGasLimit), tc.pGasLimit; have != want { - t.Errorf("test %d: have %d want %d", i, have, want) - } - } -}
diff --git go-ethereum/core/chain_makers_test.go celo/core/chain_makers_test.go index 2022f95821fec36493337bdda8a6166eb293fe78..b97656f1394ce5533222e2ef2bab965ebf8665bb 100644 --- go-ethereum/core/chain_makers_test.go +++ celo/core/chain_makers_test.go @@ -20,7 +20,7 @@ import ( "fmt" "math/big"   - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -41,7 +41,7 @@ )   // Ensure that key1 has some funds in the genesis block. gspec := &Genesis{ - Config: &params.ChainConfig{HomesteadBlock: new(big.Int)}, + Config: &params.ChainConfig{HomesteadBlock: new(big.Int), Istanbul: &params.IstanbulConfig{}}, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}}, } genesis := gspec.MustCommit(db) @@ -50,36 +50,28 @@ // This call generates a chain of 5 blocks. The function runs for // each block and adds different features to gen based on the // block index. signer := types.HomesteadSigner{} - chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) { + chain, _ := GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), db, 5, func(i int, gen *BlockGen) { switch i { case 0: // In block 1, addr1 sends addr2 some ether. - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil, nil, nil, nil), signer, key1) gen.AddTx(tx) case 1: // In block 2, addr1 sends some more ether to addr2. // addr2 passes it on to addr3. - tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) - tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) + tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key1) + tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, key2) gen.AddTx(tx1) gen.AddTx(tx2) case 2: // Block 3 is empty but was mined by addr3. gen.SetCoinbase(addr3) - gen.SetExtra([]byte("yeehaw")) - case 3: - // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). - b2 := gen.PrevBlock(1).Header() - b2.Extra = []byte("foo") - gen.AddUncle(b2) - b3 := gen.PrevBlock(2).Header() - b3.Extra = []byte("foo") - gen.AddUncle(b3) + gen.SetExtra(CreateEmptyIstanbulExtra([]byte("yeehaw"))) } })   // Import the chain. This runs all block validation rules. - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) defer blockchain.Stop()   if i, err := blockchain.InsertChain(chain); err != nil { @@ -96,5 +88,5 @@ // Output: // last block: #5 // balance of addr1: 989000 // balance of addr2: 10000 - // balance of addr3: 19687500000000001000 + // balance of addr3: 1003 }
diff --git go-ethereum/core/tx_pool.go celo/core/tx_pool.go index cdb370b3f135197c4abb35980b14f2f7f3197351..41def9a367fb200bdce51b0bf364fa4a8fbb006d 100644 --- go-ethereum/core/tx_pool.go +++ celo/core/tx_pool.go @@ -21,15 +21,20 @@ "errors" "math" "math/big" "sort" + "strconv" "sync" "sync/atomic" "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/prque" - "github.com/ethereum/go-ethereum/consensus/misc" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" + "github.com/ethereum/go-ethereum/contracts/currency" + gpm "github.com/ethereum/go-ethereum/contracts/gasprice_minimum" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -85,6 +90,10 @@ // ErrOversizedData is returned if the input data of a transaction is greater // than some meaningful limit a user might use. This is not a consensus error // making the transaction invalid, rather a DOS protection. ErrOversizedData = errors.New("oversized data") + + // ErrTransfersFrozen is returned if a transaction attempts to transfer between + // non-whitelisted addresses while transfers are frozen. + ErrTransfersFrozen = errors.New("transfers are currently frozen") )   var ( @@ -139,14 +148,39 @@ TxStatusPending TxStatusIncluded )   +func (s TxStatus) String() string { + switch s { + case TxStatusUnknown: + return "TxStatusUnknown" + case TxStatusQueued: + return "TxStatusQueued" + case TxStatusPending: + return "TxStatusPending" + case TxStatusIncluded: + return "TxStatusIncluded" + default: + return strconv.FormatUint(uint64(s), 10) + } +} + // blockChain provides the state of blockchain and current gas limit to do // some pre checks in tx pool and event subscribers. type blockChain interface { CurrentBlock() *types.Block GetBlock(hash common.Hash, number uint64) *types.Block StateAt(root common.Hash) (*state.StateDB, error) + + NewEVMRunner(header *types.Header, state vm.StateDB) vm.EVMRunner   SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription + + // Engine retrieves the chain's consensus engine. + Engine() consensus.Engine + + // GetHeader returns the header corresponding to their hash. + GetHeader(common.Hash, uint64) *types.Header + + GetVMConfig() *vm.Config }   // TxPoolConfig are the configuration parameters of the transaction pool. @@ -173,7 +207,7 @@ var DefaultTxPoolConfig = TxPoolConfig{ Journal: "transactions.rlp", Rejournal: time.Hour,   - PriceLimit: 1, + PriceLimit: 0, PriceBump: 10,   AccountSlots: 16, @@ -192,10 +226,6 @@ if conf.Rejournal < time.Second { log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second) conf.Rejournal = time.Second } - if conf.PriceLimit < 1 { - log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit) - conf.PriceLimit = DefaultTxPoolConfig.PriceLimit - } if conf.PriceBump < 1 { log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump) conf.PriceBump = DefaultTxPoolConfig.PriceBump @@ -221,6 +251,12 @@ log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultTxPoolConfig.Lifetime) conf.Lifetime = DefaultTxPoolConfig.Lifetime } return conf +} + +type txPoolContext struct { + *SysContractCallCtx + *currency.CurrencyManager + celoGasPriceMinimumFloor *big.Int }   // TxPool contains all currently known transactions. Transactions @@ -241,12 +277,14 @@ signer types.Signer mu sync.RWMutex   istanbul bool // Fork indicator whether we are in the istanbul stage. - eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions. - eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions. + donut bool // Fork indicator for the Donut fork. + espresso bool // Fork indicator for the Espresso fork.   currentState *state.StateDB // Current state in the blockchain head + currentVMRunner vm.EVMRunner // Current EVMRunner pendingNonces *txNoncer // Pending state tracking virtual nonces currentMaxGas uint64 // Current gas limit for transaction caps + currentCtx atomic.Value // Current block context (holds a txPoolContext)   locals *accountSet // Set of local transaction to exempt from eviction rules journal *txJournal // Journal of local transaction to back up to disk @@ -255,7 +293,7 @@ pending map[common.Address]*txList // All currently processable transactions queue map[common.Address]*txList // Queued but non-processable transactions beats map[common.Address]time.Time // Last heartbeat from each known account all *txLookup // All transactions to allow lookups - priced *txPricedList // All transactions sorted by price + priced *txPricedList // All transactions sorted by price. One heap per fee currency.   chainHeadCh chan ChainHeadEvent chainHeadSub event.Subscription @@ -304,8 +342,12 @@ for _, addr := range config.Locals { log.Info("Setting new local account", "address", addr) pool.locals.add(addr) } - pool.priced = newTxPricedList(pool.all) + pool.reset(nil, chain.CurrentBlock().Header()) + // TODO: Does this reordering cause a problem? + // priced list depends on the current ctx which is set in reset + // Use the global slots as the max amount of stale transactions in the priced heap before a re-heap. + pool.priced = newTxPricedList(pool.all, &pool.currentCtx, int64(pool.config.GlobalSlots))   // Start the reorg loop early so it can handle requests generated during journal loading. pool.wg.Add(1) @@ -450,7 +492,7 @@ pool.gasPrice = price // if the min miner fee increased, remove transactions below the new threshold if price.Cmp(old) > 0 { // pool.priced is sorted by GasFeeCap, so we have to iterate through pool.all instead - drop := pool.all.RemotesBelowTip(price) + drop := pool.all.RemotesBelowTip(price, pool.ctx()) for _, tx := range drop { pool.removeTx(tx.Hash(), false) } @@ -460,6 +502,25 @@ log.Info("Transaction pool price threshold updated", "price", price) }   +// setGasLimit updates the maximum allowed gas for a new transaction in the +// pool, and drops all transactions above this threshold. +// +// DO NOT USE, ONLY FOR TESTING +func (pool *TxPool) setGasLimit(gasLimit uint64) { + pool.mu.Lock() + defer pool.mu.Unlock() + + pool.currentMaxGas = gasLimit + pool.demoteUnexecutables() + + for _, list := range pool.queue { + rm, _ := list.FilterOnGasLimit(gasLimit) + for _, tx := range rm { + pool.removeTx(tx.Hash(), false) + } + } +} + // Nonce returns the next nonce of an account, with all transactions executable // by the pool already applied on top. func (pool *TxPool) Nonce(addr common.Address) uint64 { @@ -543,8 +604,10 @@ txs := list.Flatten()   // If the miner requests tip enforcement, cap the lists now if enforceTips && !pool.locals.contains(addr) { + txCtx := pool.ctx() for i, tx := range txs { - if tx.EffectiveGasTipIntCmp(pool.gasPrice, pool.priced.urgent.baseFee) < 0 { + curr, _ := txCtx.GetCurrency(tx.FeeCurrency()) + if tx.EffectiveGasTipIntCmp(curr.FromCELO(pool.gasPrice), txCtx.GetGasPriceMinimum(tx.FeeCurrency())) < 0 { txs = txs[:i] break } @@ -581,15 +644,30 @@ } return txs }   +func (pool *TxPool) ctx() *txPoolContext { + ctx := pool.currentCtx.Load().(txPoolContext) + return &ctx +} + // validateTx checks whether a transaction is valid according to the consensus // rules and adheres to some heuristic limits of the local node (price and size). func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { + if pool.donut && !pool.espresso && !tx.Protected() { + return ErrUnprotectedTransaction + } + if tx.EthCompatible() && !pool.donut { + return ErrEthCompatibleTransactionsNotSupported + } + if err := tx.CheckEthCompatibility(); err != nil { + return err + } + // Accept only legacy transactions until EIP-2718/2930 activates. - if !pool.eip2718 && tx.Type() != types.LegacyTxType { + if !pool.espresso && tx.Type() != types.LegacyTxType { return ErrTxTypeNotSupported } // Reject dynamic fee transactions until EIP-1559 activates. - if !pool.eip1559 && tx.Type() == types.DynamicFeeTxType { + if !pool.espresso && tx.Type() == types.DynamicFeeTxType { return ErrTxTypeNotSupported } // Reject transactions over defined size to prevent DOS attacks @@ -603,6 +681,7 @@ return ErrNegativeValue } // Ensure the transaction doesn't exceed the current block limit gas. if pool.currentMaxGas < tx.Gas() { + log.Debug("max gas limit exceeded", "pool.currentMaxGas", pool.currentMaxGas, "tx.Gas()", tx.Gas()) return ErrGasLimit } // Sanity check for extremely large numbers @@ -621,8 +700,14 @@ from, err := types.Sender(pool.signer, tx) if err != nil { return ErrInvalidSender } + + isWhitelisted := pool.ctx().IsWhitelisted(tx.FeeCurrency()) + if !isWhitelisted { + return ErrNonWhitelistedFeeCurrency + } + // Drop non-local transactions under our own minimal accepted gas price or tip - if !local && tx.GasTipCapIntCmp(pool.gasPrice) < 0 { + if !local && pool.ctx().CmpValues(tx.GasTipCap(), tx.FeeCurrency(), pool.gasPrice, nil) < 0 { return ErrUnderpriced } // Ensure the transaction adheres to nonce ordering @@ -630,18 +715,28 @@ if pool.currentState.GetNonce(from) > tx.Nonce() { return ErrNonceTooLow } // Transactor should have enough funds to cover the costs - // cost == V + GP * GL - if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 { - return ErrInsufficientFunds + err = ValidateTransactorBalanceCoversTx(tx, from, pool.currentState, pool.currentVMRunner, pool.espresso) + if err != nil { + return err } + // Ensure the transaction has more gas than the basic tx fee. - intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul) + intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, tx.FeeCurrency(), pool.ctx().GetIntrinsicGasForAlternativeFeeCurrency(), pool.istanbul) if err != nil { + log.Debug("validateTx gas less than intrinsic gas", "intrGas", intrGas, "err", err) return err } if tx.Gas() < intrGas { + log.Debug("validateTx gas less than intrinsic gas", "tx.Gas", tx.Gas(), "intrinsic Gas", intrGas) return ErrIntrinsicGas } + + ctx := pool.currentCtx.Load().(txPoolContext) + if ctx.CmpValues(ctx.celoGasPriceMinimumFloor, nil, tx.GasPrice(), tx.FeeCurrency()) > 0 { + log.Debug("gasPrice less than the minimum floor", "gasPrice", tx.GasPrice(), "feeCurrency", tx.FeeCurrency(), "gasPriceMinimumFloor (Celo)", ctx.celoGasPriceMinimumFloor) + return ErrGasPriceDoesNotExceedMinimumFloor + } + return nil }   @@ -710,6 +805,7 @@ } // Try to replace an existing transaction in the pending pool from, _ := types.Sender(pool.signer, tx) // already validated if list := pool.pending[from]; list != nil && list.Overlaps(tx) { + // Nonce already pending, check if required price bump is met inserted, old := list.Add(tx, pool.config.PriceBump) if !inserted { @@ -759,7 +855,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local bool, addAll bool) (bool, error) { // Try to insert the transaction into the future queue from, _ := types.Sender(pool.signer, tx) // already validated if pool.queue[from] == nil { - pool.queue[from] = newTxList(false) + pool.queue[from] = newTxList(false, &pool.currentCtx) } inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump) if !inserted { @@ -789,6 +885,10 @@ // If we never record the heartbeat, do it right now. if _, exist := pool.beats[from]; !exist { pool.beats[from] = time.Now() } + // If we never record the heartbeat, do it right now. + if _, exist := pool.beats[from]; !exist { + pool.beats[from] = time.Now() + } return old != nil, nil }   @@ -811,7 +911,7 @@ // Note, this method assumes the pool lock is held! func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool { // Try to insert the transaction into the pending queue if pool.pending[addr] == nil { - pool.pending[addr] = newTxList(true) + pool.pending[addr] = newTxList(true, &pool.currentCtx) } list := pool.pending[addr]   @@ -1178,9 +1278,11 @@ // remove any transaction that has been included in the block or was invalidated // because of another transaction (e.g. higher gas price). if reset != nil { pool.demoteUnexecutables() - if reset.newHead != nil && pool.chainconfig.IsLondon(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) { - pendingBaseFee := misc.CalcBaseFee(pool.chainconfig, reset.newHead) - pool.priced.SetBaseFee(pendingBaseFee) + if reset.newHead != nil && pool.chainconfig.IsEspresso(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) { + pool.priced.SetBaseFee(pool.ctx()) + } else { + // Prevent the price heap from growing indefinitely + pool.priced.Reheap() } } // Ensure pool.queue and pool.pending sizes stay within the configured limits. @@ -1290,7 +1392,17 @@ return } pool.currentState = statedb pool.pendingNonces = newTxNoncer(statedb) - pool.currentMaxGas = newHead.GasLimit + + pool.currentVMRunner = pool.chain.NewEVMRunner(newHead, statedb) + pool.currentMaxGas = blockchain_parameters.GetBlockGasLimitOrDefault(pool.currentVMRunner) + gasPriceMinimumFloor, _ := gpm.GetGasPriceMinimumFloor(pool.currentVMRunner) + // atomic store of the new txPoolContext + newCtx := txPoolContext{ + NewSysContractCallCtx(newHead, statedb, pool.chain), + currency.NewManager(pool.currentVMRunner), + gasPriceMinimumFloor, + } + pool.currentCtx.Store(newCtx)   // Inject any transactions discarded due to reorgs log.Debug("Reinjecting stale transactions", "count", len(reinject)) @@ -1300,8 +1412,8 @@ // Update all fork indicator by next pending block number. next := new(big.Int).Add(newHead.Number, big.NewInt(1)) pool.istanbul = pool.chainconfig.IsIstanbul(next) - pool.eip2718 = pool.chainconfig.IsBerlin(next) - pool.eip1559 = pool.chainconfig.IsLondon(next) + pool.donut = pool.chainconfig.IsDonut(next) + pool.espresso = pool.chainconfig.IsEspresso(next) }   // promoteExecutables moves transactions that have become processable from the @@ -1324,8 +1436,15 @@ hash := tx.Hash() pool.all.Remove(hash) } log.Trace("Removed old queued transactions", "count", len(forwards)) + // Get balances in each currency + balances := make(map[common.Address]*big.Int) + allCurrencies := list.FeeCurrencies() + for _, feeCurrency := range allCurrencies { + feeCurrencyBalance, _ := currency.GetBalanceOf(pool.currentVMRunner, addr, feeCurrency) + balances[feeCurrency] = feeCurrencyBalance + } // Drop all transactions that are too costly (low balance or out of gas) - drops, _ := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas) + drops, _ := list.Filter(pool.currentState.GetBalance(addr), balances, pool.currentMaxGas) for _, tx := range drops { hash := tx.Hash() pool.all.Remove(hash) @@ -1521,8 +1640,15 @@ hash := tx.Hash() pool.all.Remove(hash) log.Trace("Removed old pending transaction", "hash", hash) } + // Get balances in each currency + balances := make(map[common.Address]*big.Int) + allCurrencies := list.FeeCurrencies() + for _, feeCurrency := range allCurrencies { + feeCurrencyBalance, _ := currency.GetBalanceOf(pool.currentVMRunner, addr, feeCurrency) + balances[feeCurrency] = feeCurrencyBalance + } // Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later - drops, invalids := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas) + drops, invalids := list.Filter(pool.currentState.GetBalance(addr), balances, pool.currentMaxGas) for _, tx := range drops { hash := tx.Hash() log.Trace("Removed unpayable pending transaction", "hash", hash) @@ -1562,6 +1688,70 @@ } } }   +// ValidateTransactorBalanceCoversTx validates transactor has enough funds to cover transaction cost, the rules are consistent with state_transition. +// +// For native token(CELO) as feeCurrency: +// - Pre-Espresso: it ensures balance >= GasPrice * gas + value + gatewayFee (1) +// - Post-Espresso: it ensures balance >= GasFeeCap * gas + value + gatewayFee (2) +// For non-native tokens(cUSD, cEUR, ...) as feeCurrency: +// - Pre-Espresso: it ensures balance > GasPrice * gas + gatewayFee (3) +// - Post-Espresso: it ensures balance >= GasFeeCap * gas + gatewayFee (4) +func ValidateTransactorBalanceCoversTx(tx *types.Transaction, from common.Address, currentState *state.StateDB, currentVMRunner vm.EVMRunner, espresso bool) error { + if tx.FeeCurrency() == nil { + balance := currentState.GetBalance(from) + var cost *big.Int + + if espresso { + // cost = GasFeeCap * gas + value + gatewayFee, as in (2) + cost = new(big.Int).SetUint64(tx.Gas()) + cost.Mul(cost, tx.GasFeeCap()) + cost.Add(cost, tx.Value()) + if tx.GatewayFeeRecipient() != nil { + cost.Add(cost, tx.GatewayFee()) + } + } else { + // cost = GasPrice * gas + value + gatewayFee, as in (1) + cost = tx.Cost() + } + + if balance.Cmp(cost) < 0 { + log.Debug("ValidateTransactorBalanceCoversTx: insufficient CELO funds", + "from", from, "Transaction cost", cost, "to", tx.To(), + "gas", tx.Gas(), "gas price", tx.GasPrice(), "nonce", tx.Nonce(), + "value", tx.Value(), "fee currency", tx.FeeCurrency(), "balance", balance) + return ErrInsufficientFunds + } + } else { + balance, err := currency.GetBalanceOf(currentVMRunner, from, *tx.FeeCurrency()) + if err != nil { + log.Debug("ValidateTransactorBalanceCoversTx: error in getting fee currency balance", "feeCurrency", tx.FeeCurrency()) + return err + } + + if espresso { + // cost = GasFeeCap * gas + gatewayFee, as in (4) + cost := new(big.Int).SetUint64(tx.Gas()) + cost.Mul(cost, tx.GasFeeCap()) + if tx.GatewayFeeRecipient() != nil { + cost.Add(cost, tx.GatewayFee()) + } + if balance.Cmp(cost) < 0 { + log.Debug("ValidateTransactorBalanceCoversTx: insufficient funds", "feeCurrency", tx.FeeCurrency(), "balance", balance) + return ErrInsufficientFunds + } + } else { + // cost = GasPrice * gas + gatewayFee, as in (3) + cost := tx.Fee() + if balance.Cmp(cost) <= 0 { + log.Debug("ValidateTransactorBalanceCoversTx: insufficient funds", "feeCurrency", tx.FeeCurrency(), "balance", balance) + return ErrInsufficientFunds + } + } + } + + return nil +} + // addressByHeartbeat is an account address tagged with its last activity timestamp. type addressByHeartbeat struct { address common.Address @@ -1786,6 +1976,7 @@ log.Error("No transaction found to be deleted", "hash", hash) return } t.slots -= numSlots(tx) + slotsGauge.Update(int64(t.slots))   delete(t.locals, hash) @@ -1810,10 +2001,11 @@ return migrated }   // RemotesBelowTip finds all remote transactions below the given tip threshold. -func (t *txLookup) RemotesBelowTip(threshold *big.Int) types.Transactions { +func (t *txLookup) RemotesBelowTip(threshold *big.Int, txCtx *txPoolContext) types.Transactions { found := make(types.Transactions, 0, 128) t.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool { - if tx.GasTipCapIntCmp(threshold) < 0 { + curr, _ := txCtx.GetCurrency(tx.FeeCurrency()) + if tx.GasTipCapIntCmp(curr.FromCELO(threshold)) < 0 { found = append(found, tx) } return true
diff --git go-ethereum/core/blockchain_sethead_test.go celo/core/blockchain_sethead_test.go index 8b803f78f85aeecc59a2ef50ec89ababcaaa7546..6393b3087d9e00cdd49b585d4ffdf57ba9f6fd1f 100644 --- go-ethereum/core/blockchain_sethead_test.go +++ celo/core/blockchain_sethead_test.go @@ -22,14 +22,13 @@ import ( "fmt" "io/ioutil" - "math/big" "os" "strings" "testing" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -53,7 +52,7 @@ expHeadFastBlock uint64 // Block number of the expected head fast sync block expHeadBlock uint64 // Block number of the expected head full block }   -func (tt *rewindTest) dump(crash bool) string { +func (tt *rewindTest) Dump(crash bool) string { buffer := new(strings.Builder)   fmt.Fprint(buffer, "Chain:\n G") @@ -561,161 +560,6 @@ // Expected head block : G testSetHead(t, &rewindTest{ canonicalBlocks: 10, sidechainBlocks: 8, - freezeThreshold: 16, - commitBlock: 0, - pivotBlock: uint64ptr(4), - setheadBlock: 7, - expCanonicalBlocks: 7, - expSidechainBlocks: 7, - expFrozen: 0, - expHeadHeader: 7, - expHeadFastBlock: 7, - expHeadBlock: 0, - }, snapshots) -} - -// Tests a sethead for a short canonical chain and a longer side chain, where a -// recent block was already committed to disk and then sethead was called. In this -// case we expect the canonical full chain to be rolled back to the committed block. -// All data above the sethead point should be deleted. In between the committed -// block and the requested head the data can remain as "fast sync" data to avoid -// having to redownload it. The side chain should be truncated to the head set. -// -// The side chain could be left to be if the fork point was before the new head -// we are deleting to, but it would be exceedingly hard to detect that case and -// properly handle it, so we'll trade extra work in exchange for simpler code. -func TestShortReorgedSetHead(t *testing.T) { testShortReorgedSetHead(t, false) } -func TestShortReorgedSetHeadWithSnapshots(t *testing.T) { testShortReorgedSetHead(t, true) } - -func testShortReorgedSetHead(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10 - // - // Frozen: none - // Commit: G, C4 - // Pivot : none - // - // SetHead(7) - // - // ------------------------------ - // - // Expected in leveldb: - // G->C1->C2->C3->C4->C5->C6->C7 - // └->S1->S2->S3->S4->S5->S6->S7 - // - // Expected head header : C7 - // Expected head fast block: C7 - // Expected head block : C4 - testSetHead(t, &rewindTest{ - canonicalBlocks: 8, - sidechainBlocks: 10, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: nil, - setheadBlock: 7, - expCanonicalBlocks: 7, - expSidechainBlocks: 7, - expFrozen: 0, - expHeadHeader: 7, - expHeadFastBlock: 7, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a sethead for a short canonical chain and a longer side chain, where -// the fast sync pivot point was already committed to disk and then sethead was -// called. In this case we expect the canonical full chain to be rolled back to -// the committed block. All data above the sethead point should be deleted. In -// between the committed block and the requested head the data can remain as -// "fast sync" data to avoid having to redownload it. The side chain should be -// truncated to the head set. -// -// The side chain could be left to be if the fork point was before the new head -// we are deleting to, but it would be exceedingly hard to detect that case and -// properly handle it, so we'll trade extra work in exchange for simpler code. -func TestShortReorgedFastSyncedSetHead(t *testing.T) { - testShortReorgedFastSyncedSetHead(t, false) -} -func TestShortReorgedFastSyncedSetHeadWithSnapshots(t *testing.T) { - testShortReorgedFastSyncedSetHead(t, true) -} - -func testShortReorgedFastSyncedSetHead(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10 - // - // Frozen: none - // Commit: G, C4 - // Pivot : C4 - // - // SetHead(7) - // - // ------------------------------ - // - // Expected in leveldb: - // G->C1->C2->C3->C4->C5->C6->C7 - // └->S1->S2->S3->S4->S5->S6->S7 - // - // Expected head header : C7 - // Expected head fast block: C7 - // Expected head block : C4 - testSetHead(t, &rewindTest{ - canonicalBlocks: 8, - sidechainBlocks: 10, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: uint64ptr(4), - setheadBlock: 7, - expCanonicalBlocks: 7, - expSidechainBlocks: 7, - expFrozen: 0, - expHeadHeader: 7, - expHeadFastBlock: 7, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a sethead for a short canonical chain and a longer side chain, where -// the fast sync pivot point was not yet committed, but sethead was called. In -// this case we expect the chain to detect that it was fast syncing and delete -// everything from the new head, since we can just pick up fast syncing from -// there. -// -// The side chain could be left to be if the fork point was before the new head -// we are deleting to, but it would be exceedingly hard to detect that case and -// properly handle it, so we'll trade extra work in exchange for simpler code. -func TestShortReorgedFastSyncingSetHead(t *testing.T) { - testShortReorgedFastSyncingSetHead(t, false) -} -func TestShortReorgedFastSyncingSetHeadWithSnapshots(t *testing.T) { - testShortReorgedFastSyncingSetHead(t, true) -} - -func testShortReorgedFastSyncingSetHead(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10 - // - // Frozen: none - // Commit: G - // Pivot : C4 - // - // SetHead(7) - // - // ------------------------------ - // - // Expected in leveldb: - // G->C1->C2->C3->C4->C5->C6->C7 - // └->S1->S2->S3->S4->S5->S6->S7 - // - // Expected head header : C7 - // Expected head fast block: C7 - // Expected head block : G - testSetHead(t, &rewindTest{ - canonicalBlocks: 8, - sidechainBlocks: 10, freezeThreshold: 16, commitBlock: 0, pivotBlock: uint64ptr(4), @@ -1648,307 +1492,6 @@ expHeadBlock: 0, }, snapshots) }   -// Tests a sethead for a long canonical chain with frozen blocks and a longer side -// chain, where a recent block - newer than the ancient limit - was already committed -// to disk and then sethead was called. In this case the freezer will delete the -// sidechain since it's dangling, reverting to TestLongShallowSetHead. -func TestLongReorgedShallowSetHead(t *testing.T) { testLongReorgedShallowSetHead(t, false) } -func TestLongReorgedShallowSetHeadWithSnapshots(t *testing.T) { testLongReorgedShallowSetHead(t, true) } - -func testLongReorgedShallowSetHead(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2 - // - // Commit: G, C4 - // Pivot : none - // - // SetHead(6) - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2 - // - // Expected in leveldb: - // C2)->C3->C4->C5->C6 - // - // Expected head header : C6 - // Expected head fast block: C6 - // Expected head block : C4 - testSetHead(t, &rewindTest{ - canonicalBlocks: 18, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: nil, - setheadBlock: 6, - expCanonicalBlocks: 6, - expSidechainBlocks: 0, - expFrozen: 3, - expHeadHeader: 6, - expHeadFastBlock: 6, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a sethead for a long canonical chain with frozen blocks and a longer side -// chain, where a recent block - older than the ancient limit - was already committed -// to disk and then sethead was called. In this case the freezer will delete the -// sidechain since it's dangling, reverting to TestLongDeepSetHead. -func TestLongReorgedDeepSetHead(t *testing.T) { testLongReorgedDeepSetHead(t, false) } -func TestLongReorgedDeepSetHeadWithSnapshots(t *testing.T) { testLongReorgedDeepSetHead(t, true) } - -func testLongReorgedDeepSetHead(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2->C3->C4->C5->C6->C7->C8 - // - // Commit: G, C4 - // Pivot : none - // - // SetHead(6) - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2->C3->C4 - // - // Expected in leveldb: none - // - // Expected head header : C4 - // Expected head fast block: C4 - // Expected head block : C4 - testSetHead(t, &rewindTest{ - canonicalBlocks: 24, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: nil, - setheadBlock: 6, - expCanonicalBlocks: 4, - expSidechainBlocks: 0, - expFrozen: 5, - expHeadHeader: 4, - expHeadFastBlock: 4, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a sethead for a long canonical chain with frozen blocks and a longer -// side chain, where the fast sync pivot point - newer than the ancient limit - -// was already committed to disk and then sethead was called. In this case the -// freezer will delete the sidechain since it's dangling, reverting to -// TestLongFastSyncedShallowSetHead. -func TestLongReorgedFastSyncedShallowSetHead(t *testing.T) { - testLongReorgedFastSyncedShallowSetHead(t, false) -} -func TestLongReorgedFastSyncedShallowSetHeadWithSnapshots(t *testing.T) { - testLongReorgedFastSyncedShallowSetHead(t, true) -} - -func testLongReorgedFastSyncedShallowSetHead(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2 - // - // Commit: G, C4 - // Pivot : C4 - // - // SetHead(6) - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2 - // - // Expected in leveldb: - // C2)->C3->C4->C5->C6 - // - // Expected head header : C6 - // Expected head fast block: C6 - // Expected head block : C4 - testSetHead(t, &rewindTest{ - canonicalBlocks: 18, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: uint64ptr(4), - setheadBlock: 6, - expCanonicalBlocks: 6, - expSidechainBlocks: 0, - expFrozen: 3, - expHeadHeader: 6, - expHeadFastBlock: 6, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a sethead for a long canonical chain with frozen blocks and a longer -// side chain, where the fast sync pivot point - older than the ancient limit - -// was already committed to disk and then sethead was called. In this case the -// freezer will delete the sidechain since it's dangling, reverting to -// TestLongFastSyncedDeepSetHead. -func TestLongReorgedFastSyncedDeepSetHead(t *testing.T) { - testLongReorgedFastSyncedDeepSetHead(t, false) -} -func TestLongReorgedFastSyncedDeepSetHeadWithSnapshots(t *testing.T) { - testLongReorgedFastSyncedDeepSetHead(t, true) -} - -func testLongReorgedFastSyncedDeepSetHead(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2->C3->C4->C5->C6->C7->C8 - // - // Commit: G, C4 - // Pivot : C4 - // - // SetHead(6) - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2->C3->C4 - // - // Expected in leveldb: none - // - // Expected head header : C4 - // Expected head fast block: C4 - // Expected head block : C4 - testSetHead(t, &rewindTest{ - canonicalBlocks: 24, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 4, - pivotBlock: uint64ptr(4), - setheadBlock: 6, - expCanonicalBlocks: 4, - expSidechainBlocks: 0, - expFrozen: 5, - expHeadHeader: 4, - expHeadFastBlock: 4, - expHeadBlock: 4, - }, snapshots) -} - -// Tests a sethead for a long canonical chain with frozen blocks and a longer -// side chain, where the fast sync pivot point - newer than the ancient limit - -// was not yet committed, but sethead was called. In this case we expect the -// chain to detect that it was fast syncing and delete everything from the new -// head, since we can just pick up fast syncing from there. The side chain is -// completely nuked by the freezer. -func TestLongReorgedFastSyncingShallowSetHead(t *testing.T) { - testLongReorgedFastSyncingShallowSetHead(t, false) -} -func TestLongReorgedFastSyncingShallowSetHeadWithSnapshots(t *testing.T) { - testLongReorgedFastSyncingShallowSetHead(t, true) -} - -func testLongReorgedFastSyncingShallowSetHead(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2 - // - // Commit: G - // Pivot : C4 - // - // SetHead(6) - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2 - // - // Expected in leveldb: - // C2)->C3->C4->C5->C6 - // - // Expected head header : C6 - // Expected head fast block: C6 - // Expected head block : G - testSetHead(t, &rewindTest{ - canonicalBlocks: 18, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 0, - pivotBlock: uint64ptr(4), - setheadBlock: 6, - expCanonicalBlocks: 6, - expSidechainBlocks: 0, - expFrozen: 3, - expHeadHeader: 6, - expHeadFastBlock: 6, - expHeadBlock: 0, - }, snapshots) -} - -// Tests a sethead for a long canonical chain with frozen blocks and a longer -// side chain, where the fast sync pivot point - older than the ancient limit - -// was not yet committed, but sethead was called. In this case we expect the -// chain to detect that it was fast syncing and delete everything from the new -// head, since we can just pick up fast syncing from there. The side chain is -// completely nuked by the freezer. -func TestLongReorgedFastSyncingDeepSetHead(t *testing.T) { - testLongReorgedFastSyncingDeepSetHead(t, false) -} -func TestLongReorgedFastSyncingDeepSetHeadWithSnapshots(t *testing.T) { - testLongReorgedFastSyncingDeepSetHead(t, true) -} - -func testLongReorgedFastSyncingDeepSetHead(t *testing.T, snapshots bool) { - // Chain: - // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD) - // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 - // - // Frozen: - // G->C1->C2->C3->C4->C5->C6->C7->C8 - // - // Commit: G - // Pivot : C4 - // - // SetHead(6) - // - // ------------------------------ - // - // Expected in freezer: - // G->C1->C2->C3->C4->C5->C6 - // - // Expected in leveldb: none - // - // Expected head header : C6 - // Expected head fast block: C6 - // Expected head block : G - testSetHead(t, &rewindTest{ - canonicalBlocks: 24, - sidechainBlocks: 26, - freezeThreshold: 16, - commitBlock: 0, - pivotBlock: uint64ptr(4), - setheadBlock: 6, - expCanonicalBlocks: 6, - expSidechainBlocks: 0, - expFrozen: 7, - expHeadHeader: 6, - expHeadFastBlock: 6, - expHeadBlock: 0, - }, snapshots) -} - func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) { // It's hard to follow the test case, visualize the input // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) @@ -1969,8 +1512,8 @@ defer db.Close()   // Initialize a fresh chain var ( - genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) - engine = ethash.NewFullFaker() + genesis = new(Genesis).MustCommit(db) + engine = mockEngine.NewFaker() config = &CacheConfig{ TrieCleanLimit: 256, TrieDirtyLimit: 256, @@ -1982,7 +1525,7 @@ if snapshots { config.SnapshotLimit = 256 config.SnapshotWait = true } - chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(db, config, params.IstanbulTestChainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to create chain: %v", err) } @@ -1998,7 +1541,7 @@ } } canonblocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0x02}) - b.SetDifficulty(big.NewInt(1000000)) + // b.SetDifficulty(big.NewInt(1000000)) }) if _, err := chain.InsertChain(canonblocks[:tt.commitBlock]); err != nil { t.Fatalf("Failed to import canonical chain start: %v", err) @@ -2065,6 +1608,7 @@ var end uint64 for i := uint64(0); i <= uint64(len(inserted)); i++ { header := chain.GetHeaderByNumber(i) + // fmt.Printf("header %v\n", header); if header == nil && end == 0 { end = i } @@ -2080,6 +1624,7 @@ } end = 0 for i := uint64(0); i <= uint64(len(inserted)); i++ { block := chain.GetBlockByNumber(i) + // fmt.Printf("block %v\n", block); if block == nil && end == 0 { end = i } @@ -2095,6 +1640,7 @@ } end = 0 for i := uint64(1); i <= uint64(len(inserted)); i++ { receipts := chain.GetReceiptsByHash(inserted[i-1].Hash()) + // fmt.Printf("receipt %v\n", receipts); if receipts == nil && end == 0 { end = i }
diff --git go-ethereum/core/evm.go celo/core/evm.go index c87311897276d1eb36dff87ac382144c0b2c8763..44b070b861a9d2104d029712f68086d1b642a47a 100644 --- go-ethereum/core/evm.go +++ celo/core/evm.go @@ -23,45 +23,30 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/vmcontext" + "github.com/ethereum/go-ethereum/params" )   -// ChainContext supports retrieving headers and consensus parameters from the -// current blockchain to be used during transaction processing. +// ChainContext supports retrieving chain data and consensus parameters +// from the blockchain to be used during transaction processing. type ChainContext interface { - // Engine retrieves the chain's consensus engine. + // Engine retrieves the blockchain's consensus engine. Engine() consensus.Engine   - // GetHeader returns the hash corresponding to their hash. + // GetHeader returns the hash corresponding to the given hash and number. GetHeader(common.Hash, uint64) *types.Header + + // GetHeaderByNumber returns the hash corresponding number. + // in the correct fork. + GetHeaderByNumber(uint64) *types.Header + + // Config returns the blockchain's chain configuration + Config() *params.ChainConfig }   // NewEVMBlockContext creates a new context for use in the EVM. -func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext { - var ( - beneficiary common.Address - baseFee *big.Int - ) - - // If we don't have an explicit author (i.e. not mining), extract from the header - if author == nil { - beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation - } else { - beneficiary = *author - } - if header.BaseFee != nil { - baseFee = new(big.Int).Set(header.BaseFee) - } - return vm.BlockContext{ - CanTransfer: CanTransfer, - Transfer: Transfer, - GetHash: GetHashFn(header, chain), - Coinbase: beneficiary, - BlockNumber: new(big.Int).Set(header.Number), - Time: new(big.Int).SetUint64(header.Time), - Difficulty: new(big.Int).Set(header.Difficulty), - BaseFee: baseFee, - GasLimit: header.GasLimit, - } +func NewEVMBlockContext(header *types.Header, chain ChainContext, txFeeRecipient *common.Address) vm.BlockContext { + return vmcontext.NewBlockContext(header, chain, txFeeRecipient) }   // NewEVMTxContext creates a new transaction context for a single transaction. @@ -71,49 +56,3 @@ Origin: msg.From(), GasPrice: new(big.Int).Set(msg.GasPrice()), } } - -// GetHashFn returns a GetHashFunc which retrieves header hashes by number -func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash { - // Cache will initially contain [refHash.parent], - // Then fill up with [refHash.p, refHash.pp, refHash.ppp, ...] - var cache []common.Hash - - return func(n uint64) common.Hash { - // If there's no hash cache yet, make one - if len(cache) == 0 { - cache = append(cache, ref.ParentHash) - } - if idx := ref.Number.Uint64() - n - 1; idx < uint64(len(cache)) { - return cache[idx] - } - // No luck in the cache, but we can start iterating from the last element we already know - lastKnownHash := cache[len(cache)-1] - lastKnownNumber := ref.Number.Uint64() - uint64(len(cache)) - - for { - header := chain.GetHeader(lastKnownHash, lastKnownNumber) - if header == nil { - break - } - cache = append(cache, header.ParentHash) - lastKnownHash = header.ParentHash - lastKnownNumber = header.Number.Uint64() - 1 - if n == lastKnownNumber { - return lastKnownHash - } - } - return common.Hash{} - } -} - -// CanTransfer checks whether there are enough funds in the address' account to make a transfer. -// This does not take the necessary gas in to account to make the transfer valid. -func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { - return db.GetBalance(addr).Cmp(amount) >= 0 -} - -// Transfer subtracts amount from sender and adds amount to recipient using the given Db -func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { - db.SubBalance(sender, amount) - db.AddBalance(recipient, amount) -}
diff --git go-ethereum/core/tx_multicurrency_priceheap_test.go celo/core/tx_multicurrency_priceheap_test.go new file mode 100644 index 0000000000000000000000000000000000000000..50e2cc4d234b4ac4432326941202a1f44476431a --- /dev/null +++ celo/core/tx_multicurrency_priceheap_test.go @@ -0,0 +1,326 @@ +package core + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/assert" +) + +func curr(currency int) *common.Address { + curr := common.BigToAddress(big.NewInt(int64(currency))) + return &curr +} + +func tx(price int) *types.Transaction { + return types.NewTx(&types.LegacyTx{GasPrice: big.NewInt(int64(price))}) +} + +func txC(price int, currency *common.Address) *types.Transaction { + return types.NewTx(&types.LegacyTx{ + GasPrice: big.NewInt(int64(price)), + FeeCurrency: currency, + }) +} + +func TestNilPushes(t *testing.T) { + m := newMultiCurrencyPriceHeap(nil, nil) + m.Push(tx(100)) + m.Push(tx(50)) + m.Push(tx(200)) + m.Push(tx(75)) + assert.Equal(t, 4, m.Len()) + tm := m.Pop() + assert.Equal(t, big.NewInt(50), tm.GasPrice()) + assert.Equal(t, 3, m.Len()) +} + +func TestCurrencyPushes(t *testing.T) { + c := curr(1) + gpm := map[common.Address]*big.Int{ + *c: big.NewInt(1000), + } + m := newMultiCurrencyPriceHeap(nil, gpm) + m.Push(txC(100, c)) + m.Push(txC(50, c)) + m.Push(txC(200, c)) + m.Push(txC(75, c)) + assert.Equal(t, 4, m.Len()) + tm := m.Pop() + assert.Equal(t, big.NewInt(50), tm.GasPrice()) + assert.Equal(t, 3, m.Len()) +} + +func TestNilAdds(t *testing.T) { + m := newMultiCurrencyPriceHeap(nil, nil) + m.Add(tx(100)) + m.Add(tx(250)) + m.Add(tx(50)) + m.Add(tx(200)) + m.Add(tx(75)) + assert.Equal(t, 5, m.Len()) + tm := m.Pop() + // there was no Init after the adds, so it should return them in FIFO order + assert.Equal(t, big.NewInt(100), tm.GasPrice()) + assert.Equal(t, 4, m.Len()) + + m.Init() + tm2 := m.Pop() + assert.Equal(t, big.NewInt(50), tm2.GasPrice()) + assert.Equal(t, 3, m.Len()) +} + +func TestCurrencyAdds(t *testing.T) { + c := curr(1) + gpm := map[common.Address]*big.Int{ + *c: big.NewInt(1000), + } + m := newMultiCurrencyPriceHeap(nil, gpm) + m.Add(txC(100, c)) + m.Add(txC(250, c)) + m.Add(txC(50, c)) + m.Add(txC(200, c)) + m.Add(txC(75, c)) + assert.Equal(t, 5, m.Len()) + tm := m.Pop() + // there was no Init after the adds, so it should return them in FIFO order + assert.Equal(t, big.NewInt(100), tm.GasPrice()) + assert.Equal(t, 4, m.Len()) + + m.Init() + tm2 := m.Pop() + assert.Equal(t, big.NewInt(50), tm2.GasPrice()) + assert.Equal(t, 3, m.Len()) +} + +func TestMultiPushPop(t *testing.T) { + c1 := curr(1) + c2 := curr(2) + + gpm := map[common.Address]*big.Int{ + *c1: big.NewInt(10), + *c2: big.NewInt(20), + } + var cmp CurrencyCmpFn = func(p1 *big.Int, cc1 *common.Address, p2 *big.Int, cc2 *common.Address) int { + var val1 int = int(p1.Int64()) + var val2 int = int(p2.Int64()) + if cc1 == c1 { + val1 *= 10 + } + if cc2 == c1 { + val2 *= 10 + } + if cc1 == c2 { + val1 *= 100 + } + if cc2 == c2 { + val2 *= 100 + } + return val1 - val2 + } + m := newMultiCurrencyPriceHeap(cmp, gpm) + m.Push(txC(100, c1)) // 1000 + m.Push(txC(250, c1)) // 2500 + m.Push(txC(50, c1)) // 500 + m.Push(txC(200, c1)) // 2000 + m.Push(txC(75, c1)) // 750 + + m.Push(txC(9, c2)) // 900 + m.Push(txC(26, c2)) // 2600 + m.Push(txC(4, c2)) // 400 + m.Push(txC(21, c2)) // 2100 + m.Push(txC(7, c2)) // 700 + + m.Push(tx(1100)) // 1100 + m.Push(tx(2700)) // 2700 + m.Push(tx(560)) // 560 + m.Push(tx(2150)) // 2150 + m.Push(tx(750)) // 750 + + assert.Equal(t, 15, m.Len()) + tm := m.Pop() + assert.Equal(t, 14, m.Len()) + // 400 + assert.Equal(t, big.NewInt(4), tm.GasPrice()) + assert.Equal(t, c2, tm.FeeCurrency()) + + tm2 := m.Pop() + assert.Equal(t, 13, m.Len()) + // 500 + assert.Equal(t, big.NewInt(50), tm2.GasPrice()) + assert.Equal(t, c1, tm2.FeeCurrency()) + + tm3 := m.Pop() + assert.Equal(t, 12, m.Len()) + // 560 + assert.Equal(t, big.NewInt(560), tm3.GasPrice()) + assert.Nil(t, tm3.FeeCurrency()) + + // A few more re-pushes + m.Push(tx(585)) // 585 + m.Push(txC(3, c2)) // 300 + assert.Equal(t, 14, m.Len()) + + tm4 := m.Pop() + assert.Equal(t, 13, m.Len()) + // 300 + assert.Equal(t, big.NewInt(3), tm4.GasPrice()) + assert.Equal(t, c2, tm4.FeeCurrency()) + + tm5 := m.Pop() + assert.Equal(t, 12, m.Len()) + // 585 + assert.Equal(t, big.NewInt(585), tm5.GasPrice()) + assert.Nil(t, tm5.FeeCurrency()) +} + +func TestMultiAddInit(t *testing.T) { + c1 := curr(1) + c2 := curr(2) + + gpm := map[common.Address]*big.Int{ + *c1: big.NewInt(10), + *c2: big.NewInt(20), + } + var cmp CurrencyCmpFn = func(p1 *big.Int, cc1 *common.Address, p2 *big.Int, cc2 *common.Address) int { + var val1 int = int(p1.Int64()) + var val2 int = int(p2.Int64()) + if cc1 == c1 { + val1 *= 10 + } + if cc2 == c1 { + val2 *= 10 + } + if cc1 == c2 { + val1 *= 100 + } + if cc2 == c2 { + val2 *= 100 + } + return val1 - val2 + } + m := newMultiCurrencyPriceHeap(cmp, gpm) + m.Add(txC(100, c1)) // 1000 + m.Add(txC(250, c1)) // 2500 + m.Add(txC(50, c1)) // 500 + m.Add(txC(200, c1)) // 2000 + m.Add(txC(75, c1)) // 750 + + m.Add(txC(9, c2)) // 900 + m.Add(txC(26, c2)) // 2600 + m.Add(txC(4, c2)) // 400 + m.Add(txC(21, c2)) // 2100 + m.Add(txC(7, c2)) // 700 + + m.Add(tx(1100)) // 1100 + m.Add(tx(2700)) // 2700 + m.Add(tx(560)) // 560 + m.Add(tx(2150)) // 2150 + m.Add(tx(750)) // 750 + + // no init yet, returns the cheapest of the first of every currency + assert.Equal(t, 15, m.Len()) + odd := m.Pop() + assert.Equal(t, 14, m.Len()) + assert.Equal(t, big.NewInt(9), odd.GasPrice()) + assert.Equal(t, c2, odd.FeeCurrency()) + + m.Init() + + tm := m.Pop() + assert.Equal(t, 13, m.Len()) + // 400 + assert.Equal(t, big.NewInt(4), tm.GasPrice()) + assert.Equal(t, c2, tm.FeeCurrency()) + + tm2 := m.Pop() + assert.Equal(t, 12, m.Len()) + // 500 + assert.Equal(t, big.NewInt(50), tm2.GasPrice()) + assert.Equal(t, c1, tm2.FeeCurrency()) + + tm3 := m.Pop() + assert.Equal(t, 11, m.Len()) + // 560 + assert.Equal(t, big.NewInt(560), tm3.GasPrice()) + assert.Nil(t, tm3.FeeCurrency()) + + // Re add and break it + m.Add(tx(585)) // 585 + m.Add(txC(3, c2)) // 300 + assert.Equal(t, 13, m.Len()) + + tm4 := m.Pop() + assert.Equal(t, 12, m.Len()) + // No Init, next in line should be the 700 tx + assert.Equal(t, big.NewInt(7), tm4.GasPrice()) + assert.Equal(t, c2, tm4.FeeCurrency()) + + m.Init() + tm5 := m.Pop() + assert.Equal(t, 11, m.Len()) + // Init called, new 300 one should be popped first + assert.Equal(t, big.NewInt(3), tm5.GasPrice()) + assert.Equal(t, c2, tm5.FeeCurrency()) +} + +func TestClear(t *testing.T) { + c := curr(1) + gpm := map[common.Address]*big.Int{ + *c: big.NewInt(1000), + } + m := newMultiCurrencyPriceHeap(nil, gpm) + m.Push(txC(100, c)) + m.Push(txC(250, c)) + m.Push(txC(50, c)) + m.Push(txC(200, c)) + m.Push(txC(75, c)) + m.Push(tx(100)) + m.Push(tx(700)) + assert.Equal(t, 7, m.Len()) + m.Clear() + assert.Equal(t, 0, m.Len()) + assert.Nil(t, m.Pop()) +} + +func TestIsCheaper_FwdFields(t *testing.T) { + curr1 := common.BigToAddress(big.NewInt(123)) + price1 := big.NewInt(100) + curr2 := common.BigToAddress(big.NewInt(123)) + price2 := big.NewInt(200) + tx1 := types.NewTx(&types.LegacyTx{ + GasPrice: price1, + FeeCurrency: &curr1, + }) + tx2 := types.NewTx(&types.LegacyTx{ + GasPrice: price2, + FeeCurrency: &curr2, + }) + var cmp CurrencyCmpFn = func(p1 *big.Int, c1 *common.Address, p2 *big.Int, c2 *common.Address) int { + assert.Equal(t, price1, p1) + assert.Equal(t, price2, p2) + assert.Equal(t, curr1, *c1) + assert.Equal(t, curr2, *c2) + return -1 + } + assert.True(t, cmp.IsCheaper(tx1, tx2)) +} + +func TestIsCheaper(t *testing.T) { + tx1 := types.NewTx(&types.LegacyTx{}) + tx2 := types.NewTx(&types.LegacyTx{}) + var cheaper CurrencyCmpFn = func(p1 *big.Int, c1 *common.Address, p2 *big.Int, c2 *common.Address) int { + return -1 + } + var equal CurrencyCmpFn = func(p1 *big.Int, c1 *common.Address, p2 *big.Int, c2 *common.Address) int { + return 0 + } + var notCheaper CurrencyCmpFn = func(p1 *big.Int, c1 *common.Address, p2 *big.Int, c2 *common.Address) int { + return 1 + } + assert.True(t, cheaper.IsCheaper(tx1, tx2)) + assert.False(t, equal.IsCheaper(tx1, tx2)) + assert.False(t, notCheaper.IsCheaper(tx1, tx2)) +}
diff --git go-ethereum/core/chain_indexer_test.go celo/core/chain_indexer_test.go index dcbc8ce4c2c4c9a392a38173fc9849ad63ce5088..48d245dd286ba53923106ca91d77ceae13567e9b 100644 --- go-ethereum/core/chain_indexer_test.go +++ celo/core/chain_indexer_test.go @@ -60,7 +60,7 @@ sectionSize = uint64(rand.Intn(100) + 1) confirmsReq = uint64(rand.Intn(10)) ) backends[i] = &testChainIndexBackend{t: t, processCh: make(chan uint64)} - backends[i].indexer = NewChainIndexer(db, rawdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i)) + backends[i].indexer = NewChainIndexer(db, rawdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i), true)   if sections, _, _ := backends[i].indexer.Sections(); sections != 0 { t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, 0)
diff --git go-ethereum/core/headerchain.go celo/core/headerchain.go index b6b24fff919449046b5be33b0b7e48d5c5eca761..77ddfec3ffc5df9a71186a8a24ad017314023d89 100644 --- go-ethereum/core/headerchain.go +++ celo/core/headerchain.go @@ -103,10 +103,12 @@ if hc.genesisHeader == nil { return nil, ErrNoGenesis }   + log.Trace("Set current header", "number", hc.genesisHeader.Number) hc.currentHeader.Store(hc.genesisHeader) if head := rawdb.ReadHeadBlockHash(chainDb); head != (common.Hash{}) { if chead := hc.GetHeaderByHash(head); chead != nil { hc.currentHeader.Store(chead) + log.Trace("Set current header", "number", chead.Number) } } hc.currentHeaderHash = hc.CurrentHeader().Hash() @@ -151,13 +153,13 @@ if len(headers) == 0 { return &headerWriteResult{}, nil } ptd := hc.GetTd(headers[0].ParentHash, headers[0].Number.Uint64()-1) - if ptd == nil { + if ptd == nil && hc.config.FullHeaderChainAvailable { return &headerWriteResult{}, consensus.ErrUnknownAncestor } var ( lastNumber = headers[0].Number.Uint64() - 1 // Last successfully imported number lastHash = headers[0].ParentHash // Last imported header hash - newTD = new(big.Int).Set(ptd) // Total difficulty of inserted chain + newTD *big.Int // Total difficulty of inserted chain   lastHeader *types.Header inserted []numberHash // Ephemeral lookup of number/hash for the chain @@ -177,7 +179,7 @@ } else { hash = header.Hash() } number := header.Number.Uint64() - newTD.Add(newTD, header.Difficulty) + newTD = big.NewInt(int64(number + 1))   // If the parent was not present, store it // If the header is already known, skip it, otherwise store @@ -212,9 +214,10 @@ batch.Reset()   var ( head = hc.CurrentHeader().Number.Uint64() - localTD = hc.GetTd(hc.currentHeaderHash, head) + localTD = big.NewInt(int64(head + 1)) status = SideStatTy ) + // If the total difficulty is higher than our known, add it to the canonical chain // Second clause in the if statement reduces the vulnerability to selfish mining. // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf @@ -253,6 +256,13 @@ headNumber = headers[0].Number.Uint64() - 1 // inserted[0].num-1 ? headHeader = hc.GetHeader(headHash, headNumber) ) for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash { + // In some sync modes we do not have all headers. + if !hc.config.FullHeaderChainAvailable { + if headHeader == nil { + log.Debug("WriteHeader/nil head header encountered") + break + } + } rawdb.WriteCanonicalHash(markerBatch, headHash, headNumber) headHash = headHeader.ParentHash headNumber = headHeader.Number.Uint64() - 1 @@ -300,29 +310,29 @@ lastHeader: lastHeader, }, nil }   -func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) (int, error) { +func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int, contiguousHeaders bool) (int, error) { // Do a sanity check that the provided chain is actually ordered and linked - for i := 1; i < len(chain); i++ { - if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 { - hash := chain[i].Hash() - parentHash := chain[i-1].Hash() - // Chain broke ancestry, log a message (programming error) and skip insertion - log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", hash, - "parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", parentHash) - - return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, chain[i-1].Number, - parentHash.Bytes()[:4], i, chain[i].Number, hash.Bytes()[:4], chain[i].ParentHash[:4]) - } - // If the header is a banned one, straight out abort - if BadHashes[chain[i].ParentHash] { - return i - 1, ErrBannedHash - } - // If it's the last header in the cunk, we need to check it too - if i == len(chain)-1 && BadHashes[chain[i].Hash()] { - return i, ErrBannedHash + if contiguousHeaders { + for i := 1; i < len(chain); i++ { + if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 { + hash := chain[i].Hash() + parentHash := chain[i-1].Hash() + // Chain broke ancestry, log a message (programming error) and skip insertion + log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", hash, + "parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", parentHash) + return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, chain[i-1].Number, + parentHash.Bytes()[:4], i, chain[i].Number, hash.Bytes()[:4], chain[i].ParentHash[:4]) + } + // If the header is a banned one, straight out abort + if BadHashes[chain[i].ParentHash] { + return i - 1, ErrBannedHash + } + // If it's the last header in the cunk, we need to check it too + if i == len(chain)-1 && BadHashes[chain[i].Hash()] { + return i, ErrBannedHash + } } } - // Generate the list of seal verification requests, and start the parallel verifier seals := make([]bool, len(chain)) if checkFreq != 0 { @@ -342,7 +352,7 @@ abort, results := hc.engine.VerifyHeaders(hc, chain, seals) defer close(abort)   // Iterate over the headers and ensure they all check out - for i := range chain { + for i, header := range chain { // If the chain is terminating, stop processing blocks if hc.procInterrupt() { log.Debug("Premature abort during headers verification") @@ -350,6 +360,7 @@ return 0, errors.New("aborted") } // Otherwise wait for headers checks and ensure they pass if err := <-results; err != nil { + log.Error(fmt.Sprintf("Error \"%v\" at block %d", err, header.Number)) return i, err } } @@ -470,6 +481,7 @@ } // Cache the found body for next time and return hc.tdCache.Add(hash, td) return td + //return big.NewInt(int64(number + 1)) }   // GetTdByHash retrieves a block's total difficulty in the canonical chain from the @@ -570,8 +582,14 @@ for hdr := hc.CurrentHeader(); hdr != nil && hdr.Number.Uint64() > head; hdr = hc.CurrentHeader() { num := hdr.Number.Uint64()   // Rewind block chain to new head. + var nums []uint64 parent := hc.GetHeader(hdr.ParentHash, num-1) if parent == nil { + if !hc.config.FullHeaderChainAvailable { + for i := hc.config.Istanbul.Epoch; i < num; i += hc.config.Istanbul.Epoch { + nums = append(nums, i) + } + } parent = hc.genesisHeader } parentHash = parent.Hash() @@ -602,7 +620,6 @@ headHeaderGauge.Update(parent.Number.Int64())   // If this is the first iteration, wipe any leftover data upwards too so // we don't end up with dangling daps in the database - var nums []uint64 if origin { for n := num + 1; len(rawdb.ReadAllHashes(hc.chainDb, n)) > 0; n++ { nums = append([]uint64{n}, nums...) // suboptimal, but we don't really expect this path
diff --git go-ethereum/core/genesis_extradata.go celo/core/genesis_extradata.go new file mode 100644 index 0000000000000000000000000000000000000000..d702e5ce80185ffc7e9eccf436bcc9f151eec226 --- /dev/null +++ celo/core/genesis_extradata.go @@ -0,0 +1,25 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package core + +const mainnetExtraData = "0xecc833a7747eaa8327335e8e0c6b6d8aa3a38d0063591e43ce116ccf5c89753ef91f4df905559427f326ab753d62cdb55018c62e9ee7e671bbb121947eec94733d16b96c6fe877464630bb5be1e5c3f294f0d17b624521c0a599b063d73a73f9719307b48f94a7681958b7f07f4fb8bd5b0e075fb2aa2b021d5994ecc20b5f3d6cdf41fc12707ad7872ff64256e29794e5617da4dfa6ce4912f1d39889fe2dec2f7e66b39439ec4f2a82f9f0f39929415c65db9ea5df54e41d94341dec14b7a56c242ce9cf939815ec7bb11042449469901924e6c045a03cc163c6b8ae8af80fa1ee80940a6641d4736767d1f2bcf2450200ef384391b4419466bdb4d2ff2ee4c68517e6fcd25cc3034c86916094606311948f7426ddfd23c1521b15eddb52e83b29944cb90ebba92141ed3021f5dc4e6c8bb642095846948c6f9aad8281a21e7f6522602f2d6469c950e0bb942a1bff2452aca1ca5ffabbd34b2744109d11e4f594b4fa2d21b238e12ee4a863517cb5092f2330cb1b9442d441b6793e6162b979fbf6ad0af0063cbec96a94436d12f639a32509685080161ff0365fc15545f094e0c5f6172673ad70a76ff264cbc0df783930b47d9421245b0a2c3235f1108d1aa01ae376849d36e68494f27bb4eabc4400a1abe9d80d7537ab0ef1b058bf948440e805b89f48c932265e3c4ad033813669d87a942eb79345089ca6f703f3b3c4235315cbeaad6d3c943aa1fa695aa89958ebdb5346d6760b72250dc1d794173c75c8f1be201ce89cf426fe12c9997d7096269474288dbefa3a55986c039953b67139a466474fc49482f0e7879314516952f7961b15c63fc6b2734dfb9459f7b67e6beae0223ddc91eec010b670c553e8e094e10a8cc6c22cdc320c67bd600a1d8a0a46d7f40094e3020350aceea29b783e0c947ae001692b8f624894b952930a3656a9cbab21df5919f94c61a495bf79942289a63b4b4700eeeca35323ea51785f366dd705940223e40d1f93a6fe5bef63605992ada10740e13b940610b8b4e6f5c3241d53ed3374ddca8969cd053c9463b4b616c5345e3dcc9e21db69297e2129447f4e94c9a7781729f95b88239c3bfc91fb52f92b44a1169456259f876eb6a7264d9f1a59952baad599ff964094f139e74adec329e715ab49a68c5548a00e40cbc29433657019d60a0a41f1b9970bd4b28a3a83dbeafc940f5640bd556b0be19262d1817b213ddb3424d91d94ed8bf82d2e579ff6363aef139f8b147a0105f17e94d8c68ebecb6f074ac5c4fb66a690ac0ad38a5a3c94c6f916ad6e360651bb95f8e67c1c28805745d084943ed95d6d4ce36ea7b349cd401e324316d956331a944a03c4c2e101ac4612d89b79f61c9c5bdd51929d94dd0f3f7beb37fe9d4496f8098446b65ddfb1fa0294642cbe89a58909ba712dd11ed4c4b2359bd8c85d94ffbcf262c1d5c4392ef469ba79f2cd195d2affda9468e0104fd2b5a2c93e97c2ba172c4d2a4223f76f945cab520442de9babc290b25e5e2e6a1194ec670794bc6963fc0e2f5547ba949ed39e80b8388321104f9443882141555003b3e71110f567373b59ac4cb0bd9448cedc58b10af13d688631bc3cb78a05b8a6e56a9465698c9ec5af10345cd1e39472d60fb6133bad6f94d507309fd69635aa37810a65a4da27ec47a1ba0594c46dc0741ff61af883e284daea062ae7382e709194198958f0b860ab0e3937f468fe366aac9eebad2e940ec5a403212d732d8d7ced050e9510f6327453c694464cce7999b3d90a8f1ffaf94a71fbdea1e6543594097095b8cf5cabf0c39e548d8df55dd635d84d0794aa937da037e617e868795eac1dcd43c663014d3294f11073eb2d259b90a91954cae30d0e6e9ecc7f11945b55452bdba5971d606f47647bd383f3c3fa728594439d5e4d7578ecc9efa52a8cf1887b11fd0fb9009492f628b0157d47c992f5c69dbbd038b110e27826f918e2b86005f784c52234d0c40ab13b0636c5728217a8f3100046593c30271d39d59fcdb9a0053a874710e9c65e0d8a030c16a700b4ec430207cc5037c5e2698ef839fa0b7a4372f4b5217deda2a8df087e3b552f300016ce9147a2ff4db4c8a87d77b080b860d3f640c3da685893c41eb6301fdde2920c5a09296438abe5894074592f27547fb4ad02d222b1a330ff5aea065eff5200973d4fcb57808d8d9b09337e09aec279dc3337e758c51a3219596699006ee3683ff0c733f16eb917fad37f7f5d505201b86057b5158540d1672de49073e469e5426766094ce6514ac29580803ff9d22cdef3b6fed28a8ad9de63bf938c1c7b0e7a0156fb03f5363d09bf337713d8d45475df48ccea0fa793f591fbb62673545fc846c04e6aa9e2eaaab72c3b9ba4069ea280b860ca75a4c4e1f2898c0ae0ed53698b0271d1f8869ecd945891ae6a9b02682335af625c29f80572b440f91a3a343f3ea600a49a426960b6c1c5dd7b24540ecb745329312dfdd525fd173418c846f0096acba466a27446114f3a2b2a887986846b01b860b3dff5505fe9ec48fa11a67dbf166e7d5ab3473ff9f5fdc90c690eb1142ad687b17eb8ab0c08615c9397b351c53e79016d593d588f8c607b2d1136713d05066ffd8a30589357afa54a0a7ac8777372787261dde126c4f0c04bf741f809c3be80b860451da71ff5b1e9b4fd587fd76f53808191096cc5869596eda8f3ad697426a98a659f5f4818e57866d4b0dd90f0684900742a167b314f0276e5e6a8bc6f40ce1cd1d35c7f9be56f1240de596b30a4b40010dfd21b7fc9aef400faaa421e27a080b860277d5eedd8cf7d549296a6f8ad2869ec6e90183b9e8ebbf49a9d3ecd817757b50c6acc6cab9f0dbcf4b87251b6e82a0192a00c1e509225ec1ec08dcbeb04ba2808b16f72af90f33277e614dc19383256b3980421441ce3e75356ee35e5f62d80b86007431d3192f7a273382b645bc300cb0161c2395c005bf389698ce4a3ac56cffb9405456102f5b6e41b42bb28e7cb1f0141d3e1be1b066a94cc2befe88b0f5eef63c60a1ab8a1f0cc02e73c0ce5aac464332d3a2c1074a5126807a75323280301b8600d39e1cd00ff4ce5e41fa9ea90526f3e3540b64ba52954e5682f31ec20360b2995d5caac7cee778ff7c13aaca74b9b004032f87a331867e9ebc7ed06c5b77707c88614714f2e0ed30c85c519aa823aa9d1a7a56766a294c7802aad300b408280b860ce9efb226a3c085071c7aea84f66040dc16a1600893eb1e6ef507fa72c6684f24d2c3b4ffee9881142fc41cfbd6b3b01933838ed7bd05c16450cab703b12ee623f8745f7f618981601a9c4de1337fe881975bc8572536281b9dcc51400177680b860075724b0f2fa9ced3440a76107a53b02a0b8b5a5a510d94b02b5c0696db96910204d54ee3138b3e5949464585d98de00a2964d480ca31adf09c962802895ea160d195bce583e59094bf7c11d33eaffef12cb999284bcf842bbab2c87647d8801b8603b0ffa0a101f959879b7e14d830862981d6c311ffd440f214eee0a2891743a25355e9230266aeadbd20f3bde0ccb30008bf326df8fba9b701d4764ad49663da19417132edf4281df4560715649491d9794dc64e9540d16b51b362e139c3aa701b8601a65e75f0f47d7c92da92de15372a0052338b952cc1307d5ee1596a670fedeb3b5ac8bc690f3f5e5f34fb8d18a5441011d6023e3c2b343bffc655972102a7181dffcab47bd0a2d6d46eedc6f2b70cd64025715de2b8ae23c032e61b380663c80b86071ca0f2be942a733074e8e0d92ae0adc2617bcfe503b97f8339c8f1c498d85608fa5966da7abdd9651c9ce4e3411e900c4d65d5c7d858f1e5ee212c9e543d3727aeaa9abbdcb68848e7c990c06aa70a4bf2585959943935549e7a9e2c2ba0d80b8600122d3f6ae5e2bc6fdee3cd35a3522ea519bfa6f365451fa275c4688bcfb5cec418fa053a8859981e973ea273ca68f014e840d0ccf18cc7c498742103c3116732ef197a9f90f50bf64f83754a86ad441fe9691bfdf384a1aaacf38430a2b1001b8601135b86a2587c75c55db8274e498bd3f2887a1a455a665b8314655124c5e8197d3ef892f3dea2bd80b047530f837ae00dc263da929f0135a4f8ee73d47d5c2ffcaaa7b1e3a9cc59e496feeea2323e5eb16c9de2e3b973c35c0a82ca587477201b860e162bf706b79405326d52e29e2ea247a55ebf69ac34900e4e1249c3fe28fd3dabf40d7022f57bc7b16bedbc78bb49000ba7cd92e123b69309a0572cf49b1bdf4c5525ba656decffa5da91ed4fb565d384979e9a58f72986b654908bcc7308580b860e9dbc5c1d073954ad6ace9afb19d8679e8c87fd12cd1356999a8e167390f3de966f3485b500c40c61f829e12f56512001b4d4c4fcb19737735eba15d24d43231ce64cf348de3765fdbd01f4612af6ad7ebf9fafb594ade18a7717c189e9c2080b860bd8e3d6ad24e5a7e4f084b3142c6bebe26d248e09363a9bbfc9cd07ed59a65bc140a39564f6ecdf287c2ee1ed57a930035a92306b6d5925af8785c813bad6113ba42da88d3c0586e438df75bee506bdf9f10c927493524897ba485946f976f00b86030feb59a6804a2df697b3a40c691f176575ffb0d09fa167a59642d041dfec97e97414ea7abc8bc7bdad5f1a684e2bc00fb2c01bc03aa9e8caaaa82ca3210de31f9c7abff6baba5e08e2dbdfa85953ac39bf3b895627e7be493fbb6e346061400b860f24f6f1e6423fd83d1ef275484a5950fed90794d7bfb6224de768bf716646dee263de0d9ed4c681c0def78d16df0f000f1e17b8657f1d8e30dcb7e94cd1c79b9c82d8f04252b078ccf3719c7658dd82b9d93e02671925b5aeb4ae38163129100b860f6a586f1ceb85a23980bdfea738678f5906de5303a7ff6ceb3ad675f93a84fcc502275fd7bd8fde4de89fd381cb8eb000b3d496e248208d00eae4f421ce041d73789e794a372ca788655eb5413c3b7cab1c993e47ff6c568ca4da484bac88b80b86086ab977d6064531b136aba2fbcc50cfb332b939e0af22c7ff3800c804b33aad998ef69a97204076028a6946d627d2f0086992cba351fd538a91986253884325aba6d66ec1ac10eb8e18330180820cc388c22de7d64f9f0592b74ee5b35745a80b860fda02a60f25f87c0fc86be0bf52bf1d0be37db80661a2fa35c7f52a4c5f5edccca04768c762caaa2303f8cac2ba93e016dce7c8382e3d002e751e0bf33563eb8f6e7937ab2998300e96f5ed3d857d081ff389c495febaabe836a7cd06012ae80b8609271a103e5971edd694b3c6363a2517b0676849b05660094baf240ea9677e171169a8eebed5ad6000871b4f42d4ca701b74b10f91c0de059d225ced0222e391cf7bdb5934d1677193837d5a1f17bb710f4747956d819db51c817cbba53324b81b860719b2de30cbcdee6e62445b7c2eedb63520beb4cb6e4a55f7e121a5bc6a1143e1322d3ffea28ab624d30f6da8962b8003eeb056df8e46a49035c0aaf139a372e954d8ebc3cbd2d0b2e5727d54f3f4fcd0e95dc29bfa9f9ddcda98bbe8107ae00b860b054d545899f55cbb84853125d50ed06211ace3eeb6160a85583de508569e65592f5a221a000cb99997c0dd2738527010f2b1e83eeb7ca41c40ad9295b35f1172004a63567f140c1d2b0e04338161e8f1be88c1543ac75d587e42c3ed7dc1880b860e8de954d3100b125b2a3c1e4a81e3ac5f4b38f85c2ed91d752c507d4a37c338ca215e474a48511c599407b85c01d37012cd5012c74c5c0c3f88b4480a73bb52d1ff49848ca014f54cde1a891e6efa483b5f64016a9303515806c45d9f79a7e81b860bfce6153e2c2dabc724d9ccf5cad8916753dce1a495a29053ff7e5b74b0eace4889d5fd2d5859400d82b6bf29cecc9006b6e37152cb1f4f72e7d0c0897439f4ef886e26b4345d2f3e73bcf08d47f02c02976730620d7f844313c099894176980b860d56a6034613c0db14cad99e0d2dc3a2482ff8167a032218ac10508bb04fb8ef91d40a6ef0731f1e162761f09be237800363199f518a1bad98f9f2d179d2028137f9a86588c5cd91534a53d4edcc718b40aab61343571636d052eb7b288578c80b86025138a99de6619b2ad03707f847ac0f5d79b0fba2c67a02f5430a57c7a651d6cd63988cb91aa331acd8b7221a41fc4007be73ad86365de5029c22186d2c90a864fd3d386cc074b25f33b4e5f17839d2780b62390f1fe63ec690c1022c039f500b860c1e12e9c0b26d73c27dd5f60f08aa15c952afaa395dbc8b4cb99453bbf3f14ede8a9fc579f64500497344cb3637aa900ac8618d237580558d9ab5b042ac411fd121713744378c9f4d897907f09a71af9331bfecb3853e3f0577be558c9019881b860c8e6cf45b640fa91385b4f7e891fe68e5ef6847d032d6ee1b52eef0ff577ad796b881537f94adcbd1b52ba3e0e5d7e014e97406029095598a373fd6d9b6c7754aa87b87560e363faee89c11482cae4a063d7cbe65747f2b095cd2f6688a8bd00b86008fb22b9fe04caf62059216dfa1d2274ade1df9e8dc0b3c37c2f04a8b3d30a73f8b7ddd873d08c9b9302b936d303380147ce0e19c4600b780a1814730462147fb8280fbd7fe0aa7290d325a2b7b5092e05c037664a0aaea127b0d95fd13e3d81b86068dd5abe43baf4f4817a49b094c0bb19ed4971c49a6f91863163f655b6f312d80b7c7843b4cb7a27f7e54d48c41c7a01c5e22e1b0e38a5dbb03dfee76076a9af56146359add6003410005332417f6b65db718acce693cfa21a8fd92f6ad72800b86091785b64cfd3ac6025b6b536499ac63868344b8f86660ed73b574b000477807d53dacf1d9da258ee1b113485a01579001c5a510dd3b3c5ff1f417c4d97faf997aecedf7602bd96155f8396f5c9fa53d2c601b0de0241fff49939b8a97ce59f81b86000d869e78871139e8a5ff6d590593d94ea39ea73e87ac8d901f0f524372b93752a488b34e05afe05ab53b1c1b5b9a50040341dc01361110551358c9e982f30755e19ec2ad1c1a40afba9998f763599149b976caaded14c1793fe8c469b17c900b860e87f2bfc49c076239d7522280098c9c4808524c67f3891608c42a510340d64c7e3c1c074c34050007837c13536ee4001108efc87fb2eb8c3236a738122ec086a7b8e6857e82454ff6284a81bf0362424ba96be762d4c06e6aa1b59f20c870780b8608bd2cb218f465c82702aba73a061227cd83dd8e3115ae592811a4d9a8236e3cb99cfe81b2e22e3620635d42f63b7db00cd546bdf9fad7b49d0b4686962c8e0f4544c8df9f41d30d7d489c86745ef9b61a7dfb498f7554eeaf7e24654e5b2e080b8602a5d5145b7a8100575e96cadf2513e7e64dc1354bd891a24f985c2574c9f5f136c6837eab290543d769227785520970133022637837b6e711918f3d2e98291bf3b518247b54e925a0cd080d517731b2725be888fca298ae9ec6f5d2c19146b01b8609cedde7d49e2e115896b789ece09a4e8816fae153de891774f0f716a3088e01754498f8ab82d3a4ef5d53dbe17338f0089d1de1eb60bfe3ec2b9701cd882b1c174bdcfaac2225f57481676bd3ee845825be823b9f9317e407b015159b9ce7e80b8609124b91b223bf9bcb33f75ab8fad0180fdc57536d34349945b80458abc8e59cb06005fabea656b2cd0dc74c0d06160019706965ec3ee0d3b9dd76eb9d39158bd512fe7384bb9e139bc6391d5f7b969397bd85a91bb91670880d861e8fcbd0700b860338fa194daae9ba1245931653fa349ce85d37e2f42f871c6b67397401e190b497d15ff23e8f6e9d8637f84fc2dd4640066fee692f5d38208233e7570c3ed49a4ba032d79c7a974883fa5b5c2a113259b924a346bc4edaf717b3d272ceb152980b8606150a62f3bae3a35a639d66dc37ffbc979dda85d6f5c278a5e3a563ed865018c86563be9b878261ef8d34edd3c313500a4f170e22f386674d538b9e15930f39216a38c2a0b60380a140e5897f42d37077784cd0f1a93746e4da42a5da8de1081b8605fd1581e41fa22e7ea0d3ee0674e80e2168fdb42c1aaa3eb8aa395fdde11de5ca98ee71c50b365ef6ad9850bacf7ab011eb446da79efba3ae91fe1f7d9274dacda39b0b3bfaec7d7904eac59d72683c52b6ea091ca1374d8b744fd1d595c5700b860e04a671c7f9af919093a9c7aaf5c3d04eff31189d65f5a2aef8a08119ed1c150464c40085bfe2fc1b598fc2ebb095101d0103b995e3a762a9063d823ef6686422caa5a390f9cef5ee6c4b911fd2a818f43fa47ab4d21b01cc7b071b517438d81b86014cfe4d455820e0b9189ccb32b565b90d414ed7d315d3e61141f085b6aaea34b0c90d1f502f544408e7ed0573e1d1701d38063b239cf847018daa41b7ef75b4458d2fa23e459e9e8039668aca6bc70df1051a6466a5a5bc636e68bc8e2380a80b86045bab7cfb2814e1e3e1a40779219bb48c5d51419ce4c27359d0dfea9fbf1d193ca1cf07934ebf3d6699093387d0e0f000dc5fd2058e7ac50e67c26e5172567c2e7ba9fefa6376e44de800c8955cb330df27476d8ba9c8ec06bba46fddeb95e81b860a9fc64c64c039cbd8b23c10c706e51e0013289201111cf8c50e22781161df397fa9c1b91dbab44969aa4f8819f222e01bab90190bebfaecff7076de325dae8080bfe921fbb4ca474d45126fe5ae699dadea98a5255c048b955ab9ba1de8f4c01b860aa5729ac27a01f26924e863eea7d7b49e4f8ccb15c758ea5ac4d19691a8df31f70138e7f3011fc1e53d52d7063dd8d01a9b3800a66914956769a13ff8d7960725bf1b9073052f3ff85e57b34165702baede05d4e807bfb565a38c24e3542c780b86014dafe41c0d96b22188f0cf6cb99274e09d8ed0546bb57b28a3590449726aedf809c049663a2fe4e40b91830ac864d006a9c5b41cbddeabeef99cfddf6c298de0d482b6e945993241d8987f7fac004fe159f2206060b69d1f8f7f67f63f26280b860878790bb38b1d31707aa37b5af439d64d4a29ea64418b7c01139a26e4f297da6c61a806361e6fee8e40556500c8a6000183b2a42c54d714a143fe12492081f373e3e27e3921adeb276a8daac8b87a534c0fa1d45195d8a35cbda27f883128680b8607983d25868e1915ca8c20603bb70475b398acb04fef221106475f0fc25a3df25d181050214da347bfcd8f8292e5257014ba77cf67ee5f42202257317808f126930b4eaaf4bec801c4465ced54bd78331f8d3dadfdf2f91de294836c13e946001b860c700f20fab4ebb8ba2f47adf177e3a9dbbfb8d999acbaf0055cc540758f8c00c4564813d910e7be02e4ceaa97fb88000b3e8e6fb4308fb943fa0a3d8ee32c3a16e67eae0817370de54bec1e13193cc845cedb70e48171e097550475684cc6600b8603a3857a5e4a709e1891a13797d55c1698f931755492099926314feec955da31fcea8a8c8e52ce2efd74008f32dbc1c00c1b3d36c1f3097d806f1687eff4a979ff967a8233f923a1af6a3104c12d9d44388bece91a248bd86f253e709cea7a380b860d8704fbf6968ffd9f39f78173726dfebbaaffebd8a33dfe29a72b9a64204debf2016ca94cbecb1b84cab9d5cc168f70082530b8cfab0225a37c8cc3044c288ab0fea2f484b56a33fe6c92d6c50f254e6b1689ac6bb1429cc36a0a7dfa85cf880b86089bc906b5ea07752b1981558fd5d329f6d428bc14dfde022745303eae0077612b9c473cd66e4d91778282ec98bbd74001b61cd71448750df642fb580ce81a62ff2f5ab4378528ccd65acff67ac2175678f598529c768247c77938ca2d529aa01b860fb9de050c059a11175be23daeef1bfc374a688d8fd776e7d2dc81fe301068a3af03ad522bd915a154dc8b5b8e12d38003f17bf9f3001e8bc682e38cfc93c6901417afe1ddbfe11b04f42ca4f321f7ba73fa98f0e8c8d75781b14c0dea847a500b860bef17561ef747e0f11fd339987a9813fa8ac1cbbd6457ab8fae4841f2217de019de7caf421631372b7eec730d9210d0128e6c560948c19ab6141d6699e0283a5b98abb8266d731f2b6435a07b44360a3f5622cff59103ec95938ab74fe1d8f01b860a47fd022dda04d8dd36e0a8127be68736e47e490b946d2d88a9fdcca0a5114a930db2319e7e9f378d1b81da865d38b01a063e9994e620483b08047da39ee2fba35f66df5734e1545d297be0225ddfda34e494c1f09825eed303d166a8b702f01b8605966b41d69c0e94e202f65823338c3ee0357b024a6ef625765bcf3c20e1786258a18221e0b9a34b43380e06984d92f0183d0f86d0dfe0cac237ee38b28876dada3ae9d1a7c1e6627c3b524d0e5d4270bbe159661622821412b3c1162881ead80b8608fad3c2111494834f30e6d8a3368f2aa156c991fa4b9d55c31da88310a61248305697ad5e885404bc474419383de4f006c89c84955321716f6ceb4d5db433fcabe843d566a5e9526d29c58b38fda9ff2a13f7841f25cda8ff091c965bdf91780b8607f9df4fdb96e20e0a480a62c48896646d96982d7fccccdf70c61f2f52ab4ea2901b5ef468203f39d9d8325843ada0d00e9562c64f79ec16d69b5921ad36306ae9ae2ab97806b354af8cb63722a96aed5fa0542fff58d2e41f6aee3cb641f7781b8609cc39207797e5c37f6336c4444cf275d0afd43a9b257a08086f15867886833744c9b5753fcff76fce3790d7f25a45e004af9831464bf43e39634ecfce8da4641da78ad4ba20e540c007dbc19e391ae69cfb7809b2d20627d553a16d7d71b5a80b860e6631bd0b82c41327b582d7b239c9893cd1162cae05f360d8808700b38ee0ee965e9bd9246d79e00c234c87aa7699800c73428f7eccd77337bf79bf6b43f48345be265c38d128d2cd7f37167cadb3565d66e6a7f67b12f4bed63e5d50748010080b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f86480b86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080f86480b86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080" + +const baklavaExtraData = "0xecc833a7747eaa8327335e8e0c6b6d8aa3a38d0063591e43ce116ccf5c89753ef90f08f90276940cc59ed03b3e763c02d54d695ffe353055f1502d943f5084d3d4692cf19b0c98a9b22de614e49e147094ef0186b8eda17be7d1230eeb8389fa85e157e1fb94edddb60ef5e90fb09707246df193a55df3564c9d94d5e454462b3fd98b85640977d7a5c783ca16222894a4f1bad7996f346c3e90b90b60a1ca8b67b51e4b945b991cc1da0b6d54f8befa9de701d8bc85c92324946dfdaa51d146ecff3b97614ef05629ea83f4997e94d2b16050810600296c9580d947e9d919d0c332ed94fe144d67068737628effb701207b3eb30ef93c699482e64996b355625efeaad12120710706275b5b9a94241752a3f65890f4ac3eaec518ff94567954e7b5941bddeaf571d5da96ce6a127feb3cadadb531f43394f86345e9c9b39ab1cbe82d7ad35854f905b8b835945c3512b1697302c497b861cbfda158f8a3c5122c94a02a692d70fd9a5269397c044aebdf1085ba090f94ac91f591f12a8b6531be43e0ccf21cd5fa0e80b094718a8ac0943a6d3ffa3ec670086bfb03817ed54094b30980ce21679314e240de5cbf437c15ad459eb89499eca23623e59c795eceb0edb666eca9ec27233994c030e92d19229c3efd708cf4b85876543ee1a3f7945c98a3414cb6ff5c24d145f952cd19f5f1f56643941979b042ae2272197f0b74170b3a6f44c3cc5c0594db871070334b961804a15f3606fbb4fac7c7f93294c656c97b765d61e0fbcb1197dc1f3a91cc80c2a494ad95a2f518c197dc9b12ee6381d88bba11f2e0e5944d4b5bf033e4a7359146c9ddb13b1c821fe1d0d3949c64da169d71c57f85b3d7a17db27c1ce94fbde494b5f32e89ccad3d396f50da32e0a599e43ce87dd794ba40db8ab5325494c9e7e07a4c4720990a39305cf90b7cb860d5b174e904a2dfbcc181640651bc4c627c86fc9611d92795f18bfe779efb377ae799329419f4ee5a1e5c12103f9a5201257da7a821439d4c86731efe1959bf4076fccf1404c6bca78b6065a466571ef3428f9e203a8ccef519aad1622c9e7401b86078e6e671e6b9cf6f8fc21fb3f9eb8cf1d64bbdc1fd1213856c7fad711e9b26600f609a2e9cf6322f5cac415eaca793018b9fa902b49d0828258aed10b247e4409c320244adcfc941af6a09e87df18edd5239df5d12e4c38858f9159f5cabb680b860dea0e3506854e5d08b932c558da137c231185169d8490cebe070eee539eea00842a3a9787c79859ee82fec298bad0e012e8e4d859b9a13bbd7c97af622a1409276b01d51cb6e8cc36c8ff7a4d2492c35d9c258d37e8eb2cd59b9c9dcd7cc3e80b860dd794eab9568cea95207f500651867934d5c923bec817eb11536ac8ca4107b127d266ff9e67d407c10a82c4f529ac100080357a69ec30d5e7cf0c68109312b6137ca43cbe7ca4a58a7ff34d1d9c70be34fbf3dce59edd1248709091b80c8b580b86049ab79869c44658468d010f831680cf798bc9b7a26145be11b7f6ce547d1e7c13781548663cf710b1d9dfde04fa44700865048c711d0bf2715dc089ed9337e822fb8ce2b889e1512d1de58b17004548e246c14f0fa37988c9edc5cc8f895b400b860855b3dc7efe86a6907eb56a0ab2b663123e28f469359bdf558864b12a867b52e73acf0833534303236c350e2f3489101289db1d0d3f9a9ebe00e18b20109a91e012dda871a62cd513b85f42fbe5684c8131067402c7e944d27ad10c76458fa80b8601e8b9ec07ff41b4a7b9636470920c7dbc175ac79441f12b3f93da55c26a48c17367ac3421106c955b4913460c9558501410c7dca5cdcd9372a82fa22eed8b9b7c725e390fb97b3a354cafeae18b99b24e07609122ac17924ae40362c48ca3b80b86021eafc9e179b482c450eb092766e79c74bddc02126eb07a56954a74d9cae7bf9aabacd22178a4646fcb2e57ba8826d00c71802db142b2f9d62de6295a9c21835965e0ea5564f37e57241561341bb870723fc828f20100250af23f59f32fa3900b8603044b1df058de34a35bf47d35a83e346e961a4cb4426fb5ad0ea9fc4d7a2d1f74ca6aa9ff3218f41e23b2c097181a80150f3e56f6ad0aa9d57ec9e71b1538a22761cf430783b73c12c0aed2232318374fdc18347270c1f4ce0fb032a788c0f00b860796cdfcfcc45ac40ce1c476d419805d59bb3511aebfc8e2c5bfbeb757ae5005713994fa21ced588c3b73d4e469dd1201a41c53b85c2a16a0582b9c0e375dc35b92b4e3fd4fc8a8802337565b840a9537368e16c4c076deebef8e81d463c77101b86070534c36b6f06081d3e2228167b4d3f0cf8e235b63850f8b2cbff9ded8c6bb62abb424a9aabfed54ce07143c374b5800d80b39dd3fcce57961858fff9f306bd74dfc576c55a6f8710bd9f35db772a53331fe69cda78ce9ab3ad4c2d2e08a8981b86051360cc471b15b3d2b1c88d1ed8595cfd89d9b9fab605408ffd1a98b0af0bc1293cbc2fe360a4362ffea907ac22bac0067e3259fb5d4064cc4813ec9449297f7b924034e9631ddc91e088b467eeae97b6133fd275f5f5830a936ba81b2bc9681b860ae05dd90d739565eae5aca380bef7a2dd3a6bdeb429f2a514864e52d8e60c0a9bc2e022292167ae221adea3ed2d19b009d715d34e3d5924a2cbc11503439c0bbe669e097698cf49a91012640c95dd7a9c77d2e06311a08d338b9541726162000b860c71dde0acd7a9014a2b4fbc0eee9d56711fa9683b6dfc9469c34ff61d52ba49f983c2de743aae2fa2bf7520de5bb9800333d40b75108cb4208d42597b60600de1bd83b282bed596e36783821af3675fd6267b578eba2613f186a43264d410801b86059e868a7daeb24dae2a81a8a397c8b3b4c80b74dbc566c0e8813194c4a0a94850abbc5fb63b56c2c55ad2a65d8534900c9810ecffa3fe2e3b5b759c9716ee3dba16eff9a1785c8b2f1c637322e951a4cc752c31ac400d8222c8ba25c47557680b860f5e06a742ce9ee4408a75a38eeb5c06803a87b97709e43308e14fd6a027e048706a31c4ef62084c8758754f95b897801b316a4cb159f60880f69a806ba64cd243727d644d1b10b8890935c2d1d753321f10ea9543418aa7e2b324ff76ab1cc80b86042cf85912f3b0420d43bc42775d51e77693fcb33a862fbfba18a05d5a8da3cdf7c5f105ee2e3413a9bed07e502af44002e3f48bec159a159db5253ea243d37a2090ff6e9c6214632100f364df129732e57512161b3957cb6f84603afc4859701b860d6592adca17cd2958a84bc1ed35e04aa33abc6ab93de2d3f78cb9e48f3036885fab3282ab7318e4103a4c078659fd9008e4ccde2494eb493f9f851eb1e0d338e2124b393d9491ad2f17dd4f468fa2ab80a7c0c2c4142f6bbb8eb6b33efcf3d01b8609787a55f5dfa9539d1b0146177f6cd56e7ca3b58a63c36cfcff3b14a78b68d7a5096219162a55e84525080dd834fb000e4157ec4baa6e21c51161f3e8b4aa8f828edcc93e1d8c6fa9a6608d01880064f86c60014c38087a83d8d8acd54375000b860effce8fdc3099723aa7be8d75a6600d3aca652f52aed0191dcb6bfb02cc88b8993fb29627ae5459f8c7affe1349a92001acdc1745ab09d073001521a9ec27158c5bf242db7fdf5745de83e340d7dbe7a9940c207a3e32d27d6406cca25552e00b8604c61d0bf7d775a61a61a5ef721663de02d0b379a1b589fe62d05917f67962459f0f8854f91384ad02b4efeed68ab3301ac31ec73f5f7e4546f329dcfab7666acbcd5022a51b229b7a9fe979d486b83984659a6cddee2bc49317e56af54498781b860d21657eb1b8e3d38c32873ce326195ee957059968d8926ea219852700d9a884289cd45c5dff6baf2528cb20bb3dc2f00f53c8221eb6c8056b14da363a98e573e4eaf46bc84eed3468364aa24848951c1de38cbe9945e47995e23ce3455dd1880b860da25af7a34042ca2e366d359dede467837181b89c6bd8a4d68c47f15952808f7a8c625a524d604a02936b7467f2a6f016a6a9bdf109c3aa4d3c6f6314dc04ff64bc4297e85232b16071eda952b0cf2b297f9a6cbba24e9805c6ea440c04d3f80b860ced170a03c9d7e505d04e13307eed25e1af3b96317799088881154ab6891fb13ca05a4ddbf7a63c233957502c2ff67002ee5015e2083b498773be347939124008c2f7f856ced900701077b5c4c6a1622c08b0f69fbf7e2e97f92ca8f1c870901b860a4c7328ef48a970211572e76c1c3bb2b8a1440233774e12479125808290fbc0e57f276034fa2735f1550fdf7a1b56101c97845c23dba5023aa922c6152028bbc93f39f222b4c58a015db70094efde2f5567f03c7eda6355e8ef9ad9c43065c81b8603b07a92815d862c4be414d89f03c9f5354190954ed4c3dd0282e5964aef7954469dee36726340db09aadfba322354c016077b90479c8c5301b362f83b71d9c351ffb57ca39ef611ca4f5d6a8d614411e77a2fcab5de1e6adb9f1a6e3c5f77a80b860aa6f0e66fe03056fdd2a7a003e0b9f29133f2ebab7c26d3699500b6ac51248f1e3f747a3c3a5b2d11d817b1252679400291d023ae71f3a9f5723b122081a6932075a974f75467aef517944c8d206d307c4a5ebe8fc8aa374baf75dfbece96c00b86038e7f70fab621ef588c0fe24d9f288387913faaf52a5813f7da47d5fa8a9e32a098e6323ad2c1133221a43bd97b40c01d6cad22a6c7c2969beb123a42b41539c3a38da1b0ed7d762aa9ab67239a1d0afdfc3466287ca4ad33bb69b647f144600b860acde65212b2f8f1e15bba36f0b2cc0d345678718695e78c7bfbe1566c4691c0352237896e812217184cdc897bf96f500cf616132520278666d00d27a07610de649687cfc28f8c568dce232583004b21d0ab8ea4361d84eb6f9540a1d0b3c3800b860761f7d6dd8c2df51b358a60bd809c693a364b0aefcfa4cb492525ab82b9a3c506e1ba17347f82cff57411ddd6bfd9d01cf9bd337de5916008358cf6f1b411131f43818607db22f439ecb431ae773cea4d39b8c3057b35c64b40beedfdd9e6d8080b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f86480b86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080f86480b86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080" + +const alfajoresExtraData = "0xecc833a7747eaa8327335e8e0c6b6d8aa3a38d0063591e43ce116ccf5c89753ef905bbf8d294456f41406b32c45d59e539e4bba3d7898c3584da94dd1f519f63423045f526b8c83edc0eb4ba6434a494050f34537f5b2a00b9b9c752cb8500a3fce3da7d94cda518f6b5a797c3ec45d37c65b83e0b0748edca94b4e92c94a2712e98c020a81868264bde52c188cb94ae1ec841923811219b98aceb1db297aade2f46f394621843731fe33418007c06ee48cfd71e0ea828d9942a43f97f8bf959e31f69a894ebd80a88572c855394ad682035be6ab6f06e478d2bdab0eab6477b460e9430d060f129817c4de5fbc1366d53e19f43c8c64ff903d4b86011877b768127c8eb0f122fbe69553bc9d142d27c06a85c6eeb7b8b457f511e50c33a57fcbc5fd6d1823f69a111f8010151a17f6a8798a25343f5403b1e6a595c7d9698af3db78b013d26a761fc201b3cf793be5f0a0a849b3f68a8bfa81e7001b860d882cd4cc09109928e9517644d5303610155978cf5e3b7ad6122daa19c3dab3da8c439bc763d6d3eef18a38ebb0d3200664b94fab11adbb3f44b963969763b590af45931c482396be88a185214c9c8690615aae5197e852bc1d04b3dbd03ab80b86051588d46ba8998d944a30cde93bfe946e774ef1f6fe2fb559a74ffebf60d1ad967b876a038c6e312d0c20752cbc8440012293b6ea417f32a163caedeaaae7aad3c1b31be1fe86c405924b1be7d0aaae6f3ba567ee907d0d4c00dce5091442380b8601f2becc31c1f0141e8c5768c5f07d02d1342c086c037cce70aaf3629b40ea017884a81163f58697b020b21fe39c440006970bc1f52b847d7262599ae92ee7db45ad38efe5612c8ed42d9db9380da0769bab713f5259b7c015998296bf02a0a01b860d02ec615b916bba4fe7e65a3d79e607aa27bb5a84b0c2f242e9d8f379512cf40051a43030e55aca965d91c905b656d006434d95b7034bfc2e5e2ef7384e8cd640efae740558216f6f9db24c6d1acf755746dfbb68c76961593741105725d5680b860d6e86d5e73db3b3a2c96c6caa1a7e153e17adb13fb541943a44bfa90beab38aa73ad453d918fea2ba57c0a67115d0401c56946d8894f346d796864e9344fd1439dd1345de762f85d7e18e311b35c3cbe492886ef8bc872b4aabfa23c2e38a901b8601cf59939da60cdb9aff09f76e6070a17fa21356ca7016390ef4444243e12ab7ed7a233d7ca48b0d17870ba015a4410014e5cac8d456e03ec2908d347627d5e9ecd496ce990d10900ddc529300eef3d037e48d79f03ad2b6bcd48affe2ddf2681b8601cfe8876c0b89ef15128bb27eb69e7939b4a888b0a81195d5fd1bbda748a29838274e652dcf857f4090bb85343055300ca3e75a980b100403d3b6d34f62c6a86bbd75203391c63dd405725c69241a828e6892f623ed5b35c8dc132b032061201b860a6fc71d63c5adedb7b30b9e0ba3d83debf86d12ba235c13584a9cbad410f082030427be4f8a9127889979c3eea58860031af128deece487df5aef9d999c8dc2fb51f308eb1ee229e6bbd6860138d4fcf4209eb7bec62ca70dd8643104003c200b8606b7adb5d01e3fd72ae2c4ff17e6620dc383431e0ebe06c9af5b94207f380287429043e7bbe417b82d0aed2e43dc7b8002bb52886773e4a2c23bf0ebfd401471e8da3cf3a0a7e0949d9ad4de38138a787a975993ba311525ce8be331cd60d670080b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f86480b86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080f86480b86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080" + +const developerExtraData = "0xecc833a7747eaa8327335e8e0c6b6d8aa3a38d0063591e43ce116ccf5c89753ef9018ad59447e172f6cfb6c7d01c1574fa3e2be7cc73269d95f862b8604fa3f67fc913878b068d1fa1cdddc54913d3bf988dbe5a36a20fa888f20d4894c408a6773f3d7bde11154f2a3076b700d345a42fd25a0e5e83f4db5586ac7979ac2053cd95d8f2efd3e959571ceccaa743e02cf4be3f5d7aaddb0b06fc9aff0080b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f86480b86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080f86480b86000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080"
diff --git go-ethereum/core/forkid/forkid_test.go celo/core/forkid/forkid_test.go index 3e33632ddf8fad5421e1cbb5cfbe8f67c2bc822c..c8a1a4c5994b2f8e8f1e72e71cc407130b5956a4 100644 --- go-ethereum/core/forkid/forkid_test.go +++ celo/core/forkid/forkid_test.go @@ -29,6 +29,7 @@ // TestCreation tests that different genesis and fork rule combinations result in // the correct fork ID. func TestCreation(t *testing.T) { + t.Skip("no test cases") type testcase struct { head uint64 want ID @@ -37,101 +38,7 @@ tests := []struct { config *params.ChainConfig genesis common.Hash cases []testcase - }{ - // Mainnet test cases - { - params.MainnetChainConfig, - params.MainnetGenesisHash, - []testcase{ - {0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced - {1149999, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block - {1150000, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // First Homestead block - {1919999, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // Last Homestead block - {1920000, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // First DAO block - {2462999, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // Last DAO block - {2463000, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // First Tangerine block - {2674999, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // Last Tangerine block - {2675000, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // First Spurious block - {4369999, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // Last Spurious block - {4370000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // First Byzantium block - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // Last Byzantium block - {7280000, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // First and last Constantinople, first Petersburg block - {9068999, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // Last Petersburg block - {9069000, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // First Istanbul and first Muir Glacier block - {9199999, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block - {9200000, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // First Muir Glacier block - {12243999, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // Last Muir Glacier block - {12244000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // First Berlin block - {12964999, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // Last Berlin block - {12965000, ID{Hash: checksumToBytes(0xb715077d), Next: 0}}, // First London block - {20000000, ID{Hash: checksumToBytes(0xb715077d), Next: 0}}, // Future London block - }, - }, - // Ropsten test cases - { - params.RopstenChainConfig, - params.RopstenGenesisHash, - []testcase{ - {0, ID{Hash: checksumToBytes(0x30c7ddbc), Next: 10}}, // Unsynced, last Frontier, Homestead and first Tangerine block - {9, ID{Hash: checksumToBytes(0x30c7ddbc), Next: 10}}, // Last Tangerine block - {10, ID{Hash: checksumToBytes(0x63760190), Next: 1700000}}, // First Spurious block - {1699999, ID{Hash: checksumToBytes(0x63760190), Next: 1700000}}, // Last Spurious block - {1700000, ID{Hash: checksumToBytes(0x3ea159c7), Next: 4230000}}, // First Byzantium block - {4229999, ID{Hash: checksumToBytes(0x3ea159c7), Next: 4230000}}, // Last Byzantium block - {4230000, ID{Hash: checksumToBytes(0x97b544f3), Next: 4939394}}, // First Constantinople block - {4939393, ID{Hash: checksumToBytes(0x97b544f3), Next: 4939394}}, // Last Constantinople block - {4939394, ID{Hash: checksumToBytes(0xd6e2149b), Next: 6485846}}, // First Petersburg block - {6485845, ID{Hash: checksumToBytes(0xd6e2149b), Next: 6485846}}, // Last Petersburg block - {6485846, ID{Hash: checksumToBytes(0x4bc66396), Next: 7117117}}, // First Istanbul block - {7117116, ID{Hash: checksumToBytes(0x4bc66396), Next: 7117117}}, // Last Istanbul block - {7117117, ID{Hash: checksumToBytes(0x6727ef90), Next: 9812189}}, // First Muir Glacier block - {9812188, ID{Hash: checksumToBytes(0x6727ef90), Next: 9812189}}, // Last Muir Glacier block - {9812189, ID{Hash: checksumToBytes(0xa157d377), Next: 10499401}}, // First Berlin block - {10499400, ID{Hash: checksumToBytes(0xa157d377), Next: 10499401}}, // Last Berlin block - {10499401, ID{Hash: checksumToBytes(0x7119b6b3), Next: 0}}, // First London block - {11000000, ID{Hash: checksumToBytes(0x7119b6b3), Next: 0}}, // Future London block - }, - }, - // Rinkeby test cases - { - params.RinkebyChainConfig, - params.RinkebyGenesisHash, - []testcase{ - {0, ID{Hash: checksumToBytes(0x3b8e0691), Next: 1}}, // Unsynced, last Frontier block - {1, ID{Hash: checksumToBytes(0x60949295), Next: 2}}, // First and last Homestead block - {2, ID{Hash: checksumToBytes(0x8bde40dd), Next: 3}}, // First and last Tangerine block - {3, ID{Hash: checksumToBytes(0xcb3a64bb), Next: 1035301}}, // First Spurious block - {1035300, ID{Hash: checksumToBytes(0xcb3a64bb), Next: 1035301}}, // Last Spurious block - {1035301, ID{Hash: checksumToBytes(0x8d748b57), Next: 3660663}}, // First Byzantium block - {3660662, ID{Hash: checksumToBytes(0x8d748b57), Next: 3660663}}, // Last Byzantium block - {3660663, ID{Hash: checksumToBytes(0xe49cab14), Next: 4321234}}, // First Constantinople block - {4321233, ID{Hash: checksumToBytes(0xe49cab14), Next: 4321234}}, // Last Constantinople block - {4321234, ID{Hash: checksumToBytes(0xafec6b27), Next: 5435345}}, // First Petersburg block - {5435344, ID{Hash: checksumToBytes(0xafec6b27), Next: 5435345}}, // Last Petersburg block - {5435345, ID{Hash: checksumToBytes(0xcbdb8838), Next: 8290928}}, // First Istanbul block - {8290927, ID{Hash: checksumToBytes(0xcbdb8838), Next: 8290928}}, // Last Istanbul block - {8290928, ID{Hash: checksumToBytes(0x6910c8bd), Next: 8897988}}, // First Berlin block - {8897987, ID{Hash: checksumToBytes(0x6910c8bd), Next: 8897988}}, // Last Berlin block - {8897988, ID{Hash: checksumToBytes(0x8E29F2F3), Next: 0}}, // First London block - {10000000, ID{Hash: checksumToBytes(0x8E29F2F3), Next: 0}}, // Future London block - }, - }, - // Goerli test cases - { - params.GoerliChainConfig, - params.GoerliGenesisHash, - []testcase{ - {0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block - {1561650, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Last Petersburg block - {1561651, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // First Istanbul block - {4460643, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // Last Istanbul block - {4460644, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // First Berlin block - {5000000, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // Last Berlin block - {5062605, ID{Hash: checksumToBytes(0xB8C6299D), Next: 0}}, // First London block - {6000000, ID{Hash: checksumToBytes(0xB8C6299D), Next: 0}}, // Future London block - }, - }, - } + }{} for i, tt := range tests { for j, ttt := range tt.cases { if have := NewID(tt.config, tt.genesis, ttt.head); have != ttt.want { @@ -144,77 +51,12 @@ // TestValidation tests that a local peer correctly validates and accepts a remote // fork ID. func TestValidation(t *testing.T) { + t.Skip("no test cases") tests := []struct { head uint64 id ID err error - }{ - // Local is mainnet Petersburg, remote announces the same. No future fork is announced. - {7987396, ID{Hash: checksumToBytes(0x668db0af), Next: 0}, nil}, - - // Local is mainnet Petersburg, remote announces the same. Remote also announces a next fork - // at block 0xffffffff, but that is uncertain. - {7987396, ID{Hash: checksumToBytes(0x668db0af), Next: math.MaxUint64}, nil}, - - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // also Byzantium, but it's not yet aware of Petersburg (e.g. non updated node before the fork). - // In this case we don't know if Petersburg passed yet or not. - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, - - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // also Byzantium, and it's also aware of Petersburg (e.g. updated node before the fork). We - // don't know if Petersburg passed yet (will pass) or not. - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, - - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // also Byzantium, and it's also aware of some random fork (e.g. misconfigured Petersburg). As - // neither forks passed at neither nodes, they may mismatch, but we still connect for now. - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: math.MaxUint64}, nil}, - - // Local is mainnet exactly on Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote - // is simply out of sync, accept. - {7280000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, - - // Local is mainnet Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote - // is simply out of sync, accept. - {7987396, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil}, - - // Local is mainnet Petersburg, remote announces Spurious + knowledge about Byzantium. Remote - // is definitely out of sync. It may or may not need the Petersburg update, we don't know yet. - {7987396, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil}, - - // Local is mainnet Byzantium, remote announces Petersburg. Local is out of sync, accept. - {7279999, ID{Hash: checksumToBytes(0x668db0af), Next: 0}, nil}, - - // Local is mainnet Spurious, remote announces Byzantium, but is not aware of Petersburg. Local - // out of sync. Local also knows about a future fork, but that is uncertain yet. - {4369999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil}, - - // Local is mainnet Petersburg. remote announces Byzantium but is not aware of further forks. - // Remote needs software update. - {7987396, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, ErrRemoteStale}, - - // Local is mainnet Petersburg, and isn't aware of more forks. Remote announces Petersburg + - // 0xffffffff. Local needs software update, reject. - {7987396, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, - - // Local is mainnet Byzantium, and is aware of Petersburg. Remote announces Petersburg + - // 0xffffffff. Local needs software update, reject. - {7279999, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale}, - - // Local is mainnet Petersburg, remote is Rinkeby Petersburg. - {7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale}, - - // Local is mainnet London, far in the future. Remote announces Gopherium (non existing fork) - // at some future block 88888888, for itself, but past block for local. Local is incompatible. - // - // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). - {88888888, ID{Hash: checksumToBytes(0xb715077d), Next: 88888888}, ErrLocalIncompatibleOrStale}, - - // Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing - // fork) at block 7279999, before Petersburg. Local is incompatible. - {7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrLocalIncompatibleOrStale}, - } + }{} for i, tt := range tests { filter := newFilter(params.MainnetChainConfig, params.MainnetGenesisHash, func() uint64 { return tt.head }) if err := filter(tt.id); err != tt.err {
diff --git go-ethereum/core/asm/asm_test.go celo/core/asm/asm_test.go index 92b26b67a5caa47cf6c5fe166d34209c705950d0..3a20d2db7471ab7431957da7e90d552405d1a25a 100644 --- go-ethereum/core/asm/asm_test.go +++ celo/core/asm/asm_test.go @@ -17,9 +17,8 @@ package asm   import ( + "encoding/hex" "testing" - - "encoding/hex" )   // Tests disassembling the instructions for valid evm code
diff --git go-ethereum/core/state/statedb.go celo/core/state/statedb.go index 448ada0fce6df6cfdecae3039476bb180465bbc5..34d15fe1e15b6418ee1f5c3f3c4100881758635e 100644 --- go-ethereum/core/state/statedb.go +++ celo/core/state/statedb.go @@ -701,6 +701,7 @@ } for hash, preimage := range s.preimages { state.preimages[hash] = preimage } + // Do we need to copy the access list? In practice: No. At the start of a // transaction, the access list is empty. In practice, we only ever copy state // _between_ transactions/blocks, never in the middle of a transaction.
diff --git go-ethereum/core/rawdb/freezer_table_test.go celo/core/rawdb/freezer_table_test.go index aa91816fde93aa1c0f2beb16f6e99ee04ddf422c..6797852d7ad55f4e232bbf3bc9aadcc992019f50 100644 --- go-ethereum/core/rawdb/freezer_table_test.go +++ celo/core/rawdb/freezer_table_test.go @@ -25,12 +25,15 @@ "path/filepath" "testing" "time"   + "github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/metrics" "github.com/stretchr/testify/require" )   func init() { rand.Seed(time.Now().Unix()) + max, _ := fdlimit.Maximum() + fdlimit.Raise(uint64(max)) }   // TestFreezerBasics test initializing a freezertable from scratch, writing to the table, @@ -97,7 +100,10 @@ data := getChunk(15, x) batch := f.newBatch() require.NoError(t, batch.AppendRaw(uint64(x), data)) require.NoError(t, batch.commit()) - f.Close() + err = f.Close() + if err != nil { + t.Fatal(err) + }   f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, true) if err != nil { @@ -115,7 +121,10 @@ } if !bytes.Equal(got, exp) { t.Fatalf("test %d, got \n%x != \n%x", y, got, exp) } - f.Close() + err = f.Close() + if err != nil { + t.Fatal(err) + } f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, true) if err != nil { t.Fatal(err) @@ -233,6 +242,9 @@ // And if we open it, we should now be able to read all of them (new values) { f, _ := newTable(os.TempDir(), fname, rm, wm, sg, 50, true) + if err != nil { + t.Fatal(err) + } for y := 1; y < 255; y++ { exp := getChunk(15, ^y) got, err := f.Retrieve(uint64(y))
diff --git go-ethereum/core/rawdb/chain_iterator_test.go celo/core/rawdb/chain_iterator_test.go index aa92f559d710c11a6e04b090621744b6bf561b84..cb393e641830726eace77f04e16145842ad6178a 100644 --- go-ethereum/core/rawdb/chain_iterator_test.go +++ celo/core/rawdb/chain_iterator_test.go @@ -49,6 +49,7 @@ Value: big.NewInt(111), Data: []byte{0x11, 0x11, 0x11}, }) } else { + tx = types.NewTx(&types.AccessListTx{ ChainID: big.NewInt(1337), Nonce: i, @@ -127,6 +128,7 @@ Value: big.NewInt(111), Data: []byte{0x11, 0x11, 0x11}, }) } else { + tx = types.NewTx(&types.AccessListTx{ ChainID: big.NewInt(1337), Nonce: i,
diff --git go-ethereum/core/rawdb/accessors_indexes_test.go celo/core/rawdb/accessors_indexes_test.go index 48026b66c1bc77ee8f9a2b1b833d0fb67fad1552..e5620e888a0cea6595cf2aad5e11e401ca60eb6a 100644 --- go-ethereum/core/rawdb/accessors_indexes_test.go +++ celo/core/rawdb/accessors_indexes_test.go @@ -94,9 +94,9 @@ for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { db := NewMemoryDatabase()   - tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) - tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22}) - tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) + tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), nil, nil, nil, []byte{0x11, 0x11, 0x11}) + tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), nil, nil, nil, []byte{0x22, 0x22, 0x22}) + tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), nil, nil, nil, []byte{0x33, 0x33, 0x33}) txs := []*types.Transaction{tx1, tx2, tx3}   block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, newHasher()) @@ -141,7 +141,7 @@ db := NewMemoryDatabase() for i := uint(0); i < 2; i++ { for s := uint64(0); s < 2; s++ { WriteBloomBits(db, i, s, params.MainnetGenesisHash, []byte{0x01, 0x02}) - WriteBloomBits(db, i, s, params.RinkebyGenesisHash, []byte{0x01, 0x02}) + WriteBloomBits(db, i, s, params.AlfajoresGenesisHash, []byte{0x01, 0x02}) } } check := func(bit uint, section uint64, head common.Hash, exist bool) { @@ -155,25 +155,25 @@ } } // Check the existence of written data. check(0, 0, params.MainnetGenesisHash, true) - check(0, 0, params.RinkebyGenesisHash, true) + check(0, 0, params.AlfajoresGenesisHash, true)   // Check the existence of deleted data. DeleteBloombits(db, 0, 0, 1) check(0, 0, params.MainnetGenesisHash, false) - check(0, 0, params.RinkebyGenesisHash, false) + check(0, 0, params.AlfajoresGenesisHash, false) check(0, 1, params.MainnetGenesisHash, true) - check(0, 1, params.RinkebyGenesisHash, true) + check(0, 1, params.AlfajoresGenesisHash, true)   // Check the existence of deleted data. DeleteBloombits(db, 0, 0, 2) check(0, 0, params.MainnetGenesisHash, false) - check(0, 0, params.RinkebyGenesisHash, false) + check(0, 0, params.AlfajoresGenesisHash, false) check(0, 1, params.MainnetGenesisHash, false) - check(0, 1, params.RinkebyGenesisHash, false) + check(0, 1, params.AlfajoresGenesisHash, false)   // Bit1 shouldn't be affect. check(1, 0, params.MainnetGenesisHash, true) - check(1, 0, params.RinkebyGenesisHash, true) + check(1, 0, params.AlfajoresGenesisHash, true) check(1, 1, params.MainnetGenesisHash, true) - check(1, 1, params.RinkebyGenesisHash, true) + check(1, 1, params.AlfajoresGenesisHash, true) }
diff --git go-ethereum/core/rawdb/doc.go celo/core/rawdb/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..8da89086714af70adbf886cb826c683c2104b2b5 --- /dev/null +++ celo/core/rawdb/doc.go @@ -0,0 +1,38 @@ +/* +Package rawdb contains a collection of low level database accessors. + +The rawdb serves as a wrapper around level db and a file based database +providing an ethereum specific interface. + +The rawdb introduces the concept of ancient items, which are stored in the +freezer (a flat file based db) rather than in leveldb, this structure was +chosen to improve performance and reduce memory consumption. See this +PR (https://github.com/ethereum/go-ethereum/pull/19244) for more detail. + +The freezer is split into a number of tables, each holding a different type of data. + +They are: + +* block headers +* canonical block hashes +* block bodies +* receipts +* total difficulty + +Each table contains one entry per block and the data for each table is stored +in a file on disk that is only ever appended to. If a file grows beyond a +certain threshold then subsequent entries for that table are appended into a +new sequentially numbered file. + +Sometimes the tables can get out of sync and in such situations they are all +truncated to match the table with the smallest amount of entries. + +The freezer starts a background routine to periodically collect values written +to leveldb and move them to freezer files. Since data in the freezer cannot be +modified the items added to the freezer must be considered final (I.E, no +chance of a re-org) currently that is ensured by only freezing blocks at least +90000 behind the head block. This 90000 threshold is referred to as the +'FullImmutabilityThreshold'. + +*/ +package rawdb
diff --git go-ethereum/core/rawdb/accessors_chain_test.go celo/core/rawdb/accessors_chain_test.go index 17514497eccc805de4c127e137b4806741f8131f..77bce12a6fc3afb7bad29b23c8a69911f552b560 100644 --- go-ethereum/core/rawdb/accessors_chain_test.go +++ celo/core/rawdb/accessors_chain_test.go @@ -73,7 +73,7 @@ func TestBodyStorage(t *testing.T) { db := NewMemoryDatabase()   // Create a test body to move around the database and make sure it's really new - body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}} + body := &types.Body{Randomness: &types.Randomness{}, EpochSnarkData: &types.EpochSnarkData{}}   hasher := sha3.NewLegacyKeccak256() rlp.Encode(hasher, body) @@ -86,7 +86,7 @@ // Write and verify the body in the database WriteBody(db, hash, 0, body) if entry := ReadBody(db, hash, 0); entry == nil { t.Fatalf("Stored body not found") - } else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(types.Transactions(body.Transactions), newHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) { + } else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(types.Transactions(body.Transactions), newHasher()) { t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body) } if entry := ReadBodyRLP(db, hash, 0); entry == nil { @@ -113,7 +113,6 @@ // Create a test block to move around the database and make sure it's really new block := types.NewBlockWithHeader(&types.Header{ Extra: []byte("test block"), - UncleHash: types.EmptyUncleHash, TxHash: types.EmptyRootHash, ReceiptHash: types.EmptyRootHash, }) @@ -140,7 +139,7 @@ t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header()) } if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil { t.Fatalf("Stored body not found") - } else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(block.Transactions(), newHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) { + } else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(block.Transactions(), newHasher()) { t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body()) } // Delete the block and verify the execution @@ -161,7 +160,6 @@ func TestPartialBlockStorage(t *testing.T) { db := NewMemoryDatabase() block := types.NewBlockWithHeader(&types.Header{ Extra: []byte("test block"), - UncleHash: types.EmptyUncleHash, TxHash: types.EmptyRootHash, ReceiptHash: types.EmptyRootHash, }) @@ -198,7 +196,6 @@ // Create a test block to move around the database and make sure it's really new block := types.NewBlockWithHeader(&types.Header{ Number: big.NewInt(1), Extra: []byte("bad block"), - UncleHash: types.EmptyUncleHash, TxHash: types.EmptyRootHash, ReceiptHash: types.EmptyRootHash, }) @@ -216,7 +213,6 @@ // Write one more bad block blockTwo := types.NewBlockWithHeader(&types.Header{ Number: big.NewInt(2), Extra: []byte("bad block two"), - UncleHash: types.EmptyUncleHash, TxHash: types.EmptyRootHash, ReceiptHash: types.EmptyRootHash, }) @@ -235,7 +231,6 @@ for _, n := range rand.Perm(100) { block := types.NewBlockWithHeader(&types.Header{ Number: big.NewInt(int64(n)), Extra: []byte("bad block"), - UncleHash: types.EmptyUncleHash, TxHash: types.EmptyRootHash, ReceiptHash: types.EmptyRootHash, }) @@ -345,10 +340,10 @@ func TestBlockReceiptStorage(t *testing.T) { db := NewMemoryDatabase()   // Create a live block since we need metadata to reconstruct the receipt - tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil) - tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil) + tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil, nil, nil, nil) + tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil, nil, nil, nil)   - body := &types.Body{Transactions: types.Transactions{tx1, tx2}} + body := &types.Body{Transactions: types.Transactions{tx1, tx2}, Randomness: &types.Randomness{}, EpochSnarkData: &types.EpochSnarkData{}}   // Create the two receipts to manage afterwards receipt1 := &types.Receipt{ @@ -380,7 +375,7 @@ receipts := []*types.Receipt{receipt1, receipt2}   // Check that no receipt entries are in a pristine database hash := common.BytesToHash([]byte{0x03, 0x14}) - if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 { + if rs := ReadReceipts(db, hash, 0, params.IstanbulTestChainConfig); len(rs) != 0 { t.Fatalf("non existent receipts returned: %v", rs) } // Insert the body that corresponds to the receipts @@ -388,7 +383,7 @@ WriteBody(db, hash, 0, body)   // Insert the receipt slice into the database and check presence WriteReceipts(db, hash, 0, receipts) - if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) == 0 { + if rs := ReadReceipts(db, hash, 0, params.IstanbulTestChainConfig); len(rs) == 0 { t.Fatalf("no receipts returned") } else { if err := checkReceiptsRLP(rs, receipts); err != nil { @@ -397,7 +392,7 @@ } } // Delete the body and ensure that the receipts are no longer returned (metadata can't be recomputed) DeleteBody(db, hash, 0) - if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); rs != nil { + if rs := ReadReceipts(db, hash, 0, params.IstanbulTestChainConfig); rs != nil { t.Fatalf("receipts returned when body was deleted: %v", rs) } // Ensure that receipts without metadata can be returned without the block body too @@ -408,7 +403,7 @@ // Sanity check that body alone without the receipt is a full purge WriteBody(db, hash, 0, body)   DeleteReceipts(db, hash, 0) - if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 { + if rs := ReadReceipts(db, hash, 0, params.IstanbulTestChainConfig); len(rs) != 0 { t.Fatalf("deleted receipts returned: %v", rs) } } @@ -450,7 +445,6 @@ // Create a test block block := types.NewBlockWithHeader(&types.Header{ Number: big.NewInt(0), Extra: []byte("test block"), - UncleHash: types.EmptyUncleHash, TxHash: types.EmptyRootHash, ReceiptHash: types.EmptyRootHash, }) @@ -536,9 +530,8 @@ func TestHashesInRange(t *testing.T) { mkHeader := func(number, seq int) *types.Header { h := types.Header{ - Difficulty: new(big.Int), Number: big.NewInt(int64(number)), - GasLimit: uint64(seq), + GasUsed: uint64(seq), } return &h } @@ -648,7 +641,7 @@ header := &types.Header{ Number: big.NewInt(int64(i)), Extra: []byte("test block"), } - blocks[i] = types.NewBlockWithHeader(header).WithBody(txs, nil) + blocks[i] = types.NewBlockWithHeader(header).WithBody(txs, nil, nil) blocks[i].Hash() // pre-cache the block hash } return blocks @@ -695,15 +688,18 @@ Index: l.Index, } }   +func TestReadLogsCommonBlock(t *testing.T) { testReadLogs(t, false) } +func TestReadLogsIBFTBlock(t *testing.T) { testReadLogs(t, true) } + // Tests that logs associated with a single block can be retrieved. -func TestReadLogs(t *testing.T) { +func testReadLogs(t *testing.T, ibftBlock bool) { db := NewMemoryDatabase()   // Create a live block since we need metadata to reconstruct the receipt - tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil) - tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil) + tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil, nil, nil, nil) + tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil, nil, nil, nil)   - body := &types.Body{Transactions: types.Transactions{tx1, tx2}} + body := &types.Body{Randomness: &types.Randomness{}, EpochSnarkData: &types.EpochSnarkData{}, Transactions: types.Transactions{tx1, tx2}}   // Create the two receipts to manage afterwards receipt1 := &types.Receipt{ @@ -734,6 +730,21 @@ receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2}) receipts := []*types.Receipt{receipt1, receipt2}   hash := common.BytesToHash([]byte{0x03, 0x14}) + if ibftBlock { + receipt3 := types.NewReceipt(nil, false, 0) + receipt3.Logs = []*types.Log{ + {Address: common.BytesToAddress([]byte{0x33})}, + {Address: common.BytesToAddress([]byte{0x03, 0x33})}, + } + receipt3.Bloom = types.CreateBloom(types.Receipts{receipt3}) + for i := range receipt3.Logs { + receipt3.Logs[i].TxIndex = uint(len(receipts)) + receipt3.Logs[i].TxHash = hash + receipt3.Logs[i].BlockHash = hash + } + receipts = append(receipts, receipt3) + } + // Check that no receipt entries are in a pristine database if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 { t.Fatalf("non existent receipts returned: %v", rs) @@ -748,7 +759,7 @@ logs := ReadLogs(db, hash, 0) if len(logs) == 0 { t.Fatalf("no logs returned") } - if have, want := len(logs), 2; have != want { + if have, want := len(logs), len(receipts); have != want { t.Fatalf("unexpected number of logs returned, have %d want %d", have, want) } if have, want := len(logs[0]), 2; have != want { @@ -756,6 +767,11 @@ t.Fatalf("unexpected number of logs[0] returned, have %d want %d", have, want) } if have, want := len(logs[1]), 2; have != want { t.Fatalf("unexpected number of logs[1] returned, have %d want %d", have, want) + } + if ibftBlock { + if have, want := len(logs[2]), 2; have != want { + t.Fatalf("unexpected number of logs[2] returned, have %d want %d", have, want) + } }   // Fill in log fields so we can compare their rlp encoding
diff --git go-ethereum/core/rawdb/database.go celo/core/rawdb/database.go index 24bb3259ea84706611cd5addb1d81c3b405fecac..dc117b441a0d159c68ca421ce2f0a21809f2163f 100644 --- go-ethereum/core/rawdb/database.go +++ celo/core/rawdb/database.go @@ -305,7 +305,6 @@ accountSnaps stat storageSnaps stat preimages stat bloomBits stat - cliqueSnaps stat   // Ancient store statistics ancientHeadersSize common.StorageSize @@ -363,8 +362,6 @@ case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength): bloomBits.Add(size) case bytes.HasPrefix(key, BloomBitsIndexPrefix): bloomBits.Add(size) - case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength: - cliqueSnaps.Add(size) case bytes.HasPrefix(key, []byte("cht-")) || bytes.HasPrefix(key, []byte("chtIndexV2-")) || bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie @@ -425,7 +422,6 @@ {"Key-Value store", "Trie nodes", tries.Size(), tries.Count()}, {"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()}, {"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()}, {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()}, - {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()}, {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()}, {"Ancient store", "Headers", ancientHeadersSize.String(), ancients.String()}, {"Ancient store", "Bodies", ancientBodiesSize.String(), ancients.String()},
diff --git go-ethereum/core/rawdb/celo.go celo/core/rawdb/celo.go new file mode 100644 index 0000000000000000000000000000000000000000..0cd61b6720751ce184d384a844526a231127cfc6 --- /dev/null +++ celo/core/rawdb/celo.go @@ -0,0 +1,48 @@ +package rawdb + +import ( + "bytes" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +// WriteRandomCommitmentCache will write a random beacon commitment's associated block parent hash +// (which is used to calculate the commitmented random number). +func WriteRandomCommitmentCache(db ethdb.KeyValueWriter, commitment common.Hash, parentHash common.Hash) { + if err := db.Put(randomnessCommitmentKey(commitment), parentHash.Bytes()); err != nil { + log.Crit("Failed to store randomness commitment cache entry", "err", err) + } +} + +// ReadRandomCommitmentCache will retun the random beacon commit's associated block parent hash. +func ReadRandomCommitmentCache(db ethdb.Reader, commitment common.Hash) common.Hash { + parentHash, err := db.Get(randomnessCommitmentKey(commitment)) + if err != nil { + log.Warn("Error in trying to retrieve randomness commitment cache entry", "error", err) + return common.Hash{} + } + + return common.BytesToHash(parentHash) +} + +// randomnessCommitmentKey will return the key for where the +// given commitment's cached key-value entry +func randomnessCommitmentKey(commitment common.Hash) []byte { + dbRandomnessPrefix := []byte("db-randomness-prefix") + return append(dbRandomnessPrefix, commitment.Bytes()...) +} + +// Extra hash comparison is necessary since ancient database only maintains +// the canonical data. +func headerHash(data []byte) common.Hash { + header := new(types.Header) + if err := rlp.Decode(bytes.NewReader(data), header); err != nil { + log.Error("Error decoding stored block header", "err", err) + } + + return header.Hash() +}
diff --git go-ethereum/core/rawdb/accessors_chain.go celo/core/rawdb/accessors_chain.go index eb1e152f5091ef5ff895264cfd74019ad115f96f..c5d0f27e05bf52b715df00d79d6e7d63fc5ddb62 100644 --- go-ethereum/core/rawdb/accessors_chain.go +++ celo/core/rawdb/accessors_chain.go @@ -26,7 +26,6 @@ "sort"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -308,7 +307,8 @@ // First try to look up the data in ancient database. Extra hash // comparison is necessary since ancient database only maintains // the canonical data. data, _ := db.Ancient(freezerHeaderTable, number) - if len(data) > 0 && crypto.Keccak256Hash(data) == hash { + + if len(data) > 0 && headerHash(data) == hash { return data } // Then try to look up the data in leveldb. @@ -321,7 +321,7 @@ // So during the first check for ancient db, the data is not yet in there, // but when we reach into leveldb, the data was already moved. That would // result in a not found error. data, _ = db.Ancient(freezerHeaderTable, number) - if len(data) > 0 && crypto.Keccak256Hash(data) == hash { + if len(data) > 0 && headerHash(data) == hash { return data } return nil // Can't find the data anywhere. @@ -692,14 +692,25 @@ } return nil }   +func isBlockReceipt(receipt *receiptLogs) bool { + return len(receipt.Logs) > 0 && receipt.Logs[0].TxHash == receipt.Logs[0].BlockHash +} + // DeriveLogFields fills the logs in receiptLogs with information such as block number, txhash, etc. func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, txs types.Transactions) error { logIndex := uint(0) - if len(txs) != len(receipts) { + // The receipts may include an additional "block finalization" receipt (only IBFT) + if len(txs) != len(receipts) && (len(txs)+1 != len(receipts) || !isBlockReceipt(receipts[len(receipts)-1])) { return errors.New("transaction and receipt count mismatch") } for i := 0; i < len(receipts); i++ { - txHash := txs[i].Hash() + // Only IBFT: block finalization receipt has TxHash == blockHash + var txHash common.Hash + if i == len(txs) { + txHash = hash + } else { + txHash = txs[i].Hash() + } // The derived log fields can simply be set from the block and transaction for j := 0; j < len(receipts[i].Logs); j++ { receipts[i].Logs[j].BlockNumber = number @@ -759,7 +770,7 @@ body := ReadBody(db, hash, number) if body == nil { return nil } - return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles) + return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Randomness, body.EpochSnarkData) }   // WriteBlock serializes a block into the database, header and body separately. @@ -769,9 +780,8 @@ WriteHeader(db, block.Header()) }   // WriteAncientBlock writes entire block data into ancient store and returns the total written size. -func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts, td *big.Int) (int64, error) { +func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts, _ *big.Int) (int64, error) { var ( - tdSum = new(big.Int).Set(td) stReceipts []*types.ReceiptForStorage ) return db.ModifyAncients(func(op ethdb.AncientWriteOp) error { @@ -782,10 +792,7 @@ for _, receipt := range receipts[i] { stReceipts = append(stReceipts, (*types.ReceiptForStorage)(receipt)) } header := block.Header() - if i > 0 { - tdSum.Add(tdSum, header.Difficulty) - } - if err := writeAncientBlock(op, block, header, stReceipts, tdSum); err != nil { + if err := writeAncientBlock(op, block, header, stReceipts, big.NewInt(int64(block.NumberU64())+1)); err != nil { return err } } @@ -859,7 +866,7 @@ return nil } for _, bad := range badBlocks { if bad.Header.Hash() == hash { - return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles) + return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Randomness, bad.Body.EpochSnarkData) } } return nil @@ -878,7 +885,7 @@ return nil } var blocks []*types.Block for _, bad := range badBlocks { - blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles)) + blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Randomness, bad.Body.EpochSnarkData)) } return blocks }
diff --git go-ethereum/core/types/receipt_test.go celo/core/types/receipt_test.go index 0c785c11a03a0a46cb6ff08556504453b9d74e6c..7c2968ab462774be41e0c98a252c46c7ac1e0794 100644 --- go-ethereum/core/types/receipt_test.go +++ celo/core/types/receipt_test.go @@ -47,17 +47,9 @@ { "StoredReceiptRLP", encodeAsStoredReceiptRLP, }, - { - "V4StoredReceiptRLP", - encodeAsV4StoredReceiptRLP, - }, - { - "V3StoredReceiptRLP", - encodeAsV3StoredReceiptRLP, - }, }   - tx := NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil) + tx := NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil, nil, nil, nil) receipt := &Receipt{ Status: ReceiptStatusFailed, CumulativeGasUsed: 1, @@ -129,37 +121,6 @@ } return rlp.EncodeToBytes(stored) }   -func encodeAsV4StoredReceiptRLP(want *Receipt) ([]byte, error) { - stored := &v4StoredReceiptRLP{ - PostStateOrStatus: want.statusEncoding(), - CumulativeGasUsed: want.CumulativeGasUsed, - TxHash: want.TxHash, - ContractAddress: want.ContractAddress, - Logs: make([]*LogForStorage, len(want.Logs)), - GasUsed: want.GasUsed, - } - for i, log := range want.Logs { - stored.Logs[i] = (*LogForStorage)(log) - } - return rlp.EncodeToBytes(stored) -} - -func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) { - stored := &v3StoredReceiptRLP{ - PostStateOrStatus: want.statusEncoding(), - CumulativeGasUsed: want.CumulativeGasUsed, - Bloom: want.Bloom, - TxHash: want.TxHash, - ContractAddress: want.ContractAddress, - Logs: make([]*LogForStorage, len(want.Logs)), - GasUsed: want.GasUsed, - } - for i, log := range want.Logs { - stored.Logs[i] = (*LogForStorage)(log) - } - return rlp.EncodeToBytes(stored) -} - // Tests that receipt data can be correctly derived from the contextual infos func TestDeriveFields(t *testing.T) { // Create a few transactions to have receipts for @@ -229,11 +190,11 @@ number := big.NewInt(1) hash := common.BytesToHash([]byte{0x03, 0x14})   clearComputedFieldsOnReceipts(t, receipts) - if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), txs); err != nil { + if err := receipts.DeriveFields(params.IstanbulTestChainConfig, hash, number.Uint64(), txs); err != nil { t.Fatalf("DeriveFields(...) = %v, want <nil>", err) } // Iterate over all the computed fields and check that they're correct - signer := MakeSigner(params.TestChainConfig, number) + signer := MakeSigner(params.IstanbulTestChainConfig, number)   logIndex := uint(0) for i := range receipts {
diff --git go-ethereum/core/types/istanbul_test.go celo/core/types/istanbul_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9440b555de1f70f1768bb3fc33f29282e88b8cf8 --- /dev/null +++ celo/core/types/istanbul_test.go @@ -0,0 +1,93 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package types + +import ( + "bytes" + "math/big" + "reflect" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +func TestHeaderHash(t *testing.T) { + // 0xaf194652cebbd181065203aded680c42d72eed7ae7d59d8e347b0bbb64ac5772 + expectedExtra := common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000000f89af8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b440b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0") + expectedHash := common.HexToHash("0x5c012c65d46edfbfca86a426da5111c51114b75577fec9b82161d3e05d83b723") + + // for istanbul consensus + header := &Header{Extra: expectedExtra} + if !reflect.DeepEqual(header.Hash(), expectedHash) { + t.Errorf("expected: %v, but got: %v", expectedHash.Hex(), header.Hash().Hex()) + } + + // append useless information to extra-data + unexpectedExtra := append(expectedExtra, []byte{1, 2, 3}...) + header.Extra = unexpectedExtra + if !reflect.DeepEqual(header.Hash(), rlpHash(header)) { + t.Errorf("expected: %v, but got: %v", rlpHash(header).Hex(), header.Hash().Hex()) + } +} + +func TestExtractToIstanbul(t *testing.T) { + testCases := []struct { + vanity []byte + istRawData []byte + expectedResult *IstanbulExtra + expectedErr error + }{ + { + // normal case + bytes.Repeat([]byte{0x00}, IstanbulExtraVanity), + hexutil.MustDecode("0xf6ea9444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212c00c80c3808080c3808080"), + &IstanbulExtra{ + AddedValidators: []common.Address{ + common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a")), + common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212")), + }, + AddedValidatorsPublicKeys: []blscrypto.SerializedPublicKey{}, + RemovedValidators: big.NewInt(12), //1100 + Seal: []byte{}, + AggregatedSeal: IstanbulAggregatedSeal{big.NewInt(0), []byte{}, big.NewInt(0)}, + ParentAggregatedSeal: IstanbulAggregatedSeal{big.NewInt(0), []byte{}, big.NewInt(0)}, + }, + nil, + }, + { + // insufficient vanity + bytes.Repeat([]byte{0x00}, IstanbulExtraVanity-1), + nil, + nil, + ErrInvalidIstanbulHeaderExtra, + }, + } + for _, test := range testCases { + h := &Header{Extra: append(test.vanity, test.istRawData...)} + + istanbulExtra, err := h.IstanbulExtra() + if err != test.expectedErr { + t.Errorf("expected: %v, but got: %v", test.expectedErr, err) + } + if !reflect.DeepEqual(istanbulExtra, test.expectedResult) { + t.Errorf("expected: %v, but got: %v", test.expectedResult, istanbulExtra) + } + } +}
diff --git go-ethereum/core/types/block.go celo/core/types/block.go index 439df9fcf00056cac9ce4ecc4e174852ed1745f6..9cbc0decfc2ca31357418ab308697d5fc0215755 100644 --- go-ethereum/core/types/block.go +++ celo/core/types/block.go @@ -18,14 +18,16 @@ // Package types contains data types related to Ethereum consensus. package types   import ( - "encoding/binary" + "encoding/json" "fmt" "io" "math/big" "reflect" + "sync" "sync/atomic" "time"   + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rlp" @@ -33,75 +35,49 @@ )   var ( EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - EmptyUncleHash = rlpHash([]*Header(nil)) + EmptyRandomness = Randomness{} + EmptyEpochSnarkData = EpochSnarkData{} )   -// A BlockNonce is a 64-bit hash which proves (combined with the -// mix-hash) that a sufficient amount of computation has been carried -// out on a block. -type BlockNonce [8]byte - -// EncodeNonce converts the given integer to a block nonce. -func EncodeNonce(i uint64) BlockNonce { - var n BlockNonce - binary.BigEndian.PutUint64(n[:], i) - return n -} - -// Uint64 returns the integer value of a block nonce. -func (n BlockNonce) Uint64() uint64 { - return binary.BigEndian.Uint64(n[:]) -} - -// MarshalText encodes n as a hex string with 0x prefix. -func (n BlockNonce) MarshalText() ([]byte, error) { - return hexutil.Bytes(n[:]).MarshalText() -} - -// UnmarshalText implements encoding.TextUnmarshaler. -func (n *BlockNonce) UnmarshalText(input []byte) error { - return hexutil.UnmarshalFixedText("BlockNonce", input, n[:]) -} - //go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go   // Header represents a block header in the Ethereum blockchain. type Header struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` - UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` Coinbase common.Address `json:"miner" gencodec:"required"` Root common.Hash `json:"stateRoot" gencodec:"required"` TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *big.Int `json:"difficulty" gencodec:"required"` Number *big.Int `json:"number" gencodec:"required"` - GasLimit uint64 `json:"gasLimit" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` Time uint64 `json:"timestamp" gencodec:"required"` Extra []byte `json:"extraData" gencodec:"required"` - MixDigest common.Hash `json:"mixHash"` - Nonce BlockNonce `json:"nonce"`   - // BaseFee was added by EIP-1559 and is ignored in legacy headers. - BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` + // Used to cache deserialized istanbul extra data + extraLock sync.Mutex + extraValue *IstanbulExtra + extraError error }   // field type overrides for gencodec type headerMarshaling struct { - Difficulty *hexutil.Big Number *hexutil.Big - GasLimit hexutil.Uint64 GasUsed hexutil.Uint64 Time hexutil.Uint64 Extra hexutil.Bytes - BaseFee *hexutil.Big Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON }   // Hash returns the block hash of the header, which is simply the keccak256 hash of its // RLP encoding. func (h *Header) Hash() common.Hash { + // Seal is reserved in extra-data. To prove block is signed by the proposer. + if len(h.Extra) >= IstanbulExtraVanity { + if istanbulHeader := IstanbulFilteredHeader(h, true); istanbulHeader != nil { + return rlpHash(istanbulHeader) + } + } return rlpHash(h) }   @@ -110,7 +86,7 @@ // Size returns the approximate memory used by all internal contents. It is used // to approximate and limit the memory consumption of various caches. func (h *Header) Size() common.StorageSize { - return headerSize + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen())/8) + return headerSize + common.StorageSize(len(h.Extra)+(h.Number.BitLen()/8)) }   // SanityCheck checks a few basic things -- these checks are way beyond what @@ -121,26 +97,20 @@ func (h *Header) SanityCheck() error { if h.Number != nil && !h.Number.IsUint64() { return fmt.Errorf("too large block number: bitlen %d", h.Number.BitLen()) } - if h.Difficulty != nil { - if diffLen := h.Difficulty.BitLen(); diffLen > 80 { - return fmt.Errorf("too large block difficulty: bitlen %d", diffLen) - } - } if eLen := len(h.Extra); eLen > 100*1024 { return fmt.Errorf("too large block extradata: size %d", eLen) } - if h.BaseFee != nil { - if bfLen := h.BaseFee.BitLen(); bfLen > 256 { - return fmt.Errorf("too large base fee: bitlen %d", bfLen) - } - } return nil }   // EmptyBody returns true if there is no additional 'body' to complete the header -// that is: no transactions and no uncles. +// that is: no transactions. func (h *Header) EmptyBody() bool { - return h.TxHash == EmptyRootHash && h.UncleHash == EmptyUncleHash + if _, err := h.IstanbulExtra(); err == nil { + return false + } + + return h.TxHash == EmptyRootHash }   // EmptyReceipts returns true if there are no receipts for this header/block. @@ -148,18 +118,104 @@ func (h *Header) EmptyReceipts() bool { return h.ReceiptHash == EmptyRootHash }   +type Randomness struct { + Revealed common.Hash + Committed common.Hash +} + +func (r *Randomness) Size() common.StorageSize { + return common.StorageSize(64) +} + +func (r *Randomness) DecodeRLP(s *rlp.Stream) error { + var random struct { + Revealed common.Hash + Committed common.Hash + } + if err := s.Decode(&random); err != nil { + return err + } + r.Revealed, r.Committed = random.Revealed, random.Committed + return nil +} + +func (r *Randomness) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{r.Revealed, r.Committed}) +} + +type EpochSnarkData struct { + Bitmap *big.Int + Signature []byte +} + +// Size returns the approximate memory used by all internal contents. It is used +// to approximate and limit the memory consumption of various caches. +func (r *EpochSnarkData) Size() common.StorageSize { + return common.StorageSize(blscrypto.SIGNATUREBYTES + (r.Bitmap.BitLen() / 8)) +} + +func (r *EpochSnarkData) DecodeRLP(s *rlp.Stream) error { + var epochSnarkData struct { + Bitmap *big.Int + Signature []byte + } + if err := s.Decode(&epochSnarkData); err != nil { + return err + } + r.Bitmap = epochSnarkData.Bitmap + r.Signature = epochSnarkData.Signature + return nil +} + +func (r *EpochSnarkData) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{r.Bitmap, r.Signature}) +} + +func (r *EpochSnarkData) IsEmpty() bool { + return len(r.Signature) == 0 +} + +// MarshalJSON marshals as JSON. +func (r EpochSnarkData) MarshalJSON() ([]byte, error) { + type EpochSnarkData struct { + Bitmap hexutil.Bytes + Signature hexutil.Bytes + } + var enc EpochSnarkData + enc.Bitmap = r.Bitmap.Bytes() + enc.Signature = r.Signature + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (r *EpochSnarkData) UnmarshalJSON(input []byte) error { + type EpochSnarkData struct { + Bitmap hexutil.Bytes + Signature hexutil.Bytes + } + var dec EpochSnarkData + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + r.Bitmap = new(big.Int).SetBytes(dec.Bitmap) + r.Signature = dec.Signature + return nil +} + // Body is a simple (mutable, non-safe) data container for storing and moving // a block's data contents (transactions and uncles) together. type Body struct { Transactions []*Transaction - Uncles []*Header + Randomness *Randomness + EpochSnarkData *EpochSnarkData }   // Block represents an entire block in the Ethereum blockchain. type Block struct { header *Header - uncles []*Header transactions Transactions + randomness *Randomness + epochSnarkData *EpochSnarkData   // caches hash atomic.Value @@ -179,18 +235,18 @@ // "external" block encoding. used for eth protocol, etc. type extblock struct { Header *Header Txs []*Transaction - Uncles []*Header + Randomness *Randomness + EpochSnarkData *EpochSnarkData }   // NewBlock creates a new block. The input data is copied, // changes to header and to the field values will not affect the // block. // -// The values of TxHash, UncleHash, ReceiptHash and Bloom in header -// are ignored and set to values derived from the given txs, uncles -// and receipts. -func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher) *Block { - b := &Block{header: CopyHeader(header), td: new(big.Int)} +// The values of TxHash, ReceiptHash and Bloom in header +// are ignored and set to values derived from the given txs and receipts. +func NewBlock(header *Header, txs []*Transaction, receipts []*Receipt, randomness *Randomness, hasher TrieHasher) *Block { + b := &Block{header: CopyHeader(header), td: new(big.Int), randomness: randomness, epochSnarkData: &EmptyEpochSnarkData}   // TODO: panic if len(txs) != len(receipts) if len(txs) == 0 { @@ -208,14 +264,8 @@ b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher) b.header.Bloom = CreateBloom(receipts) }   - if len(uncles) == 0 { - b.header.UncleHash = EmptyUncleHash - } else { - b.header.UncleHash = CalcUncleHash(uncles) - b.uncles = make([]*Header, len(uncles)) - for i := range uncles { - b.uncles[i] = CopyHeader(uncles[i]) - } + if randomness == nil { + b.randomness = &EmptyRandomness }   return b @@ -225,22 +275,27 @@ // NewBlockWithHeader creates a block with the given header data. The // header data is copied, changes to header and to the field values // will not affect the block. func NewBlockWithHeader(header *Header) *Block { - return &Block{header: CopyHeader(header)} + return &Block{header: CopyHeader(header), randomness: &EmptyRandomness, epochSnarkData: &EmptyEpochSnarkData} }   // CopyHeader creates a deep copy of a block header to prevent side effects from // modifying a header variable. func CopyHeader(h *Header) *Header { - cpy := *h - if cpy.Difficulty = new(big.Int); h.Difficulty != nil { - cpy.Difficulty.Set(h.Difficulty) + cpy := Header{ + ParentHash: h.ParentHash, + Coinbase: h.Coinbase, + Root: h.Root, + TxHash: h.TxHash, + ReceiptHash: h.ReceiptHash, + Bloom: h.Bloom, + Number: new(big.Int), + GasUsed: h.GasUsed, + Time: h.Time, } - if cpy.Number = new(big.Int); h.Number != nil { + + if h.Number != nil { cpy.Number.Set(h.Number) } - if h.BaseFee != nil { - cpy.BaseFee = new(big.Int).Set(h.BaseFee) - } if len(h.Extra) > 0 { cpy.Extra = make([]byte, len(h.Extra)) copy(cpy.Extra, h.Extra) @@ -255,7 +310,7 @@ _, size, _ := s.Kind() if err := s.Decode(&eb); err != nil { return err } - b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs + b.header, b.transactions, b.randomness, b.epochSnarkData = eb.Header, eb.Txs, eb.Randomness, eb.EpochSnarkData b.size.Store(common.StorageSize(rlp.ListSize(size))) return nil } @@ -265,14 +320,16 @@ func (b *Block) EncodeRLP(w io.Writer) error { return rlp.Encode(w, extblock{ Header: b.header, Txs: b.transactions, - Uncles: b.uncles, + Randomness: b.randomness, + EpochSnarkData: b.epochSnarkData, }) }   // TODO: copies   -func (b *Block) Uncles() []*Header { return b.uncles } func (b *Block) Transactions() Transactions { return b.transactions } +func (b *Block) Randomness() *Randomness { return b.randomness } +func (b *Block) EpochSnarkData() *EpochSnarkData { return b.epochSnarkData }   func (b *Block) Transaction(hash common.Hash) *Transaction { for _, transaction := range b.transactions { @@ -284,34 +341,23 @@ return nil }   func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) } -func (b *Block) GasLimit() uint64 { return b.header.GasLimit } func (b *Block) GasUsed() uint64 { return b.header.GasUsed } -func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) } func (b *Block) Time() uint64 { return b.header.Time } - +func (b *Block) TotalDifficulty() *big.Int { return new(big.Int).Add(b.header.Number, big.NewInt(1)) } func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() } -func (b *Block) MixDigest() common.Hash { return b.header.MixDigest } -func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) } func (b *Block) Bloom() Bloom { return b.header.Bloom } func (b *Block) Coinbase() common.Address { return b.header.Coinbase } func (b *Block) Root() common.Hash { return b.header.Root } func (b *Block) ParentHash() common.Hash { return b.header.ParentHash } func (b *Block) TxHash() common.Hash { return b.header.TxHash } func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } -func (b *Block) UncleHash() common.Hash { return b.header.UncleHash } func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) }   -func (b *Block) BaseFee() *big.Int { - if b.header.BaseFee == nil { - return nil - } - return new(big.Int).Set(b.header.BaseFee) -} - func (b *Block) Header() *Header { return CopyHeader(b.header) } +func (b *Block) MutableHeader() *Header { return b.header }   // Body returns the non-header content of the block. -func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} } +func (b *Block) Body() *Body { return &Body{b.transactions, b.randomness, b.epochSnarkData} }   // Size returns the true RLP encoded storage size of the block, either by encoding // and returning it, or returning a previsouly cached value. @@ -338,35 +384,55 @@ *c += writeCounter(len(b)) return len(b), nil }   -func CalcUncleHash(uncles []*Header) common.Hash { - if len(uncles) == 0 { - return EmptyUncleHash +// WithHeader returns a new block with the data from b but the header replaced with +// the sealed one. +func (b *Block) WithHeader(header *Header) *Block { + cpy := CopyHeader(header) + + return &Block{ + header: cpy, + transactions: b.transactions, + randomness: b.randomness, + epochSnarkData: b.epochSnarkData, } - return rlpHash(uncles) }   -// WithSeal returns a new block with the data from b but the header replaced with -// the sealed one. -func (b *Block) WithSeal(header *Header) *Block { - cpy := *header +// WithRandomness returns a new block with the given randomness. +func (b *Block) WithRandomness(randomness *Randomness) *Block { + block := &Block{ + header: b.header, + transactions: b.transactions, + randomness: randomness, + epochSnarkData: b.epochSnarkData, + } + return block +}   - return &Block{ - header: &cpy, +// WithEpochSnarkData returns a new block with the given epoch SNARK data. +func (b *Block) WithEpochSnarkData(epochSnarkData *EpochSnarkData) *Block { + block := &Block{ + header: b.header, transactions: b.transactions, - uncles: b.uncles, + randomness: b.randomness, + epochSnarkData: epochSnarkData, } + return block }   -// WithBody returns a new block with the given transaction and uncle contents. -func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { +// WithBody returns a new block with the given transaction contents. +func (b *Block) WithBody(transactions []*Transaction, randomness *Randomness, epochSnarkData *EpochSnarkData) *Block { block := &Block{ header: CopyHeader(b.header), transactions: make([]*Transaction, len(transactions)), - uncles: make([]*Header, len(uncles)), + randomness: randomness, + epochSnarkData: epochSnarkData, } copy(block.transactions, transactions) - for i := range uncles { - block.uncles[i] = CopyHeader(uncles[i]) + if randomness == nil { + block.randomness = &EmptyRandomness + } + if epochSnarkData == nil { + block.epochSnarkData = &EmptyEpochSnarkData } return block }
diff --git go-ethereum/core/types/transaction_signing_test.go celo/core/types/transaction_signing_test.go index 689fc38a9b660dca828d7b490927e967af855a3b..4e40662ee6ddb708659c044c6478889e5efb697e 100644 --- go-ethereum/core/types/transaction_signing_test.go +++ celo/core/types/transaction_signing_test.go @@ -30,7 +30,7 @@ key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey)   signer := NewEIP155Signer(big.NewInt(18)) - tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil), signer, key) + tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil, nil, nil, nil), signer, key) if err != nil { t.Fatal(err) } @@ -49,7 +49,7 @@ key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey)   signer := NewEIP155Signer(big.NewInt(18)) - tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil), signer, key) + tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil, nil, nil, nil), signer, key) if err != nil { t.Fatal(err) } @@ -61,7 +61,7 @@ if tx.ChainId().Cmp(signer.chainId) != 0 { t.Error("expected chainId to be", signer.chainId, "got", tx.ChainId()) }   - tx = NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil) + tx = NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil, nil, nil, nil) tx, err = SignTx(tx, HomesteadSigner{}, key) if err != nil { t.Fatal(err) @@ -81,16 +81,11 @@ // Test vectors come from http://vitalik.ca/files/eip155_testvec.txt for i, test := range []struct { txRlp, addr string }{ - {"f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce"}, - {"f864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112"}, - {"f864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2e485e0c23b4c3c542628a5f672eeab0ad4888be"}, - {"f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0x82a88539669a3fd524d669e858935de5e5410cf0"}, - {"f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf9358f2538fd5ccfeb848b64a96b743fcc930554"}, - {"f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xa8f7aba377317440bc5b26198a363ad22af1f3a4"}, - {"f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xf1f571dc362a0e5b2696b8e775f8491d3e50de35"}, - {"f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xd37922162ab7cea97c97a87551ed02c9a38b7332"}, - {"f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x9bddad43f934d313c2b79ca28a432dd2b7281029"}, - {"f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f"}, + {"f867808398968082a410808080943535353535353535353535353535353535353535018255441ba05e13b77d1ca6cab8f479a0f979e0bc2556ce17b306c832fb2b9313e57021c75ba02302d57566abf5a33ae7691b9751f5e70585260bea55b8cd5184192342372d7a", "0xb37b36527046ca6791276c205429eb1c4feb562e"}, + {"f868018401312d00825208808080943535353535353535353535353535353535353535018255441ba02a15ff927052f41cb41d8f8622564ba726abbd8a8e15994e87d6e33dc0e2aa03a02f77057ad12f763f5cc1200b105056a45e5c90413fc3641de56b02d36d533dc5", "0x2b7f6efbfa37b5233104da0f9f2ab7fc9e0a1ad1"}, + {"f87e0a8402625a00826d6080943535373535353535353535353535353535353535824e209435353735353535353535353535353535353535350a8255441ba05547015b5fedaa3a9b37b2c456369e0a031578df6f84863babe6a45af9357ecaa02144e0ffa81eff11ed857503423c77f8b44f85bfa532b8160efb4ec40b4cfe01", "0xb8691d7283eb5b3aa7b9b80633392d30992f1b47"}, + {"f87e0a8402625a00826d6080943535373535353535353535353535353535353535824e209435353735353535353535353535353535353535350a8255741ba03b08a1ba373e618447ea87e8f16b60600fdfc3e3a145f147cded36ed67fb08eda02414c92fd86494343b2e24c6e5b14c10f216fc68d1f37ddfc8ea7e0fed7bb2ab", "0x3155b94fa4129ea44a01990998a9815bb81bc535"}, + {"f87e0a8402625a00826d60809435353735353535353535353535353535353535a5824e209435353735353535353535353535353535353535350a8255741ca00d1dd55bd93b85484812c37fee292e36166cef2a979f19a794b48ffb254b8e6ba07ac6fbb7918b7a0112238eeb52858974ad5df3ca30cada2f40ddcabab69e66ed", "0x058e690781d4300a0bc29911f2a605474e4f96b7"}, } { signer := NewEIP155Signer(big.NewInt(1))   @@ -118,7 +113,7 @@ func TestChainId(t *testing.T) { key, _ := defaultTestKey()   - tx := NewTransaction(0, common.Address{}, new(big.Int), 0, new(big.Int), nil) + tx := NewTransaction(0, common.Address{}, new(big.Int), 0, new(big.Int), nil, nil, nil, nil)   var err error tx, err = SignTx(tx, NewEIP155Signer(big.NewInt(1)), key)
diff --git go-ethereum/core/types/receipt.go celo/core/types/receipt.go index d8245e05095d93f6195fb12ab6b66519b954f144..328fcbe8466c56f132f5fc7b55878456fb4b212a 100644 --- go-ethereum/core/types/receipt.go +++ celo/core/types/receipt.go @@ -359,9 +359,14 @@ signer := MakeSigner(config, new(big.Int).SetUint64(number))   logIndex := uint(0) if len(txs) != len(r) { - return errors.New("transaction and receipt count mismatch") + // The receipts may include an additional "block finalization" receipt (only IBFT) + if len(txs)+1 != len(r) || !isBlockReceipt(r[len(r)-1]) { + return errors.New("transaction and receipt count mismatch") + } } - for i := 0; i < len(r); i++ { + + // len(r) is not always strictly equal to len(txs) because of the block finalization receipt (IBFT) + for i := 0; i < len(txs); i++ { // The transaction type and hash can be retrieved from the transaction itself r[i].Type = txs[i].Type() r[i].TxHash = txs[i].Hash() @@ -393,5 +398,23 @@ r[i].Logs[j].Index = logIndex logIndex++ } } + + // Handle block finalization receipt (only IBFT) which is always the last receipt + if len(txs)+1 == len(r) { + j := len(txs) + for k := 0; k < len(r[j].Logs); k++ { + r[j].Logs[k].BlockNumber = number + r[j].Logs[k].BlockHash = hash + r[j].Logs[k].TxHash = hash + r[j].Logs[k].TxIndex = uint(j) + r[j].Logs[k].Index = logIndex + logIndex++ + } + } + return nil } + +func isBlockReceipt(receipt *Receipt) bool { + return len(receipt.Logs) > 0 && receipt.Logs[0].TxHash == receipt.Logs[0].BlockHash +}
diff --git go-ethereum/core/types/transaction_marshalling.go celo/core/types/transaction_marshalling.go index 5c3b203122ac450a468717fc5ae5ee56c01ec6bf..64f364df7bf3d488bf04d6aa0621cda58a42e6f2 100644 --- go-ethereum/core/types/transaction_marshalling.go +++ celo/core/types/transaction_marshalling.go @@ -42,12 +42,20 @@ R *hexutil.Big `json:"r"` S *hexutil.Big `json:"s"` To *common.Address `json:"to"`   + // Celo specific fields + FeeCurrency *common.Address `json:"feeCurrency"` // nil means native currency + GatewayFeeRecipient *common.Address `json:"gatewayFeeRecipient"` // nil means no gateway fee is paid + GatewayFee *hexutil.Big `json:"gatewayFee"` + // Access list transaction fields: ChainID *hexutil.Big `json:"chainId,omitempty"` AccessList *AccessList `json:"accessList,omitempty"`   // Only used for encoding: Hash common.Hash `json:"hash"` + + // Whether this is an ethereum-compatible transaction (i.e. with FeeCurrency, GatewayFeeRecipient and GatewayFee omitted) + EthCompatible bool `json:"ethCompatible"` }   // MarshalJSON marshals as JSON with a hash. @@ -67,8 +75,12 @@ enc.Value = (*hexutil.Big)(tx.Value) enc.Data = (*hexutil.Bytes)(&tx.Data) enc.To = t.To() enc.V = (*hexutil.Big)(tx.V) + enc.FeeCurrency = tx.FeeCurrency // todo: check if needs deep copy + enc.GatewayFeeRecipient = tx.GatewayFeeRecipient + enc.GatewayFee = (*hexutil.Big)(tx.GatewayFee) enc.R = (*hexutil.Big)(tx.R) enc.S = (*hexutil.Big)(tx.S) + enc.EthCompatible = tx.EthCompatible case *AccessListTx: enc.ChainID = (*hexutil.Big)(tx.ChainID) enc.AccessList = &tx.AccessList @@ -129,6 +141,13 @@ itx.Gas = uint64(*dec.Gas) if dec.Value == nil { return errors.New("missing required field 'value' in transaction") } + itx.FeeCurrency = dec.FeeCurrency + itx.GatewayFeeRecipient = dec.GatewayFeeRecipient + itx.GatewayFee = new(big.Int) + if dec.GatewayFee != nil { + itx.GatewayFee.Set((*big.Int)(dec.GatewayFee)) + } + itx.EthCompatible = dec.EthCompatible itx.Value = (*big.Int)(dec.Value) if dec.Data == nil { return errors.New("missing required field 'input' in transaction")
diff --git go-ethereum/core/types/celo_additions.go celo/core/types/celo_additions.go new file mode 100644 index 0000000000000000000000000000000000000000..eca7ee61ce10be2814dcfab9d2d6216fd3e81706 --- /dev/null +++ celo/core/types/celo_additions.go @@ -0,0 +1,18 @@ +package types + +// IstanbulExtra returns the 'Extra' field of the header deserialized into an +// IstanbulExtra struct, if there is an error deserializing the 'Extra' field +// it will be returned. +func (h *Header) IstanbulExtra() (*IstanbulExtra, error) { + h.extraLock.Lock() + defer h.extraLock.Unlock() + + if h.extraValue == nil && h.extraError == nil { + extra, err := extractIstanbulExtra(h) + + h.extraValue = extra + h.extraError = err + } + + return h.extraValue, h.extraError +}
diff --git go-ethereum/core/types/istanbul.go celo/core/types/istanbul.go new file mode 100644 index 0000000000000000000000000000000000000000..a6e955cee9b8a33d1a6c9c8b97f5ac330ffd5b9c --- /dev/null +++ celo/core/types/istanbul.go @@ -0,0 +1,167 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package types + +import ( + "errors" + "fmt" + "io" + "math/big" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + IstanbulExtraVanity = 32 // Fixed number of extra-data bytes reserved for validator vanity + IstanbulExtraBlsSignature = blscrypto.SIGNATUREBYTES // Fixed number of extra-data bytes reserved for validator seal on the current block + IstanbulExtraSeal = 65 // Fixed number of extra-data bytes reserved for validator seal + + // ErrInvalidIstanbulHeaderExtra is returned if the length of extra-data is less than 32 bytes + ErrInvalidIstanbulHeaderExtra = errors.New("invalid istanbul header extra-data") + EmptyBlockSeal = []byte{} +) + +type IstanbulAggregatedSeal struct { + // Bitmap is a bitmap having an active bit for each validator that signed this block + Bitmap *big.Int + // Signature is an aggregated BLS signature resulting from signatures by each validator that signed this block + Signature []byte + // Round is the round in which the signature was created. + Round *big.Int +} + +type IstanbulEpochValidatorSetSeal struct { + // Bitmap is a bitmap having an active bit for each validator that signed this epoch data + Bitmap *big.Int + + // Signature is an aggregated BLS signature resulting from signatures by each validator that signed this block + Signature []byte +} + +// EncodeRLP serializes ist into the Ethereum RLP format. +func (ist *IstanbulAggregatedSeal) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{ + ist.Bitmap, + ist.Signature, + ist.Round, + }) +} + +// DecodeRLP implements rlp.Decoder, and load the istanbul fields from a RLP stream. +func (ist *IstanbulAggregatedSeal) DecodeRLP(s *rlp.Stream) error { + var istanbulAggregatedSeal struct { + Bitmap *big.Int + Signature []byte + Round *big.Int + } + if err := s.Decode(&istanbulAggregatedSeal); err != nil { + return err + } + ist.Bitmap, ist.Signature, ist.Round = istanbulAggregatedSeal.Bitmap, istanbulAggregatedSeal.Signature, istanbulAggregatedSeal.Round + return nil +} + +func (ist *IstanbulAggregatedSeal) String() string { + return fmt.Sprintf("{round: %s, bitmap: %s, signature: %x}", ist.Round.String(), ist.Bitmap.Text(2), ist.Signature) +} + +type IstanbulExtra struct { + // AddedValidators are the validators that have been added in the block + AddedValidators []common.Address + // AddedValidatorsPublicKeys are the BLS public keys for the validators added in the block + AddedValidatorsPublicKeys []blscrypto.SerializedPublicKey + // RemovedValidators is a bitmap having an active bit for each removed validator in the block + RemovedValidators *big.Int + // Seal is an ECDSA signature by the proposer + Seal []byte + // AggregatedSeal contains the aggregated BLS signature created via IBFT consensus. + AggregatedSeal IstanbulAggregatedSeal + // ParentAggregatedSeal contains and aggregated BLS signature for the previous block. + ParentAggregatedSeal IstanbulAggregatedSeal +} + +// EncodeRLP serializes ist into the Ethereum RLP format. +func (ist *IstanbulExtra) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{ + ist.AddedValidators, + ist.AddedValidatorsPublicKeys, + ist.RemovedValidators, + ist.Seal, + &ist.AggregatedSeal, + &ist.ParentAggregatedSeal, + }) +} + +// DecodeRLP implements rlp.Decoder, and load the istanbul fields from a RLP stream. +func (ist *IstanbulExtra) DecodeRLP(s *rlp.Stream) error { + var istanbulExtra struct { + AddedValidators []common.Address + AddedValidatorsPublicKeys []blscrypto.SerializedPublicKey + RemovedValidators *big.Int + Seal []byte + AggregatedSeal IstanbulAggregatedSeal + ParentAggregatedSeal IstanbulAggregatedSeal + } + if err := s.Decode(&istanbulExtra); err != nil { + return err + } + ist.AddedValidators, ist.AddedValidatorsPublicKeys, ist.RemovedValidators, ist.Seal, ist.AggregatedSeal, ist.ParentAggregatedSeal = istanbulExtra.AddedValidators, istanbulExtra.AddedValidatorsPublicKeys, istanbulExtra.RemovedValidators, istanbulExtra.Seal, istanbulExtra.AggregatedSeal, istanbulExtra.ParentAggregatedSeal + return nil +} + +// ExtractIstanbulExtra extracts all values of the IstanbulExtra from the header. It returns an +// error if the length of the given extra-data is less than 32 bytes or the extra-data can not +// be decoded. +func extractIstanbulExtra(h *Header) (*IstanbulExtra, error) { + if len(h.Extra) < IstanbulExtraVanity { + return nil, ErrInvalidIstanbulHeaderExtra + } + + var istanbulExtra *IstanbulExtra + err := rlp.DecodeBytes(h.Extra[IstanbulExtraVanity:], &istanbulExtra) + if err != nil { + return nil, err + } + return istanbulExtra, nil +} + +// IstanbulFilteredHeader returns a filtered header which some information (like seal, aggregated signature) +// are clean to fulfill the Istanbul hash rules. It returns nil if the extra-data cannot be +// decoded/encoded by rlp. +func IstanbulFilteredHeader(h *Header, keepSeal bool) *Header { + newHeader := CopyHeader(h) + istanbulExtra, err := extractIstanbulExtra(newHeader) + if err != nil { + return nil + } + + if !keepSeal { + istanbulExtra.Seal = []byte{} + } + istanbulExtra.AggregatedSeal = IstanbulAggregatedSeal{} + + payload, err := rlp.EncodeToBytes(&istanbulExtra) + if err != nil { + return nil + } + + newHeader.Extra = append(newHeader.Extra[:IstanbulExtraVanity], payload...) + + return newHeader +}
diff --git go-ethereum/core/types/bloom9_test.go celo/core/types/bloom9_test.go index cd325b40c1d8bdef5a5f4ad67dc1f9763e59b779..ea2f3e3011764c0ef516daf6352a11ca46df76c8 100644 --- go-ethereum/core/types/bloom9_test.go +++ celo/core/types/bloom9_test.go @@ -94,8 +94,8 @@ func BenchmarkCreateBloom(b *testing.B) {   var txs = Transactions{ - NewContractCreation(1, big.NewInt(1), 1, big.NewInt(1), nil), - NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil), + NewContractCreation(1, big.NewInt(1), 1, big.NewInt(1), nil, nil, nil, nil), + NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil, nil, nil, nil), } var rSmall = Receipts{ &Receipt{
diff --git go-ethereum/core/types/gen_header_json.go celo/core/types/gen_header_json.go index 616d648a1ad9f28db97974903865acf5355b2b94..f204e856d4ec254f97b63369b9b8e6a815c77f91 100644 --- go-ethereum/core/types/gen_header_json.go +++ celo/core/types/gen_header_json.go @@ -17,40 +17,28 @@ // MarshalJSON marshals as JSON. func (h Header) MarshalJSON() ([]byte, error) { type Header struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` - UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` Coinbase common.Address `json:"miner" gencodec:"required"` Root common.Hash `json:"stateRoot" gencodec:"required"` TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` Extra hexutil.Bytes `json:"extraData" gencodec:"required"` - MixDigest common.Hash `json:"mixHash"` - Nonce BlockNonce `json:"nonce"` - BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` Hash common.Hash `json:"hash"` } var enc Header enc.ParentHash = h.ParentHash - enc.UncleHash = h.UncleHash enc.Coinbase = h.Coinbase enc.Root = h.Root enc.TxHash = h.TxHash enc.ReceiptHash = h.ReceiptHash enc.Bloom = h.Bloom - enc.Difficulty = (*hexutil.Big)(h.Difficulty) enc.Number = (*hexutil.Big)(h.Number) - enc.GasLimit = hexutil.Uint64(h.GasLimit) enc.GasUsed = hexutil.Uint64(h.GasUsed) enc.Time = hexutil.Uint64(h.Time) enc.Extra = h.Extra - enc.MixDigest = h.MixDigest - enc.Nonce = h.Nonce - enc.BaseFee = (*hexutil.Big)(h.BaseFee) enc.Hash = h.Hash() return json.Marshal(&enc) } @@ -59,21 +47,15 @@ // UnmarshalJSON unmarshals from JSON. func (h *Header) UnmarshalJSON(input []byte) error { type Header struct { ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` Coinbase *common.Address `json:"miner" gencodec:"required"` Root *common.Hash `json:"stateRoot" gencodec:"required"` TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom *Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` - MixDigest *common.Hash `json:"mixHash"` - Nonce *BlockNonce `json:"nonce"` - BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { @@ -83,10 +65,6 @@ if dec.ParentHash == nil { return errors.New("missing required field 'parentHash' for Header") } h.ParentHash = *dec.ParentHash - if dec.UncleHash == nil { - return errors.New("missing required field 'sha3Uncles' for Header") - } - h.UncleHash = *dec.UncleHash if dec.Coinbase == nil { return errors.New("missing required field 'miner' for Header") } @@ -107,18 +85,10 @@ if dec.Bloom == nil { return errors.New("missing required field 'logsBloom' for Header") } h.Bloom = *dec.Bloom - if dec.Difficulty == nil { - return errors.New("missing required field 'difficulty' for Header") - } - h.Difficulty = (*big.Int)(dec.Difficulty) if dec.Number == nil { return errors.New("missing required field 'number' for Header") } h.Number = (*big.Int)(dec.Number) - if dec.GasLimit == nil { - return errors.New("missing required field 'gasLimit' for Header") - } - h.GasLimit = uint64(*dec.GasLimit) if dec.GasUsed == nil { return errors.New("missing required field 'gasUsed' for Header") } @@ -131,14 +101,5 @@ if dec.Extra == nil { return errors.New("missing required field 'extraData' for Header") } h.Extra = *dec.Extra - if dec.MixDigest != nil { - h.MixDigest = *dec.MixDigest - } - if dec.Nonce != nil { - h.Nonce = *dec.Nonce - } - if dec.BaseFee != nil { - h.BaseFee = (*big.Int)(dec.BaseFee) - } return nil }
diff --git go-ethereum/core/types/dynamic_fee_tx.go celo/core/types/dynamic_fee_tx.go index 8475de1287e40d079ab2eb65db9a268c39129a5f..89ad5779b767a97ec6cd5f1aa0f024dd5e73f381 100644 --- go-ethereum/core/types/dynamic_fee_tx.go +++ celo/core/types/dynamic_fee_tx.go @@ -94,6 +94,10 @@ func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap } func (tx *DynamicFeeTx) value() *big.Int { return tx.Value } func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce } func (tx *DynamicFeeTx) to() *common.Address { return tx.To } +func (tx *DynamicFeeTx) feeCurrency() *common.Address { return nil } +func (tx *DynamicFeeTx) gatewayFeeRecipient() *common.Address { return nil } +func (tx *DynamicFeeTx) gatewayFee() *big.Int { return nil } +func (tx *DynamicFeeTx) ethCompatible() bool { return false }   func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S
diff --git go-ethereum/core/types/hashing_test.go celo/core/types/hashing_test.go index 510466b0256055a13aa8c274850e96cdd876c2cf..1e22139127d5baba7ecb853b6598675557ad22d6 100644 --- go-ethereum/core/types/hashing_test.go +++ celo/core/types/hashing_test.go @@ -150,7 +150,7 @@ } var addr = crypto.PubkeyToAddress(key.PublicKey) newTx := func(i uint64) (*types.Transaction, error) { signer := types.NewEIP155Signer(big.NewInt(18)) - utx := types.NewTransaction(i, addr, new(big.Int), 0, new(big.Int).SetUint64(10000000), nil) + utx := types.NewTransaction(i, addr, new(big.Int), 0, new(big.Int).SetUint64(10000000), nil, nil, nil, nil) tx, err := types.SignTx(utx, signer, key) return tx, err }
diff --git go-ethereum/core/types/block_test.go celo/core/types/block_test.go index ab18f53d4ca0228f15da038fd683718128e191c0..a13d420fc8342a38af678007632a4aaaada7592e 100644 --- go-ethereum/core/types/block_test.go +++ celo/core/types/block_test.go @@ -23,6 +23,7 @@ "math/big" "reflect" "testing"   + _ "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" @@ -31,9 +32,11 @@ "github.com/ethereum/go-ethereum/rlp" "golang.org/x/crypto/sha3" )   -// from bcValidBlockTest.json, "SimpleTx" func TestBlockEncoding(t *testing.T) { - blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0") + blockEnc := common.FromHex("f90244f901a6a07285abd5b24742f184ad676e31f6054663b3529bc35ea2fcad8a3e0f642a46f7948888f1f195afa192cfee860698584c030f4c9db1a0ecc60e00b3fe5ce9f6e1a10e5469764daf51f1fe93c22ec3f9a7583a80357217a0d35d334d87c0cc0a202e3756bf81fae08b1575f286c7ee7a3f8df4f0f3afc55da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001825208845c47775c80e3e2800a82c35080808094095e7baea6a6c7c4c2dfeb977efac326af552d870a80808080f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f280b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + var raw rlp.RawValue + rlp.DecodeBytes(blockEnc, &raw) + var block Block if err := rlp.DecodeBytes(blockEnc, &block); err != nil { t.Fatal("decode error: ", err) @@ -44,162 +47,24 @@ if !reflect.DeepEqual(got, want) { t.Errorf("%s mismatch: got %v, want %v", f, got, want) } } - check("Difficulty", block.Difficulty(), big.NewInt(131072)) - check("GasLimit", block.GasLimit(), uint64(3141592)) check("GasUsed", block.GasUsed(), uint64(21000)) check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1")) - check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498")) - check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017")) - check("Hash", block.Hash(), common.HexToHash("0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e")) - check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4)) - check("Time", block.Time(), uint64(1426516743)) + check("Root", block.Root(), common.HexToHash("ecc60e00b3fe5ce9f6e1a10e5469764daf51f1fe93c22ec3f9a7583a80357217")) + check("Hash", block.Hash(), common.HexToHash("2e14ef428293e41c5f81a108b5d36f892b2bee3e34aec4223474c4a31618ea69")) + check("Time", block.Time(), uint64(1548187484)) check("Size", block.Size(), common.StorageSize(len(blockEnc))) + check("ParentHash", block.ParentHash(), common.HexToHash("7285abd5b24742f184ad676e31f6054663b3529bc35ea2fcad8a3e0f642a46f7"))   - tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil) - tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")) check("len(Transactions)", len(block.Transactions()), 1) - check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash()) - ourBlockEnc, err := rlp.EncodeToBytes(&block) - if err != nil { - t.Fatal("encode error: ", err) - } - if !bytes.Equal(ourBlockEnc, blockEnc) { - t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc) - } -} + check("Transactions[0].Hash", block.Transactions()[0].Hash(), common.HexToHash("ef3dcc9051f9e7d1ecb59426f3e5a24b0ad455129eb4525849ca1b0b3d955889"))   -func TestEIP1559BlockEncoding(t *testing.T) { - blockEnc := common.FromHex("f9030bf901fea083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4843b9aca00f90106f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b8a302f8a0018080843b9aca008301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8c0") - var block Block - if err := rlp.DecodeBytes(blockEnc, &block); err != nil { - t.Fatal("decode error: ", err) - } - - check := func(f string, got, want interface{}) { - if !reflect.DeepEqual(got, want) { - t.Errorf("%s mismatch: got %v, want %v", f, got, want) - } - } - - check("Difficulty", block.Difficulty(), big.NewInt(131072)) - check("GasLimit", block.GasLimit(), uint64(3141592)) - check("GasUsed", block.GasUsed(), uint64(21000)) - check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1")) - check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498")) - check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017")) - check("Hash", block.Hash(), common.HexToHash("c7252048cd273fe0dac09650027d07f0e3da4ee0675ebbb26627cea92729c372")) - check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4)) - check("Time", block.Time(), uint64(1426516743)) - check("Size", block.Size(), common.StorageSize(len(blockEnc))) - check("BaseFee", block.BaseFee(), new(big.Int).SetUint64(params.InitialBaseFee)) - - tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil) - tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")) - - addr := common.HexToAddress("0x0000000000000000000000000000000000000001") - accesses := AccessList{AccessTuple{ - Address: addr, - StorageKeys: []common.Hash{ - {0}, - }, - }} - to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87") - txdata := &DynamicFeeTx{ - ChainID: big.NewInt(1), - Nonce: 0, - To: &to, - Gas: 123457, - GasFeeCap: new(big.Int).Set(block.BaseFee()), - GasTipCap: big.NewInt(0), - AccessList: accesses, - Data: []byte{}, - } - tx2 := NewTx(txdata) - tx2, err := tx2.WithSignature(LatestSignerForChainID(big.NewInt(1)), common.Hex2Bytes("fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a800")) - if err != nil { - t.Fatal("invalid signature error: ", err) - } - - check("len(Transactions)", len(block.Transactions()), 2) - check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash()) - check("Transactions[1].Hash", block.Transactions()[1].Hash(), tx2.Hash()) - check("Transactions[1].Type", block.Transactions()[1].Type(), tx2.Type()) ourBlockEnc, err := rlp.EncodeToBytes(&block) - if err != nil { - t.Fatal("encode error: ", err) - } - if !bytes.Equal(ourBlockEnc, blockEnc) { - t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc) - } -}   -func TestEIP2718BlockEncoding(t *testing.T) { - blockEnc := common.FromHex("f90319f90211a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a0e6e49996c7ec59f7a23d22b83239a60151512c65613bf84a0d7da336399ebc4aa0cafe75574d59780665a97fbfd11365c7545aa8f1abf4e5e12e8243334ef7286bb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000820200832fefd882a410845506eb0796636f6f6c65737420626c6f636b206f6e20636861696ea0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f90101f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b89e01f89b01800a8301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000001a03dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335a0476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef14c0") - var block Block - if err := rlp.DecodeBytes(blockEnc, &block); err != nil { - t.Fatal("decode error: ", err) - } - - check := func(f string, got, want interface{}) { - if !reflect.DeepEqual(got, want) { - t.Errorf("%s mismatch: got %v, want %v", f, got, want) - } - } - check("Difficulty", block.Difficulty(), big.NewInt(131072)) - check("GasLimit", block.GasLimit(), uint64(3141592)) - check("GasUsed", block.GasUsed(), uint64(42000)) - check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1")) - check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498")) - check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017")) - check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4)) - check("Time", block.Time(), uint64(1426516743)) - check("Size", block.Size(), common.StorageSize(len(blockEnc))) - - // Create legacy tx. - to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87") - tx1 := NewTx(&LegacyTx{ - Nonce: 0, - To: &to, - Value: big.NewInt(10), - Gas: 50000, - GasPrice: big.NewInt(10), - }) - sig := common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100") - tx1, _ = tx1.WithSignature(HomesteadSigner{}, sig) - - // Create ACL tx. - addr := common.HexToAddress("0x0000000000000000000000000000000000000001") - tx2 := NewTx(&AccessListTx{ - ChainID: big.NewInt(1), - Nonce: 0, - To: &to, - Gas: 123457, - GasPrice: big.NewInt(10), - AccessList: AccessList{{Address: addr, StorageKeys: []common.Hash{{0}}}}, - }) - sig2 := common.Hex2Bytes("3dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef1401") - tx2, _ = tx2.WithSignature(NewEIP2930Signer(big.NewInt(1)), sig2) - - check("len(Transactions)", len(block.Transactions()), 2) - check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash()) - check("Transactions[1].Hash", block.Transactions()[1].Hash(), tx2.Hash()) - check("Transactions[1].Type()", block.Transactions()[1].Type(), uint8(AccessListTxType)) - - ourBlockEnc, err := rlp.EncodeToBytes(&block) if err != nil { t.Fatal("encode error: ", err) } if !bytes.Equal(ourBlockEnc, blockEnc) { t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc) - } -} - -func TestUncleHash(t *testing.T) { - uncles := make([]*Header, 0) - h := CalcUncleHash(uncles) - exp := common.HexToHash("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") - if h != exp { - t.Fatalf("empty uncle hash is wrong, got %x != %x", h, exp) } }   @@ -246,13 +111,11 @@ var ( key, _ = crypto.GenerateKey() txs = make([]*Transaction, 70) receipts = make([]*Receipt, len(txs)) - signer = LatestSigner(params.TestChainConfig) + signer = NewEIP155Signer(params.TestChainConfig.ChainID) uncles = make([]*Header, 3) ) header := &Header{ - Difficulty: math.BigPow(11, 11), Number: math.BigPow(2, 9), - GasLimit: 12345678, GasUsed: 1476322, Time: 9876543, Extra: []byte("coolest block on chain"), @@ -261,7 +124,7 @@ for i := range txs { amount := math.BigPow(2, int64(i)) price := big.NewInt(300000) data := make([]byte, 100) - tx := NewTransaction(uint64(i), common.Address{}, amount, 123457, price, data) + tx := NewTransaction(uint64(i), common.Address{}, amount, 123457, price, nil, nil, nil, data) signedTx, err := SignTx(tx, signer, key) if err != nil { panic(err) @@ -271,13 +134,11 @@ receipts[i] = NewReceipt(make([]byte, 32), false, tx.Gas()) } for i := range uncles { uncles[i] = &Header{ - Difficulty: math.BigPow(11, 11), Number: math.BigPow(2, 9), - GasLimit: 12345678, GasUsed: 1476322, Time: 9876543, Extra: []byte("benchmark uncle"), } } - return NewBlock(header, txs, uncles, receipts, newHasher()) + return NewBlock(header, txs, receipts, nil, newHasher()) }
diff --git go-ethereum/core/types/transaction_signing.go celo/core/types/transaction_signing.go index 791569ec9b6dc28ff0981472bc43cc4c0a6843d5..ff570b9fb9fe2a9a06cb3a67e4d5b94b3df43d3f 100644 --- go-ethereum/core/types/transaction_signing.go +++ celo/core/types/transaction_signing.go @@ -40,10 +40,8 @@ // MakeSigner returns a Signer based on the given chain config and block number. func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { var signer Signer switch { - case config.IsLondon(blockNumber): + case config.IsEspresso(blockNumber): signer = NewLondonSigner(config.ChainID) - case config.IsBerlin(blockNumber): - signer = NewEIP2930Signer(config.ChainID) case config.IsEIP155(blockNumber): signer = NewEIP155Signer(config.ChainID) case config.IsHomestead(blockNumber): @@ -63,11 +61,8 @@ // Use this in transaction-handling code where the current block number is unknown. If you // have the current block number available, use MakeSigner instead. func LatestSigner(config *params.ChainConfig) Signer { if config.ChainID != nil { - if config.LondonBlock != nil { + if config.EspressoBlock != nil { return NewLondonSigner(config.ChainID) - } - if config.BerlinBlock != nil { - return NewEIP2930Signer(config.ChainID) } if config.EIP155Block != nil { return NewEIP155Signer(config.ChainID) @@ -298,15 +293,30 @@ // It does not uniquely identify the transaction. func (s eip2930Signer) Hash(tx *Transaction) common.Hash { switch tx.Type() { case LegacyTxType: - return rlpHash([]interface{}{ - tx.Nonce(), - tx.GasPrice(), - tx.Gas(), - tx.To(), - tx.Value(), - tx.Data(), - s.chainId, uint(0), uint(0), - }) + if tx.EthCompatible() { + return rlpHash([]interface{}{ + tx.Nonce(), + tx.GasPrice(), + tx.Gas(), + tx.To(), + tx.Value(), + tx.Data(), + s.chainId, uint(0), uint(0), + }) + } else { + return rlpHash([]interface{}{ + tx.Nonce(), + tx.GasPrice(), + tx.Gas(), + tx.FeeCurrency(), + tx.GatewayFeeRecipient(), + tx.GatewayFee(), + tx.To(), + tx.Value(), + tx.Data(), + s.chainId, uint(0), uint(0), + }) + } case AccessListTxType: return prefixedRlpHash( tx.Type(), @@ -389,15 +399,30 @@ // Hash returns the hash to be signed by the sender. // It does not uniquely identify the transaction. func (s EIP155Signer) Hash(tx *Transaction) common.Hash { - return rlpHash([]interface{}{ - tx.Nonce(), - tx.GasPrice(), - tx.Gas(), - tx.To(), - tx.Value(), - tx.Data(), - s.chainId, uint(0), uint(0), - }) + if tx.EthCompatible() { + return rlpHash([]interface{}{ + tx.Nonce(), + tx.GasPrice(), + tx.Gas(), + tx.To(), + tx.Value(), + tx.Data(), + s.chainId, uint(0), uint(0), + }) + } else { + return rlpHash([]interface{}{ + tx.Nonce(), + tx.GasPrice(), + tx.Gas(), + tx.FeeCurrency(), + tx.GatewayFeeRecipient(), + tx.GatewayFee(), + tx.To(), + tx.Value(), + tx.Data(), + s.chainId, uint(0), uint(0), + }) + } }   // HomesteadTransaction implements TransactionInterface using the @@ -427,6 +452,15 @@ v, r, s := tx.RawSignatureValues() return recoverPlain(hs.Hash(tx), r, s, v, true) }   +func (hs HomesteadSigner) SenderData(data common.Hash, sig []byte) (common.Address, []byte, error) { + r, s, v, err := hs.SignatureValues(nil, sig) + v = new(big.Int).Sub(v, big.NewInt(27)) + if err != nil { + return common.Address{}, nil, err + } + return recoverPlainWithPublic(data, r, s, v, true) +} + type FrontierSigner struct{}   func (s FrontierSigner) ChainID() *big.Int { @@ -459,14 +493,28 @@ // Hash returns the hash to be signed by the sender. // It does not uniquely identify the transaction. func (fs FrontierSigner) Hash(tx *Transaction) common.Hash { - return rlpHash([]interface{}{ - tx.Nonce(), - tx.GasPrice(), - tx.Gas(), - tx.To(), - tx.Value(), - tx.Data(), - }) + if tx.EthCompatible() { + return rlpHash([]interface{}{ + tx.Nonce(), + tx.GasPrice(), + tx.Gas(), + tx.To(), + tx.Value(), + tx.Data(), + }) + } else { + return rlpHash([]interface{}{ + tx.Nonce(), + tx.GasPrice(), + tx.Gas(), + tx.FeeCurrency(), + tx.GatewayFeeRecipient(), + tx.GatewayFee(), + tx.To(), + tx.Value(), + tx.Data(), + }) + } }   func decodeSignature(sig []byte) (r, s, v *big.Int) { @@ -480,12 +528,17 @@ return r, s, v }   func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) { + addr, _, err := recoverPlainWithPublic(sighash, R, S, Vb, homestead) + return addr, err +} + +func recoverPlainWithPublic(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, []byte, error) { if Vb.BitLen() > 8 { - return common.Address{}, ErrInvalidSig + return common.Address{}, nil, ErrInvalidSig } V := byte(Vb.Uint64() - 27) if !crypto.ValidateSignatureValues(V, R, S, homestead) { - return common.Address{}, ErrInvalidSig + return common.Address{}, nil, ErrInvalidSig } // encode the signature in uncompressed format r, s := R.Bytes(), S.Bytes() @@ -496,14 +549,14 @@ sig[64] = V // recover the public key from the signature pub, err := crypto.Ecrecover(sighash[:], sig) if err != nil { - return common.Address{}, err + return common.Address{}, nil, err } if len(pub) == 0 || pub[0] != 4 { - return common.Address{}, errors.New("invalid public key") + return common.Address{}, nil, errors.New("invalid public key") } var addr common.Address copy(addr[:], crypto.Keccak256(pub[1:])[12:]) - return addr, nil + return addr, pub, nil }   // deriveChainId derives the chain id from the given v parameter
diff --git go-ethereum/core/types/transaction.go celo/core/types/transaction.go index e0ba40237f69e3ed8dcbd799a30a9d8c2c1d307b..ebecaad9ef2c44840462f66db15c7ce327ee512f 100644 --- go-ethereum/core/types/transaction.go +++ celo/core/types/transaction.go @@ -28,7 +28,12 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" +) + +const ( + ethCompatibleTxNumFields = 9 )   var ( @@ -38,6 +43,9 @@ ErrInvalidTxType = errors.New("transaction type not valid in this context") ErrTxTypeNotSupported = errors.New("transaction type not supported") ErrGasFeeCapTooLow = errors.New("fee cap less than base fee") errEmptyTypedTx = errors.New("empty typed transaction bytes") + // ErrEthCompatibleTransactionIsntCompatible is returned if the transaction has EthCompatible: true + // but has non-nil-or-0 values for some of the Celo-only fields + ErrEthCompatibleTransactionIsntCompatible = errors.New("ethCompatible is true, but non-eth-compatible fields are present") )   // Transaction types. @@ -82,6 +90,13 @@ gasFeeCap() *big.Int value() *big.Int nonce() uint64 to() *common.Address + + // Celo specific fields + feeCurrency() *common.Address + gatewayFeeRecipient() *common.Address + gatewayFee() *big.Int + // Whether this is an ethereum-compatible transaction (i.e. with FeeCurrency, GatewayFeeRecipient and GatewayFee omitted) + ethCompatible() bool   rawSignatureValues() (v, r, s *big.Int) setSignatureValues(chainID, v, r, s *big.Int) @@ -257,6 +272,36 @@ func (tx *Transaction) ChainId() *big.Int { return tx.inner.chainID() }   +// FeeCurrency returns the fee currency of the transaction. Nil implies paying in CELO. +func (tx *Transaction) FeeCurrency() *common.Address { return tx.inner.feeCurrency() } + +// GatewayFeeRecipient returns the address to the send the gateway fee to. Nil implies no recipient. +func (tx *Transaction) GatewayFeeRecipient() *common.Address { return tx.inner.gatewayFeeRecipient() } + +// GatewayFee returns the fee that should be paid to the gateway fee recipient. +// Will not return nil, but instead returns 0 if the underlying transction does not have a gatewayfee. +func (tx *Transaction) GatewayFee() *big.Int { + if tx.inner.gatewayFee() != nil { + return new(big.Int).Set(tx.inner.gatewayFee()) + } else { + return big.NewInt(0) + } +} + +// EthCompatible returns true iff the RLP form of the LegacyTx does not have the celo specific fields. +func (tx *Transaction) EthCompatible() bool { return tx.inner.ethCompatible() } + +// Fee calculates the fess paid by the transaction include the gateway fee. +func (tx *Transaction) Fee() *big.Int { + return Fee(tx.inner.gasPrice(), tx.inner.gas(), tx.GatewayFee()) +} + +// Fee calculates the transaction fee (gasLimit * gasPrice + gatewayFee) +func Fee(gasPrice *big.Int, gasLimit uint64, gatewayFee *big.Int) *big.Int { + gasFee := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasLimit))) + return gasFee.Add(gasFee, gatewayFee) +} + // Data returns the input data of the transaction. func (tx *Transaction) Data() []byte { return tx.inner.data() }   @@ -293,10 +338,13 @@ cpy := *ito return &cpy }   -// Cost returns gas * gasPrice + value. +// Cost returns value + gasprice * gaslimit + gatewayfee. func (tx *Transaction) Cost() *big.Int { total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())) total.Add(total, tx.Value()) + if gatewayFee := tx.GatewayFee(); gatewayFee != nil { + total.Add(total, gatewayFee) + } return total }   @@ -307,21 +355,35 @@ return tx.inner.rawSignatureValues() }   // GasFeeCapCmp compares the fee cap of two transactions. +// The two transactions must have the same fee currency for the result to be valid. func (tx *Transaction) GasFeeCapCmp(other *Transaction) int { + fca := tx.FeeCurrency() + fcb := other.FeeCurrency() + if (fca == nil && fcb != nil) || (fca != nil && fcb == nil) || (fca != nil && fcb != nil && *fca != *fcb) { + log.Error("Dev error: using GasFeeCapCmp with transactions that have differing fee currencies") + } return tx.inner.gasFeeCap().Cmp(other.inner.gasFeeCap()) }   // GasFeeCapIntCmp compares the fee cap of the transaction against the given fee cap. +// `other` must be in the same fee currency that the transaction is using. func (tx *Transaction) GasFeeCapIntCmp(other *big.Int) int { return tx.inner.gasFeeCap().Cmp(other) }   // GasTipCapCmp compares the gasTipCap of two transactions. +// The two transactions must have the same fee currency for the result to be valid. func (tx *Transaction) GasTipCapCmp(other *Transaction) int { + fca := tx.FeeCurrency() + fcb := other.FeeCurrency() + if (fca == nil && fcb != nil) || (fca != nil && fcb == nil) || (fca != nil && fcb != nil && *fca != *fcb) { + log.Error("Dev error: using GasTipCapCmp with transactions that have differing fee currencies") + } return tx.inner.gasTipCap().Cmp(other.inner.gasTipCap()) }   // GasTipCapIntCmp compares the gasTipCap of the transaction against the given gasTipCap. +// `other` must be in the same fee currency that the transaction is using. func (tx *Transaction) GasTipCapIntCmp(other *big.Int) int { return tx.inner.gasTipCap().Cmp(other) } @@ -329,6 +391,8 @@ // EffectiveGasTip returns the effective miner gasTipCap for the given base fee. // Note: if the effective gasTipCap is negative, this method returns both error // the actual negative value, _and_ ErrGasFeeCapTooLow +// Note: `baseFee` must be in the same FeeCurrency as the transactions and +// The returned value is in the FeeCurrency of the transaction func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) { if baseFee == nil { return tx.GasTipCap(), nil @@ -343,13 +407,21 @@ }   // EffectiveGasTipValue is identical to EffectiveGasTip, but does not return an // error in case the effective gasTipCap is negative +// Note: `baseFee` must be in the same FeeCurrency as the transactions and +// the returned value is in the FeeCurrency of the transaction func (tx *Transaction) EffectiveGasTipValue(baseFee *big.Int) *big.Int { effectiveTip, _ := tx.EffectiveGasTip(baseFee) return effectiveTip }   // EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee. +// `other` must has the same feecurrency as `tx` and `baseFee` must be in the same fee currency. func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int) int { + fca := tx.FeeCurrency() + fcb := other.FeeCurrency() + if (fca == nil && fcb != nil) || (fca != nil && fcb == nil) || (fca != nil && fcb != nil && *fca != *fcb) { + log.Error("Dev error: using EffectiveGasTipCmp with transactions that have differing fee currencies") + } if baseFee == nil { return tx.GasTipCapCmp(other) } @@ -357,6 +429,7 @@ return tx.EffectiveGasTipValue(baseFee).Cmp(other.EffectiveGasTipValue(baseFee)) }   // EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap. +// `other` and `baseFee` must be in the same fee currency. func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) int { if baseFee == nil { return tx.GasTipCapIntCmp(other) @@ -390,6 +463,14 @@ c := writeCounter(0) rlp.Encode(&c, &tx.inner) tx.size.Store(common.StorageSize(c)) return common.StorageSize(c) +} + +// CheckEthCompatibility checks that the Celo-only fields are nil-or-0 if EthCompatible is true +func (tx *Transaction) CheckEthCompatibility() error { + if tx.EthCompatible() && !(tx.FeeCurrency() == nil && tx.GatewayFeeRecipient() == nil && tx.GatewayFee().Sign() == 0) { + return ErrEthCompatibleTransactionIsntCompatible + } + return nil }   // WithSignature returns a new transaction with the given signature. @@ -452,20 +533,20 @@ // TxWithMinerFee wraps a transaction with its gas price or effective miner gasTipCap type TxWithMinerFee struct { tx *Transaction - minerFee *big.Int + minerFee *big.Int // in CELO }   // NewTxWithMinerFee creates a wrapped transaction, calculating the effective -// miner gasTipCap if a base fee is provided. +// miner gasTipCap if a base fee is provided. The MinerFee is converted to CELO. // Returns error in case of a negative effective miner gasTipCap. -func NewTxWithMinerFee(tx *Transaction, baseFee *big.Int) (*TxWithMinerFee, error) { - minerFee, err := tx.EffectiveGasTip(baseFee) +func NewTxWithMinerFee(tx *Transaction, baseFeeFn func(feeCurrency *common.Address) *big.Int, toCELO func(amount *big.Int, feeCurrency *common.Address) *big.Int) (*TxWithMinerFee, error) { + minerFee, err := tx.EffectiveGasTip(baseFeeFn(tx.FeeCurrency())) if err != nil { return nil, err } return &TxWithMinerFee{ tx: tx, - minerFee: minerFee, + minerFee: toCELO(minerFee, tx.FeeCurrency()), }, nil }   @@ -504,7 +585,8 @@ type TransactionsByPriceAndNonce struct { txs map[common.Address]Transactions // Per account nonce-sorted list of transactions heads TxByPriceAndTime // Next transaction for each unique account (price heap) signer Signer // Signer for the set of transactions - baseFee *big.Int // Current base fee + baseFeeFn func(feeCurrency *common.Address) *big.Int // Function to get the basefee for the specified feecurrency. + toCELO func(amount *big.Int, feeCurrency *common.Address) *big.Int // Current exchange rate to CELO }   // NewTransactionsByPriceAndNonce creates a transaction set that can retrieve @@ -512,12 +594,13 @@ // price sorted transactions in a nonce-honouring way. // // Note, the input map is reowned so the caller should not interact any more with // if after providing it to the constructor. -func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions, baseFee *big.Int) *TransactionsByPriceAndNonce { +// Note: txCmpFunc should handle the basefee +func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions, baseFeeFn func(feeCurrency *common.Address) *big.Int, toCELO func(amount *big.Int, feeCurrency *common.Address) *big.Int) *TransactionsByPriceAndNonce { // Initialize a price and received time based heap with the head transactions heads := make(TxByPriceAndTime, 0, len(txs)) for from, accTxs := range txs { acc, _ := Sender(signer, accTxs[0]) - wrapped, err := NewTxWithMinerFee(accTxs[0], baseFee) + wrapped, err := NewTxWithMinerFee(accTxs[0], baseFeeFn, toCELO) // Remove transaction if sender doesn't match from, or if wrapping fails. if acc != from || err != nil { delete(txs, from) @@ -533,7 +616,8 @@ return &TransactionsByPriceAndNonce{ txs: txs, heads: heads, signer: signer, - baseFee: baseFee, + baseFeeFn: baseFeeFn, + toCELO: toCELO, } }   @@ -549,7 +633,7 @@ // Shift replaces the current best head with the next one from the same account. func (t *TransactionsByPriceAndNonce) Shift() { acc, _ := Sender(t.signer, t.heads[0].tx) if txs, ok := t.txs[acc]; ok && len(txs) > 0 { - if wrapped, err := NewTxWithMinerFee(txs[0], t.baseFee); err == nil { + if wrapped, err := NewTxWithMinerFee(txs[0], t.baseFeeFn, t.toCELO); err == nil { t.heads[0], t.txs[acc] = wrapped, txs[1:] heap.Fix(&t.heads, 0) return @@ -577,13 +661,20 @@ gasLimit uint64 gasPrice *big.Int gasFeeCap *big.Int gasTipCap *big.Int + feeCurrency *common.Address + gatewayFeeRecipient *common.Address + gatewayFee *big.Int data []byte accessList AccessList + ethCompatible bool isFake bool }   -func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, isFake bool) Message { - return Message{ +func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, + gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, + feeCurrency, gatewayFeeRecipient *common.Address, gatewayFee *big.Int, + data []byte, accessList AccessList, ethCompatible, isFake bool) Message { + m := Message{ from: from, to: to, nonce: nonce, @@ -592,10 +683,18 @@ gasLimit: gasLimit, gasPrice: gasPrice, gasFeeCap: gasFeeCap, gasTipCap: gasTipCap, + feeCurrency: feeCurrency, + gatewayFeeRecipient: gatewayFeeRecipient, + gatewayFee: gatewayFee, data: data, accessList: accessList, + ethCompatible: ethCompatible, isFake: isFake, } + if m.gatewayFee == nil { + m.gatewayFee = new(big.Int) + } + return m }   // AsMessage returns the transaction as a core.Message. @@ -606,16 +705,23 @@ gasLimit: tx.Gas(), gasPrice: new(big.Int).Set(tx.GasPrice()), gasFeeCap: new(big.Int).Set(tx.GasFeeCap()), gasTipCap: new(big.Int).Set(tx.GasTipCap()), + feeCurrency: tx.FeeCurrency(), + gatewayFeeRecipient: tx.GatewayFeeRecipient(), + gatewayFee: new(big.Int), to: tx.To(), amount: tx.Value(), data: tx.Data(), accessList: tx.AccessList(), isFake: false, + ethCompatible: tx.EthCompatible(), } // If baseFee provided, set gasPrice to effectiveGasPrice. if baseFee != nil { msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap) } + if gatewayFee := tx.GatewayFee(); gatewayFee != nil { + msg.gatewayFee.Set(gatewayFee) + } var err error msg.from, err = Sender(s, tx) return msg, err @@ -626,9 +732,18 @@ func (m Message) To() *common.Address { return m.to } func (m Message) GasPrice() *big.Int { return m.gasPrice } func (m Message) GasFeeCap() *big.Int { return m.gasFeeCap } func (m Message) GasTipCap() *big.Int { return m.gasTipCap } +func (m Message) EthCompatible() bool { return m.ethCompatible } +func (m Message) FeeCurrency() *common.Address { return m.feeCurrency } +func (m Message) GatewayFeeRecipient() *common.Address { return m.gatewayFeeRecipient } +func (m Message) GatewayFee() *big.Int { return m.gatewayFee } func (m Message) Value() *big.Int { return m.amount } func (m Message) Gas() uint64 { return m.gasLimit } func (m Message) Nonce() uint64 { return m.nonce } func (m Message) Data() []byte { return m.data } func (m Message) AccessList() AccessList { return m.accessList } func (m Message) IsFake() bool { return m.isFake } + +func (m Message) Fee() *big.Int { + gasFee := new(big.Int).Mul(m.gasPrice, big.NewInt(int64(m.gasLimit))) + return gasFee.Add(gasFee, m.gatewayFee) +}
diff --git go-ethereum/core/types/types_test.go celo/core/types/types_test.go index 8e14dbf744b6a20ed5a4e7cc651469d18388afcb..ba87afce568771ac7af6efdb90051ff9f17c2d3b 100644 --- go-ethereum/core/types/types_test.go +++ celo/core/types/types_test.go @@ -51,9 +51,7 @@ }{ { "legacy-header", &Header{ - Difficulty: big.NewInt(10000000000), Number: big.NewInt(1000), - GasLimit: 8_000_000, GasUsed: 8_000_000, Time: 555, Extra: make([]byte, 32), @@ -62,13 +60,10 @@ }, { "london-header", &Header{ - Difficulty: big.NewInt(10000000000), Number: big.NewInt(1000), - GasLimit: 8_000_000, GasUsed: 8_000_000, Time: 555, Extra: make([]byte, 32), - BaseFee: big.NewInt(10000000000), }, }, {
diff --git go-ethereum/core/types/transaction_test.go celo/core/types/transaction_test.go index 183169e28d74fa273a67f37323b7ddba88acfa17..81c25723a84ab106be4cb7ad5381f25cd1e4d41d 100644 --- go-ethereum/core/types/transaction_test.go +++ celo/core/types/transaction_test.go @@ -42,6 +42,9 @@ 0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(0), 0, big.NewInt(0), nil, + nil, + nil, + nil, )   rightvrsTx, _ = NewTransaction( @@ -50,6 +53,9 @@ testAddr, big.NewInt(10), 2000, big.NewInt(1), + nil, + nil, + nil, common.FromHex("5544"), ).WithSignature( HomesteadSigner{}, @@ -83,11 +89,11 @@ }   func TestTransactionSigHash(t *testing.T) { var homestead HomesteadSigner - if homestead.Hash(emptyTx) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") { - t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash()) + if homestead.Hash(emptyTx) != common.HexToHash("0884127f4e682c55978e9e8a4cecc734bf3fa14776a3c2b28adc16855cb3a491") { + t.Errorf("empty transaction hash mismatch, got %x", homestead.Hash(emptyTx)) } - if homestead.Hash(rightvrsTx) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") { - t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash()) + if homestead.Hash(rightvrsTx) != common.HexToHash("b879218b07bdecfa13fbfcca8ea298aab32d06e5ecd7b021a51b53c5f8919209") { + t.Errorf("RightVRS transaction hash mismatch, got %x", homestead.Hash(rightvrsTx)) } }   @@ -96,7 +102,7 @@ txb, err := rlp.EncodeToBytes(rightvrsTx) if err != nil { t.Fatalf("encode error: %v", err) } - should := common.FromHex("f86103018207d094b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a8255441ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3") + should := common.FromHex("f86403018207d080808094b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a8255441ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3") if !bytes.Equal(txb, should) { t.Errorf("encoded RLP mismatch, got %x", txb) } @@ -226,9 +232,23 @@ addr := crypto.PubkeyToAddress(key.PublicKey) return key, addr }   +func signAndEncodeTx(tx *Transaction) []byte { + key, _ := defaultTestKey() + + signer := HomesteadSigner{} + tx, _ = SignTx(tx, signer, key) + + buf := bytes.NewBuffer([]byte{}) + tx.EncodeRLP(buf) + byteArray := make([]byte, buf.Len()) + buf.Read(byteArray) + return byteArray +} + func TestRecipientEmpty(t *testing.T) { _, addr := defaultTestKey() - tx, err := decodeTx(common.Hex2Bytes("f8498080808080011ca09b16de9d5bdee2cf56c28d16275a4da68cd30273e2525f3959f5d62557489921a0372ebd8fb3345f7db7b5a86d42e24d36e983e259b0664ceb8c227ec9af572f3d")) + tx := NewTx(&LegacyTx{}) + tx, err := decodeTx(signAndEncodeTx(tx)) if err != nil { t.Fatal(err) } @@ -245,7 +265,7 @@ func TestRecipientNormal(t *testing.T) { _, addr := defaultTestKey()   - tx, err := decodeTx(common.Hex2Bytes("f85d80808094000000000000000000000000000000000000000080011ca0527c0d8f5c63f7b9f41324a7c8a563ee1190bcbf0dac8ab446291bdbf32f5c79a0552c4ef0a09a04395074dab9ed34d3fbfb843c2f2546cc30fe89ec143ca94ca6")) + tx, err := decodeTx(signAndEncodeTx(rightvrsTx)) if err != nil { t.Fatal(err) } @@ -259,14 +279,136 @@ t.Fatal("derived address doesn't match") } }   +// Tests that a modified transaction does not produce a valid signature +func TestTxAmountChanged(t *testing.T) { + _, addr := defaultTestKey() + + tx, err := decodeTx(signAndEncodeTx(rightvrsTx)) + if err != nil { + t.Error(err) + t.FailNow() + } + + tx.inner.(*LegacyTx).Value = big.NewInt(20) + + from, err := Sender(HomesteadSigner{}, tx) + if err != nil { + t.Error(err) + t.FailNow() + } + + if addr == from { + t.Error("derived address shouldn't match") + } +} + +func TestTxGatewayFeeRecipientChanged(t *testing.T) { + _, addr := defaultTestKey() + + tx, err := decodeTx(signAndEncodeTx(rightvrsTx)) + if err != nil { + t.Error(err) + t.FailNow() + } + + recipientAddr := common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b") + tx.inner.(*LegacyTx).GatewayFeeRecipient = &recipientAddr + + from, err := Sender(HomesteadSigner{}, tx) + if err != nil { + t.Error(err) + t.FailNow() + } + + if addr == from { + t.Error("derived address shouldn't match") + } +} + +func TestTxGatewayFee(t *testing.T) { + _, addr := defaultTestKey() + + tx, err := decodeTx(signAndEncodeTx(rightvrsTx)) + if err != nil { + t.Error(err) + t.FailNow() + } + + tx.inner.(*LegacyTx).GatewayFee.SetInt64(5) + + from, err := Sender(HomesteadSigner{}, tx) + if err != nil { + t.Error(err) + t.FailNow() + } + + if addr == from { + t.Error("derived address shouldn't match") + } +} + +func TestTxEthCompatible(t *testing.T) { + key, addr := defaultTestKey() + tx := NewTransactionEthCompatible( + 3, + common.Address{19}, + big.NewInt(9), + 7, + big.NewInt(13), + common.FromHex("ff05ff"), + ) + + var encoded []byte + var parsed *Transaction + + encoded, _ = rlp.EncodeToBytes(tx) + parsed = &Transaction{} + rlp.DecodeBytes(encoded, &parsed) + if tx.Hash() != parsed.Hash() { + t.Errorf("RLP parsed pre-signing tx differs from original, want %v, got %v", tx, parsed) + } + encoded, _ = tx.MarshalJSON() + parsed = &Transaction{} + parsed.UnmarshalJSON(encoded) + if tx.Hash() != parsed.Hash() { + t.Errorf("JSON parsed pre-signing tx differs from original, want %v, got %v", tx, parsed) + } + + // Repeat the tests but now with a signed transaction + signer := NewEIP155Signer(common.Big1) + signed, _ := SignTx(tx, signer, key) + sender, _ := Sender(signer, signed) + if sender != addr { + t.Errorf("recovered sender differs from original, want %v, got %v", addr, sender) + } + + encoded, _ = rlp.EncodeToBytes(signed) + parsed = &Transaction{} + rlp.DecodeBytes(encoded, &parsed) + if signed.Hash() != parsed.Hash() { + t.Errorf("RLP parsed post-signing tx differs from original, want %v, got %v", signed, parsed) + } + encoded, _ = signed.MarshalJSON() + parsed = &Transaction{} + parsed.UnmarshalJSON(encoded) + if signed.Hash() != parsed.Hash() { + t.Errorf("JSON parsed post-signing tx differs from original, want %v, got %v", signed, parsed) + } +} + +// toCELO converter assuming that feeCurrency is always nil +func toCELOMockFn(amount *big.Int, feeCurrency *common.Address) *big.Int { + return amount +} + func TestTransactionPriceNonceSortLegacy(t *testing.T) { testTransactionPriceNonceSort(t, nil) }   func TestTransactionPriceNonceSort1559(t *testing.T) { - testTransactionPriceNonceSort(t, big.NewInt(0)) + // testTransactionPriceNonceSort(t, big.NewInt(0)) testTransactionPriceNonceSort(t, big.NewInt(5)) - testTransactionPriceNonceSort(t, big.NewInt(50)) + // testTransactionPriceNonceSort(t, big.NewInt(50)) }   // Tests that transactions can be correctly sorted according to their price in @@ -320,8 +462,14 @@ groups[addr] = append(groups[addr], tx) } expectedCount += count } + baseFeeFn := func(fc *common.Address) *big.Int { + if fc != nil { + panic("unsupported fee currency") + } + return baseFee + } // Sort the transactions and cross check the nonce ordering - txset := NewTransactionsByPriceAndNonce(signer, groups, baseFee) + txset := NewTransactionsByPriceAndNonce(signer, groups, baseFeeFn, toCELOMockFn)   txs := Transactions{} for tx := txset.Peek(); tx != nil; tx = txset.Peek() { @@ -348,7 +496,7 @@ fromNext, _ := Sender(signer, next) tip, err := txi.EffectiveGasTip(baseFee) nextTip, nextErr := next.EffectiveGasTip(baseFee) if err != nil || nextErr != nil { - t.Errorf("error calculating effective tip") + t.Fatal("error calculating effective tip") } if fromi != fromNext && tip.Cmp(nextTip) < 0 { t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", i, fromi[:4], txi.GasPrice(), i+1, fromNext[:4], next.GasPrice()) @@ -372,13 +520,14 @@ groups := map[common.Address]Transactions{} for start, key := range keys { addr := crypto.PubkeyToAddress(key.PublicKey)   - tx, _ := SignTx(NewTransaction(0, common.Address{}, big.NewInt(100), 100, big.NewInt(1), nil), signer, key) + tx, _ := SignTx(NewTransaction(0, common.Address{}, big.NewInt(100), 100, big.NewInt(1), nil, nil, nil, nil), signer, key) tx.time = time.Unix(0, int64(len(keys)-start))   groups[addr] = append(groups[addr], tx) } // Sort the transactions and cross check the nonce ordering - txset := NewTransactionsByPriceAndNonce(signer, groups, nil) + baseFeeFn := func(*common.Address) *big.Int { return nil } + txset := NewTransactionsByPriceAndNonce(signer, groups, baseFeeFn, toCELOMockFn)   txs := Transactions{} for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
diff --git go-ethereum/core/types/access_list_tx.go celo/core/types/access_list_tx.go index 1e102f9336618660bdc2c0a0780efa309734bbc7..6ac5321e4d845ac70f003b887be6cb9b4eadbc43 100644 --- go-ethereum/core/types/access_list_tx.go +++ celo/core/types/access_list_tx.go @@ -106,6 +106,10 @@ func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice } func (tx *AccessListTx) value() *big.Int { return tx.Value } func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } func (tx *AccessListTx) to() *common.Address { return tx.To } +func (tx *AccessListTx) feeCurrency() *common.Address { return nil } +func (tx *AccessListTx) gatewayFeeRecipient() *common.Address { return nil } +func (tx *AccessListTx) gatewayFee() *big.Int { return nil } +func (tx *AccessListTx) ethCompatible() bool { return false }   func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S
diff --git go-ethereum/core/types/legacy_tx.go celo/core/types/legacy_tx.go index 9385f39640200afc9ae5cef7f11c0c29f8385744..3eeaeb77d546c87ef8620e2806855d03ce89bbbd 100644 --- go-ethereum/core/types/legacy_tx.go +++ celo/core/types/legacy_tx.go @@ -17,9 +17,11 @@ package types   import ( + "io" "math/big"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" )   // LegacyTx is the transaction data of regular Ethereum transactions. @@ -27,15 +29,134 @@ type LegacyTx struct { Nonce uint64 // nonce of sender account GasPrice *big.Int // wei per gas Gas uint64 // gas limit + FeeCurrency *common.Address // nil means native currency + GatewayFeeRecipient *common.Address // nil means no gateway fee is paid + GatewayFee *big.Int To *common.Address `rlp:"nil"` // nil means contract creation Value *big.Int // wei amount Data []byte // contract invocation input data V, R, S *big.Int // signature values + + // This is only used when marshaling to JSON. + Hash *common.Hash `rlp:"-"` + + // Whether this is an ethereum-compatible transaction (i.e. with FeeCurrency, GatewayFeeRecipient and GatewayFee omitted) + EthCompatible bool `rlp:"-"` +} + +// ethCompatibleTxRlpList is used for RLP encoding/decoding of eth-compatible transactions. +// As such, it: +// (a) excludes the Celo-only fields, +// (b) doesn't need the Hash or EthCompatible fields, and +// (c) doesn't need the `json` or `gencodec` tags +type ethCompatibleTxRlpList struct { + Nonce uint64 // nonce of sender account + GasPrice *big.Int // wei per gas + Gas uint64 // gas limit + To *common.Address `rlp:"nil"` // nil means contract creation + Value *big.Int // wei amount + Data []byte // contract invocation input data + V, R, S *big.Int // signature values +} + +// celoTxRlpList is used for RLP encoding/decoding of celo transactions. +type celoTxRlpList struct { + Nonce uint64 // nonce of sender account + GasPrice *big.Int // wei per gas + Gas uint64 // gas limit + FeeCurrency *common.Address `rlp:"nil"` // nil means native currency + GatewayFeeRecipient *common.Address `rlp:"nil"` // nil means no gateway fee is paid + GatewayFee *big.Int `rlp:"nil"` + To *common.Address `rlp:"nil"` // nil means contract creation + Value *big.Int // wei amount + Data []byte // contract invocation input data + V, R, S *big.Int // signature values +} + +func toEthCompatibleRlpList(tx LegacyTx) ethCompatibleTxRlpList { + return ethCompatibleTxRlpList{ + Nonce: tx.Nonce, + GasPrice: tx.GasPrice, + Gas: tx.Gas, + To: tx.To, + Value: tx.Value, + Data: tx.Data, + V: tx.V, + R: tx.R, + S: tx.S, + } +} + +func toCeloRlpList(tx LegacyTx) celoTxRlpList { + return celoTxRlpList{ + Nonce: tx.Nonce, + GasPrice: tx.GasPrice, + Gas: tx.Gas, + FeeCurrency: tx.FeeCurrency, + GatewayFeeRecipient: tx.GatewayFeeRecipient, + GatewayFee: tx.GatewayFee, + To: tx.To, + Value: tx.Value, + Data: tx.Data, + V: tx.V, + R: tx.R, + S: tx.S, + } +} + +func (tx *LegacyTx) setTxFromEthCompatibleRlpList(rlplist ethCompatibleTxRlpList) { + tx.Nonce = rlplist.Nonce + tx.GasPrice = rlplist.GasPrice + tx.Gas = rlplist.Gas + tx.FeeCurrency = nil + tx.GatewayFeeRecipient = nil + tx.GatewayFee = big.NewInt(0) + tx.To = rlplist.To + tx.Value = rlplist.Value + tx.Data = rlplist.Data + tx.V = rlplist.V + tx.R = rlplist.R + tx.S = rlplist.S + tx.Hash = nil // txdata.Hash is calculated and saved inside tx.Hash() + tx.EthCompatible = true +} + +func (tx *LegacyTx) setTxFromCeloRlpList(rlplist celoTxRlpList) { + tx.Nonce = rlplist.Nonce + tx.GasPrice = rlplist.GasPrice + tx.Gas = rlplist.Gas + tx.FeeCurrency = rlplist.FeeCurrency + tx.GatewayFeeRecipient = rlplist.GatewayFeeRecipient + tx.GatewayFee = rlplist.GatewayFee + tx.To = rlplist.To + tx.Value = rlplist.Value + tx.Data = rlplist.Data + tx.V = rlplist.V + tx.R = rlplist.R + tx.S = rlplist.S + tx.Hash = nil // txdata.Hash is calculated and saved inside tx.Hash() + tx.EthCompatible = false }   // NewTransaction creates an unsigned legacy transaction. // Deprecated: use NewTx instead. -func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { +func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, feeCurrency, gatewayFeeRecipient *common.Address, gatewayFee *big.Int, data []byte) *Transaction { + return NewTx(&LegacyTx{ + Nonce: nonce, + To: &to, + Value: amount, + Gas: gasLimit, + GasPrice: gasPrice, + Data: data, + FeeCurrency: feeCurrency, + GatewayFeeRecipient: gatewayFeeRecipient, + GatewayFee: gatewayFee, + }) +} + +// NewTransaction creates an unsigned legacy transaction. +// Deprecated: use NewTx instead. +func NewTransactionEthCompatible(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { return NewTx(&LegacyTx{ Nonce: nonce, To: &to, @@ -43,21 +164,73 @@ Value: amount, Gas: gasLimit, GasPrice: gasPrice, Data: data, + EthCompatible: true, }) }   // NewContractCreation creates an unsigned legacy transaction. // Deprecated: use NewTx instead. -func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { +func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, feeCurrency, gatewayFeeRecipient *common.Address, gatewayFee *big.Int, data []byte) *Transaction { return NewTx(&LegacyTx{ Nonce: nonce, Value: amount, Gas: gasLimit, GasPrice: gasPrice, Data: data, + FeeCurrency: feeCurrency, + GatewayFeeRecipient: gatewayFeeRecipient, + GatewayFee: gatewayFee, }) }   +// NewContractCreation creates an unsigned legacy transaction. +// Deprecated: use NewTx instead. +func NewContractCreationEthCompatible(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { + return NewTx(&LegacyTx{ + Nonce: nonce, + Value: amount, + Gas: gasLimit, + GasPrice: gasPrice, + Data: data, + EthCompatible: true, + }) +} + +// EncodeRLP implements rlp.Encoder +func (tx *LegacyTx) EncodeRLP(w io.Writer) error { + if tx.EthCompatible { + return rlp.Encode(w, toEthCompatibleRlpList(*tx)) + } else { + return rlp.Encode(w, toCeloRlpList(*tx)) + } +} + +// DecodeRLP implements rlp.Decoder +func (tx *LegacyTx) DecodeRLP(s *rlp.Stream) (err error) { + _, size, _ := s.Kind() + var raw rlp.RawValue + err = s.Decode(&raw) + if err != nil { + return err + } + headerSize := len(raw) - int(size) + numElems, err := rlp.CountValues(raw[headerSize:]) + if err != nil { + return err + } + if numElems == ethCompatibleTxNumFields { + rlpList := ethCompatibleTxRlpList{} + err = rlp.DecodeBytes(raw, &rlpList) + tx.setTxFromEthCompatibleRlpList(rlpList) + } else { + var rlpList celoTxRlpList + err = rlp.DecodeBytes(raw, &rlpList) + tx.setTxFromCeloRlpList(rlpList) + } + + return err +} + // copy creates a deep copy of the transaction data and initializes all fields. func (tx *LegacyTx) copy() TxData { cpy := &LegacyTx{ @@ -65,12 +238,19 @@ Nonce: tx.Nonce, To: tx.To, // TODO: copy pointed-to address Data: common.CopyBytes(tx.Data), Gas: tx.Gas, + FeeCurrency: tx.FeeCurrency, // TODO: copy pointed-to address + GatewayFeeRecipient: tx.GatewayFeeRecipient, // TODO: copy pointed-to address + EthCompatible: tx.EthCompatible, // These are initialized below. + GatewayFee: new(big.Int), Value: new(big.Int), GasPrice: new(big.Int), V: new(big.Int), R: new(big.Int), S: new(big.Int), + } + if tx.GatewayFee != nil { + cpy.GatewayFee.Set(tx.GatewayFee) } if tx.Value != nil { cpy.Value.Set(tx.Value) @@ -102,6 +282,10 @@ func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice } func (tx *LegacyTx) value() *big.Int { return tx.Value } func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } func (tx *LegacyTx) to() *common.Address { return tx.To } +func (tx *LegacyTx) feeCurrency() *common.Address { return tx.FeeCurrency } +func (tx *LegacyTx) gatewayFeeRecipient() *common.Address { return tx.GatewayFeeRecipient } +func (tx *LegacyTx) gatewayFee() *big.Int { return tx.GatewayFee } +func (tx *LegacyTx) ethCompatible() bool { return tx.EthCompatible }   func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S
diff --git go-ethereum/core/vm/errors.go celo/core/vm/errors.go index e47b660b979db7ca72e34ef4e0cdb9f38a742cc5..cef0230020a935b5277652944c76fac495fdd3be 100644 --- go-ethereum/core/vm/errors.go +++ celo/core/vm/errors.go @@ -26,8 +26,17 @@ var ( ErrOutOfGas = errors.New("out of gas") ErrCodeStoreOutOfGas = errors.New("contract creation code storage out of gas") ErrDepth = errors.New("max call depth exceeded") + ErrTraceLimitReached = errors.New("the number of logs reached the specified limit") ErrInsufficientBalance = errors.New("insufficient balance for transfer") ErrContractAddressCollision = errors.New("contract address collision") + ErrNoCompatibleInterpreter = errors.New("no compatible interpreter") + ErrValidatorsOutOfBounds = errors.New("validator index out of bounds") + ErrBlockNumberOutOfBounds = errors.New("block number out of bounds") + ErrInputLength = errors.New("invalid input length") + ErrInputDecode = errors.New("unable to decode input") + ErrInputVerification = errors.New("unable to verify header") + ErrEngineIncompatible = errors.New("blockchain engine incompatible with request") + ErrUnexpected = errors.New("unexpected execution error") ErrExecutionReverted = errors.New("execution reverted") ErrMaxCodeSizeExceeded = errors.New("max code size exceeded") ErrInvalidJump = errors.New("invalid jump destination")
diff --git go-ethereum/core/vm/instructions.go celo/core/vm/instructions.go index ff09098fcf988cc42fea46ea482be9f2761d8e64..fd538b2b9ffa1c73e6cc5462c9fd314bb7b8f0ab 100644 --- go-ethereum/core/vm/instructions.go +++ celo/core/vm/instructions.go @@ -469,17 +469,6 @@ scope.Stack.push(v) return nil, nil }   -func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - v, _ := uint256.FromBig(interpreter.evm.Context.Difficulty) - scope.Stack.push(v) - return nil, nil -} - -func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) - return nil, nil -} - func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { scope.Stack.pop() return nil, nil @@ -669,7 +658,9 @@ temp.SetOne() } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - ret = common.CopyBytes(ret) + if interpreter.evm.chainRules.IsEspresso { + ret = common.CopyBytes(ret) + } scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } scope.Contract.Gas += returnGas @@ -704,7 +695,9 @@ temp.SetOne() } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - ret = common.CopyBytes(ret) + if interpreter.evm.chainRules.IsEspresso { + ret = common.CopyBytes(ret) + } scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } scope.Contract.Gas += returnGas @@ -732,7 +725,9 @@ temp.SetOne() } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - ret = common.CopyBytes(ret) + if interpreter.evm.chainRules.IsEspresso { + ret = common.CopyBytes(ret) + } scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } scope.Contract.Gas += returnGas @@ -760,7 +755,9 @@ temp.SetOne() } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - ret = common.CopyBytes(ret) + if interpreter.evm.chainRules.IsEspresso { + ret = common.CopyBytes(ret) + } scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } scope.Contract.Gas += returnGas
diff --git go-ethereum/core/vm/evm.go celo/core/vm/evm.go index 5cc41edd6fd895dd68f800292cf1d9de6461219e..657c306ff4ba8956e0ae1f5837c17540403c92bf 100644 --- go-ethereum/core/vm/evm.go +++ celo/core/vm/evm.go @@ -22,6 +22,8 @@ "sync/atomic" "time"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" @@ -39,13 +41,23 @@ TransferFunc func(StateDB, common.Address, common.Address, *big.Int) // GetHashFunc returns the n'th block hash in the blockchain // and is used by the BLOCKHASH EVM op code. GetHashFunc func(uint64) common.Hash + // GetHeaderByNumberFunc returns the header of the nth block in the chain. + GetHeaderByNumberFunc func(uint64) *types.Header + // VerifySealFunc returns true if the given header contains a valid seal + // according to the engine's consensus rules. + VerifySealFunc func(*types.Header) bool + + // GetValidatorsFunc is the signature for the GetValidators function + GetValidatorsFunc func(blockNumber *big.Int, headerHash common.Hash) []istanbul.Validator )   -func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { - var precompiles map[common.Address]PrecompiledContract +func (evm *EVM) precompile(addr common.Address) (CeloPrecompiledContract, bool) { + var precompiles map[common.Address]CeloPrecompiledContract switch { - case evm.chainRules.IsBerlin: - precompiles = PrecompiledContractsBerlin + case evm.chainRules.IsEspresso: + precompiles = PrecompiledContractsE + case evm.chainRules.IsDonut: + precompiles = PrecompiledContractsDonut case evm.chainRules.IsIstanbul: precompiles = PrecompiledContractsIstanbul case evm.chainRules.IsByzantium: @@ -67,13 +79,18 @@ // Transfer transfers ether from one account to the other Transfer TransferFunc // GetHash returns the hash corresponding to n GetHash GetHashFunc + // GetParentSealBitmap returns the parent seal bitmap corresponding to n + GetHeaderByNumber GetHeaderByNumberFunc + // VerifySeal verifies or returns an error for the given header + VerifySeal VerifySealFunc   // Block information Coinbase common.Address // Provides information for COINBASE - GasLimit uint64 // Provides information for GASLIMIT BlockNumber *big.Int // Provides information for NUMBER Time *big.Int // Provides information for TIME - Difficulty *big.Int // Provides information for DIFFICULTY + + EpochSize uint64 + GetValidators GetValidatorsFunc BaseFee *big.Int // Provides information for BASEFEE }   @@ -120,6 +137,8 @@ // callGasTemp holds the gas available for the current call. This is needed because the // available gas is calculated in gasCall* according to the 63/64 rule and later // applied in opCall*. callGasTemp uint64 + + dontMeterGas bool }   // NewEVM returns a new EVM. The returned EVM is not thread safe and should @@ -132,6 +151,7 @@ StateDB: statedb, Config: config, chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber), + dontMeterGas: false, } evm.interpreter = NewEVMInterpreter(evm, config) return evm @@ -209,7 +229,7 @@ } }   if isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = RunPrecompiledContract(p, input, gas, NewContext(caller.Address(), evm)) } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. @@ -275,7 +295,7 @@ }   // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = RunPrecompiledContract(p, input, gas, NewContext(caller.Address(), evm)) } else { addrCopy := addr // Initialise a new contract and set the code that is to be used by the EVM. @@ -319,7 +339,7 @@ }   // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = RunPrecompiledContract(p, input, gas, NewContext(caller.Address(), evm)) } else { addrCopy := addr // Initialise a new contract and make initialise the delegate values @@ -371,7 +391,7 @@ }(gas) }   if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = RunPrecompiledContract(p, input, gas, NewContext(caller.Address(), evm)) } else { // At this point, we use a copy of address. If we don't, the go compiler will // leak the 'contract' to the outer scope, and make allocation for 'contract' @@ -422,7 +442,7 @@ nonce := evm.StateDB.GetNonce(caller.Address()) evm.StateDB.SetNonce(caller.Address(), nonce+1) // We add this to the access list _before_ taking a snapshot. Even if the creation fails, // the access-list change should not be rolled back - if evm.chainRules.IsBerlin { + if evm.chainRules.IsEspresso { evm.StateDB.AddAddressToAccessList(address) } // Ensure there's no existing contract already at the designated address @@ -465,7 +485,7 @@ err = ErrMaxCodeSizeExceeded }   // Reject code starting with 0xEF if EIP-3541 is enabled. - if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon { + if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsEspresso { err = ErrInvalidCode }   @@ -520,3 +540,26 @@ }   // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } + +func (evm *EVM) GetStateDB() StateDB { + return evm.StateDB +} + +func (evm *EVM) GetDebug() bool { + return evm.Config.Debug +} + +func (evm *EVM) SetDebug(value bool) { + // Set both of these in sync since they refer to the same config data + // and are interchangeably used in the interpreter & evm. + evm.Config.Debug = value + evm.interpreter.cfg.Debug = value +} + +func (evm *EVM) StopGasMetering() { + evm.dontMeterGas = true +} + +func (evm *EVM) StartGasMetering() { + evm.dontMeterGas = false +}
diff --git go-ethereum/core/vm/contracts_test.go celo/core/vm/contracts_test.go index 299ce1d9e15981770698c42867e716f8ae63afdc..04fb2912f60231872cb713c140761ddf9190726d 100644 --- go-ethereum/core/vm/contracts_test.go +++ celo/core/vm/contracts_test.go @@ -33,6 +33,7 @@ Input, Expected string Gas uint64 Name string NoBenchmark bool // Benchmark primarily the worst-cases + ErrorExpected bool }   // precompiledFailureTest defines the input/error pairs for precompiled @@ -45,26 +46,38 @@ }   // allPrecompiles does not map to the actual set of precompiles, as it also contains // repriced versions of precompiles at certain slots -var allPrecompiles = map[common.Address]PrecompiledContract{ - common.BytesToAddress([]byte{1}): &ecrecover{}, - common.BytesToAddress([]byte{2}): &sha256hash{}, - common.BytesToAddress([]byte{3}): &ripemd160hash{}, - common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, - common.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{9}): &blake2F{}, - common.BytesToAddress([]byte{10}): &bls12381G1Add{}, - common.BytesToAddress([]byte{11}): &bls12381G1Mul{}, - common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{}, - common.BytesToAddress([]byte{13}): &bls12381G2Add{}, - common.BytesToAddress([]byte{14}): &bls12381G2Mul{}, - common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{}, - common.BytesToAddress([]byte{16}): &bls12381Pairing{}, - common.BytesToAddress([]byte{17}): &bls12381MapG1{}, - common.BytesToAddress([]byte{18}): &bls12381MapG2{}, +var allPrecompiles = map[common.Address]CeloPrecompiledContract{ + common.BytesToAddress([]byte{1}): &wrap{&ecrecover{}}, + common.BytesToAddress([]byte{2}): &wrap{&sha256hash{}}, + common.BytesToAddress([]byte{3}): &wrap{&ripemd160hash{}}, + common.BytesToAddress([]byte{4}): &wrap{&dataCopy{}}, + common.BytesToAddress([]byte{5}): &wrap{&bigModExp{eip2565: false}}, + common.BytesToAddress([]byte{0xf1, 0xf5}): &wrap{&bigModExp{eip2565: true}}, // "f1f5" otherwise "f5" collides with our precompile (getParentSealBitmapAddress) + common.BytesToAddress([]byte{6}): &wrap{&bn256AddIstanbul{}}, + common.BytesToAddress([]byte{7}): &wrap{&bn256ScalarMulIstanbul{}}, + common.BytesToAddress([]byte{8}): &wrap{&bn256PairingIstanbul{}}, + common.BytesToAddress([]byte{9}): &wrap{&blake2F{}}, + common.BytesToAddress([]byte{10}): &wrap{&bls12381G1Add{}}, + common.BytesToAddress([]byte{11}): &wrap{&bls12381G1Mul{}}, + common.BytesToAddress([]byte{12}): &wrap{&bls12381G1MultiExp{}}, + common.BytesToAddress([]byte{13}): &wrap{&bls12381G2Add{}}, + common.BytesToAddress([]byte{14}): &wrap{&bls12381G2Mul{}}, + common.BytesToAddress([]byte{15}): &wrap{&bls12381G2MultiExp{}}, + common.BytesToAddress([]byte{16}): &wrap{&bls12381Pairing{}}, + common.BytesToAddress([]byte{17}): &wrap{&bls12381MapG1{}}, + common.BytesToAddress([]byte{18}): &wrap{&bls12381MapG2{}}, + + // Celo Precompiled Contracts + celoPrecompileAddress(5): &getValidator{}, + celoPrecompileAddress(6): &numberValidators{}, + celoPrecompileAddress(7): &epochSize{}, + celoPrecompileAddress(8): &wrap{&blockNumberFromHeader{}}, + celoPrecompileAddress(9): &wrap{&hashHeader{}}, + celoPrecompileAddress(10): &getParentSealBitmap{}, + celoPrecompileAddress(11): &getVerifiedSealBitmap{}, + + // New in Donut hard fork + celoPrecompileAddress(30): &getValidatorBLS{}, }   // EIP-152 test vectors @@ -96,13 +109,23 @@ p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - if res, _, err := RunPrecompiledContract(p, in, gas); err != nil { - t.Error(err) - } else if common.Bytes2Hex(res) != test.Expected { - t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) - } - if expGas := test.Gas; expGas != gas { - t.Errorf("%v: gas wrong, expected %d, got %d", test.Name, expGas, gas) + res, _, err := RunPrecompiledContract(p, in, gas, NewContext(common.HexToAddress("1337"), mockEVM)) + if test.ErrorExpected { + if err == nil { + t.Errorf("Expected error: %v, but no error occurred", test.Expected) + } else if err.Error() != test.Expected { + t.Errorf("Expected error: \"%v\", but got \"%v\"", test.Expected, err.Error()) + } + } else { + if err != nil { + t.Error(err) + } else if common.Bytes2Hex(res) != test.Expected { + t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) + } + // TODO: Calculate and add our actual gas to every json file + // if expGas := test.Gas; expGas != gas { + // t.Errorf("%v: gas wrong, expected %d, got %d", test.Name, expGas, gas) + // } } // Verify that the precompile did not touch the input buffer exp := common.Hex2Bytes(test.Input) @@ -118,7 +141,7 @@ in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) - 1   t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas) + _, _, err := RunPrecompiledContract(p, in, gas, NewContext(common.HexToAddress("1337"), mockEVM)) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } @@ -135,7 +158,7 @@ p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(test.Name, func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas) + _, _, err := RunPrecompiledContract(p, in, gas, NewContext(common.HexToAddress("1337"), mockEVM)) if err.Error() != test.ExpectedError { t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) } @@ -167,7 +190,7 @@ start := time.Now() bench.ResetTimer() for i := 0; i < bench.N; i++ { copy(data, in) - res, _, err = RunPrecompiledContract(p, data, reqGas) + res, _, err = RunPrecompiledContract(p, data, reqGas, NewContext(common.HexToAddress("1337"), mockEVM)) } bench.StopTimer() elapsed := uint64(time.Since(start)) @@ -235,8 +258,8 @@ // Tests the sample inputs from the ModExp EIP 198. func TestPrecompiledModExp(t *testing.T) { testJson("modexp", "05", t) } func BenchmarkPrecompiledModExp(b *testing.B) { benchJson("modexp", "05", b) }   -func TestPrecompiledModExpEip2565(t *testing.T) { testJson("modexp_eip2565", "f5", t) } -func BenchmarkPrecompiledModExpEip2565(b *testing.B) { benchJson("modexp_eip2565", "f5", b) } +func TestPrecompiledModExpEip2565(t *testing.T) { testJson("modexp_eip2565", "f1f5", t) } +func BenchmarkPrecompiledModExpEip2565(b *testing.B) { benchJson("modexp_eip2565", "f1f5", b) }   // Tests the sample inputs from the elliptic curve addition EIP 213. func TestPrecompiledBn256Add(t *testing.T) { testJson("bn256Add", "06", t) }
diff --git go-ethereum/core/vm/contract.go celo/core/vm/contract.go index 3ece2ca402786df8385dcf1ad0b7ed1a2185559a..33c5c8fc28a5353335ed1118a630d7eb5fde4601 100644 --- go-ethereum/core/vm/contract.go +++ celo/core/vm/contract.go @@ -20,6 +20,7 @@ import ( "math/big"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" "github.com/holiman/uint256" )   @@ -166,6 +167,7 @@ // UseGas attempts the use gas and subtracts it and returns true on success func (c *Contract) UseGas(gas uint64) (ok bool) { if c.Gas < gas { + log.Debug("UseGas failed due to insufficient gas", "available gas", c.Gas, "gas required", gas) return false } c.Gas -= gas
diff --git go-ethereum/core/vm/operations_acl.go celo/core/vm/operations_acl.go index 271fec20dd2c78abf76a8514ac905d8725601d17..f3b3f58464ab8ea8891b817fd4ac2d4b3fbc9cc4 100644 --- go-ethereum/core/vm/operations_acl.go +++ celo/core/vm/operations_acl.go @@ -98,8 +98,8 @@ // gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929 // For SLOAD, if the (address, storage_key) pair (where address is the address of the contract // whose storage is being read) is not yet in accessed_storage_keys, -// charge 2100 gas and add the pair to accessed_storage_keys. -// If the pair is already in accessed_storage_keys, charge 100 gas. +// charge COLD_SLOAD_COST gas and add the pair to accessed_storage_keys. +// If the pair is already in accessed_storage_keys, charge WARM_STORAGE_READ_COST gas. func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { loc := stack.peek() slot := common.Hash(loc.Bytes32()) @@ -215,7 +215,7 @@ // see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200)   // gasSStoreEIP2539 implements gas cost for SSTORE according to EPI-2539 - // Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800) + // Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (6,100) gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529) )
diff --git go-ethereum/core/vm/celo_contracts_test.go celo/core/vm/celo_contracts_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d6367e81ad815e5c5da0aeff54a3ba351f09dea8 --- /dev/null +++ celo/core/vm/celo_contracts_test.go @@ -0,0 +1,153 @@ +package vm + +import ( + "math/big" + "testing" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/crypto/sha3" +) + +func getValidators(number *big.Int, _ common.Hash) []istanbul.Validator { + preimage := append([]byte("fakevalidators"), common.LeftPadBytes(number.Bytes()[:], 32)...) + hash := sha3.Sum256(preimage) + var validators []istanbul.Validator + for i := 0; i < 16; i, hash = i+1, sha3.Sum256(hash[:]) { + key, _ := crypto.ToECDSA(hash[:]) + blsPrivateKey, _ := blscrypto.ECDSAToBLS(key) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + addr := crypto.PubkeyToAddress(key.PublicKey) + validators = append(validators, validator.New(addr, blsPublicKey)) + } + return validators +} + +func makeTestSeal(number *big.Int) types.IstanbulAggregatedSeal { + preimage := append([]byte("fakeseal"), common.LeftPadBytes(number.Bytes()[:], 32)...) + hash := sha3.Sum256(preimage) + return types.IstanbulAggregatedSeal{Bitmap: new(big.Int).SetBytes(hash[:2])} +} + +func makeTestHeaderHash(number *big.Int) common.Hash { + preimage := append([]byte("fakeheader"), common.LeftPadBytes(number.Bytes()[:], 32)...) + return common.Hash(sha3.Sum256(preimage)) +} + +func makeTestHeaderExtra(number *big.Int) *types.IstanbulExtra { + return &types.IstanbulExtra{ + AggregatedSeal: makeTestSeal(number), + ParentAggregatedSeal: makeTestSeal(new(big.Int).Sub(number, common.Big1)), + } +} + +func makeTestHeader(number *big.Int) *types.Header { + extra, err := rlp.EncodeToBytes(makeTestHeaderExtra(number)) + if err != nil { + panic(err) + } + return &types.Header{ + ParentHash: makeTestHeaderHash(new(big.Int).Sub(number, common.Big1)), + Number: number, + GasUsed: params.DefaultGasLimit / 2, + Extra: append(make([]byte, types.IstanbulExtraVanity), extra...), + Time: number.Uint64() * 5, + } +} + +func getHeaderByNumber(number uint64) *types.Header { + return makeTestHeader(new(big.Int).SetUint64(number)) +} + +var testHeader = makeTestHeader(big.NewInt(10000)) + +var vmBlockCtx = BlockContext{ + CanTransfer: func(db StateDB, addr common.Address, amount *big.Int) bool { + return db.GetBalance(addr).Cmp(amount) >= 0 + }, + Transfer: func(s StateDB, a1, a2 common.Address, i *big.Int) { panic("transfer: not implemented") }, + GetHash: func(u uint64) common.Hash { panic("getHash: not implemented") }, + VerifySeal: func(header *types.Header) bool { + // If the block is later than the unsealed reference block, return false. + return !(header.Number.Cmp(testHeader.Number) > 0) + }, + Coinbase: common.Address{}, + BlockNumber: new(big.Int).Set(testHeader.Number), + Time: new(big.Int).SetUint64(testHeader.Time), + + EpochSize: 100, + GetValidators: getValidators, + GetHeaderByNumber: getHeaderByNumber, +} + +var vmTxCtx = TxContext{ + GasPrice: common.Big1, + Origin: common.HexToAddress("a11ce"), +} + +// Create a global mock EVM for use in the following tests. +var mockEVM = &EVM{ + Context: vmBlockCtx, + TxContext: vmTxCtx, +} + +var getVerifiedSealBitmapTests = []precompiledTest{ + // Input is a block header. Output is bitmap. + { + Input: "", + Expected: "unable to decode input", + Name: "input_invalid_empty", + ErrorExpected: true, + }, + { + Input: func() string { + header := makeTestHeader(common.Big1) + encoded, _ := rlp.EncodeToBytes(header) + return hexutil.Encode(encoded)[2:] + }(), + Expected: "0000000000000000000000000000000000000000000000000000000000007b1d", + Name: "correct_verified_header", + }, + { + Input: func() string { + header := makeTestHeader(common.Big1) + header.Extra = nil + encoded, _ := rlp.EncodeToBytes(header) + return hexutil.Encode(encoded)[2:] + }(), + Expected: "blockchain engine incompatible with request", + Name: "input_incompatible_engine", + ErrorExpected: true, + }, +} + +// Tests sample inputs for getValidator +func TestGetValidator(t *testing.T) { testJson("getValidator", "fa", t) } + +func TestGetValidatorBLSPublicKey(t *testing.T) { testJson("getValidatorBLSPublicKey", "e1", t) } + +// Tests sample inputs for numberValidators +func TestNumberValidators(t *testing.T) { testJson("numberValidators", "f9", t) } + +// Tests sample inputs for getBlockNumberFromHeader +func TestGetBlockNumberFromHeader(t *testing.T) { testJson("blockNumberFromHeader", "f7", t) } + +// Tests sample inputs for hashHeader +func TestPrecompiledHashHeader(t *testing.T) { testJson("hashHeader", "f6", t) } + +// Tests sample inputs for getParentSealBitmapTests +func TestGetParentSealBitmap(t *testing.T) { testJson("getParentSealBitmap", "f5", t) } + +// Tests sample inputs for getParentSealBitmapTests +func TestGetVerifiedSealBitmap(t *testing.T) { + for _, test := range getVerifiedSealBitmapTests { + testPrecompiled("f4", test, t) + } +}
diff --git go-ethereum/core/vm/gas_table_test.go celo/core/vm/gas_table_test.go index 53982322ad32659864d13f445d974311e0c532d2..9e334e43043329fff82372acba2eba66a8ae20c3 100644 --- go-ethereum/core/vm/gas_table_test.go +++ celo/core/vm/gas_table_test.go @@ -91,7 +91,7 @@ vmctx := BlockContext{ CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, } - vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) + vmenv := NewEVM(vmctx, TxContext{}, statedb, params.IstanbulTestChainConfig, Config{ExtraEips: []int{2200}})   _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int)) if err != tt.failure {
diff --git go-ethereum/core/vm/jump_table.go celo/core/vm/jump_table.go index a8e021811d73090f99c8a48187eb5f65d0232294..1de969d5c523cbe7b866d6a792b93cab67579215 100644 --- go-ethereum/core/vm/jump_table.go +++ celo/core/vm/jump_table.go @@ -56,27 +56,26 @@ spuriousDragonInstructionSet = newSpuriousDragonInstructionSet() byzantiumInstructionSet = newByzantiumInstructionSet() constantinopleInstructionSet = newConstantinopleInstructionSet() istanbulInstructionSet = newIstanbulInstructionSet() - berlinInstructionSet = newBerlinInstructionSet() - londonInstructionSet = newLondonInstructionSet() + espressoInstructionSet = newEspressoInstructionSet() + gforkInstructionSet = newGforkInstructionSet() )   // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation   -// newLondonInstructionSet returns the frontier, homestead, byzantium, -// contantinople, istanbul, petersburg, berlin and london instructions. -func newLondonInstructionSet() JumpTable { - instructionSet := newBerlinInstructionSet() - enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529 - enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198 +// newGforkInstructionSet returns the frontier, homestead, byzantium, +// constantinople, istanbul, petersburg, espresso and g-fork instructions. +func newGforkInstructionSet() JumpTable { + instructionSet := newEspressoInstructionSet() return instructionSet }   -// newBerlinInstructionSet returns the frontier, homestead, byzantium, -// contantinople, istanbul, petersburg and berlin instructions. -func newBerlinInstructionSet() JumpTable { +// newEspressoInstructionSet returns the frontier, homestead, byzantium, +// constantinople, istanbul, petersburg and espresso instructions. +func newEspressoInstructionSet() JumpTable { instructionSet := newIstanbulInstructionSet() enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929 + enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529 return instructionSet }   @@ -464,18 +463,6 @@ maxStack: maxStack(0, 1), }, NUMBER: { execute: opNumber, - constantGas: GasQuickStep, - minStack: minStack(0, 1), - maxStack: maxStack(0, 1), - }, - DIFFICULTY: { - execute: opDifficulty, - constantGas: GasQuickStep, - minStack: minStack(0, 1), - maxStack: maxStack(0, 1), - }, - GASLIMIT: { - execute: opGasLimit, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1),
diff --git go-ethereum/core/vm/celo_contracts.go celo/core/vm/celo_contracts.go new file mode 100644 index 0000000000000000000000000000000000000000..f67a2f693790120995f50560ddb0a526c65b65a9 --- /dev/null +++ celo/core/vm/celo_contracts.go @@ -0,0 +1,315 @@ +package vm + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + errBLS12377InvalidInputLength = errors.New("invalid input length") + errBLS12377G1PointSubgroup = errors.New("g1 point is not on correct subgroup") + errBLS12377G2PointSubgroup = errors.New("g2 point is not on correct subgroup") +) + +type CeloPrecompiledContract interface { + RequiredGas(input []byte) uint64 // RequiredGas calculates the contract gas use + Run(input []byte, ctx *celoPrecompileContext) ([]byte, error) // Run runs the precompiled contract +} + +type wrap struct { + PrecompiledContract +} + +func (pw *wrap) Run(input []byte, ctx *celoPrecompileContext) ([]byte, error) { + return pw.PrecompiledContract.Run(input) +} + +type celoPrecompileContext struct { + *BlockContext + *params.Rules + + caller common.Address + evm *EVM +} + +func NewContext(caller common.Address, evm *EVM) *celoPrecompileContext { + return &celoPrecompileContext{ + BlockContext: &evm.Context, + Rules: &evm.chainRules, + caller: caller, + evm: evm, + } +} + +func celoPrecompileAddress(index byte) common.Address { + celoPrecompiledContractsAddressOffset := byte(0xff) + return common.BytesToAddress(append([]byte{0}, (celoPrecompiledContractsAddressOffset - index))) +} + +type getValidator struct{} + +func (c *getValidator) RequiredGas(input []byte) uint64 { + return params.GetValidatorGas +} + +// Return the validators that are required to sign the given, possibly unsealed, block number. If this block is +// the last in an epoch, note that that may mean one or more of those validators may no longer be elected +// for subsequent blocks. +// WARNING: Validator set is always constructed from the canonical chain, therefore this precompile is undefined +// if the engine is aware of a chain with higher total difficulty. +func (c *getValidator) Run(input []byte, ctx *celoPrecompileContext) ([]byte, error) { + // input is comprised of two arguments: + // index: 32 byte integer representing the index of the validator to get + // blockNumber: 32 byte integer representing the block number to access + if (ctx.IsGFork && len(input) != 64) || len(input) < 64 { + return nil, ErrInputLength + } + + index := new(big.Int).SetBytes(input[0:32]) + + blockNumber := new(big.Int).SetBytes(input[32:64]) + if blockNumber.Cmp(common.Big0) == 0 { + // Validator set for the genesis block is empty, so any index is out of bounds. + return nil, ErrValidatorsOutOfBounds + } + if blockNumber.Cmp(ctx.BlockNumber) > 0 { + return nil, ErrBlockNumberOutOfBounds + } + + // Note: Passing empty hash as here as it is an extra expense and the hash is not actually used. + validators := ctx.GetValidators(new(big.Int).Sub(blockNumber, common.Big1), common.Hash{}) + + // Ensure index, which is guaranteed to be non-negative, is valid. + if index.Cmp(big.NewInt(int64(len(validators)))) >= 0 { + return nil, ErrValidatorsOutOfBounds + } + + validatorAddress := validators[index.Uint64()].Address() + addressBytes := common.LeftPadBytes(validatorAddress[:], 32) + + return addressBytes, nil +} + +type getValidatorBLS struct{} + +func (c *getValidatorBLS) RequiredGas(input []byte) uint64 { + return params.GetValidatorBLSGas +} + +func copyBLSNumber(result []byte, offset int, uncompressedBytes []byte, offset2 int) { + for i := 0; i < 48; i++ { + result[63-i+offset] = uncompressedBytes[i+offset2] + } +} + +// Return the validator BLS public key for the validator at given index. The public key is given in uncompressed format, 4*48 bytes. +func (c *getValidatorBLS) Run(input []byte, ctx *celoPrecompileContext) ([]byte, error) { + // input is comprised of two arguments: + // index: 32 byte integer representing the index of the validator to get + // blockNumber: 32 byte integer representing the block number to access + if len(input) < 64 { + return nil, ErrInputLength + } + + index := new(big.Int).SetBytes(input[0:32]) + + blockNumber := new(big.Int).SetBytes(input[32:64]) + if blockNumber.Cmp(common.Big0) == 0 { + // Validator set for the genesis block is empty, so any index is out of bounds. + return nil, ErrValidatorsOutOfBounds + } + if blockNumber.Cmp(ctx.BlockNumber) > 0 { + return nil, ErrBlockNumberOutOfBounds + } + + // Note: Passing empty hash as here as it is an extra expense and the hash is not actually used. + validators := ctx.GetValidators(new(big.Int).Sub(blockNumber, common.Big1), common.Hash{}) + + // Ensure index, which is guaranteed to be non-negative, is valid. + if index.Cmp(big.NewInt(int64(len(validators)))) >= 0 { + return nil, ErrValidatorsOutOfBounds + } + + validator := validators[index.Uint64()] + uncompressedBytes := validator.BLSPublicKeyUncompressed() + if len(uncompressedBytes) != 192 { + return nil, ErrUnexpected + } + + result := make([]byte, 256) + for i := 0; i < 256; i++ { + result[i] = 0 + } + + copyBLSNumber(result, 0, uncompressedBytes, 0) + copyBLSNumber(result, 64, uncompressedBytes, 48) + copyBLSNumber(result, 128, uncompressedBytes, 96) + copyBLSNumber(result, 192, uncompressedBytes, 144) + + return result, nil +} + +type numberValidators struct{} + +func (c *numberValidators) RequiredGas(input []byte) uint64 { + return params.GetValidatorGas +} + +// Return the number of validators that are required to sign this current, possibly unsealed, block. If this block is +// the last in an epoch, note that that may mean one or more of those validators may no longer be elected +// for subsequent blocks. +// WARNING: Validator set is always constructed from the canonical chain, therefore this precompile is undefined +// if the engine is aware of a chain with higher total difficulty. +func (c *numberValidators) Run(input []byte, ctx *celoPrecompileContext) ([]byte, error) { + // input is comprised of a single argument: + // blockNumber: 32 byte integer representing the block number to access + if len(input) < 32 { + return nil, ErrInputLength + } + + blockNumber := new(big.Int).SetBytes(input[0:32]) + if blockNumber.Cmp(common.Big0) == 0 { + // Genesis validator set is empty. Return 0. + return make([]byte, 32), nil + } + if blockNumber.Cmp(ctx.BlockNumber) > 0 { + return nil, ErrBlockNumberOutOfBounds + } + + // Note: Passing empty hash as here as it is an extra expense and the hash is not actually used. + validators := ctx.GetValidators(new(big.Int).Sub(blockNumber, common.Big1), common.Hash{}) + + numberValidators := big.NewInt(int64(len(validators))).Bytes() + numberValidatorsBytes := common.LeftPadBytes(numberValidators[:], 32) + return numberValidatorsBytes, nil +} + +type epochSize struct{} + +func (c *epochSize) RequiredGas(input []byte) uint64 { + return params.GetEpochSizeGas +} + +func (c *epochSize) Run(input []byte, ctx *celoPrecompileContext) ([]byte, error) { + epochSize := new(big.Int).SetUint64(ctx.EpochSize).Bytes() + epochSizeBytes := common.LeftPadBytes(epochSize[:], 32) + + return epochSizeBytes, nil +} + +type blockNumberFromHeader struct{} + +func (c *blockNumberFromHeader) RequiredGas(input []byte) uint64 { + return params.GetBlockNumberFromHeaderGas +} + +func (c *blockNumberFromHeader) Run(input []byte) ([]byte, error) { + var header types.Header + err := rlp.DecodeBytes(input, &header) + if err != nil { + return nil, ErrInputDecode + } + + blockNumber := header.Number.Bytes() + blockNumberBytes := common.LeftPadBytes(blockNumber[:], 32) + + return blockNumberBytes, nil +} + +type hashHeader struct{} + +func (c *hashHeader) RequiredGas(input []byte) uint64 { + return params.HashHeaderGas +} + +func (c *hashHeader) Run(input []byte) ([]byte, error) { + var header types.Header + err := rlp.DecodeBytes(input, &header) + if err != nil { + return nil, ErrInputDecode + } + + hashBytes := header.Hash().Bytes() + + return hashBytes, nil +} + +type getParentSealBitmap struct{} + +func (c *getParentSealBitmap) RequiredGas(input []byte) uint64 { + return params.GetParentSealBitmapGas +} + +// Return the signer bitmap from the parent seal of a past block in the chain. +// Requested parent seal must have occurred within 4 epochs of the current block number. +func (c *getParentSealBitmap) Run(input []byte, ctx *celoPrecompileContext) ([]byte, error) { + // input is comprised of a single argument: + // blockNumber: 32 byte integer representing the block number to access + if len(input) < 32 { + return nil, ErrInputLength + } + + blockNumber := new(big.Int).SetBytes(input[0:32]) + + // Ensure the request is for information from a previously sealed block. + if blockNumber.Cmp(common.Big0) == 0 || blockNumber.Cmp(ctx.BlockNumber) > 0 { + return nil, ErrBlockNumberOutOfBounds + } + + // Ensure the request is for a sufficiently recent block to limit state expansion. + historyLimit := new(big.Int).SetUint64(ctx.EpochSize * 4) + if blockNumber.Cmp(new(big.Int).Sub(ctx.BlockNumber, historyLimit)) <= 0 { + return nil, ErrBlockNumberOutOfBounds + } + + header := ctx.GetHeaderByNumber(blockNumber.Uint64()) + if header == nil { + log.Error("Unexpected failure to retrieve block in getParentSealBitmap precompile", "blockNumber", blockNumber) + return nil, ErrUnexpected + } + + extra, err := header.IstanbulExtra() + if err != nil { + log.Error("Header without Istanbul extra data encountered in getParentSealBitmap precompile", "blockNumber", blockNumber, "err", err) + return nil, ErrEngineIncompatible + } + + return common.LeftPadBytes(extra.ParentAggregatedSeal.Bitmap.Bytes()[:], 32), nil +} + +// getVerifiedSealBitmap is a precompile to verify the seal on a given header and extract its bitmap. +type getVerifiedSealBitmap struct{} + +func (c *getVerifiedSealBitmap) RequiredGas(input []byte) uint64 { + return params.GetVerifiedSealBitmapGas +} + +func (c *getVerifiedSealBitmap) Run(input []byte, ctx *celoPrecompileContext) ([]byte, error) { + // input is comprised of a single argument: + // header: rlp encoded block header + var header types.Header + if err := rlp.DecodeBytes(input, &header); err != nil { + return nil, ErrInputDecode + } + + // Verify the seal against the engine rules. + if !ctx.VerifySeal(&header) { + return nil, ErrInputVerification + } + + // Extract the verified seal from the header. + extra, err := header.IstanbulExtra() + if err != nil { + log.Error("Header without Istanbul extra data encountered in getVerifiedSealBitmap precompile", "extraData", header.Extra, "err", err) + // Seal verified by a non-Istanbul engine. Return an error. + return nil, ErrEngineIncompatible + } + + return common.LeftPadBytes(extra.AggregatedSeal.Bitmap.Bytes()[:], 32), nil +}
diff --git go-ethereum/core/vm/opcodes.go celo/core/vm/opcodes.go index 378ffba4c8379608200d97660ec34be7122727cf..9662c56cfa7679921c74dabb20ef573d03b353c8 100644 --- go-ethereum/core/vm/opcodes.go +++ celo/core/vm/opcodes.go @@ -99,8 +99,6 @@ BLOCKHASH OpCode = 0x40 + iota COINBASE TIMESTAMP NUMBER - DIFFICULTY - GASLIMIT CHAINID OpCode = 0x46 SELFBALANCE OpCode = 0x47 BASEFEE OpCode = 0x48 @@ -277,8 +275,6 @@ BLOCKHASH: "BLOCKHASH", COINBASE: "COINBASE", TIMESTAMP: "TIMESTAMP", NUMBER: "NUMBER", - DIFFICULTY: "DIFFICULTY", - GASLIMIT: "GASLIMIT", CHAINID: "CHAINID", SELFBALANCE: "SELFBALANCE", BASEFEE: "BASEFEE", @@ -449,8 +445,6 @@ "BLOCKHASH": BLOCKHASH, "COINBASE": COINBASE, "TIMESTAMP": TIMESTAMP, "NUMBER": NUMBER, - "DIFFICULTY": DIFFICULTY, - "GASLIMIT": GASLIMIT, "SELFBALANCE": SELFBALANCE, "POP": POP, "MLOAD": MLOAD,
diff --git go-ethereum/core/vm/interface.go celo/core/vm/interface.go index ad9b05d666a876c96640b7074e06c66e61ecba4f..f3d20485648bf0d8905007d0579ef1590a0d6602 100644 --- go-ethereum/core/vm/interface.go +++ celo/core/vm/interface.go @@ -74,6 +74,8 @@ AddLog(*types.Log) AddPreimage(common.Hash, []byte)   ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error + + Finalise(bool) }   // CallContext provides a basic interface for the EVM calling conventions. The EVM @@ -88,3 +90,24 @@ DelegateCall(env *EVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) // Create a new contract Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) } + +// EVMRunner provides a simplified API to run EVM calls +// EVM's sender, gasPrice, txFeeRecipient and state are set by the runner on each call +// This object can be re-used many times in contrast to the EVM's single use behaviour. +type EVMRunner interface { + // Execute performs a potentially write operation over the runner's state + // It can be seen as a message (input,value) from sender to recipient that returns `ret` + Execute(recipient common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, err error) + + // Query performs a read operation over the runner's state + // It can be seen as a message (input,value) from sender to recipient that returns `ret` + Query(recipient common.Address, input []byte, gas uint64) (ret []byte, err error) + + // StopGasMetering backward compatibility method to stop gas metering + // Deprecated. DO NOT USE + StopGasMetering() + + // StartGasMetering backward compatibility method to start gas metering + // Deprecated. DO NOT USE + StartGasMetering() +}
diff --git go-ethereum/core/vm/eips.go celo/core/vm/eips.go index 3f5a48dac727ebed83ee732f36867abfa7f8ee4b..d8129548713f50469cd8ce51920e346aa335b167 100644 --- go-ethereum/core/vm/eips.go +++ celo/core/vm/eips.go @@ -20,13 +20,13 @@ import ( "fmt" "sort"   + // "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" )   var activators = map[int]func(*JumpTable){ 3529: enable3529, - 3198: enable3198, 2929: enable2929, 2200: enable2200, 1884: enable1884, @@ -65,9 +65,11 @@ // - Increase cost of SLOAD to 800 // - Define SELFBALANCE, with cost GasFastStep (5) func enable1884(jt *JumpTable) { // Gas cost changes - jt[SLOAD].constantGas = params.SloadGasEIP1884 - jt[BALANCE].constantGas = params.BalanceGasEIP1884 - jt[EXTCODEHASH].constantGas = params.ExtcodeHashGasEIP1884 + // Celo does not include these gas changes at genesis. + // It is planned to apply them at a later date. + // jt[SLOAD].constantGas = params.SloadGasEIP1884 + // jt[BALANCE].constantGas = params.BalanceGasEIP1884 + // jt[EXTCODEHASH].constantGas = params.ExtcodeHashGasEIP1884   // New opcode jt[SELFBALANCE] = &operation{ @@ -105,7 +107,11 @@ }   // enable2200 applies EIP-2200 (Rebalance net-metered SSTORE) func enable2200(jt *JumpTable) { - jt[SLOAD].constantGas = params.SloadGasEIP2200 + // This change to SLOAD was added upstream after the Istanbul fork, to make the EIP-2200 + // implementation correct even apart from EIP-1884 (go-ethereum PR #20646). But since + // for Celo we didn't adopt the EIP-1884 gas cost changes (see above in enable1884()), + // this change here must be commented out as well, to avoid an inadvertent Celo hard fork. + // jt[SLOAD].constantGas = params.SloadGasEIP2200 jt[SSTORE].dynamicGas = gasSStoreEIP2200 }   @@ -155,22 +161,3 @@ func enable3529(jt *JumpTable) { jt[SSTORE].dynamicGas = gasSStoreEIP3529 jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529 } - -// enable3198 applies EIP-3198 (BASEFEE Opcode) -// - Adds an opcode that returns the current block's base fee. -func enable3198(jt *JumpTable) { - // New opcode - jt[BASEFEE] = &operation{ - execute: opBaseFee, - constantGas: GasQuickStep, - minStack: minStack(0, 1), - maxStack: maxStack(0, 1), - } -} - -// opBaseFee implements BASEFEE opcode -func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee) - scope.Stack.push(baseFee) - return nil, nil -}
diff --git go-ethereum/core/vm/logger_test.go celo/core/vm/logger_test.go index 9780de92050980996039d21526b5c6b9f06f1a57..545119cb4b9e7a0615190711631db7f2e2746f98 100644 --- go-ethereum/core/vm/logger_test.go +++ celo/core/vm/logger_test.go @@ -50,7 +50,7 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 }   func TestStoreCapture(t *testing.T) { var ( - env = NewEVM(BlockContext{}, TxContext{}, &dummyStatedb{}, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, &dummyStatedb{}, params.IstanbulTestChainConfig, Config{}) logger = NewStructLogger(nil) contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) scope = &ScopeContext{
diff --git go-ethereum/core/vm/instructions_test.go celo/core/vm/instructions_test.go index 70abd1ed81e3def4eb04c8404fd17bcd17de4f4d..25a93b65cd31a1d78ef49414e251a6452fcab414 100644 --- go-ethereum/core/vm/instructions_test.go +++ celo/core/vm/instructions_test.go @@ -93,7 +93,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {   var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, params.IstanbulTestChainConfig, Config{}) stack = newstack() pc = uint64(0) evmInterpreter = env.interpreter @@ -231,7 +231,7 @@ // getResult is a convenience function to generate the expected values func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, params.IstanbulTestChainConfig, Config{}) stack = newstack() pc = uint64(0) interpreter = env.interpreter @@ -281,7 +281,7 @@ }   func opBenchmark(bench *testing.B, op executionFunc, args ...string) { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, params.IstanbulTestChainConfig, Config{}) stack = newstack() evmInterpreter = NewEVMInterpreter(env, env.Config) ) @@ -515,7 +515,7 @@ }   func TestOpMstore(t *testing.T) { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, params.IstanbulTestChainConfig, Config{}) stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.Config) @@ -539,7 +539,7 @@ }   func BenchmarkOpMstore(bench *testing.B) { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, params.IstanbulTestChainConfig, Config{}) stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.Config) @@ -560,7 +560,7 @@ }   func BenchmarkOpSHA3(bench *testing.B) { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, params.IstanbulTestChainConfig, Config{}) stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.Config)
diff --git go-ethereum/core/vm/interpreter.go celo/core/vm/interpreter.go index d95ab14c202641991382c9c313fdb1c3861622ba..719c7f82d8b1e2cdcecf9af9d56c3fa68740af2f 100644 --- go-ethereum/core/vm/interpreter.go +++ celo/core/vm/interpreter.go @@ -74,10 +74,10 @@ // we'll set the default jump table. if cfg.JumpTable[STOP] == nil { var jt JumpTable switch { - case evm.chainRules.IsLondon: - jt = londonInstructionSet - case evm.chainRules.IsBerlin: - jt = berlinInstructionSet + case evm.chainRules.IsGFork: + jt = gforkInstructionSet + case evm.chainRules.IsEspresso: + jt = espressoInstructionSet case evm.chainRules.IsIstanbul: jt = istanbulInstructionSet case evm.chainRules.IsConstantinople: @@ -217,7 +217,7 @@ } } // Static portion of gas cost = operation.constantGas // For tracing - if !contract.UseGas(operation.constantGas) { + if !in.evm.dontMeterGas && !contract.UseGas(operation.constantGas) { return nil, ErrOutOfGas }   @@ -237,10 +237,13 @@ if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { return nil, ErrGasUintOverflow } } + + // Celo - don't meter gas if this is a ievmh call + // Dynamic portion of gas // consume the gas and return an error if not enough gas is available. // cost is explicitly set so that the capture state defer method can get the proper cost - if operation.dynamicGas != nil { + if !in.evm.dontMeterGas && operation.dynamicGas != nil { var dynamicCost uint64 dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) cost += dynamicCost // total cost, for debug tracing @@ -262,7 +265,11 @@ res, err = operation.execute(&pc, in, callContext) // if the operation clears the return data (e.g. it has returning data) // set the last return to the result of the operation. if operation.returns { - in.returnData = res + if in.evm.chainRules.IsChurrito && !in.evm.chainRules.IsEspresso { + in.returnData = common.CopyBytes(res) + } else { + in.returnData = res + } }   switch {
diff --git go-ethereum/core/vm/contracts.go celo/core/vm/contracts.go index 88d9d9c2b6551ea79124413c0c63241e9696e9b2..eb37dc0d3c3dd0e2ebd2e706ed614e45ecb37744 100644 --- go-ethereum/core/vm/contracts.go +++ celo/core/vm/contracts.go @@ -22,11 +22,11 @@ "encoding/binary" "errors" "math/big"   + "github.com/celo-org/celo-blockchain/crypto/bls12381" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/blake2b" - "github.com/ethereum/go-ethereum/crypto/bls12381" "github.com/ethereum/go-ethereum/crypto/bn256" "github.com/ethereum/go-ethereum/params"   @@ -38,76 +38,119 @@ // PrecompiledContract is the basic interface for native Go contracts. The implementation // requires a deterministic gas count based on the input size of the Run method of the // contract. type PrecompiledContract interface { - RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use + RequiredGas(input []byte) uint64 // RequiredGas calculates the contract gas use Run(input []byte) ([]byte, error) // Run runs the precompiled contract }   // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum // contracts used in the Frontier and Homestead releases. -var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{ - common.BytesToAddress([]byte{1}): &ecrecover{}, - common.BytesToAddress([]byte{2}): &sha256hash{}, - common.BytesToAddress([]byte{3}): &ripemd160hash{}, - common.BytesToAddress([]byte{4}): &dataCopy{}, +var PrecompiledContractsHomestead = map[common.Address]CeloPrecompiledContract{ + common.BytesToAddress([]byte{1}): &wrap{&ecrecover{}}, + common.BytesToAddress([]byte{2}): &wrap{&sha256hash{}}, + common.BytesToAddress([]byte{3}): &wrap{&ripemd160hash{}}, + common.BytesToAddress([]byte{4}): &wrap{&dataCopy{}}, }   // PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum // contracts used in the Byzantium release. -var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ - common.BytesToAddress([]byte{1}): &ecrecover{}, - common.BytesToAddress([]byte{2}): &sha256hash{}, - common.BytesToAddress([]byte{3}): &ripemd160hash{}, - common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, - common.BytesToAddress([]byte{6}): &bn256AddByzantium{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{}, - common.BytesToAddress([]byte{8}): &bn256PairingByzantium{}, +var PrecompiledContractsByzantium = map[common.Address]CeloPrecompiledContract{ + common.BytesToAddress([]byte{1}): &wrap{&ecrecover{}}, + common.BytesToAddress([]byte{2}): &wrap{&sha256hash{}}, + common.BytesToAddress([]byte{3}): &wrap{&ripemd160hash{}}, + common.BytesToAddress([]byte{4}): &wrap{&dataCopy{}}, + common.BytesToAddress([]byte{5}): &wrap{&bigModExp{eip2565: false}}, + common.BytesToAddress([]byte{6}): &wrap{&bn256AddByzantium{}}, + common.BytesToAddress([]byte{7}): &wrap{&bn256ScalarMulByzantium{}}, + common.BytesToAddress([]byte{8}): &wrap{&bn256PairingByzantium{}}, + + // Celo Precompiled Contracts + celoPrecompileAddress(5): &getValidator{}, + celoPrecompileAddress(6): &numberValidators{}, + celoPrecompileAddress(7): &epochSize{}, + celoPrecompileAddress(8): &wrap{&blockNumberFromHeader{}}, + celoPrecompileAddress(9): &wrap{&hashHeader{}}, + celoPrecompileAddress(10): &getParentSealBitmap{}, + celoPrecompileAddress(11): &getVerifiedSealBitmap{}, }   // PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum // contracts used in the Istanbul release. -var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ - common.BytesToAddress([]byte{1}): &ecrecover{}, - common.BytesToAddress([]byte{2}): &sha256hash{}, - common.BytesToAddress([]byte{3}): &ripemd160hash{}, - common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, - common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{9}): &blake2F{}, +var PrecompiledContractsIstanbul = map[common.Address]CeloPrecompiledContract{ + common.BytesToAddress([]byte{1}): &wrap{&ecrecover{}}, + common.BytesToAddress([]byte{2}): &wrap{&sha256hash{}}, + common.BytesToAddress([]byte{3}): &wrap{&ripemd160hash{}}, + common.BytesToAddress([]byte{4}): &wrap{&dataCopy{}}, + common.BytesToAddress([]byte{5}): &wrap{&bigModExp{eip2565: false}}, + common.BytesToAddress([]byte{6}): &wrap{&bn256AddIstanbul{}}, + common.BytesToAddress([]byte{7}): &wrap{&bn256ScalarMulIstanbul{}}, + common.BytesToAddress([]byte{8}): &wrap{&bn256PairingIstanbul{}}, + common.BytesToAddress([]byte{9}): &wrap{&blake2F{}}, + + // Celo Precompiled Contracts + celoPrecompileAddress(5): &getValidator{}, + celoPrecompileAddress(6): &numberValidators{}, + celoPrecompileAddress(7): &epochSize{}, + celoPrecompileAddress(8): &wrap{&blockNumberFromHeader{}}, + celoPrecompileAddress(9): &wrap{&hashHeader{}}, + celoPrecompileAddress(10): &getParentSealBitmap{}, + celoPrecompileAddress(11): &getVerifiedSealBitmap{}, +} + +// PrecompiledContractsDonut contains the default set of pre-compiled Ethereum +// contracts used in the Donit release. +var PrecompiledContractsDonut = map[common.Address]CeloPrecompiledContract{ + common.BytesToAddress([]byte{1}): &wrap{&ecrecover{}}, + common.BytesToAddress([]byte{2}): &wrap{&sha256hash{}}, + common.BytesToAddress([]byte{3}): &wrap{&ripemd160hash{}}, + common.BytesToAddress([]byte{4}): &wrap{&dataCopy{}}, + common.BytesToAddress([]byte{5}): &wrap{&bigModExp{eip2565: false}}, + common.BytesToAddress([]byte{6}): &wrap{&bn256AddIstanbul{}}, + common.BytesToAddress([]byte{7}): &wrap{&bn256ScalarMulIstanbul{}}, + common.BytesToAddress([]byte{8}): &wrap{&bn256PairingIstanbul{}}, + common.BytesToAddress([]byte{9}): &wrap{&blake2F{}}, + + // Celo Precompiled Contracts + celoPrecompileAddress(5): &getValidator{}, + celoPrecompileAddress(6): &numberValidators{}, + celoPrecompileAddress(7): &epochSize{}, + celoPrecompileAddress(8): &wrap{&blockNumberFromHeader{}}, + celoPrecompileAddress(9): &wrap{&hashHeader{}}, + celoPrecompileAddress(10): &getParentSealBitmap{}, + celoPrecompileAddress(11): &getVerifiedSealBitmap{}, + + // New in Donut hard fork + celoPrecompileAddress(30): &getValidatorBLS{}, }   // PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum // contracts used in the Berlin release. -var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{ - common.BytesToAddress([]byte{1}): &ecrecover{}, - common.BytesToAddress([]byte{2}): &sha256hash{}, - common.BytesToAddress([]byte{3}): &ripemd160hash{}, - common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{9}): &blake2F{}, -} +var PrecompiledContractsE = map[common.Address]CeloPrecompiledContract{ + common.BytesToAddress([]byte{1}): &wrap{&ecrecover{}}, + common.BytesToAddress([]byte{2}): &wrap{&sha256hash{}}, + common.BytesToAddress([]byte{3}): &wrap{&ripemd160hash{}}, + common.BytesToAddress([]byte{4}): &wrap{&dataCopy{}}, + common.BytesToAddress([]byte{5}): &wrap{&bigModExp{eip2565: true}}, + common.BytesToAddress([]byte{6}): &wrap{&bn256AddIstanbul{}}, + common.BytesToAddress([]byte{7}): &wrap{&bn256ScalarMulIstanbul{}}, + common.BytesToAddress([]byte{8}): &wrap{&bn256PairingIstanbul{}}, + common.BytesToAddress([]byte{9}): &wrap{&blake2F{}},   -// PrecompiledContractsBLS contains the set of pre-compiled Ethereum -// contracts specified in EIP-2537. These are exported for testing purposes. -var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ - common.BytesToAddress([]byte{10}): &bls12381G1Add{}, - common.BytesToAddress([]byte{11}): &bls12381G1Mul{}, - common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{}, - common.BytesToAddress([]byte{13}): &bls12381G2Add{}, - common.BytesToAddress([]byte{14}): &bls12381G2Mul{}, - common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{}, - common.BytesToAddress([]byte{16}): &bls12381Pairing{}, - common.BytesToAddress([]byte{17}): &bls12381MapG1{}, - common.BytesToAddress([]byte{18}): &bls12381MapG2{}, + // Celo Precompiled Contracts + celoPrecompileAddress(5): &getValidator{}, + celoPrecompileAddress(6): &numberValidators{}, + celoPrecompileAddress(7): &epochSize{}, + celoPrecompileAddress(8): &wrap{&blockNumberFromHeader{}}, + celoPrecompileAddress(9): &wrap{&hashHeader{}}, + celoPrecompileAddress(10): &getParentSealBitmap{}, + celoPrecompileAddress(11): &getVerifiedSealBitmap{}, + + // New in Donut hard fork + celoPrecompileAddress(30): &getValidatorBLS{}, }   var ( - PrecompiledAddressesBerlin []common.Address + PrecompiledAddressesE []common.Address + PrecompiledAddressesDonut []common.Address PrecompiledAddressesIstanbul []common.Address PrecompiledAddressesByzantium []common.Address PrecompiledAddressesHomestead []common.Address @@ -123,16 +166,21 @@ } for k := range PrecompiledContractsIstanbul { PrecompiledAddressesIstanbul = append(PrecompiledAddressesIstanbul, k) } - for k := range PrecompiledContractsBerlin { - PrecompiledAddressesBerlin = append(PrecompiledAddressesBerlin, k) + for k := range PrecompiledContractsDonut { + PrecompiledAddressesDonut = append(PrecompiledAddressesDonut, k) + } + for k := range PrecompiledContractsE { + PrecompiledAddressesE = append(PrecompiledAddressesE, k) } }   // ActivePrecompiles returns the precompiles enabled with the current configuration. func ActivePrecompiles(rules params.Rules) []common.Address { switch { - case rules.IsBerlin: - return PrecompiledAddressesBerlin + case rules.IsEspresso: + return PrecompiledAddressesE + case rules.IsDonut: + return PrecompiledAddressesDonut case rules.IsIstanbul: return PrecompiledAddressesIstanbul case rules.IsByzantium: @@ -147,13 +195,13 @@ // It returns // - the returned bytes, // - the _remaining_ gas, // - any error that occurred -func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) { +func RunPrecompiledContract(p CeloPrecompiledContract, input []byte, suppliedGas uint64, ctx *celoPrecompileContext) (ret []byte, remainingGas uint64, err error) { gasCost := p.RequiredGas(input) if suppliedGas < gasCost { return nil, 0, ErrOutOfGas } suppliedGas -= gasCost - output, err := p.Run(input) + output, err := p.Run(input, ctx) return output, suppliedGas, err }   @@ -266,9 +314,10 @@ // modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198 // // def mult_complexity(x): -// if x <= 64: return x ** 2 -// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 -// else: return x ** 2 // 16 + 480 * x - 199680 +// +// if x <= 64: return x ** 2 +// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 +// else: return x ** 2 // 16 + 480 * x - 199680 // // where is x is max(length_of_MODULUS, length_of_BASE) func modexpMultComplexity(x *big.Int) *big.Int {
diff --git go-ethereum/core/vm/vmcontext/context.go celo/core/vm/vmcontext/context.go new file mode 100644 index 0000000000000000000000000000000000000000..894dabfce29200b084669d455e92112d2b4f0013 --- /dev/null +++ celo/core/vm/vmcontext/context.go @@ -0,0 +1,129 @@ +package vmcontext + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" +) + +// chainContext defines methods required to build a context +// a copy of this exist on core.ChainContext (needed to break dependency) +type chainContext interface { + // Engine retrieves the blockchain's consensus engine. + Engine() consensus.Engine + + // GetHeader returns the hash corresponding to the given hash and number. + GetHeader(common.Hash, uint64) *types.Header + + // GetHeaderByNumber returns the hash corresponding number. + // in the correct fork. + GetHeaderByNumber(uint64) *types.Header + + // Config returns the blockchain's chain configuration + Config() *params.ChainConfig +} + +// New creates a new context for use in the EVM. +func NewBlockContext(header *types.Header, chain chainContext, txFeeRecipient *common.Address) vm.BlockContext { + // If we don't have an explicit txFeeRecipient (i.e. not mining), extract from the header + // The only call that fills the txFeeRecipient, is the ApplyTransaction from the state processor + // All the other calls, assume that will be retrieved from the header + var beneficiary common.Address + if txFeeRecipient == nil { + beneficiary = header.Coinbase + } else { + beneficiary = *txFeeRecipient + } + + ctx := vm.BlockContext{ + CanTransfer: CanTransfer, + Transfer: Transfer, + GetHash: GetHashFn(header, chain), + VerifySeal: VerifySealFn(header, chain), + Coinbase: beneficiary, + BlockNumber: new(big.Int).Set(header.Number), + Time: new(big.Int).SetUint64(header.Time), + } + + if chain != nil { + ctx.EpochSize = chain.Engine().EpochSize() + ctx.GetValidators = chain.Engine().GetValidators + ctx.GetHeaderByNumber = chain.GetHeaderByNumber + } else { + ctx.GetValidators = func(blockNumber *big.Int, headerHash common.Hash) []istanbul.Validator { return nil } + ctx.GetHeaderByNumber = func(uint64) *types.Header { panic("evm context without blockchain context") } + } + return ctx +} + +// GetHashFn returns a GetHashFunc which retrieves header hashes by number +func GetHashFn(ref *types.Header, chain chainContext) func(uint64) common.Hash { + // Cache will initially contain [refHash.parent], + // Then fill up with [refHash.p, refHash.pp, refHash.ppp, ...] + var cache []common.Hash + + return func(n uint64) common.Hash { + // If there's no hash cache yet, make one + if len(cache) == 0 { + cache = append(cache, ref.ParentHash) + } + if idx := ref.Number.Uint64() - n - 1; idx < uint64(len(cache)) { + return cache[idx] + } + // No luck in the cache, but we can start iterating from the last element we already know + lastKnownHash := cache[len(cache)-1] + lastKnownNumber := ref.Number.Uint64() - uint64(len(cache)) + + for { + header := chain.GetHeader(lastKnownHash, lastKnownNumber) + if header == nil { + break + } + cache = append(cache, header.ParentHash) + lastKnownHash = header.ParentHash + lastKnownNumber = header.Number.Uint64() - 1 + if n == lastKnownNumber { + return lastKnownHash + } + } + return common.Hash{} + } +} + +// CanTransfer checks whether there are enough funds in the address' account to make a transfer. +// This does not take the necessary gas into account to make the transfer valid. +func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { + return db.GetBalance(addr).Cmp(amount) >= 0 +} + +// Transfer subtracts amount from sender and adds amount to recipient using the given Db +func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { + db.SubBalance(sender, amount) + db.AddBalance(recipient, amount) +} + +// VerifySealFn returns a function which returns true when the given header has a verifiable seal. +func VerifySealFn(ref *types.Header, chain chainContext) func(*types.Header) bool { + return func(header *types.Header) bool { + // If the block is later than the unsealed reference block, return false. + if header.Number.Cmp(ref.Number) > 0 { + return false + } + + // FIXME: Implementation currently relies on the Istanbul engine's internal view of the + // chain, so return false if this is not an Istanbul chain. As a consequence of this the + // seal is always verified against the canonical chain, which makes behavior undefined if + // this function is evaluated on a chain which does not have the highest total difficulty. + if chain.Config().Istanbul == nil { + return false + } + + // Submit the header to the engine's seal verification function. + return chain.Engine().VerifySeal(header) == nil + } +}
diff --git go-ethereum/core/vm/runtime/env.go celo/core/vm/runtime/env.go index c1cff03a826e223549b90f5aea5a005817dd6cb5..4d9c441386c5bc616ad90c84e4740f872744f9b3 100644 --- go-ethereum/core/vm/runtime/env.go +++ celo/core/vm/runtime/env.go @@ -17,8 +17,8 @@ package runtime   import ( - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/vmcontext" )   func NewEnv(cfg *Config) *vm.EVM { @@ -27,15 +27,17 @@ Origin: cfg.Origin, GasPrice: cfg.GasPrice, } blockContext := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, + CanTransfer: vmcontext.CanTransfer, + Transfer: vmcontext.Transfer, GetHash: cfg.GetHashFn, Coinbase: cfg.Coinbase, BlockNumber: cfg.BlockNumber, Time: cfg.Time, - Difficulty: cfg.Difficulty, - GasLimit: cfg.GasLimit, - BaseFee: cfg.BaseFee, + // BaseFee: cfg.BaseFee, // TODO: Set to GPM + } + + if cfg.ChainConfig.Istanbul != nil { + blockContext.EpochSize = cfg.ChainConfig.Istanbul.Epoch }   return vm.NewEVM(blockContext, txContext, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
diff --git go-ethereum/core/vm/vmcontext/runner.go celo/core/vm/vmcontext/runner.go new file mode 100644 index 0000000000000000000000000000000000000000..2e12f65ea67a61116644a96aab26b471f23e2f45 --- /dev/null +++ celo/core/vm/vmcontext/runner.go @@ -0,0 +1,90 @@ +package vmcontext + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" +) + +// VMAddress is the address the VM uses to make internal calls to contracts +var VMAddress = common.ZeroAddress + +// evmRunnerContext defines methods required to create an EVMRunner +type evmRunnerContext interface { + chainContext + + // GetVMConfig returns the node's vm configuration + GetVMConfig() *vm.Config +} + +type evmRunner struct { + newEVM func(from common.Address) *vm.EVM + state vm.StateDB + + dontMeterGas bool +} + +func NewEVMRunner(chain evmRunnerContext, header *types.Header, state vm.StateDB) vm.EVMRunner { + + return &evmRunner{ + state: state, + newEVM: func(from common.Address) *vm.EVM { + // The EVM Context requires a msg, but the actual field values don't really matter for this case. + // Putting in zero values for gas price and tx fee recipient + blockContext := NewBlockContext(header, chain, nil) + txContext := vm.TxContext{ + Origin: from, + GasPrice: common.Big0, + } + return vm.NewEVM(blockContext, txContext, state, chain.Config(), *chain.GetVMConfig()) + }, + } +} + +func (ev *evmRunner) Execute(recipient common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, err error) { + evm := ev.newEVM(VMAddress) + if ev.dontMeterGas { + evm.StopGasMetering() + } + ret, _, err = evm.Call(vm.AccountRef(evm.Origin), recipient, input, gas, value) + return ret, err +} + +func (ev *evmRunner) Query(recipient common.Address, input []byte, gas uint64) (ret []byte, err error) { + evm := ev.newEVM(VMAddress) + if ev.dontMeterGas { + evm.StopGasMetering() + } + ret, _, err = evm.StaticCall(vm.AccountRef(evm.Origin), recipient, input, gas) + return ret, err +} + +func (ev *evmRunner) StopGasMetering() { + ev.dontMeterGas = true +} + +func (ev *evmRunner) StartGasMetering() { + ev.dontMeterGas = false +} + +// GetStateDB implements Backend.GetStateDB +func (ev *evmRunner) GetStateDB() vm.StateDB { + return ev.state +} + +// SharedEVMRunner is an evm runner that REUSES an evm +// This MUST NOT BE USED, but it's here for backward compatibility +// purposes +type SharedEVMRunner struct{ *vm.EVM } + +func (sev *SharedEVMRunner) Execute(recipient common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, err error) { + ret, _, err = sev.Call(vm.AccountRef(VMAddress), recipient, input, gas, value) + return ret, err +} + +func (sev *SharedEVMRunner) Query(recipient common.Address, input []byte, gas uint64) (ret []byte, err error) { + ret, _, err = sev.StaticCall(vm.AccountRef(VMAddress), recipient, input, gas) + return ret, err +}
diff --git go-ethereum/core/vm/runtime/runtime.go celo/core/vm/runtime/runtime.go index 990e2d2a14cb19bdca6d23b2cc8db8f27cf29f2c..4156ae5af001c59a1866a207f7ec26b598a8b8ec 100644 --- go-ethereum/core/vm/runtime/runtime.go +++ celo/core/vm/runtime/runtime.go @@ -33,7 +33,6 @@ // Config is a basic type specifying certain configuration flags for running // the EVM. type Config struct { ChainConfig *params.ChainConfig - Difficulty *big.Int Origin common.Address Coinbase common.Address BlockNumber *big.Int @@ -65,15 +64,12 @@ ByzantiumBlock: new(big.Int), ConstantinopleBlock: new(big.Int), PetersburgBlock: new(big.Int), IstanbulBlock: new(big.Int), - MuirGlacierBlock: new(big.Int), - BerlinBlock: new(big.Int), - LondonBlock: new(big.Int), + ChurritoBlock: new(big.Int), + DonutBlock: new(big.Int), + EspressoBlock: new(big.Int), + GForkBlock: new(big.Int), } } - - if cfg.Difficulty == nil { - cfg.Difficulty = new(big.Int) - } if cfg.Time == nil { cfg.Time = big.NewInt(time.Now().Unix()) } @@ -94,9 +90,6 @@ cfg.GetHashFn = func(n uint64) common.Hash { return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String()))) } } - if cfg.BaseFee == nil { - cfg.BaseFee = big.NewInt(params.InitialBaseFee) - } }   // Execute executes the code using the input as call data during the execution. @@ -118,7 +111,7 @@ address = common.BytesToAddress([]byte("contract")) vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin { + if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEspresso { cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) } cfg.State.CreateAccount(address) @@ -150,7 +143,8 @@ var ( vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin { + + if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEspresso { cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil) } // Call the code with the given configuration. @@ -176,7 +170,7 @@ sender := cfg.State.GetOrNewStateObject(cfg.Origin) statedb := cfg.State   - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin { + if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEspresso { statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) } // Call the code with the given configuration.
diff --git go-ethereum/core/vm/runtime/runtime_test.go celo/core/vm/runtime/runtime_test.go index afdc3dd44db7c6698c5347df5b85e201c6f41eb4..153c8d353211422d81f2f69299c3a845979c99e9 100644 --- go-ethereum/core/vm/runtime/runtime_test.go +++ celo/core/vm/runtime/runtime_test.go @@ -27,12 +27,12 @@ "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/asm" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/vmcontext" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/params" ) @@ -40,10 +40,6 @@ func TestDefaults(t *testing.T) { cfg := new(Config) setDefaults(cfg) - - if cfg.Difficulty == nil { - t.Error("expected difficulty to be non nil") - }   if cfg.Time == nil { t.Error("expected time to be non nil") @@ -73,9 +69,7 @@ } }()   Execute([]byte{ - byte(vm.DIFFICULTY), byte(vm.TIMESTAMP), - byte(vm.GASLIMIT), byte(vm.PUSH1), byte(vm.ORIGIN), byte(vm.BLOCKHASH), @@ -170,7 +164,6 @@ runtimeConfig := Config{ Origin: sender, State: statedb, GasLimit: 10000000, - Difficulty: big.NewInt(0x200000), Time: new(big.Int).SetUint64(0), Coinbase: common.Address{}, BlockNumber: new(big.Int).SetUint64(1), @@ -218,10 +211,7 @@ Coinbase: common.HexToAddress("0x00000000000000000000000000000000deadbeef"), Number: big.NewInt(int64(n)), ParentHash: parentHash, Time: 1000, - Nonce: types.BlockNonce{0x1}, Extra: []byte{}, - Difficulty: big.NewInt(0), - GasLimit: 100000, } return &header } @@ -247,6 +237,29 @@ //fmt.Printf("GetHeader(%x, %d) => header with parent %x\n", h, n, parentHash) return fakeHeader(n, parentHash) }   +// GetHeaderByNumber returns the hash corresponding number. +func (d *dummyChain) GetHeaderByNumber(number uint64) *types.Header { + return d.GetHeader(common.Hash{}, number) +} + +// GetVMConfig returns the node's vm configuration +func (d *dummyChain) GetVMConfig() *vm.Config { + return nil +} + +func (d *dummyChain) CurrentHeader() *types.Header { + return nil +} + +func (d *dummyChain) State() (*state.StateDB, error) { + return nil, nil +} + +// Config returns the blockchain's chain configuration +func (d *dummyChain) Config() *params.ChainConfig { + return nil +} + // TestBlockhash tests the blockhash operation. It's a bit special, since it internally // requires access to a chain reader. func TestBlockhash(t *testing.T) { @@ -295,7 +308,7 @@ // The method call to 'test()' input := common.Hex2Bytes("f8a8fd6d") chain := &dummyChain{} ret, _, err := Execute(data, input, &Config{ - GetHashFn: core.GetHashFn(header, chain), + GetHashFn: vmcontext.GetHashFn(header, chain), BlockNumber: new(big.Int).Set(header.Number), }) if err != nil { @@ -339,6 +352,7 @@ func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { s.steps++ // Enable this for more output //s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err) + }   // benchmarkNonModifyingCode benchmarks code, but if the code modifies the @@ -632,12 +646,12 @@ }{ { // EXTCODEHASH(0xff) code: []byte{byte(vm.PUSH1), 0xFF, byte(vm.EXTCODEHASH), byte(vm.POP)}, step: 1, - want: 2600, + want: 900, }, { // BALANCE(0xff) code: []byte{byte(vm.PUSH1), 0xFF, byte(vm.BALANCE), byte(vm.POP)}, step: 1, - want: 2600, + want: 900, }, { // CALL(0xff) code: []byte{ @@ -646,7 +660,7 @@ byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALL), byte(vm.POP), }, step: 7, - want: 2855, + want: 1155, }, { // CALLCODE(0xff) code: []byte{ @@ -655,7 +669,7 @@ byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALLCODE), byte(vm.POP), }, step: 7, - want: 2855, + want: 1155, }, { // DELEGATECALL(0xff) code: []byte{ @@ -664,7 +678,7 @@ byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.DELEGATECALL), byte(vm.POP), }, step: 6, - want: 2855, + want: 1155, }, { // STATICCALL(0xff) code: []byte{ @@ -673,14 +687,14 @@ byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.STATICCALL), byte(vm.POP), }, step: 6, - want: 2855, + want: 1155, }, { // SELFDESTRUCT(0xff) code: []byte{ byte(vm.PUSH1), 0xff, byte(vm.SELFDESTRUCT), }, step: 1, - want: 7600, + want: 5900, }, } { tracer := vm.NewStructLogger(nil) @@ -695,7 +709,7 @@ if want := tc.want; have != want { for ii, op := range tracer.StructLogs() { t.Logf("%d: %v %d", ii, op.OpName(), op.GasCost) } - t.Fatalf("tescase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want) + t.Fatalf("test case %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want) } } } @@ -772,12 +786,14 @@ code: []byte{ // outsize, outoffset, insize, inoffset byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, // value - byte(vm.PUSH1), 0xbb, //address + byte(vm.PUSH1), 0xab, //address byte(vm.GAS), // gas byte(vm.CALL), byte(vm.POP), }, - results: []string{`"1,1,4294964716,6,13"`, `"1,1,4294964716,6,0"`}, + // Different gas value from upstream due to (#1712): + // Links: https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0048.md + results: []string{`"1,1,4294966390,6,13"`, `"1,1,4294966390,6,0"`}, }, { // CALLCODE @@ -785,36 +801,42 @@ code: []byte{ // outsize, outoffset, insize, inoffset byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, // value - byte(vm.PUSH1), 0xcc, //address + byte(vm.PUSH1), 0xac, //address byte(vm.GAS), // gas byte(vm.CALLCODE), byte(vm.POP), }, - results: []string{`"1,1,4294964716,6,13"`, `"1,1,4294964716,6,0"`}, + // Different gas value from upstream due to (#1712): + // Links: https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0048.md + results: []string{`"1,1,4294966390,6,13"`, `"1,1,4294966390,6,0"`}, }, { // STATICCALL code: []byte{ // outsize, outoffset, insize, inoffset byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0xdd, //address + byte(vm.PUSH1), 0xad, //address byte(vm.GAS), // gas byte(vm.STATICCALL), byte(vm.POP), }, - results: []string{`"1,1,4294964719,6,12"`, `"1,1,4294964719,6,0"`}, + // Different gas value from upstream due to (#1712): + // Links: https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0048.md + results: []string{`"1,1,4294966393,6,12"`, `"1,1,4294966393,6,0"`}, }, { // DELEGATECALL code: []byte{ // outsize, outoffset, insize, inoffset byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0xee, //address + byte(vm.PUSH1), 0xae, //address byte(vm.GAS), // gas byte(vm.DELEGATECALL), byte(vm.POP), }, - results: []string{`"1,1,4294964719,6,12"`, `"1,1,4294964719,6,0"`}, + // Different gas value from upstream due to (#1712): + // Links: https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0048.md + results: []string{`"1,1,4294966393,6,12"`, `"1,1,4294966393,6,0"`}, }, { // CALL self-destructing contract @@ -822,7 +844,7 @@ code: []byte{ // outsize, outoffset, insize, inoffset byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, // value - byte(vm.PUSH1), 0xff, //address + byte(vm.PUSH1), 0xaf, //address byte(vm.GAS), // gas byte(vm.CALL), byte(vm.POP), @@ -844,11 +866,13 @@ for i, jsTracer := range jsTracers { for j, tc := range tests { statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) statedb.SetCode(main, tc.code) - statedb.SetCode(common.HexToAddress("0xbb"), calleeCode) - statedb.SetCode(common.HexToAddress("0xcc"), calleeCode) - statedb.SetCode(common.HexToAddress("0xdd"), calleeCode) - statedb.SetCode(common.HexToAddress("0xee"), calleeCode) - statedb.SetCode(common.HexToAddress("0xff"), depressedCode) + // Addresses changed from upstream to avoid colliding with the + // celo precompiled contracts. + statedb.SetCode(common.HexToAddress("0xab"), calleeCode) + statedb.SetCode(common.HexToAddress("0xac"), calleeCode) + statedb.SetCode(common.HexToAddress("0xad"), calleeCode) + statedb.SetCode(common.HexToAddress("0xae"), calleeCode) + statedb.SetCode(common.HexToAddress("0xaf"), depressedCode)   tracer, err := tracers.New(jsTracer, new(tracers.Context)) if err != nil {
diff --git go-ethereum/core/vm/testdata/precompiles/getValidatorBLSPublicKey.json celo/core/vm/testdata/precompiles/getValidatorBLSPublicKey.json new file mode 100644 index 0000000000000000000000000000000000000000..d69a812bba613e806c7a0a5f5a67a66f8cb31634 --- /dev/null +++ celo/core/vm/testdata/precompiles/getValidatorBLSPublicKey.json @@ -0,0 +1,41 @@ +[ + { + "Input": "", + "Expected": "invalid input length", + "Name": "input_invalid_empty", + "ErrorExpected": true + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "validator index out of bounds", + "Name": "invalid_genesis_block", + "ErrorExpected": true + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff", + "Expected": "00000000000000000000000000000000001fd78b7dee6db285d05c16aedcc6f33652607220efb1ab99d512c77d3dd77dba78c81d4190b4a633720d92c1a577f9000000000000000000000000000000000021bd7f370c65fba1afbcf16e256a82b6cdfea0a27d8df313edd1a881f41780609ee055b78974b1c8d97dac3c9461c300000000000000000000000000000000016366c10606045d43fb18300560995394a24bd1358b9210540f4ec29fdaaf8506af7651a8504230734a62b6599f744c0000000000000000000000000000000000209e3562ca2dc97e0182cd354662ff20bc2f312866b4655c3eed0af045de4133ab8b5f5a4cde9b2f12113e47d1157e", + "Name": "correct_block_0xff_index_0x0" + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000ff", + "Expected": "00000000000000000000000000000000015398774c6805e5f7edc1cd2ce40c44bd5b430caa3dfefe740b12efa1708bb20aeadce71d5bfe288158b87f07dc82c700000000000000000000000000000000000d3dca6f108fa022d1e106db969e36622eaec7b2a4b3eefa94aee9982a0133ca09a86b142b71fab14c770e7e70763a0000000000000000000000000000000000ac437ff49e987559a9c9ec9a421d46f6b1044b2888e4c23653827e94b9c3729c897fc02db7364694f8bc5e783529c30000000000000000000000000000000000bc9531ebddea1c9ca9a08c0eafd7d0d734d17d54150d75c5d3d0ab3d416807693da02dababe8ef734a50083c2ef889", + "Name": "correct_block_0xff_index_0xa" + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000ff", + "Expected": "validator index out of bounds", + "Name": "invalid_index_out_of_bounds", + "ErrorExpected": true + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002710", + "Expected": "000000000000000000000000000000000173d1ea9889757e8f176737629c7b6c446a6832ddec53562ebe0cda09455792927597a3025dd613021de96ebc2ecc130000000000000000000000000000000000866721e8b88328b0df9e6c52bd9bdcce076d9febeba542cf7d48e7fae54bbe783c785131678fb3f15c665f5c1fd1c500000000000000000000000000000000008a6412232130f837d31ce5223e088e338997fad1de25908ae7e4b65bfe2fe4839dcd41f5ae613d146796d98d65255c000000000000000000000000000000000039d57467b392c8c30b81e961469ff750a3473ffa423c12f671264ea9967f5bfd21b0247d02e132cdfd2682cf207ff9", + "Name": "correct_chain_head" + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002711", + "Expected": "block number out of bounds", + "Name": "invalid_future_block", + "ErrorExpected": true + } +] \ No newline at end of file
diff --git go-ethereum/core/vm/testdata/precompiles/hashHeader.json celo/core/vm/testdata/precompiles/hashHeader.json new file mode 100644 index 0000000000000000000000000000000000000000..f174de3253ebd174d577be1848fc5aeb615fd54e --- /dev/null +++ celo/core/vm/testdata/precompiles/hashHeader.json @@ -0,0 +1,19 @@ +[ + { + "Input": "", + "Expected": "unable to decode input", + "Name": "input_invalid_empty", + "ErrorExpected": true + }, + { + "Input": "f901a6a07285abd5b24742f184ad676e31f6054663b3529bc35ea2fcad8a3e0f642a46f7948888f1f195afa192cfee860698584c030f4c9db1a0ecc60e00b3fe5ce9f6e1a10e5469764daf51f1fe93c22ec3f9a7583a80357217a0d35d334d87c0cc0a202e3756bf81fae08b1575f286c7ee7a3f8df4f0f3afc55da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001825208845c47775c8", + "Expected": "unable to decode input", + "Name": "input_invalid_removed_byte", + "ErrorExpected": true + }, + { + "Input": "f901a6a07285abd5b24742f184ad676e31f6054663b3529bc35ea2fcad8a3e0f642a46f7948888f1f195afa192cfee860698584c030f4c9db1a0ecc60e00b3fe5ce9f6e1a10e5469764daf51f1fe93c22ec3f9a7583a80357217a0d35d334d87c0cc0a202e3756bf81fae08b1575f286c7ee7a3f8df4f0f3afc55da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001825208845c47775c80", + "Expected": "2e14ef428293e41c5f81a108b5d36f892b2bee3e34aec4223474c4a31618ea69", + "Name": "correct_hash" + } +] \ No newline at end of file
diff --git go-ethereum/core/vm/testdata/precompiles/numberValidators.json celo/core/vm/testdata/precompiles/numberValidators.json new file mode 100644 index 0000000000000000000000000000000000000000..8a303b827f5f3e221d645e4ca6c7b8615a29191f --- /dev/null +++ celo/core/vm/testdata/precompiles/numberValidators.json @@ -0,0 +1,29 @@ +[ + { + "Input": "", + "Expected": "invalid input length", + "Name": "input_invalid_empty", + "ErrorExpected": true + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000000", + "Expected": "0000000000000000000000000000000000000000000000000000000000000000", + "Name": "correct_genesis_block" + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000ff", + "Expected": "0000000000000000000000000000000000000000000000000000000000000010", + "Name": "correct_block_0xff" + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000002710", + "Expected": "0000000000000000000000000000000000000000000000000000000000000010", + "Name": "correct_chain_head" + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000002711", + "Expected": "block number out of bounds", + "Name": "invalid_future_block", + "ErrorExpected": true + } +] \ No newline at end of file
diff --git go-ethereum/core/vm/testdata/precompiles/getValidator.json celo/core/vm/testdata/precompiles/getValidator.json new file mode 100644 index 0000000000000000000000000000000000000000..8edf09d66a8130dbeaf831cbe67c53c293c95c4d --- /dev/null +++ celo/core/vm/testdata/precompiles/getValidator.json @@ -0,0 +1,41 @@ +[ + { + "Input": "", + "Expected": "invalid input length", + "Name": "input_invalid_empty", + "ErrorExpected": true + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "validator index out of bounds", + "Name": "invalid_genesis_block", + "ErrorExpected": true + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff", + "Expected": "000000000000000000000000fbb697cf00d3de24bc81225b4b2a9e7e763f8136", + "Name": "correct_block_0xff_index_0x0" + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000ff", + "Expected": "0000000000000000000000003e70ba60978582c2770aa95579e542a521456668", + "Name": "correct_block_0xff_index_0xa" + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000ff", + "Expected": "validator index out of bounds", + "Name": "invalid_index_out_of_bounds", + "ErrorExpected": true + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002710", + "Expected": "000000000000000000000000a7fdb33d85f63259ff56133b5be795ef669a5756", + "Name": "correct_chain_head" + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002711", + "Expected": "block number out of bounds", + "Name": "invalid_future_block", + "ErrorExpected": true + } +] \ No newline at end of file
diff --git go-ethereum/core/vm/testdata/precompiles/blockNumberFromHeader.json celo/core/vm/testdata/precompiles/blockNumberFromHeader.json new file mode 100644 index 0000000000000000000000000000000000000000..045ab4f1f05fbcd3a6d5cfc42baa71c44baebdc1 --- /dev/null +++ celo/core/vm/testdata/precompiles/blockNumberFromHeader.json @@ -0,0 +1,19 @@ +[ + { + "Input": "", + "Expected": "unable to decode input", + "Name": "input_invalid_empty", + "ErrorExpected": true + }, + { + "Input": "f901a6a07285abd5b24742f184ad676e31f6054663b3529bc35ea2fcad8a3e0f642a46f7948888f1f195afa192cfee860698584c030f4c9db1a0ecc60e00b3fe5ce9f6e1a10e5469764daf51f1fe93c22ec3f9a7583a80357217a0d35d334d87c0cc0a202e3756bf81fae08b1575f286c7ee7a3f8df4f0f3afc55da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001825208845c47775c8", + "Expected": "unable to decode input", + "Name": "input_invalid_removed_byte", + "ErrorExpected": true + }, + { + "Input": "f901a6a07285abd5b24742f184ad676e31f6054663b3529bc35ea2fcad8a3e0f642a46f7948888f1f195afa192cfee860698584c030f4c9db1a0ecc60e00b3fe5ce9f6e1a10e5469764daf51f1fe93c22ec3f9a7583a80357217a0d35d334d87c0cc0a202e3756bf81fae08b1575f286c7ee7a3f8df4f0f3afc55da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001825208845c47775c80", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "correct_number" + } +] \ No newline at end of file
diff --git go-ethereum/core/vm/testdata/precompiles/getParentSealBitmap.json celo/core/vm/testdata/precompiles/getParentSealBitmap.json new file mode 100644 index 0000000000000000000000000000000000000000..60b10c5940a58e729f78077a0cc019a34058b4dc --- /dev/null +++ celo/core/vm/testdata/precompiles/getParentSealBitmap.json @@ -0,0 +1,36 @@ +[ + { + "Input": "", + "Expected": "invalid input length", + "Name": "input_invalid_empty", + "ErrorExpected": true + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000000", + "Expected": "block number out of bounds", + "Name": "invalid_genesis_block", + "ErrorExpected": true + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000002580", + "Expected": "block number out of bounds", + "Name": "invalid_outside_history_limit", + "ErrorExpected": true + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000002581", + "Expected": "000000000000000000000000000000000000000000000000000000000000645c", + "Name": "correct_last_block_in_history_limit" + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000002711", + "Expected": "block number out of bounds", + "Name": "invalid_chain_head_child", + "ErrorExpected": true + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000002710", + "Expected": "0000000000000000000000000000000000000000000000000000000000007ff0", + "Name": "correct_chain_head" + } +] \ No newline at end of file
diff --git go-ethereum/miner/miner.go celo/miner/miner.go index a273a3b25078d5d4586ac466da838eb3e1715e0a..d4efb0a1681c48934cd8cf2b18f466d76eeca049 100644 --- go-ethereum/miner/miner.go +++ celo/miner/miner.go @@ -19,16 +19,17 @@ package miner   import ( "fmt" - "math/big" - "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/contracts/random" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -42,44 +43,77 @@ }   // Config is the configuration parameters of mining. type Config struct { - Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards (default = first account) - Notify []string `toml:",omitempty"` // HTTP URL list to be notified of new work packages (only useful in ethash). - NotifyFull bool `toml:",omitempty"` // Notify with pending block headers instead of work packages + Validator common.Address `toml:",omitempty"` // Public address for block signing and randomness (default = first account) ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner - GasFloor uint64 // Target gas floor for mined blocks. - GasCeil uint64 // Target gas ceiling for mined blocks. - GasPrice *big.Int // Minimum gas price for mining a transaction - Recommit time.Duration // The time interval for miner to re-create mining work. - Noverify bool // Disable remote mining solution verification(only useful in ethash). }   // Miner creates blocks and searches for proof-of-work values. type Miner struct { mux *event.TypeMux worker *worker - coinbase common.Address + validator common.Address eth Backend engine consensus.Engine + db ethdb.Database // Needed for randomness + exitCh chan struct{} - startCh chan common.Address + startCh chan struct{} stopCh chan struct{} }   -func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(block *types.Block) bool) *Miner { +func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, db ethdb.Database) *Miner { miner := &Miner{ eth: eth, mux: mux, engine: engine, exitCh: make(chan struct{}), - startCh: make(chan common.Address), + startCh: make(chan struct{}), stopCh: make(chan struct{}), - worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true), + worker: newWorker(config, chainConfig, engine, eth, mux, db), + db: db, } go miner.update()   return miner }   +func (miner *Miner) recoverRandomness() { + // If this is using the istanbul consensus engine, then we need to check + // for the randomness cache for the randomness beacon protocol + _, isIstanbul := miner.engine.(consensus.Istanbul) + if isIstanbul { + // getCurrentBlockAndState + currentBlock := miner.eth.BlockChain().CurrentBlock() + currentHeader := currentBlock.Header() + currentState, err := miner.eth.BlockChain().StateAt(currentBlock.Root()) + if err != nil { + log.Error("Error in retrieving state", "block hash", currentHeader.Hash(), "error", err) + return + } + + if currentHeader.Number.Uint64() > 0 { + vmRunner := miner.eth.BlockChain().NewEVMRunner(currentHeader, currentState) + // Check to see if we already have the commitment cache + lastCommitment, err := random.GetLastCommitment(vmRunner, miner.validator) + if err != nil { + log.Error("Error in retrieving last commitment", "error", err) + return + } + + // If there is a non empty last commitment and if we don't have that commitment's + // cache entry, then we need to recover it. + if (lastCommitment != common.Hash{}) && (rawdb.ReadRandomCommitmentCache(miner.db, lastCommitment) == common.Hash{}) { + err := miner.eth.BlockChain().RecoverRandomnessCache(lastCommitment, currentBlock.Hash()) + if err != nil { + log.Error("Error in recovering randomness cache", "error", err) + return + } + } + } + } + +} + // update keeps track of the downloader events. Please be aware that this is a one shot type of update loop. // It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks @@ -116,20 +150,18 @@ } case downloader.FailedEvent: canStart = true if shouldStart { - miner.SetEtherbase(miner.coinbase) miner.worker.start() } case downloader.DoneEvent: + miner.recoverRandomness() canStart = true if shouldStart { - miner.SetEtherbase(miner.coinbase) miner.worker.start() } // Stop reacting to downloader events events.Unsubscribe() } - case addr := <-miner.startCh: - miner.SetEtherbase(addr) + case <-miner.startCh: if canStart { miner.worker.start() } @@ -139,13 +171,16 @@ shouldStart = false miner.worker.stop() case <-miner.exitCh: miner.worker.close() + miner.exitCh <- struct{}{} return } } }   -func (miner *Miner) Start(coinbase common.Address) { - miner.startCh <- coinbase +func (miner *Miner) Start(validator common.Address, txFeeRecipient common.Address) { + miner.SetValidator(validator) + miner.SetTxFeeRecipient(txFeeRecipient) + miner.startCh <- struct{}{} }   func (miner *Miner) Stop() { @@ -153,20 +188,14 @@ miner.stopCh <- struct{}{} }   func (miner *Miner) Close() { - close(miner.exitCh) + miner.exitCh <- struct{}{} + <-miner.exitCh }   func (miner *Miner) Mining() bool { return miner.worker.isRunning() }   -func (miner *Miner) Hashrate() uint64 { - if pow, ok := miner.engine.(consensus.PoW); ok { - return uint64(pow.Hashrate()) - } - return 0 -} - func (miner *Miner) SetExtra(extra []byte) error { if uint64(len(extra)) > params.MaximumExtraDataSize { return fmt.Errorf("extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize) @@ -175,11 +204,6 @@ miner.worker.setExtra(extra) return nil }   -// SetRecommitInterval sets the interval for sealing work resubmitting. -func (miner *Miner) SetRecommitInterval(interval time.Duration) { - miner.worker.setRecommitInterval(interval) -} - // Pending returns the currently pending block and associated state. func (miner *Miner) Pending() (*types.Block, *state.StateDB) { return miner.worker.pending() @@ -194,37 +218,20 @@ func (miner *Miner) PendingBlock() *types.Block { return miner.worker.pendingBlock() }   -// PendingBlockAndReceipts returns the currently pending block and corresponding receipts. -func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) { - return miner.worker.pendingBlockAndReceipts() -} - -func (miner *Miner) SetEtherbase(addr common.Address) { - miner.coinbase = addr - miner.worker.setEtherbase(addr) -} - -// SetGasCeil sets the gaslimit to strive for when mining blocks post 1559. -// For pre-1559 blocks, it sets the ceiling. -func (miner *Miner) SetGasCeil(ceil uint64) { - miner.worker.setGasCeil(ceil) +// SetValidator sets the miner and worker's address for message and block signing +func (miner *Miner) SetValidator(addr common.Address) { + miner.validator = addr + miner.worker.setValidator(addr) }   -// EnablePreseal turns on the preseal mining feature. It's enabled by default. -// Note this function shouldn't be exposed to API, it's unnecessary for users -// (miners) to actually know the underlying detail. It's only for outside project -// which uses this library. -func (miner *Miner) EnablePreseal() { - miner.worker.enablePreseal() +// SetTxFeeRecipient sets the address where the miner and worker will receive fees +func (miner *Miner) SetTxFeeRecipient(addr common.Address) { + miner.worker.setTxFeeRecipient(addr) }   -// DisablePreseal turns off the preseal mining feature. It's necessary for some -// fake consensus engine which can seal blocks instantaneously. -// Note this function shouldn't be exposed to API, it's unnecessary for users -// (miners) to actually know the underlying detail. It's only for outside project -// which uses this library. -func (miner *Miner) DisablePreseal() { - miner.worker.disablePreseal() +// PendingBlockAndReceipts returns the currently pending block and corresponding receipts. +func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) { + return miner.worker.pendingBlockAndReceipts() }   // SubscribePendingLogs starts delivering logs from pending transactions
diff --git go-ethereum/miner/unconfirmed.go celo/miner/unconfirmed.go deleted file mode 100644 index 0db52c526571ceb0315f8b921ec52de91b904ef0..0000000000000000000000000000000000000000 --- go-ethereum/miner/unconfirmed.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package miner - -import ( - "container/ring" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/log" -) - -// chainRetriever is used by the unconfirmed block set to verify whether a previously -// mined block is part of the canonical chain or not. -type chainRetriever interface { - // GetHeaderByNumber retrieves the canonical header associated with a block number. - GetHeaderByNumber(number uint64) *types.Header - - // GetBlockByNumber retrieves the canonical block associated with a block number. - GetBlockByNumber(number uint64) *types.Block -} - -// unconfirmedBlock is a small collection of metadata about a locally mined block -// that is placed into a unconfirmed set for canonical chain inclusion tracking. -type unconfirmedBlock struct { - index uint64 - hash common.Hash -} - -// unconfirmedBlocks implements a data structure to maintain locally mined blocks -// have not yet reached enough maturity to guarantee chain inclusion. It is -// used by the miner to provide logs to the user when a previously mined block -// has a high enough guarantee to not be reorged out of the canonical chain. -type unconfirmedBlocks struct { - chain chainRetriever // Blockchain to verify canonical status through - depth uint // Depth after which to discard previous blocks - blocks *ring.Ring // Block infos to allow canonical chain cross checks - lock sync.Mutex // Protects the fields from concurrent access -} - -// newUnconfirmedBlocks returns new data structure to track currently unconfirmed blocks. -func newUnconfirmedBlocks(chain chainRetriever, depth uint) *unconfirmedBlocks { - return &unconfirmedBlocks{ - chain: chain, - depth: depth, - } -} - -// Insert adds a new block to the set of unconfirmed ones. -func (set *unconfirmedBlocks) Insert(index uint64, hash common.Hash) { - // If a new block was mined locally, shift out any old enough blocks - set.Shift(index) - - // Create the new item as its own ring - item := ring.New(1) - item.Value = &unconfirmedBlock{ - index: index, - hash: hash, - } - // Set as the initial ring or append to the end - set.lock.Lock() - defer set.lock.Unlock() - - if set.blocks == nil { - set.blocks = item - } else { - set.blocks.Move(-1).Link(item) - } - // Display a log for the user to notify of a new mined block unconfirmed - log.Info("🔨 mined potential block", "number", index, "hash", hash) -} - -// Shift drops all unconfirmed blocks from the set which exceed the unconfirmed sets depth -// allowance, checking them against the canonical chain for inclusion or staleness -// report. -func (set *unconfirmedBlocks) Shift(height uint64) { - set.lock.Lock() - defer set.lock.Unlock() - - for set.blocks != nil { - // Retrieve the next unconfirmed block and abort if too fresh - next := set.blocks.Value.(*unconfirmedBlock) - if next.index+uint64(set.depth) > height { - break - } - // Block seems to exceed depth allowance, check for canonical status - header := set.chain.GetHeaderByNumber(next.index) - switch { - case header == nil: - log.Warn("Failed to retrieve header of mined block", "number", next.index, "hash", next.hash) - case header.Hash() == next.hash: - log.Info("🔗 block reached canonical chain", "number", next.index, "hash", next.hash) - default: - // Block is not canonical, check whether we have an uncle or a lost block - included := false - for number := next.index; !included && number < next.index+uint64(set.depth) && number <= height; number++ { - if block := set.chain.GetBlockByNumber(number); block != nil { - for _, uncle := range block.Uncles() { - if uncle.Hash() == next.hash { - included = true - break - } - } - } - } - if included { - log.Info("⑂ block became an uncle", "number", next.index, "hash", next.hash) - } else { - log.Info("😱 block lost", "number", next.index, "hash", next.hash) - } - } - // Drop the block out of the ring - if set.blocks.Value == set.blocks.Next().Value { - set.blocks = nil - } else { - set.blocks = set.blocks.Move(-1) - set.blocks.Unlink(1) - set.blocks = set.blocks.Move(1) - } - } -}
diff --git go-ethereum/miner/worker.go celo/miner/worker.go index 1dc502b4e7f070b38f749d7bfc7884967ddc4be8..76b7988622063911b73ed767a4592b5114a991dc 100644 --- go-ethereum/miner/worker.go +++ celo/miner/worker.go @@ -17,80 +17,41 @@ package miner   import ( - "bytes" - "errors" - "math/big" + "context" "sync" "sync/atomic" "time"   - mapset "github.com/deckarep/golang-set" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" )   const ( - // resultQueueSize is the size of channel listening to sealing result. - resultQueueSize = 10 - // txChanSize is the size of channel listening to NewTxsEvent. // The number is referenced from the size of tx pool. txChanSize = 4096   // chainHeadChanSize is the size of channel listening to ChainHeadEvent. chainHeadChanSize = 10 - - // chainSideChanSize is the size of channel listening to ChainSideEvent. - chainSideChanSize = 10 - - // resubmitAdjustChanSize is the size of resubmitting interval adjustment channel. - resubmitAdjustChanSize = 10 - - // miningLogAtDepth is the number of confirmations before logging successful mining. - miningLogAtDepth = 7 - - // minRecommitInterval is the minimal time interval to recreate the mining block with - // any newly arrived transactions. - minRecommitInterval = 1 * time.Second - - // maxRecommitInterval is the maximum time interval to recreate the mining block with - // any newly arrived transactions. - maxRecommitInterval = 15 * time.Second - - // intervalAdjustRatio is the impact a single interval adjustment has on sealing work - // resubmitting interval. - intervalAdjustRatio = 0.1 - - // intervalAdjustBias is applied during the new resubmit interval calculation in favor of - // increasing upper limit or decreasing lower limit so that the limit can be reachable. - intervalAdjustBias = 200 * 1000.0 * 1000.0 - - // staleThreshold is the maximum depth of the acceptable stale block. - staleThreshold = 7 )   -// environment is the worker's current environment and holds all of the current state information. -type environment struct { - signer types.Signer - - state *state.StateDB // apply state changes here - ancestors mapset.Set // ancestor set (used for checking uncle parent validity) - family mapset.Set // family set (used for checking uncle invalidity) - uncles mapset.Set // uncle set - tcount int // tx count in cycle - gasPool *core.GasPool // available gas used to pack transactions - - header *types.Header - txs []*types.Transaction - receipts []*types.Receipt +// callBackEngine is a subset of the consensus.Istanbul interface. It is used over consensus.Istanbul to enable sealing +// for the MockEngine (which implements this and the engine interface, but not the full istanbul interface). +type callBackEngine interface { + // SetCallBacks sets call back functions + SetCallBacks(hasBadBlock func(common.Hash) bool, + processBlock func(*types.Block, *state.StateDB) (types.Receipts, []*types.Log, uint64, error), + validateState func(*types.Block, *state.StateDB, types.Receipts, uint64) error, + onNewConsensusBlock func(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB)) error }   // task contains all information for consensus engine sealing and result submitting. @@ -101,25 +62,6 @@ block *types.Block createdAt time.Time }   -const ( - commitInterruptNone int32 = iota - commitInterruptNewHead - commitInterruptResubmit -) - -// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier. -type newWorkReq struct { - interrupt *int32 - noempty bool - timestamp int64 -} - -// intervalAdjust represents a resubmitting interval adjustment. -type intervalAdjust struct { - ratio float64 - inc bool -} - // worker is the main object which takes care of submitting new work to consensus engine // and gathering the sealing result. type worker struct { @@ -138,30 +80,17 @@ txsCh chan core.NewTxsEvent txsSub event.Subscription chainHeadCh chan core.ChainHeadEvent chainHeadSub event.Subscription - chainSideCh chan core.ChainSideEvent - chainSideSub event.Subscription   // Channels - newWorkCh chan *newWorkReq - taskCh chan *task - resultCh chan *types.Block startCh chan struct{} exitCh chan struct{} - resubmitIntervalCh chan time.Duration - resubmitAdjustCh chan *intervalAdjust + wg sync.WaitGroup   - current *environment // An environment for current running cycle. - localUncles map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks. - remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks. - unconfirmed *unconfirmedBlocks // A set of locally mined blocks pending canonicalness confirmations. - - mu sync.RWMutex // The lock used to protect the coinbase and extra fields - coinbase common.Address + mu sync.RWMutex // The lock used to protect the validator, txFeeRecipient and extra fields + validator common.Address + txFeeRecipient common.Address extra []byte   - pendingMu sync.RWMutex - pendingTasks map[common.Hash]*task - snapshotMu sync.RWMutex // The lock used to protect the snapshots below snapshotBlock *types.Block snapshotReceipts types.Receipts @@ -169,26 +98,19 @@ snapshotState *state.StateDB   // atomic status counters running int32 // The indicator whether the consensus engine is running or not. - newTxs int32 // New arrival transaction count since last sealing work submitting. - - // noempty is the flag used to control whether the feature of pre-seal empty - // block is enabled. The default value is false(pre-seal is enabled by default). - // But in some special scenario the consensus engine will seal blocks instantaneously, - // in this case this feature will add all empty blocks into canonical chain - // non-stop and no real transaction will be included. - noempty uint32 - - // External functions - isLocalBlock func(block *types.Block) bool // Function used to determine whether the specified block is mined by local miner.   // Test hooks newTaskHook func(*task) // Method to call upon receiving a new sealing task. skipSealHook func(*task) bool // Method to decide whether skipping the sealing. fullTaskHook func() // Method to call before pushing the full sealing task. - resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval. + + // Needed for randomness + db ethdb.Database + + blockConstructGauge metrics.Gauge }   -func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(*types.Block) bool, init bool) *worker { +func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, db ethdb.Database) *worker { worker := &worker{ config: config, chainConfig: chainConfig, @@ -196,58 +118,39 @@ engine: engine, eth: eth, mux: mux, chain: eth.BlockChain(), - isLocalBlock: isLocalBlock, - localUncles: make(map[common.Hash]*types.Block), - remoteUncles: make(map[common.Hash]*types.Block), - unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth), - pendingTasks: make(map[common.Hash]*task), txsCh: make(chan core.NewTxsEvent, txChanSize), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), - chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), - newWorkCh: make(chan *newWorkReq), - taskCh: make(chan *task), - resultCh: make(chan *types.Block, resultQueueSize), exitCh: make(chan struct{}), startCh: make(chan struct{}, 1), - resubmitIntervalCh: make(chan time.Duration), - resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize), + db: db, + blockConstructGauge: metrics.NewRegisteredGauge("miner/worker/block_construct", nil), } // Subscribe NewTxsEvent for tx pool worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh) // Subscribe events for blockchain worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) - worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)   - // Sanitize recommit interval if the user-specified one is too short. - recommit := worker.config.Recommit - if recommit < minRecommitInterval { - log.Warn("Sanitizing miner recommit interval", "provided", recommit, "updated", minRecommitInterval) - recommit = minRecommitInterval - } + worker.wg.Add(1) + go func() { + defer worker.wg.Done() + worker.mainLoop() + }()   - go worker.mainLoop() - go worker.newWorkLoop(recommit) - go worker.resultLoop() - go worker.taskLoop() - - // Submit first work to initialize pending state. - if init { - worker.startCh <- struct{}{} - } return worker }   -// setEtherbase sets the etherbase used to initialize the block coinbase field. -func (w *worker) setEtherbase(addr common.Address) { +// setValidator sets the validator address that signs messages and commits randomness +func (w *worker) setValidator(addr common.Address) { w.mu.Lock() defer w.mu.Unlock() - w.coinbase = addr + w.validator = addr }   -func (w *worker) setGasCeil(ceil uint64) { +// setTxFeeRecipient sets the address to receive tx fees, stored in header.Coinbase +func (w *worker) setTxFeeRecipient(addr common.Address) { w.mu.Lock() defer w.mu.Unlock() - w.config.GasCeil = ceil + w.txFeeRecipient = addr }   // setExtra sets the content used to initialize the block extra field. @@ -257,21 +160,6 @@ defer w.mu.Unlock() w.extra = extra }   -// setRecommitInterval updates the interval for miner sealing work recommitting. -func (w *worker) setRecommitInterval(interval time.Duration) { - w.resubmitIntervalCh <- interval -} - -// disablePreseal disables pre-sealing mining feature -func (w *worker) disablePreseal() { - atomic.StoreUint32(&w.noempty, 1) -} - -// enablePreseal enables pre-sealing mining feature -func (w *worker) enablePreseal() { - atomic.StoreUint32(&w.noempty, 0) -} - // pending returns the pending state and corresponding block. func (w *worker) pending() (*types.Block, *state.StateDB) { // return a snapshot to avoid contention on currentMu mutex @@ -280,7 +168,24 @@ defer w.snapshotMu.RUnlock() if w.snapshotState == nil { return nil, nil } - return w.snapshotBlock, w.snapshotState.Copy() + stateCopy := w.snapshotState.Copy() + // Call Prepare to ensure that any access logs from the last executed + // transaction have been erased. + // + // Prior to the upstream PR + // https://github.com/ethereum/go-ethereum/pull/21509 the state returned + // from pending was ready to use for transaction execution, that PR + // essentially changed the contract of the pendng method, in that the + // returned state was not ready for transaction execution and required + // Prepare to be called on it first, but notably the PR did not update any + // of the callers of pending to ensure that Prepare was called. I think + // this broke some of the eth rpc apis. Calling Prepare here essentially + // restores the previous contract for this method which was that the + // returned state is ready to use for transaction execution. + // + // See https://github.com/ethereum/go-ethereum/pull/1858#issuecomment-1054159493 for more details. + stateCopy.Prepare(common.Hash{}, 0) + return w.snapshotBlock, stateCopy }   // pendingBlock returns pending block. @@ -303,11 +208,43 @@ // start sets the running status as 1 and triggers new work submitting. func (w *worker) start() { atomic.StoreInt32(&w.running, 1) w.startCh <- struct{}{} + + if cbEngine, ok := w.engine.(callBackEngine); ok { + cbEngine.SetCallBacks(w.chain.HasBadBlock, + func(block *types.Block, state *state.StateDB) (types.Receipts, []*types.Log, uint64, error) { + return w.chain.Processor().Process(block, state, *w.chain.GetVMConfig()) + }, + w.chain.Validator().ValidateState, + func(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB) { + if err := w.chain.InsertPreprocessedBlock(block, receipts, logs, state); err != nil { + log.Error("Failed to insert produced block", "blockNumber", block.Number(), "hash", block.Hash(), "err", err) + return + } + log.Info("Successfully produced new block", "number", block.Number(), "hash", block.Hash()) + + if err := w.mux.Post(core.NewMinedBlockEvent{Block: block}); err != nil { + log.Error("Error when posting NewMinedBlockEvent", "err", err) + } + }) + } + + if istanbul, ok := w.engine.(consensus.Istanbul); ok { + if istanbul.IsPrimary() { + istanbul.StartValidating() + } + } }   // stop sets the running status as 0. func (w *worker) stop() { atomic.StoreInt32(&w.running, 0) + + if istanbul, ok := w.engine.(consensus.Istanbul); ok { + err := istanbul.StopValidating() + if err != nil { + log.Error("Error while calling engine.StopValidating", "err", err) + } + } }   // isRunning returns an indicator whether worker is running or not. @@ -318,756 +255,231 @@ // close terminates all background threads maintained by the worker. // Note the worker does not support being closed multiple times. func (w *worker) close() { - if w.current != nil && w.current.state != nil { - w.current.state.StopPrefetcher() - } atomic.StoreInt32(&w.running, 0) close(w.exitCh) + w.wg.Wait() }   -// recalcRecommit recalculates the resubmitting interval upon feedback. -func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) time.Duration { - var ( - prevF = float64(prev.Nanoseconds()) - next float64 - ) - if inc { - next = prevF*(1-intervalAdjustRatio) + intervalAdjustRatio*(target+intervalAdjustBias) - max := float64(maxRecommitInterval.Nanoseconds()) - if next > max { - next = max - } - } else { - next = prevF*(1-intervalAdjustRatio) + intervalAdjustRatio*(target-intervalAdjustBias) - min := float64(minRecommit.Nanoseconds()) - if next < min { - next = min - } - } - return time.Duration(int64(next)) -} - -// newWorkLoop is a standalone goroutine to submit new mining work upon received events. -func (w *worker) newWorkLoop(recommit time.Duration) { - var ( - interrupt *int32 - minRecommit = recommit // minimal resubmit interval specified by user. - timestamp int64 // timestamp for each round of mining. - ) - - timer := time.NewTimer(0) - defer timer.Stop() - <-timer.C // discard the initial tick +// constructAndSubmitNewBlock constructs a new block and if the worker is running, submits +// a task to the engine +func (w *worker) constructAndSubmitNewBlock(ctx context.Context) { + start := time.Now()   - // commit aborts in-flight transaction execution with given signal and resubmits a new one. - commit := func(noempty bool, s int32) { - if interrupt != nil { - atomic.StoreInt32(interrupt, s) - } - interrupt = new(int32) - select { - case w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp}: - case <-w.exitCh: - return + // Initialize the block. + // Note: In the current implementation, this will sleep until the time of the next block. + b, err := prepareBlock(w) + defer func() { + if b != nil { + b.close() } - timer.Reset(recommit) - atomic.StoreInt32(&w.newTxs, 0) + }() + if err != nil { + log.Error("Failed to create mining context", "err", err) + return } - // clearPending cleans the stale pending tasks. - clearPending := func(number uint64) { - w.pendingMu.Lock() - for h, t := range w.pendingTasks { - if t.block.NumberU64()+staleThreshold <= number { - delete(w.pendingTasks, h) - } - } - w.pendingMu.Unlock() + w.updatePendingBlock(b) + + startConstruction := time.Now() + err = b.selectAndApplyTransactions(ctx, w) + if err != nil { + log.Error("Failed to apply transactions to the block", "err", err) + return } + w.updatePendingBlock(b)   - for { - select { - case <-w.startCh: - clearPending(w.chain.CurrentBlock().NumberU64()) - timestamp = time.Now().Unix() - commit(false, commitInterruptNewHead) + block, err := b.finalizeAndAssemble(w) + if err != nil { + log.Error("Failed to finalize and assemble the block", "err", err) + return + } + w.updatePendingBlock(b)   - case head := <-w.chainHeadCh: - clearPending(head.Block.NumberU64()) - timestamp = time.Now().Unix() - commit(false, commitInterruptNewHead) + // We update the block construction metric here, rather than at the end of the function, because + // `submitTaskToEngine` may take a long time if the engine's handler is busy (e.g. if we are not + // the proposer and the engine has already gotten and is verifying the proposal). See + // https://github.com/ethereum/go-ethereum/issues/1639#issuecomment-888611039 + // And we subtract the time we spent sleeping, since we want the time spent actually building the block. + w.blockConstructGauge.Update(time.Since(startConstruction).Nanoseconds())   - case <-timer.C: - // If mining is running resubmit a new work cycle periodically to pull in - // higher priced transactions. Disable this overhead for pending blocks. - if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) { - // Short circuit if no new transaction arrives. - if atomic.LoadInt32(&w.newTxs) == 0 { - timer.Reset(recommit) - continue - } - commit(true, commitInterruptResubmit) - } + if w.isRunning() { + if w.fullTaskHook != nil { + w.fullTaskHook() + } + w.submitTaskToEngine(&task{receipts: b.receipts, state: b.state, block: block, createdAt: time.Now()}) + baseFeeFn, toCELO := createConversionFunctions(b.sysCtx, w.chain, b.header, b.state) + feesCelo := totalFees(block, b.receipts, baseFeeFn, toCELO, w.chainConfig.IsEspresso(b.header.Number)) + log.Info("Commit new mining work", "number", block.Number(), "txs", b.tcount, "gas", block.GasUsed(), + "fees", feesCelo, "elapsed", common.PrettyDuration(time.Since(start)))   - case interval := <-w.resubmitIntervalCh: - // Adjust resubmit interval explicitly by user. - if interval < minRecommitInterval { - log.Warn("Sanitizing miner recommit interval", "provided", interval, "updated", minRecommitInterval) - interval = minRecommitInterval - } - log.Info("Miner recommit interval update", "from", minRecommit, "to", interval) - minRecommit, recommit = interval, interval + } +}   - if w.resubmitHook != nil { - w.resubmitHook(minRecommit, recommit) - } - - case adjust := <-w.resubmitAdjustCh: - // Adjust resubmit interval by feedback. - if adjust.inc { - before := recommit - target := float64(recommit.Nanoseconds()) / adjust.ratio - recommit = recalcRecommit(minRecommit, recommit, target, true) - log.Trace("Increase miner recommit interval", "from", before, "to", recommit) - } else { - before := recommit - recommit = recalcRecommit(minRecommit, recommit, float64(minRecommit.Nanoseconds()), false) - log.Trace("Decrease miner recommit interval", "from", before, "to", recommit) - } - - if w.resubmitHook != nil { - w.resubmitHook(minRecommit, recommit) - } - - case <-w.exitCh: - return +// constructPendingStateBlock constructs a new block and keeps applying new transactions to it. +// until it is full or the context is cancelled. +func (w *worker) constructPendingStateBlock(ctx context.Context, txsCh chan core.NewTxsEvent) { + // Initialize the block. + b, err := prepareBlock(w) + defer func() { + if b != nil { + b.close() } + }() + if err != nil { + log.Error("Failed to create mining context", "err", err) + return } -} + w.updatePendingBlock(b)   -// mainLoop is a standalone goroutine to regenerate the sealing task based on the received event. -func (w *worker) mainLoop() { - defer w.txsSub.Unsubscribe() - defer w.chainHeadSub.Unsubscribe() - defer w.chainSideSub.Unsubscribe() + err = b.selectAndApplyTransactions(ctx, w) + if err != nil { + log.Error("Failed to apply transactions to the block", "err", err) + return + } + w.updatePendingBlock(b) + + w.mu.RLock() + txFeeRecipient := w.txFeeRecipient + if !w.chainConfig.IsDonut(b.header.Number) && w.txFeeRecipient != w.validator { + txFeeRecipient = w.validator + log.Warn("TxFeeRecipient and Validator flags set before split etherbase fork is active. Defaulting to the given validator address for the coinbase.") + } + w.mu.RUnlock()   for { select { - case req := <-w.newWorkCh: - w.commitNewWork(req.interrupt, req.noempty, req.timestamp) - - case ev := <-w.chainSideCh: - // Short circuit for duplicate side blocks - if _, exist := w.localUncles[ev.Block.Hash()]; exist { - continue - } - if _, exist := w.remoteUncles[ev.Block.Hash()]; exist { - continue - } - // Add side block to possible uncle block set depending on the author. - if w.isLocalBlock != nil && w.isLocalBlock(ev.Block) { - w.localUncles[ev.Block.Hash()] = ev.Block - } else { - w.remoteUncles[ev.Block.Hash()] = ev.Block - } - // If our mining block contains less than 2 uncle blocks, - // add the new uncle block if valid and regenerate a mining block. - if w.isRunning() && w.current != nil && w.current.uncles.Cardinality() < 2 { - start := time.Now() - if err := w.commitUncle(w.current, ev.Block.Header()); err == nil { - var uncles []*types.Header - w.current.uncles.Each(func(item interface{}) bool { - hash, ok := item.(common.Hash) - if !ok { - return false - } - uncle, exist := w.localUncles[hash] - if !exist { - uncle, exist = w.remoteUncles[hash] - } - if !exist { - return false - } - uncles = append(uncles, uncle.Header()) - return false - }) - w.commit(uncles, nil, true, start) - } - } - - case ev := <-w.txsCh: - // Apply transactions to the pending state if we're not mining. - // - // Note all transactions received may not be continuous with transactions - // already included in the current mining block. These transactions will - // be automatically eliminated. - if !w.isRunning() && w.current != nil { + case <-ctx.Done(): + return + case ev := <-txsCh: + if !w.isRunning() { // If block is already full, abort - if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas { - continue + if gp := b.gasPool; gp != nil && gp.Gas() < params.TxGas { + return } - w.mu.RLock() - coinbase := w.coinbase - w.mu.RUnlock()   txs := make(map[common.Address]types.Transactions) for _, tx := range ev.Txs { - acc, _ := types.Sender(w.current.signer, tx) + acc, _ := types.Sender(b.signer, tx) txs[acc] = append(txs[acc], tx) } - txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, w.current.header.BaseFee) - tcount := w.current.tcount - w.commitTransactions(txset, coinbase, nil) + + baseFeeFn, toCElOFn := createConversionFunctions(b.sysCtx, w.chain, b.header, b.state) + txset := types.NewTransactionsByPriceAndNonce(b.signer, txs, baseFeeFn, toCElOFn) + tcount := b.tcount + b.commitTransactions(ctx, w, txset, txFeeRecipient) // Only update the snapshot if any new transactons were added // to the pending block - if tcount != w.current.tcount { - w.updateSnapshot() - } - } else { - // Special case, if the consensus engine is 0 period clique(dev mode), - // submit mining work here since all empty submission will be rejected - // by clique. Of course the advance sealing(empty submission) is disabled. - if w.chainConfig.Clique != nil && w.chainConfig.Clique.Period == 0 { - w.commitNewWork(nil, true, time.Now().Unix()) + if tcount != b.tcount { + w.updatePendingBlock(b) } } - atomic.AddInt32(&w.newTxs, int32(len(ev.Txs))) - - // System stopped - case <-w.exitCh: - return - case <-w.txsSub.Err(): - return - case <-w.chainHeadSub.Err(): - return - case <-w.chainSideSub.Err(): - return } } + }   -// taskLoop is a standalone goroutine to fetch sealing task from the generator and -// push them to consensus engine. -func (w *worker) taskLoop() { - var ( - stopCh chan struct{} - prev common.Hash - ) +// mainLoop is a standalone goroutine to create tasks and submit to the engine. +func (w *worker) mainLoop() { + defer w.chainHeadSub.Unsubscribe() + defer w.txsSub.Unsubscribe() + // Context and cancel function for the currently executing block construction + // Cancel needs to be called in each exit path to make the linter happy + // because go struggles with analyzing lexical scoping. + var taskCtx context.Context + var cancel context.CancelFunc + var wg sync.WaitGroup + + // Ensure that block construction is complete before exiting this function. + defer wg.Wait() + txsCh := make(chan core.NewTxsEvent, txChanSize)   - // interrupt aborts the in-flight sealing task. - interrupt := func() { - if stopCh != nil { - close(stopCh) - stopCh = nil + generateNewBlock := func() { + if cancel != nil { + cancel() } - } - for { - select { - case task := <-w.taskCh: - if w.newTaskHook != nil { - w.newTaskHook(task) - } - // Reject duplicate sealing work due to resubmitting. - sealHash := w.engine.SealHash(task.block.Header()) - if sealHash == prev { - continue - } - // Interrupt previous sealing operation - interrupt() - stopCh, prev = make(chan struct{}), sealHash + wg.Wait() + taskCtx, cancel = context.WithCancel(context.Background()) + wg.Add(1)   - if w.skipSealHook != nil && w.skipSealHook(task) { - continue + if w.isRunning() { + // engine.NewWork posts the FinalCommitted Event to IBFT to signal the start of the next round + if h, ok := w.engine.(consensus.Handler); ok { + h.NewWork() } - w.pendingMu.Lock() - w.pendingTasks[sealHash] = task - w.pendingMu.Unlock()   - if err := w.engine.Seal(w.chain, task.block, w.resultCh, stopCh); err != nil { - log.Warn("Block sealing failed", "err", err) - } - case <-w.exitCh: - interrupt() - return + go func() { + w.constructAndSubmitNewBlock(taskCtx) + wg.Done() + }() + } else { + go func() { + w.constructPendingStateBlock(taskCtx, txsCh) + wg.Done() + }() } } -}   -// resultLoop is a standalone goroutine to handle sealing result submitting -// and flush relative data to the database. -func (w *worker) resultLoop() { for { select { - case block := <-w.resultCh: - // Short circuit when receiving empty result. - if block == nil { - continue - } - // Short circuit when receiving duplicate result caused by resubmitting. - if w.chain.HasBlock(block.Hash(), block.NumberU64()) { - continue - } - var ( - sealhash = w.engine.SealHash(block.Header()) - hash = block.Hash() - ) - w.pendingMu.RLock() - task, exist := w.pendingTasks[sealhash] - w.pendingMu.RUnlock() - if !exist { - log.Error("Block found but no relative pending task", "number", block.Number(), "sealhash", sealhash, "hash", hash) - continue - } - // Different block could share same sealhash, deep copy here to prevent write-write conflict. - var ( - receipts = make([]*types.Receipt, len(task.receipts)) - logs []*types.Log - ) - for i, receipt := range task.receipts { - // add block location fields - receipt.BlockHash = hash - receipt.BlockNumber = block.Number() - receipt.TransactionIndex = uint(i) + case <-w.startCh: + generateNewBlock() + + case <-w.chainHeadCh: + generateNewBlock()   - receipts[i] = new(types.Receipt) - *receipts[i] = *receipt - // Update the block hash in all logs since it is now available and not when the - // receipt/log of individual transactions were created. - for _, log := range receipt.Logs { - log.BlockHash = hash + case ev := <-w.txsCh: + // Drain tx sub channel as a validator, + // otherwise pass it to the full node loop + // if the full node loop's channel is full, just drop the transaction + if !w.isRunning() { + select { + case txsCh <- ev: + default: } - logs = append(logs, receipt.Logs...) } - // Commit block and state to database. - _, err := w.chain.WriteBlockWithState(block, receipts, logs, task.state, true) - if err != nil { - log.Error("Failed writing block to chain", "err", err) - continue + // System stopped + case <-w.exitCh: + if cancel != nil { + cancel() } - log.Info("Successfully sealed new block", "number", block.Number(), "sealhash", sealhash, "hash", hash, - "elapsed", common.PrettyDuration(time.Since(task.createdAt))) - - // Broadcast the block and announce chain insertion event - w.mux.Post(core.NewMinedBlockEvent{Block: block}) - - // Insert the block into the set of pending ones to resultLoop for confirmations - w.unconfirmed.Insert(block.NumberU64(), block.Hash()) - - case <-w.exitCh: return - } - } -} - -// makeCurrent creates a new environment for the current cycle. -func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { - // Retrieve the parent state to execute on top and start a prefetcher for - // the miner to speed block sealing up a bit - state, err := w.chain.StateAt(parent.Root()) - if err != nil { - return err - } - state.StartPrefetcher("miner") - - env := &environment{ - signer: types.MakeSigner(w.chainConfig, header.Number), - state: state, - ancestors: mapset.NewSet(), - family: mapset.NewSet(), - uncles: mapset.NewSet(), - header: header, - } - // when 08 is processed ancestors contain 07 (quick block) - for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) { - for _, uncle := range ancestor.Uncles() { - env.family.Add(uncle.Hash()) + case <-w.chainHeadSub.Err(): + if cancel != nil { + cancel() + } + return + case <-w.txsSub.Err(): + if cancel != nil { + cancel() + } + return } - env.family.Add(ancestor.Hash()) - env.ancestors.Add(ancestor.Hash()) } - // Keep track of transactions which return errors so they can be removed - env.tcount = 0 - - // Swap out the old work with the new one, terminating any leftover prefetcher - // processes in the mean time and starting a new one. - if w.current != nil && w.current.state != nil { - w.current.state.StopPrefetcher() - } - w.current = env - return nil }   -// commitUncle adds the given block to uncle block set, returns error if failed to add. -func (w *worker) commitUncle(env *environment, uncle *types.Header) error { - hash := uncle.Hash() - if env.uncles.Contains(hash) { - return errors.New("uncle not unique") +func (w *worker) submitTaskToEngine(task *task) { + if w.newTaskHook != nil { + w.newTaskHook(task) } - if env.header.ParentHash == uncle.ParentHash { - return errors.New("uncle is sibling") + if w.skipSealHook != nil && w.skipSealHook(task) { + return } - if !env.ancestors.Contains(uncle.ParentHash) { - return errors.New("uncle's parent unknown") + if err := w.engine.Seal(w.chain, task.block); err != nil { + log.Warn("Block sealing failed", "err", err) } - if env.family.Contains(hash) { - return errors.New("uncle already included") - } - env.uncles.Add(uncle.Hash()) - return nil }   -// updateSnapshot updates pending snapshot block and state. -// Note this function assumes the current variable is thread safe. -func (w *worker) updateSnapshot() { +// updatePendingBlock updates pending snapshot block and state. +func (w *worker) updatePendingBlock(b *blockState) { w.snapshotMu.Lock() defer w.snapshotMu.Unlock()   - var uncles []*types.Header - w.current.uncles.Each(func(item interface{}) bool { - hash, ok := item.(common.Hash) - if !ok { - return false - } - uncle, exist := w.localUncles[hash] - if !exist { - uncle, exist = w.remoteUncles[hash] - } - if !exist { - return false - } - uncles = append(uncles, uncle.Header()) - return false - }) - w.snapshotBlock = types.NewBlock( - w.current.header, - w.current.txs, - uncles, - w.current.receipts, + b.header, + b.txs, + b.receipts, + b.randomness, trie.NewStackTrie(nil), ) - w.snapshotReceipts = copyReceipts(w.current.receipts) - w.snapshotState = w.current.state.Copy() -} - -func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { - snap := w.current.state.Snapshot() - - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig()) - if err != nil { - w.current.state.RevertToSnapshot(snap) - return nil, err - } - w.current.txs = append(w.current.txs, tx) - w.current.receipts = append(w.current.receipts, receipt) - - return receipt.Logs, nil -} - -func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) bool { - // Short circuit if current is nil - if w.current == nil { - return true - } - - gasLimit := w.current.header.GasLimit - if w.current.gasPool == nil { - w.current.gasPool = new(core.GasPool).AddGas(gasLimit) - } - - var coalescedLogs []*types.Log - - for { - // In the following three cases, we will interrupt the execution of the transaction. - // (1) new head block event arrival, the interrupt signal is 1 - // (2) worker start or restart, the interrupt signal is 1 - // (3) worker recreate the mining block with any newly arrived transactions, the interrupt signal is 2. - // For the first two cases, the semi-finished work will be discarded. - // For the third case, the semi-finished work will be submitted to the consensus engine. - if interrupt != nil && atomic.LoadInt32(interrupt) != commitInterruptNone { - // Notify resubmit loop to increase resubmitting interval due to too frequent commits. - if atomic.LoadInt32(interrupt) == commitInterruptResubmit { - ratio := float64(gasLimit-w.current.gasPool.Gas()) / float64(gasLimit) - if ratio < 0.1 { - ratio = 0.1 - } - w.resubmitAdjustCh <- &intervalAdjust{ - ratio: ratio, - inc: true, - } - } - return atomic.LoadInt32(interrupt) == commitInterruptNewHead - } - // If we don't have enough gas for any further transactions then we're done - if w.current.gasPool.Gas() < params.TxGas { - log.Trace("Not enough gas for further transactions", "have", w.current.gasPool, "want", params.TxGas) - break - } - // Retrieve the next transaction and abort if all done - tx := txs.Peek() - if tx == nil { - break - } - // Error may be ignored here. The error has already been checked - // during transaction acceptance is the transaction pool. - // - // We use the eip155 signer regardless of the current hf. - from, _ := types.Sender(w.current.signer, tx) - // Check whether the tx is replay protected. If we're not in the EIP155 hf - // phase, start ignoring the sender until we do. - if tx.Protected() && !w.chainConfig.IsEIP155(w.current.header.Number) { - log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) - - txs.Pop() - continue - } - // Start executing the transaction - w.current.state.Prepare(tx.Hash(), w.current.tcount) - - logs, err := w.commitTransaction(tx, coinbase) - switch { - case errors.Is(err, core.ErrGasLimitReached): - // Pop the current out-of-gas transaction without shifting in the next from the account - log.Trace("Gas limit exceeded for current block", "sender", from) - txs.Pop() - - case errors.Is(err, core.ErrNonceTooLow): - // New head notification data race between the transaction pool and miner, shift - log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) - txs.Shift() - - case errors.Is(err, core.ErrNonceTooHigh): - // Reorg notification data race between the transaction pool and miner, skip account = - log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) - txs.Pop() - - case errors.Is(err, nil): - // Everything ok, collect the logs and shift in the next transaction from the same account - coalescedLogs = append(coalescedLogs, logs...) - w.current.tcount++ - txs.Shift() - - case errors.Is(err, core.ErrTxTypeNotSupported): - // Pop the unsupported transaction without shifting in the next from the account - log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) - txs.Pop() - - default: - // Strange error, discard the transaction and get the next in line (note, the - // nonce-too-high clause will prevent us from executing in vain). - log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) - txs.Shift() - } - } - - if !w.isRunning() && len(coalescedLogs) > 0 { - // We don't push the pendingLogsEvent while we are mining. The reason is that - // when we are mining, the worker will regenerate a mining block every 3 seconds. - // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing. - - // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined - // logs by filling in the block hash when the block was mined by the local miner. This can - // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. - cpy := make([]*types.Log, len(coalescedLogs)) - for i, l := range coalescedLogs { - cpy[i] = new(types.Log) - *cpy[i] = *l - } - w.pendingLogsFeed.Send(cpy) - } - // Notify resubmit loop to decrease resubmitting interval if current interval is larger - // than the user-specified one. - if interrupt != nil { - w.resubmitAdjustCh <- &intervalAdjust{inc: false} - } - return false -} - -// commitNewWork generates several new sealing tasks based on the parent block. -func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) { - w.mu.RLock() - defer w.mu.RUnlock() - - tstart := time.Now() - parent := w.chain.CurrentBlock() - - if parent.Time() >= uint64(timestamp) { - timestamp = int64(parent.Time() + 1) - } - num := parent.Number() - header := &types.Header{ - ParentHash: parent.Hash(), - Number: num.Add(num, common.Big1), - GasLimit: core.CalcGasLimit(parent.GasLimit(), w.config.GasCeil), - Extra: w.extra, - Time: uint64(timestamp), - } - // Set baseFee and GasLimit if we are on an EIP-1559 chain - if w.chainConfig.IsLondon(header.Number) { - header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent.Header()) - if !w.chainConfig.IsLondon(parent.Number()) { - parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier - header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil) - } - } - // Only set the coinbase if our consensus engine is running (avoid spurious block rewards) - if w.isRunning() { - if w.coinbase == (common.Address{}) { - log.Error("Refusing to mine without etherbase") - return - } - header.Coinbase = w.coinbase - } - if err := w.engine.Prepare(w.chain, header); err != nil { - log.Error("Failed to prepare header for mining", "err", err) - return - } - // If we are care about TheDAO hard-fork check whether to override the extra-data or not - if daoBlock := w.chainConfig.DAOForkBlock; daoBlock != nil { - // Check whether the block is among the fork extra-override range - limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) - if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 { - // Depending whether we support or oppose the fork, override differently - if w.chainConfig.DAOForkSupport { - header.Extra = common.CopyBytes(params.DAOForkBlockExtra) - } else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { - header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data - } - } - } - // Could potentially happen if starting to mine in an odd state. - err := w.makeCurrent(parent, header) - if err != nil { - log.Error("Failed to create mining context", "err", err) - return - } - // Create the current work task and check any fork transitions needed - env := w.current - if w.chainConfig.DAOForkSupport && w.chainConfig.DAOForkBlock != nil && w.chainConfig.DAOForkBlock.Cmp(header.Number) == 0 { - misc.ApplyDAOHardFork(env.state) - } - // Accumulate the uncles for the current block - uncles := make([]*types.Header, 0, 2) - commitUncles := func(blocks map[common.Hash]*types.Block) { - // Clean up stale uncle blocks first - for hash, uncle := range blocks { - if uncle.NumberU64()+staleThreshold <= header.Number.Uint64() { - delete(blocks, hash) - } - } - for hash, uncle := range blocks { - if len(uncles) == 2 { - break - } - if err := w.commitUncle(env, uncle.Header()); err != nil { - log.Trace("Possible uncle rejected", "hash", hash, "reason", err) - } else { - log.Debug("Committing new uncle to block", "hash", hash) - uncles = append(uncles, uncle.Header()) - } - } - } - // Prefer to locally generated uncle - commitUncles(w.localUncles) - commitUncles(w.remoteUncles) + w.snapshotState = b.state.Copy()   - // Create an empty block based on temporary copied state for - // sealing in advance without waiting block execution finished. - if !noempty && atomic.LoadUint32(&w.noempty) == 0 { - w.commit(uncles, nil, false, tstart) - } - - // Fill the block with all available pending transactions. - pending, err := w.eth.TxPool().Pending(true) - if err != nil { - log.Error("Failed to fetch pending transactions", "err", err) - return - } - // Short circuit if there is no available pending transactions. - // But if we disable empty precommit already, ignore it. Since - // empty block is necessary to keep the liveness of the network. - if len(pending) == 0 && atomic.LoadUint32(&w.noempty) == 0 { - w.updateSnapshot() - return - } - // Split the pending transactions into locals and remotes - localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending - for _, account := range w.eth.TxPool().Locals() { - if txs := remoteTxs[account]; len(txs) > 0 { - delete(remoteTxs, account) - localTxs[account] = txs - } - } - if len(localTxs) > 0 { - txs := types.NewTransactionsByPriceAndNonce(w.current.signer, localTxs, header.BaseFee) - if w.commitTransactions(txs, w.coinbase, interrupt) { - return - } - } - if len(remoteTxs) > 0 { - txs := types.NewTransactionsByPriceAndNonce(w.current.signer, remoteTxs, header.BaseFee) - if w.commitTransactions(txs, w.coinbase, interrupt) { - return - } - } - w.commit(uncles, w.fullTaskHook, true, tstart) -} - -// commit runs any post-transaction state modifications, assembles the final block -// and commits new work if consensus engine is running. -func (w *worker) commit(uncles []*types.Header, interval func(), update bool, start time.Time) error { - // Deep copy receipts here to avoid interaction between different tasks. - receipts := copyReceipts(w.current.receipts) - s := w.current.state.Copy() - block, err := w.engine.FinalizeAndAssemble(w.chain, w.current.header, s, w.current.txs, uncles, receipts) - if err != nil { - return err - } - if w.isRunning() { - if interval != nil { - interval() - } - select { - case w.taskCh <- &task{receipts: receipts, state: s, block: block, createdAt: time.Now()}: - w.unconfirmed.Shift(block.NumberU64() - 1) - log.Info("Commit new mining work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()), - "uncles", len(uncles), "txs", w.current.tcount, - "gas", block.GasUsed(), "fees", totalFees(block, receipts), - "elapsed", common.PrettyDuration(time.Since(start))) - - case <-w.exitCh: - log.Info("Worker has exited") - } - } - if update { - w.updateSnapshot() - } - return nil -} - -// copyReceipts makes a deep copy of the given receipts. -func copyReceipts(receipts []*types.Receipt) []*types.Receipt { - result := make([]*types.Receipt, len(receipts)) - for i, l := range receipts { - cpy := *l - result[i] = &cpy - } - return result -} - -// postSideBlock fires a side chain event, only use it for testing. -func (w *worker) postSideBlock(event core.ChainSideEvent) { - select { - case w.chainSideCh <- event: - case <-w.exitCh: - } -} - -// totalFees computes total consumed miner fees in ETH. Block transactions and receipts have to have the same order. -func totalFees(block *types.Block, receipts []*types.Receipt) *big.Float { - feesWei := new(big.Int) - for i, tx := range block.Transactions() { - minerFee, _ := tx.EffectiveGasTip(block.BaseFee()) - feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), minerFee)) - } - return new(big.Float).Quo(new(big.Float).SetInt(feesWei), new(big.Float).SetInt(big.NewInt(params.Ether))) }
diff --git go-ethereum/miner/worker_test.go celo/miner/worker_test.go index a0d1348d2e2f93fa222ec7e8d3ad52b9ba305669..33c1818698426612ec2a378d5c1b9d544f177621 100644 --- go-ethereum/miner/worker_test.go +++ celo/miner/worker_test.go @@ -19,20 +19,25 @@ import ( "math/big" "math/rand" - "sync/atomic" "testing" "time"   + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/clique" - "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/consensus/consensustest" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/backend" + istanbulBackend "github.com/ethereum/go-ethereum/consensus/istanbul/backend" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" @@ -50,8 +55,7 @@ var ( // Test chain configurations testTxPoolConfig core.TxPoolConfig - ethashChainConfig *params.ChainConfig - cliqueChainConfig *params.ChainConfig + istanbulChainConfig *params.ChainConfig   // Test accounts testBankKey, _ = crypto.GenerateKey() @@ -65,42 +69,29 @@ // Test transactions pendingTxs []*types.Transaction newTxs []*types.Transaction   - testConfig = &Config{ - Recommit: time.Second, - GasCeil: params.GenesisGasLimit, - } + testConfig = &Config{} )   func init() { testTxPoolConfig = core.DefaultTxPoolConfig testTxPoolConfig.Journal = "" - ethashChainConfig = new(params.ChainConfig) - *ethashChainConfig = *params.TestChainConfig - cliqueChainConfig = new(params.ChainConfig) - *cliqueChainConfig = *params.TestChainConfig - cliqueChainConfig.Clique = &params.CliqueConfig{ - Period: 10, + istanbulChainConfig = params.IstanbulEHFTestChainConfig + istanbulChainConfig.Istanbul = &params.IstanbulConfig{ Epoch: 30000, + ProposerPolicy: 0, }   - signer := types.LatestSigner(params.TestChainConfig) + signer := types.LatestSigner(params.IstanbulEHFTestChainConfig) tx1 := types.MustSignNewTx(testBankKey, signer, &types.AccessListTx{ - ChainID: params.TestChainConfig.ChainID, + ChainID: params.IstanbulEHFTestChainConfig.ChainID, Nonce: 0, To: &testUserAddress, Value: big.NewInt(1000), Gas: params.TxGas, - GasPrice: big.NewInt(params.InitialBaseFee), + GasPrice: new(big.Int), }) pendingTxs = append(pendingTxs, tx1) - - tx2 := types.MustSignNewTx(testBankKey, signer, &types.LegacyTx{ - Nonce: 1, - To: &testUserAddress, - Value: big.NewInt(1000), - Gas: params.TxGas, - GasPrice: big.NewInt(params.InitialBaseFee), - }) + tx2, _ := types.SignTx(types.NewTransaction(1, testUserAddress, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, testBankKey) newTxs = append(newTxs, tx2)   rand.Seed(time.Now().UnixNano()) @@ -108,12 +99,11 @@ }   // testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing. type testWorkerBackend struct { + accountManager *accounts.Manager db ethdb.Database txPool *core.TxPool chain *core.BlockChain - testTxFeed event.Feed genesis *core.Genesis - uncleBlock *types.Block }   func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend { @@ -122,23 +112,35 @@ Config: chainConfig, Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, }   - switch e := engine.(type) { - case *clique.Clique: - gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength) - copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes()) - e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) { - return crypto.Sign(crypto.Keccak256(data), testBankKey) + switch engine.(type) { + case *consensustest.MockEngine: + case *istanbulBackend.Backend: + blsPrivateKey, _ := blscrypto.ECDSAToBLS(testBankKey) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + istanbulBackend.AppendValidatorsToGenesisBlock(&gspec, []istanbul.ValidatorData{ + { + Address: testBankAddress, + BLSPublicKey: blsPublicKey, + }, }) - case *ethash.Ethash: default: t.Fatalf("unexpected consensus engine type: %T", engine) } genesis := gspec.MustCommit(db)   chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil) + txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain)   - // Generate a small n-block chain and an uncle block for it + // If istanbul engine used, set the objects in that engine + if istanbul, ok := engine.(consensus.Istanbul); ok { + istanbul.SetChain(chain, chain.CurrentBlock, func(parentHash common.Hash) (*state.StateDB, error) { + parentStateRoot := chain.GetHeaderByHash(parentHash).Root + return chain.StateAt(parentStateRoot) + }) + } + + // Generate a small n-block chain. if n > 0 { blocks, _ := core.GenerateChain(chainConfig, genesis, engine, db, n, func(i int, gen *core.BlockGen) { gen.SetCoinbase(testBankAddress) @@ -147,86 +149,60 @@ if _, err := chain.InsertChain(blocks); err != nil { t.Fatalf("failed to insert origin chain: %v", err) } } - parent := genesis - if n > 0 { - parent = chain.GetBlockByHash(chain.CurrentBlock().ParentHash()) - } - blocks, _ := core.GenerateChain(chainConfig, parent, engine, db, 1, func(i int, gen *core.BlockGen) { - gen.SetCoinbase(testUserAddress) - }) + + var backends []accounts.Backend + accountManager := accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: true}, backends...)   return &testWorkerBackend{ + accountManager: accountManager, db: db, chain: chain, txPool: txpool, genesis: &gspec, - uncleBlock: blocks[0], } }   +func (b *testWorkerBackend) AccountManager() *accounts.Manager { return b.accountManager } func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain } func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool }   -func (b *testWorkerBackend) newRandomUncle() *types.Block { - var parent *types.Block - cur := b.chain.CurrentBlock() - if cur.NumberU64() == 0 { - parent = b.chain.Genesis() - } else { - parent = b.chain.GetBlockByHash(b.chain.CurrentBlock().ParentHash()) - } - blocks, _ := core.GenerateChain(b.chain.Config(), parent, b.chain.Engine(), b.db, 1, func(i int, gen *core.BlockGen) { - var addr = make([]byte, common.AddressLength) - rand.Read(addr) - gen.SetCoinbase(common.BytesToAddress(addr)) - }) - return blocks[0] -} - func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction { + signer := types.LatestSigner(b.chain.Config()) var tx *types.Transaction - gasPrice := big.NewInt(10 * params.InitialBaseFee) + gasPrice := big.NewInt(10) if creation { - tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(testBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(testCode)), types.HomesteadSigner{}, testBankKey) + tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(testBankAddress), big.NewInt(0), testGas, gasPrice, nil, nil, nil, common.FromHex(testCode)), signer, testBankKey) } else { - tx, _ = types.SignTx(types.NewTransaction(b.txPool.Nonce(testBankAddress), testUserAddress, big.NewInt(1000), params.TxGas, gasPrice, nil), types.HomesteadSigner{}, testBankKey) + tx, _ = types.SignTx(types.NewTransaction(b.txPool.Nonce(testBankAddress), testUserAddress, big.NewInt(1000), params.TxGas, gasPrice, nil, nil, nil, nil), signer, testBankKey) } return tx }   -func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) { +func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int, shouldAddPendingTxs bool) (*worker, *testWorkerBackend) { backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks) - backend.txPool.AddLocals(pendingTxs) - w := newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false) - w.setEtherbase(testBankAddress) - return w, backend -} + if shouldAddPendingTxs { + backend.txPool.AddLocals(pendingTxs) + }   -func TestGenerateBlockAndImportEthash(t *testing.T) { - testGenerateBlockAndImport(t, false) -} + w := newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), backend.db) + w.setTxFeeRecipient(testBankAddress) + w.setValidator(testBankAddress)   -func TestGenerateBlockAndImportClique(t *testing.T) { - testGenerateBlockAndImport(t, true) + return w, backend }   -func testGenerateBlockAndImport(t *testing.T, isClique bool) { +func TestGenerateBlockAndImport(t *testing.T) { var ( engine consensus.Engine chainConfig *params.ChainConfig db = rawdb.NewMemoryDatabase() ) - if isClique { - chainConfig = params.AllCliqueProtocolChanges - chainConfig.Clique = &params.CliqueConfig{Period: 1, Epoch: 30000} - engine = clique.New(chainConfig.Clique, db) - } else { - chainConfig = params.AllEthashProtocolChanges - engine = ethash.NewFaker() - } + chainConfig = params.IstanbulEHFTestChainConfig + + engine = mockEngine.NewFaker() + + w, b := newTestWorker(t, chainConfig, engine, db, 0, true)   - chainConfig.LondonBlock = big.NewInt(0) - w, b := newTestWorker(t, chainConfig, engine, db, 0) defer w.close()   // This test chain imports the mined blocks. @@ -247,35 +223,68 @@ // Start mining! w.start()   + sentTxs := 1 // from newTestWorker w/ shouldAddPendingTxs = true + totalTxs := 0 + for i := 0; i < 5; i++ { b.txPool.AddLocal(b.newRandomTx(true)) b.txPool.AddLocal(b.newRandomTx(false)) - w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()}) - w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()}) - + sentTxs += 2 select { case ev := <-sub.Chan(): - block := ev.Data.(core.NewMinedBlockEvent).Block - if _, err := chain.InsertChain([]*types.Block{block}); err != nil { - t.Fatalf("failed to insert new mined block %d: %v", block.NumberU64(), err) + block, ok := ev.Data.(core.NewMinedBlockEvent) + if !ok { + t.Fatal("Could not decode NewMinedBlockEvent from subscription channel") } + totalTxs += len(block.Block.Transactions()) + case <-time.After(3 * time.Second): // Worker needs 1s to include new changes. t.Fatalf("timeout") } } + + if sentTxs != totalTxs { + t.Errorf("Expected %v transactions, got %v transactions", sentTxs, totalTxs) + } }   -func TestEmptyWorkEthash(t *testing.T) { - testEmptyWork(t, ethashChainConfig, ethash.NewFaker()) +func getAuthorizedIstanbulEngine() consensus.Istanbul { + + decryptFn := func(_ accounts.Account, c, s1, s2 []byte) ([]byte, error) { + eciesKey := ecies.ImportECDSA(testBankKey) + return eciesKey.Decrypt(c, s1, s2) + } + + signerFn := backend.SignFn(testBankKey) + signBLSFn := backend.SignBLSFn(testBankKey) + signHashFn := backend.SignHashFn(testBankKey) + address := crypto.PubkeyToAddress(testBankKey.PublicKey) + + config := istanbul.DefaultConfig + config.ReplicaStateDBPath = "" + config.RoundStateDBPath = "" + config.ValidatorEnodeDBPath = "" + config.VersionCertificateDBPath = "" + + engine := istanbulBackend.New(config, rawdb.NewMemoryDatabase()) + engine.(*istanbulBackend.Backend).SetBroadcaster(&consensustest.MockBroadcaster{}) + engine.(*istanbulBackend.Backend).SetP2PServer(consensustest.NewMockP2PServer(nil)) + engine.(*istanbulBackend.Backend).Authorize(address, address, &testBankKey.PublicKey, decryptFn, signerFn, signBLSFn, signHashFn) + engine.(*istanbulBackend.Backend).StartAnnouncing() + return engine } -func TestEmptyWorkClique(t *testing.T) { - testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) + +func TestEmptyWorkIstanbul(t *testing.T) { + // TODO(nambrot): Fix this + t.Skip("Disabled due to flakyness") + testEmptyWork(t, istanbulChainConfig, getAuthorizedIstanbulEngine(), false, true) + testEmptyWork(t, istanbulChainConfig, getAuthorizedIstanbulEngine(), true, false) }   -func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { +func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, expectEmptyBlock bool, shouldAddPendingTxs bool) { defer engine.Close()   - w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) + w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0, shouldAddPendingTxs) defer w.close()   var ( @@ -285,10 +294,13 @@ ) checkEqual := func(t *testing.T, task *task, index int) { // The first empty work without any txs included receiptLen, balance := 0, big.NewInt(0) + + // if !expectEmptyBlock || (index == 1 && shouldAddPendingTxs) { if index == 1 { // The second full work with 1 tx included receiptLen, balance = 1, big.NewInt(1000) } + if len(task.receipts) != receiptLen { t.Fatalf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) } @@ -308,78 +320,35 @@ w.fullTaskHook = func() { time.Sleep(100 * time.Millisecond) } w.start() // Start mining! - for i := 0; i < 2; i += 1 { + expectedTasksLen := 1 + if shouldAddPendingTxs && expectEmptyBlock { + expectedTasksLen = 2 + } + for i := 0; i < expectedTasksLen; i += 1 { select { case <-taskCh: case <-time.NewTimer(3 * time.Second).C: - t.Error("new task timeout") - } - } -} - -func TestStreamUncleBlock(t *testing.T) { - ethash := ethash.NewFaker() - defer ethash.Close() - - w, b := newTestWorker(t, ethashChainConfig, ethash, rawdb.NewMemoryDatabase(), 1) - defer w.close() - - var taskCh = make(chan struct{}) - - taskIndex := 0 - w.newTaskHook = func(task *task) { - if task.block.NumberU64() == 2 { - // The first task is an empty task, the second - // one has 1 pending tx, the third one has 1 tx - // and 1 uncle. - if taskIndex == 2 { - have := task.block.Header().UncleHash - want := types.CalcUncleHash([]*types.Header{b.uncleBlock.Header()}) - if have != want { - t.Errorf("uncle hash mismatch: have %s, want %s", have.Hex(), want.Hex()) - } - } - taskCh <- struct{}{} - taskIndex += 1 - } - } - w.skipSealHook = func(task *task) bool { - return true - } - w.fullTaskHook = func() { - time.Sleep(100 * time.Millisecond) - } - w.start() - - for i := 0; i < 2; i += 1 { - select { - case <-taskCh: - case <-time.NewTimer(time.Second).C: t.Error("new task timeout") } }   - w.postSideBlock(core.ChainSideEvent{Block: b.uncleBlock}) - select { case <-taskCh: + t.Error("should have not received another task") case <-time.NewTimer(time.Second).C: - t.Error("new task timeout") } }   -func TestRegenerateMiningBlockEthash(t *testing.T) { - testRegenerateMiningBlock(t, ethashChainConfig, ethash.NewFaker()) -} - -func TestRegenerateMiningBlockClique(t *testing.T) { - testRegenerateMiningBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) -} +// For Ethhash and Clique, it is safe and even desired to start another seal process in the presence of new transactions +// that potentially increase the fee revenue for the sealer. In Istanbul, that is not possible and even counter productive +// as proposing another block after having already done so is clearly byzantine behavior. +func TestRegenerateMiningBlockIstanbul(t *testing.T) { + chainConfig := istanbulChainConfig + engine := getAuthorizedIstanbulEngine()   -func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { defer engine.Close()   - w, b := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) + w, b := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0, true) defer w.close()   var taskCh = make(chan struct{}) @@ -387,16 +356,12 @@ taskIndex := 0 w.newTaskHook = func(task *task) { if task.block.NumberU64() == 1 { - // The first task is an empty task, the second - // one has 1 pending tx, the third one has 2 txs - if taskIndex == 2 { - receiptLen, balance := 2, big.NewInt(2000) - if len(task.receipts) != receiptLen { - t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) - } - if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { - t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) - } + receiptLen, balance := 1, big.NewInt(1000) + if len(task.receipts) != receiptLen { + t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) + } + if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { + t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) } taskCh <- struct{}{} taskIndex += 1 @@ -410,114 +375,43 @@ time.Sleep(100 * time.Millisecond) }   w.start() - // Ignore the first two works - for i := 0; i < 2; i += 1 { - select { - case <-taskCh: - case <-time.NewTimer(time.Second).C: - t.Error("new task timeout") - } - } - b.txPool.AddLocals(newTxs) - time.Sleep(time.Second) + // expect one work   select { case <-taskCh: case <-time.NewTimer(time.Second).C: t.Error("new task timeout") } -}   -func TestAdjustIntervalEthash(t *testing.T) { - testAdjustInterval(t, ethashChainConfig, ethash.NewFaker()) -} - -func TestAdjustIntervalClique(t *testing.T) { - testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) -} - -func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { - defer engine.Close() - - w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) - defer w.close() - - w.skipSealHook = func(task *task) bool { - return true - } - w.fullTaskHook = func() { - time.Sleep(100 * time.Millisecond) - } - var ( - progress = make(chan struct{}, 10) - result = make([]float64, 0, 10) - index = 0 - start uint32 - ) - w.resubmitHook = func(minInterval time.Duration, recommitInterval time.Duration) { - // Short circuit if interval checking hasn't started. - if atomic.LoadUint32(&start) == 0 { - return - } - var wantMinInterval, wantRecommitInterval time.Duration - - switch index { - case 0: - wantMinInterval, wantRecommitInterval = 3*time.Second, 3*time.Second - case 1: - origin := float64(3 * time.Second.Nanoseconds()) - estimate := origin*(1-intervalAdjustRatio) + intervalAdjustRatio*(origin/0.8+intervalAdjustBias) - wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond - case 2: - estimate := result[index-1] - min := float64(3 * time.Second.Nanoseconds()) - estimate = estimate*(1-intervalAdjustRatio) + intervalAdjustRatio*(min-intervalAdjustBias) - wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond - case 3: - wantMinInterval, wantRecommitInterval = time.Second, time.Second - } - - // Check interval - if minInterval != wantMinInterval { - t.Errorf("resubmit min interval mismatch: have %v, want %v ", minInterval, wantMinInterval) - } - if recommitInterval != wantRecommitInterval { - t.Errorf("resubmit interval mismatch: have %v, want %v", recommitInterval, wantRecommitInterval) - } - result = append(result, float64(recommitInterval.Nanoseconds())) - index += 1 - progress <- struct{}{} - } - w.start() + b.txPool.AddLocals(newTxs) + time.Sleep(time.Second)   - time.Sleep(time.Second) // Ensure two tasks have been summitted due to start opt - atomic.StoreUint32(&start, 1) - - w.setRecommitInterval(3 * time.Second) select { - case <-progress: + case <-taskCh: + t.Error("Should have not received another task") case <-time.NewTimer(time.Second).C: - t.Error("interval reset timeout") } +}   - w.resubmitAdjustCh <- &intervalAdjust{inc: true, ratio: 0.8} - select { - case <-progress: - case <-time.NewTimer(time.Second).C: - t.Error("interval reset timeout") +func TestNoTxChDeadLockValidator(t *testing.T) { testNoTxChDeadlock(t, true) } +func TestNoTxChDeadLockNonValidator(t *testing.T) { testNoTxChDeadlock(t, false) } + +func testNoTxChDeadlock(t *testing.T, validating bool) { + chainConfig := params.IstanbulTestChainConfig + engine := mockEngine.NewFaker() + db := rawdb.NewMemoryDatabase() + w, b := newTestWorker(t, chainConfig, engine, db, 0, true) + defer w.close() + if validating { + w.start() } - - w.resubmitAdjustCh <- &intervalAdjust{inc: false} - select { - case <-progress: - case <-time.NewTimer(time.Second).C: - t.Error("interval reset timeout") + for i := 0; i < txChanSize+1; i++ { + b.txPool.AddLocal(b.newRandomTx(false)) } - - w.setRecommitInterval(500 * time.Millisecond) select { - case <-progress: - case <-time.NewTimer(time.Second).C: - t.Error("interval reset timeout") + case w.exitCh <- struct{}{}: + // exitCh is unbuffered, so if the send succeeds it means mainLoop isn't deadlocked + case <-time.After(100 * time.Millisecond): + t.Error("Deadlock in mainLoop's select statement") } }
(new)
+388
-0
diff --git go-ethereum/miner/block.go celo/miner/block.go new file mode 100644 index 0000000000000000000000000000000000000000..4bb4cca52265e5d107befbad806326a8bd78c0dd --- /dev/null +++ celo/miner/block.go @@ -0,0 +1,388 @@ +// Copyright 2021 The celo Authors +// This file is part of the celo library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package miner + +import ( + "context" + "errors" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" + "github.com/ethereum/go-ethereum/contracts/currency" + "github.com/ethereum/go-ethereum/contracts/random" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" +) + +// blockState is the collection of modified state that is used to assemble a block +type blockState struct { + signer types.Signer + + state *state.StateDB // apply state changes here + tcount int // tx count in cycle + gasPool *core.GasPool // available gas used to pack transactions + gasLimit uint64 + sysCtx *core.SysContractCallCtx + + header *types.Header + txs []*types.Transaction + receipts []*types.Receipt + randomness *types.Randomness // The types.Randomness of the last block by mined by this worker. + txFeeRecipient common.Address +} + +// prepareBlock intializes a new blockState that is ready to have transaction included to. +// Note that if blockState is not nil, blockState.close() needs to be called to shut down the state prefetcher. +func prepareBlock(w *worker) (*blockState, error) { + w.mu.RLock() + defer w.mu.RUnlock() + + timestamp := time.Now().Unix() + parent := w.chain.CurrentBlock() + + if parent.Time() >= uint64(timestamp) { + timestamp = int64(parent.Time() + 1) + } + + num := parent.Number() + header := &types.Header{ + ParentHash: parent.Hash(), + Number: num.Add(num, common.Big1), + Extra: w.extra, + Time: uint64(timestamp), + } + + txFeeRecipient := w.txFeeRecipient + if !w.chainConfig.IsDonut(header.Number) && w.txFeeRecipient != w.validator { + txFeeRecipient = w.validator + log.Warn("TxFeeRecipient and Validator flags set before split etherbase fork is active. Defaulting to the given validator address for the coinbase.") + } + + // Only set the coinbase if our consensus engine is running (avoid spurious block rewards) + if w.isRunning() { + if txFeeRecipient == (common.Address{}) { + return nil, errors.New("Refusing to mine without etherbase") + } + header.Coinbase = txFeeRecipient + } + // Note: The parent seal will not be set when not validating + if err := w.engine.Prepare(w.chain, header); err != nil { + log.Error("Failed to prepare header for mining", "err", err) + return nil, fmt.Errorf("Failed to prepare header for mining: %w", err) + } + + // Initialize the block state itself + state, err := w.chain.StateAt(parent.Root()) + if err != nil { + return nil, fmt.Errorf("Failed to get the parent state: %w:", err) + } + state.StartPrefetcher("miner") + + vmRunner := w.chain.NewEVMRunner(header, state) + b := &blockState{ + signer: types.LatestSigner(w.chainConfig), + state: state, + tcount: 0, + gasLimit: blockchain_parameters.GetBlockGasLimitOrDefault(vmRunner), + header: header, + txFeeRecipient: txFeeRecipient, + sysCtx: core.NewSysContractCallCtx(header, state.Copy(), w.chain), + } + b.gasPool = new(core.GasPool).AddGas(b.gasLimit) + + // Play our part in generating the random beacon. + if w.isRunning() && random.IsRunning(vmRunner) { + istanbul, ok := w.engine.(consensus.Istanbul) + if !ok { + log.Crit("Istanbul consensus engine must be in use for the randomness beacon") + } + + lastCommitment, err := random.GetLastCommitment(vmRunner, w.validator) + if err != nil { + return b, fmt.Errorf("Failed to get last commitment: %w", err) + } + + lastRandomness := common.Hash{} + if (lastCommitment != common.Hash{}) { + lastRandomnessParentHash := rawdb.ReadRandomCommitmentCache(w.db, lastCommitment) + if (lastRandomnessParentHash == common.Hash{}) { + log.Warn("Randomness cache miss while building a block. Attempting to recover.", "number", header.Number.Uint64()) + + // We missed on the cache which should have been populated, attempt to repopulate the cache. + err := w.chain.RecoverRandomnessCache(lastCommitment, b.header.ParentHash) + if err != nil { + log.Error("Error in recovering randomness cache", "error", err, "number", header.Number.Uint64()) + return b, errors.New("failed to to recover the randomness cache after miss") + } + lastRandomnessParentHash = rawdb.ReadRandomCommitmentCache(w.db, lastCommitment) + if (lastRandomnessParentHash == common.Hash{}) { + // Recover failed to fix the issue. Bail. + return b, errors.New("failed to get last randomness cache entry and failed to recover") + } + } + + var err error + lastRandomness, _, err = istanbul.GenerateRandomness(lastRandomnessParentHash) + if err != nil { + return b, fmt.Errorf("Failed to generate last randomness: %w", err) + } + } + + _, newCommitment, err := istanbul.GenerateRandomness(b.header.ParentHash) + if err != nil { + return b, fmt.Errorf("Failed to generate new randomness: %w", err) + } + + err = random.RevealAndCommit(vmRunner, lastRandomness, newCommitment, w.validator) + if err != nil { + return b, fmt.Errorf("Failed to reveal and commit randomness: %w", err) + } + // always true (EIP158) + b.state.IntermediateRoot(true) + + b.randomness = &types.Randomness{Revealed: lastRandomness, Committed: newCommitment} + } else { + b.randomness = &types.EmptyRandomness + } + + return b, nil +} + +// selectAndApplyTransactions selects and applies transactions to the in flight block state. +func (b *blockState) selectAndApplyTransactions(ctx context.Context, w *worker) error { + // Fill the block with all available pending transactions. + pending, err := w.eth.TxPool().Pending(true) + + // TODO: should this be a fatal error? + if err != nil { + log.Error("Failed to fetch pending transactions", "err", err) + return nil + } + + // Short circuit if there is no available pending transactions. + if len(pending) == 0 { + return nil + } + // Split the pending transactions into locals and remotes + localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending + for _, account := range w.eth.TxPool().Locals() { + if txs := remoteTxs[account]; len(txs) > 0 { + delete(remoteTxs, account) + localTxs[account] = txs + } + } + + // TODO: Properly inject the basefee & toCELO function here + // txComparator := createTxCmp(w.chain, b.header, b.state) + if len(localTxs) > 0 { + baseFeeFn, toCElOFn := createConversionFunctions(b.sysCtx, w.chain, b.header, b.state) + txs := types.NewTransactionsByPriceAndNonce(b.signer, localTxs, baseFeeFn, toCElOFn) + if err := b.commitTransactions(ctx, w, txs, b.txFeeRecipient); err != nil { + return fmt.Errorf("Failed to commit local transactions: %w", err) + } + } + if len(remoteTxs) > 0 { + baseFeeFn, toCElOFn := createConversionFunctions(b.sysCtx, w.chain, b.header, b.state) + txs := types.NewTransactionsByPriceAndNonce(b.signer, remoteTxs, baseFeeFn, toCElOFn) + if err := b.commitTransactions(ctx, w, txs, b.txFeeRecipient); err != nil { + return fmt.Errorf("Failed to commit remote transactions: %w", err) + } + } + return nil +} + +// commitTransactions attempts to commit every transaction in the transactions list until the block is full or there are no more valid transactions. +func (b *blockState) commitTransactions(ctx context.Context, w *worker, txs *types.TransactionsByPriceAndNonce, txFeeRecipient common.Address) error { + var coalescedLogs []*types.Log + +loop: + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + // pass + } + // If we don't have enough gas for any further transactions then we're done + if b.gasPool.Gas() < params.TxGas { + log.Trace("Not enough gas for further transactions", "have", b.gasPool, "want", params.TxGas) + break + } + // Retrieve the next transaction and abort if all done + tx := txs.Peek() + if tx == nil { + break + } + // Short-circuit if the transaction requires more gas than we have in the pool. + // If we didn't short-circuit here, we would get core.ErrGasLimitReached below. + // Short-circuiting here saves us the trouble of checking the GPM and so on when the tx can't be included + // anyway due to the block not having enough gas left. + if b.gasPool.Gas() < tx.Gas() { + log.Trace("Skipping transaction which requires more gas than is left in the block", "hash", tx.Hash(), "gas", b.gasPool.Gas(), "txgas", tx.Gas()) + txs.Pop() + continue + } + // Error may be ignored here. The error has already been checked + // during transaction acceptance is the transaction pool. + // + // We use the eip155 signer regardless of the current hf. + from, _ := types.Sender(b.signer, tx) + // Check whether the tx is replay protected. If we're not in the EIP155 hf + // phase, start ignoring the sender until we do. + if tx.Protected() && !w.chainConfig.IsEIP155(b.header.Number) { + log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) + + txs.Pop() + continue + } + // Start executing the transaction + b.state.Prepare(tx.Hash(), b.tcount) + + logs, err := b.commitTransaction(w, tx, txFeeRecipient) + switch { + case errors.Is(err, core.ErrGasLimitReached): + // Pop the current out-of-gas transaction without shifting in the next from the account + log.Trace("Gas limit exceeded for current block", "sender", from) + txs.Pop() + + case errors.Is(err, core.ErrNonceTooLow): + // New head notification data race between the transaction pool and miner, shift + log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) + txs.Shift() + + case errors.Is(err, core.ErrNonceTooHigh): + // Reorg notification data race between the transaction pool and miner, skip account = + log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) + txs.Pop() + + case errors.Is(err, core.ErrGasPriceDoesNotExceedMinimum): + // We are below the GPM, so we can stop (the rest of the transactions will either have + // even lower gas price or won't be mineable yet due to their nonce) + log.Trace("Skipping remaining transaction below the gas price minimum") + break loop + + case errors.Is(err, nil): + // Everything ok, collect the logs and shift in the next transaction from the same account + coalescedLogs = append(coalescedLogs, logs...) + b.tcount++ + txs.Shift() + + default: + // Strange error, discard the transaction and get the next in line (note, the + // nonce-too-high clause will prevent us from executing in vain). + log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) + txs.Shift() + } + } + + if !w.isRunning() && len(coalescedLogs) > 0 { + // We don't push the pendingLogsEvent while we are mining. The reason is that + // when we are mining, the worker will regenerate a mining block every 3 seconds. + // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing. + + // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined + // logs by filling in the block hash when the block was mined by the local miner. This can + // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. + cpy := make([]*types.Log, len(coalescedLogs)) + for i, l := range coalescedLogs { + cpy[i] = new(types.Log) + *cpy[i] = *l + } + w.pendingLogsFeed.Send(cpy) + } + return nil +} + +// commitTransaction attempts to appply a single transaction. If the transaction fails, it's modifications are reverted. +func (b *blockState) commitTransaction(w *worker, tx *types.Transaction, txFeeRecipient common.Address) ([]*types.Log, error) { + snap := b.state.Snapshot() + vmRunner := w.chain.NewEVMRunner(b.header, b.state) + + receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &txFeeRecipient, b.gasPool, b.state, b.header, tx, &b.header.GasUsed, *w.chain.GetVMConfig(), vmRunner, b.sysCtx) + if err != nil { + b.state.RevertToSnapshot(snap) + return nil, err + } + b.txs = append(b.txs, tx) + b.receipts = append(b.receipts, receipt) + + return receipt.Logs, nil +} + +// finalizeAndAssemble runs post-transaction state modification and assembles the final block. +func (b *blockState) finalizeAndAssemble(w *worker) (*types.Block, error) { + block, err := w.engine.FinalizeAndAssemble(w.chain, b.header, b.state, b.txs, b.receipts, b.randomness) + if err != nil { + return nil, fmt.Errorf("Error in FinalizeAndAssemble: %w", err) + } + + // Set the validator set diff in the new header if we're using Istanbul and it's the last block of the epoch + if istanbul, ok := w.engine.(consensus.Istanbul); ok { + if err := istanbul.UpdateValSetDiff(w.chain, block.MutableHeader(), b.state); err != nil { + return nil, fmt.Errorf("Unable to update Validator Set Diff: %w", err) + } + } + // FinalizeAndAssemble adds the "block receipt" to then calculate the Bloom filter and receipts hash. + // But it doesn't return the receipts. So we have to add the "block receipt" to b.receipts here, for + // use in calculating the "pending" block (and also in the `task`, though we could remove it from that). + b.receipts = core.AddBlockReceipt(b.receipts, b.state, block.Hash()) + + return block, nil +} + +// totalFees computes total consumed fees in CELO. Block transactions and receipts have to have the same order. +func totalFees(block *types.Block, receipts []*types.Receipt, baseFeeFn func(*common.Address) *big.Int, toCELO func(*big.Int, *common.Address) *big.Int, espresso bool) *big.Float { + feesWei := new(big.Int) + for i, tx := range block.Transactions() { + var basefee *big.Int + if espresso { + basefee = baseFeeFn(tx.FeeCurrency()) + } + fee := toCELO(new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), tx.EffectiveGasTipValue(basefee)), tx.FeeCurrency()) + feesWei.Add(feesWei, fee) + } + return new(big.Float).Quo(new(big.Float).SetInt(feesWei), new(big.Float).SetInt(big.NewInt(params.Ether))) +} + +// createConversionFunctions creates a function to convert any currency to Celo and a function to get the gas price minimum for that currency. +// Both functions internally cache their results. +func createConversionFunctions(sysCtx *core.SysContractCallCtx, chain *core.BlockChain, header *types.Header, state *state.StateDB) (func(feeCurrency *common.Address) *big.Int, func(amount *big.Int, feeCurrency *common.Address) *big.Int) { + vmRunner := chain.NewEVMRunner(header, state) + currencyManager := currency.NewManager(vmRunner) + + baseFeeFn := func(feeCurrency *common.Address) *big.Int { + return sysCtx.GetGasPriceMinimum(feeCurrency) + } + toCeloFn := func(amount *big.Int, feeCurrency *common.Address) *big.Int { + curr, _ := currencyManager.GetCurrency(feeCurrency) + return curr.ToCELO(amount) + } + + return baseFeeFn, toCeloFn +} + +func (b *blockState) close() { + b.state.StopPrefetcher() +}
diff --git go-ethereum/miner/miner_test.go celo/miner/miner_test.go index ae29b2e1699af10ba45d1de87fbd20c238bb9521..d524fd88db93af6aca53b25b04e849f29c0f5bd1 100644 --- go-ethereum/miner/miner_test.go +++ celo/miner/miner_test.go @@ -22,7 +22,9 @@ "testing" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/clique" + "github.com/ethereum/go-ethereum/consensus" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/contracts/testutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -58,12 +60,11 @@ type testBlockChain struct { statedb *state.StateDB gasLimit uint64 chainHeadFeed *event.Feed + celoMock testutil.CeloMock }   func (bc *testBlockChain) CurrentBlock() *types.Block { - return types.NewBlock(&types.Header{ - GasLimit: bc.gasLimit, - }, nil, nil, nil, trie.NewStackTrie(nil)) + return types.NewBlock(&types.Header{}, nil, nil, nil, trie.NewStackTrie(nil)) }   func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { @@ -78,9 +79,26 @@ func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { return bc.chainHeadFeed.Subscribe(ch) }   +func (bc *testBlockChain) Engine() consensus.Engine { + return mockEngine.NewFaker() +} + +func (bc *testBlockChain) GetHeader(common.Hash, uint64) *types.Header { + return nil +} + +func (bc *testBlockChain) NewEVMRunner(header *types.Header, state vm.StateDB) vm.EVMRunner { + return bc.celoMock.Runner +} + +func (bc *testBlockChain) GetVMConfig() *vm.Config { + return nil +} + func TestMiner(t *testing.T) { miner, mux := createMiner(t) - miner.Start(common.HexToAddress("0x12345")) + validatorAddress := common.HexToAddress("0x12345") + miner.Start(validatorAddress, validatorAddress) waitForMiningState(t, miner, true) // Start the downloader mux.Post(downloader.StartEvent{}) @@ -107,7 +125,8 @@ // An initial FailedEvent should allow mining to stop on a subsequent // downloader StartEvent. func TestMinerDownloaderFirstFails(t *testing.T) { miner, mux := createMiner(t) - miner.Start(common.HexToAddress("0x12345")) + validatorAddress := common.HexToAddress("0x12345") + miner.Start(validatorAddress, validatorAddress) waitForMiningState(t, miner, true) // Start the downloader mux.Post(downloader.StartEvent{}) @@ -139,7 +158,8 @@ func TestMinerStartStopAfterDownloaderEvents(t *testing.T) { miner, mux := createMiner(t)   - miner.Start(common.HexToAddress("0x12345")) + validatorAddress := common.HexToAddress("0x12345") + miner.Start(validatorAddress, validatorAddress) waitForMiningState(t, miner, true) // Start the downloader mux.Post(downloader.StartEvent{}) @@ -152,7 +172,8 @@ miner.Stop() waitForMiningState(t, miner, false)   - miner.Start(common.HexToAddress("0x678910")) + validator2Address := common.HexToAddress("0x678910") + miner.Start(validator2Address, validator2Address) waitForMiningState(t, miner, true)   miner.Stop() @@ -162,20 +183,22 @@ func TestStartWhileDownload(t *testing.T) { miner, mux := createMiner(t) waitForMiningState(t, miner, false) - miner.Start(common.HexToAddress("0x12345")) + validatorAddress := common.HexToAddress("0x12345") + miner.Start(validatorAddress, validatorAddress) waitForMiningState(t, miner, true) // Stop the downloader and wait for the update loop to run mux.Post(downloader.StartEvent{}) waitForMiningState(t, miner, false) // Starting the miner after the downloader should not work - miner.Start(common.HexToAddress("0x12345")) + miner.Start(validatorAddress, validatorAddress) waitForMiningState(t, miner, false) }   func TestStartStopMiner(t *testing.T) { miner, _ := createMiner(t) waitForMiningState(t, miner, false) - miner.Start(common.HexToAddress("0x12345")) + validatorAddress := common.HexToAddress("0x12345") + miner.Start(validatorAddress, validatorAddress) waitForMiningState(t, miner, true) miner.Stop() waitForMiningState(t, miner, false) @@ -184,32 +207,35 @@ func TestCloseMiner(t *testing.T) { miner, _ := createMiner(t) waitForMiningState(t, miner, false) - miner.Start(common.HexToAddress("0x12345")) + validatorAddress := common.HexToAddress("0x12345") + miner.Start(validatorAddress, validatorAddress) waitForMiningState(t, miner, true) // Terminate the miner and wait for the update loop to run miner.Close() waitForMiningState(t, miner, false) }   -// TestMinerSetEtherbase checks that etherbase becomes set even if mining isn't +// TestMinerSetEtherbase checks that validator becomes set even if mining isn't // possible at the moment -func TestMinerSetEtherbase(t *testing.T) { +func TestMinerSetValidator(t *testing.T) { miner, mux := createMiner(t) // Start with a 'bad' mining address - miner.Start(common.HexToAddress("0xdead")) + deadAddress := common.HexToAddress("0xdead") + miner.Start(deadAddress, deadAddress) waitForMiningState(t, miner, true) // Start the downloader mux.Post(downloader.StartEvent{}) waitForMiningState(t, miner, false) // Now user tries to configure proper mining address - miner.Start(common.HexToAddress("0x1337")) + validatorAddress := common.HexToAddress("0x1337") + miner.Start(validatorAddress, validatorAddress) // Stop the downloader and wait for the update loop to run mux.Post(downloader.DoneEvent{})   waitForMiningState(t, miner, true) // The miner should now be using the good address - if got, exp := miner.coinbase, common.HexToAddress("0x1337"); got != exp { - t.Fatalf("Wrong coinbase, got %x expected %x", got, exp) + if got, exp := miner.validator, validatorAddress; got != exp { + t.Fatalf("Wrong validator, got %x expected %x", got, exp) } }   @@ -232,25 +258,25 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux) { // Create Ethash config config := Config{ - Etherbase: common.HexToAddress("123456789"), + Validator: common.HexToAddress("123456789"), } // Create chainConfig memdb := memorydb.New() chainDB := rawdb.NewDatabase(memdb) - genesis := core.DeveloperGenesisBlock(15, common.HexToAddress("12345")) + genesis := core.DeveloperGenesisBlock(1) chainConfig, _, err := core.SetupGenesisBlock(chainDB, genesis) if err != nil { t.Fatalf("can't create new chain config: %v", err) } // Create consensus engine - engine := clique.New(chainConfig.Clique, chainDB) + engine := mockEngine.NewFaker() // Create Ethereum backend bc, err := core.NewBlockChain(chainDB, nil, chainConfig, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("can't create new chain %v", err) } statedb, _ := state.New(common.Hash{}, state.NewDatabase(chainDB), nil) - blockchain := &testBlockChain{statedb, 10000000, new(event.Feed)} + blockchain := &testBlockChain{statedb, 10000000, new(event.Feed), testutil.NewCeloMock()}   pool := core.NewTxPool(testTxPoolConfig, chainConfig, blockchain) backend := NewMockBackend(bc, pool)
diff --git go-ethereum/miner/unconfirmed_test.go celo/miner/unconfirmed_test.go deleted file mode 100644 index dc83cb92652d37dd775ad69674a53b4567dd85b3..0000000000000000000000000000000000000000 --- go-ethereum/miner/unconfirmed_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package miner - -import ( - "testing" - - "github.com/ethereum/go-ethereum/core/types" -) - -// noopChainRetriever is an implementation of headerRetriever that always -// returns nil for any requested headers. -type noopChainRetriever struct{} - -func (r *noopChainRetriever) GetHeaderByNumber(number uint64) *types.Header { - return nil -} -func (r *noopChainRetriever) GetBlockByNumber(number uint64) *types.Block { - return nil -} - -// Tests that inserting blocks into the unconfirmed set accumulates them until -// the desired depth is reached, after which they begin to be dropped. -func TestUnconfirmedInsertBounds(t *testing.T) { - limit := uint(10) - - pool := newUnconfirmedBlocks(new(noopChainRetriever), limit) - for depth := uint64(0); depth < 2*uint64(limit); depth++ { - // Insert multiple blocks for the same level just to stress it - for i := 0; i < int(depth); i++ { - pool.Insert(depth, [32]byte{byte(depth), byte(i)}) - } - // Validate that no blocks below the depth allowance are left in - pool.blocks.Do(func(block interface{}) { - if block := block.(*unconfirmedBlock); block.index+uint64(limit) <= depth { - t.Errorf("depth %d: block %x not dropped", depth, block.hash) - } - }) - } -} - -// Tests that shifting blocks out of the unconfirmed set works both for normal -// cases as well as for corner cases such as empty sets, empty shifts or full -// shifts. -func TestUnconfirmedShifts(t *testing.T) { - // Create a pool with a few blocks on various depths - limit, start := uint(10), uint64(25) - - pool := newUnconfirmedBlocks(new(noopChainRetriever), limit) - for depth := start; depth < start+uint64(limit); depth++ { - pool.Insert(depth, [32]byte{byte(depth)}) - } - // Try to shift below the limit and ensure no blocks are dropped - pool.Shift(start + uint64(limit) - 1) - if n := pool.blocks.Len(); n != int(limit) { - t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit) - } - // Try to shift half the blocks out and verify remainder - pool.Shift(start + uint64(limit) - 1 + uint64(limit/2)) - if n := pool.blocks.Len(); n != int(limit)/2 { - t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit/2) - } - // Try to shift all the remaining blocks out and verify emptyness - pool.Shift(start + 2*uint64(limit)) - if n := pool.blocks.Len(); n != 0 { - t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0) - } - // Try to shift out from the empty set and make sure it doesn't break - pool.Shift(start + 3*uint64(limit)) - if n := pool.blocks.Len(); n != 0 { - t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0) - } -}
diff --git go-ethereum/miner/stress/ethash/main.go celo/miner/stress/ethash/main.go deleted file mode 100644 index c44241666f6abc753373bf3cd02d59a64b18b8f1..0000000000000000000000000000000000000000 --- go-ethereum/miner/stress/ethash/main.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// This file contains a miner stress test based on the Ethash consensus engine. -package main - -import ( - "crypto/ecdsa" - "io/ioutil" - "math/big" - "math/rand" - "os" - "path/filepath" - "time" - - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/fdlimit" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/miner" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/params" -) - -func main() { - log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) - fdlimit.Raise(2048) - - // Generate a batch of accounts to seal and fund with - faucets := make([]*ecdsa.PrivateKey, 128) - for i := 0; i < len(faucets); i++ { - faucets[i], _ = crypto.GenerateKey() - } - // Pre-generate the ethash mining DAG so we don't race - ethash.MakeDataset(1, filepath.Join(os.Getenv("HOME"), ".ethash")) - - // Create an Ethash network based off of the Ropsten config - genesis := makeGenesis(faucets) - - var ( - nodes []*eth.Ethereum - enodes []*enode.Node - ) - for i := 0; i < 4; i++ { - // Start the node and wait until it's up - stack, ethBackend, err := makeMiner(genesis) - if err != nil { - panic(err) - } - defer stack.Close() - - for stack.Server().NodeInfo().Ports.Listener == 0 { - time.Sleep(250 * time.Millisecond) - } - // Connect the node to all the previous ones - for _, n := range enodes { - stack.Server().AddPeer(n) - } - // Start tracking the node and its enode - nodes = append(nodes, ethBackend) - enodes = append(enodes, stack.Server().Self()) - - // Inject the signer key and start sealing with it - store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - if _, err := store.NewAccount(""); err != nil { - panic(err) - } - } - - // Iterate over all the nodes and start mining - time.Sleep(3 * time.Second) - for _, node := range nodes { - if err := node.StartMining(1); err != nil { - panic(err) - } - } - time.Sleep(3 * time.Second) - - // Start injecting transactions from the faucets like crazy - nonces := make([]uint64, len(faucets)) - for { - // Pick a random mining node - index := rand.Intn(len(faucets)) - backend := nodes[index%len(nodes)] - - // Create a self transaction and inject into the pool - tx, err := types.SignTx(types.NewTransaction(nonces[index], crypto.PubkeyToAddress(faucets[index].PublicKey), new(big.Int), 21000, big.NewInt(100000000000+rand.Int63n(65536)), nil), types.HomesteadSigner{}, faucets[index]) - if err != nil { - panic(err) - } - if err := backend.TxPool().AddLocal(tx); err != nil { - panic(err) - } - nonces[index]++ - - // Wait if we're too saturated - if pend, _ := backend.TxPool().Stats(); pend > 2048 { - time.Sleep(100 * time.Millisecond) - } - } -} - -// makeGenesis creates a custom Ethash genesis block based on some pre-defined -// faucet accounts. -func makeGenesis(faucets []*ecdsa.PrivateKey) *core.Genesis { - genesis := core.DefaultRopstenGenesisBlock() - genesis.Difficulty = params.MinimumDifficulty - genesis.GasLimit = 25000000 - - genesis.Config.ChainID = big.NewInt(18) - genesis.Config.EIP150Hash = common.Hash{} - - genesis.Alloc = core.GenesisAlloc{} - for _, faucet := range faucets { - genesis.Alloc[crypto.PubkeyToAddress(faucet.PublicKey)] = core.GenesisAccount{ - Balance: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil), - } - } - return genesis -} - -func makeMiner(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) { - // Define the basic configurations for the Ethereum node - datadir, _ := ioutil.TempDir("", "") - - config := &node.Config{ - Name: "geth", - Version: params.Version, - DataDir: datadir, - P2P: p2p.Config{ - ListenAddr: "0.0.0.0:0", - NoDiscovery: true, - MaxPeers: 25, - }, - UseLightweightKDF: true, - } - // Create the node and configure a full Ethereum node on it - stack, err := node.New(config) - if err != nil { - return nil, nil, err - } - ethBackend, err := eth.New(stack, &ethconfig.Config{ - Genesis: genesis, - NetworkId: genesis.Config.ChainID.Uint64(), - SyncMode: downloader.FullSync, - DatabaseCache: 256, - DatabaseHandles: 256, - TxPool: core.DefaultTxPoolConfig, - GPO: ethconfig.Defaults.GPO, - Ethash: ethconfig.Defaults.Ethash, - Miner: miner.Config{ - GasCeil: genesis.GasLimit * 11 / 10, - GasPrice: big.NewInt(1), - Recommit: time.Second, - }, - }) - if err != nil { - return nil, nil, err - } - - err = stack.Start() - return stack, ethBackend, err -}
diff --git go-ethereum/miner/stress/clique/main.go celo/miner/stress/clique/main.go deleted file mode 100644 index 19da81fb50324ef48f2f23dee90da442595c729d..0000000000000000000000000000000000000000 --- go-ethereum/miner/stress/clique/main.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// This file contains a miner stress test based on the Clique consensus engine. -package main - -import ( - "bytes" - "crypto/ecdsa" - "io/ioutil" - "math/big" - "math/rand" - "os" - "time" - - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/fdlimit" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/miner" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/params" -) - -func main() { - log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) - fdlimit.Raise(2048) - - // Generate a batch of accounts to seal and fund with - faucets := make([]*ecdsa.PrivateKey, 128) - for i := 0; i < len(faucets); i++ { - faucets[i], _ = crypto.GenerateKey() - } - sealers := make([]*ecdsa.PrivateKey, 4) - for i := 0; i < len(sealers); i++ { - sealers[i], _ = crypto.GenerateKey() - } - // Create a Clique network based off of the Rinkeby config - genesis := makeGenesis(faucets, sealers) - - var ( - nodes []*eth.Ethereum - enodes []*enode.Node - ) - - for _, sealer := range sealers { - // Start the node and wait until it's up - stack, ethBackend, err := makeSealer(genesis) - if err != nil { - panic(err) - } - defer stack.Close() - - for stack.Server().NodeInfo().Ports.Listener == 0 { - time.Sleep(250 * time.Millisecond) - } - // Connect the node to all the previous ones - for _, n := range enodes { - stack.Server().AddPeer(n) - } - // Start tracking the node and its enode - nodes = append(nodes, ethBackend) - enodes = append(enodes, stack.Server().Self()) - - // Inject the signer key and start sealing with it - store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - signer, err := store.ImportECDSA(sealer, "") - if err != nil { - panic(err) - } - if err := store.Unlock(signer, ""); err != nil { - panic(err) - } - } - - // Iterate over all the nodes and start signing on them - time.Sleep(3 * time.Second) - for _, node := range nodes { - if err := node.StartMining(1); err != nil { - panic(err) - } - } - time.Sleep(3 * time.Second) - - // Start injecting transactions from the faucet like crazy - nonces := make([]uint64, len(faucets)) - for { - // Pick a random signer node - index := rand.Intn(len(faucets)) - backend := nodes[index%len(nodes)] - - // Create a self transaction and inject into the pool - tx, err := types.SignTx(types.NewTransaction(nonces[index], crypto.PubkeyToAddress(faucets[index].PublicKey), new(big.Int), 21000, big.NewInt(100000000000), nil), types.HomesteadSigner{}, faucets[index]) - if err != nil { - panic(err) - } - if err := backend.TxPool().AddLocal(tx); err != nil { - panic(err) - } - nonces[index]++ - - // Wait if we're too saturated - if pend, _ := backend.TxPool().Stats(); pend > 2048 { - time.Sleep(100 * time.Millisecond) - } - } -} - -// makeGenesis creates a custom Clique genesis block based on some pre-defined -// signer and faucet accounts. -func makeGenesis(faucets []*ecdsa.PrivateKey, sealers []*ecdsa.PrivateKey) *core.Genesis { - // Create a Clique network based off of the Rinkeby config - genesis := core.DefaultRinkebyGenesisBlock() - genesis.GasLimit = 25000000 - - genesis.Config.ChainID = big.NewInt(18) - genesis.Config.Clique.Period = 1 - genesis.Config.EIP150Hash = common.Hash{} - - genesis.Alloc = core.GenesisAlloc{} - for _, faucet := range faucets { - genesis.Alloc[crypto.PubkeyToAddress(faucet.PublicKey)] = core.GenesisAccount{ - Balance: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil), - } - } - // Sort the signers and embed into the extra-data section - signers := make([]common.Address, len(sealers)) - for i, sealer := range sealers { - signers[i] = crypto.PubkeyToAddress(sealer.PublicKey) - } - for i := 0; i < len(signers); i++ { - for j := i + 1; j < len(signers); j++ { - if bytes.Compare(signers[i][:], signers[j][:]) > 0 { - signers[i], signers[j] = signers[j], signers[i] - } - } - } - genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65) - for i, signer := range signers { - copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:]) - } - // Return the genesis block for initialization - return genesis -} - -func makeSealer(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) { - // Define the basic configurations for the Ethereum node - datadir, _ := ioutil.TempDir("", "") - - config := &node.Config{ - Name: "geth", - Version: params.Version, - DataDir: datadir, - P2P: p2p.Config{ - ListenAddr: "0.0.0.0:0", - NoDiscovery: true, - MaxPeers: 25, - }, - } - // Start the node and configure a full Ethereum node on it - stack, err := node.New(config) - if err != nil { - return nil, nil, err - } - // Create and register the backend - ethBackend, err := eth.New(stack, &ethconfig.Config{ - Genesis: genesis, - NetworkId: genesis.Config.ChainID.Uint64(), - SyncMode: downloader.FullSync, - DatabaseCache: 256, - DatabaseHandles: 256, - TxPool: core.DefaultTxPoolConfig, - GPO: ethconfig.Defaults.GPO, - Miner: miner.Config{ - GasCeil: genesis.GasLimit * 11 / 10, - GasPrice: big.NewInt(1), - Recommit: time.Second, - }, - }) - if err != nil { - return nil, nil, err - } - - err = stack.Start() - return stack, ethBackend, err -}
diff --git go-ethereum/miner/stress/1559/main.go celo/miner/stress/1559/main.go index e93f00612948833827fd6bfc5197e98c8b7d440a..fc43575a06ae5e85d5dc3bd6d5e07dfa345f113e 100644 --- go-ethereum/miner/stress/1559/main.go +++ celo/miner/stress/1559/main.go @@ -23,13 +23,11 @@ "io/ioutil" "math/big" "math/rand" "os" - "path/filepath" "time"   "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" - "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -37,7 +35,6 @@ "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" @@ -45,7 +42,7 @@ "github.com/ethereum/go-ethereum/params" )   var ( - londonBlock = big.NewInt(30) // Predefined london fork block for activating eip 1559. + espressoBlock = big.NewInt(30) // Predefined london fork block for activating eip 1559. )   func main() { @@ -57,8 +54,6 @@ faucets := make([]*ecdsa.PrivateKey, 128) for i := 0; i < len(faucets); i++ { faucets[i], _ = crypto.GenerateKey() } - // Pre-generate the ethash mining DAG so we don't race - ethash.MakeDataset(1, filepath.Join(os.Getenv("HOME"), ".ethash"))   // Create an Ethash network based off of the Ropsten config genesis := makeGenesis(faucets) @@ -80,7 +75,7 @@ time.Sleep(250 * time.Millisecond) } // Connect the node to all the previous ones for _, n := range enodes { - stack.Server().AddPeer(n) + stack.Server().AddPeer(n, p2p.NoPurpose) } // Start tracking the node and its enode nodes = append(nodes, ethBackend) @@ -96,7 +91,7 @@ // Iterate over all the nodes and start mining time.Sleep(3 * time.Second) for _, node := range nodes { - if err := node.StartMining(1); err != nil { + if err := node.StartMining(); err != nil { panic(err) } } @@ -115,8 +110,10 @@ // Pick a random mining node index := rand.Intn(len(faucets)) backend := nodes[index%len(nodes)]   - headHeader := backend.BlockChain().CurrentHeader() - baseFee := headHeader.BaseFee + // headHeader := backend.BlockChain().CurrentHeader() + // baseFee := headHeader.BaseFee + // TODO: Get GPM here + var baseFee *big.Int   // Create a self transaction and inject into the pool. The legacy // and 1559 transactions can all be created by random even if the @@ -142,7 +139,7 @@ func makeTransaction(nonce uint64, privKey *ecdsa.PrivateKey, signer types.Signer, baseFee *big.Int) *types.Transaction { // Generate legacy transaction if rand.Intn(2) == 0 { - tx, err := types.SignTx(types.NewTransaction(nonce, crypto.PubkeyToAddress(privKey.PublicKey), new(big.Int), 21000, big.NewInt(100000000000+rand.Int63n(65536)), nil), signer, privKey) + tx, err := types.SignTx(types.NewTransaction(nonce, crypto.PubkeyToAddress(privKey.PublicKey), new(big.Int), 21000, big.NewInt(100000000000+rand.Int63n(65536)), nil, nil, nil, nil), signer, privKey) if err != nil { panic(err) } @@ -186,14 +183,10 @@ // makeGenesis creates a custom Ethash genesis block based on some pre-defined // faucet accounts. func makeGenesis(faucets []*ecdsa.PrivateKey) *core.Genesis { - genesis := core.DefaultRopstenGenesisBlock() + genesis := core.DefaultBaklavaGenesisBlock()   - genesis.Config = params.AllEthashProtocolChanges - genesis.Config.LondonBlock = londonBlock - genesis.Difficulty = params.MinimumDifficulty - - // Small gaslimit for easier basefee moving testing. - genesis.GasLimit = 8_000_000 + genesis.Config = params.BaklavaChainConfig + genesis.Config.EspressoBlock = espressoBlock   genesis.Config.ChainID = big.NewInt(18) genesis.Config.EIP150Hash = common.Hash{} @@ -204,10 +197,10 @@ genesis.Alloc[crypto.PubkeyToAddress(faucet.PublicKey)] = core.GenesisAccount{ Balance: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil), } } - if londonBlock.Sign() == 0 { + if espressoBlock.Sign() == 0 { log.Info("Enabled the eip 1559 by default") } else { - log.Info("Registered the london fork", "number", londonBlock) + log.Info("Registered the london fork", "number", espressoBlock) } return genesis } @@ -239,13 +232,6 @@ SyncMode: downloader.FullSync, DatabaseCache: 256, DatabaseHandles: 256, TxPool: core.DefaultTxPoolConfig, - GPO: ethconfig.Defaults.GPO, - Ethash: ethconfig.Defaults.Ethash, - Miner: miner.Config{ - GasCeil: genesis.GasLimit * 11 / 10, - GasPrice: big.NewInt(1), - Recommit: time.Second, - }, }) if err != nil { return nil, nil, err
diff --git go-ethereum/p2p/metrics.go celo/p2p/metrics.go index 5ebb9ba8952ac6da336ac359a9a5226ccdabebac..e582c560b535b4085a88244df7d3177d5c5378f6 100644 --- go-ethereum/p2p/metrics.go +++ celo/p2p/metrics.go @@ -37,10 +37,15 @@ )   var ( ingressConnectMeter = metrics.NewRegisteredMeter("p2p/serves", nil) + ingressConnectWithHandshakeMeter = metrics.NewRegisteredMeter("p2p/serves/handshakes", nil) // Meter counting the ingress with successful handshake connections ingressTrafficMeter = metrics.NewRegisteredMeter(ingressMeterName, nil) egressConnectMeter = metrics.NewRegisteredMeter("p2p/dials", nil) + egressConnectWithHandshakeMeter = metrics.NewRegisteredMeter("p2p/dials/handshakes", nil) // Meter counting the egress with successful handshake connections egressTrafficMeter = metrics.NewRegisteredMeter(egressMeterName, nil) activePeerGauge = metrics.NewRegisteredGauge("p2p/peers", nil) + activeValidatorsPeerGauge = metrics.NewRegisteredGauge("p2p/peers/validators", nil) // Gauge tracking the current validators peer count + activeProxiesPeerGauge = metrics.NewRegisteredGauge("p2p/peers/proxies", nil) // Gauge tracking the current proxies peer count + discoveredPeersCounter = metrics.NewRegisteredCounter("p2p/peers/discovered", nil) // Counter of the total discovered peers )   // meteredConn is a wrapper around a net.Conn that meters both the
diff --git go-ethereum/p2p/peer_test.go celo/p2p/peer_test.go index 8ff60e4dfe73ca599d8db274672cd63b444807eb..cf5dd9c53d59916175f0db23b8a2fcab429b9b08 100644 --- go-ethereum/p2p/peer_test.go +++ celo/p2p/peer_test.go @@ -100,7 +100,7 @@ c1.caps = append(c1.caps, p.cap()) c2.caps = append(c2.caps, p.cap()) }   - peer := newPeer(log.Root(), c1, protos) + peer := newPeer(log.Root(), c1, protos, NoPurpose, nil) errc := make(chan error, 1) go func() { _, err := peer.run()
diff --git go-ethereum/p2p/dial.go celo/p2p/dial.go index 8170e89cd5412bd3468522864114281a63818f86..c03a0974bc82c586a77a04ecd00ce17b18d9623f 100644 --- go-ethereum/p2p/dial.go +++ celo/p2p/dial.go @@ -112,7 +112,7 @@ dialPeers int // current number of dialed peers   // The static map tracks all static dial tasks. The subset of usable static dial tasks // (i.e. those passing checkDial) is kept in staticPool. The scheduler prefers - // launching random static tasks from the pool over launching dynamic dials from the + // launching static tasks from the pool over launching dynamic dials from the // iterator. static map[enode.ID]*dialTask staticPool []*dialTask @@ -139,6 +139,9 @@ dialer NodeDialer log log.Logger clock mclock.Clock rand *mrand.Rand + + // The time waited before redialling a certain node + dialHistoryExpiration time.Duration }   func (cfg dialConfig) withDefaults() dialConfig { @@ -157,6 +160,9 @@ crand.Read(seedb) seed := int64(binary.BigEndian.Uint64(seedb)) cfg.rand = mrand.New(mrand.NewSource(seed)) } + if cfg.dialHistoryExpiration == 0 { + cfg.dialHistoryExpiration = dialHistoryExpiration + } return cfg }   @@ -231,7 +237,7 @@ loop: for { // Launch new dials if slots are available. slots := d.freeDialSlots() - slots -= d.startStaticDials(slots) + slots -= d.startStaticDials() if slots > 0 { nodesCh = d.nodesIn } else { @@ -320,6 +326,7 @@ func (d *dialScheduler) readNodes(it enode.Iterator) { defer d.wg.Done()   for it.Next() { + discoveredPeersCounter.Inc(1) select { case d.nodesIn <- it.Node(): case <-d.ctx.Done(): @@ -374,9 +381,10 @@ }) }   // freeDialSlots returns the number of free dial slots. The result can be negative -// when peers are connected while their task is still running. +// when peers are connected while their task is still running, or because static dials +// are exempt from the limit. func (d *dialScheduler) freeDialSlots() int { - slots := (d.maxDialPeers - d.dialPeers) * 2 + slots := d.maxDialPeers - d.dialPeers if slots > d.maxActiveDials { slots = d.maxActiveDials } @@ -410,9 +418,10 @@ } return nil }   -// startStaticDials starts n static dial tasks. -func (d *dialScheduler) startStaticDials(n int) (started int) { - for started = 0; started < n && len(d.staticPool) > 0; started++ { +// startStaticDials starts static dials nodes in the static pool, subject to the maxActiveDials limit +func (d *dialScheduler) startStaticDials() (started int) { + limit := d.maxActiveDials - len(d.dialing) + for started = 0; started < limit && len(d.staticPool) > 0; started++ { idx := d.rand.Intn(len(d.staticPool)) task := d.staticPool[idx] d.startDial(task) @@ -453,7 +462,7 @@ // startDial runs the given dial task in a separate goroutine. func (d *dialScheduler) startDial(task *dialTask) { d.log.Trace("Starting p2p dial", "id", task.dest.ID(), "ip", task.dest.IP(), "flag", task.flags) hkey := string(task.dest.ID().Bytes()) - d.history.add(hkey, d.clock.Now().Add(dialHistoryExpiration)) + d.history.add(hkey, d.clock.Now().Add(d.dialHistoryExpiration)) d.dialing[task.dest.ID()] = task go func() { task.run(d)
diff --git go-ethereum/p2p/dial_test.go celo/p2p/dial_test.go index 6d4a876573eb783ffbf06245a38f27c57bd4fb1d..74875c553f6c2326fcb4f64fb59fd85c15e5b4d6 100644 --- go-ethereum/p2p/dial_test.go +++ celo/p2p/dial_test.go @@ -39,30 +39,32 @@ func TestDialSchedDynDial(t *testing.T) { t.Parallel()   config := dialConfig{ - maxActiveDials: 5, + maxActiveDials: 2, maxDialPeers: 4, } runDialTest(t, config, []dialTestRound{ - // 3 out of 4 peers are connected, leaving 2 dial slots. - // 9 nodes are discovered, but only 2 are dialed. + // 3 out of 4 peers are connected, leaving 1 dial slot. + // 9 nodes are discovered, but only 1 is dialed. { + update: func(d *dialScheduler) { + d.addStatic(newNode(uintID(0x01), "127.0.0.1:30303")) + }, peersAdded: []*conn{ - {flags: staticDialedConn, node: newNode(uintID(0x00), "")}, - {flags: dynDialedConn, node: newNode(uintID(0x01), "")}, + {flags: staticDialedConn, node: newNode(uintID(0x01), "")}, {flags: dynDialedConn, node: newNode(uintID(0x02), "")}, + {flags: dynDialedConn, node: newNode(uintID(0x03), "")}, }, discovered: []*enode.Node{ - newNode(uintID(0x00), "127.0.0.1:30303"), // not dialed because already connected as static peer - newNode(uintID(0x02), "127.0.0.1:30303"), // ... - newNode(uintID(0x03), "127.0.0.1:30303"), + newNode(uintID(0x01), "127.0.0.1:30303"), // not dialed because already connected as static peer + newNode(uintID(0x03), "127.0.0.1:30303"), // ... newNode(uintID(0x04), "127.0.0.1:30303"), - newNode(uintID(0x05), "127.0.0.1:30303"), // not dialed because there are only two slots + newNode(uintID(0x05), "127.0.0.1:30303"), // not dialed because there is only one slot newNode(uintID(0x06), "127.0.0.1:30303"), // ... newNode(uintID(0x07), "127.0.0.1:30303"), // ... newNode(uintID(0x08), "127.0.0.1:30303"), // ... + newNode(uintID(0x09), "127.0.0.1:30303"), // ... }, wantNewDials: []*enode.Node{ - newNode(uintID(0x03), "127.0.0.1:30303"), newNode(uintID(0x04), "127.0.0.1:30303"), }, }, @@ -77,40 +79,34 @@ newNode(uintID(0x05), "127.0.0.1:30303"), }, },   - // Dial to 0x03 completes, filling the last remaining peer slot. + // Dial to 0x05 completes, filling the last remaining peer slot. { succeeded: []enode.ID{ - uintID(0x03), - }, - failed: []enode.ID{ uintID(0x05), }, - discovered: []*enode.Node{ - newNode(uintID(0x09), "127.0.0.1:30303"), // not dialed because there are no free slots - }, },   - // 3 peers drop off, creating 6 dial slots. Check that 5 of those slots + // 3 peers drop off, creating 3 dial slots. Check that 2 of those slots // (i.e. up to maxActiveDialTasks) are used. { peersRemoved: []enode.ID{ - uintID(0x00), - uintID(0x01), uintID(0x02), - }, - discovered: []*enode.Node{ - newNode(uintID(0x0a), "127.0.0.1:30303"), - newNode(uintID(0x0b), "127.0.0.1:30303"), - newNode(uintID(0x0c), "127.0.0.1:30303"), - newNode(uintID(0x0d), "127.0.0.1:30303"), - newNode(uintID(0x0f), "127.0.0.1:30303"), + uintID(0x03), + uintID(0x05), }, wantNewDials: []*enode.Node{ newNode(uintID(0x06), "127.0.0.1:30303"), newNode(uintID(0x07), "127.0.0.1:30303"), + }, + }, + // The two dials succeed, check that we now dial to fill the 3rd slot + { + succeeded: []enode.ID{ + uintID(0x06), + uintID(0x07), + }, + wantNewDials: []*enode.Node{ newNode(uintID(0x08), "127.0.0.1:30303"), - newNode(uintID(0x09), "127.0.0.1:30303"), - newNode(uintID(0x0a), "127.0.0.1:30303"), }, }, }) @@ -152,7 +148,8 @@ }, }) }   -// This test checks that static dials work and obey the limits. +// This test checks that static dials work and are exempt from the limits, but do +// count towards the limit when considering whether to start dynamic dials func TestDialSchedStaticDial(t *testing.T) { t.Parallel()   @@ -167,55 +164,49 @@ { peersAdded: []*conn{ {flags: dynDialedConn, node: newNode(uintID(0x01), "127.0.0.1:30303")}, {flags: dynDialedConn, node: newNode(uintID(0x02), "127.0.0.2:30303")}, + {flags: dynDialedConn, node: newNode(uintID(0x03), "127.0.0.3:30303")}, }, update: func(d *dialScheduler) { - // These two are not dialed because they're already connected + // These three are not dialed because they're already connected // as dynamic peers. d.addStatic(newNode(uintID(0x01), "127.0.0.1:30303")) d.addStatic(newNode(uintID(0x02), "127.0.0.2:30303")) - // These nodes will be dialed: d.addStatic(newNode(uintID(0x03), "127.0.0.3:30303")) + // These nodes will be dialed, even though only 2 dial slots are available, + // because these are static and are exempt from the limit d.addStatic(newNode(uintID(0x04), "127.0.0.4:30303")) d.addStatic(newNode(uintID(0x05), "127.0.0.5:30303")) d.addStatic(newNode(uintID(0x06), "127.0.0.6:30303")) - d.addStatic(newNode(uintID(0x07), "127.0.0.7:30303")) - d.addStatic(newNode(uintID(0x08), "127.0.0.8:30303")) - d.addStatic(newNode(uintID(0x09), "127.0.0.9:30303")) + }, + discovered: []*enode.Node{ + newNode(uintID(0x07), "127.0.0.7:30303"), + newNode(uintID(0x08), "127.0.0.8:30303"), + newNode(uintID(0x09), "127.0.0.9:30303"), }, wantNewDials: []*enode.Node{ - newNode(uintID(0x03), "127.0.0.3:30303"), newNode(uintID(0x04), "127.0.0.4:30303"), newNode(uintID(0x05), "127.0.0.5:30303"), newNode(uintID(0x06), "127.0.0.6:30303"), }, }, - // Dial to 0x03 completes, filling a peer slot. One slot remains, - // two dials are launched to attempt to fill it. + // Dial to 0x04 completes, filling the last peer slot + // 0x05 are 0x06 are static, but won't be dialed again yet because they're in the history { succeeded: []enode.ID{ - uintID(0x03), + uintID(0x04), }, failed: []enode.ID{ - uintID(0x04), uintID(0x05), uintID(0x06), }, wantResolves: map[enode.ID]*enode.Node{ - uintID(0x04): nil, uintID(0x05): nil, uintID(0x06): nil, }, - wantNewDials: []*enode.Node{ - newNode(uintID(0x08), "127.0.0.8:30303"), - newNode(uintID(0x09), "127.0.0.9:30303"), - }, }, - // Peer 0x01 drops and 0x07 connects as inbound peer. - // Only 0x01 is dialed. + // Peer 0x01 drops, freeing one dial slots. It's filled by 0x01 (static). + // 0x07 is not dialed since there is only one slot. { - peersAdded: []*conn{ - {flags: inboundConn, node: newNode(uintID(0x07), "127.0.0.7:30303")}, - }, peersRemoved: []enode.ID{ uintID(0x01), }, @@ -223,6 +214,13 @@ wantNewDials: []*enode.Node{ newNode(uintID(0x01), "127.0.0.1:30303"), }, }, + // 0x05 and 0x06 will be dialed now, having expired from the history + { + wantNewDials: []*enode.Node{ + newNode(uintID(0x05), "127.0.0.5:30303"), + newNode(uintID(0x06), "127.0.0.6:30303"), + }, + }, }) }   @@ -240,13 +238,12 @@ { update: func(d *dialScheduler) { d.addStatic(newNode(uintID(0x01), "127.0.0.1:30303")) d.addStatic(newNode(uintID(0x02), "127.0.0.2:30303")) - d.addStatic(newNode(uintID(0x03), "127.0.0.3:30303")) }, wantNewDials: []*enode.Node{ newNode(uintID(0x01), "127.0.0.1:30303"), }, }, - // Dial to 0x01 fails. + // Dial to 0x01 fails, freeing up an active dial slot for 0x02 { failed: []enode.ID{ uintID(0x01), @@ -258,14 +255,16 @@ wantNewDials: []*enode.Node{ newNode(uintID(0x02), "127.0.0.2:30303"), }, }, - // All static nodes are removed. 0x01 is in history, 0x02 is being - // dialed, 0x03 is in staticPool. + // All static nodes are removed. 0x01 is in history, 0x02 is being dialed { update: func(d *dialScheduler) { d.removeStatic(newNode(uintID(0x01), "127.0.0.1:30303")) d.removeStatic(newNode(uintID(0x02), "127.0.0.2:30303")) d.removeStatic(newNode(uintID(0x03), "127.0.0.3:30303")) }, + }, + // 0x02 fails and moves to the history, 0x01 is no longer in the history + { failed: []enode.ID{ uintID(0x02), }, @@ -278,34 +277,38 @@ {}, {}, {}, }) }   -// This test checks that static dials are selected at random. +// This test checks that static dials are selected at random up to maxActiveDials limit func TestDialSchedManyStaticNodes(t *testing.T) { t.Parallel()   - config := dialConfig{maxDialPeers: 2} + config := dialConfig{maxDialPeers: 2, maxActiveDials: 2} runDialTest(t, config, []dialTestRound{ { - peersAdded: []*conn{ - {flags: dynDialedConn, node: newNode(uintID(0xFFFE), "")}, - {flags: dynDialedConn, node: newNode(uintID(0xFFFF), "")}, - }, update: func(d *dialScheduler) { for id := uint16(0); id < 2000; id++ { n := newNode(uintID(id), "127.0.0.1:30303") d.addStatic(n) } + }, + // These are dialed in order, because it happens before others are added + wantNewDials: []*enode.Node{ + newNode(uintID(0x0001), "127.0.0.1:30303"), + newNode(uintID(0x0002), "127.0.0.1:30303"), }, }, { - peersRemoved: []enode.ID{ - uintID(0xFFFE), - uintID(0xFFFF), + failed: []enode.ID{ + uintID(0x01), + uintID(0x02), + }, + wantResolves: map[enode.ID]*enode.Node{ + uintID(0x01): nil, + uintID(0x02): nil, }, + // These two will be dialed at random, because the pool has many nodes in it wantNewDials: []*enode.Node{ - newNode(uintID(0x0085), "127.0.0.1:30303"), - newNode(uintID(0x02dc), "127.0.0.1:30303"), - newNode(uintID(0x0285), "127.0.0.1:30303"), - newNode(uintID(0x00cb), "127.0.0.1:30303"), + newNode(uintID(0x0087), "127.0.0.1:30303"), + newNode(uintID(0x02de), "127.0.0.1:30303"), }, }, }) @@ -452,6 +455,7 @@ if c == nil { t.Fatalf("round %d: can't remove non-existent peer %v", i, id) } dialsched.peerRemoved(c) + delete(peers, c.node.ID()) }   // Init round.
diff --git go-ethereum/p2p/server_test.go celo/p2p/server_test.go index 383b773ace72475c96ec4482780b252383a3d58a..9898f56084d7016456c8c963925038e98a80f31b 100644 --- go-ethereum/p2p/server_test.go +++ celo/p2p/server_test.go @@ -149,7 +149,7 @@ // tell the server to connect tcpAddr := listener.Addr().(*net.TCPAddr) node := enode.NewV4(remid, tcpAddr.IP, tcpAddr.Port, 0) - srv.AddPeer(node) + srv.AddPeer(node, ExplicitStaticPurpose)   select { case conn := <-accepted: @@ -179,11 +179,11 @@ t.Errorf("peer is trusted prematurely: %v", peer) } done := make(chan bool) go func() { - srv.AddTrustedPeer(node) + srv.AddTrustedPeer(node, ExplicitTrustedPurpose) if peer := srv.Peers()[0]; !peer.Info().Network.Trusted { t.Errorf("peer is not trusted after AddTrustedPeer: %v", peer) } - srv.RemoveTrustedPeer(node) + srv.RemoveTrustedPeer(node, ExplicitTrustedPurpose) if peer := srv.Peers()[0]; peer.Info().Network.Trusted { t.Errorf("peer is trusted after RemoveTrustedPeer: %v", peer) } @@ -227,12 +227,58 @@ if !syncAddPeer(srv1, srv2.Self()) { t.Fatal("peer not connected") } - srv1.RemovePeer(srv2.Self()) + srv1.RemovePeer(srv2.Self(), ExplicitStaticPurpose) if srv1.PeerCount() > 0 { t.Fatal("removed peer still connected") } }   +// This test checks that RemovePeer returns if trying to remove a peer we're not connected to +func TestServerRemovePeerNotConnected(t *testing.T) { + srv := &Server{Config: Config{ + PrivateKey: newkey(), + MaxPeers: 1, + NoDiscovery: true, + Logger: testlog.Logger(t, log.LvlTrace).New("server", "1"), + }} + srv.Start() + defer srv.Stop() + + peer := enode.NewV4(&newkey().PublicKey, net.IP{127, 0, 0, 1}, 1, 1) + srv.RemovePeer(peer, ValidatorPurpose) +} + +// This test checks that RemovePeer returns (without disconnecting the peer) if the peer still has a purpose +func TestServerRemovePeerNoDisconnect(t *testing.T) { + srv1 := &Server{Config: Config{ + PrivateKey: newkey(), + MaxPeers: 1, + NoDiscovery: true, + Logger: testlog.Logger(t, log.LvlTrace).New("server", "1"), + }} + srv2 := &Server{Config: Config{ + PrivateKey: newkey(), + MaxPeers: 1, + NoDiscovery: true, + NoDial: true, + ListenAddr: "127.0.0.1:0", + Logger: testlog.Logger(t, log.LvlTrace).New("server", "2"), + }} + srv1.Start() + defer srv1.Stop() + srv2.Start() + defer srv2.Stop() + + if !syncAddPeer(srv1, srv2.Self()) { + t.Fatal("peer not connected") + } + + srv1.RemovePeer(srv2.Self(), ValidatorPurpose) + if srv1.PeerCount() == 0 { + t.Fatal("peer was removed, but shouldn't have been") + } +} + // This test checks that connections are disconnected just after the encryption handshake // when the server is at capacity. Trusted connections should still be accepted. func TestServerAtCap(t *testing.T) { @@ -269,12 +315,16 @@ } } // Try inserting a non-trusted connection. anotherID := randomID() + /* KJUE - Re-enable after restoring peer check in server.go c := newconn(anotherID) + if err := srv.checkpoint(c, srv.checkpointPostHandshake); err != DiscTooManyPeers { t.Error("wrong error for insert:", err) } + */ + // Try inserting a trusted connection. - c = newconn(trustedID) + c := newconn(trustedID) if err := srv.checkpoint(c, srv.checkpointPostHandshake); err != nil { t.Error("unexpected error for trusted conn @posthandshake:", err) } @@ -283,14 +333,17 @@ t.Error("Server did not set trusted flag") }   // Remove from trusted set and try again - srv.RemoveTrustedPeer(newNode(trustedID, "")) + /* KJUE - Re-enable after restoring peer check in server.go + srv.RemoveTrustedPeer(newNode(trustedID, ""), ExplicitTrustedPurpose) c = newconn(trustedID) + if err := srv.checkpoint(c, srv.checkpointPostHandshake); err != DiscTooManyPeers { t.Error("wrong error for insert:", err) } + */   // Add anotherID to trusted set and try again - srv.AddTrustedPeer(newNode(anotherID, "")) + srv.AddTrustedPeer(newNode(anotherID, ""), ExplicitTrustedPurpose) c = newconn(anotherID) if err := srv.checkpoint(c, srv.checkpointPostHandshake); err != nil { t.Error("unexpected error for trusted conn @posthandshake:", err) @@ -335,12 +388,15 @@ flags := dynDialedConn dialDest := clientnode conn, _ := net.Pipe() srv.SetupConn(conn, flags, dialDest) + + /* KJUE - Re-enable after restoring peer check in server.go if tp.closeErr != DiscTooManyPeers { t.Errorf("unexpected close error: %q", tp.closeErr) } conn.Close() + */   - srv.AddTrustedPeer(clientnode) + srv.AddTrustedPeer(clientnode, ExplicitTrustedPurpose)   // Check that server allows a trusted peer despite being full. conn, _ = net.Pipe() @@ -354,14 +410,18 @@ t.Errorf("unexpected close error: %q", tp.closeErr) } conn.Close()   - srv.RemoveTrustedPeer(clientnode) + srv.RemoveTrustedPeer(clientnode, ExplicitTrustedPurpose)   // Check that server is full again. conn, _ = net.Pipe() srv.SetupConn(conn, flags, dialDest) + + /* KJUE - Re-enable after restoring peer check in server.go if tp.closeErr != DiscTooManyPeers { t.Errorf("unexpected close error: %q", tp.closeErr) } + */ + conn.Close() }   @@ -607,7 +667,7 @@ sub = srv.SubscribeEvents(ch) timeout = time.After(2 * time.Second) ) defer sub.Unsubscribe() - srv.AddPeer(node) + srv.AddPeer(node, ExplicitStaticPurpose) for { select { case ev := <-ch:
diff --git go-ethereum/p2p/message.go celo/p2p/message.go index f054ad1ca49b1b341671443f1b28add5ef672233..35346db5269ece2dbef535865ae084771b6b3a03 100644 --- go-ethereum/p2p/message.go +++ celo/p2p/message.go @@ -229,6 +229,7 @@ // code and encoded RLP content match the provided values. // If content is nil, the payload is discarded and not verified. func ExpectMsg(r MsgReader, code uint64, content interface{}) error { msg, err := r.ReadMsg() + defer msg.Discard() if err != nil { return err } @@ -236,7 +237,7 @@ if msg.Code != code { return fmt.Errorf("message code mismatch: got %d, expected %d", msg.Code, code) } if content == nil { - return msg.Discard() + return nil } contentEnc, err := rlp.EncodeToBytes(content) if err != nil {
diff --git go-ethereum/p2p/peer_error.go celo/p2p/peer_error.go index dd2bf67347a5f55020dd3af6e2b468b2c017487e..77d620cfc5965bc02873db00d24fbf321b800b52 100644 --- go-ethereum/p2p/peer_error.go +++ celo/p2p/peer_error.go @@ -54,7 +54,7 @@ }   var errProtocolReturned = errors.New("protocol returned")   -type DiscReason uint +type DiscReason uint8   const ( DiscRequested DiscReason = iota @@ -62,6 +62,7 @@ DiscNetworkError DiscProtocolError DiscUselessPeer DiscTooManyPeers + DiscTooManyInboundPeers DiscAlreadyConnected DiscIncompatibleVersion DiscInvalidIdentity @@ -78,6 +79,7 @@ DiscNetworkError: "network error", DiscProtocolError: "breach of protocol", DiscUselessPeer: "useless peer", DiscTooManyPeers: "too many peers", + DiscTooManyInboundPeers: "too many inbound peers", DiscAlreadyConnected: "already connected", DiscIncompatibleVersion: "incompatible p2p protocol version", DiscInvalidIdentity: "invalid node identity",
diff --git go-ethereum/p2p/peer.go celo/p2p/peer.go index 517bdbe2cce25fe18ca39b60a915d98a7b4b67b4..75a3cc3d57389349732efe24d9410446c3eeb834 100644 --- go-ethereum/p2p/peer.go +++ celo/p2p/peer.go @@ -116,6 +116,12 @@ disc chan DiscReason   // events receives message send / receive events if set events *event.Feed + + purposesMu sync.Mutex + purposes PurposeFlag + + Server *Server + testPipe *MsgPipeRW // for testing }   @@ -124,11 +130,47 @@ func NewPeer(id enode.ID, name string, caps []Cap) *Peer { pipe, _ := net.Pipe() node := enode.SignNull(new(enr.Record), id) conn := &conn{fd: pipe, transport: nil, node: node, caps: caps, name: name} - peer := newPeer(log.Root(), conn, nil) + peer := newPeer(log.Root(), conn, nil, NoPurpose, nil) close(peer.closed) // ensures Disconnect doesn't block return peer }   +func (p *Peer) AddPurpose(purpose PurposeFlag) { + p.purposesMu.Lock() + defer p.purposesMu.Unlock() + + // assumes we are still connected... + if purpose.IsSet(ValidatorPurpose) && !p.purposes.IsSet(ValidatorPurpose) { + activeValidatorsPeerGauge.Inc(1) + } + if purpose.IsSet(ProxyPurpose) && !p.purposes.IsSet(ProxyPurpose) { + activeProxiesPeerGauge.Inc(1) + } + + p.purposes = p.purposes.Add(purpose) +} + +func (p *Peer) RemovePurpose(purpose PurposeFlag) { + p.purposesMu.Lock() + defer p.purposesMu.Unlock() + + // assumes we are still connected... + if purpose.IsSet(ValidatorPurpose) && p.purposes.IsSet(ValidatorPurpose) { + activeValidatorsPeerGauge.Dec(1) + } + if purpose.IsSet(ProxyPurpose) && p.purposes.IsSet(ProxyPurpose) { + activeProxiesPeerGauge.Dec(1) + } + + p.purposes = p.purposes.Remove(purpose) +} + +func (p *Peer) HasPurpose(purpose PurposeFlag) bool { + p.purposesMu.Lock() + defer p.purposesMu.Unlock() + return p.purposes.IsSet(purpose) +} + // NewPeerPipe creates a peer for testing purposes. // The message pipe given as the last parameter is closed when // Disconnect is called on the peer. @@ -216,7 +258,7 @@ func (p *Peer) Inbound() bool { return p.rw.is(inboundConn) }   -func newPeer(log log.Logger, conn *conn, protocols []Protocol) *Peer { +func newPeer(log log.Logger, conn *conn, protocols []Protocol, purpose PurposeFlag, server *Server) *Peer { protomap := matchProtocols(protocols, conn.caps, conn) p := &Peer{ rw: conn, @@ -226,7 +268,18 @@ disc: make(chan DiscReason), protoErr: make(chan error, len(protomap)+1), // protocols + pingLoop closed: make(chan struct{}), log: log.New("id", conn.node.ID(), "conn", conn.flags), + purposes: purpose, + Server: server, } + + // Increase connection metrics for proxies & validators + if p.purposes.IsSet(ValidatorPurpose) { + activeValidatorsPeerGauge.Inc(1) + } + if p.purposes.IsSet(ProxyPurpose) { + activeProxiesPeerGauge.Inc(1) + } + return p }   @@ -278,6 +331,24 @@ break loop } }   + // Decrease connection metrics for proxies & validators + p.purposesMu.Lock() + if p.purposes.IsSet(ValidatorPurpose) { + activeValidatorsPeerGauge.Dec(1) + } + if p.purposes.IsSet(ProxyPurpose) { + activeProxiesPeerGauge.Dec(1) + } + + if p.purposes.HasPurpose() { + if err != nil { + p.log.Info("Disconnecting from static or trusted peer", "purpose", p.purposes, "reason", reason, "remoteRequested", remoteRequested, "err", err) + } else { + p.log.Info("Disconnecting from static or trusted peer", "purpose", p.purposes, "reason", reason, "remoteRequested", remoteRequested) + } + } + p.purposesMu.Unlock() + close(p.closed) p.rw.close(reason) p.wg.Wait() @@ -324,11 +395,11 @@ case msg.Code == pingMsg: msg.Discard() go SendItems(p.rw, pongMsg) case msg.Code == discMsg: - var reason [1]DiscReason // This is the last message. We don't need to discard or // check errors because, the connection will be closed after it. - rlp.Decode(msg.Payload, &reason) - return reason[0] + var m struct{ R DiscReason } + rlp.Decode(msg.Payload, &m) + return m.R case msg.Code < baseProtocolLength: // ignore other base protocol messages return msg.Discard() @@ -387,6 +458,7 @@ continue outer } } } + return result }   @@ -479,6 +551,7 @@ Enode string `json:"enode"` // Node URL ID string `json:"id"` // Unique node identifier Name string `json:"name"` // Name of the node, including client type, version, OS, custom data Caps []string `json:"caps"` // Protocols advertised by this peer + Purposes string `json:"purposes"` // Purposes for the peer Network struct { LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection @@ -525,5 +598,8 @@ } } info.Protocols[proto.Name] = protoInfo } + + info.Purposes = p.purposes.String() + return info }
diff --git go-ethereum/p2p/server.go celo/p2p/server.go index f0a3755257b349d2ddc4566593383b1e4a954a67..c9bb1cf470a44dca4a8977f06b79c8df174a38cc 100644 --- go-ethereum/p2p/server.go +++ celo/p2p/server.go @@ -75,6 +75,10 @@ // MaxPeers is the maximum number of peers that can be // connected. It must be greater than zero. MaxPeers int   + // MaxLightClients is the maximum number of light clients that can be connected. + // It is zero if the LES server isn't running (includes the case that we are an LES client). + MaxLightClients int + // MaxPendingPeers is the maximum number of peers that can be pending in the // handshake phase, counted separately for inbound and outbound connections. // Zero defaults to preset values. @@ -119,9 +123,16 @@ // If this option is set to a non-nil value, only hosts which match one of the // IP networks contained in the list are considered. NetRestrict *netutil.Netlist `toml:",omitempty"`   + // PingIPFromPacket uses the IP address from p2p discovery ping packet + // rather than the UDP header. See https://github.com/ethereum/go-ethereum/pull/301 + PingIPFromPacket bool + // NodeDatabase is the path to the database containing the previously seen // live nodes in the network. NodeDatabase string `toml:",omitempty"` + + // UseInMemoryNodeDatabase specifies whether the node database should be in-memory or on-disk + UseInMemoryNodeDatabase bool   // Protocols should contain the protocols supported // by the server. Matching protocols are launched for @@ -136,6 +147,9 @@ // ListenAddr field will be updated with the actual address when // the server is started. ListenAddr string   + // This contains the network id that is specified via the command line (--networkid) + NetworkId uint64 + // If set to a non-nil value, the given NAT port mapper // is used to make the listening port available to the // Internet. @@ -156,6 +170,15 @@ // Logger is a custom logger to use with the p2p.Server. Logger log.Logger `toml:",omitempty"`   clock mclock.Clock + + // DialHistoryExpiration is the time waited between dialling a specific node. + DialHistoryExpiration time.Duration + + // InboundThrottleTime is used to rate limit inbound connection attempts + // from a specific IP. If setting up a small private network this should + // probably be set smaller than DialHistoryExpiration to avoid lots of + // failed dial attempts. + InboundThrottleTime time.Duration }   // Server manages all peer connections. @@ -187,19 +210,86 @@ dialsched *dialScheduler   // Channels into the run loop. quit chan struct{} - addtrusted chan *enode.Node - removetrusted chan *enode.Node + addstatic chan *nodeArgs + removestatic chan removestaticArgs + addtrusted chan *nodeArgs + removetrusted chan *nodeArgs peerOp chan peerOpFunc peerOpDone chan struct{} delpeer chan peerDrop checkpointPostHandshake chan *conn checkpointAddPeer chan *conn + getInboundCount chan func(int) + getInboundCountDone chan struct{}   // State of run loop and listenLoop. inboundHistory expHeap }   -type peerOpFunc func(map[enode.ID]*Peer) +type peerOpFunc func(peers map[enode.ID]*Peer) + +// Note that this type is NOT threadsafe. The reason that it is not is that it's read and written +// only by the p2p server's single threaded event loop. +type PurposeFlag uint32 + +const ( + NoPurpose PurposeFlag = 0 + ExplicitStaticPurpose = 1 << 0 + ExplicitTrustedPurpose = 1 << 1 + ValidatorPurpose = 1 << 2 + ProxyPurpose = 1 << 3 + AnyPurpose = ExplicitStaticPurpose | ExplicitTrustedPurpose | ValidatorPurpose | ProxyPurpose // This value should be the bitwise OR of all possible PurposeFlag values +) + +func (pf PurposeFlag) Add(f PurposeFlag) PurposeFlag { + return pf | f +} + +func (pf PurposeFlag) Remove(f PurposeFlag) PurposeFlag { + return pf & ^f +} + +func (pf PurposeFlag) IsSet(f PurposeFlag) bool { + return (pf & f) != 0 +} + +func (pf PurposeFlag) HasNoPurpose() bool { + return pf == NoPurpose +} + +func (pf PurposeFlag) HasPurpose() bool { + return pf != NoPurpose +} + +func (pf PurposeFlag) String() string { + s := "" + if pf.IsSet(ExplicitStaticPurpose) { + s += "-ExplicitStaticPurpose" + } + if pf.IsSet(ExplicitTrustedPurpose) { + s += "-ExplicitTrustedPurpose" + } + if pf.IsSet(ValidatorPurpose) { + s += "-ValidatorPurpose" + } + if pf.IsSet(ProxyPurpose) { + s += "-ProxyPurpose" + } + if s != "" { + s = s[1:] + } + return s +} + +type nodeArgs struct { + node *enode.Node + purpose PurposeFlag +} + +type removestaticArgs struct { + *nodeArgs + done chan<- struct{} +}   type peerDrop struct { *Peer @@ -316,56 +406,52 @@ }) return count }   -// AddPeer adds the given node to the static node set. When there is room in the peer set, -// the server will connect to the node. If the connection fails for any reason, the server -// will attempt to reconnect the peer. -func (srv *Server) AddPeer(node *enode.Node) { - srv.dialsched.addStatic(node) +// InboundCount returns the number of inbound peers. +func (srv *Server) inboundCount() int { + var count int + select { + case srv.getInboundCount <- func(inboundCount int) { + count = inboundCount + }: + <-srv.getInboundCountDone + case <-srv.quit: + } + return count }   -// RemovePeer removes a node from the static node set. It also disconnects from the given -// node if it is currently connected as a peer. -// -// This method blocks until all protocols have exited and the peer is removed. Do not use -// RemovePeer in protocol implementations, call Disconnect on the Peer instead. -func (srv *Server) RemovePeer(node *enode.Node) { - var ( - ch chan *PeerEvent - sub event.Subscription - ) - // Disconnect the peer on the main loop. - srv.doPeerOp(func(peers map[enode.ID]*Peer) { - srv.dialsched.removeStatic(node) - if peer := peers[node.ID()]; peer != nil { - ch = make(chan *PeerEvent, 1) - sub = srv.peerFeed.Subscribe(ch) - peer.Disconnect(DiscRequested) - } - }) - // Wait for the peer connection to end. - if ch != nil { - defer sub.Unsubscribe() - for ev := range ch { - if ev.Peer == node.ID() && ev.Type == PeerEventTypeDrop { - return - } - } +// AddPeer connects to the given node and maintains the connection until the +// server is shut down. If the connection fails for any reason, the server will +// attempt to reconnect the peer. +func (srv *Server) AddPeer(node *enode.Node, purpose PurposeFlag) { + select { + case srv.addstatic <- &nodeArgs{node: node, purpose: purpose}: + case <-srv.quit: + } +} + +// RemovePeer disconnects from the given node +func (srv *Server) RemovePeer(node *enode.Node, purpose PurposeFlag) { + done := make(chan struct{}) + select { + case srv.removestatic <- removestaticArgs{nodeArgs: &nodeArgs{node: node, purpose: purpose}, done: done}: + <-done + case <-srv.quit: } }   // AddTrustedPeer adds the given node to a reserved trusted list which allows the // node to always connect, even if the slot are full. -func (srv *Server) AddTrustedPeer(node *enode.Node) { +func (srv *Server) AddTrustedPeer(node *enode.Node, purpose PurposeFlag) { select { - case srv.addtrusted <- node: + case srv.addtrusted <- &nodeArgs{node: node, purpose: purpose}: case <-srv.quit: } }   // RemoveTrustedPeer removes the given node from the trusted peer set. -func (srv *Server) RemoveTrustedPeer(node *enode.Node) { +func (srv *Server) RemoveTrustedPeer(node *enode.Node, purpose PurposeFlag) { select { - case srv.removetrusted <- node: + case srv.removetrusted <- &nodeArgs{node: node, purpose: purpose}: case <-srv.quit: } } @@ -387,6 +473,15 @@ } return ln.Node() }   +// DiscoverTableInfo gets information on all the buckets in the +// discover table +func (srv *Server) DiscoverTableInfo() *discover.TableInfo { + if srv.ntab != nil { + return srv.ntab.Info() + } + return nil +} + // Stop terminates the server and all active peer connections. // It blocks until all active connections have been closed. func (srv *Server) Stop() { @@ -405,16 +500,16 @@ srv.lock.Unlock() srv.loopWG.Wait() }   -// sharedUDPConn implements a shared connection. Write sends messages to the underlying connection while read returns +// SharedUDPConn implements a shared connection. Write sends messages to the underlying connection while read returns // messages that were found unprocessable and sent to the unhandled channel by the primary listener. -type sharedUDPConn struct { +type SharedUDPConn struct { *net.UDPConn - unhandled chan discover.ReadPacket + Unhandled chan discover.ReadPacket }   // ReadFromUDP implements discover.UDPConn -func (s *sharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { - packet, ok := <-s.unhandled +func (s *SharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { + packet, ok := <-s.Unhandled if !ok { return 0, nil, errors.New("connection was closed") } @@ -427,7 +522,7 @@ return l, packet.Addr, nil }   // Close implements discover.UDPConn -func (s *sharedUDPConn) Close() error { +func (s *SharedUDPConn) Close() error { return nil }   @@ -439,6 +534,9 @@ defer srv.lock.Unlock() if srv.running { return errors.New("server already running") } + if srv.InboundThrottleTime == 0 { + srv.InboundThrottleTime = inboundThrottleTime + } srv.running = true srv.log = srv.Config.Logger if srv.log == nil { @@ -465,10 +563,14 @@ srv.quit = make(chan struct{}) srv.delpeer = make(chan peerDrop) srv.checkpointPostHandshake = make(chan *conn) srv.checkpointAddPeer = make(chan *conn) - srv.addtrusted = make(chan *enode.Node) - srv.removetrusted = make(chan *enode.Node) + srv.addstatic = make(chan *nodeArgs) + srv.removestatic = make(chan removestaticArgs) + srv.addtrusted = make(chan *nodeArgs) + srv.removetrusted = make(chan *nodeArgs) srv.peerOp = make(chan peerOpFunc) srv.peerOpDone = make(chan struct{}) + srv.getInboundCount = make(chan func(int)) + srv.getInboundCountDone = make(chan struct{})   if err := srv.setupLocalNode(); err != nil { return err @@ -503,8 +605,9 @@ if err != nil { return err } srv.nodedb = db - srv.localnode = enode.NewLocalNode(db, srv.PrivateKey) + srv.localnode = enode.NewLocalNode(db, srv.PrivateKey, srv.Config.NetworkId) srv.localnode.SetFallbackIP(net.IP{127, 0, 0, 1}) + // TODO: check conflicts for _, p := range srv.Protocols { for _, e := range p.Attributes { @@ -572,13 +675,14 @@ srv.localnode.SetFallbackUDP(realaddr.Port)   // Discovery V4 var unhandled chan discover.ReadPacket - var sconn *sharedUDPConn + var sconn *SharedUDPConn if !srv.NoDiscovery { if srv.DiscoveryV5 { unhandled = make(chan discover.ReadPacket, 100) - sconn = &sharedUDPConn{conn, unhandled} + sconn = &SharedUDPConn{conn, unhandled} } cfg := discover.Config{ + PingIPFromPacket: srv.PingIPFromPacket, PrivateKey: srv.PrivateKey, NetRestrict: srv.NetRestrict, Bootnodes: srv.BootstrapNodes, @@ -623,6 +727,7 @@ log: srv.Logger, netRestrict: srv.NetRestrict, dialer: srv.Dialer, clock: srv.clock, + dialHistoryExpiration: srv.DialHistoryExpiration, } if srv.ntab != nil { config.resolver = srv.ntab @@ -644,10 +749,11 @@ func (srv *Server) maxDialedConns() (limit int) { if srv.NoDial || srv.MaxPeers == 0 { return 0 } + maxEthPeers := srv.MaxPeers - srv.MaxLightClients if srv.DialRatio == 0 { - limit = srv.MaxPeers / defaultDialRatio + limit = maxEthPeers / defaultDialRatio } else { - limit = srv.MaxPeers / srv.DialRatio + limit = maxEthPeers / srv.DialRatio } if limit == 0 { limit = 1 @@ -692,79 +798,168 @@ }   // run is the main loop of the server. func (srv *Server) run() { - srv.log.Info("Started P2P networking", "self", srv.localnode.Node().URLv4()) + srv.log.Info("Started P2P networking", "self", srv.localnode.Node().URLv4(), "maxdialed", srv.maxDialedConns(), "maxinbound", srv.maxInboundConns()) defer srv.loopWG.Done() defer srv.nodedb.Close() defer srv.discmix.Close() defer srv.dialsched.stop()   var ( + static = make(map[enode.ID]PurposeFlag, len(srv.StaticNodes)) peers = make(map[enode.ID]*Peer) inboundCount = 0 - trusted = make(map[enode.ID]bool, len(srv.TrustedNodes)) + trusted = make(map[enode.ID]PurposeFlag, len(srv.TrustedNodes)) ) // Put trusted nodes into a map to speed up checks. // Trusted peers are loaded on startup or added via AddTrustedPeer RPC. for _, n := range srv.TrustedNodes { - trusted[n.ID()] = true + trusted[n.ID()] = ExplicitTrustedPurpose }   -running: - for { - select { - case <-srv.quit: - // The server was stopped. Run the cleanup logic. - break running + // Put static nodes specified in a file into a map. + for _, n := range srv.StaticNodes { + static[n.ID()] = ExplicitStaticPurpose + }   - case n := <-srv.addtrusted: - // This channel is used by AddTrustedPeer to add a node - // to the trusted node set. - srv.log.Trace("Adding trusted node", "node", n) - trusted[n.ID()] = true + addStatic := func(n *enode.Node, purpose PurposeFlag) { + newPurpose := static[n.ID()].Add(purpose) + static[n.ID()] = newPurpose + + // If already connected, set the peer's static node purpose set + if p, ok := peers[n.ID()]; ok { + p.AddPurpose(purpose) + } + + srv.dialsched.addStatic(n) + } + + removeStatic := func(n *enode.Node, purpose PurposeFlag, done chan<- struct{}) { + newPurpose := static[n.ID()].Remove(purpose) + disconnecting := false + if newPurpose.HasNoPurpose() { + srv.dialsched.removeStatic(n) + delete(static, n.ID()) if p, ok := peers[n.ID()]; ok { - p.rw.set(trustedConn, true) + disconnecting = true + ch := make(chan *PeerEvent, 1) + sub := srv.peerFeed.Subscribe(ch) + // Wait for the peer connection to end, in a new thread + go func() { + for ev := range ch { + if ev.Peer == n.ID() && ev.Type == PeerEventTypeDrop { + sub.Unsubscribe() + close(done) + return + } + } + }() + // Since we're disconnecting (destroying), no need to remove the purpose + p.Disconnect(DiscRequested) } + } else { + static[n.ID()] = newPurpose + if p, ok := peers[n.ID()]; ok { + p.RemovePurpose(purpose) + } + } + if !disconnecting { + // We aren't disconnecting the peer, so no need to wait for anything further + close(done) + } + }   - case n := <-srv.removetrusted: - // This channel is used by RemoveTrustedPeer to remove a node - // from the trusted node set. - srv.log.Trace("Removing trusted node", "node", n) + addTrusted := func(n *enode.Node, purpose PurposeFlag) { + trusted[n.ID()] = trusted[n.ID()].Add(purpose) + + // Mark any already-connected peer as trusted + if p, ok := peers[n.ID()]; ok { + // If already connected, updated val peer counters and set the validatorConn flag in the connection + p.rw.set(trustedConn, true) + p.AddPurpose(purpose) + } + } + + removeTrusted := func(n *enode.Node, purpose PurposeFlag) { + newPurpose := trusted[n.ID()].Remove(purpose) + + if newPurpose.HasNoPurpose() { delete(trusted, n.ID()) + // Unmark any already-connected peer as trusted if p, ok := peers[n.ID()]; ok { p.rw.set(trustedConn, false) + p.RemovePurpose(purpose) } + } else { + trusted[n.ID()] = newPurpose + if p, ok := peers[n.ID()]; ok { + p.RemovePurpose(purpose) + } + } + }   +running: + for { + select { + case <-srv.quit: + // The server was stopped. Run the cleanup logic. + break running + case addStaticArgs := <-srv.addstatic: + // This channel is used by AddPeer to add to the + // ephemeral static peer list. Add it to the dialer, + // it will keep the node connected. + srv.log.Trace("Adding static node", "node", addStaticArgs.node, "purpose", addStaticArgs.purpose) + addStatic(addStaticArgs.node, addStaticArgs.purpose) + case removeStaticArgs := <-srv.removestatic: + // This channel is used by RemovePeer to send a + // disconnect request to a peer and begin the + // stop keeping the node connected. + srv.log.Trace("Removing static node", "node", removeStaticArgs.node, "purpose", removeStaticArgs.purpose) + removeStatic(removeStaticArgs.node, removeStaticArgs.purpose, removeStaticArgs.done) + case addTrustedArgs := <-srv.addtrusted: + // This channel is used by AddTrustedPeer to add an enode + // to the trusted node set. + srv.log.Trace("Adding trusted node", "node", addTrustedArgs.node, "purpose", addTrustedArgs.purpose) + addTrusted(addTrustedArgs.node, addTrustedArgs.purpose) + case removeTrustedArgs := <-srv.removetrusted: + // This channel is used by RemoveTrustedPeer to remove an enode + // from the trusted node set. + srv.log.Trace("Removing trusted node", "node", removeTrustedArgs.node, "purpose", removeTrustedArgs.purpose) + removeTrusted(removeTrustedArgs.node, removeTrustedArgs.purpose) case op := <-srv.peerOp: - // This channel is used by Peers and PeerCount. + // This channel is used by Peers and PeerCount and ValPeers. op(peers) srv.peerOpDone <- struct{}{} - + case cb := <-srv.getInboundCount: + cb(inboundCount) + srv.getInboundCountDone <- struct{}{} case c := <-srv.checkpointPostHandshake: // A connection has passed the encryption handshake so // the remote identity is known (but hasn't been verified yet). - if trusted[c.node.ID()] { + if trusted[c.node.ID()].HasPurpose() { // Ensure that the trusted flag is set before checking against MaxPeers. c.flags |= trustedConn } // TODO: track in-progress inbound node IDs (pre-Peer) to avoid dialing them. - c.cont <- srv.postHandshakeChecks(peers, inboundCount, c) - + c.cont <- srv.postHandshakeChecks(peers, c) case c := <-srv.checkpointAddPeer: // At this point the connection is past the protocol handshake. // Its capabilities are known and the remote identity is verified. - err := srv.addPeerChecks(peers, inboundCount, c) + err := srv.addPeerChecks(peers, c) if err == nil { // The handshakes are done and it passed all checks. - p := srv.launchPeer(c) + purpose := static[c.node.ID()].Add(trusted[c.node.ID()]) + p := srv.launchPeer(c, purpose) peers[c.node.ID()] = p srv.log.Debug("Adding p2p peer", "peercount", len(peers), "id", p.ID(), "conn", c.flags, "addr", p.RemoteAddr(), "name", p.Name()) srv.dialsched.peerAdded(c) if p.Inbound() { inboundCount++ + ingressConnectWithHandshakeMeter.Mark(1) + } else { + egressConnectWithHandshakeMeter.Mark(1) } } c.cont <- err - case pd := <-srv.delpeer: // A peer disconnected. d := common.PrettyDuration(mclock.Now() - pd.created) @@ -800,12 +995,10 @@ delete(peers, p.ID()) } }   -func (srv *Server) postHandshakeChecks(peers map[enode.ID]*Peer, inboundCount int, c *conn) error { +// postHandshakeChecks performs basic checks on a new peer after a handshake. +// Max peer checks are left to the protocols. +func (srv *Server) postHandshakeChecks(peers map[enode.ID]*Peer, c *conn) error { switch { - case !c.is(trustedConn) && len(peers) >= srv.MaxPeers: - return DiscTooManyPeers - case !c.is(trustedConn) && c.is(inboundConn) && inboundCount >= srv.maxInboundConns(): - return DiscTooManyPeers case peers[c.node.ID()] != nil: return DiscAlreadyConnected case c.node.ID() == srv.localnode.ID(): @@ -815,14 +1008,30 @@ return nil } }   -func (srv *Server) addPeerChecks(peers map[enode.ID]*Peer, inboundCount int, c *conn) error { +func (srv *Server) addPeerChecks(peers map[enode.ID]*Peer, c *conn) error { // Drop connections with no matching protocols. if len(srv.Protocols) > 0 && countMatchingProtocols(srv.Protocols, c.caps) == 0 { return DiscUselessPeer } // Repeat the post-handshake checks because the // peer set might have changed since those checks were performed. - return srv.postHandshakeChecks(peers, inboundCount, c) + return srv.postHandshakeChecks(peers, c) +} + +// CheckPeerCounts performs some checks for a peer that has already been included +// in the peer counts +func (srv *Server) CheckPeerCounts(peer *Peer) error { + switch { + case peer.Info().Network.Trusted || peer.Info().Network.Static: + return nil + // KJUE - Remove the peerOp not nil check after restoring peer check in server.go + case srv.peerOp != nil && (srv.PeerCount() > srv.MaxPeers): + return DiscTooManyPeers + case srv.inboundCount() > srv.maxInboundConns(): + return DiscTooManyInboundPeers + default: + return nil + } }   // listenLoop runs in its own goroutine and accepts @@ -911,7 +1120,7 @@ srv.inboundHistory.expire(now, nil) if !netutil.IsLAN(remoteIP) && srv.inboundHistory.contains(remoteIP.String()) { return fmt.Errorf("too many attempts") } - srv.inboundHistory.add(remoteIP.String(), now.Add(inboundThrottleTime)) + srv.inboundHistory.add(remoteIP.String(), now.Add(srv.InboundThrottleTime)) return nil }   @@ -1012,8 +1221,8 @@ } return <-c.cont }   -func (srv *Server) launchPeer(c *conn) *Peer { - p := newPeer(srv.log, c, srv.Protocols) +func (srv *Server) launchPeer(c *conn, purpose PurposeFlag) *Peer { + p := newPeer(srv.log, c, srv.Protocols, purpose, srv) if srv.EnableMsgEvents { // If message events are enabled, pass the peerFeed // to the peer. @@ -1100,11 +1309,16 @@ } return info }   -// PeersInfo returns an array of metadata objects describing connected peers. +// PeersInfo returns an array of metadata objects describing all connected peers. func (srv *Server) PeersInfo() []*PeerInfo { + return peersInfo(srv.Peers()) +} + +// peersInfo returns a sorted array of metadata objects describing an array of peers +func peersInfo(peers []*Peer) []*PeerInfo { // Gather all the generic and sub-protocol specific infos - infos := make([]*PeerInfo, 0, srv.PeerCount()) - for _, peer := range srv.Peers() { + infos := make([]*PeerInfo, 0, len(peers)) + for _, peer := range peers { if peer != nil { infos = append(infos, peer.Info()) }
diff --git go-ethereum/p2p/discover/table_test.go celo/p2p/discover/table_test.go index 3512b7d8277fdb32d6422522d244c896857a5272..3cf40419185a6fa8d3318a185b62c355c14b8483 100644 --- go-ethereum/p2p/discover/table_test.go +++ celo/p2p/discover/table_test.go @@ -20,7 +20,6 @@ import ( "crypto/ecdsa" "fmt" "math/rand" - "net" "reflect" "testing"
diff --git go-ethereum/mobile/geth_ios.go celo/p2p/discover/metrics.go rename from mobile/geth_ios.go rename to p2p/discover/metrics.go index aab839727fa5ebd4467fad7b45bec209c025a736..0948b43de2947cc990852f9bd21ab164f78b252e 100644 --- go-ethereum/mobile/geth_ios.go +++ celo/p2p/discover/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// Copyright 2018 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -14,10 +14,11 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.   -//go:build ios -// +build ios +package discover   -package geth +import "github.com/ethereum/go-ethereum/metrics"   -// clientIdentifier is a hard coded identifier to report into the network. -var clientIdentifier = "iGeth" +var ( + ingressTrafficMeter = metrics.NewRegisteredMeter("discover/ingress", nil) + egressTrafficMeter = metrics.NewRegisteredMeter("discover/egress", nil) +)
diff --git go-ethereum/p2p/enode/localnode.go celo/p2p/enode/localnode.go index 8ac36790641311c7d82821c0083778d8ac85ae1d..e05809791b5df9a563230df280ca91fe70ae54d7 100644 --- go-ethereum/p2p/enode/localnode.go +++ celo/p2p/enode/localnode.go @@ -45,11 +45,12 @@ // LocalNode produces the signed node record of a local node, i.e. a node run in the // current process. Setting ENR entries via the Set method updates the record. A new version // of the record is signed on demand when the Node method is called. type LocalNode struct { - cur atomic.Value // holds a non-nil node pointer while the record is up-to-date + cur atomic.Value // holds a non-nil node pointer while the record is up-to-date.   id ID key *ecdsa.PrivateKey db *DB + networkId uint64   // everything below is protected by a lock mu sync.RWMutex @@ -67,7 +68,7 @@ fallbackUDP int }   // NewLocalNode creates a local node. -func NewLocalNode(db *DB, key *ecdsa.PrivateKey) *LocalNode { +func NewLocalNode(db *DB, key *ecdsa.PrivateKey, networkId uint64) *LocalNode { ln := &LocalNode{ id: PubkeyToIDV4(&key.PublicKey), db: db, @@ -79,6 +80,7 @@ }, endpoint6: lnEndpoint{ track: netutil.NewIPTracker(iptrackWindow, iptrackContactWindow, iptrackMinStatements), }, + networkId: networkId, } ln.seq = db.localSeq(ln.id) ln.update = time.Now() @@ -317,6 +319,10 @@ func (ln *LocalNode) bumpSeq() { ln.seq++ ln.db.storeLocalSeq(ln.id, ln.seq) +} + +func (ln *LocalNode) NetworkId() uint64 { + return ln.networkId }   // nowMilliseconds gives the current timestamp at millisecond precision.
diff --git go-ethereum/p2p/discover/v5_udp_test.go celo/p2p/discover/v5_udp_test.go index ed094eafb7c51a663ed95583d89c3389befc22c8..efa71ffc26e7da130caa952916ec38cc83857ea4 100644 --- go-ethereum/p2p/discover/v5_udp_test.go +++ celo/p2p/discover/v5_udp_test.go @@ -74,7 +74,7 @@ func startLocalhostV5(t *testing.T, cfg Config) *UDPv5 { cfg.PrivateKey = newkey() db, _ := enode.OpenDB("") - ln := enode.NewLocalNode(db, cfg.PrivateKey) + ln := enode.NewLocalNode(db, cfg.PrivateKey, testNetworkId)   // Prefix logs with node ID. lprefix := fmt.Sprintf("(%s)", ln.ID().TerminalString()) @@ -705,7 +705,7 @@ nodesByID: make(map[enode.ID]*enode.LocalNode), nodesByIP: make(map[string]*enode.LocalNode), } test.db, _ = enode.OpenDB("") - ln := enode.NewLocalNode(test.db, test.localkey) + ln := enode.NewLocalNode(test.db, test.localkey, testNetworkId) ln.SetStaticIP(net.IP{10, 0, 0, 1}) ln.Set(enr.UDP(30303)) test.udp, _ = ListenV5(test.pipe, ln, Config{ @@ -748,7 +748,7 @@ id := encodePubkey(&key.PublicKey).id() ln := test.nodesByID[id] if ln == nil { db, _ := enode.OpenDB("") - ln = enode.NewLocalNode(db, key) + ln = enode.NewLocalNode(db, key, testNetworkId) ln.SetStaticIP(addr.IP) ln.Set(enr.UDP(addr.Port)) test.nodesByID[id] = ln
diff --git go-ethereum/p2p/discover/v4_udp_test.go celo/p2p/discover/v4_udp_test.go index 4562a23c5e146e62d26687dc3abe85d0300b1573..e5ff8a840009313e8ff751fff57052141f87f1e7 100644 --- go-ethereum/p2p/discover/v4_udp_test.go +++ celo/p2p/discover/v4_udp_test.go @@ -45,6 +45,7 @@ testTarget = v4wire.Pubkey{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1} testRemote = v4wire.Endpoint{IP: net.ParseIP("1.1.1.1").To4(), UDP: 1, TCP: 2} testLocalAnnounced = v4wire.Endpoint{IP: net.ParseIP("2.2.2.2").To4(), UDP: 3, TCP: 4} testLocal = v4wire.Endpoint{IP: net.ParseIP("3.3.3.3").To4(), UDP: 5, TCP: 6} + testNetworkId = uint64(1) )   type udpTest struct { @@ -68,7 +69,7 @@ remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303}, }   test.db, _ = enode.OpenDB("") - ln := enode.NewLocalNode(test.db, test.localkey) + ln := enode.NewLocalNode(test.db, test.localkey, testNetworkId) test.udp, _ = ListenV4(test.pipe, ln, Config{ PrivateKey: test.localkey, Log: testlog.Logger(t, log.LvlTrace), @@ -136,10 +137,11 @@ func TestUDPv4_packetErrors(t *testing.T) { test := newUDPTest(t) defer test.close()   - test.packetIn(errExpired, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4}) + test.packetIn(errExpired, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, NetworkId: testNetworkId}) test.packetIn(errUnsolicitedReply, &v4wire.Pong{ReplyTok: []byte{}, Expiration: futureExp}) test.packetIn(errUnknownNode, &v4wire.Findnode{Expiration: futureExp}) test.packetIn(errUnsolicitedReply, &v4wire.Neighbors{Expiration: futureExp}) + test.packetIn(errBadNetworkId, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp, NetworkId: testNetworkId + 1}) }   func TestUDPv4_pingTimeout(t *testing.T) { @@ -367,7 +369,7 @@ randToken := make([]byte, 32) crand.Read(randToken)   - test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) + test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp, NetworkId: testNetworkId}) test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {}) test.waitPacketOut(func(*v4wire.Ping, *net.UDPAddr, []byte) {}) test.packetIn(errUnsolicitedReply, &v4wire.Pong{ReplyTok: randToken, To: testLocalAnnounced, Expiration: futureExp}) @@ -378,7 +380,7 @@ func TestUDPv4_pingMatchIP(t *testing.T) { test := newUDPTest(t) defer test.close()   - test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) + test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp, NetworkId: testNetworkId}) test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {})   test.waitPacketOut(func(p *v4wire.Ping, to *net.UDPAddr, hash []byte) { @@ -398,7 +400,7 @@ test.table.nodeAddedHook = func(n *node) { added <- n } defer test.close()   // The remote side sends a ping packet to initiate the exchange. - go test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) + go test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp, NetworkId: testNetworkId})   // The ping is replied to. test.waitPacketOut(func(p *v4wire.Pong, to *net.UDPAddr, hash []byte) { @@ -468,7 +470,7 @@ // ENR requests aren't allowed before endpoint proof. test.packetIn(errUnknownNode, &v4wire.ENRRequest{Expiration: futureExp})   // Perform endpoint proof and check for sequence number in packet tail. - test.packetIn(nil, &v4wire.Ping{Expiration: futureExp}) + test.packetIn(nil, &v4wire.Ping{Expiration: futureExp, NetworkId: testNetworkId}) test.waitPacketOut(func(p *v4wire.Pong, addr *net.UDPAddr, hash []byte) { if p.ENRSeq != wantNode.Seq() { t.Errorf("wrong sequence number in pong: %d, want %d", p.ENRSeq, wantNode.Seq()) @@ -553,7 +555,7 @@ t.Helper()   cfg.PrivateKey = newkey() db, _ := enode.OpenDB("") - ln := enode.NewLocalNode(db, cfg.PrivateKey) + ln := enode.NewLocalNode(db, cfg.PrivateKey, testNetworkId)   // Prefix logs with node ID. lprefix := fmt.Sprintf("(%s)", ln.ID().TerminalString())
diff --git go-ethereum/p2p/discover/common.go celo/p2p/discover/common.go index ac91a46f606f399d795fb23382ccf14fbfde70e7..1d4831e58c81a7d14db8fa20c5de64fb2b49b5e2 100644 --- go-ethereum/p2p/discover/common.go +++ celo/p2p/discover/common.go @@ -41,10 +41,11 @@ // These settings are required and configure the UDP listener: PrivateKey *ecdsa.PrivateKey   // These settings are optional: - NetRestrict *netutil.Netlist // list of allowed IP networks + NetRestrict *netutil.Netlist // network whitelist Bootnodes []*enode.Node // list of bootstrap nodes Unhandled chan<- ReadPacket // unhandled packets are sent on this channel Log log.Logger // if set, log messages go here + PingIPFromPacket bool ValidSchemes enr.IdentityScheme // allowed identity schemes Clock mclock.Clock }
diff --git go-ethereum/p2p/enode/urlv4.go celo/p2p/enode/urlv4.go index f42d51b279be7b35c4d77f50ba7a00b8313d413c..626ec5a02c0c2ebb5d5b07800b091f93616e8aa5 100644 --- go-ethereum/p2p/enode/urlv4.go +++ celo/p2p/enode/urlv4.go @@ -194,6 +194,20 @@ } return u.String() }   +var ( + block24bit = net.IPNet{IP: net.IPv4(10, 0, 0, 0), Mask: net.IPv4Mask(255, 0, 0, 0)} + block20bit = net.IPNet{IP: net.IPv4(172, 16, 0, 0), Mask: net.IPv4Mask(255, 240, 0, 0)} + block16bit = net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 0, 0)} +) + +// Returns true if the ip is a loopback or private ip, not generally accessible from the internet. +func (n *Node) IsPrivateIP() bool { + return (!n.IP().IsGlobalUnicast() || + block24bit.Contains(n.IP()) || + block20bit.Contains(n.IP()) || + block16bit.Contains(n.IP())) +} + // PubkeyToIDV4 derives the v4 node address from the given public key. func PubkeyToIDV4(key *ecdsa.PublicKey) ID { e := make([]byte, 64)
diff --git go-ethereum/p2p/enode/urlv4_test.go celo/p2p/enode/urlv4_test.go index 1a770d15e2c5a7874244b721bd776c8f75adc63a..c1774354852413c084c026dfdc0e0b0429c2fc8d 100644 --- go-ethereum/p2p/enode/urlv4_test.go +++ celo/p2p/enode/urlv4_test.go @@ -41,6 +41,7 @@ var parseNodeTests = []struct { input string wantError string wantResult *Node + wantPrivate bool }{ // Records { @@ -55,6 +56,7 @@ SignV4(&r, testKey) n, _ := New(ValidSchemes, &r) return n }(), + wantPrivate: true, }, // Invalid Records { @@ -75,8 +77,9 @@ input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@invalid.:3", wantError: `no such host`, }, { + // net/url.Parse(rawurl) returns an error with rawurl and why the parse failed. input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo", - wantError: `invalid port`, + wantError: "invalid port", }, { input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:3?discport=foo", @@ -90,6 +93,17 @@ net.IP{127, 0, 0, 1}, 52150, 52150, ), + wantPrivate: true, + }, + { + input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@5.1.1.1:52150", + wantResult: NewV4( + hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{0x5, 0x1, 0x1, 0x1}, + 52150, + 52150, + ), + wantPrivate: false, }, { input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150", @@ -99,6 +113,17 @@ net.ParseIP("::"), 52150, 52150, ), + wantPrivate: true, + }, + { + input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@172.17.0.3:52150", + wantResult: NewV4( + hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.ParseIP("172.17.0.3"), + 52150, + 52150, + ), + wantPrivate: true, }, { input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150", @@ -117,6 +142,7 @@ net.IP{0x7f, 0x0, 0x0, 0x1}, 52150, 22334, ), + wantPrivate: true, }, // Incomplete node URLs with no address { @@ -183,6 +209,9 @@ continue } if !reflect.DeepEqual(n, test.wantResult) { t.Errorf("test %q:\n result mismatch:\ngot: %#v\nwant: %#v", test.input, n, test.wantResult) + } + if !n.Incomplete() && n.IsPrivateIP() != test.wantPrivate { + t.Errorf("test %q:\n isPrivate mismatch:\nfor %#v\ngot: %#v\nwant: %#v", test.input, n, n.IsPrivateIP(), test.wantPrivate) } } }
diff --git go-ethereum/p2p/discover/table.go celo/p2p/discover/table.go index a52ae8b7c6c553d9d9677d0830f924f42bf3c0b6..5c3a6ee81b39ecae0e3722e1b29fef4a2bef2f79 100644 --- go-ethereum/p2p/discover/table.go +++ celo/p2p/discover/table.go @@ -76,13 +76,28 @@ db *enode.DB // database of known nodes net transport refreshReq chan chan struct{} initDone chan struct{} + closeReq chan struct{} closed chan struct{}   nodeAddedHook func(*node) // for testing }   -// transport is implemented by the UDP transports. +type bucketInfo struct { + Entries []*node `json:"entries"` + Replacements []*node `json:"replacements"` + IPs string `json:"ips"` +} + +// TableInfo provides information on the discovery table +type TableInfo struct { + Buckets [nBuckets]*bucketInfo `json:"buckets"` + IPs string `json:"ips"` +} + +// transport is implemented by the UDP transport. +// it is an interface so we can test without opening lots of UDP +// sockets and without generating a private key. type transport interface { Self() *enode.Node RequestENR(*enode.Node) (*enode.Node, error) @@ -347,10 +362,12 @@ return } // No reply received, pick a replacement or delete the node if there aren't // any replacements. - if r := tab.replace(b, last); r != nil { + if r := tab.replace(b, last); r == nil { + tab.log.Debug("Removed dead node", "b", bi, "id", last.ID(), "ip", last.IP(), "checks", last.livenessChecks) + } else if r == last { + tab.log.Debug("Left dead node in bucket", "b", bi, "id", last.ID(), "ip", last.IP(), "checks", last.livenessChecks, "r", r.ID(), "rip", r.IP()) + } else { tab.log.Debug("Replaced dead node", "b", bi, "id", last.ID(), "ip", last.IP(), "checks", last.livenessChecks, "r", r.ID(), "rip", r.IP()) - } else { - tab.log.Debug("Removed dead node", "b", bi, "id", last.ID(), "ip", last.IP(), "checks", last.livenessChecks) } }   @@ -586,6 +603,7 @@ // replace removes n from the replacement list and replaces 'last' with it if it is the // last entry in the bucket. If 'last' isn't the last entry, it has either been replaced // with someone else or became active. +// If last is the only node in the bucket and there are no replacements, leave it there. func (tab *Table) replace(b *bucket, last *node) *node { if len(b.entries) == 0 || b.entries[len(b.entries)-1].ID() != last.ID() { // Entry has moved, don't replace it. @@ -593,6 +611,9 @@ return nil } // Still the last entry. if len(b.replacements) == 0 { + if len(b.entries) == 1 { + return last + } tab.deleteInBucket(b, last) return nil } @@ -629,6 +650,22 @@ func (tab *Table) deleteInBucket(b *bucket, n *node) { b.entries = deleteNode(b.entries, n) tab.removeIP(b, n.IP()) +} + +// Info gives information on all the buckets and IPs in the Table +func (tab *Table) Info() *TableInfo { + var buckets [nBuckets]*bucketInfo + for i := 0; i < nBuckets; i++ { + buckets[i] = &bucketInfo{ + Entries: tab.buckets[i].entries, + Replacements: tab.buckets[i].replacements, + IPs: tab.buckets[i].ips.String(), + } + } + return &TableInfo{ + Buckets: buckets, + IPs: tab.ips.String(), + } }   func contains(ns []*node, id enode.ID) bool {
diff --git go-ethereum/p2p/enode/localnode_test.go celo/p2p/enode/localnode_test.go index ef8efc985d1e6a528b41aa0706eb6b026d514cd9..cc1cb5b1a72cd290e0142a5cd142e4cf6df5b8f2 100644 --- go-ethereum/p2p/enode/localnode_test.go +++ celo/p2p/enode/localnode_test.go @@ -29,7 +29,7 @@ func newLocalNodeForTesting() (*LocalNode, *DB) { db, _ := OpenDB("") key, _ := crypto.GenerateKey() - return NewLocalNode(db, key), db + return NewLocalNode(db, key, 1), db }   func TestLocalNode(t *testing.T) { @@ -69,7 +69,7 @@ // Create a new instance, it should reload the sequence number. // The number increases just after that because a new record is // created without the "x" entry. - ln2 := NewLocalNode(db, ln.key) + ln2 := NewLocalNode(db, ln.key, 1) if s := ln2.Node().Seq(); s != initialSeq+2 { t.Fatalf("wrong seq %d on new instance, want %d", s, initialSeq+2) } @@ -79,7 +79,7 @@ // Create a new instance with a different node key on the same database. // This should reset the sequence number. key, _ := crypto.GenerateKey() - ln3 := NewLocalNode(db, key) + ln3 := NewLocalNode(db, key, 1) if s := ln3.Node().Seq(); s < finalSeq { t.Fatalf("wrong seq %d on instance with changed key, want >= %d", s, finalSeq) }
diff --git go-ethereum/p2p/discover/v4_udp.go celo/p2p/discover/v4_udp.go index 940f4353e5be074e0ebd6ad597e6f9f7ceb1a30b..fb3849a84da5221dc366c2736c80f111b3e6a10c 100644 --- go-ethereum/p2p/discover/v4_udp.go +++ celo/p2p/discover/v4_udp.go @@ -44,6 +44,7 @@ errUnknownNode = errors.New("unknown node") errTimeout = errors.New("RPC timeout") errClockWarp = errors.New("reply deadline too far in the future") errClosed = errors.New("socket closed") + errBadNetworkId = errors.New("bad networkId") errLowPort = errors.New("low port") )   @@ -74,6 +75,7 @@ db *enode.DB tab *Table closeOnce sync.Once wg sync.WaitGroup + pingIPFromPacket bool   addReplyMatcher chan *replyMatcher gotreply chan reply @@ -140,6 +142,7 @@ addReplyMatcher: make(chan *replyMatcher), closeCtx: closeCtx, cancelCloseCtx: cancel, log: cfg.Log, + pingIPFromPacket: cfg.PingIPFromPacket, }   tab, err := newTable(t, ln.Database(), cfg.Bootnodes, t.log) @@ -200,6 +203,10 @@ } return n }   +func (t *UDPv4) Info() *TableInfo { + return t.tab.Info() +} + func (t *UDPv4) ourEndpoint() v4wire.Endpoint { n := t.Self() a := &net.UDPAddr{IP: n.IP(), Port: n.UDP()} @@ -252,6 +259,7 @@ Version: 4, From: t.ourEndpoint(), To: v4wire.NewEndpoint(toaddr, 0), Expiration: uint64(time.Now().Add(expiration).Unix()), + NetworkId: t.localNode.NetworkId(), ENRSeq: t.localNode.Node().Seq(), } } @@ -457,7 +465,7 @@ case r := <-t.gotreply: var matched bool // whether any replyMatcher considered the reply acceptable. for el := plist.Front(); el != nil; el = el.Next() { p := el.Value.(*replyMatcher) - if p.from == r.from && p.ptype == r.data.Kind() && p.ip.Equal(r.ip) { + if p.from == r.from && p.ptype == r.data.Kind() && (t.pingIPFromPacket || p.ip.Equal(r.ip)) { ok, requestDone := p.callback(r.data) matched = matched || ok p.reply = r.data @@ -466,8 +474,6 @@ if requestDone { p.errc <- nil plist.Remove(el) } - // Reset the continuous timeout counter (time drift detection) - contTimeouts = 0 } } r.matched <- matched @@ -505,8 +511,11 @@ return hash, t.write(toaddr, toid, req.Name(), packet) }   func (t *UDPv4) write(toaddr *net.UDPAddr, toid enode.ID, what string, packet []byte) error { - _, err := t.conn.WriteToUDP(packet, toaddr) + nbytes, err := t.conn.WriteToUDP(packet, toaddr) t.log.Trace(">> "+what, "id", toid, "addr", toaddr, "err", err) + if err == nil { + egressTrafficMeter.Mark(int64(nbytes)) + } return err }   @@ -520,6 +529,7 @@ buf := make([]byte, maxPacketSize) for { nbytes, from, err := t.conn.ReadFromUDP(buf) + ingressTrafficMeter.Mark(int64(nbytes)) if netutil.IsTemporaryError(err) { // Ignore temporary read errors. t.log.Debug("Temporary UDP read error", "err", err) @@ -643,6 +653,9 @@ func (t *UDPv4) verifyPing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { req := h.Packet.(*v4wire.Ping)   + if t.localNode.NetworkId() != req.NetworkId { + return errBadNetworkId + } senderKey, err := v4wire.DecodePubkey(crypto.S256(), fromKey) if err != nil { return err
diff --git go-ethereum/p2p/discover/v4wire/v4wire_test.go celo/p2p/discover/v4wire/v4wire_test.go index 8307a6a2b0b56a1d5a5f0e87a41428ab48a1e43e..d838e2411d7166f15ab51328168a721781132a6d 100644 --- go-ethereum/p2p/discover/v4wire/v4wire_test.go +++ celo/p2p/discover/v4wire/v4wire_test.go @@ -27,33 +27,38 @@ "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" )   +var testNetworkId = uint64(1) + // EIP-8 test vectors. var testPackets = []struct { input string wantPacket interface{} }{ { - input: "71dbda3a79554728d4f94411e42ee1f8b0d561c10e1e5f5893367948c6a7d70bb87b235fa28a77070271b6c164a2dce8c7e13a5739b53b5e96f2e5acb0e458a02902f5965d55ecbeb2ebb6cabb8b2b232896a36b737666c55265ad0a68412f250001ea04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a355", + input: "5f76a8dbcc2cfb869e84ed53a0c511642bcf4b4725ac09f4bbb05758519b4a0c820b24a50e9a92ab6b54c29ec27415e4b1fb2e7221ae54df539e24eb7b0708ec5cd65263edbf18c639658308a5fb6cbe273b11231dc6db1eb8f0e91ebcd52e740101eb04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a35501", wantPacket: &Ping{ Version: 4, From: Endpoint{net.ParseIP("127.0.0.1").To4(), 3322, 5544}, To: Endpoint{net.ParseIP("::1"), 2222, 3333}, Expiration: 1136239445, + NetworkId: testNetworkId, + Rest: nil, }, }, { - input: "e9614ccfd9fc3e74360018522d30e1419a143407ffcce748de3e22116b7e8dc92ff74788c0b6663aaa3d67d641936511c8f8d6ad8698b820a7cf9e1be7155e9a241f556658c55428ec0563514365799a4be2be5a685a80971ddcfa80cb422cdd0101ec04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a3550102", + input: "c552fb8e82b033d29aa9a0d8a419430ccb60ccbd850c772c2b566b6f5648567563ece5c430d9583ce11f1ef5cf2eaba463d3b0b3dcb48d2989803052eca8189173a60da7d08d5c756d0aad6fc05cecdfa7ab4149be85e4c1e9ee32e34457ca050101ed04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a355010102", wantPacket: &Ping{ Version: 4, From: Endpoint{net.ParseIP("127.0.0.1").To4(), 3322, 5544}, To: Endpoint{net.ParseIP("::1"), 2222, 3333}, Expiration: 1136239445, + NetworkId: testNetworkId, ENRSeq: 1, Rest: []rlp.RawValue{{0x02}}, }, }, { - input: "c7c44041b9f7c7e41934417ebac9a8e1a4c6298f74553f2fcfdcae6ed6fe53163eb3d2b52e39fe91831b8a927bf4fc222c3902202027e5e9eb812195f95d20061ef5cd31d502e47ecb61183f74a504fe04c51e73df81f25c4d506b26db4517490103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f8443b9a35582999983999999280dc62cc8255c73471e0a61da0c89acdc0e035e260add7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396", + input: "b4ddc372344d2fea1d58c26edfc7cdb8f7359cb4f6858484cf48ec23feeeaff0fee71339958ee7859a936d61e6e4e43f74f5dc119fffcd6b424df1929f55197b159aaef76f9bac9fed4f35677e85b049a618cdb62d5cdb70a3b238439c79bce30103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f8443b9a35582999983999999", wantPacket: &Findnode{ Target: hexPubkey("ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f"), Expiration: 1136239445, @@ -61,7 +66,7 @@ Rest: []rlp.RawValue{{0x82, 0x99, 0x99}, {0x83, 0x99, 0x99, 0x99}}, }, }, { - input: "c679fc8fe0b8b12f06577f2e802d34f6fa257e6137a995f6f4cbfc9ee50ed3710faf6e66f932c4c8d81d64343f429651328758b47d3dbc02c4042f0fff6946a50f4a49037a72bb550f3a7872363a83e1b9ee6469856c24eb4ef80b7535bcf99c0004f9015bf90150f84d846321163782115c82115db8403155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32f84984010203040101b840312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069dbf8599020010db83c4d001500000000abcdef12820d05820d05b84038643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aacf8599020010db885a308d313198a2e037073488203e78203e8b8408dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df738443b9a355010203b525a138aa34383fec3d2719a0", + input: "46742cb11a565879175e92ca349eaf9f9ce0380ac4d8976c8ea01f6c2620475c56b8409b9176e182b36ebc0715d6197a69b0eb806d6a7b7aa8615677891e15705c4cf3849f0ff477db229126dc4c0715e11f3ee9172659726dbb3eff8a64a1590004f9015bf90150f84d846321163782115c82115db8403155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32f84984010203040101b840312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069dbf8599020010db83c4d001500000000abcdef12820d05820d05b84038643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aacf8599020010db885a308d313198a2e037073488203e78203e8b8408dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df738443b9a355010203", wantPacket: &Neighbors{ Nodes: []Node{ {
diff --git go-ethereum/p2p/discover/v5wire/encoding_test.go celo/p2p/discover/v5wire/encoding_test.go index 513ade45421f492f3c40639d2c7d1c3a92454f83..10d4aa4334b4724128289d53c54bb9d38a88c061 100644 --- go-ethereum/p2p/discover/v5wire/encoding_test.go +++ celo/p2p/discover/v5wire/encoding_test.go @@ -510,7 +510,7 @@ }   func (n *handshakeTestNode) init(key *ecdsa.PrivateKey, ip net.IP, clock mclock.Clock) { db, _ := enode.OpenDB("") - n.ln = enode.NewLocalNode(db, key) + n.ln = enode.NewLocalNode(db, key, 1) n.ln.SetStaticIP(ip) n.c = NewCodec(n.ln, key, clock) }
diff --git go-ethereum/p2p/discover/v4wire/v4wire.go celo/p2p/discover/v4wire/v4wire.go index ee4e78f362d6224242bf619c6fbdbb66c5a115ed..84f754894907adc7d55c6a3961aac8ee59853d4a 100644 --- go-ethereum/p2p/discover/v4wire/v4wire.go +++ celo/p2p/discover/v4wire/v4wire.go @@ -50,8 +50,8 @@ Ping struct { Version uint From, To Endpoint Expiration uint64 + NetworkId uint64 ENRSeq uint64 `rlp:"optional"` // Sequence number of local record, added by EIP-868. - // Ignore additional fields (for forward compatibility). Rest []rlp.RawValue `rlp:"tail"` } @@ -205,13 +205,15 @@ )   var headSpace = make([]byte, headSize)   +var celoClientSalt = []byte{0x63, 0x65, 0x6C, 0x6F} + // Decode reads a discovery v4 packet. func Decode(input []byte) (Packet, Pubkey, []byte, error) { if len(input) < headSize+1 { return nil, Pubkey{}, nil, ErrPacketTooSmall } hash, sig, sigdata := input[:macSize], input[macSize:headSize], input[headSize:] - shouldhash := crypto.Keccak256(input[macSize:]) + shouldhash := crypto.Keccak256(input[macSize:], celoClientSalt) if !bytes.Equal(hash, shouldhash) { return nil, Pubkey{}, nil, ErrBadHash } @@ -257,7 +259,7 @@ return nil, nil, err } copy(packet[macSize:], sig) // Add the hash to the front. Note: this doesn't protect the packet in any way. - hash = crypto.Keccak256(packet[macSize:]) + hash = crypto.Keccak256(packet[macSize:], celoClientSalt) copy(packet, hash) return packet, hash, nil }
diff --git go-ethereum/p2p/simulations/adapters/exec.go celo/p2p/simulations/adapters/exec.go index 017315b2395c4afa32a8b1a248296a5f500cb6d4..e24057eab7e610bfb78165a44574050533a3cffb 100644 --- go-ethereum/p2p/simulations/adapters/exec.go +++ celo/p2p/simulations/adapters/exec.go @@ -428,6 +428,7 @@ }   // Send status to the host. statusJSON, _ := json.Marshal(status) + // #nosec (we don't use this) if _, err := http.Post(statusURL, "application/json", bytes.NewReader(statusJSON)); err != nil { log.Crit("Can't post startup info", "url", statusURL, "err", err) }
diff --git go-ethereum/crypto/celo_crypto.go celo/crypto/celo_crypto.go new file mode 100644 index 0000000000000000000000000000000000000000..b987ee99e89c9959c4ef007b23566cb10db21e54 --- /dev/null +++ celo/crypto/celo_crypto.go @@ -0,0 +1,42 @@ +package crypto + +import ( + "crypto/ecdsa" + "encoding/hex" +) + +func PrivECDSAToHex(k *ecdsa.PrivateKey) []byte { + return hexEncode(FromECDSA(k)) +} + +func PubECDSAToHex(k *ecdsa.PublicKey) []byte { + return hexEncode(FromECDSAPub(k)) +} + +func PrivECDSAFromHex(k []byte) (*ecdsa.PrivateKey, error) { + data, err := hexDecode(k) + if err != nil { + return nil, err + } + return ToECDSA(data) +} + +func PubECDSAFromHex(k []byte) (*ecdsa.PublicKey, error) { + data, err := hexDecode(k) + if err != nil { + return nil, err + } + return UnmarshalPubkey(data) +} + +func hexEncode(src []byte) []byte { + dst := make([]byte, hex.EncodedLen(len(src))) + hex.Encode(dst, src) + return dst +} + +func hexDecode(src []byte) ([]byte, error) { + dst := make([]byte, hex.DecodedLen(len(src))) + _, err := hex.Decode(dst, src) + return dst, err +}
diff --git go-ethereum/crypto/signature_nocgo.go celo/crypto/signature_nocgo.go index fd1e66c7e6fa9ba8ed2dc4f88b694062fae5c6e3..3e48e51e84ebc9a11c15148b5b0e2d592a913b6e 100644 --- go-ethereum/crypto/signature_nocgo.go +++ celo/crypto/signature_nocgo.go @@ -24,37 +24,48 @@ "crypto/ecdsa" "crypto/elliptic" "errors" "fmt" - "math/big"   - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa" )   // Ecrecover returns the uncompressed public key that created the given signature. func Ecrecover(hash, sig []byte) ([]byte, error) { - pub, err := SigToPub(hash, sig) + pub, err := sigToPub(hash, sig) if err != nil { return nil, err } - bytes := (*btcec.PublicKey)(pub).SerializeUncompressed() + bytes := pub.SerializeUncompressed() return bytes, err }   -// SigToPub returns the public key that created the given signature. -func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { +func sigToPub(hash, sig []byte) (*btcec.PublicKey, error) { + if len(sig) != SignatureLength { + return nil, errors.New("invalid signature") + } // Convert to btcec input format with 'recovery id' v at the beginning. btcsig := make([]byte, SignatureLength) - btcsig[0] = sig[64] + 27 + btcsig[0] = sig[RecoveryIDOffset] + 27 copy(btcsig[1:], sig)   - pub, _, err := btcec.RecoverCompact(btcec.S256(), btcsig, hash) - return (*ecdsa.PublicKey)(pub), err + pub, _, err := btc_ecdsa.RecoverCompact(btcsig, hash) + return pub, err +} + +// SigToPub returns the public key that created the given signature. +func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { + pub, err := sigToPub(hash, sig) + if err != nil { + return nil, err + } + return pub.ToECDSA(), nil }   // Sign calculates an ECDSA signature. // // This function is susceptible to chosen plaintext attacks that can leak // information about the private key that is used for signing. Callers must -// be aware that the given hash cannot be chosen by an adversery. Common +// be aware that the given hash cannot be chosen by an adversary. Common // solution is to hash any input before calculating the signature. // // The produced signature is in the [R || S || V] format where V is 0 or 1. @@ -65,14 +76,20 @@ } if prv.Curve != btcec.S256() { return nil, fmt.Errorf("private key curve is not secp256k1") } - sig, err := btcec.SignCompact(btcec.S256(), (*btcec.PrivateKey)(prv), hash, false) + // ecdsa.PrivateKey -> btcec.PrivateKey + var priv btcec.PrivateKey + if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() { + return nil, fmt.Errorf("invalid private key") + } + defer priv.Zero() + sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey if err != nil { return nil, err } // Convert to Ethereum signature format with 'recovery id' v at the end. v := sig[0] - 27 copy(sig, sig[1:]) - sig[64] = v + sig[RecoveryIDOffset] = v return sig, nil }   @@ -83,13 +100,20 @@ func VerifySignature(pubkey, hash, signature []byte) bool { if len(signature) != 64 { return false } - sig := &btcec.Signature{R: new(big.Int).SetBytes(signature[:32]), S: new(big.Int).SetBytes(signature[32:])} - key, err := btcec.ParsePubKey(pubkey, btcec.S256()) + var r, s btcec.ModNScalar + if r.SetByteSlice(signature[:32]) { + return false // overflow + } + if s.SetByteSlice(signature[32:]) { + return false + } + sig := btc_ecdsa.NewSignature(&r, &s) + key, err := btcec.ParsePubKey(pubkey) if err != nil { return false } // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't. - if sig.S.Cmp(secp256k1halfN) > 0 { + if s.IsOverHalfOrder() { return false } return sig.Verify(hash, key) @@ -100,16 +124,26 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) { if len(pubkey) != 33 { return nil, errors.New("invalid compressed public key length") } - key, err := btcec.ParsePubKey(pubkey, btcec.S256()) + key, err := btcec.ParsePubKey(pubkey) if err != nil { return nil, err } return key.ToECDSA(), nil }   -// CompressPubkey encodes a public key to the 33-byte compressed format. +// CompressPubkey encodes a public key to the 33-byte compressed format. The +// provided PublicKey must be valid. Namely, the coordinates must not be larger +// than 32 bytes each, they must be less than the field prime, and it must be a +// point on the secp256k1 curve. This is the case for a PublicKey constructed by +// elliptic.Unmarshal (see UnmarshalPubkey), or by ToECDSA and ecdsa.GenerateKey +// when constructing a PrivateKey. func CompressPubkey(pubkey *ecdsa.PublicKey) []byte { - return (*btcec.PublicKey)(pubkey).SerializeCompressed() + // NOTE: the coordinates may be validated with + // btcec.ParsePubKey(FromECDSAPub(pubkey)) + var x, y btcec.FieldVal + x.SetByteSlice(pubkey.X.Bytes()) + y.SetByteSlice(pubkey.Y.Bytes()) + return btcec.NewPublicKey(&x, &y).SerializeCompressed() }   // S256 returns an instance of the secp256k1 curve.
diff --git go-ethereum/crypto/bls/bls_test.go celo/crypto/bls/bls_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f6da2f36d6175cab9745653afba2163c7c182763 --- /dev/null +++ celo/crypto/bls/bls_test.go @@ -0,0 +1,58 @@ +package blscrypto + +import ( + "encoding/hex" + "testing" + + //nolint:goimports + "github.com/celo-org/celo-bls-go/bls" + "github.com/ethereum/go-ethereum/crypto" +) + +func TestECDSAToBLS(t *testing.T) { + privateKeyECDSA, _ := crypto.HexToECDSA("4f837096cd8578c1f14c9644692c444bbb61426297ff9e8a78a1e7242f541fb3") + privateKeyBLSBytes, _ := ECDSAToBLS(privateKeyECDSA) + t.Logf("private key: %x", privateKeyBLSBytes) + privateKeyBLS, _ := bls.DeserializePrivateKey(privateKeyBLSBytes) + publicKeyBLS, _ := privateKeyBLS.ToPublic() + publicKeyBLSBytes, _ := publicKeyBLS.Serialize() + t.Logf("public key: %x", publicKeyBLSBytes) + + address, _ := hex.DecodeString("4f837096cd8578c1f14c9644692c444bbb614262") + pop, _ := privateKeyBLS.SignPoP(address) + popBytes, _ := pop.Serialize() + t.Logf("pop: %x", popBytes) +} + +func split(buf []byte, lim int) []SerializedPublicKey { + chunks := make([]SerializedPublicKey, 0, len(buf)/lim+1) + for len(buf) >= lim { + pubKeyBytesFixed := SerializedPublicKey{} + copy(pubKeyBytesFixed[:], buf[:lim]) + buf = buf[lim:] + chunks = append(chunks, pubKeyBytesFixed) + } + if len(buf) > 0 { + pubKeyBytesFixed := SerializedPublicKey{} + copy(pubKeyBytesFixed[:], buf) + chunks = append(chunks, pubKeyBytesFixed) + } + return chunks +} + +func TestEncodeEpochSnarkData(t *testing.T) { + // Serialize the public keys for the validators in the validator set. + pubkeys := "45a3ed64a457fbc0e875b0d6dcc372216f96571eefd7a07d373a4de2b73cbebe6b7d43025a4306d356f5fc189ea720013295a3110785f5f7783e7e22a582b810ffdc5e3b10a61c38d3ee0f70ddc59294dd03d4753c7a3500f3c1456d19571981d13b719de39cbf8c84a840484820d3b80836bfa161971f0c32dcd6b23d72adf3d817b9e648082d7e1c0a39fb6393390153ba4ca1ec7fb74a7c4c4f77c2399a214535b303c629b298fa946bbb4c7325ed3a7ac15fe8fdb311287cb06b75ba94813e511d58c8c12709103dfd66c13797c404509da9659f6395b318866b448a2150ffbc4f3f4524d3c5fc453e7020f7a2009ea4bdceed84a0431f153aa834a947bb1ed239f95d9c32c3110e0937687012e44d5e68cadefdc10f7bea106bfcb07881b66e28d8b7fb1418bf311830eba1b0cffe5ec9348ec6b54f2bb21434dce17176279d5525694499b6988b4ecaa8232000f473f369e191669fa3e5ff781f3040fd3b16f694b6bb6798d7f3067c62d49180022cbb9f33f964bb4ddfb20019c85780" + pk1, _ := hex.DecodeString(pubkeys) + blsPubKeys := split(pk1, PUBLICKEYBYTES) + + maxNonSigners := uint32(1) + + // Before the Donut fork, use the snark data encoding without epoch entropy. + encodedEpochBlock, encodedEpochBlockExtraData, err := EncodeEpochSnarkData(blsPubKeys, maxNonSigners, 1) + if err != nil { + t.Log("Error ", err) + } + t.Logf("Encoded epoch block: %x", encodedEpochBlock) + t.Logf("Encoded epoch block extra data: %x", encodedEpochBlockExtraData) +}
diff --git go-ethereum/crypto/bls/bls.go celo/crypto/bls/bls.go new file mode 100644 index 0000000000000000000000000000000000000000..ab9931263f508e560424fba174f63569073b779e --- /dev/null +++ celo/crypto/bls/bls.go @@ -0,0 +1,266 @@ +package blscrypto + +import ( + "bytes" + "crypto/ecdsa" + "encoding/hex" + "errors" + "fmt" + "math/big" + "reflect" + + "github.com/ethereum/go-ethereum/common" + + "github.com/celo-org/celo-bls-go/bls" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" +) + +const ( + PUBLICKEYBYTES = bls.PUBLICKEYBYTES + SIGNATUREBYTES = bls.SIGNATUREBYTES + EPOCHENTROPYBYTES = bls.EPOCHENTROPYBYTES +) + +var ( + serializedPublicKeyT = reflect.TypeOf(SerializedPublicKey{}) + serializedSignatureT = reflect.TypeOf(SerializedSignature{}) +) + +type SerializedPublicKey [PUBLICKEYBYTES]byte + +// EpochEntropyFromHash truncates the given hash to the length of epoch SNARK entropy. +func EpochEntropyFromHash(hash common.Hash) bls.EpochEntropy { + var entropy bls.EpochEntropy + copy(entropy[:], hash[:EPOCHENTROPYBYTES]) + return entropy +} + +// MarshalText returns the hex representation of pk. +func (pk SerializedPublicKey) MarshalText() ([]byte, error) { + return hexutil.Bytes(pk[:]).MarshalText() +} + +// UnmarshalText parses a BLS public key in hex syntax. +func (pk *SerializedPublicKey) UnmarshalText(input []byte) error { + return hexutil.UnmarshalFixedText("SerializedPublicKey", input, pk[:]) +} + +// UnmarshalJSON parses a BLS public key in hex syntax. +func (pk *SerializedPublicKey) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalFixedJSON(serializedPublicKeyT, input, pk[:]) +} + +type SerializedSignature [SIGNATUREBYTES]byte + +// MarshalText returns the hex representation of sig. +func (sig SerializedSignature) MarshalText() ([]byte, error) { + return hexutil.Bytes(sig[:]).MarshalText() +} + +// UnmarshalText parses a BLS signature in hex syntax. +func (sig *SerializedSignature) UnmarshalText(input []byte) error { + return hexutil.UnmarshalFixedText("SerializedSignature", input, sig[:]) +} + +// UnmarshalJSON parses a BLS signature in hex syntax. +func (sig *SerializedSignature) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalFixedJSON(serializedSignatureT, input, sig[:]) +} + +func ECDSAToBLS(privateKeyECDSA *ecdsa.PrivateKey) ([]byte, error) { + for i := 0; i < 256; i++ { + modulus := big.NewInt(0) + modulus, ok := modulus.SetString(bls.MODULUS377, 10) + if !ok { + return nil, errors.New("can't parse modulus") + } + privateKeyECDSABytes := crypto.FromECDSA(privateKeyECDSA) + + keyBytes := []byte("ecdsatobls") + keyBytes = append(keyBytes, uint8(i)) + keyBytes = append(keyBytes, privateKeyECDSABytes...) + + privateKeyBLSBytes := crypto.Keccak256(keyBytes) + privateKeyBLSBytes[0] &= bls.MODULUSMASK + privateKeyBLSBig := big.NewInt(0) + privateKeyBLSBig.SetBytes(privateKeyBLSBytes) + if privateKeyBLSBig.Cmp(modulus) >= 0 { + continue + } + + privateKeyBytes := privateKeyBLSBig.Bytes() + for len(privateKeyBytes) < len(privateKeyBLSBytes) { + privateKeyBytes = append([]byte{0x00}, privateKeyBytes...) + } + if !bytes.Equal(privateKeyBLSBytes, privateKeyBytes) { + return nil, fmt.Errorf("private key bytes should have been the same: %s, %s", hex.EncodeToString(privateKeyBLSBytes), hex.EncodeToString(privateKeyBytes)) + } + // reverse order, as the BLS library expects little endian + for i := len(privateKeyBytes)/2 - 1; i >= 0; i-- { + opp := len(privateKeyBytes) - 1 - i + privateKeyBytes[i], privateKeyBytes[opp] = privateKeyBytes[opp], privateKeyBytes[i] + } + + privateKeyBLS, err := bls.DeserializePrivateKey(privateKeyBytes) + if err != nil { + return nil, err + } + defer privateKeyBLS.Destroy() + privateKeyBLSBytesFromLib, err := privateKeyBLS.Serialize() + if err != nil { + return nil, err + } + if !bytes.Equal(privateKeyBytes, privateKeyBLSBytesFromLib) { + return nil, errors.New("private key bytes from library should have been the same") + } + + return privateKeyBLSBytesFromLib, nil + } + + return nil, errors.New("couldn't derive a BLS key from an ECDSA key") +} + +func PrivateToPublic(privateKeyBytes []byte) (SerializedPublicKey, error) { + privateKey, err := bls.DeserializePrivateKey(privateKeyBytes) + if err != nil { + return SerializedPublicKey{}, err + } + defer privateKey.Destroy() + + publicKey, err := privateKey.ToPublic() + if err != nil { + return SerializedPublicKey{}, err + } + defer publicKey.Destroy() + + pubKeyBytes, err := publicKey.Serialize() + if err != nil { + return SerializedPublicKey{}, err + } + + pubKeyBytesFixed := SerializedPublicKey{} + copy(pubKeyBytesFixed[:], pubKeyBytes) + + return pubKeyBytesFixed, nil +} + +func VerifyAggregatedSignature(publicKeys []SerializedPublicKey, message []byte, extraData []byte, signature []byte, shouldUseCompositeHasher, cip22 bool) error { + publicKeyObjs := []*bls.PublicKey{} + for _, publicKey := range publicKeys { + publicKeyObj, err := bls.DeserializePublicKeyCached(publicKey[:]) + if err != nil { + return err + } + defer publicKeyObj.Destroy() + publicKeyObjs = append(publicKeyObjs, publicKeyObj) + } + apk, err := bls.AggregatePublicKeys(publicKeyObjs) + if err != nil { + return err + } + defer apk.Destroy() + + signatureObj, err := bls.DeserializeSignature(signature) + if err != nil { + return err + } + defer signatureObj.Destroy() + + err = apk.VerifySignature(message, extraData, signatureObj, shouldUseCompositeHasher, cip22) + return err +} + +func AggregateSignatures(signatures [][]byte) ([]byte, error) { + signatureObjs := []*bls.Signature{} + for _, signature := range signatures { + signatureObj, err := bls.DeserializeSignature(signature) + if err != nil { + return nil, err + } + defer signatureObj.Destroy() + signatureObjs = append(signatureObjs, signatureObj) + } + + asig, err := bls.AggregateSignatures(signatureObjs) + if err != nil { + return nil, err + } + defer asig.Destroy() + + asigBytes, err := asig.Serialize() + if err != nil { + return nil, err + } + + return asigBytes, nil +} + +func VerifySignature(publicKey SerializedPublicKey, message []byte, extraData []byte, signature []byte, shouldUseCompositeHasher, cip22 bool) error { + publicKeyObj, err := bls.DeserializePublicKeyCached(publicKey[:]) + if err != nil { + return err + } + defer publicKeyObj.Destroy() + + signatureObj, err := bls.DeserializeSignature(signature) + if err != nil { + return err + } + defer signatureObj.Destroy() + + err = publicKeyObj.VerifySignature(message, extraData, signatureObj, shouldUseCompositeHasher, cip22) + return err +} + +func EncodeEpochSnarkData(newValSet []SerializedPublicKey, maximumNonSigners uint32, epochIndex uint16) ([]byte, []byte, error) { + pubKeys := []*bls.PublicKey{} + for _, pubKey := range newValSet { + publicKeyObj, err := bls.DeserializePublicKeyCached(pubKey[:]) + if err != nil { + return nil, nil, err + } + defer publicKeyObj.Destroy() + + pubKeys = append(pubKeys, publicKeyObj) + } + + message, err := bls.EncodeEpochToBytes(epochIndex, maximumNonSigners, pubKeys) + return message, nil, err +} + +func EncodeEpochSnarkDataCIP22(newValSet []SerializedPublicKey, maximumNonSigners, maxValidators uint32, epochIndex uint16, round uint8, blockHash, parentHash bls.EpochEntropy) ([]byte, []byte, error) { + pubKeys := []*bls.PublicKey{} + for _, pubKey := range newValSet { + publicKeyObj, err := bls.DeserializePublicKeyCached(pubKey[:]) + if err != nil { + return nil, nil, err + } + defer publicKeyObj.Destroy() + + pubKeys = append(pubKeys, publicKeyObj) + } + + return bls.EncodeEpochToBytesCIP22(epochIndex, round, blockHash, parentHash, maximumNonSigners, maxValidators, pubKeys) +} + +func SerializedSignatureFromBytes(serializedSignature []byte) (SerializedSignature, error) { + if len(serializedSignature) != SIGNATUREBYTES { + return SerializedSignature{}, fmt.Errorf("wrong length for serialized signature: expected %d, got %d", SIGNATUREBYTES, len(serializedSignature)) + } + signatureBytesFixed := SerializedSignature{} + copy(signatureBytesFixed[:], serializedSignature) + return signatureBytesFixed, nil +} + +func UncompressKey(serialized SerializedPublicKey) ([]byte, error) { + publicKey, err := bls.DeserializePublicKeyCached(serialized[:]) + if err != nil { + return nil, err + } + uncompressedBytes, err := publicKey.SerializeUncompressed() + if err != nil { + return nil, err + } + return uncompressedBytes, nil +}
diff --git go-ethereum/crypto/bls12381/bls12_381_test.go celo/crypto/bls12381/bls12_381_test.go index 6bf5834105943eb6cc6463ea278284612ea71b81..8c71b2c0dc46861bc1ead3b183c0d314e049aba4 100644 --- go-ethereum/crypto/bls12381/bls12_381_test.go +++ celo/crypto/bls12381/bls12_381_test.go @@ -2,12 +2,47 @@ package bls12381   import ( "crypto/rand" + "encoding/hex" + "errors" "math/big" )   var fuz = 10   func randScalar(max *big.Int) *big.Int { - a, _ := rand.Int(rand.Reader, max) + a, err := rand.Int(rand.Reader, max) + if err != nil { + panic(errors.New("")) + } return a } + +func fromHex(size int, hexStrs ...string) []byte { + var out []byte + if size > 0 { + out = make([]byte, size*len(hexStrs)) + } + for i := 0; i < len(hexStrs); i++ { + hexStr := hexStrs[i] + if hexStr[:2] == "0x" { + hexStr = hexStr[2:] + } + if len(hexStr)%2 == 1 { + hexStr = "0" + hexStr + } + bytes, err := hex.DecodeString(hexStr) + if err != nil { + return nil + } + if size <= 0 { + out = append(out, bytes...) + } else { + if len(bytes) > size { + return nil + } + offset := i*size + (size - len(bytes)) + copy(out[offset:], bytes) + } + } + return out +}
diff --git go-ethereum/crypto/bls12381/isogeny.go celo/crypto/bls12381/isogeny.go index 91e03936d57dd34761aba6caefbcc3cd62f9a951..f43213b9b65d4e1529c3d9db7b9d3f37af6aa1b1 100644 --- go-ethereum/crypto/bls12381/isogeny.go +++ celo/crypto/bls12381/isogeny.go @@ -1,19 +1,3 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   // isogenyMapG1 applies 11-isogeny map for BLS12-381 G1 defined at draft-irtf-cfrg-hash-to-curve-06. @@ -77,149 +61,149 @@ y.set(yNum) }   var isogenyConstansG1 = [4][16]*fe{ - [16]*fe{ - &fe{0x4d18b6f3af00131c, 0x19fa219793fee28c, 0x3f2885f1467f19ae, 0x23dcea34f2ffb304, 0xd15b58d2ffc00054, 0x0913be200a20bef4}, - &fe{0x898985385cdbbd8b, 0x3c79e43cc7d966aa, 0x1597e193f4cd233a, 0x8637ef1e4d6623ad, 0x11b22deed20d827b, 0x07097bc5998784ad}, - &fe{0xa542583a480b664b, 0xfc7169c026e568c6, 0x5ba2ef314ed8b5a6, 0x5b5491c05102f0e7, 0xdf6e99707d2a0079, 0x0784151ed7605524}, - &fe{0x494e212870f72741, 0xab9be52fbda43021, 0x26f5577994e34c3d, 0x049dfee82aefbd60, 0x65dadd7828505289, 0x0e93d431ea011aeb}, - &fe{0x90ee774bd6a74d45, 0x7ada1c8a41bfb185, 0x0f1a8953b325f464, 0x104c24211be4805c, 0x169139d319ea7a8f, 0x09f20ead8e532bf6}, - &fe{0x6ddd93e2f43626b7, 0xa5482c9aa1ccd7bd, 0x143245631883f4bd, 0x2e0a94ccf77ec0db, 0xb0282d480e56489f, 0x18f4bfcbb4368929}, - &fe{0x23c5f0c953402dfd, 0x7a43ff6958ce4fe9, 0x2c390d3d2da5df63, 0xd0df5c98e1f9d70f, 0xffd89869a572b297, 0x1277ffc72f25e8fe}, - &fe{0x79f4f0490f06a8a6, 0x85f894a88030fd81, 0x12da3054b18b6410, 0xe2a57f6505880d65, 0xbba074f260e400f1, 0x08b76279f621d028}, - &fe{0xe67245ba78d5b00b, 0x8456ba9a1f186475, 0x7888bff6e6b33bb4, 0xe21585b9a30f86cb, 0x05a69cdcef55feee, 0x09e699dd9adfa5ac}, - &fe{0x0de5c357bff57107, 0x0a0db4ae6b1a10b2, 0xe256bb67b3b3cd8d, 0x8ad456574e9db24f, 0x0443915f50fd4179, 0x098c4bf7de8b6375}, - &fe{0xe6b0617e7dd929c7, 0xfe6e37d442537375, 0x1dafdeda137a489e, 0xe4efd1ad3f767ceb, 0x4a51d8667f0fe1cf, 0x054fdf4bbf1d821c}, - &fe{0x72db2a50658d767b, 0x8abf91faa257b3d5, 0xe969d6833764ab47, 0x464170142a1009eb, 0xb14f01aadb30be2f, 0x18ae6a856f40715d}, - &fe{0, 0, 0, 0, 0, 0}, - &fe{0, 0, 0, 0, 0, 0}, - &fe{0, 0, 0, 0, 0, 0}, - &fe{0, 0, 0, 0, 0, 0}, + { + {0x4d18b6f3af00131c, 0x19fa219793fee28c, 0x3f2885f1467f19ae, 0x23dcea34f2ffb304, 0xd15b58d2ffc00054, 0x0913be200a20bef4}, + {0x898985385cdbbd8b, 0x3c79e43cc7d966aa, 0x1597e193f4cd233a, 0x8637ef1e4d6623ad, 0x11b22deed20d827b, 0x07097bc5998784ad}, + {0xa542583a480b664b, 0xfc7169c026e568c6, 0x5ba2ef314ed8b5a6, 0x5b5491c05102f0e7, 0xdf6e99707d2a0079, 0x0784151ed7605524}, + {0x494e212870f72741, 0xab9be52fbda43021, 0x26f5577994e34c3d, 0x049dfee82aefbd60, 0x65dadd7828505289, 0x0e93d431ea011aeb}, + {0x90ee774bd6a74d45, 0x7ada1c8a41bfb185, 0x0f1a8953b325f464, 0x104c24211be4805c, 0x169139d319ea7a8f, 0x09f20ead8e532bf6}, + {0x6ddd93e2f43626b7, 0xa5482c9aa1ccd7bd, 0x143245631883f4bd, 0x2e0a94ccf77ec0db, 0xb0282d480e56489f, 0x18f4bfcbb4368929}, + {0x23c5f0c953402dfd, 0x7a43ff6958ce4fe9, 0x2c390d3d2da5df63, 0xd0df5c98e1f9d70f, 0xffd89869a572b297, 0x1277ffc72f25e8fe}, + {0x79f4f0490f06a8a6, 0x85f894a88030fd81, 0x12da3054b18b6410, 0xe2a57f6505880d65, 0xbba074f260e400f1, 0x08b76279f621d028}, + {0xe67245ba78d5b00b, 0x8456ba9a1f186475, 0x7888bff6e6b33bb4, 0xe21585b9a30f86cb, 0x05a69cdcef55feee, 0x09e699dd9adfa5ac}, + {0x0de5c357bff57107, 0x0a0db4ae6b1a10b2, 0xe256bb67b3b3cd8d, 0x8ad456574e9db24f, 0x0443915f50fd4179, 0x098c4bf7de8b6375}, + {0xe6b0617e7dd929c7, 0xfe6e37d442537375, 0x1dafdeda137a489e, 0xe4efd1ad3f767ceb, 0x4a51d8667f0fe1cf, 0x054fdf4bbf1d821c}, + {0x72db2a50658d767b, 0x8abf91faa257b3d5, 0xe969d6833764ab47, 0x464170142a1009eb, 0xb14f01aadb30be2f, 0x18ae6a856f40715d}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, }, - [16]*fe{ - &fe{0xb962a077fdb0f945, 0xa6a9740fefda13a0, 0xc14d568c3ed6c544, 0xb43fc37b908b133e, 0x9c0b3ac929599016, 0x0165aa6c93ad115f}, - &fe{0x23279a3ba506c1d9, 0x92cfca0a9465176a, 0x3b294ab13755f0ff, 0x116dda1c5070ae93, 0xed4530924cec2045, 0x083383d6ed81f1ce}, - &fe{0x9885c2a6449fecfc, 0x4a2b54ccd37733f0, 0x17da9ffd8738c142, 0xa0fba72732b3fafd, 0xff364f36e54b6812, 0x0f29c13c660523e2}, - &fe{0xe349cc118278f041, 0xd487228f2f3204fb, 0xc9d325849ade5150, 0x43a92bd69c15c2df, 0x1c2c7844bc417be4, 0x12025184f407440c}, - &fe{0x587f65ae6acb057b, 0x1444ef325140201f, 0xfbf995e71270da49, 0xccda066072436a42, 0x7408904f0f186bb2, 0x13b93c63edf6c015}, - &fe{0xfb918622cd141920, 0x4a4c64423ecaddb4, 0x0beb232927f7fb26, 0x30f94df6f83a3dc2, 0xaeedd424d780f388, 0x06cc402dd594bbeb}, - &fe{0xd41f761151b23f8f, 0x32a92465435719b3, 0x64f436e888c62cb9, 0xdf70a9a1f757c6e4, 0x6933a38d5b594c81, 0x0c6f7f7237b46606}, - &fe{0x693c08747876c8f7, 0x22c9850bf9cf80f0, 0x8e9071dab950c124, 0x89bc62d61c7baf23, 0xbc6be2d8dad57c23, 0x17916987aa14a122}, - &fe{0x1be3ff439c1316fd, 0x9965243a7571dfa7, 0xc7f7f62962f5cd81, 0x32c6aa9af394361c, 0xbbc2ee18e1c227f4, 0x0c102cbac531bb34}, - &fe{0x997614c97bacbf07, 0x61f86372b99192c0, 0x5b8c95fc14353fc3, 0xca2b066c2a87492f, 0x16178f5bbf698711, 0x12a6dcd7f0f4e0e8}, - &fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, - &fe{0, 0, 0, 0, 0, 0}, - &fe{0, 0, 0, 0, 0, 0}, - &fe{0, 0, 0, 0, 0, 0}, - &fe{0, 0, 0, 0, 0, 0}, - &fe{0, 0, 0, 0, 0, 0}, + { + {0xb962a077fdb0f945, 0xa6a9740fefda13a0, 0xc14d568c3ed6c544, 0xb43fc37b908b133e, 0x9c0b3ac929599016, 0x0165aa6c93ad115f}, + {0x23279a3ba506c1d9, 0x92cfca0a9465176a, 0x3b294ab13755f0ff, 0x116dda1c5070ae93, 0xed4530924cec2045, 0x083383d6ed81f1ce}, + {0x9885c2a6449fecfc, 0x4a2b54ccd37733f0, 0x17da9ffd8738c142, 0xa0fba72732b3fafd, 0xff364f36e54b6812, 0x0f29c13c660523e2}, + {0xe349cc118278f041, 0xd487228f2f3204fb, 0xc9d325849ade5150, 0x43a92bd69c15c2df, 0x1c2c7844bc417be4, 0x12025184f407440c}, + {0x587f65ae6acb057b, 0x1444ef325140201f, 0xfbf995e71270da49, 0xccda066072436a42, 0x7408904f0f186bb2, 0x13b93c63edf6c015}, + {0xfb918622cd141920, 0x4a4c64423ecaddb4, 0x0beb232927f7fb26, 0x30f94df6f83a3dc2, 0xaeedd424d780f388, 0x06cc402dd594bbeb}, + {0xd41f761151b23f8f, 0x32a92465435719b3, 0x64f436e888c62cb9, 0xdf70a9a1f757c6e4, 0x6933a38d5b594c81, 0x0c6f7f7237b46606}, + {0x693c08747876c8f7, 0x22c9850bf9cf80f0, 0x8e9071dab950c124, 0x89bc62d61c7baf23, 0xbc6be2d8dad57c23, 0x17916987aa14a122}, + {0x1be3ff439c1316fd, 0x9965243a7571dfa7, 0xc7f7f62962f5cd81, 0x32c6aa9af394361c, 0xbbc2ee18e1c227f4, 0x0c102cbac531bb34}, + {0x997614c97bacbf07, 0x61f86372b99192c0, 0x5b8c95fc14353fc3, 0xca2b066c2a87492f, 0x16178f5bbf698711, 0x12a6dcd7f0f4e0e8}, + {0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, }, - [16]*fe{ - &fe{0x2b567ff3e2837267, 0x1d4d9e57b958a767, 0xce028fea04bd7373, 0xcc31a30a0b6cd3df, 0x7d7b18a682692693, 0x0d300744d42a0310}, - &fe{0x99c2555fa542493f, 0xfe7f53cc4874f878, 0x5df0608b8f97608a, 0x14e03832052b49c8, 0x706326a6957dd5a4, 0x0a8dadd9c2414555}, - &fe{0x13d942922a5cf63a, 0x357e33e36e261e7d, 0xcf05a27c8456088d, 0x0000bd1de7ba50f0, 0x83d0c7532f8c1fde, 0x13f70bf38bbf2905}, - &fe{0x5c57fd95bfafbdbb, 0x28a359a65e541707, 0x3983ceb4f6360b6d, 0xafe19ff6f97e6d53, 0xb3468f4550192bf7, 0x0bb6cde49d8ba257}, - &fe{0x590b62c7ff8a513f, 0x314b4ce372cacefd, 0x6bef32ce94b8a800, 0x6ddf84a095713d5f, 0x64eace4cb0982191, 0x0386213c651b888d}, - &fe{0xa5310a31111bbcdd, 0xa14ac0f5da148982, 0xf9ad9cc95423d2e9, 0xaa6ec095283ee4a7, 0xcf5b1f022e1c9107, 0x01fddf5aed881793}, - &fe{0x65a572b0d7a7d950, 0xe25c2d8183473a19, 0xc2fcebe7cb877dbd, 0x05b2d36c769a89b0, 0xba12961be86e9efb, 0x07eb1b29c1dfde1f}, - &fe{0x93e09572f7c4cd24, 0x364e929076795091, 0x8569467e68af51b5, 0xa47da89439f5340f, 0xf4fa918082e44d64, 0x0ad52ba3e6695a79}, - &fe{0x911429844e0d5f54, 0xd03f51a3516bb233, 0x3d587e5640536e66, 0xfa86d2a3a9a73482, 0xa90ed5adf1ed5537, 0x149c9c326a5e7393}, - &fe{0x462bbeb03c12921a, 0xdc9af5fa0a274a17, 0x9a558ebde836ebed, 0x649ef8f11a4fae46, 0x8100e1652b3cdc62, 0x1862bd62c291dacb}, - &fe{0x05c9b8ca89f12c26, 0x0194160fa9b9ac4f, 0x6a643d5a6879fa2c, 0x14665bdd8846e19d, 0xbb1d0d53af3ff6bf, 0x12c7e1c3b28962e5}, - &fe{0xb55ebf900b8a3e17, 0xfedc77ec1a9201c4, 0x1f07db10ea1a4df4, 0x0dfbd15dc41a594d, 0x389547f2334a5391, 0x02419f98165871a4}, - &fe{0xb416af000745fc20, 0x8e563e9d1ea6d0f5, 0x7c763e17763a0652, 0x01458ef0159ebbef, 0x8346fe421f96bb13, 0x0d2d7b829ce324d2}, - &fe{0x93096bb538d64615, 0x6f2a2619951d823a, 0x8f66b3ea59514fa4, 0xf563e63704f7092f, 0x724b136c4cf2d9fa, 0x046959cfcfd0bf49}, - &fe{0xea748d4b6e405346, 0x91e9079c2c02d58f, 0x41064965946d9b59, 0xa06731f1d2bbe1ee, 0x07f897e267a33f1b, 0x1017290919210e5f}, - &fe{0x872aa6c17d985097, 0xeecc53161264562a, 0x07afe37afff55002, 0x54759078e5be6838, 0xc4b92d15db8acca8, 0x106d87d1b51d13b9}, + { + {0x2b567ff3e2837267, 0x1d4d9e57b958a767, 0xce028fea04bd7373, 0xcc31a30a0b6cd3df, 0x7d7b18a682692693, 0x0d300744d42a0310}, + {0x99c2555fa542493f, 0xfe7f53cc4874f878, 0x5df0608b8f97608a, 0x14e03832052b49c8, 0x706326a6957dd5a4, 0x0a8dadd9c2414555}, + {0x13d942922a5cf63a, 0x357e33e36e261e7d, 0xcf05a27c8456088d, 0x0000bd1de7ba50f0, 0x83d0c7532f8c1fde, 0x13f70bf38bbf2905}, + {0x5c57fd95bfafbdbb, 0x28a359a65e541707, 0x3983ceb4f6360b6d, 0xafe19ff6f97e6d53, 0xb3468f4550192bf7, 0x0bb6cde49d8ba257}, + {0x590b62c7ff8a513f, 0x314b4ce372cacefd, 0x6bef32ce94b8a800, 0x6ddf84a095713d5f, 0x64eace4cb0982191, 0x0386213c651b888d}, + {0xa5310a31111bbcdd, 0xa14ac0f5da148982, 0xf9ad9cc95423d2e9, 0xaa6ec095283ee4a7, 0xcf5b1f022e1c9107, 0x01fddf5aed881793}, + {0x65a572b0d7a7d950, 0xe25c2d8183473a19, 0xc2fcebe7cb877dbd, 0x05b2d36c769a89b0, 0xba12961be86e9efb, 0x07eb1b29c1dfde1f}, + {0x93e09572f7c4cd24, 0x364e929076795091, 0x8569467e68af51b5, 0xa47da89439f5340f, 0xf4fa918082e44d64, 0x0ad52ba3e6695a79}, + {0x911429844e0d5f54, 0xd03f51a3516bb233, 0x3d587e5640536e66, 0xfa86d2a3a9a73482, 0xa90ed5adf1ed5537, 0x149c9c326a5e7393}, + {0x462bbeb03c12921a, 0xdc9af5fa0a274a17, 0x9a558ebde836ebed, 0x649ef8f11a4fae46, 0x8100e1652b3cdc62, 0x1862bd62c291dacb}, + {0x05c9b8ca89f12c26, 0x0194160fa9b9ac4f, 0x6a643d5a6879fa2c, 0x14665bdd8846e19d, 0xbb1d0d53af3ff6bf, 0x12c7e1c3b28962e5}, + {0xb55ebf900b8a3e17, 0xfedc77ec1a9201c4, 0x1f07db10ea1a4df4, 0x0dfbd15dc41a594d, 0x389547f2334a5391, 0x02419f98165871a4}, + {0xb416af000745fc20, 0x8e563e9d1ea6d0f5, 0x7c763e17763a0652, 0x01458ef0159ebbef, 0x8346fe421f96bb13, 0x0d2d7b829ce324d2}, + {0x93096bb538d64615, 0x6f2a2619951d823a, 0x8f66b3ea59514fa4, 0xf563e63704f7092f, 0x724b136c4cf2d9fa, 0x046959cfcfd0bf49}, + {0xea748d4b6e405346, 0x91e9079c2c02d58f, 0x41064965946d9b59, 0xa06731f1d2bbe1ee, 0x07f897e267a33f1b, 0x1017290919210e5f}, + {0x872aa6c17d985097, 0xeecc53161264562a, 0x07afe37afff55002, 0x54759078e5be6838, 0xc4b92d15db8acca8, 0x106d87d1b51d13b9}, }, - [16]*fe{ - &fe{0xeb6c359d47e52b1c, 0x18ef5f8a10634d60, 0xddfa71a0889d5b7e, 0x723e71dcc5fc1323, 0x52f45700b70d5c69, 0x0a8b981ee47691f1}, - &fe{0x616a3c4f5535b9fb, 0x6f5f037395dbd911, 0xf25f4cc5e35c65da, 0x3e50dffea3c62658, 0x6a33dca523560776, 0x0fadeff77b6bfe3e}, - &fe{0x2be9b66df470059c, 0x24a2c159a3d36742, 0x115dbe7ad10c2a37, 0xb6634a652ee5884d, 0x04fe8bb2b8d81af4, 0x01c2a7a256fe9c41}, - &fe{0xf27bf8ef3b75a386, 0x898b367476c9073f, 0x24482e6b8c2f4e5f, 0xc8e0bbd6fe110806, 0x59b0c17f7631448a, 0x11037cd58b3dbfbd}, - &fe{0x31c7912ea267eec6, 0x1dbf6f1c5fcdb700, 0xd30d4fe3ba86fdb1, 0x3cae528fbee9a2a4, 0xb1cce69b6aa9ad9a, 0x044393bb632d94fb}, - &fe{0xc66ef6efeeb5c7e8, 0x9824c289dd72bb55, 0x71b1a4d2f119981d, 0x104fc1aafb0919cc, 0x0e49df01d942a628, 0x096c3a09773272d4}, - &fe{0x9abc11eb5fadeff4, 0x32dca50a885728f0, 0xfb1fa3721569734c, 0xc4b76271ea6506b3, 0xd466a75599ce728e, 0x0c81d4645f4cb6ed}, - &fe{0x4199f10e5b8be45b, 0xda64e495b1e87930, 0xcb353efe9b33e4ff, 0x9e9efb24aa6424c6, 0xf08d33680a237465, 0x0d3378023e4c7406}, - &fe{0x7eb4ae92ec74d3a5, 0xc341b4aa9fac3497, 0x5be603899e907687, 0x03bfd9cca75cbdeb, 0x564c2935a96bfa93, 0x0ef3c33371e2fdb5}, - &fe{0x7ee91fd449f6ac2e, 0xe5d5bd5cb9357a30, 0x773a8ca5196b1380, 0xd0fda172174ed023, 0x6cb95e0fa776aead, 0x0d22d5a40cec7cff}, - &fe{0xf727e09285fd8519, 0xdc9d55a83017897b, 0x7549d8bd057894ae, 0x178419613d90d8f8, 0xfce95ebdeb5b490a, 0x0467ffaef23fc49e}, - &fe{0xc1769e6a7c385f1b, 0x79bc930deac01c03, 0x5461c75a23ede3b5, 0x6e20829e5c230c45, 0x828e0f1e772a53cd, 0x116aefa749127bff}, - &fe{0x101c10bf2744c10a, 0xbbf18d053a6a3154, 0xa0ecf39ef026f602, 0xfc009d4996dc5153, 0xb9000209d5bd08d3, 0x189e5fe4470cd73c}, - &fe{0x7ebd546ca1575ed2, 0xe47d5a981d081b55, 0x57b2b625b6d4ca21, 0xb0a1ba04228520cc, 0x98738983c2107ff3, 0x13dddbc4799d81d6}, - &fe{0x09319f2e39834935, 0x039e952cbdb05c21, 0x55ba77a9a2f76493, 0xfd04e3dfc6086467, 0xfb95832e7d78742e, 0x0ef9c24eccaf5e0e}, - &fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + { + {0xeb6c359d47e52b1c, 0x18ef5f8a10634d60, 0xddfa71a0889d5b7e, 0x723e71dcc5fc1323, 0x52f45700b70d5c69, 0x0a8b981ee47691f1}, + {0x616a3c4f5535b9fb, 0x6f5f037395dbd911, 0xf25f4cc5e35c65da, 0x3e50dffea3c62658, 0x6a33dca523560776, 0x0fadeff77b6bfe3e}, + {0x2be9b66df470059c, 0x24a2c159a3d36742, 0x115dbe7ad10c2a37, 0xb6634a652ee5884d, 0x04fe8bb2b8d81af4, 0x01c2a7a256fe9c41}, + {0xf27bf8ef3b75a386, 0x898b367476c9073f, 0x24482e6b8c2f4e5f, 0xc8e0bbd6fe110806, 0x59b0c17f7631448a, 0x11037cd58b3dbfbd}, + {0x31c7912ea267eec6, 0x1dbf6f1c5fcdb700, 0xd30d4fe3ba86fdb1, 0x3cae528fbee9a2a4, 0xb1cce69b6aa9ad9a, 0x044393bb632d94fb}, + {0xc66ef6efeeb5c7e8, 0x9824c289dd72bb55, 0x71b1a4d2f119981d, 0x104fc1aafb0919cc, 0x0e49df01d942a628, 0x096c3a09773272d4}, + {0x9abc11eb5fadeff4, 0x32dca50a885728f0, 0xfb1fa3721569734c, 0xc4b76271ea6506b3, 0xd466a75599ce728e, 0x0c81d4645f4cb6ed}, + {0x4199f10e5b8be45b, 0xda64e495b1e87930, 0xcb353efe9b33e4ff, 0x9e9efb24aa6424c6, 0xf08d33680a237465, 0x0d3378023e4c7406}, + {0x7eb4ae92ec74d3a5, 0xc341b4aa9fac3497, 0x5be603899e907687, 0x03bfd9cca75cbdeb, 0x564c2935a96bfa93, 0x0ef3c33371e2fdb5}, + {0x7ee91fd449f6ac2e, 0xe5d5bd5cb9357a30, 0x773a8ca5196b1380, 0xd0fda172174ed023, 0x6cb95e0fa776aead, 0x0d22d5a40cec7cff}, + {0xf727e09285fd8519, 0xdc9d55a83017897b, 0x7549d8bd057894ae, 0x178419613d90d8f8, 0xfce95ebdeb5b490a, 0x0467ffaef23fc49e}, + {0xc1769e6a7c385f1b, 0x79bc930deac01c03, 0x5461c75a23ede3b5, 0x6e20829e5c230c45, 0x828e0f1e772a53cd, 0x116aefa749127bff}, + {0x101c10bf2744c10a, 0xbbf18d053a6a3154, 0xa0ecf39ef026f602, 0xfc009d4996dc5153, 0xb9000209d5bd08d3, 0x189e5fe4470cd73c}, + {0x7ebd546ca1575ed2, 0xe47d5a981d081b55, 0x57b2b625b6d4ca21, 0xb0a1ba04228520cc, 0x98738983c2107ff3, 0x13dddbc4799d81d6}, + {0x09319f2e39834935, 0x039e952cbdb05c21, 0x55ba77a9a2f76493, 0xfd04e3dfc6086467, 0xfb95832e7d78742e, 0x0ef9c24eccaf5e0e}, + {0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, }, }   var isogenyConstantsG2 = [4][4]*fe2{ - [4]*fe2{ - &fe2{ + { + { fe{0x47f671c71ce05e62, 0x06dd57071206393e, 0x7c80cd2af3fd71a2, 0x048103ea9e6cd062, 0xc54516acc8d037f6, 0x13808f550920ea41}, fe{0x47f671c71ce05e62, 0x06dd57071206393e, 0x7c80cd2af3fd71a2, 0x048103ea9e6cd062, 0xc54516acc8d037f6, 0x13808f550920ea41}, }, - &fe2{ + { fe{0, 0, 0, 0, 0, 0}, fe{0x5fe55555554c71d0, 0x873fffdd236aaaa3, 0x6a6b4619b26ef918, 0x21c2888408874945, 0x2836cda7028cabc5, 0x0ac73310a7fd5abd}, }, - &fe2{ + { fe{0x0a0c5555555971c3, 0xdb0c00101f9eaaae, 0xb1fb2f941d797997, 0xd3960742ef416e1c, 0xb70040e2c20556f4, 0x149d7861e581393b}, fe{0xaff2aaaaaaa638e8, 0x439fffee91b55551, 0xb535a30cd9377c8c, 0x90e144420443a4a2, 0x941b66d3814655e2, 0x0563998853fead5e}, }, - &fe2{ + { fe{0x40aac71c71c725ed, 0x190955557a84e38e, 0xd817050a8f41abc3, 0xd86485d4c87f6fb1, 0x696eb479f885d059, 0x198e1a74328002d2}, fe{0, 0, 0, 0, 0, 0}, }, }, - [4]*fe2{ - &fe2{ + { + { fe{0, 0, 0, 0, 0, 0}, fe{0x1f3affffff13ab97, 0xf25bfc611da3ff3e, 0xca3757cb3819b208, 0x3e6427366f8cec18, 0x03977bc86095b089, 0x04f69db13f39a952}, }, - &fe2{ + { fe{0x447600000027552e, 0xdcb8009a43480020, 0x6f7ee9ce4a6e8b59, 0xb10330b7c0a95bc6, 0x6140b1fcfb1e54b7, 0x0381be097f0bb4e1}, fe{0x7588ffffffd8557d, 0x41f3ff646e0bffdf, 0xf7b1e8d2ac426aca, 0xb3741acd32dbb6f8, 0xe9daf5b9482d581f, 0x167f53e0ba7431b8}, }, - &fe2{ + { fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, fe{0, 0, 0, 0, 0, 0}, }, - &fe2{ + { fe{0, 0, 0, 0, 0, 0}, fe{0, 0, 0, 0, 0, 0}, }, }, - [4]*fe2{ - &fe2{ + { + { fe{0x96d8f684bdfc77be, 0xb530e4f43b66d0e2, 0x184a88ff379652fd, 0x57cb23ecfae804e1, 0x0fd2e39eada3eba9, 0x08c8055e31c5d5c3}, fe{0x96d8f684bdfc77be, 0xb530e4f43b66d0e2, 0x184a88ff379652fd, 0x57cb23ecfae804e1, 0x0fd2e39eada3eba9, 0x08c8055e31c5d5c3}, }, - &fe2{ + { fe{0, 0, 0, 0, 0, 0}, fe{0xbf0a71c71c91b406, 0x4d6d55d28b7638fd, 0x9d82f98e5f205aee, 0xa27aa27b1d1a18d5, 0x02c3b2b2d2938e86, 0x0c7d13420b09807f}, }, - &fe2{ + { fe{0xd7f9555555531c74, 0x21cffff748daaaa8, 0x5a9ad1866c9bbe46, 0x4870a2210221d251, 0x4a0db369c0a32af1, 0x02b1ccc429ff56af}, fe{0xe205aaaaaaac8e37, 0xfcdc000768795556, 0x0c96011a8a1537dd, 0x1c06a963f163406e, 0x010df44c82a881e6, 0x174f45260f808feb}, }, - &fe2{ + { fe{0xa470bda12f67f35c, 0xc0fe38e23327b425, 0xc9d3d0f2c6f0678d, 0x1c55c9935b5a982e, 0x27f6c0e2f0746764, 0x117c5e6e28aa9054}, fe{0, 0, 0, 0, 0, 0}, }, }, - [4]*fe2{ - &fe2{ + { + { fe{0x0162fffffa765adf, 0x8f7bea480083fb75, 0x561b3c2259e93611, 0x11e19fc1a9c875d5, 0xca713efc00367660, 0x03c6a03d41da1151}, fe{0x0162fffffa765adf, 0x8f7bea480083fb75, 0x561b3c2259e93611, 0x11e19fc1a9c875d5, 0xca713efc00367660, 0x03c6a03d41da1151}, }, - &fe2{ + { fe{0, 0, 0, 0, 0, 0}, fe{0x5db0fffffd3b02c5, 0xd713f52358ebfdba, 0x5ea60761a84d161a, 0xbb2c75a34ea6c44a, 0x0ac6735921c1119b, 0x0ee3d913bdacfbf6}, }, - &fe2{ + { fe{0x66b10000003affc5, 0xcb1400e764ec0030, 0xa73e5eb56fa5d106, 0x8984c913a0fe09a9, 0x11e10afb78ad7f13, 0x05429d0e3e918f52}, fe{0x534dffffffc4aae6, 0x5397ff174c67ffcf, 0xbff273eb870b251d, 0xdaf2827152870915, 0x393a9cbaca9e2dc3, 0x14be74dbfaee5748}, }, - &fe2{ + { fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, fe{0, 0, 0, 0, 0, 0}, },
diff --git go-ethereum/crypto/bls12381/fp6.go celo/crypto/bls12381/fp6.go index 304173baa3f7d597f02f16ee382beb385bcc398e..3537a0504333855a4ee695d5863265dde6afc114 100644 --- go-ethereum/crypto/bls12381/fp6.go +++ celo/crypto/bls12381/fp6.go @@ -1,19 +1,3 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   import ( @@ -47,19 +31,19 @@ return &fp6{f, t} }   func (e *fp6) fromBytes(b []byte) (*fe6, error) { - if len(b) < 288 { - return nil, errors.New("input string should be larger than 288 bytes") + if len(b) != 288 { + return nil, errors.New("input string length must be equal to 288 bytes") } fp2 := e.fp2 - u2, err := fp2.fromBytes(b[:96]) + u2, err := fp2.fromBytes(b[:2*fpByteSize]) if err != nil { return nil, err } - u1, err := fp2.fromBytes(b[96:192]) + u1, err := fp2.fromBytes(b[2*fpByteSize : 4*fpByteSize]) if err != nil { return nil, err } - u0, err := fp2.fromBytes(b[192:]) + u0, err := fp2.fromBytes(b[4*fpByteSize:]) if err != nil { return nil, err } @@ -68,10 +52,10 @@ }   func (e *fp6) toBytes(a *fe6) []byte { fp2 := e.fp2 - out := make([]byte, 288) - copy(out[:96], fp2.toBytes(&a[2])) - copy(out[96:192], fp2.toBytes(&a[1])) - copy(out[192:], fp2.toBytes(&a[0])) + out := make([]byte, 6*fpByteSize) + copy(out[:2*fpByteSize], fp2.toBytes(&a[2])) + copy(out[2*fpByteSize:4*fpByteSize], fp2.toBytes(&a[1])) + copy(out[4*fpByteSize:], fp2.toBytes(&a[0])) return out }   @@ -89,6 +73,9 @@ }   func (e *fp6) add(c, a, b *fe6) { fp2 := e.fp2 + // c0 = a0 + b0 + // c1 = a1 + b1 + // c2 = a2 + b2 fp2.add(&c[0], &a[0], &b[0]) fp2.add(&c[1], &a[1], &b[1]) fp2.add(&c[2], &a[2], &b[2]) @@ -96,6 +83,9 @@ }   func (e *fp6) addAssign(a, b *fe6) { fp2 := e.fp2 + // a0 = a0 + b0 + // a1 = a1 + b1 + // a2 = a2 + b2 fp2.addAssign(&a[0], &b[0]) fp2.addAssign(&a[1], &b[1]) fp2.addAssign(&a[2], &b[2]) @@ -103,6 +93,9 @@ }   func (e *fp6) double(c, a *fe6) { fp2 := e.fp2 + // c0 = 2a0 + // c1 = 2a1 + // c2 = 2a2 fp2.double(&c[0], &a[0]) fp2.double(&c[1], &a[1]) fp2.double(&c[2], &a[2]) @@ -110,6 +103,9 @@ }   func (e *fp6) doubleAssign(a *fe6) { fp2 := e.fp2 + // c0 = 2c0 + // c1 = 2c1 + // c2 = 2c2 fp2.doubleAssign(&a[0]) fp2.doubleAssign(&a[1]) fp2.doubleAssign(&a[2]) @@ -117,6 +113,9 @@ }   func (e *fp6) sub(c, a, b *fe6) { fp2 := e.fp2 + // c0 = a0 - b0 + // c1 = a1 - b1 + // c2 = a2 - b2 fp2.sub(&c[0], &a[0], &b[0]) fp2.sub(&c[1], &a[1], &b[1]) fp2.sub(&c[2], &a[2], &b[2]) @@ -124,6 +123,9 @@ }   func (e *fp6) subAssign(a, b *fe6) { fp2 := e.fp2 + // a0 = a0 - b0 + // a1 = a1 - b1 + // a2 = a2 - b2 fp2.subAssign(&a[0], &b[0]) fp2.subAssign(&a[1], &b[1]) fp2.subAssign(&a[2], &b[2]) @@ -131,6 +133,9 @@ }   func (e *fp6) neg(c, a *fe6) { fp2 := e.fp2 + // c0 = -a0 + // c1 = -a1 + // c2 = -a2 fp2.neg(&c[0], &a[0]) fp2.neg(&c[1], &a[1]) fp2.neg(&c[2], &a[2]) @@ -138,30 +143,33 @@ }   func (e *fp6) mul(c, a, b *fe6) { fp2, t := e.fp2, e.t - fp2.mul(t[0], &a[0], &b[0]) - fp2.mul(t[1], &a[1], &b[1]) - fp2.mul(t[2], &a[2], &b[2]) - fp2.add(t[3], &a[1], &a[2]) - fp2.add(t[4], &b[1], &b[2]) - fp2.mulAssign(t[3], t[4]) - fp2.add(t[4], t[1], t[2]) - fp2.subAssign(t[3], t[4]) - fp2.mulByNonResidue(t[3], t[3]) - fp2.add(t[5], t[0], t[3]) - fp2.add(t[3], &a[0], &a[1]) - fp2.add(t[4], &b[0], &b[1]) - fp2.mulAssign(t[3], t[4]) - fp2.add(t[4], t[0], t[1]) - fp2.subAssign(t[3], t[4]) - fp2.mulByNonResidue(t[4], t[2]) - fp2.add(&c[1], t[3], t[4]) - fp2.add(t[3], &a[0], &a[2]) - fp2.add(t[4], &b[0], &b[2]) - fp2.mulAssign(t[3], t[4]) - fp2.add(t[4], t[0], t[2]) - fp2.subAssign(t[3], t[4]) - fp2.add(&c[2], t[1], t[3]) - c[0].set(t[5]) + // Guide to Pairing Based Cryptography + // Algorithm 5.21 + + fp2.mul(t[0], &a[0], &b[0]) // v0 = a0b0 + fp2.mul(t[1], &a[1], &b[1]) // v1 = a1b1 + fp2.mul(t[2], &a[2], &b[2]) // v2 = a2b2 + fp2.add(t[3], &a[1], &a[2]) // a1 + a2 + fp2.add(t[4], &b[1], &b[2]) // b1 + b2 + fp2.mulAssign(t[3], t[4]) // (a1 + a2)(b1 + b2) + fp2.add(t[4], t[1], t[2]) // v1 + v2 + fp2.subAssign(t[3], t[4]) // (a1 + a2)(b1 + b2) - v1 - v2 + fp2.mulByNonResidue(t[3], t[3]) // ((a1 + a2)(b1 + b2) - v1 - v2)β + fp2.addAssign(t[3], t[0]) // c0 = ((a1 + a2)(b1 + b2) - v1 - v2)β + v0 + fp2.add(t[5], &a[0], &a[1]) // a0 + a1 + fp2.add(t[4], &b[0], &b[1]) // b0 + b1 + fp2.mulAssign(t[5], t[4]) // (a0 + a1)(b0 + b1) + fp2.add(t[4], t[0], t[1]) // v0 + v1 + fp2.subAssign(t[5], t[4]) // (a0 + a1)(b0 + b1) - v0 - v1 + fp2.mulByNonResidue(t[4], t[2]) // βv2 + fp2.add(&c[1], t[5], t[4]) // c1 = (a0 + a1)(b0 + b1) - v0 - v1 + βv2 + fp2.add(t[5], &a[0], &a[2]) // a0 + a2 + fp2.add(t[4], &b[0], &b[2]) // b0 + b2 + fp2.mulAssign(t[5], t[4]) // (a0 + a2)(b0 + b2) + fp2.add(t[4], t[0], t[2]) // v0 + v2 + fp2.subAssign(t[5], t[4]) // (a0 + a2)(b0 + b2) - v0 - v2 + fp2.add(&c[2], t[1], t[5]) // c2 = (a0 + a2)(b0 + b2) - v0 - v2 + v1 + c[0].set(t[3]) }   func (e *fp6) mulAssign(a, b *fe6) { @@ -175,86 +183,79 @@ fp2.mulAssign(t[3], t[4]) fp2.add(t[4], t[1], t[2]) fp2.subAssign(t[3], t[4]) fp2.mulByNonResidue(t[3], t[3]) - fp2.add(t[5], t[0], t[3]) - fp2.add(t[3], &a[0], &a[1]) + fp2.addAssign(t[3], t[0]) + fp2.add(t[5], &a[0], &a[1]) fp2.add(t[4], &b[0], &b[1]) - fp2.mulAssign(t[3], t[4]) + fp2.mulAssign(t[5], t[4]) fp2.add(t[4], t[0], t[1]) - fp2.subAssign(t[3], t[4]) + fp2.subAssign(t[5], t[4]) fp2.mulByNonResidue(t[4], t[2]) - fp2.add(&a[1], t[3], t[4]) - fp2.add(t[3], &a[0], &a[2]) + fp2.add(&a[1], t[5], t[4]) + fp2.add(t[5], &a[0], &a[2]) fp2.add(t[4], &b[0], &b[2]) - fp2.mulAssign(t[3], t[4]) + fp2.mulAssign(t[5], t[4]) fp2.add(t[4], t[0], t[2]) - fp2.subAssign(t[3], t[4]) - fp2.add(&a[2], t[1], t[3]) - a[0].set(t[5]) + fp2.subAssign(t[5], t[4]) + fp2.add(&a[2], t[1], t[5]) + a[0].set(t[3]) }   func (e *fp6) square(c, a *fe6) { fp2, t := e.fp2, e.t - fp2.square(t[0], &a[0]) - fp2.mul(t[1], &a[0], &a[1]) - fp2.doubleAssign(t[1]) - fp2.sub(t[2], &a[0], &a[1]) - fp2.addAssign(t[2], &a[2]) - fp2.squareAssign(t[2]) - fp2.mul(t[3], &a[1], &a[2]) - fp2.doubleAssign(t[3]) - fp2.square(t[4], &a[2]) - fp2.mulByNonResidue(t[5], t[3]) - fp2.add(&c[0], t[0], t[5]) - fp2.mulByNonResidue(t[5], t[4]) - fp2.add(&c[1], t[1], t[5]) + // Multiplication and Squaring on Pairing-Friendly Fields + // Algorithm CH-SQR2 + // https://eprint.iacr.org/2006/471 + + fp2.square(t[0], &a[0]) // s0 = a0^2 + fp2.mul(t[1], &a[0], &a[1]) // a0a1 + fp2.doubleAssign(t[1]) // s1 = 2a0a1 + fp2.sub(t[2], &a[0], &a[1]) // a0 - a1 + fp2.addAssign(t[2], &a[2]) // a0 - a1 + a2 + fp2.squareAssign(t[2]) // s2 = (a0 - a1 + a2)^2 + fp2.mul(t[3], &a[1], &a[2]) // a1a2 + fp2.doubleAssign(t[3]) // s3 = 2a1a2 + fp2.square(t[4], &a[2]) // s4 = a2^2 + fp2.mulByNonResidue(t[5], t[3]) // βs3 + fp2.add(&c[0], t[0], t[5]) // c0 = s0 + βs3 + fp2.mulByNonResidue(t[5], t[4]) // βs4 + fp2.add(&c[1], t[1], t[5]) // c1 = s1 + βs4 fp2.addAssign(t[1], t[2]) fp2.addAssign(t[1], t[3]) fp2.addAssign(t[0], t[4]) - fp2.sub(&c[2], t[1], t[0]) + fp2.sub(&c[2], t[1], t[0]) // c2 = s1 + s2 - s0 - s4 }   -func (e *fp6) mulBy01Assign(a *fe6, b0, b1 *fe2) { +func (e *fp6) mul01(c, a *fe6, b0, b1 *fe2) { fp2, t := e.fp2, e.t - fp2.mul(t[0], &a[0], b0) - fp2.mul(t[1], &a[1], b1) - fp2.add(t[5], &a[1], &a[2]) - fp2.mul(t[2], b1, t[5]) - fp2.subAssign(t[2], t[1]) - fp2.mulByNonResidue(t[2], t[2]) - fp2.add(t[5], &a[0], &a[2]) - fp2.mul(t[3], b0, t[5]) - fp2.subAssign(t[3], t[0]) - fp2.add(&a[2], t[3], t[1]) - fp2.add(t[4], b0, b1) - fp2.add(t[5], &a[0], &a[1]) - fp2.mulAssign(t[4], t[5]) - fp2.subAssign(t[4], t[0]) - fp2.sub(&a[1], t[4], t[1]) - fp2.add(&a[0], t[2], t[0]) -} + // v0 = a0b0 + // v1 = a1b1 + // c0 = (b1(a1 + a2) - v1)β + v0 + // c1 = (a0 + a1)(b0 + b1) - v0 - v1 + // c2 = b0(a0 + a2) - v0 + v1   -func (e *fp6) mulBy01(c, a *fe6, b0, b1 *fe2) { - fp2, t := e.fp2, e.t - fp2.mul(t[0], &a[0], b0) - fp2.mul(t[1], &a[1], b1) - fp2.add(t[2], &a[1], &a[2]) - fp2.mulAssign(t[2], b1) - fp2.subAssign(t[2], t[1]) - fp2.mulByNonResidue(t[2], t[2]) - fp2.add(t[3], &a[0], &a[2]) - fp2.mulAssign(t[3], b0) - fp2.subAssign(t[3], t[0]) - fp2.add(&c[2], t[3], t[1]) - fp2.add(t[4], b0, b1) - fp2.add(t[3], &a[0], &a[1]) - fp2.mulAssign(t[4], t[3]) - fp2.subAssign(t[4], t[0]) - fp2.sub(&c[1], t[4], t[1]) - fp2.add(&c[0], t[2], t[0]) + fp2.mul(t[0], &a[0], b0) // v0 = b0a0 + fp2.mul(t[1], &a[1], b1) // v1 = a1b1 + fp2.add(t[2], &a[1], &a[2]) // a1 + a2 + fp2.mulAssign(t[2], b1) // b1(a1 + a2) + fp2.subAssign(t[2], t[1]) // b1(a1 + a2) - v1 + fp2.mulByNonResidue(t[2], t[2]) // (b1(a1 + a2) - v1)β + fp2.add(t[3], &a[0], &a[2]) // a0 + a2 + fp2.mulAssign(t[3], b0) // b0(a0 + a2) + fp2.subAssign(t[3], t[0]) // b0(a0 + a2) - v0 + fp2.add(&c[2], t[3], t[1]) // b0(a0 + a2) - v0 + v1 + fp2.add(t[4], b0, b1) // (b0 + b1) + fp2.add(t[3], &a[0], &a[1]) // (a0 + a1) + fp2.mulAssign(t[4], t[3]) // (a0 + a1)(b0 + b1) + fp2.subAssign(t[4], t[0]) // (a0 + a1)(b0 + b1) - v0 + fp2.sub(&c[1], t[4], t[1]) // (a0 + a1)(b0 + b1) - v0 - v1 + fp2.add(&c[0], t[2], t[0]) // (b1(a1 + a2) - v1)β + v0 }   -func (e *fp6) mulBy1(c, a *fe6, b1 *fe2) { +func (e *fp6) mul1(c, a *fe6, b1 *fe2) { fp2, t := e.fp2, e.t + // c0 = βa2b1 + // c1 = a0b1 + // c2 = a1b1 fp2.mul(t[0], &a[2], b1) fp2.mul(&c[2], &a[1], b1) fp2.mul(&c[1], &a[0], b1) @@ -269,13 +270,6 @@ c[2].set(&a[1]) c[1].set(t[0]) }   -func (e *fp6) mulByBaseField(c, a *fe6, b *fe2) { - fp2 := e.fp2 - fp2.mul(&c[0], &a[0], b) - fp2.mul(&c[1], &a[1], b) - fp2.mul(&c[2], &a[2], b) -} - func (e *fp6) exp(c, a *fe6, s *big.Int) { z := e.one() for i := s.BitLen() - 1; i >= 0; i-- { @@ -288,64 +282,63 @@ c.set(z) }   func (e *fp6) inverse(c, a *fe6) { + // Guide to Pairing Based Cryptography + // Algorithm 5.23 + fp2, t := e.fp2, e.t fp2.square(t[0], &a[0]) fp2.mul(t[1], &a[1], &a[2]) fp2.mulByNonResidue(t[1], t[1]) - fp2.subAssign(t[0], t[1]) - fp2.square(t[1], &a[1]) - fp2.mul(t[2], &a[0], &a[2]) - fp2.subAssign(t[1], t[2]) - fp2.square(t[2], &a[2]) - fp2.mulByNonResidue(t[2], t[2]) - fp2.mul(t[3], &a[0], &a[1]) - fp2.subAssign(t[2], t[3]) - fp2.mul(t[3], &a[2], t[2]) - fp2.mul(t[4], &a[1], t[1]) - fp2.addAssign(t[3], t[4]) - fp2.mulByNonResidue(t[3], t[3]) - fp2.mul(t[4], &a[0], t[0]) - fp2.addAssign(t[3], t[4]) - fp2.inverse(t[3], t[3]) - fp2.mul(&c[0], t[0], t[3]) - fp2.mul(&c[1], t[2], t[3]) - fp2.mul(&c[2], t[1], t[3]) + fp2.subAssign(t[0], t[1]) // A = v0 - βv5 + fp2.square(t[1], &a[1]) // v1 = a1^2 + fp2.mul(t[2], &a[0], &a[2]) // v4 = a0a2 + fp2.subAssign(t[1], t[2]) // C = v1 - v4 + fp2.square(t[2], &a[2]) // v2 = a2^2 + fp2.mulByNonResidue(t[2], t[2]) // βv2 + fp2.mul(t[3], &a[0], &a[1]) // v3 = a0a1 + fp2.subAssign(t[2], t[3]) // B = βv2 - v3 + fp2.mul(t[3], &a[2], t[2]) // B * a2 + fp2.mul(t[4], &a[1], t[1]) // C * a1 + fp2.addAssign(t[3], t[4]) // Ca1 + Ba2 + fp2.mulByNonResidue(t[3], t[3]) // β(Ca1 + Ba2) + fp2.mul(t[4], &a[0], t[0]) // Aa0 + fp2.addAssign(t[3], t[4]) // v6 = Aa0 + β(Ca1 + Ba2) + fp2.inverse(t[3], t[3]) // F = v6^-1 + fp2.mul(&c[0], t[0], t[3]) // c0 = AF + fp2.mul(&c[1], t[2], t[3]) // c1 = BF + fp2.mul(&c[2], t[1], t[3]) // c2 = CF }   -func (e *fp6) frobeniusMap(c, a *fe6, power uint) { +func (e *fp6) frobeniusMap(a *fe6, power int) { fp2 := e.fp2 - fp2.frobeniusMap(&c[0], &a[0], power) - fp2.frobeniusMap(&c[1], &a[1], power) - fp2.frobeniusMap(&c[2], &a[2], power) - switch power % 6 { - case 0: - return - case 3: - neg(&c[0][0], &a[1][1]) - c[1][1].set(&a[1][0]) - fp2.neg(&a[2], &a[2]) - default: - fp2.mul(&c[1], &c[1], &frobeniusCoeffs61[power%6]) - fp2.mul(&c[2], &c[2], &frobeniusCoeffs62[power%6]) - } + fp2.frobeniusMap(&a[0], power) + fp2.frobeniusMap(&a[1], power) + fp2.frobeniusMap(&a[2], power) + fp2.mulAssign(&a[1], &frobeniusCoeffs61[power%6]) + fp2.mulAssign(&a[2], &frobeniusCoeffs62[power%6]) }   -func (e *fp6) frobeniusMapAssign(a *fe6, power uint) { +func (e *fp6) frobeniusMap1(a *fe6) { fp2 := e.fp2 - fp2.frobeniusMapAssign(&a[0], power) - fp2.frobeniusMapAssign(&a[1], power) - fp2.frobeniusMapAssign(&a[2], power) - t := e.t - switch power % 6 { - case 0: - return - case 3: - neg(&t[0][0], &a[1][1]) - a[1][1].set(&a[1][0]) - a[1][0].set(&t[0][0]) - fp2.neg(&a[2], &a[2]) - default: - fp2.mulAssign(&a[1], &frobeniusCoeffs61[power%6]) - fp2.mulAssign(&a[2], &frobeniusCoeffs62[power%6]) - } + fp2.frobeniusMap1(&a[0]) + fp2.frobeniusMap1(&a[1]) + fp2.frobeniusMap1(&a[2]) + fp2.mulAssign(&a[1], &frobeniusCoeffs61[1]) + fp2.mulAssign(&a[2], &frobeniusCoeffs62[1]) +} + +func (e *fp6) frobeniusMap2(a *fe6) { + e.fp2.mulAssign(&a[1], &frobeniusCoeffs61[2]) + e.fp2.mulAssign(&a[2], &frobeniusCoeffs62[2]) +} + +func (e *fp6) frobeniusMap3(a *fe6) { + fp2, t := e.fp2, e.t + fp2.frobeniusMap1(&a[0]) + fp2.frobeniusMap1(&a[1]) + fp2.frobeniusMap1(&a[2]) + neg(&t[0][0], &a[1][1]) + a[1][1].set(&a[1][0]) + a[1][0].set(&t[0][0]) + fp2.neg(&a[2], &a[2]) }
diff --git go-ethereum/crypto/bls12381/wnaf_test.go celo/crypto/bls12381/wnaf_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9893fed6a88e972f9812f1f44bcad7967afc7a93 --- /dev/null +++ celo/crypto/bls12381/wnaf_test.go @@ -0,0 +1,26 @@ +package bls12381 + +import ( + "crypto/rand" + "math/big" + "testing" +) + +var maxWindowSize uint = 9 + +func TestWNAF(t *testing.T) { + var w uint + for w = 1; w <= maxWindowSize; w++ { + for i := 0; i < fuz; i++ { + e0, err := rand.Int(rand.Reader, new(big.Int).SetUint64(100)) + if err != nil { + t.Fatal(err) + } + n0 := bigToWNAF(e0, w) + e1 := bigFromWNAF(n0) + if e0.Cmp(e1) != 0 { + t.Fatal("wnaf conversion failed") + } + } + } +}
diff --git go-ethereum/crypto/bls12381/pairing.go celo/crypto/bls12381/pairing.go index c8cf02bb993e1b854d32f22a6c61e2298c93e8e1..23e670ae93847bb90728be70a47c8c4b095fcac3 100644 --- go-ethereum/crypto/bls12381/pairing.go +++ celo/crypto/bls12381/pairing.go @@ -1,19 +1,3 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   type pair struct { @@ -68,8 +52,9 @@ // AddPair adds a g1, g2 point pair to pairing engine func (e *Engine) AddPair(g1 *PointG1, g2 *PointG2) *Engine { p := newPair(g1, g2) - if !e.isZero(p) { - e.affine(p) + if !(e.G1.IsZero(p.g1) || e.G2.IsZero(p.g2)) { + e.G1.Affine(p.g1) + e.G2.Affine(p.g2) e.pairs = append(e.pairs, p) } return e @@ -77,8 +62,9 @@ }   // AddPairInv adds a G1, G2 point pair to pairing engine. G1 point is negated. func (e *Engine) AddPairInv(g1 *PointG1, g2 *PointG2) *Engine { - e.G1.Neg(g1, g1) - e.AddPair(g1, g2) + ng1 := e.G1.New().Set(g1) + e.G1.Neg(ng1, g1) + e.AddPair(ng1, g2) return e }   @@ -88,42 +74,32 @@ e.pairs = []pair{} return e }   -func (e *Engine) isZero(p pair) bool { - return e.G1.IsZero(p.g1) || e.G2.IsZero(p.g2) -} - -func (e *Engine) affine(p pair) { - e.G1.Affine(p.g1) - e.G2.Affine(p.g2) -} - -func (e *Engine) doublingStep(coeff *[3]fe2, r *PointG2) { - // Adaptation of Formula 3 in https://eprint.iacr.org/2010/526.pdf +func (e *Engine) doublingStep(coeff *fe6, r *PointG2) { fp2 := e.fp2 t := e.t2 fp2.mul(t[0], &r[0], &r[1]) - fp2.mulByFq(t[0], t[0], twoInv) + fp2.mul0(t[0], t[0], twoInv) fp2.square(t[1], &r[1]) fp2.square(t[2], &r[2]) fp2.double(t[7], t[2]) - fp2.add(t[7], t[7], t[2]) + fp2.addAssign(t[7], t[2]) fp2.mulByB(t[3], t[7]) fp2.double(t[4], t[3]) - fp2.add(t[4], t[4], t[3]) + fp2.addAssign(t[4], t[3]) fp2.add(t[5], t[1], t[4]) - fp2.mulByFq(t[5], t[5], twoInv) + fp2.mul0(t[5], t[5], twoInv) fp2.add(t[6], &r[1], &r[2]) - fp2.square(t[6], t[6]) + fp2.squareAssign(t[6]) fp2.add(t[7], t[2], t[1]) - fp2.sub(t[6], t[6], t[7]) + fp2.subAssign(t[6], t[7]) fp2.sub(&coeff[0], t[3], t[1]) fp2.square(t[7], &r[0]) fp2.sub(t[4], t[1], t[4]) fp2.mul(&r[0], t[4], t[0]) fp2.square(t[2], t[3]) fp2.double(t[3], t[2]) - fp2.add(t[3], t[3], t[2]) - fp2.square(t[5], t[5]) + fp2.addAssign(t[3], t[2]) + fp2.squareAssign(t[5]) fp2.sub(&r[1], t[5], t[3]) fp2.mul(&r[2], t[1], t[6]) fp2.double(t[0], t[7]) @@ -131,30 +107,29 @@ fp2.add(&coeff[1], t[0], t[7]) fp2.neg(&coeff[2], t[6]) }   -func (e *Engine) additionStep(coeff *[3]fe2, r, q *PointG2) { - // Algorithm 12 in https://eprint.iacr.org/2010/526.pdf +func (e *Engine) additionStep(coeff *fe6, r, q *PointG2) { fp2 := e.fp2 t := e.t2 fp2.mul(t[0], &q[1], &r[2]) fp2.neg(t[0], t[0]) - fp2.add(t[0], t[0], &r[1]) + fp2.addAssign(t[0], &r[1]) fp2.mul(t[1], &q[0], &r[2]) fp2.neg(t[1], t[1]) - fp2.add(t[1], t[1], &r[0]) + fp2.addAssign(t[1], &r[0]) fp2.square(t[2], t[0]) fp2.square(t[3], t[1]) fp2.mul(t[4], t[1], t[3]) fp2.mul(t[2], &r[2], t[2]) - fp2.mul(t[3], &r[0], t[3]) + fp2.mulAssign(t[3], &r[0]) fp2.double(t[5], t[3]) fp2.sub(t[5], t[4], t[5]) - fp2.add(t[5], t[5], t[2]) + fp2.addAssign(t[5], t[2]) fp2.mul(&r[0], t[1], t[5]) - fp2.sub(t[2], t[3], t[5]) - fp2.mul(t[2], t[2], t[0]) - fp2.mul(t[3], &r[1], t[4]) - fp2.sub(&r[1], t[2], t[3]) - fp2.mul(&r[2], &r[2], t[4]) + fp2.subAssign(t[3], t[5]) + fp2.mulAssign(t[3], t[0]) + fp2.mul(t[2], &r[1], t[4]) + fp2.sub(&r[1], t[3], t[2]) + fp2.mulAssign(&r[2], t[4]) fp2.mul(t[2], t[1], &q[1]) fp2.mul(t[3], t[0], &q[0]) fp2.sub(&coeff[0], t[3], t[2]) @@ -162,95 +137,117 @@ fp2.neg(&coeff[1], t[0]) coeff[2].set(t[1]) }   -func (e *Engine) preCompute(ellCoeffs *[68][3]fe2, twistPoint *PointG2) { - // Algorithm 5 in https://eprint.iacr.org/2019/077.pdf - if e.G2.IsZero(twistPoint) { - return - } - r := new(PointG2).Set(twistPoint) - j := 0 - for i := x.BitLen() - 2; i >= 0; i-- { - e.doublingStep(&ellCoeffs[j], r) - if x.Bit(i) != 0 { +func (e *Engine) precompute() [][68]fe6 { + n := len(e.pairs) + coeffs := make([][68]fe6, len(e.pairs)) + for i := 0; i < n; i++ { + r := new(PointG2).Set(e.pairs[i].g2) + j := 0 + for k := 62; k >= 0; k-- { + e.doublingStep(&coeffs[i][j], r) + if x&(1<<k) != 0 { + j++ + e.additionStep(&coeffs[i][j], r, e.pairs[i].g2) + } j++ - ellCoeffs[j] = fe6{} - e.additionStep(&ellCoeffs[j], r, twistPoint) } - j++ + } + return coeffs +} + +func (e *Engine) lineEval(f *fe12, coeffs [][68]fe6, j int) { + t := e.t2 + for i := 0; i < len(e.pairs); i++ { + e.fp2.mul0(t[0], &coeffs[i][j][2], &e.pairs[i].g1[1]) + e.fp2.mul0(t[1], &coeffs[i][j][1], &e.pairs[i].g1[0]) + e.fp12.mul014(f, &coeffs[i][j][0], t[1], t[0]) } }   func (e *Engine) millerLoop(f *fe12) { - pairs := e.pairs - ellCoeffs := make([][68][3]fe2, len(pairs)) - for i := 0; i < len(pairs); i++ { - e.preCompute(&ellCoeffs[i], pairs[i].g2) - } - fp12, fp2 := e.fp12, e.fp2 - t := e.t2 + coeffs := e.precompute() f.one() j := 0 - for i := 62; /* x.BitLen() - 2 */ i >= 0; i-- { + for i := 62; i >= 0; i-- { if i != 62 { - fp12.square(f, f) - } - for i := 0; i <= len(pairs)-1; i++ { - fp2.mulByFq(t[0], &ellCoeffs[i][j][2], &pairs[i].g1[1]) - fp2.mulByFq(t[1], &ellCoeffs[i][j][1], &pairs[i].g1[0]) - fp12.mulBy014Assign(f, &ellCoeffs[i][j][0], t[1], t[0]) + e.fp12.square(f, f) } - if x.Bit(i) != 0 { + e.lineEval(f, coeffs, j) + if x&(1<<i) != 0 { j++ - for i := 0; i <= len(pairs)-1; i++ { - fp2.mulByFq(t[0], &ellCoeffs[i][j][2], &pairs[i].g1[1]) - fp2.mulByFq(t[1], &ellCoeffs[i][j][1], &pairs[i].g1[0]) - fp12.mulBy014Assign(f, &ellCoeffs[i][j][0], t[1], t[0]) - } + e.lineEval(f, coeffs, j) } j++ } - fp12.conjugate(f, f) + e.fp12.conjugate(f, f) }   +// exp raises element by x = -15132376222941642752 func (e *Engine) exp(c, a *fe12) { + // Adapted from https://github.com/supranational/blst/blob/master/src/pairing.c fp12 := e.fp12 - fp12.cyclotomicExp(c, a, x) + chain := func(n int) { + fp12.mulAssign(c, a) + for i := 0; i < n; i++ { + fp12.cyclotomicSquare(c, c) + } + } + fp12.cyclotomicSquare(c, a) // (a ^ 2) + chain(2) // (a ^ (2 + 1)) ^ (2 ^ 2) = a ^ 12 + chain(3) // (a ^ (12 + 1)) ^ (2 ^ 3) = a ^ 104 + chain(9) // (a ^ (104 + 1)) ^ (2 ^ 9) = a ^ 53760 + chain(32) // (a ^ (53760 + 1)) ^ (2 ^ 32) = a ^ 230901736800256 + chain(16) // (a ^ (230901736800256 + 1)) ^ (2 ^ 16) = a ^ 15132376222941642752 + // invert chain result since x is negative fp12.conjugate(c, c) }   func (e *Engine) finalExp(f *fe12) { - fp12 := e.fp12 - t := e.t12 + fp12, t := e.fp12, e.t12 // easy part - fp12.frobeniusMap(&t[0], f, 6) - fp12.inverse(&t[1], f) - fp12.mul(&t[2], &t[0], &t[1]) - t[1].set(&t[2]) - fp12.frobeniusMapAssign(&t[2], 2) - fp12.mulAssign(&t[2], &t[1]) - fp12.cyclotomicSquare(&t[1], &t[2]) - fp12.conjugate(&t[1], &t[1]) + + fp12.inverse(&t[1], f) // t1 = f0 ^ -1 + fp12.conjugate(&t[0], f) // t0 = f0 ^ p6 + fp12.mul(&t[2], &t[0], &t[1]) // t2 = f0 ^ (p6 - 1) + t[1].set(&t[2]) // t1 = f0 ^ (p6 - 1) + fp12.frobeniusMap2(&t[2]) // t2 = f0 ^ ((p6 - 1) * p2) + fp12.mulAssign(&t[2], &t[1]) // t2 = f0 ^ ((p6 - 1) * (p2 + 1)) + + // f = f0 ^ ((p6 - 1) * (p2 + 1)) + // hard part - e.exp(&t[3], &t[2]) - fp12.cyclotomicSquare(&t[4], &t[3]) - fp12.mul(&t[5], &t[1], &t[3]) - e.exp(&t[1], &t[5]) - e.exp(&t[0], &t[1]) - e.exp(&t[6], &t[0]) - fp12.mulAssign(&t[6], &t[4]) - e.exp(&t[4], &t[6]) - fp12.conjugate(&t[5], &t[5]) - fp12.mulAssign(&t[4], &t[5]) - fp12.mulAssign(&t[4], &t[2]) - fp12.conjugate(&t[5], &t[2]) - fp12.mulAssign(&t[1], &t[2]) - fp12.frobeniusMapAssign(&t[1], 3) - fp12.mulAssign(&t[6], &t[5]) - fp12.frobeniusMapAssign(&t[6], 1) - fp12.mulAssign(&t[3], &t[0]) - fp12.frobeniusMapAssign(&t[3], 2) + // https://eprint.iacr.org/2016/130 + // On the Computation of the Optimal Ate Pairing at the 192-bit Security Level + // Section 3 + // f ^ d = λ_0 + λ_1 * p + λ_2 * p^2 + λ_3 * p^3 + + fp12.conjugate(&t[1], &t[2]) + fp12.cyclotomicSquare(&t[1], &t[1]) // t1 = f ^ (-2) + e.exp(&t[3], &t[2]) // t3 = f ^ (u) + fp12.cyclotomicSquare(&t[4], &t[3]) // t4 = f ^ (2u) + fp12.mul(&t[5], &t[1], &t[3]) // t5 = f ^ (u - 2) + e.exp(&t[1], &t[5]) // t1 = f ^ (u^2 - 2 * u) + e.exp(&t[0], &t[1]) // t0 = f ^ (u^3 - 2 * u^2) + e.exp(&t[6], &t[0]) // t6 = f ^ (u^4 - 2 * u^3) + fp12.mulAssign(&t[6], &t[4]) // t6 = f ^ (u^4 - 2 * u^3 + 2 * u) + e.exp(&t[4], &t[6]) // t4 = f ^ (u^4 - 2 * u^3 + 2 * u^2) + fp12.conjugate(&t[5], &t[5]) // t5 = f ^ (2 - u) + fp12.mulAssign(&t[4], &t[5]) // t4 = f ^ (u^4 - 2 * u^3 + 2 * u^2 - u + 2) + fp12.mulAssign(&t[4], &t[2]) // f_λ_0 = t4 = f ^ (u^4 - 2 * u^3 + 2 * u^2 - u + 3) + + fp12.conjugate(&t[5], &t[2]) // t5 = f ^ (-1) + fp12.mulAssign(&t[5], &t[6]) // t1 = f ^ (u^4 - 2 * u^3 + 2 * u - 1) + fp12.frobeniusMap1(&t[5]) // f_λ_1 = t1 = f ^ ((u^4 - 2 * u^3 + 2 * u - 1) ^ p) + + fp12.mulAssign(&t[3], &t[0]) // t3 = f ^ (u^3 - 2 * u^2 + u) + fp12.frobeniusMap2(&t[3]) // f_λ_2 = t3 = f ^ ((u^3 - 2 * u^2 + u) ^ p^2) + + fp12.mulAssign(&t[1], &t[2]) // t1 = f ^ (u^2 - 2 * u + 1) + fp12.frobeniusMap3(&t[1]) // f_λ_3 = t1 = f ^ ((u^2 - 2 * u + 1) ^ p^3) + + // out = f ^ (λ_0 + λ_1 + λ_2 + λ_3) fp12.mulAssign(&t[3], &t[1]) - fp12.mulAssign(&t[3], &t[6]) + fp12.mulAssign(&t[3], &t[5]) fp12.mul(f, &t[3], &t[4]) }
diff --git go-ethereum/crypto/bls12381/fp12.go celo/crypto/bls12381/fp12.go index 38f5f70cce488258de8fcdb91e59ea0b1fcb520f..d162db336ff30adde04f5383184bb8b71b3928b7 100644 --- go-ethereum/crypto/bls12381/fp12.go +++ celo/crypto/bls12381/fp12.go @@ -1,19 +1,3 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   import ( @@ -58,14 +42,14 @@ }   func (e *fp12) fromBytes(in []byte) (*fe12, error) { if len(in) != 576 { - return nil, errors.New("input string should be larger than 96 bytes") + return nil, errors.New("input string length must be equal to 576 bytes") } fp6 := e.fp6 - c1, err := fp6.fromBytes(in[:288]) + c1, err := fp6.fromBytes(in[:6*fpByteSize]) if err != nil { return nil, err } - c0, err := fp6.fromBytes(in[288:]) + c0, err := fp6.fromBytes(in[6*fpByteSize:]) if err != nil { return nil, err } @@ -74,9 +58,9 @@ }   func (e *fp12) toBytes(a *fe12) []byte { fp6 := e.fp6 - out := make([]byte, 576) - copy(out[:288], fp6.toBytes(&a[1])) - copy(out[288:], fp6.toBytes(&a[0])) + out := make([]byte, 12*fpByteSize) + copy(out[:6*fpByteSize], fp6.toBytes(&a[1])) + copy(out[6*fpByteSize:], fp6.toBytes(&a[0])) return out }   @@ -94,6 +78,8 @@ }   func (e *fp12) add(c, a, b *fe12) { fp6 := e.fp6 + // c0 = a0 + b0 + // c1 = a1 + b1 fp6.add(&c[0], &a[0], &b[0]) fp6.add(&c[1], &a[1], &b[1])   @@ -101,12 +87,16 @@ }   func (e *fp12) double(c, a *fe12) { fp6 := e.fp6 + // c0 = 2a0 + // c1 = 2a1 fp6.double(&c[0], &a[0]) fp6.double(&c[1], &a[1]) }   func (e *fp12) sub(c, a, b *fe12) { fp6 := e.fp6 + // c0 = a0 - b0 + // c1 = a1 - b1s fp6.sub(&c[0], &a[0], &b[0]) fp6.sub(&c[1], &a[1], &b[1])   @@ -114,31 +104,42 @@ }   func (e *fp12) neg(c, a *fe12) { fp6 := e.fp6 + // c0 = -a0 + // c1 = -a1 fp6.neg(&c[0], &a[0]) fp6.neg(&c[1], &a[1]) }   func (e *fp12) conjugate(c, a *fe12) { fp6 := e.fp6 + // c0 = a0 + // c1 = -a1 c[0].set(&a[0]) fp6.neg(&c[1], &a[1]) }   func (e *fp12) square(c, a *fe12) { fp6, t := e.fp6, e.t6 - fp6.add(t[0], &a[0], &a[1]) - fp6.mul(t[2], &a[0], &a[1]) - fp6.mulByNonResidue(t[1], &a[1]) - fp6.addAssign(t[1], &a[0]) - fp6.mulByNonResidue(t[3], t[2]) - fp6.mulAssign(t[0], t[1]) - fp6.subAssign(t[0], t[2]) - fp6.sub(&c[0], t[0], t[3]) - fp6.double(&c[1], t[2]) + // Multiplication and Squaring on Pairing-Friendly Fields + // Complex squaring algorithm + // https://eprint.iacr.org/2006/471 + + fp6.add(t[0], &a[0], &a[1]) // a0 + a1 + fp6.mul(t[2], &a[0], &a[1]) // v0 = a0a1 + fp6.mulByNonResidue(t[1], &a[1]) // βa1 + fp6.addAssign(t[1], &a[0]) // a0 + βa1 + fp6.mulByNonResidue(t[3], t[2]) // βa0a1 + fp6.mulAssign(t[0], t[1]) // (a0 + a1)(a0 + βa1) + fp6.subAssign(t[0], t[2]) // (a0 + a1)(a0 + βa1) - v0 + fp6.sub(&c[0], t[0], t[3]) // c0 = (a0 + a1)(a0 + βa1) - v0 - βa0a1 + fp6.double(&c[1], t[2]) // c1 = 2v0 }   func (e *fp12) cyclotomicSquare(c, a *fe12) { t, fp2 := e.t2, e.fp2() + // Guide to Pairing Based Cryptography + // 5.5.4 Airthmetic in Cyclotomic Groups + e.fp4Square(t[3], t[4], &a[0][0], &a[1][1]) fp2.sub(t[2], t[3], &a[0][0]) fp2.doubleAssign(t[2]) @@ -165,67 +166,75 @@ }   func (e *fp12) mul(c, a, b *fe12) { t, fp6 := e.t6, e.fp6 - fp6.mul(t[1], &a[0], &b[0]) - fp6.mul(t[2], &a[1], &b[1]) - fp6.add(t[0], t[1], t[2]) - fp6.mulByNonResidue(t[2], t[2]) - fp6.add(t[3], t[1], t[2]) - fp6.add(t[1], &a[0], &a[1]) - fp6.add(t[2], &b[0], &b[1]) - fp6.mulAssign(t[1], t[2]) - c[0].set(t[3]) - fp6.sub(&c[1], t[1], t[0]) + // Guide to Pairing Based Cryptography + // Algorithm 5.16 + + fp6.mul(t[1], &a[0], &b[0]) // v0 = a0b0 + fp6.mul(t[2], &a[1], &b[1]) // v1 = a1b1 + fp6.add(t[0], &a[0], &a[1]) // a0 + a1 + fp6.add(t[3], &b[0], &b[1]) // b0 + b1 + fp6.mulAssign(t[0], t[3]) // (a0 + a1)(b0 + b1) + fp6.subAssign(t[0], t[1]) // (a0 + a1)(b0 + b1) - v0 + fp6.sub(&c[1], t[0], t[2]) // c1 = (a0 + a1)(b0 + b1) - v0 - v1 + fp6.mulByNonResidue(t[2], t[2]) // βv1 + fp6.add(&c[0], t[1], t[2]) // c0 = v0 + βv1 }   func (e *fp12) mulAssign(a, b *fe12) { t, fp6 := e.t6, e.fp6 - fp6.mul(t[1], &a[0], &b[0]) - fp6.mul(t[2], &a[1], &b[1]) - fp6.add(t[0], t[1], t[2]) - fp6.mulByNonResidue(t[2], t[2]) - fp6.add(t[3], t[1], t[2]) - fp6.add(t[1], &a[0], &a[1]) - fp6.add(t[2], &b[0], &b[1]) - fp6.mulAssign(t[1], t[2]) - a[0].set(t[3]) - fp6.sub(&a[1], t[1], t[0]) + fp6.mul(t[1], &a[0], &b[0]) // v0 = a0b0 + fp6.mul(t[2], &a[1], &b[1]) // v1 = a1b1 + fp6.add(t[0], &a[0], &a[1]) // a0 + a1 + fp6.add(t[3], &b[0], &b[1]) // b0 + b1 + fp6.mulAssign(t[0], t[3]) // (a0 + a1)(b0 + b1) + fp6.subAssign(t[0], t[1]) // (a0 + a1)(b0 + b1) - v0 + fp6.sub(&a[1], t[0], t[2]) // c1 = (a0 + a1)(b0 + b1) - v0 - v1 + fp6.mulByNonResidue(t[2], t[2]) // βv1 + fp6.add(&a[0], t[1], t[2]) // c0 = v0 + βv1 }   func (e *fp12) fp4Square(c0, c1, a0, a1 *fe2) { t, fp2 := e.t2, e.fp2() - fp2.square(t[0], a0) - fp2.square(t[1], a1) - fp2.mulByNonResidue(t[2], t[1]) - fp2.add(c0, t[2], t[0]) - fp2.add(t[2], a0, a1) - fp2.squareAssign(t[2]) - fp2.subAssign(t[2], t[0]) - fp2.sub(c1, t[2], t[1]) + // Multiplication and Squaring on Pairing-Friendly Fields + // Karatsuba squaring algorithm + // https://eprint.iacr.org/2006/471 + + fp2.square(t[0], a0) // a0^2 + fp2.square(t[1], a1) // a1^2 + fp2.mulByNonResidue(t[2], t[1]) // βa1^2 + fp2.add(c0, t[2], t[0]) // c0 = βa1^2 + a0^2 + fp2.add(t[2], a0, a1) // a0 + a1 + fp2.squareAssign(t[2]) // (a0 + a1)^2 + fp2.subAssign(t[2], t[0]) // (a0 + a1)^2 - a0^2 + fp2.sub(c1, t[2], t[1]) // (a0 + a1)^2 - a0^2 - a1^2 }   func (e *fp12) inverse(c, a *fe12) { + // Guide to Pairing Based Cryptography + // Algorithm 5.16 + fp6, t := e.fp6, e.t6 - fp6.square(t[0], &a[0]) - fp6.square(t[1], &a[1]) - fp6.mulByNonResidue(t[1], t[1]) - fp6.sub(t[1], t[0], t[1]) - fp6.inverse(t[0], t[1]) - fp6.mul(&c[0], &a[0], t[0]) - fp6.mulAssign(t[0], &a[1]) - fp6.neg(&c[1], t[0]) + fp6.square(t[0], &a[0]) // a0^2 + fp6.square(t[1], &a[1]) // a1^2 + fp6.mulByNonResidue(t[1], t[1]) // βa1^2 + fp6.subAssign(t[0], t[1]) // v = (a0^2 - a1^2) + fp6.inverse(t[1], t[0]) // v = v^-1 + fp6.mul(&c[0], &a[0], t[1]) // c0 = a0v + fp6.mulAssign(t[1], &a[1]) // + fp6.neg(&c[1], t[1]) // c1 = -a1v }   -func (e *fp12) mulBy014Assign(a *fe12, c0, c1, c4 *fe2) { - fp2, fp6, t, t2 := e.fp2(), e.fp6, e.t6, e.t2[0] - fp6.mulBy01(t[0], &a[0], c0, c1) - fp6.mulBy1(t[1], &a[1], c4) - fp2.add(t2, c1, c4) - fp6.add(t[2], &a[1], &a[0]) - fp6.mulBy01Assign(t[2], c0, t2) - fp6.subAssign(t[2], t[0]) - fp6.sub(&a[1], t[2], t[1]) - fp6.mulByNonResidue(t[1], t[1]) - fp6.add(&a[0], t[1], t[0]) +func (e *fp12) mul014(a *fe12, b0, b1, b4 *fe2) { + fp2, fp6, t, u := e.fp2(), e.fp6, e.t6, e.t2[0] + fp6.mul01(t[0], &a[0], b0, b1) // t0 = a0b0 + fp6.mul1(t[1], &a[1], b4) // t1 = a1b1 + fp2.add(u, b1, b4) // u = b01 + b10 + fp6.add(t[2], &a[1], &a[0]) // a0 + a1 + fp6.mul01(t[2], t[2], b0, u) // v1 = u(a0 + a1s) + fp6.subAssign(t[2], t[0]) // v1 - t0 + fp6.sub(&a[1], t[2], t[1]) // c1 = v1 - t0 - t1 + fp6.mulByNonResidue(t[1], t[1]) // βt1 + fp6.add(&a[0], t[1], t[0]) // c0 = t0 + βt1 }   func (e *fp12) exp(c, a *fe12, s *big.Int) { @@ -250,30 +259,29 @@ } c.set(z) }   -func (e *fp12) frobeniusMap(c, a *fe12, power uint) { - fp6 := e.fp6 - fp6.frobeniusMap(&c[0], &a[0], power) - fp6.frobeniusMap(&c[1], &a[1], power) - switch power { - case 0: - return - case 6: - fp6.neg(&c[1], &c[1]) - default: - fp6.mulByBaseField(&c[1], &c[1], &frobeniusCoeffs12[power]) - } +func (e *fp12) frobeniusMap1(a *fe12) { + fp6, fp2 := e.fp6, e.fp6.fp2 + fp6.frobeniusMap1(&a[0]) + fp6.frobeniusMap1(&a[1]) + fp2.mulAssign(&a[1][0], &frobeniusCoeffs12[1]) + fp2.mulAssign(&a[1][1], &frobeniusCoeffs12[1]) + fp2.mulAssign(&a[1][2], &frobeniusCoeffs12[1]) +} + +func (e *fp12) frobeniusMap2(a *fe12) { + fp6, fp2 := e.fp6, e.fp6.fp2 + fp6.frobeniusMap2(&a[0]) + fp6.frobeniusMap2(&a[1]) + fp2.mulAssign(&a[1][0], &frobeniusCoeffs12[2]) + fp2.mulAssign(&a[1][1], &frobeniusCoeffs12[2]) + fp2.mulAssign(&a[1][2], &frobeniusCoeffs12[2]) }   -func (e *fp12) frobeniusMapAssign(a *fe12, power uint) { - fp6 := e.fp6 - fp6.frobeniusMapAssign(&a[0], power) - fp6.frobeniusMapAssign(&a[1], power) - switch power { - case 0: - return - case 6: - fp6.neg(&a[1], &a[1]) - default: - fp6.mulByBaseField(&a[1], &a[1], &frobeniusCoeffs12[power]) - } +func (e *fp12) frobeniusMap3(a *fe12) { + fp6, fp2 := e.fp6, e.fp6.fp2 + fp6.frobeniusMap3(&a[0]) + fp6.frobeniusMap3(&a[1]) + fp2.mulAssign(&a[1][0], &frobeniusCoeffs12[3]) + fp2.mulAssign(&a[1][1], &frobeniusCoeffs12[3]) + fp2.mulAssign(&a[1][2], &frobeniusCoeffs12[3]) }
diff --git go-ethereum/crypto/bls12381/glv.go celo/crypto/bls12381/glv.go new file mode 100644 index 0000000000000000000000000000000000000000..02b84b5ddd08240a08eee19be1b65a5577979b94 --- /dev/null +++ celo/crypto/bls12381/glv.go @@ -0,0 +1,117 @@ +package bls12381 + +import ( + "math/big" +) + +// Guide to Pairing Based Cryptography +// 6.3.2. Decompositions for the k = 12 BLS Family + +// glvQ1 = x^2 * R / q +var glvQ1 = bigFromHex("0x017c6becf1e01faadd63f6e522f6cfee30") + +// glvQ2 = R / q = 2 +var glvQ2 = bigFromHex("0x02") + +// glvB1 = x^2 - 1 = 0xac45a4010001a40200000000ffffffff +var glvB1 = bigFromHex("0xac45a4010001a40200000000ffffffff") + +// glvB2 = x^2 = 0xac45a4010001a4020000000100000000 +var glvB2 = bigFromHex("0xac45a4010001a4020000000100000000") + +// glvLambdaA = x^2 - 1 +var glvLambda = bigFromHex("0xac45a4010001a40200000000ffffffff") + +// halfR = 2**256 / 2 +var halfR = bigFromHex("0x8000000000000000000000000000000000000000000000000000000000000000") + +// glvPhi1 ^ 3 = 1 +var glvPhi1 = &fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741} + +// glvPhi2 ^ 3 = 1 +var glvPhi2 = &fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160} + +var glvMulWindowG1 uint = 4 +var glvMulWindowG2 uint = 4 + +type glvVector struct { + k1 *big.Int + k2 *big.Int +} + +func (v *glvVector) wnaf(w uint) (nafNumber, nafNumber) { + naf1, naf2 := bigToWNAF(v.k1, w), bigToWNAF(v.k2, w) + zero := new(big.Int) + if v.k1.Cmp(zero) < 0 { + naf1.neg() + } + if v.k2.Cmp(zero) > 0 { + naf2.neg() + } + return naf1, naf2 +} + +func (v *glvVector) new(m *big.Int) *glvVector { + // Guide to Pairing Based Cryptography + // 6.3.2. Decompositions for the k = 12 BLS Family + + // alpha1 = round(x^2 * m / r) + alpha1 := new(big.Int).Mul(m, glvQ1) + alpha1.Add(alpha1, halfR) + alpha1.Rsh(alpha1, fourWordBitSize) + + // alpha2 = round(m / r) + alpha2 := new(big.Int).Mul(m, glvQ2) + alpha2.Add(alpha2, halfR) + alpha2.Rsh(alpha2, fourWordBitSize) + + z1, z2 := new(big.Int), new(big.Int) + // z1 = (x^2 - 1) * round(x^2 * m / r) + z1.Mul(alpha1, glvB1).Mod(z1, q) + // z2 = x^2 * round(m / r) + z2.Mul(alpha2, glvB2).Mod(z2, q) + + k1, k2 := new(big.Int), new(big.Int) + + // k1 = m - z1 - alpha2 + k1.Sub(m, z1) + k1.Sub(k1, alpha2) + + // k2 = z2 - alpha1 + k2.Sub(z2, alpha1) + + v.k1 = new(big.Int).Set(k1) + v.k2 = new(big.Int).Set(k2) + return v +} + +func phi(a, b *fe) { + mul(a, b, glvPhi1) +} + +func (e *fp2) phi(a, b *fe2) { + mul(&a[0], &b[0], glvPhi2) + mul(&a[1], &b[1], glvPhi2) +} + +func (g *G1) glvEndomorphism(r, p *PointG1) { + t := g.Affine(p) + if g.IsZero(p) { + r.Zero() + return + } + r[1].set(&t[1]) + phi(&r[0], &t[0]) + r[2].one() +} + +func (g *G2) glvEndomorphism(r, p *PointG2) { + t := g.Affine(p) + if g.IsZero(p) { + r.Zero() + return + } + r[1].set(&t[1]) + g.f.phi(&r[0], &t[0]) + r[2].one() +}
diff --git go-ethereum/crypto/bls12381/hash_to_field.go celo/crypto/bls12381/hash_to_field.go new file mode 100644 index 0000000000000000000000000000000000000000..9e440076916e980e8c37872771e261348d10b264 --- /dev/null +++ celo/crypto/bls12381/hash_to_field.go @@ -0,0 +1,70 @@ +package bls12381 + +import ( + "crypto/sha256" + "errors" +) + +func hashToFpXMDSHA256(msg []byte, domain []byte, count int) ([]*fe, error) { + randBytes, err := expandMsgSHA256XMD(msg, domain, count*64) + if err != nil { + return nil, err + } + els := make([]*fe, count) + for i := 0; i < count; i++ { + els[i], err = from64Bytes(randBytes[i*64 : (i+1)*64]) + if err != nil { + return nil, err + } + } + return els, nil +} + +func expandMsgSHA256XMD(msg []byte, domain []byte, outLen int) ([]byte, error) { + h := sha256.New() + domainLen := uint8(len(domain)) + if domainLen > 255 { + return nil, errors.New("invalid domain length") + } + // DST_prime = DST || I2OSP(len(DST), 1) + // b_0 = H(Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime) + _, _ = h.Write(make([]byte, h.BlockSize())) + _, _ = h.Write(msg) + _, _ = h.Write([]byte{uint8(outLen >> 8), uint8(outLen)}) + _, _ = h.Write([]byte{0}) + _, _ = h.Write(domain) + _, _ = h.Write([]byte{domainLen}) + b0 := h.Sum(nil) + + // b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) + h.Reset() + _, _ = h.Write(b0) + _, _ = h.Write([]byte{1}) + _, _ = h.Write(domain) + _, _ = h.Write([]byte{domainLen}) + b1 := h.Sum(nil) + + // b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) + ell := (outLen + h.Size() - 1) / h.Size() + bi := b1 + out := make([]byte, outLen) + for i := 1; i < ell; i++ { + h.Reset() + // b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) + tmp := make([]byte, h.Size()) + for j := 0; j < h.Size(); j++ { + tmp[j] = b0[j] ^ bi[j] + } + _, _ = h.Write(tmp) + _, _ = h.Write([]byte{1 + uint8(i)}) + _, _ = h.Write(domain) + _, _ = h.Write([]byte{domainLen}) + + // b_1 || ... || b_(ell - 1) + copy(out[(i-1)*h.Size():i*h.Size()], bi[:]) + bi = h.Sum(nil) + } + // b_ell + copy(out[(ell-1)*h.Size():], bi[:]) + return out[:outLen], nil +}
diff --git go-ethereum/crypto/bls12381/g2_test.go celo/crypto/bls12381/g2_test.go index bfeea24c5c7253bab77604f53b3e1637b6cb1313..d042b3c4d5e43460309a4455a10620471c62f5fe 100644 --- go-ethereum/crypto/bls12381/g2_test.go +++ celo/crypto/bls12381/g2_test.go @@ -3,53 +3,87 @@ import ( "bytes" "crypto/rand" + "fmt" "math/big" "testing" - - "github.com/ethereum/go-ethereum/common" )   func (g *G2) one() *PointG2 { - one, _ := g.fromBytesUnchecked( - common.FromHex("" + - "13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e" + - "024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8" + - "0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be" + - "0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801", - ), - ) - return one + return g.New().Set(&g2One) }   func (g *G2) rand() *PointG2 { - k, err := rand.Int(rand.Reader, q) - if err != nil { - panic(err) + p := &PointG2{} + z, _ := new(fe2).rand(rand.Reader) + z6, bz6 := new(fe2), new(fe2) + g.f.square(z6, z) + g.f.square(z6, z6) + g.f.mul(z6, z6, z) + g.f.mul(z6, z6, z) + g.f.mul(bz6, z6, b2) + for { + x, _ := new(fe2).rand(rand.Reader) + y := new(fe2) + g.f.square(y, x) + g.f.mul(y, y, x) + g.f.add(y, y, bz6) + if g.f.sqrt(y, y) { + p.Set(&PointG2{*x, *y, *z}) + break + } } - return g.MulScalar(&PointG2{}, g.one(), k) + if !g.IsOnCurve(p) { + panic("rand point must be on curve") + } + if g.InCorrectSubgroup(p) { + panic("rand point must be out of correct subgroup") + } + return p +} + +func (g *G2) randCorrect() *PointG2 { + p := g.ClearCofactor(g.rand()) + if !g.InCorrectSubgroup(p) { + panic("must be in correct subgroup") + } + return p +} + +func (g *G2) randAffine() *PointG2 { + return g.Affine(g.randCorrect()) }   func TestG2Serialization(t *testing.T) { - g2 := NewG2() + var err error + g := NewG2() + zero := g.Zero() + b0 := g.ToBytes(zero) + p0, err := g.FromBytes(b0) + if err != nil { + t.Fatal(err) + } + if !g.IsZero(p0) { + t.Fatal("infinity serialization failed") + } for i := 0; i < fuz; i++ { - a := g2.rand() - buf := g2.ToBytes(a) - b, err := g2.FromBytes(buf) + a := g.rand() + uncompressed := g.ToBytes(a) + b, err := g.FromBytes(uncompressed) if err != nil { t.Fatal(err) } - if !g2.Equal(a, b) { - t.Fatal("bad serialization from/to") + if !g.Equal(a, b) { + t.Fatal("serialization failed") } } for i := 0; i < fuz; i++ { - a := g2.rand() - encoded := g2.EncodePoint(a) - b, err := g2.DecodePoint(encoded) + a := g.rand() + encoded := g.EncodePoint(a) + b, err := g.DecodePoint(encoded) if err != nil { t.Fatal(err) } - if !g2.Equal(a, b) { + if !g.Equal(a, b) { t.Fatal("bad serialization encode/decode") } } @@ -68,6 +102,26 @@ t.Fatal("(1, 1) is not on curve") } }   +func TestG2BatchAffine(t *testing.T) { + n := 20 + g := NewG2() + points0 := make([]*PointG2, n) + points1 := make([]*PointG2, n) + for i := 0; i < n; i++ { + points0[i] = g.rand() + points1[i] = g.New().Set(points0[i]) + if g.IsAffine(points0[i]) { + t.Fatal("expect non affine point") + } + } + g.AffineBatch(points0) + for i := 0; i < n; i++ { + if !g.Equal(points0[i], points1[i]) { + t.Fatal("batch affine failed") + } + } +} + func TestG2AdditiveProperties(t *testing.T) { g := NewG2() t0, t1 := g.New(), g.New() @@ -138,19 +192,71 @@ } } }   +func TestG2MixedAdd(t *testing.T) { + g := NewG2() + for i := 0; i < fuz; i++ { + a, b := g.rand(), g.rand() + if g.IsAffine(a) || g.IsAffine(b) { + t.Fatal("expect non affine points") + } + bAffine := g.New().Set(b) + g.Affine(bAffine) + r0, r1 := g.New(), g.New() + g.Add(r0, a, b) + g.AddMixed(r1, a, bAffine) + if !g.Equal(r0, r1) { + t.Fatal("mixed addition failed") + } + aAffine := g.New().Set(a) + g.Affine(aAffine) + g.AddMixed(r0, a, aAffine) + g.Double(r1, a) + if !g.Equal(r0, r1) { + t.Fatal("mixed addition must double where points are equal") + } + } +} + +func TestG2MultiplicationCross(t *testing.T) { + g := NewG2() + for i := 0; i < fuz; i++ { + + a := g.randCorrect() + s := randScalar(q) + res0, res1, res2, res3 := g.New(), g.New(), g.New(), g.New() + + g.mulScalar(res0, a, s) + g.glvMul(res1, a, s) + g.wnafMul(res2, a, s) + g.MultiExp(res3, []*PointG2{a}, []*big.Int{s}) + + if !g.Equal(res0, res1) { + t.Fatal("cross multiplication failed (glv)", i) + } + if !g.Equal(res0, res2) { + t.Fatal("cross multiplication failed (wnaf)", i) + } + if !g.Equal(res0, res3) { + t.Fatal("cross multiplication failed (multiexp)", i) + } + } +} + func TestG2MultiplicativeProperties(t *testing.T) { g := NewG2() t0, t1 := g.New(), g.New() zero := g.Zero() + one := new(big.Int).SetUint64(1) for i := 0; i < fuz; i++ { - a := g.rand() - s1, s2, s3 := randScalar(q), randScalar(q), randScalar(q) - sone := big.NewInt(1) + a := g.randCorrect() + s1 := randScalar(q) + s2 := randScalar(q) + s3 := randScalar(q) g.MulScalar(t0, zero, s1) if !g.Equal(t0, zero) { t.Fatal(" 0 ^ s == 0") } - g.MulScalar(t0, a, sone) + g.MulScalar(t0, a, one) if !g.Equal(t0, a) { t.Fatal(" a ^ 1 == a") } @@ -163,7 +269,7 @@ g.MulScalar(t0, t0, s2) s3.Mul(s1, s2) g.MulScalar(t1, a, s3) if !g.Equal(t0, t1) { - t.Errorf(" (a ^ s1) ^ s2 == a ^ (s1 * s2)") + t.Fatal(" (a ^ s1) ^ s2 == a ^ (s1 * s2)") } g.MulScalar(t0, a, s1) g.MulScalar(t1, a, s2) @@ -171,7 +277,7 @@ g.Add(t0, t0, t1) s3.Add(s1, s2) g.MulScalar(t1, a, s3) if !g.Equal(t0, t1) { - t.Errorf(" (a ^ s1) + (a ^ s2) == a ^ (s1 + s2)") + t.Fatal(" (a ^ s1) + (a ^ s2) == a ^ (s1 + s2)") } } } @@ -185,36 +291,50 @@ scalars[0] = big.NewInt(2) scalars[1] = big.NewInt(3) bases[0], bases[1] = new(PointG2).Set(one), new(PointG2).Set(one) expected, result := g.New(), g.New() - g.MulScalar(expected, one, big.NewInt(5)) + g.mulScalar(expected, one, big.NewInt(5)) _, _ = g.MultiExp(result, bases[:], scalars[:]) if !g.Equal(expected, result) { - t.Fatal("bad multi-exponentiation") + t.Fatal("multi-exponentiation failed") } }   -func TestG2MultiExpBatch(t *testing.T) { +func TestG2MultiExp(t *testing.T) { g := NewG2() - one := g.one() - n := 1000 - bases := make([]*PointG2, n) - scalars := make([]*big.Int, n) - // scalars: [s0,s1 ... s(n-1)] - // bases: [P0,P1,..P(n-1)] = [s(n-1)*G, s(n-2)*G ... s0*G] - for i, j := 0, n-1; i < n; i, j = i+1, j-1 { - scalars[j], _ = rand.Int(rand.Reader, big.NewInt(100000)) - bases[i] = g.New() - g.MulScalar(bases[i], one, scalars[j]) + for n := 1; n < 1024+1; n = n * 2 { + bases := make([]*PointG2, n) + scalars := make([]*big.Int, n) + var err error + for i := 0; i < n; i++ { + scalars[i], err = rand.Int(rand.Reader, q) + if err != nil { + t.Fatal(err) + } + bases[i] = g.rand() + } + expected, tmp := g.New(), g.New() + for i := 0; i < n; i++ { + g.mulScalar(tmp, bases[i], scalars[i]) + g.Add(expected, expected, tmp) + } + result := g.New() + _, _ = g.MultiExp(result, bases, scalars) + if !g.Equal(expected, result) { + t.Fatal("multi-exponentiation failed") + } } - // expected: s(n-1)*P0 + s(n-2)*P1 + s0*P(n-1) - expected, tmp := g.New(), g.New() - for i := 0; i < n; i++ { - g.MulScalar(tmp, bases[i], scalars[i]) - g.Add(expected, expected, tmp) - } - result := g.New() - _, _ = g.MultiExp(result, bases, scalars) - if !g.Equal(expected, result) { - t.Fatal("bad multi-exponentiation") +} + +func TestG2ClearCofactor(t *testing.T) { + g := NewG2() + for i := 0; i < fuz; i++ { + p0 := g.rand() + if g.InCorrectSubgroup(p0) { + t.Fatal("rand point should be out of correct subgroup") + } + g.ClearCofactor(p0) + if !g.InCorrectSubgroup(p0) { + t.Fatal("cofactor clearing is failed") + } } }   @@ -224,24 +344,60 @@ u []byte expected []byte }{ { - u: make([]byte, 96), - expected: common.FromHex("0a67d12118b5a35bb02d2e86b3ebfa7e23410db93de39fb06d7025fa95e96ffa428a7a27c3ae4dd4b40bd251ac658892" + "018320896ec9eef9d5e619848dc29ce266f413d02dd31d9b9d44ec0c79cd61f18b075ddba6d7bd20b7ff27a4b324bfce" + "04c69777a43f0bda07679d5805e63f18cf4e0e7c6112ac7f70266d199b4f76ae27c6269a3ceebdae30806e9a76aadf5c" + "0260e03644d1a2c321256b3246bad2b895cad13890cbe6f85df55106a0d334604fb143c7a042d878006271865bc35941"), + u: make([]byte, 2*fpByteSize), + expected: fromHex(-1, "0a67d12118b5a35bb02d2e86b3ebfa7e23410db93de39fb06d7025fa95e96ffa428a7a27c3ae4dd4b40bd251ac658892", + "018320896ec9eef9d5e619848dc29ce266f413d02dd31d9b9d44ec0c79cd61f18b075ddba6d7bd20b7ff27a4b324bfce", + "04c69777a43f0bda07679d5805e63f18cf4e0e7c6112ac7f70266d199b4f76ae27c6269a3ceebdae30806e9a76aadf5c", + "0260e03644d1a2c321256b3246bad2b895cad13890cbe6f85df55106a0d334604fb143c7a042d878006271865bc35941", + ), }, { - u: common.FromHex("025fbc07711ba267b7e70c82caa70a16fbb1d470ae24ceef307f5e2000751677820b7013ad4e25492dcf30052d3e5eca" + "0e775d7827adf385b83e20e4445bd3fab21d7b4498426daf3c1d608b9d41e9edb5eda0df022e753b8bb4bc3bb7db4914"), - expected: common.FromHex("0d4333b77becbf9f9dfa3ca928002233d1ecc854b1447e5a71f751c9042d000f42db91c1d6649a5e0ad22bd7bf7398b8" + "027e4bfada0b47f9f07e04aec463c7371e68f2fd0c738cd517932ea3801a35acf09db018deda57387b0f270f7a219e4d" + "0cc76dc777ea0d447e02a41004f37a0a7b1fafb6746884e8d9fc276716ccf47e4e0899548a2ec71c2bdf1a2a50e876db" + "053674cba9ef516ddc218fedb37324e6c47de27f88ab7ef123b006127d738293c0277187f7e2f80a299a24d84ed03da7"), + u: fromHex(-1, + "025fbc07711ba267b7e70c82caa70a16fbb1d470ae24ceef307f5e2000751677820b7013ad4e25492dcf30052d3e5eca", + "0e775d7827adf385b83e20e4445bd3fab21d7b4498426daf3c1d608b9d41e9edb5eda0df022e753b8bb4bc3bb7db4914", + ), + expected: fromHex(-1, + "0d4333b77becbf9f9dfa3ca928002233d1ecc854b1447e5a71f751c9042d000f42db91c1d6649a5e0ad22bd7bf7398b8", + "027e4bfada0b47f9f07e04aec463c7371e68f2fd0c738cd517932ea3801a35acf09db018deda57387b0f270f7a219e4d", + "0cc76dc777ea0d447e02a41004f37a0a7b1fafb6746884e8d9fc276716ccf47e4e0899548a2ec71c2bdf1a2a50e876db", + "053674cba9ef516ddc218fedb37324e6c47de27f88ab7ef123b006127d738293c0277187f7e2f80a299a24d84ed03da7", + ), }, { - u: common.FromHex("1870a7dbfd2a1deb74015a3546b20f598041bf5d5202997956a94a368d30d3f70f18cdaa1d33ce970a4e16af961cbdcb" + "045ab31ce4b5a8ba7c4b2851b64f063a66cd1223d3c85005b78e1beee65e33c90ceef0244e45fc45a5e1d6eab6644fdb"), - expected: common.FromHex("18f0f87b40af67c056915dbaf48534c592524e82c1c2b50c3734d02c0172c80df780a60b5683759298a3303c5d942778" + "09349f1cb5b2e55489dcd45a38545343451cc30a1681c57acd4fb0a6db125f8352c09f4a67eb7d1d8242cb7d3405f97b" + "10a2ba341bc689ab947b7941ce6ef39be17acaab067bd32bd652b471ab0792c53a2bd03bdac47f96aaafe96e441f63c0" + "02f2d9deb2c7742512f5b8230bf0fd83ea42279d7d39779543c1a43b61c885982b611f6a7a24b514995e8a098496b811"), + u: fromHex(-1, + "1870a7dbfd2a1deb74015a3546b20f598041bf5d5202997956a94a368d30d3f70f18cdaa1d33ce970a4e16af961cbdcb", + "045ab31ce4b5a8ba7c4b2851b64f063a66cd1223d3c85005b78e1beee65e33c90ceef0244e45fc45a5e1d6eab6644fdb", + ), + expected: fromHex(-1, + "18f0f87b40af67c056915dbaf48534c592524e82c1c2b50c3734d02c0172c80df780a60b5683759298a3303c5d942778", + "09349f1cb5b2e55489dcd45a38545343451cc30a1681c57acd4fb0a6db125f8352c09f4a67eb7d1d8242cb7d3405f97b", + "10a2ba341bc689ab947b7941ce6ef39be17acaab067bd32bd652b471ab0792c53a2bd03bdac47f96aaafe96e441f63c0", + "02f2d9deb2c7742512f5b8230bf0fd83ea42279d7d39779543c1a43b61c885982b611f6a7a24b514995e8a098496b811", + ), }, { - u: common.FromHex("088fe329b054db8a6474f21a7fbfdf17b4c18044db299d9007af582c3d5f17d00e56d99921d4b5640fce44b05219b5de" + "0b6e6135a4cd31ba980ddbd115ac48abef7ec60e226f264d7befe002c165f3a496f36f76dd524efd75d17422558d10b4"), - expected: common.FromHex("19808ec5930a53c7cf5912ccce1cc33f1b3dcff24a53ce1cc4cba41fd6996dbed4843ccdd2eaf6a0cd801e562718d163" + "149fe43777d34f0d25430dea463889bd9393bdfb4932946db23671727081c629ebb98a89604f3433fba1c67d356a4af7" + "04783e391c30c83f805ca271e353582fdf19d159f6a4c39b73acbb637a9b8ac820cfbe2738d683368a7c07ad020e3e33" + "04c0d6793a766233b2982087b5f4a254f261003ccb3262ea7c50903eecef3e871d1502c293f9e063d7d293f6384f4551"), + u: fromHex(-1, + "088fe329b054db8a6474f21a7fbfdf17b4c18044db299d9007af582c3d5f17d00e56d99921d4b5640fce44b05219b5de", + "0b6e6135a4cd31ba980ddbd115ac48abef7ec60e226f264d7befe002c165f3a496f36f76dd524efd75d17422558d10b4", + ), + expected: fromHex(-1, + "19808ec5930a53c7cf5912ccce1cc33f1b3dcff24a53ce1cc4cba41fd6996dbed4843ccdd2eaf6a0cd801e562718d163", + "149fe43777d34f0d25430dea463889bd9393bdfb4932946db23671727081c629ebb98a89604f3433fba1c67d356a4af7", + "04783e391c30c83f805ca271e353582fdf19d159f6a4c39b73acbb637a9b8ac820cfbe2738d683368a7c07ad020e3e33", + "04c0d6793a766233b2982087b5f4a254f261003ccb3262ea7c50903eecef3e871d1502c293f9e063d7d293f6384f4551", + ), }, { - u: common.FromHex("03df16a66a05e4c1188c234788f43896e0565bfb64ac49b9639e6b284cc47dad73c47bb4ea7e677db8d496beb907fbb6" + "0f45b50647d67485295aa9eb2d91a877b44813677c67c8d35b2173ff3ba95f7bd0806f9ca8a1436b8b9d14ee81da4d7e"), - expected: common.FromHex("0b8e0094c886487870372eb6264613a6a087c7eb9804fab789be4e47a57b29eb19b1983a51165a1b5eb025865e9fc63a" + "0804152cbf8474669ad7d1796ab92d7ca21f32d8bed70898a748ed4e4e0ec557069003732fc86866d938538a2ae95552" + "14c80f068ece15a3936bb00c3c883966f75b4e8d9ddde809c11f781ab92d23a2d1d103ad48f6f3bb158bf3e3a4063449" + "09e5c8242dd7281ad32c03fe4af3f19167770016255fb25ad9b67ec51d62fade31a1af101e8f6172ec2ee8857662be3a"), + u: fromHex(-1, + "03df16a66a05e4c1188c234788f43896e0565bfb64ac49b9639e6b284cc47dad73c47bb4ea7e677db8d496beb907fbb6", + "0f45b50647d67485295aa9eb2d91a877b44813677c67c8d35b2173ff3ba95f7bd0806f9ca8a1436b8b9d14ee81da4d7e", + ), + expected: fromHex(-1, + "0b8e0094c886487870372eb6264613a6a087c7eb9804fab789be4e47a57b29eb19b1983a51165a1b5eb025865e9fc63a", + "0804152cbf8474669ad7d1796ab92d7ca21f32d8bed70898a748ed4e4e0ec557069003732fc86866d938538a2ae95552", + "14c80f068ece15a3936bb00c3c883966f75b4e8d9ddde809c11f781ab92d23a2d1d103ad48f6f3bb158bf3e3a4063449", + "09e5c8242dd7281ad32c03fe4af3f19167770016255fb25ad9b67ec51d62fade31a1af101e8f6172ec2ee8857662be3a", + ), }, } { g := NewG2() @@ -255,6 +411,114 @@ } } }   +func TestG2EncodeToCurve(t *testing.T) { + domain := []byte("BLS12381G2_XMD:SHA-256_SSWU_NU_TESTGEN") + for i, v := range []struct { + msg []byte + expected []byte + }{ + { + msg: []byte(""), + expected: fromHex(-1, + "0d4333b77becbf9f9dfa3ca928002233d1ecc854b1447e5a71f751c9042d000f42db91c1d6649a5e0ad22bd7bf7398b8", + "027e4bfada0b47f9f07e04aec463c7371e68f2fd0c738cd517932ea3801a35acf09db018deda57387b0f270f7a219e4d", + "0cc76dc777ea0d447e02a41004f37a0a7b1fafb6746884e8d9fc276716ccf47e4e0899548a2ec71c2bdf1a2a50e876db", + "053674cba9ef516ddc218fedb37324e6c47de27f88ab7ef123b006127d738293c0277187f7e2f80a299a24d84ed03da7", + ), + }, + { + msg: []byte("abc"), + expected: fromHex(-1, + "18f0f87b40af67c056915dbaf48534c592524e82c1c2b50c3734d02c0172c80df780a60b5683759298a3303c5d942778", + "09349f1cb5b2e55489dcd45a38545343451cc30a1681c57acd4fb0a6db125f8352c09f4a67eb7d1d8242cb7d3405f97b", + "10a2ba341bc689ab947b7941ce6ef39be17acaab067bd32bd652b471ab0792c53a2bd03bdac47f96aaafe96e441f63c0", + "02f2d9deb2c7742512f5b8230bf0fd83ea42279d7d39779543c1a43b61c885982b611f6a7a24b514995e8a098496b811", + ), + }, + { + msg: []byte("abcdef0123456789"), + expected: fromHex(-1, + "19808ec5930a53c7cf5912ccce1cc33f1b3dcff24a53ce1cc4cba41fd6996dbed4843ccdd2eaf6a0cd801e562718d163", + "149fe43777d34f0d25430dea463889bd9393bdfb4932946db23671727081c629ebb98a89604f3433fba1c67d356a4af7", + "04783e391c30c83f805ca271e353582fdf19d159f6a4c39b73acbb637a9b8ac820cfbe2738d683368a7c07ad020e3e33", + "04c0d6793a766233b2982087b5f4a254f261003ccb3262ea7c50903eecef3e871d1502c293f9e063d7d293f6384f4551", + ), + }, + { + msg: []byte("a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + expected: fromHex(-1, + "0b8e0094c886487870372eb6264613a6a087c7eb9804fab789be4e47a57b29eb19b1983a51165a1b5eb025865e9fc63a", + "0804152cbf8474669ad7d1796ab92d7ca21f32d8bed70898a748ed4e4e0ec557069003732fc86866d938538a2ae95552", + "14c80f068ece15a3936bb00c3c883966f75b4e8d9ddde809c11f781ab92d23a2d1d103ad48f6f3bb158bf3e3a4063449", + "09e5c8242dd7281ad32c03fe4af3f19167770016255fb25ad9b67ec51d62fade31a1af101e8f6172ec2ee8857662be3a", + ), + }, + } { + g := NewG2() + p0, err := g.EncodeToCurve(v.msg, domain) + if err != nil { + t.Fatal("encode to point fails", i, err) + } + if !bytes.Equal(g.ToBytes(p0), v.expected) { + t.Fatal("encode to point fails x", i) + } + } +} + +func TestG2HashToCurve(t *testing.T) { + domain := []byte("BLS12381G2_XMD:SHA-256_SSWU_RO_TESTGEN") + for i, v := range []struct { + msg []byte + expected []byte + }{ + { + msg: []byte(""), + expected: fromHex(-1, + "0fbdae26f9f9586a46d4b0b70390d09064ef2afe5c99348438a3c7d9756471e015cb534204c1b6824617a85024c772dc", + "0a650bd36ae7455cb3fe5d8bb1310594551456f5c6593aec9ee0c03d2f6cb693bd2c5e99d4e23cbaec767609314f51d3", + "02e5cf8f9b7348428cc9e66b9a9b36fe45ba0b0a146290c3a68d92895b1af0e1f2d9f889fb412670ae8478d8abd4c5aa", + "0d8d49e7737d8f9fc5cef7c4b8817633103faf2613016cb86a1f3fc29968fe2413e232d9208d2d74a89bf7a48ac36f83", + ), + }, + { + msg: []byte("abc"), + expected: fromHex(-1, + "03578447618463deb106b60e609c6f7cc446dc6035f84a72801ba17c94cd800583b493b948eff0033f09086fdd7f6175", + "1953ce6d4267939c7360756d9cca8eb34aac4633ef35369a7dc249445069888e7d1b3f9d2e75fbd468fbcbba7110ea02", + "0184d26779ae9d4670aca9b267dbd4d3b30443ad05b8546d36a195686e1ccc3a59194aea05ed5bce7c3144a29ec047c4", + "0882ab045b8fe4d7d557ebb59a63a35ac9f3d312581b509af0f8eaa2960cbc5e1e36bb969b6e22980b5cbdd0787fcf4e", + ), + }, + { + msg: []byte("abcdef0123456789"), + expected: fromHex(-1, + "195fad48982e186ce3c5c82133aefc9b26d55979b6f530992a8849d4263ec5d57f7a181553c8799bcc83da44847bdc8d", + "17b461fc3b96a30c2408958cbfa5f5927b6063a8ad199d5ebf2d7cdeffa9c20c85487204804fab53f950b2f87db365aa", + "005cdf3d984e3391e7e969276fb4bc02323c5924a4449af167030d855acc2600cf3d4fab025432c6d868c79571a95bef", + "174a3473a3af2d0302b9065e895ca4adba4ece6ce0b41148ba597001abb152f852dd9a96fb45c9de0a43d944746f833e", + ), + }, + { + msg: []byte("a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + expected: fromHex(-1, + "123b6bd9feeba26dd4ad00f8bfda2718c9700dc093ea5287d7711844644eb981848316d3f3f57d5d3a652c6cdc816aca", + "0a162306f3b0f2bb326f0c4fb0e1fea020019c3af796dcd1d7264f50ddae94cacf3cade74603834d44b9ab3d5d0a6c98", + "05483f3b96d9252dd4fc0868344dfaf3c9d145e3387db23fa8e449304fab6a7b6ec9c15f05c0a1ea66ff0efcc03e001a", + "15c1d4f1a685bb63ee67ca1fd96155e3d091e852a684b78d085fd34f6091e5249ddddbdcf2e7ec82ce6c04c63647eeb7", + ), + }, + } { + g := NewG2() + p0, err := g.HashToCurve(v.msg, domain) + if err != nil { + t.Fatal("encode to point fails", i, err) + } + if !bytes.Equal(g.ToBytes(p0), v.expected) { + t.Fatal("encode to point fails x", i) + } + } +} + func BenchmarkG2Add(t *testing.B) { g2 := NewG2() a, b, c := g2.rand(), g2.rand(), PointG2{} @@ -264,17 +528,94 @@ g2.Add(&c, a, b) } }   -func BenchmarkG2Mul(t *testing.B) { +func BenchmarkG2MulWNAF(t *testing.B) { + g := NewG2() + p := new(PointG2).Set(&g2One) + s := randScalar(q) + res := new(PointG2) + t.Run("Naive", func(t *testing.B) { + t.ResetTimer() + for i := 0; i < t.N; i++ { + g.mulScalar(res, p, s) + } + }) + for i := 1; i < 8; i++ { + wnafMulWindowG2 = uint(i) + t.Run(fmt.Sprintf("window: %d", i), func(t *testing.B) { + t.ResetTimer() + for i := 0; i < t.N; i++ { + g.wnafMul(res, p, s) + } + }) + } +} + +func BenchmarkG2MulGLV(t *testing.B) { + + g := NewG2() + p := new(PointG2).Set(&g2One) + s := randScalar(q) + res := new(PointG2) + t.Run("Naive", func(t *testing.B) { + t.ResetTimer() + for i := 0; i < t.N; i++ { + g.mulScalar(res, p, s) + } + }) + for i := 1; i < 8; i++ { + glvMulWindowG2 = uint(i) + t.Run(fmt.Sprintf("window: %d", i), func(t *testing.B) { + t.ResetTimer() + for i := 0; i < t.N; i++ { + g.glvMul(res, p, s) + } + }) + } +} + +func BenchmarkG2MultiExp(t *testing.B) { + g := NewG2() + v := func(n int) ([]*PointG2, []*big.Int) { + bases := make([]*PointG2, n) + scalars := make([]*big.Int, n) + for i := 0; i < n; i++ { + scalars[i] = randScalar(q) + bases[i] = g.randAffine() + } + return bases, scalars + } + for _, i := range []int{1, 2, 10, 100, 1000} { + t.Run(fmt.Sprint(i), func(t *testing.B) { + bases, scalars := v(i) + result := g.New() + t.ResetTimer() + for i := 0; i < t.N; i++ { + _, _ = g.MultiExp(result, bases, scalars) + } + }) + } +} + +func BenchmarkG2ClearCofactor(t *testing.B) { + g2 := NewG2() + a := g2.rand() + t.ResetTimer() + for i := 0; i < t.N; i++ { + g2.ClearCofactor(a) + } +} + +func BenchmarkG2SubgroupCheck(t *testing.B) { g2 := NewG2() - a, e, c := g2.rand(), q, PointG2{} + a := g2.rand() t.ResetTimer() for i := 0; i < t.N; i++ { - g2.MulScalar(&c, a, e) + g2.InCorrectSubgroup(a) } }   func BenchmarkG2SWUMap(t *testing.B) { - a := make([]byte, 96) + a := fromHex(2*fpByteSize, "0x1234") g2 := NewG2() t.ResetTimer() for i := 0; i < t.N; i++ {
diff --git go-ethereum/crypto/bls12381/utils.go celo/crypto/bls12381/utils.go index de8bf495fe7de6a6a9b7856878fb8848576371f0..47c54effdf1113d4b11280dc9e8467be7ccde256 100644 --- go-ethereum/crypto/bls12381/utils.go +++ celo/crypto/bls12381/utils.go @@ -1,30 +1,16 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   import ( "errors" "math/big" - - "github.com/ethereum/go-ethereum/common" )   func bigFromHex(hex string) *big.Int { - return new(big.Int).SetBytes(common.FromHex(hex)) + if len(hex) > 1 && hex[:2] == "0x" { + hex = hex[2:] + } + n, _ := new(big.Int).SetString(hex, 16) + return n }   // decodeFieldElement expects 64 byte input with zero top 16 bytes,
diff --git go-ethereum/crypto/bls12381/fp_fallback.go celo/crypto/bls12381/fp_fallback.go new file mode 100644 index 0000000000000000000000000000000000000000..2178dd979569a714884336c642cfb3bba7aedfb7 --- /dev/null +++ celo/crypto/bls12381/fp_fallback.go @@ -0,0 +1,433 @@ +//go:build !amd64 || generic +// +build !amd64 generic + +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by goff (v0.3.5) DO NOT EDIT + +// /!\ WARNING /!\ +// this code has not been audited and is provided as-is. In particular, +// there is no security guarantees such as constant time implementation +// or side-channel attack resistance +// /!\ WARNING /!\ + +package bls12381 + +import "math/bits" + +func add(z, x, y *fe) { + var carry uint64 + + z[0], carry = bits.Add64(x[0], y[0], 0) + z[1], carry = bits.Add64(x[1], y[1], carry) + z[2], carry = bits.Add64(x[2], y[2], carry) + z[3], carry = bits.Add64(x[3], y[3], carry) + z[4], carry = bits.Add64(x[4], y[4], carry) + z[5], _ = bits.Add64(x[5], y[5], carry) + + // if z > q --> z -= q + // note: this is NOT constant time + if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) + z[1], b = bits.Sub64(z[1], 2210141511517208575, b) + z[2], b = bits.Sub64(z[2], 7435674573564081700, b) + z[3], b = bits.Sub64(z[3], 7239337960414712511, b) + z[4], b = bits.Sub64(z[4], 5412103778470702295, b) + z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) + } +} + +func addAssign(z, y *fe) { + var carry uint64 + + z[0], carry = bits.Add64(z[0], y[0], 0) + z[1], carry = bits.Add64(z[1], y[1], carry) + z[2], carry = bits.Add64(z[2], y[2], carry) + z[3], carry = bits.Add64(z[3], y[3], carry) + z[4], carry = bits.Add64(z[4], y[4], carry) + z[5], _ = bits.Add64(z[5], y[5], carry) + + // if z > q --> z -= q + // note: this is NOT constant time + if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) + z[1], b = bits.Sub64(z[1], 2210141511517208575, b) + z[2], b = bits.Sub64(z[2], 7435674573564081700, b) + z[3], b = bits.Sub64(z[3], 7239337960414712511, b) + z[4], b = bits.Sub64(z[4], 5412103778470702295, b) + z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) + } +} + +func laddAssign(z, y *fe) { + var carry uint64 + + z[0], carry = bits.Add64(z[0], y[0], 0) + z[1], carry = bits.Add64(z[1], y[1], carry) + z[2], carry = bits.Add64(z[2], y[2], carry) + z[3], carry = bits.Add64(z[3], y[3], carry) + z[4], carry = bits.Add64(z[4], y[4], carry) + z[5], _ = bits.Add64(z[5], y[5], carry) +} + +func double(z, x *fe) { + var carry uint64 + + z[0], carry = bits.Add64(x[0], x[0], 0) + z[1], carry = bits.Add64(x[1], x[1], carry) + z[2], carry = bits.Add64(x[2], x[2], carry) + z[3], carry = bits.Add64(x[3], x[3], carry) + z[4], carry = bits.Add64(x[4], x[4], carry) + z[5], _ = bits.Add64(x[5], x[5], carry) + + // if z > q --> z -= q + // note: this is NOT constant time + if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) + z[1], b = bits.Sub64(z[1], 2210141511517208575, b) + z[2], b = bits.Sub64(z[2], 7435674573564081700, b) + z[3], b = bits.Sub64(z[3], 7239337960414712511, b) + z[4], b = bits.Sub64(z[4], 5412103778470702295, b) + z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) + } +} + +func doubleAssign(z *fe) { + var carry uint64 + + z[0], carry = bits.Add64(z[0], z[0], 0) + z[1], carry = bits.Add64(z[1], z[1], carry) + z[2], carry = bits.Add64(z[2], z[2], carry) + z[3], carry = bits.Add64(z[3], z[3], carry) + z[4], carry = bits.Add64(z[4], z[4], carry) + z[5], _ = bits.Add64(z[5], z[5], carry) + + // if z > q --> z -= q + // note: this is NOT constant time + if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) + z[1], b = bits.Sub64(z[1], 2210141511517208575, b) + z[2], b = bits.Sub64(z[2], 7435674573564081700, b) + z[3], b = bits.Sub64(z[3], 7239337960414712511, b) + z[4], b = bits.Sub64(z[4], 5412103778470702295, b) + z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) + } +} + +func sub(z, x, y *fe) { + var b uint64 + z[0], b = bits.Sub64(x[0], y[0], 0) + z[1], b = bits.Sub64(x[1], y[1], b) + z[2], b = bits.Sub64(x[2], y[2], b) + z[3], b = bits.Sub64(x[3], y[3], b) + z[4], b = bits.Sub64(x[4], y[4], b) + z[5], b = bits.Sub64(x[5], y[5], b) + if b != 0 { + var c uint64 + z[0], c = bits.Add64(z[0], 13402431016077863595, 0) + z[1], c = bits.Add64(z[1], 2210141511517208575, c) + z[2], c = bits.Add64(z[2], 7435674573564081700, c) + z[3], c = bits.Add64(z[3], 7239337960414712511, c) + z[4], c = bits.Add64(z[4], 5412103778470702295, c) + z[5], _ = bits.Add64(z[5], 1873798617647539866, c) + } +} + +func subAssign(z, y *fe) { + var b uint64 + z[0], b = bits.Sub64(z[0], y[0], 0) + z[1], b = bits.Sub64(z[1], y[1], b) + z[2], b = bits.Sub64(z[2], y[2], b) + z[3], b = bits.Sub64(z[3], y[3], b) + z[4], b = bits.Sub64(z[4], y[4], b) + z[5], b = bits.Sub64(z[5], y[5], b) + if b != 0 { + var c uint64 + z[0], c = bits.Add64(z[0], 13402431016077863595, 0) + z[1], c = bits.Add64(z[1], 2210141511517208575, c) + z[2], c = bits.Add64(z[2], 7435674573564081700, c) + z[3], c = bits.Add64(z[3], 7239337960414712511, c) + z[4], c = bits.Add64(z[4], 5412103778470702295, c) + z[5], _ = bits.Add64(z[5], 1873798617647539866, c) + } +} + +func lsubAssign(z, y *fe) { + var b uint64 + z[0], b = bits.Sub64(z[0], y[0], 0) + z[1], b = bits.Sub64(z[1], y[1], b) + z[2], b = bits.Sub64(z[2], y[2], b) + z[3], b = bits.Sub64(z[3], y[3], b) + z[4], b = bits.Sub64(z[4], y[4], b) + z[5], b = bits.Sub64(z[5], y[5], b) +} + +func neg(z, x *fe) { + if x.isZero() { + z.zero() + return + } + var borrow uint64 + z[0], borrow = bits.Sub64(13402431016077863595, x[0], 0) + z[1], borrow = bits.Sub64(2210141511517208575, x[1], borrow) + z[2], borrow = bits.Sub64(7435674573564081700, x[2], borrow) + z[3], borrow = bits.Sub64(7239337960414712511, x[3], borrow) + z[4], borrow = bits.Sub64(5412103778470702295, x[4], borrow) + z[5], _ = bits.Sub64(1873798617647539866, x[5], borrow) +} + +func mul(z, x, y *fe) { + + var t [6]uint64 + var c [3]uint64 + { + // round 0 + v := x[0] + c[1], c[0] = bits.Mul64(v, y[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd1(v, y[1], c[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd1(v, y[2], c[1]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd1(v, y[3], c[1]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd1(v, y[4], c[1]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd1(v, y[5], c[1]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 1 + v := x[1] + c[1], c[0] = madd1(v, y[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, y[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, y[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, y[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, y[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, y[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 2 + v := x[2] + c[1], c[0] = madd1(v, y[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, y[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, y[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, y[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, y[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, y[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 3 + v := x[3] + c[1], c[0] = madd1(v, y[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, y[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, y[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, y[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, y[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, y[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 4 + v := x[4] + c[1], c[0] = madd1(v, y[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, y[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, y[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, y[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, y[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, y[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 5 + v := x[5] + c[1], c[0] = madd1(v, y[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, y[1], c[1], t[1]) + c[2], z[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, y[2], c[1], t[2]) + c[2], z[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, y[3], c[1], t[3]) + c[2], z[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, y[4], c[1], t[4]) + c[2], z[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, y[5], c[1], t[5]) + z[5], z[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + + // if z > q --> z -= q + // note: this is NOT constant time + if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) + z[1], b = bits.Sub64(z[1], 2210141511517208575, b) + z[2], b = bits.Sub64(z[2], 7435674573564081700, b) + z[3], b = bits.Sub64(z[3], 7239337960414712511, b) + z[4], b = bits.Sub64(z[4], 5412103778470702295, b) + z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) + } +} + +func square(z, x *fe) { + + var t [6]uint64 + var c [3]uint64 + { + // round 0 + v := x[0] + c[1], c[0] = bits.Mul64(v, x[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd1(v, x[1], c[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd1(v, x[2], c[1]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd1(v, x[3], c[1]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd1(v, x[4], c[1]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd1(v, x[5], c[1]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 1 + v := x[1] + c[1], c[0] = madd1(v, x[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, x[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, x[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, x[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, x[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, x[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 2 + v := x[2] + c[1], c[0] = madd1(v, x[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, x[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, x[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, x[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, x[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, x[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 3 + v := x[3] + c[1], c[0] = madd1(v, x[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, x[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, x[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, x[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, x[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, x[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 4 + v := x[4] + c[1], c[0] = madd1(v, x[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, x[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, x[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, x[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, x[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, x[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 5 + v := x[5] + c[1], c[0] = madd1(v, x[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, x[1], c[1], t[1]) + c[2], z[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, x[2], c[1], t[2]) + c[2], z[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, x[3], c[1], t[3]) + c[2], z[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, x[4], c[1], t[4]) + c[2], z[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, x[5], c[1], t[5]) + z[5], z[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + + // if z > q --> z -= q + // note: this is NOT constant time + if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) + z[1], b = bits.Sub64(z[1], 2210141511517208575, b) + z[2], b = bits.Sub64(z[2], 7435674573564081700, b) + z[3], b = bits.Sub64(z[3], 7239337960414712511, b) + z[4], b = bits.Sub64(z[4], 5412103778470702295, b) + z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) + } +}
diff --git go-ethereum/crypto/bls12381/field_element_test.go celo/crypto/bls12381/field_element_test.go index 0f6abd280cbb003f5c05053789259509513a8d35..84c866fd3fb6898804effc4d56c4faf88b8853ce 100644 --- go-ethereum/crypto/bls12381/field_element_test.go +++ celo/crypto/bls12381/field_element_test.go @@ -8,6 +8,7 @@ "testing" )   func TestFieldElementValidation(t *testing.T) { + // fe zero := new(fe).zero() if !zero.isValid() { t.Fatal("zero must be valid") @@ -160,13 +161,13 @@ }   func TestFieldElementSerialization(t *testing.T) { t.Run("zero", func(t *testing.T) { - in := make([]byte, 48) + in := make([]byte, fpByteSize) fe := new(fe).setBytes(in) if !fe.isZero() { - t.Fatal("bad serialization") + t.Fatal("serialization failed") } if !bytes.Equal(in, fe.bytes()) { - t.Fatal("bad serialization") + t.Fatal("serialization failed") } }) t.Run("bytes", func(t *testing.T) { @@ -174,7 +175,7 @@ for i := 0; i < fuz; i++ { a, _ := new(fe).rand(rand.Reader) b := new(fe).setBytes(a.bytes()) if !a.equal(b) { - t.Fatal("bad serialization") + t.Fatal("serialization failed") } } }) @@ -183,7 +184,7 @@ for i := 0; i < fuz; i++ { a, _ := new(fe).rand(rand.Reader) b := new(fe).setBig(a.big()) if !a.equal(b) { - t.Fatal("bad encoding or decoding") + t.Fatal("encoding or decoding failed") } } }) @@ -195,7 +196,7 @@ if err != nil { t.Fatal(err) } if !a.equal(b) { - t.Fatal("bad encoding or decoding") + t.Fatal("encoding or decoding failed") } } }) @@ -206,24 +207,24 @@ zero := new(fe).zero() in := make([]byte, 0) a := new(fe).setBytes(in) if !a.equal(zero) { - t.Fatal("bad serialization") + t.Fatal("serialization failed") } - in = make([]byte, 48) + in = make([]byte, fpByteSize) a = new(fe).setBytes(in) if !a.equal(zero) { - t.Fatal("bad serialization") + t.Fatal("serialization failed") } - in = make([]byte, 64) + in = make([]byte, fpByteSize+200) a = new(fe).setBytes(in) if !a.equal(zero) { - t.Fatal("bad serialization") + t.Fatal("serialization failed") } - in = make([]byte, 49) - in[47] = 1 + in = make([]byte, fpByteSize+1) + in[fpByteSize-1] = 1 normalOne := &fe{1, 0, 0, 0, 0, 0} a = new(fe).setBytes(in) if !a.equal(normalOne) { - t.Fatal("bad serialization") + t.Fatal("serialization failed") } }   @@ -231,21 +232,21 @@ func TestFieldElementCopy(t *testing.T) { a, _ := new(fe).rand(rand.Reader) b := new(fe).set(a) if !a.equal(b) { - t.Fatal("bad copy, 1") + t.Fatal("copy failed") } a2, _ := new(fe2).rand(rand.Reader) b2 := new(fe2).set(a2) if !a2.equal(b2) { - t.Fatal("bad copy, 2") + t.Fatal("copy failed") } a6, _ := new(fe6).rand(rand.Reader) b6 := new(fe6).set(a6) if !a6.equal(b6) { - t.Fatal("bad copy, 6") + t.Fatal("copy failed") } a12, _ := new(fe12).rand(rand.Reader) b12 := new(fe12).set(a12) if !a12.equal(b12) { - t.Fatal("bad copy, 12") + t.Fatal("copy failed2") } }
diff --git go-ethereum/crypto/bls12381/arithmetic_decl.go celo/crypto/bls12381/arithmetic_decl.go index f6d232d658be505c10eb8cf2aabc9035c7f12f80..d4e616cd689e42894eae7cbf38dc1eda8a932568 100644 --- go-ethereum/crypto/bls12381/arithmetic_decl.go +++ celo/crypto/bls12381/arithmetic_decl.go @@ -1,21 +1,5 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -//go:build (amd64 && blsasm) || (amd64 && blsadx) -// +build amd64,blsasm amd64,blsadx +//go:build amd64 && !generic +// +build amd64,!generic   package bls12381   @@ -24,12 +8,11 @@ "golang.org/x/sys/cpu" )   func init() { - if !enableADX || !cpu.X86.HasADX || !cpu.X86.HasBMI2 { + if !cpu.X86.HasADX || !cpu.X86.HasBMI2 { mul = mulNoADX } }   -// Use ADX backend for default var mul func(c, a, b *fe) = mulADX   func square(c, a *fe) { @@ -51,9 +34,6 @@ //go:noescape func addAssign(a, b *fe)   //go:noescape -func ladd(c, a, b *fe) - -//go:noescape func laddAssign(a, b *fe)   //go:noescape @@ -61,9 +41,6 @@ func double(c, a *fe)   //go:noescape func doubleAssign(a *fe) - -//go:noescape -func ldouble(c, a *fe)   //go:noescape func sub(c, a, b *fe)
diff --git go-ethereum/crypto/bls12381/glv_test.go celo/crypto/bls12381/glv_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d075a810aba8936c75d0a8a589e0769b70f40ca5 --- /dev/null +++ celo/crypto/bls12381/glv_test.go @@ -0,0 +1,82 @@ +package bls12381 + +import ( + "math/big" + "testing" +) + +func TestGLVConstruction(t *testing.T) { + t.Run("Parameters", func(t *testing.T) { + t0, t1 := new(big.Int), new(big.Int) + one := new(big.Int).SetUint64(1) + t0.Mul(glvLambda, glvLambda) + t0.Add(t0, glvLambda) + t1.Sub(q, one) + if t0.Cmp(t1) != 0 { + t.Fatal("lambda1^2 + lambda1 + 1 = 0") + } + c0 := new(fe) + square(c0, glvPhi1) + mul(c0, c0, glvPhi1) + if !c0.isOne() { + t.Fatal("phi1^3 = 1") + } + square(c0, glvPhi2) + mul(c0, c0, glvPhi2) + if !c0.isOne() { + t.Fatal("phi2^3 = 1") + } + }) + t.Run("Endomorphism G1", func(t *testing.T) { + g := NewG1() + { + p0, p1 := g.randAffine(), g.New() + g.MulScalar(p1, p0, glvLambda) + g.Affine(p1) + r := g.New() + g.glvEndomorphism(r, p0) + if !g.Equal(r, p1) { + t.Fatal("f(x, y) = (phi * x, y)") + } + } + }) + t.Run("Endomorphism G2", func(t *testing.T) { + g := NewG2() + { + p0, p1 := g.randAffine(), g.New() + g.MulScalar(p1, p0, glvLambda) + g.Affine(p1) + r := g.New() + g.glvEndomorphism(r, p0) + if !g.Equal(r, p1) { + t.Fatal("f(x, y) = (phi * x, y)") + } + } + }) + t.Run("Scalar Decomposition", func(t *testing.T) { + for i := 0; i < fuz; i++ { + + k := randScalar(q) + var v *glvVector + + r128 := bigFromHex("0x100000000000000000000000000000000") + { + v = new(glvVector).new(k) + + if new(big.Int).Abs(v.k1).Cmp(r128) >= 0 { + t.Fatal("bad scalar component, k1") + } + if new(big.Int).Abs(v.k2).Cmp(r128) >= 0 { + t.Fatal("bad scalar component, k2") + } + + r := new(big.Int) + r.Mul(glvLambda, v.k2) + r.Sub(v.k1, k).Mod(k, q) + if k.Cmp(r) != 0 { + t.Fatal("scalar decomposing with failed", i) + } + } + } + }) +}
diff --git go-ethereum/crypto/bls12381/wnaf.go celo/crypto/bls12381/wnaf.go new file mode 100644 index 0000000000000000000000000000000000000000..f5f31f4da6f2008b973bfac98a59b8f1c93c84ba --- /dev/null +++ celo/crypto/bls12381/wnaf.go @@ -0,0 +1,55 @@ +package bls12381 + +import ( + "math/big" +) + +type nafNumber []int + +func (n nafNumber) neg() { + for i := 0; i < len(n); i++ { + n[i] = -n[i] + } +} + +var bigZero = big.NewInt(0) +var bigOne = big.NewInt(1) + +// caution: does not cover negative case +func bigToWNAF(e *big.Int, w uint) nafNumber { + naf := nafNumber{} + if w == 0 { + return naf + } + windowSize := new(big.Int).Lsh(bigOne, w+1) + halfSize := new(big.Int).Rsh(windowSize, 1) + ee := new(big.Int).Abs(e) + for ee.Cmp(bigZero) != 0 { + if ee.Bit(0) == 1 { + nafSign := new(big.Int) + nafSign.Mod(ee, windowSize) + if nafSign.Cmp(halfSize) >= 0 { + nafSign.Sub(nafSign, windowSize) + } + naf = append(naf, int(nafSign.Int64())) + ee.Sub(ee, nafSign) + } else { + naf = append(naf, 0) + } + ee.Rsh(ee, 1) + } + return naf +} + +func bigFromWNAF(naf nafNumber) *big.Int { + acc := new(big.Int) + k := new(big.Int).Set(bigOne) + for i := 0; i < len(naf); i++ { + if naf[i] != 0 { + z := new(big.Int).Mul(k, big.NewInt(int64(naf[i]))) + acc.Add(acc, z) + } + k.Lsh(k, 1) + } + return acc +}
diff --git go-ethereum/crypto/bls12381/fp.go celo/crypto/bls12381/fp.go index 09f6f49bc011e52c1413a227400af7d12d5953f7..83396a5bb86db5977c7969712bb5c96f1c4b90c9 100644 --- go-ethereum/crypto/bls12381/fp.go +++ celo/crypto/bls12381/fp.go @@ -1,19 +1,3 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   import ( @@ -23,8 +7,8 @@ )   func fromBytes(in []byte) (*fe, error) { fe := &fe{} - if len(in) != 48 { - return nil, errors.New("input string should be equal 48 bytes") + if len(in) != fpByteSize { + return nil, errors.New("input string must be equal 48 bytes") } fe.setBytes(in) if !fe.isValid() { @@ -34,6 +18,37 @@ toMont(fe, fe) return fe, nil }   +func from64Bytes(in []byte) (*fe, error) { + if len(in) != 32*2 { + return nil, errors.New("input string must be equal 64 bytes") + } + a0 := make([]byte, fpByteSize) + copy(a0[fpByteSize-32:fpByteSize], in[:32]) + a1 := make([]byte, fpByteSize) + copy(a1[fpByteSize-32:fpByteSize], in[32:]) + e0, err := fromBytes(a0) + if err != nil { + return nil, err + } + e1, err := fromBytes(a1) + if err != nil { + return nil, err + } + // F = 2 ^ 256 * R + F := fe{ + 0x75b3cd7c5ce820f, + 0x3ec6ba621c3edb0b, + 0x168a13d82bff6bce, + 0x87663c4bf8c449d2, + 0x15f34c83ddc8d830, + 0xf9628b49caa2e85, + } + + mul(e0, e0, &F) + add(e1, e1, e0) + return e1, nil +} + func fromBig(in *big.Int) (*fe, error) { fe := new(fe).setBig(in) if !fe.isValid() { @@ -105,7 +120,7 @@ var k int var z uint64 var found = false // Phase 1 - for i := 0; i < 768; i++ { + for i := 0; i < sixWordBitSize*2; i++ { if v.isZero() { found = true break @@ -135,7 +150,7 @@ inv.zero() return }   - if k < 381 || k > 381+384 { + if k < fpBitSize || k > fpBitSize+sixWordBitSize { inv.zero() return } @@ -147,21 +162,189 @@ u.set(&modulus) lsubAssign(u, r)   // Phase 2 - for i := k; i < 384*2; i++ { + for i := k; i < 2*sixWordBitSize; i++ { double(u, u) } inv.set(u) }   +func inverseBatch(in []fe) { + + n, N, setFirst := 0, len(in), false + + for i := 0; i < len(in); i++ { + if !in[i].isZero() { + n++ + } + } + if n == 0 { + return + } + + tA := make([]fe, n) + tB := make([]fe, n) + + for i, j := 0, 0; i < N; i++ { + if !in[i].isZero() { + if !setFirst { + setFirst = true + tA[j].set(&in[i]) + } else { + mul(&tA[j], &in[i], &tA[j-1]) + } + j = j + 1 + } + } + + inverse(&tB[n-1], &tA[n-1]) + for i, j := N-1, n-1; j != 0; i-- { + if !in[i].isZero() { + mul(&tB[j-1], &tB[j], &in[i]) + j = j - 1 + } + } + + for i, j := 0, 0; i < N; i++ { + if !in[i].isZero() { + if setFirst { + setFirst = false + in[i].set(&tB[j]) + } else { + mul(&in[i], &tA[j-1], &tB[j]) + } + j = j + 1 + } + } +} + +func rsqrt(c, a *fe) bool { + t0, t1 := new(fe), new(fe) + sqrtAddchain(t0, a) + mul(t1, t0, a) + square(t1, t1) + ret := t1.equal(a) + c.set(t0) + return ret +} + func sqrt(c, a *fe) bool { u, v := new(fe).set(a), new(fe) + // a ^ (p - 3) / 4 + sqrtAddchain(c, a) + // a ^ (p + 1) / 4 + mul(c, c, u) + + square(v, c) + return u.equal(v) +} + +func _sqrt(c, a *fe) bool { + u, v := new(fe).set(a), new(fe) exp(c, a, pPlus1Over4) square(v, c) return u.equal(v) }   -func isQuadraticNonResidue(elem *fe) bool { - result := new(fe) - exp(result, elem, pMinus1Over2) - return !result.isOne() +func sqrtAddchain(c, a *fe) { + chain := func(c *fe, n int, a *fe) { + for i := 0; i < n; i++ { + square(c, c) + } + mul(c, c, a) + } + + t := make([]fe, 16) + t[13].set(a) + square(&t[0], &t[13]) + mul(&t[8], &t[0], &t[13]) + square(&t[4], &t[0]) + mul(&t[1], &t[8], &t[0]) + mul(&t[6], &t[4], &t[8]) + mul(&t[9], &t[1], &t[4]) + mul(&t[12], &t[6], &t[4]) + mul(&t[3], &t[9], &t[4]) + mul(&t[7], &t[12], &t[4]) + mul(&t[15], &t[3], &t[4]) + mul(&t[10], &t[7], &t[4]) + mul(&t[2], &t[15], &t[4]) + mul(&t[11], &t[10], &t[4]) + square(&t[0], &t[3]) + mul(&t[14], &t[11], &t[4]) + mul(&t[5], &t[0], &t[8]) + mul(&t[4], &t[0], &t[1]) + + chain(&t[0], 12, &t[15]) + chain(&t[0], 7, &t[7]) + chain(&t[0], 4, &t[1]) + chain(&t[0], 6, &t[6]) + chain(&t[0], 7, &t[11]) + chain(&t[0], 5, &t[4]) + chain(&t[0], 2, &t[8]) + chain(&t[0], 6, &t[3]) + chain(&t[0], 6, &t[3]) + chain(&t[0], 6, &t[9]) + chain(&t[0], 3, &t[8]) + chain(&t[0], 7, &t[3]) + chain(&t[0], 4, &t[3]) + chain(&t[0], 6, &t[7]) + chain(&t[0], 6, &t[14]) + chain(&t[0], 3, &t[13]) + chain(&t[0], 8, &t[3]) + chain(&t[0], 7, &t[11]) + chain(&t[0], 5, &t[12]) + chain(&t[0], 6, &t[3]) + chain(&t[0], 6, &t[5]) + chain(&t[0], 4, &t[9]) + chain(&t[0], 8, &t[5]) + chain(&t[0], 4, &t[3]) + chain(&t[0], 7, &t[11]) + chain(&t[0], 9, &t[10]) + chain(&t[0], 2, &t[8]) + chain(&t[0], 5, &t[6]) + chain(&t[0], 7, &t[1]) + chain(&t[0], 7, &t[9]) + chain(&t[0], 6, &t[11]) + chain(&t[0], 5, &t[5]) + chain(&t[0], 5, &t[10]) + chain(&t[0], 5, &t[10]) + chain(&t[0], 8, &t[3]) + chain(&t[0], 7, &t[2]) + chain(&t[0], 9, &t[7]) + chain(&t[0], 5, &t[3]) + chain(&t[0], 3, &t[8]) + chain(&t[0], 8, &t[7]) + chain(&t[0], 3, &t[8]) + chain(&t[0], 7, &t[9]) + chain(&t[0], 9, &t[7]) + chain(&t[0], 6, &t[2]) + chain(&t[0], 6, &t[4]) + chain(&t[0], 5, &t[4]) + chain(&t[0], 5, &t[4]) + chain(&t[0], 4, &t[3]) + chain(&t[0], 3, &t[8]) + chain(&t[0], 8, &t[2]) + chain(&t[0], 7, &t[4]) + chain(&t[0], 5, &t[4]) + chain(&t[0], 5, &t[4]) + chain(&t[0], 4, &t[7]) + chain(&t[0], 4, &t[6]) + chain(&t[0], 7, &t[4]) + chain(&t[0], 5, &t[5]) + chain(&t[0], 5, &t[4]) + chain(&t[0], 5, &t[4]) + chain(&t[0], 5, &t[4]) + chain(&t[0], 5, &t[4]) + chain(&t[0], 5, &t[4]) + chain(&t[0], 5, &t[4]) + chain(&t[0], 4, &t[3]) + chain(&t[0], 6, &t[2]) + chain(&t[0], 4, &t[1]) + square(c, &t[0]) +} + +func isQuadraticNonResidue(a *fe) bool { + if a.isZero() { + return true + } + return !sqrt(new(fe), a) }
diff --git go-ethereum/crypto/bls12381/g1_test.go celo/crypto/bls12381/g1_test.go index 3c7d317f104c72f40033f9ca5b7fd55339902ea0..521f0550ee8041c5529c831694525b7175860ade 100644 --- go-ethereum/crypto/bls12381/g1_test.go +++ celo/crypto/bls12381/g1_test.go @@ -3,51 +3,87 @@ import ( "bytes" "crypto/rand" + "fmt" "math/big" "testing" - - "github.com/ethereum/go-ethereum/common" )   func (g *G1) one() *PointG1 { - one, _ := g.fromBytesUnchecked( - common.FromHex("" + - "17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb" + - "08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1", - ), - ) - return one + return g.New().Set(&g1One) }   func (g *G1) rand() *PointG1 { - k, err := rand.Int(rand.Reader, q) - if err != nil { - panic(err) + p := &PointG1{} + z, _ := new(fe).rand(rand.Reader) + z6, bz6 := new(fe), new(fe) + square(z6, z) + square(z6, z6) + mul(z6, z6, z) + mul(z6, z6, z) + mul(bz6, z6, b) + for { + x, _ := new(fe).rand(rand.Reader) + y := new(fe) + square(y, x) + mul(y, y, x) + add(y, y, bz6) + if sqrt(y, y) { + p.Set(&PointG1{*x, *y, *z}) + break + } + } + if !g.IsOnCurve(p) { + panic("rand point must be on curve") } - return g.MulScalar(&PointG1{}, g.one(), k) + if g.InCorrectSubgroup(p) { + panic("rand point must be out of correct subgroup") + } + return p +} + +func (g *G1) randCorrect() *PointG1 { + p := g.ClearCofactor(g.rand()) + if !g.InCorrectSubgroup(p) { + panic("must be in correct subgroup") + } + return p +} + +func (g *G1) randAffine() *PointG1 { + return g.Affine(g.randCorrect()) }   func TestG1Serialization(t *testing.T) { - g1 := NewG1() + var err error + g := NewG1() + zero := g.Zero() + b0 := g.ToBytes(zero) + p0, err := g.FromBytes(b0) + if err != nil { + t.Fatal(err) + } + if !g.IsZero(p0) { + t.Fatal("infinity serialization failed") + } for i := 0; i < fuz; i++ { - a := g1.rand() - buf := g1.ToBytes(a) - b, err := g1.FromBytes(buf) + a := g.randAffine() + uncompressed := g.ToBytes(a) + b, err := g.FromBytes(uncompressed) if err != nil { t.Fatal(err) } - if !g1.Equal(a, b) { - t.Fatal("bad serialization from/to") + if !g.Equal(a, b) { + t.Fatal("serialization failed") } } for i := 0; i < fuz; i++ { - a := g1.rand() - encoded := g1.EncodePoint(a) - b, err := g1.DecodePoint(encoded) + a := g.rand() + encoded := g.EncodePoint(a) + b, err := g.DecodePoint(encoded) if err != nil { t.Fatal(err) } - if !g1.Equal(a, b) { + if !g.Equal(a, b) { t.Fatal("bad serialization encode/decode") } } @@ -66,6 +102,26 @@ t.Fatal("(1, 1) is not on curve") } }   +func TestG1BatchAffine(t *testing.T) { + n := 20 + g := NewG1() + points0 := make([]*PointG1, n) + points1 := make([]*PointG1, n) + for i := 0; i < n; i++ { + points0[i] = g.rand() + points1[i] = g.New().Set(points0[i]) + if g.IsAffine(points0[i]) { + t.Fatal("expect non affine point") + } + } + g.AffineBatch(points0) + for i := 0; i < n; i++ { + if !g.Equal(points0[i], points1[i]) { + t.Fatal("batch affine failed") + } + } +} + func TestG1AdditiveProperties(t *testing.T) { g := NewG1() t0, t1 := g.New(), g.New() @@ -135,19 +191,71 @@ } } }   +func TestG1MixedAdd(t *testing.T) { + g := NewG1() + for i := 0; i < fuz; i++ { + a, b := g.rand(), g.rand() + if g.IsAffine(a) || g.IsAffine(b) { + t.Fatal("expect non affine points") + } + bAffine := g.New().Set(b) + g.Affine(bAffine) + r0, r1 := g.New(), g.New() + g.Add(r0, a, b) + g.AddMixed(r1, a, bAffine) + if !g.Equal(r0, r1) { + t.Fatal("mixed addition failed") + } + aAffine := g.New().Set(a) + g.Affine(aAffine) + g.AddMixed(r0, a, aAffine) + g.Double(r1, a) + if !g.Equal(r0, r1) { + t.Fatal("mixed addition must double where points are equal") + } + } +} + +func TestG1MultiplicationCross(t *testing.T) { + g := NewG1() + for i := 0; i < fuz; i++ { + + a := g.randCorrect() + s := randScalar(q) + res0, res1, res2, res3 := g.New(), g.New(), g.New(), g.New() + + g.mulScalar(res0, a, s) + g.glvMul(res1, a, s) + g.wnafMul(res2, a, s) + g.MultiExp(res3, []*PointG1{a}, []*big.Int{s}) + + if !g.Equal(res0, res1) { + t.Fatal("cross multiplication failed (glv)", i) + } + if !g.Equal(res0, res2) { + t.Fatal("cross multiplication failed (wnaf)", i) + } + if !g.Equal(res0, res3) { + t.Fatal("cross multiplication failed (multiexp)", i) + } + } +} + func TestG1MultiplicativeProperties(t *testing.T) { g := NewG1() t0, t1 := g.New(), g.New() zero := g.Zero() + one := new(big.Int).SetUint64(1) for i := 0; i < fuz; i++ { - a := g.rand() - s1, s2, s3 := randScalar(q), randScalar(q), randScalar(q) - sone := big.NewInt(1) + a := g.randCorrect() + s1 := randScalar(q) + s2 := randScalar(q) + s3 := randScalar(q) g.MulScalar(t0, zero, s1) if !g.Equal(t0, zero) { t.Fatal(" 0 ^ s == 0") } - g.MulScalar(t0, a, sone) + g.MulScalar(t0, a, one) if !g.Equal(t0, a) { t.Fatal(" a ^ 1 == a") } @@ -160,7 +268,7 @@ g.MulScalar(t0, t0, s2) s3.Mul(s1, s2) g.MulScalar(t1, a, s3) if !g.Equal(t0, t1) { - t.Errorf(" (a ^ s1) ^ s2 == a ^ (s1 * s2)") + t.Fatal(" (a ^ s1) ^ s2 == a ^ (s1 * s2)") } g.MulScalar(t0, a, s1) g.MulScalar(t1, a, s2) @@ -168,7 +276,7 @@ g.Add(t0, t0, t1) s3.Add(s1, s2) g.MulScalar(t1, a, s3) if !g.Equal(t0, t1) { - t.Errorf(" (a ^ s1) + (a ^ s2) == a ^ (s1 + s2)") + t.Fatal(" (a ^ s1) + (a ^ s2) == a ^ (s1 + s2)") } } } @@ -178,40 +286,54 @@ g := NewG1() one := g.one() var scalars [2]*big.Int var bases [2]*PointG1 - scalars[0] = big.NewInt(2) - scalars[1] = big.NewInt(3) + scalars[0] = new(big.Int).SetUint64(2) + scalars[1] = new(big.Int).SetUint64(3) bases[0], bases[1] = new(PointG1).Set(one), new(PointG1).Set(one) expected, result := g.New(), g.New() - g.MulScalar(expected, one, big.NewInt(5)) + g.mulScalar(expected, one, new(big.Int).SetUint64(5)) _, _ = g.MultiExp(result, bases[:], scalars[:]) if !g.Equal(expected, result) { - t.Fatal("bad multi-exponentiation") + t.Fatal("multi-exponentiation failed") } }   -func TestG1MultiExpBatch(t *testing.T) { +func TestG1MultiExp(t *testing.T) { g := NewG1() - one := g.one() - n := 1000 - bases := make([]*PointG1, n) - scalars := make([]*big.Int, n) - // scalars: [s0,s1 ... s(n-1)] - // bases: [P0,P1,..P(n-1)] = [s(n-1)*G, s(n-2)*G ... s0*G] - for i, j := 0, n-1; i < n; i, j = i+1, j-1 { - scalars[j], _ = rand.Int(rand.Reader, big.NewInt(100000)) - bases[i] = g.New() - g.MulScalar(bases[i], one, scalars[j]) - } - // expected: s(n-1)*P0 + s(n-2)*P1 + s0*P(n-1) - expected, tmp := g.New(), g.New() - for i := 0; i < n; i++ { - g.MulScalar(tmp, bases[i], scalars[i]) - g.Add(expected, expected, tmp) + for n := 1; n < 1024+1; n = n * 2 { + bases := make([]*PointG1, n) + scalars := make([]*big.Int, n) + var err error + for i := 0; i < n; i++ { + scalars[i] = randScalar(q) + if err != nil { + t.Fatal(err) + } + bases[i] = g.randAffine() + } + expected, tmp := g.New(), g.New() + for i := 0; i < n; i++ { + g.mulScalar(tmp, bases[i], scalars[i]) + g.Add(expected, expected, tmp) + } + result := g.New() + _, _ = g.MultiExp(result, bases, scalars) + if !g.Equal(expected, result) { + t.Fatal("multi-exponentiation failed") + } } - result := g.New() - _, _ = g.MultiExp(result, bases, scalars) - if !g.Equal(expected, result) { - t.Fatal("bad multi-exponentiation") +} + +func TestG1ClearCofactor(t *testing.T) { + g := NewG1() + for i := 0; i < fuz; i++ { + p0 := g.rand() + if g.InCorrectSubgroup(p0) { + t.Fatal("rand point should be out of correct subgroup") + } + g.ClearCofactor(p0) + if !g.InCorrectSubgroup(p0) { + t.Fatal("cofactor clearing is failed") + } } }   @@ -221,24 +343,39 @@ u []byte expected []byte }{ { - u: make([]byte, 48), - expected: common.FromHex("11a9a0372b8f332d5c30de9ad14e50372a73fa4c45d5f2fa5097f2d6fb93bcac592f2e1711ac43db0519870c7d0ea415" + "092c0f994164a0719f51c24ba3788de240ff926b55f58c445116e8bc6a47cd63392fd4e8e22bdf9feaa96ee773222133"), + u: make([]byte, fpByteSize), + expected: fromHex(-1, + "11a9a0372b8f332d5c30de9ad14e50372a73fa4c45d5f2fa5097f2d6fb93bcac592f2e1711ac43db0519870c7d0ea415", + "092c0f994164a0719f51c24ba3788de240ff926b55f58c445116e8bc6a47cd63392fd4e8e22bdf9feaa96ee773222133", + ), }, { - u: common.FromHex("07fdf49ea58e96015d61f6b5c9d1c8f277146a533ae7fbca2a8ef4c41055cd961fbc6e26979b5554e4b4f22330c0e16d"), - expected: common.FromHex("1223effdbb2d38152495a864d78eee14cb0992d89a241707abb03819a91a6d2fd65854ab9a69e9aacb0cbebfd490732c" + "0f925d61e0b235ecd945cbf0309291878df0d06e5d80d6b84aa4ff3e00633b26f9a7cb3523ef737d90e6d71e8b98b2d5"), + u: fromHex(-1, "07fdf49ea58e96015d61f6b5c9d1c8f277146a533ae7fbca2a8ef4c41055cd961fbc6e26979b5554e4b4f22330c0e16d"), + expected: fromHex(-1, + "1223effdbb2d38152495a864d78eee14cb0992d89a241707abb03819a91a6d2fd65854ab9a69e9aacb0cbebfd490732c", + "0f925d61e0b235ecd945cbf0309291878df0d06e5d80d6b84aa4ff3e00633b26f9a7cb3523ef737d90e6d71e8b98b2d5", + ), }, { - u: common.FromHex("1275ab3adbf824a169ed4b1fd669b49cf406d822f7fe90d6b2f8c601b5348436f89761bb1ad89a6fb1137cd91810e5d2"), - expected: common.FromHex("179d3fd0b4fb1da43aad06cea1fb3f828806ddb1b1fa9424b1e3944dfdbab6e763c42636404017da03099af0dcca0fd6" + "0d037cb1c6d495c0f5f22b061d23f1be3d7fe64d3c6820cfcd99b6b36fa69f7b4c1f4addba2ae7aa46fb25901ab483e4"), + u: fromHex(-1, "1275ab3adbf824a169ed4b1fd669b49cf406d822f7fe90d6b2f8c601b5348436f89761bb1ad89a6fb1137cd91810e5d2"), + expected: fromHex(-1, + "179d3fd0b4fb1da43aad06cea1fb3f828806ddb1b1fa9424b1e3944dfdbab6e763c42636404017da03099af0dcca0fd6", + "0d037cb1c6d495c0f5f22b061d23f1be3d7fe64d3c6820cfcd99b6b36fa69f7b4c1f4addba2ae7aa46fb25901ab483e4", + ), }, { - u: common.FromHex("0e93d11d30de6d84b8578827856f5c05feef36083eef0b7b263e35ecb9b56e86299614a042e57d467fa20948e8564909"), - expected: common.FromHex("15aa66c77eded1209db694e8b1ba49daf8b686733afaa7b68c683d0b01788dfb0617a2e2d04c0856db4981921d3004af" + "0952bb2f61739dd1d201dd0a79d74cda3285403d47655ee886afe860593a8a4e51c5b77a22d2133e3a4280eaaaa8b788"), + u: fromHex(-1, "0e93d11d30de6d84b8578827856f5c05feef36083eef0b7b263e35ecb9b56e86299614a042e57d467fa20948e8564909"), + expected: fromHex(-1, + "15aa66c77eded1209db694e8b1ba49daf8b686733afaa7b68c683d0b01788dfb0617a2e2d04c0856db4981921d3004af", + "0952bb2f61739dd1d201dd0a79d74cda3285403d47655ee886afe860593a8a4e51c5b77a22d2133e3a4280eaaaa8b788", + ), }, { - u: common.FromHex("015a41481155d17074d20be6d8ec4d46632a51521cd9c916e265bd9b47343b3689979b50708c8546cbc2916b86cb1a3a"), - expected: common.FromHex("06328ce5106e837935e8da84bd9af473422e62492930aa5f460369baad9545defa468d9399854c23a75495d2a80487ee" + "094bfdfe3e552447433b5a00967498a3f1314b86ce7a7164c8a8f4131f99333b30a574607e301d5f774172c627fd0bca"), + u: fromHex(-1, "015a41481155d17074d20be6d8ec4d46632a51521cd9c916e265bd9b47343b3689979b50708c8546cbc2916b86cb1a3a"), + expected: fromHex(-1, + "06328ce5106e837935e8da84bd9af473422e62492930aa5f460369baad9545defa468d9399854c23a75495d2a80487ee", + "094bfdfe3e552447433b5a00967498a3f1314b86ce7a7164c8a8f4131f99333b30a574607e301d5f774172c627fd0bca", + ), }, } { g := NewG1() @@ -252,30 +389,199 @@ } } }   +func TestG1EncodeToCurve(t *testing.T) { + domain := []byte("BLS12381G1_XMD:SHA-256_SSWU_NU_TESTGEN") + for i, v := range []struct { + msg []byte + expected []byte + }{ + { + msg: []byte(""), + expected: fromHex(-1, + "1223effdbb2d38152495a864d78eee14cb0992d89a241707abb03819a91a6d2fd65854ab9a69e9aacb0cbebfd490732c", + "0f925d61e0b235ecd945cbf0309291878df0d06e5d80d6b84aa4ff3e00633b26f9a7cb3523ef737d90e6d71e8b98b2d5", + ), + }, + { + msg: []byte("abc"), + expected: fromHex(-1, + "179d3fd0b4fb1da43aad06cea1fb3f828806ddb1b1fa9424b1e3944dfdbab6e763c42636404017da03099af0dcca0fd6", + "0d037cb1c6d495c0f5f22b061d23f1be3d7fe64d3c6820cfcd99b6b36fa69f7b4c1f4addba2ae7aa46fb25901ab483e4", + ), + }, + { + msg: []byte("abcdef0123456789"), + expected: fromHex(-1, + "15aa66c77eded1209db694e8b1ba49daf8b686733afaa7b68c683d0b01788dfb0617a2e2d04c0856db4981921d3004af", + "0952bb2f61739dd1d201dd0a79d74cda3285403d47655ee886afe860593a8a4e51c5b77a22d2133e3a4280eaaaa8b788", + ), + }, + { + msg: []byte("a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + expected: fromHex(-1, + "06328ce5106e837935e8da84bd9af473422e62492930aa5f460369baad9545defa468d9399854c23a75495d2a80487ee", + "094bfdfe3e552447433b5a00967498a3f1314b86ce7a7164c8a8f4131f99333b30a574607e301d5f774172c627fd0bca", + ), + }, + } { + g := NewG1() + p0, err := g.EncodeToCurve(v.msg, domain) + if err != nil { + t.Fatal("encode to point fails", i, err) + } + if !bytes.Equal(g.ToBytes(p0), v.expected) { + t.Fatal("encode to point fails", i) + } + } +} + +func TestG1HashToCurve(t *testing.T) { + domain := []byte("BLS12381G1_XMD:SHA-256_SSWU_RO_TESTGEN") + for i, v := range []struct { + msg []byte + expected []byte + }{ + { + msg: []byte(""), + expected: fromHex(-1, + "0576730ab036cbac1d95b38dca905586f28d0a59048db4e8778782d89bff856ddef89277ead5a21e2975c4a6e3d8c79e", + "1273e568bebf1864393c517f999b87c1eaa1b8432f95aea8160cd981b5b05d8cd4a7cf00103b6ef87f728e4b547dd7ae", + ), + }, + { + msg: []byte("abc"), + expected: fromHex(-1, + "061daf0cc00d8912dac1d4cf5a7c32fca97f8b3bf3f805121888e5eb89f77f9a9f406569027ac6d0e61b1229f42c43d6", + "0de1601e5ba02cb637c1d35266f5700acee9850796dc88e860d022d7b9e7e3dce5950952e97861e5bb16d215c87f030d", + ), + }, + { + msg: []byte("abcdef0123456789"), + expected: fromHex(-1, + "0fb3455436843e76079c7cf3dfef75e5a104dfe257a29a850c145568d500ad31ccfe79be9ae0ea31a722548070cf98cd", + "177989f7e2c751658df1b26943ee829d3ebcf131d8f805571712f3a7527ee5334ecff8a97fc2a50cea86f5e6212e9a57", + ), + }, + { + msg: []byte("a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + expected: fromHex(-1, + "0514af2137c1ae1d78d5cb97ee606ea142824c199f0f25ac463a0c78200de57640d34686521d3e9cf6b3721834f8a038", + "047a85d6898416a0899e26219bca7c4f0fa682717199de196b02b95eaf9fb55456ac3b810e78571a1b7f5692b7c58ab6", + ), + }, + } { + g := NewG1() + p0, err := g.HashToCurve(v.msg, domain) + if err != nil { + t.Fatal("hash to point fails", i, err) + } + if !bytes.Equal(g.ToBytes(p0), v.expected) { + t.Fatal("hash to point fails", i) + } + } +} + func BenchmarkG1Add(t *testing.B) { - g1 := NewG1() - a, b, c := g1.rand(), g1.rand(), PointG1{} + g := NewG1() + a, b, c := g.rand(), g.rand(), PointG1{} + t.ResetTimer() + for i := 0; i < t.N; i++ { + g.Add(&c, a, b) + } +} + +func BenchmarkG1MulWNAF(t *testing.B) { + g := NewG1() + p := new(PointG1).Set(&g1One) + s := randScalar(q) + res := new(PointG1) + t.Run("Naive", func(t *testing.B) { + t.ResetTimer() + for i := 0; i < t.N; i++ { + g.mulScalar(res, p, s) + } + }) + for i := 1; i < 8; i++ { + wnafMulWindowG1 = uint(i) + t.Run(fmt.Sprintf("window: %d", i), func(t *testing.B) { + t.ResetTimer() + for i := 0; i < t.N; i++ { + g.wnafMul(res, p, s) + } + }) + } +} + +func BenchmarkG1MulGLV(t *testing.B) { + + g := NewG1() + p := new(PointG1).Set(&g1One) + s := randScalar(q) + res := new(PointG1) + t.Run("Naive", func(t *testing.B) { + t.ResetTimer() + for i := 0; i < t.N; i++ { + g.mulScalar(res, p, s) + } + }) + for i := 1; i < 8; i++ { + glvMulWindowG1 = uint(i) + t.Run(fmt.Sprintf("window: %d", i), func(t *testing.B) { + t.ResetTimer() + for i := 0; i < t.N; i++ { + g.glvMul(res, p, s) + } + }) + } +} + +func BenchmarkG1MultiExp(t *testing.B) { + g := NewG1() + v := func(n int) ([]*PointG1, []*big.Int) { + bases := make([]*PointG1, n) + scalars := make([]*big.Int, n) + for i := 0; i < n; i++ { + scalars[i] = randScalar(q) + bases[i] = g.randAffine() + } + return bases, scalars + } + for _, i := range []int{1, 2, 10, 100, 1000} { + t.Run(fmt.Sprint(i), func(t *testing.B) { + bases, scalars := v(i) + result := g.New() + t.ResetTimer() + for i := 0; i < t.N; i++ { + _, _ = g.MultiExp(result, bases, scalars) + } + }) + } +} + +func BenchmarkG1ClearCofactor(t *testing.B) { + g := NewG1() + a := g.rand() t.ResetTimer() for i := 0; i < t.N; i++ { - g1.Add(&c, a, b) + g.ClearCofactor(a) } }   -func BenchmarkG1Mul(t *testing.B) { - g1 := NewG1() - a, e, c := g1.rand(), q, PointG1{} +func BenchmarkG1SubgroupCheck(t *testing.B) { + g := NewG1() + a := g.rand() t.ResetTimer() for i := 0; i < t.N; i++ { - g1.MulScalar(&c, a, e) + g.InCorrectSubgroup(a) } }   func BenchmarkG1MapToCurve(t *testing.B) { - a := make([]byte, 48) - g1 := NewG1() + a := fromHex(fpByteSize, "0x1234") + g := NewG1() t.ResetTimer() for i := 0; i < t.N; i++ { - _, err := g1.MapToCurve(a) + _, err := g.MapToCurve(a) if err != nil { t.Fatal(err) }
diff --git go-ethereum/crypto/bls12381/field_element.go celo/crypto/bls12381/field_element.go index 9fdddc618435cb614ff9ab5a66abe25db8da6a58..bbc92851c19a27dd48ad183bf468a90e79fe4f44 100644 --- go-ethereum/crypto/bls12381/field_element.go +++ celo/crypto/bls12381/field_element.go @@ -1,19 +1,3 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   import ( @@ -25,31 +9,30 @@ "math/big" )   // fe is base field element representation -type fe [6]uint64 +type fe /*** ***/ [fpNumberOfLimbs]uint64   // fe2 is element representation of 'fp2' which is quadratic extension of base field 'fp' // Representation follows c[0] + c[1] * u encoding order. -type fe2 [2]fe +type fe2 /** ***/ [2]fe   // fe6 is element representation of 'fp6' field which is cubic extension of 'fp2' // Representation follows c[0] + c[1] * v + c[2] * v^2 encoding order. -type fe6 [3]fe2 +type fe6 /** ***/ [3]fe2   // fe12 is element representation of 'fp12' field which is quadratic extension of 'fp6' // Representation follows c[0] + c[1] * w encoding order. -type fe12 [2]fe6 +type fe12 /** ***/ [2]fe6   func (fe *fe) setBytes(in []byte) *fe { - size := 48 l := len(in) - if l >= size { - l = size + if l >= fpByteSize { + l = fpByteSize } - padded := make([]byte, size) - copy(padded[size-l:], in[:]) + padded := make([]byte, fpByteSize) + copy(padded[fpByteSize-l:], in[:]) var a int - for i := 0; i < 6; i++ { - a = size - i*8 + for i := 0; i < fpNumberOfLimbs; i++ { + a = fpByteSize - i*8 fe[i] = uint64(padded[a-1]) | uint64(padded[a-2])<<8 | uint64(padded[a-3])<<16 | uint64(padded[a-4])<<24 | uint64(padded[a-5])<<32 | uint64(padded[a-6])<<40 | @@ -84,10 +67,10 @@ return fe }   func (fe *fe) bytes() []byte { - out := make([]byte, 48) + out := make([]byte, fpByteSize) var a int - for i := 0; i < 6; i++ { - a = 48 - i*8 + for i := 0; i < fpNumberOfLimbs; i++ { + a = fpByteSize - i*8 out[a-1] = byte(fe[i]) out[a-2] = byte(fe[i] >> 8) out[a-3] = byte(fe[i] >> 16) @@ -105,7 +88,7 @@ return new(big.Int).SetBytes(fe.bytes()) }   func (fe *fe) string() (s string) { - for i := 5; i >= 0; i-- { + for i := fpNumberOfLimbs - 1; i >= 0; i-- { s = fmt.Sprintf("%s%16.16x", s, fe[i]) } return "0x" + s @@ -134,7 +117,7 @@ return fe.setBig(bi), nil }   func (fe *fe) isValid() bool { - return fe.cmp(&modulus) < 0 + return fe.cmp(&modulus) == -1 }   func (fe *fe) isOdd() bool { @@ -156,7 +139,7 @@ return fe.equal(r1) }   func (fe *fe) cmp(fe2 *fe) int { - for i := 5; i >= 0; i-- { + for i := fpNumberOfLimbs - 1; i >= 0; i-- { if fe[i] > fe2[i] { return 1 } else if fe[i] < fe2[i] { @@ -176,24 +159,24 @@ fromMont(r, e) return r[0]&1 == 0 }   -func (fe *fe) div2(e uint64) { - fe[0] = fe[0]>>1 | fe[1]<<63 - fe[1] = fe[1]>>1 | fe[2]<<63 - fe[2] = fe[2]>>1 | fe[3]<<63 - fe[3] = fe[3]>>1 | fe[4]<<63 - fe[4] = fe[4]>>1 | fe[5]<<63 - fe[5] = fe[5]>>1 | e<<63 +func (e *fe) div2(u uint64) { + e[0] = e[0]>>1 | e[1]<<63 + e[1] = e[1]>>1 | e[2]<<63 + e[2] = e[2]>>1 | e[3]<<63 + e[3] = e[3]>>1 | e[4]<<63 + e[4] = e[4]>>1 | e[5]<<63 + e[5] = e[5]>>1 | u<<63 }   -func (fe *fe) mul2() uint64 { - e := fe[5] >> 63 - fe[5] = fe[5]<<1 | fe[4]>>63 - fe[4] = fe[4]<<1 | fe[3]>>63 - fe[3] = fe[3]<<1 | fe[2]>>63 - fe[2] = fe[2]<<1 | fe[1]>>63 - fe[1] = fe[1]<<1 | fe[0]>>63 - fe[0] = fe[0] << 1 - return e +func (e *fe) mul2() uint64 { + u := e[5] >> 63 + e[5] = e[5]<<1 | e[4]>>63 + e[4] = e[4]<<1 | e[3]>>63 + e[3] = e[3]<<1 | e[2]>>63 + e[2] = e[2]<<1 | e[1]>>63 + e[1] = e[1]<<1 | e[0]>>63 + e[0] = e[0] << 1 + return u }   func (e *fe2) zero() *fe2 {
diff --git go-ethereum/crypto/bls12381/fp_test.go celo/crypto/bls12381/fp_test.go index 97528d9db32ea3b49ca77bf2d83838570e979dc6..bcb12fad2c57994715aa6b1d0924fbb40e9164de 100644 --- go-ethereum/crypto/bls12381/fp_test.go +++ celo/crypto/bls12381/fp_test.go @@ -9,16 +9,16 @@ )   func TestFpSerialization(t *testing.T) { t.Run("zero", func(t *testing.T) { - in := make([]byte, 48) + in := make([]byte, fpByteSize) fe, err := fromBytes(in) if err != nil { t.Fatal(err) } if !fe.isZero() { - t.Fatal("bad serialization") + t.Fatal("serialization failed") } if !bytes.Equal(in, toBytes(fe)) { - t.Fatal("bad serialization") + t.Fatal("serialization failed") } }) t.Run("bytes", func(t *testing.T) { @@ -29,7 +29,7 @@ if err != nil { t.Fatal(err) } if !a.equal(b) { - t.Fatal("bad serialization") + t.Fatal("serialization failed") } } }) @@ -41,7 +41,7 @@ if err != nil { t.Fatal(err) } if !a.equal(b) { - t.Fatal("bad encoding or decoding") + t.Fatal("encoding or decoding failed") } } }) @@ -53,7 +53,7 @@ if err != nil { t.Fatal(err) } if !a.equal(b) { - t.Fatal("bad encoding or decoding") + t.Fatal("encoding or decoding failed") } } }) @@ -64,32 +64,32 @@ for i := 0; i < fuz; i++ { a, _ := new(fe).rand(rand.Reader) b, _ := new(fe).rand(rand.Reader) c := new(fe) - big_a := toBig(a) - big_b := toBig(b) + big_a := a.big() + big_b := b.big() big_c := new(big.Int) add(c, a, b) - out_1 := toBytes(c) - out_2 := padBytes(big_c.Add(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), 48) + out_1 := c.bytes() + out_2 := padBytes(big_c.Add(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), fpByteSize) if !bytes.Equal(out_1, out_2) { - t.Fatal("cross test against big.Int is not satisfied A") + t.Fatal("cross test against big.Int is failed A") } double(c, a) - out_1 = toBytes(c) - out_2 = padBytes(big_c.Add(big_a, big_a).Mod(big_c, modulus.big()).Bytes(), 48) + out_1 = c.bytes() + out_2 = padBytes(big_c.Add(big_a, big_a).Mod(big_c, modulus.big()).Bytes(), fpByteSize) if !bytes.Equal(out_1, out_2) { - t.Fatal("cross test against big.Int is not satisfied B") + t.Fatal("cross test against big.Int is failed B") } sub(c, a, b) - out_1 = toBytes(c) - out_2 = padBytes(big_c.Sub(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), 48) + out_1 = c.bytes() + out_2 = padBytes(big_c.Sub(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), fpByteSize) if !bytes.Equal(out_1, out_2) { - t.Fatal("cross test against big.Int is not satisfied C") + t.Fatal("cross test against big.Int is failed C") } neg(c, a) - out_1 = toBytes(c) - out_2 = padBytes(big_c.Neg(big_a).Mod(big_c, modulus.big()).Bytes(), 48) + out_1 = c.bytes() + out_2 = padBytes(big_c.Neg(big_a).Mod(big_c, modulus.big()).Bytes(), fpByteSize) if !bytes.Equal(out_1, out_2) { - t.Fatal("cross test against big.Int is not satisfied D") + t.Fatal("cross test against big.Int is failed D") } } } @@ -98,35 +98,36 @@ func TestFpAdditionCrossAgainstBigIntAssigned(t *testing.T) { for i := 0; i < fuz; i++ { a, _ := new(fe).rand(rand.Reader) b, _ := new(fe).rand(rand.Reader) - big_a, big_b := toBig(a), toBig(b) + big_a, big_b := a.big(), b.big() addAssign(a, b) - out_1 := toBytes(a) - out_2 := padBytes(big_a.Add(big_a, big_b).Mod(big_a, modulus.big()).Bytes(), 48) + out_1 := a.bytes() + out_2 := padBytes(big_a.Add(big_a, big_b).Mod(big_a, modulus.big()).Bytes(), fpByteSize) if !bytes.Equal(out_1, out_2) { - t.Fatal("cross test against big.Int is not satisfied A") + t.Fatal("cross test against big.Int is failed A") } a, _ = new(fe).rand(rand.Reader) - big_a = toBig(a) + big_a = a.big() doubleAssign(a) - out_1 = toBytes(a) - out_2 = padBytes(big_a.Add(big_a, big_a).Mod(big_a, modulus.big()).Bytes(), 48) + out_1 = a.bytes() + out_2 = padBytes(big_a.Add(big_a, big_a).Mod(big_a, modulus.big()).Bytes(), fpByteSize) if !bytes.Equal(out_1, out_2) { - t.Fatal("cross test against big.Int is not satisfied B") + t.Fatal("cross test against big.Int is failed B") } a, _ = new(fe).rand(rand.Reader) b, _ = new(fe).rand(rand.Reader) - big_a, big_b = toBig(a), toBig(b) + big_a, big_b = a.big(), b.big() subAssign(a, b) - out_1 = toBytes(a) - out_2 = padBytes(big_a.Sub(big_a, big_b).Mod(big_a, modulus.big()).Bytes(), 48) + out_1 = a.bytes() + out_2 = padBytes(big_a.Sub(big_a, big_b).Mod(big_a, modulus.big()).Bytes(), fpByteSize) if !bytes.Equal(out_1, out_2) { - t.Fatal("cross test against big.Int is not satisfied A") + t.Fatal("cross test against big.Int is failed A") } } }   func TestFpAdditionProperties(t *testing.T) { for i := 0; i < fuz; i++ { + zero := new(fe).zero() a, _ := new(fe).rand(rand.Reader) b, _ := new(fe).rand(rand.Reader) @@ -266,26 +267,6 @@ for i := 0; i < fuz; i++ { a, _ := new(fe).rand(rand.Reader) b, _ := new(fe).rand(rand.Reader) c, _ := new(fe).rand(rand.Reader) - c0 := new(fe) - c1 := new(fe) - ladd(c0, a, b) - add(c1, a, b) - mul(c0, c0, c) - mul(c1, c1, c) - if !c0.equal(c1) { - // l+ operator stands for lazy addition - t.Fatal("(a + b) * c == (a l+ b) * c") - } - _, _ = a.rand(rand.Reader) - b.set(a) - ldouble(a, a) - ladd(b, b, b) - if !a.equal(b) { - t.Fatal("2 l* a = a l+ a") - } - _, _ = a.rand(rand.Reader) - _, _ = b.rand(rand.Reader) - _, _ = c.rand(rand.Reader) a0 := new(fe).set(a) lsubAssign(a, b) laddAssign(a, &modulus) @@ -308,9 +289,9 @@ big_b := toBig(b) big_c := new(big.Int) mul(c, a, b) out_1 := toBytes(c) - out_2 := padBytes(big_c.Mul(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), 48) + out_2 := padBytes(big_c.Mul(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), fpByteSize) if !bytes.Equal(out_1, out_2) { - t.Fatal("cross test against big.Int is not satisfied") + t.Fatal("cross test against big.Int is failed") } } } @@ -419,21 +400,49 @@ } } }   +func TestFpBatchInversion(t *testing.T) { + n := 20 + for i := 0; i < n; i++ { + e0 := make([]fe, n) + e1 := make([]fe, n) + for j := 0; j < n; j++ { + if j != i { + e, err := new(fe).rand(rand.Reader) + if err != nil { + t.Fatal(err) + } + e0[j].set(e) + } + inverse(&e1[j], &e0[j]) + } + + inverseBatch(e0) + for j := 0; j < n; j++ { + if !e0[j].equal(&e1[j]) { + t.Fatal("batch inversion failed") + } + } + } +} + func TestFpSquareRoot(t *testing.T) { - r := new(fe) - if sqrt(r, nonResidue1) { + if sqrt(new(fe), nonResidue1) { t.Fatal("non residue cannot have a sqrt") } for i := 0; i < fuz; i++ { a, _ := new(fe).rand(rand.Reader) - aa, rr, r := &fe{}, &fe{}, &fe{} - square(aa, a) - if !sqrt(r, aa) { - t.Fatal("bad sqrt 1") + r0, r1 := new(fe), new(fe) + d0 := sqrt(r0, a) + d1 := _sqrt(r1, a) + if d0 != d1 { + t.Fatal("sqrt decision failed") } - square(rr, r) - if !rr.equal(aa) { - t.Fatal("bad sqrt 2") + if d0 { + square(r0, r0) + square(r1, r1) + if !r0.equal(r1) { + t.Fatal("sqrt failed") + } } } } @@ -451,7 +460,7 @@ } for i := 0; i < fuz; i++ { a, _ := new(fe).rand(rand.Reader) square(a, a) - if isQuadraticNonResidue(new(fe).one()) { + if isQuadraticNonResidue(a) { t.Fatal("element is not quadratic non residue") } } @@ -477,7 +486,7 @@ if err != nil { t.Fatal(err) } if !a.equal(b) { - t.Fatal("bad serialization") + t.Fatal("serialization failed") } } } @@ -621,32 +630,6 @@ } } }   -func TestFp2LazyOperations(t *testing.T) { - field := newFp2() - for i := 0; i < fuz; i++ { - a, _ := new(fe2).rand(rand.Reader) - b, _ := new(fe2).rand(rand.Reader) - c, _ := new(fe2).rand(rand.Reader) - c0 := new(fe2) - c1 := new(fe2) - field.ladd(c0, a, b) - field.add(c1, a, b) - field.mulAssign(c0, c) - field.mulAssign(c1, c) - if !c0.equal(c1) { - // l+ operator stands for lazy addition - t.Fatal("(a + b) * c == (a l+ b) * c") - } - _, _ = a.rand(rand.Reader) - b.set(a) - field.ldouble(a, a) - field.ladd(b, b, b) - if !a.equal(b) { - t.Fatal("2 l* a = a l+ a") - } - } -} - func TestFp2MultiplicationProperties(t *testing.T) { field := newFp2() for i := 0; i < fuz; i++ { @@ -781,42 +764,50 @@ } } }   -func TestFp2SquareRoot(t *testing.T) { +func TestFp2BatchInversion(t *testing.T) { field := newFp2() - for z := 0; z < 1000; z++ { - zi := new(fe) - sub(zi, &modulus, &fe{uint64(z * z)}) - // r = (-z*z, 0) - r := &fe2{*zi, fe{0}} - toMont(&r[0], &r[0]) - toMont(&r[1], &r[1]) - c := field.new() - // sqrt((-z*z, 0)) = (0, z) - if !field.sqrt(c, r) { - t.Fatal("z*z does have a square root") + n := 20 + for i := 0; i < n; i++ { + e0 := make([]fe2, n) + e1 := make([]fe2, n) + for j := 0; j < n; j++ { + if j != i { + e, err := new(fe2).rand(rand.Reader) + if err != nil { + t.Fatal(err) + } + e0[j].set(e) + } + field.inverse(&e1[j], &e0[j]) } - e := &fe2{fe{uint64(0)}, fe{uint64(z)}} - toMont(&e[0], &e[0]) - toMont(&e[1], &e[1]) - field.square(e, e) - field.square(c, c) - if !e.equal(c) { - t.Fatal("square root failed") + field.inverseBatch(e0) + for j := 0; j < n; j++ { + if !e0[j].equal(&e1[j]) { + t.Fatal("batch inversion failed") + } } } - if field.sqrt(field.new(), nonResidue2) { +} + +func TestFp2SquareRoot(t *testing.T) { + e := newFp2() + if e.sqrtBLST(e.new(), nonResidue2) { t.Fatal("non residue cannot have a sqrt") } for i := 0; i < fuz; i++ { a, _ := new(fe2).rand(rand.Reader) - aa, rr, r := field.new(), field.new(), field.new() - field.square(aa, a) - if !field.sqrt(r, aa) { - t.Fatal("bad sqrt 1") + r0, r1 := new(fe2), new(fe2) + d0 := e.sqrt(r0, a) + d1 := e.sqrtBLST(r1, a) + if d0 != d1 { + t.Fatal("sqrt decision failed") } - field.square(rr, r) - if !rr.equal(aa) { - t.Fatal("bad sqrt 2") + if d0 { + e.square(r0, r0) + e.square(r1, r1) + if !r0.equal(r1) { + t.Fatal("sqrt failed") + } } } } @@ -835,7 +826,7 @@ } for i := 0; i < fuz; i++ { a, _ := new(fe2).rand(rand.Reader) field.squareAssign(a) - if field.isQuadraticNonResidue(new(fe2).one()) { + if field.isQuadraticNonResidue(a) { t.Fatal("element is not quadratic non residue") } } @@ -860,7 +851,7 @@ if err != nil { t.Fatal(err) } if !a.equal(b) { - t.Fatal("bad serialization") + t.Fatal("serialization") } } } @@ -1013,9 +1004,9 @@ b, _ = new(fe6).rand(rand.Reader) u, _ = new(fe6).rand(rand.Reader) b[2].zero() fp6.mul(u, a, b) - fp6.mulBy01(a, a, &b[0], &b[1]) + fp6.mul01(a, a, &b[0], &b[1]) if !a.equal(u) { - t.Fatal("bad mul by 01") + t.Fatal("mul by 01") } } for j := 0; j < fuz; j++ { @@ -1025,9 +1016,9 @@ u, _ = new(fe6).rand(rand.Reader) b[2].zero() b[0].zero() fp6.mul(u, a, b) - fp6.mulBy1(a, a, &b[1]) + fp6.mul1(a, a, &b[1]) if !a.equal(u) { - t.Fatal("bad mul by 1") + t.Fatal("mul by 1") } } } @@ -1169,7 +1160,7 @@ if err != nil { t.Fatal(err) } if !a.equal(b) { - t.Fatal("bad serialization") + t.Fatal("serialization") } } } @@ -1329,9 +1320,9 @@ b[0][2].zero() b[1][0].zero() b[1][2].zero() fp12.mul(u, a, b) - fp12.mulBy014Assign(a, &b[0][0], &b[0][1], &b[1][1]) + fp12.mul014(a, &b[0][0], &b[0][1], &b[1][1]) if !a.equal(u) { - t.Fatal("bad mul by 01") + t.Fatal("mul by 01") } } } @@ -1380,6 +1371,125 @@ field.mul(u, u, a) if !u.equal(one) { t.Fatal("(r*a) * r*(a^-1) == r)") } + } +} + +func TestFrobeniusMapping2(t *testing.T) { + f := newFp2() + a, _ := new(fe2).rand(rand.Reader) + b0, b1, b2, b3 := new(fe2), new(fe2), new(fe2), new(fe2) + f.exp(b0, a, modulus.big()) + f.conjugate(b1, a) + b2.set(a) + f.frobeniusMap1(b2) + b3.set(a) + f.frobeniusMap(b3, 1) + if !b0.equal(b3) { + t.Fatal("frobenius map failed") + } + if !b1.equal(b3) { + t.Fatal("frobenius map failed") + } + if !b2.equal(b3) { + t.Fatal("frobenius map failed") + } +} + +func TestFrobeniusMapping6(t *testing.T) { + { + f := newFp2() + z := nonResidue2 + for i := 0; i < 6; i++ { + p, r, e := modulus.big(), new(fe2), big.NewInt(0) + // p ^ i + p.Exp(p, big.NewInt(int64(i)), nil) + // (p ^ i - 1) / 3 + e.Sub(p, big.NewInt(1)).Div(e, big.NewInt(3)) + // r = z ^ (p ^ i - 1) / 3 + f.exp(r, z, e) + if !r.equal(&frobeniusCoeffs61[i]) { + t.Fatalf("bad frobenius fp6 1q coefficient") + } + } + for i := 0; i < 6; i++ { + p, r, e := modulus.big(), new(fe2), big.NewInt(0) + // p ^ i + p.Exp(p, big.NewInt(int64(i)), nil).Mul(p, big.NewInt(2)) + // (2 * p ^ i - 2) / 3 + e.Sub(p, big.NewInt(2)).Div(e, big.NewInt(3)) + // r = z ^ (2 * p ^ i - 2) / 3 + f.exp(r, z, e) + if !r.equal(&frobeniusCoeffs62[i]) { + t.Fatalf("bad frobenius fp6 2q coefficient") + } + } + } + f := newFp6(nil) + r0, r1 := f.new(), f.new() + e, _ := new(fe6).rand(rand.Reader) + r0.set(e) + r1.set(e) + f.frobeniusMap(r1, 1) + f.frobeniusMap1(r0) + if !r0.equal(r1) { + t.Fatalf("frobenius mapping by 1 failed") + } + r0.set(e) + r1.set(e) + f.frobeniusMap(r1, 2) + f.frobeniusMap2(r0) + if !r0.equal(r1) { + t.Fatalf("frobenius mapping by 2 failed") + } + r0.set(e) + r1.set(e) + f.frobeniusMap(r1, 3) + f.frobeniusMap3(r0) + if !r0.equal(r1) { + t.Fatalf("frobenius mapping by 3 failed") + } +} + +func TestFrobeniusMapping12(t *testing.T) { + { + f := newFp2() + z := nonResidue2 + for i := 0; i < 12; i++ { + p, r, e := modulus.big(), new(fe2), big.NewInt(0) + // p ^ i + p.Exp(p, big.NewInt(int64(i)), nil) + // (p ^ i - 1) / 6 + e.Sub(p, big.NewInt(1)).Div(e, big.NewInt(6)) + // r = z ^ (p ^ i - 1) / 6 + f.exp(r, z, e) + if !r.equal(&frobeniusCoeffs12[i]) { + t.Fatalf("bad frobenius fp12 coefficient") + } + } + } + f := newFp12(nil) + r0, r1 := f.new(), f.new() + e, _ := new(fe12).rand(rand.Reader) + p := modulus.big() + f.exp(r0, e, p) + r1.set(e) + f.frobeniusMap1(r1) + if !r0.equal(r1) { + t.Fatalf("frobenius mapping by 1 failed") + } + p.Mul(p, modulus.big()) + f.exp(r0, e, p) + r1.set(e) + f.frobeniusMap2(r1) + if !r0.equal(r1) { + t.Fatalf("frobenius mapping by 2 failed") + } + p.Mul(p, modulus.big()) + f.exp(r0, e, p) + r1.set(e) + f.frobeniusMap3(r1) + if !r0.equal(r1) { + t.Fatalf("frobenius mapping by 2 failed") } }
diff --git go-ethereum/crypto/bls12381/swu.go celo/crypto/bls12381/swu.go index d16b07a42f96a8999343ab1eb677a6e9c7eb7449..c013e8a0d80ab733fb07d25ee1e81bde3f2d2c33 100644 --- go-ethereum/crypto/bls12381/swu.go +++ celo/crypto/bls12381/swu.go @@ -1,19 +1,3 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   // swuMapG1 is implementation of Simplified Shallue-van de Woestijne-Ulas Method @@ -107,7 +91,7 @@ x.set(x2) y2.set(gx2) } y := e.new() - e.sqrt(y, y2) + e.sqrtBLST(y, y2) if y.sign() != u.sign() { e.neg(y, y) }
diff --git go-ethereum/crypto/bls12381/g2.go celo/crypto/bls12381/g2.go index fa110e3edfc5b3d305fc200524f6d0157ab9295b..1473ac18cfc9539e79b817caace4cced5ac86ba0 100644 --- go-ethereum/crypto/bls12381/g2.go +++ celo/crypto/bls12381/g2.go @@ -1,19 +1,3 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   import ( @@ -22,10 +6,11 @@ "math" "math/big" )   -// PointG2 is type for point in G2. -// PointG2 is both used for Affine and Jacobian point representation. -// If z is equal to one the point is considered as in affine form. +// PointG2 is type for point in G2 and used for both affine and Jacobian representation. +// A point is accounted as in affine form if z is equal to one. type PointG2 [3]fe2 + +var wnafMulWindowG2 uint = 6   // Set copies valeus of one point to another. func (p *PointG2) Set(p2 *PointG2) *PointG2 { @@ -35,13 +20,16 @@ p[2].set(&p2[2]) return p }   -// Zero returns G2 point in point at infinity representation func (p *PointG2) Zero() *PointG2 { p[0].zero() p[1].one() p[2].zero() return p +}   +// IsAffine checks a G1 point whether it is in affine form. +func (p *PointG2) IsAffine() bool { + return p[2].isOne() }   type tempG2 struct { @@ -80,33 +68,18 @@ func (g *G2) Q() *big.Int { return new(big.Int).Set(q) }   -func (g *G2) fromBytesUnchecked(in []byte) (*PointG2, error) { - p0, err := g.f.fromBytes(in[:96]) - if err != nil { - return nil, err - } - p1, err := g.f.fromBytes(in[96:]) - if err != nil { - return nil, err - } - p2 := new(fe2).one() - return &PointG2{*p0, *p1, *p2}, nil -} - // FromBytes constructs a new point given uncompressed byte input. -// FromBytes does not take zcash flags into account. -// Byte input expected to be larger than 96 bytes. -// First 192 bytes should be concatenation of x and y values +// Input string expected to be 192 bytes and concatenation of x and y values // Point (0, 0) is considered as infinity. func (g *G2) FromBytes(in []byte) (*PointG2, error) { - if len(in) != 192 { - return nil, errors.New("input string should be equal or larger than 192") + if len(in) != 4*fpByteSize { + return nil, errors.New("input string length must be equal to 192 bytes") } - p0, err := g.f.fromBytes(in[:96]) + p0, err := g.f.fromBytes(in[:2*fpByteSize]) if err != nil { return nil, err } - p1, err := g.f.fromBytes(in[96:]) + p1, err := g.f.fromBytes(in[2*fpByteSize:]) if err != nil { return nil, err } @@ -152,16 +125,15 @@ return g.FromBytes(pointBytes) }   // ToBytes serializes a point into bytes in uncompressed form, -// does not take zcash flags into account, // returns (0, 0) if point is infinity. func (g *G2) ToBytes(p *PointG2) []byte { - out := make([]byte, 192) + out := make([]byte, 4*fpByteSize) if g.IsZero(p) { return out } g.Affine(p) - copy(out[:96], g.f.toBytes(&p[0])) - copy(out[96:], g.f.toBytes(&p[1])) + copy(out[:2*fpByteSize], g.f.toBytes(&p[0])) + copy(out[2*fpByteSize:], g.f.toBytes(&p[1])) return out }   @@ -213,35 +185,32 @@ g.f.square(t[0], &p1[2]) g.f.square(t[1], &p2[2]) g.f.mul(t[2], t[0], &p2[0]) g.f.mul(t[3], t[1], &p1[0]) - g.f.mul(t[0], t[0], &p1[2]) - g.f.mul(t[1], t[1], &p2[2]) - g.f.mul(t[1], t[1], &p1[1]) - g.f.mul(t[0], t[0], &p2[1]) + g.f.mulAssign(t[0], &p1[2]) + g.f.mulAssign(t[1], &p2[2]) + g.f.mulAssign(t[1], &p1[1]) + g.f.mulAssign(t[0], &p2[1]) return t[0].equal(t[1]) && t[2].equal(t[3]) }   -// InCorrectSubgroup checks whether given point is in correct subgroup. -func (g *G2) InCorrectSubgroup(p *PointG2) bool { - tmp := &PointG2{} - g.MulScalar(tmp, p, q) - return g.IsZero(tmp) -} - // IsOnCurve checks a G2 point is on curve. func (g *G2) IsOnCurve(p *PointG2) bool { if g.IsZero(p) { return true } t := g.t - g.f.square(t[0], &p[1]) - g.f.square(t[1], &p[0]) - g.f.mul(t[1], t[1], &p[0]) - g.f.square(t[2], &p[2]) - g.f.square(t[3], t[2]) - g.f.mul(t[2], t[2], t[3]) - g.f.mul(t[2], b2, t[2]) - g.f.add(t[1], t[1], t[2]) - return t[0].equal(t[1]) + g.f.square(t[0], &p[1]) // y^2 + g.f.square(t[1], &p[0]) // x^2 + g.f.mul(t[1], t[1], &p[0]) // x^3 + if p.IsAffine() { + g.f.add(t[1], t[1], b2) // x^2 + b + return t[0].equal(t[1]) // y^2 ?= x^3 + b + } + g.f.square(t[2], &p[2]) // z^2 + g.f.square(t[3], t[2]) // z^4 + g.f.mulAssign(t[2], t[3]) // z^6 + g.f.mulAssign(t[2], b2) // b*z^6 + g.f.addAssign(t[1], t[2]) // x^3 + b * z^6 + return t[0].equal(t[1]) // y^2 ?= x^3 + b * z^6 }   // IsAffine checks a G2 point whether it is in affine form. @@ -251,19 +220,44 @@ }   // Affine calculates affine form of given G2 point. func (g *G2) Affine(p *PointG2) *PointG2 { + return g.affine(p, p) +} + +func (g *G2) affine(r, p *PointG2) *PointG2 { if g.IsZero(p) { - return p + return r.Zero() } if !g.IsAffine(p) { t := g.t - g.f.inverse(t[0], &p[2]) - g.f.square(t[1], t[0]) - g.f.mul(&p[0], &p[0], t[1]) - g.f.mul(t[0], t[0], t[1]) - g.f.mul(&p[1], &p[1], t[0]) - p[2].one() + g.f.inverse(t[0], &p[2]) // z^-1 + g.f.square(t[1], t[0]) // z^-2 + g.f.mulAssign(&r[0], t[1]) // x = x * z^-2 + g.f.mulAssign(t[0], t[1]) // z^-3 + g.f.mulAssign(&r[1], t[0]) // y = y * z^-3 + r[2].one() // z = 1 + } else { + r.Set(p) } - return p + return r +} + +// AffineBatch given multiple of points returns affine representations +func (g *G2) AffineBatch(p []*PointG2) { + inverses := make([]fe2, len(p)) + for i := 0; i < len(p); i++ { + inverses[i].set(&p[i][2]) + } + g.f.inverseBatch(inverses) + t := g.t + for i := 0; i < len(p); i++ { + if !g.IsAffine(p[i]) && !g.IsZero(p[i]) { + g.f.square(t[1], &inverses[i]) + g.f.mulAssign(&p[i][0], t[1]) + g.f.mul(t[0], &inverses[i], t[1]) + g.f.mulAssign(&p[i][1], t[0]) + p[i][2].one() + } + } }   // Add adds two G2 points p1, p2 and assigns the result to point at first argument. @@ -275,42 +269,89 @@ } if g.IsZero(p2) { return r.Set(p1) } + if g.IsAffine(p2) { + return g.AddMixed(r, p1, p2) + } t := g.t - g.f.square(t[7], &p1[2]) - g.f.mul(t[1], &p2[0], t[7]) - g.f.mul(t[2], &p1[2], t[7]) - g.f.mul(t[0], &p2[1], t[2]) - g.f.square(t[8], &p2[2]) - g.f.mul(t[3], &p1[0], t[8]) - g.f.mul(t[4], &p2[2], t[8]) - g.f.mul(t[2], &p1[1], t[4]) + g.f.square(t[7], &p1[2]) // z1z1 + g.f.mul(t[1], &p2[0], t[7]) // u2 = x2 * z1z1 + g.f.mul(t[2], &p1[2], t[7]) // z1z1 * z1 + g.f.mul(t[0], &p2[1], t[2]) // s2 = y2 * z1z1 * z1 + g.f.square(t[8], &p2[2]) // z2z2 + g.f.mul(t[3], &p1[0], t[8]) // u1 = x1 * z2z2 + g.f.mul(t[4], &p2[2], t[8]) // z2z2 * z2 + g.f.mul(t[2], &p1[1], t[4]) // s1 = y1 * z2z2 * z2 if t[1].equal(t[3]) { if t[0].equal(t[2]) { return g.Double(r, p1) } return r.Zero() } - g.f.sub(t[1], t[1], t[3]) - g.f.double(t[4], t[1]) - g.f.square(t[4], t[4]) - g.f.mul(t[5], t[1], t[4]) - g.f.sub(t[0], t[0], t[2]) - g.f.double(t[0], t[0]) - g.f.square(t[6], t[0]) - g.f.sub(t[6], t[6], t[5]) - g.f.mul(t[3], t[3], t[4]) - g.f.double(t[4], t[3]) - g.f.sub(&r[0], t[6], t[4]) - g.f.sub(t[4], t[3], &r[0]) - g.f.mul(t[6], t[2], t[5]) - g.f.double(t[6], t[6]) - g.f.mul(t[0], t[0], t[4]) - g.f.sub(&r[1], t[0], t[6]) - g.f.add(t[0], &p1[2], &p2[2]) - g.f.square(t[0], t[0]) - g.f.sub(t[0], t[0], t[7]) - g.f.sub(t[0], t[0], t[8]) - g.f.mul(&r[2], t[0], t[1]) + g.f.subAssign(t[1], t[3]) // h = u2 - u1 + g.f.double(t[4], t[1]) // 2h + g.f.squareAssign(t[4]) // i = 2h^2 + g.f.mul(t[5], t[1], t[4]) // j = h*i + g.f.subAssign(t[0], t[2]) // s2 - s1 + g.f.doubleAssign(t[0]) // r = 2*(s2 - s1) + g.f.square(t[6], t[0]) // r^2 + g.f.subAssign(t[6], t[5]) // r^2 - j + g.f.mulAssign(t[3], t[4]) // v = u1 * i + g.f.double(t[4], t[3]) // 2*v + g.f.sub(&r[0], t[6], t[4]) // x3 = r^2 - j - 2*v + g.f.sub(t[4], t[3], &r[0]) // v - x3 + g.f.mul(t[6], t[2], t[5]) // s1 * j + g.f.doubleAssign(t[6]) // 2 * s1 * j + g.f.mulAssign(t[0], t[4]) // r * (v - x3) + g.f.sub(&r[1], t[0], t[6]) // y3 = r * (v - x3) - (2 * s1 * j) + g.f.add(t[0], &p1[2], &p2[2]) // z1 + z2 + g.f.squareAssign(t[0]) // (z1 + z2)^2 + g.f.subAssign(t[0], t[7]) // (z1 + z2)^2 - z1z1 + g.f.subAssign(t[0], t[8]) // (z1 + z2)^2 - z1z1 - z2z2 + g.f.mul(&r[2], t[0], t[1]) // z3 = ((z1 + z2)^2 - z1z1 - z2z2) * h + return r +} + +// Add adds two G1 points p1, p2 and assigns the result to point at first argument. +// Expects the second point p2 in affine form. +func (g *G2) AddMixed(r, p1, p2 *PointG2) *PointG2 { + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl + if g.IsZero(p1) { + return r.Set(p2) + } + if g.IsZero(p2) { + return r.Set(p1) + } + t := g.t + g.f.square(t[7], &p1[2]) // z1z1 + g.f.mul(t[1], &p2[0], t[7]) // u2 = x2 * z1z1 + g.f.mul(t[2], &p1[2], t[7]) // z1z1 * z1 + g.f.mul(t[0], &p2[1], t[2]) // s2 = y2 * z1z1 * z1 + + if p1[0].equal(t[1]) && p1[1].equal(t[0]) { + return g.Double(r, p1) + } + + g.f.sub(t[1], t[1], &p1[0]) // h = u2 - x1 + g.f.square(t[2], t[1]) // hh + g.f.double(t[4], t[2]) + g.f.doubleAssign(t[4]) // 4hh + g.f.mul(t[5], t[1], t[4]) // j = h*i + g.f.subAssign(t[0], &p1[1]) // s2 - y1 + g.f.doubleAssign(t[0]) // r = 2*(s2 - y1) + g.f.square(t[6], t[0]) // r^2 + g.f.subAssign(t[6], t[5]) // r^2 - j + g.f.mul(t[3], &p1[0], t[4]) // v = x1 * i + g.f.double(t[4], t[3]) // 2*v + g.f.sub(&r[0], t[6], t[4]) // x3 = r^2 - j - 2*v + g.f.sub(t[4], t[3], &r[0]) // v - x3 + g.f.mul(t[6], &p1[1], t[5]) // y1 * j + g.f.doubleAssign(t[6]) // 2 * y1 * j + g.f.mulAssign(t[0], t[4]) // r * (v - x3) + g.f.sub(&r[1], t[0], t[6]) // y3 = r * (v - x3) - (2 * y1 * j) + g.f.add(t[0], &p1[2], t[1]) // z1 + h + g.f.squareAssign(t[0]) // (z1 + h)^2 + g.f.subAssign(t[0], t[7]) // (z1 + h)^2 - z1z1 + g.f.sub(&r[2], t[0], t[2]) // z3 = (z1 + z2)^2 - z1z1 - hh return r }   @@ -321,28 +362,28 @@ if g.IsZero(p) { return r.Set(p) } t := g.t - g.f.square(t[0], &p[0]) - g.f.square(t[1], &p[1]) - g.f.square(t[2], t[1]) - g.f.add(t[1], &p[0], t[1]) - g.f.square(t[1], t[1]) - g.f.sub(t[1], t[1], t[0]) - g.f.sub(t[1], t[1], t[2]) - g.f.double(t[1], t[1]) - g.f.double(t[3], t[0]) - g.f.add(t[0], t[3], t[0]) - g.f.square(t[4], t[0]) - g.f.double(t[3], t[1]) - g.f.sub(&r[0], t[4], t[3]) - g.f.sub(t[1], t[1], &r[0]) - g.f.double(t[2], t[2]) - g.f.double(t[2], t[2]) - g.f.double(t[2], t[2]) - g.f.mul(t[0], t[0], t[1]) - g.f.sub(t[1], t[0], t[2]) - g.f.mul(t[0], &p[1], &p[2]) - r[1].set(t[1]) - g.f.double(&r[2], t[0]) + g.f.square(t[0], &p[0]) // a = x^2 + g.f.square(t[1], &p[1]) // b = y^2 + g.f.square(t[2], t[1]) // c = b^2 + g.f.addAssign(t[1], &p[0]) // b + x1 + g.f.squareAssign(t[1]) // (b + x1)^2 + g.f.subAssign(t[1], t[0]) // (b + x1)^2 - a + g.f.subAssign(t[1], t[2]) // (b + x1)^2 - a - c + g.f.doubleAssign(t[1]) // d = 2((b+x1)^2 - a - c) + g.f.double(t[3], t[0]) // 2a + g.f.addAssign(t[0], t[3]) // e = 3a + g.f.square(t[4], t[0]) // f = e^2 + g.f.double(t[3], t[1]) // 2d + g.f.sub(&r[0], t[4], t[3]) // x3 = f - 2d + g.f.sub(t[1], t[1], &r[0]) // d-x3 + g.f.doubleAssign(t[2]) // + g.f.doubleAssign(t[2]) // + g.f.doubleAssign(t[2]) // 8c + g.f.mulAssign(t[0], t[1]) // e * (d - x3) + g.f.sub(t[1], t[0], t[2]) // x3 = e * (d - x3) - 8c + g.f.mul(t[0], &p[1], &p[2]) // y1 * z1 + r[1].set(t[1]) // + g.f.double(&r[2], t[0]) // z3 = 2(y1 * z1) return r }   @@ -363,7 +404,11 @@ return c }   // MulScalar multiplies a point by given scalar value in big.Int and assigns the result to point at first argument. -func (g *G2) MulScalar(c, p *PointG2, e *big.Int) *PointG2 { +func (g *G2) MulScalar(r, p *PointG2, e *big.Int) *PointG2 { + return g.glvMul(r, p, e) +} + +func (g *G2) mulScalar(c, p *PointG2, e *big.Int) *PointG2 { q, n := &PointG2{}, &PointG2{} n.Set(p) l := e.BitLen() @@ -376,67 +421,238 @@ } return c.Set(q) }   -// ClearCofactor maps given a G2 point to correct subgroup -func (g *G2) ClearCofactor(p *PointG2) { - g.MulScalar(p, p, cofactorEFFG2) +func (g *G2) wnafMul(r, p *PointG2, e *big.Int) *PointG2 { + wnaf := bigToWNAF(e, wnafMulWindowG2) + return g._wnafMul(r, p, wnaf) +} + +func (g *G2) _wnafMul(c, p *PointG2, wnaf nafNumber) *PointG2 { + + l := (1 << (wnafMulWindowG2 - 1)) + + twoP, acc := g.New(), new(PointG2).Set(p) + g.Double(twoP, p) + g.Affine(twoP) + + // table = {p, 3p, 5p, ..., -p, -3p, -5p} + table := make([]*PointG2, l*2) + table[0], table[l] = g.New(), g.New() + table[0].Set(p) + g.Neg(table[l], table[0]) + + for i := 1; i < l; i++ { + g.AddMixed(acc, acc, twoP) + table[i], table[i+l] = g.New(), g.New() + table[i].Set(acc) + g.Neg(table[i+l], table[i]) + } + + q := g.Zero() + for i := len(wnaf) - 1; i >= 0; i-- { + if wnaf[i] > 0 { + g.Add(q, q, table[wnaf[i]>>1]) + } else if wnaf[i] < 0 { + g.Add(q, q, table[((-wnaf[i])>>1)+l]) + } + if i != 0 { + g.Double(q, q) + } + } + return c.Set(q) }   -// MultiExp calculates multi exponentiation. Given pairs of G2 point and scalar values -// (P_0, e_0), (P_1, e_1), ... (P_n, e_n) calculates r = e_0 * P_0 + e_1 * P_1 + ... + e_n * P_n +func (g *G2) glvMul(r, p *PointG2, e *big.Int) *PointG2 { + return g._glvMul(r, p, new(glvVector).new(e)) +} + +func (g *G2) _glvMul(r, p0 *PointG2, v *glvVector) *PointG2 { + + w := glvMulWindowG2 + l := 1 << (w - 1) + + // prepare tables + // tableK1 = {P, 3P, 5P, ...} + // tableK2 = {λP, 3λP, 5λP, ...} + tableK1, tableK2 := make([]*PointG2, l), make([]*PointG2, l) + double := g.New() + g.Double(double, p0) + g.affine(double, double) + tableK1[0] = new(PointG2) + tableK1[0].Set(p0) + for i := 1; i < l; i++ { + tableK1[i] = new(PointG2) + g.AddMixed(tableK1[i], tableK1[i-1], double) + } + g.AffineBatch(tableK1) + for i := 0; i < l; i++ { + tableK2[i] = new(PointG2) + g.glvEndomorphism(tableK2[i], tableK1[i]) + } + + // recode small scalars + naf1, naf2 := v.wnaf(w) + lenNAF1, lenNAF2 := len(naf1), len(naf2) + lenNAF := lenNAF1 + if lenNAF2 > lenNAF { + lenNAF = lenNAF2 + } + + acc, p1 := g.New(), g.New() + + // function for naf addition + add := func(table []*PointG2, naf int) { + if naf != 0 { + nafAbs := naf + if nafAbs < 0 { + nafAbs = -nafAbs + } + p1.Set(table[nafAbs>>1]) + if naf < 0 { + g.Neg(p1, p1) + } + g.AddMixed(acc, acc, p1) + } + } + + // sliding + for i := lenNAF - 1; i >= 0; i-- { + if i < lenNAF1 { + add(tableK1, naf1[i]) + } + if i < lenNAF2 { + add(tableK2, naf2[i]) + } + if i != 0 { + g.Double(acc, acc) + } + } + return r.Set(acc) +} + +// MultiExp calculates multi exponentiation. Scalar values are received as big.Int type. +// Given pairs of G2 point and scalar values `(P_0, e_0), (P_1, e_1), ... (P_n, e_n)`, +// calculates `r = e_0 * P_0 + e_1 * P_1 + ... + e_n * P_n`. // Length of points and scalars are expected to be equal, otherwise an error is returned. // Result is assigned to point at first argument. -func (g *G2) MultiExp(r *PointG2, points []*PointG2, powers []*big.Int) (*PointG2, error) { - if len(points) != len(powers) { +func (g *G2) MultiExp(r *PointG2, points []*PointG2, scalars []*big.Int) (*PointG2, error) { + if len(points) != len(scalars) { return nil, errors.New("point and scalar vectors should be in same length") } - var c uint32 = 3 - if len(powers) >= 32 { - c = uint32(math.Ceil(math.Log10(float64(len(powers))))) - } - bucketSize, numBits := (1<<c)-1, uint32(g.Q().BitLen()) - windows := make([]*PointG2, numBits/c+1) - bucket := make([]*PointG2, bucketSize) - acc, sum := g.New(), g.New() - for i := 0; i < bucketSize; i++ { - bucket[i] = g.New() + + c := 3 + if len(scalars) >= 32 { + c = int(math.Ceil(math.Log(float64(len(scalars))))) } - mask := (uint64(1) << c) - 1 - j := 0 - var cur uint32 - for cur <= numBits { - acc.Zero() - bucket = make([]*PointG2, (1<<c)-1) - for i := 0; i < len(bucket); i++ { - bucket[i] = g.New() + + bucketSize := (1 << c) - 1 + windows := make([]PointG2, 255/c+1) + bucket := make([]PointG2, bucketSize) + + for j := 0; j < len(windows); j++ { + + for i := 0; i < bucketSize; i++ { + bucket[i].Zero() } - for i := 0; i < len(powers); i++ { - s0 := powers[i].Uint64() - index := uint(s0 & mask) + + for i := 0; i < len(scalars); i++ { + index := bucketSize & int(new(big.Int).Rsh(scalars[i], uint(c*j)).Int64()) if index != 0 { - g.Add(bucket[index-1], bucket[index-1], points[i]) + g.Add(&bucket[index-1], &bucket[index-1], points[i]) } - powers[i] = new(big.Int).Rsh(powers[i], uint(c)) } - sum.Zero() - for i := len(bucket) - 1; i >= 0; i-- { - g.Add(sum, sum, bucket[i]) + + acc, sum := g.New(), g.New() + for i := bucketSize - 1; i >= 0; i-- { + g.Add(sum, sum, &bucket[i]) g.Add(acc, acc, sum) } - windows[j] = g.New() windows[j].Set(acc) - j++ - cur += c } - acc.Zero() + + acc := g.New() for i := len(windows) - 1; i >= 0; i-- { - for j := uint32(0); j < c; j++ { + for j := 0; j < c; j++ { g.Double(acc, acc) } - g.Add(acc, acc, windows[i]) + g.Add(acc, acc, &windows[i]) } return r.Set(acc), nil }   +// InCorrectSubgroup checks whether given point is in correct subgroup. +func (g *G2) InCorrectSubgroup(p *PointG2) bool { + + // Faster Subgroup Checks for BLS12-381 + // S. Bowe + // https://eprint.iacr.org/2019/814.pdf + + // [z]ψ^3(P) − ψ^2(P) + P = O + t0, t1 := g.New().Set(p), g.New() + + g.psi(t0) + g.psi(t0) + g.Neg(t1, t0) // - ψ^2(P) + g.psi(t0) // ψ^3(P) + g.mulX(t0) // - x ψ^3(P) + g.Neg(t0, t0) + + g.Add(t0, t0, t1) + g.Add(t0, t0, p) + + return g.IsZero(t0) +} + +// ClearCofactor maps given a G2 point to correct subgroup +func (g *G2) ClearCofactor(p *PointG2) *PointG2 { + + // Efficient hash maps to G2 on BLS curves + // A. Budroni, F. Pintore + // https://eprint.iacr.org/2017/419.pdf + + // [h(ψ)]P = [x^2 − x − 1]P + [x − 1]ψ(P) + ψ^2(2P) + t0, t1, t2, t3 := g.New().Set(p), g.New().Set(p), g.New().Set(p), g.New() + + g.Double(t0, t0) + g.psi(t0) + g.psi(t0) // P2 = ψ^2(2P) + g.psi(t2) // P1 = ψ(P) + g.mulX(t1) // -xP0 + + g.Sub(t3, t1, t2) // -xP0 - P1 + g.mulX(t3) // (x^2)P0 + xP1 + g.Sub(t1, t1, p) // (-x-1)P0 + g.Add(t3, t3, t1) // (x^2-x-1)P0 + xP1 + g.Sub(t3, t3, t2) // (x^2-x-1)P0 + (x-1)P1 + g.Add(t3, t3, t0) // (x^2-x-1)P0 + (x-1)P1 + P2 + return p.Set(t3) +} + +func (g *G2) psi(p *PointG2) { + g.f.conjugate(&p[0], &p[0]) + g.f.conjugate(&p[1], &p[1]) + g.f.conjugate(&p[2], &p[2]) + g.f.mul(&p[0], &p[0], &psix) + g.f.mul(&p[1], &p[1], &psiy) +} + +func (g *G2) mulX(p *PointG2) { + + chain := func(p0 *PointG2, n int, p1 *PointG2) { + g.Add(p0, p0, p1) + for i := 0; i < n; i++ { + g.Double(p0, p0) + } + } + + t := g.New().Set(p) + g.Double(p, t) + chain(p, 2, t) + chain(p, 3, t) + chain(p, 9, t) + chain(p, 32, t) + chain(p, 16, t) +} + // MapToCurve given a byte slice returns a valid G2 point. // This mapping function implements the Simplified Shallue-van de Woestijne-Ulas method. // https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#section-6.6.2 @@ -454,3 +670,45 @@ q := &PointG2{*x, *y, *z} g.ClearCofactor(q) return g.Affine(q), nil } + +// EncodeToCurve given a message and domain separator tag returns the hash result +// which is a valid curve point. +// Implementation follows BLS12381G1_XMD:SHA-256_SSWU_NU_ suite at +// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06 +func (g *G2) EncodeToCurve(msg, domain []byte) (*PointG2, error) { + hashRes, err := hashToFpXMDSHA256(msg, domain, 2) + if err != nil { + return nil, err + } + fp2 := g.f + u := &fe2{*hashRes[0], *hashRes[1]} + x, y := swuMapG2(fp2, u) + isogenyMapG2(fp2, x, y) + z := new(fe2).one() + q := &PointG2{*x, *y, *z} + g.ClearCofactor(q) + return g.Affine(q), nil +} + +// HashToCurve given a message and domain separator tag returns the hash result +// which is a valid curve point. +// Implementation follows BLS12381G1_XMD:SHA-256_SSWU_RO_ suite at +// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06 +func (g *G2) HashToCurve(msg, domain []byte) (*PointG2, error) { + hashRes, err := hashToFpXMDSHA256(msg, domain, 4) + if err != nil { + return nil, err + } + fp2 := g.f + u0, u1 := &fe2{*hashRes[0], *hashRes[1]}, &fe2{*hashRes[2], *hashRes[3]} + x0, y0 := swuMapG2(fp2, u0) + x1, y1 := swuMapG2(fp2, u1) + z0 := new(fe2).one() + z1 := new(fe2).one() + p0, p1 := &PointG2{*x0, *y0, *z0}, &PointG2{*x1, *y1, *z1} + g.Add(p0, p0, p1) + g.Affine(p0) + isogenyMapG2(fp2, &p0[0], &p0[1]) + g.ClearCofactor(p0) + return g.Affine(p0), nil +}
diff --git go-ethereum/crypto/bls12381/g1.go celo/crypto/bls12381/g1.go index d853823cd2987035c8f9632472b1b74215133921..5b77b092baca3a8e546db07daacad3d65817cd0e 100644 --- go-ethereum/crypto/bls12381/g1.go +++ celo/crypto/bls12381/g1.go @@ -1,19 +1,3 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   import ( @@ -22,10 +6,11 @@ "math" "math/big" )   -// PointG1 is type for point in G1. -// PointG1 is both used for Affine and Jacobian point representation. -// If z is equal to one the point is considered as in affine form. +// PointG1 is type for point in G1 and used for both Affine and Jacobian point representation. +// A point is accounted as in affine form if z is equal to one. type PointG1 [3]fe + +var wnafMulWindowG1 uint = 5   func (p *PointG1) Set(p2 *PointG1) *PointG1 { p[0].set(&p2[0]) @@ -34,12 +19,16 @@ p[2].set(&p2[2]) return p }   -// Zero returns G1 point in point at infinity representation func (p *PointG1) Zero() *PointG1 { p[0].zero() p[1].one() p[2].zero() return p +} + +// IsAffine checks a G1 point whether it is in affine form. +func (p *PointG1) IsAffine() bool { + return p[2].isOne() }   type tempG1 struct { @@ -70,33 +59,18 @@ func (g *G1) Q() *big.Int { return new(big.Int).Set(q) }   -func (g *G1) fromBytesUnchecked(in []byte) (*PointG1, error) { - p0, err := fromBytes(in[:48]) - if err != nil { - return nil, err - } - p1, err := fromBytes(in[48:]) - if err != nil { - return nil, err - } - p2 := new(fe).one() - return &PointG1{*p0, *p1, *p2}, nil -} - // FromBytes constructs a new point given uncompressed byte input. -// FromBytes does not take zcash flags into account. -// Byte input expected to be larger than 96 bytes. -// First 96 bytes should be concatenation of x and y values. -// Point (0, 0) is considered as infinity. +// Input string is expected to be equal to 96 bytes and concatenation of x and y cooridanates. +// (0, 0) is considered as infinity. func (g *G1) FromBytes(in []byte) (*PointG1, error) { - if len(in) != 96 { - return nil, errors.New("input string should be equal or larger than 96") + if len(in) != 2*fpByteSize { + return nil, errors.New("input string length must be equal to 96 bytes") } - p0, err := fromBytes(in[:48]) + p0, err := fromBytes(in[:fpByteSize]) if err != nil { return nil, err } - p1, err := fromBytes(in[48:]) + p1, err := fromBytes(in[fpByteSize:]) if err != nil { return nil, err } @@ -134,16 +108,15 @@ return g.FromBytes(pointBytes) }   // ToBytes serializes a point into bytes in uncompressed form. -// ToBytes does not take zcash flags into account. // ToBytes returns (0, 0) if point is infinity. func (g *G1) ToBytes(p *PointG1) []byte { - out := make([]byte, 96) + out := make([]byte, 2*fpByteSize) if g.IsZero(p) { return out } g.Affine(p) - copy(out[:48], toBytes(&p[0])) - copy(out[48:], toBytes(&p[1])) + copy(out[:fpByteSize], toBytes(&p[0])) + copy(out[fpByteSize:], toBytes(&p[1])) return out }   @@ -201,9 +174,33 @@ }   // InCorrectSubgroup checks whether given point is in correct subgroup. func (g *G1) InCorrectSubgroup(p *PointG1) bool { - tmp := &PointG1{} - g.MulScalar(tmp, p, q) - return g.IsZero(tmp) + + // Faster Subgroup Checks for BLS12-381 + // S. Bowe + // https://eprint.iacr.org/2019/814.pdf + + mulZ := func(p *PointG1) { + // z = [(x^2 − 1)/3] + z, _ := new(big.Int).SetString("76329603384216526021617858986798044501", 10) + e := bigToWNAF(z, wnafMulWindowG1) + g._wnafMul(p, p, e) + } + + sigma := func(p *PointG1) { + mul(&p[0], &p[0], glvPhi1) + } + + // [(x^2 − 1)/3](2σ(P) − P − σ^2(P)) − σ^2(P) ?= O + t0 := g.New().Set(p) + sigma(t0) + t1 := g.New().Set(t0) // σ(P) + sigma(t0) // σ^2(P) + g.Double(t1, t1) // 2σ(P) + g.Sub(t1, t1, p) // 2σ(P) − P + g.Sub(t1, t1, t0) // 2σ(P) − P − σ^2(P) + mulZ(t1) // [(x^2 − 1)/3](2σ(P) − P − σ^2(P)) + g.Sub(t1, t1, t0) // [(x^2 − 1)/3](2σ(P) − P − σ^2(P)) − σ^2(P) + return g.IsZero(t1) }   // IsOnCurve checks a G1 point is on curve. @@ -212,15 +209,19 @@ if g.IsZero(p) { return true } t := g.t - square(t[0], &p[1]) - square(t[1], &p[0]) - mul(t[1], t[1], &p[0]) - square(t[2], &p[2]) - square(t[3], t[2]) - mul(t[2], t[2], t[3]) - mul(t[2], b, t[2]) - add(t[1], t[1], t[2]) - return t[0].equal(t[1]) + square(t[0], &p[1]) // y^2 + square(t[1], &p[0]) // x^2 + mul(t[1], t[1], &p[0]) // x^3 + if p.IsAffine() { + addAssign(t[1], b) // x^2 + b + return t[0].equal(t[1]) // y^2 ?= x^3 + b + } + square(t[2], &p[2]) // z^2 + square(t[3], t[2]) // z^4 + mul(t[2], t[2], t[3]) // z^6 + mul(t[2], b, t[2]) // b * z^6 + add(t[1], t[1], t[2]) // x^3 + b * z^6 + return t[0].equal(t[1]) // y^2 ?= x^3 + b * z^6 }   // IsAffine checks a G1 point whether it is in affine form. @@ -228,25 +229,51 @@ func (g *G1) IsAffine(p *PointG1) bool { return p[2].isOne() }   -// Add adds two G1 points p1, p2 and assigns the result to point at first argument. +// Affine returns the affine representation of the given point func (g *G1) Affine(p *PointG1) *PointG1 { + return g.affine(p, p) +} + +func (g *G1) affine(r, p *PointG1) *PointG1 { if g.IsZero(p) { - return p + return r.Zero() } if !g.IsAffine(p) { t := g.t - inverse(t[0], &p[2]) - square(t[1], t[0]) - mul(&p[0], &p[0], t[1]) - mul(t[0], t[0], t[1]) - mul(&p[1], &p[1], t[0]) - p[2].one() + inverse(t[0], &p[2]) // z^-1 + square(t[1], t[0]) // z^-2 + mul(&r[0], &p[0], t[1]) // x = x * z^-2 + mul(t[0], t[0], t[1]) // z^-3 + mul(&r[1], &p[1], t[0]) // y = y * z^-3 + r[2].one() // z = 1 + } else { + r.Set(p) + } + return r +} + +// AffineBatch given multiple of points returns affine representations +func (g *G1) AffineBatch(p []*PointG1) { + inverses := make([]fe, len(p)) + for i := 0; i < len(p); i++ { + inverses[i].set(&p[i][2]) + } + inverseBatch(inverses) + t := g.t + for i := 0; i < len(p); i++ { + if !g.IsAffine(p[i]) && !g.IsZero(p[i]) { + square(t[1], &inverses[i]) + mul(&p[i][0], &p[i][0], t[1]) + mul(t[0], &inverses[i], t[1]) + mul(&p[i][1], &p[i][1], t[0]) + p[i][2].one() + } } - return p }   // Add adds two G1 points p1, p2 and assigns the result to point at first argument. func (g *G1) Add(r, p1, p2 *PointG1) *PointG1 { + // http://www.hyperelliptic.org/EFD/gp/auto-shortw-jacobian-0.html#addition-add-2007-bl if g.IsZero(p1) { return r.Set(p2) @@ -254,42 +281,89 @@ } if g.IsZero(p2) { return r.Set(p1) } + if g.IsAffine(p2) { + return g.AddMixed(r, p1, p2) + } t := g.t - square(t[7], &p1[2]) - mul(t[1], &p2[0], t[7]) - mul(t[2], &p1[2], t[7]) - mul(t[0], &p2[1], t[2]) - square(t[8], &p2[2]) - mul(t[3], &p1[0], t[8]) - mul(t[4], &p2[2], t[8]) - mul(t[2], &p1[1], t[4]) + square(t[7], &p1[2]) // z1z1 + mul(t[1], &p2[0], t[7]) // u2 = x2 * z1z1 + mul(t[2], &p1[2], t[7]) // z1z1 * z1 + mul(t[0], &p2[1], t[2]) // s2 = y2 * z1z1 * z1 + square(t[8], &p2[2]) // z2z2 + mul(t[3], &p1[0], t[8]) // u1 = x1 * z2z2 + mul(t[4], &p2[2], t[8]) // z2z2 * z2 + mul(t[2], &p1[1], t[4]) // s1 = y1 * z2z2 * z2 if t[1].equal(t[3]) { if t[0].equal(t[2]) { return g.Double(r, p1) } return r.Zero() } - sub(t[1], t[1], t[3]) - double(t[4], t[1]) - square(t[4], t[4]) - mul(t[5], t[1], t[4]) - sub(t[0], t[0], t[2]) - double(t[0], t[0]) - square(t[6], t[0]) - sub(t[6], t[6], t[5]) - mul(t[3], t[3], t[4]) - double(t[4], t[3]) - sub(&r[0], t[6], t[4]) - sub(t[4], t[3], &r[0]) - mul(t[6], t[2], t[5]) - double(t[6], t[6]) - mul(t[0], t[0], t[4]) - sub(&r[1], t[0], t[6]) - add(t[0], &p1[2], &p2[2]) - square(t[0], t[0]) - sub(t[0], t[0], t[7]) - sub(t[0], t[0], t[8]) - mul(&r[2], t[0], t[1]) + subAssign(t[1], t[3]) // h = u2 - u1 + double(t[4], t[1]) // 2h + square(t[4], t[4]) // i = 2h^2 + mul(t[5], t[1], t[4]) // j = h*i + subAssign(t[0], t[2]) // s2 - s1 + doubleAssign(t[0]) // r = 2*(s2 - s1) + square(t[6], t[0]) // r^2 + subAssign(t[6], t[5]) // r^2 - j + mul(t[3], t[3], t[4]) // v = u1 * i + double(t[4], t[3]) // 2*v + sub(&r[0], t[6], t[4]) // x3 = r^2 - j - 2*v + sub(t[4], t[3], &r[0]) // v - x3 + mul(t[6], t[2], t[5]) // s1 * j + doubleAssign(t[6]) // 2 * s1 * j + mul(t[0], t[0], t[4]) // r * (v - x3) + sub(&r[1], t[0], t[6]) // y3 = r * (v - x3) - (2 * s1 * j) + add(t[0], &p1[2], &p2[2]) // z1 + z2 + square(t[0], t[0]) // (z1 + z2)^2 + subAssign(t[0], t[7]) // (z1 + z2)^2 - z1z1 + subAssign(t[0], t[8]) // (z1 + z2)^2 - z1z1 - z2z2 + mul(&r[2], t[0], t[1]) // z3 = ((z1 + z2)^2 - z1z1 - z2z2) * h + return r +} + +// Add adds two G1 points p1, p2 and assigns the result to point at first argument. +// Expects the second point p2 in affine form. +func (g *G1) AddMixed(r, p1, p2 *PointG1) *PointG1 { + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl + if g.IsZero(p1) { + return r.Set(p2) + } + if g.IsZero(p2) { + return r.Set(p1) + } + t := g.t + square(t[7], &p1[2]) // z1z1 + mul(t[1], &p2[0], t[7]) // u2 = x2 * z1z1 + mul(t[2], &p1[2], t[7]) // z1z1 * z1 + mul(t[0], &p2[1], t[2]) // s2 = y2 * z1z1 * z1 + + if p1[0].equal(t[1]) && p1[1].equal(t[0]) { + return g.Double(r, p1) + } + + sub(t[1], t[1], &p1[0]) // h = u2 - x1 + square(t[2], t[1]) // hh + double(t[4], t[2]) + doubleAssign(t[4]) // 4hh + mul(t[5], t[1], t[4]) // j = h*i + subAssign(t[0], &p1[1]) // s2 - y1 + doubleAssign(t[0]) // r = 2*(s2 - y1) + square(t[6], t[0]) // r^2 + subAssign(t[6], t[5]) // r^2 - j + mul(t[3], &p1[0], t[4]) // v = x1 * i + double(t[4], t[3]) // 2*v + sub(&r[0], t[6], t[4]) // x3 = r^2 - j - 2*v + sub(t[4], t[3], &r[0]) // v - x3 + mul(t[6], &p1[1], t[5]) // y1 * j + doubleAssign(t[6]) // 2 * y1 * j + mul(t[0], t[0], t[4]) // r * (v - x3) + sub(&r[1], t[0], t[6]) // y3 = r * (v - x3) - (2 * y1 * j) + add(t[0], &p1[2], t[1]) // z1 + h + square(t[0], t[0]) // (z1 + h)^2 + subAssign(t[0], t[7]) // (z1 + h)^2 - z1z1 + sub(&r[2], t[0], t[2]) // z3 = (z1 + z2)^2 - z1z1 - hh return r }   @@ -297,31 +371,31 @@ // Double doubles a G1 point p and assigns the result to the point at first argument. func (g *G1) Double(r, p *PointG1) *PointG1 { // http://www.hyperelliptic.org/EFD/gp/auto-shortw-jacobian-0.html#doubling-dbl-2009-l if g.IsZero(p) { - return r.Set(p) + return r.Zero() } t := g.t - square(t[0], &p[0]) - square(t[1], &p[1]) - square(t[2], t[1]) - add(t[1], &p[0], t[1]) - square(t[1], t[1]) - sub(t[1], t[1], t[0]) - sub(t[1], t[1], t[2]) - double(t[1], t[1]) - double(t[3], t[0]) - add(t[0], t[3], t[0]) - square(t[4], t[0]) - double(t[3], t[1]) - sub(&r[0], t[4], t[3]) - sub(t[1], t[1], &r[0]) - double(t[2], t[2]) - double(t[2], t[2]) - double(t[2], t[2]) - mul(t[0], t[0], t[1]) - sub(t[1], t[0], t[2]) - mul(t[0], &p[1], &p[2]) - r[1].set(t[1]) - double(&r[2], t[0]) + square(t[0], &p[0]) // a = x^2 + square(t[1], &p[1]) // b = y^2 + square(t[2], t[1]) // c = b^2 + add(t[1], &p[0], t[1]) // b + x1 + square(t[1], t[1]) // (b + x1)^2 + subAssign(t[1], t[0]) // (b + x1)^2 - a + subAssign(t[1], t[2]) // (b + x1)^2 - a - c + doubleAssign(t[1]) // d = 2((b+x1)^2 - a - c) + double(t[3], t[0]) // 2a + addAssign(t[0], t[3]) // e = 3a + square(t[4], t[0]) // f = e^2 + double(t[3], t[1]) // 2d + sub(&r[0], t[4], t[3]) // x3 = f - 2d + subAssign(t[1], &r[0]) // d-x3 + doubleAssign(t[2]) // + doubleAssign(t[2]) // + doubleAssign(t[2]) // 8c + mul(t[0], t[0], t[1]) // e * (d - x3) + sub(t[1], t[0], t[2]) // x3 = e * (d - x3) - 8c + mul(t[0], &p[1], &p[2]) // y1 * z1 + r[1].set(t[1]) // + double(&r[2], t[0]) // z3 = 2(y1 * z1) return r }   @@ -342,11 +416,14 @@ return c }   // MulScalar multiplies a point by given scalar value in big.Int and assigns the result to point at first argument. -func (g *G1) MulScalar(c, p *PointG1, e *big.Int) *PointG1 { +func (g *G1) MulScalar(r, p *PointG1, e *big.Int) *PointG1 { + return g.glvMul(r, p, e) +} + +func (g *G1) mulScalar(c, p *PointG1, e *big.Int) *PointG1 { q, n := &PointG1{}, &PointG1{} n.Set(p) - l := e.BitLen() - for i := 0; i < l; i++ { + for i := 0; i < frBitSize; i++ { if e.Bit(i) == 1 { g.Add(q, q, n) } @@ -355,67 +432,181 @@ } return c.Set(q) }   -// ClearCofactor maps given a G1 point to correct subgroup -func (g *G1) ClearCofactor(p *PointG1) { - g.MulScalar(p, p, cofactorEFFG1) +func (g *G1) wnafMul(r, p *PointG1, e *big.Int) *PointG1 { + wnaf := bigToWNAF(e, wnafMulWindowG1) + return g._wnafMul(r, p, wnaf) +} + +func (g *G1) _wnafMul(c, p *PointG1, wnaf nafNumber) *PointG1 { + + l := (1 << (wnafMulWindowG1 - 1)) + + twoP, acc := g.New(), new(PointG1).Set(p) + g.Double(twoP, p) + g.Affine(twoP) + + // table = {p, 3p, 5p, ..., -p, -3p, -5p} + table := make([]*PointG1, l*2) + table[0], table[l] = g.New(), g.New() + table[0].Set(p) + g.Neg(table[l], table[0]) + + for i := 1; i < l; i++ { + g.AddMixed(acc, acc, twoP) + table[i], table[i+l] = g.New(), g.New() + table[i].Set(acc) + g.Neg(table[i+l], table[i]) + } + + q := g.Zero() + for i := len(wnaf) - 1; i >= 0; i-- { + if wnaf[i] > 0 { + g.Add(q, q, table[wnaf[i]>>1]) + } else if wnaf[i] < 0 { + g.Add(q, q, table[((-wnaf[i])>>1)+l]) + } + if i != 0 { + g.Double(q, q) + } + } + return c.Set(q) +} + +func (g *G1) glvMul(r, p *PointG1, e *big.Int) *PointG1 { + return g._glvMul(r, p, new(glvVector).new(e)) +} + +func (g *G1) _glvMul(r, p0 *PointG1, v *glvVector) *PointG1 { + + w := glvMulWindowG1 + l := 1 << (w - 1) + + // prepare tables + // tableK1 = {P, 3P, 5P, ...} + // tableK2 = {λP, 3λP, 5λP, ...} + tableK1, tableK2 := make([]*PointG1, l), make([]*PointG1, l) + double := g.New() + g.Double(double, p0) + g.affine(double, double) + tableK1[0] = new(PointG1) + tableK1[0].Set(p0) + for i := 1; i < l; i++ { + tableK1[i] = new(PointG1) + g.AddMixed(tableK1[i], tableK1[i-1], double) + } + g.AffineBatch(tableK1) + for i := 0; i < l; i++ { + tableK2[i] = new(PointG1) + g.glvEndomorphism(tableK2[i], tableK1[i]) + } + + // recode small scalars + naf1, naf2 := v.wnaf(w) + lenNAF1, lenNAF2 := len(naf1), len(naf2) + lenNAF := lenNAF1 + if lenNAF2 > lenNAF { + lenNAF = lenNAF2 + } + + acc, p1 := g.New(), g.New() + + // function for naf addition + add := func(table []*PointG1, naf int) { + if naf != 0 { + nafAbs := naf + if nafAbs < 0 { + nafAbs = -nafAbs + } + p1.Set(table[nafAbs>>1]) + if naf < 0 { + g.Neg(p1, p1) + } + g.AddMixed(acc, acc, p1) + } + } + + // sliding + for i := lenNAF - 1; i >= 0; i-- { + if i < lenNAF1 { + add(tableK1, naf1[i]) + } + if i < lenNAF2 { + add(tableK2, naf2[i]) + } + if i != 0 { + g.Double(acc, acc) + } + } + return r.Set(acc) }   -// MultiExp calculates multi exponentiation. Given pairs of G1 point and scalar values -// (P_0, e_0), (P_1, e_1), ... (P_n, e_n) calculates r = e_0 * P_0 + e_1 * P_1 + ... + e_n * P_n +// MultiExp calculates multi exponentiation. Scalar values are received as big.Int type. +// Given pairs of G1 point and scalar values `(P_0, e_0), (P_1, e_1), ... (P_n, e_n)`, +// calculates `r = e_0 * P_0 + e_1 * P_1 + ... + e_n * P_n`. // Length of points and scalars are expected to be equal, otherwise an error is returned. // Result is assigned to point at first argument. -func (g *G1) MultiExp(r *PointG1, points []*PointG1, powers []*big.Int) (*PointG1, error) { - if len(points) != len(powers) { +func (g *G1) MultiExp(r *PointG1, points []*PointG1, scalars []*big.Int) (*PointG1, error) { + if len(points) != len(scalars) { return nil, errors.New("point and scalar vectors should be in same length") } - var c uint32 = 3 - if len(powers) >= 32 { - c = uint32(math.Ceil(math.Log10(float64(len(powers))))) + + c := 3 + if len(scalars) >= 32 { + c = int(math.Ceil(math.Log(float64(len(scalars))))) } - bucketSize, numBits := (1<<c)-1, uint32(g.Q().BitLen()) - windows := make([]*PointG1, numBits/c+1) - bucket := make([]*PointG1, bucketSize) - acc, sum := g.New(), g.New() - for i := 0; i < bucketSize; i++ { - bucket[i] = g.New() - } - mask := (uint64(1) << c) - 1 - j := 0 - var cur uint32 - for cur <= numBits { - acc.Zero() - bucket = make([]*PointG1, (1<<c)-1) - for i := 0; i < len(bucket); i++ { - bucket[i] = g.New() + + bucketSize := (1 << c) - 1 + windows := make([]PointG1, 255/c+1) + bucket := make([]PointG1, bucketSize) + + for j := 0; j < len(windows); j++ { + + for i := 0; i < bucketSize; i++ { + bucket[i].Zero() } - for i := 0; i < len(powers); i++ { - s0 := powers[i].Uint64() - index := uint(s0 & mask) + + for i := 0; i < len(scalars); i++ { + index := bucketSize & int(new(big.Int).Rsh(scalars[i], uint(c*j)).Int64()) if index != 0 { - g.Add(bucket[index-1], bucket[index-1], points[i]) + g.Add(&bucket[index-1], &bucket[index-1], points[i]) } - powers[i] = new(big.Int).Rsh(powers[i], uint(c)) } - sum.Zero() - for i := len(bucket) - 1; i >= 0; i-- { - g.Add(sum, sum, bucket[i]) + + acc, sum := g.New(), g.New() + for i := bucketSize - 1; i >= 0; i-- { + g.Add(sum, sum, &bucket[i]) g.Add(acc, acc, sum) } - windows[j] = g.New() windows[j].Set(acc) - j++ - cur += c } - acc.Zero() + + acc := g.New() for i := len(windows) - 1; i >= 0; i-- { - for j := uint32(0); j < c; j++ { + for j := 0; j < c; j++ { g.Double(acc, acc) } - g.Add(acc, acc, windows[i]) + g.Add(acc, acc, &windows[i]) } return r.Set(acc), nil }   +func (g *G1) ClearCofactor(p *PointG1) *PointG1 { + chain := func(p0 *PointG1, n int, p1 *PointG1) { + for i := 0; i < n; i++ { + g.Double(p0, p0) + } + g.Add(p0, p0, p1) + } + t := g.New().Set(p) + chain(p, 1, t) + chain(p, 2, t) + chain(p, 3, t) + chain(p, 9, t) + chain(p, 32, t) + chain(p, 16, t) + return p +} + // MapToCurve given a byte slice returns a valid G1 point. // This mapping function implements the Simplified Shallue-van de Woestijne-Ulas method. // https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06 @@ -432,3 +623,42 @@ p := &PointG1{*x, *y, *one} g.ClearCofactor(p) return g.Affine(p), nil } + +// EncodeToCurve given a message and domain separator tag returns the hash result +// which is a valid curve point. +// Implementation follows BLS12381G1_XMD:SHA-256_SSWU_NU_ suite at +// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06 +func (g *G1) EncodeToCurve(msg, domain []byte) (*PointG1, error) { + hashRes, err := hashToFpXMDSHA256(msg, domain, 1) + if err != nil { + return nil, err + } + u := hashRes[0] + x, y := swuMapG1(u) + isogenyMapG1(x, y) + one := new(fe).one() + p := &PointG1{*x, *y, *one} + g.ClearCofactor(p) + return g.Affine(p), nil +} + +// HashToCurve given a message and domain separator tag returns the hash result +// which is a valid curve point. +// Implementation follows BLS12381G1_XMD:SHA-256_SSWU_RO_ suite at +// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06 +func (g *G1) HashToCurve(msg, domain []byte) (*PointG1, error) { + hashRes, err := hashToFpXMDSHA256(msg, domain, 2) + if err != nil { + return nil, err + } + u0, u1 := hashRes[0], hashRes[1] + x0, y0 := swuMapG1(u0) + x1, y1 := swuMapG1(u1) + one := new(fe).one() + p0, p1 := &PointG1{*x0, *y0, *one}, &PointG1{*x1, *y1, *one} + g.Add(p0, p0, p1) + g.Affine(p0) + isogenyMapG1(&p0[0], &p0[1]) + g.ClearCofactor(p0) + return g.Affine(p0), nil +}
diff --git go-ethereum/crypto/bls12381/gt.go celo/crypto/bls12381/gt.go index 2ac265e9568b608d8b62daa68459d6956dcef1ea..a7617faf8cfbfc2ec665422e8a7f487e1843359d 100644 --- go-ethereum/crypto/bls12381/gt.go +++ celo/crypto/bls12381/gt.go @@ -1,19 +1,3 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   import ( @@ -29,6 +13,7 @@ type GT struct { fp12 *fp12 }   +// Set copies given value into the destination func (e *E) Set(e2 *E) *E { return e.set(e2) }
diff --git go-ethereum/crypto/bls12381/arithmetic_x86.s celo/crypto/bls12381/arithmetic_x86.s index 2cebbc46f7964baa4051b528f6690186de943131..c084a467114fbcd85779ccc88ce68507ca64321a 100644 --- go-ethereum/crypto/bls12381/arithmetic_x86.s +++ celo/crypto/bls12381/arithmetic_x86.s @@ -1,9 +1,9 @@ -// +build amd64,blsasm amd64,blsadx +// +build amd64,!generic   #include "textflag.h"   -// addition w/ modular reduction -// a = (a + b) % p +// single-precision addition w/ modular reduction +// a' = (a + b) % p TEXT ·addAssign(SB), NOSPLIT, $0-16 // | MOVQ a+0(FP), DI @@ -63,7 +63,7 @@ /* | end */   -// addition w/ modular reduction +// single-precision addition w/ modular reduction // c = (a + b) % p TEXT ·add(SB), NOSPLIT, $0-24 // | @@ -124,43 +124,8 @@ RET /* | end */   -// addition w/o reduction check -// c = (a + b) -TEXT ·ladd(SB), NOSPLIT, $0-24 - // | - MOVQ a+8(FP), DI - MOVQ b+16(FP), SI - - // | - MOVQ (DI), R8 - MOVQ 8(DI), R9 - MOVQ 16(DI), R10 - MOVQ 24(DI), R11 - MOVQ 32(DI), R12 - MOVQ 40(DI), R13 - - // | - ADDQ (SI), R8 - ADCQ 8(SI), R9 - ADCQ 16(SI), R10 - ADCQ 24(SI), R11 - ADCQ 32(SI), R12 - ADCQ 40(SI), R13 - - // | - MOVQ c+0(FP), DI - MOVQ R8, (DI) - MOVQ R9, 8(DI) - MOVQ R10, 16(DI) - MOVQ R11, 24(DI) - MOVQ R12, 32(DI) - MOVQ R13, 40(DI) - RET -/* | end */ - - -// addition w/o reduction check -// a = a + b +// single-precision addition w/o check +// a' = a + b TEXT ·laddAssign(SB), NOSPLIT, $0-16 // | MOVQ a+0(FP), DI @@ -194,7 +159,7 @@ RET /* | end */   -// subtraction w/ modular reduction +// single-precision subtraction with modular reduction // c = (a - b) % p TEXT ·sub(SB), NOSPLIT, $0-24 // | @@ -248,8 +213,8 @@ RET /* | end */   -// subtraction w/ modular reduction -// a = (a - b) % p +// single-precision subtraction with modular reduction +// a' = (a - b) % p TEXT ·subAssign(SB), NOSPLIT, $0-16 // | MOVQ a+0(FP), DI @@ -302,8 +267,8 @@ RET /* | end */   -// subtraction w/o reduction check -// a = (a - b) +// single-precision subtraction no modular red check +// a' = (a - b) TEXT ·lsubAssign(SB), NOSPLIT, $0-16 // | MOVQ a+0(FP), DI @@ -334,7 +299,7 @@ MOVQ R13, 40(DI) RET /* | end */   -// doubling w/ reduction +// single-precision doubling // c = (2 * a) % p TEXT ·double(SB), NOSPLIT, $0-16 // | @@ -391,8 +356,8 @@ RET /* | end */   -// doubling w/ reduction -// a = (2 * a) % p +// single-precision doubling +// a' = (2 * a) % p TEXT ·doubleAssign(SB), NOSPLIT, $0-8 // | MOVQ a+0(FP), DI @@ -446,40 +411,6 @@ RET /* | end */   -// doubling w/o reduction -// c = 2 * a -TEXT ·ldouble(SB), NOSPLIT, $0-16 - // | - MOVQ a+8(FP), DI - - MOVQ (DI), R8 - MOVQ 8(DI), R9 - MOVQ 16(DI), R10 - MOVQ 24(DI), R11 - MOVQ 32(DI), R12 - MOVQ 40(DI), R13 - - // | - ADDQ R8, R8 - ADCQ R9, R9 - ADCQ R10, R10 - ADCQ R11, R11 - ADCQ R12, R12 - ADCQ R13, R13 - - // | - MOVQ c+0(FP), DI - MOVQ R8, (DI) - MOVQ R9, 8(DI) - MOVQ R10, 16(DI) - MOVQ R11, 24(DI) - MOVQ R12, 32(DI) - MOVQ R13, 40(DI) - - RET -/* | end */ - - TEXT ·_neg(SB), NOSPLIT, $0-16 // | MOVQ a+8(FP), DI @@ -508,6 +439,7 @@ MOVQ R12, 32(DI) MOVQ R13, 40(DI) RET /* | end */ +   // multiplication without using MULX/ADX
diff --git go-ethereum/crypto/bls12381/fp2.go celo/crypto/bls12381/fp2.go index 0f1c5a23ac5ff080f9edb338e33c070af76cf480..6561df7028efcb7eb34ac8e05ac9c1dd51b5c0bc 100644 --- go-ethereum/crypto/bls12381/fp2.go +++ celo/crypto/bls12381/fp2.go @@ -1,19 +1,3 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   import ( @@ -43,14 +27,14 @@ return &fp2{t} }   func (e *fp2) fromBytes(in []byte) (*fe2, error) { - if len(in) != 96 { - return nil, errors.New("length of input string should be 96 bytes") + if len(in) != 2*fpByteSize { + return nil, errors.New("input string must be equal to 96 bytes") } - c1, err := fromBytes(in[:48]) + c1, err := fromBytes(in[:fpByteSize]) if err != nil { return nil, err } - c0, err := fromBytes(in[48:]) + c0, err := fromBytes(in[fpByteSize:]) if err != nil { return nil, err } @@ -58,9 +42,9 @@ return &fe2{*c0, *c1}, nil }   func (e *fp2) toBytes(a *fe2) []byte { - out := make([]byte, 96) - copy(out[:48], toBytes(&a[1])) - copy(out[48:], toBytes(&a[0])) + out := make([]byte, 2*fpByteSize) + copy(out[:fpByteSize], toBytes(&a[1])) + copy(out[fpByteSize:], toBytes(&a[0])) return out }   @@ -77,60 +61,74 @@ return new(fe2).one() }   func (e *fp2) add(c, a, b *fe2) { + // c0 = a0 + b0 + // c1 = a1 + b1 add(&c[0], &a[0], &b[0]) add(&c[1], &a[1], &b[1]) }   func (e *fp2) addAssign(a, b *fe2) { + // a0 = a0 + b0 + // a1 = a1 + b1 addAssign(&a[0], &b[0]) addAssign(&a[1], &b[1]) }   -func (e *fp2) ladd(c, a, b *fe2) { - ladd(&c[0], &a[0], &b[0]) - ladd(&c[1], &a[1], &b[1]) -} - func (e *fp2) double(c, a *fe2) { + // c0 = 2a0 + // c1 = 2a1 double(&c[0], &a[0]) double(&c[1], &a[1]) }   func (e *fp2) doubleAssign(a *fe2) { + // a0 = 2a0 + // a1 = 2a1 doubleAssign(&a[0]) doubleAssign(&a[1]) }   -func (e *fp2) ldouble(c, a *fe2) { - ldouble(&c[0], &a[0]) - ldouble(&c[1], &a[1]) -} - func (e *fp2) sub(c, a, b *fe2) { + // c0 = a0 - b0 + // c1 = a1 - b1 sub(&c[0], &a[0], &b[0]) sub(&c[1], &a[1], &b[1]) }   func (e *fp2) subAssign(c, a *fe2) { + // a0 = a0 - b0 + // a1 = a1 - b1 subAssign(&c[0], &a[0]) subAssign(&c[1], &a[1]) }   func (e *fp2) neg(c, a *fe2) { + // c0 = -a0 + // c1 = -a1 neg(&c[0], &a[0]) neg(&c[1], &a[1]) }   +func (e *fp2) conjugate(c, a *fe2) { + // c0 = a0 + // c1 = -a1 + c[0].set(&a[0]) + neg(&c[1], &a[1]) +} + func (e *fp2) mul(c, a, b *fe2) { t := e.t - mul(t[1], &a[0], &b[0]) - mul(t[2], &a[1], &b[1]) - add(t[0], &a[0], &a[1]) - add(t[3], &b[0], &b[1]) - sub(&c[0], t[1], t[2]) - addAssign(t[1], t[2]) - mul(t[0], t[0], t[3]) - sub(&c[1], t[0], t[1]) + // Guide to Pairing Based Cryptography + // Algorithm 5.16 + + mul(t[1], &a[0], &b[0]) // a0b0 + mul(t[2], &a[1], &b[1]) // a1b1 + add(t[0], &a[0], &a[1]) // a0 + a1 + add(t[3], &b[0], &b[1]) // b0 + b1 + sub(&c[0], t[1], t[2]) // c0 = a0b0 - a1b1 + addAssign(t[1], t[2]) // a0b0 + a1b1 + mul(t[0], t[0], t[3]) // (a0 + a1)(b0 + b1) + sub(&c[1], t[0], t[1]) // c1 = (a0 + a1)(b0 + b1) - (a0b0 + a1b1) }   func (e *fp2) mulAssign(a, b *fe2) { @@ -147,24 +145,34 @@ }   func (e *fp2) square(c, a *fe2) { t := e.t - ladd(t[0], &a[0], &a[1]) - sub(t[1], &a[0], &a[1]) - ldouble(t[2], &a[0]) - mul(&c[0], t[0], t[1]) - mul(&c[1], t[2], &a[1]) + // Guide to Pairing Based Cryptography + // Algorithm 5.16 + + add(t[0], &a[0], &a[1]) // (a0 + a1) + sub(t[1], &a[0], &a[1]) // (a0 - a1) + double(t[2], &a[0]) // 2a0 + mul(&c[0], t[0], t[1]) // c0 = (a0 + a1)(a0 - a1) + mul(&c[1], t[2], &a[1]) // c1 = 2a0a1 }   func (e *fp2) squareAssign(a *fe2) { t := e.t - ladd(t[0], &a[0], &a[1]) + add(t[0], &a[0], &a[1]) sub(t[1], &a[0], &a[1]) - ldouble(t[2], &a[0]) + double(t[2], &a[0]) mul(&a[0], t[0], t[1]) mul(&a[1], t[2], &a[1]) }   +func (e *fp2) mul0(c, a *fe2, b *fe) { + mul(&c[0], &a[0], b) + mul(&c[1], &a[1], b) +} + func (e *fp2) mulByNonResidue(c, a *fe2) { t := e.t + // c0 = (a0 - a1) + // c1 = (a0 + a1) sub(t[0], &a[0], &a[1]) add(&c[1], &a[0], &a[1]) c[0].set(t[0]) @@ -172,9 +180,11 @@ }   func (e *fp2) mulByB(c, a *fe2) { t := e.t + // c0 = 4a0 - 4a1 + // c1 = 4a0 + 4a1 double(t[0], &a[0]) - double(t[1], &a[1]) doubleAssign(t[0]) + double(t[1], &a[1]) doubleAssign(t[1]) sub(&c[0], t[0], t[1]) add(&c[1], t[0], t[1]) @@ -182,18 +192,70 @@ }   func (e *fp2) inverse(c, a *fe2) { t := e.t - square(t[0], &a[0]) - square(t[1], &a[1]) - addAssign(t[0], t[1]) - inverse(t[0], t[0]) - mul(&c[0], &a[0], t[0]) - mul(t[0], t[0], &a[1]) - neg(&c[1], t[0]) + // Guide to Pairing Based Cryptography + // Algorithm 5.16 + + square(t[0], &a[0]) // a0^2 + square(t[1], &a[1]) // a1^2 + addAssign(t[0], t[1]) // a0^2 + a1^2 + inverse(t[0], t[0]) // (a0^2 + a1^2)^-1 + mul(&c[0], &a[0], t[0]) // c0 = a0(a0^2 + a1^2)^-1 + mul(t[0], t[0], &a[1]) // a1(a0^2 + a1^2)^-1 + neg(&c[1], t[0]) // c1 = a1(a0^2 + a1^2)^-1 }   -func (e *fp2) mulByFq(c, a *fe2, b *fe) { - mul(&c[0], &a[0], b) - mul(&c[1], &a[1], b) +func (e *fp2) inverseBatch(in []fe2) { + + n, N, setFirst := 0, len(in), false + + for i := 0; i < len(in); i++ { + if !in[i].isZero() { + n++ + } + } + if n == 0 { + return + } + + tA := make([]fe2, n) + tB := make([]fe2, n) + + // a, ab, abc, abcd, ... + for i, j := 0, 0; i < N; i++ { + if !in[i].isZero() { + if !setFirst { + setFirst = true + tA[j].set(&in[i]) + } else { + e.mul(&tA[j], &in[i], &tA[j-1]) + } + j = j + 1 + } + } + + // (abcd...)^-1 + e.inverse(&tB[n-1], &tA[n-1]) + + // a^-1, ab^-1, abc^-1, abcd^-1, ... + for i, j := N-1, n-1; j != 0; i-- { + if !in[i].isZero() { + e.mul(&tB[j-1], &tB[j], &in[i]) + j = j - 1 + } + } + + // a^-1, b^-1, c^-1, d^-1 + for i, j := 0, 0; i < N; i++ { + if !in[i].isZero() { + if setFirst { + setFirst = false + in[i].set(&tB[j]) + } else { + e.mul(&in[i], &tA[j-1], &tB[j]) + } + j = j + 1 + } + } }   func (e *fp2) exp(c, a *fe2, s *big.Int) { @@ -207,19 +269,13 @@ } c.set(z) }   -func (e *fp2) frobeniusMap(c, a *fe2, power uint) { - c[0].set(&a[0]) - if power%2 == 1 { - neg(&c[1], &a[1]) - return - } - c[1].set(&a[1]) +func (e *fp2) frobeniusMap1(a *fe2) { + e.conjugate(a, a) }   -func (e *fp2) frobeniusMapAssign(a *fe2, power uint) { - if power%2 == 1 { - neg(&a[1], &a[1]) - return +func (e *fp2) frobeniusMap(a *fe2, power int) { + if power&1 == 1 { + e.conjugate(a, a) } }   @@ -243,10 +299,74 @@ return alpha.equal(u) }   func (e *fp2) isQuadraticNonResidue(a *fe2) bool { - // https://github.com/leovt/constructible/wiki/Taking-Square-Roots-in-quadratic-extension-Fields c0, c1 := new(fe), new(fe) square(c0, &a[0]) square(c1, &a[1]) add(c1, c1, c0) return isQuadraticNonResidue(c1) } + +// faster square root algorith is adapted from blst library +// https://github.com/supranational/blst/blob/master/src/sqrt.c + +func (e *fp2) sqrtBLST(out, inp *fe2) bool { + aa, bb := new(fe), new(fe) + ret := new(fe2) + square(aa, &inp[0]) + square(bb, &inp[1]) + add(aa, aa, bb) + sqrt(aa, aa) + sub(bb, &inp[0], aa) + add(aa, &inp[0], aa) + if aa.isZero() { + aa.set(bb) + } + mul(aa, aa, twoInv) + rsqrt(&ret[0], aa) + ret[1].set(&inp[1]) + mul(&ret[1], &ret[1], twoInv) + mul(&ret[1], &ret[1], &ret[0]) + mul(&ret[0], &ret[0], aa) + return e.sqrtAlignBLST(out, ret, ret, inp) +} + +func (e *fp2) sqrtAlignBLST(out, ret, sqrt, inp *fe2) bool { + + t0, t1 := new(fe2), new(fe2) + coeff := e.one() + e.square(t0, sqrt) + + // + e.sub(t1, t0, inp) + isSqrt := t1.isZero() + + // + e.add(t1, t0, inp) + flag := t1.isZero() + if flag { + coeff.set(sqrtMinus1) + } + isSqrt = flag || isSqrt + + // + sub(&t1[0], &t0[0], &inp[1]) + add(&t1[1], &t0[1], &inp[0]) + flag = t1.isZero() + if flag { + coeff.set(sqrtSqrtMinus1) + } + isSqrt = flag || isSqrt + + // + add(&t1[0], &t0[0], &inp[1]) + sub(&t1[1], &t0[1], &inp[0]) + flag = t1.isZero() + if flag { + + coeff.set(sqrtMinusSqrtMinus1) + } + isSqrt = flag || isSqrt + + e.mul(out, coeff, ret) + return isSqrt +}
diff --git go-ethereum/crypto/bls12381/arithmetic_fallback.go celo/crypto/bls12381/arithmetic_fallback.go index c94ee5ed7b959da012b93f13deb353d166160fae..57daa3c9ad073fd41fab78968ffd0a331dacbebb 100644 --- go-ethereum/crypto/bls12381/arithmetic_fallback.go +++ celo/crypto/bls12381/arithmetic_fallback.go @@ -1,8 +1,7 @@ -// Native go field arithmetic code is generated with 'goff' -// https://github.com/ConsenSys/goff -// Many function signature of field operations are renamed. +//go:build !amd64 || generic +// +build !amd64 generic   -// Copyright 2020 ConsenSys AG +// Copyright 2020 ConsenSys Software Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,23 +15,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License.   -// field modulus q = -// -// 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 -// Code generated by goff DO NOT EDIT -// goff version: v0.1.0 - build: 790f1f56eac432441e043abff8819eacddd1d668 -// fe are assumed to be in Montgomery form in all methods - -// /!\ WARNING /!\ -// this code has not been audited and is provided as-is. In particular, -// there is no security guarantees such as constant time implementation -// or side-channel attack resistance -// /!\ WARNING /!\ - -// Package bls (generated by goff) contains field arithmetics operations - -//go:build !amd64 || (!blsasm && !blsadx) -// +build !amd64 !blsasm,!blsadx +// Code generated by goff (v0.3.5) DO NOT EDIT   package bls12381   @@ -40,440 +23,6 @@ import ( "math/bits" )   -func add(z, x, y *fe) { - var carry uint64 - - z[0], carry = bits.Add64(x[0], y[0], 0) - z[1], carry = bits.Add64(x[1], y[1], carry) - z[2], carry = bits.Add64(x[2], y[2], carry) - z[3], carry = bits.Add64(x[3], y[3], carry) - z[4], carry = bits.Add64(x[4], y[4], carry) - z[5], _ = bits.Add64(x[5], y[5], carry) - - // if z > q --> z -= q - // note: this is NOT constant time - if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { - var b uint64 - z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) - z[1], b = bits.Sub64(z[1], 2210141511517208575, b) - z[2], b = bits.Sub64(z[2], 7435674573564081700, b) - z[3], b = bits.Sub64(z[3], 7239337960414712511, b) - z[4], b = bits.Sub64(z[4], 5412103778470702295, b) - z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) - } -} - -func addAssign(x, y *fe) { - var carry uint64 - - x[0], carry = bits.Add64(x[0], y[0], 0) - x[1], carry = bits.Add64(x[1], y[1], carry) - x[2], carry = bits.Add64(x[2], y[2], carry) - x[3], carry = bits.Add64(x[3], y[3], carry) - x[4], carry = bits.Add64(x[4], y[4], carry) - x[5], _ = bits.Add64(x[5], y[5], carry) - - // if z > q --> z -= q - // note: this is NOT constant time - if !(x[5] < 1873798617647539866 || (x[5] == 1873798617647539866 && (x[4] < 5412103778470702295 || (x[4] == 5412103778470702295 && (x[3] < 7239337960414712511 || (x[3] == 7239337960414712511 && (x[2] < 7435674573564081700 || (x[2] == 7435674573564081700 && (x[1] < 2210141511517208575 || (x[1] == 2210141511517208575 && (x[0] < 13402431016077863595))))))))))) { - var b uint64 - x[0], b = bits.Sub64(x[0], 13402431016077863595, 0) - x[1], b = bits.Sub64(x[1], 2210141511517208575, b) - x[2], b = bits.Sub64(x[2], 7435674573564081700, b) - x[3], b = bits.Sub64(x[3], 7239337960414712511, b) - x[4], b = bits.Sub64(x[4], 5412103778470702295, b) - x[5], _ = bits.Sub64(x[5], 1873798617647539866, b) - } -} - -func ladd(z, x, y *fe) { - var carry uint64 - z[0], carry = bits.Add64(x[0], y[0], 0) - z[1], carry = bits.Add64(x[1], y[1], carry) - z[2], carry = bits.Add64(x[2], y[2], carry) - z[3], carry = bits.Add64(x[3], y[3], carry) - z[4], carry = bits.Add64(x[4], y[4], carry) - z[5], _ = bits.Add64(x[5], y[5], carry) -} - -func laddAssign(x, y *fe) { - var carry uint64 - x[0], carry = bits.Add64(x[0], y[0], 0) - x[1], carry = bits.Add64(x[1], y[1], carry) - x[2], carry = bits.Add64(x[2], y[2], carry) - x[3], carry = bits.Add64(x[3], y[3], carry) - x[4], carry = bits.Add64(x[4], y[4], carry) - x[5], _ = bits.Add64(x[5], y[5], carry) -} - -func double(z, x *fe) { - var carry uint64 - - z[0], carry = bits.Add64(x[0], x[0], 0) - z[1], carry = bits.Add64(x[1], x[1], carry) - z[2], carry = bits.Add64(x[2], x[2], carry) - z[3], carry = bits.Add64(x[3], x[3], carry) - z[4], carry = bits.Add64(x[4], x[4], carry) - z[5], _ = bits.Add64(x[5], x[5], carry) - - // if z > q --> z -= q - // note: this is NOT constant time - if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { - var b uint64 - z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) - z[1], b = bits.Sub64(z[1], 2210141511517208575, b) - z[2], b = bits.Sub64(z[2], 7435674573564081700, b) - z[3], b = bits.Sub64(z[3], 7239337960414712511, b) - z[4], b = bits.Sub64(z[4], 5412103778470702295, b) - z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) - } -} - -func doubleAssign(z *fe) { - var carry uint64 - - z[0], carry = bits.Add64(z[0], z[0], 0) - z[1], carry = bits.Add64(z[1], z[1], carry) - z[2], carry = bits.Add64(z[2], z[2], carry) - z[3], carry = bits.Add64(z[3], z[3], carry) - z[4], carry = bits.Add64(z[4], z[4], carry) - z[5], _ = bits.Add64(z[5], z[5], carry) - - // if z > q --> z -= q - // note: this is NOT constant time - if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { - var b uint64 - z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) - z[1], b = bits.Sub64(z[1], 2210141511517208575, b) - z[2], b = bits.Sub64(z[2], 7435674573564081700, b) - z[3], b = bits.Sub64(z[3], 7239337960414712511, b) - z[4], b = bits.Sub64(z[4], 5412103778470702295, b) - z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) - } -} - -func ldouble(z, x *fe) { - var carry uint64 - - z[0], carry = bits.Add64(x[0], x[0], 0) - z[1], carry = bits.Add64(x[1], x[1], carry) - z[2], carry = bits.Add64(x[2], x[2], carry) - z[3], carry = bits.Add64(x[3], x[3], carry) - z[4], carry = bits.Add64(x[4], x[4], carry) - z[5], _ = bits.Add64(x[5], x[5], carry) -} - -func sub(z, x, y *fe) { - var b uint64 - z[0], b = bits.Sub64(x[0], y[0], 0) - z[1], b = bits.Sub64(x[1], y[1], b) - z[2], b = bits.Sub64(x[2], y[2], b) - z[3], b = bits.Sub64(x[3], y[3], b) - z[4], b = bits.Sub64(x[4], y[4], b) - z[5], b = bits.Sub64(x[5], y[5], b) - if b != 0 { - var c uint64 - z[0], c = bits.Add64(z[0], 13402431016077863595, 0) - z[1], c = bits.Add64(z[1], 2210141511517208575, c) - z[2], c = bits.Add64(z[2], 7435674573564081700, c) - z[3], c = bits.Add64(z[3], 7239337960414712511, c) - z[4], c = bits.Add64(z[4], 5412103778470702295, c) - z[5], _ = bits.Add64(z[5], 1873798617647539866, c) - } -} - -func subAssign(z, x *fe) { - var b uint64 - z[0], b = bits.Sub64(z[0], x[0], 0) - z[1], b = bits.Sub64(z[1], x[1], b) - z[2], b = bits.Sub64(z[2], x[2], b) - z[3], b = bits.Sub64(z[3], x[3], b) - z[4], b = bits.Sub64(z[4], x[4], b) - z[5], b = bits.Sub64(z[5], x[5], b) - if b != 0 { - var c uint64 - z[0], c = bits.Add64(z[0], 13402431016077863595, 0) - z[1], c = bits.Add64(z[1], 2210141511517208575, c) - z[2], c = bits.Add64(z[2], 7435674573564081700, c) - z[3], c = bits.Add64(z[3], 7239337960414712511, c) - z[4], c = bits.Add64(z[4], 5412103778470702295, c) - z[5], _ = bits.Add64(z[5], 1873798617647539866, c) - } -} - -func lsubAssign(z, x *fe) { - var b uint64 - z[0], b = bits.Sub64(z[0], x[0], 0) - z[1], b = bits.Sub64(z[1], x[1], b) - z[2], b = bits.Sub64(z[2], x[2], b) - z[3], b = bits.Sub64(z[3], x[3], b) - z[4], b = bits.Sub64(z[4], x[4], b) - z[5], _ = bits.Sub64(z[5], x[5], b) -} - -func neg(z *fe, x *fe) { - if x.isZero() { - z.zero() - return - } - var borrow uint64 - z[0], borrow = bits.Sub64(13402431016077863595, x[0], 0) - z[1], borrow = bits.Sub64(2210141511517208575, x[1], borrow) - z[2], borrow = bits.Sub64(7435674573564081700, x[2], borrow) - z[3], borrow = bits.Sub64(7239337960414712511, x[3], borrow) - z[4], borrow = bits.Sub64(5412103778470702295, x[4], borrow) - z[5], _ = bits.Sub64(1873798617647539866, x[5], borrow) -} - -func mul(z, x, y *fe) { - var t [6]uint64 - var c [3]uint64 - { - // round 0 - v := x[0] - c[1], c[0] = bits.Mul64(v, y[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd1(v, y[1], c[1]) - c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd1(v, y[2], c[1]) - c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd1(v, y[3], c[1]) - c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd1(v, y[4], c[1]) - c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd1(v, y[5], c[1]) - t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - { - // round 1 - v := x[1] - c[1], c[0] = madd1(v, y[0], t[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd2(v, y[1], c[1], t[1]) - c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd2(v, y[2], c[1], t[2]) - c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd2(v, y[3], c[1], t[3]) - c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd2(v, y[4], c[1], t[4]) - c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd2(v, y[5], c[1], t[5]) - t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - { - // round 2 - v := x[2] - c[1], c[0] = madd1(v, y[0], t[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd2(v, y[1], c[1], t[1]) - c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd2(v, y[2], c[1], t[2]) - c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd2(v, y[3], c[1], t[3]) - c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd2(v, y[4], c[1], t[4]) - c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd2(v, y[5], c[1], t[5]) - t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - { - // round 3 - v := x[3] - c[1], c[0] = madd1(v, y[0], t[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd2(v, y[1], c[1], t[1]) - c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd2(v, y[2], c[1], t[2]) - c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd2(v, y[3], c[1], t[3]) - c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd2(v, y[4], c[1], t[4]) - c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd2(v, y[5], c[1], t[5]) - t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - { - // round 4 - v := x[4] - c[1], c[0] = madd1(v, y[0], t[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd2(v, y[1], c[1], t[1]) - c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd2(v, y[2], c[1], t[2]) - c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd2(v, y[3], c[1], t[3]) - c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd2(v, y[4], c[1], t[4]) - c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd2(v, y[5], c[1], t[5]) - t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - { - // round 5 - v := x[5] - c[1], c[0] = madd1(v, y[0], t[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd2(v, y[1], c[1], t[1]) - c[2], z[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd2(v, y[2], c[1], t[2]) - c[2], z[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd2(v, y[3], c[1], t[3]) - c[2], z[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd2(v, y[4], c[1], t[4]) - c[2], z[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd2(v, y[5], c[1], t[5]) - z[5], z[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - - // if z > q --> z -= q - // note: this is NOT constant time - if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { - var b uint64 - z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) - z[1], b = bits.Sub64(z[1], 2210141511517208575, b) - z[2], b = bits.Sub64(z[2], 7435674573564081700, b) - z[3], b = bits.Sub64(z[3], 7239337960414712511, b) - z[4], b = bits.Sub64(z[4], 5412103778470702295, b) - z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) - } -} - -func square(z, x *fe) { - - var p [6]uint64 - - var u, v uint64 - { - // round 0 - u, p[0] = bits.Mul64(x[0], x[0]) - m := p[0] * 9940570264628428797 - C := madd0(m, 13402431016077863595, p[0]) - var t uint64 - t, u, v = madd1sb(x[0], x[1], u) - C, p[0] = madd2(m, 2210141511517208575, v, C) - t, u, v = madd1s(x[0], x[2], t, u) - C, p[1] = madd2(m, 7435674573564081700, v, C) - t, u, v = madd1s(x[0], x[3], t, u) - C, p[2] = madd2(m, 7239337960414712511, v, C) - t, u, v = madd1s(x[0], x[4], t, u) - C, p[3] = madd2(m, 5412103778470702295, v, C) - _, u, v = madd1s(x[0], x[5], t, u) - p[5], p[4] = madd3(m, 1873798617647539866, v, C, u) - } - { - // round 1 - m := p[0] * 9940570264628428797 - C := madd0(m, 13402431016077863595, p[0]) - u, v = madd1(x[1], x[1], p[1]) - C, p[0] = madd2(m, 2210141511517208575, v, C) - var t uint64 - t, u, v = madd2sb(x[1], x[2], p[2], u) - C, p[1] = madd2(m, 7435674573564081700, v, C) - t, u, v = madd2s(x[1], x[3], p[3], t, u) - C, p[2] = madd2(m, 7239337960414712511, v, C) - t, u, v = madd2s(x[1], x[4], p[4], t, u) - C, p[3] = madd2(m, 5412103778470702295, v, C) - _, u, v = madd2s(x[1], x[5], p[5], t, u) - p[5], p[4] = madd3(m, 1873798617647539866, v, C, u) - } - { - // round 2 - m := p[0] * 9940570264628428797 - C := madd0(m, 13402431016077863595, p[0]) - C, p[0] = madd2(m, 2210141511517208575, p[1], C) - u, v = madd1(x[2], x[2], p[2]) - C, p[1] = madd2(m, 7435674573564081700, v, C) - var t uint64 - t, u, v = madd2sb(x[2], x[3], p[3], u) - C, p[2] = madd2(m, 7239337960414712511, v, C) - t, u, v = madd2s(x[2], x[4], p[4], t, u) - C, p[3] = madd2(m, 5412103778470702295, v, C) - _, u, v = madd2s(x[2], x[5], p[5], t, u) - p[5], p[4] = madd3(m, 1873798617647539866, v, C, u) - } - { - // round 3 - m := p[0] * 9940570264628428797 - C := madd0(m, 13402431016077863595, p[0]) - C, p[0] = madd2(m, 2210141511517208575, p[1], C) - C, p[1] = madd2(m, 7435674573564081700, p[2], C) - u, v = madd1(x[3], x[3], p[3]) - C, p[2] = madd2(m, 7239337960414712511, v, C) - var t uint64 - t, u, v = madd2sb(x[3], x[4], p[4], u) - C, p[3] = madd2(m, 5412103778470702295, v, C) - _, u, v = madd2s(x[3], x[5], p[5], t, u) - p[5], p[4] = madd3(m, 1873798617647539866, v, C, u) - } - { - // round 4 - m := p[0] * 9940570264628428797 - C := madd0(m, 13402431016077863595, p[0]) - C, p[0] = madd2(m, 2210141511517208575, p[1], C) - C, p[1] = madd2(m, 7435674573564081700, p[2], C) - C, p[2] = madd2(m, 7239337960414712511, p[3], C) - u, v = madd1(x[4], x[4], p[4]) - C, p[3] = madd2(m, 5412103778470702295, v, C) - _, u, v = madd2sb(x[4], x[5], p[5], u) - p[5], p[4] = madd3(m, 1873798617647539866, v, C, u) - } - { - // round 5 - m := p[0] * 9940570264628428797 - C := madd0(m, 13402431016077863595, p[0]) - C, z[0] = madd2(m, 2210141511517208575, p[1], C) - C, z[1] = madd2(m, 7435674573564081700, p[2], C) - C, z[2] = madd2(m, 7239337960414712511, p[3], C) - C, z[3] = madd2(m, 5412103778470702295, p[4], C) - u, v = madd1(x[5], x[5], p[5]) - z[5], z[4] = madd3(m, 1873798617647539866, v, C, u) - } - - // if z > q --> z -= q - // note: this is NOT constant time - if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { - var b uint64 - z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) - z[1], b = bits.Sub64(z[1], 2210141511517208575, b) - z[2], b = bits.Sub64(z[2], 7435674573564081700, b) - z[3], b = bits.Sub64(z[3], 7239337960414712511, b) - z[4], b = bits.Sub64(z[4], 5412103778470702295, b) - z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) - } -} - -// arith.go -// Copyright 2020 ConsenSys AG -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by goff DO NOT EDIT - -func madd(a, b, t, u, v uint64) (uint64, uint64, uint64) { - var carry uint64 - hi, lo := bits.Mul64(a, b) - v, carry = bits.Add64(lo, v, 0) - u, carry = bits.Add64(hi, u, carry) - t, _ = bits.Add64(t, 0, carry) - return t, u, v -} - // madd0 hi = a*b + c (discards lo bits) func madd0(a, b, c uint64) (hi uint64) { var carry, lo uint64 @@ -499,59 +48,6 @@ hi, lo = bits.Mul64(a, b) c, carry = bits.Add64(c, d, 0) hi, _ = bits.Add64(hi, 0, carry) lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -// madd2s superhi, hi, lo = 2*a*b + c + d + e -func madd2s(a, b, c, d, e uint64) (superhi, hi, lo uint64) { - var carry, sum uint64 - - hi, lo = bits.Mul64(a, b) - lo, carry = bits.Add64(lo, lo, 0) - hi, superhi = bits.Add64(hi, hi, carry) - - sum, carry = bits.Add64(c, e, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, sum, 0) - hi, _ = bits.Add64(hi, 0, carry) - hi, _ = bits.Add64(hi, 0, d) - return -} - -func madd1s(a, b, d, e uint64) (superhi, hi, lo uint64) { - var carry uint64 - - hi, lo = bits.Mul64(a, b) - lo, carry = bits.Add64(lo, lo, 0) - hi, superhi = bits.Add64(hi, hi, carry) - lo, carry = bits.Add64(lo, e, 0) - hi, _ = bits.Add64(hi, 0, carry) - hi, _ = bits.Add64(hi, 0, d) - return -} - -func madd2sb(a, b, c, e uint64) (superhi, hi, lo uint64) { - var carry, sum uint64 - - hi, lo = bits.Mul64(a, b) - lo, carry = bits.Add64(lo, lo, 0) - hi, superhi = bits.Add64(hi, hi, carry) - - sum, carry = bits.Add64(c, e, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, sum, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -func madd1sb(a, b, e uint64) (superhi, hi, lo uint64) { - var carry uint64 - - hi, lo = bits.Mul64(a, b) - lo, carry = bits.Add64(lo, lo, 0) - hi, superhi = bits.Add64(hi, hi, carry) - lo, carry = bits.Add64(lo, e, 0) hi, _ = bits.Add64(hi, 0, carry) return }
diff --git go-ethereum/crypto/bls12381/bls12_381.go celo/crypto/bls12381/bls12_381.go index e204a927d1a92aa46a76a6b8c815925a2551f58b..4b305366b0e5d13fe61de5ec882760080971eecf 100644 --- go-ethereum/crypto/bls12381/bls12_381.go +++ celo/crypto/bls12381/bls12_381.go @@ -1,79 +1,66 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - package bls12381   -/* - Field Constants -*/ +const fpNumberOfLimbs = 6 +const fpByteSize = 48 +const fpBitSize = 381 +const sixWordBitSize = 384 +const fourWordBitSize = 256 +const frBitSize = 255   -// Base field modulus +// Base Field // p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab - -// Size of six words // r = 2 ^ 384   // modulus = p var modulus = fe{0xb9feffffffffaaab, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a}   -var ( - // -p^(-1) mod 2^64 - inp uint64 = 0x89f3fffcfffcfffd - // This value is used in assembly code - _ = inp -) +// -p^(-1) mod 2^64 +var inp uint64 = 0x89f3fffcfffcfffd   -// r mod p +// suppress linter warning: 'inp' is used in assembly code +var _ = inp + +// r1 = r mod p var r1 = &fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}   -// r^2 mod p +// r2 = r^2 mod p var r2 = &fe{ 0xf4df1f341c341746, 0x0a76e6a609d104f1, 0x8de5476c4c95b6d5, 0x67eb88a9939d83c0, 0x9a793e85b519952d, 0x11988fe592cae3aa, }   -// -1 + 0 * u +// negativeOne2 = -1 + 0 * u var negativeOne2 = &fe2{ fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }   -// 2 ^ (-1) +// twoInv = 2^(-1) var twoInv = &fe{0x1804000000015554, 0x855000053ab00001, 0x633cb57c253c276f, 0x6e22d1ec31ebb502, 0xd3916126f2d14ca2, 0x17fbb8571a006596}   -// (p - 3) / 4 +// pMinus3Over4 = (p - 3) / 4 var pMinus3Over4 = bigFromHex("0x680447a8e5ff9a692c6e9ed90d2eb35d91dd2e13ce144afd9cc34a83dac3d8907aaffffac54ffffee7fbfffffffeaaa")   -// (p + 1) / 4 +// pPlus1Over4 = (p + 1) / 4 var pPlus1Over4 = bigFromHex("0x680447a8e5ff9a692c6e9ed90d2eb35d91dd2e13ce144afd9cc34a83dac3d8907aaffffac54ffffee7fbfffffffeaab")   -// (p - 1) / 2 +// pMinus1Over2 = (p - 1) / 2 var pMinus1Over2 = bigFromHex("0xd0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd555")   -// -1 +// nonResidue1 = -1 var nonResidue1 = &fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206}   -// (1 + 1 * u) +// nonResidue2 = (1 + 1 * u) var nonResidue2 = &fe2{ fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, }   -/* - Curve Constants -*/ +// Scalar Field +// q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 + +var q = bigFromHex("0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001") + +// Curve Constants   // b coefficient for G1 var b = &fe{0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x09d645513d83de7e} @@ -84,21 +71,16 @@ fe{0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x09d645513d83de7e}, fe{0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x09d645513d83de7e}, }   -// Curve order -var q = bigFromHex("0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001") - -// Efficient cofactor of G1 -var cofactorEFFG1 = bigFromHex("0xd201000000010001") - -// Efficient cofactor of G2 -var cofactorEFFG2 = bigFromHex("0x0bc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551") - +// G1 generator var g1One = PointG1{ fe{0x5cb38790fd530c16, 0x7817fc679976fff5, 0x154f95c7143ba1c1, 0xf0ae6acdf3d0e747, 0xedce6ecc21dbf440, 0x120177419e0bfb75}, fe{0xbaac93d50ce72271, 0x8c22631a7918fd8e, 0xdd595f13570725ce, 0x51ac582950405194, 0x0e1c8c3fad0059c0, 0x0bbc3efc5008a26a}, fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, }   +var G1One = g1One + +// G2 generator var g2One = PointG2{ fe2{ fe{0xf5f28fa202940a10, 0xb3f5fb2687b4961a, 0xa1a893b53e2ae580, 0x9894999d1a3caee9, 0x6f67b7631863366b, 0x058191924350bcd7}, @@ -114,117 +96,169 @@ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, }   -/* - Frobenious Coeffs -*/ +var G2One = g2One   +// Psi values for faster cofactor clearing + +var psix = fe2{ + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + fe{0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a}, +} + +var psiy = fe2{ + fe{0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0x0bd592fc7d825ec8}, + fe{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0x0e2b7eedbbfd87d2}, +} + +// Frobenius Coeffs + +// z = u + 1 var frobeniusCoeffs61 = [6]fe2{ - fe2{ + // z ^ (( p ^ 0 - 1) / 3) + { fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ (( p ^ 1 - 1) / 3) + { fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741}, }, - fe2{ + // z ^ (( p ^ 2 - 1) / 3) + { fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ (( p ^ 3 - 1) / 3) + { fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, }, - fe2{ + // z ^ (( p ^ 4 - 1) / 3) + { fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ (( p ^ 5 - 1) / 3) + { fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160}, }, }   +// z = u + 1 var frobeniusCoeffs62 = [6]fe2{ - fe2{ + // z ^ (( 2 * p ^ 0 - 2) / 3) + { fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ (( 2 * p ^ 1 - 2) / 3) + { fe{0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ (( 2 * p ^ 2 - 2) / 3) + { fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ (( 2 * p ^ 3 - 2) / 3) + { fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ (( 2 * p ^ 4 - 2) / 3) + { fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ (( 2 * p ^ 5 - 2) / 3) + { fe{0xecfb361b798dba3a, 0xc100ddb891865a2c, 0x0ec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x0110f184e51c5f59}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, }   var frobeniusCoeffs12 = [12]fe2{ - fe2{ + // z = u + 1 + // z ^ ((p ^ 0 - 1) / 6) + { fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ ((p ^ 1 - 1) / 6) + { fe{0x07089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x08f2220fb0fb66eb}, fe{0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf}, }, - fe2{ + // z ^ ((p ^ 2 - 1) / 6) + { fe{0xecfb361b798dba3a, 0xc100ddb891865a2c, 0x0ec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x0110f184e51c5f59}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ ((p ^ 3 - 1) / 6) + { fe{0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0x0bd592fc7d825ec8}, fe{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0x0e2b7eedbbfd87d2}, }, - fe2{ + // z ^ ((p ^ 4 - 1) / 6) + { fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ ((p ^ 5 - 1) / 6) + { fe{0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x0095ba654ed2226b, 0x02e370eccc86f7dd}, fe{0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd}, }, - fe2{ + // z ^ ((p ^ 6 - 1) / 6) + { fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ ((p ^ 7 - 1) / 6) + { fe{0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf}, fe{0x07089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x08f2220fb0fb66eb}, }, - fe2{ + // z ^ ((p ^ 8 - 1) / 6) + { fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ ((p ^ 9 - 1) / 6) + { fe{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0x0e2b7eedbbfd87d2}, fe{0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0x0bd592fc7d825ec8}, }, - fe2{ + // z ^ ((p ^ 10 - 1) / 6) + { fe{0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a}, fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, }, - fe2{ + // z ^ ((p ^ 11 - 1) / 6) + { fe{0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd}, fe{0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x0095ba654ed2226b, 0x02e370eccc86f7dd}, }, }   -/* - x -*/ +// x   -var x = bigFromHex("0xd201000000010000") +// var x = bigFromHex("0xd201000000010000") +var x uint64 = 0xd201000000010000 + +// square root + +var sqrtMinus1 = &fe2{*new(fe).zero(), *new(fe).one()} + +var sqrtSqrtMinus1 = &fe2{ + fe{0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0x0bd592fc7d825ec8}, + fe{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0x0e2b7eedbbfd87d2}, +} + +var sqrtMinusSqrtMinus1 = &fe2{ + fe{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0x0e2b7eedbbfd87d2}, + fe{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0x0e2b7eedbbfd87d2}, +}
diff --git go-ethereum/crypto/bls12381/pairing_test.go celo/crypto/bls12381/pairing_test.go index 77676fe9b1f385cfbc32d2e7f29ecd949937b57f..2c5e444fc227d9d1cc6420f17f572102329e4488 100644 --- go-ethereum/crypto/bls12381/pairing_test.go +++ celo/crypto/bls12381/pairing_test.go @@ -3,8 +3,6 @@ import ( "math/big" "testing" - - "github.com/ethereum/go-ethereum/common" )   func TestPairingExpected(t *testing.T) { @@ -12,19 +10,20 @@ bls := NewPairingEngine() G1, G2 := bls.G1, bls.G2 GT := bls.GT() expected, err := GT.FromBytes( - common.FromHex("" + - "0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b676631" + - "04c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef" + - "03350f55a7aefcd3c31b4fcb6ce5771cc6a0e9786ab5973320c806ad360829107ba810c5a09ffdd9be2291a0c25a99a2" + - "11b8b424cd48bf38fcef68083b0b0ec5c81a93b330ee1a677d0d15ff7b984e8978ef48881e32fac91b93b47333e2ba57" + - "06fba23eb7c5af0d9f80940ca771b6ffd5857baaf222eb95a7d2809d61bfe02e1bfd1b68ff02f0b8102ae1c2d5d5ab1a" + - "19f26337d205fb469cd6bd15c3d5a04dc88784fbb3d0b2dbdea54d43b2b73f2cbb12d58386a8703e0f948226e47ee89d" + - "018107154f25a764bd3c79937a45b84546da634b8f6be14a8061e55cceba478b23f7dacaa35c8ca78beae9624045b4b6" + - "01b2f522473d171391125ba84dc4007cfbf2f8da752f7c74185203fcca589ac719c34dffbbaad8431dad1c1fb597aaa5" + - "193502b86edb8857c273fa075a50512937e0794e1e65a7617c90d8bd66065b1fffe51d7a579973b1315021ec3c19934f" + - "1368bb445c7c2d209703f239689ce34c0378a68e72a6b3b216da0e22a5031b54ddff57309396b38c881c4c849ec23e87" + - "089a1c5b46e5110b86750ec6a532348868a84045483c92b7af5af689452eafabf1a8943e50439f1d59882a98eaa0170f" + - "1250ebd871fc0a92a7b2d83168d0d727272d441befa15c503dd8e90ce98db3e7b6d194f60839c508a84305aaca1789b6", + fromHex( + fpByteSize, + "0x0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b676631", + "0x04c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef", + "0x03350f55a7aefcd3c31b4fcb6ce5771cc6a0e9786ab5973320c806ad360829107ba810c5a09ffdd9be2291a0c25a99a2", + "0x11b8b424cd48bf38fcef68083b0b0ec5c81a93b330ee1a677d0d15ff7b984e8978ef48881e32fac91b93b47333e2ba57", + "0x06fba23eb7c5af0d9f80940ca771b6ffd5857baaf222eb95a7d2809d61bfe02e1bfd1b68ff02f0b8102ae1c2d5d5ab1a", + "0x19f26337d205fb469cd6bd15c3d5a04dc88784fbb3d0b2dbdea54d43b2b73f2cbb12d58386a8703e0f948226e47ee89d", + "0x018107154f25a764bd3c79937a45b84546da634b8f6be14a8061e55cceba478b23f7dacaa35c8ca78beae9624045b4b6", + "0x01b2f522473d171391125ba84dc4007cfbf2f8da752f7c74185203fcca589ac719c34dffbbaad8431dad1c1fb597aaa5", + "0x193502b86edb8857c273fa075a50512937e0794e1e65a7617c90d8bd66065b1fffe51d7a579973b1315021ec3c19934f", + "0x1368bb445c7c2d209703f239689ce34c0378a68e72a6b3b216da0e22a5031b54ddff57309396b38c881c4c849ec23e87", + "0x089a1c5b46e5110b86750ec6a532348868a84045483c92b7af5af689452eafabf1a8943e50439f1d59882a98eaa0170f", + "0x1250ebd871fc0a92a7b2d83168d0d727272d441befa15c503dd8e90ce98db3e7b6d194f60839c508a84305aaca1789b6", ), ) if err != nil { @@ -32,7 +31,7 @@ t.Fatal(err) } r := bls.AddPair(G1.One(), G2.One()).Result() if !r.Equal(expected) { - t.Fatal("bad pairing") + t.Fatal("expected pairing failed") } if !GT.IsValid(r) { t.Fatal("element is not in correct subgroup") @@ -89,19 +88,20 @@ // bls.Reset() { expected, err := GT.FromBytes( - common.FromHex("" + - "0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b676631" + - "04c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef" + - "03350f55a7aefcd3c31b4fcb6ce5771cc6a0e9786ab5973320c806ad360829107ba810c5a09ffdd9be2291a0c25a99a2" + - "11b8b424cd48bf38fcef68083b0b0ec5c81a93b330ee1a677d0d15ff7b984e8978ef48881e32fac91b93b47333e2ba57" + - "06fba23eb7c5af0d9f80940ca771b6ffd5857baaf222eb95a7d2809d61bfe02e1bfd1b68ff02f0b8102ae1c2d5d5ab1a" + - "19f26337d205fb469cd6bd15c3d5a04dc88784fbb3d0b2dbdea54d43b2b73f2cbb12d58386a8703e0f948226e47ee89d" + - "018107154f25a764bd3c79937a45b84546da634b8f6be14a8061e55cceba478b23f7dacaa35c8ca78beae9624045b4b6" + - "01b2f522473d171391125ba84dc4007cfbf2f8da752f7c74185203fcca589ac719c34dffbbaad8431dad1c1fb597aaa5" + - "193502b86edb8857c273fa075a50512937e0794e1e65a7617c90d8bd66065b1fffe51d7a579973b1315021ec3c19934f" + - "1368bb445c7c2d209703f239689ce34c0378a68e72a6b3b216da0e22a5031b54ddff57309396b38c881c4c849ec23e87" + - "089a1c5b46e5110b86750ec6a532348868a84045483c92b7af5af689452eafabf1a8943e50439f1d59882a98eaa0170f" + - "1250ebd871fc0a92a7b2d83168d0d727272d441befa15c503dd8e90ce98db3e7b6d194f60839c508a84305aaca1789b6", + fromHex( + fpByteSize, + "0x0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b676631", + "0x04c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef", + "0x03350f55a7aefcd3c31b4fcb6ce5771cc6a0e9786ab5973320c806ad360829107ba810c5a09ffdd9be2291a0c25a99a2", + "0x11b8b424cd48bf38fcef68083b0b0ec5c81a93b330ee1a677d0d15ff7b984e8978ef48881e32fac91b93b47333e2ba57", + "0x06fba23eb7c5af0d9f80940ca771b6ffd5857baaf222eb95a7d2809d61bfe02e1bfd1b68ff02f0b8102ae1c2d5d5ab1a", + "0x19f26337d205fb469cd6bd15c3d5a04dc88784fbb3d0b2dbdea54d43b2b73f2cbb12d58386a8703e0f948226e47ee89d", + "0x018107154f25a764bd3c79937a45b84546da634b8f6be14a8061e55cceba478b23f7dacaa35c8ca78beae9624045b4b6", + "0x01b2f522473d171391125ba84dc4007cfbf2f8da752f7c74185203fcca589ac719c34dffbbaad8431dad1c1fb597aaa5", + "0x193502b86edb8857c273fa075a50512937e0794e1e65a7617c90d8bd66065b1fffe51d7a579973b1315021ec3c19934f", + "0x1368bb445c7c2d209703f239689ce34c0378a68e72a6b3b216da0e22a5031b54ddff57309396b38c881c4c849ec23e87", + "0x089a1c5b46e5110b86750ec6a532348868a84045483c92b7af5af689452eafabf1a8943e50439f1d59882a98eaa0170f", + "0x1250ebd871fc0a92a7b2d83168d0d727272d441befa15c503dd8e90ce98db3e7b6d194f60839c508a84305aaca1789b6", ), ) if err != nil { @@ -113,7 +113,7 @@ bls.AddPair(g1Zero, g2Zero) bls.AddPair(g1One, g2One) e := bls.Result() if !e.Equal(expected) { - t.Fatal("bad pairing") + t.Fatal("pairing failed") } } } @@ -134,10 +134,10 @@ g2.MulScalar(P2, G2, b) e1 := bls.AddPair(P1, P2).Result() gt.Exp(e0, e0, c) if !e0.Equal(e1) { - t.Fatal("bad pairing, 1") + t.Fatal("pairing failed") } } - // e(a * G1, b * G2) = e((a + b) * G1, G2) + // e(a * G1, b * G2) = e((a * b) * G1, G2) { // scalars a, b := big.NewInt(17), big.NewInt(117) @@ -153,10 +153,10 @@ g2.MulScalar(P2, P2, b) bls.AddPairInv(P1, P2) // should be one if !bls.Check() { - t.Fatal("bad pairing, 2") + t.Fatal("pairing failed") } } - // e(a * G1, b * G2) = e((a + b) * G1, G2) + // e(a * G1, b * G2) = e(G1, (a * b) * G2) { // scalars a, b := big.NewInt(17), big.NewInt(117) @@ -172,7 +172,7 @@ g2.MulScalar(H2, H2, b) bls.AddPairInv(H1, H2) // should be one if !bls.Check() { - t.Fatal("bad pairing, 3") + t.Fatal("pairing failed") } } }
diff --git go-ethereum/crypto/blake2b/blake2b_test.go celo/crypto/blake2b/blake2b_test.go index da6b1acbe7d1aa3c23755baf8bc765284a00bf4b..222d1f7ae8b0d2639a80b5e822bbae9161dbb06d 100644 --- go-ethereum/crypto/blake2b/blake2b_test.go +++ celo/crypto/blake2b/blake2b_test.go @@ -14,14 +14,6 @@ "io" "testing" )   -func fromHex(s string) []byte { - b, err := hex.DecodeString(s) - if err != nil { - panic(err) - } - return b -} - func TestHashes(t *testing.T) { defer func(sse4, avx, avx2 bool) { useSSE4, useAVX, useAVX2 = sse4, avx, avx2
diff --git go-ethereum/crypto/bn256/cloudflare/mul_arm64.h celo/crypto/bn256/cloudflare/mul_arm64.h index d405eb8f7287a6f91088312c3acfe7a4a3dd86de..ff9ad2cd8ff60ae39224cad47ae6c00be28bb8c7 100644 --- go-ethereum/crypto/bn256/cloudflare/mul_arm64.h +++ celo/crypto/bn256/cloudflare/mul_arm64.h @@ -130,4 +130,4 @@ \ CSEL CS, R10, R21, R1 \ CSEL CS, R11, R22, R2 \ CSEL CS, R12, R23, R3 \ - CSEL CS, R13, R24, R4 + CSEL CS, R13, R24, R4 \ No newline at end of file
diff --git go-ethereum/crypto/bn256/cloudflare/main_test.go celo/crypto/bn256/cloudflare/main_test.go index c0c85457becabc0fded7f74593cd80dff35eade8..710f82a2a6d98f29a7e833c8014da75c2657be67 100644 --- go-ethereum/crypto/bn256/cloudflare/main_test.go +++ celo/crypto/bn256/cloudflare/main_test.go @@ -1,9 +1,8 @@ package bn256   import ( + "crypto/rand" "testing" - - "crypto/rand" )   func TestRandomG2Marshal(t *testing.T) {
diff --git go-ethereum/crypto/bn256/cloudflare/lattice_test.go celo/crypto/bn256/cloudflare/lattice_test.go index 4d52ad9b27a353668a5685ac71f2cbef991056b9..50e602cd018e68526d1604684274b1499bdb682b 100644 --- go-ethereum/crypto/bn256/cloudflare/lattice_test.go +++ celo/crypto/bn256/cloudflare/lattice_test.go @@ -2,7 +2,6 @@ package bn256   import ( "crypto/rand" - "testing" )
diff --git go-ethereum/crypto/bn256/cloudflare/gfp_decl.go celo/crypto/bn256/cloudflare/gfp_decl.go index ec4018e88a0c0ca47f38704afd93fcffaaaeec30..89670407fb20109484e284bd738fe1f221fe3fc4 100644 --- go-ethereum/crypto/bn256/cloudflare/gfp_decl.go +++ celo/crypto/bn256/cloudflare/gfp_decl.go @@ -10,7 +10,7 @@ import ( "golang.org/x/sys/cpu" )   -//nolint:varcheck +//nolint:varcheck,unused var hasBMI2 = cpu.X86.HasBMI2   // go:noescape
diff --git go-ethereum/crypto/bn256/cloudflare/optate.go celo/crypto/bn256/cloudflare/optate.go index b71e50e3a21ceff53c98027c54c592b977b4ee06..e8caa7a08656953ebd6306fa78f3efc1f3351b37 100644 --- go-ethereum/crypto/bn256/cloudflare/optate.go +++ celo/crypto/bn256/cloudflare/optate.go @@ -199,9 +199,8 @@ mulLine(ret, a, b, c) r = newR   r2.Square(&minusQ2.y) - a, b, c, newR = lineFunctionAdd(r, minusQ2, bAffine, r2) + a, b, c, _ = lineFunctionAdd(r, minusQ2, bAffine, r2) mulLine(ret, a, b, c) - r = newR   return ret }
diff --git go-ethereum/crypto/bn256/google/main_test.go celo/crypto/bn256/google/main_test.go index c0c85457becabc0fded7f74593cd80dff35eade8..710f82a2a6d98f29a7e833c8014da75c2657be67 100644 --- go-ethereum/crypto/bn256/google/main_test.go +++ celo/crypto/bn256/google/main_test.go @@ -1,9 +1,8 @@ package bn256   import ( + "crypto/rand" "testing" - - "crypto/rand" )   func TestRandomG2Marshal(t *testing.T) {
diff --git go-ethereum/eth/handler_eth_test.go celo/eth/handler_eth_test.go index b787cdcff3ea9b40f28e9cd986179ad95ab97619..4a67bd5468e0f03e35f05f15256fbe22d19ea2b9 100644 --- go-ethereum/eth/handler_eth_test.go +++ celo/eth/handler_eth_test.go @@ -25,7 +25,8 @@ "testing" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/rawdb" @@ -46,15 +47,29 @@ type testEthHandler struct { blockBroadcasts event.Feed txAnnounces event.Feed txBroadcasts event.Feed + chain *core.BlockChain }   -func (h *testEthHandler) Chain() *core.BlockChain { panic("no backing chain") } +func (h *testEthHandler) Chain() *core.BlockChain { return h.chain } func (h *testEthHandler) StateBloom() *trie.SyncBloom { panic("no backing state bloom") } func (h *testEthHandler) TxPool() eth.TxPool { panic("no backing tx pool") } func (h *testEthHandler) AcceptTxs() bool { return true } func (h *testEthHandler) RunPeer(*eth.Peer, eth.Handler) error { panic("not used in tests") } func (h *testEthHandler) PeerInfo(enode.ID) interface{} { panic("not used in tests") }   +func newTestEthHandler() *testEthHandler { + var ( + engine = mockEngine.NewFaker() + testdb = rawdb.NewMemoryDatabase() + ) + (&core.Genesis{Config: params.TestChainConfig}).MustCommit(testdb) + chainAux, err := core.NewBlockChain(testdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + if err != nil { + panic(err) + } + return &testEthHandler{chain: chainAux} +} + func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error { switch packet := packet.(type) { case *eth.NewBlockPacket: @@ -80,21 +95,29 @@ }   // Tests that peers are correctly accepted (or rejected) based on the advertised // fork IDs in the protocol handshake. -func TestForkIDSplit66(t *testing.T) { testForkIDSplit(t, eth.ETH66) } +func TestForkIDSplit67(t *testing.T) { testForkIDSplit(t, istanbul.Celo67) }   func testForkIDSplit(t *testing.T, protocol uint) { t.Parallel()   var ( - engine = ethash.NewFaker() - - configNoFork = &params.ChainConfig{HomesteadBlock: big.NewInt(1)} + engine = mockEngine.NewFaker() + configNoFork = &params.ChainConfig{ + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(1), + } configProFork = &params.ChainConfig{ - HomesteadBlock: big.NewInt(1), - EIP150Block: big.NewInt(2), - EIP155Block: big.NewInt(2), - EIP158Block: big.NewInt(2), - ByzantiumBlock: big.NewInt(3), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(1), + ConstantinopleBlock: big.NewInt(2), + PetersburgBlock: big.NewInt(2), + IstanbulBlock: big.NewInt(3), } dbNoFork = rawdb.NewMemoryDatabase() dbProFork = rawdb.NewMemoryDatabase() @@ -166,7 +189,7 @@ case <-time.After(250 * time.Millisecond): t.Fatalf("frontier nofork <-> profork handler timeout") } } - // Progress into Homestead. Fork's match, so we don't care what the future holds + // Progress into Byzantium. Fork's match, so we don't care what the future holds chainNoFork.InsertChain(blocksNoFork[:1]) chainProFork.InsertChain(blocksProFork[:1])   @@ -191,10 +214,10 @@ for i := 0; i < 2; i++ { select { case err := <-errc: if err != nil { - t.Fatalf("homestead nofork <-> profork failed: %v", err) + t.Fatalf("byzantium nofork <-> profork failed: %v", err) } case <-time.After(250 * time.Millisecond): - t.Fatalf("homestead nofork <-> profork handler timeout") + t.Fatalf("byzantium nofork <-> profork handler timeout") } } // Progress into Spurious. Forks mismatch, signalling differing chains, reject @@ -235,7 +258,7 @@ } }   // Tests that received transactions are added to the local pool. -func TestRecvTransactions66(t *testing.T) { testRecvTransactions(t, eth.ETH66) } +func TestRecvTransactions67(t *testing.T) { testRecvTransactions(t, istanbul.Celo67) }   func testRecvTransactions(t *testing.T, protocol uint) { t.Parallel() @@ -273,7 +296,7 @@ if err := src.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil { t.Fatalf("failed to run protocol handshake") } // Send the transaction to the sink and verify that it's added to the tx pool - tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil) + tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil, nil, nil, nil) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)   if err := src.SendTransactions([]*types.Transaction{tx}); err != nil { @@ -292,7 +315,7 @@ } }   // This test checks that pending transactions are sent. -func TestSendTransactions66(t *testing.T) { testSendTransactions(t, eth.ETH66) } +func TestSendTransactions67(t *testing.T) { testSendTransactions(t, istanbul.Celo67) }   func testSendTransactions(t *testing.T, protocol uint) { t.Parallel() @@ -303,7 +326,7 @@ defer handler.close()   insert := make([]*types.Transaction, 100) for nonce := range insert { - tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, 10240)) + tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil, nil, nil, make([]byte, 10240)) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)   insert[nonce] = tx @@ -335,7 +358,8 @@ t.Fatalf("failed to run protocol handshake") } // After the handshake completes, the source handler should stream the sink // the transactions, subscribe to all inbound network events - backend := new(testEthHandler) + backend := newTestEthHandler() + defer backend.Chain().Stop()   anns := make(chan []common.Hash) annSub := backend.txAnnounces.Subscribe(anns) @@ -351,7 +375,7 @@ // Make sure we get all the transactions on the correct channels seen := make(map[common.Hash]struct{}) for len(seen) < len(insert) { switch protocol { - case 65, 66: + case 66, 67: select { case hashes := <-anns: for _, hash := range hashes { @@ -377,7 +401,7 @@ }   // Tests that transactions get propagated to all attached peers, either via direct // broadcasts or via announcements/retrievals. -func TestTransactionPropagation66(t *testing.T) { testTransactionPropagation(t, eth.ETH66) } +func TestTransactionPropagation67(t *testing.T) { testTransactionPropagation(t, istanbul.Celo67) }   func testTransactionPropagation(t *testing.T, protocol uint) { t.Parallel() @@ -426,7 +450,7 @@ } // Fill the source pool with transactions and wait for them at the sinks txs := make([]*types.Transaction, 1024) for nonce := range txs { - tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil) + tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil, nil, nil, nil) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)   txs[nonce] = tx @@ -517,8 +541,8 @@ p2pLocal, p2pRemote := p2p.MsgPipe() defer p2pLocal.Close() defer p2pRemote.Close()   - local := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pLocal), p2pLocal, handler.txpool) - remote := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pRemote), p2pRemote, handler.txpool) + local := eth.NewPeer(istanbul.Celo67, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pLocal), p2pLocal, handler.txpool) + remote := eth.NewPeer(istanbul.Celo67, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pRemote), p2pRemote, handler.txpool) defer local.Close() defer remote.Close()   @@ -545,7 +569,7 @@ msg, err := p2pRemote.ReadMsg() if err != nil { t.Fatalf("failed to read checkpoint challenge: %v", err) } - request := new(eth.GetBlockHeadersPacket66) + request := new(eth.GetBlockHeadersPacket67) if err := msg.Decode(request); err != nil { t.Fatalf("failed to decode checkpoint challenge: %v", err) } @@ -610,7 +634,8 @@ defer source.close()   sinks := make([]*testEthHandler, peers) for i := 0; i < len(sinks); i++ { - sinks[i] = new(testEthHandler) + sinks[i] = newTestEthHandler() + defer sinks[i].Chain().Stop() } // Interconnect all the sink handlers with the source handler var ( @@ -624,8 +649,8 @@ sourcePipe, sinkPipe := p2p.MsgPipe() defer sourcePipe.Close() defer sinkPipe.Close()   - sourcePeer := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{byte(i)}, "", nil, sourcePipe), sourcePipe, nil) - sinkPeer := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{0}, "", nil, sinkPipe), sinkPipe, nil) + sourcePeer := eth.NewPeer(istanbul.Celo67, p2p.NewPeerPipe(enode.ID{byte(i)}, "", nil, sourcePipe), sourcePipe, nil) + sinkPeer := eth.NewPeer(istanbul.Celo67, p2p.NewPeerPipe(enode.ID{0}, "", nil, sinkPipe), sinkPipe, nil) defer sourcePeer.Close() defer sinkPeer.Close()   @@ -676,7 +701,7 @@ }   // Tests that a propagated malformed block (uncles or transactions don't match // with the hashes in the header) gets discarded and not broadcast forward. -func TestBroadcastMalformedBlock66(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH66) } +func TestBroadcastMalformedBlock67(t *testing.T) { testBroadcastMalformedBlock(t, istanbul.Celo67) }   func testBroadcastMalformedBlock(t *testing.T, protocol uint) { t.Parallel() @@ -709,7 +734,8 @@ t.Fatalf("failed to run protocol handshake") } // After the handshake completes, the source handler should stream the sink // the blocks, subscribe to inbound network events - backend := new(testEthHandler) + backend := newTestEthHandler() + defer backend.Chain().Stop()   blocks := make(chan *types.Block, 1) sub := backend.blockBroadcasts.Subscribe(blocks) @@ -720,17 +746,12 @@ // Create various combinations of malformed blocks head := source.chain.CurrentBlock()   - malformedUncles := head.Header() - malformedUncles.UncleHash[0]++ malformedTransactions := head.Header() malformedTransactions.TxHash[0]++ - malformedEverything := head.Header() - malformedEverything.UncleHash[0]++ - malformedEverything.TxHash[0]++   // Try to broadcast all malformations and ensure they all get discarded - for _, header := range []*types.Header{malformedUncles, malformedTransactions, malformedEverything} { - block := types.NewBlockWithHeader(header).WithBody(head.Transactions(), head.Uncles()) + for _, header := range []*types.Header{malformedTransactions} { + block := types.NewBlockWithHeader(header).WithBody(head.Transactions(), nil, nil) if err := src.SendNewBlock(block, big.NewInt(131136)); err != nil { t.Fatalf("failed to broadcast block: %v", err) }
diff --git go-ethereum/eth/api.go celo/eth/api.go index 21fbe7de592013b7aa0687a69dcba74a0ad6bfa2..6cd32a5ccdeac0875fdb2698f75dc01a969ca3ee 100644 --- go-ethereum/eth/api.go +++ celo/eth/api.go @@ -24,9 +24,7 @@ "fmt" "io" "math/big" "os" - "runtime" "strings" - "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -51,19 +49,28 @@ func NewPublicEthereumAPI(e *Ethereum) *PublicEthereumAPI { return &PublicEthereumAPI{e} }   -// Etherbase is the address that mining rewards will be send to -func (api *PublicEthereumAPI) Etherbase() (common.Address, error) { - return api.e.Etherbase() +// Validator is the address that will sign messages +func (api *PublicEthereumAPI) Validator() (common.Address, error) { + return api.e.Validator() +} + +// TxFeeRecipient is the address that mining rewards will be sent to +func (api *PublicEthereumAPI) TxFeeRecipient() (common.Address, error) { + return api.e.TxFeeRecipient() }   -// Coinbase is the address that mining rewards will be send to (alias for Etherbase) +// Coinbase is the address that mining rewards will be sent to (alias for TxFeeRecipient) func (api *PublicEthereumAPI) Coinbase() (common.Address, error) { - return api.Etherbase() + return api.TxFeeRecipient() }   -// Hashrate returns the POW hashrate -func (api *PublicEthereumAPI) Hashrate() hexutil.Uint64 { - return hexutil.Uint64(api.e.Miner().Hashrate()) +// ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config. +func (api *PublicEthereumAPI) ChainId() hexutil.Uint64 { + chainID := new(big.Int) + if config := api.e.blockchain.Config(); config.IsEIP155(api.e.blockchain.CurrentBlock().Number()) { + chainID = config.ChainID + } + return (hexutil.Uint64)(chainID.Uint64()) }   // PublicMinerAPI provides an API to control the miner. @@ -93,16 +100,13 @@ func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI { return &PrivateMinerAPI{e: e} }   -// Start starts the miner with the given number of threads. If threads is nil, -// the number of workers started is equal to the number of logical CPUs that are -// usable by this process. If mining is already running, this method adjust the -// number of threads allowed to use and updates the minimum price required by the -// transaction pool. -func (api *PrivateMinerAPI) Start(threads *int) error { - if threads == nil { - return api.e.StartMining(runtime.NumCPU()) +// Start starts the miner +func (api *PrivateMinerAPI) Start() error { + if api.e.config.Istanbul.Proxy { + return errors.New("Can't mine if node is a proxy") } - return api.e.StartMining(*threads) + + return api.e.StartMining() }   // Stop terminates the miner, both at the consensus engine level as well as at @@ -121,29 +125,15 @@ }   // SetGasPrice sets the minimum accepted gas price for the miner. func (api *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool { - api.e.lock.Lock() - api.e.gasPrice = (*big.Int)(&gasPrice) - api.e.lock.Unlock() - api.e.txPool.SetGasPrice((*big.Int)(&gasPrice)) return true }   -// SetGasLimit sets the gaslimit to target towards during mining. -func (api *PrivateMinerAPI) SetGasLimit(gasLimit hexutil.Uint64) bool { - api.e.Miner().SetGasCeil(uint64(gasLimit)) - return true -} - // SetEtherbase sets the etherbase of the miner func (api *PrivateMinerAPI) SetEtherbase(etherbase common.Address) bool { - api.e.SetEtherbase(etherbase) + api.e.SetValidator(etherbase) + api.e.SetTxFeeRecipient(etherbase) return true -} - -// SetRecommitInterval updates the interval for miner sealing work recommitting. -func (api *PrivateMinerAPI) SetRecommitInterval(interval int) { - api.e.Miner().SetRecommitInterval(time.Duration(interval) * time.Millisecond) }   // PrivateAdminAPI is the collection of Ethereum full node-related APIs @@ -342,7 +332,11 @@ blockRlp = err.Error() // Hacky, but hey, it works } else { blockRlp = fmt.Sprintf("0x%x", rlpBytes) } - if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true); err != nil { + baseFeeFn := func() (*big.Int, error) { + // We don't have information, so we return nil + return nil, nil + } + if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true, baseFeeFn); err != nil { blockJSON = map[string]interface{}{"error": err.Error()} } results = append(results, &BadBlockArgs{ @@ -429,7 +423,7 @@ block := api.eth.blockchain.GetBlockByHash(blockHash) if block == nil { return StorageRangeResult{}, fmt.Errorf("block %#x not found", blockHash) } - _, _, statedb, err := api.eth.stateAtTransaction(block, txIndex, 0) + _, _, _, statedb, err := api.eth.stateAtTransaction(block, txIndex, 0) if err != nil { return StorageRangeResult{}, err }
diff --git go-ethereum/eth/handler_test.go celo/eth/handler_test.go index 20f733cb16a44e952a0e0c9aeccf932d82bc8e6a..e32aaba41e6ce3f3c71d23115e0837bdccdb3c77 100644 --- go-ethereum/eth/handler_test.go +++ celo/eth/handler_test.go @@ -22,7 +22,7 @@ "sort" "sync"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -136,10 +136,10 @@ (&core.Genesis{ Config: params.TestChainConfig, Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}}, }).MustCommit(db) - - chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + engine := mockEngine.NewFaker() + chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)   - bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, nil) + bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), engine, db, blocks, nil) if _, err := chain.InsertChain(bs); err != nil { panic(err) }
diff --git go-ethereum/eth/sync.go celo/eth/sync.go index def0df3ab8b4c80a353f2025676922af56dc48d9..84c6ad79cf23730d8e851991e28d4c125c13a0b5 100644 --- go-ethereum/eth/sync.go +++ celo/eth/sync.go @@ -50,7 +50,7 @@ } if len(txs) == 0 { return } - // The eth/65 protocol introduces proper transaction announcements, so instead + // The eth/65 (celo/66) protocol introduces proper transaction announcements, so instead // of dripping transactions across multiple peers, just send the entire list as // an announcement and let the remote side decide what they need (likely nothing). hashes := make([]common.Hash, len(txs)) @@ -67,6 +67,9 @@ force *time.Timer forced bool // true when force timer fired peerEventCh chan struct{} doneCh chan error // non-nil when sync is running + + // The minimum number of peers required to initiate a sync. + minSyncPeers int }   // chainSyncOp is a scheduled sync operation. @@ -77,11 +80,17 @@ td *big.Int head common.Hash }   -// newChainSyncer creates a chainSyncer. -func newChainSyncer(handler *handler) *chainSyncer { +// newChainSyncer creates a chainSyncer, specifying a protocol manager and the +// minimum number of peers required to sync, if minSyncPeers is 0 then the +// default value is used. +func newChainSyncer(handler *handler, minSyncPeers int) *chainSyncer { + if minSyncPeers == 0 { + minSyncPeers = defaultMinSyncPeers + } return &chainSyncer{ handler: handler, peerEventCh: make(chan struct{}), + minSyncPeers: minSyncPeers, } }   @@ -147,7 +156,7 @@ return nil // Sync already running. }   // Ensure we're at minimum peer count. - minPeers := defaultMinSyncPeers + minPeers := cs.minSyncPeers if cs.forced { minPeers = 1 } else if minPeers > cs.handler.maxPeers {
diff --git go-ethereum/eth/state_accessor.go celo/eth/state_accessor.go index 830ea3540725f5af400868eb76881133675a3183..5d295b7a51c4a7ed7aa9bd81a126ae264e11e694 100644 --- go-ethereum/eth/state_accessor.go +++ celo/eth/state_accessor.go @@ -19,6 +19,7 @@ import ( "errors" "fmt" + "math/big" "time"   "github.com/ethereum/go-ethereum/common" @@ -140,44 +141,55 @@ return statedb, nil }   // stateAtTransaction returns the execution environment of a certain transaction. -func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { +func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, vm.EVMRunner, *state.StateDB, error) { // Short circuit if it's genesis block. if block.NumberU64() == 0 { - return nil, vm.BlockContext{}, nil, errors.New("no transaction in genesis") + return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis") } // Create the parent state database parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { - return nil, vm.BlockContext{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash()) + return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("parent %#x not found", block.ParentHash()) } // Lookup the statedb of parent block from the live database, // otherwise regenerate it on the flight. statedb, err := eth.stateAtBlock(parent, reexec, nil, true) if err != nil { - return nil, vm.BlockContext{}, nil, err + return nil, vm.BlockContext{}, nil, nil, err } if txIndex == 0 && len(block.Transactions()) == 0 { - return nil, vm.BlockContext{}, statedb, nil + return nil, vm.BlockContext{}, nil, statedb, nil + } + // Get the SysContractCallCtx + var sysCtx *core.SysContractCallCtx + espresso := eth.blockchain.Config().IsEspresso(block.Number()) + if espresso { + sysCtx = core.NewSysContractCallCtx(block.Header(), statedb, eth.blockchain) } // Recompute transactions up to the target index. signer := types.MakeSigner(eth.blockchain.Config(), block.Number()) for idx, tx := range block.Transactions() { // Assemble the transaction call message and return if the requested offset - msg, _ := tx.AsMessage(signer, block.BaseFee()) + var baseFee *big.Int + if espresso { + baseFee = sysCtx.GetGasPriceMinimum(tx.FeeCurrency()) + } + msg, _ := tx.AsMessage(signer, baseFee) txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) + vmRunner := eth.blockchain.NewEVMRunner(block.Header(), statedb) if idx == txIndex { - return msg, context, statedb, nil + return msg, context, vmRunner, statedb, nil } // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{}) statedb.Prepare(tx.Hash(), idx) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { - return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner, sysCtx); err != nil { + return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } // Ensure any modifications are committed to the state // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) } - return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) + return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) }
diff --git go-ethereum/eth/handler_eth.go celo/eth/handler_eth.go index adfb4abd5104ab29b7ea3252503965a10c3d8240..81f4e797e9ee162bf0a27b6911b227225f58b937 100644 --- go-ethereum/eth/handler_eth.go +++ celo/eth/handler_eth.go @@ -68,8 +68,8 @@ case *eth.BlockHeadersPacket: return h.handleHeaders(peer, *packet)   case *eth.BlockBodiesPacket: - txset, uncleset := packet.Unpack() - return h.handleBodies(peer, txset, uncleset) + blockHashes, transactions, randomness, epochSnarkData := packet.Unpack() + return h.handleBodies(peer, blockHashes, transactions, randomness, epochSnarkData)   case *eth.NodeDataPacket: if err := h.downloader.DeliverNodeData(peer.ID(), *packet); err != nil { @@ -162,14 +162,14 @@ }   // handleBodies is invoked from a peer's message handler when it transmits a batch // of block bodies for the local node to process. -func (h *ethHandler) handleBodies(peer *eth.Peer, txs [][]*types.Transaction, uncles [][]*types.Header) error { +func (h *ethHandler) handleBodies(peer *eth.Peer, blockHashes []common.Hash, transactions [][]*types.Transaction, randomness []*types.Randomness, epochSnarkData []*types.EpochSnarkData) error { // Filter out any explicitly requested bodies, deliver the rest to the downloader - filter := len(txs) > 0 || len(uncles) > 0 + filter := len(blockHashes) > 0 || len(transactions) > 0 || len(randomness) > 0 || len(epochSnarkData) > 0 if filter { - txs, uncles = h.blockFetcher.FilterBodies(peer.ID(), txs, uncles, time.Now()) + blockHashes, transactions, randomness, epochSnarkData = h.blockFetcher.FilterBodies(peer.ID(), blockHashes, transactions, randomness, epochSnarkData, time.Now()) } - if len(txs) > 0 || len(uncles) > 0 || !filter { - err := h.downloader.DeliverBodies(peer.ID(), txs, uncles) + if len(blockHashes) > 0 || len(transactions) > 0 || len(randomness) > 0 || len(epochSnarkData) > 0 || !filter { + err := h.downloader.DeliverBodies(peer.ID(), transactions, randomness, epochSnarkData) if err != nil { log.Debug("Failed to deliver bodies", "err", err) } @@ -207,7 +207,7 @@ // Assuming the block is importable by the peer, but possibly not yet done so, // calculate the head hash and TD that the peer truly must have. var ( trueHead = block.ParentHash() - trueTD = new(big.Int).Sub(td, block.Difficulty()) + trueTD = new(big.Int).Sub(td, big.NewInt(1)) ) // Update the peer's total difficulty if better than the previous if _, td := peer.Head(); trueTD.Cmp(td) > 0 {
diff --git go-ethereum/eth/handler.go celo/eth/handler.go index b83b5444edbbc1cc4234993c8a18cc55074297f5..65c29c3cbce4cfbaba719235497eaaaa7d8525ce 100644 --- go-ethereum/eth/handler.go +++ celo/eth/handler.go @@ -25,6 +25,7 @@ "sync/atomic" "time"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/types" @@ -36,6 +37,7 @@ "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" ) @@ -85,6 +87,9 @@ BloomCache uint64 // Megabytes to alloc for fast sync bloom EventMux *event.TypeMux // Legacy event mux, deprecate for `feed` Checkpoint *params.TrustedCheckpoint // Hard coded checkpoint for sync challenges Whitelist map[uint64]common.Hash // Hard coded whitelist for sync challenged + server *p2p.Server + proxyServer *p2p.Server + MinSyncPeers int // The minimum peers required to sstart syncing }   type handler struct { @@ -122,9 +127,11 @@ chainSync *chainSyncer wg sync.WaitGroup peerWG sync.WaitGroup + + server *p2p.Server + proxyServer *p2p.Server }   -// newHandler returns a handler for all Ethereum chain management protocol. func newHandler(config *handlerConfig) (*handler, error) { // Create the protocol manager with the base fields if config.EventMux == nil { @@ -140,6 +147,13 @@ chain: config.Chain, peers: newPeerSet(), whitelist: config.Whitelist, quitSync: make(chan struct{}), + server: config.server, + proxyServer: config.proxyServer, + } + + if consensusHandler, ok := h.chain.Engine().(consensus.Handler); ok { + consensusHandler.SetBroadcaster(h) + consensusHandler.SetP2PServer(h.server) } if config.Sync == downloader.FullSync { // The database seems empty as the current block is the genesis. Yet the fast @@ -227,7 +241,7 @@ } return p.RequestTxs(hashes) } h.txFetcher = fetcher.NewTxFetcher(h.txpool.Has, h.txpool.AddRemotes, fetchTx) - h.chainSync = newChainSyncer(h) + h.chainSync = newChainSyncer(h, config.MinSyncPeers) return h, nil }   @@ -260,6 +274,35 @@ forkID := forkid.NewID(h.chain.Config(), h.chain.Genesis().Hash(), h.chain.CurrentHeader().Number.Uint64()) if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil { peer.Log().Debug("Ethereum handshake failed", "err", err) return err + } + forcePeer := false + if handler, ok := h.chain.Engine().(consensus.Handler); ok { + isValidator, err := handler.Handshake(peer) + if err != nil { + peer.Log().Warn("Istanbul handshake failed", "err", err) + return err + } + forcePeer = isValidator + peer.Log().Debug("Peer completed Istanbul handshake", "forcePeer", forcePeer) + } + // Ignore max peer and max inbound peer check if: + // - this is a trusted or statically dialed peer + // - the peer is from from the proxy server (e.g. peers connected to this node's internal network interface) + // - forcePeer is true + if !forcePeer { + // KJUE - Remove the server not nil check after restoring peer check in server.go + if peer.Peer.Server != nil { + if err := peer.Peer.Server.CheckPeerCounts(peer.Peer); err != nil { + return err + } + } + // The p2p server CheckPeerCounts only checks if the total peer count + // (eth and les) exceeds the total max peers. This checks if the number + // of eth peers exceeds the eth max peers. + isStaticOrTrusted := peer.Peer.Info().Network.Trusted || peer.Peer.Info().Network.Static + if !isStaticOrTrusted && h.peers.len() >= h.maxPeers && peer.Peer.Server != h.proxyServer { + return p2p.DiscTooManyPeers + } } reject := false // reserved peer slots if atomic.LoadUint32(&h.snapSync) == 1 { @@ -296,6 +339,13 @@ if err := h.downloader.RegisterPeer(peer.ID(), peer.Version(), peer); err != nil { peer.Log().Error("Failed to register peer in eth syncer", "err", err) return err } + // Register the peer with the consensus engine. + if handler, ok := h.chain.Engine().(consensus.Handler); ok { + if err := handler.RegisterPeer(peer, peer.Peer.Server == h.proxyServer); err != nil { + return err + } + } + if snap != nil { if err := h.downloader.SnapSyncer.Register(snap); err != nil { peer.Log().Error("Failed to register peer in snap syncer", "err", err) @@ -354,14 +404,14 @@ }   // removePeer requests disconnection of a peer. func (h *handler) removePeer(id string) { - peer := h.peers.peer(id) + peer := h.unregisterPeer(id) if peer != nil { peer.Peer.Disconnect(p2p.DiscUselessPeer) } }   // unregisterPeer removes a peer from the downloader, fetchers and main peer set. -func (h *handler) unregisterPeer(id string) { +func (h *handler) unregisterPeer(id string) *ethPeer { // Create a custom logger to avoid printing the entire id var logger log.Logger if len(id) < 16 { @@ -374,7 +424,7 @@ // Abort if the peer does not exist peer := h.peers.peer(id) if peer == nil { logger.Error("Ethereum peer removal failed", "err", errPeerNotRegistered) - return + return nil } // Remove the `eth` peer if it exists logger.Debug("Removing Ethereum peer", "snap", peer.snapExt != nil) @@ -383,12 +433,21 @@ // Remove the `snap` extension if it exists if peer.snapExt != nil { h.downloader.SnapSyncer.Unregister(id) } - h.downloader.UnregisterPeer(id) - h.txFetcher.Drop(id) - + // Unregister the peer from the downloader, tx fetcher, consensus engine, and Ethereum peer set + if err := h.downloader.UnregisterPeer(id); err != nil { + log.Error("Peer removal from downloader failed", "peer", id, "err", err) + } + if err := h.txFetcher.Drop(id); err != nil { + log.Error("Peer removal from tx fetcher failed", "peer", id, "err", err) + } + if handler, ok := h.chain.Engine().(consensus.Handler); ok { + handler.UnregisterPeer(peer, peer.Peer.Server == h.proxyServer) + } if err := h.peers.unregisterPeer(id); err != nil { logger.Error("Ethereum peer removal failed", "err", err) } + + return peer }   func (h *handler) Start(maxPeers int) { @@ -429,6 +488,10 @@ log.Info("Ethereum protocol stopped") }   +func (h *handler) Enqueue(id string, block *types.Block) { + h.blockFetcher.Enqueue(id, block) +} + // BroadcastBlock will either propagate a block to a subset of its peers, or // will only announce its availability (depending what's requested). func (h *handler) BroadcastBlock(block *types.Block, propagate bool) { @@ -440,7 +503,7 @@ if propagate { // Calculate the TD of the block (it's not imported yet, so block.Td is not valid) var td *big.Int if parent := h.chain.GetBlock(block.ParentHash(), block.NumberU64()-1); parent != nil { - td = new(big.Int).Add(block.Difficulty(), h.chain.GetTd(block.ParentHash(), block.NumberU64()-1)) + td = new(big.Int).Add(big.NewInt(1), h.chain.GetTd(block.ParentHash(), block.NumberU64()-1)) } else { log.Error("Propagating dangling block", "number", block.Number(), "hash", hash) return @@ -529,3 +592,16 @@ return } } } + +func (h *handler) FindPeers(targets map[enode.ID]bool, purpose p2p.PurposeFlag) map[enode.ID]consensus.Peer { + m := make(map[enode.ID]consensus.Peer) + for _, p := range h.peers.Peers() { + id := p.Node().ID() + if targets[id] || (targets == nil) { + if p.PurposeIsSet(purpose) { + m[id] = p + } + } + } + return m +}
diff --git go-ethereum/eth/peerset.go celo/eth/peerset.go index 7ded147c6ea2afc3c131c16ee14d50590adf6549..d00377b51a78781a3bfd5685e4938e41db20e68e 100644 --- go-ethereum/eth/peerset.go +++ celo/eth/peerset.go @@ -22,6 +22,7 @@ "math/big" "sync"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/p2p" @@ -73,7 +74,7 @@ // being until the `eth` main protocol starts looking for it. func (ps *peerSet) registerSnapExtension(peer *snap.Peer) error { // Reject the peer if it advertises `snap` without `eth` as `snap` is only a // satellite protocol meaningful with the chain selection of `eth` - if !peer.RunningCap(eth.ProtocolName, eth.ProtocolVersions) { + if !peer.RunningCap(eth.ProtocolName, istanbul.ProtocolVersions) { return errSnapWithoutEth } // Ensure nobody can double connect @@ -245,6 +246,18 @@ bestPeer, bestTd = p.Peer, td } } return bestPeer +} + +// Peers returns all registered peers +func (ps *peerSet) Peers() map[string]*ethPeer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + set := make(map[string]*ethPeer) + for id, p := range ps.peers { + set[id] = p + } + return set }   // close disconnects all peers.
diff --git go-ethereum/eth/sync_test.go celo/eth/sync_test.go index e96b9ee81f6911e53b174201be42f8349052f847..1e90e69f3d47eecf805827e34e10ae9bdc9381fc 100644 --- go-ethereum/eth/sync_test.go +++ celo/eth/sync_test.go @@ -21,6 +21,7 @@ "sync/atomic" "testing" "time"   + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/p2p" @@ -28,7 +29,7 @@ "github.com/ethereum/go-ethereum/p2p/enode" )   // Tests that fast sync is disabled after a successful sync cycle. -func TestFastSyncDisabling66(t *testing.T) { testFastSyncDisabling(t, eth.ETH66) } +func TestFastSyncDisabling67(t *testing.T) { testFastSyncDisabling(t, istanbul.Celo67) }   // Tests that fast sync gets disabled as soon as a real block is successfully // imported into the blockchain.
diff --git go-ethereum/eth/api_backend.go celo/eth/api_backend.go index ed08c1d5cced56ba20c7189c07c802f8ded571dc..c075b7af9a6d6f3b9280241430c1f2b1b3d5be4e 100644 --- go-ethereum/eth/api_backend.go +++ celo/eth/api_backend.go @@ -19,21 +19,24 @@ import ( "context" "errors" + "fmt" "math/big"   "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" + gpm "github.com/ethereum/go-ethereum/contracts/gasprice_minimum" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -44,7 +47,6 @@ type EthAPIBackend struct { extRPCEnabled bool allowUnprotectedTxs bool eth *Ethereum - gpo *gasprice.Oracle }   // ChainConfig returns the active chain configuration. @@ -141,6 +143,9 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { // Pending state is only known by the miner if number == rpc.PendingBlockNumber { block, state := b.eth.miner.Pending() + if block == nil && state == nil { + return nil, nil, errors.New("no pending block") + } return state, block.Header(), nil } // Otherwise resolve the block number and return its state @@ -188,6 +193,8 @@ return nil, errors.New("failed to get block number from hash") } logs := rawdb.ReadLogs(db, hash, *number) if logs == nil { + // Even if this changes the behaviour of the old rpc call (was returning an empty list, not an error) which we try to avoid + // we decided to keep the error to maintain tooling compatibility with upstream return nil, errors.New("failed to get logs for block") } return logs, nil @@ -284,12 +291,84 @@ func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress { return b.eth.Downloader().Progress() }   -func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestTipCap(ctx) +func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) { + vmRunner, err := b.eth.BlockChain().NewEVMRunnerForCurrentBlock() + if err != nil { + return nil, err + } + return gpm.GetGasTipCapSuggestion(vmRunner, currencyAddress) +} + +func (b *EthAPIBackend) CurrentGasPriceMinimum(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) { + vmRunner, err := b.eth.BlockChain().NewEVMRunnerForCurrentBlock() + if err != nil { + return nil, err + } + return gpm.GetGasPriceMinimum(vmRunner, currencyAddress) +} + +func (b *EthAPIBackend) GasPriceMinimumForHeader(ctx context.Context, currencyAddress *common.Address, header *types.Header) (*big.Int, error) { + state, err := b.eth.blockchain.StateAt(header.Root) + if err != nil { + return nil, err + } + vmRunner := b.eth.BlockChain().NewEVMRunner(header, state) + return gpm.GetGasPriceMinimum(vmRunner, currencyAddress) +} + +func (b *EthAPIBackend) RealGasPriceMinimumForHeader(ctx context.Context, currencyAddress *common.Address, header *types.Header) (*big.Int, error) { + state, err := b.eth.blockchain.StateAt(header.Root) + if err != nil { + return nil, err + } + vmRunner := b.eth.BlockChain().NewEVMRunner(header, state) + return gpm.GetRealGasPriceMinimum(vmRunner, currencyAddress) +} + +func (b *EthAPIBackend) SuggestPrice(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) { + vmRunner, err := b.eth.BlockChain().NewEVMRunnerForCurrentBlock() + if err != nil { + return nil, err + } + return gpm.GetGasPriceSuggestion(vmRunner, currencyAddress) +} + +func (b *EthAPIBackend) GetBlockGasLimit(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) uint64 { + statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if err != nil { + log.Warn("Cannot create evmCaller to get blockGasLimit", "err", err) + return params.DefaultGasLimit + } + + vmRunner := b.eth.BlockChain().NewEVMRunner(header, statedb) + return blockchain_parameters.GetBlockGasLimitOrDefault(vmRunner) }   -func (b *EthAPIBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { - return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) +func (b *EthAPIBackend) GetRealBlockGasLimit(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (uint64, error) { + statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if err != nil { + return 0, fmt.Errorf("EthApiBackend failed to retrieve state for block gas limit for block %v: %w", blockNrOrHash, err) + } + + caller := b.eth.BlockChain().NewEVMRunner(header, statedb) + limit, err := blockchain_parameters.GetBlockGasLimit(caller) + if err != nil { + return 0, fmt.Errorf("EthApiBackend failed to retrieve block gas limit from blockchain parameters constract for block %v: %w", blockNrOrHash, err) + } + return limit, nil +} + +func (b *EthAPIBackend) NewEVMRunner(header *types.Header, state vm.StateDB) vm.EVMRunner { + return b.eth.BlockChain().NewEVMRunner(header, state) +} + +func (b *EthAPIBackend) GetIntrinsicGasForAlternativeFeeCurrency(ctx context.Context) uint64 { + vmRunner, err := b.eth.BlockChain().NewEVMRunnerForCurrentBlock() + if err != nil { + log.Warn("Cannot create evmCaller to get intrinsic gas for alternative fee currency", "err", err) + return blockchain_parameters.DefaultIntrinsicGasForAlternativeFeeCurrency + } + return blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(vmRunner) }   func (b *EthAPIBackend) ChainDb() ethdb.Database { @@ -312,8 +391,16 @@ func (b *EthAPIBackend) UnprotectedAllowed() bool { return b.allowUnprotectedTxs }   +func (b *EthAPIBackend) RPCGasInflationRate() float64 { + return b.eth.config.RPCGasInflationRate +} + func (b *EthAPIBackend) RPCGasCap() uint64 { return b.eth.config.RPCGasCap +} + +func (b *EthAPIBackend) RPCEthCompatibility() bool { + return b.eth.config.RPCEthCompatibility }   func (b *EthAPIBackend) RPCTxFeeCap() float64 { @@ -331,6 +418,14 @@ go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests) } }   +func (b *EthAPIBackend) GatewayFeeRecipient() common.Address { + return b.eth.GatewayFeeRecipient() +} + +func (b *EthAPIBackend) GatewayFee() *big.Int { + return b.eth.GatewayFee() +} + func (b *EthAPIBackend) Engine() consensus.Engine { return b.eth.engine } @@ -343,14 +438,14 @@ func (b *EthAPIBackend) Miner() *miner.Miner { return b.eth.Miner() }   -func (b *EthAPIBackend) StartMining(threads int) error { - return b.eth.StartMining(threads) +func (b *EthAPIBackend) StartMining() error { + return b.eth.StartMining() }   func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) { return b.eth.stateAtBlock(block, reexec, base, checkLive) }   -func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { +func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, vm.EVMRunner, *state.StateDB, error) { return b.eth.stateAtTransaction(block, txIndex, reexec) }
diff --git go-ethereum/eth/backend.go celo/eth/backend.go index dc7c2782fad7b57cd7faf53adfae222cdf90fb83..33b0ffb297a1880bc261425b987308ab5bbec236 100644 --- go-ethereum/eth/backend.go +++ celo/eth/backend.go @@ -30,19 +30,21 @@ "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/clique" + "github.com/ethereum/go-ethereum/consensus/istanbul" + istanbulBackend "github.com/ethereum/go-ethereum/consensus/istanbul/backend" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state/pruner" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/filters" - "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/protocols/eth" - "github.com/ethereum/go-ethereum/eth/protocols/snap" + + // "github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -86,30 +88,28 @@ APIBackend *EthAPIBackend   miner *miner.Miner - gasPrice *big.Int - etherbase common.Address + gatewayFee *big.Int + validator common.Address + txFeeRecipient common.Address + blsbase common.Address   networkID uint64 netRPCService *ethapi.PublicNetAPI   p2pServer *p2p.Server   - lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase) + lock sync.RWMutex // Protects the variadic fields (e.g. gas price, validator and txFeeRecipient) }   // New creates a new Ethereum object (including the // initialisation of the common Ethereum object) func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { // Ensure configuration values are compatible and sane - if config.SyncMode == downloader.LightSync { - return nil, errors.New("can't run eth.Ethereum in light sync mode, use les.LightEthereum") + if !config.SyncMode.SyncFullBlockChain() { + return nil, errors.New("can't run eth.Ethereum in light sync mode or lightest sync mode, use les.LightEthereum") } if !config.SyncMode.IsValid() { return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) - } - if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(common.Big0) <= 0 { - log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice) - config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice) } if config.NoPruning && config.TrieDirtyCache > 0 { if config.SnapshotCache > 0 { @@ -122,36 +122,43 @@ config.TrieDirtyCache = 0 } log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)   - // Transfer mining-related config to the ethash config. - ethashConfig := config.Ethash - ethashConfig.NotifyFull = config.Miner.NotifyFull - + if config.GatewayFee == nil || config.GatewayFee.Cmp(common.Big0) < 0 { + log.Warn("Sanitizing invalid gateway fee", "provided", config.GatewayFee, "updated", ethconfig.Defaults.GatewayFee) + config.GatewayFee = new(big.Int).Set(ethconfig.Defaults.GatewayFee) + } // Assemble the Ethereum object chainDb, err := stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/", false) if err != nil { return nil, err } - chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideLondon) + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideEHardfork) if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr } log.Info("Initialised chain configuration", "config", chainConfig) + chainConfig.FullHeaderChainAvailable = config.SyncMode.SyncFullHeaderChain()   if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil { log.Error("Failed to recover state", "error", err) } + if config.RPCGasInflationRate == 0 { + // if it was not set, default it as 1 + config.RPCGasInflationRate = 1 + } eth := &Ethereum{ config: config, chainDb: chainDb, eventMux: stack.EventMux(), accountManager: stack.AccountManager(), - engine: ethconfig.CreateConsensusEngine(stack, chainConfig, &ethashConfig, config.Miner.Notify, config.Miner.Noverify, chainDb), + engine: ethconfig.CreateConsensusEngine(stack, chainConfig, config, chainDb), closeBloomHandler: make(chan struct{}), networkID: config.NetworkId, - gasPrice: config.Miner.GasPrice, - etherbase: config.Miner.Etherbase, + validator: config.Miner.Validator, + txFeeRecipient: config.TxFeeRecipient, + gatewayFee: config.GatewayFee, + blsbase: config.BLSbase, bloomRequests: make(chan chan *bloombits.Retrieval), - bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms), + bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms, chainConfig.FullHeaderChainAvailable), p2pServer: stack.Server(), }   @@ -160,7 +167,7 @@ var dbVer = "<nil>" if bcVersion != nil { dbVer = fmt.Sprintf("%d", *bcVersion) } - log.Info("Initialising Ethereum protocol", "network", config.NetworkId, "dbversion", dbVer) + log.Info("Initialising Ethereum protocol", "versions", istanbul.ProtocolVersions, "network", config.NetworkId, "dbversion", dbVer)   if !config.SkipBcVersionCheck { if bcVersion != nil && *bcVersion > core.BlockChainVersion { @@ -203,6 +210,7 @@ if config.TxPool.Journal != "" { config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal) } + eth.txPool = core.NewTxPool(config.TxPool, chainConfig, eth.blockchain)   // Permit the downloader to use the trie cache allowance during fast sync @@ -221,22 +229,27 @@ BloomCache: uint64(cacheLimit), EventMux: eth.eventMux, Checkpoint: checkpoint, Whitelist: config.Whitelist, + server: stack.Server(), + proxyServer: stack.ProxyServer(), + MinSyncPeers: config.MinSyncPeers, }); err != nil { return nil, err }   - eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) + // If the engine is istanbul, then inject the blockchain + if istanbul, isIstanbul := eth.engine.(*istanbulBackend.Backend); isIstanbul { + istanbul.SetChain( + eth.blockchain, eth.blockchain.CurrentBlock, + func(hash common.Hash) (*state.StateDB, error) { + stateRoot := eth.blockchain.GetHeaderByHash(hash).Root + return eth.blockchain.StateAt(stateRoot) + }) + } + + eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, chainDb) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))   - eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} - if eth.APIBackend.allowUnprotectedTxs { - log.Info("Unprotected transactions allowed") - } - gpoParams := config.GPO - if gpoParams.Default == nil { - gpoParams.Default = config.Miner.GasPrice - } - eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams) + eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), true, eth}   // Setup DNS discovery iterators. dnsclient := dnsdisc.NewClient(dnsdisc.Config{}) @@ -350,45 +363,56 @@ func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) { s.blockchain.ResetWithGenesisBlock(gb) }   -func (s *Ethereum) Etherbase() (eb common.Address, err error) { +func (s *Ethereum) Validator() (val common.Address, err error) { s.lock.RLock() - etherbase := s.etherbase + validator := s.validator s.lock.RUnlock()   - if etherbase != (common.Address{}) { - return etherbase, nil + if validator != (common.Address{}) { + return validator, nil } - if wallets := s.AccountManager().Wallets(); len(wallets) > 0 { - if accounts := wallets[0].Accounts(); len(accounts) > 0 { - etherbase := accounts[0].Address + return common.Address{}, fmt.Errorf("validator must be explicitly specified") +}   - s.lock.Lock() - s.etherbase = etherbase - s.lock.Unlock() +func (s *Ethereum) TxFeeRecipient() (common.Address, error) { + s.lock.RLock() + txFeeRecipient := s.txFeeRecipient + s.lock.RUnlock()   - log.Info("Etherbase automatically configured", "address", etherbase) - return etherbase, nil - } + if txFeeRecipient != (common.Address{}) { + return txFeeRecipient, nil + } + return common.Address{}, fmt.Errorf("txFeeRecipient must be explicitly specified") +} + +func (s *Ethereum) BLSbase() (eb common.Address, err error) { + s.lock.RLock() + blsbase := s.blsbase + s.lock.RUnlock() + + if blsbase != (common.Address{}) { + return blsbase, nil } - return common.Address{}, fmt.Errorf("etherbase must be explicitly specified") + + return s.Validator() }   // isLocalBlock checks whether the specified block is mined // by local miner accounts. // -// We regard two types of accounts as local miner account: etherbase -// and accounts specified via `txpool.locals` flag. +// We regard two types of accounts as local miner account: the validator +// address and accounts specified via `txpool.locals` flag. func (s *Ethereum) isLocalBlock(block *types.Block) bool { author, err := s.engine.Author(block.Header()) if err != nil { log.Warn("Failed to retrieve block author", "number", block.NumberU64(), "hash", block.Hash(), "err", err) return false } - // Check whether the given address is etherbase. + // Check whether the given address is configured validator. s.lock.RLock() - etherbase := s.etherbase + validator := s.validator s.lock.RUnlock() - if author == etherbase { + if author == validator { return true } // Check whether the given address is specified by `txpool.local` @@ -405,79 +429,83 @@ // shouldPreserve checks whether we should preserve the given block // during the chain reorg depending on whether the author of block // is a local account. func (s *Ethereum) shouldPreserve(block *types.Block) bool { - // The reason we need to disable the self-reorg preserving for clique - // is it can be probable to introduce a deadlock. - // - // e.g. If there are 7 available signers - // - // r1 A - // r2 B - // r3 C - // r4 D - // r5 A [X] F G - // r6 [X] - // - // In the round5, the inturn signer E is offline, so the worst case - // is A, F and G sign the block of round5 and reject the block of opponents - // and in the round6, the last available signer B is offline, the whole - // network is stuck. - if _, ok := s.engine.(*clique.Clique); ok { - return false - } return s.isLocalBlock(block) }   -// SetEtherbase sets the mining reward address. -func (s *Ethereum) SetEtherbase(etherbase common.Address) { +// SetValidator sets the address to sign consensus messages. +func (s *Ethereum) SetValidator(validator common.Address) { s.lock.Lock() - s.etherbase = etherbase + s.validator = validator s.lock.Unlock()   - s.miner.SetEtherbase(etherbase) + s.miner.SetValidator(validator) }   -// StartMining starts the miner with the given number of CPU threads. If mining -// is already running, this method adjust the number of threads allowed to use -// and updates the minimum price required by the transaction pool. -func (s *Ethereum) StartMining(threads int) error { - // Update the thread count within the consensus engine - type threaded interface { - SetThreads(threads int) - } - if th, ok := s.engine.(threaded); ok { - log.Info("Updated mining threads", "threads", threads) - if threads == 0 { - threads = -1 // Disable the miner from within - } - th.SetThreads(threads) - } +// SetTxFeeRecipient sets the mining reward address. +func (s *Ethereum) SetTxFeeRecipient(txFeeRecipient common.Address) { + s.lock.Lock() + s.txFeeRecipient = txFeeRecipient + s.lock.Unlock() + + s.miner.SetTxFeeRecipient(txFeeRecipient) +} + +// StartMining starts the miner +func (s *Ethereum) StartMining() error { // If the miner was not running, initialize it if !s.IsMining() { - // Propagate the initial price point to the transaction pool - s.lock.RLock() - price := s.gasPrice - s.lock.RUnlock() - s.txPool.SetGasPrice(price)   // Configure the local mining address - eb, err := s.Etherbase() + validator, err := s.Validator() + if err != nil { + log.Error("Cannot start mining without validator", "err", err) + return fmt.Errorf("validator missing: %v", err) + } + + txFeeRecipient, err := s.TxFeeRecipient() + if err != nil { + log.Error("Cannot start mining without txFeeRecipient", "err", err) + return fmt.Errorf("txFeeRecipient missing: %v", err) + } + + blsbase, err := s.BLSbase() if err != nil { - log.Error("Cannot start mining without etherbase", "err", err) - return fmt.Errorf("etherbase missing: %v", err) + log.Error("Cannot start mining without blsbase", "err", err) + return fmt.Errorf("blsbase missing: %v", err) } - if clique, ok := s.engine.(*clique.Clique); ok { - wallet, err := s.accountManager.Find(accounts.Account{Address: eb}) + + if istanbul, isIstanbul := s.engine.(*istanbulBackend.Backend); isIstanbul { + valAccount := accounts.Account{Address: validator} + wallet, err := s.accountManager.Find(valAccount) if wallet == nil || err != nil { - log.Error("Etherbase account unavailable locally", "err", err) + log.Error("Validator account unavailable locally", "err", err) return fmt.Errorf("signer missing: %v", err) } - clique.Authorize(eb, wallet.SignData) + publicKey, err := wallet.GetPublicKey(valAccount) + if err != nil { + return fmt.Errorf("ECDSA public key missing: %v", err) + } + blswallet, err := s.accountManager.Find(accounts.Account{Address: blsbase}) + if blswallet == nil || err != nil { + log.Error("BLSbase account unavailable locally", "err", err) + return fmt.Errorf("BLS signer missing: %v", err) + } + + istanbul.Authorize(validator, blsbase, publicKey, wallet.Decrypt, wallet.SignData, blswallet.SignBLS, wallet.SignHash) + + if istanbul.IsProxiedValidator() { + if err := istanbul.StartProxiedValidatorEngine(); err != nil { + log.Error("Error in starting proxied validator engine", "err", err) + return err + } + } } + // If mining is started, we can disable the transaction rejection mechanism // introduced to speed sync times. atomic.StoreUint32(&s.handler.acceptTxs, 1)   - go s.miner.Start(eb) + go s.miner.Start(validator, txFeeRecipient) } return nil } @@ -485,15 +513,33 @@ // StopMining terminates the miner, both at the consensus engine level as well as // at the block creation level. func (s *Ethereum) StopMining() { - // Update the thread count within the consensus engine - type threaded interface { - SetThreads(threads int) - } - if th, ok := s.engine.(threaded); ok { - th.SetThreads(-1) - } // Stop the block creating itself s.miner.Stop() + + // Stop the proxied validator engine + if istanbul, isIstanbul := s.engine.(*istanbulBackend.Backend); isIstanbul { + if istanbul.IsProxiedValidator() { + if err := istanbul.StopProxiedValidatorEngine(); err != nil { + log.Warn("Error in stopping proxied validator engine", "err", err) + } + } + } +} + +func (s *Ethereum) startAnnounce() error { + if istanbul, ok := s.engine.(consensus.Istanbul); ok { + return istanbul.StartAnnouncing() + } + + return nil +} + +func (s *Ethereum) stopAnnounce() error { + if istanbul, ok := s.engine.(consensus.Istanbul); ok { + return istanbul.StopAnnouncing() + } + + return nil }   func (s *Ethereum) IsMining() bool { return s.miner.Mining() } @@ -501,12 +547,17 @@ func (s *Ethereum) Miner() *miner.Miner { return s.miner }   func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } +func (s *Ethereum) Config() *Config { return s.config } func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } func (s *Ethereum) Engine() consensus.Engine { return s.engine } func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } func (s *Ethereum) IsListening() bool { return true } // Always listening +func (s *Ethereum) EthVersion() int { return int(istanbul.ProtocolVersions[0]) } +func (s *Ethereum) NetVersion() uint64 { return s.networkID } func (s *Ethereum) Downloader() *downloader.Downloader { return s.handler.downloader } +func (s *Ethereum) GatewayFeeRecipient() common.Address { return common.Address{} } // Full-nodes do not make use of gateway fee. +func (s *Ethereum) GatewayFee() *big.Int { return common.Big0 } func (s *Ethereum) Synced() bool { return atomic.LoadUint32(&s.handler.acceptTxs) == 1 } func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning } func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer } @@ -516,7 +567,7 @@ // network protocols to start. func (s *Ethereum) Protocols() []p2p.Protocol { protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.ethDialCandidates) if s.config.SnapshotCache > 0 { - protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...) + // protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...) } return protos } @@ -532,13 +583,18 @@ // Figure out a max peers count based on the server limits maxPeers := s.p2pServer.MaxPeers if s.config.LightServ > 0 { - if s.config.LightPeers >= s.p2pServer.MaxPeers { + if s.config.LightPeers != 0 && s.config.LightPeers >= s.p2pServer.MaxPeers { return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, s.p2pServer.MaxPeers) } maxPeers -= s.config.LightPeers } // Start the networking layer and the light server if requested s.handler.Start(maxPeers) + + if err := s.startAnnounce(); err != nil { + return err + } + return nil }   @@ -546,6 +602,7 @@ // Stop implements node.Lifecycle, terminating all internal goroutines used by the // Ethereum protocol. func (s *Ethereum) Stop() error { // Stop all the peer-related stuff first. + s.stopAnnounce() s.ethDialCandidates.Close() s.snapDialCandidates.Close() s.handler.Stop() @@ -555,6 +612,7 @@ s.bloomIndexer.Close() close(s.closeBloomHandler) s.txPool.Stop() s.miner.Stop() + s.miner.Close() s.blockchain.Stop() s.engine.Close() rawdb.PopUncleanShutdownMarker(s.chainDb)
diff --git go-ethereum/eth/catalyst/api_test.go celo/eth/catalyst/api_test.go deleted file mode 100644 index 10f2d0fe72a836162c5f607342626dda48a961b7..0000000000000000000000000000000000000000 --- go-ethereum/eth/catalyst/api_test.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package catalyst - -import ( - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/params" -) - -var ( - // testKey is a private key to use for funding a tester account. - testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - - // testAddr is the Ethereum address of the tester account. - testAddr = crypto.PubkeyToAddress(testKey.PublicKey) - - testBalance = big.NewInt(2e15) -) - -func generateTestChain() (*core.Genesis, []*types.Block) { - db := rawdb.NewMemoryDatabase() - config := params.AllEthashProtocolChanges - genesis := &core.Genesis{ - Config: config, - Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, - ExtraData: []byte("test genesis"), - Timestamp: 9000, - BaseFee: big.NewInt(params.InitialBaseFee), - } - generate := func(i int, g *core.BlockGen) { - g.OffsetTime(5) - g.SetExtra([]byte("test")) - } - gblock := genesis.ToBlock(db) - engine := ethash.NewFaker() - blocks, _ := core.GenerateChain(config, gblock, engine, db, 10, generate) - blocks = append([]*types.Block{gblock}, blocks...) - return genesis, blocks -} - -func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, []*types.Block) { - if fork >= n { - fork = n - 1 - } - db := rawdb.NewMemoryDatabase() - config := &params.ChainConfig{ - ChainID: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - CatalystBlock: big.NewInt(0), - Ethash: new(params.EthashConfig), - } - genesis := &core.Genesis{ - Config: config, - Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, - ExtraData: []byte("test genesis"), - Timestamp: 9000, - BaseFee: big.NewInt(params.InitialBaseFee), - } - generate := func(i int, g *core.BlockGen) { - g.OffsetTime(5) - g.SetExtra([]byte("test")) - } - generateFork := func(i int, g *core.BlockGen) { - g.OffsetTime(5) - g.SetExtra([]byte("testF")) - } - gblock := genesis.ToBlock(db) - engine := ethash.NewFaker() - blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate) - blocks = append([]*types.Block{gblock}, blocks...) - forkedBlocks, _ := core.GenerateChain(config, blocks[fork], engine, db, n-fork, generateFork) - return genesis, blocks, forkedBlocks -} - -func TestEth2AssembleBlock(t *testing.T) { - genesis, blocks := generateTestChain() - n, ethservice := startEthService(t, genesis, blocks[1:9]) - defer n.Close() - - api := newConsensusAPI(ethservice) - signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID) - tx, err := types.SignTx(types.NewTransaction(0, blocks[8].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey) - if err != nil { - t.Fatalf("error signing transaction, err=%v", err) - } - ethservice.TxPool().AddLocal(tx) - blockParams := assembleBlockParams{ - ParentHash: blocks[8].ParentHash(), - Timestamp: blocks[8].Time(), - } - execData, err := api.AssembleBlock(blockParams) - - if err != nil { - t.Fatalf("error producing block, err=%v", err) - } - - if len(execData.Transactions) != 1 { - t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) - } -} - -func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { - genesis, blocks := generateTestChain() - n, ethservice := startEthService(t, genesis, blocks[1:9]) - defer n.Close() - - api := newConsensusAPI(ethservice) - - // Put the 10th block's tx in the pool and produce a new block - api.addBlockTxs(blocks[9]) - blockParams := assembleBlockParams{ - ParentHash: blocks[9].ParentHash(), - Timestamp: blocks[9].Time(), - } - execData, err := api.AssembleBlock(blockParams) - if err != nil { - t.Fatalf("error producing block, err=%v", err) - } - - if len(execData.Transactions) != blocks[9].Transactions().Len() { - t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) - } -} - -func TestEth2NewBlock(t *testing.T) { - genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 4) - n, ethservice := startEthService(t, genesis, blocks[1:5]) - defer n.Close() - - api := newConsensusAPI(ethservice) - for i := 5; i < 10; i++ { - p := executableData{ - ParentHash: ethservice.BlockChain().CurrentBlock().Hash(), - Miner: blocks[i].Coinbase(), - StateRoot: blocks[i].Root(), - GasLimit: blocks[i].GasLimit(), - GasUsed: blocks[i].GasUsed(), - Transactions: encodeTransactions(blocks[i].Transactions()), - ReceiptRoot: blocks[i].ReceiptHash(), - LogsBloom: blocks[i].Bloom().Bytes(), - BlockHash: blocks[i].Hash(), - Timestamp: blocks[i].Time(), - Number: uint64(i), - } - success, err := api.NewBlock(p) - if err != nil || !success.Valid { - t.Fatalf("Failed to insert block: %v", err) - } - } - - exp := ethservice.BlockChain().CurrentBlock().Hash() - - // Introduce the fork point. - lastBlockNum := blocks[4].Number() - lastBlock := blocks[4] - for i := 0; i < 4; i++ { - lastBlockNum.Add(lastBlockNum, big.NewInt(1)) - p := executableData{ - ParentHash: lastBlock.Hash(), - Miner: forkedBlocks[i].Coinbase(), - StateRoot: forkedBlocks[i].Root(), - Number: lastBlockNum.Uint64(), - GasLimit: forkedBlocks[i].GasLimit(), - GasUsed: forkedBlocks[i].GasUsed(), - Transactions: encodeTransactions(blocks[i].Transactions()), - ReceiptRoot: forkedBlocks[i].ReceiptHash(), - LogsBloom: forkedBlocks[i].Bloom().Bytes(), - BlockHash: forkedBlocks[i].Hash(), - Timestamp: forkedBlocks[i].Time(), - } - success, err := api.NewBlock(p) - if err != nil || !success.Valid { - t.Fatalf("Failed to insert forked block #%d: %v", i, err) - } - lastBlock, err = insertBlockParamsToBlock(ethservice.BlockChain().Config(), lastBlock.Header(), p) - if err != nil { - t.Fatal(err) - } - } - - if ethservice.BlockChain().CurrentBlock().Hash() != exp { - t.Fatalf("Wrong head after inserting fork %x != %x", exp, ethservice.BlockChain().CurrentBlock().Hash()) - } -} - -// startEthService creates a full node instance for testing. -func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { - t.Helper() - - n, err := node.New(&node.Config{}) - if err != nil { - t.Fatal("can't create node:", err) - } - - ethcfg := &ethconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}} - ethservice, err := eth.New(n, ethcfg) - if err != nil { - t.Fatal("can't create eth service:", err) - } - if err := n.Start(); err != nil { - t.Fatal("can't start node:", err) - } - if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil { - n.Close() - t.Fatal("can't import test blocks:", err) - } - ethservice.SetEtherbase(testAddr) - - return n, ethservice -}
diff --git go-ethereum/eth/catalyst/api_types.go celo/eth/catalyst/api_types.go deleted file mode 100644 index b62091fbf50d33e3e0faebc7c4288d2e5dc41bcc..0000000000000000000000000000000000000000 --- go-ethereum/eth/catalyst/api_types.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package catalyst - -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -//go:generate go run github.com/fjl/gencodec -type assembleBlockParams -field-override assembleBlockParamsMarshaling -out gen_blockparams.go - -// Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ -type assembleBlockParams struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - Timestamp uint64 `json:"timestamp" gencodec:"required"` -} - -// JSON type overrides for assembleBlockParams. -type assembleBlockParamsMarshaling struct { - Timestamp hexutil.Uint64 -} - -//go:generate go run github.com/fjl/gencodec -type executableData -field-override executableDataMarshaling -out gen_ed.go - -// Structure described at https://notes.ethereum.org/@n0ble/rayonism-the-merge-spec#Parameters1 -type executableData struct { - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - Miner common.Address `json:"miner" gencodec:"required"` - StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - Number uint64 `json:"number" gencodec:"required"` - GasLimit uint64 `json:"gasLimit" gencodec:"required"` - GasUsed uint64 `json:"gasUsed" gencodec:"required"` - Timestamp uint64 `json:"timestamp" gencodec:"required"` - ReceiptRoot common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom []byte `json:"logsBloom" gencodec:"required"` - Transactions [][]byte `json:"transactions" gencodec:"required"` -} - -// JSON type overrides for executableData. -type executableDataMarshaling struct { - Number hexutil.Uint64 - GasLimit hexutil.Uint64 - GasUsed hexutil.Uint64 - Timestamp hexutil.Uint64 - LogsBloom hexutil.Bytes - Transactions []hexutil.Bytes -} - -type newBlockResponse struct { - Valid bool `json:"valid"` -} - -type genericResponse struct { - Success bool `json:"success"` -}
diff --git go-ethereum/eth/catalyst/api.go celo/eth/catalyst/api.go deleted file mode 100644 index 23dc43800c0c670dbc44465f4efedc12c73066d9..0000000000000000000000000000000000000000 --- go-ethereum/eth/catalyst/api.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Package catalyst implements the temporary eth1/eth2 RPC integration. -package catalyst - -import ( - "errors" - "fmt" - "math/big" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/misc" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/node" - chainParams "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/trie" -) - -// Register adds catalyst APIs to the node. -func Register(stack *node.Node, backend *eth.Ethereum) error { - chainconfig := backend.BlockChain().Config() - if chainconfig.CatalystBlock == nil { - return errors.New("catalystBlock is not set in genesis config") - } else if chainconfig.CatalystBlock.Sign() != 0 { - return errors.New("catalystBlock of genesis config must be zero") - } - - log.Warn("Catalyst mode enabled") - stack.RegisterAPIs([]rpc.API{ - { - Namespace: "consensus", - Version: "1.0", - Service: newConsensusAPI(backend), - Public: true, - }, - }) - return nil -} - -type consensusAPI struct { - eth *eth.Ethereum -} - -func newConsensusAPI(eth *eth.Ethereum) *consensusAPI { - return &consensusAPI{eth: eth} -} - -// blockExecutionEnv gathers all the data required to execute -// a block, either when assembling it or when inserting it. -type blockExecutionEnv struct { - chain *core.BlockChain - state *state.StateDB - tcount int - gasPool *core.GasPool - - header *types.Header - txs []*types.Transaction - receipts []*types.Receipt -} - -func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase common.Address) error { - vmconfig := *env.chain.GetVMConfig() - snap := env.state.Snapshot() - receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vmconfig) - if err != nil { - env.state.RevertToSnapshot(snap) - return err - } - env.txs = append(env.txs, tx) - env.receipts = append(env.receipts, receipt) - return nil -} - -func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) (*blockExecutionEnv, error) { - state, err := api.eth.BlockChain().StateAt(parent.Root()) - if err != nil { - return nil, err - } - env := &blockExecutionEnv{ - chain: api.eth.BlockChain(), - state: state, - header: header, - gasPool: new(core.GasPool).AddGas(header.GasLimit), - } - return env, nil -} - -// AssembleBlock creates a new block, inserts it into the chain, and returns the "execution -// data" required for eth2 clients to process the new block. -func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableData, error) { - log.Info("Producing block", "parentHash", params.ParentHash) - - bc := api.eth.BlockChain() - parent := bc.GetBlockByHash(params.ParentHash) - if parent == nil { - log.Warn("Cannot assemble block with parent hash to unknown block", "parentHash", params.ParentHash) - return nil, fmt.Errorf("cannot assemble block with unknown parent %s", params.ParentHash) - } - - pool := api.eth.TxPool() - - if parent.Time() >= params.Timestamp { - return nil, fmt.Errorf("child timestamp lower than parent's: %d >= %d", parent.Time(), params.Timestamp) - } - if now := uint64(time.Now().Unix()); params.Timestamp > now+1 { - wait := time.Duration(params.Timestamp-now) * time.Second - log.Info("Producing block too far in the future", "wait", common.PrettyDuration(wait)) - time.Sleep(wait) - } - - pending, err := pool.Pending(true) - if err != nil { - return nil, err - } - - coinbase, err := api.eth.Etherbase() - if err != nil { - return nil, err - } - num := parent.Number() - header := &types.Header{ - ParentHash: parent.Hash(), - Number: num.Add(num, common.Big1), - Coinbase: coinbase, - GasLimit: parent.GasLimit(), // Keep the gas limit constant in this prototype - Extra: []byte{}, - Time: params.Timestamp, - } - if config := api.eth.BlockChain().Config(); config.IsLondon(header.Number) { - header.BaseFee = misc.CalcBaseFee(config, parent.Header()) - } - err = api.eth.Engine().Prepare(bc, header) - if err != nil { - return nil, err - } - - env, err := api.makeEnv(parent, header) - if err != nil { - return nil, err - } - - var ( - signer = types.MakeSigner(bc.Config(), header.Number) - txHeap = types.NewTransactionsByPriceAndNonce(signer, pending, nil) - transactions []*types.Transaction - ) - for { - if env.gasPool.Gas() < chainParams.TxGas { - log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", chainParams.TxGas) - break - } - tx := txHeap.Peek() - if tx == nil { - break - } - - // The sender is only for logging purposes, and it doesn't really matter if it's correct. - from, _ := types.Sender(signer, tx) - - // Execute the transaction - env.state.Prepare(tx.Hash(), env.tcount) - err = env.commitTransaction(tx, coinbase) - switch err { - case core.ErrGasLimitReached: - // Pop the current out-of-gas transaction without shifting in the next from the account - log.Trace("Gas limit exceeded for current block", "sender", from) - txHeap.Pop() - - case core.ErrNonceTooLow: - // New head notification data race between the transaction pool and miner, shift - log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) - txHeap.Shift() - - case core.ErrNonceTooHigh: - // Reorg notification data race between the transaction pool and miner, skip account = - log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce()) - txHeap.Pop() - - case nil: - // Everything ok, collect the logs and shift in the next transaction from the same account - env.tcount++ - txHeap.Shift() - transactions = append(transactions, tx) - - default: - // Strange error, discard the transaction and get the next in line (note, the - // nonce-too-high clause will prevent us from executing in vain). - log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) - txHeap.Shift() - } - } - - // Create the block. - block, err := api.eth.Engine().FinalizeAndAssemble(bc, header, env.state, transactions, nil /* uncles */, env.receipts) - if err != nil { - return nil, err - } - return &executableData{ - BlockHash: block.Hash(), - ParentHash: block.ParentHash(), - Miner: block.Coinbase(), - StateRoot: block.Root(), - Number: block.NumberU64(), - GasLimit: block.GasLimit(), - GasUsed: block.GasUsed(), - Timestamp: block.Time(), - ReceiptRoot: block.ReceiptHash(), - LogsBloom: block.Bloom().Bytes(), - Transactions: encodeTransactions(block.Transactions()), - }, nil -} - -func encodeTransactions(txs []*types.Transaction) [][]byte { - var enc = make([][]byte, len(txs)) - for i, tx := range txs { - enc[i], _ = tx.MarshalBinary() - } - return enc -} - -func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { - var txs = make([]*types.Transaction, len(enc)) - for i, encTx := range enc { - var tx types.Transaction - if err := tx.UnmarshalBinary(encTx); err != nil { - return nil, fmt.Errorf("invalid transaction %d: %v", i, err) - } - txs[i] = &tx - } - return txs, nil -} - -func insertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Header, params executableData) (*types.Block, error) { - txs, err := decodeTransactions(params.Transactions) - if err != nil { - return nil, err - } - - number := big.NewInt(0) - number.SetUint64(params.Number) - header := &types.Header{ - ParentHash: params.ParentHash, - UncleHash: types.EmptyUncleHash, - Coinbase: params.Miner, - Root: params.StateRoot, - TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), - ReceiptHash: params.ReceiptRoot, - Bloom: types.BytesToBloom(params.LogsBloom), - Difficulty: big.NewInt(1), - Number: number, - GasLimit: params.GasLimit, - GasUsed: params.GasUsed, - Time: params.Timestamp, - } - if config.IsLondon(number) { - header.BaseFee = misc.CalcBaseFee(config, parent) - } - block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) - return block, nil -} - -// NewBlock creates an Eth1 block, inserts it in the chain, and either returns true, -// or false + an error. This is a bit redundant for go, but simplifies things on the -// eth2 side. -func (api *consensusAPI) NewBlock(params executableData) (*newBlockResponse, error) { - parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) - if parent == nil { - return &newBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash) - } - block, err := insertBlockParamsToBlock(api.eth.BlockChain().Config(), parent.Header(), params) - if err != nil { - return nil, err - } - _, err = api.eth.BlockChain().InsertChainWithoutSealVerification(block) - return &newBlockResponse{err == nil}, err -} - -// Used in tests to add a the list of transactions from a block to the tx pool. -func (api *consensusAPI) addBlockTxs(block *types.Block) error { - for _, tx := range block.Transactions() { - api.eth.TxPool().AddLocal(tx) - } - return nil -} - -// FinalizeBlock is called to mark a block as synchronized, so -// that data that is no longer needed can be removed. -func (api *consensusAPI) FinalizeBlock(blockHash common.Hash) (*genericResponse, error) { - return &genericResponse{true}, nil -} - -// SetHead is called to perform a force choice. -func (api *consensusAPI) SetHead(newHead common.Hash) (*genericResponse, error) { - return &genericResponse{true}, nil -}
diff --git go-ethereum/eth/catalyst/gen_ed.go celo/eth/catalyst/gen_ed.go deleted file mode 100644 index 4a5acdd749ecf0cf9c23fe990919faf338482ffa..0000000000000000000000000000000000000000 --- go-ethereum/eth/catalyst/gen_ed.go +++ /dev/null @@ -1,117 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package catalyst - -import ( - "encoding/json" - "errors" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*executableDataMarshaling)(nil) - -// MarshalJSON marshals as JSON. -func (e executableData) MarshalJSON() ([]byte, error) { - type executableData struct { - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - Miner common.Address `json:"miner" gencodec:"required"` - StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - Number hexutil.Uint64 `json:"number" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ReceiptRoot common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` - } - var enc executableData - enc.BlockHash = e.BlockHash - enc.ParentHash = e.ParentHash - enc.Miner = e.Miner - enc.StateRoot = e.StateRoot - enc.Number = hexutil.Uint64(e.Number) - enc.GasLimit = hexutil.Uint64(e.GasLimit) - enc.GasUsed = hexutil.Uint64(e.GasUsed) - enc.Timestamp = hexutil.Uint64(e.Timestamp) - enc.ReceiptRoot = e.ReceiptRoot - enc.LogsBloom = e.LogsBloom - if e.Transactions != nil { - enc.Transactions = make([]hexutil.Bytes, len(e.Transactions)) - for k, v := range e.Transactions { - enc.Transactions[k] = v - } - } - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals from JSON. -func (e *executableData) UnmarshalJSON(input []byte) error { - type executableData struct { - BlockHash *common.Hash `json:"blockHash" gencodec:"required"` - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - Miner *common.Address `json:"miner" gencodec:"required"` - StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` - Number *hexutil.Uint64 `json:"number" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ReceiptRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` - } - var dec executableData - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.BlockHash == nil { - return errors.New("missing required field 'blockHash' for executableData") - } - e.BlockHash = *dec.BlockHash - if dec.ParentHash == nil { - return errors.New("missing required field 'parentHash' for executableData") - } - e.ParentHash = *dec.ParentHash - if dec.Miner == nil { - return errors.New("missing required field 'miner' for executableData") - } - e.Miner = *dec.Miner - if dec.StateRoot == nil { - return errors.New("missing required field 'stateRoot' for executableData") - } - e.StateRoot = *dec.StateRoot - if dec.Number == nil { - return errors.New("missing required field 'number' for executableData") - } - e.Number = uint64(*dec.Number) - if dec.GasLimit == nil { - return errors.New("missing required field 'gasLimit' for executableData") - } - e.GasLimit = uint64(*dec.GasLimit) - if dec.GasUsed == nil { - return errors.New("missing required field 'gasUsed' for executableData") - } - e.GasUsed = uint64(*dec.GasUsed) - if dec.Timestamp == nil { - return errors.New("missing required field 'timestamp' for executableData") - } - e.Timestamp = uint64(*dec.Timestamp) - if dec.ReceiptRoot == nil { - return errors.New("missing required field 'receiptsRoot' for executableData") - } - e.ReceiptRoot = *dec.ReceiptRoot - if dec.LogsBloom == nil { - return errors.New("missing required field 'logsBloom' for executableData") - } - e.LogsBloom = *dec.LogsBloom - if dec.Transactions == nil { - return errors.New("missing required field 'transactions' for executableData") - } - e.Transactions = make([][]byte, len(dec.Transactions)) - for k, v := range dec.Transactions { - e.Transactions[k] = v - } - return nil -}
diff --git go-ethereum/eth/catalyst/gen_blockparams.go celo/eth/catalyst/gen_blockparams.go deleted file mode 100644 index 9fe26f6bae78451b5e60e19ea357debaac2b2f08..0000000000000000000000000000000000000000 --- go-ethereum/eth/catalyst/gen_blockparams.go +++ /dev/null @@ -1,46 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package catalyst - -import ( - "encoding/json" - "errors" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*assembleBlockParamsMarshaling)(nil) - -// MarshalJSON marshals as JSON. -func (a assembleBlockParams) MarshalJSON() ([]byte, error) { - type assembleBlockParams struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` - } - var enc assembleBlockParams - enc.ParentHash = a.ParentHash - enc.Timestamp = hexutil.Uint64(a.Timestamp) - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals from JSON. -func (a *assembleBlockParams) UnmarshalJSON(input []byte) error { - type assembleBlockParams struct { - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - } - var dec assembleBlockParams - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.ParentHash == nil { - return errors.New("missing required field 'parentHash' for assembleBlockParams") - } - a.ParentHash = *dec.ParentHash - if dec.Timestamp == nil { - return errors.New("missing required field 'timestamp' for assembleBlockParams") - } - a.Timestamp = uint64(*dec.Timestamp) - return nil -}
diff --git go-ethereum/eth/downloader/resultstore.go celo/eth/downloader/resultstore.go index 852591fb24e5cbefe806f1ef58aa2adcb2330658..ddcbc0121b2cf1b831c3893278e71a79d93f6df4 100644 --- go-ethereum/eth/downloader/resultstore.go +++ celo/eth/downloader/resultstore.go @@ -141,7 +141,7 @@ // countCompleted returns the number of items ready for delivery, stopping at // the first non-complete item. // -// The mthod assumes (at least) rlock is held. +// The method assumes (at least) rlock is held. func (r *resultStore) countCompleted() int { // We iterate from the already known complete point, and see // if any more has completed since last count
diff --git go-ethereum/eth/downloader/doc.go celo/eth/downloader/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..b9aab7bf103a4c56374084208630f3909ccb4c5a --- /dev/null +++ celo/eth/downloader/doc.go @@ -0,0 +1,207 @@ +/* +Package downloader handles downloading data from other nodes for sync. Sync +refers to the process of catching up with other nodes, once caught up nodes +use a different process to maintain their syncronisation. + +There are a few different modes for syncing + +Full: Get all the blocks and apply them all to build the chain state + +Fast: Get all the blocks and block receipts and insert them in the db without +processing them and at the same time download the state for a block near the +head block. Once the state has been downloaded, process subsequent blocks as a +full sync would in order to reach the tip of the chain. + +Fast sync introduces the concept of the pivot, which is a block at some point +behind the head block for which the node attempts to sync state for. In geth +the pivot was chosen to be 64 blocks behind the head block, the reason for +choosing a point behind the head was to ensure that the block that you are +syncing state for a block which is on the main chain and won't get reorged out. +(see https://github.com/ethereum/go-ethereum/issues/25100), it was called the +pivot because before the pivot the fast sync approach is used but after the +pivot full sync is used, so you could imagine the syncing strategy pivoting +around that point. + +In celo we don't have the problem of reorgs but we still retain the pivot point +because the validator uptime scores historically were required to be calculated +by processing blocks from an epoch boundary. However since +https://github.com/ethereum/go-ethereum/pull/1833 which removes the +requirement to process blocks from an epoch boundary we could in fact drop the +concept of pivot. + +Snap: Not currently working, but in theory works like fast sync except that +nodes download a flat file to get the state, as opposed to making hundreds of +thousands of individual requests for it. This should significantly speed up +sync. + +Light: Downloads only headers during sync and then downloads other data on +demand in order to service rpc requests. + +Lightest: Like light but downloads only one header per epoch, which on mainnet +means one header out of every 17280 headers. This is particularly fast only +takes 20 seconds or so to get synced. + +Sync process detail + +Syncing is initiated with one peer (see eth.loop), the peer selected to sync +with is the one with the highest total difficulty of all peers (see +eth.nextSyncOp). Syncing may be cancelled and started with a different peer if +a peer with a higher total difficulty becomes available. + +Syncing introduces the concept of a checkpoint (see params.TrustedCheckpoint). +The checkpoint is a hard coded set of trie roots that allow state sync to start +before the whole header chain has been downloaded. + +The pivot point is the point which the fast sync syncs state for, it is +calculated as the first block of the epoch containing the block that is +fsMinFullBlocks behind the current head block of the peer we are syncing +against (fsMinFullBlocks is hardcoded to 64, chosen by the geth team to make +re-orgs of the synced state unlikely). + +The first step in syncing with a peer is to fetch the latest block header and +pivot header. The geth implementation simply calculates the pivot as being 64 +blocks before (fsMinFullBlocks) the head, and so if the head is currently < 64 +then there is no valid pivot, in that case geth code uses the head as the pivot +(they say to avoid nil pointer exceptions, but its not clear what this will do +to the sync). From the celo side there should never be a case without a pivot +block because we instead choose the pivot to be zero if head is currently < 64. + +Next the sync finds the common ancestor (aka origin) between the node and the +peer is syncing against. + +If fast syncing { + + The pivot is written to a file. + + If the origin turns out to be after the pivot then it is set to be just + before the pivot. + + The ancient limit is set on the downloader (it would be much nicer if the + concept of ancient could be encapsulated in the database rather than + leaking here). The ancient defines boundary between freezer blocks and + current blocks. Setting ancient limit here enables "direct-ancient mode" + which I guess bypasses putting stuff into the main chain and then having it + be moved to the freezer later. I guess in full sync mode since all blocks + need to be processed all blocks need to go into the main database first and + only after they have been process can they be moved to the freezer, but + since fast sync does not process all blocks that step can be skipped. + + Then, and I'm not really clear why if the origin is greater than the last + frozen block (IE there is stuff in the current database beyond whats in the + Freezer) the "direct-ancient mode is disabled", maybe because it is only + applicable for nodes that are starting from scratch or have never reached + the pivot. + + If the origin turns out to be lower than the most recent frozen block then + the blockchain is rewound to the origin. + + set the pivotHeader on the downloader as the pivot. + +} + +Then a number of go routines are started to fetch data from the origin to the head. + +fetchHeaders +fetchBodies +fetchReceipts + +And one to process the headers. (processHeaders) + +If fast sync { + start a routine to process the fast sync content (processFastSyncContent) +} +If full syncing { + + start a routine to process the full sync content (processFullSyncContent) +} + + +These goroutines form a pipeline where the downloaded data flows as follows. + + -> fetchBodies -> processFullSyncContent + / \ +fetchHeaders -> processHeaders \ + \ \ + -> fetchReceipts --> processFastSyncContent + + + +fetchHeaders + +fetchHeaders introduces the skeleton concept. The idea is that the node +requests a set of headers from the peer that are spaced out at regular +intervals, and then uses all peers to request headers to fill the gaps. The +header hashes allow the node to easily verify that the received headers match +the chain of the main peer they are syncing against. Whether fetching skeleton +headers or not requests for headers are done in batches of up to 192 +(MaxHeaderFetch) headers. + +If lightest sync { + fetch just epoch headers till current epoch then fetch all subsequent headers. (no skeleton) +} else { + fetch headers using the skeleton approach, until no more skeleton headers + are returned then switch to requesting all subsequent headers from the + peer. +} + + +Wait for headers to be received. + +Pass the received headers to the processHeaders routine. + +If no more headers are returned and the pivot state has been fully synced then +exit. (The pivot being synced is communicated via an atomic from +processFastSyncContent) + +Fetch more headers as done to start with. + +processHeaders + +Waits to receive headers from fetchHeaders inserts the received headers into the header chain. + +If full sync { + request blocks for inserted headers. (fetchBodies) +} +If fast sync { + request blocks and receipts for inserted headers. (fetchBodies & fetchReceipts) +} + +processFastSyncContent + +Reads fetch results (each fetch result has all the data required for a block +(header txs, receipts, randomnes & epochSnarkData)from the downloader queue. + +Updates the pivot block point if it has fallen sufficiently behind head. + +Splits the fetch results around the pivot. + +Results before the pivot are inserted with BlockChain.InsertReceiptChain (which +inserts receipts, because in fast sync most blocks are not processed) and those +after the pivot + +If the pivot has completed syncing { + Inserts the results after the pivot with, BlockChain.InsertChain and exits. +} else { + Start the process again prepending the results after the pivot point to the + newly fetched results. (Note that if the pivot point is subsequently + updated those results will be processed as fast sync results and inserted + via BlockChain.InsertReceiptChain, but there seems to be a problem with our + current implementation that means that the pivot would have to get 2 days + old before it would be updated, so actually it looks like the list of + result s will grow a lot during this time could be an OOM consideration) +} + + +fetchBodies + +A routine that gets notified of bodies to fetch and calls into a beast of a +function (fetchParts) to fetch batches of block bodies from different peers, +those bodies are delivered to the queue that collates them along with other +delivered data into fetch results that are then retrieved by either +processFastSyncContent or processFullSyncContent. + +fetchReceipts + +like fetchBodies but for receipts. +*/ +package downloader
diff --git go-ethereum/eth/downloader/downloader_test.go celo/eth/downloader/downloader_test.go index 2fb2014f8e5ae37eddea25adcc3c6662aec4a25c..852b934f9aad45cbcfc73c4b82d357d9bbe74fca 100644 --- go-ethereum/eth/downloader/downloader_test.go +++ celo/eth/downloader/downloader_test.go @@ -28,12 +28,13 @@ "time"   "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" )   @@ -43,6 +44,11 @@ fullMaxForkAncestry = 10000 lightMaxForkAncestry = 10000 blockCacheMaxItems = 1024 fsHeaderContCheck = 500 * time.Millisecond + + // configure logger by uncommenting this to enable more logging + //glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true))) + //glogger.Verbosity(log.LvlInfo) + //log.Root().SetHandler(glogger) }   // downloadTester is a test simulator for mocking out local block chain. @@ -68,6 +74,10 @@ lock sync.RWMutex }   +func (dl *downloadTester) Config() *params.ChainConfig { + return nil +} + // newTester creates a new downloader test mocker. func newTester() *downloadTester { tester := &downloadTester{ @@ -78,13 +88,13 @@ ownHashes: []common.Hash{testGenesis.Hash()}, ownHeaders: map[common.Hash]*types.Header{testGenesis.Hash(): testGenesis.Header()}, ownBlocks: map[common.Hash]*types.Block{testGenesis.Hash(): testGenesis}, ownReceipts: map[common.Hash]types.Receipts{testGenesis.Hash(): nil}, - ownChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.Difficulty()}, + ownChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.TotalDifficulty()},   // Initialize ancient store with test genesis block ancientHeaders: map[common.Hash]*types.Header{testGenesis.Hash(): testGenesis.Header()}, ancientBlocks: map[common.Hash]*types.Block{testGenesis.Hash(): testGenesis}, ancientReceipts: map[common.Hash]types.Receipts{testGenesis.Hash(): nil}, - ancientChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.Difficulty()}, + ancientChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.TotalDifficulty()}, } tester.stateDb = rawdb.NewMemoryDatabase() tester.stateDb.Put(testGenesis.Root().Bytes(), []byte{0x00}) @@ -160,6 +170,30 @@ } return dl.ownHeaders[hash] }   +// GetHeaderByNumber retrieves a header from the testers canonical chain. +func (dl *downloadTester) GetHeaderByNumber(number uint64) *types.Header { + dl.lock.RLock() + defer dl.lock.RUnlock() + return dl.getHeaderByNumber(number) +} + +// getHeaderByNumber returns the header if found either within ancients or own blocks) +// This method assumes that the caller holds at least the read-lock (dl.lock) +func (dl *downloadTester) getHeaderByNumber(number uint64) *types.Header { + for _, header := range dl.ancientHeaders { + if header != nil && header.Number.Uint64() == number { + return header + } + } + + for _, header := range dl.ownHeaders { + if header != nil && header.Number.Uint64() == number { + return header + } + } + return nil +} + // GetBlock retrieves a block from the testers canonical chain. func (dl *downloadTester) GetBlockByHash(hash common.Hash) *types.Block { dl.lock.RLock() @@ -172,6 +206,25 @@ } return dl.ownBlocks[hash] }   +// GetBlock retrieves a block from the testers canonical chain. +func (dl *downloadTester) GetBlockByNumber(number uint64) *types.Block { + dl.lock.RLock() + defer dl.lock.RUnlock() + + for _, block := range dl.ancientBlocks { + if block != nil && block.NumberU64() == number { + return block + } + } + + for _, block := range dl.ownBlocks { + if block != nil && block.NumberU64() == number { + return block + } + } + return nil +} + // CurrentHeader retrieves the current head header from the canonical chain. func (dl *downloadTester) CurrentHeader() *types.Header { dl.lock.RLock() @@ -254,7 +307,7 @@ return dl.ownChainTd[hash] }   // InsertHeaderChain injects a new batch of headers into the simulated chain. -func (dl *downloadTester) InsertHeaderChain(headers []*types.Header, checkFreq int) (i int, err error) { +func (dl *downloadTester) InsertHeaderChain(headers []*types.Header, checkFreq int, contiguousHeaders bool) (i int, err error) { dl.lock.Lock() defer dl.lock.Unlock() // Do a quick check, as the blockchain.InsertHeaderChain doesn't insert anything in case of errors @@ -283,8 +336,7 @@ } dl.ownHashes = append(dl.ownHashes, hash) dl.ownHeaders[hash] = header   - td := dl.getTd(header.ParentHash) - dl.ownChainTd[hash] = new(big.Int).Add(td, header.Difficulty) + dl.ownChainTd[hash] = new(big.Int).Add(header.Number, big.NewInt(1)) } return len(headers), nil } @@ -306,8 +358,7 @@ } dl.ownBlocks[block.Hash()] = block dl.ownReceipts[block.Hash()] = make(types.Receipts, 0) dl.stateDb.Put(block.Root().Bytes(), []byte{0x00}) - td := dl.getTd(block.ParentHash()) - dl.ownChainTd[block.Hash()] = new(big.Int).Add(td, block.Difficulty()) + dl.ownChainTd[block.Hash()] = block.TotalDifficulty() } return len(blocks), nil } @@ -332,7 +383,8 @@ dl.ancientReceipts[blocks[i].Hash()] = receipts[i]   // Migrate from active db to ancient db dl.ancientHeaders[blocks[i].Hash()] = blocks[i].Header() - dl.ancientChainTd[blocks[i].Hash()] = new(big.Int).Add(dl.ancientChainTd[blocks[i].ParentHash()], blocks[i].Difficulty()) + dl.ancientChainTd[blocks[i].Hash()] = blocks[i].TotalDifficulty() + delete(dl.ownHeaders, blocks[i].Hash()) delete(dl.ownChainTd, blocks[i].Hash()) } else { @@ -451,8 +503,8 @@ // RequestBodies constructs a getBlockBodies method associated with a particular // peer in the download tester. The returned function can be used to retrieve // batches of block bodies from the particularly requested peer. func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash) error { - txs, uncles := dlp.chain.bodies(hashes) - go dlp.dl.downloader.DeliverBodies(dlp.id, txs, uncles) + txs, randomness, epochSnarkData := dlp.chain.bodies(hashes) + go dlp.dl.downloader.DeliverBodies(dlp.id, txs, randomness, epochSnarkData) return nil }   @@ -522,9 +574,9 @@ t.Fatalf("synchronised receipts mismatch: have %v, want %v", rs, receipts) } }   -func TestCanonicalSynchronisation66Full(t *testing.T) { testCanonSync(t, eth.ETH66, FullSync) } -func TestCanonicalSynchronisation66Fast(t *testing.T) { testCanonSync(t, eth.ETH66, FastSync) } -func TestCanonicalSynchronisation66Light(t *testing.T) { testCanonSync(t, eth.ETH66, LightSync) } +func TestCanonicalSynchronisation67Full(t *testing.T) { testCanonSync(t, istanbul.Celo67, FullSync) } +func TestCanonicalSynchronisation67Fast(t *testing.T) { testCanonSync(t, istanbul.Celo67, FastSync) } +func TestCanonicalSynchronisation67Light(t *testing.T) { testCanonSync(t, istanbul.Celo67, LightSync) }   func testCanonSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -545,8 +597,8 @@ }   // Tests that if a large batch of blocks are being downloaded, it is throttled // until the cached blocks are retrieved. -func TestThrottling66Full(t *testing.T) { testThrottling(t, eth.ETH66, FullSync) } -func TestThrottling66Fast(t *testing.T) { testThrottling(t, eth.ETH66, FastSync) } +func TestThrottling67Full(t *testing.T) { testThrottling(t, istanbul.Celo67, FullSync) } +func TestThrottling67Fast(t *testing.T) { testThrottling(t, istanbul.Celo67, FastSync) }   func testThrottling(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -578,7 +630,7 @@ break } // Wait a bit for sync to throttle itself var cached, frozen int - for start := time.Now(); time.Since(start) < 3*time.Second; { + for start := time.Now(); time.Since(start) < 6*time.Second; { time.Sleep(25 * time.Millisecond)   tester.lock.Lock() @@ -627,9 +679,9 @@ // Tests that simple synchronization against a forked chain works correctly. In // this test common ancestor lookup should *not* be short circuited, and a full // binary search should be executed. -func TestForkedSync66Full(t *testing.T) { testForkedSync(t, eth.ETH66, FullSync) } -func TestForkedSync66Fast(t *testing.T) { testForkedSync(t, eth.ETH66, FastSync) } -func TestForkedSync66Light(t *testing.T) { testForkedSync(t, eth.ETH66, LightSync) } +func TestForkedSync67Full(t *testing.T) { testForkedSync(t, istanbul.Celo67, FullSync) } +func TestForkedSync67Fast(t *testing.T) { testForkedSync(t, istanbul.Celo67, FastSync) } +func TestForkedSync67Light(t *testing.T) { testForkedSync(t, istanbul.Celo67, LightSync) }   func testForkedSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -656,9 +708,9 @@ }   // Tests that synchronising against a much shorter but much heavyer fork works // corrently and is not dropped. -func TestHeavyForkedSync66Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FullSync) } -func TestHeavyForkedSync66Fast(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FastSync) } -func TestHeavyForkedSync66Light(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, LightSync) } +func TestHeavyForkedSync67Full(t *testing.T) { testHeavyForkedSync(t, istanbul.Celo67, FullSync) } +func TestHeavyForkedSync67Fast(t *testing.T) { testHeavyForkedSync(t, istanbul.Celo67, FastSync) } +func TestHeavyForkedSync67Light(t *testing.T) { testHeavyForkedSync(t, istanbul.Celo67, LightSync) }   func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -687,9 +739,9 @@ // Tests that chain forks are contained within a certain interval of the current // chain head, ensuring that malicious peers cannot waste resources by feeding // long dead chains. -func TestBoundedForkedSync66Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FullSync) } -func TestBoundedForkedSync66Fast(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FastSync) } -func TestBoundedForkedSync66Light(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, LightSync) } +func TestBoundedForkedSync67Full(t *testing.T) { testBoundedForkedSync(t, istanbul.Celo67, FullSync) } +func TestBoundedForkedSync67Fast(t *testing.T) { testBoundedForkedSync(t, istanbul.Celo67, FastSync) } +func TestBoundedForkedSync67Light(t *testing.T) { testBoundedForkedSync(t, istanbul.Celo67, LightSync) }   func testBoundedForkedSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -717,14 +769,14 @@ // Tests that chain forks are contained within a certain interval of the current // chain head for short but heavy forks too. These are a bit special because they // take different ancestor lookup paths. -func TestBoundedHeavyForkedSync66Full(t *testing.T) { - testBoundedHeavyForkedSync(t, eth.ETH66, FullSync) +func TestBoundedHeavyForkedSync67Full(t *testing.T) { + testBoundedHeavyForkedSync(t, istanbul.Celo67, FullSync) } -func TestBoundedHeavyForkedSync66Fast(t *testing.T) { - testBoundedHeavyForkedSync(t, eth.ETH66, FastSync) +func TestBoundedHeavyForkedSync67Fast(t *testing.T) { + testBoundedHeavyForkedSync(t, istanbul.Celo67, FastSync) } -func TestBoundedHeavyForkedSync66Light(t *testing.T) { - testBoundedHeavyForkedSync(t, eth.ETH66, LightSync) +func TestBoundedHeavyForkedSync67Light(t *testing.T) { + testBoundedHeavyForkedSync(t, istanbul.Celo67, LightSync) }   func testBoundedHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) { @@ -752,7 +804,7 @@ }   // Tests that an inactive downloader will not accept incoming block headers, // bodies and receipts. -func TestInactiveDownloader63(t *testing.T) { +func TestInactiveDownloader64(t *testing.T) { t.Parallel()   tester := newTester() @@ -762,7 +814,7 @@ // Check that neither block headers nor bodies are accepted if err := tester.downloader.DeliverHeaders("bad peer", []*types.Header{}); err != errNoSyncActive { t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) } - if err := tester.downloader.DeliverBodies("bad peer", [][]*types.Transaction{}, [][]*types.Header{}); err != errNoSyncActive { + if err := tester.downloader.DeliverBodies("bad peer", [][]*types.Transaction{}, []*types.Randomness{}, []*types.EpochSnarkData{}); err != errNoSyncActive { t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) } if err := tester.downloader.DeliverReceipts("bad peer", [][]*types.Receipt{}); err != errNoSyncActive { @@ -771,9 +823,9 @@ } }   // Tests that a canceled download wipes all previously accumulated state. -func TestCancel66Full(t *testing.T) { testCancel(t, eth.ETH66, FullSync) } -func TestCancel66Fast(t *testing.T) { testCancel(t, eth.ETH66, FastSync) } -func TestCancel66Light(t *testing.T) { testCancel(t, eth.ETH66, LightSync) } +func TestCancel67Full(t *testing.T) { testCancel(t, istanbul.Celo67, FullSync) } +func TestCancel67Fast(t *testing.T) { testCancel(t, istanbul.Celo67, FastSync) } +func TestCancel67Light(t *testing.T) { testCancel(t, istanbul.Celo67, LightSync) }   func testCancel(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -800,9 +852,15 @@ } }   // Tests that synchronisation from multiple peers works as intended (multi thread sanity test). -func TestMultiSynchronisation66Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FullSync) } -func TestMultiSynchronisation66Fast(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FastSync) } -func TestMultiSynchronisation66Light(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, LightSync) } +func TestMultiSynchronisation67Full(t *testing.T) { + testMultiSynchronisation(t, istanbul.Celo67, FullSync) +} +func TestMultiSynchronisation67Fast(t *testing.T) { + testMultiSynchronisation(t, istanbul.Celo67, FastSync) +} +func TestMultiSynchronisation67Light(t *testing.T) { + testMultiSynchronisation(t, istanbul.Celo67, LightSync) +}   func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -826,9 +884,15 @@ }   // Tests that synchronisations behave well in multi-version protocol environments // and not wreak havoc on other nodes in the network. -func TestMultiProtoSynchronisation66Full(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FullSync) } -func TestMultiProtoSynchronisation66Fast(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FastSync) } -func TestMultiProtoSynchronisation66Light(t *testing.T) { testMultiProtoSync(t, eth.ETH66, LightSync) } +func TestMultiProtoSynchronisation67Full(t *testing.T) { + testMultiProtoSync(t, istanbul.Celo67, FullSync) +} +func TestMultiProtoSynchronisation67Fast(t *testing.T) { + testMultiProtoSync(t, istanbul.Celo67, FastSync) +} +func TestMultiProtoSynchronisation67Light(t *testing.T) { + testMultiProtoSync(t, istanbul.Celo67, LightSync) +}   func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -840,8 +904,7 @@ // Create a small enough block chain to download chain := testChainBase.shorten(blockCacheMaxItems - 15)   // Create peers of every type - tester.newPeer("peer 66", eth.ETH66, chain) - //tester.newPeer("peer 65", eth.ETH67, chain) + tester.newPeer("peer 67", istanbul.Celo67, chain)   // Synchronise with the requested peer and make sure all blocks were retrieved if err := tester.sync(fmt.Sprintf("peer %d", protocol), nil, mode); err != nil { @@ -850,7 +913,7 @@ } assertOwnChain(t, tester, chain.len())   // Check that no peers have been dropped off - for _, version := range []int{66} { + for _, version := range []int{67} { peer := fmt.Sprintf("peer %d", version) if _, ok := tester.peers[peer]; !ok { t.Errorf("%s dropped", peer) @@ -858,61 +921,17 @@ } } }   -// Tests that if a block is empty (e.g. header only), no body request should be -// made, and instead the header should be assembled into a whole block in itself. -func TestEmptyShortCircuit66Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FullSync) } -func TestEmptyShortCircuit66Fast(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FastSync) } -func TestEmptyShortCircuit66Light(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, LightSync) } - -func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) { - t.Parallel() - - tester := newTester() - defer tester.terminate() - - // Create a block chain to download - chain := testChainBase - tester.newPeer("peer", protocol, chain) - - // Instrument the downloader to signal body requests - bodiesHave, receiptsHave := int32(0), int32(0) - tester.downloader.bodyFetchHook = func(headers []*types.Header) { - atomic.AddInt32(&bodiesHave, int32(len(headers))) - } - tester.downloader.receiptFetchHook = func(headers []*types.Header) { - atomic.AddInt32(&receiptsHave, int32(len(headers))) - } - // Synchronise with the peer and make sure all blocks were retrieved - if err := tester.sync("peer", nil, mode); err != nil { - t.Fatalf("failed to synchronise blocks: %v", err) - } - assertOwnChain(t, tester, chain.len()) - - // Validate the number of block bodies that should have been requested - bodiesNeeded, receiptsNeeded := 0, 0 - for _, block := range chain.blockm { - if mode != LightSync && block != tester.genesis && (len(block.Transactions()) > 0 || len(block.Uncles()) > 0) { - bodiesNeeded++ - } - } - for _, receipt := range chain.receiptm { - if mode == FastSync && len(receipt) > 0 { - receiptsNeeded++ - } - } - if int(bodiesHave) != bodiesNeeded { - t.Errorf("body retrieval count mismatch: have %v, want %v", bodiesHave, bodiesNeeded) - } - if int(receiptsHave) != receiptsNeeded { - t.Errorf("receipt retrieval count mismatch: have %v, want %v", receiptsHave, receiptsNeeded) - } -} - // Tests that headers are enqueued continuously, preventing malicious nodes from // stalling the downloader by feeding gapped header chains. -func TestMissingHeaderAttack66Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FullSync) } -func TestMissingHeaderAttack66Fast(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FastSync) } -func TestMissingHeaderAttack66Light(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, LightSync) } +func TestMissingHeaderAttack67Full(t *testing.T) { + testMissingHeaderAttack(t, istanbul.Celo67, FullSync) +} +func TestMissingHeaderAttack67Fast(t *testing.T) { + testMissingHeaderAttack(t, istanbul.Celo67, FastSync) +} +func TestMissingHeaderAttack67Light(t *testing.T) { + testMissingHeaderAttack(t, istanbul.Celo67, LightSync) +}   func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -938,9 +957,15 @@ }   // Tests that if requested headers are shifted (i.e. first is missing), the queue // detects the invalid numbering. -func TestShiftedHeaderAttack66Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FullSync) } -func TestShiftedHeaderAttack66Fast(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FastSync) } -func TestShiftedHeaderAttack66Light(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, LightSync) } +func TestShiftedHeaderAttack67Full(t *testing.T) { + testShiftedHeaderAttack(t, istanbul.Celo67, FullSync) +} +func TestShiftedHeaderAttack67Fast(t *testing.T) { + testShiftedHeaderAttack(t, istanbul.Celo67, FastSync) +} +func TestShiftedHeaderAttack67Light(t *testing.T) { + testShiftedHeaderAttack(t, istanbul.Celo67, LightSync) +}   func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -971,7 +996,9 @@ // Tests that upon detecting an invalid header, the recent ones are rolled back // for various failure scenarios. Afterwards a full sync is attempted to make // sure no state was corrupted. -func TestInvalidHeaderRollback66Fast(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH66, FastSync) } +func TestInvalidHeaderRollback67Fast(t *testing.T) { + testInvalidHeaderRollback(t, istanbul.Celo67, FastSync) +}   func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -979,7 +1006,7 @@ tester := newTester()   // Create a small enough block chain to download - targetBlocks := 3*fsHeaderSafetyNet + 256 + fsMinFullBlocks + targetBlocks := 3*fsHeaderSafetyNet + 256 + int(fsMinFullBlocks) chain := testChainBase.shorten(targetBlocks)   // Attempt to sync with an attacker that feeds junk during the fast sync phase. @@ -1061,14 +1088,14 @@ }   // Tests that a peer advertising a high TD doesn't get to stall the downloader // afterwards by not sending any useful hashes. -func TestHighTDStarvationAttack66Full(t *testing.T) { - testHighTDStarvationAttack(t, eth.ETH66, FullSync) +func TestHighTDStarvationAttack67Full(t *testing.T) { + testHighTDStarvationAttack(t, istanbul.Celo67, FullSync) } -func TestHighTDStarvationAttack66Fast(t *testing.T) { - testHighTDStarvationAttack(t, eth.ETH66, FastSync) +func TestHighTDStarvationAttack67Fast(t *testing.T) { + testHighTDStarvationAttack(t, istanbul.Celo67, FastSync) } -func TestHighTDStarvationAttack66Light(t *testing.T) { - testHighTDStarvationAttack(t, eth.ETH66, LightSync) +func TestHighTDStarvationAttack67Light(t *testing.T) { + testHighTDStarvationAttack(t, istanbul.Celo67, LightSync) }   func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) { @@ -1085,7 +1112,9 @@ tester.terminate() }   // Tests that misbehaving peers are disconnected, whilst behaving ones are not. -func TestBlockHeaderAttackerDropping66(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH66) } +func TestBlockHeaderAttackerDropping67(t *testing.T) { + testBlockHeaderAttackerDropping(t, istanbul.Celo67) +}   func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) { t.Parallel() @@ -1137,9 +1166,9 @@ }   // Tests that synchronisation progress (origin block number, current block number // and highest block number) is tracked and updated correctly. -func TestSyncProgress66Full(t *testing.T) { testSyncProgress(t, eth.ETH66, FullSync) } -func TestSyncProgress66Fast(t *testing.T) { testSyncProgress(t, eth.ETH66, FastSync) } -func TestSyncProgress66Light(t *testing.T) { testSyncProgress(t, eth.ETH66, LightSync) } +func TestSyncProgress67Full(t *testing.T) { testSyncProgress(t, istanbul.Celo67, FullSync) } +func TestSyncProgress67Fast(t *testing.T) { testSyncProgress(t, istanbul.Celo67, FastSync) } +func TestSyncProgress67Light(t *testing.T) { testSyncProgress(t, istanbul.Celo67, LightSync) }   func testSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -1217,9 +1246,11 @@ // Tests that synchronisation progress (origin block number and highest block // number) is tracked and updated correctly in case of a fork (or manual head // revertal). -func TestForkedSyncProgress66Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FullSync) } -func TestForkedSyncProgress66Fast(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FastSync) } -func TestForkedSyncProgress66Light(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, LightSync) } +func TestForkedSyncProgress67Full(t *testing.T) { testForkedSyncProgress(t, istanbul.Celo67, FullSync) } +func TestForkedSyncProgress67Fast(t *testing.T) { testForkedSyncProgress(t, istanbul.Celo67, FastSync) } +func TestForkedSyncProgress67Light(t *testing.T) { + testForkedSyncProgress(t, istanbul.Celo67, LightSync) +}   func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -1289,9 +1320,11 @@ // Tests that if synchronisation is aborted due to some failure, then the progress // origin is not updated in the next sync cycle, as it should be considered the // continuation of the previous sync and not a new instance. -func TestFailedSyncProgress66Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FullSync) } -func TestFailedSyncProgress66Fast(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FastSync) } -func TestFailedSyncProgress66Light(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, LightSync) } +func TestFailedSyncProgress67Full(t *testing.T) { testFailedSyncProgress(t, istanbul.Celo67, FullSync) } +func TestFailedSyncProgress67Fast(t *testing.T) { testFailedSyncProgress(t, istanbul.Celo67, FastSync) } +func TestFailedSyncProgress67Light(t *testing.T) { + testFailedSyncProgress(t, istanbul.Celo67, LightSync) +}   func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -1358,9 +1391,9 @@ }   // Tests that if an attacker fakes a chain height, after the attack is detected, // the progress height is successfully reduced at the next sync invocation. -func TestFakedSyncProgress66Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FullSync) } -func TestFakedSyncProgress66Fast(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FastSync) } -func TestFakedSyncProgress66Light(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, LightSync) } +func TestFakedSyncProgress67Full(t *testing.T) { testFakedSyncProgress(t, istanbul.Celo67, FullSync) } +func TestFakedSyncProgress67Fast(t *testing.T) { testFakedSyncProgress(t, istanbul.Celo67, FastSync) } +func TestFakedSyncProgress67Light(t *testing.T) { testFakedSyncProgress(t, istanbul.Celo67, LightSync) }   func testFakedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -1431,9 +1464,11 @@ }   // This test reproduces an issue where unexpected deliveries would // block indefinitely if they arrived at the right time. -func TestDeliverHeadersHang66Full(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FullSync) } -func TestDeliverHeadersHang66Fast(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FastSync) } -func TestDeliverHeadersHang66Light(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, LightSync) } +func TestDeliverHeadersHang67Full(t *testing.T) { testDeliverHeadersHang(t, istanbul.Celo67, FullSync) } +func TestDeliverHeadersHang67Fast(t *testing.T) { testDeliverHeadersHang(t, istanbul.Celo67, FastSync) } +func TestDeliverHeadersHang67Light(t *testing.T) { + testDeliverHeadersHang(t, istanbul.Celo67, LightSync) +}   func testDeliverHeadersHang(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -1588,10 +1623,14 @@ }   // Tests that peers below a pre-configured checkpoint block are prevented from // being fast-synced from, avoiding potential cheap eclipse attacks. -func TestCheckpointEnforcement66Full(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FullSync) } -func TestCheckpointEnforcement66Fast(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FastSync) } -func TestCheckpointEnforcement66Light(t *testing.T) { - testCheckpointEnforcement(t, eth.ETH66, LightSync) +func TestCheckpointEnforcement67Full(t *testing.T) { + testCheckpointEnforcement(t, istanbul.Celo67, FullSync) +} +func TestCheckpointEnforcement67Fast(t *testing.T) { + testCheckpointEnforcement(t, istanbul.Celo67, FastSync) +} +func TestCheckpointEnforcement67Light(t *testing.T) { + testCheckpointEnforcement(t, istanbul.Celo67, LightSync) }   func testCheckpointEnforcement(t *testing.T, protocol uint, mode SyncMode) { @@ -1601,7 +1640,7 @@ // Create a new tester with a particular hard coded checkpoint block tester := newTester() defer tester.terminate()   - tester.downloader.checkpoint = uint64(fsMinFullBlocks) + 256 + tester.downloader.checkpoint = fsMinFullBlocks + 256 chain := testChainBase.shorten(int(tester.downloader.checkpoint) - 1)   // Attempt to sync with the peer and validate the result @@ -1620,3 +1659,24 @@ } else { assertOwnChain(t, tester, chain.len()) } } + +func TestPivot(t *testing.T) { + testCases := []struct { + height uint64 + epoch uint64 + expected uint64 + }{ + {0, 0, 0}, + {172, 17280, 0}, + {17280, 17280, 0}, + {17280*10 + 1000, 17280, 17280*10 + 1}, + {17280*10 + 10, 17280, 17280*9 + 1}, + {17280 * 10, 17280, 17280*9 + 1}, + {17280*10 - 1000, 17280, 17280*9 + 1}, + } + for _, tt := range testCases { + if res := computePivot(tt.height, tt.epoch); res != tt.expected { + t.Errorf("Got %v expected %v for value %v", res, tt.expected, tt.height) + } + } +}
diff --git go-ethereum/eth/downloader/queue_test.go celo/eth/downloader/queue_test.go index 321872d7442d232d797a513ab8c77dbbf9ed1ff5..9eeee4e6716052079e7ab7eaec4a475b497cb22b 100644 --- go-ethereum/eth/downloader/queue_test.go +++ celo/eth/downloader/queue_test.go @@ -25,7 +25,7 @@ "testing" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -43,12 +43,12 @@ // the returned hash chain is ordered head->parent. In addition, every 3rd block // contains a transaction and every 5th an uncle to allow testing correct block // reassembly. func makeChain(n int, seed byte, parent *types.Block, empty bool) ([]*types.Block, []types.Receipts) { - blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, func(i int, block *core.BlockGen) { + blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, mockEngine.NewFaker(), testdb, n, func(i int, block *core.BlockGen) { block.SetCoinbase(common.Address{seed}) // Add one tx to every secondblock if !empty && i%2 == 0 { signer := types.MakeSigner(params.TestChainConfig, block.Number()) - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, testKey) if err != nil { panic(err) } @@ -131,7 +131,7 @@ // queue size is only 10, so throttling should occur t.Fatal("should throttle") } // But we should still get the first things to fetch - if got, exp := len(fetchReq.Headers), 5; got != exp { + if got, exp := len(fetchReq.Headers), 10; got != exp { t.Fatalf("expected %d requests, got %d", exp, got) } if got, exp := fetchReq.Headers[0].Number.Uint64(), uint64(1); got != exp { @@ -223,9 +223,9 @@ // Reserve blocks peer := dummyPeer("peer-1") fetchReq, _, _ := q.ReserveBodies(peer, 50)   - // there should be nothing to fetch, blocks are empty - if fetchReq != nil { - t.Fatal("there should be no body fetch tasks remaining") + // blocks are empty, but must be fetched the bodies so that the random beacon can be updated correctly + if fetchReq == nil { + t.Fatal("there should be body fetch tasks remaining") }   } @@ -250,16 +250,17 @@ } if q.receiptTaskQueue.Size() != 0 { t.Errorf("expected receipt task queue to be %d, got %d", 0, q.receiptTaskQueue.Size()) } - if got, exp := q.resultCache.countCompleted(), 10; got != exp { + if got, exp := q.resultCache.countCompleted(), 0; got != exp { t.Errorf("wrong processable count, got %d, exp %d", got, exp) } }   -// XTestDelivery does some more extensive testing of events that happen, +// TestDelivery does some more extensive testing of events that happen, // blocks that become known and peers that make reservations and deliveries. // disabled since it's not really a unit-test, but can be executed to test // some more advanced scenarios -func XTestDelivery(t *testing.T) { +func TestDelivery(t *testing.T) { + t.Skip("not really a unit-test, but can be executed to test some more advanced scenarios") // the outside network, holding blocks blo, rec := makeChain(128, 0, genesis, false) world := newNetwork() @@ -311,16 +312,17 @@ for { peer := dummyPeer(fmt.Sprintf("peer-%d", i)) f, _, _ := q.ReserveBodies(peer, rand.Intn(30)) if f != nil { - var emptyList []*types.Header var txs [][]*types.Transaction - var uncles [][]*types.Header + var randomnessList []*types.Randomness + var epochSnarkDataList []*types.EpochSnarkData numToSkip := rand.Intn(len(f.Headers)) for _, hdr := range f.Headers[0 : len(f.Headers)-numToSkip] { txs = append(txs, world.getTransactions(hdr.Number.Uint64())) - uncles = append(uncles, emptyList) + randomnessList = append(randomnessList, &types.Randomness{}) + epochSnarkDataList = append(epochSnarkDataList, &types.EpochSnarkData{}) } time.Sleep(100 * time.Millisecond) - _, err := q.DeliverBodies(peer.id, txs, uncles) + _, err := q.DeliverBodies(peer.id, txs, randomnessList, epochSnarkDataList) if err != nil { fmt.Printf("delivered %d bodies %v\n", len(txs), err) }
diff --git go-ethereum/eth/downloader/peer.go celo/eth/downloader/peer.go index 52ca8c99e958ce069822ced25706ff6b16e11125..d13be564e31028e316ea933edb9ca3d8dabe6942 100644 --- go-ethereum/eth/downloader/peer.go +++ celo/eth/downloader/peer.go @@ -28,6 +28,7 @@ "sync/atomic" "time"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -413,7 +414,7 @@ } throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.BlockHeadersMsg, time.Second) } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) + return ps.idlePeers(istanbul.Celo67, istanbul.Celo67, idle, throughput) }   // BodyIdlePeers retrieves a flat list of all the currently body-idle peers within @@ -425,7 +426,7 @@ } throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.BlockBodiesMsg, time.Second) } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) + return ps.idlePeers(istanbul.Celo67, istanbul.Celo67, idle, throughput) }   // ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers @@ -437,7 +438,7 @@ } throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.ReceiptsMsg, time.Second) } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) + return ps.idlePeers(istanbul.Celo67, istanbul.Celo67, idle, throughput) }   // NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle @@ -449,7 +450,7 @@ } throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.NodeDataMsg, time.Second) } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) + return ps.idlePeers(istanbul.Celo67, istanbul.Celo67, idle, throughput) }   // idlePeers retrieves a flat list of all currently idle peers satisfying the
diff --git go-ethereum/eth/downloader/downloader.go celo/eth/downloader/downloader.go index 4558270a57c034eaef5f957316a8601dcc386ef0..f01be7031fc7ba7800b2e6a603f0b0bc381181c8 100644 --- go-ethereum/eth/downloader/downloader.go +++ celo/eth/downloader/downloader.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" + "math" "math/big" "sync" "sync/atomic" @@ -27,10 +28,10 @@ "time"   "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" @@ -43,6 +44,7 @@ var ( MaxBlockFetch = 128 // Amount of blocks to be fetched per retrieval request MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request + MaxEpochHeaderFetch = 192 // Number of epoch block headers to fetch (only used in IBFT consensus + Lightest sync mode) MaxSkeletonSize = 128 // Number of header fetches to need for a skeleton assembly MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request MaxStateFetch = 384 // Amount of node state values to allow fetching per request @@ -60,7 +62,7 @@ fsHeaderCheckFrequency = 100 // Verification frequency of the downloaded headers during fast sync fsHeaderSafetyNet = 2048 // Number of headers to discard in case a chain violation is detected fsHeaderForceVerify = 24 // Number of headers to verify before and after the pivot to accept it fsHeaderContCheck = 3 * time.Second // Time interval to check for header continuations during state download - fsMinFullBlocks = 64 // Number of blocks to retrieve fully even in fast sync + fsMinFullBlocks uint64 = 64 // Number of blocks to retrieve fully even in fast sync )   var ( @@ -85,6 +87,10 @@ errTooOld = errors.New("peer's protocol version too old") errNoAncestorFound = errors.New("no common ancestor found") )   +// If you adding a new variable add it at the bottom. Otherwise, you can end up making some uint64 unaligned to 8-byte +// boundary. That seems fine with ARM but on X86 (emulator), atomic loading of 64-bit variables causes a confusing crash. +// Some variables like rttEstimate are loaded atomically with atomic.LoadUint64() + type Downloader struct { mode uint32 // Synchronisation mode defining the strategy used (per sync cycle), use d.getMode() to get the SyncMode mux *event.TypeMux // Event multiplexer to announce sync operation events @@ -115,6 +121,7 @@ synchronising int32 notified int32 committed int32 ancientLimit uint64 // The maximum block number which can be regarded as ancient data. + ancientLimitMu sync.Mutex   // Channels headerCh chan dataPack // Channel receiving inbound block headers @@ -142,6 +149,8 @@ cancelWg sync.WaitGroup // Make sure all fetcher goroutines have exited.   quitCh chan struct{} // Quit channel to signal termination quitLock sync.Mutex // Lock to prevent double closes + epoch uint64 // Epoch value is useful in IBFT consensus + ibftConsensus bool // True if we are in IBFT consensus mode   // Testing hooks syncInitHook func(uint64, uint64) // Method to call upon initiating a new sync run @@ -158,6 +167,9 @@ // GetHeaderByHash retrieves a header from the local chain. GetHeaderByHash(common.Hash) *types.Header   + // GetHeaderByHash retrieves a header from the local chain by number. + GetHeaderByNumber(uint64) *types.Header + // CurrentHeader retrieves the head header from the local chain. CurrentHeader() *types.Header   @@ -165,8 +177,9 @@ // GetTd returns the total difficulty of a local block. GetTd(common.Hash, uint64) *big.Int   // InsertHeaderChain inserts a batch of headers into the local chain. - InsertHeaderChain([]*types.Header, int) (int, error) + InsertHeaderChain([]*types.Header, int, bool) (int, error)   + Config() *params.ChainConfig // SetHead rewinds the local chain to a new head. SetHead(uint64) error } @@ -198,16 +211,35 @@ InsertChain(types.Blocks) (int, error)   // InsertReceiptChain inserts a batch of receipts into the local chain. InsertReceiptChain(types.Blocks, []types.Receipts, uint64) (int, error) + + // GetBlockByNumber retrieves a block from the database by number. + GetBlockByNumber(uint64) *types.Block   // Snapshots returns the blockchain snapshot tree to paused it during sync. Snapshots() *snapshot.Tree }   +// TODO(tim) previously passing mode here! + // New creates a new downloader to fetch hashes and blocks from remote peers. func New(checkpoint uint64, stateDb ethdb.Database, stateBloom *trie.SyncBloom, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn) *Downloader { if lightchain == nil { lightchain = chain } + + ibftConsensus := false + epoch := uint64(0) + if chain != nil && chain.Config() != nil && chain.Config().Istanbul != nil { + epoch = chain.Config().Istanbul.Epoch + ibftConsensus = true + } else if lightchain != nil && lightchain.Config() != nil && lightchain.Config().Istanbul != nil { + epoch = lightchain.Config().Istanbul.Epoch + ibftConsensus = true + } + if epoch > math.MaxInt32 { + panic(fmt.Sprintf("epoch is too big(%d), the code to fetch epoch headers casts epoch to an int to calculate value for skip variable", epoch)) + } + dl := &Downloader{ stateDB: stateDb, stateBloom: stateBloom, @@ -232,6 +264,8 @@ syncStatsState: stateSyncStats{ processed: rawdb.ReadFastTrieProgress(stateDb), }, trackStateReq: make(chan *stateReq), + ibftConsensus: ibftConsensus, + epoch: epoch, } go dl.stateFetcher() return dl @@ -261,6 +295,7 @@ current = d.lightchain.CurrentHeader().Number.Uint64() default: log.Error("Unknown downloader chain/mode combo", "light", d.lightchain != nil, "full", d.blockchain != nil, "mode", mode) } + log.Debug(fmt.Sprintf("Current head is %v", current)) return ethereum.SyncProgress{ StartingBlock: d.syncStatsChainOrigin, CurrentBlock: current, @@ -448,8 +483,8 @@ latest := d.lightchain.CurrentHeader() d.mux.Post(DoneEvent{latest}) } }() - if p.version < eth.ETH66 { - return fmt.Errorf("%w: advertized %d < required %d", errTooOld, p.version, eth.ETH66) + if p.version < istanbul.Celo67 { + return fmt.Errorf("%w: advertized %d < required %d", errTooOld, p.version, istanbul.Celo67) } mode := d.getMode()   @@ -480,21 +515,20 @@ d.syncStatsLock.Lock() if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin { d.syncStatsChainOrigin = origin } + log.Debug(fmt.Sprintf("After the check origin is %d height is %d", origin, height)) d.syncStatsChainHeight = height d.syncStatsLock.Unlock()   // Ensure our origin point is below any fast sync pivot point if mode == FastSync { - if height <= uint64(fsMinFullBlocks) { + pivotNumber := pivot.Number.Uint64() + // Write out the pivot into the database so a rollback beyond it will + // reenable fast sync + rawdb.WriteLastPivotNumber(d.stateDB, pivotNumber) + if pivotNumber == 0 { origin = 0 - } else { - pivotNumber := pivot.Number.Uint64() - if pivotNumber <= origin { - origin = pivotNumber - 1 - } - // Write out the pivot into the database so a rollback beyond it will - // reenable fast sync - rawdb.WriteLastPivotNumber(d.stateDB, pivotNumber) + } else if pivotNumber <= origin { + origin = pivotNumber - 1 } } d.committed = 1 @@ -546,7 +580,7 @@ if d.syncInitHook != nil { d.syncInitHook(origin, height) } fetchers := []func() error{ - func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved + func() error { return d.fetchHeaders(p, origin+1, height) }, // Headers are always retrieved func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync func() error { return d.processHeaders(origin+1, td) }, @@ -613,6 +647,10 @@ // finish before returning. func (d *Downloader) Cancel() { d.cancel() d.cancelWg.Wait() + d.ancientLimitMu.Lock() + defer d.ancientLimitMu.Unlock() + d.ancientLimit = 0 + log.Debug("Reset ancient limit to zero") }   // Terminate interrupts the downloader, canceling all pending operations. @@ -641,12 +679,18 @@ p.log.Debug("Retrieving remote chain head") mode := d.getMode()   // Request the advertised remote head block and wait for the response - latest, _ := p.peer.Head() + latest, td := p.peer.Head() fetch := 1 if mode == FastSync { fetch = 2 // head + pivot headers } - go p.peer.RequestHeadersByHash(latest, fetch, fsMinFullBlocks-1, true) + height := td.Uint64() - 1 // height == TD - 1 + beginningEpochBlockNumber := d.calcPivot(height) + // NOTE: the beginningEpochBlockNumber is subtracting fsMinFullBlocks to the height, + // so, height and beginningEpochBlockNumber will be the same ONLY if the head is the genesis block + blocksFromHeightToEpochBlock := height - beginningEpochBlockNumber + + go p.peer.RequestHeadersByHash(latest, fetch, int(blocksFromHeightToEpochBlock-1), true)   ttl := d.peers.rates.TargetTimeout() timeout := time.After(ttl) @@ -674,7 +718,7 @@ if (mode == FastSync || mode == LightSync) && head.Number.Uint64() < d.checkpoint { return nil, nil, fmt.Errorf("%w: remote head %d below checkpoint %d", errUnsyncedPeer, head.Number, d.checkpoint) } if len(headers) == 1 { - if mode == FastSync && head.Number.Uint64() > uint64(fsMinFullBlocks) { + if mode == FastSync && head.Number.Uint64() > blocksFromHeightToEpochBlock { return nil, nil, fmt.Errorf("%w: no pivot included along head header", errBadPeer) } p.log.Debug("Remote head identified, no pivot", "number", head.Number, "hash", head.Hash()) @@ -683,8 +727,8 @@ } // At this point we have 2 headers in total and the first is the // validated head of the chain. Check the pivot number and return, pivot := headers[1] - if pivot.Number.Uint64() != head.Number.Uint64()-uint64(fsMinFullBlocks) { - return nil, nil, fmt.Errorf("%w: remote pivot %d != requested %d", errInvalidChain, pivot.Number, head.Number.Uint64()-uint64(fsMinFullBlocks)) + if pivot.Number.Uint64() != beginningEpochBlockNumber { + return nil, nil, fmt.Errorf("%w: remote pivot %d != requested %d", errInvalidChain, pivot.Number, beginningEpochBlockNumber) } return head, pivot, nil   @@ -783,9 +827,12 @@ if localHeight >= maxForkAncestry { // We're above the max reorg threshold, find the earliest fork point floor = int64(localHeight - maxForkAncestry) } + + // TODO(tim) TODO(ashishb) see https://github.com/ethereum/go-ethereum/commit/6c312a24b6041385c33eca066ff5604af315a41e + // If we're doing a light sync, ensure the floor doesn't go below the CHT, as // all headers before that point will be missing. - if mode == LightSync { + if !mode.SyncFullBlockChain() { // If we don't know the current CHT position, find it if d.genesis == 0 { header := d.lightchain.CurrentHeader() @@ -855,7 +902,7 @@ // Make sure the peer's reply conforms to the request for i, header := range headers { expectNumber := from + int64(i)*int64(skip+1) if number := header.Number.Int64(); number != expectNumber { - p.log.Warn("Head headers broke chain ordering", "index", i, "requested", expectNumber, "received", number) + p.log.Warn("Head headers broke chain ordering", "index", i, "requested", expectNumber, "received", number, "localHeight", localHeight, "remoteHeight", remoteHeight) return 0, fmt.Errorf("%w: %v", errInvalidChain, errors.New("head headers broke chain ordering")) } } @@ -997,8 +1044,9 @@ // syncing with, and fill in the missing headers using anyone else. Headers from // other peers are only accepted if they map cleanly to the skeleton. If no one // can fill in the skeleton - not even the origin peer - it's assumed invalid and // the origin is dropped. -func (d *Downloader) fetchHeaders(p *peerConnection, from uint64) error { - p.log.Debug("Directing header downloads", "origin", from) +// height = latest block announced by the peers. +func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, height uint64) error { + p.log.Debug("fetchHeaders", "origin", from, "height", height) defer p.log.Debug("Header download terminated")   // Create a timeout timer, and the associated header fetcher @@ -1008,6 +1056,7 @@ request := time.Now() // time of the last skeleton fetch request timeout := time.NewTimer(0) // timer to dump a non-responsive active peer <-timeout.C // timeout channel should be initially empty defer timeout.Stop() + epoch := d.epoch   var ttl time.Duration getHeaders := func(from uint64) { @@ -1020,29 +1069,91 @@ if skeleton { p.log.Trace("Fetching skeleton headers", "count", MaxHeaderFetch, "from", from) go p.peer.RequestHeadersByNumber(from+uint64(MaxHeaderFetch)-1, MaxSkeletonSize, MaxHeaderFetch-1, false) } else { - p.log.Trace("Fetching full headers", "count", MaxHeaderFetch, "from", from) - go p.peer.RequestHeadersByNumber(from, MaxHeaderFetch, 0, false) + count := MaxHeaderFetch + skip := 0 + p.log.Trace("Fetching full headers", "count", count, "from", from) + go p.peer.RequestHeadersByNumber(from, MaxHeaderFetch, skip, false) } } - getNextPivot := func() { - pivoting = true + + mode := d.getMode() + getEpochHeaders := func(fromEpochBlock uint64) { + if mode != LightestSync { + panic("This method should be called only in LightestSync mode") + } + if fromEpochBlock%epoch != 0 { + panic(fmt.Sprintf( + "Logic error: getEpochHeaders received a request to fetch non-epoch block %d with epoch %d", + fromEpochBlock, epoch)) + } + request = time.Now()   ttl = d.peers.rates.TargetTimeout() timeout.Reset(ttl)   - d.pivotLock.RLock() - pivot := d.pivotHeader.Number.Uint64() - d.pivotLock.RUnlock() + // if epoch is 100 and we fetch from=1000 and skip=100 then we will get + // 1000, 1101, 1202, 1303 ... + // So, skip has to be epoch - 1 to get the right set of blocks. + skip := int(epoch - 1) + count := MaxEpochHeaderFetch + log.Trace("getEpochHeaders", "from", fromEpochBlock, "count", count, "skip", skip) + p.log.Trace("Fetching full headers", "count", count, "from", fromEpochBlock) + go p.peer.RequestHeadersByNumber(fromEpochBlock, count, skip, false) + }   - p.log.Trace("Fetching next pivot header", "number", pivot+uint64(fsMinFullBlocks)) - go p.peer.RequestHeadersByNumber(pivot+uint64(fsMinFullBlocks), 2, fsMinFullBlocks-9, false) // move +64 when it's 2x64-8 deep + // Returns true if a header(s) fetch request was made, false if the syncing is finished. + getEpochOrNormalHeaders := func(from uint64) bool { + // Download the epoch headers including and beyond the current head. + nextEpochBlock := (from-1)/epoch*epoch + epoch + // If we're still not synced up to the latest epoch, sync only epoch headers. + // Otherwise, sync block headers as we would normally in light sync. + log.Trace("Getting headers in lightest sync mode", "from", from, "height", height, "nextEpochBlock", nextEpochBlock, "epoch", epoch) + if nextEpochBlock < height { + getEpochHeaders(nextEpochBlock) + return true + } else if from <= height { + getHeaders(height) + return true + } else { + // During repeated invocations, "from" can be more than height since the blocks could have + // created after this method was invoked and in that case, the from which is one beyond the + // last fetched header number can be more than the height. + // If we have already fetched a block header >= height block header then we declare that the sync + // is finished and stop. + return false + } } + // TODO(ponti): Re add the "moving" pivot, after changing the way we calculate the uptimeScore + // getNextPivot := func() { + // pivoting = true + // request = time.Now() + + // ttl = d.requestTTL() + // timeout.Reset(ttl) + + // d.pivotLock.RLock() + // pivot := d.pivotHeader.Number.Uint64() + // d.pivotLock.RUnlock() + + // p.log.Trace("Fetching next pivot header", "number", pivot+fsMinFullBlocks) + // go p.peer.RequestHeadersByNumber(pivot+fsMinFullBlocks, 2, int(fsMinFullBlocks-9), false) // move +64 when it's 2x64-8 deep + // } // Start pulling the header chain skeleton until all is done ancestor := from - getHeaders(from) + + if mode == LightestSync { + if epoch == 0 { + panic("Epoch cannot be 0 in IBFT + LightestSync") + } + // Don't fetch skeleton, only fetch the headers. + skeleton = false + getEpochOrNormalHeaders(from) + } else { + log.Trace("getHeaders#initialHeaderDownload", "from", from) + getHeaders(from) + }   - mode := d.getMode() for { select { case <-d.cancelCh: @@ -1071,11 +1182,11 @@ if packet.Items() == 2 { // Retrieve the headers and do some sanity checks, just in case headers := packet.(*headerPack).headers   - if have, want := headers[0].Number.Uint64(), pivot+uint64(fsMinFullBlocks); have != want { + if have, want := headers[0].Number.Uint64(), pivot+fsMinFullBlocks; have != want { log.Warn("Peer sent invalid next pivot", "have", have, "want", want) return fmt.Errorf("%w: next pivot number %d != requested %d", errInvalidChain, have, want) } - if have, want := headers[1].Number.Uint64(), pivot+2*uint64(fsMinFullBlocks)-8; have != want { + if have, want := headers[1].Number.Uint64(), pivot+2*fsMinFullBlocks-8; have != want { log.Warn("Peer sent invalid pivot confirmer", "have", have, "want", want) return fmt.Errorf("%w: next pivot confirmer number %d != requested %d", errInvalidChain, have, want) } @@ -1098,6 +1209,7 @@ } // If the skeleton's finished, pull any remaining head headers directly from the origin if skeleton && packet.Items() == 0 { skeleton = false + log.Trace("getHeaders, skeleton finished, download remaining headers") getHeaders(from) continue } @@ -1123,6 +1235,7 @@ case <-d.cancelCh: return errCanceled } } + // Received headers headers := packet.(*headerPack).headers   // If we received a skeleton batch, resolve internals concurrently @@ -1138,30 +1251,34 @@ } else { // If we're closing in on the chain head, but haven't yet reached it, delay // the last few headers so mini reorgs on the head don't cause invalid hash // chain errors. - if n := len(headers); n > 0 { - // Retrieve the current head we're at - var head uint64 - if mode == LightSync { - head = d.lightchain.CurrentHeader().Number.Uint64() - } else { - head = d.blockchain.CurrentFastBlock().NumberU64() - if full := d.blockchain.CurrentBlock().NumberU64(); head < full { - head = full + + // Don't delay last few headers in IBFT since we are not expecting chain reorgs in IBFT + if !d.ibftConsensus { + if n := len(headers); n > 0 { + // Retrieve the current head we're at + var head uint64 + if mode == LightSync { + head = d.lightchain.CurrentHeader().Number.Uint64() + } else { + head = d.blockchain.CurrentFastBlock().NumberU64() + if full := d.blockchain.CurrentBlock().NumberU64(); head < full { + head = full + } } - } - // If the head is below the common ancestor, we're actually deduplicating - // already existing chain segments, so use the ancestor as the fake head. - // Otherwise we might end up delaying header deliveries pointlessly. - if head < ancestor { - head = ancestor - } - // If the head is way older than this batch, delay the last few headers - if head+uint64(reorgProtThreshold) < headers[n-1].Number.Uint64() { - delay := reorgProtHeaderDelay - if delay > n { - delay = n + // If the head is below the common ancestor, we're actually deduplicating + // already existing chain segments, so use the ancestor as the fake head. + // Otherwise we might end up delaying header deliveries pointlessly. + if head < ancestor { + head = ancestor + } + // If the head is way older than this batch, delay the last few headers + if head+uint64(reorgProtThreshold) < headers[n-1].Number.Uint64() { + delay := reorgProtHeaderDelay + if delay > n { + delay = n + } + headers = headers[:n-delay] } - headers = headers[:n-delay] } } } @@ -1173,21 +1290,43 @@ case d.headerProcCh <- headers: case <-d.cancelCh: return errCanceled } - from += uint64(len(headers)) + // In all other sync modes, we fetch the block immediately after the current block. + // In the lightest sync mode, increment the value by epoch instead. + if mode == LightestSync { + lastFetchedHeaderNumber := headers[len(headers)-1].Number.Uint64() + moreHeaderFetchesPending := getEpochOrNormalHeaders(lastFetchedHeaderNumber + 1) + if !moreHeaderFetchesPending { + p.log.Debug("No more headers available") + select { + case d.headerProcCh <- nil: + return nil + case <-d.cancelCh: + return errCanceled + } + } + } else { + from += uint64(len(headers)) + log.Trace("getHeaders#downloadMoreHeaders", "from", from) + // If we're still skeleton filling fast sync, check pivot staleness + // before continuing to the next skeleton filling   - // If we're still skeleton filling fast sync, check pivot staleness - // before continuing to the next skeleton filling - if skeleton && pivot > 0 { - getNextPivot() - } else { + // TODO(ponti): Re add the "moving" pivot, after changing the way we calculate the uptimeScore + // if skeleton && pivot > 0 { + // getNextPivot() + // } else { getHeaders(from) + // } } } else { // No headers delivered, or all of them being delayed, sleep a bit and retry p.log.Trace("All headers delayed, waiting") select { case <-time.After(fsHeaderContCheck): - getHeaders(from) + if mode == LightestSync { + getEpochOrNormalHeaders(from) + } else { + getHeaders(from) + } continue case <-d.cancelCh: return errCanceled @@ -1202,7 +1341,7 @@ p.log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", p.id) break } // Header retrieval timed out, consider the peer bad and drop - p.log.Debug("Header request timed out", "elapsed", ttl) + p.log.Warn("Header request timed out, dropping peer", "elapsed", ttl) headerTimeoutMeter.Mark(1) d.dropPeer(p.id)   @@ -1269,7 +1408,7 @@ var ( deliver = func(packet dataPack) (int, error) { pack := packet.(*bodyPack) - return d.queue.DeliverBodies(pack.peerID, pack.transactions, pack.uncles) + return d.queue.DeliverBodies(pack.peerID, pack.transactions, pack.randomness, pack.epochSnarkData) } expire = func() map[string]int { return d.queue.ExpireBodies(d.peers.rates.TargetTimeout()) } fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchBodies(req) } @@ -1422,7 +1561,7 @@ if fails > 2 { peer.log.Trace("Data delivery timed out", "type", kind) setIdle(peer, 0, time.Now()) } else { - peer.log.Debug("Stalling delivery, dropping", "type", kind) + peer.log.Warn("Stalling delivery, dropping", "type", kind)   if d.dropPeer == nil { // The dropPeer method is nil when `--copydb` is used for a local copy. @@ -1520,7 +1659,7 @@ ) defer func() { if rollback > 0 { lastHeader, lastFastBlock, lastBlock := d.lightchain.CurrentHeader().Number, common.Big0, common.Big0 - if mode != LightSync { + if mode.SyncFullBlockChain() { lastFastBlock = d.blockchain.CurrentFastBlock().Number() lastBlock = d.blockchain.CurrentBlock().Number() } @@ -1529,7 +1668,7 @@ // We're already unwinding the stack, only print the error to make it more visible log.Error("Failed to roll back chain segment", "head", rollback-1, "err", err) } curFastBlock, curBlock := common.Big0, common.Big0 - if mode != LightSync { + if mode.SyncFullBlockChain() { curFastBlock = d.blockchain.CurrentFastBlock().Number() curBlock = d.blockchain.CurrentBlock().Number() } @@ -1570,9 +1709,10 @@ // L: Import of block 11 finishes // L: Sync begins, and finds common ancestor at 11 // L: Request new headers up from 11 (R's TD was higher, it must have something) // R: Nothing to give - if mode != LightSync { + if mode.SyncFullBlockChain() { head := d.blockchain.CurrentBlock() if !gotHeaders && td.Cmp(d.blockchain.GetTd(head.Hash(), head.NumberU64())) > 0 { + rollbackErr = errStallingPeer return errStallingPeer } } @@ -1586,6 +1726,7 @@ // peer gave us something useful, we're already happy/progressed (above check). if mode == FastSync || mode == LightSync { head := d.lightchain.CurrentHeader() if td.Cmp(d.lightchain.GetTd(head.Hash(), head.Number.Uint64())) > 0 { + rollbackErr = errStallingPeer return errStallingPeer } } @@ -1611,7 +1752,7 @@ } chunk := headers[:limit]   // In case of header only syncing, validate the chunk immediately - if mode == FastSync || mode == LightSync { + if mode == FastSync || !mode.SyncFullBlockChain() { // mode != FullSync ? // If we're importing pure headers, verify based on their recentness var pivot uint64   @@ -1625,7 +1766,7 @@ frequency := fsHeaderCheckFrequency if chunk[len(chunk)-1].Number.Uint64()+uint64(fsHeaderForceVerify) > pivot { frequency = 1 } - if n, err := d.lightchain.InsertHeaderChain(chunk, frequency); err != nil { + if n, err := d.lightchain.InsertHeaderChain(chunk, frequency, mode.SyncFullHeaderChain()); err != nil { rollbackErr = err   // If some headers were inserted, track them as uncertain @@ -1646,7 +1787,7 @@ } } } // Unless we're doing light chains, schedule the headers for associated content retrieval - if mode == FullSync || mode == FastSync { + if mode.SyncFullBlockChain() { // If we've reached the allowed number of pending headers, stall a bit for d.queue.PendingBlocks() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders { select { @@ -1659,6 +1800,7 @@ } // Otherwise insert the headers for content retrieval inserts := d.queue.Schedule(chunk, origin) if len(inserts) != len(chunk) { + log.Debug("Stale headers") rollbackErr = fmt.Errorf("stale headers: len inserts %v len(chunk) %v", len(inserts), len(chunk)) return fmt.Errorf("%w: stale headers", errBadPeer) } @@ -1718,7 +1860,7 @@ "lastnum", last.Number, "lasthash", last.Hash(), ) blocks := make([]*types.Block, len(results)) for i, result := range results { - blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles) + blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Randomness, result.EpochSnarkData) } if index, err := d.blockchain.InsertChain(blocks); err != nil { if index < len(results) { @@ -1735,6 +1877,42 @@ } return nil }   +func max(a uint64, b uint64) uint64 { + if a < b { + return b + } + return a +} + +func computePivot(height uint64, epochSize uint64) uint64 { + if height <= fsMinFullBlocks { + return 0 + } + target := height - fsMinFullBlocks + targetEpoch := istanbul.GetEpochNumber(target, epochSize) + + // if target is on first epoch start on genesis + if targetEpoch <= 1 { + return 0 + } + + // else start on first block of the epoch + pivot, _ := istanbul.GetEpochFirstBlockNumber(targetEpoch, epochSize) + return pivot + +} + +func (d *Downloader) calcPivot(height uint64) uint64 { + // If epoch is not set (not IBFT) use old logic + if d.epoch == 0 { + if fsMinFullBlocks > height { + return 0 + } + return height - fsMinFullBlocks + } + return computePivot(height, d.epoch) +} + // processFastSyncContent takes fetch results from the queue and writes them to the // database. It also controls the synchronisation of state nodes of the pivot block. func (d *Downloader) processFastSyncContent() error { @@ -1757,7 +1935,6 @@ d.queue.Close() // wake up Results } } go closeOnErr(sync) - // To cater for moving pivot points, track the pivot block and subsequently // accumulated download results separately. var ( @@ -1810,17 +1987,17 @@ // // Note, we have `reorgProtHeaderDelay` number of blocks withheld, Those // need to be taken into account, otherwise we're detecting the pivot move // late and will drop peers due to unavailable state!!! - if height := latest.Number.Uint64(); height >= pivot.Number.Uint64()+2*uint64(fsMinFullBlocks)-uint64(reorgProtHeaderDelay) { - log.Warn("Pivot became stale, moving", "old", pivot.Number.Uint64(), "new", height-uint64(fsMinFullBlocks)+uint64(reorgProtHeaderDelay)) - pivot = results[len(results)-1-fsMinFullBlocks+reorgProtHeaderDelay].Header // must exist as lower old pivot is uncommitted + if height := latest.Number.Uint64(); height > pivot.Number.Uint64()+2*max(d.epoch, fsMinFullBlocks)-uint64(reorgProtHeaderDelay) { + newPivot := d.calcPivot(height) + log.Warn("Pivot became stale, moving", "old", pivot, "new", newPivot)   + pivot = d.lightchain.GetHeaderByNumber(newPivot) d.pivotLock.Lock() d.pivotHeader = pivot d.pivotLock.Unlock() - // Write out the pivot into the database so a rollback beyond it will // reenable fast sync - rawdb.WriteLastPivotNumber(d.stateDB, pivot.Number.Uint64()) + rawdb.WriteLastPivotNumber(d.stateDB, newPivot) } } P, beforeP, afterP := splitAroundPivot(pivot.Number.Uint64(), results) @@ -1905,7 +2082,7 @@ ) blocks := make([]*types.Block, len(results)) receipts := make([]types.Receipts, len(results)) for i, result := range results { - blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles) + blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Randomness, result.EpochSnarkData) receipts[i] = result.Receipts } if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil { @@ -1916,7 +2093,7 @@ return nil }   func (d *Downloader) commitPivotBlock(result *fetchResult) error { - block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles) + block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Randomness, result.EpochSnarkData) log.Debug("Committing fast sync pivot as new head", "number", block.Number(), "hash", block.Hash())   // Commit the pivot block as the new head, will require full sync from here on @@ -1946,8 +2123,8 @@ return d.deliver(d.headerCh, &headerPack{id, headers}, headerInMeter, headerDropMeter) }   // DeliverBodies injects a new batch of block bodies received from a remote node. -func (d *Downloader) DeliverBodies(id string, transactions [][]*types.Transaction, uncles [][]*types.Header) error { - return d.deliver(d.bodyCh, &bodyPack{id, transactions, uncles}, bodyInMeter, bodyDropMeter) +func (d *Downloader) DeliverBodies(id string, transactions [][]*types.Transaction, randomness []*types.Randomness, epochSnarkData []*types.EpochSnarkData) (err error) { + return d.deliver(d.bodyCh, &bodyPack{id, transactions, randomness, epochSnarkData}, bodyInMeter, bodyDropMeter) }   // DeliverReceipts injects a new batch of receipts received from a remote node.
diff --git go-ethereum/eth/downloader/modes.go celo/eth/downloader/modes.go index 103af4f0b30f02165bdd706aa2b184243d98e695..79e3ca72c3d8d80c4ae24abeb25da745a1a0bf6e 100644 --- go-ethereum/eth/downloader/modes.go +++ celo/eth/downloader/modes.go @@ -24,13 +24,14 @@ type SyncMode uint32   const ( FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks - FastSync // Quickly download the headers, full sync only at the chain + FastSync // Quickly download the headers, full sync only at the chain head SnapSync // Download the chain and the state via compact snapshots LightSync // Download only the headers and terminate afterwards + LightestSync // Synchronise one block per Epoch (Celo-specific mode) )   func (mode SyncMode) IsValid() bool { - return mode >= FullSync && mode <= LightSync + return mode >= FullSync && mode <= LightestSync }   // String implements the stringer interface. @@ -40,10 +41,12 @@ case FullSync: return "full" case FastSync: return "fast" - case SnapSync: - return "snap" + // case SnapSync: + // return "snap" case LightSync: return "light" + case LightestSync: + return "lightest" default: return "unknown" } @@ -55,10 +58,13 @@ case FullSync: return []byte("full"), nil case FastSync: return []byte("fast"), nil - case SnapSync: - return []byte("snap"), nil + // case SnapSync: + // return []byte("snap"), nil + // TODO: Implement snap sync case LightSync: return []byte("light"), nil + case LightestSync: + return []byte("lightest"), nil default: return nil, fmt.Errorf("unknown sync mode %d", mode) } @@ -70,12 +76,50 @@ case "full": *mode = FullSync case "fast": *mode = FastSync - case "snap": - *mode = SnapSync + // case "snap": + // *mode = SnapSync + // TODO: Implement snap sync case "light": *mode = LightSync + case "lightest": + *mode = LightestSync default: - return fmt.Errorf(`unknown sync mode %q, want "full", "fast" or "light"`, text) + return fmt.Errorf(`unknown sync mode %q, want "full", "fast", "light", or "lightest"`, text) } return nil } + +// TODO: Enable snap sync mode here. (https://github.com/ethereum/go-ethereum/issues/1735) + +// Returns true if the all headers and not just some a small, discontinuous, set of headers are fetched. +func (mode SyncMode) SyncFullHeaderChain() bool { + switch mode { + case FullSync: + return true + case FastSync: + return true + case LightSync: + return true + case LightestSync: + return false + default: + panic(fmt.Errorf("unknown sync mode %d", mode)) + } +} + +// Returns true if the full blocks (and not just headers) are fetched. +// If a mode returns true here then it will return true for `SyncFullHeaderChain` as well. +func (mode SyncMode) SyncFullBlockChain() bool { + switch mode { + case FullSync: + return true + case FastSync: + return true + case LightSync: + return false + case LightestSync: + return false + default: + panic(fmt.Errorf("unknown sync mode %d", mode)) + } +}
diff --git go-ethereum/eth/downloader/queue.go celo/eth/downloader/queue.go index ada8d4f271227d321d3d0dc4f522f922afabdaf3..0c34f3149dde5b3c24a11d7f2b87a471051e374f 100644 --- go-ethereum/eth/downloader/queue.go +++ celo/eth/downloader/queue.go @@ -65,9 +65,10 @@ type fetchResult struct { pending int32 // Flag telling what deliveries are outstanding   Header *types.Header - Uncles []*types.Header Transactions types.Transactions Receipts types.Receipts + Randomness *types.Randomness + EpochSnarkData *types.EpochSnarkData }   func newFetchResult(header *types.Header, fastSync bool) *fetchResult { @@ -366,15 +367,13 @@ results := q.resultCache.GetCompleted(maxResultsProcess) for _, result := range results { // Recalculate the result item weights to prevent memory exhaustion size := result.Header.Size() - for _, uncle := range result.Uncles { - size += uncle.Size() - } for _, receipt := range result.Receipts { size += receipt.Size() } for _, tx := range result.Transactions { size += tx.Size() } + size += result.Randomness.Size() q.resultSize = common.StorageSize(blockCacheSizeWeight)*size + (1-common.StorageSize(blockCacheSizeWeight))*q.resultSize } @@ -780,7 +779,7 @@ // DeliverBodies injects a block body retrieval response into the results queue. // The method returns the number of blocks bodies accepted from the delivery and // also wakes any threads waiting for data delivery. -func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLists [][]*types.Header) (int, error) { +func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, randomnessList []*types.Randomness, epochSnarkDataList []*types.EpochSnarkData) (int, error) { q.lock.Lock() defer q.lock.Unlock() trieHasher := trie.NewStackTrie(nil) @@ -788,15 +787,13 @@ validate := func(index int, header *types.Header) error { if types.DeriveSha(types.Transactions(txLists[index]), trieHasher) != header.TxHash { return errInvalidBody } - if types.CalcUncleHash(uncleLists[index]) != header.UncleHash { - return errInvalidBody - } return nil }   reconstruct := func(index int, result *fetchResult) { result.Transactions = txLists[index] - result.Uncles = uncleLists[index] + result.Randomness = randomnessList[index] + result.EpochSnarkData = epochSnarkDataList[index] result.SetBodyDone() } return q.deliver(id, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool,
diff --git go-ethereum/eth/downloader/types.go celo/eth/downloader/types.go index ab653d7416b68ae5967cbdbb7460321341e8639d..ae97debcc82d79504699d39aad9629b44b2eb1ad 100644 --- go-ethereum/eth/downloader/types.go +++ celo/eth/downloader/types.go @@ -46,17 +46,15 @@ // bodyPack is a batch of block bodies returned by a peer. type bodyPack struct { peerID string transactions [][]*types.Transaction - uncles [][]*types.Header + randomness []*types.Randomness + epochSnarkData []*types.EpochSnarkData }   func (p *bodyPack) PeerId() string { return p.peerID } func (p *bodyPack) Items() int { - if len(p.transactions) <= len(p.uncles) { - return len(p.transactions) - } - return len(p.uncles) + return len(p.transactions) } -func (p *bodyPack) Stats() string { return fmt.Sprintf("%d:%d", len(p.transactions), len(p.uncles)) } +func (p *bodyPack) Stats() string { return fmt.Sprintf("%d", len(p.transactions)) }   // receiptPack is a batch of receipts returned by a peer. type receiptPack struct {
diff --git go-ethereum/eth/downloader/testchain_test.go celo/eth/downloader/testchain_test.go index 9420b8f57a349ce2a657e65a54c056c016e9f907..22dbb5a3e9205e0ae56ec96ebb2b6e67968c9880 100644 --- go-ethereum/eth/downloader/testchain_test.go +++ celo/eth/downloader/testchain_test.go @@ -22,7 +22,7 @@ "math/big" "sync"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -69,7 +69,7 @@ tc := new(testChain).copy(length) tc.genesis = genesis tc.chain = append(tc.chain, genesis.Hash()) tc.headerm[tc.genesis.Hash()] = tc.genesis.Header() - tc.tdm[tc.genesis.Hash()] = tc.genesis.Difficulty() + tc.tdm[tc.genesis.Hash()] = tc.genesis.TotalDifficulty() tc.blockm[tc.genesis.Hash()] = tc.genesis tc.generate(length-1, 0, genesis, false) return tc @@ -112,13 +112,12 @@ }   // generate creates a chain of n blocks starting at and including parent. // the returned hash chain is ordered head->parent. In addition, every 22th block -// contains a transaction and every 5th an uncle to allow testing correct block -// reassembly. +// contains a transaction to allow testing correct block reassembly. func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool) { // start := time.Now() // defer func() { fmt.Printf("test chain generated in %v\n", time.Since(start)) }()   - blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testDB, n, func(i int, block *core.BlockGen) { + blocks, receipts := core.GenerateChain(params.IstanbulTestChainConfig, parent, mockEngine.NewFaker(), testDB, n, func(i int, block *core.BlockGen) { block.SetCoinbase(common.Address{seed}) // If a heavy chain is requested, delay blocks to raise difficulty if heavy { @@ -126,26 +125,18 @@ block.OffsetTime(-1) } // Include transactions to the miner to make blocks more interesting. if parent == tc.genesis && i%22 == 0 { - signer := types.MakeSigner(params.TestChainConfig, block.Number()) - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey) + signer := types.MakeSigner(params.IstanbulTestChainConfig, block.Number()) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, testKey) if err != nil { panic(err) } block.AddTx(tx) } - // if the block number is a multiple of 5, add a bonus uncle to the block - if i > 0 && i%5 == 0 { - block.AddUncle(&types.Header{ - ParentHash: block.PrevBlock(i - 1).Hash(), - Number: big.NewInt(block.Number().Int64() - 1), - }) - } })   // Convert the block-chain into a hash-chain and header/block maps - td := new(big.Int).Set(tc.td(parent.Hash())) for i, b := range blocks { - td := td.Add(td, b.Difficulty()) + td := b.TotalDifficulty() hash := b.Hash() tc.chain = append(tc.chain, hash) tc.blockm[hash] = b @@ -208,16 +199,18 @@ return results }   // bodies returns the block bodies of the given block hashes. -func (tc *testChain) bodies(hashes []common.Hash) ([][]*types.Transaction, [][]*types.Header) { +func (tc *testChain) bodies(hashes []common.Hash) ([][]*types.Transaction, []*types.Randomness, []*types.EpochSnarkData) { transactions := make([][]*types.Transaction, 0, len(hashes)) - uncles := make([][]*types.Header, 0, len(hashes)) + randomness := make([]*types.Randomness, 0, len(hashes)) + epochSnarkData := make([]*types.EpochSnarkData, 0, len(hashes)) for _, hash := range hashes { if block, ok := tc.blockm[hash]; ok { transactions = append(transactions, block.Transactions()) - uncles = append(uncles, block.Uncles()) + randomness = append(randomness, block.Randomness()) + epochSnarkData = append(epochSnarkData, block.EpochSnarkData()) } } - return transactions, uncles + return transactions, randomness, epochSnarkData }   func (tc *testChain) hashToNumber(target common.Hash) (uint64, bool) {
diff --git go-ethereum/eth/ethconfig/gen_config.go celo/eth/ethconfig/gen_config.go index 9fc305a9ccc5aa5d7f74ba1758a004a659ef461c..906b6668f2f44247780743b17f5c5e619791c3b0 100644 --- go-ethereum/eth/ethconfig/gen_config.go +++ celo/eth/ethconfig/gen_config.go @@ -7,10 +7,9 @@ "math/big" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/params" ) @@ -34,6 +33,10 @@ LightPeers int `toml:",omitempty"` LightNoPrune bool `toml:",omitempty"` LightNoSyncServe bool `toml:",omitempty"` SyncFromCheckpoint bool `toml:",omitempty"` + GatewayFee *big.Int `toml:",omitempty"` + Validator common.Address `toml:",omitempty"` + TxFeeRecipient common.Address `toml:",omitempty"` + BLSbase common.Address `toml:",omitempty"` UltraLightServers []string `toml:",omitempty"` UltraLightFraction int `toml:",omitempty"` UltraLightOnlyAnnounce bool `toml:",omitempty"` @@ -49,16 +52,18 @@ TrieTimeout time.Duration SnapshotCache int Preimages bool Miner miner.Config - Ethash ethash.Config TxPool core.TxPoolConfig - GPO gasprice.Config EnablePreimageRecording bool + Istanbul istanbul.Config DocRoot string `toml:"-"` + RPCGasInflationRate float64 RPCGasCap uint64 RPCTxFeeCap float64 + RPCEthCompatibility bool Checkpoint *params.TrustedCheckpoint `toml:",omitempty"` CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` - OverrideLondon *big.Int `toml:",omitempty"` + OverrideEHardfork *big.Int `toml:",omitempty"` + MinSyncPeers int `toml:",omitempty"` } var enc Config enc.Genesis = c.Genesis @@ -77,6 +82,10 @@ enc.LightPeers = c.LightPeers enc.LightNoPrune = c.LightNoPrune enc.LightNoSyncServe = c.LightNoSyncServe enc.SyncFromCheckpoint = c.SyncFromCheckpoint + enc.GatewayFee = c.GatewayFee + enc.Validator = c.Validator + enc.TxFeeRecipient = c.TxFeeRecipient + enc.BLSbase = c.BLSbase enc.UltraLightServers = c.UltraLightServers enc.UltraLightFraction = c.UltraLightFraction enc.UltraLightOnlyAnnounce = c.UltraLightOnlyAnnounce @@ -92,16 +101,18 @@ enc.TrieTimeout = c.TrieTimeout enc.SnapshotCache = c.SnapshotCache enc.Preimages = c.Preimages enc.Miner = c.Miner - enc.Ethash = c.Ethash enc.TxPool = c.TxPool - enc.GPO = c.GPO enc.EnablePreimageRecording = c.EnablePreimageRecording + enc.Istanbul = c.Istanbul enc.DocRoot = c.DocRoot + enc.RPCGasInflationRate = c.RPCGasInflationRate enc.RPCGasCap = c.RPCGasCap enc.RPCTxFeeCap = c.RPCTxFeeCap + enc.RPCEthCompatibility = c.RPCEthCompatibility enc.Checkpoint = c.Checkpoint enc.CheckpointOracle = c.CheckpointOracle - enc.OverrideLondon = c.OverrideLondon + enc.OverrideEHardfork = c.OverrideEHardfork + enc.MinSyncPeers = c.MinSyncPeers return &enc, nil }   @@ -124,6 +135,10 @@ LightPeers *int `toml:",omitempty"` LightNoPrune *bool `toml:",omitempty"` LightNoSyncServe *bool `toml:",omitempty"` SyncFromCheckpoint *bool `toml:",omitempty"` + GatewayFee *big.Int `toml:",omitempty"` + Validator *common.Address `toml:",omitempty"` + TxFeeRecipient *common.Address `toml:",omitempty"` + BLSbase *common.Address `toml:",omitempty"` UltraLightServers []string `toml:",omitempty"` UltraLightFraction *int `toml:",omitempty"` UltraLightOnlyAnnounce *bool `toml:",omitempty"` @@ -139,16 +154,18 @@ TrieTimeout *time.Duration SnapshotCache *int Preimages *bool Miner *miner.Config - Ethash *ethash.Config TxPool *core.TxPoolConfig - GPO *gasprice.Config EnablePreimageRecording *bool + Istanbul *istanbul.Config DocRoot *string `toml:"-"` + RPCGasInflationRate *float64 RPCGasCap *uint64 RPCTxFeeCap *float64 + RPCEthCompatibility *bool Checkpoint *params.TrustedCheckpoint `toml:",omitempty"` CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` - OverrideLondon *big.Int `toml:",omitempty"` + OverrideEHardfork *big.Int `toml:",omitempty"` + MinSyncPeers *int `toml:",omitempty"` } var dec Config if err := unmarshal(&dec); err != nil { @@ -202,6 +219,18 @@ } if dec.SyncFromCheckpoint != nil { c.SyncFromCheckpoint = *dec.SyncFromCheckpoint } + if dec.GatewayFee != nil { + c.GatewayFee = dec.GatewayFee + } + if dec.Validator != nil { + c.Validator = *dec.Validator + } + if dec.TxFeeRecipient != nil { + c.TxFeeRecipient = *dec.TxFeeRecipient + } + if dec.BLSbase != nil { + c.BLSbase = *dec.BLSbase + } if dec.UltraLightServers != nil { c.UltraLightServers = dec.UltraLightServers } @@ -247,35 +276,41 @@ } if dec.Miner != nil { c.Miner = *dec.Miner } - if dec.Ethash != nil { - c.Ethash = *dec.Ethash - } if dec.TxPool != nil { c.TxPool = *dec.TxPool } - if dec.GPO != nil { - c.GPO = *dec.GPO - } if dec.EnablePreimageRecording != nil { c.EnablePreimageRecording = *dec.EnablePreimageRecording + } + if dec.Istanbul != nil { + c.Istanbul = *dec.Istanbul } if dec.DocRoot != nil { c.DocRoot = *dec.DocRoot } + if dec.RPCGasInflationRate != nil { + c.RPCGasInflationRate = *dec.RPCGasInflationRate + } if dec.RPCGasCap != nil { c.RPCGasCap = *dec.RPCGasCap } if dec.RPCTxFeeCap != nil { c.RPCTxFeeCap = *dec.RPCTxFeeCap } + if dec.RPCEthCompatibility != nil { + c.RPCEthCompatibility = *dec.RPCEthCompatibility + } if dec.Checkpoint != nil { c.Checkpoint = dec.Checkpoint } if dec.CheckpointOracle != nil { c.CheckpointOracle = dec.CheckpointOracle } - if dec.OverrideLondon != nil { - c.OverrideLondon = dec.OverrideLondon + if dec.OverrideEHardfork != nil { + c.OverrideEHardfork = dec.OverrideEHardfork + } + if dec.MinSyncPeers != nil { + c.MinSyncPeers = *dec.MinSyncPeers } return nil }
diff --git go-ethereum/eth/ethconfig/config.go celo/eth/ethconfig/config.go index 01f7b0664384dbd537aa29bf1390a905ad137346..3909d1ab5775165626152c220e770d914526dbfd 100644 --- go-ethereum/eth/ethconfig/config.go +++ celo/eth/ethconfig/config.go @@ -18,20 +18,17 @@ // Package ethconfig contains the configuration of the ETH and LES protocols. package ethconfig   import ( + "fmt" "math/big" - "os" - "os/user" - "path/filepath" - "runtime" "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/clique" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/consensus/istanbul" + istanbulBackend "github.com/ethereum/go-ethereum/consensus/istanbul/backend" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" @@ -39,41 +36,13 @@ "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" )   -// FullNodeGPO contains default gasprice oracle settings for full node. -var FullNodeGPO = gasprice.Config{ - Blocks: 20, - Percentile: 60, - MaxHeaderHistory: 1024, - MaxBlockHistory: 1024, - MaxPrice: gasprice.DefaultMaxPrice, - IgnorePrice: gasprice.DefaultIgnorePrice, -} - -// LightClientGPO contains default gasprice oracle settings for light client. -var LightClientGPO = gasprice.Config{ - Blocks: 2, - Percentile: 60, - MaxHeaderHistory: 300, - MaxBlockHistory: 5, - MaxPrice: gasprice.DefaultMaxPrice, - IgnorePrice: gasprice.DefaultIgnorePrice, -} - // Defaults contains default settings for use on the Ethereum main net. var Defaults = Config{ - SyncMode: downloader.SnapSync, - Ethash: ethash.Config{ - CacheDir: "ethash", - CachesInMem: 2, - CachesOnDisk: 3, - CachesLockMmap: false, - DatasetsInMem: 1, - DatasetsOnDisk: 2, - DatasetsLockMmap: false, - }, + SyncMode: downloader.FastSync, NetworkId: 1, TxLookupLimit: 2350000, LightPeers: 100, + LightServ: 0, UltraLightFraction: 75, DatabaseCache: 512, TrieCleanCache: 154, @@ -82,36 +51,14 @@ TrieCleanCacheRejournal: 60 * time.Minute, TrieDirtyCache: 256, TrieTimeout: 60 * time.Minute, SnapshotCache: 102, - Miner: miner.Config{ - GasCeil: 8000000, - GasPrice: big.NewInt(params.GWei), - Recommit: 3 * time.Second, - }, + GatewayFee: big.NewInt(0), + TxPool: core.DefaultTxPoolConfig, - RPCGasCap: 50000000, - GPO: FullNodeGPO, - RPCTxFeeCap: 1, // 1 ether -} + RPCGasInflationRate: 1.3, + RPCGasCap: 25000000, + RPCTxFeeCap: 500, // 500 celo   -func init() { - home := os.Getenv("HOME") - if home == "" { - if user, err := user.Current(); err == nil { - home = user.HomeDir - } - } - if runtime.GOOS == "darwin" { - Defaults.Ethash.DatasetDir = filepath.Join(home, "Library", "Ethash") - } else if runtime.GOOS == "windows" { - localappdata := os.Getenv("LOCALAPPDATA") - if localappdata != "" { - Defaults.Ethash.DatasetDir = filepath.Join(localappdata, "Ethash") - } else { - Defaults.Ethash.DatasetDir = filepath.Join(home, "AppData", "Local", "Ethash") - } - } else { - Defaults.Ethash.DatasetDir = filepath.Join(home, ".ethash") - } + Istanbul: *istanbul.DefaultConfig, }   //go:generate gencodec -type Config -formats toml -out gen_config.go @@ -147,6 +94,13 @@ LightPeers int `toml:",omitempty"` // Maximum number of LES client peers LightNoPrune bool `toml:",omitempty"` // Whether to disable light chain pruning LightNoSyncServe bool `toml:",omitempty"` // Whether to serve light clients before syncing SyncFromCheckpoint bool `toml:",omitempty"` // Whether to sync the header chain from the configured checkpoint + // Minimum gateway fee value to serve a transaction from a light client + GatewayFee *big.Int `toml:",omitempty"` + // Validator is the address used to sign consensus messages. Also the address for block transaction rewards. + Validator common.Address `toml:",omitempty"` + // TxFeeRecipient is the GatewayFeeRecipient light clients need to specify in order for their transactions to be accepted by this node. + TxFeeRecipient common.Address `toml:",omitempty"` + BLSbase common.Address `toml:",omitempty"`   // Ultra Light client options UltraLightServers []string `toml:",omitempty"` // List of trusted ultra light servers @@ -170,21 +124,21 @@ // Mining options Miner miner.Config   - // Ethash options - Ethash ethash.Config - // Transaction pool options TxPool core.TxPoolConfig - - // Gas Price Oracle options - GPO gasprice.Config   // Enables tracking of SHA3 preimages in the VM EnablePreimageRecording bool   + // Istanbul options + Istanbul istanbul.Config + // Miscellaneous options DocRoot string `toml:"-"`   + // RPCGasInflationRate is a global multiplier applied to the gas estimations + RPCGasInflationRate float64 + // RPCGasCap is the global gas cap for eth-call variants. RPCGasCap uint64   @@ -192,43 +146,38 @@ // RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for // send-transction variants. The unit is ether. RPCTxFeeCap float64   + // RPCEthCompatibility is used to determine whether the 'gaslimit' end + // 'baseFeePerGas' fields should be added to blocks returned by the RPC + // API. Where true indicates the fields should be added. + RPCEthCompatibility bool + // Checkpoint is a hardcoded checkpoint which can be nil. Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`   // CheckpointOracle is the configuration for checkpoint oracle. CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`   - // Berlin block override (TODO: remove after the fork) - OverrideLondon *big.Int `toml:",omitempty"` + // E block override (TODO: remove after the fork) + OverrideEHardfork *big.Int `toml:",omitempty"` + + // The minimum required peers in order for syncing to be initiated, if left + // at 0 then the default will be used. + MinSyncPeers int `toml:",omitempty"` }   -// CreateConsensusEngine creates a consensus engine for the given chain configuration. -func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine { - // If proof-of-authority is requested, set it up - if chainConfig.Clique != nil { - return clique.New(chainConfig.Clique, db) +// CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service +func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *Config, db ethdb.Database) consensus.Engine { + if chainConfig.Faker { + return mockEngine.NewFaker() } - // Otherwise assume proof-of-work - switch config.PowMode { - case ethash.ModeFake: - log.Warn("Ethash used in fake mode") - case ethash.ModeTest: - log.Warn("Ethash used in test mode") - case ethash.ModeShared: - log.Warn("Ethash used in shared mode") + // If Istanbul is requested, set it up + if chainConfig.Istanbul != nil { + log.Debug("Setting up Istanbul consensus engine") + if err := istanbul.ApplyParamsChainConfigToConfig(chainConfig, &config.Istanbul); err != nil { + log.Crit("Invalid Configuration for Istanbul Engine", "err", err) + } + return istanbulBackend.New(&config.Istanbul, db) } - engine := ethash.New(ethash.Config{ - PowMode: config.PowMode, - CacheDir: stack.ResolvePath(config.CacheDir), - CachesInMem: config.CachesInMem, - CachesOnDisk: config.CachesOnDisk, - CachesLockMmap: config.CachesLockMmap, - DatasetDir: config.DatasetDir, - DatasetsInMem: config.DatasetsInMem, - DatasetsOnDisk: config.DatasetsOnDisk, - DatasetsLockMmap: config.DatasetsLockMmap, - NotifyFull: config.NotifyFull, - }, notify, noverify) - engine.SetThreads(-1) // Disable CPU mining - return engine + log.Error(fmt.Sprintf("Only Istanbul Consensus is supported: %v", chainConfig)) + return nil }
diff --git go-ethereum/eth/fetcher/tx_fetcher_test.go celo/eth/fetcher/tx_fetcher_test.go index d57ece3e7e165f85d3c997a4815c41b8d7de03b4..d28f4c9c03c2785925ed477ff77e26d944b97f91 100644 --- go-ethereum/eth/fetcher/tx_fetcher_test.go +++ celo/eth/fetcher/tx_fetcher_test.go @@ -32,10 +32,10 @@ var ( // testTxs is a set of transactions to use during testing that have meaningful hashes. testTxs = []*types.Transaction{ - types.NewTransaction(5577006791947779410, common.Address{0x0f}, new(big.Int), 0, new(big.Int), nil), - types.NewTransaction(15352856648520921629, common.Address{0xbb}, new(big.Int), 0, new(big.Int), nil), - types.NewTransaction(3916589616287113937, common.Address{0x86}, new(big.Int), 0, new(big.Int), nil), - types.NewTransaction(9828766684487745566, common.Address{0xac}, new(big.Int), 0, new(big.Int), nil), + types.NewTransaction(5577006791947779410, common.Address{0x0f}, new(big.Int), 0, new(big.Int), nil, nil, nil, nil), + types.NewTransaction(15352856648520921629, common.Address{0xbb}, new(big.Int), 0, new(big.Int), nil, nil, nil, nil), + types.NewTransaction(3916589616287113937, common.Address{0x86}, new(big.Int), 0, new(big.Int), nil, nil, nil, nil), + types.NewTransaction(9828766684487745566, common.Address{0xac}, new(big.Int), 0, new(big.Int), nil, nil, nil, nil), } // testTxsHashes is the hashes of the test transactions above testTxsHashes = []common.Hash{testTxs[0].Hash(), testTxs[1].Hash(), testTxs[2].Hash(), testTxs[3].Hash()} @@ -482,13 +482,13 @@ }, }, // Deliver the middle transaction requested, the one before which // should be dropped and the one after re-requested. - doTxEnqueue{peer: "A", txs: []*types.Transaction{testTxs[0]}, direct: true}, // This depends on the deterministic random + doTxEnqueue{peer: "A", txs: []*types.Transaction{testTxs[2]}, direct: true}, // This depends on the deterministic random isScheduled{ tracking: map[string][]common.Hash{ - "A": {testTxsHashes[2]}, + "A": {testTxsHashes[0]}, }, fetching: map[string][]common.Hash{ - "A": {testTxsHashes[2]}, + "A": {testTxsHashes[0]}, }, }, }, @@ -528,7 +528,7 @@ }, }, // Deliver the middle transaction requested, the one before which // should be dropped and the one after re-requested. - doTxEnqueue{peer: "A", txs: []*types.Transaction{testTxs[1]}, direct: true}, // This depends on the deterministic random + doTxEnqueue{peer: "A", txs: []*types.Transaction{testTxs[0]}, direct: true}, // This depends on the deterministic random isScheduled{nil, nil, nil}, }, }) @@ -908,7 +908,7 @@ // Create a slew of transactions to max out the underpriced set var txs []*types.Transaction for i := 0; i < maxTxUnderpricedSetSize+1; i++ { - txs = append(txs, types.NewTransaction(rand.Uint64(), common.Address{byte(rand.Intn(256))}, new(big.Int), 0, new(big.Int), nil)) + txs = append(txs, types.NewTransaction(rand.Uint64(), common.Address{byte(rand.Intn(256))}, new(big.Int), 0, new(big.Int), nil, nil, nil, nil)) } hashes := make([]common.Hash, len(txs)) for i, tx := range txs {
diff --git go-ethereum/eth/fetcher/block_fetcher.go celo/eth/fetcher/block_fetcher.go index 9fb42dfa1c8ada43153a84e678124ed0a0ab2924..87e8f1bea0931ff80a5d6b2113768fb86ed3e86e 100644 --- go-ethereum/eth/fetcher/block_fetcher.go +++ celo/eth/fetcher/block_fetcher.go @@ -28,7 +28,6 @@ "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" - "github.com/ethereum/go-ethereum/trie" )   const ( @@ -118,12 +117,14 @@ headers []*types.Header // Collection of headers to filter time time.Time // Arrival time of the headers }   -// bodyFilterTask represents a batch of block bodies (transactions and uncles) +// bodyFilterTask represents a batch of block bodies // needing fetcher filtering. type bodyFilterTask struct { peer string // The source peer of block bodies + blockHashes []common.Hash transactions [][]*types.Transaction // Collection of transactions per block bodies - uncles [][]*types.Header // Collection of uncles per block bodies + randomness []*types.Randomness + epochSnarkData []*types.EpochSnarkData time time.Time // Arrival time of the blocks' contents }   @@ -301,8 +302,8 @@ }   // FilterBodies extracts all the block bodies that were explicitly requested by // the fetcher, returning those that should be handled differently. -func (f *BlockFetcher) FilterBodies(peer string, transactions [][]*types.Transaction, uncles [][]*types.Header, time time.Time) ([][]*types.Transaction, [][]*types.Header) { - log.Trace("Filtering bodies", "peer", peer, "txs", len(transactions), "uncles", len(uncles)) +func (f *BlockFetcher) FilterBodies(peer string, blockHashes []common.Hash, transactions [][]*types.Transaction, randomness []*types.Randomness, epochSnarkData []*types.EpochSnarkData, time time.Time) ([]common.Hash, [][]*types.Transaction, []*types.Randomness, []*types.EpochSnarkData) { + log.Trace("Filtering bodies", "peer", peer, "txs", len(transactions))   // Send the filter channel to the fetcher filter := make(chan *bodyFilterTask) @@ -310,20 +311,20 @@ select { case f.bodyFilter <- filter: case <-f.quit: - return nil, nil + return nil, nil, nil, nil } // Request the filtering of the body list select { - case filter <- &bodyFilterTask{peer: peer, transactions: transactions, uncles: uncles, time: time}: + case filter <- &bodyFilterTask{peer: peer, blockHashes: blockHashes, transactions: transactions, randomness: randomness, epochSnarkData: epochSnarkData, time: time}: case <-f.quit: - return nil, nil + return nil, nil, nil, nil } // Retrieve the bodies remaining after filtering select { case task := <-filter: - return task.transactions, task.uncles + return task.blockHashes, task.transactions, task.randomness, task.epochSnarkData case <-f.quit: - return nil, nil + return nil, nil, nil, nil } }   @@ -524,7 +525,7 @@ // Filter fetcher-requested headers from other synchronisation algorithms if announce := f.fetching[hash]; announce != nil && announce.origin == task.peer && f.fetched[hash] == nil && f.completing[hash] == nil && f.queued[hash] == nil { // If the delivered header does not match the promised number, drop the announcer if header.Number.Uint64() != announce.number { - log.Trace("Invalid block number fetched", "peer", announce.origin, "hash", header.Hash(), "announced", announce.number, "provided", header.Number) + log.Warn("Invalid block number fetched", "peer", announce.origin, "hash", header.Hash(), "announced", announce.number, "provided", header.Number) f.dropPeer(announce.origin) f.forgetHash(hash) continue @@ -544,17 +545,6 @@ if f.getBlock(hash) == nil { announce.header = header announce.time = task.time   - // If the block is empty (header only), short circuit into the final import queue - if header.TxHash == types.EmptyRootHash && header.UncleHash == types.EmptyUncleHash { - log.Trace("Block empty, skipping body retrieval", "peer", announce.origin, "number", header.Number, "hash", header.Hash()) - - block := types.NewBlockWithHeader(header) - block.ReceivedAt = task.time - - complete = append(complete, block) - f.completing[hash] = announce - continue - } // Otherwise add to the list of blocks needing completion incomplete = append(incomplete, announce) } else { @@ -606,33 +596,20 @@ bodyFilterInMeter.Mark(int64(len(task.transactions))) blocks := []*types.Block{} // abort early if there's nothing explicitly requested if len(f.completing) > 0 { - for i := 0; i < len(task.transactions) && i < len(task.uncles); i++ { + for i := 0; i < len(task.blockHashes) && i < len(task.transactions) && i < len(task.randomness) && i < len(task.epochSnarkData); i++ { // Match up a body to any possible completion request - var ( - matched = false - uncleHash common.Hash // calculated lazily and reused - txnHash common.Hash // calculated lazily and reused - ) + var matched = false for hash, announce := range f.completing { if f.queued[hash] != nil || announce.origin != task.peer { continue } - if uncleHash == (common.Hash{}) { - uncleHash = types.CalcUncleHash(task.uncles[i]) - } - if uncleHash != announce.header.UncleHash { - continue - } - if txnHash == (common.Hash{}) { - txnHash = types.DeriveSha(types.Transactions(task.transactions[i]), trie.NewStackTrie(nil)) - } - if txnHash != announce.header.TxHash { + if task.blockHashes[i] != announce.header.Hash() { continue } // Mark the body matched, reassemble if still unknown matched = true if f.getBlock(hash) == nil { - block := types.NewBlockWithHeader(announce.header).WithBody(task.transactions[i], task.uncles[i]) + block := types.NewBlockWithHeader(announce.header).WithBody(task.transactions[i], task.randomness[i], task.epochSnarkData[i]) block.ReceivedAt = task.time blocks = append(blocks, block) } else { @@ -641,8 +618,10 @@ }   } if matched { + task.blockHashes = append(task.blockHashes[:i], task.blockHashes[i+1:]...) task.transactions = append(task.transactions[:i], task.transactions[i+1:]...) - task.uncles = append(task.uncles[:i], task.uncles[i+1:]...) + task.randomness = append(task.randomness[:i], task.randomness[i+1:]...) + task.epochSnarkData = append(task.epochSnarkData[:i], task.epochSnarkData[i+1:]...) i-- continue } @@ -717,7 +696,7 @@ } // Ensure the peer isn't DOSing us count := f.queues[peer] + 1 if count > blockLimit { - log.Debug("Discarded delivered header or block, exceeded allowance", "peer", peer, "number", number, "hash", hash, "limit", blockLimit) + log.Info("Discarded delivered header or block, exceeded allowance", "peer", peer, "number", number, "hash", hash, "limit", blockLimit) blockBroadcastDOSMeter.Mark(1) f.forgetHash(hash) return @@ -809,7 +788,7 @@ // Weird future block, don't fail, but neither propagate   default: // Something went very wrong, drop the peer - log.Debug("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err) + log.Warn("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err) f.dropPeer(peer) return }
diff --git go-ethereum/eth/fetcher/block_fetcher_test.go celo/eth/fetcher/block_fetcher_test.go index 83505854c1e52537df58e03b1de614eac767332e..c5c4b3773e99d24dc41b4365aff65b98ff047f64 100644 --- go-ethereum/eth/fetcher/block_fetcher_test.go +++ celo/eth/fetcher/block_fetcher_test.go @@ -25,7 +25,7 @@ "testing" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -39,29 +39,25 @@ testdb = rawdb.NewMemoryDatabase() testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testAddress = crypto.PubkeyToAddress(testKey.PublicKey) genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000000000)) - unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit, BaseFee: big.NewInt(params.InitialBaseFee)}, nil, nil, nil, trie.NewStackTrie(nil)) + unknownBlock = types.NewBlock(&types.Header{}, nil, nil, nil, trie.NewStackTrie(nil)) )   // makeChain creates a chain of n blocks starting at and including parent. // the returned hash chain is ordered head->parent. In addition, every 3rd block -// contains a transaction and every 5th an uncle to allow testing correct block +// contains a transaction to test correct block // reassembly. func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) { - blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, func(i int, block *core.BlockGen) { + blocks, _ := core.GenerateChain(params.IstanbulTestChainConfig, parent, mockEngine.NewFaker(), testdb, n, func(i int, block *core.BlockGen) { block.SetCoinbase(common.Address{seed})   // If the block number is multiple of 3, send a bonus transaction to the miner if parent == genesis && i%3 == 0 { - signer := types.MakeSigner(params.TestChainConfig, block.Number()) - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey) + signer := types.MakeSigner(params.IstanbulTestChainConfig, block.Number()) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, testKey) if err != nil { panic(err) } block.AddTx(tx) - } - // If the block number is a multiple of 5, add a bonus uncle to the block - if i%5 == 0 { - block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 1).Hash(), Number: big.NewInt(int64(i - 1))}) } }) hashes := make([]common.Hash, n+1) @@ -217,53 +213,23 @@ } // Create a function that returns blocks from the closure return func(hashes []common.Hash) error { // Gather the block bodies to return + blockHashes := make([]common.Hash, 0, len(hashes)) transactions := make([][]*types.Transaction, 0, len(hashes)) - uncles := make([][]*types.Header, 0, len(hashes)) + randomness := make([]*types.Randomness, 0, len(hashes)) + epochSnarkData := make([]*types.EpochSnarkData, 0, len(hashes))   for _, hash := range hashes { if block, ok := closure[hash]; ok { + blockHashes = append(blockHashes, hash) transactions = append(transactions, block.Transactions()) - uncles = append(uncles, block.Uncles()) + randomness = append(randomness, block.Randomness()) + epochSnarkData = append(epochSnarkData, block.EpochSnarkData()) } } // Return on a new thread - go f.fetcher.FilterBodies(peer, transactions, uncles, time.Now().Add(drift)) + go f.fetcher.FilterBodies(peer, blockHashes, transactions, randomness, epochSnarkData, time.Now().Add(drift))   return nil - } -} - -// verifyFetchingEvent verifies that one single event arrive on a fetching channel. -func verifyFetchingEvent(t *testing.T, fetching chan []common.Hash, arrive bool) { - if arrive { - select { - case <-fetching: - case <-time.After(time.Second): - t.Fatalf("fetching timeout") - } - } else { - select { - case <-fetching: - t.Fatalf("fetching invoked") - case <-time.After(10 * time.Millisecond): - } - } -} - -// verifyCompletingEvent verifies that one single event arrive on an completing channel. -func verifyCompletingEvent(t *testing.T, completing chan []common.Hash, arrive bool) { - if arrive { - select { - case <-completing: - case <-time.After(time.Second): - t.Fatalf("completing timeout") - } - } else { - select { - case <-completing: - t.Fatalf("completing invoked") - case <-time.After(10 * time.Millisecond): - } } }   @@ -750,46 +716,6 @@ tester.lock.RUnlock()   if dropped { t.Fatalf("peer with valid numbered announcement dropped") - } - verifyImportDone(t, imported) -} - -// Tests that if a block is empty (i.e. header only), no body request should be -// made, and instead the header should be assembled into a whole block in itself. -func TestEmptyBlockShortCircuit(t *testing.T) { - // Create a chain of blocks to import - hashes, blocks := makeChain(32, 0, genesis) - - tester := newTester(false) - headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) - bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) - - // Add a monitoring hook for all internal events - fetching := make(chan []common.Hash) - tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes } - - completing := make(chan []common.Hash) - tester.fetcher.completingHook = func(hashes []common.Hash) { completing <- hashes } - - imported := make(chan interface{}) - tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { - if block == nil { - t.Fatalf("Fetcher try to import empty block") - } - imported <- block - } - // Iteratively announce blocks until all are imported - for i := len(hashes) - 2; i >= 0; i-- { - tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) - - // All announces should fetch the header - verifyFetchingEvent(t, fetching, true) - - // Only blocks with data contents should request bodies - verifyCompletingEvent(t, completing, len(blocks[hashes[i]].Transactions()) > 0 || len(blocks[hashes[i]].Uncles()) > 0) - - // Irrelevant of the construct, import should succeed - verifyImportEvent(t, imported, true) } verifyImportDone(t, imported) }
diff --git go-ethereum/eth/filters/filter_system_test.go celo/eth/filters/filter_system_test.go index ccc12436f694aabc7fd5668bac6ca8515dfb3178..0d760eb64c3e8575e43dfbbf4ae79670fb78c8b2 100644 --- go-ethereum/eth/filters/filter_system_test.go +++ celo/eth/filters/filter_system_test.go @@ -28,7 +28,7 @@ "time"   "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" @@ -44,7 +44,6 @@ deadline = 5 * time.Minute )   type testBackend struct { - mux *event.TypeMux db ethdb.Database sections uint64 txFeed event.Feed @@ -87,7 +86,7 @@ }   func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { if number := rawdb.ReadHeaderNumber(b.db, hash); number != nil { - return rawdb.ReadReceipts(b.db, hash, *number, params.TestChainConfig), nil + return rawdb.ReadReceipts(b.db, hash, *number, params.IstanbulTestChainConfig), nil } return nil, nil } @@ -97,7 +96,7 @@ number := rawdb.ReadHeaderNumber(b.db, hash) if number == nil { return nil, nil } - receipts := rawdb.ReadReceipts(b.db, hash, *number, params.TestChainConfig) + receipts := rawdb.ReadReceipts(b.db, hash, *number, params.IstanbulTestChainConfig)   logs := make([][]*types.Log, len(receipts)) for i, receipt := range receipts { @@ -157,6 +156,10 @@ } }() }   +func (b *testBackend) RealGasPriceMinimumForHeader(ctx context.Context, currencyAddress *common.Address, header *types.Header) (*big.Int, error) { + return nil, fmt.Errorf("testBackend does not implement RealGasPriceMinimumForHeader") +} + // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events. // It creates multiple subscriptions: // - one at the start and should receive all posted chain events and a second (blockHashes) @@ -169,8 +172,8 @@ var ( db = rawdb.NewMemoryDatabase() backend = &testBackend{db: db} api = NewPublicFilterAPI(backend, false, deadline) - genesis = (&core.Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) - chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {}) + genesis = (&core.Genesis{}).MustCommit(db) + chain, _ = core.GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {}) chainEvents = []core.ChainEvent{} )   @@ -223,11 +226,11 @@ backend = &testBackend{db: db} api = NewPublicFilterAPI(backend, false, deadline)   transactions = []*types.Transaction{ - types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), - types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), - types.NewTransaction(2, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), - types.NewTransaction(3, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), - types.NewTransaction(4, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), + types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil, nil, nil, nil), + types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil, nil, nil, nil), + types.NewTransaction(2, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil, nil, nil, nil), + types.NewTransaction(3, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil, nil, nil, nil), + types.NewTransaction(4, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil, nil, nil, nil), }   hashes []common.Hash @@ -630,7 +633,7 @@ return default: }   - tx := types.NewTransaction(i, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil) + tx := types.NewTransaction(i, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil, nil, nil, nil) backend.txFeed.Send(core.NewTxsEvent{Txs: []*types.Transaction{tx}}) i++ }
diff --git go-ethereum/eth/filters/api.go celo/eth/filters/api.go index cec9ca6fc303ccf7c2879445e84595303f4c9550..817101115001bb04f07d4da7bf9d10cc614d3810 100644 --- go-ethereum/eth/filters/api.go +++ celo/eth/filters/api.go @@ -30,7 +30,8 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" )   @@ -49,8 +50,6 @@ // PublicFilterAPI offers support to create and manage filters. This will allow external clients to retrieve various // information related to the Ethereum protocol such als blocks, transactions and logs. type PublicFilterAPI struct { backend Backend - mux *event.TypeMux - quit chan struct{} chainDb ethdb.Database events *EventSystem filtersMu sync.Mutex @@ -226,7 +225,14 @@ for { select { case h := <-headers: - notifier.Notify(rpcSub.ID, h) + jsonHeader := ethapi.RPCMarshalHeader(h) + baseFee, err := api.backend.RealGasPriceMinimumForHeader(ctx, nil, h) + if err != nil { + log.Debug("Not adding baseFeePerGas to header subscription, failed to retrieve gas price minimum", "block", h.Number.Uint64(), "err", err) + } else { + jsonHeader["baseFeePerGas"] = (*hexutil.Big)(baseFee) + } + notifier.Notify(rpcSub.ID, jsonHeader) case <-rpcSub.Err(): headersSub.Unsubscribe() return
diff --git go-ethereum/eth/filters/filter.go celo/eth/filters/filter.go index 49fcb9de48a975549dfa1f68489034bd0a035e9c..5ca4ba9f3cdc3cd9df2243268e89fe4763ff985b 100644 --- go-ethereum/eth/filters/filter.go +++ celo/eth/filters/filter.go @@ -45,6 +45,7 @@ SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription   BloomStatus() (uint64, uint64) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) + RealGasPriceMinimumForHeader(ctx context.Context, currencyAddress *common.Address, header *types.Header) (*big.Int, error) }   // Filter can be used to retrieve and filter logs.
diff --git go-ethereum/eth/filters/filter_test.go celo/eth/filters/filter_test.go index 7ddb589d39fc62584546cd53ba61b360fceb9c3d..d743266dd0c9b5e6c2b780d143320eca02a0fefe 100644 --- go-ethereum/eth/filters/filter_test.go +++ celo/eth/filters/filter_test.go @@ -24,7 +24,7 @@ "os" "testing"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -60,7 +60,7 @@ ) defer db.Close()   genesis := core.GenesisBlockForTesting(db, addr1, big.NewInt(1000000)) - chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 100010, func(i int, gen *core.BlockGen) { + chain, receipts := core.GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), db, 100010, func(i int, gen *core.BlockGen) { switch i { case 2403: receipt := makeReceipt(addr1) @@ -116,7 +116,7 @@ ) defer db.Close()   genesis := core.GenesisBlockForTesting(db, addr, big.NewInt(1000000)) - chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 1000, func(i int, gen *core.BlockGen) { + chain, receipts := core.GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), db, 1000, func(i int, gen *core.BlockGen) { switch i { case 1: receipt := types.NewReceipt(nil, false, 0) @@ -127,7 +127,7 @@ Topics: []common.Hash{hash1}, }, } gen.AddUncheckedReceipt(receipt) - gen.AddUncheckedTx(types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, gen.BaseFee(), nil)) + gen.AddUncheckedTx(types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, nil, nil, nil, nil, nil)) case 2: receipt := types.NewReceipt(nil, false, 0) receipt.Logs = []*types.Log{ @@ -137,7 +137,7 @@ Topics: []common.Hash{hash2}, }, } gen.AddUncheckedReceipt(receipt) - gen.AddUncheckedTx(types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, gen.BaseFee(), nil)) + gen.AddUncheckedTx(types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, nil, nil, nil, nil, nil))   case 998: receipt := types.NewReceipt(nil, false, 0) @@ -148,7 +148,7 @@ Topics: []common.Hash{hash3}, }, } gen.AddUncheckedReceipt(receipt) - gen.AddUncheckedTx(types.NewTransaction(998, common.HexToAddress("0x998"), big.NewInt(998), 998, gen.BaseFee(), nil)) + gen.AddUncheckedTx(types.NewTransaction(998, common.HexToAddress("0x998"), big.NewInt(998), 998, nil, nil, nil, nil, nil)) case 999: receipt := types.NewReceipt(nil, false, 0) receipt.Logs = []*types.Log{ @@ -158,7 +158,7 @@ Topics: []common.Hash{hash4}, }, } gen.AddUncheckedReceipt(receipt) - gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil)) + gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, nil, nil, nil, nil, nil)) } }) for i, block := range chain {
diff --git go-ethereum/eth/gasprice/gasprice.go celo/eth/gasprice/gasprice.go deleted file mode 100644 index 6fe4ef1b47f1d572179fd3ec4178d36f5a8330b2..0000000000000000000000000000000000000000 --- go-ethereum/eth/gasprice/gasprice.go +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package gasprice - -import ( - "context" - "math/big" - "sort" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" - lru "github.com/hashicorp/golang-lru" -) - -const sampleNumber = 3 // Number of transactions sampled in a block - -var ( - DefaultMaxPrice = big.NewInt(500 * params.GWei) - DefaultIgnorePrice = big.NewInt(2 * params.Wei) -) - -type Config struct { - Blocks int - Percentile int - MaxHeaderHistory int - MaxBlockHistory int - Default *big.Int `toml:",omitempty"` - MaxPrice *big.Int `toml:",omitempty"` - IgnorePrice *big.Int `toml:",omitempty"` -} - -// OracleBackend includes all necessary background APIs for oracle. -type OracleBackend interface { - HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) - BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) - GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) - PendingBlockAndReceipts() (*types.Block, types.Receipts) - ChainConfig() *params.ChainConfig - SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription -} - -// Oracle recommends gas prices based on the content of recent -// blocks. Suitable for both light and full clients. -type Oracle struct { - backend OracleBackend - lastHead common.Hash - lastPrice *big.Int - maxPrice *big.Int - ignorePrice *big.Int - cacheLock sync.RWMutex - fetchLock sync.Mutex - - checkBlocks, percentile int - maxHeaderHistory, maxBlockHistory int - historyCache *lru.Cache -} - -// NewOracle returns a new gasprice oracle which can recommend suitable -// gasprice for newly created transaction. -func NewOracle(backend OracleBackend, params Config) *Oracle { - blocks := params.Blocks - if blocks < 1 { - blocks = 1 - log.Warn("Sanitizing invalid gasprice oracle sample blocks", "provided", params.Blocks, "updated", blocks) - } - percent := params.Percentile - if percent < 0 { - percent = 0 - log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent) - } - if percent > 100 { - percent = 100 - log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent) - } - maxPrice := params.MaxPrice - if maxPrice == nil || maxPrice.Int64() <= 0 { - maxPrice = DefaultMaxPrice - log.Warn("Sanitizing invalid gasprice oracle price cap", "provided", params.MaxPrice, "updated", maxPrice) - } - ignorePrice := params.IgnorePrice - if ignorePrice == nil || ignorePrice.Int64() <= 0 { - ignorePrice = DefaultIgnorePrice - log.Warn("Sanitizing invalid gasprice oracle ignore price", "provided", params.IgnorePrice, "updated", ignorePrice) - } else if ignorePrice.Int64() > 0 { - log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice) - } - - cache, _ := lru.New(2048) - headEvent := make(chan core.ChainHeadEvent, 1) - backend.SubscribeChainHeadEvent(headEvent) - go func() { - var lastHead common.Hash - for ev := range headEvent { - if ev.Block.ParentHash() != lastHead { - cache.Purge() - } - lastHead = ev.Block.Hash() - } - }() - - return &Oracle{ - backend: backend, - lastPrice: params.Default, - maxPrice: maxPrice, - ignorePrice: ignorePrice, - checkBlocks: blocks, - percentile: percent, - maxHeaderHistory: params.MaxHeaderHistory, - maxBlockHistory: params.MaxBlockHistory, - historyCache: cache, - } -} - -// SuggestTipCap returns a tip cap so that newly created transaction can have a -// very high chance to be included in the following blocks. -// -// Note, for legacy transactions and the legacy eth_gasPrice RPC call, it will be -// necessary to add the basefee to the returned number to fall back to the legacy -// behavior. -func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) { - head, _ := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) - headHash := head.Hash() - - // If the latest gasprice is still available, return it. - oracle.cacheLock.RLock() - lastHead, lastPrice := oracle.lastHead, oracle.lastPrice - oracle.cacheLock.RUnlock() - if headHash == lastHead { - return new(big.Int).Set(lastPrice), nil - } - oracle.fetchLock.Lock() - defer oracle.fetchLock.Unlock() - - // Try checking the cache again, maybe the last fetch fetched what we need - oracle.cacheLock.RLock() - lastHead, lastPrice = oracle.lastHead, oracle.lastPrice - oracle.cacheLock.RUnlock() - if headHash == lastHead { - return new(big.Int).Set(lastPrice), nil - } - var ( - sent, exp int - number = head.Number.Uint64() - result = make(chan results, oracle.checkBlocks) - quit = make(chan struct{}) - results []*big.Int - ) - for sent < oracle.checkBlocks && number > 0 { - go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit) - sent++ - exp++ - number-- - } - for exp > 0 { - res := <-result - if res.err != nil { - close(quit) - return new(big.Int).Set(lastPrice), res.err - } - exp-- - // Nothing returned. There are two special cases here: - // - The block is empty - // - All the transactions included are sent by the miner itself. - // In these cases, use the latest calculated price for sampling. - if len(res.values) == 0 { - res.values = []*big.Int{lastPrice} - } - // Besides, in order to collect enough data for sampling, if nothing - // meaningful returned, try to query more blocks. But the maximum - // is 2*checkBlocks. - if len(res.values) == 1 && len(results)+1+exp < oracle.checkBlocks*2 && number > 0 { - go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit) - sent++ - exp++ - number-- - } - results = append(results, res.values...) - } - price := lastPrice - if len(results) > 0 { - sort.Sort(bigIntArray(results)) - price = results[(len(results)-1)*oracle.percentile/100] - } - if price.Cmp(oracle.maxPrice) > 0 { - price = new(big.Int).Set(oracle.maxPrice) - } - oracle.cacheLock.Lock() - oracle.lastHead = headHash - oracle.lastPrice = price - oracle.cacheLock.Unlock() - - return new(big.Int).Set(price), nil -} - -type results struct { - values []*big.Int - err error -} - -type txSorter struct { - txs []*types.Transaction - baseFee *big.Int -} - -func newSorter(txs []*types.Transaction, baseFee *big.Int) *txSorter { - return &txSorter{ - txs: txs, - baseFee: baseFee, - } -} - -func (s *txSorter) Len() int { return len(s.txs) } -func (s *txSorter) Swap(i, j int) { - s.txs[i], s.txs[j] = s.txs[j], s.txs[i] -} -func (s *txSorter) Less(i, j int) bool { - // It's okay to discard the error because a tx would never be - // accepted into a block with an invalid effective tip. - tip1, _ := s.txs[i].EffectiveGasTip(s.baseFee) - tip2, _ := s.txs[j].EffectiveGasTip(s.baseFee) - return tip1.Cmp(tip2) < 0 -} - -// getBlockPrices calculates the lowest transaction gas price in a given block -// and sends it to the result channel. If the block is empty or all transactions -// are sent by the miner itself(it doesn't make any sense to include this kind of -// transaction prices for sampling), nil gasprice is returned. -func (oracle *Oracle) getBlockValues(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) { - block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) - if block == nil { - select { - case result <- results{nil, err}: - case <-quit: - } - return - } - // Sort the transaction by effective tip in ascending sort. - txs := make([]*types.Transaction, len(block.Transactions())) - copy(txs, block.Transactions()) - sorter := newSorter(txs, block.BaseFee()) - sort.Sort(sorter) - - var prices []*big.Int - for _, tx := range sorter.txs { - tip, _ := tx.EffectiveGasTip(block.BaseFee()) - if ignoreUnder != nil && tip.Cmp(ignoreUnder) == -1 { - continue - } - sender, err := types.Sender(signer, tx) - if err == nil && sender != block.Coinbase() { - prices = append(prices, tip) - if len(prices) >= limit { - break - } - } - } - select { - case result <- results{prices, nil}: - case <-quit: - } -} - -type bigIntArray []*big.Int - -func (s bigIntArray) Len() int { return len(s) } -func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 } -func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
diff --git go-ethereum/eth/gasprice/feehistory.go celo/eth/gasprice/feehistory.go deleted file mode 100644 index c8c6c0a37bcec9ef5bbbc4d1daaff41bc1475140..0000000000000000000000000000000000000000 --- go-ethereum/eth/gasprice/feehistory.go +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package gasprice - -import ( - "context" - "encoding/binary" - "errors" - "fmt" - "math" - "math/big" - "sort" - "sync/atomic" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/misc" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rpc" -) - -var ( - errInvalidPercentile = errors.New("invalid reward percentile") - errRequestBeyondHead = errors.New("request beyond head block") -) - -const ( - // maxBlockFetchers is the max number of goroutines to spin up to pull blocks - // for the fee history calculation (mostly relevant for LES). - maxBlockFetchers = 4 -) - -// blockFees represents a single block for processing -type blockFees struct { - // set by the caller - blockNumber uint64 - header *types.Header - block *types.Block // only set if reward percentiles are requested - receipts types.Receipts - // filled by processBlock - results processedFees - err error -} - -// processedFees contains the results of a processed block and is also used for caching -type processedFees struct { - reward []*big.Int - baseFee, nextBaseFee *big.Int - gasUsedRatio float64 -} - -// txGasAndReward is sorted in ascending order based on reward -type ( - txGasAndReward struct { - gasUsed uint64 - reward *big.Int - } - sortGasAndReward []txGasAndReward -) - -func (s sortGasAndReward) Len() int { return len(s) } -func (s sortGasAndReward) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} -func (s sortGasAndReward) Less(i, j int) bool { - return s[i].reward.Cmp(s[j].reward) < 0 -} - -// processBlock takes a blockFees structure with the blockNumber, the header and optionally -// the block field filled in, retrieves the block from the backend if not present yet and -// fills in the rest of the fields. -func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { - chainconfig := oracle.backend.ChainConfig() - if bf.results.baseFee = bf.header.BaseFee; bf.results.baseFee == nil { - bf.results.baseFee = new(big.Int) - } - if chainconfig.IsLondon(big.NewInt(int64(bf.blockNumber + 1))) { - bf.results.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header) - } else { - bf.results.nextBaseFee = new(big.Int) - } - bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit) - if len(percentiles) == 0 { - // rewards were not requested, return null - return - } - if bf.block == nil || (bf.receipts == nil && len(bf.block.Transactions()) != 0) { - log.Error("Block or receipts are missing while reward percentiles are requested") - return - } - - bf.results.reward = make([]*big.Int, len(percentiles)) - if len(bf.block.Transactions()) == 0 { - // return an all zero row if there are no transactions to gather data from - for i := range bf.results.reward { - bf.results.reward[i] = new(big.Int) - } - return - } - - sorter := make(sortGasAndReward, len(bf.block.Transactions())) - for i, tx := range bf.block.Transactions() { - reward, _ := tx.EffectiveGasTip(bf.block.BaseFee()) - sorter[i] = txGasAndReward{gasUsed: bf.receipts[i].GasUsed, reward: reward} - } - sort.Sort(sorter) - - var txIndex int - sumGasUsed := sorter[0].gasUsed - - for i, p := range percentiles { - thresholdGasUsed := uint64(float64(bf.block.GasUsed()) * p / 100) - for sumGasUsed < thresholdGasUsed && txIndex < len(bf.block.Transactions())-1 { - txIndex++ - sumGasUsed += sorter[txIndex].gasUsed - } - bf.results.reward[i] = sorter[txIndex].reward - } -} - -// resolveBlockRange resolves the specified block range to absolute block numbers while also -// enforcing backend specific limitations. The pending block and corresponding receipts are -// also returned if requested and available. -// Note: an error is only returned if retrieving the head header has failed. If there are no -// retrievable blocks in the specified range then zero block count is returned with no error. -func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.BlockNumber, blocks int) (*types.Block, []*types.Receipt, uint64, int, error) { - var ( - headBlock rpc.BlockNumber - pendingBlock *types.Block - pendingReceipts types.Receipts - ) - // query either pending block or head header and set headBlock - if lastBlock == rpc.PendingBlockNumber { - if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil { - lastBlock = rpc.BlockNumber(pendingBlock.NumberU64()) - headBlock = lastBlock - 1 - } else { - // pending block not supported by backend, process until latest block - lastBlock = rpc.LatestBlockNumber - blocks-- - if blocks == 0 { - return nil, nil, 0, 0, nil - } - } - } - if pendingBlock == nil { - // if pending block is not fetched then we retrieve the head header to get the head block number - if latestHeader, err := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err == nil { - headBlock = rpc.BlockNumber(latestHeader.Number.Uint64()) - } else { - return nil, nil, 0, 0, err - } - } - if lastBlock == rpc.LatestBlockNumber { - lastBlock = headBlock - } else if pendingBlock == nil && lastBlock > headBlock { - return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, lastBlock, headBlock) - } - // ensure not trying to retrieve before genesis - if rpc.BlockNumber(blocks) > lastBlock+1 { - blocks = int(lastBlock + 1) - } - return pendingBlock, pendingReceipts, uint64(lastBlock), blocks, nil -} - -// FeeHistory returns data relevant for fee estimation based on the specified range of blocks. -// The range can be specified either with absolute block numbers or ending with the latest -// or pending block. Backends may or may not support gathering data from the pending block -// or blocks older than a certain age (specified in maxHistory). The first block of the -// actually processed range is returned to avoid ambiguity when parts of the requested range -// are not available or when the head has changed during processing this request. -// Three arrays are returned based on the processed blocks: -// - reward: the requested percentiles of effective priority fees per gas of transactions in each -// block, sorted in ascending order and weighted by gas used. -// - baseFee: base fee per gas in the given block -// - gasUsedRatio: gasUsed/gasLimit in the given block -// Note: baseFee includes the next block after the newest of the returned range, because this -// value can be derived from the newest block. -func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { - if blocks < 1 { - return common.Big0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks - } - maxFeeHistory := oracle.maxHeaderHistory - if len(rewardPercentiles) != 0 { - maxFeeHistory = oracle.maxBlockHistory - } - if blocks > maxFeeHistory { - log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory) - blocks = maxFeeHistory - } - for i, p := range rewardPercentiles { - if p < 0 || p > 100 { - return common.Big0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p) - } - if i > 0 && p < rewardPercentiles[i-1] { - return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) - } - } - var ( - pendingBlock *types.Block - pendingReceipts []*types.Receipt - err error - ) - pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks) - if err != nil || blocks == 0 { - return common.Big0, nil, nil, nil, err - } - oldestBlock := lastBlock + 1 - uint64(blocks) - - var ( - next = oldestBlock - results = make(chan *blockFees, blocks) - ) - percentileKey := make([]byte, 8*len(rewardPercentiles)) - for i, p := range rewardPercentiles { - binary.LittleEndian.PutUint64(percentileKey[i*8:(i+1)*8], math.Float64bits(p)) - } - for i := 0; i < maxBlockFetchers && i < blocks; i++ { - go func() { - for { - // Retrieve the next block number to fetch with this goroutine - blockNumber := atomic.AddUint64(&next, 1) - 1 - if blockNumber > lastBlock { - return - } - - fees := &blockFees{blockNumber: blockNumber} - if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() { - fees.block, fees.receipts = pendingBlock, pendingReceipts - fees.header = fees.block.Header() - oracle.processBlock(fees, rewardPercentiles) - results <- fees - } else { - cacheKey := struct { - number uint64 - percentiles string - }{blockNumber, string(percentileKey)} - - if p, ok := oracle.historyCache.Get(cacheKey); ok { - fees.results = p.(processedFees) - results <- fees - } else { - if len(rewardPercentiles) != 0 { - fees.block, fees.err = oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNumber)) - if fees.block != nil && fees.err == nil { - fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash()) - fees.header = fees.block.Header() - } - } else { - fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber)) - } - if fees.header != nil && fees.err == nil { - oracle.processBlock(fees, rewardPercentiles) - if fees.err == nil { - oracle.historyCache.Add(cacheKey, fees.results) - } - } - // send to results even if empty to guarantee that blocks items are sent in total - results <- fees - } - } - } - }() - } - var ( - reward = make([][]*big.Int, blocks) - baseFee = make([]*big.Int, blocks+1) - gasUsedRatio = make([]float64, blocks) - firstMissing = blocks - ) - for ; blocks > 0; blocks-- { - fees := <-results - if fees.err != nil { - return common.Big0, nil, nil, nil, fees.err - } - i := int(fees.blockNumber - oldestBlock) - if fees.results.baseFee != nil { - reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.results.reward, fees.results.baseFee, fees.results.nextBaseFee, fees.results.gasUsedRatio - } else { - // getting no block and no error means we are requesting into the future (might happen because of a reorg) - if i < firstMissing { - firstMissing = i - } - } - } - if firstMissing == 0 { - return common.Big0, nil, nil, nil, nil - } - if len(rewardPercentiles) != 0 { - reward = reward[:firstMissing] - } else { - reward = nil - } - baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing] - return new(big.Int).SetUint64(oldestBlock), reward, baseFee, gasUsedRatio, nil -}
diff --git go-ethereum/eth/gasprice/feehistory_test.go celo/eth/gasprice/feehistory_test.go deleted file mode 100644 index 5d8641706bbc1860aa2acfcc74ca025af6449957..0000000000000000000000000000000000000000 --- go-ethereum/eth/gasprice/feehistory_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package gasprice - -import ( - "context" - "errors" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/rpc" -) - -func TestFeeHistory(t *testing.T) { - var cases = []struct { - pending bool - maxHeader, maxBlock int - count int - last rpc.BlockNumber - percent []float64 - expFirst uint64 - expCount int - expErr error - }{ - {false, 1000, 1000, 10, 30, nil, 21, 10, nil}, - {false, 1000, 1000, 10, 30, []float64{0, 10}, 21, 10, nil}, - {false, 1000, 1000, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentile}, - {false, 1000, 1000, 1000000000, 30, nil, 0, 31, nil}, - {false, 1000, 1000, 1000000000, rpc.LatestBlockNumber, nil, 0, 33, nil}, - {false, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead}, - {true, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead}, - {false, 20, 2, 100, rpc.LatestBlockNumber, nil, 13, 20, nil}, - {false, 20, 2, 100, rpc.LatestBlockNumber, []float64{0, 10}, 31, 2, nil}, - {false, 20, 2, 100, 32, []float64{0, 10}, 31, 2, nil}, - {false, 1000, 1000, 1, rpc.PendingBlockNumber, nil, 0, 0, nil}, - {false, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 1, nil}, - {true, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 2, nil}, - {true, 1000, 1000, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil}, - } - for i, c := range cases { - config := Config{ - MaxHeaderHistory: c.maxHeader, - MaxBlockHistory: c.maxBlock, - } - backend := newTestBackend(t, big.NewInt(16), c.pending) - oracle := NewOracle(backend, config) - - first, reward, baseFee, ratio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent) - - expReward := c.expCount - if len(c.percent) == 0 { - expReward = 0 - } - expBaseFee := c.expCount - if expBaseFee != 0 { - expBaseFee++ - } - - if first.Uint64() != c.expFirst { - t.Fatalf("Test case %d: first block mismatch, want %d, got %d", i, c.expFirst, first) - } - if len(reward) != expReward { - t.Fatalf("Test case %d: reward array length mismatch, want %d, got %d", i, expReward, len(reward)) - } - if len(baseFee) != expBaseFee { - t.Fatalf("Test case %d: baseFee array length mismatch, want %d, got %d", i, expBaseFee, len(baseFee)) - } - if len(ratio) != c.expCount { - t.Fatalf("Test case %d: gasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(ratio)) - } - if err != c.expErr && !errors.Is(err, c.expErr) { - t.Fatalf("Test case %d: error mismatch, want %v, got %v", i, c.expErr, err) - } - } -}
diff --git go-ethereum/eth/gasprice/gasprice_test.go celo/eth/gasprice/gasprice_test.go deleted file mode 100644 index ff8b767c9c18d28e8c38c135265e7f871fccb652..0000000000000000000000000000000000000000 --- go-ethereum/eth/gasprice/gasprice_test.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package gasprice - -import ( - "context" - "math" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" -) - -const testHead = 32 - -type testBackend struct { - chain *core.BlockChain - pending bool // pending block available -} - -func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { - if number > testHead { - return nil, nil - } - if number == rpc.LatestBlockNumber { - number = testHead - } - if number == rpc.PendingBlockNumber { - if b.pending { - number = testHead + 1 - } else { - return nil, nil - } - } - return b.chain.GetHeaderByNumber(uint64(number)), nil -} - -func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { - if number > testHead { - return nil, nil - } - if number == rpc.LatestBlockNumber { - number = testHead - } - if number == rpc.PendingBlockNumber { - if b.pending { - number = testHead + 1 - } else { - return nil, nil - } - } - return b.chain.GetBlockByNumber(uint64(number)), nil -} - -func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { - return b.chain.GetReceiptsByHash(hash), nil -} - -func (b *testBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) { - if b.pending { - block := b.chain.GetBlockByNumber(testHead + 1) - return block, b.chain.GetReceiptsByHash(block.Hash()) - } - return nil, nil -} - -func (b *testBackend) ChainConfig() *params.ChainConfig { - return b.chain.Config() -} - -func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { - return nil -} - -func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBackend { - var ( - key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - addr = crypto.PubkeyToAddress(key.PublicKey) - gspec = &core.Genesis{ - Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, - } - signer = types.LatestSigner(gspec.Config) - ) - if londonBlock != nil { - gspec.Config.LondonBlock = londonBlock - signer = types.LatestSigner(gspec.Config) - } else { - gspec.Config.LondonBlock = nil - } - engine := ethash.NewFaker() - db := rawdb.NewMemoryDatabase() - genesis, _ := gspec.Commit(db) - - // Generate testing blocks - blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, testHead+1, func(i int, b *core.BlockGen) { - b.SetCoinbase(common.Address{1}) - - var tx *types.Transaction - if londonBlock != nil && b.Number().Cmp(londonBlock) >= 0 { - txdata := &types.DynamicFeeTx{ - ChainID: gspec.Config.ChainID, - Nonce: b.TxNonce(addr), - To: &common.Address{}, - Gas: 30000, - GasFeeCap: big.NewInt(100 * params.GWei), - GasTipCap: big.NewInt(int64(i+1) * params.GWei), - Data: []byte{}, - } - tx = types.NewTx(txdata) - } else { - txdata := &types.LegacyTx{ - Nonce: b.TxNonce(addr), - To: &common.Address{}, - Gas: 21000, - GasPrice: big.NewInt(int64(i+1) * params.GWei), - Value: big.NewInt(100), - Data: []byte{}, - } - tx = types.NewTx(txdata) - } - tx, err := types.SignTx(tx, signer, key) - if err != nil { - t.Fatalf("failed to create tx: %v", err) - } - b.AddTx(tx) - }) - // Construct testing chain - diskdb := rawdb.NewMemoryDatabase() - gspec.Commit(diskdb) - chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("Failed to create local chain, %v", err) - } - chain.InsertChain(blocks) - return &testBackend{chain: chain, pending: pending} -} - -func (b *testBackend) CurrentHeader() *types.Header { - return b.chain.CurrentHeader() -} - -func (b *testBackend) GetBlockByNumber(number uint64) *types.Block { - return b.chain.GetBlockByNumber(number) -} - -func TestSuggestTipCap(t *testing.T) { - config := Config{ - Blocks: 3, - Percentile: 60, - Default: big.NewInt(params.GWei), - } - var cases = []struct { - fork *big.Int // London fork number - expect *big.Int // Expected gasprice suggestion - }{ - {nil, big.NewInt(params.GWei * int64(30))}, - {big.NewInt(0), big.NewInt(params.GWei * int64(30))}, // Fork point in genesis - {big.NewInt(1), big.NewInt(params.GWei * int64(30))}, // Fork point in first block - {big.NewInt(32), big.NewInt(params.GWei * int64(30))}, // Fork point in last block - {big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future - } - for _, c := range cases { - backend := newTestBackend(t, c.fork, false) - oracle := NewOracle(backend, config) - - // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G - got, err := oracle.SuggestTipCap(context.Background()) - if err != nil { - t.Fatalf("Failed to retrieve recommended gas price: %v", err) - } - if got.Cmp(c.expect) != 0 { - t.Fatalf("Gas price mismatch, want %d, got %d", c.expect, got) - } - } -}
diff --git go-ethereum/eth/protocols/eth/handler.go celo/eth/protocols/eth/handler.go index 5a14960a533f6b005e20181d067649ebf5aee149..a5ccc56ccb491a7b7c1397cc911a39cdded03dd5 100644 --- go-ethereum/eth/protocols/eth/handler.go +++ celo/eth/protocols/eth/handler.go @@ -22,8 +22,11 @@ "math/big" "time"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" @@ -102,14 +105,14 @@ }   // MakeProtocols constructs the P2P protocol definitions for `eth`. func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2p.Protocol { - protocols := make([]p2p.Protocol, len(ProtocolVersions)) - for i, version := range ProtocolVersions { + protocols := make([]p2p.Protocol, len(istanbul.ProtocolVersions)) + for i, version := range istanbul.ProtocolVersions { version := version // Closure   protocols[i] = p2p.Protocol{ - Name: ProtocolName, + Name: istanbul.ProtocolName, Version: version, - Length: protocolLengths[version], + Length: istanbul.ProtocolLengths[version], Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { peer := NewPeer(version, p, rw, backend.TxPool()) defer peer.Close() @@ -171,39 +174,47 @@ Decode(val interface{}) error Time() time.Time }   -var eth66 = map[uint64]msgHandler{ +var celo67 = map[uint64]msgHandler{ NewBlockHashesMsg: handleNewBlockhashes, NewBlockMsg: handleNewBlock, TransactionsMsg: handleTransactions, NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes, - GetBlockHeadersMsg: handleGetBlockHeaders66, - BlockHeadersMsg: handleBlockHeaders66, - GetBlockBodiesMsg: handleGetBlockBodies66, - BlockBodiesMsg: handleBlockBodies66, - GetNodeDataMsg: handleGetNodeData66, - NodeDataMsg: handleNodeData66, - GetReceiptsMsg: handleGetReceipts66, - ReceiptsMsg: handleReceipts66, - GetPooledTransactionsMsg: handleGetPooledTransactions66, - PooledTransactionsMsg: handlePooledTransactions66, + GetBlockHeadersMsg: handleGetBlockHeaders67, + BlockHeadersMsg: handleBlockHeaders67, + GetBlockBodiesMsg: handleGetBlockBodies67, + BlockBodiesMsg: handleBlockBodies67, + GetNodeDataMsg: handleGetNodeData67, + NodeDataMsg: handleNodeData67, + GetReceiptsMsg: handleGetReceipts67, + ReceiptsMsg: handleReceipts67, + GetPooledTransactionsMsg: handleGetPooledTransactions67, + PooledTransactionsMsg: handlePooledTransactions67, }   // handleMessage is invoked whenever an inbound message is received from a remote // peer. The remote connection is torn down upon returning any error. func handleMessage(backend Backend, peer *Peer) error { // Read the next message from the remote peer, and ensure it's fully consumed - msg, err := peer.rw.ReadMsg() + msg, err := peer.ReadMsg() if err != nil { return err } - if msg.Size > maxMessageSize { - return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize) + defer msg.Discard() + + // Send messages to the consensus engine first. If they are consensus related, + // e.g. for IBFT, let the consensus handler handle the message. + if consensusHandler, ok := backend.Chain().Engine().(consensus.Handler); ok { + pubKey := peer.Node().Pubkey() + addr := crypto.PubkeyToAddress(*pubKey) + handled, err := consensusHandler.HandleMsg(addr, msg, peer) + if handled { + return err + } } - defer msg.Discard()   - var handlers = eth66 - //if peer.Version() >= ETH67 { // Left in as a sample when new protocol is added - // handlers = eth67 + var handlers = celo67 + //if peer.Version() >= celo68 { // Left in as a sample when new protocol is added + // handlers = celo68 //}   // Track the amount of time it takes to serve the request and run the handler
diff --git go-ethereum/eth/protocols/eth/handshake.go celo/eth/protocols/eth/handshake.go index 786663ae6dea7bd9cd4fe176414090ed01440d5d..a6bec4c24142b1758af42bbdc6097ba398c22c7b 100644 --- go-ethereum/eth/protocols/eth/handshake.go +++ celo/eth/protocols/eth/handshake.go @@ -77,15 +77,12 @@ }   // readStatus reads the remote handshake message. func (p *Peer) readStatus(network uint64, status *StatusPacket, genesis common.Hash, forkFilter forkid.Filter) error { - msg, err := p.rw.ReadMsg() + msg, err := p.ReadMsg() if err != nil { return err } if msg.Code != StatusMsg { return fmt.Errorf("%w: first msg has code %x (!= %x)", errNoStatusMsg, msg.Code, StatusMsg) - } - if msg.Size > maxMessageSize { - return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize) } // Decode the handshake and make sure everything matches if err := msg.Decode(&status); err != nil { @@ -98,7 +95,7 @@ if uint(status.ProtocolVersion) != p.version { return fmt.Errorf("%w: %d (!= %d)", errProtocolVersionMismatch, status.ProtocolVersion, p.version) } if status.Genesis != genesis { - return fmt.Errorf("%w: %x (!= %x)", errGenesisMismatch, status.Genesis, genesis) + return fmt.Errorf("%w: peer: %x (local: %x)", errGenesisMismatch, status.Genesis, genesis) } if err := forkFilter(status.ForkID); err != nil { return fmt.Errorf("%w: %v", errForkIDRejected, err)
diff --git go-ethereum/eth/protocols/snap/sync_test.go celo/eth/protocols/snap/sync_test.go index c6e746a31af81a183515575867fe7b1b4bc61c4a..e9b8cc41e02546c0da2444cc96b9d6974b96de65 100644 --- go-ethereum/eth/protocols/snap/sync_test.go +++ celo/eth/protocols/snap/sync_test.go @@ -1658,7 +1658,7 @@ // sync cycle starts. When popping the queue, we do not look it up again. // Doing so would bring this number down to zero in this artificial testcase, // but only add extra IO for no reason in practice. if have, want := src.nTrienodeRequests, 1; have != want { - fmt.Printf(src.Stats()) + fmt.Println(src.Stats()) t.Errorf("trie node heal requests wrong, want %d, have %d", want, have) } }
diff --git go-ethereum/eth/protocols/eth/handlers.go celo/eth/protocols/eth/handlers.go index df70fbb0a07af1e794a6c43dd55042e726dc7cd5..08940f31a97ab5acce9f4a26d5b91c26cb6546f2 100644 --- go-ethereum/eth/protocols/eth/handlers.go +++ celo/eth/protocols/eth/handlers.go @@ -27,10 +27,10 @@ "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" )   -// handleGetBlockHeaders66 is the eth/66 version of handleGetBlockHeaders -func handleGetBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error { +// handleGetBlockHeaders67 is the celo/67 (eth/66) version of handleGetBlockHeaders +func handleGetBlockHeaders67(backend Backend, msg Decoder, peer *Peer) error { // Decode the complex header query - var query GetBlockHeadersPacket66 + var query GetBlockHeadersPacket67 if err := msg.Decode(&query); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } @@ -124,38 +124,53 @@ } return headers }   -func handleGetBlockBodies66(backend Backend, msg Decoder, peer *Peer) error { +func handleGetBlockBodies67(backend Backend, msg Decoder, peer *Peer) error { // Decode the block body retrieval message - var query GetBlockBodiesPacket66 + var query GetBlockBodiesPacket67 if err := msg.Decode(&query); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } - response := answerGetBlockBodiesQuery(backend, query.GetBlockBodiesPacket, peer) + response, err := answerGetBlockBodiesQuery(backend, query.GetBlockBodiesPacket, peer) + if err != nil { + return err + } return peer.ReplyBlockBodiesRLP(query.RequestId, response) }   -func answerGetBlockBodiesQuery(backend Backend, query GetBlockBodiesPacket, peer *Peer) []rlp.RawValue { +// In Celo the return value of the `GetBlockBodies` query has been changed to include both the block hash +// and the block bodies. This is necessary because received block bodies can not be matched to the header +// directly, as there is body data (i.e. `Randomness` and `EpochSnarkData`) which is not represented in the +// header. That means that that the block fetcher cannot find the corresponding header for given blockdata +// without executing the block contents. This is avoided by passing the block hash with the body data. +func answerGetBlockBodiesQuery(backend Backend, query GetBlockBodiesPacket, peer *Peer) ([]rlp.RawValue, error) { // Gather blocks until the fetch or network limits is reached var ( bytes int - bodies []rlp.RawValue + bodiesAndBlockHashes []rlp.RawValue ) for lookups, hash := range query { - if bytes >= softResponseLimit || len(bodies) >= maxBodiesServe || + if bytes >= softResponseLimit || len(bodiesAndBlockHashes) >= maxBodiesServe || lookups >= 2*maxBodiesServe { break } - if data := backend.Chain().GetBodyRLP(hash); len(data) != 0 { - bodies = append(bodies, data) - bytes += len(data) + // Retrieve the requested block body, stopping if enough was found + if body := backend.Chain().GetBody(hash); body != nil { + bh := &blockBodyWithBlockHash{BlockHash: hash, BlockBody: body} + bhRLPbytes, err := rlp.EncodeToBytes(bh) + if err != nil { + return nil, err + } + bhRLP := rlp.RawValue(bhRLPbytes) + bodiesAndBlockHashes = append(bodiesAndBlockHashes, bhRLP) + bytes += len(bhRLP) } } - return bodies + return bodiesAndBlockHashes, nil }   -func handleGetNodeData66(backend Backend, msg Decoder, peer *Peer) error { +func handleGetNodeData67(backend Backend, msg Decoder, peer *Peer) error { // Decode the trie node data retrieval message - var query GetNodeDataPacket66 + var query GetNodeDataPacket67 if err := msg.Decode(&query); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } @@ -192,9 +207,9 @@ } return nodes }   -func handleGetReceipts66(backend Backend, msg Decoder, peer *Peer) error { +func handleGetReceipts67(backend Backend, msg Decoder, peer *Peer) error { // Decode the block receipts retrieval message - var query GetReceiptsPacket66 + var query GetReceiptsPacket67 if err := msg.Decode(&query); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } @@ -254,10 +269,6 @@ } if err := ann.sanityCheck(); err != nil { return err } - if hash := types.CalcUncleHash(ann.Block.Uncles()); hash != ann.Block.UncleHash() { - log.Warn("Propagated block has invalid uncles", "have", hash, "exp", ann.Block.UncleHash()) - return nil // TODO(karalabe): return error eventually, but wait a few releases - } if hash := types.DeriveSha(ann.Block.Transactions(), trie.NewStackTrie(nil)); hash != ann.Block.TxHash() { log.Warn("Propagated block has invalid body", "have", hash, "exp", ann.Block.TxHash()) return nil // TODO(karalabe): return error eventually, but wait a few releases @@ -271,9 +282,9 @@ return backend.Handle(peer, ann) }   -func handleBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error { +func handleBlockHeaders67(backend Backend, msg Decoder, peer *Peer) error { // A batch of headers arrived to one of our previous requests - res := new(BlockHeadersPacket66) + res := new(BlockHeadersPacket67) if err := msg.Decode(res); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } @@ -282,9 +293,9 @@ return backend.Handle(peer, &res.BlockHeadersPacket) }   -func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error { +func handleBlockBodies67(backend Backend, msg Decoder, peer *Peer) error { // A batch of block bodies arrived to one of our previous requests - res := new(BlockBodiesPacket66) + res := new(BlockBodiesPacket67) if err := msg.Decode(res); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } @@ -293,9 +304,9 @@ return backend.Handle(peer, &res.BlockBodiesPacket) }   -func handleNodeData66(backend Backend, msg Decoder, peer *Peer) error { +func handleNodeData67(backend Backend, msg Decoder, peer *Peer) error { // A batch of node state data arrived to one of our previous requests - res := new(NodeDataPacket66) + res := new(NodeDataPacket67) if err := msg.Decode(res); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } @@ -304,9 +315,9 @@ return backend.Handle(peer, &res.NodeDataPacket) }   -func handleReceipts66(backend Backend, msg Decoder, peer *Peer) error { +func handleReceipts67(backend Backend, msg Decoder, peer *Peer) error { // A batch of receipts arrived to one of our previous requests - res := new(ReceiptsPacket66) + res := new(ReceiptsPacket67) if err := msg.Decode(res); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } @@ -332,9 +343,9 @@ } return backend.Handle(peer, ann) }   -func handleGetPooledTransactions66(backend Backend, msg Decoder, peer *Peer) error { +func handleGetPooledTransactions67(backend Backend, msg Decoder, peer *Peer) error { // Decode the pooled transactions retrieval message - var query GetPooledTransactionsPacket66 + var query GetPooledTransactionsPacket67 if err := msg.Decode(&query); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } @@ -390,13 +401,13 @@ } return backend.Handle(peer, &txs) }   -func handlePooledTransactions66(backend Backend, msg Decoder, peer *Peer) error { +func handlePooledTransactions67(backend Backend, msg Decoder, peer *Peer) error { // Transactions arrived, make sure we have a valid and fresh chain to handle them if !backend.AcceptTxs() { return nil } // Transactions can be processed, parse all of them and deliver to the pool - var txs PooledTransactionsPacket66 + var txs PooledTransactionsPacket67 if err := msg.Decode(&txs); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) }
diff --git go-ethereum/eth/protocols/eth/handshake_test.go celo/eth/protocols/eth/handshake_test.go index d27e774397bcff1fe39c9e66e4980b6ffe2e8455..41fafc02ac07a228f1162cc96917853530e3d365 100644 --- go-ethereum/eth/protocols/eth/handshake_test.go +++ celo/eth/protocols/eth/handshake_test.go @@ -21,13 +21,14 @@ "errors" "testing"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" )   // Tests that handshake failures are detected and reported correctly. -func TestHandshake66(t *testing.T) { testHandshake(t, ETH66) } +func TestHandshake67(t *testing.T) { testHandshake(t, istanbul.Celo67) }   func testHandshake(t *testing.T, protocol uint) { t.Parallel()
diff --git go-ethereum/eth/protocols/eth/peer.go celo/eth/protocols/eth/peer.go index fe505315b5c7d1ab06fd078ae5ae74d96aa5a763..bd2cab9cc2999e70f0e31ef9331246b05ae84704 100644 --- go-ethereum/eth/protocols/eth/peer.go +++ celo/eth/protocols/eth/peer.go @@ -17,6 +17,7 @@ package eth   import ( + "fmt" "math/big" "math/rand" "sync" @@ -172,6 +173,12 @@ // If we reached the memory allowance, drop a previously known transaction hash p.knownTxs.Add(hash) }   +// Send writes an RLP-encoded message with the given code. +// data should encode as an RLP list. +func (p *Peer) Send(msgcode uint64, data interface{}) error { + return p2p.Send(p.rw, msgcode, data) +} + // SendTransactions sends transactions to the peer and includes the hashes // in its transaction hash set for future reference. // @@ -227,13 +234,13 @@ p.Log().Debug("Dropping transaction announcement", "count", len(hashes)) } }   -// ReplyPooledTransactionsRLP is the eth/66 version of SendPooledTransactionsRLP. +// ReplyPooledTransactionsRLP is the celo/67 (eth/66) version of SendPooledTransactionsRLP. func (p *Peer) ReplyPooledTransactionsRLP(id uint64, hashes []common.Hash, txs []rlp.RawValue) error { // Mark all the transactions as known, but ensure we don't overflow our limits p.knownTxs.Add(hashes...)   // Not packed into PooledTransactionsPacket to avoid RLP decoding - return p2p.Send(p.rw, PooledTransactionsMsg, PooledTransactionsRLPPacket66{ + return p2p.Send(p.rw, PooledTransactionsMsg, PooledTransactionsRLPPacket67{ RequestId: id, PooledTransactionsRLPPacket: txs, }) @@ -288,34 +295,34 @@ p.Log().Debug("Dropping block propagation", "number", block.NumberU64(), "hash", block.Hash()) } }   -// ReplyBlockHeaders is the eth/66 version of SendBlockHeaders. +// ReplyBlockHeaders is the celo/67 (eth/66) version of SendBlockHeaders. func (p *Peer) ReplyBlockHeaders(id uint64, headers []*types.Header) error { - return p2p.Send(p.rw, BlockHeadersMsg, BlockHeadersPacket66{ + return p2p.Send(p.rw, BlockHeadersMsg, BlockHeadersPacket67{ RequestId: id, BlockHeadersPacket: headers, }) }   -// ReplyBlockBodiesRLP is the eth/66 version of SendBlockBodiesRLP. +// ReplyBlockBodiesRLP is the celo/67 (eth/66) version of SendBlockBodiesRLP. func (p *Peer) ReplyBlockBodiesRLP(id uint64, bodies []rlp.RawValue) error { // Not packed into BlockBodiesPacket to avoid RLP decoding - return p2p.Send(p.rw, BlockBodiesMsg, BlockBodiesRLPPacket66{ + return p2p.Send(p.rw, BlockBodiesMsg, BlockBodiesRLPPacket67{ RequestId: id, BlockBodiesRLPPacket: bodies, }) }   -// ReplyNodeData is the eth/66 response to GetNodeData. +// ReplyNodeData is the celo/67 (eth/66) response to GetNodeData. func (p *Peer) ReplyNodeData(id uint64, data [][]byte) error { - return p2p.Send(p.rw, NodeDataMsg, NodeDataPacket66{ + return p2p.Send(p.rw, NodeDataMsg, NodeDataPacket67{ RequestId: id, NodeDataPacket: data, }) }   -// ReplyReceiptsRLP is the eth/66 response to GetReceipts. +// ReplyReceiptsRLP is the celo/67 (eth/66) response to GetReceipts. func (p *Peer) ReplyReceiptsRLP(id uint64, receipts []rlp.RawValue) error { - return p2p.Send(p.rw, ReceiptsMsg, ReceiptsRLPPacket66{ + return p2p.Send(p.rw, ReceiptsMsg, ReceiptsRLPPacket67{ RequestId: id, ReceiptsRLPPacket: receipts, }) @@ -328,7 +335,7 @@ p.Log().Debug("Fetching single header", "hash", hash) id := rand.Uint64()   requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id) - return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{ + return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket67{ RequestId: id, GetBlockHeadersPacket: &GetBlockHeadersPacket{ Origin: HashOrNumber{Hash: hash}, @@ -346,7 +353,7 @@ p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse) id := rand.Uint64()   requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id) - return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{ + return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket67{ RequestId: id, GetBlockHeadersPacket: &GetBlockHeadersPacket{ Origin: HashOrNumber{Hash: origin}, @@ -364,7 +371,7 @@ p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse) id := rand.Uint64()   requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id) - return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{ + return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket67{ RequestId: id, GetBlockHeadersPacket: &GetBlockHeadersPacket{ Origin: HashOrNumber{Number: origin}, @@ -382,7 +389,7 @@ p.Log().Debug("Fetching batch of block bodies", "count", len(hashes)) id := rand.Uint64()   requestTracker.Track(p.id, p.version, GetBlockBodiesMsg, BlockBodiesMsg, id) - return p2p.Send(p.rw, GetBlockBodiesMsg, &GetBlockBodiesPacket66{ + return p2p.Send(p.rw, GetBlockBodiesMsg, &GetBlockBodiesPacket67{ RequestId: id, GetBlockBodiesPacket: hashes, }) @@ -395,7 +402,7 @@ p.Log().Debug("Fetching batch of state data", "count", len(hashes)) id := rand.Uint64()   requestTracker.Track(p.id, p.version, GetNodeDataMsg, NodeDataMsg, id) - return p2p.Send(p.rw, GetNodeDataMsg, &GetNodeDataPacket66{ + return p2p.Send(p.rw, GetNodeDataMsg, &GetNodeDataPacket67{ RequestId: id, GetNodeDataPacket: hashes, }) @@ -407,7 +414,7 @@ p.Log().Debug("Fetching batch of receipts", "count", len(hashes)) id := rand.Uint64()   requestTracker.Track(p.id, p.version, GetReceiptsMsg, ReceiptsMsg, id) - return p2p.Send(p.rw, GetReceiptsMsg, &GetReceiptsPacket66{ + return p2p.Send(p.rw, GetReceiptsMsg, &GetReceiptsPacket67{ RequestId: id, GetReceiptsPacket: hashes, }) @@ -419,10 +426,25 @@ p.Log().Debug("Fetching batch of transactions", "count", len(hashes)) id := rand.Uint64()   requestTracker.Track(p.id, p.version, GetPooledTransactionsMsg, PooledTransactionsMsg, id) - return p2p.Send(p.rw, GetPooledTransactionsMsg, &GetPooledTransactionsPacket66{ + return p2p.Send(p.rw, GetPooledTransactionsMsg, &GetPooledTransactionsPacket67{ RequestId: id, GetPooledTransactionsPacket: hashes, }) +} + +func (p *Peer) ReadMsg() (p2p.Msg, error) { + msg, err := p.rw.ReadMsg() + if err != nil { + return msg, err + } + if msg.Size > maxMessageSize { + return msg, fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize) + } + return msg, nil +} + +func (p *Peer) PurposeIsSet(purpose p2p.PurposeFlag) bool { + return purpose == p2p.AnyPurpose || p.HasPurpose(purpose) }   // knownCache is a cache for known hashes.
diff --git go-ethereum/eth/protocols/eth/protocol_test.go celo/eth/protocols/eth/protocol_test.go index 56bd6c40a9d9133a4d093b069864dcd937b889ec..d4213a7286d97e747f7e2f90ff80483dad5d9015 100644 --- go-ethereum/eth/protocols/eth/protocol_test.go +++ celo/eth/protocols/eth/protocol_test.go @@ -70,46 +70,46 @@ } } }   -// TestEth66EmptyMessages tests encoding of empty eth66 messages -func TestEth66EmptyMessages(t *testing.T) { +// TestCelo67EmptyMessages tests encoding of empty celo67 (eth66) messages +func TestCelo67EmptyMessages(t *testing.T) { // All empty messages encodes to the same format want := common.FromHex("c4820457c0")   for i, msg := range []interface{}{ // Headers - GetBlockHeadersPacket66{1111, nil}, - BlockHeadersPacket66{1111, nil}, + GetBlockHeadersPacket67{1111, nil}, + BlockHeadersPacket67{1111, nil}, // Bodies - GetBlockBodiesPacket66{1111, nil}, - BlockBodiesPacket66{1111, nil}, - BlockBodiesRLPPacket66{1111, nil}, + GetBlockBodiesPacket67{1111, nil}, + BlockBodiesPacket67{1111, nil}, + BlockBodiesRLPPacket67{1111, nil}, // Node data - GetNodeDataPacket66{1111, nil}, - NodeDataPacket66{1111, nil}, + GetNodeDataPacket67{1111, nil}, + NodeDataPacket67{1111, nil}, // Receipts - GetReceiptsPacket66{1111, nil}, - ReceiptsPacket66{1111, nil}, + GetReceiptsPacket67{1111, nil}, + ReceiptsPacket67{1111, nil}, // Transactions - GetPooledTransactionsPacket66{1111, nil}, - PooledTransactionsPacket66{1111, nil}, - PooledTransactionsRLPPacket66{1111, nil}, + GetPooledTransactionsPacket67{1111, nil}, + PooledTransactionsPacket67{1111, nil}, + PooledTransactionsRLPPacket67{1111, nil},   // Headers - BlockHeadersPacket66{1111, BlockHeadersPacket([]*types.Header{})}, + BlockHeadersPacket67{1111, BlockHeadersPacket([]*types.Header{})}, // Bodies - GetBlockBodiesPacket66{1111, GetBlockBodiesPacket([]common.Hash{})}, - BlockBodiesPacket66{1111, BlockBodiesPacket([]*BlockBody{})}, - BlockBodiesRLPPacket66{1111, BlockBodiesRLPPacket([]rlp.RawValue{})}, + GetBlockBodiesPacket67{1111, GetBlockBodiesPacket([]common.Hash{})}, + BlockBodiesPacket67{1111, BlockBodiesPacket{}}, + BlockBodiesRLPPacket67{1111, BlockBodiesRLPPacket([]rlp.RawValue{})}, // Node data - GetNodeDataPacket66{1111, GetNodeDataPacket([]common.Hash{})}, - NodeDataPacket66{1111, NodeDataPacket([][]byte{})}, + GetNodeDataPacket67{1111, GetNodeDataPacket([]common.Hash{})}, + NodeDataPacket67{1111, NodeDataPacket([][]byte{})}, // Receipts - GetReceiptsPacket66{1111, GetReceiptsPacket([]common.Hash{})}, - ReceiptsPacket66{1111, ReceiptsPacket([][]*types.Receipt{})}, + GetReceiptsPacket67{1111, GetReceiptsPacket([]common.Hash{})}, + ReceiptsPacket67{1111, ReceiptsPacket([][]*types.Receipt{})}, // Transactions - GetPooledTransactionsPacket66{1111, GetPooledTransactionsPacket([]common.Hash{})}, - PooledTransactionsPacket66{1111, PooledTransactionsPacket([]*types.Transaction{})}, - PooledTransactionsRLPPacket66{1111, PooledTransactionsRLPPacket([]rlp.RawValue{})}, + GetPooledTransactionsPacket67{1111, GetPooledTransactionsPacket([]common.Hash{})}, + PooledTransactionsPacket67{1111, PooledTransactionsPacket([]*types.Transaction{})}, + PooledTransactionsRLPPacket67{1111, PooledTransactionsRLPPacket([]rlp.RawValue{})}, } { if have, _ := rlp.EncodeToBytes(msg); !bytes.Equal(have, want) { t.Errorf("test %d, type %T, have\n\t%x\nwant\n\t%x", i, msg, have, want) @@ -118,13 +118,13 @@ }   }   -// TestEth66Messages tests the encoding of all redefined eth66 messages -func TestEth66Messages(t *testing.T) { +// TestCelo67Messages tests the encoding of all redefined celo/67 (eth66) messages +func TestCelo67Messages(t *testing.T) {   // Some basic structs used during testing var ( header *types.Header - blockBody *BlockBody + blockBody *types.Body blockBodyRlp rlp.RawValue txs []*types.Transaction txRlps []rlp.RawValue @@ -135,9 +135,7 @@ err error ) header = &types.Header{ - Difficulty: big.NewInt(2222), Number: big.NewInt(3333), - GasLimit: 4444, GasUsed: 5555, Time: 6666, Extra: []byte{0x77, 0x88}, @@ -158,11 +156,11 @@ txRlps = append(txRlps, rlpdata) } } // init the block body data, both object and rlp form - blockBody = &BlockBody{ + blockBody = &types.Body{ Transactions: txs, - Uncles: []*types.Header{header}, } - blockBodyRlp, err = rlp.EncodeToBytes(blockBody) + blockBodyWithBlockHash := &blockBodyWithBlockHash{BlockBody: blockBody, BlockHash: header.TxHash} + blockBodyRlp, err = rlp.EncodeToBytes(blockBodyWithBlockHash) if err != nil { t.Fatal(err) } @@ -205,59 +203,59 @@ message interface{} want []byte }{ { - GetBlockHeadersPacket66{1111, &GetBlockHeadersPacket{HashOrNumber{hashes[0], 0}, 5, 5, false}}, + GetBlockHeadersPacket67{1111, &GetBlockHeadersPacket{HashOrNumber{hashes[0], 0}, 5, 5, false}}, common.FromHex("e8820457e4a000000000000000000000000000000000000000000000000000000000deadc0de050580"), }, { - GetBlockHeadersPacket66{1111, &GetBlockHeadersPacket{HashOrNumber{common.Hash{}, 9999}, 5, 5, false}}, + GetBlockHeadersPacket67{1111, &GetBlockHeadersPacket{HashOrNumber{common.Hash{}, 9999}, 5, 5, false}}, common.FromHex("ca820457c682270f050580"), }, { - BlockHeadersPacket66{1111, BlockHeadersPacket{header}}, - common.FromHex("f90202820457f901fcf901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"), + BlockHeadersPacket67{1111, BlockHeadersPacket{header}}, + common.FromHex("f901b1820457f901abf901a8a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000820d058215b3821a0a827788"), }, { - GetBlockBodiesPacket66{1111, GetBlockBodiesPacket(hashes)}, + GetBlockBodiesPacket67{1111, GetBlockBodiesPacket(hashes)}, common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"), }, { - BlockBodiesPacket66{1111, BlockBodiesPacket([]*BlockBody{blockBody})}, - common.FromHex("f902dc820457f902d6f902d3f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afbf901fcf901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"), + BlockBodiesPacket67{1111, BlockBodiesPacket{blockBodyWithBlockHash}}, + common.FromHex("f90100820457f8fbf8f9a00000000000000000000000000000000000000000000000000000000000000000f8d6f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afbc0c0"), }, { // Identical to non-rlp-shortcut version - BlockBodiesRLPPacket66{1111, BlockBodiesRLPPacket([]rlp.RawValue{blockBodyRlp})}, - common.FromHex("f902dc820457f902d6f902d3f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afbf901fcf901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"), + BlockBodiesRLPPacket67{1111, BlockBodiesRLPPacket([]rlp.RawValue{blockBodyRlp})}, + common.FromHex("f90100820457f8fbf8f9a00000000000000000000000000000000000000000000000000000000000000000f8d6f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afbc0c0"), }, { - GetNodeDataPacket66{1111, GetNodeDataPacket(hashes)}, + GetNodeDataPacket67{1111, GetNodeDataPacket(hashes)}, common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"), }, { - NodeDataPacket66{1111, NodeDataPacket(byteSlices)}, + NodeDataPacket67{1111, NodeDataPacket(byteSlices)}, common.FromHex("ce820457ca84deadc0de84feedbeef"), }, { - GetReceiptsPacket66{1111, GetReceiptsPacket(hashes)}, + GetReceiptsPacket67{1111, GetReceiptsPacket(hashes)}, common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"), }, { - ReceiptsPacket66{1111, ReceiptsPacket([][]*types.Receipt{receipts})}, + ReceiptsPacket67{1111, ReceiptsPacket([][]*types.Receipt{receipts})}, common.FromHex("f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"), }, { - ReceiptsRLPPacket66{1111, ReceiptsRLPPacket([]rlp.RawValue{receiptsRlp})}, + ReceiptsRLPPacket67{1111, ReceiptsRLPPacket([]rlp.RawValue{receiptsRlp})}, common.FromHex("f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"), }, { - GetPooledTransactionsPacket66{1111, GetPooledTransactionsPacket(hashes)}, + GetPooledTransactionsPacket67{1111, GetPooledTransactionsPacket(hashes)}, common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"), }, { - PooledTransactionsPacket66{1111, PooledTransactionsPacket(txs)}, + PooledTransactionsPacket67{1111, PooledTransactionsPacket(txs)}, common.FromHex("f8d7820457f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb"), }, { - PooledTransactionsRLPPacket66{1111, PooledTransactionsRLPPacket(txRlps)}, + PooledTransactionsRLPPacket67{1111, PooledTransactionsRLPPacket(txRlps)}, common.FromHex("f8d7820457f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb"), }, } {
diff --git go-ethereum/eth/protocols/eth/protocol.go celo/eth/protocols/eth/protocol.go index 2f556f99de26325ac11f450d2a539c58d8dcfdff..3a1593aa2cf8e04852388bdd38aba49663f2e70f 100644 --- go-ethereum/eth/protocols/eth/protocol.go +++ celo/eth/protocols/eth/protocol.go @@ -41,10 +41,6 @@ // ProtocolVersions are the supported versions of the `eth` protocol (first // is primary). var ProtocolVersions = []uint{ETH66}   -// protocolLengths are the number of implemented message corresponding to -// different protocol versions. -var protocolLengths = map[uint]uint64{ETH66: 17} - // maxMessageSize is the maximum cap on the size of a protocol message. const maxMessageSize = 10 * 1024 * 1024   @@ -124,8 +120,8 @@ Skip uint64 // Blocks to skip between consecutive headers Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis) }   -// GetBlockHeadersPacket66 represents a block header query over eth/66 -type GetBlockHeadersPacket66 struct { +// GetBlockHeadersPacket represents a block header query over celo/67 (eth/66) +type GetBlockHeadersPacket67 struct { RequestId uint64 *GetBlockHeadersPacket } @@ -169,8 +165,8 @@ // BlockHeadersPacket represents a block header response. type BlockHeadersPacket []*types.Header   -// BlockHeadersPacket represents a block header response over eth/66. -type BlockHeadersPacket66 struct { +// BlockHeadersPacket represents a block header response over celo/67 (eth/66). +type BlockHeadersPacket67 struct { RequestId uint64 BlockHeadersPacket } @@ -197,17 +193,22 @@ // GetBlockBodiesPacket represents a block body query. type GetBlockBodiesPacket []common.Hash   -// GetBlockBodiesPacket represents a block body query over eth/66. -type GetBlockBodiesPacket66 struct { +// GetBlockBodiesPacket represents a block body query over celo/67 (eth/66). +type GetBlockBodiesPacket67 struct { RequestId uint64 GetBlockBodiesPacket }   +type blockBodyWithBlockHash struct { + BlockHash common.Hash + BlockBody *types.Body +} + // BlockBodiesPacket is the network packet for block content distribution. -type BlockBodiesPacket []*BlockBody +type BlockBodiesPacket []*blockBodyWithBlockHash   -// BlockBodiesPacket is the network packet for block content distribution over eth/66. -type BlockBodiesPacket66 struct { +// BlockBodiesPacket is the network packet for block content distribution over celo/67 (eth/66). +type BlockBodiesPacket67 struct { RequestId uint64 BlockBodiesPacket } @@ -217,8 +218,8 @@ // where we already have them RLP-encoded, and thus can avoid the decode-encode // roundtrip. type BlockBodiesRLPPacket []rlp.RawValue   -// BlockBodiesRLPPacket66 is the BlockBodiesRLPPacket over eth/66 -type BlockBodiesRLPPacket66 struct { +// BlockBodiesRLPPacket67 is the BlockBodiesRLPPacket over celo/67 (eth/66) +type BlockBodiesRLPPacket67 struct { RequestId uint64 BlockBodiesRLPPacket } @@ -231,22 +232,28 @@ }   // Unpack retrieves the transactions and uncles from the range packet and returns // them in a split flat format that's more consistent with the internal data structures. -func (p *BlockBodiesPacket) Unpack() ([][]*types.Transaction, [][]*types.Header) { +func (p *BlockBodiesPacket) Unpack() ([]common.Hash, [][]*types.Transaction, []*types.Randomness, []*types.EpochSnarkData) { var ( - txset = make([][]*types.Transaction, len(*p)) - uncleset = make([][]*types.Header, len(*p)) + blockHashes = make([]common.Hash, len(*p)) + transactions = make([][]*types.Transaction, len(*p)) + randomness = make([]*types.Randomness, len(*p)) + epochSnarkData = make([]*types.EpochSnarkData, len(*p)) ) - for i, body := range *p { - txset[i], uncleset[i] = body.Transactions, body.Uncles + + for i, blockBodyWithBlockHash := range *p { + blockHashes[i] = blockBodyWithBlockHash.BlockHash + transactions[i] = blockBodyWithBlockHash.BlockBody.Transactions + randomness[i] = blockBodyWithBlockHash.BlockBody.Randomness + epochSnarkData[i] = blockBodyWithBlockHash.BlockBody.EpochSnarkData } - return txset, uncleset + return blockHashes, transactions, randomness, epochSnarkData }   // GetNodeDataPacket represents a trie node data query. type GetNodeDataPacket []common.Hash   -// GetNodeDataPacket represents a trie node data query over eth/66. -type GetNodeDataPacket66 struct { +// GetNodeDataPacket represents a trie node data query over celo/67 (eth/66). +type GetNodeDataPacket67 struct { RequestId uint64 GetNodeDataPacket } @@ -254,8 +261,8 @@ // NodeDataPacket is the network packet for trie node data distribution. type NodeDataPacket [][]byte   -// NodeDataPacket is the network packet for trie node data distribution over eth/66. -type NodeDataPacket66 struct { +// NodeDataPacket is the network packet for trie node data distribution over celo/67 (eth/66). +type NodeDataPacket67 struct { RequestId uint64 NodeDataPacket } @@ -263,8 +270,8 @@ // GetReceiptsPacket represents a block receipts query. type GetReceiptsPacket []common.Hash   -// GetReceiptsPacket represents a block receipts query over eth/66. -type GetReceiptsPacket66 struct { +// GetReceiptsPacket represents a block receipts query over celo/67 (eth/66). +type GetReceiptsPacket67 struct { RequestId uint64 GetReceiptsPacket } @@ -272,8 +279,8 @@ // ReceiptsPacket is the network packet for block receipts distribution. type ReceiptsPacket [][]*types.Receipt   -// ReceiptsPacket is the network packet for block receipts distribution over eth/66. -type ReceiptsPacket66 struct { +// ReceiptsPacket is the network packet for block receipts distribution over celo/67 (eth/66). +type ReceiptsPacket67 struct { RequestId uint64 ReceiptsPacket } @@ -281,8 +288,8 @@ // ReceiptsRLPPacket is used for receipts, when we already have it encoded type ReceiptsRLPPacket []rlp.RawValue   -// ReceiptsPacket66 is the eth-66 version of ReceiptsRLPPacket -type ReceiptsRLPPacket66 struct { +// ReceiptsPacket67 is the celo/67 (eth/66) version of ReceiptsRLPPacket +type ReceiptsRLPPacket67 struct { RequestId uint64 ReceiptsRLPPacket } @@ -293,7 +300,7 @@ // GetPooledTransactionsPacket represents a transaction query. type GetPooledTransactionsPacket []common.Hash   -type GetPooledTransactionsPacket66 struct { +type GetPooledTransactionsPacket67 struct { RequestId uint64 GetPooledTransactionsPacket } @@ -301,8 +308,8 @@ // PooledTransactionsPacket is the network packet for transaction distribution. type PooledTransactionsPacket []*types.Transaction   -// PooledTransactionsPacket is the network packet for transaction distribution over eth/66. -type PooledTransactionsPacket66 struct { +// PooledTransactionsPacket is the network packet for transaction distribution over celo/67 (eth/66). +type PooledTransactionsPacket67 struct { RequestId uint64 PooledTransactionsPacket } @@ -311,8 +318,8 @@ // PooledTransactionsPacket is the network packet for transaction distribution, used // in the cases we already have them in rlp-encoded form type PooledTransactionsRLPPacket []rlp.RawValue   -// PooledTransactionsRLPPacket66 is the eth/66 form of PooledTransactionsRLPPacket -type PooledTransactionsRLPPacket66 struct { +// PooledTransactionsRLPPacket67 is the celo/67 (eth/66) form of PooledTransactionsRLPPacket +type PooledTransactionsRLPPacket67 struct { RequestId uint64 PooledTransactionsRLPPacket }
diff --git go-ethereum/eth/protocols/eth/handler_test.go celo/eth/protocols/eth/handler_test.go index efafc86d61c45ca2a35e4e0c3a4895d56d170fe3..0712fab0dfea29d625277c8859cda49267a5efe9 100644 --- go-ethereum/eth/protocols/eth/handler_test.go +++ celo/eth/protocols/eth/handler_test.go @@ -23,7 +23,8 @@ "math/rand" "testing"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -69,9 +70,9 @@ Config: params.TestChainConfig, Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}}, }).MustCommit(db)   - chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, mockEngine.NewFaker(), vm.Config{}, nil, nil)   - bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, generator) + bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), mockEngine.NewFaker(), db, blocks, generator) if _, err := chain.InsertChain(bs); err != nil { panic(err) } @@ -110,7 +111,7 @@ panic("data processing tests should be done in the handler package") }   // Tests that block headers can be retrieved from a remote chain based on user queries. -func TestGetBlockHeaders66(t *testing.T) { testGetBlockHeaders(t, ETH66) } +func TestGetBlockHeaders67(t *testing.T) { testGetBlockHeaders(t, istanbul.Celo67) }   func testGetBlockHeaders(t *testing.T, protocol uint) { t.Parallel() @@ -253,11 +254,11 @@ for _, hash := range tt.expect { headers = append(headers, backend.chain.GetBlockByHash(hash).Header()) } // Send the hash request and verify the response - p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{ + p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket67{ RequestId: 123, GetBlockHeadersPacket: tt.query, }) - if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{ + if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket67{ RequestId: 123, BlockHeadersPacket: headers, }); err != nil { @@ -267,24 +268,24 @@ // If the test used number origins, repeat with hashes as the too if tt.query.Origin.Hash == (common.Hash{}) { if origin := backend.chain.GetBlockByNumber(tt.query.Origin.Number); origin != nil { tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0 - - p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{ + p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket67{ RequestId: 456, GetBlockHeadersPacket: tt.query, }) - if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{ + if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket67{ RequestId: 456, BlockHeadersPacket: headers, }); err != nil { t.Errorf("test %d: headers mismatch: %v", i, err) } + } } } }   // Tests that block contents can be retrieved from a remote chain based on their hashes. -func TestGetBlockBodies66(t *testing.T) { testGetBlockBodies(t, ETH66) } +func TestGetBlockBodies67(t *testing.T) { testGetBlockBodies(t, istanbul.Celo67) }   func testGetBlockBodies(t *testing.T, protocol uint) { t.Parallel() @@ -327,7 +328,7 @@ for i, tt := range tests { // Collect the hashes to request, and the response to expectva var ( hashes []common.Hash - bodies []*BlockBody + bodiesAndBlockHashes BlockBodiesPacket seen = make(map[int64]bool) ) for j := 0; j < tt.random; j++ { @@ -338,8 +339,12 @@ seen[num] = true   block := backend.chain.GetBlockByNumber(uint64(num)) hashes = append(hashes, block.Hash()) - if len(bodies) < tt.expected { - bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) + if len(bodiesAndBlockHashes) < tt.expected { + bhEntry := &blockBodyWithBlockHash{BlockHash: block.Hash(), + BlockBody: &types.Body{Transactions: block.Transactions(), + Randomness: block.Randomness(), + EpochSnarkData: block.EpochSnarkData()}} + bodiesAndBlockHashes = append(bodiesAndBlockHashes, bhEntry) } break } @@ -347,19 +352,23 @@ } } for j, hash := range tt.explicit { hashes = append(hashes, hash) - if tt.available[j] && len(bodies) < tt.expected { + if tt.available[j] && len(bodiesAndBlockHashes) < tt.expected { block := backend.chain.GetBlockByHash(hash) - bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) + bhEntry := &blockBodyWithBlockHash{BlockHash: block.Hash(), + BlockBody: &types.Body{Transactions: block.Transactions(), + Randomness: block.Randomness(), + EpochSnarkData: block.EpochSnarkData()}} + bodiesAndBlockHashes = append(bodiesAndBlockHashes, bhEntry) } } // Send the hash request and verify the response - p2p.Send(peer.app, GetBlockBodiesMsg, GetBlockBodiesPacket66{ + p2p.Send(peer.app, GetBlockBodiesMsg, GetBlockBodiesPacket67{ RequestId: 123, GetBlockBodiesPacket: hashes, }) - if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, BlockBodiesPacket66{ + if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, BlockBodiesPacket67{ RequestId: 123, - BlockBodiesPacket: bodies, + BlockBodiesPacket: bodiesAndBlockHashes, }); err != nil { t.Errorf("test %d: bodies mismatch: %v", i, err) } @@ -367,7 +376,7 @@ } }   // Tests that the state trie nodes can be retrieved based on hashes. -func TestGetNodeData66(t *testing.T) { testGetNodeData(t, ETH66) } +func TestGetNodeData67(t *testing.T) { testGetNodeData(t, istanbul.Celo67) }   func testGetNodeData(t *testing.T, protocol uint) { t.Parallel() @@ -384,27 +393,19 @@ generator := func(i int, block *core.BlockGen) { switch i { case 0: // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(10_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(10_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, testKey) block.AddTx(tx) case 1: // In block 2, the test bank sends some more ether to account #1. // acc1Addr passes it on to account #2. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testKey) - tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, acc1Key) + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, testKey) + tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, acc1Key) block.AddTx(tx1) block.AddTx(tx2) case 2: // Block 3 is empty but was mined by account #2. block.SetCoinbase(acc2Addr) - block.SetExtra([]byte("yeehaw")) - case 3: - // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). - b2 := block.PrevBlock(1).Header() - b2.Extra = []byte("foo") - block.AddUncle(b2) - b3 := block.PrevBlock(2).Header() - b3.Extra = []byte("foo") - block.AddUncle(b3) + block.SetExtra(core.CreateEmptyIstanbulExtra([]byte("yeehaw"))) } } // Assemble the test environment @@ -424,8 +425,7 @@ hashes = append(hashes, common.BytesToHash(key)) } } it.Release() - - p2p.Send(peer.app, GetNodeDataMsg, GetNodeDataPacket66{ + p2p.Send(peer.app, GetNodeDataMsg, GetNodeDataPacket67{ RequestId: 123, GetNodeDataPacket: hashes, }) @@ -438,7 +438,7 @@ t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, NodeDataMsg) } var ( data [][]byte - res NodeDataPacket66 + res NodeDataPacket67 ) if err := msg.Decode(&res); err != nil { t.Fatalf("failed to decode response node data: %v", err) @@ -474,7 +474,7 @@ } }   // Tests that the transaction receipts can be retrieved based on hashes. -func TestGetBlockReceipts66(t *testing.T) { testGetBlockReceipts(t, ETH66) } +func TestGetBlockReceipts67(t *testing.T) { testGetBlockReceipts(t, istanbul.Celo67) }   func testGetBlockReceipts(t *testing.T, protocol uint) { t.Parallel() @@ -491,27 +491,19 @@ generator := func(i int, block *core.BlockGen) { switch i { case 0: // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(10_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(10_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, testKey) block.AddTx(tx) case 1: // In block 2, the test bank sends some more ether to account #1. // acc1Addr passes it on to account #2. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testKey) - tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, acc1Key) + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, testKey) + tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, acc1Key) block.AddTx(tx1) block.AddTx(tx2) case 2: // Block 3 is empty but was mined by account #2. block.SetCoinbase(acc2Addr) - block.SetExtra([]byte("yeehaw")) - case 3: - // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). - b2 := block.PrevBlock(1).Header() - b2.Extra = []byte("foo") - block.AddUncle(b2) - b3 := block.PrevBlock(2).Header() - b3.Extra = []byte("foo") - block.AddUncle(b3) + block.SetExtra(core.CreateEmptyIstanbulExtra([]byte("yeehaw"))) } } // Assemble the test environment @@ -533,11 +525,11 @@ hashes = append(hashes, block.Hash()) receipts = append(receipts, backend.chain.GetReceiptsByHash(block.Hash())) } // Send the hash request and verify the response - p2p.Send(peer.app, GetReceiptsMsg, GetReceiptsPacket66{ + p2p.Send(peer.app, GetReceiptsMsg, GetReceiptsPacket67{ RequestId: 123, GetReceiptsPacket: hashes, }) - if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, ReceiptsPacket66{ + if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, ReceiptsPacket67{ RequestId: 123, ReceiptsPacket: receipts, }); err != nil {
diff --git go-ethereum/eth/tracers/tracer_test.go celo/eth/tracers/tracer_test.go index c9ee08a7e146e764ba377d2e137fee38cd08d3c5..b534ec32b603feae7006ea92698a60bc57325eaf 100644 --- go-ethereum/eth/tracers/tracer_test.go +++ celo/eth/tracers/tracer_test.go @@ -151,7 +151,8 @@ tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", new(Context)) if err != nil { t.Fatal(err) } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, params.IstanbulTestChainConfig, vm.Config{Debug: true, Tracer: tracer}) scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } @@ -207,10 +208,10 @@ } }   func TestIsPrecompile(t *testing.T) { - chaincfg := &params.ChainConfig{ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: false, EIP150Block: big.NewInt(0), EIP150Hash: common.Hash{}, EIP155Block: big.NewInt(0), EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(100), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(200), MuirGlacierBlock: big.NewInt(0), BerlinBlock: big.NewInt(300), LondonBlock: big.NewInt(0), CatalystBlock: nil, Ethash: new(params.EthashConfig), Clique: nil} + chaincfg := &params.ChainConfig{ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: false, EIP150Block: big.NewInt(0), EIP150Hash: common.Hash{}, EIP155Block: big.NewInt(0), EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(100), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(200), EspressoBlock: big.NewInt(0)} chaincfg.ByzantiumBlock = big.NewInt(100) chaincfg.IstanbulBlock = big.NewInt(200) - chaincfg.BerlinBlock = big.NewInt(300) + chaincfg.EspressoBlock = big.NewInt(300) txCtx := vm.TxContext{GasPrice: big.NewInt(100000)} tracer, err := New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) if err != nil {
diff --git go-ethereum/eth/tracers/tracers_test.go celo/eth/tracers/tracers_test.go index 933f439b616f3781355ce812ccbfcaa4ff8cfdfb..0c3e67cab882088b790ee3f11a310127f5c48982 100644 --- go-ethereum/eth/tracers/tracers_test.go +++ celo/eth/tracers/tracers_test.go @@ -1,8 +1,7 @@ // Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by +// The go-ethereum library is free software: you can redistribute it and/or modif// it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // @@ -30,10 +29,12 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/contracts/testutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/vmcontext" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -55,11 +56,9 @@ delete genesis.gasUsed; delete genesis.logsBloom; delete genesis.parentHash; delete genesis.receiptsRoot; - delete genesis.sha3Uncles; delete genesis.size; delete genesis.transactions; delete genesis.transactionsRoot; - delete genesis.uncles;   genesis.gasLimit = genesis.gasLimit.toString(); genesis.number = genesis.number.toString(); @@ -79,7 +78,6 @@ console.log(JSON.stringify({ genesis: genesis, context: { number: block.number.toString(), - difficulty: block.difficulty, timestamp: block.timestamp.toString(), gasLimit: block.gasLimit.toString(), miner: block.miner, @@ -121,8 +119,9 @@ Result *callTrace `json:"result"` }   func TestPrestateTracerCreate2(t *testing.T) { - unsignedTx := types.NewTransaction(1, common.HexToAddress("0x00000000000000000000000000000000deadbeef"), - new(big.Int), 5000000, big.NewInt(1), []byte{}) + celoMock := testutil.NewCeloMock() + + unsignedTx := types.NewTransaction(1, common.HexToAddress("0x00000000000000000000000000000000deadbeef"), new(big.Int), 5000000, big.NewInt(1), nil, nil, nil, []byte{})   privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) if err != nil { @@ -148,13 +147,11 @@ Origin: origin, GasPrice: big.NewInt(1), } context := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, + CanTransfer: vmcontext.CanTransfer, + Transfer: vmcontext.Transfer, Coinbase: common.Address{}, BlockNumber: new(big.Int).SetUint64(8000000), Time: new(big.Int).SetUint64(5), - Difficulty: big.NewInt(0x30000), - GasLimit: uint64(6000000), } alloc := core.GenesisAlloc{}   @@ -177,13 +174,15 @@ tracer, err := New("prestateTracer", new(Context)) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) + vmConfig := vm.Config{Debug: true, Tracer: tracer} + evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vmConfig)   msg, err := tx.AsMessage(signer, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + + st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), celoMock.Runner, nil) if _, err = st.TransitionDb(); err != nil { t.Fatalf("failed to execute transaction: %v", err) } @@ -201,6 +200,72 @@ t.Fatalf("Expected 0x60f3f640a8508fc6a86d45df051962668e1e8ac7 in result") } }   +func TestPrestateTracerTransfer(t *testing.T) { + celoMock := testutil.NewCeloMock() + + toAddr := "0x00000000000000000000000000000000deadbeef" + unsignedTx := types.NewTransaction(1, common.HexToAddress(toAddr), new(big.Int), 5000000, big.NewInt(1), nil, nil, nil, []byte{}) + + privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) + if err != nil { + t.Fatalf("err %v", err) + } + signer := types.NewEIP155Signer(big.NewInt(1)) + tx, err := types.SignTx(unsignedTx, signer, privateKeyECDSA) + if err != nil { + t.Fatalf("err %v", err) + } + origin, _ := signer.Sender(tx) + txContext := vm.TxContext{ + Origin: origin, + GasPrice: big.NewInt(1), + } + context := vm.BlockContext{ + CanTransfer: vmcontext.CanTransfer, + Transfer: vmcontext.Transfer, + Coinbase: common.Address{}, + BlockNumber: new(big.Int).SetUint64(8000000), + Time: new(big.Int).SetUint64(5), + } + alloc := core.GenesisAlloc{} + alloc[origin] = core.GenesisAccount{ + Nonce: 1, + Code: []byte{}, + Balance: big.NewInt(500000000000000), + } + _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false) + + // Create the tracer, the EVM environment and run it + tracer, err := New("prestateTracer", new(Context)) + if err != nil { + t.Fatalf("failed to create prestate tracer: %v", err) + } + vmConfig := vm.Config{Debug: true, Tracer: tracer} + evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vmConfig) + + msg, err := tx.AsMessage(signer, nil) + if err != nil { + t.Fatalf("failed to prepare transaction for tracing: %v", err) + } + + st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), celoMock.Runner, nil) + if _, err = st.TransitionDb(); err != nil { + t.Fatalf("failed to execute transaction: %v", err) + } + // Retrieve the trace result and compare against the etalon + res, err := tracer.GetResult() + if err != nil { + t.Fatalf("failed to retrieve trace result: %v", err) + } + ret := make(map[string]interface{}) + if err := json.Unmarshal(res, &ret); err != nil { + t.Fatalf("failed to unmarshal trace result: %v", err) + } + if _, has := ret[toAddr]; !has { + t.Fatalf("Expected %s in result", toAddr) + } +} + // Iterates over all the input-output datasets in the tracer test harness and // runs the JavaScript tracers against them. func TestCallTracerLegacy(t *testing.T) { @@ -208,6 +273,7 @@ testCallTracer("callTracerLegacy", "call_tracer_legacy", t) }   func testCallTracer(tracer string, dirPath string, t *testing.T) { + celoMock := testutil.NewCeloMock() files, err := ioutil.ReadDir(filepath.Join("testdata", dirPath)) if err != nil { t.Fatalf("failed to retrieve tracer test suite: %v", err) @@ -216,6 +282,7 @@ for _, file := range files { if !strings.HasSuffix(file.Name(), ".json") { continue } + file := file // capture range variable t.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(t *testing.T) { t.Parallel() @@ -241,13 +308,11 @@ Origin: origin, GasPrice: tx.GasPrice(), } context := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, + CanTransfer: vmcontext.CanTransfer, + Transfer: vmcontext.Transfer, Coinbase: test.Context.Miner, BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), Time: new(big.Int).SetUint64(uint64(test.Context.Time)), - Difficulty: (*big.Int)(test.Context.Difficulty), - GasLimit: uint64(test.Context.GasLimit), } _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)   @@ -262,7 +327,7 @@ msg, err := tx.AsMessage(signer, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), celoMock.Runner, nil) if _, err = st.TransitionDb(); err != nil { t.Fatalf("failed to execute transaction: %v", err) } @@ -310,6 +375,7 @@ return reflect.DeepEqual(xTrace, yTrace) }   func BenchmarkTransactionTrace(b *testing.B) { + celoMock := testutil.NewCeloMock() key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") from := crypto.PubkeyToAddress(key.PublicKey) gas := uint64(1000000) // 1M gas @@ -330,14 +396,13 @@ Origin: from, GasPrice: tx.GasPrice(), } context := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, + CanTransfer: vmcontext.CanTransfer, + Transfer: vmcontext.Transfer, Coinbase: common.Address{}, BlockNumber: new(big.Int).SetUint64(uint64(5)), Time: new(big.Int).SetUint64(uint64(5)), - Difficulty: big.NewInt(0xffffffff), - GasLimit: gas, } + alloc := core.GenesisAlloc{} // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns // the address @@ -364,7 +429,7 @@ //DisableStorage: true, //EnableMemory: false, //EnableReturnData: false, }) - evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer, nil) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) @@ -374,7 +439,7 @@ b.ReportAllocs()   for i := 0; i < b.N; i++ { snap := statedb.Snapshot() - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), celoMock.Runner, nil) _, err = st.TransitionDb() if err != nil { b.Fatal(err) @@ -428,13 +493,9 @@ Origin: origin, GasPrice: tx.GasPrice(), } context := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, Coinbase: test.Context.Miner, BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), Time: new(big.Int).SetUint64(uint64(test.Context.Time)), - Difficulty: (*big.Int)(test.Context.Difficulty), - GasLimit: uint64(test.Context.GasLimit), } _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)   @@ -449,7 +510,7 @@ b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { snap := statedb.Snapshot() - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), nil, nil) if _, err = st.TransitionDb(); err != nil { b.Fatalf("failed to execute transaction: %v", err) }
diff --git go-ethereum/eth/tracers/api_test.go celo/eth/tracers/api_test.go index b5aa5dcd1550e6d4422323444aee157eaf29e8a5..1f05c8a7e61e652f482b885147ee604886c364ff 100644 --- go-ethereum/eth/tracers/api_test.go +++ celo/eth/tracers/api_test.go @@ -32,7 +32,8 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/contracts/testutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -59,9 +60,14 @@ chain *core.BlockChain }   func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend { + chainConfig := params.TestChainConfig + if gspec.Config != nil { + chainConfig = gspec.Config + } + chainConfig.Faker = true backend := &testBackend{ - chainConfig: params.TestChainConfig, - engine: ethash.NewFaker(), + chainConfig: chainConfig, + engine: mockEngine.NewFaker(), chaindb: rawdb.NewMemoryDatabase(), } // Generate blocks for testing @@ -122,6 +128,10 @@ } return tx, hash, blockNumber, index, nil }   +func (b *testBackend) RPCGasInflationRate() float64 { + return 1 +} + func (b *testBackend) RPCGasCap() uint64 { return 25000000 } @@ -146,34 +156,39 @@ } return statedb, nil }   -func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { +func (b *testBackend) NewEVMRunner(header *types.Header, state vm.StateDB) vm.EVMRunner { + return testutil.NewMockEVMRunner() +} + +func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, vm.EVMRunner, *state.StateDB, error) { parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { - return nil, vm.BlockContext{}, nil, errBlockNotFound + return nil, vm.BlockContext{}, nil, nil, errBlockNotFound } statedb, err := b.chain.StateAt(parent.Root()) if err != nil { - return nil, vm.BlockContext{}, nil, errStateNotFound + return nil, vm.BlockContext{}, nil, nil, errStateNotFound } if txIndex == 0 && len(block.Transactions()) == 0 { - return nil, vm.BlockContext{}, statedb, nil + return nil, vm.BlockContext{}, nil, statedb, nil } // Recompute transactions up to the target index. signer := types.MakeSigner(b.chainConfig, block.Number()) for idx, tx := range block.Transactions() { - msg, _ := tx.AsMessage(signer, block.BaseFee()) + msg, _ := tx.AsMessage(signer, nil) txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), b.chain, nil) + vmRunner := b.chain.NewEVMRunner(block.Header(), statedb) if idx == txIndex { - return msg, context, statedb, nil + return msg, context, vmRunner, statedb, nil } vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { - return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner, nil); err != nil { + return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) } - return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) + return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) }   func TestTraceCall(t *testing.T) { @@ -192,7 +207,7 @@ api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, accounts[0].key) b.AddTx(tx) }))   @@ -322,7 +337,7 @@ api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, accounts[0].key) b.AddTx(tx) })) randomAccounts, tracer := newAccounts(3), "callTracer" @@ -369,7 +384,7 @@ }, config: &TraceCallConfig{ Tracer: &tracer, }, - expectErr: core.ErrInsufficientFunds, + expectErr: core.ErrInsufficientFundsForTransfer, expect: nil, }, // Successful simple contract call @@ -412,7 +427,7 @@ To: randomAccounts[2].addr, Input: hexutil.Bytes(common.Hex2Bytes("8381f58a")), Output: hexutil.Bytes(common.BigToHash(big.NewInt(123)).Bytes()), Gas: newRPCUint64(24978936), - GasUsed: newRPCUint64(2283), + GasUsed: newRPCUint64(383), // TODO ethereum cost 2283, check if this is right Value: (*hexutil.Big)(big.NewInt(0)), }, }, @@ -462,7 +477,7 @@ api := NewAPI(newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, accounts[0].key) b.AddTx(tx) target = tx.Hash() })) @@ -480,6 +495,119 @@ t.Error("Transaction tracing result is different") } }   +// Regression test for debug get/set logic in the EVM & Interpreter. +// Include registry to ensure that Celo-specific EVM Call's within +// Tobin Tax and fee distribution logic are triggered. +func TestTraceTransactionWithRegistryDeployed(t *testing.T) { + t.Parallel() + + // Initialize test accounts + accounts := newAccounts(2) + genesis := &core.Genesis{Alloc: core.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(params.Ether)}, + accounts[1].addr: {Balance: big.NewInt(params.Ether)}, + common.HexToAddress("0xce10"): { // Registry Proxy + Code: testutil.RegistryProxyOpcodes, + Storage: map[common.Hash]common.Hash{ + common.HexToHash("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"): common.HexToHash("0xce11"), // Registry Implementation + common.HexToHash("0x91646b8507bf2e54d7c3de9155442ba111546b81af1cbdd1f68eeb6926b98d58"): common.HexToHash("0xd023"), // Governance Proxy + }, + Balance: big.NewInt(0), + }, + common.HexToAddress("0xce11"): { // Registry Implementation + Code: testutil.RegistryOpcodes, + Balance: big.NewInt(0), + }, + }} + + target := common.Hash{} + signer := types.HomesteadSigner{} + api := NewAPI(newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { + // Transfer from account[0] to account[1] + // value: 1000 wei + // fee: 0 wei + tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, accounts[0].key) + b.AddTx(tx) + target = tx.Hash() + })) + result, err := api.TraceTransaction(context.Background(), target, nil) + if err != nil { + t.Errorf("Failed to trace transaction %v", err) + } + if !reflect.DeepEqual(result, &ethapi.ExecutionResult{ + Gas: params.TxGas, + Failed: false, + ReturnValue: "", + StructLogs: []ethapi.StructLogRes{}, + }) { + t.Error("Transaction tracing result is different") + } +} + +// Use the callTracer to trace a native CELO transfer after the +// registry has been deployed, as above. +func TestCallTraceTransactionNativeTransfer(t *testing.T) { + t.Parallel() + + // Initialize test accounts + accounts := newAccounts(2) + genesis := &core.Genesis{Alloc: core.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(params.Ether)}, + accounts[1].addr: {Balance: big.NewInt(params.Ether)}, + common.HexToAddress("0xce10"): { // Registry Proxy + Code: testutil.RegistryProxyOpcodes, + Storage: map[common.Hash]common.Hash{ + // Hashes represent the storage slot for Registry.sol's `registry` mapping + // which is stored in the RegistryProxy's storage. + // Hashes are computed by taking: keccack(packed(config.GoldTokenRegistryId, 1)), + // where 1 is the storage offset) + common.HexToHash("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"): common.HexToHash("0xce11"), // Registry Implementation + common.HexToHash("0x91646b8507bf2e54d7c3de9155442ba111546b81af1cbdd1f68eeb6926b98d58"): common.HexToHash("0xd023"), // Governance Proxy + }, + Balance: big.NewInt(0), + }, + common.HexToAddress("0xce11"): { // Registry Implementation + Code: testutil.RegistryOpcodes, + Balance: big.NewInt(0), + }, + }} + + target := common.Hash{} + signer := types.HomesteadSigner{} + transferVal := big.NewInt(1000) + api := NewAPI(newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { + // Transfer from account[0] to account[1] + // value: 1000 wei + // fee: 0 wei + tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, transferVal, params.TxGas, nil, nil, nil, nil, nil), signer, accounts[0].key) + b.AddTx(tx) + target = tx.Hash() + })) + tracerStr := "callTracer" + result, err := api.TraceTransaction(context.Background(), target, &TraceConfig{Tracer: &tracerStr}) + if err != nil { + t.Errorf("Failed to trace transaction %v", err) + } + + ret := new(callTrace) + if err := json.Unmarshal(result.(json.RawMessage), ret); err != nil { + t.Fatalf("failed to unmarshal trace result: %v", err) + } + expectedTrace := &callTrace{ + Type: "CALL", + From: accounts[0].addr, + To: accounts[1].addr, + Input: hexutil.Bytes(common.Hex2Bytes("0x")), + Output: hexutil.Bytes(common.Hex2Bytes("0x")), + Gas: newRPCUint64(0), + GasUsed: newRPCUint64(0), + Value: (*hexutil.Big)(transferVal), + } + if !jsonEqual(ret, expectedTrace) { + t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", ret, expectedTrace) + } +} + func TestTraceBlock(t *testing.T) { t.Parallel()   @@ -496,7 +624,7 @@ api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, accounts[0].key) b.AddTx(tx) }))   @@ -588,6 +716,82 @@ if !reflect.DeepEqual(result, testspec.expect) { t.Errorf("Result mismatch, want %v, get %v", testspec.expect, result) } } + } +} + +// Regression test for https://github.com/ethereum/go-ethereum/issues/2002 +// The tracer module didn't correctly calculate gas prices when EIP1559 style +// transactions are used. +func TestTraceBlockWithEIP1559Tx(t *testing.T) { + // Initialize test accounts + accounts := newAccounts(2) + genesis := &core.Genesis{ + Config: params.IstanbulEHFTestChainConfig, + Alloc: core.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(231001)}, + accounts[1].addr: {Balance: common.Big0}, + common.HexToAddress("0xce10"): { // Registry Proxy + Code: testutil.RegistryProxyOpcodes, + Storage: map[common.Hash]common.Hash{ + common.HexToHash("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"): common.HexToHash("0xce11"), // Registry Implementation + common.HexToHash("0x91646b8507bf2e54d7c3de9155442ba111546b81af1cbdd1f68eeb6926b98d58"): common.HexToHash("0xd023"), // Governance Proxy + }, + Balance: big.NewInt(0), + }, + common.HexToAddress("0xce11"): { // Registry Implementation + Code: testutil.RegistryOpcodes, + Balance: big.NewInt(0), + }, + }, + } + + api := NewAPI(newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { + // The block base fee is mocked to be 3 + // Two transactions are build, so that the correct gas price is 5 (base fee of 3 + tip of 2), but the + // incorrect calculation leads to a gas price of 6. + // The account balance is chosen in a way that the second transaction won't be able to execute if the + // calculation is wrong. + + bf := core.MockSysContractCallCtx().GetGasPriceMinimum(nil) + tip := big.NewInt(2) + cap := new(big.Int).Set(common.Big1) + cap = cap.Add(cap, tip).Add(cap, bf) + + txdata1 := types.NewTx(&types.DynamicFeeTx{ + ChainID: b.Config().ChainID, + Nonce: 0, + GasTipCap: tip, + GasFeeCap: cap, + Gas: 21_000, + To: &accounts[1].addr, + }) + tx1, _ := types.SignTx( + txdata1, + types.LatestSignerForChainID(b.Config().ChainID), + accounts[0].key, + ) + b.AddTx(tx1) + + txdata2 := types.NewTx(&types.DynamicFeeTx{ + ChainID: b.Config().ChainID, + Nonce: 1, + GasTipCap: tip, + GasFeeCap: cap, + Gas: 21_000, + To: &accounts[1].addr, + }) + tx2, _ := types.SignTx( + txdata2, + types.LatestSignerForChainID(b.Config().ChainID), + accounts[0].key, + ) + b.AddTx(tx2) + })) + + // Run tracing, this should not throw + _, err := api.TraceBlockByNumber(context.Background(), rpc.LatestBlockNumber, nil) + if err != nil { + t.Errorf("Expect no error, get %v", err) } }
diff --git go-ethereum/eth/tracers/api.go celo/eth/tracers/api.go index 8554d32b39bdb259db13c5ec4c5e3fc07904f9a0..b3fae61165b3e040a2c20b4f8ce3045d43ec825c 100644 --- go-ethereum/eth/tracers/api.go +++ celo/eth/tracers/api.go @@ -23,6 +23,7 @@ "context" "errors" "fmt" "io/ioutil" + "math/big" "os" "runtime" "sync" @@ -68,7 +69,8 @@ ChainConfig() *params.ChainConfig Engine() consensus.Engine ChainDb() ethdb.Database StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) - StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) + StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, vm.EVMRunner, *state.StateDB, error) + NewEVMRunner(*types.Header, vm.StateDB) vm.EVMRunner }   // API is the collection of tracing APIs exposed over the private debugging endpoint. @@ -105,6 +107,18 @@ } return header }   +func (context *chainContext) GetHeaderByNumber(headerNumber uint64) *types.Header { + header, err := context.api.backend.HeaderByNumber(context.ctx, rpc.BlockNumber(headerNumber)) + if err != nil { + return nil + } + return header +} + +func (context *chainContext) Config() *params.ChainConfig { + return context.api.backend.ChainConfig() +} + // chainContext construts the context reader which is used by the evm for reading // the necessary chain context. func (api *API) chainContext(ctx context.Context) core.ChainContext { @@ -262,15 +276,23 @@ // Fetch and execute the next block trace tasks for task := range tasks { signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number()) blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil) + + isEspresso := api.backend.ChainConfig().IsEspresso(blockCtx.BlockNumber) + var sysCtx *core.SysContractCallCtx + if isEspresso { + sysCtx = core.NewSysContractCallCtx(task.block.Header(), task.statedb, api.backend) + } // Trace all the transactions contained within for i, tx := range task.block.Transactions() { - msg, _ := tx.AsMessage(signer, task.block.BaseFee()) + baseFee := getBaseFee(isEspresso, sysCtx, tx.FeeCurrency()) + msg, _ := tx.AsMessage(signer, baseFee) txctx := &Context{ BlockHash: task.block.Hash(), TxIndex: i, TxHash: tx.Hash(), } - res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config) + vmRunner := api.backend.NewEVMRunner(task.block.Header(), task.statedb) + res, err := api.traceTx(localctx, msg, txctx, blockCtx, vmRunner, task.statedb, sysCtx, config) if err != nil { task.results[i] = &txTraceResult{Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) @@ -507,14 +529,21 @@ chainConfig = api.backend.ChainConfig() vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) ) + isEspresso := api.backend.ChainConfig().IsEspresso(block.Number()) + var sysCtx *core.SysContractCallCtx + if isEspresso { + sysCtx = core.NewSysContractCallCtx(block.Header(), statedb, api.backend) + } + vmRunner := api.backend.NewEVMRunner(block.Header(), statedb) for i, tx := range block.Transactions() { var ( - msg, _ = tx.AsMessage(signer, block.BaseFee()) + baseFee = getBaseFee(isEspresso, sysCtx, tx.FeeCurrency()) + msg, _ = tx.AsMessage(signer, baseFee) txContext = core.NewEVMTxContext(msg) vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{}) ) statedb.Prepare(tx.Hash(), i) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), vmRunner, sysCtx); err != nil { log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) // We intentionally don't return the error here: if we do, then the RPC server will not // return the roots. Most likely, the caller already knows that a certain transaction fails to @@ -576,19 +605,27 @@ threads = len(txs) } blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) blockHash := block.Hash() + + isEspresso := api.backend.ChainConfig().IsEspresso(block.Number()) + var sysCtx *core.SysContractCallCtx + if isEspresso { + sysCtx = core.NewSysContractCallCtx(block.Header(), statedb, api.backend) + } for th := 0; th < threads; th++ { pend.Add(1) go func() { defer pend.Done() // Fetch and execute the next transaction trace tasks for task := range jobs { - msg, _ := txs[task.index].AsMessage(signer, block.BaseFee()) + baseFee := getBaseFee(isEspresso, sysCtx, txs[task.index].FeeCurrency()) + msg, _ := txs[task.index].AsMessage(signer, baseFee) txctx := &Context{ BlockHash: blockHash, TxIndex: task.index, TxHash: txs[task.index].Hash(), } - res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) + vmRunner := api.backend.NewEVMRunner(block.Header(), task.statedb) + res, err := api.traceTx(ctx, msg, txctx, blockCtx, vmRunner, task.statedb, sysCtx, config) if err != nil { results[task.index] = &txTraceResult{Error: err.Error()} continue @@ -597,6 +634,7 @@ results[task.index] = &txTraceResult{Result: res} } }() } + vmRunner := api.backend.NewEVMRunner(block.Header(), statedb) // Feed the transactions into the tracers and return var failed error for i, tx := range txs { @@ -604,10 +642,11 @@ // Send the trace task over for execution jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}   // Generate the next state snapshot fast without tracing - msg, _ := tx.AsMessage(signer, block.BaseFee()) + baseFee := getBaseFee(isEspresso, sysCtx, tx.FeeCurrency()) + msg, _ := tx.AsMessage(signer, baseFee) statedb.Prepare(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), vmRunner, sysCtx); err != nil { failed = err break } @@ -677,19 +716,25 @@ // Therefore, it's perfectly valid to specify `"futureForkBlock": 0`, to enable `futureFork`   if config != nil && config.Overrides != nil { // Copy the config, to not screw up the main config - // Note: the Clique-part is _not_ deep copied chainConfigCopy := new(params.ChainConfig) *chainConfigCopy = *chainConfig chainConfig = chainConfigCopy - if berlin := config.LogConfig.Overrides.BerlinBlock; berlin != nil { - chainConfig.BerlinBlock = berlin + if E := config.LogConfig.Overrides.EspressoBlock; E != nil { + chainConfig.EspressoBlock = E canon = false } } + + isEspresso := api.backend.ChainConfig().IsEspresso(block.Number()) + var sysCtx *core.SysContractCallCtx + if isEspresso { + sysCtx = core.NewSysContractCallCtx(block.Header(), statedb, api.backend) + } for i, tx := range block.Transactions() { - // Prepare the trasaction for un-traced execution + // Prepare the transaction for un-traced execution var ( - msg, _ = tx.AsMessage(signer, block.BaseFee()) + baseFee = getBaseFee(isEspresso, sysCtx, tx.FeeCurrency()) + msg, _ = tx.AsMessage(signer, baseFee) txContext = core.NewEVMTxContext(msg) vmConf vm.Config dump *os.File @@ -720,7 +765,8 @@ } // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) statedb.Prepare(tx.Hash(), i) - _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) + vmRunner := api.backend.NewEVMRunner(block.Header(), statedb) + _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), vmRunner, sysCtx) if writer != nil { writer.Flush() } @@ -743,6 +789,14 @@ } return dumps, nil }   +func getBaseFee(isEspresso bool, sysCtx *core.SysContractCallCtx, feeCurrency *common.Address) *big.Int { + var baseFee *big.Int + if isEspresso { + baseFee = sysCtx.GetGasPriceMinimum(feeCurrency) + } + return baseFee +} + // containsTx reports whether the transaction with a certain hash // is contained within the specified block. func containsTx(block *types.Block, hash common.Hash) bool { @@ -773,7 +827,21 @@ block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash) if err != nil { return nil, err } - msg, vmctx, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) + + var sysCtx *core.SysContractCallCtx + if api.backend.ChainConfig().IsEspresso(block.Number()) { + parent, err := api.blockByNumber(ctx, rpc.BlockNumber(blockNumber-1)) + if err != nil { + return nil, err + } + sysStateDB, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true) + if err != nil { + return nil, err + } + sysCtx = core.NewSysContractCallCtx(block.Header(), sysStateDB, api.backend) + } + + msg, vmctx, vmRunner, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) if err != nil { return nil, err } @@ -782,7 +850,7 @@ BlockHash: blockHash, TxIndex: int(index), TxHash: hash, } - return api.traceTx(ctx, msg, txctx, vmctx, statedb, config) + return api.traceTx(ctx, msg, txctx, vmctx, vmRunner, statedb, sysCtx, config) }   // TraceCall lets you trace a given eth_call. It collects the structured logs @@ -821,12 +889,14 @@ return nil, err } } // Execute the trace - msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee()) + msg, err := args.ToMessage(api.backend.RPCGasCap(), nil) if err != nil { return nil, err } - vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) - + var sysCtx *core.SysContractCallCtx + if api.backend.ChainConfig().IsEspresso(block.Number()) { + sysCtx = core.NewSysContractCallCtx(block.Header(), statedb, api.backend) + } var traceConfig *TraceConfig if config != nil { traceConfig = &TraceConfig{ @@ -836,13 +906,15 @@ Timeout: config.Timeout, Reexec: config.Reexec, } } - return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig) + vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + vmRunner := api.backend.NewEVMRunner(block.Header(), statedb) + return api.traceTx(ctx, msg, new(Context), vmctx, vmRunner, statedb, sysCtx, traceConfig) }   // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, vmRunner vm.EVMRunner, statedb *state.StateDB, sysCtx *core.SysContractCallCtx, config *TraceConfig) (interface{}, error) { // Assemble the structured logger or the JavaScript tracer var ( tracer vm.Tracer @@ -884,7 +956,7 @@ // Call Prepare to clear out the statedb access list statedb.Prepare(txctx.TxHash, txctx.TxIndex)   - result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())) + result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), vmRunner, sysCtx) if err != nil { return nil, fmt.Errorf("tracing failed: %w", err) }
diff --git go-ethereum/eth/tracers/tracer.go celo/eth/tracers/tracer.go index 663521d2393faf6ccd86af902f908b5bc3dd2b18..d50674600df7343027cec6ccd71b28afebe7d661 100644 --- go-ethereum/eth/tracers/tracer.go +++ celo/eth/tracers/tracer.go @@ -690,9 +690,8 @@ rules := env.ChainConfig().Rules(env.Context.BlockNumber) jst.activePrecompiles = vm.ActivePrecompiles(rules)   // Compute intrinsic gas - isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber) isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber) - intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul) + intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", nil, 0, isIstanbul) if err != nil { return } @@ -793,9 +792,6 @@ // CaptureExit is called when EVM exits a scope, even if the scope didn't // execute any code. func (jst *Tracer) CaptureExit(output []byte, gasUsed uint64, err error) { if !jst.traceCallFrames { - return - } - if jst.err != nil { return } // If tracing was interrupted, set the error and stop
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/deep_calls.json celo/eth/tracers/testdata/call_tracer_legacy/deep_calls.json index 0353d4cfa9ac4039ed03ef75f5f2be298d0c5cb0..cfb21e4b98b07c47bd83e04123cde46abb93aafa 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/deep_calls.json +++ celo/eth/tracers/testdata/call_tracer_legacy/deep_calls.json @@ -93,7 +93,8 @@ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "117124093", "extraData": "0xd5830105008650617269747986312e31322e31826d61",
diff --git go-ethereum/eth/tracers/testdata/call_tracer/oog.json celo/eth/tracers/testdata/call_tracer/oog.json index de4fed6ab1fbfc8c19a648d2303590c67dfae091..7a7f55ffedbb8df79b886bd502a1650930e683fd 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/oog.json +++ celo/eth/tracers/testdata/call_tracer/oog.json @@ -1,7 +1,5 @@ { "context": { - "difficulty": "3699098917", - "gasLimit": "5258985", "miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511", "number": "2294631", "timestamp": "1513675366" @@ -31,15 +29,12 @@ "eip150Block": 0, "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, - "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, - "difficulty": "3699098917", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444", - "gasLimit": "5263953", "hash": "0x03a0f62a8106793dafcfae7b75fd2654322062d585a19cea568314d7205790dc", "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3", - "mixHash": "0x15482cc64b7c00a947f5bf015dfc010db1a6a668c74df61974d6a7848c174408", "nonce": "0xd1bdb150f6fd170e", "number": "2294630", "stateRoot": "0x1ab1a534e84cc787cda1db21e0d5920ab06017948075b759166cfea7274657a1",
diff --git go-ethereum/eth/tracers/testdata/call_tracer/selfdestruct.json celo/eth/tracers/testdata/call_tracer/selfdestruct.json index dd717906bc03e27e060eaeb74d637a86bb7567a5..124b0a67058ad617640f3d12072f6cc6c6c63cf3 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/selfdestruct.json +++ celo/eth/tracers/testdata/call_tracer/selfdestruct.json @@ -36,7 +36,8 @@ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "3509749784", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/revert.json celo/eth/tracers/testdata/call_tracer_legacy/revert.json index 059040a1c8114ed0f04e62c274415b947c2954f1..cda37b2c4a3e79cfa4f91d12c6614c7d1d6f2ad0 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/revert.json +++ celo/eth/tracers/testdata/call_tracer_legacy/revert.json @@ -30,7 +30,8 @@ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "3672229776", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
diff --git go-ethereum/eth/tracers/internal/tracers/prestate_tracer.js celo/eth/tracers/internal/tracers/prestate_tracer.js index 084c04ec46b89211c6d735d0950a4dea74ca1c4b..40aa356a33ed5473348e0fa10cc7f0abbed0b37b 100644 --- go-ethereum/eth/tracers/internal/tracers/prestate_tracer.js +++ celo/eth/tracers/internal/tracers/prestate_tracer.js @@ -47,6 +47,12 @@ // result is invoked when all the opcodes have been iterated over and returns // the final result of the tracing. result: function(ctx, db) { + if (this.prestate === null) { + this.prestate = {}; + // If tx is transfer-only, the recipient account + // hasn't been populated. + this.lookupAccount(ctx.to, db); + } // At this point, we need to deduct the 'value' from the // outer transaction, and move it back to the origin this.lookupAccount(ctx.from, db); @@ -79,7 +85,7 @@ this.lookupAccount(log.contract.getAddress(), db); } // Whenever new state is accessed, add it to the prestate switch (log.op.toString()) { - case "EXTCODECOPY": case "EXTCODESIZE": case "BALANCE": + case "EXTCODECOPY": case "EXTCODESIZE": case "EXTCODEHASH": case "BALANCE": this.lookupAccount(toAddress(log.stack.peek(0).toString(16)), db); break; case "CREATE":
diff --git go-ethereum/eth/tracers/testdata/call_tracer/deep_calls.json celo/eth/tracers/testdata/call_tracer/deep_calls.json index 0353d4cfa9ac4039ed03ef75f5f2be298d0c5cb0..c60c87ac0180eaf41d28d30c364b5bd74d07c522 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/deep_calls.json +++ celo/eth/tracers/testdata/call_tracer/deep_calls.json @@ -1,7 +1,5 @@ { "context": { - "difficulty": "117066904", - "gasLimit": "4712384", "miner": "0x1977c248e1014cc103929dd7f154199c916e39ec", "number": "25001", "timestamp": "1479891545" @@ -92,15 +90,12 @@ "eip150Block": 0, "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, - "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, - "difficulty": "117124093", "extraData": "0xd5830105008650617269747986312e31322e31826d61", - "gasLimit": "4707788", "hash": "0xad325e4c49145fb7a4058a68ac741cc8607a71114e23fc88083c7e881dd653e7", "miner": "0x00714b9ac97fd6bd9325a059a70c9b9fa94ce050", - "mixHash": "0x0af918f65cb4af04b608fc1f14a849707696986a0e7049e97ef3981808bcc65f", "nonce": "0x38dee147326a8d40", "number": "25000", "stateRoot": "0xc5d6bbcd46236fcdcc80b332ffaaa5476b980b01608f9708408cfef01b58bd5b", @@ -263,7 +258,6 @@ "from": "0x3e9286eafa2db8101246c2131c09b49080d00690", "gas": "0x20ee1", "gasUsed": "0x5374", "input": "0x581d5d60000000000000000000000000c212e03b9e060e36facad5fd8f4435412ca22e6b0000000000000000000000000000000000000000000000280faf689c35ac0000", - "output": "0x", "to": "0xcf00ffd997ad14939736f026006498e3f099baaf", "type": "CALL", "value": "0x0" @@ -305,7 +299,6 @@ "from": "0x3e9286eafa2db8101246c2131c09b49080d00690", "gas": "0x1a91d", "gasUsed": "0x12fa", "input": "0x0accce0600000000000000000000000000000000000000000000000000000000000000025842545553440000000000000000000000000000000000000000000000000000000000000000000000000000c212e03b9e060e36facad5fd8f4435412ca22e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "output": "0x", "to": "0x2a98c5f40bfa3dee83431103c535f6fae9a8ad38", "type": "CALL", "value": "0x0" @@ -377,7 +370,6 @@ "from": "0x3e9286eafa2db8101246c2131c09b49080d00690", "gas": "0x16e62", "gasUsed": "0xebb", "input": "0x645a3b72584254555344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002816d180e30c390000", - "output": "0x", "to": "0x2a98c5f40bfa3dee83431103c535f6fae9a8ad38", "type": "CALL", "value": "0x0" @@ -387,7 +379,6 @@ "from": "0xb4fe7aa695b326c9d219158d2ca50db77b39f99f", "gas": "0x283b9", "gasUsed": "0xc51c", "input": "0x949ae479000000000000000000000000c212e03b9e060e36facad5fd8f4435412ca22e6b0000000000000000000000000000000000000000000000280faf689c35ac0000", - "output": "0x", "to": "0x3e9286eafa2db8101246c2131c09b49080d00690", "type": "CALL", "value": "0x0" @@ -397,7 +388,6 @@ "from": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b", "gas": "0x30b4a", "gasUsed": "0xedb7", "input": "0x51a34eb80000000000000000000000000000000000000000000000280faf689c35ac0000", - "output": "0x", "to": "0xb4fe7aa695b326c9d219158d2ca50db77b39f99f", "type": "CALL", "value": "0x0" @@ -407,7 +397,6 @@ "from": "0x70c9217d814985faef62b124420f8dfbddd96433", "gas": "0x37b38", "gasUsed": "0x12bb3", "input": "0x51a34eb80000000000000000000000000000000000000000000000280faf689c35ac0000", - "output": "0x", "to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b", "type": "CALL", "value": "0x0"
diff --git go-ethereum/eth/tracers/internal/tracers/call_tracer.js celo/eth/tracers/internal/tracers/call_tracer.js index 98cfa0e6d4554026eb02f17fa83e334f4875e0e6..7da7bf216a258df815a3ee484e02d2470ab72715 100644 --- go-ethereum/eth/tracers/internal/tracers/call_tracer.js +++ celo/eth/tracers/internal/tracers/call_tracer.js @@ -18,77 +18,68 @@ // callFrameTracer uses the new call frame tracing methods to report useful information // about internal messages of a transaction. { - callstack: [{}], - fault: function(log, db) { - var len = this.callstack.length - if (len > 1) { - var call = this.callstack.pop() - if (this.callstack[len-1].calls === undefined) { - this.callstack[len-1].calls = [] - } - this.callstack[len-1].calls.push(call) - } - }, - result: function(ctx, db) { - // Prepare outer message info - var result = { - type: ctx.type, - from: toHex(ctx.from), - to: toHex(ctx.to), - value: '0x' + ctx.value.toString(16), - gas: '0x' + bigInt(ctx.gas).toString(16), - gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16), - input: toHex(ctx.input), - output: toHex(ctx.output), - } - if (this.callstack[0].calls !== undefined) { - result.calls = this.callstack[0].calls - } - if (this.callstack[0].error !== undefined) { - result.error = this.callstack[0].error - } else if (ctx.error !== undefined) { - result.error = ctx.error - } - if (result.error !== undefined && (result.error !== "execution reverted" || result.output ==="0x")) { - delete result.output - } + callstack: [{}], + fault: function(log, db) {}, + result: function(ctx, db) { + // Prepare outer message info + var result = { + type: ctx.type, + from: toHex(ctx.from), + to: toHex(ctx.to), + value: '0x' + ctx.value.toString(16), + gas: '0x' + bigInt(ctx.gas).toString(16), + gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16), + input: toHex(ctx.input), + output: toHex(ctx.output), + } + if (this.callstack[0].calls !== undefined) { + result.calls = this.callstack[0].calls + } + if (this.callstack[0].error !== undefined) { + result.error = this.callstack[0].error + } else if (ctx.error !== undefined) { + result.error = ctx.error + } + if (result.error !== undefined && (result.error !== "execution reverted" || result.output ==="0x")) { + delete result.output + }   - return this.finalize(result) - }, - enter: function(frame) { - var call = { - type: frame.getType(), - from: toHex(frame.getFrom()), - to: toHex(frame.getTo()), - input: toHex(frame.getInput()), - gas: '0x' + bigInt(frame.getGas()).toString('16'), - } - if (frame.getValue() !== undefined){ - call.value='0x' + bigInt(frame.getValue()).toString(16) - } - this.callstack.push(call) - }, - exit: function(frameResult) { - var len = this.callstack.length - if (len > 1) { - var call = this.callstack.pop() - call.gasUsed = '0x' + bigInt(frameResult.getGasUsed()).toString('16') - var error = frameResult.getError() - if (error === undefined) { - call.output = toHex(frameResult.getOutput()) - } else { - call.error = error - if (call.type === 'CREATE' || call.type === 'CREATE2') { - delete call.to - } - } - len -= 1 - if (this.callstack[len-1].calls === undefined) { - this.callstack[len-1].calls = [] - } - this.callstack[len-1].calls.push(call) - } - }, + return this.finalize(result) + }, + enter: function(frame) { + var call = { + type: frame.getType(), + from: toHex(frame.getFrom()), + to: toHex(frame.getTo()), + input: toHex(frame.getInput()), + gas: '0x' + bigInt(frame.getGas()).toString('16'), + } + if (frame.getValue() !== undefined){ + call.value='0x' + bigInt(frame.getValue()).toString(16) + } + this.callstack.push(call) + }, + exit: function(frameResult) { + var len = this.callstack.length + if (len > 1) { + var call = this.callstack.pop() + call.gasUsed = '0x' + bigInt(frameResult.getGasUsed()).toString('16') + var error = frameResult.getError() + if (error === undefined) { + call.output = toHex(frameResult.getOutput()) + } else { + call.error = error + if (call.type === 'CREATE' || call.type === 'CREATE2') { + delete call.to + } + } + len -= 1 + if (this.callstack[len-1].calls === undefined) { + this.callstack[len-1].calls = [] + } + this.callstack[len-1].calls.push(call) + } + }, // finalize recreates a call object using the final desired field oder for json // serialization. This is a nicety feature to pass meaningfully ordered results // to users who don't interpret it, just display it.
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json celo/eth/tracers/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json index 72152e27e7f7e57ccbf7564f85220ec350ce6fe9..fff1bd3c7033ab28e1f4ac8d4cd9f4d0d688a77c 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json +++ celo/eth/tracers/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json @@ -1,7 +1,5 @@ { "context": { - "difficulty": "3451177886", - "gasLimit": "4709286", "miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724", "number": "2290744", "timestamp": "1513616439" @@ -37,15 +35,12 @@ "eip150Block": 0, "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, - "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, - "difficulty": "3451177886", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444", - "gasLimit": "4713874", "hash": "0x5d52a672417cd1269bf4f7095e25dcbf837747bba908cd5ef809dc1bd06144b5", "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3", - "mixHash": "0x01a12845ed546b94a038a7a03e8df8d7952024ed41ccb3db7a7ade4abc290ce1", "nonce": "0x28c446f1cb9748c1", "number": "2290743", "stateRoot": "0x4898aceede76739daef76448a367d10015a2c022c9e7909b99a10fbf6fb16708",
diff --git go-ethereum/eth/tracers/testdata/call_tracer/inner_throw_outer_revert.json celo/eth/tracers/testdata/call_tracer/inner_throw_outer_revert.json index 7627c8c23d68b59ce3d74face4de2a485caa575c..a9ece4478fbeaa37e33d75a0a3de0d01db82d357 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/inner_throw_outer_revert.json +++ celo/eth/tracers/testdata/call_tracer/inner_throw_outer_revert.json @@ -1,7 +1,5 @@ { "context": { - "difficulty": "3956606365", - "gasLimit": "5413248", "miner": "0x00d8ae40d9a06d0e7a2877b62e32eb959afbe16d", "number": "2295104", "timestamp": "1513681256" @@ -40,15 +38,12 @@ "eip150Block": 0, "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, - "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, - "difficulty": "3956606365", "extraData": "0x566961425443", - "gasLimit": "5418523", "hash": "0x6f37eb930a25da673ea1bb80fd9e32ddac19cdf7cd4bb2eac62cc13598624077", "miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511", - "mixHash": "0x10971cde68c587c750c23b8589ae868ce82c2c646636b97e7d9856470c5297c7", "nonce": "0x810f923ff4b450a1", "number": "2295103", "stateRoot": "0xff403612573d76dfdaf4fea2429b77dbe9764021ae0e38dc8ac79a3cf551179e",
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/revert_reason.json celo/eth/tracers/testdata/call_tracer_legacy/revert_reason.json index 094b0446779fe2ba4ec07f8deb95cc6276d80e51..fda0b2d4f1b19832982f0d73696e047bc92f0f12 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/revert_reason.json +++ celo/eth/tracers/testdata/call_tracer_legacy/revert_reason.json @@ -35,7 +35,8 @@ "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "3509749784", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444", @@ -54,7 +55,7 @@ "result": { "error": "execution reverted", "from": "0xf7579c3d8a669c89d5ed246a22eb6db8f6fedbf1", "gas": "0x2d7308", - "gasUsed": "0x588", + "gasUsed": "0x330", "input": "0x5c19a95c000000000000000000000000f7579c3d8a669c89d5ed246a22eb6db8f6fedbf1", "to": "0xf58833cf0c791881b494eb79d461e08a1f043f52", "type": "CALL",
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/inner_throw_outer_revert.json celo/eth/tracers/testdata/call_tracer_legacy/inner_throw_outer_revert.json index 7627c8c23d68b59ce3d74face4de2a485caa575c..be08bc435107025c89528c534b01bc6898493c98 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/inner_throw_outer_revert.json +++ celo/eth/tracers/testdata/call_tracer_legacy/inner_throw_outer_revert.json @@ -41,7 +41,8 @@ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "3956606365", "extraData": "0x566961425443",
diff --git go-ethereum/eth/tracers/testdata/call_tracer/create.json celo/eth/tracers/testdata/call_tracer/create.json index 8699bf3e7e9ca77a4bd1936a33606028e4716e10..44911d39f37b333555bda571936eb0ff38444129 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/create.json +++ celo/eth/tracers/testdata/call_tracer/create.json @@ -1,7 +1,5 @@ { "context": { - "difficulty": "3755480783", - "gasLimit": "5401723", "miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511", "number": "2294702", "timestamp": "1513676146" @@ -29,15 +27,12 @@ "eip150Block": 0, "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, - "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, - "difficulty": "3757315409", "extraData": "0x566961425443", - "gasLimit": "5406414", "hash": "0xae107f592eebdd9ff8d6ba00363676096e6afb0e1007a7d3d0af88173077378d", "miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511", - "mixHash": "0xc927aa05a38bc3de864e95c33b3ae559d3f39c4ccd51cef6f113f9c50ba0caf1", "nonce": "0x93363bbd2c95f410", "number": "2294701", "stateRoot": "0x6b6737d5bde8058990483e915866bd1578014baeff57bd5e4ed228a2bfad635c",
diff --git go-ethereum/eth/tracers/testdata/call_tracer/throw.json celo/eth/tracers/testdata/call_tracer/throw.json index 09cf449776fbfbb10c093b8e74cf04caa544177f..b84bcc568d1a5d068baadec1a1cafcf7a716e570 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/throw.json +++ celo/eth/tracers/testdata/call_tracer/throw.json @@ -1,7 +1,5 @@ { "context": { - "difficulty": "117009631", - "gasLimit": "4712388", "miner": "0x294e5d6c39a36ce38af1dca70c1060f78dee8070", "number": "25009", "timestamp": "1479891666" @@ -33,16 +31,12 @@ "eip150Block": 0, "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, - "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, - "difficulty": "117066792", "extraData": "0xd783010502846765746887676f312e372e33856c696e7578", - "gasLimit": "4712388", "hash": "0xe23e8d4562a1045b70cbc99fefb20c101a8f0fc8559a80d65fea8896e2f1d46e", "miner": "0x71842f946b98800fe6feb49f0ae4e253259031c9", - "mixHash": "0x0aada9d6e93dd4db0d09c0488dc0a048fca2ccdc1f3fc7b83ba2a8d393a3a4ff", - "nonce": "0x70849d5838dee2e9", "number": "25008", "stateRoot": "0x1e01d2161794768c5b917069e73d86e8dca80cd7f3168c0597de420ab93a3b7b", "timestamp": "1479891641",
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/throw.json celo/eth/tracers/testdata/call_tracer_legacy/throw.json index 09cf449776fbfbb10c093b8e74cf04caa544177f..dd970c8810c67d0ca470b4a9e9b0cd00d831f168 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/throw.json +++ celo/eth/tracers/testdata/call_tracer_legacy/throw.json @@ -34,7 +34,8 @@ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "117066792", "extraData": "0xd783010502846765746887676f312e372e33856c696e7578",
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/oog.json celo/eth/tracers/testdata/call_tracer_legacy/oog.json index de4fed6ab1fbfc8c19a648d2303590c67dfae091..be12b5e0b415ef6366415acba68f6fac495b0929 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/oog.json +++ celo/eth/tracers/testdata/call_tracer_legacy/oog.json @@ -32,7 +32,8 @@ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "3699098917", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/inner_instafail.json celo/eth/tracers/testdata/call_tracer_legacy/inner_instafail.json index 86070d130857c6ee939aacf1530a78b3a59511a3..4da82bbb178b12bea049713853950ecb6d3c3aef 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/inner_instafail.json +++ celo/eth/tracers/testdata/call_tracer_legacy/inner_instafail.json @@ -38,6 +38,7 @@ "constantinopleBlock": 4230000, "petersburgBlock": 4939394, "istanbulBlock": 6485846, "muirGlacierBlock": 7117117, + "donutBlock": 0, "ethash": {} } },
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/delegatecall.json celo/eth/tracers/testdata/call_tracer_legacy/delegatecall.json index f7ad6df5f526fb2231b9f281dcd1fe783ce3a06d..e7d9b0f70d31a5b98697adf8931d4c7000a1dcf6 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/delegatecall.json +++ celo/eth/tracers/testdata/call_tracer_legacy/delegatecall.json @@ -46,7 +46,8 @@ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "31912170", "extraData": "0xd783010502846765746887676f312e372e33856c696e7578",
diff --git go-ethereum/eth/tracers/testdata/call_tracer/revert_reason.json celo/eth/tracers/testdata/call_tracer/revert_reason.json index 094b0446779fe2ba4ec07f8deb95cc6276d80e51..fda0b2d4f1b19832982f0d73696e047bc92f0f12 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/revert_reason.json +++ celo/eth/tracers/testdata/call_tracer/revert_reason.json @@ -35,7 +35,8 @@ "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "3509749784", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444", @@ -54,7 +55,7 @@ "result": { "error": "execution reverted", "from": "0xf7579c3d8a669c89d5ed246a22eb6db8f6fedbf1", "gas": "0x2d7308", - "gasUsed": "0x588", + "gasUsed": "0x330", "input": "0x5c19a95c000000000000000000000000f7579c3d8a669c89d5ed246a22eb6db8f6fedbf1", "to": "0xf58833cf0c791881b494eb79d461e08a1f043f52", "type": "CALL",
diff --git go-ethereum/eth/tracers/testdata/call_tracer/delegatecall.json celo/eth/tracers/testdata/call_tracer/delegatecall.json index f7ad6df5f526fb2231b9f281dcd1fe783ce3a06d..40c70d2cc907d622b0e1bc524d1916f78793e5f8 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/delegatecall.json +++ celo/eth/tracers/testdata/call_tracer/delegatecall.json @@ -1,7 +1,5 @@ { "context": { - "difficulty": "31927752", - "gasLimit": "4707788", "miner": "0x5659922ce141eedbc2733678f9806c77b4eebee8", "number": "11495", "timestamp": "1479735917" @@ -45,15 +43,12 @@ "eip150Block": 0, "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, - "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, - "difficulty": "31912170", "extraData": "0xd783010502846765746887676f312e372e33856c696e7578", - "gasLimit": "4712388", "hash": "0x0855914bdc581bccdc62591fd438498386ffb59ea4d5361ed5c3702e26e2c72f", "miner": "0x334391aa808257952a462d1475562ee2106a6c90", - "mixHash": "0x64bb70b8ca883cadb8fbbda2c70a861612407864089ed87b98e5de20acceada6", "nonce": "0x684129f283aaef18", "number": "11494", "stateRoot": "0x7057f31fe3dab1d620771adad35224aae43eb70e94861208bc84c557ff5b9d10", @@ -89,7 +84,6 @@ "from": "0xa529806c67cc6486d4d62024471772f47f6fd672", "gas": "0x2d6e28", "gasUsed": "0x64bd", "input": "0x7065cb480000000000000000000000001523e55a1ca4efbae03355775ae89f8d7699ad9e", - "output": "0x", "to": "0x269296dddce321a6bcbaa2f0181127593d732cba", "type": "CALL", "value": "0x0"
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/simple.json celo/eth/tracers/testdata/call_tracer_legacy/simple.json index b46432122dd0dc906d38c3f54602b1aaca04f06a..8f735dfc151afcce674d13f707ee1d29d2e65eb9 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/simple.json +++ celo/eth/tracers/testdata/call_tracer_legacy/simple.json @@ -1,7 +1,5 @@ { "context": { - "difficulty": "3502894804", - "gasLimit": "4722976", "miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724", "number": "2289806", "timestamp": "1513601314" @@ -40,16 +38,12 @@ "eip150Block": 0, "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, - "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, - "difficulty": "3509749784", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444", - "gasLimit": "4727564", "hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440", "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3", - "mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada", - "nonce": "0x4eb12e19c16d43da", "number": "2289805", "stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f", "timestamp": "1513601261",
diff --git go-ethereum/eth/tracers/testdata/call_tracer/inner_instafail.json celo/eth/tracers/testdata/call_tracer/inner_instafail.json index 6e221b3c079b6f2f2e6b4c6d83f7b8d594beb5ec..2e97de626b866b60312defb58c4fe99a99946bee 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/inner_instafail.json +++ celo/eth/tracers/testdata/call_tracer/inner_instafail.json @@ -28,6 +28,7 @@ }, "config": { "chainId": 3, "homesteadBlock": 0, + "donutBlock": 0, "daoForkSupport": true, "eip150Block": 0, "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
diff --git go-ethereum/eth/tracers/testdata/call_tracer/simple.json celo/eth/tracers/testdata/call_tracer/simple.json index 08cb7b2d00c05c0fe66b3c8f3c30da0d31079d90..a068d0126911ca017c4708d2ffd67d980f503bf6 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/simple.json +++ celo/eth/tracers/testdata/call_tracer/simple.json @@ -41,7 +41,8 @@ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "3509749784", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
diff --git go-ethereum/eth/tracers/testdata/call_tracer/revert.json celo/eth/tracers/testdata/call_tracer/revert.json index 059040a1c8114ed0f04e62c274415b947c2954f1..53faa8c695efc351b97c11ce70bce40ac1cb4bb8 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/revert.json +++ celo/eth/tracers/testdata/call_tracer/revert.json @@ -1,7 +1,5 @@ { "context": { - "difficulty": "3665057456", - "gasLimit": "5232723", "miner": "0xf4d8e706cfb25c0decbbdd4d2e2cc10c66376a3f", "number": "2294501", "timestamp": "1513673601" @@ -29,15 +27,12 @@ "eip150Block": 0, "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, - "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, - "difficulty": "3672229776", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444", - "gasLimit": "5227619", "hash": "0xa07b3d6c6bf63f5f981016db9f2d1d93033833f2c17e8bf7209e85f1faf08076", "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3", - "mixHash": "0x806e151ce2817be922e93e8d5921fa0f0d0fd213d6b2b9a3fa17458e74a163d0", "nonce": "0xbc5d43adc2c30c7d", "number": "2294500", "stateRoot": "0xca645b335888352ef9d8b1ef083e9019648180b259026572e3139717270de97d",
diff --git go-ethereum/eth/tracers/testdata/call_tracer/inner_create_oog_outer_throw.json celo/eth/tracers/testdata/call_tracer/inner_create_oog_outer_throw.json index 9395eb401c2a6c20f63abb4f9957904c8fa1a1b3..fd34842356ac904dea70edeffb91bb8b7ec91468 100644 --- go-ethereum/eth/tracers/testdata/call_tracer/inner_create_oog_outer_throw.json +++ celo/eth/tracers/testdata/call_tracer/inner_create_oog_outer_throw.json @@ -38,7 +38,8 @@ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "3451177886", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/create.json celo/eth/tracers/testdata/call_tracer_legacy/create.json index 8699bf3e7e9ca77a4bd1936a33606028e4716e10..b118b75ac40100a676486fbb89e172d71335e096 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/create.json +++ celo/eth/tracers/testdata/call_tracer_legacy/create.json @@ -30,7 +30,8 @@ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "3757315409", "extraData": "0x566961425443",
diff --git go-ethereum/eth/tracers/internal/tracers/assets.go celo/eth/tracers/internal/tracers/assets.go index 60e7e7b774d3500806fbdf24563a1b562540b272..b0ca8dafcd0d556b9012e7caab3bc459f7afc9c1 100644 --- go-ethereum/eth/tracers/internal/tracers/assets.go +++ celo/eth/tracers/internal/tracers/assets.go @@ -3,12 +3,12 @@ // sources: // 4byte_tracer.js (2.224kB) // 4byte_tracer_legacy.js (2.933kB) // bigram_tracer.js (1.712kB) -// call_tracer.js (4.251kB) +// call_tracer.js (3.497kB) // call_tracer_legacy.js (8.956kB) // evmdis_tracer.js (4.195kB) // noop_tracer.js (1.271kB) // opcount_tracer.js (1.372kB) -// prestate_tracer.js (4.287kB) +// prestate_tracer.js (4.482kB) // trigram_tracer.js (1.788kB) // unigram_tracer.js (1.469kB)   @@ -20,7 +20,6 @@ "compress/gzip" "crypto/sha256" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strings" @@ -139,7 +138,7 @@ a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x77, 0x6c, 0xd, 0x24, 0xf2, 0x49, 0xbd, 0x58, 0x8b, 0xb5, 0xd1, 0xc9, 0xcd, 0xcf, 0x5b, 0x3e, 0x5c, 0xfb, 0x14, 0x50, 0xe7, 0xe3, 0xb9, 0xd1, 0x54, 0x69, 0xe6, 0x5e, 0x45, 0xa6, 0x2c, 0x6c}} return a, nil }   -var _call_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x57\x4f\x6f\xdb\xb8\x12\x3f\xcb\x9f\x62\x5e\x0e\xb5\x8d\xba\x56\xd2\x07\xf4\xe0\xd6\x05\xfc\x82\xa4\x35\x90\x97\x04\x8e\xb3\x45\x10\xe4\x40\x5b\x23\x89\x2d\x4d\x0a\x24\x15\xc7\xdb\xfa\xbb\x2f\x86\x94\x64\x49\xb1\xd3\xec\x69\xb1\x39\xc5\xc3\xdf\xfc\x66\x38\xff\x38\x0a\x43\x38\x55\xd9\x46\xf3\x24\xb5\xf0\xfe\xf8\xfd\x09\xcc\x53\x84\x44\xbd\x43\x9b\xa2\xc6\x7c\x05\x93\xdc\xa6\x4a\x9b\x4e\x18\xc2\x3c\xe5\x06\x62\x2e\x10\xb8\x81\x8c\x69\x0b\x2a\x06\xdb\xc2\x0b\xbe\xd0\x4c\x6f\x86\x9d\x30\xf4\x3a\x7b\x8f\x89\x21\xd6\x88\x60\x54\x6c\xd7\x4c\xe3\x08\x36\x2a\x87\x25\x93\xa0\x31\xe2\xc6\x6a\xbe\xc8\x2d\x02\xb7\xc0\x64\x14\x2a\x0d\x2b\x15\xf1\x78\x43\x94\xdc\x42\x2e\x23\xd4\xce\xb4\x45\xbd\x32\xa5\x1f\x5f\x2e\x6f\xe1\x02\x8d\x41\x0d\x5f\x50\xa2\x66\x02\xae\xf3\x85\xe0\x4b\xb8\xe0\x4b\x94\x06\x81\x19\xc8\x48\x62\x52\x8c\x60\xe1\xe8\x48\xf1\x9c\x5c\xb9\x29\x5c\x81\x73\x95\xcb\x88\x59\xae\xe4\x00\x90\x93\xe7\xf0\x88\xda\x70\x25\xe1\xbf\xa5\xa9\x82\x70\x00\x4a\x13\x49\x8f\x59\xba\x80\x06\x95\x91\x5e\x1f\x98\xdc\x80\x60\x76\xa7\xfa\x8a\x80\xec\xee\x1d\x01\x97\xce\x4c\xaa\x32\x04\x9b\x32\x4b\xb7\x5e\x73\x21\x60\x81\x90\x1b\x8c\x73\x31\x20\xb6\x45\x6e\xe1\xdb\x74\xfe\xf5\xea\x76\x0e\x93\xcb\x3b\xf8\x36\x99\xcd\x26\x97\xf3\xbb\x8f\xb0\xe6\x36\x55\xb9\x05\x7c\x44\x4f\xc5\x57\x99\xe0\x18\xc1\x9a\x69\xcd\xa4\xdd\x80\x8a\x89\xe1\xff\x67\xb3\xd3\xaf\x93\xcb\xf9\xe4\x7f\xd3\x8b\xe9\xfc\x0e\x94\x86\xf3\xe9\xfc\xf2\xec\xe6\x06\xce\xaf\x66\x30\x81\xeb\xc9\x6c\x3e\x3d\xbd\xbd\x98\xcc\xe0\xfa\x76\x76\x7d\x75\x73\x36\x84\x1b\x24\xaf\x90\xf4\x7f\x1f\xf3\xd8\x65\x4f\x23\x44\x68\x19\x17\xa6\x8c\xc4\x9d\xca\xc1\xa4\x2a\x17\x11\xa4\xec\x11\x41\xe3\x12\xf9\x23\x46\xc0\x60\xa9\xb2\xcd\xab\x93\x4a\x5c\x4c\x28\x99\xb8\x3b\x1f\x2c\x48\x98\xc6\x20\x95\x1d\x80\x41\x84\x4f\xa9\xb5\xd9\x28\x0c\xd7\xeb\xf5\x30\x91\xf9\x50\xe9\x24\x14\x9e\xce\x84\x9f\x87\x9d\x0e\x91\x2e\x99\x10\xe7\x9a\xad\x70\xae\xd9\x12\x35\xc5\xdd\x38\x7a\x89\x6b\x77\x08\x31\x9d\x82\xd5\x6c\xc9\x65\x02\x2b\xb4\xa9\x8a\x0c\x58\x05\x1a\x33\xa5\x6d\x91\x29\xe0\x32\x56\x7a\xe5\x2a\xca\x39\xbb\xa0\xc4\x70\x69\x51\x4b\x26\x60\x85\xc6\xb0\x04\x5d\x15\x33\x22\x93\x86\x2d\xad\x2b\x99\x9f\x1d\x00\x70\xa6\x8c\x65\xcb\x1f\x23\xb8\xff\xb9\x7d\x18\x38\x61\xcc\x72\x61\x47\x10\xe7\xd2\x61\x7b\x42\x25\x03\x88\x16\x7d\xf0\x3a\xf4\xf7\xc8\x34\x08\x94\x30\x06\x9b\x72\x33\xac\x68\x86\x02\x65\x62\xd3\x0a\xc7\x63\xe8\x11\xee\x33\x9c\xd4\xd5\x4b\x0a\x77\xd3\x67\x1c\x99\xca\x7a\xfd\x06\x96\x68\x9a\xa0\x7b\x81\xf2\xdd\xc9\x83\x17\xc0\x78\x3c\x76\x8d\x1b\x73\x89\x51\xdb\x10\xfd\xbd\xa8\x0c\xf7\x0f\x0d\x85\x6d\xe7\x95\xaa\xc3\x2c\x37\x69\x8f\xfe\xdd\xb9\xeb\x95\xb7\x3e\x92\x1a\x4d\x33\x94\x4b\xfb\xd4\x0e\x65\x18\xc2\xb5\xc6\x8c\xa6\x83\xca\xa9\xab\x8b\xa4\xb9\xd4\x36\x02\xee\xd9\x60\xdc\xba\x9f\xdd\x64\x38\x72\xc9\xb4\x4f\x43\xfa\x31\x68\x1c\xc7\x5a\xad\xdc\xb1\x55\x5f\xf1\x89\x3c\x18\x92\xa8\xdf\x44\x59\x35\x2a\xff\x29\x51\x56\xb5\x30\x8f\x4c\xe4\xce\x52\xf7\xf8\xa9\x0b\x6f\x9d\x3d\x27\x1b\x5a\x75\x63\x35\x97\x49\xef\xe4\x43\x4b\x27\x61\xc6\x13\x17\x3a\x0b\x9e\x4c\xa5\x75\xfc\x09\x33\xfd\x97\x35\x6f\x0d\x46\xa3\xfd\x9a\x74\xf4\x92\x36\x97\x59\x6e\x47\x8d\xfb\x38\x51\x0b\xa6\x72\xeb\x71\x3b\x98\x17\xd5\x70\xdb\x46\x35\xb7\xca\xe1\xb8\xac\xa2\xff\x1c\x2e\x41\x9f\xb7\xaa\xda\x0e\x30\xbc\xda\x1e\x6a\xad\xf4\x2b\xec\x79\xdc\x3e\x7b\xee\x64\x67\x0f\x50\x18\x74\xc6\xe8\xfe\x7f\x97\xbe\xd2\x39\x70\x81\x06\xbc\x41\x0b\x6f\xde\xec\x39\x3e\xc2\x27\x5c\xe6\xd4\x2d\xa0\xf1\x11\xb5\xc5\xe8\x08\x7e\xfd\x2a\xcd\xfa\xf4\x50\xc7\x1f\x1d\x3f\x1d\xf5\x9b\xae\x45\x28\xd0\x62\x13\x5a\x73\xab\xb3\xbb\x82\xcd\xb5\xf4\x91\x89\xb9\x64\x82\xff\x89\x85\x27\xfd\x7a\xff\x22\x0d\xd2\x5a\xfb\xba\xa1\xdc\x9e\x83\xc5\x10\xdb\xd7\x94\x0e\x3f\x4c\xd0\xce\x37\x19\xf6\xfa\xfb\x1a\xd3\x17\x5e\x05\x3c\xd7\x6a\xd5\xeb\xef\x69\xce\x16\x6e\xae\x9e\xa1\x8a\x92\x6f\x01\xa7\x24\x7d\x86\x75\x6d\xd9\x6c\xac\x4a\xe3\x0b\x33\xbd\x7e\xad\xb7\xba\x27\x1f\xba\x07\xdb\xa1\xd2\xfa\x83\x06\x41\xaf\xdf\x2a\x9c\x66\x50\x28\x52\x7e\x62\x8c\x0f\xd8\x2e\x58\x9a\x9d\xbd\xc7\x74\xfb\xc5\x68\xce\xe1\x32\x7b\x4f\xdc\xb6\x93\x37\xf3\x49\xfe\xe7\x9e\x32\x17\x83\x62\x80\xc1\x78\x5f\x0e\xbc\x8b\x45\x26\x08\xf6\x3c\x1b\xcf\xac\x97\xcd\xd8\x22\x38\x23\xf1\x9e\xb7\xb4\x80\xff\xee\xd5\x74\xbe\x96\x0d\x57\x2f\xac\x9d\x85\x2b\x77\xda\xeb\x37\x6d\x14\x23\xe5\x00\x63\xe9\x6c\x73\x6a\xd4\xfd\x73\x30\x6a\x21\xe7\x63\xf7\x74\x76\x36\x99\x9f\x75\x69\x0a\xec\x3d\x79\xdf\xdd\xe7\x3d\xec\x06\x82\xd7\x52\xcf\x20\xdb\x17\xde\x7d\xca\xf5\xbb\x31\x9c\xfc\xeb\x17\x91\x20\x0c\xa1\x1c\x72\xb4\x09\x6b\x64\x16\x0d\xad\xc2\x54\xb2\x6a\xf1\x1d\x97\xb4\x4e\xd2\x9a\x49\x1b\xa8\x83\x42\x84\x86\x6b\x8c\x20\xe6\x28\x22\x50\xf4\x4d\x44\xcb\xf6\x77\xa3\xa4\x23\x34\xa8\x39\x31\xba\xcd\x73\xe8\xbf\xdf\x38\x91\x4a\xbe\x44\xbb\x81\x18\x99\xcd\x35\xd2\xc2\x9a\x31\x63\x60\x85\x4c\x72\x99\xc4\xb9\x10\x1b\x50\x3a\x42\x22\xf7\x13\xd7\x38\x42\xab\x68\xa5\xd5\x06\xd6\xa9\x82\x48\xc9\x6e\xb1\xc6\x66\x1a\xe9\x0b\x65\x00\xdf\x73\x63\xe9\x3b\x26\x13\x6c\x03\xdc\x0e\x3b\x41\x79\xa9\xfa\x7e\x45\x21\x80\x9f\x9d\x20\xa0\xae\x30\x8a\x5e\x0f\x37\x9b\x83\x20\xd8\xed\x49\x65\x0d\x0d\x48\x5c\xed\x47\x4e\x4c\xbf\x9c\xb8\x5a\x88\x8a\xda\x71\xc2\x6a\x03\xda\x4d\x32\x27\xaf\xb6\x9c\xb2\xbb\x4b\xa9\xdf\x60\xea\x3d\xef\x4e\xaa\xed\xc4\x9d\xb8\x5f\x4e\x5e\xad\x23\xb5\xce\x73\x07\xae\x55\x46\x8d\x06\xf2\x5e\xf2\x55\xfd\x4e\x7c\xe5\xfd\x71\x45\x51\xc1\xdd\x2f\x92\x6f\x3b\x41\x40\x59\xec\x51\x70\x7e\xe0\x86\x3e\x05\x7d\x8c\x7c\xcc\x02\x2a\x6f\x2f\xb8\xff\x81\x9b\x87\xe7\xe5\x1c\x04\x41\x50\xf4\x54\x0d\x47\xe2\x6d\xc1\xbf\xa3\x38\xb4\x18\x05\x35\x27\xf8\xf8\xf8\x23\xf0\x4f\x75\x85\x62\xee\x7e\x04\xfe\xf6\x6d\x69\xb2\x7e\x7e\xcf\x1f\xca\x39\x5b\x3d\xdd\xad\xf3\x7e\xdd\xa1\xe2\xad\xf7\x90\x4e\xb0\xed\x6c\x3b\x7f\x05\x00\x00\xff\xff\x71\x10\x40\x55\x9b\x10\x00\x00") +var _call_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x56\x5f\x6f\xdb\x38\x0c\x7f\x8e\x3f\x05\xaf\x0f\x4b\x82\x65\x71\xbb\x03\xf6\xd0\x2d\x03\x72\x45\xbb\x05\xe8\xb5\x45\x9a\xde\x50\x14\x7d\x50\x6c\xda\xd6\xa6\x48\x86\x44\x37\xcd\x6d\xfd\xee\x07\x4a\x76\x6a\x67\x59\x6f\x2f\x06\x2c\x92\x3f\xfe\xfb\x51\x54\x1c\xc3\x89\x29\x37\x56\xe6\x05\xc1\xdb\xc3\xb7\x47\xb0\x28\x10\x72\xf3\x06\xa9\x40\x8b\xd5\x0a\xa6\x15\x15\xc6\xba\x28\x8e\x61\x51\x48\x07\x99\x54\x08\xd2\x41\x29\x2c\x81\xc9\x80\x76\xf4\x95\x5c\x5a\x61\x37\xe3\x28\x8e\x83\xcd\x5e\x31\x23\x64\x16\x11\x9c\xc9\x68\x2d\x2c\x1e\xc3\xc6\x54\x90\x08\x0d\x16\x53\xe9\xc8\xca\x65\x45\x08\x92\x40\xe8\x34\x36\x16\x56\x26\x95\xd9\x86\x21\x25\x41\xa5\x53\xb4\xde\x35\xa1\x5d\xb9\x26\x8e\x4f\x17\x37\x70\x8e\xce\xa1\x85\x4f\xa8\xd1\x0a\x05\x57\xd5\x52\xc9\x04\xce\x65\x82\xda\x21\x08\x07\x25\x9f\xb8\x02\x53\x58\x7a\x38\x36\x3c\xe3\x50\xae\xeb\x50\xe0\xcc\x54\x3a\x15\x24\x8d\x1e\x01\x4a\x8e\x1c\x1e\xd0\x3a\x69\x34\xfc\xd9\xb8\xaa\x01\x47\x60\x2c\x83\x0c\x04\x71\x02\x16\x4c\xc9\x76\x43\x10\x7a\x03\x4a\xd0\xb3\xe9\x6f\x14\xe4\x39\xef\x14\xa4\xf6\x6e\x0a\x53\x22\x50\x21\x88\xb3\x5e\x4b\xa5\x60\x89\x50\x39\xcc\x2a\x35\x62\xb4\x65\x45\xf0\x65\xb6\xf8\x7c\x79\xb3\x80\xe9\xc5\x2d\x7c\x99\xce\xe7\xd3\x8b\xc5\xed\x7b\x58\x4b\x2a\x4c\x45\x80\x0f\x18\xa0\xe4\xaa\x54\x12\x53\x58\x0b\x6b\x85\xa6\x0d\x98\x8c\x11\xfe\x3e\x9d\x9f\x7c\x9e\x5e\x2c\xa6\x7f\xcd\xce\x67\x8b\x5b\x30\x16\xce\x66\x8b\x8b\xd3\xeb\x6b\x38\xbb\x9c\xc3\x14\xae\xa6\xf3\xc5\xec\xe4\xe6\x7c\x3a\x87\xab\x9b\xf9\xd5\xe5\xf5\xe9\x18\xae\x91\xa3\x42\xb6\xff\xff\x9a\x67\xbe\x7b\x16\x21\x45\x12\x52\xb9\xa6\x12\xb7\xa6\x02\x57\x98\x4a\xa5\x50\x88\x07\x04\x8b\x09\xca\x07\x4c\x41\x40\x62\xca\xcd\x6f\x37\x95\xb1\x84\x32\x3a\xf7\x39\xff\x92\x90\x30\xcb\x40\x1b\x1a\x81\x43\x84\x0f\x05\x51\x79\x1c\xc7\xeb\xf5\x7a\x9c\xeb\x6a\x6c\x6c\x1e\xab\x00\xe7\xe2\x8f\xe3\x28\x62\xd0\x44\x28\x75\x66\xc5\x0a\x17\x56\x24\x68\xb9\xee\xce\xc3\x6b\x5c\x7b\x21\x64\x2c\x05\xb2\x22\x91\x3a\x87\x15\x52\x61\x52\x07\x64\xc0\x62\x69\x2c\xd5\x9d\x02\xa9\x33\x63\x57\x9e\x51\x3e\xd8\x25\x37\x46\x6a\x42\xab\x85\x82\x15\x3a\x27\x72\xf4\x2c\x16\x0c\xa6\x9d\x48\xc8\x53\xe6\x7b\xd4\x63\x3f\x8e\x44\xf2\xed\x18\xee\xbe\x3f\xdd\x8f\xa2\x5e\x26\x2a\x45\xc7\x90\x55\xda\x6b\x0d\x94\xc9\x47\x90\x2e\x87\xf0\xfd\x69\x14\xf5\x2c\xba\xae\x38\xa1\xc7\x5a\x1c\xf5\x7a\x71\x0c\x57\x16\x4b\x66\xb9\xa9\x98\x9d\xb5\x73\x1f\x62\xd4\xeb\x3d\x08\x0b\x01\x01\x26\xde\xa0\x47\x9b\x12\x8f\x01\x00\x12\x7a\x1c\xf3\xcf\x88\x4f\x33\x6b\x56\xfe\x94\xcc\x67\x7c\x64\x1f\x63\x3e\x1a\x7a\x21\x19\x2f\x6a\x0b\xc9\x04\xd1\x83\x50\x95\x87\xeb\x1f\x3e\xf6\xe1\xb5\x07\xf5\x67\x63\x32\xd7\x64\xa5\xce\x07\x47\xef\x82\x6a\x2e\x5c\x80\xa9\x55\x97\x32\x9f\x69\xf2\x68\xb9\x70\xc3\xbd\x06\x37\x0e\xd3\xe3\xfd\x06\x2c\xda\x63\x24\x75\x59\xd1\x71\x27\x56\x7f\x14\xa4\xa6\xa2\x20\x7e\x96\x86\x23\x2f\x7e\x8a\x7a\x3d\x99\xc1\x80\x0a\xe9\xc6\xdb\x3e\xdd\x1d\xde\x87\x1f\xf8\x63\x32\xf1\x37\x55\x26\x35\xa6\xa1\xfe\x75\x7b\x6a\x85\x09\xfc\xc2\xf4\x45\x70\xb4\xd6\xd8\x97\xc0\x83\xc2\x3e\x70\x2f\x61\x70\x40\xe5\x10\x18\x9f\x73\xfa\x6d\xc4\xad\x72\x2b\xc0\x8e\x4a\x07\x03\x5e\xbd\xda\x23\x3e\xc0\x47\x4c\x2a\xa6\x26\x58\x7c\x40\x4b\x98\x1e\xc0\x8f\x1f\x35\xed\xea\xfa\xc2\x64\x32\x39\x38\x7c\x3c\x18\xd6\x71\xa4\xa8\x90\xb0\xab\xe3\x63\x88\x38\x46\xaa\xac\x0e\xd9\x66\x52\x0b\x25\xff\xc5\xda\xed\x30\xea\xf1\x4c\x20\x8f\x5a\x6b\x24\xfc\xd8\x06\x64\x26\xbc\x1f\xe5\x0e\xdd\xbd\xc2\x38\x47\x5a\x6c\x4a\x1c\x0c\x5b\x94\x0f\x44\xd8\xca\xcf\xac\x59\x0d\x86\xcf\xb4\xdf\x11\x2f\x4c\x23\xac\x79\xb6\x23\x9f\xf1\x69\xa3\xe2\x09\xdf\xe5\xee\x56\xf1\x93\x70\x83\x61\x8b\xbe\xfd\xa3\x77\xfd\x0e\x07\xb7\x9a\xff\xf0\x34\x0d\x86\x3b\xdd\xf4\xb9\x71\x9e\x61\xda\x26\xbf\x70\x53\x1b\x77\xe7\xa4\xf6\xd2\x65\xd3\xb8\xac\x5c\x31\xe0\xdf\xa6\xc6\x8f\x92\x76\x4b\x3c\x0f\x4d\xd8\x16\x5a\xa1\xfe\x89\x96\x63\x85\x3a\xa7\xa2\x4e\x83\x35\x3e\xc2\x51\xdd\xf5\x56\x73\x76\xbd\x9b\x72\x30\xdc\xe6\x54\x8f\x37\x4c\xf6\x95\x2f\x04\x51\x17\x91\xd5\x7e\x2e\x64\xe3\xab\xa1\xf9\x8e\xdd\x29\x1f\x07\x77\x1c\x63\xad\xb5\x67\x5a\x42\x34\x0d\x83\xdb\xcd\x7e\x06\xbb\xf4\xd2\xc1\xd0\xc3\xd5\x73\xd8\x32\x6e\x42\x68\xa6\x2c\xb8\xf4\x22\xa6\xa6\x77\xdb\x3f\x99\x9f\x4e\x17\xa7\x7d\x9e\x9a\xbd\x92\xb7\xfd\x26\xa0\x66\x70\x82\x9a\xf1\x67\x4f\x51\xf3\xe1\x6a\xbf\x99\xc0\x51\x93\xd9\xce\x85\xa1\x50\xbf\x39\x6a\x2e\xb3\xbd\xf9\xbe\x68\x00\x77\xf7\x5b\x4f\x2f\x28\x76\x98\xc4\xda\xcc\xa6\x38\x86\x66\x94\xf9\x5d\x60\x51\x10\x3a\x7e\x18\x30\x1b\xcc\xf2\x2b\x26\xbc\x5c\x79\xe9\xf2\x3e\xf6\xaa\x90\xa2\x93\x16\x53\xc8\x24\xaa\x14\x0c\xbf\x10\xf9\xe9\xf1\xd5\x19\xed\x01\x1d\x5a\xc9\x88\x7e\x0f\x8f\xc3\x6b\x56\x32\xa8\x96\x09\xd2\x06\x32\x14\x54\x59\xe4\xf5\x5d\x0a\xe7\x60\x85\x42\x4b\x9d\x67\x95\x52\x1b\x30\x36\x45\x06\x0f\xf7\x8a\xf3\x80\x64\x78\xc1\x5b\x07\xeb\xc2\x40\x6a\x74\xbf\x5e\xea\xa5\x45\x7e\xaf\x8d\xe0\x6b\xe5\x88\x5f\x75\xa5\x12\x1b\x90\x34\x8e\x7a\x4d\x52\xed\xfd\xcc\x99\x6f\x47\xc4\x19\xbe\x10\x7f\x5e\xbe\x4d\x9b\xbb\xdb\xd7\x1f\xf3\x5f\x77\xef\xd6\xdd\xee\x6e\xdc\xe7\xe9\xef\xae\xd7\x66\x82\xba\x3b\xb4\x3d\x57\xdd\x45\xe9\x25\xfe\xaf\xbb\x22\x5b\xdc\xf7\x02\xcf\xe0\xad\x81\xff\x0b\x51\xca\x55\x3b\x27\xb9\x0a\xf1\x78\x2e\x6c\xd5\xfd\x5f\x73\xbf\x71\x17\x07\x5c\x9c\x6f\xb8\xe1\x87\x71\xa8\x51\xcd\x41\xe6\x6d\x38\xb8\xfb\x86\x9b\xfb\xfd\x3c\xad\xa7\xa0\xa5\xd7\x30\xb3\xb9\x3f\x83\xe8\x85\xc5\xbd\x0d\x42\x4e\x0e\xdf\x83\xfc\xd0\x36\xa8\xef\xb0\xf7\x20\x5f\xbf\x6e\x5c\xb6\xe5\x77\xf2\xbe\xb9\xc2\xb6\x0b\x6a\x47\x3e\x6c\x07\x54\x6f\xb4\xa0\x12\xf5\x9e\xa2\xa7\xe8\xbf\x00\x00\x00\xff\xff\x2a\xac\x9f\xff\xa9\x0d\x00\x00")   func call_tracerJsBytes() ([]byte, error) { return bindataRead( @@ -155,7 +154,7 @@ return nil, err }   info := bindataFileInfo{name: "call_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x96, 0x54, 0x29, 0x1, 0x3b, 0x86, 0xea, 0xb2, 0x35, 0xbd, 0x97, 0xb1, 0x17, 0x8c, 0x17, 0x79, 0x1c, 0x4c, 0x8e, 0x7b, 0xe2, 0x5f, 0x11, 0x59, 0xa0, 0x94, 0x35, 0x43, 0xec, 0x18, 0x2a, 0xd9}} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x42, 0x13, 0x7a, 0x14, 0xbf, 0xa7, 0x49, 0x4f, 0xb4, 0x4f, 0x45, 0x1, 0xbc, 0x9e, 0xd1, 0x8e, 0xc7, 0xee, 0x61, 0xfa, 0x82, 0x52, 0xa4, 0x78, 0xfe, 0xff, 0xb1, 0x68, 0x1d, 0xcc, 0x1d, 0x8e}} return a, nil }   @@ -239,7 +238,7 @@ a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x27, 0xe, 0x97, 0x88, 0x9b, 0x53, 0xbb, 0x20, 0x44, 0xd8, 0xf5, 0xeb, 0x41, 0xd2, 0x7e, 0xd6, 0xda, 0x6b, 0xf5, 0xaf, 0x0, 0x75, 0x9f, 0xd9, 0x22, 0xc, 0x6e, 0x74, 0xac, 0x2a, 0xa9, 0xa7}} return a, nil }   -var _prestate_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x57\xdd\x6f\xdb\x38\x12\x7f\xb6\xfe\x8a\x41\x5f\x6c\x5d\x5d\xb9\xcd\x02\x7b\x80\x73\x39\x40\x75\xdd\x36\x40\x36\x09\x6c\xe7\x72\xb9\xc5\x3e\x50\xe4\x48\xe6\x9a\x26\x05\x92\xb2\xe3\x2b\xf2\xbf\x1f\x86\xfa\xf0\x47\x93\xa6\x7b\x6f\x16\x39\xfc\xcd\xf7\x6f\xc6\xa3\x11\x4c\x4c\xb9\xb3\xb2\x58\x7a\x38\x7b\xff\xe1\xef\xb0\x58\x22\x14\xe6\x1d\xfa\x25\x5a\xac\xd6\x90\x56\x7e\x69\xac\x8b\x46\x23\x58\x2c\xa5\x83\x5c\x2a\x04\xe9\xa0\x64\xd6\x83\xc9\xc1\x9f\xc8\x2b\x99\x59\x66\x77\x49\x34\x1a\xd5\x6f\x9e\xbd\x26\x84\xdc\x22\x82\x33\xb9\xdf\x32\x8b\x63\xd8\x99\x0a\x38\xd3\x60\x51\x48\xe7\xad\xcc\x2a\x8f\x20\x3d\x30\x2d\x46\xc6\xc2\xda\x08\x99\xef\x08\x52\x7a\xa8\xb4\x40\x1b\x54\x7b\xb4\x6b\xd7\xda\xf1\xe5\xfa\x0e\xae\xd0\x39\xb4\xf0\x05\x35\x5a\xa6\xe0\xb6\xca\x94\xe4\x70\x25\x39\x6a\x87\xc0\x1c\x94\x74\xe2\x96\x28\x20\x0b\x70\xf4\xf0\x33\x99\x32\x6f\x4c\x81\xcf\xa6\xd2\x82\x79\x69\xf4\x10\x50\x92\xe5\xb0\x41\xeb\xa4\xd1\xf0\x4b\xab\xaa\x01\x1c\x82\xb1\x04\x32\x60\x9e\x1c\xb0\x60\x4a\x7a\x17\x03\xd3\x3b\x50\xcc\xef\x9f\xfe\x44\x40\xf6\x7e\x0b\x90\x3a\xa8\x59\x9a\x12\xc1\x2f\x99\x27\xaf\xb7\x52\x29\xc8\x10\x2a\x87\x79\xa5\x86\x84\x96\x55\x1e\xee\x2f\x17\x5f\x6f\xee\x16\x90\x5e\x3f\xc0\x7d\x3a\x9b\xa5\xd7\x8b\x87\x73\xd8\x4a\xbf\x34\x95\x07\xdc\x60\x0d\x25\xd7\xa5\x92\x28\x60\xcb\xac\x65\xda\xef\xc0\xe4\x84\xf0\xdb\x74\x36\xf9\x9a\x5e\x2f\xd2\x8f\x97\x57\x97\x8b\x07\x30\x16\x3e\x5f\x2e\xae\xa7\xf3\x39\x7c\xbe\x99\x41\x0a\xb7\xe9\x6c\x71\x39\xb9\xbb\x4a\x67\x70\x7b\x37\xbb\xbd\x99\x4f\x13\x98\x23\x59\x85\xf4\xfe\xf5\x98\xe7\x21\x7b\x16\x41\xa0\x67\x52\xb9\x36\x12\x0f\xa6\x02\xb7\x34\x95\x12\xb0\x64\x1b\x04\x8b\x1c\xe5\x06\x05\x30\xe0\xa6\xdc\xfd\x74\x52\x09\x8b\x29\xa3\x8b\xe0\xf3\x8b\x05\x09\x97\x39\x68\xe3\x87\xe0\x10\xe1\x1f\x4b\xef\xcb\xf1\x68\xb4\xdd\x6e\x93\x42\x57\x89\xb1\xc5\x48\xd5\x70\x6e\xf4\xcf\x24\x22\xcc\xd2\xa2\xf3\xcc\xe3\xc2\x32\x8e\x16\x4c\xe5\xcb\xca\x3b\x70\x55\x9e\x4b\x2e\x51\x7b\x90\x3a\x37\x76\x1d\x2a\x05\xbc\x01\x6e\x91\x79\x04\x06\xca\x70\xa6\x00\x1f\x91\x57\xe1\xae\x8e\x74\x28\x57\xcb\xb4\x63\x3c\x9c\xe6\xd6\xac\xc9\xd7\xca\x79\xfa\xe1\x1c\xae\x33\x85\x02\x0a\xd4\xe8\xa4\x83\x4c\x19\xbe\x4a\xa2\x6f\x51\xef\xc0\x18\xaa\x93\xe0\x61\x23\x14\x6a\x63\x8b\x7d\x8b\x90\x55\x52\x09\xa9\x8b\x24\xea\xb5\xd2\x63\xd0\x95\x52\xc3\x28\x40\x28\x63\x56\x55\x99\x72\x6e\xaa\x60\xfb\x9f\xc8\x7d\x0d\xe6\x4a\xe4\x32\xa7\xe2\x60\xdd\xad\x37\xe1\xaa\xd3\x6b\x32\x92\x4f\xa2\xde\x11\xcc\x18\xf2\x4a\x07\x77\x06\x4c\x08\x3b\x04\x91\xc5\xdf\xa2\x5e\x6f\xc3\x2c\x61\xc1\x05\x78\xf3\x15\x1f\xc3\x65\x7c\x1e\xf5\x7a\x32\x87\x81\x5f\x4a\x97\xb4\xc0\xbf\x33\xce\xff\x80\x8b\x8b\x8b\xd0\xd4\xb9\xd4\x28\x62\x20\x88\xde\x73\x62\xf5\x4d\x2f\x63\x8a\x69\x8e\x63\xe8\xbf\x7f\xec\xc3\x5b\x10\x59\x52\xa0\xff\x58\x9f\xd6\xca\x12\x6f\xe6\xde\x4a\x5d\x0c\x3e\xfc\x1a\x0f\xc3\x2b\x6d\xc2\x1b\x68\xc4\xaf\x4d\x27\x5c\xdf\x73\x23\xc2\x75\x63\x73\x2d\x35\x31\xa2\x11\x6a\xa4\x9c\x37\x96\x15\x38\x86\x6f\x4f\xf4\xfd\x44\x5e\x3d\x45\xbd\xa7\xa3\x28\xcf\x6b\xa1\x17\xa2\xdc\x40\x00\x6a\x6f\xbb\x3a\x2f\x24\x75\xea\x61\x02\x02\xde\x8f\x92\x30\x6f\x4d\x39\x49\xc2\x0a\x77\xaf\x67\x82\x2e\xa4\x78\xec\x2e\x56\xb8\x8b\xcf\xa3\x17\x53\x94\x34\x46\xff\x2e\xc5\xe3\xcf\xe6\xeb\xe4\xcd\x51\x5c\xe7\x24\xb5\xb7\x37\x8e\x4f\xe2\x68\xd1\x55\xca\x53\xb9\x4b\xbd\x31\x2b\x22\xae\x25\xc5\x47\xa9\x10\x12\x53\x52\xb6\x5c\xcd\x1c\x19\xa2\x06\xe9\xd1\x32\xa2\x4e\xb3\x41\x4b\x53\x03\x2c\xfa\xca\x6a\xd7\x85\x31\x97\x9a\xa9\x16\xb8\x89\xba\xb7\x8c\xd7\x3d\x53\x9f\x1f\xc4\x92\xfb\xc7\x10\xc5\xe0\xdd\x68\x04\xa9\x07\x72\x11\x4a\x23\xb5\x1f\xc2\x16\x41\x23\x0a\x6a\x7c\x81\xa2\xe2\x3e\xe0\xf5\x37\x4c\x55\xd8\xaf\x9b\x9b\x28\x32\x3c\x35\x15\x4d\x82\x83\xe6\x1f\x06\x03\xd7\x66\x13\x46\x5c\xc6\xf8\x0a\x9a\x86\x33\x56\x16\x52\x47\x4d\x38\x8f\x9a\x8d\x2c\x4a\x08\x38\x98\x15\x72\x45\x49\xa4\x93\x8f\x4c\xc1\x05\x64\xb2\xb8\xd4\xfe\x24\x79\x75\xd0\xdb\xa7\xf1\x1f\x49\xd3\x3c\x89\x23\xc2\x1b\x9c\xc5\x43\xf8\xf0\x6b\x57\x11\xde\x10\x14\xbc\x0e\xe6\xcd\xcb\x50\xd1\x69\x31\x3c\xff\x2c\xa8\xa1\x0e\x7e\x1b\xb4\x26\xae\xca\x28\x1d\xb5\x9f\x21\x8e\xc7\x5d\x7c\xfe\x03\xdc\x63\xdf\x5a\xdc\x26\x34\x09\x13\xe2\x10\x94\x3e\xc3\x77\xc1\xdc\x9d\x43\x01\x6f\x81\xbe\xa4\x26\x55\x4e\xf2\x2f\xcc\xc5\xf0\x37\x68\x24\x6e\xad\xe4\xdf\x59\x52\xe7\xf5\x13\x72\x8b\x6b\x1a\x05\x94\x3a\xce\x94\x42\xdb\x77\x10\x88\x66\xd8\xd4\x60\x48\x32\xae\x4b\xbf\x6b\x07\x84\x67\xb6\x40\xef\x5e\xf7\x26\xe0\xbc\x7b\xd7\xf2\x66\x88\xdf\xae\x44\xb8\xb8\x80\xfe\x64\x36\x4d\x17\xd3\x7e\xd3\x7b\xa3\x11\xdc\x63\x58\x9f\x32\x25\x33\xa1\x76\x20\x50\xa1\xc7\xda\x2e\xa3\x43\x5c\x3b\x1e\x19\xd2\x1e\x44\x1b\x0a\x3e\x4a\xe7\xa5\x2e\xa0\xa6\x97\x2d\x0d\xe3\x06\x2e\x34\x16\x67\x15\x85\xe7\x74\x72\x79\x43\x6b\x88\x45\x22\x23\x1a\x1a\xa1\x47\x99\x92\xdd\xda\x92\x4b\xeb\x3c\x94\x8a\x71\x4c\x08\xaf\x33\xe6\xe5\xa2\x68\xda\x9f\x54\xcf\x42\xdf\x06\xa0\xfd\x54\x64\x8a\xa6\x2a\xa9\x77\x30\x68\x31\xe2\xa8\xd7\xb3\xad\xf4\x01\xf6\xf9\x9e\x47\x9c\xc7\xf2\x90\x45\x68\x1b\xc1\x0d\x12\xef\x06\x0a\xa9\x27\x28\xe9\xfa\xd7\x6f\xcd\xc8\x46\x97\x44\x3d\x7a\x77\x40\x06\xca\x14\xc7\x64\x20\xea\xb0\xf0\xca\x5a\xca\x7f\xc7\xdb\x39\x11\xc3\x9f\x95\xf3\x14\x53\x4b\xe1\x69\x28\xe6\x39\x66\x0d\x3c\x4a\x23\x3a\xfe\x9e\x41\x69\xd8\x85\xe1\x42\xea\x9a\xd1\x56\xaf\x80\xa5\xf1\xa8\xbd\x64\x4a\xed\x28\x0f\x5b\x4b\xbb\x0f\x6d\x3b\x43\x70\x92\xa4\x02\x4d\x05\x51\xa9\xb9\xaa\x44\x5d\x06\xa1\xf8\x1b\x3c\x17\x6c\x3e\x5e\x9a\xd6\xe8\x1c\x2b\x30\xa1\x4a\xca\xe5\x63\xb3\x76\x6a\xe8\xd7\xcc\x38\x88\xfb\x49\x67\xe4\x31\x2f\x29\x53\x24\x6d\x91\x11\xb7\xa7\x42\x58\x74\x6e\x10\x37\x44\xd5\x65\xf6\x7e\x89\x9a\x82\x0f\x1a\xb7\xd0\xed\x33\x8c\x73\xda\xef\xc4\x10\x98\x10\xc4\x87\x27\xbb\x47\xd4\xeb\xb9\xad\xf4\x7c\x09\x41\x93\x29\xf7\xbd\x18\x37\xf5\xcf\x99\x43\x78\x33\xfd\xf7\x62\x72\xf3\x69\x3a\xb9\xb9\x7d\x78\x33\x86\xa3\xb3\xf9\xe5\x7f\xa6\xdd\xd9\xc7\xf4\x2a\xbd\x9e\x4c\xdf\x8c\xc3\x40\x7f\xc6\x21\x6f\x5a\x17\x48\xa1\xf3\x8c\xaf\x92\x12\x71\x35\x78\x7f\xcc\x03\x7b\x07\x7b\xbd\xcc\x22\x5b\x9d\xef\x8d\xa9\x1b\xb4\xd1\xd1\xf2\x34\x5c\xc0\x8b\xc1\x3a\x7f\xd9\x9a\x49\x23\x3f\x68\xd9\x7f\xbf\xbf\x04\xaa\x78\xdd\x8e\xb3\xbf\x6c\x48\xe8\x1d\xc6\x57\x63\x70\x4c\xd1\xda\x2c\xff\x4b\x7f\x77\xf2\xdc\xa1\x1f\x02\x6a\x61\xb6\xc4\x7c\x1d\x6a\x7d\xd3\xe0\x1e\x84\xec\x43\x5c\xd3\xee\x4d\x3e\x88\x3b\x61\x02\xfb\x5e\xf4\xec\x39\x51\xd4\x02\x2e\x5a\xf4\xb7\xe1\xe5\xeb\x81\x3a\x6b\x22\x75\xa2\xe0\x97\x93\xb5\x30\xdc\xaf\x71\x6d\xec\xae\x99\x61\x07\xfe\xfd\x38\xaa\xe9\xd5\x55\x57\x4f\xf4\x41\x45\xd6\x1d\x7c\x9a\x5e\x4d\xbf\xa4\x8b\xe9\x91\xd4\x7c\x91\x2e\x2e\x27\xf5\xd1\x5f\x2e\xbc\x0f\x3f\x5d\x78\xfd\xf9\x7c\x71\x33\x9b\xf6\xc7\xcd\xd7\xd5\x4d\xfa\xa9\xff\x9d\xc2\x66\x75\xfc\x51\xeb\x7a\x73\x6f\xac\xf8\x7f\x3a\xe0\x60\x8d\xcb\xd9\x73\x5b\x5c\xa0\x76\xee\xab\x93\x7f\x49\xc0\x74\xcb\xca\x79\xfd\x4f\xb1\x17\xde\x3f\xcb\xc3\x4f\xd1\x53\xf4\xbf\x00\x00\x00\xff\xff\x3a\xb7\x37\x41\xbf\x10\x00\x00") +var _prestate_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x57\xdb\x6e\x1b\x39\x12\x7d\x56\x7f\x45\x21\x2f\x92\x36\x4a\x2b\xf1\x00\xb3\x80\xbc\x5e\xa0\xa3\x28\xb1\x00\x8f\x6d\x48\xf2\x7a\xbd\x83\x79\x60\x93\xd5\x2d\x8e\x28\xb2\x41\xb2\x25\x6b\x03\xff\xfb\xa2\xd8\x17\x5d\xe2\x4b\x66\xdf\xd4\x64\xf1\x54\xd5\x61\xd5\x61\x69\x38\x84\xb1\x29\x76\x56\xe6\x4b\x0f\x67\x1f\x3f\xfd\x1d\x16\x4b\x84\xdc\x7c\x40\xbf\x44\x8b\xe5\x1a\x92\xd2\x2f\x8d\x75\xd1\x70\x08\x8b\xa5\x74\x90\x49\x85\x20\x1d\x14\xcc\x7a\x30\x19\xf8\x13\x7b\x25\x53\xcb\xec\x2e\x8e\x86\xc3\xea\xcc\xb3\xdb\x84\x90\x59\x44\x70\x26\xf3\x5b\x66\x71\x04\x3b\x53\x02\x67\x1a\x2c\x0a\xe9\xbc\x95\x69\xe9\x11\xa4\x07\xa6\xc5\xd0\x58\x58\x1b\x21\xb3\x1d\x41\x4a\x0f\xa5\x16\x68\x83\x6b\x8f\x76\xed\x9a\x38\xbe\x5d\xdf\xc1\x15\x3a\x87\x16\xbe\xa1\x46\xcb\x14\xdc\x96\xa9\x92\x1c\xae\x24\x47\xed\x10\x98\x83\x82\x56\xdc\x12\x05\xa4\x01\x8e\x0e\x7e\xa5\x50\xe6\x75\x28\xf0\xd5\x94\x5a\x30\x2f\x8d\x1e\x00\x4a\x8a\x1c\x36\x68\x9d\x34\x1a\x7e\x69\x5c\xd5\x80\x03\x30\x96\x40\x7a\xcc\x53\x02\x16\x4c\x41\xe7\xfa\xc0\xf4\x0e\x14\xf3\xfb\xa3\x3f\x41\xc8\x3e\x6f\x01\x52\x07\x37\x4b\x53\x20\xf8\x25\xf3\x94\xf5\x56\x2a\x05\x29\x42\xe9\x30\x2b\xd5\x80\xd0\xd2\xd2\xc3\xfd\x74\x71\x79\x73\xb7\x80\xe4\xfa\x01\xee\x93\xd9\x2c\xb9\x5e\x3c\x9c\xc3\x56\xfa\xa5\x29\x3d\xe0\x06\x2b\x28\xb9\x2e\x94\x44\x01\x5b\x66\x2d\xd3\x7e\x07\x26\x23\x84\xdf\x26\xb3\xf1\x65\x72\xbd\x48\x3e\x4f\xaf\xa6\x8b\x07\x30\x16\xbe\x4e\x17\xd7\x93\xf9\x1c\xbe\xde\xcc\x20\x81\xdb\x64\xb6\x98\x8e\xef\xae\x92\x19\xdc\xde\xcd\x6e\x6f\xe6\x93\x18\xe6\x48\x51\x21\x9d\x7f\x9b\xf3\x2c\xdc\x9e\x45\x10\xe8\x99\x54\xae\x61\xe2\xc1\x94\xe0\x96\xa6\x54\x02\x96\x6c\x83\x60\x91\xa3\xdc\xa0\x00\x06\xdc\x14\xbb\x9f\xbe\x54\xc2\x62\xca\xe8\x3c\xe4\xfc\x62\x41\xc2\x34\x03\x6d\xfc\x00\x1c\x22\xfc\x63\xe9\x7d\x31\x1a\x0e\xb7\xdb\x6d\x9c\xeb\x32\x36\x36\x1f\xaa\x0a\xce\x0d\xff\x19\x47\x84\x59\x58\x74\x9e\x79\x5c\x58\xc6\xd1\x82\x29\x7d\x51\x7a\x07\xae\xcc\x32\xc9\x25\x6a\x0f\x52\x67\xc6\xae\x43\xa5\x80\x37\xc0\x2d\x32\x8f\xc0\x40\x19\xce\x14\xe0\x23\xf2\x32\xec\x55\x4c\x87\x72\xb5\x4c\x3b\xc6\xc3\x6a\x66\xcd\x9a\x72\x2d\x9d\xa7\x1f\xce\xe1\x3a\x55\x28\x20\x47\x8d\x4e\x3a\x48\x95\xe1\xab\x38\xfa\x1e\x75\x0e\x82\xa1\x3a\x09\x19\xd6\x46\xa1\x36\xb6\xd8\xb5\x08\x69\x29\x95\x90\x3a\x8f\xa3\x4e\x63\x3d\x02\x5d\x2a\x35\x88\x02\x84\x32\x66\x55\x16\x09\xe7\xa6\x0c\xb1\xff\x89\xdc\x57\x60\xae\x40\x2e\x33\x2a\x0e\xd6\xee\x7a\x13\xb6\x5a\xbf\x26\x25\xfb\x38\xea\x1c\xc1\x8c\x20\x2b\x75\x48\xa7\xc7\x84\xb0\x03\x10\x69\xff\x7b\xd4\xe9\x6c\x98\x25\x2c\xb8\x00\x6f\x2e\xf1\x31\x6c\xf6\xcf\xa3\x4e\x47\x66\xd0\xf3\x4b\xe9\xe2\x06\xf8\x77\xc6\xf9\x1f\x70\x71\x71\x11\x9a\x3a\x93\x1a\x45\x1f\x08\xa2\xf3\x9c\x59\xb5\xd3\x49\x99\x62\x9a\xe3\x08\xba\x1f\x1f\xbb\xf0\x1e\x44\x1a\xe7\xe8\x3f\x57\xab\x95\xb3\xd8\x9b\xb9\xb7\x52\xe7\xbd\x4f\xbf\xf6\x07\xe1\x94\x36\xe1\x0c\xd4\xe6\xd7\xa6\x35\xae\xf6\xb9\x11\x61\xbb\x8e\xb9\xb2\x1a\x1b\x51\x1b\xd5\x56\xce\x1b\xcb\x72\x1c\xc1\xf7\x27\xfa\x7e\xa2\xac\x9e\xa2\xce\xd3\x11\xcb\xf3\xca\xe8\x05\x96\x6b\x08\x40\xed\x6d\x5b\xe7\xb9\xa4\x4e\x3d\xbc\x80\x80\xf7\xda\x25\xcc\x9b\x50\x4e\x2e\x61\x85\xbb\xb7\x6f\x82\x36\xa4\x78\x6c\x37\x56\xb8\xeb\x9f\x47\x2f\x5e\x51\x5c\x07\xfd\xbb\x14\x8f\x3f\x7b\x5f\x27\x67\x8e\x78\x9d\x93\xd5\x3e\xde\x7e\xff\x84\x47\x8b\xae\x54\x9e\xca\x5d\xea\x8d\x59\x91\x70\x2d\x89\x1f\xa5\x02\x25\xa6\xa0\xdb\x72\x95\x72\xa4\x88\x1a\xa4\x47\xcb\x48\x3a\xcd\x06\x2d\xbd\x1a\x60\xd1\x97\x56\xbb\x96\xc6\x4c\x6a\xa6\x1a\xe0\x9a\x75\x6f\x19\xaf\x7a\xa6\x5a\x3f\xe0\x92\xfb\xc7\xc0\x62\xc8\xee\x07\x52\x02\x05\xd4\x5d\xcf\x65\x4f\x85\x1a\x0a\x83\x5c\x4f\x33\xf0\x8f\xa1\x6f\xa9\xf9\x33\xb4\x1f\x8c\x56\xbb\x41\x70\x6f\x91\xcb\x22\x68\x49\x7d\xf1\xf5\x99\x25\x73\xba\xeb\xab\xc4\x0a\x53\x94\xf4\x94\x88\xb8\xf5\x73\xd4\x83\x14\x68\xec\x4d\x88\xb5\x26\x91\x20\x12\x0f\x64\x0b\x85\x91\xda\x0f\x60\x8b\xa0\x11\x05\xe9\x94\x40\x51\x72\x1f\xfc\x77\x37\x4c\x95\xd8\xad\xb4\x88\x14\x3d\x1c\x35\x25\x3d\x5c\x07\x5a\x35\x08\x7c\xae\xcd\x26\xbc\xc8\x29\xe3\x2b\xa8\xf5\xc1\x58\x99\x4b\x1d\xbd\x18\x17\x01\xd7\x91\xd5\x35\x47\x2b\x9f\x99\x82\x0b\x48\x65\x3e\xd5\xfe\xa4\xd6\xaa\x1a\x69\x8e\xf6\xff\x88\xeb\x5e\x8f\x1d\xe9\x73\xef\xac\x3f\x80\x4f\xbf\xb6\x05\xec\x0d\x41\xc1\xdb\x60\xde\xbc\x0c\x15\x9d\xd6\xee\xf3\xc7\x82\x1b\x12\x9c\xf7\xc1\x6b\xec\xca\x94\xaa\xa7\xca\x33\xf0\x78\x2c\x3a\xe7\xaf\xe0\x1e\xe7\xd6\xe0\xd6\xd4\xc4\x4c\x88\x43\x50\xfa\x0c\xdf\x39\x73\x77\x0e\x05\xbc\x07\xfa\x92\x9a\x5c\x39\xc9\xbf\x31\xd7\x87\xbf\x41\x6d\x71\x6b\x25\xff\x21\x92\xea\x5e\xbf\x20\xb7\xb8\xa6\x6a\xa3\xab\xe3\x4c\x29\xb4\x5d\x07\x41\x17\x07\x75\xcb\x84\x4b\xc6\x75\xe1\x77\xcd\x7b\xe6\x99\xcd\xd1\xbb\xb7\xb3\x09\x38\x1f\x3e\x34\x32\x1f\xf8\xdb\x15\xd4\x29\xd0\x1d\xcf\x26\xc9\x62\xd2\xad\x9b\x65\x38\x84\x7b\x0c\xd3\x5e\xaa\x64\x2a\xd4\x0e\x04\x2a\xf4\x58\xc5\x65\x74\xe0\xb5\x95\xbd\x01\x8d\x6d\x34\x50\xe1\xa3\x74\x5e\xea\x1c\xaa\x26\xdb\xd2\xec\xd0\x36\xcc\x86\x00\x4b\xa2\xe7\xf4\xa1\xf5\x86\xa6\x26\x8b\xa4\x9d\xf4\xc6\x05\x49\x61\x4a\xb6\x53\x56\x26\xad\xf3\x50\x28\xc6\x31\xf4\x58\x1b\xcc\xcb\x45\x71\xd0\x68\xb3\x20\x33\x01\x68\xff\x88\x33\x45\x43\x00\xb9\x77\xd0\x6b\x30\xfa\x51\xa7\x63\x1b\xeb\x03\xec\xf3\xbd\xec\x39\x8f\xc5\xa1\xe8\xd1\xf0\x84\x1b\xa4\x67\x22\x28\x5e\xf5\xe0\x93\xaf\x7f\xfd\x56\x4f\x18\xe8\xe2\xa8\x43\xe7\x0e\xb4\x4b\x99\x7c\xaf\x5d\x24\x06\xa2\xa2\x85\x97\xd6\x1e\xa8\x0d\xc8\x8c\x84\xe1\xcf\xd2\x79\xe2\xd4\x12\x3d\xb5\x22\xbe\xae\x79\x6f\x48\x5e\xfd\x12\x57\x13\x6b\x61\x3c\x6a\x2f\x99\x52\x3b\xba\x87\xad\xa5\x51\x8d\x86\xb3\x01\x38\x49\x56\x41\xa6\x82\xa9\xd4\x5c\x95\xa2\x2a\x83\x50\xfc\x35\x9e\x0b\x31\x1f\xcf\x78\x6b\x74\x8e\xe5\x18\x53\x25\x65\xf2\xb1\x9e\x92\x35\x74\x2b\x21\xef\xf5\xbb\x2f\xe9\xa5\x32\x79\xdc\x14\x19\x3d\x45\x89\x10\x16\x9d\xeb\xf5\x4f\x25\xf4\x7e\x89\x9a\xc8\x07\x8d\x5b\x68\xc7\x2f\xc6\x39\x8d\xa3\x62\x00\x4c\x08\xd2\xc3\x93\x51\x29\xea\x74\xdc\x56\x7a\xbe\x84\xe0\xc9\x14\xfb\x5e\xec\xd7\xf5\xcf\x99\x43\x78\x37\xf9\xf7\x62\x7c\xf3\x65\x32\xbe\xb9\x7d\x78\x37\x82\xa3\xb5\xf9\xf4\x3f\x93\xd3\xb5\xcb\x64\x7e\xd9\xae\x7d\x4e\xae\x92\xeb\xf1\xe4\xdd\x28\xcc\x24\xcf\x24\xe9\x4d\x93\x16\x05\xe1\x3c\xe3\xab\xb8\x40\x5c\xf5\x3e\x1e\x6b\xc3\x3e\xe9\x4e\x27\xb5\xc8\x56\xe7\xfb\x00\xab\xa6\xad\x7d\x34\xda\x0d\x17\xf0\x22\x81\xe7\x2f\x47\x33\xae\xed\x7b\xcd\x8b\xb0\x1f\xc1\x82\x7c\xbc\x1d\xc7\xd9\x5f\x0e\x24\xf4\x13\xe3\xab\x11\x38\xa6\x68\xf2\x97\xff\xa5\x7f\x6c\x59\xe6\xd0\x0f\x00\xb5\x30\x5b\x52\xc3\x16\xb5\xda\xa9\x71\x0f\x28\xfb\xd4\xaf\xa4\xf8\x26\xeb\xf5\x5b\x63\x02\xfb\xd1\xf4\xec\x39\x53\xd4\x02\x2e\x1a\xf4\xf7\xe1\xe4\xdb\x44\x9d\xd5\x4c\x9d\x38\xf8\xe5\x64\xb2\x0d\xfb\x6b\x5c\x1b\xbb\xab\xdf\xb5\x83\xfc\x5e\x67\x35\xb9\xba\x6a\xeb\x89\x3e\xa8\xc8\xda\x85\x2f\x93\xab\xc9\xb7\x64\x31\x39\xb2\x9a\x2f\x92\xc5\x74\x5c\x2d\xfd\xe5\xc2\xfb\xf4\xd3\x85\xd7\x9d\xcf\x17\x37\xb3\x49\x77\x54\x7f\x5d\xdd\x24\x5f\xba\x3f\x38\xac\xa7\xdf\xd7\xda\xd9\x9b\x7b\x63\xc5\xff\xd3\x01\x07\x93\x68\xc6\x9e\x1b\x44\x83\xdc\x73\x5f\x9e\xfc\xd1\x03\xa6\x1b\xa5\xce\xaa\x3f\xbb\x9d\x70\xfe\x59\x6d\x7e\x8a\x9e\xa2\xff\x05\x00\x00\xff\xff\x78\x74\x1c\xa8\x82\x11\x00\x00")   func prestate_tracerJsBytes() ([]byte, error) { return bindataRead( @@ -255,7 +254,7 @@ return nil, err }   info := bindataFileInfo{name: "prestate_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd4, 0x9, 0xf9, 0x44, 0x13, 0x31, 0x89, 0xf7, 0x35, 0x9a, 0xc6, 0xf0, 0x86, 0x9d, 0xb2, 0xe3, 0x57, 0xe2, 0xc0, 0xde, 0xc9, 0x3a, 0x4c, 0x4a, 0x94, 0x90, 0xa5, 0x92, 0x2f, 0xbf, 0xc0, 0xb8}} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x52, 0x80, 0x86, 0xa9, 0xa9, 0x19, 0xc0, 0xc0, 0x23, 0x24, 0x59, 0xd8, 0xf6, 0xb0, 0x20, 0x47, 0x84, 0x3c, 0x55, 0x12, 0x85, 0x6, 0x28, 0x57, 0x4f, 0xf8, 0xdd, 0xc0, 0x71, 0xc0, 0xf6, 0x27}} return a, nil }   @@ -410,11 +409,13 @@ // AssetDir returns the file names below a certain // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"}, // AssetDir("data/img") would return []string{"a.png", "b.png"}, // AssetDir("foo.txt") and AssetDir("notexist") would return an error, and @@ -474,7 +475,7 @@ err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err }
diff --git go-ethereum/eth/tracers/testdata/call_tracer_legacy/selfdestruct.json celo/eth/tracers/testdata/call_tracer_legacy/selfdestruct.json index 132cefa1681a2d09f38ad42034c51f099ce3672f..a1bb1ce0297f79ded40a3a1de3a9cd02b2d9689a 100644 --- go-ethereum/eth/tracers/testdata/call_tracer_legacy/selfdestruct.json +++ celo/eth/tracers/testdata/call_tracer_legacy/selfdestruct.json @@ -36,7 +36,8 @@ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "eip155Block": 10, "eip158Block": 10, "ethash": {}, - "homesteadBlock": 0 + "homesteadBlock": 0, + "donutBlock": 0 }, "difficulty": "3509749784", "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
diff --git go-ethereum/ethclient/ethclient_test.go celo/ethclient/ethclient_test.go index 5b6c59a47919635a4d3934c74ca1870d67c335e2..3e9ea696129b314062644402a22b75397a848957 100644 --- go-ethereum/ethclient/ethclient_test.go +++ celo/ethclient/ethclient_test.go @@ -28,7 +28,7 @@ "time"   "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -197,7 +197,6 @@ t.Fatalf("can't create new node: %v", err) } // Create Ethereum Service config := &ethconfig.Config{Genesis: genesis} - config.Ethash.PowMode = ethash.ModeFake ethservice, err := eth.New(n, config) if err != nil { t.Fatalf("can't create new ethereum service: %v", err) @@ -214,20 +213,21 @@ }   func generateTestChain() (*core.Genesis, []*types.Block) { db := rawdb.NewMemoryDatabase() - config := params.AllEthashProtocolChanges + config := params.TestChainConfig + + engine := mockEngine.NewFaker() + genesis := &core.Genesis{ Config: config, Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, ExtraData: []byte("test genesis"), Timestamp: 9000, - BaseFee: big.NewInt(params.InitialBaseFee), } generate := func(i int, g *core.BlockGen) { g.OffsetTime(5) - g.SetExtra([]byte("test")) + g.SetExtra(core.CreateEmptyIstanbulExtra([]byte("test"))) } gblock := genesis.ToBlock(db) - engine := ethash.NewFaker() blocks, _ := core.GenerateChain(config, gblock, engine, db, 1, generate) blocks = append([]*types.Block{gblock}, blocks...) return genesis, blocks @@ -260,9 +260,10 @@ }, "TestStatusFunctions": { func(t *testing.T) { testStatusFunctions(t, client) }, }, - "TestCallContract": { - func(t *testing.T) { testCallContract(t, client) }, - }, + // Flaky + // "TestCallContract": { + // func(t *testing.T) { testCallContract(t, client) }, + // }, "TestAtFunctions": { func(t *testing.T) { testAtFunctions(t, client) }, }, @@ -385,7 +386,7 @@ id, err := ec.ChainID(context.Background()) if err != nil { t.Fatalf("unexpected error: %v", err) } - if id == nil || id.Cmp(params.AllEthashProtocolChanges.ChainID) != 0 { + if id == nil || id.Cmp(params.TestChainConfig.ChainID) != 0 { t.Fatalf("ChainID returned wrong number: %+v", id) } } @@ -454,7 +455,10 @@ if networkID.Cmp(big.NewInt(0)) != 0 { t.Fatalf("unexpected networkID: %v", networkID) } // SuggestGasPrice (should suggest 1 Gwei) - gasPrice, err := ec.SuggestGasPrice(context.Background()) + // TODO(ponti): this call requires the registry and the gas_price_minimum contract deployed and with data + // we should test this using mycelo + // gasPrice, err := ec.SuggestGasPrice(context.Background()) + gasPrice, err := big.NewInt(1875000000), nil if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -462,7 +466,9 @@ if gasPrice.Cmp(big.NewInt(1875000000)) != 0 { // 1 gwei tip + 0.875 basefee after a 1 gwei fee empty block t.Fatalf("unexpected gas price: %v", gasPrice) } // SuggestGasTipCap (should suggest 1 Gwei) - gasTipCap, err := ec.SuggestGasTipCap(context.Background()) + // TODO(ponti): same as SuggestGasPrice + // gasTipCap, err := ec.SuggestGasTipCap(context.Background()) + gasTipCap, err := big.NewInt(1000000000), nil if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -471,6 +477,7 @@ t.Fatalf("unexpected gas tip cap: %v", gasTipCap) } }   +// nolint:deadcode func testCallContract(t *testing.T, client *rpc.Client) { ec := NewClient(client)   @@ -568,7 +575,7 @@ if err != nil { return err } // Create transaction - tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(params.InitialBaseFee), nil) + tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, nil, nil, nil, nil, nil) signer := types.LatestSignerForChainID(chainID) signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) if err != nil {
diff --git go-ethereum/ethclient/ethclient.go celo/ethclient/ethclient.go index 56a048eb80fcfe7bf477333dad7fe3811b64e222..a2cdc3bacfe9d62bda3013d18c51838f68359404 100644 --- go-ethereum/ethclient/ethclient.go +++ celo/ethclient/ethclient.go @@ -60,6 +60,16 @@ }   // Blockchain Access   +// NetworkListening indicates if the node is listening +func (ec *Client) NetworkListening(ctx context.Context) (bool, error) { + var result bool + err := ec.c.CallContext(ctx, &result, "net_listening") + if err != nil { + return false, err + } + return result, err +} + // ChainId retrieves the current chain ID for transaction replay protection. func (ec *Client) ChainID(ctx context.Context) (*big.Int, error) { var result hexutil.Big @@ -70,10 +80,40 @@ } return (*big.Int)(&result), err }   +// Validtor retrieves full nodes validator address +func (ec *Client) Validator(ctx context.Context) (*common.Address, error) { + var result common.Address + err := ec.c.CallContext(ctx, &result, "eth_validator") + if err != nil { + return nil, err + } + return &result, err +} + +// TxFeeRecipient retrieves full nodes TxFeeRecipient +func (ec *Client) TxFeeRecipient(ctx context.Context) (*common.Address, error) { + var result common.Address + err := ec.c.CallContext(ctx, &result, "eth_txFeeRecipient") + if err != nil { + return nil, err + } + return &result, err +} + +// Coinbase retrieves full nodes coinbase +func (ec *Client) Coinbase(ctx context.Context) (*common.Address, error) { + var result common.Address + err := ec.c.CallContext(ctx, &result, "eth_coinbase") + if err != nil { + return nil, err + } + return &result, err +} + // BlockByHash returns the given full block. // // Note that loading full blocks requires two requests. Use HeaderByHash -// if you don't need all transactions or uncle headers. +// if you don't need all transactions. func (ec *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { return ec.getBlock(ctx, "eth_getBlockByHash", hash, true) } @@ -82,7 +122,7 @@ // BlockByNumber returns a block from the current canonical chain. If number is nil, the // latest known block is returned. // // Note that loading full blocks requires two requests. Use HeaderByNumber -// if you don't need all transactions or uncle headers. +// if you don't need all transactions. func (ec *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { return ec.getBlock(ctx, "eth_getBlockByNumber", toBlockNumArg(number), true) } @@ -97,7 +137,8 @@ type rpcBlock struct { Hash common.Hash `json:"hash"` Transactions []rpcTransaction `json:"transactions"` - UncleHashes []common.Hash `json:"uncles"` + Randomness *types.Randomness `json:"randomness"` + EpochSnarkData *types.EpochSnarkData `json:"epochSnarkData"` }   func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { @@ -117,43 +158,12 @@ } if err := json.Unmarshal(raw, &body); err != nil { return nil, err } - // Quick-verify transaction and uncle lists. This mostly helps with debugging the server. - if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 { - return nil, fmt.Errorf("server returned non-empty uncle list but block header indicates no uncles") - } - if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 { - return nil, fmt.Errorf("server returned empty uncle list but block header indicates uncles") - } if head.TxHash == types.EmptyRootHash && len(body.Transactions) > 0 { return nil, fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions") } if head.TxHash != types.EmptyRootHash && len(body.Transactions) == 0 { return nil, fmt.Errorf("server returned empty transaction list but block header indicates transactions") } - // Load uncles because they are not included in the block response. - var uncles []*types.Header - if len(body.UncleHashes) > 0 { - uncles = make([]*types.Header, len(body.UncleHashes)) - reqs := make([]rpc.BatchElem, len(body.UncleHashes)) - for i := range reqs { - reqs[i] = rpc.BatchElem{ - Method: "eth_getUncleByBlockHashAndIndex", - Args: []interface{}{body.Hash, hexutil.EncodeUint64(uint64(i))}, - Result: &uncles[i], - } - } - if err := ec.c.BatchCallContext(ctx, reqs); err != nil { - return nil, err - } - for i := range reqs { - if reqs[i].Error != nil { - return nil, reqs[i].Error - } - if uncles[i] == nil { - return nil, fmt.Errorf("got null header for uncle %d of block %x", i, body.Hash[:]) - } - } - } // Fill the sender cache of transactions in the block. txs := make([]*types.Transaction, len(body.Transactions)) for i, tx := range body.Transactions { @@ -162,7 +172,44 @@ setSenderFromServer(tx.tx, *tx.From, body.Hash) } txs[i] = tx.tx } - return types.NewBlockWithHeader(head).WithBody(txs, uncles), nil + return types.NewBlockWithHeader(head).WithBody(txs, body.Randomness, body.EpochSnarkData), nil +} + +type HeaderAndTxnHashes struct { + types.Header + headerExtraInfo +} + +type headerExtraInfo struct { + Transactions []common.Hash `json:"transactions,omitempty"` +} + +func (rh *HeaderAndTxnHashes) UnmarshalJSON(msg []byte) error { + if err := json.Unmarshal(msg, &rh.Header); err != nil { + return err + } + return json.Unmarshal(msg, &rh.headerExtraInfo) +} + +// HeaderAndTxnHashesByHash returns the block header with the given hash. +func (ec *Client) HeaderAndTxnHashesByHash(ctx context.Context, hash common.Hash) (*HeaderAndTxnHashes, error) { + var head *HeaderAndTxnHashes + err := ec.c.CallContext(ctx, &head, "eth_getBlockByHash", hash, false) + if err == nil && head == nil { + err = ethereum.NotFound + } + return head, err +} + +// HeaderAndTxnHashesByNumber returns a block header from the current canonical chain. If number is +// nil, the latest known header is returned. +func (ec *Client) HeaderAndTxnHashesByNumber(ctx context.Context, number *big.Int) (*HeaderAndTxnHashes, error) { + var head *HeaderAndTxnHashes + err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false) + if err == nil && head == nil { + err = ethereum.NotFound + } + return head, err }   // HeaderByHash returns the block header with the given hash. @@ -489,6 +536,16 @@ } return (*big.Int)(&hex), nil }   +// SuggestGasPrice retrieves the currently suggested gas price to allow a timely +// execution of a transaction. +func (ec *Client) SuggestGasPriceInCurrency(ctx context.Context, feeCurrency *common.Address) (*big.Int, error) { + var hex hexutil.Big + if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice", feeCurrency); err != nil { + return nil, err + } + return (*big.Int)(&hex), nil +} + // SuggestGasTipCap retrieves the currently suggested gas tip cap after 1559 to // allow a timely execution of a transaction. func (ec *Client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { @@ -554,3 +611,8 @@ arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) } return arg } + +// getRPCClient returns the rpc.Client. Just for testing purpose +func (ec *Client) GetRPCClient() *rpc.Client { + return ec.c +}
diff --git go-ethereum/ethclient/gethclient/gethclient_test.go celo/ethclient/gethclient/gethclient_test.go index dcee6b83b0d9bb397694200bcd591334130c9fc7..e351c5e6d4ae88681eb780e5c867d06e59c9a9a0 100644 --- go-ethereum/ethclient/gethclient/gethclient_test.go +++ celo/ethclient/gethclient/gethclient_test.go @@ -24,7 +24,7 @@ "testing"   "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -53,7 +53,6 @@ t.Fatalf("can't create new node: %v", err) } // Create Ethereum Service config := &ethconfig.Config{Genesis: genesis} - config.Ethash.PowMode = ethash.ModeFake ethservice, err := eth.New(n, config) if err != nil { t.Fatalf("can't create new ethereum service: %v", err) @@ -70,7 +69,7 @@ }   func generateTestChain() (*core.Genesis, []*types.Block) { db := rawdb.NewMemoryDatabase() - config := params.AllEthashProtocolChanges + config := params.TestChainConfig genesis := &core.Genesis{ Config: config, Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, @@ -79,10 +78,10 @@ Timestamp: 9000, } generate := func(i int, g *core.BlockGen) { g.OffsetTime(5) - g.SetExtra([]byte("test")) + g.SetExtra(core.CreateEmptyIstanbulExtra([]byte("test"))) } gblock := genesis.ToBlock(db) - engine := ethash.NewFaker() + engine := mockEngine.NewFaker() blocks, _ := core.GenerateChain(config, gblock, engine, db, 1, generate) blocks = append([]*types.Block{gblock}, blocks...) return genesis, blocks @@ -101,10 +100,10 @@ tests := []struct { name string test func(t *testing.T) }{ - { - "TestAccessList", - func(t *testing.T) { testAccessList(t, client) }, - }, + // { + // "TestAccessList", + // func(t *testing.T) { testAccessList(t, client) }, + // }, { "TestGetProof", func(t *testing.T) { testGetProof(t, client) }, @@ -134,6 +133,7 @@ t.Run(tt.name, tt.test) } }   +// nolint:deadcode func testAccessList(t *testing.T, client *rpc.Client) { ec := New(client) // Test transfer @@ -261,7 +261,7 @@ if err != nil { t.Fatal(err) } // Create transaction - tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil) + tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil, nil, nil, nil) signer := types.LatestSignerForChainID(chainID) signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) if err != nil {
diff --git go-ethereum/ethstats/ethstats.go celo/ethstats/ethstats.go index b656b63e9352f5b90a71f2992e768ca4777e1c1a..d4e20e4957a2e7f56f207dd4ec2b55bffe343a28 100644 --- go-ethereum/ethstats/ethstats.go +++ celo/ethstats/ethstats.go @@ -24,6 +24,7 @@ "errors" "fmt" "math/big" "net/http" + "regexp" "runtime" "strconv" "strings" @@ -31,11 +32,20 @@ "sync" "time"   "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" + istanbulBackend "github.com/ethereum/go-ethereum/consensus/istanbul/backend" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" + "github.com/ethereum/go-ethereum/contracts/validators" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" ethproto "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/les" @@ -43,8 +53,10 @@ "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rpc" "github.com/gorilla/websocket" + "golang.org/x/sync/errgroup" )   const ( @@ -57,6 +69,28 @@ // The number is referenced from the size of tx pool. txChanSize = 4096 // chainHeadChanSize is the size of channel listening to ChainHeadEvent. chainHeadChanSize = 10 + // istDelegateSignChanSize is the size of the channel listening to DelegateSignEvent + istDelegateSignChanSize = 5 + + // connectionTimeout waits for the websocket connection to be established + connectionTimeout = 10 + // delegateSignTimeout waits for the proxy to sign a message + delegateSignTimeout = 5 + // wait longer if there are difficulties with login + loginTimeout = 50 + // statusUpdateInterval is the frequency of sending full node reports + statusUpdateInterval = 13 + // valSetInterval is the frequency in blocks to send the validator set + valSetInterval = 11 + + actionBlock = "block" + actionHello = "hello" + actionHistory = "history" + actionLatency = "latency" + actionNodePing = "node-ping" + actionNodePong = "node-pong" + actionPending = "pending" + actionStats = "stats" )   // backend encompasses the bare-minimum functionality needed for ethstats reporting @@ -67,6 +101,9 @@ CurrentHeader() *types.Header HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) GetTd(ctx context.Context, hash common.Hash) *big.Int Stats() (pending int, queued int) + AccountManager() *accounts.Manager + StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) + NewEVMRunner(header *types.Header, state vm.StateDB) vm.EVMRunner SyncProgress() ethereum.SyncProgress }   @@ -77,25 +114,36 @@ backend Miner() *miner.Miner BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) CurrentBlock() *types.Block - SuggestGasTipCap(ctx context.Context) (*big.Int, error) + SuggestPrice(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) + CurrentGasPriceMinimum(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) + SuggestGasTipCap(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) +} + +// StatsPayload todo: document this +type StatsPayload struct { + Action string `json:"action"` + Stats interface{} `json:"stats"` +} + +// DelegateSignMessage Payload to be signed with the peer that sent it +type DelegateSignMessage struct { + PeerID enode.ID + Payload StatsPayload }   // Service implements an Ethereum netstats reporting daemon that pushes local // chain statistics up to a monitoring server. type Service struct { server *p2p.Server // Peer-to-peer server to retrieve networking infos + engine consensus.Engine // Consensus engine to retrieve variadic block fields backend backend - engine consensus.Engine // Consensus engine to retrieve variadic block fields - - node string // Name of the node to display on the monitoring page - pass string // Password to authorize access to the monitoring page - host string // Remote address of the monitoring service + istanbulBackend *istanbulBackend.Backend // Istanbul consensus backend + nodeName string // Name of the node to display on the monitoring page + celostatsHost string // Remote address of the monitoring service + stopFn context.CancelFunc // Close ctx Done channel   pongCh chan struct{} // Pong notifications are fed into this channel histCh chan []uint64 // History request block numbers are fed into this channel - - headSub event.Subscription - txSub event.Subscription }   // connWrapper is a wrapper to prevent concurrent-write or concurrent-read on the @@ -144,58 +192,63 @@ return w.conn.Close() }   // parseEthstatsURL parses the netstats connection url. -// URL argument should be of the form <nodename:secret@host:port> -// If non-erroring, the returned slice contains 3 elements: [nodename, pass, host] -func parseEthstatsURL(url string) (parts []string, err error) { - err = fmt.Errorf("invalid netstats url: \"%s\", should be nodename:secret@host:port", url) +// URL argument should be of the form <name@host:port> +func parseEthstatsURL(url string, name *string, host *string) error { + err := fmt.Errorf("invalid netstats url: \"%s\", should be nodename@host:port", url)   hostIndex := strings.LastIndex(url, "@") if hostIndex == -1 || hostIndex == len(url)-1 { - return nil, err - } - preHost, host := url[:hostIndex], url[hostIndex+1:] - - passIndex := strings.LastIndex(preHost, ":") - if passIndex == -1 { - return []string{preHost, "", host}, nil + return err } - nodename, pass := preHost[:passIndex], "" - if passIndex != len(preHost)-1 { - pass = preHost[passIndex+1:] - } + *name = url[:hostIndex] + *host = url[hostIndex+1:]   - return []string{nodename, pass, host}, nil + return nil }   // New returns a monitoring service ready for stats reporting. func New(node *node.Node, backend backend, engine consensus.Engine, url string) error { - parts, err := parseEthstatsURL(url) - if err != nil { - return err + // Assemble and return the stats service + var ( + name string + celostatsHost string + ) + istanbulBackend := engine.(*istanbulBackend.Backend) + + if !istanbulBackend.IsProxiedValidator() { + // Parse the netstats connection url + if err := parseEthstatsURL(url, &name, &celostatsHost); err != nil { + return err + } } + ethstats := &Service{ + engine: engine, backend: backend, - engine: engine, + istanbulBackend: istanbulBackend, server: node.Server(), - node: parts[0], - pass: parts[1], - host: parts[2], + nodeName: name, + celostatsHost: celostatsHost, + stopFn: nil, pongCh: make(chan struct{}), histCh: make(chan []uint64, 1), } - node.RegisterLifecycle(ethstats) return nil }   -// Start implements node.Lifecycle, starting up the monitoring and reporting daemon. +// Protocols implements node.Service, returning the P2P network protocols used +// by the stats service (nil as it doesn't use the devp2p overlay network). +func (s *Service) Protocols() []p2p.Protocol { return nil } + +// Start implements node.Service, starting up the monitoring and reporting daemon. func (s *Service) Start() error { - // Subscribe to chain events to execute updates on - chainHeadCh := make(chan core.ChainHeadEvent, chainHeadChanSize) - s.headSub = s.backend.SubscribeChainHeadEvent(chainHeadCh) - txEventCh := make(chan core.NewTxsEvent, txChanSize) - s.txSub = s.backend.SubscribeNewTxsEvent(txEventCh) - go s.loop(chainHeadCh, txEventCh) + go func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s.stopFn = cancel + s.loop(ctx) + }()   log.Info("Stats daemon started") return nil @@ -203,147 +256,418 @@ }   // Stop implements node.Lifecycle, terminating the monitoring and reporting daemon. func (s *Service) Stop() error { - s.headSub.Unsubscribe() - s.txSub.Unsubscribe() - log.Info("Stats daemon stopped") + // TODO don't stop if already stopped + // TODO use lock + if s.stopFn != nil { + log.Info("Stats daemon stopped") + s.stopFn() + s.stopFn = nil + } + // TODO use WaitGroup to wait for loop to finish (or use errgroup for it) return nil }   // loop keeps trying to connect to the netstats server, reporting chain events // until termination. -func (s *Service) loop(chainHeadCh chan core.ChainHeadEvent, txEventCh chan core.NewTxsEvent) { +func (s *Service) loop(ctx context.Context) { // Start a goroutine that exhausts the subscriptions to avoid events piling up var ( - quitCh = make(chan struct{}) headCh = make(chan *types.Block, 1) txCh = make(chan struct{}, 1) + signCh = make(chan *DelegateSignMessage, 1) + sendCh = make(chan *StatsPayload, 1) ) - go func() { - var lastTx mclock.AbsTime + + group, ctxGroup := errgroup.WithContext(ctx) + group.Go(func() error { return s.handleDelegateSignEvents(ctxGroup, sendCh, signCh) }) + group.Go(func() error { return s.handleNewTransactionEvents(ctxGroup, txCh) }) + group.Go(func() error { return s.handleChainHeadEvents(ctxGroup, headCh) })   - HandleLoop: - for { - select { - // Notify of chain head events, but drop if too frequent - case head := <-chainHeadCh: + if s.istanbulBackend.IsProxiedValidator() { + group.Go(func() error { + for { select { - case headCh <- head.Block: - default: + case delegateSignMsg := <-signCh: + if s.istanbulBackend.IsValidating() { + s.fillWithValidatorInfo(&delegateSignMsg.Payload) + if err := s.handleDelegateSign(&delegateSignMsg.Payload, delegateSignMsg.PeerID); err != nil { + log.Warn("Delegate sign failed", "err", err) + } + } + case <-ctxGroup.Done(): + return ctxGroup.Err() } + } + }) + } else { + group.Go(func() error { + // Resolve the URL, defaulting to TLS, but falling back to none too + path := fmt.Sprintf("%s/api", s.celostatsHost) + urls := []string{path}   - // Notify of new transaction events, but drop if too frequent - case <-txEventCh: - if time.Duration(mclock.Now()-lastTx) < time.Second { - continue - } - lastTx = mclock.Now() + // url.Parse and url.IsAbs is unsuitable (https://github.com/golang/go/issues/19779) + if !strings.Contains(path, "://") { + urls = []string{"wss://" + path, "ws://" + path} + }   + errTimer := time.NewTimer(0) + defer errTimer.Stop() + // Loop reporting until termination + for { select { - case txCh <- struct{}{}: - default: - } + case <-ctxGroup.Done(): + return ctxGroup.Err() + case <-errTimer.C: + var ( + conn *connWrapper + err error + ) + // Establish a websocket connection to the server on any supported URL + dialer := websocket.Dialer{HandshakeTimeout: 5 * time.Second} + header := make(http.Header) + header.Set("origin", "http://localhost") + for _, url := range urls { + c, _, e := dialer.Dial(url, header) + err = e + if err == nil { + conn = newConnectionWrapper(c) + break + } + } + + if err != nil { + log.Warn("Stats server unreachable", "err", err) + errTimer.Reset(connectionTimeout * time.Second) + continue + } + + // Authenticate the client with the server + if err = s.login(conn, sendCh); err != nil { + log.Warn("Stats login failed", "err", err) + conn.Close() + errTimer.Reset(connectionTimeout * time.Second) + continue + } + + // This go routine will close when the connection gets closed/lost + go s.readLoop(conn) + + // Send the initial stats so our node looks decent from the get go + if err = s.report(conn, sendCh); err != nil { + log.Warn("Initial stats report failed", "err", err) + conn.Close() + errTimer.Reset(0) + continue + } + + // Keep sending status updates until the connection breaks + fullReport := time.NewTicker(statusUpdateInterval * time.Second) + + for err == nil { + select { + case <-ctxGroup.Done(): + fullReport.Stop() + // Make sure the connection is closed + conn.Close() + return ctxGroup.Err() + + case <-fullReport.C: + if err = s.report(conn, sendCh); err != nil { + log.Warn("Full stats report failed", "err", err) + } + case list := <-s.histCh: + if err = s.reportHistory(conn, list); err != nil { + log.Warn("Requested history report failed", "err", err) + } + case head := <-headCh: + if err = s.reportBlock(conn, head); err != nil { + log.Warn("Block stats report failed", "err", err) + } + if err = s.reportPending(conn); err != nil { + log.Warn("Post-block transaction stats report failed", "err", err) + } + case <-txCh: + if err = s.reportPending(conn); err != nil { + log.Warn("Transaction stats report failed", "err", err) + } + case signedMessage := <-sendCh: + // if it is a ping or hello message, it shouldn't be handled here + if signedMessage.Action != actionNodePing && signedMessage.Action != actionHello { + if err = s.handleDelegateSend(conn, signedMessage); err != nil { + log.Warn("Delegate send failed", "err", err) + } + } else { + // As both discarded messages, if they were required should eventually close the connection + // we just warn the user to avoid possible unnecessary disconnections (for example, from + // another backup validator) + log.Warn("Signed message discarded", "Action", signedMessage.Action) + } + } + } + fullReport.Stop()   - // node stopped - case <-s.txSub.Err(): - break HandleLoop - case <-s.headSub.Err(): - break HandleLoop + // Make sure the connection is closed + conn.Close() + errTimer.Reset(0) + // Continues with the for, and tries to login again until the ctx was cancelled + } } + }) + } + + group.Wait() +} + +// login tries to authorize the client at the remote server. +func (s *Service) login(conn *connWrapper, sendCh chan *StatsPayload) error { + // Construct and send the login authentication + infos := s.server.NodeInfo() + + var protocols []string + for _, proto := range s.server.Protocols { + protocols = append(protocols, fmt.Sprintf("%s/%d", proto.Name, proto.Version)) + } + var network string + if info := infos.Protocols[istanbul.ProtocolName]; info != nil { + ethInfo, ok := info.(*ethproto.NodeInfo) + if !ok { + return errors.New("could not resolve NodeInfo") } - close(quitCh) + network = fmt.Sprintf("%d", ethInfo.Network) + } else { + lesProtocol, ok := infos.Protocols["les"] + if !ok { + return errors.New("no LES protocol found") + } + lesInfo, ok := lesProtocol.(*les.NodeInfo) + if !ok { + return errors.New("could not resolve NodeInfo") + } + network = fmt.Sprintf("%d", lesInfo.Network) + } + + auth := &authMsg{ + ID: s.istanbulBackend.ValidatorAddress().String(), + Info: nodeInfo{ + Name: s.nodeName, + Node: infos.Name, + Port: infos.Ports.Listener, + Network: network, + Protocol: strings.Join(protocols, ", "), + API: "No", + Os: runtime.GOOS, + OsVer: runtime.GOARCH, + Client: "0.1.1", + History: true, + }, + } + + if err := s.sendStats(conn, actionHello, auth); err != nil { + return err + } + + if s.istanbulBackend.IsProxy() { + // Proxy needs a delegate send of a hello action here to get ACK + if err := s.waitAndDelegateMessageWithTimeout(conn, sendCh, actionHello); err != nil { + return err + } + } + + // Retrieve the remote ack or connection termination + var ack map[string][]string + + signalCh := make(chan error, 1) + + go func() { + signalCh <- conn.ReadJSON(&ack) }()   - // Resolve the URL, defaulting to TLS, but falling back to none too - path := fmt.Sprintf("%s/api", s.host) - urls := []string{path} + select { + case <-time.After(loginTimeout * time.Second): + // Login timeout, abort + return errors.New("delegation of login timed out") + case err := <-signalCh: + if err != nil { + return errors.New("unauthorized, try registering your validator to get whitelisted") + } + } + + emit, ok := ack["emit"] + + if !ok { + return errors.New("emit not in ack") + }   - // url.Parse and url.IsAbs is unsuitable (https://github.com/golang/go/issues/19779) - if !strings.Contains(path, "://") { - urls = []string{"wss://" + path, "ws://" + path} + if len(emit) != 1 || emit[0] != "ready" { + return errors.New("unauthorized") }   - errTimer := time.NewTimer(0) - defer errTimer.Stop() - // Loop reporting until termination + return nil +} + +func (s *Service) waitAndDelegateMessageWithTimeout(conn *connWrapper, sendCh chan *StatsPayload, action string) error { for { select { - case <-quitCh: - return - case <-errTimer.C: - // Establish a websocket connection to the server on any supported URL - var ( - conn *connWrapper - err error - ) - dialer := websocket.Dialer{HandshakeTimeout: 5 * time.Second} - header := make(http.Header) - header.Set("origin", "http://localhost") - for _, url := range urls { - c, _, e := dialer.Dial(url, header) - err = e - if err == nil { - conn = newConnectionWrapper(c) - break + case signedMessage := <-sendCh: + err := s.handleDelegateSend(conn, signedMessage) + // The wait and delegate message, basically requires that some message was already returned. + // It is possible to receive an old message signed that was queue before. + // With this, we only continue if that message was present, otherwise we delegate that message + // and continue + if signedMessage.Action == action { + return err + } else { + if err != nil { + log.Warn("Delegate send failed", "err", err) + } + } + case <-time.After(delegateSignTimeout * time.Second): + return errors.New("delegation sign timeout") + } + } +} + +var nodeNameRegex = regexp.MustCompile(`(.*)/(.*)/(.*)/(.*)`) + +func (s *Service) fillWithValidatorInfo(message *StatsPayload) { + if message.Action == actionHello { + msg, ok := message.Stats.(map[string]interface{}) + if ok { + proxyInfo, ok := msg["info"].(map[string]interface{}) + if ok { + infos := s.server.NodeInfo() + proxyNode := proxyInfo["node"].(string) + proxyNodeParts := nodeNameRegex.FindStringSubmatch(proxyNode) + validatorNodeParts := nodeNameRegex.FindStringSubmatch(infos.Name) + // if one of the regex failed, maintain the proxy node name + if proxyNodeParts != nil && validatorNodeParts != nil { + proxyInfo["node"] = fmt.Sprintf( + "%s/%s(val:%s)/%s/%s", + proxyNodeParts[1], + proxyNodeParts[2], + validatorNodeParts[2], + proxyNodeParts[3], + proxyNodeParts[4], + ) } } - if err != nil { - log.Warn("Stats server unreachable", "err", err) - errTimer.Reset(10 * time.Second) + } + } +} + +func (s *Service) handleDelegateSign(messageToSign *StatsPayload, peerID enode.ID) error { + signedStats, err := s.signStats(messageToSign.Stats) + if err != nil { + return err + } + + signedMessage := &StatsPayload{ + Action: messageToSign.Action, + Stats: signedStats, + } + msg, err := json.Marshal(signedMessage) + if err != nil { + return err + } + return s.istanbulBackend.SendDelegateSignMsgToProxy(msg, peerID) +} + +func (s *Service) handleDelegateSend(conn *connWrapper, signedMessage *StatsPayload) error { + report := map[string][]interface{}{ + "emit": {signedMessage.Action, signedMessage.Stats}, + } + return conn.WriteJSON(report) +} + +func (s *Service) handleDelegateSignEvents(ctx context.Context, sendCh chan *StatsPayload, signCh chan *DelegateSignMessage) error { + ch := make(chan istanbul.MessageWithPeerIDEvent, istDelegateSignChanSize) + subscription := s.istanbulBackend.SubscribeNewDelegateSignEvent(ch) + defer subscription.Unsubscribe() + + for { + select { + case msg := <-ch: + var delegateSignMessage DelegateSignMessage + delegateSignMessage.PeerID = msg.PeerID + if err := json.Unmarshal(msg.Payload, &delegateSignMessage.Payload); err != nil { continue } - // Authenticate the client with the server - if err = s.login(conn); err != nil { - log.Warn("Stats login failed", "err", err) - conn.Close() - errTimer.Reset(10 * time.Second) - continue + if s.istanbulBackend.IsProxy() { + // proxy should send to websocket + select { + case sendCh <- &delegateSignMessage.Payload: + default: + } + } else if s.istanbulBackend.IsProxiedValidator() { + // proxied validator should sign + select { + case signCh <- &delegateSignMessage: + default: + } } - go s.readLoop(conn) + case err := <-subscription.Err(): + log.Error("Subscription for handle signing messages failed", "err", err) + return err + case <-ctx.Done(): + return ctx.Err() + } + } +}   - // Send the initial stats so our node looks decent from the get go - if err = s.report(conn); err != nil { - log.Warn("Initial stats report failed", "err", err) - conn.Close() - errTimer.Reset(0) +func (s *Service) handleNewTransactionEvents(ctx context.Context, txChan chan struct{}) error { + var lastTx mclock.AbsTime + ch := make(chan core.NewTxsEvent, txChanSize) + subscription := s.backend.SubscribeNewTxsEvent(ch) + if subscription == nil { + log.Error("Stats daemon stopped due to nil head subscription") + return errors.New("nil head subscription") + } + defer subscription.Unsubscribe() + + for { + select { + // Notify of new transaction events, but drop if too frequent + case <-ch: + if time.Duration(mclock.Now()-lastTx) < time.Second { continue } - // Keep sending status updates until the connection breaks - fullReport := time.NewTicker(15 * time.Second) - - for err == nil { - select { - case <-quitCh: - fullReport.Stop() - // Make sure the connection is closed - conn.Close() - return + lastTx = mclock.Now()   - case <-fullReport.C: - if err = s.report(conn); err != nil { - log.Warn("Full stats report failed", "err", err) - } - case list := <-s.histCh: - if err = s.reportHistory(conn, list); err != nil { - log.Warn("Requested history report failed", "err", err) - } - case head := <-headCh: - if err = s.reportBlock(conn, head); err != nil { - log.Warn("Block stats report failed", "err", err) - } - if err = s.reportPending(conn); err != nil { - log.Warn("Post-block transaction stats report failed", "err", err) - } - case <-txCh: - if err = s.reportPending(conn); err != nil { - log.Warn("Transaction stats report failed", "err", err) - } - } + select { + case txChan <- struct{}{}: + default: } - fullReport.Stop() + case err := <-subscription.Err(): + log.Error("Subscription for handle new transactions failed", "err", err) + return err + case <-ctx.Done(): + return ctx.Err() + } + } +}   - // Close the current connection and establish a new one - conn.Close() - errTimer.Reset(0) +func (s *Service) handleChainHeadEvents(ctx context.Context, headCh chan *types.Block) error { + ch := make(chan core.ChainHeadEvent, chainHeadChanSize) + subscription := s.backend.SubscribeChainHeadEvent(ch) + if subscription == nil { + log.Error("Stats daemon stopped due to nil head subscription") + return errors.New("nil head subscription") + } + defer subscription.Unsubscribe() + + for { + select { + // Notify of chain head events, but drop if too frequent + case head := <-ch: + select { + case headCh <- head.Block: + default: + } + case err := <-subscription.Err(): + log.Error("Subscription for handle chain head failed", "err", err) + return err + case <-ctx.Done(): + return ctx.Err() } } } @@ -373,23 +697,26 @@ } continue } // Not a system ping, try to decode an actual state message - var msg map[string][]interface{} + var msg map[string]interface{} if err := json.Unmarshal(blob, &msg); err != nil { log.Warn("Failed to decode stats server message", "err", err) return } - log.Trace("Received message from stats server", "msg", msg) - if len(msg["emit"]) == 0 { - log.Warn("Stats server sent non-broadcast", "msg", msg) + msgEmit, _ := msg["emit"].([]interface{}) + + log.Trace("Received message from stats server", "msgEmit", msgEmit) + if len(msgEmit) == 0 { + log.Warn("Stats server sent non-broadcast", "msgEmit", msgEmit) return } - command, ok := msg["emit"][0].(string) + + command, ok := msgEmit[0].(string) if !ok { - log.Warn("Invalid stats server message type", "type", msg["emit"][0]) + log.Warn("Invalid stats server message type", "type", msgEmit[0]) return } // If the message is a ping reply, deliver (someone must be listening!) - if len(msg["emit"]) == 2 && command == "node-pong" { + if len(msgEmit) == 2 && command == actionNodePong { select { case s.pongCh <- struct{}{}: // Pong delivered, continue listening @@ -401,16 +728,16 @@ return } } // If the message is a history request, forward to the event processor - if len(msg["emit"]) == 2 && command == "history" { + if len(msgEmit) == 2 && command == actionHistory { // Make sure the request is valid and doesn't crash us - request, ok := msg["emit"][1].(map[string]interface{}) + request, ok := msgEmit[1].(map[string]interface{}) if !ok { - log.Warn("Invalid stats history request", "msg", msg["emit"][1]) + log.Warn("Invalid stats history request", "msg", msgEmit[1]) select { case s.histCh <- nil: // Treat it as an no indexes request default: } - continue + continue // Ethstats sometimes sends invalid history requests, ignore those } list, ok := request["list"].([]interface{}) if !ok { @@ -457,59 +784,14 @@ // authMsg is the authentication infos needed to login to a monitoring server. type authMsg struct { ID string `json:"id"` Info nodeInfo `json:"info"` - Secret string `json:"secret"` -} - -// login tries to authorize the client at the remote server. -func (s *Service) login(conn *connWrapper) error { - // Construct and send the login authentication - infos := s.server.NodeInfo() - - var protocols []string - for _, proto := range s.server.Protocols { - protocols = append(protocols, fmt.Sprintf("%s/%d", proto.Name, proto.Version)) - } - var network string - if info := infos.Protocols["eth"]; info != nil { - network = fmt.Sprintf("%d", info.(*ethproto.NodeInfo).Network) - } else { - network = fmt.Sprintf("%d", infos.Protocols["les"].(*les.NodeInfo).Network) - } - auth := &authMsg{ - ID: s.node, - Info: nodeInfo{ - Name: s.node, - Node: infos.Name, - Port: infos.Ports.Listener, - Network: network, - Protocol: strings.Join(protocols, ", "), - API: "No", - Os: runtime.GOOS, - OsVer: runtime.GOARCH, - Client: "0.1.1", - History: true, - }, - Secret: s.pass, - } - login := map[string][]interface{}{ - "emit": {"hello", auth}, - } - if err := conn.WriteJSON(login); err != nil { - return err - } - // Retrieve the remote ack or connection termination - var ack map[string][]string - if err := conn.ReadJSON(&ack); err != nil || len(ack["emit"]) != 1 || ack["emit"][0] != "ready" { - return errors.New("unauthorized") - } - return nil }   // report collects all possible data to report and send it to the stats server. // This should only be used on reconnects or rarely to avoid overloading the // server. Use the individual methods for reporting subscribed events. -func (s *Service) report(conn *connWrapper) error { - if err := s.reportLatency(conn); err != nil { +func (s *Service) report(conn *connWrapper, sendCh chan *StatsPayload) error { + if err := s.reportLatency(conn, sendCh); err != nil { + log.Warn("Latency failed to report", "err", err) return err } if err := s.reportBlock(conn, nil); err != nil { @@ -526,19 +808,23 @@ }   // reportLatency sends a ping request to the server, measures the RTT time and // finally sends a latency update. -func (s *Service) reportLatency(conn *connWrapper) error { +func (s *Service) reportLatency(conn *connWrapper, sendCh chan *StatsPayload) error { // Send the current time to the ethstats server start := time.Now()   - ping := map[string][]interface{}{ - "emit": {"node-ping", map[string]string{ - "id": s.node, - "clientTime": start.String(), - }}, + ping := map[string]interface{}{ + "id": s.istanbulBackend.ValidatorAddress().String(), + "clientTime": start.String(), } - if err := conn.WriteJSON(ping); err != nil { + if err := s.sendStats(conn, actionNodePing, ping); err != nil { return err } + // Proxy needs a delegate send of a node-ping action here to get ACK + if s.istanbulBackend.IsProxy() { + if err := s.waitAndDelegateMessageWithTimeout(conn, sendCh, actionNodePing); err != nil { + return err + } + } // Wait for the pong request to arrive back select { case <-s.pongCh: @@ -552,13 +838,11 @@ // Send back the measured latency log.Trace("Sending measured latency to ethstats", "latency", latency)   - stats := map[string][]interface{}{ - "emit": {"latency", map[string]string{ - "id": s.node, - "latency": latency, - }}, + stats := map[string]interface{}{ + "id": s.istanbulBackend.ValidatorAddress().String(), + "latency": latency, } - return conn.WriteJSON(stats) + return s.sendStats(conn, actionLatency, stats) }   // blockStats is the information to report about individual blocks. @@ -570,12 +854,13 @@ Timestamp *big.Int `json:"timestamp"` Miner common.Address `json:"miner"` GasUsed uint64 `json:"gasUsed"` GasLimit uint64 `json:"gasLimit"` - Diff string `json:"difficulty"` TotalDiff string `json:"totalDifficulty"` Txs []txStats `json:"transactions"` TxHash common.Hash `json:"transactionsRoot"` Root common.Hash `json:"stateRoot"` - Uncles uncleStats `json:"uncles"` + EpochSize uint64 `json:"epochSize"` + BlockRemain uint64 `json:"blockRemain"` + Validators validatorSet `json:"validators"` }   // txStats is the information to report about individual transactions. @@ -583,15 +868,96 @@ type txStats struct { Hash common.Hash `json:"hash"` }   -// uncleStats is a custom wrapper around an uncle array to force serializing -// empty arrays instead of returning null for them. -type uncleStats []*types.Header +func (s *Service) signStats(stats interface{}) (map[string]interface{}, error) { + msg, err := json.Marshal(stats) + if err != nil { + return nil, err + } + msgHash := crypto.Keccak256Hash(msg) + validator := s.istanbulBackend.ValidatorAddress()   -func (s uncleStats) MarshalJSON() ([]byte, error) { - if uncles := ([]*types.Header)(s); len(uncles) > 0 { - return json.Marshal(uncles) + account := accounts.Account{Address: validator} + wallet, errWallet := s.backend.AccountManager().Find(account) + if errWallet != nil { + return nil, errWallet + } + + pubkey, errPubkey := wallet.GetPublicKey(account) + if errPubkey != nil { + return nil, errPubkey + } + pubkeyBytes := crypto.FromECDSAPub(pubkey) + + signature, errSign := wallet.SignData(account, accounts.MimetypeTypedData, msg) + if errSign != nil { + return nil, errSign + } + + proof := map[string]interface{}{ + "signature": hexutil.Encode(signature), + "address": validator, + "publicKey": hexutil.Encode(pubkeyBytes), + "msgHash": msgHash.Hex(), + } + + /* Server-side verification in go: */ + // sig := signature[:len(signature)-1] + // verified := crypto.VerifySignature(pubkey, msgHash.Bytes(), sig) + // & address == crypto.PubkeyToAddress(*pubkey).Hex() + + /* Client-side verification in JS: */ + // const { Keccak } = require('sha3'); + // const EC = require('elliptic').ec; + // const addressHasher = new Keccak(256) + // addressHasher.update(publicKey.substr(4), 'hex') + // const msgHasher = new Keccak(256) + // msgHasher.update(JSON.stringify(stats)) + // const ec = new EC('secp256k1'); + // const pubkey = ec.keyFromPublic(publicKey.substr(2), 'hex') + // const signature = { + // r : signature.substr(2, 64), + // s : signature.substr(66, 64) + // } + // verified = pubkey.verify(msgHash, signature) + // && address == addressHasher.digest('hex').substr(24) + // && msgHash == msgHasher.digest('hex') + + signedStats := map[string]interface{}{ + "stats": stats, + "proof": proof, } - return []byte("[]"), nil + + return signedStats, nil +} + +func (s *Service) sendStats(conn *connWrapper, action string, stats interface{}) error { + if s.istanbulBackend.IsProxy() { + statsWithAction := map[string]interface{}{ + "stats": stats, + "action": action, + } + msg, err := json.Marshal(statsWithAction) + if err != nil { + return err + } + go func() { + err := s.istanbulBackend.SendDelegateSignMsgToProxiedValidator(msg) + if err != nil { + log.Warn("Failed to delegate", "err", err) + conn.Close() + } + }() + return nil + } + signedStats, err := s.signStats(stats) + if err != nil { + return err + } + + report := map[string][]interface{}{ + "emit": {action, signedStats}, + } + return conn.WriteJSON(report) }   // reportBlock retrieves the current chain head and reports it to the stats server. @@ -603,13 +969,10 @@ // Assemble the block report and send it to the server log.Trace("Sending new block to ethstats", "number", details.Number, "hash", details.Hash)   stats := map[string]interface{}{ - "id": s.node, + "id": s.istanbulBackend.ValidatorAddress().String(), "block": details, } - report := map[string][]interface{}{ - "emit": {"block", stats}, - } - return conn.WriteJSON(report) + return s.sendStats(conn, actionBlock, stats) }   // assembleBlockStats retrieves any required metadata to report a single block @@ -620,7 +983,8 @@ var ( header *types.Header td *big.Int txs []txStats - uncles []*types.Header + valSet validatorSet + gasLimit uint64 )   // check if backend is a full node @@ -630,26 +994,42 @@ if block == nil { block = fullBackend.CurrentBlock() } header = block.Header() - td = fullBackend.GetTd(context.Background(), header.Hash()) - txs = make([]txStats, len(block.Transactions())) for i, tx := range block.Transactions() { txs[i].Hash = tx.Hash() } - uncles = block.Uncles() } else { - // Light nodes would need on-demand lookups for transactions/uncles, skip + // Light nodes would need on-demand lookups for transactions, skip if block != nil { header = block.Header() } else { header = s.backend.CurrentHeader() } - td = s.backend.GetTd(context.Background(), header.Hash()) txs = []txStats{} } + td = s.backend.GetTd(context.Background(), header.Hash())   // Assemble and return the block stats author, _ := s.engine.Author(header) + + // Add epoch info + epochSize := s.engine.EpochSize() + blockRemain := epochSize - istanbul.GetNumberWithinEpoch(header.Number.Uint64(), epochSize) + + stateDB, _, err := s.backend.StateAndHeaderByNumberOrHash(context.Background(), rpc.BlockNumberOrHashWithHash(header.Hash(), true)) + + if err != nil { + log.Warn("Block state unavailable for reporting block stats", "hash", header.Hash(), "number", header.Number.Uint64(), "err", err) + } else { + + // only assemble every valSetInterval blocks + if block != nil && block.Number().Uint64()%valSetInterval == 0 { + valSet = s.assembleValidatorSet(block, stateDB) + } + + vmRunner := s.backend.NewEVMRunner(header, stateDB) + gasLimit = blockchain_parameters.GetBlockGasLimitOrDefault(vmRunner) + }   return &blockStats{ Number: header.Number, @@ -658,14 +1038,74 @@ ParentHash: header.ParentHash, Timestamp: new(big.Int).SetUint64(header.Time), Miner: author, GasUsed: header.GasUsed, - GasLimit: header.GasLimit, - Diff: header.Difficulty.String(), + GasLimit: gasLimit, TotalDiff: td.String(), Txs: txs, TxHash: header.TxHash, Root: header.Root, - Uncles: uncles, + EpochSize: epochSize, + BlockRemain: blockRemain, + Validators: valSet, + } +} + +type validatorSet struct { + Registered []validatorInfo `json:"registered"` + Elected []common.Address `json:"elected"` +} + +type validatorInfo struct { + Address common.Address `json:"address"` + Score string `json:"score"` + BLSPublicKey []byte `json:"blsPublicKey"` + EcdsaPublicKey []byte `json:"ecdsaPublicKey"` + Affiliation common.Address `json:"affiliation"` + Signer common.Address `json:"signer"` +} + +func (s *Service) assembleValidatorSet(block *types.Block, state vm.StateDB) validatorSet { + var ( + valSet validatorSet + valsRegistered []validatorInfo + valsElected []common.Address + ) + + vmRunner := s.backend.NewEVMRunner(block.Header(), state) + + // Add set of registered validators + valsRegisteredMap, _ := validators.RetrieveRegisteredValidators(vmRunner) + valsRegistered = make([]validatorInfo, 0, len(valsRegisteredMap)) + for _, address := range valsRegisteredMap { + valData, err := validators.GetValidator(vmRunner, address) + + if err != nil { + log.Warn("Validator data not found", "address", address.Hex(), "err", err) + } + + valsRegistered = append(valsRegistered, validatorInfo{ + Address: address, + Score: fmt.Sprintf("%d", valData.Score), + BLSPublicKey: valData.BlsPublicKey, + EcdsaPublicKey: valData.EcdsaPublicKey, + Affiliation: valData.Affiliation, + Signer: valData.Signer, + }) + } + + // Add addresses of elected validators + valsElectedList := s.istanbulBackend.GetValidators(block.Number(), block.Hash()) + + valsElected = make([]common.Address, 0, len(valsElectedList)) + for i := range valsElectedList { + valsElected = append(valsElected, valsElectedList[i].Address()) + } + + valSet = validatorSet{ + Elected: valsElected, + Registered: valsRegistered, } + + return valSet }   // reportHistory retrieves the most recent batch of blocks and reports it to the @@ -716,13 +1156,10 @@ } else { log.Trace("No history to send to stats server") } stats := map[string]interface{}{ - "id": s.node, + "id": s.istanbulBackend.ValidatorAddress().String(), "history": history, } - report := map[string][]interface{}{ - "emit": {"history", stats}, - } - return conn.WriteJSON(report) + return s.sendStats(conn, actionHistory, stats) }   // pendStats is the information to report about pending transactions. @@ -739,15 +1176,12 @@ // Assemble the transaction stats and send it to the server log.Trace("Sending pending transactions to ethstats", "count", pending)   stats := map[string]interface{}{ - "id": s.node, + "id": s.istanbulBackend.ValidatorAddress().String(), "stats": &pendStats{ Pending: pending, }, } - report := map[string][]interface{}{ - "emit": {"pending", stats}, - } - return conn.WriteJSON(report) + return s.sendStats(conn, actionPending, stats) }   // nodeStats is the information to report about the local node. @@ -755,7 +1189,8 @@ type nodeStats struct { Active bool `json:"active"` Syncing bool `json:"syncing"` Mining bool `json:"mining"` - Hashrate int `json:"hashrate"` + Proxy bool `json:"proxy"` + Elected bool `json:"elected"` Peers int `json:"peers"` GasPrice int `json:"gasPrice"` Uptime int `json:"uptime"` @@ -766,25 +1201,39 @@ // mining layer and reports it to the stats server. func (s *Service) reportStats(conn *connWrapper) error { // Gather the syncing and mining infos from the local miner instance var ( + validatorAddress common.Address mining bool - hashrate int + proxy bool + elected bool syncing bool gasprice int ) // check if backend is a full node fullBackend, ok := s.backend.(fullNodeBackend) if ok { + validatorAddress = s.istanbulBackend.ValidatorAddress() + block := fullBackend.CurrentBlock() + + proxy = s.istanbulBackend.IsProxy() mining = fullBackend.Miner().Mining() - hashrate = int(fullBackend.Miner().Hashrate()) + + elected = false + valsElected := s.istanbulBackend.GetValidators(block.Number(), block.Hash()) + + for i := range valsElected { + if valsElected[i].Address() == validatorAddress { + elected = true + } + }   sync := fullBackend.SyncProgress() syncing = fullBackend.CurrentHeader().Number.Uint64() >= sync.HighestBlock   - price, _ := fullBackend.SuggestGasTipCap(context.Background()) + price, _ := fullBackend.CurrentGasPriceMinimum(context.Background(), nil) gasprice = int(price.Uint64()) - if basefee := fullBackend.CurrentHeader().BaseFee; basefee != nil { - gasprice += int(basefee.Uint64()) - } + tip, _ := fullBackend.SuggestGasTipCap(context.Background(), nil) + gasprice += int(tip.Uint64()) + } else { sync := s.backend.SyncProgress() syncing = s.backend.CurrentHeader().Number.Uint64() >= sync.HighestBlock @@ -793,19 +1242,19 @@ // Assemble the node stats and send it to the server log.Trace("Sending node details to ethstats")   stats := map[string]interface{}{ - "id": s.node, + "id": s.istanbulBackend.ValidatorAddress().String(), + "address": validatorAddress, "stats": &nodeStats{ Active: true, Mining: mining, - Hashrate: hashrate, + Elected: elected, + Proxy: proxy, Peers: s.server.PeerCount(), GasPrice: gasprice, Syncing: syncing, Uptime: 100, }, } - report := map[string][]interface{}{ - "emit": {"stats", stats}, - } - return conn.WriteJSON(report) + + return s.sendStats(conn, actionStats, stats) }
diff --git go-ethereum/ethstats/ethstats_test.go celo/ethstats/ethstats_test.go index 9e9ddba629c7b39fe0c1be9fa4f5beb32f9eef0b..880434f08ac9652c50c30cb26402ea7111d4e5c4 100644 --- go-ethereum/ethstats/ethstats_test.go +++ celo/ethstats/ethstats_test.go @@ -11,56 +11,55 @@ url string node, pass, host string }{ { - url: `"debug meowsbits":mypass@ws://mordor.dash.fault.dev:3000`, - node: "debug meowsbits", pass: "mypass", host: "ws://mordor.dash.fault.dev:3000", + url: `"debug meowsbits"@ws://mordor.dash.fault.dev:3000`, + node: "debug meowsbits", host: "ws://mordor.dash.fault.dev:3000", }, { - url: `"debug @meowsbits":mypass@ws://mordor.dash.fault.dev:3000`, - node: "debug @meowsbits", pass: "mypass", host: "ws://mordor.dash.fault.dev:3000", + url: `"debug @meowsbits"@ws://mordor.dash.fault.dev:3000`, + node: "debug @meowsbits", host: "ws://mordor.dash.fault.dev:3000", }, { - url: `"debug: @meowsbits":mypass@ws://mordor.dash.fault.dev:3000`, - node: "debug: @meowsbits", pass: "mypass", host: "ws://mordor.dash.fault.dev:3000", + url: `"debug: @meowsbits"@ws://mordor.dash.fault.dev:3000`, + node: "debug: @meowsbits", host: "ws://mordor.dash.fault.dev:3000", }, { - url: `name:@ws://mordor.dash.fault.dev:3000`, - node: "name", pass: "", host: "ws://mordor.dash.fault.dev:3000", + url: `name@ws://mordor.dash.fault.dev:3000`, + node: "name", host: "ws://mordor.dash.fault.dev:3000", }, { url: `name@ws://mordor.dash.fault.dev:3000`, - node: "name", pass: "", host: "ws://mordor.dash.fault.dev:3000", + node: "name", host: "ws://mordor.dash.fault.dev:3000", }, { - url: `:mypass@ws://mordor.dash.fault.dev:3000`, - node: "", pass: "mypass", host: "ws://mordor.dash.fault.dev:3000", + url: `@ws://mordor.dash.fault.dev:3000`, + node: "", host: "ws://mordor.dash.fault.dev:3000", }, { - url: `:@ws://mordor.dash.fault.dev:3000`, - node: "", pass: "", host: "ws://mordor.dash.fault.dev:3000", + url: `@ws://mordor.dash.fault.dev:3000`, + node: "", host: "ws://mordor.dash.fault.dev:3000", }, }   for i, c := range cases { - parts, err := parseEthstatsURL(c.url) - if err != nil { + var ( + name string + celostatsHost string + ) + if err := parseEthstatsURL(c.url, &name, &celostatsHost); err != nil { t.Fatal(err) } - node, pass, host := parts[0], parts[1], parts[2]   // unquote because the value provided will be used as a CLI flag value, so unescaped quotes will be removed - nodeUnquote, err := strconv.Unquote(node) + nodeUnquote, err := strconv.Unquote(name) if err == nil { - node = nodeUnquote + name = nodeUnquote }   - if node != c.node { - t.Errorf("case=%d mismatch node value, got: %v ,want: %v", i, node, c.node) - } - if pass != c.pass { - t.Errorf("case=%d mismatch pass value, got: %v ,want: %v", i, pass, c.pass) + if name != c.node { + t.Errorf("case=%d mismatch node value, got: %v ,want: %v", i, name, c.node) } - if host != c.host { - t.Errorf("case=%d mismatch host value, got: %v ,want: %v", i, host, c.host) + if celostatsHost != c.host { + t.Errorf("case=%d mismatch host value, got: %v ,want: %v", i, celostatsHost, c.host) } }
diff --git go-ethereum/graphql/graphql.go celo/graphql/graphql.go index aef57c37240c799b5b18ee9f2fc4a389359603dd..b382213d2a4348965783522b2f285ccc495f96b9 100644 --- go-ethereum/graphql/graphql.go +++ celo/graphql/graphql.go @@ -246,14 +246,16 @@ tx, err := t.resolve(ctx) if err != nil || tx == nil { return nil, err } - header, err := t.block.resolveHeader(ctx) - if err != nil || header == nil { - return nil, err - } - if header.BaseFee == nil { - return (*hexutil.Big)(tx.GasPrice()), nil + switch tx.Type() { + case types.DynamicFeeTxType: + if t.block != nil { + if baseFee, _ := t.block.BaseFeePerGas(ctx); baseFee != nil { + // price = min(tip, gasFeeCap - baseFee) + baseFee + return (*hexutil.Big)(math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee.ToInt()), tx.GasFeeCap())), nil + } + } } - return (*hexutil.Big)(math.BigMin(new(big.Int).Add(tx.GasTipCap(), header.BaseFee), tx.GasFeeCap())), nil + return nil, nil }   func (t *Transaction) MaxFeePerGas(ctx context.Context) (*hexutil.Big, error) { @@ -512,7 +514,7 @@ }   // resolveHeader returns the internal Header object for this block, fetching it // if necessary. Call this function instead of `resolve` unless you need the -// additional data (transactions and uncles). +// additional data (transactions). func (b *Block) resolveHeader(ctx context.Context) (*types.Header, error) { if b.numberOrHash == nil && b.hash == (common.Hash{}) { return nil, errBlockInvariant @@ -569,14 +571,6 @@ } return b.hash, nil }   -func (b *Block) GasLimit(ctx context.Context) (Long, error) { - header, err := b.resolveHeader(ctx) - if err != nil { - return 0, err - } - return Long(header.GasLimit), nil -} - func (b *Block) GasUsed(ctx context.Context) (Long, error) { header, err := b.resolveHeader(ctx) if err != nil { @@ -586,14 +580,22 @@ return Long(header.GasUsed), nil }   func (b *Block) BaseFeePerGas(ctx context.Context) (*hexutil.Big, error) { + // To have an external API as compatible to geth as possible, we return the + // base for celo gold, here. We can add a separate variable for the base + // fees for each currency. + return b.BaseFeePerGasForCurrency(ctx, nil) +} + +func (b *Block) BaseFeePerGasForCurrency(ctx context.Context, feeCurrency *common.Address) (*hexutil.Big, error) { header, err := b.resolveHeader(ctx) if err != nil { return nil, err } - if header.BaseFee == nil { - return nil, nil + baseFee, err := b.backend.GasPriceMinimumForHeader(ctx, feeCurrency, header) + if err != nil { + return nil, err } - return (*hexutil.Big)(header.BaseFee), nil + return (*hexutil.Big)(baseFee), nil }   func (b *Block) Parent(ctx context.Context) (*Block, error) { @@ -614,14 +616,6 @@ } return nil, nil }   -func (b *Block) Difficulty(ctx context.Context) (hexutil.Big, error) { - header, err := b.resolveHeader(ctx) - if err != nil { - return hexutil.Big{}, err - } - return hexutil.Big(*header.Difficulty), nil -} - func (b *Block) Timestamp(ctx context.Context) (hexutil.Uint64, error) { header, err := b.resolveHeader(ctx) if err != nil { @@ -630,22 +624,6 @@ } return hexutil.Uint64(header.Time), nil }   -func (b *Block) Nonce(ctx context.Context) (hexutil.Bytes, error) { - header, err := b.resolveHeader(ctx) - if err != nil { - return hexutil.Bytes{}, err - } - return header.Nonce[:], nil -} - -func (b *Block) MixHash(ctx context.Context) (common.Hash, error) { - header, err := b.resolveHeader(ctx) - if err != nil { - return common.Hash{}, err - } - return header.MixDigest, nil -} - func (b *Block) TransactionsRoot(ctx context.Context) (common.Hash, error) { header, err := b.resolveHeader(ctx) if err != nil { @@ -670,40 +648,6 @@ } return header.ReceiptHash, nil }   -func (b *Block) OmmerHash(ctx context.Context) (common.Hash, error) { - header, err := b.resolveHeader(ctx) - if err != nil { - return common.Hash{}, err - } - return header.UncleHash, nil -} - -func (b *Block) OmmerCount(ctx context.Context) (*int32, error) { - block, err := b.resolve(ctx) - if err != nil || block == nil { - return nil, err - } - count := int32(len(block.Uncles())) - return &count, err -} - -func (b *Block) Ommers(ctx context.Context) (*[]*Block, error) { - block, err := b.resolve(ctx) - if err != nil || block == nil { - return nil, err - } - ret := make([]*Block, 0, len(block.Uncles())) - for _, uncle := range block.Uncles() { - blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false) - ret = append(ret, &Block{ - backend: b.backend, - numberOrHash: &blockNumberOrHash, - header: uncle, - }) - } - return &ret, nil -} - func (b *Block) ExtraData(ctx context.Context) (hexutil.Bytes, error) { header, err := b.resolveHeader(ctx) if err != nil { @@ -815,24 +759,6 @@ hash: tx.Hash(), tx: tx, block: b, index: uint64(args.Index), - }, nil -} - -func (b *Block) OmmerAt(ctx context.Context, args struct{ Index int32 }) (*Block, error) { - block, err := b.resolve(ctx) - if err != nil || block == nil { - return nil, err - } - uncles := block.Uncles() - if args.Index < 0 || int(args.Index) >= len(uncles) { - return nil, nil - } - uncle := uncles[args.Index] - blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false) - return &Block{ - backend: b.backend, - numberOrHash: &blockNumberOrHash, - header: uncle, }, nil }   @@ -1191,18 +1117,21 @@ return runFilter(ctx, r.backend, filter) }   func (r *Resolver) GasPrice(ctx context.Context) (hexutil.Big, error) { - tipcap, err := r.backend.SuggestGasTipCap(ctx) - if err != nil { - return hexutil.Big{}, err - } - if head := r.backend.CurrentHeader(); head.BaseFee != nil { - tipcap.Add(tipcap, head.BaseFee) - } - return (hexutil.Big)(*tipcap), nil + price, err := r.backend.SuggestPrice(ctx, nil) + return hexutil.Big(*price), err + + // tipcap, err := r.backend.SuggestGasTipCap(ctx) + // if err != nil { + // return hexutil.Big{}, err + // } + // if head := r.backend.CurrentHeader(); head.BaseFee != nil { + // tipcap.Add(tipcap, head.BaseFee) + // } + // return (hexutil.Big)(*tipcap), nil }   func (r *Resolver) MaxPriorityFeePerGas(ctx context.Context) (hexutil.Big, error) { - tipcap, err := r.backend.SuggestGasTipCap(ctx) + tipcap, err := r.backend.SuggestGasTipCap(ctx, nil) if err != nil { return hexutil.Big{}, err }
diff --git go-ethereum/graphql/graphql_test.go celo/graphql/graphql_test.go index 9cb1dfb1cd8539220574e12e7f6ee7c7a9988615..decb19cf0bc1e566dd8c32808040989ce95f7960 100644 --- go-ethereum/graphql/graphql_test.go +++ celo/graphql/graphql_test.go @@ -26,7 +26,7 @@ "testing" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -77,52 +77,52 @@ want: `{"data":{"block":{"number":10}}}`, code: 200, }, { // Should return info about latest block - body: `{"query": "{block{number,gasUsed,gasLimit}}","variables": null}`, - want: `{"data":{"block":{"number":10,"gasUsed":0,"gasLimit":11500000}}}`, + body: `{"query": "{block{number,gasUsed}}","variables": null}`, + want: `{"data":{"block":{"number":10,"gasUsed":0}}}`, code: 200, }, { - body: `{"query": "{block(number:0){number,gasUsed,gasLimit}}","variables": null}`, - want: `{"data":{"block":{"number":0,"gasUsed":0,"gasLimit":11500000}}}`, + body: `{"query": "{block(number:0){number,gasUsed}}","variables": null}`, + want: `{"data":{"block":{"number":0,"gasUsed":0}}}`, code: 200, }, { - body: `{"query": "{block(number:-1){number,gasUsed,gasLimit}}","variables": null}`, + body: `{"query": "{block(number:-1){number,gasUsed}}","variables": null}`, want: `{"data":{"block":null}}`, code: 200, }, { - body: `{"query": "{block(number:-500){number,gasUsed,gasLimit}}","variables": null}`, + body: `{"query": "{block(number:-500){number,gasUsed}}","variables": null}`, want: `{"data":{"block":null}}`, code: 200, }, { - body: `{"query": "{block(number:\"0\"){number,gasUsed,gasLimit}}","variables": null}`, - want: `{"data":{"block":{"number":0,"gasUsed":0,"gasLimit":11500000}}}`, + body: `{"query": "{block(number:\"0\"){number,gasUsed}}","variables": null}`, + want: `{"data":{"block":{"number":0,"gasUsed":0}}}`, code: 200, }, { - body: `{"query": "{block(number:\"-33\"){number,gasUsed,gasLimit}}","variables": null}`, + body: `{"query": "{block(number:\"-33\"){number,gasUsed}}","variables": null}`, want: `{"data":{"block":null}}`, code: 200, }, { - body: `{"query": "{block(number:\"1337\"){number,gasUsed,gasLimit}}","variables": null}`, + body: `{"query": "{block(number:\"1337\"){number,gasUsed}}","variables": null}`, want: `{"data":{"block":null}}`, code: 200, }, { - body: `{"query": "{block(number:\"0xbad\"){number,gasUsed,gasLimit}}","variables": null}`, + body: `{"query": "{block(number:\"0xbad\"){number,gasUsed}}","variables": null}`, want: `{"errors":[{"message":"strconv.ParseInt: parsing \"0xbad\": invalid syntax"}],"data":{}}`, code: 400, }, { // hex strings are currently not supported. If that's added to the spec, this test will need to change - body: `{"query": "{block(number:\"0x0\"){number,gasUsed,gasLimit}}","variables": null}`, + body: `{"query": "{block(number:\"0x0\"){number,gasUsed}}","variables": null}`, want: `{"errors":[{"message":"strconv.ParseInt: parsing \"0x0\": invalid syntax"}],"data":{}}`, code: 400, }, { - body: `{"query": "{block(number:\"a\"){number,gasUsed,gasLimit}}","variables": null}`, + body: `{"query": "{block(number:\"a\"){number,gasUsed}}","variables": null}`, want: `{"errors":[{"message":"strconv.ParseInt: parsing \"a\": invalid syntax"}],"data":{}}`, code: 400, }, @@ -161,6 +161,43 @@ } } }   +// Tests that a graphQL request is successfully handled when graphql is enabled on the specified endpoint +func TestGraphQLTransactionSerialization(t *testing.T) { + stack := createNode(t, true, true) + defer stack.Close() + // start node + if err := stack.Start(); err != nil { + t.Fatalf("could not start node: %v", err) + } + + for i, tt := range []struct { + body string + want string + code int + }{ + { + body: `{"query":"{ transaction(hash: \"0x22f565cfeb33d5e6f81c8923ef0633a49fef0848a089a6d8564b655d5605fb13\") { gas gasUsed gasPrice maxFeePerGas maxPriorityFeePerGas effectiveGasPrice index from { address } to { address } value inputData block { transactionCount baseFeePerGas } status type }}"}`, + want: `{"data":{"transaction":{"gas":"0xc350","gasUsed":22604,"gasPrice":"0xa","maxFeePerGas":"0x7530","maxPriorityFeePerGas":"0xa","effectiveGasPrice":"0xa","index":2,"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x32","inputData":"0x","block":{"transactionCount":3,"baseFeePerGas":"0x0"},"status":1,"type":2}}}`, + code: 200, + }, + } { + resp, err := http.Post(fmt.Sprintf("%s/graphql", stack.HTTPEndpoint()), "application/json", strings.NewReader(tt.body)) + if err != nil { + t.Fatalf("could not post: %v", err) + } + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("could not read from response body: %v", err) + } + if have := string(bodyBytes); have != tt.want { + t.Errorf("testcase %d %s,\nhave:\n%v\nwant:\n%v", i, tt.body, have, tt.want) + } + if tt.code != resp.StatusCode { + t.Errorf("testcase %d %s,\nwrong statuscode, have: %v, want: %v", i, tt.body, resp.StatusCode, tt.code) + } + } +} + func TestGraphQLBlockSerializationEIP2718(t *testing.T) { stack := createNode(t, true, true) defer stack.Close() @@ -176,7 +213,7 @@ code int }{ { body: `{"query": "{block {number transactions { from { address } to { address } value hash type accessList { address storageKeys } index}}}"}`, - want: `{"data":{"block":{"number":1,"transactions":[{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x64","hash":"0xd864c9d7d37fade6b70164740540c06dd58bb9c3f6b46101908d6339db6a6a7b","type":0,"accessList":[],"index":0},{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x32","hash":"0x19b35f8187b4e15fb59a9af469dca5dfa3cd363c11d372058c12f6482477b474","type":1,"accessList":[{"address":"0x0000000000000000000000000000000000000dad","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000000"]}],"index":1}]}}}`, + want: `{"data":{"block":{"number":1,"transactions":[{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x64","hash":"0x46933b8a43e70320bb41910f015c4b2aded1caaba32b55b56054e4d1811d06d6","type":0,"accessList":[],"index":0},{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x32","hash":"0x03682ed7cc9cf9e9fa3bb6f58a62d6a350c09ec4d22b71b884503ae469e8640b","type":1,"accessList":[{"address":"0x0000000000000000000000000000000000000dad","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000000"]}],"index":1},{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x32","hash":"0x22f565cfeb33d5e6f81c8923ef0633a49fef0848a089a6d8564b655d5605fb13","type":2,"accessList":[],"index":2}]}}}`, code: 200, }, } { @@ -238,12 +275,7 @@ func createGQLService(t *testing.T, stack *node.Node) { // create backend ethConf := &ethconfig.Config{ Genesis: &core.Genesis{ - Config: params.AllEthashProtocolChanges, - GasLimit: 11500000, - Difficulty: big.NewInt(1048576), - }, - Ethash: ethash.Config{ - PowMode: ethash.ModeFake, + Config: params.IstanbulTestChainConfig, }, NetworkId: 1337, TrieCleanCache: 5, @@ -252,14 +284,16 @@ TrieCleanCacheRejournal: 60 * time.Minute, TrieDirtyCache: 5, TrieTimeout: 60 * time.Minute, SnapshotCache: 5, + RPCGasInflationRate: 1, } + ethConf.Genesis.Config.Faker = true ethBackend, err := eth.New(stack, ethConf) if err != nil { t.Fatalf("could not create eth backend: %v", err) } // Create some blocks and import them - chain, _ := core.GenerateChain(params.AllEthashProtocolChanges, ethBackend.BlockChain().Genesis(), - ethash.NewFaker(), ethBackend.ChainDb(), 10, func(i int, gen *core.BlockGen) {}) + chain, _ := core.GenerateChain(params.IstanbulTestChainConfig, ethBackend.BlockChain().Genesis(), + mockEngine.NewFaker(), ethBackend.ChainDb(), 10, func(i int, gen *core.BlockGen) {}) _, err = ethBackend.BlockChain().InsertChain(chain) if err != nil { t.Fatalf("could not create import blocks: %v", err) @@ -280,9 +314,7 @@ dad := common.HexToAddress("0x0000000000000000000000000000000000000dad")   ethConf := &ethconfig.Config{ Genesis: &core.Genesis{ - Config: params.AllEthashProtocolChanges, - GasLimit: 11500000, - Difficulty: big.NewInt(1048576), + Config: params.IstanbulEHFTestChainConfig, Alloc: core.GenesisAlloc{ address: {Balance: funds}, // The address 0xdad sloads 0x00 and 0x01 @@ -297,11 +329,8 @@ Nonce: 0, Balance: big.NewInt(0), }, }, - BaseFee: big.NewInt(params.InitialBaseFee), }, - Ethash: ethash.Config{ - PowMode: ethash.ModeFake, - }, + NetworkId: 1337, TrieCleanCache: 5, TrieCleanCacheJournal: "triecache", @@ -310,6 +339,7 @@ TrieDirtyCache: 5, TrieTimeout: 60 * time.Minute, SnapshotCache: 5, } + ethConf.Genesis.Config.Faker = true   ethBackend, err := eth.New(stack, ethConf) if err != nil { @@ -317,32 +347,43 @@ t.Fatalf("could not create eth backend: %v", err) } signer := types.LatestSigner(ethConf.Genesis.Config)   + gp := core.MockSysContractCallCtx().GetGasPriceMinimum(nil) legacyTx, _ := types.SignNewTx(key, signer, &types.LegacyTx{ Nonce: uint64(0), To: &dad, Value: big.NewInt(100), Gas: 50000, - GasPrice: big.NewInt(params.InitialBaseFee), + GasPrice: gp, }) envelopTx, _ := types.SignNewTx(key, signer, &types.AccessListTx{ ChainID: ethConf.Genesis.Config.ChainID, Nonce: uint64(1), To: &dad, Gas: 30000, - GasPrice: big.NewInt(params.InitialBaseFee), + GasPrice: gp, Value: big.NewInt(50), AccessList: types.AccessList{{ Address: dad, StorageKeys: []common.Hash{{0}}, }}, }) + dynamicTx, _ := types.SignNewTx(key, signer, &types.DynamicFeeTx{ + ChainID: ethConf.Genesis.Config.ChainID, + Nonce: uint64(2), + To: &dad, + Gas: 50000, + GasFeeCap: big.NewInt(30000), + GasTipCap: big.NewInt(10), + Value: big.NewInt(50), + })   // Create some blocks and import them - chain, _ := core.GenerateChain(params.AllEthashProtocolChanges, ethBackend.BlockChain().Genesis(), - ethash.NewFaker(), ethBackend.ChainDb(), 1, func(i int, b *core.BlockGen) { + chain, _ := core.GenerateChain(params.IstanbulEHFTestChainConfig, ethBackend.BlockChain().Genesis(), + mockEngine.NewFaker(), ethBackend.ChainDb(), 1, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{1}) b.AddTx(legacyTx) b.AddTx(envelopTx) + b.AddTx(dynamicTx) })   _, err = ethBackend.BlockChain().InsertChain(chain)
diff --git go-ethereum/graphql/schema.go celo/graphql/schema.go index 2eb6682b5c8da89bf52be38a2768debee4852438..4926ff43cc60d8f2f39018c3e8b44f7655834e09 100644 --- go-ethereum/graphql/schema.go +++ celo/graphql/schema.go @@ -168,8 +168,6 @@ # Hash is the block hash of this block. hash: Bytes32! # Parent is the parent block of this block. parent: Block - # Nonce is the block nonce, an 8 byte sequence determined by the miner. - nonce: Bytes! # TransactionsRoot is the keccak256 hash of the root of the trie of transactions in this block. transactionsRoot: Bytes32! # TransactionCount is the number of transactions in this block. if @@ -183,8 +181,6 @@ # Miner is the account that mined this block. miner(block: Long): Account! # ExtraData is an arbitrary data field supplied by the miner. extraData: Bytes! - # GasLimit is the maximum amount of gas that was available to transactions in this block. - gasLimit: Long! # GasUsed is the amount of gas that was used executing transactions in this block. gasUsed: Long! # BaseFeePerGas is the fee perunit of gas burned by the protocol in this block. @@ -194,29 +190,9 @@ timestamp: Long! # LogsBloom is a bloom filter that can be used to check if a block may # contain log entries matching a filter. logsBloom: Bytes! - # MixHash is the hash that was used as an input to the PoW process. - mixHash: Bytes32! - # Difficulty is a measure of the difficulty of mining this block. - difficulty: BigInt! # TotalDifficulty is the sum of all difficulty values up to and including # this block. totalDifficulty: BigInt! - # OmmerCount is the number of ommers (AKA uncles) associated with this - # block. If ommers are unavailable, this field will be null. - ommerCount: Int - # Ommers is a list of ommer (AKA uncle) blocks associated with this block. - # If ommers are unavailable, this field will be null. Depending on your - # node, the transactions, transactionAt, transactionCount, ommers, - # ommerCount and ommerAt fields may not be available on any ommer blocks. - ommers: [Block] - # OmmerAt returns the ommer (AKA uncle) at the specified index. If ommers - # are unavailable, or the index is out of bounds, this field will be null. - ommerAt(index: Int!): Block - # OmmerHash is the keccak256 hash of all the ommers (AKA uncles) - # associated with this block. - ommerHash: Bytes32! - # Transactions is a list of transactions associated with this block. If - # transactions are unavailable for this block, this field will be null. transactions: [Transaction!] # TransactionAt returns the transaction at the specified index. If # transactions are unavailable for this block, or if the index is out of
diff --git go-ethereum/internal/ethapi/util.go celo/internal/ethapi/util.go new file mode 100644 index 0000000000000000000000000000000000000000..7bda37ef52cebbbf0aa2d3175d27b5962588296e --- /dev/null +++ celo/internal/ethapi/util.go @@ -0,0 +1,77 @@ +package ethapi + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/currency" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" +) + +// CheckTxFee is an internal function used to check whether the fee of +// the given transaction is _reasonable_(under the cap). +func CheckTxFee(cp currency.Provider, feeCurrencyAddress *common.Address, fee *big.Int, cap float64) error { + // Short circuit if there is no cap for transaction fee at all. + if cap == 0 { + return nil + } + weiCap := getWei(cap) + feeCurrency, err := cp.GetCurrency(feeCurrencyAddress) + if err != nil { + return fmt.Errorf("Can't check tx fee cap: %s", err) + } + if feeCurrency.CmpToCurrency(fee, weiCap, &currency.CELOCurrency) > 0 { + feeFloat := float64(fee.Uint64()) + feeFloat /= params.Ether + if feeCurrencyAddress != nil { + return fmt.Errorf("tx fee (%.2f of currency address '%s') exceeds the configured cap (%.2f celo)", feeFloat, feeCurrencyAddress.Hex(), cap) + } else { + return fmt.Errorf("tx fee (%.2f of currency celo) exceeds the configured cap (%.2f celo)", feeFloat, cap) + } + } + return nil +} + +// getWei converts a celo float to a big.Int Wei representation +func getWei(celo float64) *big.Int { + floatWei := new(big.Float).Mul(big.NewFloat(params.Ether), big.NewFloat(celo)) + wei, _ := floatWei.Int(nil) + return wei +} + +func checkFeeFromCeloTx(ctx context.Context, b Backend, tx *types.Transaction) error { + currencyManager, err := newCurrencyManager(ctx, b) + if err != nil { + return err + } + return CheckTxFee(currencyManager, tx.FeeCurrency(), tx.Fee(), b.RPCTxFeeCap()) +} + +func checkFeeFromCeloCurrency(ctx context.Context, b Backend, feeCurrency *common.Address, gasPrice *big.Int, gas uint64, gatewayFee *big.Int) error { + currencyManager, err := newCurrencyManager(ctx, b) + if err != nil { + return err + } + gFee := gatewayFee + if gFee == nil { + gFee = big.NewInt(0) + } + fee := types.Fee(gasPrice, gas, gFee) + return CheckTxFee(currencyManager, feeCurrency, fee, b.RPCTxFeeCap()) +} + +// newCurrencyManager creates and returns a currencyManager pointing to the latest block +// from the underlying chain from the Backend. +func newCurrencyManager(ctx context.Context, b Backend) (*currency.CurrencyManager, error) { + stateDb, header, err := b.StateAndHeaderByNumber(ctx, rpc.LatestBlockNumber) + if err != nil { + return nil, err + } + + vmRunner := b.NewEVMRunner(header, stateDb) + return currency.NewManager(vmRunner), nil +}
diff --git go-ethereum/internal/ethapi/api_test.go celo/internal/ethapi/api_test.go new file mode 100644 index 0000000000000000000000000000000000000000..db4aa41024a67a47ef48424ad5eba681143b42d8 --- /dev/null +++ celo/internal/ethapi/api_test.go @@ -0,0 +1,56 @@ +package ethapi + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/assert" +) + +// TestNewRPCTransactionDynamic tests the newRPCTransaction method with a dynamic fee tx type. +func TestNewRPCTransactionDynamic(t *testing.T) { + baseFee := big.NewInt(600) + blockHash := common.BigToHash(big.NewInt(123456)) + blockNumber := uint64(123456) + index := uint64(7) + chainId := big.NewInt(1234567) + gasTipCap := big.NewInt(888400) + bigFeeCap := big.NewInt(1999001) + smallFeeCap := big.NewInt(111222) + baseFeeFn := func() (*big.Int, error) { + return baseFee, nil + } + + t.Run("GasPrice == GasFeeCap", func(*testing.T) { + rpcTx := newRPCTransaction(types.NewTx(&types.DynamicFeeTx{ + ChainID: chainId, + + GasFeeCap: smallFeeCap, + GasTipCap: gasTipCap, + }), blockHash, blockNumber, index, baseFeeFn, true) + assert.Equal(t, (*hexutil.Big)(smallFeeCap), rpcTx.GasPrice) + }) + + t.Run("GasPrice == GasTipCap + baseFee", func(*testing.T) { + rpcTx2 := newRPCTransaction(types.NewTx(&types.DynamicFeeTx{ + ChainID: chainId, + + GasFeeCap: bigFeeCap, + GasTipCap: gasTipCap, + }), blockHash, blockNumber, index, baseFeeFn, true) + assert.Equal(t, (*hexutil.Big)(big.NewInt(0).Add(gasTipCap, baseFee)), rpcTx2.GasPrice) + }) + + t.Run("Unminned transaction. GasPrice == GasFeeCap", func(t *testing.T) { + rpcTx := newRPCTransaction(types.NewTx(&types.DynamicFeeTx{ + ChainID: chainId, + + GasFeeCap: bigFeeCap, + GasTipCap: gasTipCap, + }), common.Hash{}, 0, 0, baseFeeFn, false) + assert.Equal(t, (*hexutil.Big)(bigFeeCap), rpcTx.GasPrice) + }) +}
diff --git go-ethereum/internal/cmdtest/test_cmd.go celo/internal/cmdtest/test_cmd.go index 52af56eac218ab3fe0505fa00fa4d97916d3c183..95cadeb86de0d58175b7aea85de63c33bd62a129 100644 --- go-ethereum/internal/cmdtest/test_cmd.go +++ celo/internal/cmdtest/test_cmd.go @@ -238,7 +238,7 @@ } }   func (tt *TestCmd) withKillTimeout(fn func()) { - timeout := time.AfterFunc(5*time.Second, func() { + timeout := time.AfterFunc(15*time.Second, func() { tt.Log("killing the child process (timeout)") tt.Kill() })
diff --git go-ethereum/internal/debug/flags.go celo/internal/debug/flags.go index 1d86dd921e6ebbd2afce85b74272e0dd45920147..cee864470bed8d25cf68da08ee0d281b497bbd83 100644 --- go-ethereum/internal/debug/flags.go +++ celo/internal/debug/flags.go @@ -20,7 +20,7 @@ import ( "fmt" "io" "net/http" - _ "net/http/pprof" + _ "net/http/pprof" // #nosec TODO? "os" "runtime"   @@ -90,6 +90,18 @@ traceFlag = cli.StringFlag{ Name: "trace", Usage: "Write execution trace to the given file", } + // (Deprecated September 2021. Use --log.json) + legacyConsoleFormatFlag = cli.StringFlag{ + Name: "consoleformat", + Usage: "Write console logs as 'json' or 'term' (deprecated, use --log.json)", + } + consoleOutputFlag = cli.StringFlag{ + Name: "consoleoutput", + Usage: "(stderr|stdout|split) By default, console output goes to stderr. " + + "In stdout mode, write console logs to stdout (not stderr). " + + "In split mode, write critical(warning, error, and critical) console logs to stderr " + + "and non-critical (info, debug, and trace) to stdout", + } )   // Flags holds all command-line flags required for debugging. @@ -106,10 +118,43 @@ memprofilerateFlag, blockprofilerateFlag, cpuprofileFlag, traceFlag, + consoleOutputFlag, + legacyConsoleFormatFlag, +} + +// This is the list of deprecated debugging flags. +var DeprecatedFlags = []cli.Flag{ + legacyConsoleFormatFlag, }   var glogger *log.GlogHandler   +type StdoutStderrHandler struct { + stdoutHandler log.Handler + stderrHandler log.Handler +} + +func (this StdoutStderrHandler) Log(r *log.Record) error { + switch r.Lvl { + case log.LvlCrit: + fallthrough + case log.LvlError: + fallthrough + case log.LvlWarn: + return this.stderrHandler.Log(r) + + case log.LvlInfo: + fallthrough + case log.LvlDebug: + fallthrough + case log.LvlTrace: + return this.stdoutHandler.Log(r) + + default: + return this.stdoutHandler.Log(r) + } +} + func init() { glogger = log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) glogger.Verbosity(log.LvlInfo) @@ -121,7 +166,14 @@ // It should be called as early as possible in the program. func Setup(ctx *cli.Context) error { var ostream log.Handler output := io.Writer(os.Stderr) - if ctx.GlobalBool(logjsonFlag.Name) { + if ctx.GlobalIsSet(legacyConsoleFormatFlag.Name) { + log.Warn("The flag --consoleoutput is deprecated and will be removed in the future, please use --log.json") + if ctx.GlobalIsSet(logjsonFlag.Name) { + log.Error("--consoleoutput and --log.json are mutually exclusive") + os.Exit(1) + } + } + if ctx.GlobalBool(logjsonFlag.Name) || ctx.GlobalString(legacyConsoleFormatFlag.Name) == "json" { ostream = log.StreamHandler(output, log.JSONFormat()) } else { usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" @@ -182,6 +234,71 @@ // It cannot be imported because it will cause a cyclical dependency. StartPProf(address, !ctx.GlobalIsSet("metrics.addr")) } return nil +} + +func CreateStreamHandler(consoleFormat string, consoleOutputMode string) log.Handler { + if consoleOutputMode == "stdout" { + usecolor := useColor(os.Stdout) + var output io.Writer + if usecolor { + output = colorable.NewColorableStdout() + } else { + output = io.Writer(os.Stdout) + } + return log.StreamHandler(output, getConsoleLogFormat(consoleFormat, usecolor)) + } + + // This is the default mode to maintain backward-compatibility with the geth command-line + if consoleOutputMode == "stderr" || len(consoleOutputMode) == 0 { + usecolor := useColor(os.Stderr) + var output io.Writer + if usecolor { + output = colorable.NewColorableStderr() + } else { + output = io.Writer(os.Stderr) + } + return log.StreamHandler(output, getConsoleLogFormat(consoleFormat, usecolor)) + } + + if consoleOutputMode == "split" { + usecolorStdout := useColor(os.Stdout) + usecolorStderr := useColor(os.Stderr) + + var outputStdout io.Writer + var outputStderr io.Writer + + if usecolorStdout { + outputStdout = colorable.NewColorableStdout() + } else { + outputStdout = io.Writer(os.Stdout) + } + + if usecolorStderr { + outputStderr = colorable.NewColorableStderr() + } else { + outputStderr = io.Writer(os.Stderr) + } + + return StdoutStderrHandler{ + stdoutHandler: log.StreamHandler(outputStdout, getConsoleLogFormat(consoleFormat, usecolorStdout)), + stderrHandler: log.StreamHandler(outputStderr, getConsoleLogFormat(consoleFormat, usecolorStderr))} + } + + panic(fmt.Sprintf("Unexpected value for \"%s\" flag: \"%s\"", consoleOutputFlag.Name, consoleOutputMode)) +} + +func useColor(file *os.File) bool { + return (isatty.IsTerminal(file.Fd()) || isatty.IsCygwinTerminal(file.Fd())) && os.Getenv("TERM") != "dumb" +} + +func getConsoleLogFormat(consoleFormat string, usecolor bool) log.Format { + if consoleFormat == "json" { + return log.JSONFormat() + } + if consoleFormat == "term" || len(consoleFormat) == 0 /* No explicit format specified */ { + return log.TerminalFormat(usecolor) + } + panic(fmt.Sprintf("Unexpected value for \"%s\" flag: \"%s\"", legacyConsoleFormatFlag.Name, consoleFormat)) }   func StartPProf(address string, withMetrics bool) {
diff --git go-ethereum/internal/build/env.go celo/internal/build/env.go index 0a1ece9e6b9bc106eb4f4e509c261d4e7931a9bd..f32a7068d97e8a45bd3a9e63cf5781caf64556c6 100644 --- go-ethereum/internal/build/env.go +++ celo/internal/build/env.go @@ -34,6 +34,8 @@ GitTagFlag = flag.String("git-tag", "", `Overrides git tag being built`) BuildnumFlag = flag.String("buildnum", "", `Overrides CI build number`) PullRequestFlag = flag.Bool("pull-request", false, `Overrides pull request status of the build`) CronJobFlag = flag.Bool("cron-job", false, `Overrides cron job status of the build`) + MuslFlag = flag.Bool("musl", false, `Overrides musl config of the build`) + MetricsDefaultFlag = flag.Bool("metrics-default", false, `Overrides the metrics enabled default flag`) )   // Environment contains metadata provided by the build environment. @@ -45,18 +47,20 @@ Commit, Date, Branch, Tag string // Git info Buildnum string IsPullRequest bool IsCronJob bool + IsMusl bool + MetricsDefault bool }   func (env Environment) String() string { - return fmt.Sprintf("%s env (commit:%s date:%s branch:%s tag:%s buildnum:%s pr:%t)", - env.Name, env.Commit, env.Date, env.Branch, env.Tag, env.Buildnum, env.IsPullRequest) + return fmt.Sprintf("%s env (commit:%s date:%s branch:%s tag:%s buildnum:%s pr:%t musl:%t metrics:%t)", + env.Name, env.Commit, env.Date, env.Branch, env.Tag, env.Buildnum, env.IsPullRequest, env.IsMusl, env.MetricsDefault) }   // Env returns metadata about the current CI environment, falling back to LocalEnv // if not running on CI. func Env() Environment { switch { - case os.Getenv("CI") == "true" && os.Getenv("TRAVIS") == "true": + case stringToBool(os.Getenv("CI")) && stringToBool(os.Getenv("TRAVIS")): commit := os.Getenv("TRAVIS_PULL_REQUEST_SHA") if commit == "" { commit = os.Getenv("TRAVIS_COMMIT") @@ -70,10 +74,12 @@ Date: getDate(commit), Branch: os.Getenv("TRAVIS_BRANCH"), Tag: os.Getenv("TRAVIS_TAG"), Buildnum: os.Getenv("TRAVIS_BUILD_NUMBER"), - IsPullRequest: os.Getenv("TRAVIS_PULL_REQUEST") != "false", + IsPullRequest: stringToBool(os.Getenv("TRAVIS_PULL_REQUEST")), IsCronJob: os.Getenv("TRAVIS_EVENT_TYPE") == "cron", + IsMusl: stringToBool(os.Getenv("MUSL")), + MetricsDefault: stringToBool(os.Getenv("METRICS_DEFAULT")), } - case os.Getenv("CI") == "True" && os.Getenv("APPVEYOR") == "True": + case stringToBool(os.Getenv("CI")) && stringToBool(os.Getenv("APPVEYOR")): commit := os.Getenv("APPVEYOR_PULL_REQUEST_HEAD_COMMIT") if commit == "" { commit = os.Getenv("APPVEYOR_REPO_COMMIT") @@ -88,16 +94,47 @@ Branch: os.Getenv("APPVEYOR_REPO_BRANCH"), Tag: os.Getenv("APPVEYOR_REPO_TAG_NAME"), Buildnum: os.Getenv("APPVEYOR_BUILD_NUMBER"), IsPullRequest: os.Getenv("APPVEYOR_PULL_REQUEST_NUMBER") != "", - IsCronJob: os.Getenv("APPVEYOR_SCHEDULED_BUILD") == "True", + IsCronJob: stringToBool(os.Getenv("APPVEYOR_SCHEDULED_BUILD")), + IsMusl: stringToBool(os.Getenv("MUSL")), + MetricsDefault: stringToBool(os.Getenv("METRICS_DEFAULT")), + } + case stringToBool(os.Getenv("CI")) && stringToBool(os.Getenv("CLOUDBUILD")): + commit := os.Getenv("COMMIT_SHA") + date, err := strconv.ParseInt(strings.TrimSpace(os.Getenv("COMMIT_TIMESTAMP")), 10, 64) + if err != nil { + panic(fmt.Sprintf("failed to parse git commit date: %v", err)) + } + return Environment{ + CI: true, + Name: "cloudbuild", + Repo: os.Getenv("REPO_NAME"), + Commit: commit, + Date: time.Unix(date, 0).Format("20060102"), + Branch: os.Getenv("BRANCH_NAME"), + Tag: os.Getenv("TAG_NAME"), + Buildnum: os.Getenv("BUILD_ID"), + IsPullRequest: os.Getenv("_PR_NUMBER") != "", + IsCronJob: false, + IsMusl: stringToBool(os.Getenv("MUSL")), + MetricsDefault: stringToBool(os.Getenv("METRICS_DEFAULT")), } default: return LocalEnv() } }   +// if it's not a truly value returns false +func stringToBool(str string) bool { + b, err := strconv.ParseBool(str) + if err != nil { + return false + } + return b +} + // LocalEnv returns build environment metadata gathered from git. func LocalEnv() Environment { - env := applyEnvFlags(Environment{Name: "local", Repo: "ethereum/go-ethereum"}) + env := applyEnvFlags(Environment{Name: "local", Repo: "celo-org/celo-blockchain"})   head := readGitFile("HEAD") if fields := strings.Fields(head); len(fields) == 2 { @@ -168,5 +205,12 @@ } if *CronJobFlag { env.IsCronJob = true } + if *MuslFlag { + env.IsMusl = true + } + if *MetricsDefaultFlag { + env.MetricsDefault = true + } + return env }
diff --git go-ethereum/internal/guide/guide_test.go celo/internal/guide/guide_test.go index abc48e0e4b6a11b36937bfb1e30468d9ac00aa3d..b3cbf903787106912b317377a37c89e8116e7362 100644 --- go-ethereum/internal/guide/guide_test.go +++ celo/internal/guide/guide_test.go @@ -76,7 +76,7 @@ signer, err := ks.NewAccount("Signer password") if err != nil { t.Fatalf("Failed to create signer account: %v", err) } - tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil) + tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil, nil, nil, nil) chain := big.NewInt(1)   // Sign a transaction with a single authorization
diff --git go-ethereum/internal/ethapi/api.go celo/internal/ethapi/api.go index 2078bb69eebbff8f0c13fd269487f478a073d183..3b80106ff954e5b95f821f6d4eb6a36c92641ede 100644 --- go-ethereum/internal/ethapi/api.go +++ celo/internal/ethapi/api.go @@ -28,13 +28,9 @@ "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/accounts/scwallet" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/consensus/clique" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -45,7 +41,6 @@ "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - "github.com/tyler-smith/go-bip39" )   // PublicEthereumAPI provides an API to access Ethereum related information. @@ -60,59 +55,54 @@ return &PublicEthereumAPI{b} }   // GasPrice returns a suggestion for a gas price for legacy transactions. -func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) { - tipcap, err := s.b.SuggestGasTipCap(ctx) - if err != nil { - return nil, err - } - if head := s.b.CurrentHeader(); head.BaseFee != nil { - tipcap.Add(tipcap, head.BaseFee) - } - return (*hexutil.Big)(tipcap), err +func (s *PublicEthereumAPI) GasPrice(ctx context.Context, feeCurrency *common.Address) (*hexutil.Big, error) { + price, err := s.b.SuggestPrice(ctx, feeCurrency) + return (*hexutil.Big)(price), err }   // MaxPriorityFeePerGas returns a suggestion for a gas tip cap for dynamic fee transactions. -func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, error) { - tipcap, err := s.b.SuggestGasTipCap(ctx) +func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context, feeCurrency *common.Address) (*hexutil.Big, error) { + tipcap, err := s.b.SuggestGasTipCap(ctx, feeCurrency) if err != nil { return nil, err } return (*hexutil.Big)(tipcap), err }   -type feeHistoryResult struct { - OldestBlock *hexutil.Big `json:"oldestBlock"` - Reward [][]*hexutil.Big `json:"reward,omitempty"` - BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"` - GasUsedRatio []float64 `json:"gasUsedRatio"` -} +// TODO: Implement FeeHistory (https://github.com/ethereum/go-ethereum/issues/1744) +// type feeHistoryResult struct { +// OldestBlock *hexutil.Big `json:"oldestBlock"` +// Reward [][]*hexutil.Big `json:"reward,omitempty"` +// BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"` +// GasUsedRatio []float64 `json:"gasUsedRatio"` +// }   -func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) { - oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, int(blockCount), lastBlock, rewardPercentiles) - if err != nil { - return nil, err - } - results := &feeHistoryResult{ - OldestBlock: (*hexutil.Big)(oldest), - GasUsedRatio: gasUsed, - } - if reward != nil { - results.Reward = make([][]*hexutil.Big, len(reward)) - for i, w := range reward { - results.Reward[i] = make([]*hexutil.Big, len(w)) - for j, v := range w { - results.Reward[i][j] = (*hexutil.Big)(v) - } - } - } - if baseFee != nil { - results.BaseFee = make([]*hexutil.Big, len(baseFee)) - for i, v := range baseFee { - results.BaseFee[i] = (*hexutil.Big)(v) - } - } - return results, nil -} +// func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) { +// oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, int(blockCount), lastBlock, rewardPercentiles) +// if err != nil { +// return nil, err +// } +// results := &feeHistoryResult{ +// OldestBlock: (*hexutil.Big)(oldest), +// GasUsedRatio: gasUsed, +// } +// if reward != nil { +// results.Reward = make([][]*hexutil.Big, len(reward)) +// for i, w := range reward { +// results.Reward[i] = make([]*hexutil.Big, len(w)) +// for j, v := range w { +// results.Reward[i][j] = (*hexutil.Big)(v) +// } +// } +// } +// if baseFee != nil { +// results.BaseFee = make([]*hexutil.Big, len(baseFee)) +// for i, v := range baseFee { +// results.BaseFee[i] = (*hexutil.Big)(v) +// } +// } +// return results, nil +// }   // Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not // yet received the latest block headers from its pears. In case it is synchronizing: @@ -156,11 +146,14 @@ "queued": make(map[string]map[string]*RPCTransaction), } pending, queue := s.b.TxPoolContent() curHeader := s.b.CurrentHeader() + baseFeeFn := func() (*big.Int, error) { + return s.b.CurrentGasPriceMinimum(context.Background(), nil) + } // Flatten the pending transactions for account, txs := range pending { dump := make(map[string]*RPCTransaction) for _, tx := range txs { - dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig(), baseFeeFn) } content["pending"][account.Hex()] = dump } @@ -168,7 +161,7 @@ // Flatten the queued transactions for account, txs := range queue { dump := make(map[string]*RPCTransaction) for _, tx := range txs { - dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig(), baseFeeFn) } content["queued"][account.Hex()] = dump } @@ -183,15 +176,19 @@ curHeader := s.b.CurrentHeader()   // Build the pending transactions dump := make(map[string]*RPCTransaction, len(pending)) + baseFeeFn := func() (*big.Int, error) { + return s.b.CurrentGasPriceMinimum(context.Background(), nil) + } for _, tx := range pending { - dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig(), baseFeeFn) } content["pending"] = dump   // Build the queued transactions dump = make(map[string]*RPCTransaction, len(queue)) for _, tx := range queue { - dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig(), baseFeeFn) + } content["queued"] = dump   @@ -479,7 +476,7 @@ return nil, fmt.Errorf("nonce not specified") } // Before actually signing the transaction, ensure the transaction fee is reasonable. tx := args.toTransaction() - if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil { + if err := checkFeeFromCeloTx(ctx, s.b, tx); err != nil { return nil, err } signed, err := s.signTransaction(ctx, &args, passwd) @@ -521,6 +518,24 @@ signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper return signature, nil }   +// Decrypt will decrypt a given ciphertext with the given account via ECIES +func (s *PrivateAccountAPI) Decrypt(ctx context.Context, ciphertext hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) { + // Look up the wallet containing the requested signer + account := accounts.Account{Address: addr} + + wallet, err := s.b.AccountManager().Find(account) + if err != nil { + return nil, err + } + // Decrypt the data with the wallet + plaintext, err := wallet.Decrypt(account, ciphertext, nil, nil) + if err != nil { + log.Warn("Failed data decryption attempt", "address", addr, "err", err) + return nil, err + } + return plaintext, nil +} + // EcRecover returns the address for the account that was used to create the signature. // Note, this function is compatible with eth_sign and personal_sign. As such it recovers // the address of: @@ -553,48 +568,6 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args TransactionArgs, passwd string) (common.Hash, error) { return s.SendTransaction(ctx, args, passwd) }   -// InitializeWallet initializes a new wallet at the provided URL, by generating and returning a new private key. -func (s *PrivateAccountAPI) InitializeWallet(ctx context.Context, url string) (string, error) { - wallet, err := s.am.Wallet(url) - if err != nil { - return "", err - } - - entropy, err := bip39.NewEntropy(256) - if err != nil { - return "", err - } - - mnemonic, err := bip39.NewMnemonic(entropy) - if err != nil { - return "", err - } - - seed := bip39.NewSeed(mnemonic, "") - - switch wallet := wallet.(type) { - case *scwallet.Wallet: - return mnemonic, wallet.Initialize(seed) - default: - return "", fmt.Errorf("specified wallet does not support initialization") - } -} - -// Unpair deletes a pairing between wallet and geth. -func (s *PrivateAccountAPI) Unpair(ctx context.Context, url string, pin string) error { - wallet, err := s.am.Wallet(url) - if err != nil { - return err - } - - switch wallet := wallet.(type) { - case *scwallet.Wallet: - return wallet.Unpair([]byte(pin)) - default: - return fmt.Errorf("specified wallet does not support pairing") - } -} - // PublicBlockChainAPI provides an API to access the Ethereum blockchain. // It offers only methods that operate on public data that is freely available to anyone. type PublicBlockChainAPI struct { @@ -735,10 +708,14 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { block, err := s.b.BlockByNumber(ctx, number) if block != nil && err == nil { response, err := s.rpcMarshalBlock(ctx, block, true, fullTx) - if err == nil && number == rpc.PendingBlockNumber { - // Pending blocks need to nil out a few fields - for _, field := range []string{"hash", "nonce", "miner"} { - response[field] = nil + + if err == nil && s.b.RPCEthCompatibility() { + addEthCompatibilityFields(ctx, response, s.b, block.Header()) + if number == rpc.PendingBlockNumber { + // Pending blocks need to nil out a few fields + for _, field := range []string{"hash", "nonce", "miner"} { + response[field] = nil + } } } return response, err @@ -751,59 +728,65 @@ // detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) { block, err := s.b.BlockByHash(ctx, hash) if block != nil { - return s.rpcMarshalBlock(ctx, block, true, fullTx) + result, err := s.rpcMarshalBlock(ctx, block, true, fullTx) + if err != nil { + return nil, err + } + if s.b.RPCEthCompatibility() { + addEthCompatibilityFields(ctx, result, s.b, block.Header()) + } + return result, nil } return nil, err }   +// addEthCompatibilityFields seeks to work around the incompatibility of celo +// and ethers.js (and potentially other web3 clients) by adding fields to our +// rpc response that ethers.js depends upon. +// See https://github.com/ethereum/go-ethereum/issues/1945 +func addEthCompatibilityFields(ctx context.Context, block map[string]interface{}, b Backend, header *types.Header) { + hash := header.Hash() + numhash := rpc.BlockNumberOrHash{ + BlockHash: &hash, + } + gasLimit, err := b.GetRealBlockGasLimit(ctx, numhash) + if err != nil { + log.Debug("Not adding gasLimit to RPC response, failed to retrieve it", "block", header.Number.Uint64(), "err", err) + } else { + block["gasLimit"] = hexutil.Uint64(gasLimit) + } + + // Providing nil as the currency address gets the gas price minimum for the native celo asset. + baseFee, err := b.RealGasPriceMinimumForHeader(ctx, nil, header) + if err != nil { + log.Debug("Not adding baseFeePerGas to RPC response, failed to retrieve gas price minimum", "block", header.Number.Uint64(), "err", err) + } else { + block["baseFeePerGas"] = (*hexutil.Big)(baseFee) + } + + block["difficulty"] = 0 +} + // GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true // all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error) { - block, err := s.b.BlockByNumber(ctx, blockNr) - if block != nil { - uncles := block.Uncles() - if index >= hexutil.Uint(len(uncles)) { - log.Debug("Requested uncle not found", "number", blockNr, "hash", block.Hash(), "index", index) - return nil, nil - } - block = types.NewBlockWithHeader(uncles[index]) - return s.rpcMarshalBlock(ctx, block, false, false) - } - return nil, err + return nil, fmt.Errorf("Not implemented") }   // GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index. When fullTx is true // all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (map[string]interface{}, error) { - block, err := s.b.BlockByHash(ctx, blockHash) - if block != nil { - uncles := block.Uncles() - if index >= hexutil.Uint(len(uncles)) { - log.Debug("Requested uncle not found", "number", block.Number(), "hash", blockHash, "index", index) - return nil, nil - } - block = types.NewBlockWithHeader(uncles[index]) - return s.rpcMarshalBlock(ctx, block, false, false) - } - return nil, err + return nil, fmt.Errorf("Not implemented") }   // GetUncleCountByBlockNumber returns number of uncles in the block for the given block number -func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { - n := hexutil.Uint(len(block.Uncles())) - return &n - } - return nil +func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) (*hexutil.Uint, error) { + return nil, fmt.Errorf("Not implemented") }   // GetUncleCountByBlockHash returns number of uncles in the block for the given block hash -func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint { - if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { - n := hexutil.Uint(len(block.Uncles())) - return &n - } - return nil +func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) (*hexutil.Uint, error) { + return nil, fmt.Errorf("Not implemented") }   // GetCode returns the code stored at the given address in the state for the given block number. @@ -902,8 +885,14 @@ // Make sure the context is cancelled when the call has completed // this makes sure resources are cleaned up. defer cancel()   + // Create SysContractCallCtx + var sysCtx *core.SysContractCallCtx + if b.ChainConfig().IsEspresso(header.Number) { + sysCtx = core.NewSysContractCallCtx(header, state, b) + } + // Get a new instance of the EVM. - msg, err := args.ToMessage(globalGasCap, header.BaseFee) + msg, err := args.ToMessage(globalGasCap, common.Big0) // core.ApplyMessageWithoutGasPriceMinimum below will eventually set basefee to 0 if err != nil { return nil, err } @@ -920,7 +909,7 @@ }()   // Execute the message. gp := new(core.GasPool).AddGas(math.MaxUint64) - result, err := core.ApplyMessage(evm, msg, gp) + result, err := core.ApplyMessageWithoutGasPriceMinimum(evm, msg, gp, b.NewEVMRunner(header, state), sysCtx) if err := vmError(); err != nil { return nil, err } @@ -972,7 +961,7 @@ // // Note, this function doesn't make and changes in the state/blockchain and is // useful to execute and retrieve values. func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) { - result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, 5*time.Second, s.b.RPCGasCap()) + result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, 50*time.Second, s.b.RPCGasCap()) if err != nil { return nil, err } @@ -990,61 +979,10 @@ lo uint64 = params.TxGas - 1 hi uint64 cap uint64 ) - // Use zero address if sender unspecified. - if args.From == nil { - args.From = new(common.Address) - } - // Determine the highest gas limit can be used during the estimation. if args.Gas != nil && uint64(*args.Gas) >= params.TxGas { hi = uint64(*args.Gas) } else { - // Retrieve the block to act as the gas ceiling - block, err := b.BlockByNumberOrHash(ctx, blockNrOrHash) - if err != nil { - return 0, err - } - if block == nil { - return 0, errors.New("block not found") - } - hi = block.GasLimit() - } - // Normalize the max fee per gas the call is willing to spend. - var feeCap *big.Int - if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { - return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") - } else if args.GasPrice != nil { - feeCap = args.GasPrice.ToInt() - } else if args.MaxFeePerGas != nil { - feeCap = args.MaxFeePerGas.ToInt() - } else { - feeCap = common.Big0 - } - // Recap the highest gas limit with account's available balance. - if feeCap.BitLen() != 0 { - state, _, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) - if err != nil { - return 0, err - } - balance := state.GetBalance(*args.From) // from can't be nil - available := new(big.Int).Set(balance) - if args.Value != nil { - if args.Value.ToInt().Cmp(available) >= 0 { - return 0, errors.New("insufficient funds for transfer") - } - available.Sub(available, args.Value.ToInt()) - } - allowance := new(big.Int).Div(available, feeCap) - - // If the allowance is larger than maximum uint64, skip checking - if allowance.IsUint64() && hi > allowance.Uint64() { - transfer := args.Value - if transfer == nil { - transfer = new(hexutil.Big) - } - log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance, - "sent", transfer.ToInt(), "maxFeePerGas", feeCap, "fundable", allowance) - hi = allowance.Uint64() - } + hi = b.GetBlockGasLimit(ctx, blockNrOrHash) } // Recap the highest gas allowance with specified gascap. if gasCap != 0 && hi > gasCap { @@ -1053,6 +991,15 @@ hi = gasCap } cap = hi   + // Use zero address if sender unspecified. + if args.From == nil { + args.From = new(common.Address) + } + // Set gas price to nil (which will lead to it being zero), because the binary search + // assumes that if the transaction fails with gas limit A, and B < A, then it would + // also fail with gas limit B, which may not be the case if the gas price is non-zero, + // depending on the account's balance. + args.GasPrice = nil // Create a helper to check if a gas allowance results in an executable transaction executable := func(gas uint64) (bool, *core.ExecutionResult, error) { args.Gas = (*hexutil.Uint64)(&gas) @@ -1100,7 +1047,8 @@ // Otherwise, the specified gas cap is too low return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap) } } - return hexutil.Uint64(hi), nil + inflatedGas := hexutil.Uint64(uint64(float64(hi) * b.RPCGasInflationRate())) + return inflatedGas, nil }   // EstimateGas returns an estimate of the amount of gas needed to execute the @@ -1180,43 +1128,48 @@ result := map[string]interface{}{ "number": (*hexutil.Big)(head.Number), "hash": head.Hash(), "parentHash": head.ParentHash, - "nonce": head.Nonce, - "mixHash": head.MixDigest, - "sha3Uncles": head.UncleHash, "logsBloom": head.Bloom, "stateRoot": head.Root, "miner": head.Coinbase, - "difficulty": (*hexutil.Big)(head.Difficulty), "extraData": hexutil.Bytes(head.Extra), "size": hexutil.Uint64(head.Size()), - "gasLimit": hexutil.Uint64(head.GasLimit), "gasUsed": hexutil.Uint64(head.GasUsed), "timestamp": hexutil.Uint64(head.Time), "transactionsRoot": head.TxHash, "receiptsRoot": head.ReceiptHash, }   - if head.BaseFee != nil { - result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee) - } - return result }   // RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are // returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain // transaction hashes. -func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { +func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, baseFeeFn func() (*big.Int, error)) (map[string]interface{}, error) { fields := RPCMarshalHeader(block.Header()) fields["size"] = hexutil.Uint64(block.Size())   + fields["randomness"] = map[string]interface{}{ + "revealed": hexutil.Bytes(block.Randomness().Revealed.Bytes()), + "committed": hexutil.Bytes(block.Randomness().Committed.Bytes()), + } + epochSnarkData := block.EpochSnarkData() + if epochSnarkData != nil && !epochSnarkData.IsEmpty() { + fields["epochSnarkData"] = map[string]interface{}{ + "bitmap": hexutil.Bytes(block.EpochSnarkData().Bitmap.Bytes()), + "signature": hexutil.Bytes(block.EpochSnarkData().Signature), + } + } else { + fields["epochSnarkData"] = nil + } + if inclTx { formatTx := func(tx *types.Transaction) (interface{}, error) { return tx.Hash(), nil } if fullTx { formatTx = func(tx *types.Transaction) (interface{}, error) { - return newRPCTransactionFromBlockHash(block, tx.Hash()), nil + return newRPCTransactionFromBlockHash(block, tx.Hash(), baseFeeFn), nil } } txs := block.Transactions() @@ -1229,12 +1182,6 @@ } } fields["transactions"] = transactions } - uncles := block.Uncles() - uncleHashes := make([]common.Hash, len(uncles)) - for i, uncle := range uncles { - uncleHashes[i] = uncle.Hash() - } - fields["uncles"] = uncleHashes   return fields, nil } @@ -1250,7 +1197,8 @@ // rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field, which requires // a `PublicBlockchainAPI`. func (s *PublicBlockChainAPI) rpcMarshalBlock(ctx context.Context, b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { - fields, err := RPCMarshalBlock(b, inclTx, fullTx) + baseFeeFn := getGasPriceMinimumFromStateWithHeader(ctx, s.b, b.Header()) + fields, err := RPCMarshalBlock(b, inclTx, fullTx, baseFeeFn) if err != nil { return nil, err } @@ -1269,6 +1217,9 @@ Gas hexutil.Uint64 `json:"gas"` GasPrice *hexutil.Big `json:"gasPrice"` GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` + FeeCurrency *common.Address `json:"feeCurrency"` + GatewayFeeRecipient *common.Address `json:"gatewayFeeRecipient"` + GatewayFee *hexutil.Big `json:"gatewayFee"` Hash common.Hash `json:"hash"` Input hexutil.Bytes `json:"input"` Nonce hexutil.Uint64 `json:"nonce"` @@ -1281,11 +1232,16 @@ ChainID *hexutil.Big `json:"chainId,omitempty"` V *hexutil.Big `json:"v"` R *hexutil.Big `json:"r"` S *hexutil.Big `json:"s"` + EthCompatible bool `json:"ethCompatible"` }   // newRPCTransaction returns a transaction that will serialize to the RPC // representation, with the given location metadata set (if available). -func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, baseFee *big.Int) *RPCTransaction { +// *** Upstream difference *** +// The `inABlock` flag was added to avoid ambiguity. A Tx in a pending block, will be +// part of a block. The usage in this file of a `!inABlock`, is for asking for pending txs +// in the pool that are not even part of the pending block. +func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, baseFeeFn func() (*big.Int, error), inABlock bool) *RPCTransaction { // Determine the signer. For replay-protected transactions, use the most permissive // signer, because we assume that signers are backwards-compatible with old // transactions. For non-protected transactions, the homestead signer signer is used @@ -1303,6 +1259,9 @@ Type: hexutil.Uint64(tx.Type()), From: from, Gas: hexutil.Uint64(tx.Gas()), GasPrice: (*hexutil.Big)(tx.GasPrice()), + FeeCurrency: tx.FeeCurrency(), + GatewayFeeRecipient: tx.GatewayFeeRecipient(), + GatewayFee: (*hexutil.Big)(tx.GatewayFee()), Hash: tx.Hash(), Input: hexutil.Bytes(tx.Data()), Nonce: hexutil.Uint64(tx.Nonce()), @@ -1311,6 +1270,7 @@ Value: (*hexutil.Big)(tx.Value()), V: (*hexutil.Big)(v), R: (*hexutil.Big)(r), S: (*hexutil.Big)(s), + EthCompatible: tx.EthCompatible(), } if blockHash != (common.Hash{}) { result.BlockHash = &blockHash @@ -1329,10 +1289,18 @@ result.ChainID = (*hexutil.Big)(tx.ChainId()) result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) // if the transaction has been mined, compute the effective gas price - if baseFee != nil && blockHash != (common.Hash{}) { - // price = min(tip, gasFeeCap - baseFee) + baseFee - price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap()) - result.GasPrice = (*hexutil.Big)(price) + // if blockHash != (common.Hash{}) { // This is from upstream (check the function comment above). + if inABlock { + baseFee, err := baseFeeFn() + if err == nil { + // price = min(tip, gasFeeCap - baseFee) + baseFee + price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap()) + result.GasPrice = (*hexutil.Big)(price) + } else { + log.Warn("error retrieving baseFee for tx", "hash", tx.Hash().Hex(), "error", err) + // error != nil for DynamicFees implies that there is no state to retrieve the baseFee for that block + result.GasPrice = nil + } } else { result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) } @@ -1341,21 +1309,17 @@ return result }   // newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation -func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction { - var baseFee *big.Int - if current != nil { - baseFee = misc.CalcBaseFee(config, current) - } - return newRPCTransaction(tx, common.Hash{}, 0, 0, baseFee) +func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig, baseFeeFn func() (*big.Int, error)) *RPCTransaction { + return newRPCTransaction(tx, common.Hash{}, 0, 0, baseFeeFn, false) }   // newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation. -func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction { +func newRPCTransactionFromBlockIndex(b *types.Block, index uint64, baseFeeFn func() (*big.Int, error)) *RPCTransaction { txs := b.Transactions() if index >= uint64(len(txs)) { return nil } - return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee()) + return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, baseFeeFn, true) }   // newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. @@ -1369,10 +1333,10 @@ return blob }   // newRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation. -func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction { +func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash, baseFeeFn func() (*big.Int, error)) *RPCTransaction { for idx, tx := range b.Transactions() { if tx.Hash() == hash { - return newRPCTransactionFromBlockIndex(b, uint64(idx)) + return newRPCTransactionFromBlockIndex(b, uint64(idx), baseFeeFn) } } return nil @@ -1454,7 +1418,11 @@ // Copy the original db so we don't modify it statedb := db.Copy() // Set the accesslist to the last al args.AccessList = &accessList - msg, err := args.ToMessage(b.RPCGasCap(), header.BaseFee) + gasPriceMinimum, err := b.GasPriceMinimumForHeader(ctx, args.FeeCurrency, header) + if err != nil { + return nil, 0, nil, err + } + msg, err := args.ToMessage(b.RPCGasCap(), gasPriceMinimum) if err != nil { return nil, 0, nil, err } @@ -1466,7 +1434,8 @@ vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, &config) if err != nil { return nil, 0, nil, err } - res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) + vmRunner := b.NewEVMRunner(header, statedb) + res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), vmRunner, nil) if err != nil { return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err) } @@ -1513,7 +1482,9 @@ // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction { if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { - return newRPCTransactionFromBlockIndex(block, uint64(index)) + baseFeeFn := getGasPriceMinimumFromStateWithHeader(ctx, s.b, block.Header()) + + return newRPCTransactionFromBlockIndex(block, uint64(index), baseFeeFn) } return nil } @@ -1521,7 +1492,8 @@ // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction { if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { - return newRPCTransactionFromBlockIndex(block, uint64(index)) + baseFeeFn := getGasPriceMinimumFromStateWithHeader(ctx, s.b, block.Header()) + return newRPCTransactionFromBlockIndex(block, uint64(index), baseFeeFn) } return nil } @@ -1569,21 +1541,38 @@ if err != nil { return nil, err } if tx != nil { - header, err := s.b.HeaderByHash(ctx, blockHash) - if err != nil { - return nil, err - } - return newRPCTransaction(tx, blockHash, blockNumber, index, header.BaseFee), nil + baseFeeFn := getGasPriceMinimumFromState(ctx, s.b, blockHash) + return newRPCTransaction(tx, blockHash, blockNumber, index, baseFeeFn, true), nil } // No finalized transaction, try to retrieve it from the pool if tx := s.b.GetPoolTransaction(hash); tx != nil { - return newRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil + baseFeeFn := func() (*big.Int, error) { + return s.b.CurrentGasPriceMinimum(context.Background(), nil) + } + return newRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig(), baseFeeFn), nil }   // Transaction unknown, return as such return nil, nil }   +func getGasPriceMinimumFromState(ctx context.Context, b Backend, blockHash common.Hash) func() (*big.Int, error) { + return func() (*big.Int, error) { + header, err := b.HeaderByHash(ctx, blockHash) + if err != nil { + return nil, err + } + + return getGasPriceMinimumFromStateWithHeader(ctx, b, header)() + } +} + +func getGasPriceMinimumFromStateWithHeader(ctx context.Context, b Backend, header *types.Header) func() (*big.Int, error) { + return func() (*big.Int, error) { + return b.GasPriceMinimumForHeader(ctx, nil, header) + } +} + // GetRawTransactionByHash returns the bytes of the transaction for the given hash. func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { // Retrieve a finalized transaction, or a pooled otherwise @@ -1615,50 +1604,58 @@ if len(receipts) <= int(index) { return nil, nil } receipt := receipts[index] - - // Derive the sender. bigblock := new(big.Int).SetUint64(blockNumber) signer := types.MakeSigner(s.b.ChainConfig(), bigblock) - from, _ := types.Sender(signer, tx)   - fields := map[string]interface{}{ - "blockHash": blockHash, - "blockNumber": hexutil.Uint64(blockNumber), - "transactionHash": hash, - "transactionIndex": hexutil.Uint64(index), - "from": from, - "to": tx.To(), - "gasUsed": hexutil.Uint64(receipt.GasUsed), - "cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed), - "contractAddress": nil, - "logs": receipt.Logs, - "logsBloom": receipt.Bloom, - "type": hexutil.Uint(tx.Type()), - } + fields := generateReceiptResponse(receipt, signer, tx, blockHash, blockNumber, index) // Assign the effective gas price paid - if !s.b.ChainConfig().IsLondon(bigblock) { + if !s.b.ChainConfig().IsEspresso(bigblock) { fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64()) } else { - header, err := s.b.HeaderByHash(ctx, blockHash) - if err != nil { - return nil, err + // var gasPrice *big.Int = new(big.Int) + if tx.Type() == types.DynamicFeeTxType { + header, err := s.b.HeaderByHash(ctx, blockHash) + if err != nil { + return nil, err + } + gasPriceMinimum, err := s.b.GasPriceMinimumForHeader(ctx, nil, header) + if err == nil { + fields["effectiveGasPrice"] = hexutil.Uint64(new(big.Int).Add(gasPriceMinimum, tx.EffectiveGasTipValue(gasPriceMinimum)).Uint64()) + } + // if err != nil, it's due to a state prune. In this case no effectiveGasPrice will be returned. + } else { + fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64()) } - gasPrice := new(big.Int).Add(header.BaseFee, tx.EffectiveGasTipValue(header.BaseFee)) - fields["effectiveGasPrice"] = hexutil.Uint64(gasPrice.Uint64()) } - // Assign receipt status or post state. - if len(receipt.PostState) > 0 { - fields["root"] = hexutil.Bytes(receipt.PostState) - } else { - fields["status"] = hexutil.Uint(receipt.Status) + return fields, nil +} + +// GetBlockReceipt returns "system calls" receipt for the block with the given block hash. +func (s *PublicTransactionPoolAPI) GetBlockReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { + block, err := s.b.BlockByHash(ctx, hash) + if block == nil || err != nil { + // If no header with that hash is found, err gives "header for hash not found". + // But we return nil with no error, to match the behavior of eth_getBlockByHash and eth_getTransactionReceipt in these cases. + return nil, nil } - if receipt.Logs == nil { - fields["logs"] = [][]*types.Log{} + index := uint64(block.Transactions().Len()) + blockNumber := block.NumberU64() + receipts, err := s.b.GetReceipts(ctx, block.Hash()) + // GetReceipts() doesn't return an error if things go wrong, so we also check len(receipts) + if err != nil || len(receipts) < int(index) { + return nil, err } - // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation - if receipt.ContractAddress != (common.Address{}) { - fields["contractAddress"] = receipt.ContractAddress + + var receipt *types.Receipt + if len(receipts) == int(index) { + // The block didn't have any logs from system calls and no receipt was created. + // So we create an empty receipt to return, similarly to how system receipts are created. + receipt = types.NewReceipt(nil, false, 0) + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + } else { + receipt = receipts[index] } + fields := generateReceiptResponse(receipt, nil, nil, hash, blockNumber, index) return fields, nil }   @@ -1679,18 +1676,24 @@ // SubmitTransaction is a helper function that submits tx to txPool and logs a message. func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) { // If the transaction fee cap is already specified, ensure the // fee of the given transaction is _reasonable_. - if err := checkTxFee(tx.GasPrice(), tx.Gas(), b.RPCTxFeeCap()); err != nil { + if err := checkFeeFromCeloTx(ctx, b, tx); err != nil { return common.Hash{}, err } - if !b.UnprotectedAllowed() && !tx.Protected() { - // Ensure only eip155 signed transactions are submitted if EIP155Required is set. - return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC") + currentBlockNumber := b.CurrentBlock().Number() + if !tx.Protected() { + if !b.UnprotectedAllowed() { + // Ensure only eip155 signed transactions are submitted if EIP155Required is set. + return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC for this node") + } + if b.ChainConfig().IsDonut(currentBlockNumber) && !b.ChainConfig().IsEspresso(currentBlockNumber) { + return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC") + } } if err := b.SendTx(ctx, tx); err != nil { return common.Hash{}, err } // Print a log with full tx details for manual investigations and interventions - signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number()) + signer := types.MakeSigner(b.ChainConfig(), currentBlockNumber) from, err := types.Sender(signer, tx) if err != nil { return common.Hash{}, err @@ -1813,7 +1816,7 @@ return nil, err } // Before actually sign the transaction, ensure the transaction fee is reasonable. tx := args.toTransaction() - if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil { + if err := checkFeeFromCeloTx(ctx, s.b, tx); err != nil { return nil, err } signed, err := s.sign(args.from(), tx) @@ -1842,10 +1845,13 @@ } } curHeader := s.b.CurrentHeader() transactions := make([]*RPCTransaction, 0, len(pending)) + baseFeeFn := func() (*big.Int, error) { + return s.b.CurrentGasPriceMinimum(context.Background(), nil) + } for _, tx := range pending { from, _ := types.Sender(s.signer, tx) if _, exists := accounts[from]; exists { - transactions = append(transactions, newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())) + transactions = append(transactions, newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig(), baseFeeFn)) } } return transactions, nil @@ -1871,7 +1877,7 @@ var gas = matchTx.Gas() if gasLimit != nil { gas = uint64(*gasLimit) } - if err := checkTxFee(price, gas, s.b.RPCTxFeeCap()); err != nil { + if err := checkFeeFromCeloCurrency(ctx, s.b, sendArgs.FeeCurrency, price, gas, (*big.Int)(sendArgs.GatewayFee)); err != nil { return common.Hash{}, err } // Iterate the pending list for replacement @@ -1928,45 +1934,6 @@ } return fmt.Sprintf("%x", encoded), nil }   -// TestSignCliqueBlock fetches the given block number, and attempts to sign it as a clique header with the -// given address, returning the address of the recovered signature -// -// This is a temporary method to debug the externalsigner integration, -// TODO: Remove this method when the integration is mature -func (api *PublicDebugAPI) TestSignCliqueBlock(ctx context.Context, address common.Address, number uint64) (common.Address, error) { - block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) - if block == nil { - return common.Address{}, fmt.Errorf("block #%d not found", number) - } - header := block.Header() - header.Extra = make([]byte, 32+65) - encoded := clique.CliqueRLP(header) - - // Look up the wallet containing the requested signer - account := accounts.Account{Address: address} - wallet, err := api.b.AccountManager().Find(account) - if err != nil { - return common.Address{}, err - } - - signature, err := wallet.SignData(account, accounts.MimetypeClique, encoded) - if err != nil { - return common.Address{}, err - } - sealHash := clique.SealHash(header).Bytes() - log.Info("test signing of clique block", - "Sealhash", fmt.Sprintf("%x", sealHash), - "signature", fmt.Sprintf("%x", signature)) - pubkey, err := crypto.Ecrecover(sealHash, signature) - if err != nil { - return common.Address{}, err - } - var signer common.Address - copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) - - return signer, nil -} - // PrintBlock retrieves a block and returns its pretty printed form. func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number uint64) (string, error) { block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) @@ -1974,15 +1941,6 @@ if block == nil { return "", fmt.Errorf("block #%d not found", number) } return spew.Sdump(block), nil -} - -// SeedHash retrieves the seed hash of a block. -func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, error) { - block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) - if block == nil { - return "", fmt.Errorf("block #%d not found", number) - } - return fmt.Sprintf("0x%x", ethash.SeedHash(number)), nil }   // PrivateDebugAPI is the collection of Ethereum APIs exposed over the private @@ -2051,21 +2009,6 @@ func (s *PublicNetAPI) Version() string { return fmt.Sprintf("%d", s.networkVersion) }   -// checkTxFee is an internal function used to check whether the fee of -// the given transaction is _reasonable_(under the cap). -func checkTxFee(gasPrice *big.Int, gas uint64, cap float64) error { - // Short circuit if there is no cap for transaction fee at all. - if cap == 0 { - return nil - } - feeEth := new(big.Float).Quo(new(big.Float).SetInt(new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(gas))), new(big.Float).SetInt(big.NewInt(params.Ether))) - feeFloat, _ := feeEth.Float64() - if feeFloat > cap { - return fmt.Errorf("tx fee (%.2f ether) exceeds the configured cap (%.2f ether)", feeFloat, cap) - } - return nil -} - // toHexSlice creates a slice of hex-strings based on []byte. func toHexSlice(b [][]byte) []string { r := make([]string, len(b)) @@ -2074,3 +2017,45 @@ r[i] = hexutil.Encode(b[i]) } return r } + +// generateReceiptResponse is a helper function which generates the response for GetTransactionReceipt() and GetBlockReceipt() +func generateReceiptResponse(receipt *types.Receipt, signer types.Signer, tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) map[string]interface{} { + fields := map[string]interface{}{ + "blockHash": blockHash, + "blockNumber": hexutil.Uint64(blockNumber), + "transactionHash": common.Hash{}, + "transactionIndex": hexutil.Uint64(index), + "from": common.Address{}, + "to": nil, + "gasUsed": hexutil.Uint64(receipt.GasUsed), + "cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed), + "contractAddress": nil, + "logs": receipt.Logs, + "logsBloom": receipt.Bloom, + "type": hexutil.Uint(receipt.Type), + } + + // Assign receipt status or post state. + if len(receipt.PostState) > 0 { + fields["root"] = hexutil.Bytes(receipt.PostState) + } else { + fields["status"] = hexutil.Uint(receipt.Status) + } + if receipt.Logs == nil { + fields["logs"] = [][]*types.Log{} + } + // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation + if receipt.ContractAddress != (common.Address{}) { + fields["contractAddress"] = receipt.ContractAddress + } + if tx == nil { + fields["transactionHash"] = blockHash + } else { + fields["transactionHash"] = tx.Hash() + // Derive the sender. + fields["from"], _ = types.Sender(signer, tx) + fields["to"] = tx.To() + + } + return fields +}
diff --git go-ethereum/internal/web3ext/web3ext.go celo/internal/web3ext/web3ext.go index 78bca7af8fb6821575d491b2bd018ee42794d02c..580be6722f1395aad8800337e4c1263ae23e65c2 100644 --- go-ethereum/internal/web3ext/web3ext.go +++ celo/internal/web3ext/web3ext.go @@ -19,10 +19,9 @@ package web3ext   var Modules = map[string]string{ "admin": AdminJs, - "clique": CliqueJs, - "ethash": EthashJs, "debug": DebugJs, "eth": EthJs, + "istanbul": Istanbul_JS, "miner": MinerJs, "net": NetJs, "personal": PersonalJs, @@ -32,91 +31,6 @@ "les": LESJs, "vflux": VfluxJs, }   -const CliqueJs = ` -web3._extend({ - property: 'clique', - methods: [ - new web3._extend.Method({ - name: 'getSnapshot', - call: 'clique_getSnapshot', - params: 1, - inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] - }), - new web3._extend.Method({ - name: 'getSnapshotAtHash', - call: 'clique_getSnapshotAtHash', - params: 1 - }), - new web3._extend.Method({ - name: 'getSigners', - call: 'clique_getSigners', - params: 1, - inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] - }), - new web3._extend.Method({ - name: 'getSignersAtHash', - call: 'clique_getSignersAtHash', - params: 1 - }), - new web3._extend.Method({ - name: 'propose', - call: 'clique_propose', - params: 2 - }), - new web3._extend.Method({ - name: 'discard', - call: 'clique_discard', - params: 1 - }), - new web3._extend.Method({ - name: 'status', - call: 'clique_status', - params: 0 - }), - new web3._extend.Method({ - name: 'getSigner', - call: 'clique_getSigner', - params: 1, - inputFormatter: [null] - }), - ], - properties: [ - new web3._extend.Property({ - name: 'proposals', - getter: 'clique_proposals' - }), - ] -}); -` - -const EthashJs = ` -web3._extend({ - property: 'ethash', - methods: [ - new web3._extend.Method({ - name: 'getWork', - call: 'ethash_getWork', - params: 0 - }), - new web3._extend.Method({ - name: 'getHashrate', - call: 'ethash_getHashrate', - params: 0 - }), - new web3._extend.Method({ - name: 'submitWork', - call: 'ethash_submitWork', - params: 3, - }), - new web3._extend.Method({ - name: 'submitHashrate', - call: 'ethash_submitHashrate', - params: 2, - }), - ] -}); -` - const AdminJs = ` web3._extend({ property: 'admin', @@ -192,16 +106,20 @@ }), ], properties: [ new web3._extend.Property({ + name: 'datadir', + getter: 'admin_datadir' + }), + new web3._extend.Property({ + name: 'discoverTableInfo', + getter: 'admin_discoverTableInfo' + }), + new web3._extend.Property({ name: 'nodeInfo', getter: 'admin_nodeInfo' }), new web3._extend.Property({ name: 'peers', getter: 'admin_peers' - }), - new web3._extend.Property({ - name: 'datadir', - getter: 'admin_datadir' }), ] }); @@ -227,12 +145,6 @@ new web3._extend.Method({ name: 'getBlockRlp', call: 'debug_getBlockRlp', params: 1 - }), - new web3._extend.Method({ - name: 'testSignCliqueBlock', - call: 'debug_testSignCliqueBlock', - params: 2, - inputFormatter: [web3._extend.formatters.inputAddressFormatter, null], }), new web3._extend.Method({ name: 'setHead', @@ -535,6 +447,12 @@ params: 2, inputFormatter: [null, function (val) { return !!val; }] }), new web3._extend.Method({ + name: 'getBlockReceipt', + call: 'eth_getBlockReceipt', + params: 1, + outputFormatter: web3._extend.formatters.outputTransactionReceiptFormatter + }), + new web3._extend.Method({ name: 'getRawTransaction', call: 'eth_getRawTransactionByHash', params: 1 @@ -554,16 +472,20 @@ params: 3, inputFormatter: [web3._extend.formatters.inputAddressFormatter, null, web3._extend.formatters.inputBlockNumberFormatter] }), new web3._extend.Method({ + name: 'validator', + call: 'eth_validator', + params: 0 + }), + new web3._extend.Method({ + name: 'txFeeRecipient', + call: 'eth_txFeeRecipient', + params: 0 + }), + new web3._extend.Method({ name: 'createAccessList', call: 'eth_createAccessList', params: 2, inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter], - }), - new web3._extend.Method({ - name: 'feeHistory', - call: 'eth_feeHistory', - params: 3, - inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter, null] }), ], properties: [ @@ -593,16 +515,6 @@ web3._extend({ property: 'miner', methods: [ new web3._extend.Method({ - name: 'start', - call: 'miner_start', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'stop', - call: 'miner_stop' - }), - new web3._extend.Method({ name: 'setEtherbase', call: 'miner_setEtherbase', params: 1, @@ -620,19 +532,16 @@ params: 1, inputFormatter: [web3._extend.utils.fromDecimal] }), new web3._extend.Method({ - name: 'setGasLimit', - call: 'miner_setGasLimit', - params: 1, - inputFormatter: [web3._extend.utils.fromDecimal] - }), - new web3._extend.Method({ - name: 'setRecommitInterval', - call: 'miner_setRecommitInterval', - params: 1, + name: 'start', + call: 'miner_start', + params: 0, + inputFormatter: [] }), new web3._extend.Method({ - name: 'getHashrate', - call: 'miner_getHashrate' + name: 'stop', + call: 'miner_stop', + params: 0, + inputFormatter: [] }), ], properties: [] @@ -657,6 +566,12 @@ web3._extend({ property: 'personal', methods: [ new web3._extend.Method({ + name: 'decrypt', + call: 'personal_decrypt', + params: 2, + inputFormatter: [null, web3._extend.formatters.inputAddressFormatter] + }), + new web3._extend.Method({ name: 'importRawKey', call: 'personal_importRawKey', params: 2 @@ -749,7 +664,126 @@ name: 'contentFrom', call: 'txpool_contentFrom', params: 1, }), - ] + ] + }); +` + +const Istanbul_JS = ` +web3._extend({ + property: 'istanbul', + methods: + [ + new web3._extend.Method({ + name: 'getSnapshot', + call: 'istanbul_getSnapshot', + params: 1, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] + }), + new web3._extend.Method({ + name: 'getValidators', + call: 'istanbul_getValidators', + params: 1, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] + }), + new web3._extend.Method({ + name: 'getValidatorsBLSPublicKeys', + call: 'istanbul_getValidatorsBLSPublicKeys', + params: 1, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] + }), + new web3._extend.Method({ + name: 'getProposer', + call: 'istanbul_getProposer', + params: 2, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, null] + }), + new web3._extend.Method({ + name: 'getLookbackWindow', + call: 'istanbul_getLookbackWindow', + params: 1, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] + }), + new web3._extend.Method({ + name: 'addProxy', + call: 'istanbul_addProxy', + params: 2 + }), + new web3._extend.Method({ + name: 'removeProxy', + call: 'istanbul_removeProxy', + params: 1 + }), + new web3._extend.Method({ + name: 'startAtBlock', + call: 'istanbul_startValidatingAtBlock', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'stopAtBlock', + call: 'istanbul_stopValidatingAtBlock', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'start', + call: 'istanbul_startValidating', + params: 0, + }), + new web3._extend.Method({ + name: 'stop', + call: 'istanbul_stopValidating', + params: 0, + }), + new web3._extend.Method({ + name: 'resendPreprepare', + call: 'istanbul_resendPreprepare', + params: 0, + }), + new web3._extend.Method({ + name: 'gossipPrepares', + call: 'istanbul_gossipPrepares', + params: 0, + }), + new web3._extend.Method({ + name: 'gossipCommits', + call: 'istanbul_gossipCommits', + params: 0, + }), + new web3._extend.Property({ + name: 'valEnodeTableInfo', + getter: 'istanbul_getValEnodeTable', + }), + new web3._extend.Property({ + name: 'versionCertificateTableInfo', + getter: 'istanbul_getVersionCertificateTableInfo', + }), + new web3._extend.Property({ + name: 'currentRoundState', + getter: 'istanbul_getCurrentRoundState', + }), + new web3._extend.Property({ + name: 'currentRoundChangeSet', + getter: 'istanbul_getCurrentRoundChangeSet', + }), + new web3._extend.Property({ + name: 'proxies', + getter: 'istanbul_getProxiesInfo', + }), + new web3._extend.Property({ + name: 'proxiedValidators', + getter: 'istanbul_getProxiedValidators', + }), + new web3._extend.Property({ + name: 'validating', + getter: 'istanbul_isValidating', + }), + new web3._extend.Property({ + name: 'replicaState', + getter: 'istanbul_getCurrentReplicaState', + }), + ], + properties: [] }); `   @@ -788,6 +822,26 @@ name: 'addBalance', call: 'les_addBalance', params: 2 }), + new web3._extend.Method({ + name: 'setGatewayFeeRecipient', + call: 'les_setGatewayFeeRecipient', + params: 1 + }), + new web3._extend.Method({ + name: 'requestPeerGatewayFees', + call: 'les_requestPeerGatewayFees', + params: 0 + }), + new web3._extend.Method({ + name: 'suggestGatewayFee', + call: 'les_suggestGatewayFee', + params: 0 + }), + new web3._extend.Method({ + name: 'setGatewayFee', + call: 'les_setGatewayFee', + params: 1 + }) ], properties: [ @@ -803,6 +857,18 @@ new web3._extend.Property({ name: 'serverInfo', getter: 'les_serverInfo' }), + new web3._extend.Property({ + name: 'gatewayFee', + getter: 'les_gatewayFee' + }), + new web3._extend.Property({ + name: 'gatewayFeeRecipient', + getter: 'les_gatewayFeeRecipient' + }), + new web3._extend.Property({ + name: 'gatewayFeeCache', + getter: 'les_gatewayFeeCache' + }) ] }); `
diff --git go-ethereum/internal/fileutils/fileutils.go celo/internal/fileutils/fileutils.go new file mode 100644 index 0000000000000000000000000000000000000000..fe77b0aae8e6c055160872fde297c193bdb9ed2f --- /dev/null +++ celo/internal/fileutils/fileutils.go @@ -0,0 +1,69 @@ +// Copyright 2020 Celo Org +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fileutils + +import ( + "fmt" + "io" + "os" +) + +func FileExists(filepath string) bool { + _, err := os.Stat(filepath) + return err == nil || !os.IsNotExist(err) +} + +func IsDirectory(dirpath string) (bool, error) { + stat, err := os.Stat(dirpath) + if err != nil { + return false, err + } + return stat.IsDir(), nil + +} + +func TouchFile(filepath string) error { + file, err := os.Create(filepath) + if err != nil { + return err + } + defer file.Close() + return nil +} + +func Copy(src, dst string) (int64, error) { + sourceFileStat, err := os.Stat(src) + if err != nil { + return 0, err + } + + if !sourceFileStat.Mode().IsRegular() { + return 0, fmt.Errorf("%s is not a regular file", src) + } + + source, err := os.Open(src) + if err != nil { + return 0, err + } + defer source.Close() + + destination, err := os.Create(dst) + if err != nil { + return 0, err + } + defer destination.Close() + nBytes, err := io.Copy(destination, source) + return nBytes, err +}
diff --git go-ethereum/internal/ethapi/transaction_args.go celo/internal/ethapi/transaction_args.go index 9566628313090bc89727af038766cd8cfd61ff72..a745e106a2496a3ddcd4af8c6dd4b952b8c13807 100644 --- go-ethereum/internal/ethapi/transaction_args.go +++ celo/internal/ethapi/transaction_args.go @@ -40,8 +40,12 @@ Gas *hexutil.Uint64 `json:"gas"` GasPrice *hexutil.Big `json:"gasPrice"` MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` + FeeCurrency *common.Address `json:"feeCurrency"` + GatewayFeeRecipient *common.Address `json:"gatewayFeeRecipient"` + GatewayFee *hexutil.Big `json:"gatewayFee"` Value *hexutil.Big `json:"value"` Nonce *hexutil.Uint64 `json:"nonce"` + EthCompatible bool `json:"ethCompatible"`   // We accept "data" and "input" for backwards-compatibility reasons. // "input" is the newer name and should be preferred by clients. @@ -75,6 +79,9 @@ }   // setDefaults fills in default values for unspecified tx fields. func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { + if err := args.checkEthCompatibility(); err != nil { + return err + } if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") } @@ -84,39 +91,39 @@ // If user specifies both maxPriorityfee and maxFee, then we do not // need to consult the chain for defaults. It's definitely a London tx. if args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil { // In this clause, user left some fields unspecified. - if b.ChainConfig().IsLondon(head.Number) && args.GasPrice == nil { - if args.MaxPriorityFeePerGas == nil { - tip, err := b.SuggestGasTipCap(ctx) - if err != nil { - return err + if b.ChainConfig().IsEspresso(head.Number) { + if args.GasPrice == nil || args.GasPrice.ToInt().Cmp(big.NewInt(0)) == 0 { + if args.MaxPriorityFeePerGas == nil { + tip, err := b.SuggestGasTipCap(ctx, args.FeeCurrency) + if err != nil { + return err + } + args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) } - args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) - } - if args.MaxFeePerGas == nil { - gasFeeCap := new(big.Int).Add( - (*big.Int)(args.MaxPriorityFeePerGas), - new(big.Int).Mul(head.BaseFee, big.NewInt(2)), - ) - args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) - } - if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { - return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) + if args.MaxFeePerGas == nil { + gasPriceMinimum, err := b.CurrentGasPriceMinimum(ctx, args.FeeCurrency) + if err != nil { + return err + } + gasFeeCap := new(big.Int).Add( + (*big.Int)(args.MaxPriorityFeePerGas), + new(big.Int).Mul(gasPriceMinimum, big.NewInt(2)), + ) + args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) + } + if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) + } } } else { if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { return errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") } - if args.GasPrice == nil { - price, err := b.SuggestGasTipCap(ctx) + if args.GasPrice == nil || args.GasPrice.ToInt().Cmp(big.NewInt(0)) == 0 { + price, err := b.SuggestPrice(ctx, args.FeeCurrency) if err != nil { return err } - if b.ChainConfig().IsLondon(head.Number) { - // The legacy tx gas price suggestion should not add 2x base fee - // because all fees are consumed, so it would result in a spiral - // upwards. - price.Add(price, head.BaseFee) - } args.GasPrice = (*hexutil.Big)(price) } } @@ -142,6 +149,12 @@ } if args.To == nil && len(args.data()) == 0 { return errors.New(`contract creation without any data provided`) } + if args.GatewayFeeRecipient == nil && !args.EthCompatible { + recipient := b.GatewayFeeRecipient() + if (recipient != common.Address{}) { + args.GatewayFeeRecipient = &recipient + } + } // Estimate the gas usage if necessary. if args.Gas == nil { // These fields are immutable during the estimation, safe to @@ -153,6 +166,9 @@ To: args.To, GasPrice: args.GasPrice, MaxFeePerGas: args.MaxFeePerGas, MaxPriorityFeePerGas: args.MaxPriorityFeePerGas, + FeeCurrency: args.FeeCurrency, + GatewayFee: args.GatewayFee, + GatewayFeeRecipient: args.GatewayFeeRecipient, Value: args.Value, Data: (*hexutil.Bytes)(&data), AccessList: args.AccessList, @@ -164,6 +180,9 @@ return err } args.Gas = &estimated log.Trace("Estimate gas usage automatically", "gas", args.Gas) + } + if args.GatewayFeeRecipient != nil && args.GatewayFee == nil { + args.GatewayFee = (*hexutil.Big)(b.GatewayFee()) } if args.ChainID == nil { id := (*hexutil.Big)(b.ChainConfig().ChainID) @@ -239,7 +258,7 @@ var accessList types.AccessList if args.AccessList != nil { accessList = *args.AccessList } - msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, gasFeeCap, gasTipCap, data, accessList, true) + msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, gasFeeCap, gasTipCap, args.FeeCurrency, args.GatewayFeeRecipient, args.GatewayFee.ToInt(), data, accessList, args.EthCompatible, true) return msg, nil }   @@ -283,6 +302,12 @@ Gas: uint64(*args.Gas), GasPrice: (*big.Int)(args.GasPrice), Value: (*big.Int)(args.Value), Data: args.data(), + EthCompatible: args.EthCompatible, + } + if !args.EthCompatible { + data.(*types.LegacyTx).FeeCurrency = args.FeeCurrency + data.(*types.LegacyTx).GatewayFeeRecipient = args.GatewayFeeRecipient + data.(*types.LegacyTx).GatewayFee = args.GatewayFee.ToInt() } } return types.NewTx(data) @@ -293,3 +318,11 @@ // This assumes that setDefaults has been called. func (args *TransactionArgs) ToTransaction() *types.Transaction { return args.toTransaction() } + +func (args *TransactionArgs) checkEthCompatibility() error { + // Reject if Celo-only fields set when EthCompatible is true + if args.EthCompatible && !(args.FeeCurrency == nil && args.GatewayFeeRecipient == nil && (args.GatewayFee == nil || args.GatewayFee.ToInt().Sign() == 0)) { + return types.ErrEthCompatibleTransactionIsntCompatible + } + return nil +}
diff --git go-ethereum/internal/syncx/mutex.go celo/internal/syncx/mutex.go new file mode 100644 index 0000000000000000000000000000000000000000..96a21986c60c20832948a444dceca6f6651f4d5a --- /dev/null +++ celo/internal/syncx/mutex.go @@ -0,0 +1,64 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// Package syncx contains exotic synchronization primitives. +package syncx + +// ClosableMutex is a mutex that can also be closed. +// Once closed, it can never be taken again. +type ClosableMutex struct { + ch chan struct{} +} + +func NewClosableMutex() *ClosableMutex { + ch := make(chan struct{}, 1) + ch <- struct{}{} + return &ClosableMutex{ch} +} + +// TryLock attempts to lock cm. +// If the mutex is closed, TryLock returns false. +func (cm *ClosableMutex) TryLock() bool { + _, ok := <-cm.ch + return ok +} + +// MustLock locks cm. +// If the mutex is closed, MustLock panics. +func (cm *ClosableMutex) MustLock() { + _, ok := <-cm.ch + if !ok { + panic("mutex closed") + } +} + +// Unlock unlocks cm. +func (cm *ClosableMutex) Unlock() { + select { + case cm.ch <- struct{}{}: + default: + panic("Unlock of already-unlocked ClosableMutex") + } +} + +// Close locks the mutex, then closes it. +func (cm *ClosableMutex) Close() { + _, ok := <-cm.ch + if !ok { + panic("Close of already-closed ClosableMutex") + } + close(cm.ch) +}
diff --git go-ethereum/internal/build/download.go celo/internal/build/download.go index cbb5b61378c43848a5e24237a4778619da6b71df..21bd2e93ff98ff7b9ec7a8902836d88c2da990a6 100644 --- go-ethereum/internal/build/download.go +++ celo/internal/build/download.go @@ -82,6 +82,7 @@ } fmt.Printf("%s is stale\n", dstPath) fmt.Printf("downloading from %s\n", url)   + // #nosec (Ignoring sanitization warning. Intended to be an arbitrary url.) resp, err := http.Get(url) if err != nil { return fmt.Errorf("download error: %v", err)
diff --git go-ethereum/internal/ethapi/util_test.go celo/internal/ethapi/util_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9d48a07cc8e433969c21cfee2baa278b3152a62a --- /dev/null +++ celo/internal/ethapi/util_test.go @@ -0,0 +1,57 @@ +package ethapi + +import ( + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/currency" + "github.com/ethereum/go-ethereum/params" +) + +var mockCurrencyAddress = common.HexToAddress("01010101010101") +var noCurrError = errors.New("noCurrErr") +var mockRate, _ = currency.NewExchangeRate(common.Big2, big.NewInt(7*params.Ether)) // 3.5 celo per mock token + +type mockProvider struct{} + +func (mp *mockProvider) GetCurrency(currencyAddress *common.Address) (*currency.Currency, error) { + if currencyAddress == nil { + return &currency.CELOCurrency, nil + } + if currencyAddress.Hash() != mockCurrencyAddress.Hash() { + return nil, noCurrError + } + return currency.NewCurrency(mockCurrencyAddress, *mockRate), nil +} + +func TestCheckTxFeeCap_Currency(t *testing.T) { + p := &mockProvider{} + fee := big.NewInt(50) // * mockRate == 175 celo + if err := CheckTxFee(p, &mockCurrencyAddress, fee, 175.0+0.1); err != nil { + t.Fatal("Failed tx fee cap check on non-celo currency: false negative", err) + } + if err := CheckTxFee(p, &mockCurrencyAddress, fee, 175.0-0.1); err == nil { + t.Fatal("Failed tx fee cap check on non-celo currency: false positive") + } +} + +func TestCheckTxFeeCap_CeloCurrency(t *testing.T) { + p := &mockProvider{} + fee := big.NewInt(2 * params.Ether) + if err := CheckTxFee(p, nil, fee, 2.0+0.1); err != nil { + t.Fatal("Failed tx fee cap check on non-celo currency: false negative", err) + } + if err := CheckTxFee(p, nil, fee, 2.0-0.1); err == nil { + t.Fatal("Failed tx fee cap check on non-celo currency: false positive") + } +} + +func TestCheckTxFeeCap_NoCurrency(t *testing.T) { + p := &mockProvider{} + addr := common.HexToAddress("FFF") + if err := CheckTxFee(p, &addr, common.Big1, 999999999); err == nil { + t.Fatal("Failed tx fee cap check on non-celo currency: false positive") + } +}
diff --git go-ethereum/internal/ethapi/backend.go celo/internal/ethapi/backend.go index d3a756e5918c311998129578eebc158d872ef128..5da64d6a7d20f26c2d6df182b1a752a776d9533a 100644 --- go-ethereum/internal/ethapi/backend.go +++ celo/internal/ethapi/backend.go @@ -40,15 +40,24 @@ // Backend interface provides the common API services (that are provided by // both full and light clients) with access to necessary functions. type Backend interface { // General Ethereum API + SuggestPrice(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) + SuggestGasTipCap(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) + CurrentGasPriceMinimum(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) + GasPriceMinimumForHeader(ctx context.Context, currencyAddress *common.Address, header *types.Header) (*big.Int, error) + + // As opposed to GasPriceMinimumForHeader which returns a default value in + // some cases when it can't retrieve the gas price minimum, this function + // returns an error and no gas price minimum when it encounters a problem. + RealGasPriceMinimumForHeader(ctx context.Context, currencyAddress *common.Address, header *types.Header) (*big.Int, error) SyncProgress() ethereum.SyncProgress   - SuggestGasTipCap(ctx context.Context) (*big.Int, error) - FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) ChainDb() ethdb.Database AccountManager() *accounts.Manager ExtRPCEnabled() bool + RPCGasInflationRate() float64 // global multiplier applied to the gas estimations RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs + RPCEthCompatibility() bool // determines if the fields 'gasLimit' and 'baseFeePerGas' should be returned by the RPC API. UnprotectedAllowed() bool // allows only for EIP155 transactions.   // Blockchain API @@ -90,6 +99,17 @@ SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription   ChainConfig() *params.ChainConfig + + GatewayFeeRecipient() common.Address + GatewayFee() *big.Int + GetIntrinsicGasForAlternativeFeeCurrency(ctx context.Context) uint64 + GetBlockGasLimit(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) uint64 + + // As opposed to GetBlockGasLimit which returns a default value in the case + // that it can't retrieve the block gas limit, this function returns an + // error and no gas limit when it encounters a problem. + GetRealBlockGasLimit(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (uint64, error) + NewEVMRunner(*types.Header, vm.StateDB) vm.EVMRunner Engine() consensus.Engine }
diff --git go-ethereum/internal/build/util.go celo/internal/build/util.go index 2bdced82ee2ff550c8dd913778c079262aad5f67..6fcbea6252c2b10ae676772b80eeefe84e2dd7c8 100644 --- go-ethereum/internal/build/util.go +++ celo/internal/build/util.go @@ -43,7 +43,8 @@ if !*DryRunFlag { cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { - log.Fatal(err) + log.Printf("Command failed \"%s\", err: \"%v\"", strings.Join(cmd.Args, " "), err) + log.Fatalf("Command failed \"%s\", err: \"%v\"", strings.Join(cmd.Args, " "), err) } } }
diff --git go-ethereum/les/sync_test.go celo/les/sync_test.go index 13864b33580e18603016eed1f383602d19396f40..725f12540134973dd09d33c0b82d41b29fb59fff 100644 --- go-ethereum/les/sync_test.go +++ celo/les/sync_test.go @@ -26,6 +26,7 @@ "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/les/downloader" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/params" ) @@ -42,6 +43,7 @@ // on a verified checkpoint. func TestCheckpointSyncingLes3(t *testing.T) { testCheckpointSyncing(t, lpv3, 2) }   func testCheckpointSyncing(t *testing.T, protocol int, syncMode int) { + t.Skip("We are not using checkpoints") config := light.TestServerIndexerConfig   waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { @@ -57,6 +59,7 @@ } // Generate 128+1 blocks (totally 1 CHT section) netconfig := testnetConfig{ blocks: int(config.ChtSize + config.ChtConfirms), + syncMode: downloader.LightSync, protocol: protocol, indexFn: waitIndexers, nopruning: true, @@ -87,7 +90,7 @@ data := append([]byte{0x19, 0x00}, append(oracleAddr.Bytes(), append([]byte{0, 0, 0, 0, 0, 0, 0, 0}, cp.Hash().Bytes()...)...)...) sig, _ := crypto.Sign(crypto.Keccak256(data), signerKey) sig[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper - auth, _ := bind.NewKeyedTransactorWithChainID(signerKey, big.NewInt(1337)) + auth, _ := bind.NewKeyedTransactorWithChainID(signerKey, server.backend.Blockchain().Config().ChainID) if _, err := server.handler.server.oracle.Contract().RegisterCheckpoint(auth, cp.SectionIndex, cp.Hash().Bytes(), new(big.Int).Sub(header.Number, big.NewInt(1)), header.ParentHash, [][]byte{sig}); err != nil { t.Error("register checkpoint failed", err) } @@ -153,6 +156,7 @@ } // Generate 128+1 blocks (totally 1 CHT section) netconfig := testnetConfig{ blocks: int(config.ChtSize + config.ChtConfirms), + syncMode: downloader.LightSync, protocol: protocol, indexFn: waitIndexers, nopruning: true, @@ -251,6 +255,7 @@ // Generate 256+1 blocks (totally 2 CHT sections) netconfig := testnetConfig{ blocks: int(2*config.ChtSize + config.ChtConfirms), protocol: protocol, + syncMode: downloader.LightSync, indexFn: waitIndexers, nopruning: true, } @@ -335,6 +340,7 @@ // Generate 256+1 blocks (totally 2 CHT sections) netconfig := testnetConfig{ blocks: int(2*config.ChtSize + config.ChtConfirms), protocol: protocol, + syncMode: downloader.LightSync, indexFn: waitIndexers, nopruning: true, }
diff --git go-ethereum/les/odr_test.go celo/les/odr_test.go index d1c3629a57e44d3b2dabbdff5cf7d4769c77660d..bc52e02fd3e711deeb389d32f42329fb88423520 100644 --- go-ethereum/les/odr_test.go +++ celo/les/odr_test.go @@ -28,12 +28,16 @@ "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/contracts/testutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/les/downloader" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -123,6 +127,7 @@ func (callmsg) CheckNonce() bool { return false }   func odrContractCall(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") + celoMock := testutil.NewCeloMock()   var res []byte for i := 0; i < 3; i++ { @@ -135,27 +140,26 @@ if err == nil { from := statedb.GetOrNewStateObject(bankAddr) from.SetBalance(math.MaxBig256)   - msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)} + msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), new(big.Int), new(big.Int), nil, nil, new(big.Int), data, nil, false, true)}   context := core.NewEVMBlockContext(header, bc, nil) txContext := core.NewEVMTxContext(msg) vmenv := vm.NewEVM(context, txContext, statedb, config, vm.Config{NoBaseFee: true})   - //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) - result, _ := core.ApplyMessage(vmenv, msg, gp) + result, _ := core.ApplyMessage(vmenv, msg, gp, celoMock.Runner, nil) res = append(res, result.Return()...) } } else { header := lc.GetHeaderByHash(bhash) state := light.NewState(ctx, header, lc.Odr()) state.SetBalance(bankAddr, math.MaxBig256) - msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)} + msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), new(big.Int), new(big.Int), nil, nil, new(big.Int), data, nil, false, true)} context := core.NewEVMBlockContext(header, lc, nil) txContext := core.NewEVMTxContext(msg) vmenv := vm.NewEVM(context, txContext, state, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) - result, _ := core.ApplyMessage(vmenv, msg, gp) + result, _ := core.ApplyMessage(vmenv, msg, gp, celoMock.Runner, nil) if state.Error() == nil { res = append(res, result.Return()...) } @@ -195,6 +199,7 @@ func testOdr(t *testing.T, protocol int, expFail uint64, checkCached bool, fn odrTestFn) { // Assemble the test environment netconfig := testnetConfig{ blocks: 4, + syncMode: downloader.LightSync, protocol: protocol, connect: true, nopruning: true, @@ -256,13 +261,14 @@ test(5) } }   -func TestGetTxStatusFromUnindexedPeersLES4(t *testing.T) { testGetTxStatusFromUnindexedPeers(t, lpv4) } +func TestGetTxStatusFromUnindexedPeersLES5(t *testing.T) { testGetTxStatusFromUnindexedPeers(t, lpv5) }   func testGetTxStatusFromUnindexedPeers(t *testing.T, protocol int) { var ( blocks = 8 netconfig = testnetConfig{ blocks: blocks, + syncMode: downloader.LightSync, protocol: protocol, nopruning: true, } @@ -306,9 +312,22 @@ } } // serveMsg processes incoming GetTxStatusMsg and sends the response back. serveMsg := func(peer *testPeer, txLookup uint64) error { - msg, err := peer.app.ReadMsg() - if err != nil { - return err + var ( + msg p2p.Msg + err error + ) + loop: + for { + msg, err = peer.app.ReadMsg() + if err != nil { + return err + } + switch msg.Code { + case GetEtherbaseMsg: + continue + default: + break loop + } } if msg.Code != GetTxStatusMsg { return fmt.Errorf("message code mismatch: got %d, expected %d", msg.Code, GetTxStatusMsg) @@ -336,7 +355,9 @@ } } data, _ := rlp.EncodeToBytes(stats) reply := &reply{peer.app, TxStatusMsg, r.ReqID, data} - reply.send(testBufLimit) + if err = reply.send(testBufLimit); err != nil { + return err + } return nil }
diff --git go-ethereum/les/odr_requests.go celo/les/odr_requests.go index dc99d0ed4f9730d3f423599aa00ddc7134c357e7..c282fcfe1cc50398ca7c80d0e0b13259439d82a0 100644 --- go-ethereum/les/odr_requests.go +++ celo/les/odr_requests.go @@ -37,12 +37,12 @@ errInvalidMessageType = errors.New("invalid message type") errInvalidEntryCount = errors.New("invalid number of response entries") errHeaderUnavailable = errors.New("header unavailable") errTxHashMismatch = errors.New("transaction hash mismatch") - errUncleHashMismatch = errors.New("uncle hash mismatch") errReceiptHashMismatch = errors.New("receipt hash mismatch") errDataHashMismatch = errors.New("data hash mismatch") errCHTHashMismatch = errors.New("cht hash mismatch") errCHTNumberMismatch = errors.New("cht number mismatch") errUselessNodes = errors.New("useless nodes in merkle proof nodeset") + errRequestResponseMismatch = errors.New("header and request mismatch") )   type LesOdrRequest interface { @@ -56,6 +56,8 @@ func LesRequest(req light.OdrRequest) LesOdrRequest { switch r := req.(type) { case *light.BlockRequest: return (*BlockRequest)(r) + case *light.HeaderRequest: + return (*HeaderRequest)(r) case *light.ReceiptsRequest: return (*ReceiptsRequest)(r) case *light.TrieRequest: @@ -84,7 +86,7 @@ }   // CanSend tells if a certain peer is suitable for serving the given request func (r *BlockRequest) CanSend(peer *serverPeer) bool { - return peer.HasBlock(r.Hash, r.Number, false) + return peer.HasBlock(r.Hash, &r.Number, false) }   // Request sends an ODR request to the LES network (implementation of LesOdrRequest) @@ -119,9 +121,6 @@ } if r.Header.TxHash != types.DeriveSha(types.Transactions(body.Transactions), trie.NewStackTrie(nil)) { return errTxHashMismatch } - if r.Header.UncleHash != types.CalcUncleHash(body.Uncles) { - return errUncleHashMismatch - } // Validations passed, encode and store RLP data, err := rlp.EncodeToBytes(body) if err != nil { @@ -131,6 +130,65 @@ r.Rlp = data return nil }   +// BlockRequest is the ODR request type for block headers +type HeaderRequest light.HeaderRequest + +// GetCost returns the cost of the given ODR request according to the serving +// peer's cost table (implementation fo LesOdrRequest) +func (r *HeaderRequest) GetCost(peer *serverPeer) uint64 { + return peer.getRequestCost(GetBlockHeadersMsg, 1) +} + +// CanSend tells if a certain peer is suitable for serving the given request +func (r *HeaderRequest) CanSend(peer *serverPeer) bool { + return peer.HasBlock(r.Origin.Hash, r.Origin.Number, false) +} + +// Request sends an ODR request to the LES network (implementation of LesOdrRequest) +func (r *HeaderRequest) Request(reqId uint64, peer *serverPeer) error { + if r.isByHash() { + peer.Log().Debug("Requesting block header", "hash", r.Origin.Hash) + return peer.requestHeadersByHash(reqId, r.Origin.Hash, 1, 0, false) + } else { + peer.Log().Debug("Requesting block header", "number", r.Origin.Number) + return peer.requestHeadersByNumber(reqId, *r.Origin.Number, 1, 0, false) + } +} + +// Whether the request specified the block hash (rather than block number) +func (r *HeaderRequest) isByHash() bool { + return r.Origin.Hash != common.Hash{} +} + +// Validate processes an ODR request reply message from the LES network +// returns true and stores results in memory if the message was a valid reply +// to the request (implementation of LesOdrRequest) +func (r *HeaderRequest) Validate(db ethdb.Database, msg *Msg) error { + if msg.MsgType != MsgBlockHeaders { + log.Error("Bad message response", "type", msg.MsgType) + return errInvalidMessageType + } + headers := msg.Obj.([]*types.Header) + if len(headers) == 0 && r.isByHash() { + // For requests by number, we only send to peers for which we know the block number + // is within the range of what they have, so if they don't send us the header we reject + // the response and try other peers. + // However, for requests by hash, we have no way of knowing ahead of time whether the peer + // should have it or not (e.g. what if there is no such block?). So we need to accept + // 'no match' as a valid response, to avoid ODR endlessly trying to send to different peers. + return nil + } else if len(headers) != 1 { + return errInvalidEntryCount + } + if r.Origin.Hash != (common.Hash{}) && headers[0].Hash() != r.Origin.Hash { + return errRequestResponseMismatch + } else if r.Origin.Hash == (common.Hash{}) && headers[0].Number.Uint64() != *r.Origin.Number { + return errRequestResponseMismatch + } + r.Header = headers[0] + return nil +} + // ReceiptsRequest is the ODR request type for block receipts by block hash type ReceiptsRequest light.ReceiptsRequest   @@ -142,7 +200,7 @@ }   // CanSend tells if a certain peer is suitable for serving the given request func (r *ReceiptsRequest) CanSend(peer *serverPeer) bool { - return peer.HasBlock(r.Hash, r.Number, false) + return peer.HasBlock(r.Hash, &r.Number, false) }   // Request sends an ODR request to the LES network (implementation of LesOdrRequest) @@ -199,7 +257,7 @@ }   // CanSend tells if a certain peer is suitable for serving the given request func (r *TrieRequest) CanSend(peer *serverPeer) bool { - return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber, true) + return peer.HasBlock(r.Id.BlockHash, &r.Id.BlockNumber, true) }   // Request sends an ODR request to the LES network (implementation of LesOdrRequest) @@ -253,7 +311,7 @@ }   // CanSend tells if a certain peer is suitable for serving the given request func (r *CodeRequest) CanSend(peer *serverPeer) bool { - return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber, true) + return peer.HasBlock(r.Id.BlockHash, &r.Id.BlockNumber, true) }   // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
diff --git go-ethereum/les/sync.go celo/les/sync.go index 98de1c6aef278c598d63ea1ee0b05b1a1b55c3c2..71e8245107b19ea8ea931563cf30a87c9b8a0214 100644 --- go-ethereum/les/sync.go +++ celo/les/sync.go @@ -23,7 +23,6 @@ "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/les/downloader" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -91,9 +90,10 @@ // synchronise tries to sync up our local chain with a remote peer. func (h *clientHandler) synchronise(peer *serverPeer) { // Short circuit if the peer is nil. if peer == nil { + log.Debug("Synchronise no peer available") return } - // Make sure the peer's TD is higher than our own. + latest := h.backend.blockchain.CurrentHeader() currentTd := rawdb.ReadTd(h.backend.chainDb, latest.Hash(), latest.Number.Uint64()) if currentTd != nil && peer.Td().Cmp(currentTd) < 0 { @@ -196,7 +196,7 @@ if h.syncStart != nil { h.syncStart(h.backend.blockchain.CurrentHeader()) } // Fetch the remaining block headers based on the current chain header. - if err := h.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync); err != nil { + if err := h.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), h.syncMode); err != nil { log.Debug("Synchronise failed", "reason", err) return }
diff --git go-ethereum/les/api_backend.go celo/les/api_backend.go index 1454c2bd04f6fe618b14a2e748b383842c09978c..01e7b0ed8ad9afe6a15aed7f94b807c97bd03e3a 100644 --- go-ethereum/les/api_backend.go +++ celo/les/api_backend.go @@ -19,22 +19,26 @@ import ( "context" "errors" + "fmt" "math/big"   "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" + gpm "github.com/ethereum/go-ethereum/contracts/gasprice_minimum" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -43,7 +47,6 @@ type LesApiBackend struct { extRPCEnabled bool allowUnprotectedTxs bool eth *LightEthereum - gpo *gasprice.Oracle }   func (b *LesApiBackend) ChainConfig() *params.ChainConfig { @@ -265,12 +268,79 @@ func (b *LesApiBackend) ProtocolVersion() int { return b.eth.LesVersion() + 10000 }   -func (b *LesApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestTipCap(ctx) +func (b *LesApiBackend) SuggestPrice(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) { + vmRunner, err := b.eth.BlockChain().NewEVMRunnerForCurrentBlock() + if err != nil { + return nil, err + } + return gpm.GetGasPriceSuggestion(vmRunner, currencyAddress) +} + +func (b *LesApiBackend) GetIntrinsicGasForAlternativeFeeCurrency(ctx context.Context) uint64 { + vmRunner, err := b.eth.BlockChain().NewEVMRunnerForCurrentBlock() + if err != nil { + log.Warn("Cannot read intrinsic gas for alternative fee currency", "err", err) + return blockchain_parameters.DefaultIntrinsicGasForAlternativeFeeCurrency + } + return blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(vmRunner) +} + +func (b *LesApiBackend) GetBlockGasLimit(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) uint64 { + statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if err != nil { + log.Warn("Cannot create evmCaller to get blockGasLimit", "err", err) + return params.DefaultGasLimit + } + + caller := b.eth.BlockChain().NewEVMRunner(header, statedb) + return blockchain_parameters.GetBlockGasLimitOrDefault(caller) +} + +func (b *LesApiBackend) GetRealBlockGasLimit(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (uint64, error) { + statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if err != nil { + return 0, fmt.Errorf("LesApiBackend failed to retrieve state for block gas limit for block %v: %w", blockNrOrHash, err) + } + + caller := b.eth.BlockChain().NewEVMRunner(header, statedb) + limit, err := blockchain_parameters.GetBlockGasLimit(caller) + if err != nil { + return 0, fmt.Errorf("LesApiBackend failed to retrieve block gas limit from blockchain parameters constract for block %v: %w", blockNrOrHash, err) + } + return limit, nil }   -func (b *LesApiBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { - return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) +func (b *LesApiBackend) NewEVMRunner(header *types.Header, state vm.StateDB) vm.EVMRunner { + return b.eth.BlockChain().NewEVMRunner(header, state) +} + +func (b *LesApiBackend) SuggestGasTipCap(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) { + vmRunner, err := b.eth.BlockChain().NewEVMRunnerForCurrentBlock() + if err != nil { + return nil, err + } + return gpm.GetGasTipCapSuggestion(vmRunner, currencyAddress) +} + +func (b *LesApiBackend) CurrentGasPriceMinimum(ctx context.Context, currencyAddress *common.Address) (*big.Int, error) { + vmRunner, err := b.eth.BlockChain().NewEVMRunnerForCurrentBlock() + if err != nil { + return nil, err + } + return gpm.GetGasPriceMinimum(vmRunner, currencyAddress) +} + +func (b *LesApiBackend) GasPriceMinimumForHeader(ctx context.Context, currencyAddress *common.Address, header *types.Header) (*big.Int, error) { + state := light.NewState(ctx, header, b.eth.odr) + vmRunner := b.eth.blockchain.NewEVMRunner(header, state) + + return gpm.GetGasPriceMinimum(vmRunner, currencyAddress) +} + +func (b *LesApiBackend) RealGasPriceMinimumForHeader(ctx context.Context, currencyAddress *common.Address, header *types.Header) (*big.Int, error) { + state := light.NewState(ctx, header, b.eth.odr) + vmRunner := b.eth.blockchain.NewEVMRunner(header, state) + return gpm.GetRealGasPriceMinimum(vmRunner, currencyAddress) }   func (b *LesApiBackend) ChainDb() ethdb.Database { @@ -289,12 +359,20 @@ func (b *LesApiBackend) UnprotectedAllowed() bool { return b.allowUnprotectedTxs }   +func (b *LesApiBackend) RPCGasInflationRate() float64 { + return b.eth.config.RPCGasInflationRate +} + func (b *LesApiBackend) RPCGasCap() uint64 { return b.eth.config.RPCGasCap }   func (b *LesApiBackend) RPCTxFeeCap() float64 { return b.eth.config.RPCTxFeeCap +} + +func (b *LesApiBackend) RPCEthCompatibility() bool { + return b.eth.config.RPCEthCompatibility }   func (b *LesApiBackend) BloomStatus() (uint64, uint64) { @@ -311,6 +389,15 @@ go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests) } }   +func (b *LesApiBackend) GatewayFeeRecipient() common.Address { + return b.eth.GetRandomPeerEtherbase() +} + +func (b *LesApiBackend) GatewayFee() *big.Int { + // TODO(nategraf): Create a method to fetch the gateway fee values of peers along with the coinbase. + return ethconfig.Defaults.GatewayFee +} + func (b *LesApiBackend) Engine() consensus.Engine { return b.eth.engine } @@ -323,6 +410,6 @@ func (b *LesApiBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) { return b.eth.stateAtBlock(ctx, block, reexec) }   -func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { +func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, vm.EVMRunner, *state.StateDB, error) { return b.eth.stateAtTransaction(ctx, block, txIndex, reexec) }
diff --git go-ethereum/les/ulc_test.go celo/les/ulc_test.go index 0d14402855f43db9dbfc5845e49c92478418d044..573f75e0efd9fb7ddbde836223a500ede8248d8b 100644 --- go-ethereum/les/ulc_test.go +++ celo/les/ulc_test.go @@ -25,6 +25,7 @@ "testing" "time"   "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/les/downloader" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" ) @@ -152,6 +153,7 @@ // newTestLightPeer creates node with light sync mode func newTestLightPeer(t *testing.T, protocol int, ulcServers []string, ulcFraction int) (*testClient, func()) { netconfig := testnetConfig{ protocol: protocol, + syncMode: downloader.LightSync, ulcServers: ulcServers, ulcFraction: ulcFraction, nopruning: true,
diff --git go-ethereum/les/test_helper.go celo/les/test_helper.go index e4f8817304df43e31be207d982b92e3012702931..dbe73288305f2b9883a3102d321e4de12a4e5931 100644 --- go-ethereum/les/test_helper.go +++ celo/les/test_helper.go @@ -33,7 +33,7 @@ "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/forkid" @@ -44,6 +44,7 @@ "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/les/checkpointoracle" + "github.com/ethereum/go-ethereum/les/downloader" "github.com/ethereum/go-ethereum/les/flowcontrol" vfs "github.com/ethereum/go-ethereum/les/vflux/server" "github.com/ethereum/go-ethereum/light" @@ -123,7 +124,7 @@ oracleAddr, _, _, _ = contract.DeployCheckpointOracle(auth, backend, []common.Address{signerAddr}, sectionSize, processConfirms, big.NewInt(1))   // bankUser transfers some ether to user1 nonce, _ := backend.PendingNonceAt(ctx, bankAddr) - tx, _ := types.SignTx(types.NewTransaction(nonce, userAddr1, big.NewInt(10_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey) + tx, _ := types.SignTx(types.NewTransaction(nonce, userAddr1, big.NewInt(10_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, bankKey) backend.SendTransaction(ctx, tx) case 1: // Builtin-block @@ -134,20 +135,20 @@ bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) userNonce1, _ := backend.PendingNonceAt(ctx, userAddr1)   // bankUser transfers more ether to user1 - tx1, _ := types.SignTx(types.NewTransaction(bankNonce, userAddr1, big.NewInt(1_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey) + tx1, _ := types.SignTx(types.NewTransaction(bankNonce, userAddr1, big.NewInt(1_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, bankKey) backend.SendTransaction(ctx, tx1)   // user1 relays ether to user2 - tx2, _ := types.SignTx(types.NewTransaction(userNonce1, userAddr2, big.NewInt(1_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, userKey1) + tx2, _ := types.SignTx(types.NewTransaction(userNonce1, userAddr2, big.NewInt(1_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, userKey1) backend.SendTransaction(ctx, tx2)   // user1 deploys a test contract - tx3, _ := types.SignTx(types.NewContractCreation(userNonce1+1, big.NewInt(0), 200000, big.NewInt(params.InitialBaseFee), testContractCode), signer, userKey1) + tx3, _ := types.SignTx(types.NewContractCreation(userNonce1+1, big.NewInt(0), 200000, nil, nil, nil, nil, testContractCode), signer, userKey1) backend.SendTransaction(ctx, tx3) testContractAddr = crypto.CreateAddress(userAddr1, userNonce1+1)   // user1 deploys a event contract - tx4, _ := types.SignTx(types.NewContractCreation(userNonce1+2, big.NewInt(0), 200000, big.NewInt(params.InitialBaseFee), testEventEmitterCode), signer, userKey1) + tx4, _ := types.SignTx(types.NewContractCreation(userNonce1+2, big.NewInt(0), 200000, nil, nil, nil, nil, testEventEmitterCode), signer, userKey1) backend.SendTransaction(ctx, tx4) case 2: // Builtin-block @@ -156,12 +157,12 @@ // txs: 2   // bankUser transfer some ether to signer bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) - tx1, _ := types.SignTx(types.NewTransaction(bankNonce, signerAddr, big.NewInt(1000000000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey) + tx1, _ := types.SignTx(types.NewTransaction(bankNonce, signerAddr, big.NewInt(1000000000), params.TxGas, nil, nil, nil, nil, nil), signer, bankKey) backend.SendTransaction(ctx, tx1)   // invoke test contract data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001") - tx2, _ := types.SignTx(types.NewTransaction(bankNonce+1, testContractAddr, big.NewInt(0), 100000, big.NewInt(params.InitialBaseFee), data), signer, bankKey) + tx2, _ := types.SignTx(types.NewTransaction(bankNonce+1, testContractAddr, big.NewInt(0), 100000, nil, nil, nil, nil, data), signer, bankKey) backend.SendTransaction(ctx, tx2) case 3: // Builtin-block @@ -171,7 +172,7 @@ // invoke test contract bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002") - tx, _ := types.SignTx(types.NewTransaction(bankNonce, testContractAddr, big.NewInt(0), 100000, big.NewInt(params.InitialBaseFee), data), signer, bankKey) + tx, _ := types.SignTx(types.NewTransaction(bankNonce, testContractAddr, big.NewInt(0), 100000, nil, nil, nil, nil, data), signer, bankKey) backend.SendTransaction(ctx, tx) } backend.Commit() @@ -181,26 +182,25 @@ // testIndexers creates a set of indexers with specified params for testing purpose. func testIndexers(db ethdb.Database, odr light.OdrBackend, config *light.IndexerConfig, disablePruning bool) []*core.ChainIndexer { var indexers [3]*core.ChainIndexer - indexers[0] = light.NewChtIndexer(db, odr, config.ChtSize, config.ChtConfirms, disablePruning) - indexers[1] = core.NewBloomIndexer(db, config.BloomSize, config.BloomConfirms) - indexers[2] = light.NewBloomTrieIndexer(db, odr, config.BloomSize, config.BloomTrieSize, disablePruning) + indexers[0] = light.NewChtIndexer(db, odr, config.ChtSize, config.ChtConfirms, disablePruning, true) + indexers[1] = core.NewBloomIndexer(db, config.BloomSize, config.BloomConfirms, true) + indexers[2] = light.NewBloomTrieIndexer(db, odr, config.BloomSize, config.BloomTrieSize, disablePruning, true) // make bloomTrieIndexer as a child indexer of bloom indexer. indexers[1].AddChildIndexer(indexers[2]) return indexers[:] }   -func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, indexers []*core.ChainIndexer, db ethdb.Database, peers *serverPeerSet, ulcServers []string, ulcFraction int) (*clientHandler, func()) { +func newTestClientHandler(syncMode downloader.SyncMode, backend *backends.SimulatedBackend, odr *LesOdr, indexers []*core.ChainIndexer, db ethdb.Database, peers *serverPeerSet, ulcServers []string, ulcFraction int) (*clientHandler, func()) { var ( evmux = new(event.TypeMux) - engine = ethash.NewFaker() + engine = mockEngine.NewFaker() gspec = core.Genesis{ - Config: params.AllEthashProtocolChanges, + Config: params.IstanbulTestChainConfig, Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}}, - GasLimit: 100000000, - BaseFee: big.NewInt(params.InitialBaseFee), } oracle *checkpointoracle.CheckpointOracle ) + gspec.Config.FullHeaderChainAvailable = syncMode.SyncFullHeaderChain() genesis := gspec.MustCommit(db) chain, _ := light.NewLightChain(odr, gspec.Config, engine, nil) if indexers != nil { @@ -225,7 +225,7 @@ client := &LightEthereum{ lesCommons: lesCommons{ genesis: genesis.Hash(), config: &ethconfig.Config{LightPeers: 100, NetworkId: NetworkId}, - chainConfig: params.AllEthashProtocolChanges, + chainConfig: params.IstanbulTestChainConfig, iConfig: light.TestClientIndexerConfig, chainDb: db, oracle: oracle, @@ -240,7 +240,7 @@ engine: engine, blockchain: chain, eventMux: evmux, } - client.handler = newClientHandler(ulcServers, ulcFraction, nil, client) + client.handler = newClientHandler(syncMode, ulcServers, ulcFraction, nil, client, nil)   if client.oracle != nil { client.oracle.Start(backend) @@ -254,17 +254,15 @@ func newTestServerHandler(blocks int, indexers []*core.ChainIndexer, db ethdb.Database, clock mclock.Clock) (*serverHandler, *backends.SimulatedBackend, func()) { var ( gspec = core.Genesis{ - Config: params.AllEthashProtocolChanges, + Config: params.IstanbulTestChainConfig, Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}}, - GasLimit: 100000000, - BaseFee: big.NewInt(params.InitialBaseFee), } oracle *checkpointoracle.CheckpointOracle ) genesis := gspec.MustCommit(db)   // create a simulation backend and pre-commit several customized block to the database. - simulation := backends.NewSimulatedBackendWithDatabase(db, gspec.Alloc, 100000000) + simulation := backends.NewSimulatedBackendWithDatabase(db, gspec.Alloc) prepare(blocks, simulation)   txpoolConfig := core.DefaultTxPoolConfig @@ -292,7 +290,7 @@ server := &LesServer{ lesCommons: lesCommons{ genesis: genesis.Hash(), config: &ethconfig.Config{LightPeers: 100, NetworkId: NetworkId}, - chainConfig: params.AllEthashProtocolChanges, + chainConfig: params.IstanbulTestChainConfig, iConfig: light.TestServerIndexerConfig, chainDb: db, chainReader: simulation.Blockchain(), @@ -312,7 +310,7 @@ server.costTracker.testCostList = testCostList(0) // Disable flow control mechanism. server.clientPool = vfs.NewClientPool(db, testBufRecharge, defaultConnectedBias, clock, alwaysTrueFn) server.clientPool.Start() server.clientPool.SetLimits(10000, 10000) // Assign enough capacity for clientpool - server.handler = newServerHandler(server, simulation.Blockchain(), db, txpool, func() bool { return true }) + server.handler = newServerHandler(server, simulation.Blockchain(), db, txpool, func() bool { return true }, common.ZeroAddress, ethconfig.Defaults.GatewayFee) if server.oracle != nil { server.oracle.Start(simulation) } @@ -348,7 +346,7 @@ sendList = sendList.add("headTd", td) sendList = sendList.add("headHash", head) sendList = sendList.add("headNum", headNum) sendList = sendList.add("genesisHash", genesis) - if p.cpeer.version >= lpv4 { + if p.cpeer.version >= lpv5 { sendList = sendList.add("forkID", &forkID) } if err := p2p.ExpectMsg(p.app, StatusMsg, nil); err != nil { @@ -380,7 +378,7 @@ sendList = sendList.add("txRelay", nil) sendList = sendList.add("flowControl/BL", testBufLimit) sendList = sendList.add("flowControl/MRR", testBufRecharge) sendList = sendList.add("flowControl/MRC", costList) - if p.speer.version >= lpv4 { + if p.speer.version >= lpv5 { sendList = sendList.add("forkID", &forkID) sendList = sendList.add("recentTxLookup", recentTxLookup) } @@ -575,6 +573,7 @@ // testnetConfig wraps all the configurations for testing network. type testnetConfig struct { blocks int + syncMode downloader.SyncMode protocol int indexFn indexerCallback ulcServers []string @@ -606,7 +605,7 @@ ccIndexer, cbIndexer, cbtIndexer := cIndexers[0], cIndexers[1], cIndexers[2] odr.SetIndexers(ccIndexer, cbIndexer, cbtIndexer)   server, b, serverClose := newTestServerHandler(config.blocks, sindexers, sdb, clock) - client, clientClose := newTestClientHandler(b, odr, cIndexers, cdb, speers, config.ulcServers, config.ulcFraction) + client, clientClose := newTestClientHandler(config.syncMode, b, odr, cIndexers, cdb, speers, config.ulcServers, config.ulcFraction)   scIndexer.Start(server.blockchain) sbIndexer.Start(server.blockchain)
diff --git go-ethereum/les/benchmark.go celo/les/benchmark.go index d42d8c2f881d99958350e992c1f06a7ec97e0911..29474c28c600306bd92309949f44ffc0f1c9c865 100644 --- go-ethereum/les/benchmark.go +++ celo/les/benchmark.go @@ -114,6 +114,7 @@ }   func (b *benchmarkProofsOrCode) request(peer *serverPeer, index int) error { key := make([]byte, 32) + // #nosec (not important) rand.Read(key) if b.code { return peer.requestCode(0, []CodeReq{{BHash: b.headHash, AccKey: key}}) @@ -176,8 +177,9 @@ b.txs = make(types.Transactions, count)   for i := range b.txs { data := make([]byte, txSizeCostLimit) + // #nosec (not important) rand.Read(data) - tx, err := types.SignTx(types.NewTransaction(0, addr, new(big.Int), 0, new(big.Int), data), signer, key) + tx, err := types.SignTx(types.NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil, nil, nil, data), signer, key) if err != nil { panic(err) } @@ -200,6 +202,7 @@ }   func (b *benchmarkTxStatus) request(peer *serverPeer, index int) error { var hash common.Hash + // #nosec (not important) rand.Read(hash[:]) return peer.requestTxStatus(0, []common.Hash{hash}) } @@ -278,6 +281,7 @@ clientPipe, serverPipe := p2p.MsgPipe() clientMeteredPipe := &meteredPipe{rw: clientPipe} serverMeteredPipe := &meteredPipe{rw: serverPipe} var id enode.ID + // #nosec (not important) rand.Read(id[:])   peer1 := newServerPeer(lpv2, NetworkId, false, p2p.NewPeer(id, "client", nil), clientMeteredPipe)
diff --git go-ethereum/les/api_test.go celo/les/api_test.go index 19fb91982c431fd808af469267d9648b2c57bf80..0903d8295c80a021df28e403fb89976c89785f51 100644 --- go-ethereum/les/api_test.go +++ celo/les/api_test.go @@ -30,7 +30,6 @@ "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/eth" ethdownloader "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" @@ -496,7 +495,6 @@ func newLesClientService(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) { config := ethconfig.Defaults config.SyncMode = (ethdownloader.SyncMode)(downloader.LightSync) - config.Ethash.PowMode = ethash.ModeFake return New(stack, &config) }
diff --git go-ethereum/les/lightchainreader.go celo/les/lightchainreader.go new file mode 100644 index 0000000000000000000000000000000000000000..e40844515a99e94b5ea8d4f6dcd93697338aa9e8 --- /dev/null +++ celo/les/lightchainreader.go @@ -0,0 +1,45 @@ +package les + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/params" +) + +type LightChainReader struct { + config *params.ChainConfig + blockchain *light.LightChain +} + +// Config returns the chain configuration. +func (lcr *LightChainReader) Config() *params.ChainConfig { + return lcr.config +} + +func (lcr *LightChainReader) CurrentHeader() *types.Header { + return lcr.blockchain.CurrentHeader() +} +func (lcr *LightChainReader) GetHeaderByNumber(number uint64) *types.Header { + return lcr.blockchain.GetHeaderByNumber(number) +} +func (lcr *LightChainReader) GetHeaderByHash(hash common.Hash) *types.Header { + return lcr.blockchain.GetHeaderByHash(hash) +} +func (lcr *LightChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { + return lcr.blockchain.GetHeader(hash, number) +} +func (lcr *LightChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { + panic("GetBlock cannot be called on LightChainReader") +} + +// NewEVMRunner creates the System's EVMRunner for given header & sttate +func (lcr *LightChainReader) NewEVMRunner(header *types.Header, state vm.StateDB) vm.EVMRunner { + return lcr.blockchain.NewEVMRunner(header, state) +} + +// NewEVMRunnerForCurrentBlock creates the System's EVMRunner for current block & state +func (lcr *LightChainReader) NewEVMRunnerForCurrentBlock() (vm.EVMRunner, error) { + return lcr.blockchain.NewEVMRunnerForCurrentBlock() +}
diff --git go-ethereum/les/server.go celo/les/server.go index 60e553dda1d67b6ac57ffef08383b85eee497f3e..e52c4443306eee890bd19561530f670f35163825 100644 --- go-ethereum/les/server.go +++ celo/les/server.go @@ -18,8 +18,10 @@ package les   import ( "crypto/ecdsa" + "math/rand" "time"   + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth/ethconfig" @@ -96,8 +98,8 @@ iConfig: light.DefaultServerIndexerConfig, chainDb: e.ChainDb(), lesDb: lesDb, chainReader: e.BlockChain(), - chtIndexer: light.NewChtIndexer(e.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations, true), - bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency, true), + chtIndexer: light.NewChtIndexer(e.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations, true, true), + bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency, true, true), closeCh: make(chan struct{}), }, archiveMode: e.ArchiveMode(), @@ -114,7 +116,7 @@ issync := e.Synced if config.LightNoSyncServe { issync = func() bool { return true } } - srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), issync) + srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), issync, config.TxFeeRecipient, config.GatewayFee) srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config) srv.oracle = srv.setupOracle(node, e.BlockChain().Genesis().Hash(), config)   @@ -149,6 +151,21 @@ "chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot) } srv.chtIndexer.Start(e.BlockChain())   + // TODO(tim) + // oracle := config.CheckpointOracle + // if oracle == nil { + // oracle = params.CheckpointOracles[e.BlockChain().Genesis().Hash()] + // } + // registrar := newCheckpointOracle(oracle, srv.getLocalCheckpoint) + // // TODO(rjl493456442) Checkpoint is useless for les server, separate handler for client and server. + // pm, err := NewProtocolManager(e.BlockChain().Config(), nil, config.SyncMode, light.DefaultServerIndexerConfig, config.UltraLightServers, config.UltraLightFraction, false, config.NetworkId, e.EventMux(), newPeerSet(), e.BlockChain(), e.TxPool(), e.ChainDb(), nil, nil, registrar, quitSync, new(sync.WaitGroup), e.Synced, config.Etherbase) + // if err != nil { + // return nil, err + // } + // srv.protocolManager = pm + // pm.servingQueue = newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100) + // pm.server = srv + node.RegisterProtocols(srv.Protocols()) node.RegisterAPIs(srv.APIs()) node.RegisterLifecycle(srv) @@ -295,3 +312,29 @@ return } } } + +//This sends messages to light client peers whenever this light server updates gateway fee. +func (s *LesServer) BroadcastGatewayFeeInfo() error { + lightClientPeerNodes := s.peers.allPeers() + if s.handler.gatewayFee.Cmp(common.Big0) < 0 { + return nil + } + + for _, lightClientPeer := range lightClientPeerNodes { + currGatewayFeeResp := GatewayFeeInformation{GatewayFee: s.handler.gatewayFee, Etherbase: s.handler.etherbase} + reply := lightClientPeer.ReplyGatewayFee(rand.Uint64(), currGatewayFeeResp) + if reply == nil { + continue + } + lightClientPeer.queueSend(func() { + if err := reply.send(1); err != nil { + select { + case lightClientPeer.errCh <- err: + default: + } + } + }) + } + + return nil +}
diff --git go-ethereum/les/api.go celo/les/api.go index 8f1fd52dd7331fc5bff6e1f9c44c915a130d9413..56311e95dd7356ba3ae76750a0af4f838f5f7a36 100644 --- go-ethereum/les/api.go +++ celo/les/api.go @@ -19,8 +19,11 @@ import ( "errors" "fmt" + "math/big" + "math/rand" "time"   + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/mclock" vfs "github.com/ethereum/go-ethereum/les/vflux/server" @@ -31,6 +34,7 @@ var ( errNoCheckpoint = errors.New("no local checkpoint provided") errNotActivated = errors.New("checkpoint registrar is not activated") errUnknownBenchmarkType = errors.New("unknown benchmark type") + errInvalidGatewayFee = errors.New("invalid gateway fee") )   // PrivateLightServerAPI provides an API to access the LES light server. @@ -58,6 +62,40 @@ return node.ID(), nil } else { return enode.ID{}, err } +} + +//GatewayFee returns the current gateway fee of this light server +func (api *PrivateLightServerAPI) GatewayFee() (gf *big.Int, err error) { + return api.server.handler.gatewayFee, nil +} + +//SetGatewayFee allows this light server node to set a gateway fee +func (api *PrivateLightServerAPI) SetGatewayFee(gf *big.Int) error { + if gf.Cmp(common.Big0) < 0 { + return errInvalidGatewayFee + } + if api.server.handler.gatewayFee != gf { + api.server.handler.gatewayFee = gf + if err := api.server.BroadcastGatewayFeeInfo(); err != nil { + return err + } + } + return nil +} + +// SetGatewayFeeRecipient sets the etherbase of the gateway fee recipient +func (api *PrivateLightServerAPI) SetGatewayFeeRecipient(etherbase common.Address) error { + if api.server.handler.etherbase != etherbase { + api.server.handler.etherbase = etherbase + if err := api.server.BroadcastGatewayFeeInfo(); err != nil { + return err + } + } + return nil +} + +func (api *PrivateLightServerAPI) GatewayFeeRecipient() (eb common.Address, err error) { + return api.server.handler.etherbase, nil }   // ServerInfo returns global server parameters @@ -404,3 +442,38 @@ return "", errNotActivated } return api.backend.oracle.Contract().ContractAddr().Hex(), nil } + +// API should be for light clients of les protocol +type PrivateLightClientAPI struct { + le *LightEthereum +} + +func NewPrivateLightClientAPI(le *LightEthereum) *PrivateLightClientAPI { + return &PrivateLightClientAPI{le} +} + +func (api *PrivateLightClientAPI) GatewayFeeCache() map[string]*GatewayFeeInformation { + return api.le.handler.gatewayFeeCache.getMap() +} + +// RequestPeerGatewayFees updates cache by pulling gateway fee peer nodes +func (api *PrivateLightClientAPI) RequestPeerGatewayFees() error { + peerNodes := api.le.peers.allPeers() + for _, peerNode := range peerNodes { + cost := peerNode.getRequestCost(GetGatewayFeeMsg, int(1)) + err := peerNode.RequestGatewayFee(rand.Uint64(), cost) + if err != nil { + return err + } + } + return nil +} + +// SuggestGatewayFee suggests the best light server to choose based on different factors. Currently only minPeerGatewayFee. +func (api *PrivateLightClientAPI) SuggestGatewayFee() (*GatewayFeeInformation, error) { + bestGatewayFeeInfo, err := api.le.handler.gatewayFeeCache.MinPeerGatewayFee() + if err != nil { + return nil, err + } + return bestGatewayFeeInfo, nil +}
diff --git go-ethereum/les/peer_test.go celo/les/peer_test.go index dc93a63198f26131a9079ddd5c933f764638c739..81e76de81bae8c4cb3a4490788b66d39586a1160 100644 --- go-ethereum/les/peer_test.go +++ celo/les/peer_test.go @@ -19,6 +19,7 @@ import ( "crypto/rand" "errors" + "fmt" "math/big" "reflect" "sort" @@ -100,7 +101,7 @@ type fakeChain struct{}   func (f *fakeChain) Config() *params.ChainConfig { return params.MainnetChainConfig } func (f *fakeChain) Genesis() *types.Block { - return core.DefaultGenesisBlock().ToBlock(rawdb.NewMemoryDatabase()) + return core.MainnetGenesisBlock().ToBlock(rawdb.NewMemoryDatabase()) } func (f *fakeChain) CurrentHeader() *types.Header { return &types.Header{Number: big.NewInt(10000000)} }   @@ -165,3 +166,117 @@ t.Fatalf("timeout") } } } + +func TestWillAcceptTransaction(t *testing.T) { + tx := func(gatewayFeeRecipient *common.Address, gatewayFee *big.Int) *types.Transaction { + return types.NewTransaction(0, common.Address{}, nil, 0, nil, nil, gatewayFeeRecipient, gatewayFee, nil) + } + peerEtherbase := common.HexToAddress("deadbeef") + wrongEtherbase := common.HexToAddress("badfo00") + cases := []struct { + tx *types.Transaction + p *serverPeer + accept bool + }{ + { + tx: tx(nil, nil), + p: &serverPeer{}, + accept: true, + }, + { + tx: tx(nil, nil), + p: &serverPeer{ + etherbase: &common.Address{}, + gatewayFee: big.NewInt(0), + }, + accept: true, + }, + { + tx: tx(nil, nil), + p: &serverPeer{ + gatewayFee: big.NewInt(100), + }, + accept: true, + }, + { + tx: tx(nil, nil), + p: &serverPeer{ + etherbase: &peerEtherbase, + }, + accept: true, + }, + { + tx: tx(nil, nil), + p: &serverPeer{ + etherbase: &peerEtherbase, + gatewayFee: big.NewInt(100), + }, + accept: false, + }, + { + tx: tx(&peerEtherbase, big.NewInt(100)), + p: &serverPeer{ + etherbase: &common.Address{}, + gatewayFee: big.NewInt(0), + }, + accept: true, + }, + { + tx: tx(&peerEtherbase, big.NewInt(100)), + p: &serverPeer{ + etherbase: &peerEtherbase, + gatewayFee: big.NewInt(100), + }, + accept: true, + }, + { + tx: tx(&peerEtherbase, big.NewInt(200)), + p: &serverPeer{ + etherbase: &peerEtherbase, + gatewayFee: big.NewInt(100), + }, + accept: true, + }, + { + tx: tx(&peerEtherbase, big.NewInt(50)), + p: &serverPeer{ + etherbase: &peerEtherbase, + gatewayFee: big.NewInt(100), + }, + accept: false, + }, + { + tx: tx(&wrongEtherbase, big.NewInt(100)), + p: &serverPeer{ + etherbase: &peerEtherbase, + gatewayFee: big.NewInt(100), + }, + accept: false, + }, + { + tx: tx(nil, nil), + p: &serverPeer{ + onlyAnnounce: true, + etherbase: &common.Address{}, + gatewayFee: big.NewInt(0), + }, + accept: false, + }, + { + tx: tx(&peerEtherbase, big.NewInt(100)), + p: &serverPeer{ + onlyAnnounce: true, + etherbase: &peerEtherbase, + gatewayFee: big.NewInt(100), + }, + accept: false, + }, + } + for i, c := range cases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + if got := c.p.WillAcceptTransaction(c.tx); got != c.accept { + t.Errorf("got p.WillAcceptTransaction(...) = %v; want %v", got, c.accept) + } + }) + } +}
diff --git go-ethereum/les/txrelay.go celo/les/txrelay.go index b7ac2ff71f4630cd80a05ed3c554059a635ba326..0cf56fa45913912b91682e1b33f45bfce00e9da7 100644 --- go-ethereum/les/txrelay.go +++ celo/les/txrelay.go @@ -18,11 +18,14 @@ package les   import ( "context" + "errors" "math/rand" "sync"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/rlp" )   @@ -30,7 +33,6 @@ type lesTxRelay struct { txSent map[common.Hash]*types.Transaction txPending map[common.Hash]struct{} peerList []*serverPeer - peerStartPos int lock sync.Mutex stop chan struct{}   @@ -76,65 +78,75 @@ } } }   -// send sends a list of transactions to at most a given number of peers. -func (ltrx *lesTxRelay) send(txs types.Transactions, count int) { - sendTo := make(map[*serverPeer]types.Transactions) +func (ltrx *lesTxRelay) CanRelayTransaction(tx *types.Transaction) bool { + ltrx.lock.Lock() + defer ltrx.lock.Unlock()   - ltrx.peerStartPos++ // rotate the starting position of the peer list - if ltrx.peerStartPos >= len(ltrx.peerList) { - ltrx.peerStartPos = 0 + for _, p := range ltrx.peerList { + if p.WillAcceptTransaction(tx) { + return true + } } + return false +}   +// send sends a list of transactions to at most a given number of peers at +// once, never resending any particular transaction to the same peer twice +func (ltrx *lesTxRelay) send(txs types.Transactions) { for _, tx := range txs { hash := tx.Hash() - _, ok := ltrx.txSent[hash] - if !ok { - ltrx.txSent[hash] = tx - ltrx.txPending[hash] = struct{}{} + if _, ok := ltrx.txSent[hash]; ok { + continue } - if len(ltrx.peerList) > 0 { - cnt := count - pos := ltrx.peerStartPos - for { - peer := ltrx.peerList[pos] - sendTo[peer] = append(sendTo[peer], tx) - cnt-- - if cnt == 0 { - break // sent it to the desired number of peers - } - pos++ - if pos == len(ltrx.peerList) { - pos = 0 - } - if pos == ltrx.peerStartPos { - break // tried all available peers - } - } - } - }   - for p, list := range sendTo { - pp := p - ll := list - enc, _ := rlp.EncodeToBytes(ll) + ltrx.txSent[hash] = tx + ltrx.txPending[hash] = struct{}{} + + // Send a single transaction per request to avoid failure coupling and + // because the expected base cost of a SendTxV2 request is 0, so it + // cost no extra to send multiple requests with one transaction each. + list := types.Transactions{tx} + enc, _ := rlp.EncodeToBytes(list)   reqID := rand.Uint64() rq := &distReq{ getCost: func(dp distPeer) uint64 { - peer := dp.(*serverPeer) - return peer.getTxRelayCost(len(ll), len(enc)) + return dp.(*serverPeer).getTxRelayCost(len(list), len(enc)) }, canSend: func(dp distPeer) bool { - return !dp.(*serverPeer).onlyAnnounce && dp.(*serverPeer) == pp + return dp.(*serverPeer).WillAcceptTransaction(tx) }, request: func(dp distPeer) func() { peer := dp.(*serverPeer) - cost := peer.getTxRelayCost(len(ll), len(enc)) + cost := peer.getTxRelayCost(len(list), len(enc)) peer.fcServer.QueuedRequest(reqID, cost) - return func() { peer.sendTxs(reqID, len(ll), enc) } + return func() { peer.sendTxs(reqID, 1, enc) } }, } - go ltrx.retriever.retrieve(context.Background(), reqID, rq, func(p distPeer, msg *Msg) error { return nil }, ltrx.stop) + + // Check the response to see if the transaction was successfully added to the peer pool or mined. + // If an error is returned, the retriever will retry with any remaining suitable peers. + checkTxStatus := func(p distPeer, msg *Msg) error { + if msg.MsgType != MsgTxStatus { + return errors.New("received unexpected message code") + } + statuses, ok := msg.Obj.([]light.TxStatus) + if !ok { + return errors.New("received invalid transaction status object") + } + if len(statuses) != 1 { + return errors.New("expected single transaction status response") + } + status := statuses[0] + if status.Error != "" { + return errors.New(status.Error) + } + if status.Status == core.TxStatusUnknown { + return errors.New("transaction status unknown") + } + return nil + } + go ltrx.retriever.retrieve(context.Background(), reqID, rq, checkTxStatus, ltrx.stop) } }   @@ -142,7 +154,7 @@ func (ltrx *lesTxRelay) Send(txs types.Transactions) { ltrx.lock.Lock() defer ltrx.lock.Unlock()   - ltrx.send(txs, 3) + ltrx.send(txs) }   func (ltrx *lesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) { @@ -164,7 +176,7 @@ for hash := range ltrx.txPending { txs[i] = ltrx.txSent[hash] i++ } - ltrx.send(txs, 1) + ltrx.send(txs) } }
diff --git go-ethereum/les/peer.go celo/les/peer.go index 1c7f279fe37d10c9fe7e1c9092abdcd175959e57..7f09e56c4ce15a43e45a5cafaf5f68ab5eef54d0 100644 --- go-ethereum/les/peer.go +++ celo/les/peer.go @@ -275,7 +275,7 @@ // If the protocol version is beyond les4, then pass the forkID // as well. Check http://eips.ethereum.org/EIPS/eip-2124 for more // spec detail. - if p.version >= lpv4 { + if p.version >= lpv5 { send = send.add("forkID", forkID) } // Add client-specified or server-specified fields @@ -312,7 +312,7 @@ if int(rVersion) != p.version { return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", rVersion, p.version) } // Check forkID if the protocol version is beyond the les4 - if p.version >= lpv4 { + if p.version >= lpv5 { var forkID forkid.ID if err := recv.get("forkID", &forkID); err != nil { return err @@ -344,6 +344,10 @@ onlyAnnounce bool // The flag whether the server sends announcement only. chainSince, chainRecent uint64 // The range of chain server peer can serve. stateSince, stateRecent uint64 // The range of state server peer can serve. txHistory uint64 // The length of available tx history, 0 means all, 1 means disabled + + // Gateway fields + etherbase *common.Address + gatewayFee *big.Int   // Advertised checkpoint fields checkpointNumber uint64 // The block height which the checkpoint is registered. @@ -491,6 +495,82 @@ } return p.sendRequest(SendTxV2Msg, reqID, txs, amount) }   +// RequestEtherbase fetches the etherbase of a remote node. +func (p *serverPeer) RequestEtherbase(reqID, cost uint64) error { + p.Log().Debug("Requesting etherbase for peer", "enode", p.id) + type req struct { + ReqID uint64 + } + return p2p.Send(p.rw, GetEtherbaseMsg, req{reqID}) +} + +// RequestGatewayFee gets gateway fee of remote node +func (p *serverPeer) RequestGatewayFee(reqID, cost uint64) error { + p.Log().Debug("Requesting gatewayFee for peer", "enode", p.id) + type req struct { + ReqID uint64 + } + return p2p.Send(p.rw, GetGatewayFeeMsg, req{reqID}) +} + +func (p *serverPeer) Etherbase() (etherbase common.Address, ok bool) { + p.lock.RLock() + defer p.lock.RUnlock() + if p.etherbase != nil { + return *p.etherbase, true + } + return common.Address{}, false +} + +func (p *serverPeer) SetEtherbase(etherbase common.Address) { + p.lock.Lock() + defer p.lock.Unlock() + p.etherbase = &etherbase +} + +func (p *serverPeer) GatewayFee() (fee *big.Int, ok bool) { + p.lock.Lock() + defer p.lock.Unlock() + return p.gatewayFee, p.gatewayFee != nil +} + +func (p *serverPeer) SetGatewayFee(gatewayFee *big.Int) { + p.lock.Lock() + defer p.lock.Unlock() + p.gatewayFee = gatewayFee +} + +// Returns true if the peer has indicated it is willing to transmit the given +// transaction to the network. It may be the case that this client expects a +// node to relay a transaction, but the server decides not to. +func (p *serverPeer) WillAcceptTransaction(tx *types.Transaction) bool { + if p.onlyAnnounce { + return false + } + + // Retrieve the gateway fee information known for this peer. + // Treat unknown gateway fee or etherbase as potentially free relay. + gatewayFee, ok := p.GatewayFee() + if !ok { + return true + } + etherbase, ok := p.Etherbase() + if !ok { + return true + } + + // Check that the transaction meets the peer's gateway fee requirements. + if etherbase != (common.Address{}) && gatewayFee.Cmp(common.Big0) > 0 { + if txGateway := tx.GatewayFeeRecipient(); txGateway == nil || *txGateway != etherbase { + return false + } + if txFee := tx.GatewayFee(); txFee == nil || txFee.Cmp(gatewayFee) < 0 { + return false + } + } + return true +} + // waitBefore implements distPeer interface func (p *serverPeer) waitBefore(maxCost uint64) (time.Duration, float64) { return p.fcServer.CanSend(maxCost) @@ -535,12 +615,12 @@ return cost }   // HasBlock checks if the peer has a given block -func (p *serverPeer) HasBlock(hash common.Hash, number uint64, hasState bool) bool { +func (p *serverPeer) HasBlock(hash common.Hash, number *uint64, hasState bool) bool { p.lock.RLock() defer p.lock.RUnlock()   if p.hasBlockHook != nil { - return p.hasBlockHook(hash, number, hasState) + return p.hasBlockHook(hash, *number, hasState) } head := p.headInfo.Number var since, recent uint64 @@ -551,7 +631,12 @@ } else { since = p.chainSince recent = p.chainRecent } - return head >= number && number >= since && (recent == 0 || number+recent+4 > head) + // If number is not provided then we return an optimistic yet possible false positive + if number == nil { + return true + } + + return head >= *number && *number >= since && (recent == 0 || *number+recent+4 > head) }   // updateFlowControl updates the flow control parameters belonging to the server @@ -630,7 +715,7 @@ } if recv.get("txRelay", nil) != nil { p.onlyAnnounce = true } - if p.version >= lpv4 { + if p.version >= lpv5 { var recentTx uint if err := recv.get("recentTxLookup", &recentTx); err != nil { return err @@ -924,6 +1009,17 @@ data, _ := rlp.EncodeToBytes(stats) return &reply{p.rw, TxStatusMsg, reqID, data} }   +func (p *clientPeer) SendEtherbaseRLP(reqID uint64, etherbase common.Address) *reply { + data, _ := rlp.EncodeToBytes(etherbase) + return &reply{p.rw, EtherbaseMsg, reqID, data} +} + +//ReplyGatewayFee creates reply with gateway fee that was requested +func (p *clientPeer) ReplyGatewayFee(reqID uint64, resp GatewayFeeInformation) *reply { + data, _ := rlp.EncodeToBytes(resp) + return &reply{p.rw, GatewayFeeMsg, reqID, data} +} + // sendAnnounce announces the availability of a number of blocks through // a hash notification. func (p *clientPeer) sendAnnounce(request announceData) error { @@ -1043,7 +1139,7 @@ } if server.config.UltraLightOnlyAnnounce { recentTx = txIndexDisabled } - if recentTx != txIndexUnlimited && p.version < lpv4 { + if recentTx != txIndexUnlimited && p.version < lpv5 { return errors.New("Cannot serve old clients without a complete tx index") } // Note: clientPeer.headInfo should contain the last head announced to the client by us. @@ -1065,7 +1161,7 @@ } *lists = (*lists).add("serveRecentState", stateRecent) *lists = (*lists).add("txRelay", nil) } - if p.version >= lpv4 { + if p.version >= lpv5 { *lists = (*lists).add("recentTxLookup", recentTx) } *lists = (*lists).add("flowControl/BL", server.defParams.BufLimit) @@ -1128,6 +1224,13 @@ registerPeer(*serverPeer) unregisterPeer(*serverPeer) }   +// clientPeerSubscriber is an interface to notify services about added or +// removed client peers +type clientPeerSubscriber interface { //nolint:unused,gosimple,deadcode + registerPeer(*clientPeer) + unregisterPeer(*clientPeer) +} + // serverPeerSet represents the set of active server peers currently // participating in the Light Ethereum sub-protocol. type serverPeerSet struct { @@ -1186,7 +1289,22 @@ ps.peers[peer.id] = peer for _, sub := range ps.subscribers { sub.registerPeer(peer) } + return nil +} + +// TODO(nategraf) Remove this function when better method for choosing a peer is available. +func (ps *serverPeerSet) randomPeerEtherbase() common.Address { + ps.lock.RLock() + defer ps.lock.RUnlock() + + // Rely on golang's random map iteration order. + for _, p := range ps.peers { + if etherbase, ok := p.Etherbase(); ok { + return etherbase + } + } + return common.Address{} }   // unregister removes a remote peer from the active set, disabling any further @@ -1389,6 +1507,18 @@ ps.signedAnnounce.sign(ps.privateKey) } p.announceOrStore(ps.signedAnnounce) } +} + +// allClientPeers returns all client peers in a list. +func (ps *clientPeerSet) allPeers() []*clientPeer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + list := make([]*clientPeer, 0, len(ps.peers)) + for _, p := range ps.peers { + list = append(list, p) + } + return list }   // close disconnects all peers. No new peers can be registered
diff --git go-ethereum/les/pruner_test.go celo/les/pruner_test.go index d485bed542658e6eb05a30dedcab3a3385a4243d..b21c3c6480ba02a999d862d6a4992cbd94c3f36d 100644 --- go-ethereum/les/pruner_test.go +++ celo/les/pruner_test.go @@ -24,6 +24,7 @@ "testing" "time"   "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/les/downloader" "github.com/ethereum/go-ethereum/light" )   @@ -43,6 +44,7 @@ config = light.TestClientIndexerConfig netconfig = testnetConfig{ blocks: int(3*config.ChtSize + config.ChtConfirms), protocol: 3, + syncMode: downloader.LightSync, indexFn: waitIndexers, connect: true, }
diff --git go-ethereum/les/client.go celo/les/client.go index 0dc6c0a8c61b4df3474bfac9584fdccd7c9b70e7..eb8f6df36331886b8a0943a0e053efaa2aeee75b 100644 --- go-ethereum/les/client.go +++ celo/les/client.go @@ -26,13 +26,13 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/consensus" + istanbulBackend "github.com/ethereum/go-ethereum/consensus/istanbul/backend" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/filters" - "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/les/downloader" @@ -61,6 +61,7 @@ handler *clientHandler txPool *light.TxPool blockchain *light.LightChain serverPool *vfc.ServerPool + chainreader *LightChainReader serverPoolIterator enode.Iterator pruner *pruner   @@ -73,6 +74,7 @@ engine consensus.Engine accountManager *accounts.Manager netRPCService *ethapi.PublicNetAPI   + networkId uint64 p2pServer *p2p.Server p2pConfig *p2p.Config udpEnabled bool @@ -80,7 +82,20 @@ }   // New creates an instance of the light client. func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { - chainDb, err := stack.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/", false) + var chainName string + syncMode := downloader.FromString(config.SyncMode.String()) + var fullChainAvailable bool + if syncMode == downloader.LightSync { + chainName = "lightchaindata" + fullChainAvailable = true + } else if syncMode == downloader.LightestSync { + chainName = "lightestchaindata" + fullChainAvailable = false + } else { + panic("Unexpected sync mode: " + syncMode.String()) + } + + chainDb, err := stack.OpenDatabase(chainName, config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/", false) if err != nil { return nil, err } @@ -88,7 +103,8 @@ lesDb, err := stack.OpenDatabase("les.client", 0, 0, "eth/db/lesclient/", false) if err != nil { return nil, err } - chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideLondon) + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, + config.OverrideEHardfork) if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { return nil, genesisErr } @@ -109,9 +125,9 @@ peers: peers, eventMux: stack.EventMux(), reqDist: newRequestDistributor(peers, &mclock.System{}), accountManager: stack.AccountManager(), - engine: ethconfig.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb), + engine: ethconfig.CreateConsensusEngine(stack, chainConfig, config, chainDb), + networkId: config.NetworkId, bloomRequests: make(chan chan *bloombits.Retrieval), - bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations), p2pServer: stack.Server(), p2pConfig: &stack.Config().P2P, udpEnabled: stack.Config().P2P.DiscoveryV5, @@ -128,8 +144,12 @@ leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool.GetTimeout) leth.relay = newLesTxRelay(peers, leth.retriever)   leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.peers, leth.retriever) - leth.chtIndexer = light.NewChtIndexer(chainDb, leth.odr, params.CHTFrequency, params.HelperTrieConfirmations, config.LightNoPrune) - leth.bloomTrieIndexer = light.NewBloomTrieIndexer(chainDb, leth.odr, params.BloomBitsBlocksClient, params.BloomTrieFrequency, config.LightNoPrune) + // If the full chain is not available then indexing each block header isn't possible. + if fullChainAvailable { + leth.bloomIndexer = core.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations, fullChainAvailable) + leth.chtIndexer = light.NewChtIndexer(chainDb, leth.odr, params.CHTFrequency, params.HelperTrieConfirmations, config.LightNoPrune, fullChainAvailable) + leth.bloomTrieIndexer = light.NewBloomTrieIndexer(chainDb, leth.odr, params.BloomBitsBlocksClient, params.BloomTrieFrequency, config.LightNoPrune, fullChainAvailable) + } leth.odr.SetIndexers(leth.chtIndexer, leth.bloomTrieIndexer, leth.bloomIndexer)   checkpoint := config.Checkpoint @@ -141,6 +161,7 @@ // indexers already set but not started yet if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint); err != nil { return nil, err } + leth.chainReader = leth.blockchain leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay)   @@ -148,9 +169,13 @@ // Set up checkpoint oracle. leth.oracle = leth.setupOracle(stack, genesisHash, config)   // Note: AddChildIndexer starts the update process for the child - leth.bloomIndexer.AddChildIndexer(leth.bloomTrieIndexer) - leth.chtIndexer.Start(leth.blockchain) - leth.bloomIndexer.Start(leth.blockchain) + if leth.bloomIndexer != nil && leth.bloomTrieIndexer != nil { + leth.bloomIndexer.AddChildIndexer(leth.bloomTrieIndexer) + leth.bloomIndexer.Start(leth.blockchain) + } + if leth.chtIndexer != nil { + leth.chtIndexer.Start(leth.blockchain) + }   // Start a light chain pruner to delete useless historical data. leth.pruner = newPruner(chainDb, leth.chtIndexer, leth.bloomTrieIndexer) @@ -162,14 +187,19 @@ leth.blockchain.SetHead(compat.RewindTo) rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) }   - leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, leth, nil} - gpoParams := config.GPO - if gpoParams.Default == nil { - gpoParams.Default = config.Miner.GasPrice + leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), true, leth} + + leth.chainreader = &LightChainReader{ + config: leth.chainConfig, + blockchain: leth.blockchain, } - leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams)   - leth.handler = newClientHandler(config.UltraLightServers, config.UltraLightFraction, checkpoint, leth) + // If the engine is istanbul, then inject the blockchain + if istanbul, isIstanbul := leth.engine.(*istanbulBackend.Backend); isIstanbul { + istanbul.SetChain(leth.chainreader, nil, nil) + } + // TODO mcortesi (needs etherbase & gatewayFee?) + leth.handler = newClientHandler(syncMode, config.UltraLightServers, config.UltraLightFraction, checkpoint, leth, config.GatewayFee) if leth.handler.ulc != nil { log.Warn("Ultra light client is enabled", "trustedNodes", len(leth.handler.ulc.keys), "minTrustedFraction", leth.handler.ulc.fraction) leth.blockchain.DisableCheckFreq() @@ -299,6 +329,11 @@ Version: "1.0", Service: downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux), Public: true, }, { + Namespace: "les", + Version: "1.0", + Service: NewPrivateLightClientAPI(s), + Public: false, + }, { Namespace: "eth", Version: "1.0", Service: filters.NewPublicFilterAPI(s.ApiBackend, true, 5*time.Minute), @@ -332,6 +367,7 @@ func (s *LightEthereum) Engine() consensus.Engine { return s.engine } func (s *LightEthereum) LesVersion() int { return int(ClientProtocolVersions[0]) } func (s *LightEthereum) Downloader() *downloader.Downloader { return s.handler.downloader } func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux } +func (s *LightEthereum) ServerPool() *vfc.ServerPool { return s.serverPool }   // Protocols returns all the currently configured network protocols to start. func (s *LightEthereum) Protocols() []p2p.Protocol { @@ -366,6 +402,10 @@ return nil }   +func (s *LightEthereum) GetRandomPeerEtherbase() common.Address { + return s.peers.randomPeerEtherbase() +} + // Stop implements node.Lifecycle, terminating all internal goroutines used by the // Ethereum protocol. func (s *LightEthereum) Stop() error { @@ -375,8 +415,12 @@ s.peers.close() s.reqDist.close() s.odr.Stop() s.relay.Stop() - s.bloomIndexer.Close() - s.chtIndexer.Close() + if s.bloomIndexer != nil { + s.bloomIndexer.Close() + } + if s.chtIndexer != nil { + s.chtIndexer.Close() + } s.blockchain.Stop() s.handler.stop() s.txPool.Stop()
diff --git go-ethereum/les/request_test.go celo/les/request_test.go index 94ad977741e804855525ab2a24b0e3e4c7dafd6a..2c8a525e07c366b6bdb251ad9f6cd547b4e90429 100644 --- go-ethereum/les/request_test.go +++ celo/les/request_test.go @@ -25,6 +25,7 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/les/downloader" "github.com/ethereum/go-ethereum/light" )   @@ -39,6 +40,7 @@ func TestBlockAccessLes2(t *testing.T) { testAccess(t, 2, tfBlockAccess) } func TestBlockAccessLes3(t *testing.T) { testAccess(t, 3, tfBlockAccess) } func TestBlockAccessLes4(t *testing.T) { testAccess(t, 4, tfBlockAccess) } +func TestBlockAccessLes5(t *testing.T) { testAccess(t, 5, tfBlockAccess) }   func tfBlockAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { return &light.BlockRequest{Hash: bhash, Number: number} @@ -47,6 +49,7 @@ func TestReceiptsAccessLes2(t *testing.T) { testAccess(t, 2, tfReceiptsAccess) } func TestReceiptsAccessLes3(t *testing.T) { testAccess(t, 3, tfReceiptsAccess) } func TestReceiptsAccessLes4(t *testing.T) { testAccess(t, 4, tfReceiptsAccess) } +func TestReceiptsAccessLes5(t *testing.T) { testAccess(t, 5, tfReceiptsAccess) }   func tfReceiptsAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { return &light.ReceiptsRequest{Hash: bhash, Number: number} @@ -55,6 +58,7 @@ func TestTrieEntryAccessLes2(t *testing.T) { testAccess(t, 2, tfTrieEntryAccess) } func TestTrieEntryAccessLes3(t *testing.T) { testAccess(t, 3, tfTrieEntryAccess) } func TestTrieEntryAccessLes4(t *testing.T) { testAccess(t, 4, tfTrieEntryAccess) } +func TestTrieEntryAccessLes5(t *testing.T) { testAccess(t, 5, tfTrieEntryAccess) }   func tfTrieEntryAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { if number := rawdb.ReadHeaderNumber(db, bhash); number != nil { @@ -66,6 +70,7 @@ func TestCodeAccessLes2(t *testing.T) { testAccess(t, 2, tfCodeAccess) } func TestCodeAccessLes3(t *testing.T) { testAccess(t, 3, tfCodeAccess) } func TestCodeAccessLes4(t *testing.T) { testAccess(t, 4, tfCodeAccess) } +func TestCodeAccessLes5(t *testing.T) { testAccess(t, 5, tfCodeAccess) }   func tfCodeAccess(db ethdb.Database, bhash common.Hash, num uint64) light.OdrRequest { number := rawdb.ReadHeaderNumber(db, bhash) @@ -85,6 +90,7 @@ func testAccess(t *testing.T, protocol int, fn accessTestFn) { // Assemble the test environment netconfig := testnetConfig{ blocks: 4, + syncMode: downloader.LightSync, protocol: protocol, indexFn: nil, connect: true,
diff --git go-ethereum/les/server_handler.go celo/les/server_handler.go index 41ef6ccce08349d2df4207235875ee141321767c..6747301153ed69884e9bf9aecb61607c2db1e1fb 100644 --- go-ethereum/les/server_handler.go +++ celo/les/server_handler.go @@ -18,6 +18,8 @@ package les   import ( "errors" + "fmt" + "math/big" "sync" "sync/atomic" "time" @@ -50,6 +52,8 @@ MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request MaxHelperTrieProofsFetch = 64 // Amount of helper tries to be fetched per retrieval request MaxTxSend = 64 // Amount of transactions to be send per request MaxTxStatus = 256 // Amount of transactions to queried per request + MaxEtherbase = 1 + MaxGatewayFee = 1 )   var ( @@ -69,11 +73,15 @@ closeCh chan struct{} // Channel used to exit all background routines of handler. wg sync.WaitGroup // WaitGroup used to track all background routines of handler. synced func() bool // Callback function used to determine whether local node is synced.   + // Celo Specific + etherbase common.Address + gatewayFee *big.Int + // Testing fields addTxsSync bool }   -func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb ethdb.Database, txpool *core.TxPool, synced func() bool) *serverHandler { +func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb ethdb.Database, txpool *core.TxPool, synced func() bool, etherbase common.Address, gatewayFee *big.Int) *serverHandler { handler := &serverHandler{ forkFilter: forkid.NewFilter(blockchain), server: server, @@ -82,6 +90,8 @@ chainDb: chainDb, txpool: txpool, closeCh: make(chan struct{}), synced: synced, + etherbase: etherbase, + gatewayFee: gatewayFee, } return handler } @@ -357,6 +367,16 @@ func (h *serverHandler) AddTxsSync() bool { return h.addTxsSync }   +// GetEtherbase implements serverBackend +func (h *serverHandler) GetEtherbase() common.Address { + return h.etherbase +} + +// GetGatewayFee implements serverBackend +func (h *serverHandler) GetGatewayFee() *big.Int { + return h.gatewayFee +} + // getAccount retrieves an account from the state based on root. func getAccount(triedb *trie.Database, root, hash common.Hash) (types.StateAccount, error) { trie, err := trie.New(root, triedb) @@ -431,3 +451,30 @@ return } } } + +func (h *serverHandler) VerifyGatewayFee(gatewayFeeRecipient *common.Address, gatewayFee *big.Int) error { + + // If this node does not specify an etherbase, accept any GatewayFeeRecipient. + if h.etherbase == common.ZeroAddress { + return nil + } + + // If this node does not specify a non-zero gateway fee accept any value. + if h.gatewayFee == nil || h.gatewayFee.Cmp(common.Big0) <= 0 { + return nil + } + + // Otherwise, reject transactions that don't pay gas fees to this node. + if gatewayFeeRecipient == nil { + return fmt.Errorf("gateway fee recipient must be %s, got <nil>", h.etherbase.String()) + } + if *gatewayFeeRecipient != h.etherbase { + return fmt.Errorf("gateway fee recipient must be %s, got %s", h.etherbase.String(), (*gatewayFeeRecipient).String()) + } + + // Check that the value of the supplied gateway fee is at least the minimum. + if gatewayFee == nil || gatewayFee.Cmp(h.gatewayFee) < 0 { + return fmt.Errorf("gateway fee value must be at least %s, got %s", h.gatewayFee, gatewayFee) + } + return nil +}
diff --git go-ethereum/les/client_handler.go celo/les/client_handler.go index 1c53f79c418c42eefd4f9676dc797a1eea521e46..80e9b0f533d9fd26e9dda62665930dd954ca9df2 100644 --- go-ethereum/les/client_handler.go +++ celo/les/client_handler.go @@ -18,6 +18,8 @@ package les   import ( "context" + "errors" + "math" "math/big" "math/rand" "sync" @@ -26,9 +28,9 @@ "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/les/downloader" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" @@ -45,21 +47,90 @@ checkpoint *params.TrustedCheckpoint fetcher *lightFetcher downloader *downloader.Downloader backend *LightEthereum + syncMode downloader.SyncMode + + // TODO(nategraf) Remove this field once gateway fees can be retreived. + gatewayFee *big.Int   closeCh chan struct{} wg sync.WaitGroup // WaitGroup used to track all connected peers. - // Hooks used in the testing syncStart func(header *types.Header) // Hook called when the syncing is started syncEnd func(header *types.Header) // Hook called when the syncing is done + + gatewayFeeCache *gatewayFeeCache }   -func newClientHandler(ulcServers []string, ulcFraction int, checkpoint *params.TrustedCheckpoint, backend *LightEthereum) *clientHandler { +type GatewayFeeInformation struct { + GatewayFee *big.Int + Etherbase common.Address +} + +type gatewayFeeCache struct { + mutex *sync.RWMutex + gatewayFeeMap map[string]*GatewayFeeInformation +} + +func newGatewayFeeCache() *gatewayFeeCache { + cache := &gatewayFeeCache{ + mutex: new(sync.RWMutex), + gatewayFeeMap: make(map[string]*GatewayFeeInformation), + } + return cache +} + +func (c *gatewayFeeCache) getMap() map[string]*GatewayFeeInformation { + c.mutex.Lock() + defer c.mutex.Unlock() + + mapCopy := make(map[string]*GatewayFeeInformation) + for k, v := range c.gatewayFeeMap { + mapCopy[k] = v + } + + return mapCopy +} + +func (c *gatewayFeeCache) update(nodeID string, val *GatewayFeeInformation) error { + c.mutex.Lock() + defer c.mutex.Unlock() + + if val.Etherbase == common.ZeroAddress || val.GatewayFee.Cmp(big.NewInt(0)) < 0 { + return errors.New("invalid gatewayFeeInformation object") + } + c.gatewayFeeMap[nodeID] = val + + return nil +} + +func (c *gatewayFeeCache) MinPeerGatewayFee() (*GatewayFeeInformation, error) { + gatewayFeeMap := c.getMap() + + if len(gatewayFeeMap) == 0 { + return nil, nil + } + + minGwFee := big.NewInt(math.MaxInt64) + minEtherbase := common.ZeroAddress + for _, gwFeeInformation := range gatewayFeeMap { + if gwFeeInformation.GatewayFee.Cmp(minGwFee) < 0 { + minGwFee = gwFeeInformation.GatewayFee + minEtherbase = gwFeeInformation.Etherbase + } + } + + minGatewayFeeInformation := &GatewayFeeInformation{minGwFee, minEtherbase} + return minGatewayFeeInformation, nil +} + +func newClientHandler(syncMode downloader.SyncMode, ulcServers []string, ulcFraction int, checkpoint *params.TrustedCheckpoint, backend *LightEthereum, gatewayFee *big.Int) *clientHandler { handler := &clientHandler{ forkFilter: forkid.NewFilter(backend.blockchain), checkpoint: checkpoint, backend: backend, closeCh: make(chan struct{}), + syncMode: syncMode, + gatewayFee: gatewayFee, } if ulcServers != nil { ulc, err := newULC(ulcServers, ulcFraction) @@ -73,9 +144,12 @@ var height uint64 if checkpoint != nil { height = (checkpoint.SectionIndex+1)*params.CHTFrequency - 1 } - handler.fetcher = newLightFetcher(backend.blockchain, backend.engine, backend.peers, handler.ulc, backend.chainDb, backend.reqDist, handler.synchronise) + handler.fetcher = newLightFetcher(backend.blockchain, backend.engine, backend.peers, handler.ulc, backend.chainDb, backend.reqDist, handler.synchronise, handler.syncMode) + // TODO mcortesi lightest boolean handler.downloader = downloader.New(height, backend.chainDb, nil, backend.eventMux, nil, backend.blockchain, handler.removePeer) handler.backend.peers.subscribe((*downloaderPeerNotify)(handler)) + + handler.gatewayFeeCache = newGatewayFeeCache() return handler }   @@ -105,6 +179,12 @@ return err }   func (h *clientHandler) handle(p *serverPeer, noInitAnnounce bool) error { + // KJUE - Remove the server not nil check after restoring peer check in server.go + if p.Peer.Server != nil { + if err := p.Peer.Server.CheckPeerCounts(p.Peer); err != nil { + return err + } + } if h.backend.peers.len() >= h.backend.config.LightPeers && !p.Peer.Info().Network.Trusted { return p2p.DiscTooManyPeers } @@ -116,6 +196,10 @@ if err := p.Handshake(h.backend.blockchain.Genesis().Hash(), forkid, h.forkFilter); err != nil { p.Log().Debug("Light Ethereum handshake failed", "err", err) return err } + + // TODO(nategraf) The local gateway fee is temporarily being used as the peer gateway fee. + p.SetGatewayFee(h.gatewayFee) + // Register peer with the server pool if h.backend.serverPool != nil { if nvt, err := h.backend.serverPool.RegisterNode(p.Node()); err == nil { @@ -148,6 +232,26 @@ // signal to prevent syncing. if !noInitAnnounce { h.fetcher.announce(p, &announceData{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td}) } + // Loop until we receive a RequestEtherbase response or timeout. + go func() { + maxRequests := 10 + for requests := 1; requests <= maxRequests; requests++ { + p.Log().Trace("Requesting etherbase from new peer") + reqID := rand.Uint64() + cost := p.getRequestCost(GetEtherbaseMsg, int(1)) + err := p.RequestEtherbase(reqID, cost) + + if err != nil { + p.Log().Warn("Unable to request etherbase from peer", "err", err) + } + + time.Sleep(time.Duration(math.Pow(2, float64(requests))/2) * time.Second) + if _, ok := p.Etherbase(); ok { + return + } + } + }() + // Mark the peer starts to be served. atomic.StoreUint32(&p.serving, 1) defer atomic.StoreUint32(&p.serving, 0) @@ -229,6 +333,12 @@ p.answeredRequest(resp.ReqID)   // Filter out the explicitly requested header by the retriever if h.backend.retriever.requested(resp.ReqID) { + if len(headers) != 0 { + contiguousHeaders := h.syncMode != downloader.LightestSync + if _, err := h.fetcher.chain.InsertHeaderChain(resp.Headers, 1, contiguousHeaders); err != nil { + return err + } + } deliverMsg = &Msg{ MsgType: MsgBlockHeaders, ReqID: resp.ReqID, @@ -337,6 +447,7 @@ return errResp(ErrDecode, "msg %v: %v", msg, err) } p.fcServer.ReceivedReply(resp.ReqID, resp.BV) p.answeredRequest(resp.ReqID) + p.Log().Trace("Transaction status update", "status", resp.Status, "req", resp.ReqID) deliverMsg = &Msg{ MsgType: MsgTxStatus, ReqID: resp.ReqID, @@ -354,6 +465,33 @@ } p.fcServer.ResumeFreeze(bv) p.unfreeze() p.Log().Debug("Service resumed") + case msg.Code == EtherbaseMsg: + p.Log().Trace("Received etherbase response") + // TODO(asa): do we need to do anything with flow control here? + var resp struct { + ReqID, BV uint64 + Etherbase common.Address + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.ReceivedReply(resp.ReqID, resp.BV) + p.Log().Trace("Setting peer etherbase", "etherbase", resp.Etherbase, "Peer ID", p.ID) + p.SetEtherbase(resp.Etherbase) + + case msg.Code == GatewayFeeMsg: + var resp struct { + ReqID, BV uint64 + Data GatewayFeeInformation + } + + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + + p.fcServer.ReceivedReply(resp.ReqID, resp.BV) + h.gatewayFeeCache.update(p.id, &resp.Data) + default: p.Log().Trace("Received invalid message", "code", msg.Code) return errResp(ErrInvalidMsgCode, "%v", msg.Code) @@ -475,7 +613,7 @@ pc := &peerConnection{ handler: h, peer: p, } - h.downloader.RegisterLightPeer(p.id, eth.ETH66, pc) + h.downloader.RegisterLightPeer(p.id, istanbul.Celo67, pc) }   func (d *downloaderPeerNotify) unregisterPeer(p *serverPeer) {
diff --git go-ethereum/les/handler_test.go celo/les/handler_test.go index 8de7c593bba153b531e2d2e583fb51a80bbb017a..70c806c306d498e6dcb55950be5acf295e4cd554 100644 --- go-ethereum/les/handler_test.go +++ celo/les/handler_test.go @@ -25,7 +25,7 @@ "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -246,7 +246,7 @@ block := bc.GetBlockByNumber(uint64(num)) hashes = append(hashes, block.Hash()) if len(bodies) < tt.expected { - bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()}) + bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Randomness: block.Randomness(), EpochSnarkData: block.EpochSnarkData()}) } break } @@ -256,7 +256,7 @@ for j, hash := range tt.explicit { hashes = append(hashes, hash) if tt.available[j] && len(bodies) < tt.expected { block := bc.GetBlockByHash(hash) - bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()}) + bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Randomness: block.Randomness(), EpochSnarkData: block.EpochSnarkData()}) } } reqID++ @@ -591,6 +591,7 @@ func TestTransactionStatusLes2(t *testing.T) { testTransactionStatus(t, lpv2) } func TestTransactionStatusLes3(t *testing.T) { testTransactionStatus(t, lpv3) } func TestTransactionStatusLes4(t *testing.T) { testTransactionStatus(t, lpv4) } +func TestTransactionStatusLes5(t *testing.T) { testTransactionStatus(t, lpv5) }   func testTransactionStatus(t *testing.T, protocol int) { netconfig := testnetConfig{ @@ -623,16 +624,16 @@ } signer := types.HomesteadSigner{}   // test error status by sending an underpriced transaction - tx0, _ := types.SignTx(types.NewTransaction(0, userAddr1, big.NewInt(10000), params.TxGas, nil, nil), signer, bankKey) - test(tx0, true, light.TxStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced.Error()}) + // tx0, _ := types.SignTx(types.NewTransaction(0, userAddr1, big.NewInt(10000), params.TxGas, nil, nil), signer, bankKey) + // test(tx0, true, light.TxStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced.Error()})   - tx1, _ := types.SignTx(types.NewTransaction(0, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey) + tx1, _ := types.SignTx(types.NewTransaction(0, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil, nil, nil, nil), signer, bankKey) test(tx1, false, light.TxStatus{Status: core.TxStatusUnknown}) // query before sending, should be unknown test(tx1, true, light.TxStatus{Status: core.TxStatusPending}) // send valid processable tx, should return pending test(tx1, true, light.TxStatus{Status: core.TxStatusPending}) // adding it again should not return an error   - tx2, _ := types.SignTx(types.NewTransaction(1, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey) - tx3, _ := types.SignTx(types.NewTransaction(2, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey) + tx2, _ := types.SignTx(types.NewTransaction(1, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil, nil, nil, nil), signer, bankKey) + tx3, _ := types.SignTx(types.NewTransaction(2, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil, nil, nil, nil), signer, bankKey) // send transactions in the wrong order, tx3 should be queued test(tx3, true, light.TxStatus{Status: core.TxStatusQueued}) test(tx2, true, light.TxStatus{Status: core.TxStatusPending}) @@ -640,7 +641,7 @@ // query again, now tx3 should be pending too test(tx3, false, light.TxStatus{Status: core.TxStatusPending})   // generate and add a block with tx1 and tx2 included - gchain, _ := core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), server.db, 1, func(i int, block *core.BlockGen) { + gchain, _ := core.GenerateChain(params.IstanbulTestChainConfig, chain.GetBlockByNumber(0), mockEngine.NewFaker(), server.db, 1, func(i int, block *core.BlockGen) { block.AddTx(tx1) block.AddTx(tx2) }) @@ -668,7 +669,7 @@ test(tx2, false, light.TxStatus{Status: core.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}})   // create a reorg that rolls them back - gchain, _ = core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), server.db, 2, func(i int, block *core.BlockGen) {}) + gchain, _ = core.GenerateChain(params.IstanbulTestChainConfig, chain.GetBlockByNumber(0), mockEngine.NewFaker(), server.db, 2, func(i int, block *core.BlockGen) {}) if _, err := chain.InsertChain(gchain); err != nil { panic(err) } @@ -693,6 +694,7 @@ }   func TestStopResumeLES3(t *testing.T) { testStopResume(t, lpv3) } func TestStopResumeLES4(t *testing.T) { testStopResume(t, lpv4) } +func TestStopResumeLES5(t *testing.T) { testStopResume(t, lpv5) }   func testStopResume(t *testing.T, protocol int) { netconfig := testnetConfig{ @@ -748,3 +750,77 @@ t.Errorf("expected ResumeMsg and failed: %v", err) } } } + +func TestTransactionGatewayFeeRequirementLes2(t *testing.T) { + testTransactionGatewayFeeRequirement(t, lpv2) +} +func TestTransactionGatewayFeeRequirementLes3(t *testing.T) { + testTransactionGatewayFeeRequirement(t, lpv3) +} +func TestTransactionGatewayFeeRequirementLes4(t *testing.T) { + testTransactionGatewayFeeRequirement(t, lpv4) +} +func TestTransactionGatewayFeeRequirementLes5(t *testing.T) { + testTransactionGatewayFeeRequirement(t, lpv5) +} + +func testTransactionGatewayFeeRequirement(t *testing.T, protocol int) { + netconfig := testnetConfig{ + syncMode: downloader.LightSync, + protocol: protocol, + nopruning: true, + } + server, _, tearDown := newClientServerEnv(t, netconfig) + defer tearDown() + + server.handler.addTxsSync = true + server.handler.etherbase = common.HexToAddress("2ad937cb878d8beefc84f3d0545750c2ff78cd0e") + server.handler.gatewayFee = big.NewInt(25000) + + rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol) + defer closePeer() + + wrongAddress := common.HexToAddress("1762042962b8759e17d2b5ac6c5565273df506fd") + cases := []struct { + desc string + tx *types.Transaction + status light.TxStatus + }{{ + desc: "no recipient or fee value attached", + tx: types.NewTransaction(0, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil, nil, nil, nil), + status: light.TxStatus{Status: core.TxStatusUnknown, Error: "gateway fee recipient must be 0x2aD937cB878D8bEEfC84F3d0545750c2ff78CD0e, got <nil>"}, + }, { + desc: "wrong recipient", + tx: types.NewTransaction(1, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil, &wrongAddress, nil, nil), + status: light.TxStatus{Status: core.TxStatusUnknown, Error: "gateway fee recipient must be 0x2aD937cB878D8bEEfC84F3d0545750c2ff78CD0e, got 0x1762042962b8759E17d2B5Ac6c5565273df506fD"}, + }, { + desc: "no fee value attached", + tx: types.NewTransaction(2, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil, &server.handler.etherbase, nil, nil), + status: light.TxStatus{Status: core.TxStatusUnknown, Error: "gateway fee value must be at least 25000, got 0"}, + }, { + desc: "fee value too value", + tx: types.NewTransaction(3, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil, &server.handler.etherbase, new(big.Int).Sub(server.handler.gatewayFee, big.NewInt(1)), nil), + status: light.TxStatus{Status: core.TxStatusUnknown, Error: "gateway fee value must be at least 25000, got 24999"}, + }, { + desc: "fee value exactly enough", + tx: types.NewTransaction(4, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil, &server.handler.etherbase, server.handler.gatewayFee, nil), + status: light.TxStatus{Status: core.TxStatusQueued}, + }, { + desc: "fee value more than enough", + tx: types.NewTransaction(5, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil, &server.handler.etherbase, new(big.Int).Add(server.handler.gatewayFee, big.NewInt(1)), nil), + status: light.TxStatus{Status: core.TxStatusQueued}, + }} + + signer := types.HomesteadSigner{} + for i, c := range cases { + t.Run(c.desc, func(t *testing.T) { + tx, _ := types.SignTx(c.tx, signer, bankKey) + if err := sendRequest(rawPeer.app, SendTxV2Msg, uint64(i+1), types.Transactions{tx}); err != nil { + t.Fatalf("transaction send failed: %v", err) + } + if err := expectResponse(rawPeer.app, TxStatusMsg, uint64(i+1), testBufLimit, []light.TxStatus{c.status}); err != nil { + t.Fatalf("transaction status mismatch: %v", err) + } + }) + } +}
diff --git go-ethereum/les/pruner.go celo/les/pruner.go index 9663a2473efc4a498e2d2de3d81ded7f3b56c29f..65d3146e99a38cc876cba9dfd6b5ef386c64d0b5 100644 --- go-ethereum/les/pruner.go +++ celo/les/pruner.go @@ -34,8 +34,21 @@ closeCh chan struct{} wg sync.WaitGroup }   +// nonNil returns all non nil indexers +func nonNil(indexers []*core.ChainIndexer) []*core.ChainIndexer { + nn := make([]*core.ChainIndexer, 0, len(indexers)) + for _, i := range indexers { + if i != nil { + nn = append(nn, i) + } + } + return nn +} + // newPruner returns a light chain pruner instance. func newPruner(db ethdb.Database, indexers ...*core.ChainIndexer) *pruner { + // filter out possible nil indexers (celo lightest mode) + indexers = nonNil(indexers) pruner := &pruner{ db: db, indexers: indexers,
diff --git go-ethereum/les/state_accessor.go celo/les/state_accessor.go index 112e6fd44d12a8657ece4cfe4125e63a25f627fe..355c72504047c9629071b29bdced1b2c024c8567 100644 --- go-ethereum/les/state_accessor.go +++ celo/les/state_accessor.go @@ -34,42 +34,48 @@ return light.NewState(ctx, block.Header(), leth.odr), nil }   // stateAtTransaction returns the execution environment of a certain transaction. -func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { +func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, vm.EVMRunner, *state.StateDB, error) { // Short circuit if it's genesis block. if block.NumberU64() == 0 { - return nil, vm.BlockContext{}, nil, errors.New("no transaction in genesis") + return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis") } // Create the parent state database parent, err := leth.blockchain.GetBlock(ctx, block.ParentHash(), block.NumberU64()-1) if err != nil { - return nil, vm.BlockContext{}, nil, err + return nil, vm.BlockContext{}, nil, nil, err } statedb, err := leth.stateAtBlock(ctx, parent, reexec) if err != nil { - return nil, vm.BlockContext{}, nil, err + return nil, vm.BlockContext{}, nil, nil, err } if txIndex == 0 && len(block.Transactions()) == 0 { - return nil, vm.BlockContext{}, statedb, nil + return nil, vm.BlockContext{}, nil, statedb, nil + } + // Create SysContractCallCtx + var sysCtx *core.SysContractCallCtx + if leth.chainConfig.IsEspresso(block.Number()) { + sysCtx = core.NewSysContractCallCtx(block.Header(), statedb.Copy(), leth.blockchain) } // Recompute transactions up to the target index. signer := types.MakeSigner(leth.blockchain.Config(), block.Number()) for idx, tx := range block.Transactions() { // Assemble the transaction call message and return if the requested offset - msg, _ := tx.AsMessage(signer, block.BaseFee()) + msg, _ := tx.AsMessage(signer, sysCtx.GetGasPriceMinimum(tx.FeeCurrency())) txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil) statedb.Prepare(tx.Hash(), idx) + vmRunner := leth.blockchain.NewEVMRunner(block.Header(), statedb) if idx == txIndex { - return msg, context, statedb, nil + return msg, context, vmRunner, statedb, nil } // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { - return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner, sysCtx); err != nil { + return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } // Ensure any modifications are committed to the state // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) } - return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) + return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) }
diff --git go-ethereum/les/server_requests.go celo/les/server_requests.go index 322e20818e294953bce79fe2d877b3b228296504..e68e3eaefbb58a50ae35c99822682faf7dc4c74c 100644 --- go-ethereum/les/server_requests.go +++ celo/les/server_requests.go @@ -19,6 +19,7 @@ import ( "encoding/binary" "encoding/json" + "math/big"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -38,6 +39,9 @@ AddTxsSync() bool BlockChain() *core.BlockChain TxPool() *core.TxPool GetHelperTrie(typ uint, index uint64) *trie.Trie + VerifyGatewayFee(gatewayFeeRecipient *common.Address, gatewayFee *big.Int) error + GetEtherbase() common.Address + GetGatewayFee() *big.Int }   // Decoder is implemented by the messages passed to the handler functions @@ -144,6 +148,26 @@ OutPacketsMeter: miscOutTxStatusPacketsMeter, OutTrafficMeter: miscOutTxStatusTrafficMeter, ServingTimeMeter: miscServingTimeTxStatusTimer, Handle: handleGetTxStatus, + }, + GetEtherbaseMsg: { + Name: "etherbase request", + MaxCount: MaxEtherbase, + InPacketsMeter: miscInEtherbasePacketsMeter, + InTrafficMeter: miscInEtherbaseTrafficMeter, + OutPacketsMeter: miscOutEtherbasePacketsMeter, + OutTrafficMeter: miscOutEtherbaseTrafficMeter, + ServingTimeMeter: miscServingTimeEtherbaseTimer, + Handle: handleGetEtherbase, + }, + GetGatewayFeeMsg: { + Name: "gatewayFee request", + MaxCount: MaxGatewayFee, + InPacketsMeter: miscInGatewayFeePacketsMeter, + InTrafficMeter: miscInGatewayFeeTrafficMeter, + OutPacketsMeter: miscOutGatewayFeePacketsMeter, + OutTrafficMeter: miscOutGatewayFeeTrafficMeter, + ServingTimeMeter: miscServingTimeGatewayFeeTimer, + Handle: handleGetGatewayFee, }, }   @@ -480,7 +504,7 @@ // the headers with no valid proof. Keep the compatibility for // legacy les protocol and drop this hack when the les2/3 are // not supported. err := auxTrie.Prove(request.Key, request.FromLevel, nodes) - if p.version >= lpv4 && err != nil { + if p.version >= lpv5 && err != nil { return nil } if request.Type == htCanonical && request.AuxReq == htAuxHeader && len(request.Key) == 8 { @@ -517,6 +541,12 @@ } hash := tx.Hash() stats[i] = txStatus(backend, hash) if stats[i].Status == core.TxStatusUnknown { + // Only include transactions that have a valid gateway fee recipient & fee + if err := backend.VerifyGatewayFee(tx.GatewayFeeRecipient(), tx.GatewayFee()); err != nil { + p.Log().Trace("Rejected transaction from light peer for invalid gateway fee", "hash", hash.String(), "err", err) + stats[i].Error = err.Error() + continue + } addFn := backend.TxPool().AddRemotes // Add txs synchronously for testing purpose if backend.AddTxsSync() { @@ -527,6 +557,7 @@ stats[i].Error = errs[0].Error() continue } stats[i] = txStatus(backend, hash) + p.Log().Trace("Added transaction from light peer to pool", "hash", hash.String(), "tx", tx) } } return p.replyTxStatus(r.ReqID, stats) @@ -567,3 +598,25 @@ } } return stat } + +// handleGetEtherbase handles a transaction status query +func handleGetEtherbase(msg Decoder) (serveRequestFn, uint64, uint64, error) { + var r GetEtherbasePacket + if err := msg.Decode(&r); err != nil { + return nil, 0, 0, err + } + return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { + return p.SendEtherbaseRLP(r.ReqID, backend.GetEtherbase()) + }, r.ReqID, 1, nil +} + +// handleGetGatewayFee handles a transaction status query +func handleGetGatewayFee(msg Decoder) (serveRequestFn, uint64, uint64, error) { + var r GetGatewayFeePacket + if err := msg.Decode(&r); err != nil { + return nil, 0, 0, err + } + return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { + return p.ReplyGatewayFee(r.ReqID, GatewayFeeInformation{GatewayFee: backend.GetGatewayFee(), Etherbase: backend.GetEtherbase()}) + }, r.ReqID, 1, nil +}
diff --git go-ethereum/les/costtracker.go celo/les/costtracker.go index 51399f4e33275946d5a3e707789d5bc54191eba4..9c25c2662e86f98ecf4beb399a660e88e43b5b07 100644 --- go-ethereum/les/costtracker.go +++ celo/les/costtracker.go @@ -44,6 +44,7 @@ GetProofsV2Msg: {0, 600000}, GetHelperTrieProofsMsg: {0, 1000000}, SendTxV2Msg: {0, 450000}, GetTxStatusMsg: {0, 250000}, + GetEtherbaseMsg: {10000, 1}, } // maximum incoming message size estimates reqMaxInSize = requestCostTable{ @@ -55,6 +56,7 @@ GetProofsV2Msg: {0, 80}, GetHelperTrieProofsMsg: {0, 20}, SendTxV2Msg: {0, 16500}, GetTxStatusMsg: {0, 50}, + GetEtherbaseMsg: {0, 10}, } // maximum outgoing message size estimates reqMaxOutSize = requestCostTable{ @@ -66,6 +68,7 @@ GetProofsV2Msg: {0, 4000}, GetHelperTrieProofsMsg: {0, 4000}, SendTxV2Msg: {0, 100}, GetTxStatusMsg: {0, 100}, + GetEtherbaseMsg: {0, 100}, } // request amounts that have to fit into the minimum buffer size minBufferMultiplier times minBufferReqAmount = map[uint64]uint64{ @@ -77,6 +80,7 @@ GetProofsV2Msg: 1, GetHelperTrieProofsMsg: 16, SendTxV2Msg: 8, GetTxStatusMsg: 64, + GetEtherbaseMsg: 1, } minBufferMultiplier = 3 )
diff --git go-ethereum/les/fetcher.go celo/les/fetcher.go index f882202d09d9a2f5d9cb483ad6ee91a964f9e96b..9b0bebe0d6251570348d9c2fac05912251b37152 100644 --- go-ethereum/les/fetcher.go +++ celo/les/fetcher.go @@ -27,7 +27,9 @@ "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/les/downloader" "github.com/ethereum/go-ethereum/les/fetcher" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" @@ -135,6 +137,7 @@ reqDist *requestDistributor peerset *serverPeerSet // The global peerset of light client which shared by all components chain *light.LightChain // The local light chain which maintains the canonical header chain. fetcher *fetcher.BlockFetcher // The underlying fetcher which takes care block header retrieval. + syncMode downloader.SyncMode   // Peerset maintained by fetcher plock sync.RWMutex @@ -157,7 +160,7 @@ newHeadHook func(*types.Header) }   // newLightFetcher creates a light fetcher instance. -func newLightFetcher(chain *light.LightChain, engine consensus.Engine, peers *serverPeerSet, ulc *ulc, chaindb ethdb.Database, reqDist *requestDistributor, syncFn func(p *serverPeer)) *lightFetcher { +func newLightFetcher(chain *light.LightChain, engine consensus.Engine, peers *serverPeerSet, ulc *ulc, chaindb ethdb.Database, reqDist *requestDistributor, syncFn func(p *serverPeer), syncMode downloader.SyncMode) *lightFetcher { // Construct the fetcher by offering all necessary APIs validator := func(header *types.Header) error { // Disable seal verification explicitly if we are running in ulc mode. @@ -171,7 +174,11 @@ checkFreq := 1 if ulc != nil { checkFreq = 0 } - return chain.InsertHeaderChain(headers, checkFreq) + i, err := chain.InsertHeaderChain(headers, checkFreq, syncMode.SyncFullHeaderChain()) + if err == consensus.ErrFutureBlock { + return i, nil + } + return i, err } f := &lightFetcher{ ulc: ulc, @@ -182,6 +189,7 @@ reqDist: reqDist, fetcher: fetcher.NewBlockFetcher(true, chain.GetHeaderByHash, nil, validator, nil, heighter, inserter, nil, dropper), peers: make(map[enode.ID]*fetcherPeer), synchronise: syncFn, + syncMode: syncMode, announceCh: make(chan *announce), requestCh: make(chan *request), deliverCh: make(chan *response), @@ -441,20 +449,24 @@ // Rewind all untrusted headers for ulc mode. if ulc { head := f.chain.CurrentHeader() ancestor := rawdb.FindCommonAncestor(f.chaindb, origin, head) - var untrusted []common.Hash - for head.Number.Cmp(ancestor.Number) > 0 { - hash, number := head.Hash(), head.Number.Uint64() - if trusted, _ := trustedHeader(hash, number); trusted { - break + if ancestor != nil { + var untrusted []common.Hash + for head.Number.Cmp(ancestor.Number) > 0 { + hash, number := head.Hash(), head.Number.Uint64() + if trusted, _ := trustedHeader(hash, number); trusted { + break + } + untrusted = append(untrusted, hash) + head = f.chain.GetHeader(head.ParentHash, number-1) } - untrusted = append(untrusted, hash) - head = f.chain.GetHeader(head.ParentHash, number-1) - } - if len(untrusted) > 0 { - for i, j := 0, len(untrusted)-1; i < j; i, j = i+1, j-1 { - untrusted[i], untrusted[j] = untrusted[j], untrusted[i] + if len(untrusted) > 0 { + for i, j := 0, len(untrusted)-1; i < j; i, j = i+1, j-1 { + untrusted[i], untrusted[j] = untrusted[j], untrusted[i] + } + f.chain.Rollback(untrusted, f.syncMode.SyncFullHeaderChain()) } - f.chain.Rollback(untrusted) + } else { + log.Error("Common ancestor of origin header and current header is nil", "origin hash", origin.Hash(), "current header hash", head.Hash()) } } // Reset local status.
diff --git go-ethereum/les/protocol.go celo/les/protocol.go index 7f329b03f82cd53fbcb86b3ceb43e99308fa57de..109050fb81ebd184d6ce47b34709d950b91f60cb 100644 --- go-ethereum/les/protocol.go +++ celo/les/protocol.go @@ -35,18 +35,19 @@ // Constants to match up protocol versions and messages const ( lpv2 = 2 lpv3 = 3 - lpv4 = 4 + lpv4 = 4 // Work in progress. Breaking changes expected. + lpv5 = 5 // eth lpv4 )   // Supported versions of the les protocol (first is primary) var ( - ClientProtocolVersions = []uint{lpv2, lpv3, lpv4} - ServerProtocolVersions = []uint{lpv2, lpv3, lpv4} + ClientProtocolVersions = []uint{lpv2, lpv3, lpv4, lpv5} + ServerProtocolVersions = []uint{lpv2, lpv3, lpv4, lpv5} AdvertiseProtocolVersions = []uint{lpv2} // clients are searching for the first advertised protocol in the list )   // Number of implemented message corresponding to different protocol versions. -var ProtocolLengths = map[uint]uint64{lpv2: 22, lpv3: 24, lpv4: 24} +var ProtocolLengths = map[uint]uint64{lpv2: 24, lpv3: 26, lpv4: 28, lpv5: 28}   const ( NetworkId = 1 @@ -79,9 +80,15 @@ HelperTrieProofsMsg = 0x12 SendTxV2Msg = 0x13 GetTxStatusMsg = 0x14 TxStatusMsg = 0x15 + // Introduced in Celo v1.0 + GetEtherbaseMsg = 0x16 + EtherbaseMsg = 0x17 // Protocol messages introduced in LPV3 - StopMsg = 0x16 - ResumeMsg = 0x17 + StopMsg = 0x18 + ResumeMsg = 0x19 + // Protocol messages to be introduced in LPV4 + GetGatewayFeeMsg = 0x1A + GatewayFeeMsg = 0x1B )   // GetBlockHeadersData represents a block header query (the request ID is not included) @@ -140,6 +147,16 @@ ReqID uint64 Hashes []common.Hash }   +// GetEtherbasePacket represents a etherbase request +type GetEtherbasePacket struct { + ReqID uint64 +} + +// GetGatewayFeePacket represents a gateway fee request +type GetGatewayFeePacket struct { + ReqID uint64 +} + type requestInfo struct { name string maxCount uint64 @@ -166,6 +183,7 @@ GetProofsV2Msg: {"GetProofsV2", MaxProofsFetch, 10, 0}, GetHelperTrieProofsMsg: {"GetHelperTrieProofs", MaxHelperTrieProofsFetch, 10, 100}, SendTxV2Msg: {"SendTxV2", MaxTxSend, 1, 0}, GetTxStatusMsg: {"GetTxStatus", MaxTxStatus, 10, 0}, + GetEtherbaseMsg: {"GetEtherbase", MaxEtherbase, 1, 0}, // TODO: revisit this as we as its costs in costtracker.go } requestList []vfc.RequestInfo requestMapping map[uint32]reqMapping
diff --git go-ethereum/les/commons.go celo/les/commons.go index 82c8ccb0296955c9ac64b67d4fc33d469a2ed311..3bb76b0e550a22570d93c911f86fe26483a2428b 100644 --- go-ethereum/les/commons.go +++ celo/les/commons.go @@ -18,12 +18,10 @@ package les   import ( "fmt" - "math/big" "sync"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethclient" @@ -64,7 +62,6 @@ // NodeInfo represents a short summary of the Ethereum sub-protocol metadata // known about the host peer. type NodeInfo struct { Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3, Rinkeby=4) - Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block @@ -97,7 +94,6 @@ head := c.chainReader.CurrentHeader() hash := head.Hash() return &NodeInfo{ Network: c.config.NetworkId, - Difficulty: rawdb.ReadTd(c.chainDb, hash, head.Number.Uint64()), Genesis: c.genesis, Config: c.chainConfig, Head: hash, @@ -109,8 +105,19 @@ // latestLocalCheckpoint finds the common stored section index and returns a set // of post-processed trie roots (CHT and BloomTrie) associated with the appropriate // section index and head hash as a local checkpoint package. func (c *lesCommons) latestLocalCheckpoint() params.TrustedCheckpoint { - sections, _, _ := c.chtIndexer.Sections() - sections2, _, _ := c.bloomTrieIndexer.Sections() + var sections uint64 + var sections2 uint64 + if c.chtIndexer == nil { + sections = 0 + } else { + sections, _, _ = c.chtIndexer.Sections() + + } + if c.bloomTrieIndexer == nil { + sections2 = 0 + } else { + sections2, _, _ = c.bloomTrieIndexer.Sections() + } // Cap the section index if the two sections are not consistent. if sections > sections2 { sections = sections2 @@ -128,6 +135,9 @@ // // The returned checkpoint is only the checkpoint generated by the local indexers, // not the stable checkpoint registered in the registrar contract. func (c *lesCommons) localCheckpoint(index uint64) params.TrustedCheckpoint { + if c.chtIndexer == nil { + return params.TrustedCheckpoint{} + } sectionHead := c.chtIndexer.SectionHead(index) return params.TrustedCheckpoint{ SectionIndex: index,
diff --git go-ethereum/les/metrics.go celo/les/metrics.go index 91214c9b18219fd28e04827bce3d01dbb958354e..51801e7ce094119ef71fbec9d264a51b7c06e304 100644 --- go-ethereum/les/metrics.go +++ celo/les/metrics.go @@ -40,6 +40,10 @@ miscInTxsPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/txs", nil) miscInTxsTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/txs", nil) miscInTxStatusPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/txStatus", nil) miscInTxStatusTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/txStatus", nil) + miscInEtherbasePacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/etherbase", nil) + miscInEtherbaseTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/etherbase", nil) + miscInGatewayFeePacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/gatewayFee", nil) + miscInGatewayFeeTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/gatewayFee", nil)   miscOutPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/total", nil) miscOutTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/total", nil) @@ -59,6 +63,10 @@ miscOutTxsPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/txs", nil) miscOutTxsTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/txs", nil) miscOutTxStatusPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/txStatus", nil) miscOutTxStatusTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/txStatus", nil) + miscOutEtherbasePacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/etherbase", nil) + miscOutEtherbaseTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/etherbase", nil) + miscOutGatewayFeePacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/gatewayFee", nil) + miscOutGatewayFeeTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/gatewayFee", nil)   miscServingTimeHeaderTimer = metrics.NewRegisteredTimer("les/misc/serve/header", nil) miscServingTimeBodyTimer = metrics.NewRegisteredTimer("les/misc/serve/body", nil) @@ -68,6 +76,8 @@ miscServingTimeTrieProofTimer = metrics.NewRegisteredTimer("les/misc/serve/proof", nil) miscServingTimeHelperTrieTimer = metrics.NewRegisteredTimer("les/misc/serve/helperTrie", nil) miscServingTimeTxTimer = metrics.NewRegisteredTimer("les/misc/serve/txs", nil) miscServingTimeTxStatusTimer = metrics.NewRegisteredTimer("les/misc/serve/txStatus", nil) + miscServingTimeEtherbaseTimer = metrics.NewRegisteredTimer("les/misc/serve/etherbase", nil) + miscServingTimeGatewayFeeTimer = metrics.NewRegisteredTimer("les/misc/serve/gatewayFee", nil)   connectionTimer = metrics.NewRegisteredTimer("les/connection/duration", nil) serverConnectionGauge = metrics.NewRegisteredGauge("les/connection/server", nil)
diff --git go-ethereum/les/fetcher_test.go celo/les/fetcher_test.go index a94919c218482885d95dd5ef3fe7e50fc5549111..6e0c6023f4ccbbb79430ebb7a7d522fb459044b7 100644 --- go-ethereum/les/fetcher_test.go +++ celo/les/fetcher_test.go @@ -21,10 +21,11 @@ "math/big" "testing" "time"   - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/les/downloader" "github.com/ethereum/go-ethereum/p2p/enode" )   @@ -62,12 +63,35 @@ t.Fatalf("chain height mismatch, got %d, want %d", local, height) } }   -func TestSequentialAnnouncementsLes2(t *testing.T) { testSequentialAnnouncements(t, 2) } -func TestSequentialAnnouncementsLes3(t *testing.T) { testSequentialAnnouncements(t, 3) } +func TestSequentialAnnouncementsLes2(t *testing.T) { + testSequentialAnnouncements(t, downloader.LightSync, lpv2) +} +func TestSequentialAnnouncementsUltraLes2(t *testing.T) { + testSequentialAnnouncements(t, downloader.LightestSync, lpv2) +} +func TestSequentialAnnouncementsLes3(t *testing.T) { + testSequentialAnnouncements(t, downloader.LightSync, lpv3) +} +func TestSequentialAnnouncementsUltraLes3(t *testing.T) { + testSequentialAnnouncements(t, downloader.LightestSync, lpv3) +} +func TestSequentialAnnouncementsLes4(t *testing.T) { + testSequentialAnnouncements(t, downloader.LightSync, lpv4) +} +func TestSequentialAnnouncementsUltraLes4(t *testing.T) { + testSequentialAnnouncements(t, downloader.LightestSync, lpv4) +} +func TestSequentialAnnouncementsLes5(t *testing.T) { + testSequentialAnnouncements(t, downloader.LightSync, lpv5) +} +func TestSequentialAnnouncementsUltraLes5(t *testing.T) { + testSequentialAnnouncements(t, downloader.LightestSync, lpv5) +}   -func testSequentialAnnouncements(t *testing.T, protocol int) { +func testSequentialAnnouncements(t *testing.T, syncMode downloader.SyncMode, protocol int) { netconfig := testnetConfig{ blocks: 4, + syncMode: syncMode, protocol: protocol, nopruning: true, } @@ -100,12 +124,40 @@ verifyImportDone(t, importCh) verifyChainHeight(t, c.handler.fetcher, 4) }   -func TestGappedAnnouncementsLes2(t *testing.T) { testGappedAnnouncements(t, 2) } -func TestGappedAnnouncementsLes3(t *testing.T) { testGappedAnnouncements(t, 3) } +func TestGappedAnnouncementsLes2(t *testing.T) { + testGappedAnnouncements(t, downloader.LightSync, lpv2) +} +func TestGappedAnnouncementsUltraLes2(t *testing.T) { + t.Skip("added in the les refactor, check if necessary for LightestSync") + testGappedAnnouncements(t, downloader.LightestSync, lpv2) +} +func TestGappedAnnouncementsLes3(t *testing.T) { + testGappedAnnouncements(t, downloader.LightSync, lpv3) +} +func TestGappedAnnouncementsUltraLes3(t *testing.T) { + t.Skip("added in the les refactor, check if necessary for LightestSync") + testGappedAnnouncements(t, downloader.LightestSync, lpv3) +} +func TestGappedAnnouncementsLes4(t *testing.T) { + testGappedAnnouncements(t, downloader.LightSync, lpv4) +} +func TestGappedAnnouncementsUltraLes4(t *testing.T) { + t.Skip("added in the les refactor, check if necessary for LightestSync") + testGappedAnnouncements(t, downloader.LightestSync, lpv4) +} +func TestGappedAnnouncementsLes5(t *testing.T) { + testGappedAnnouncements(t, downloader.LightSync, lpv5) +} +func TestGappedAnnouncementsUltraLes5(t *testing.T) { + t.Skip("added in the les refactor, check if necessary for LightestSync") + testGappedAnnouncements(t, downloader.LightestSync, lpv5) +}   -func testGappedAnnouncements(t *testing.T, protocol int) { +func testGappedAnnouncements(t *testing.T, syncMode downloader.SyncMode, protocol int) { + // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) netconfig := testnetConfig{ blocks: 4, + syncMode: syncMode, protocol: protocol, nopruning: true, } @@ -138,7 +190,7 @@ verifyChainHeight(t, c.handler.fetcher, 4)   // Send a reorged announcement blocks, _ := core.GenerateChain(rawdb.ReadChainConfig(s.db, s.backend.Blockchain().Genesis().Hash()), s.backend.Blockchain().GetBlockByNumber(3), - ethash.NewFaker(), s.db, 2, func(i int, gen *core.BlockGen) { + mockEngine.NewFaker(), s.db, 2, func(i int, gen *core.BlockGen) { gen.OffsetTime(-9) // higher block difficulty }) s.backend.Blockchain().InsertChain(blocks) @@ -147,10 +199,42 @@ <-done // Wait syncing verifyChainHeight(t, c.handler.fetcher, 5) }   -func TestTrustedAnnouncementsLes2(t *testing.T) { testTrustedAnnouncement(t, 2) } -func TestTrustedAnnouncementsLes3(t *testing.T) { testTrustedAnnouncement(t, 3) } +func TestTrustedAnnouncementsLes2(t *testing.T) { + testTrustedAnnouncement(t, downloader.LightSync, lpv2) +} + +func TestTrustedAnnouncementsUltraLes2(t *testing.T) { + t.Skip("added in the les refactor, check if necessary for LightestSync") + testTrustedAnnouncement(t, downloader.LightestSync, lpv2) +} +func TestTrustedAnnouncementsLes3(t *testing.T) { + testTrustedAnnouncement(t, downloader.LightSync, lpv3) +} + +func TestTrustedAnnouncementsUltraLes3(t *testing.T) { + t.Skip("added in the les refactor, check if necessary for LightestSync") + testTrustedAnnouncement(t, downloader.LightestSync, lpv3) +} + +func TestTrustedAnnouncementsLes4(t *testing.T) { + testTrustedAnnouncement(t, downloader.LightSync, lpv4) +} + +func TestTrustedAnnouncementsUltraLes4(t *testing.T) { + t.Skip("added in the les refactor, check if necessary for LightestSync") + testTrustedAnnouncement(t, downloader.LightestSync, lpv4) +} + +func TestTrustedAnnouncementsLes5(t *testing.T) { + testTrustedAnnouncement(t, downloader.LightSync, lpv5) +} + +func TestTrustedAnnouncementsUltraLes5(t *testing.T) { + t.Skip("added in the les refactor, check if necessary for LightestSync") + testTrustedAnnouncement(t, downloader.LightestSync, lpv5) +}   -func testTrustedAnnouncement(t *testing.T, protocol int) { +func testTrustedAnnouncement(t *testing.T, syncMode downloader.SyncMode, protocol int) { var ( servers []*testServer teardowns []func() @@ -173,6 +257,7 @@ } } netconfig := testnetConfig{ protocol: protocol, + syncMode: syncMode, nopruning: true, ulcServers: ids, ulcFraction: 60, @@ -222,13 +307,16 @@ check([]uint64{4}, 4, func() { <-newHead }) // ULC-style light syncing, rollback untrusted headers check([]uint64{10}, 10, func() { <-newHead }) // Sync the whole chain. }   -func TestInvalidAnnouncesLES2(t *testing.T) { testInvalidAnnounces(t, lpv2) } -func TestInvalidAnnouncesLES3(t *testing.T) { testInvalidAnnounces(t, lpv3) } -func TestInvalidAnnouncesLES4(t *testing.T) { testInvalidAnnounces(t, lpv4) } +func TestInvalidAnnouncesLES2(t *testing.T) { testInvalidAnnounces(t, downloader.LightSync, lpv2) } +func TestInvalidAnnouncesLES3(t *testing.T) { testInvalidAnnounces(t, downloader.LightSync, lpv3) } +func TestInvalidAnnouncesLES4(t *testing.T) { testInvalidAnnounces(t, downloader.LightSync, lpv4) } +func TestInvalidAnnouncesLES5(t *testing.T) { testInvalidAnnounces(t, downloader.LightSync, lpv5) }   -func testInvalidAnnounces(t *testing.T, protocol int) { +func testInvalidAnnounces(t *testing.T, syncMode downloader.SyncMode, protocol int) { + t.Skip("Validates through a smaller td for the same header number. We verify this using the header number") netconfig := testnetConfig{ blocks: 4, + syncMode: syncMode, protocol: protocol, nopruning: true, }
diff --git go-ethereum/les/fetcher/block_fetcher.go celo/les/fetcher/block_fetcher.go index 1f6c1be2bff5041534717c1c2c5b465fe6fef6f1..87e8f1bea0931ff80a5d6b2113768fb86ed3e86e 100644 --- go-ethereum/les/fetcher/block_fetcher.go +++ celo/les/fetcher/block_fetcher.go @@ -14,10 +14,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.   -// This is a temporary package whilst working on the eth/66 blocking refactors. -// After that work is done, les needs to be refactored to use the new package, -// or alternatively use a stripped down version of it. Either way, we need to -// keep the changes scoped so duplicating temporarily seems the sanest. +// Package fetcher contains the announcement based header, blocks or transaction synchronisation. package fetcher   import ( @@ -31,7 +28,6 @@ "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" - "github.com/ethereum/go-ethereum/trie" )   const ( @@ -121,12 +117,14 @@ headers []*types.Header // Collection of headers to filter time time.Time // Arrival time of the headers }   -// bodyFilterTask represents a batch of block bodies (transactions and uncles) +// bodyFilterTask represents a batch of block bodies // needing fetcher filtering. type bodyFilterTask struct { peer string // The source peer of block bodies + blockHashes []common.Hash transactions [][]*types.Transaction // Collection of transactions per block bodies - uncles [][]*types.Header // Collection of uncles per block bodies + randomness []*types.Randomness + epochSnarkData []*types.EpochSnarkData time time.Time // Arrival time of the blocks' contents }   @@ -304,8 +302,8 @@ }   // FilterBodies extracts all the block bodies that were explicitly requested by // the fetcher, returning those that should be handled differently. -func (f *BlockFetcher) FilterBodies(peer string, transactions [][]*types.Transaction, uncles [][]*types.Header, time time.Time) ([][]*types.Transaction, [][]*types.Header) { - log.Trace("Filtering bodies", "peer", peer, "txs", len(transactions), "uncles", len(uncles)) +func (f *BlockFetcher) FilterBodies(peer string, blockHashes []common.Hash, transactions [][]*types.Transaction, randomness []*types.Randomness, epochSnarkData []*types.EpochSnarkData, time time.Time) ([]common.Hash, [][]*types.Transaction, []*types.Randomness, []*types.EpochSnarkData) { + log.Trace("Filtering bodies", "peer", peer, "txs", len(transactions))   // Send the filter channel to the fetcher filter := make(chan *bodyFilterTask) @@ -313,20 +311,20 @@ select { case f.bodyFilter <- filter: case <-f.quit: - return nil, nil + return nil, nil, nil, nil } // Request the filtering of the body list select { - case filter <- &bodyFilterTask{peer: peer, transactions: transactions, uncles: uncles, time: time}: + case filter <- &bodyFilterTask{peer: peer, blockHashes: blockHashes, transactions: transactions, randomness: randomness, epochSnarkData: epochSnarkData, time: time}: case <-f.quit: - return nil, nil + return nil, nil, nil, nil } // Retrieve the bodies remaining after filtering select { case task := <-filter: - return task.transactions, task.uncles + return task.blockHashes, task.transactions, task.randomness, task.epochSnarkData case <-f.quit: - return nil, nil + return nil, nil, nil, nil } }   @@ -527,7 +525,7 @@ // Filter fetcher-requested headers from other synchronisation algorithms if announce := f.fetching[hash]; announce != nil && announce.origin == task.peer && f.fetched[hash] == nil && f.completing[hash] == nil && f.queued[hash] == nil { // If the delivered header does not match the promised number, drop the announcer if header.Number.Uint64() != announce.number { - log.Trace("Invalid block number fetched", "peer", announce.origin, "hash", header.Hash(), "announced", announce.number, "provided", header.Number) + log.Warn("Invalid block number fetched", "peer", announce.origin, "hash", header.Hash(), "announced", announce.number, "provided", header.Number) f.dropPeer(announce.origin) f.forgetHash(hash) continue @@ -547,17 +545,6 @@ if f.getBlock(hash) == nil { announce.header = header announce.time = task.time   - // If the block is empty (header only), short circuit into the final import queue - if header.TxHash == types.EmptyRootHash && header.UncleHash == types.EmptyUncleHash { - log.Trace("Block empty, skipping body retrieval", "peer", announce.origin, "number", header.Number, "hash", header.Hash()) - - block := types.NewBlockWithHeader(header) - block.ReceivedAt = task.time - - complete = append(complete, block) - f.completing[hash] = announce - continue - } // Otherwise add to the list of blocks needing completion incomplete = append(incomplete, announce) } else { @@ -609,33 +596,20 @@ bodyFilterInMeter.Mark(int64(len(task.transactions))) blocks := []*types.Block{} // abort early if there's nothing explicitly requested if len(f.completing) > 0 { - for i := 0; i < len(task.transactions) && i < len(task.uncles); i++ { + for i := 0; i < len(task.blockHashes) && i < len(task.transactions) && i < len(task.randomness) && i < len(task.epochSnarkData); i++ { // Match up a body to any possible completion request - var ( - matched = false - uncleHash common.Hash // calculated lazily and reused - txnHash common.Hash // calculated lazily and reused - ) + var matched = false for hash, announce := range f.completing { if f.queued[hash] != nil || announce.origin != task.peer { continue } - if uncleHash == (common.Hash{}) { - uncleHash = types.CalcUncleHash(task.uncles[i]) - } - if uncleHash != announce.header.UncleHash { - continue - } - if txnHash == (common.Hash{}) { - txnHash = types.DeriveSha(types.Transactions(task.transactions[i]), trie.NewStackTrie(nil)) - } - if txnHash != announce.header.TxHash { + if task.blockHashes[i] != announce.header.Hash() { continue } // Mark the body matched, reassemble if still unknown matched = true if f.getBlock(hash) == nil { - block := types.NewBlockWithHeader(announce.header).WithBody(task.transactions[i], task.uncles[i]) + block := types.NewBlockWithHeader(announce.header).WithBody(task.transactions[i], task.randomness[i], task.epochSnarkData[i]) block.ReceivedAt = task.time blocks = append(blocks, block) } else { @@ -644,8 +618,10 @@ }   } if matched { + task.blockHashes = append(task.blockHashes[:i], task.blockHashes[i+1:]...) task.transactions = append(task.transactions[:i], task.transactions[i+1:]...) - task.uncles = append(task.uncles[:i], task.uncles[i+1:]...) + task.randomness = append(task.randomness[:i], task.randomness[i+1:]...) + task.epochSnarkData = append(task.epochSnarkData[:i], task.epochSnarkData[i+1:]...) i-- continue } @@ -720,7 +696,7 @@ } // Ensure the peer isn't DOSing us count := f.queues[peer] + 1 if count > blockLimit { - log.Debug("Discarded delivered header or block, exceeded allowance", "peer", peer, "number", number, "hash", hash, "limit", blockLimit) + log.Info("Discarded delivered header or block, exceeded allowance", "peer", peer, "number", number, "hash", hash, "limit", blockLimit) blockBroadcastDOSMeter.Mark(1) f.forgetHash(hash) return @@ -812,7 +788,7 @@ // Weird future block, don't fail, but neither propagate   default: // Something went very wrong, drop the peer - log.Debug("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err) + log.Warn("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err) f.dropPeer(peer) return }
diff --git go-ethereum/les/fetcher/block_fetcher_test.go celo/les/fetcher/block_fetcher_test.go index 83505854c1e52537df58e03b1de614eac767332e..c5c4b3773e99d24dc41b4365aff65b98ff047f64 100644 --- go-ethereum/les/fetcher/block_fetcher_test.go +++ celo/les/fetcher/block_fetcher_test.go @@ -25,7 +25,7 @@ "testing" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -39,29 +39,25 @@ testdb = rawdb.NewMemoryDatabase() testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testAddress = crypto.PubkeyToAddress(testKey.PublicKey) genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000000000)) - unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit, BaseFee: big.NewInt(params.InitialBaseFee)}, nil, nil, nil, trie.NewStackTrie(nil)) + unknownBlock = types.NewBlock(&types.Header{}, nil, nil, nil, trie.NewStackTrie(nil)) )   // makeChain creates a chain of n blocks starting at and including parent. // the returned hash chain is ordered head->parent. In addition, every 3rd block -// contains a transaction and every 5th an uncle to allow testing correct block +// contains a transaction to test correct block // reassembly. func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) { - blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, func(i int, block *core.BlockGen) { + blocks, _ := core.GenerateChain(params.IstanbulTestChainConfig, parent, mockEngine.NewFaker(), testdb, n, func(i int, block *core.BlockGen) { block.SetCoinbase(common.Address{seed})   // If the block number is multiple of 3, send a bonus transaction to the miner if parent == genesis && i%3 == 0 { - signer := types.MakeSigner(params.TestChainConfig, block.Number()) - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey) + signer := types.MakeSigner(params.IstanbulTestChainConfig, block.Number()) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, testKey) if err != nil { panic(err) } block.AddTx(tx) - } - // If the block number is a multiple of 5, add a bonus uncle to the block - if i%5 == 0 { - block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 1).Hash(), Number: big.NewInt(int64(i - 1))}) } }) hashes := make([]common.Hash, n+1) @@ -217,53 +213,23 @@ } // Create a function that returns blocks from the closure return func(hashes []common.Hash) error { // Gather the block bodies to return + blockHashes := make([]common.Hash, 0, len(hashes)) transactions := make([][]*types.Transaction, 0, len(hashes)) - uncles := make([][]*types.Header, 0, len(hashes)) + randomness := make([]*types.Randomness, 0, len(hashes)) + epochSnarkData := make([]*types.EpochSnarkData, 0, len(hashes))   for _, hash := range hashes { if block, ok := closure[hash]; ok { + blockHashes = append(blockHashes, hash) transactions = append(transactions, block.Transactions()) - uncles = append(uncles, block.Uncles()) + randomness = append(randomness, block.Randomness()) + epochSnarkData = append(epochSnarkData, block.EpochSnarkData()) } } // Return on a new thread - go f.fetcher.FilterBodies(peer, transactions, uncles, time.Now().Add(drift)) + go f.fetcher.FilterBodies(peer, blockHashes, transactions, randomness, epochSnarkData, time.Now().Add(drift))   return nil - } -} - -// verifyFetchingEvent verifies that one single event arrive on a fetching channel. -func verifyFetchingEvent(t *testing.T, fetching chan []common.Hash, arrive bool) { - if arrive { - select { - case <-fetching: - case <-time.After(time.Second): - t.Fatalf("fetching timeout") - } - } else { - select { - case <-fetching: - t.Fatalf("fetching invoked") - case <-time.After(10 * time.Millisecond): - } - } -} - -// verifyCompletingEvent verifies that one single event arrive on an completing channel. -func verifyCompletingEvent(t *testing.T, completing chan []common.Hash, arrive bool) { - if arrive { - select { - case <-completing: - case <-time.After(time.Second): - t.Fatalf("completing timeout") - } - } else { - select { - case <-completing: - t.Fatalf("completing invoked") - case <-time.After(10 * time.Millisecond): - } } }   @@ -750,46 +716,6 @@ tester.lock.RUnlock()   if dropped { t.Fatalf("peer with valid numbered announcement dropped") - } - verifyImportDone(t, imported) -} - -// Tests that if a block is empty (i.e. header only), no body request should be -// made, and instead the header should be assembled into a whole block in itself. -func TestEmptyBlockShortCircuit(t *testing.T) { - // Create a chain of blocks to import - hashes, blocks := makeChain(32, 0, genesis) - - tester := newTester(false) - headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) - bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) - - // Add a monitoring hook for all internal events - fetching := make(chan []common.Hash) - tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes } - - completing := make(chan []common.Hash) - tester.fetcher.completingHook = func(hashes []common.Hash) { completing <- hashes } - - imported := make(chan interface{}) - tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { - if block == nil { - t.Fatalf("Fetcher try to import empty block") - } - imported <- block - } - // Iteratively announce blocks until all are imported - for i := len(hashes) - 2; i >= 0; i-- { - tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) - - // All announces should fetch the header - verifyFetchingEvent(t, fetching, true) - - // Only blocks with data contents should request bodies - verifyCompletingEvent(t, completing, len(blocks[hashes[i]].Transactions()) > 0 || len(blocks[hashes[i]].Uncles()) > 0) - - // Irrelevant of the construct, import should succeed - verifyImportEvent(t, imported, true) } verifyImportDone(t, imported) }
diff --git go-ethereum/les/downloader/queue.go celo/les/downloader/queue.go index ada8d4f271227d321d3d0dc4f522f922afabdaf3..597087f3d5033cf3363a3225bffae626a6b3f314 100644 --- go-ethereum/les/downloader/queue.go +++ celo/les/downloader/queue.go @@ -65,9 +65,10 @@ type fetchResult struct { pending int32 // Flag telling what deliveries are outstanding   Header *types.Header - Uncles []*types.Header Transactions types.Transactions Receipts types.Receipts + Randomness *types.Randomness + EpochSnarkData *types.EpochSnarkData }   func newFetchResult(header *types.Header, fastSync bool) *fetchResult { @@ -366,15 +367,13 @@ results := q.resultCache.GetCompleted(maxResultsProcess) for _, result := range results { // Recalculate the result item weights to prevent memory exhaustion size := result.Header.Size() - for _, uncle := range result.Uncles { - size += uncle.Size() - } for _, receipt := range result.Receipts { size += receipt.Size() } for _, tx := range result.Transactions { size += tx.Size() } + size += result.Randomness.Size() q.resultSize = common.StorageSize(blockCacheSizeWeight)*size + (1-common.StorageSize(blockCacheSizeWeight))*q.resultSize } @@ -528,6 +527,11 @@ log.Warn("Failed to reserve headers", "err", err) // There are no resultslots available. Leave it in the task queue break } + // Only required if the reserve is for a body type + if kind == bodyType { + // All headers must be fetched so that the random beacon can be updated correctly. + item.pending |= (1 << bodyType) + } if item.Done(kind) { // If it's a noop, we can skip this task delete(taskPool, header.Hash()) @@ -780,7 +784,7 @@ // DeliverBodies injects a block body retrieval response into the results queue. // The method returns the number of blocks bodies accepted from the delivery and // also wakes any threads waiting for data delivery. -func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLists [][]*types.Header) (int, error) { +func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, randomnessList []*types.Randomness, epochSnarkDataList []*types.EpochSnarkData) (int, error) { q.lock.Lock() defer q.lock.Unlock() trieHasher := trie.NewStackTrie(nil) @@ -788,15 +792,13 @@ validate := func(index int, header *types.Header) error { if types.DeriveSha(types.Transactions(txLists[index]), trieHasher) != header.TxHash { return errInvalidBody } - if types.CalcUncleHash(uncleLists[index]) != header.UncleHash { - return errInvalidBody - } return nil }   reconstruct := func(index int, result *fetchResult) { result.Transactions = txLists[index] - result.Uncles = uncleLists[index] + result.Randomness = randomnessList[index] + result.EpochSnarkData = epochSnarkDataList[index] result.SetBodyDone() } return q.deliver(id, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool,
diff --git go-ethereum/les/downloader/peer.go celo/les/downloader/peer.go index 52ca8c99e958ce069822ced25706ff6b16e11125..d13be564e31028e316ea933edb9ca3d8dabe6942 100644 --- go-ethereum/les/downloader/peer.go +++ celo/les/downloader/peer.go @@ -28,6 +28,7 @@ "sync/atomic" "time"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -413,7 +414,7 @@ } throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.BlockHeadersMsg, time.Second) } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) + return ps.idlePeers(istanbul.Celo67, istanbul.Celo67, idle, throughput) }   // BodyIdlePeers retrieves a flat list of all the currently body-idle peers within @@ -425,7 +426,7 @@ } throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.BlockBodiesMsg, time.Second) } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) + return ps.idlePeers(istanbul.Celo67, istanbul.Celo67, idle, throughput) }   // ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers @@ -437,7 +438,7 @@ } throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.ReceiptsMsg, time.Second) } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) + return ps.idlePeers(istanbul.Celo67, istanbul.Celo67, idle, throughput) }   // NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle @@ -449,7 +450,7 @@ } throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.NodeDataMsg, time.Second) } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) + return ps.idlePeers(istanbul.Celo67, istanbul.Celo67, idle, throughput) }   // idlePeers retrieves a flat list of all currently idle peers satisfying the
diff --git go-ethereum/les/downloader/downloader_test.go celo/les/downloader/downloader_test.go index 2fb2014f8e5ae37eddea25adcc3c6662aec4a25c..852b934f9aad45cbcfc73c4b82d357d9bbe74fca 100644 --- go-ethereum/les/downloader/downloader_test.go +++ celo/les/downloader/downloader_test.go @@ -28,12 +28,13 @@ "time"   "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" )   @@ -43,6 +44,11 @@ fullMaxForkAncestry = 10000 lightMaxForkAncestry = 10000 blockCacheMaxItems = 1024 fsHeaderContCheck = 500 * time.Millisecond + + // configure logger by uncommenting this to enable more logging + //glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true))) + //glogger.Verbosity(log.LvlInfo) + //log.Root().SetHandler(glogger) }   // downloadTester is a test simulator for mocking out local block chain. @@ -68,6 +74,10 @@ lock sync.RWMutex }   +func (dl *downloadTester) Config() *params.ChainConfig { + return nil +} + // newTester creates a new downloader test mocker. func newTester() *downloadTester { tester := &downloadTester{ @@ -78,13 +88,13 @@ ownHashes: []common.Hash{testGenesis.Hash()}, ownHeaders: map[common.Hash]*types.Header{testGenesis.Hash(): testGenesis.Header()}, ownBlocks: map[common.Hash]*types.Block{testGenesis.Hash(): testGenesis}, ownReceipts: map[common.Hash]types.Receipts{testGenesis.Hash(): nil}, - ownChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.Difficulty()}, + ownChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.TotalDifficulty()},   // Initialize ancient store with test genesis block ancientHeaders: map[common.Hash]*types.Header{testGenesis.Hash(): testGenesis.Header()}, ancientBlocks: map[common.Hash]*types.Block{testGenesis.Hash(): testGenesis}, ancientReceipts: map[common.Hash]types.Receipts{testGenesis.Hash(): nil}, - ancientChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.Difficulty()}, + ancientChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.TotalDifficulty()}, } tester.stateDb = rawdb.NewMemoryDatabase() tester.stateDb.Put(testGenesis.Root().Bytes(), []byte{0x00}) @@ -160,6 +170,30 @@ } return dl.ownHeaders[hash] }   +// GetHeaderByNumber retrieves a header from the testers canonical chain. +func (dl *downloadTester) GetHeaderByNumber(number uint64) *types.Header { + dl.lock.RLock() + defer dl.lock.RUnlock() + return dl.getHeaderByNumber(number) +} + +// getHeaderByNumber returns the header if found either within ancients or own blocks) +// This method assumes that the caller holds at least the read-lock (dl.lock) +func (dl *downloadTester) getHeaderByNumber(number uint64) *types.Header { + for _, header := range dl.ancientHeaders { + if header != nil && header.Number.Uint64() == number { + return header + } + } + + for _, header := range dl.ownHeaders { + if header != nil && header.Number.Uint64() == number { + return header + } + } + return nil +} + // GetBlock retrieves a block from the testers canonical chain. func (dl *downloadTester) GetBlockByHash(hash common.Hash) *types.Block { dl.lock.RLock() @@ -172,6 +206,25 @@ } return dl.ownBlocks[hash] }   +// GetBlock retrieves a block from the testers canonical chain. +func (dl *downloadTester) GetBlockByNumber(number uint64) *types.Block { + dl.lock.RLock() + defer dl.lock.RUnlock() + + for _, block := range dl.ancientBlocks { + if block != nil && block.NumberU64() == number { + return block + } + } + + for _, block := range dl.ownBlocks { + if block != nil && block.NumberU64() == number { + return block + } + } + return nil +} + // CurrentHeader retrieves the current head header from the canonical chain. func (dl *downloadTester) CurrentHeader() *types.Header { dl.lock.RLock() @@ -254,7 +307,7 @@ return dl.ownChainTd[hash] }   // InsertHeaderChain injects a new batch of headers into the simulated chain. -func (dl *downloadTester) InsertHeaderChain(headers []*types.Header, checkFreq int) (i int, err error) { +func (dl *downloadTester) InsertHeaderChain(headers []*types.Header, checkFreq int, contiguousHeaders bool) (i int, err error) { dl.lock.Lock() defer dl.lock.Unlock() // Do a quick check, as the blockchain.InsertHeaderChain doesn't insert anything in case of errors @@ -283,8 +336,7 @@ } dl.ownHashes = append(dl.ownHashes, hash) dl.ownHeaders[hash] = header   - td := dl.getTd(header.ParentHash) - dl.ownChainTd[hash] = new(big.Int).Add(td, header.Difficulty) + dl.ownChainTd[hash] = new(big.Int).Add(header.Number, big.NewInt(1)) } return len(headers), nil } @@ -306,8 +358,7 @@ } dl.ownBlocks[block.Hash()] = block dl.ownReceipts[block.Hash()] = make(types.Receipts, 0) dl.stateDb.Put(block.Root().Bytes(), []byte{0x00}) - td := dl.getTd(block.ParentHash()) - dl.ownChainTd[block.Hash()] = new(big.Int).Add(td, block.Difficulty()) + dl.ownChainTd[block.Hash()] = block.TotalDifficulty() } return len(blocks), nil } @@ -332,7 +383,8 @@ dl.ancientReceipts[blocks[i].Hash()] = receipts[i]   // Migrate from active db to ancient db dl.ancientHeaders[blocks[i].Hash()] = blocks[i].Header() - dl.ancientChainTd[blocks[i].Hash()] = new(big.Int).Add(dl.ancientChainTd[blocks[i].ParentHash()], blocks[i].Difficulty()) + dl.ancientChainTd[blocks[i].Hash()] = blocks[i].TotalDifficulty() + delete(dl.ownHeaders, blocks[i].Hash()) delete(dl.ownChainTd, blocks[i].Hash()) } else { @@ -451,8 +503,8 @@ // RequestBodies constructs a getBlockBodies method associated with a particular // peer in the download tester. The returned function can be used to retrieve // batches of block bodies from the particularly requested peer. func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash) error { - txs, uncles := dlp.chain.bodies(hashes) - go dlp.dl.downloader.DeliverBodies(dlp.id, txs, uncles) + txs, randomness, epochSnarkData := dlp.chain.bodies(hashes) + go dlp.dl.downloader.DeliverBodies(dlp.id, txs, randomness, epochSnarkData) return nil }   @@ -522,9 +574,9 @@ t.Fatalf("synchronised receipts mismatch: have %v, want %v", rs, receipts) } }   -func TestCanonicalSynchronisation66Full(t *testing.T) { testCanonSync(t, eth.ETH66, FullSync) } -func TestCanonicalSynchronisation66Fast(t *testing.T) { testCanonSync(t, eth.ETH66, FastSync) } -func TestCanonicalSynchronisation66Light(t *testing.T) { testCanonSync(t, eth.ETH66, LightSync) } +func TestCanonicalSynchronisation67Full(t *testing.T) { testCanonSync(t, istanbul.Celo67, FullSync) } +func TestCanonicalSynchronisation67Fast(t *testing.T) { testCanonSync(t, istanbul.Celo67, FastSync) } +func TestCanonicalSynchronisation67Light(t *testing.T) { testCanonSync(t, istanbul.Celo67, LightSync) }   func testCanonSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -545,8 +597,8 @@ }   // Tests that if a large batch of blocks are being downloaded, it is throttled // until the cached blocks are retrieved. -func TestThrottling66Full(t *testing.T) { testThrottling(t, eth.ETH66, FullSync) } -func TestThrottling66Fast(t *testing.T) { testThrottling(t, eth.ETH66, FastSync) } +func TestThrottling67Full(t *testing.T) { testThrottling(t, istanbul.Celo67, FullSync) } +func TestThrottling67Fast(t *testing.T) { testThrottling(t, istanbul.Celo67, FastSync) }   func testThrottling(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -578,7 +630,7 @@ break } // Wait a bit for sync to throttle itself var cached, frozen int - for start := time.Now(); time.Since(start) < 3*time.Second; { + for start := time.Now(); time.Since(start) < 6*time.Second; { time.Sleep(25 * time.Millisecond)   tester.lock.Lock() @@ -627,9 +679,9 @@ // Tests that simple synchronization against a forked chain works correctly. In // this test common ancestor lookup should *not* be short circuited, and a full // binary search should be executed. -func TestForkedSync66Full(t *testing.T) { testForkedSync(t, eth.ETH66, FullSync) } -func TestForkedSync66Fast(t *testing.T) { testForkedSync(t, eth.ETH66, FastSync) } -func TestForkedSync66Light(t *testing.T) { testForkedSync(t, eth.ETH66, LightSync) } +func TestForkedSync67Full(t *testing.T) { testForkedSync(t, istanbul.Celo67, FullSync) } +func TestForkedSync67Fast(t *testing.T) { testForkedSync(t, istanbul.Celo67, FastSync) } +func TestForkedSync67Light(t *testing.T) { testForkedSync(t, istanbul.Celo67, LightSync) }   func testForkedSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -656,9 +708,9 @@ }   // Tests that synchronising against a much shorter but much heavyer fork works // corrently and is not dropped. -func TestHeavyForkedSync66Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FullSync) } -func TestHeavyForkedSync66Fast(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FastSync) } -func TestHeavyForkedSync66Light(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, LightSync) } +func TestHeavyForkedSync67Full(t *testing.T) { testHeavyForkedSync(t, istanbul.Celo67, FullSync) } +func TestHeavyForkedSync67Fast(t *testing.T) { testHeavyForkedSync(t, istanbul.Celo67, FastSync) } +func TestHeavyForkedSync67Light(t *testing.T) { testHeavyForkedSync(t, istanbul.Celo67, LightSync) }   func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -687,9 +739,9 @@ // Tests that chain forks are contained within a certain interval of the current // chain head, ensuring that malicious peers cannot waste resources by feeding // long dead chains. -func TestBoundedForkedSync66Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FullSync) } -func TestBoundedForkedSync66Fast(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FastSync) } -func TestBoundedForkedSync66Light(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, LightSync) } +func TestBoundedForkedSync67Full(t *testing.T) { testBoundedForkedSync(t, istanbul.Celo67, FullSync) } +func TestBoundedForkedSync67Fast(t *testing.T) { testBoundedForkedSync(t, istanbul.Celo67, FastSync) } +func TestBoundedForkedSync67Light(t *testing.T) { testBoundedForkedSync(t, istanbul.Celo67, LightSync) }   func testBoundedForkedSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -717,14 +769,14 @@ // Tests that chain forks are contained within a certain interval of the current // chain head for short but heavy forks too. These are a bit special because they // take different ancestor lookup paths. -func TestBoundedHeavyForkedSync66Full(t *testing.T) { - testBoundedHeavyForkedSync(t, eth.ETH66, FullSync) +func TestBoundedHeavyForkedSync67Full(t *testing.T) { + testBoundedHeavyForkedSync(t, istanbul.Celo67, FullSync) } -func TestBoundedHeavyForkedSync66Fast(t *testing.T) { - testBoundedHeavyForkedSync(t, eth.ETH66, FastSync) +func TestBoundedHeavyForkedSync67Fast(t *testing.T) { + testBoundedHeavyForkedSync(t, istanbul.Celo67, FastSync) } -func TestBoundedHeavyForkedSync66Light(t *testing.T) { - testBoundedHeavyForkedSync(t, eth.ETH66, LightSync) +func TestBoundedHeavyForkedSync67Light(t *testing.T) { + testBoundedHeavyForkedSync(t, istanbul.Celo67, LightSync) }   func testBoundedHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) { @@ -752,7 +804,7 @@ }   // Tests that an inactive downloader will not accept incoming block headers, // bodies and receipts. -func TestInactiveDownloader63(t *testing.T) { +func TestInactiveDownloader64(t *testing.T) { t.Parallel()   tester := newTester() @@ -762,7 +814,7 @@ // Check that neither block headers nor bodies are accepted if err := tester.downloader.DeliverHeaders("bad peer", []*types.Header{}); err != errNoSyncActive { t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) } - if err := tester.downloader.DeliverBodies("bad peer", [][]*types.Transaction{}, [][]*types.Header{}); err != errNoSyncActive { + if err := tester.downloader.DeliverBodies("bad peer", [][]*types.Transaction{}, []*types.Randomness{}, []*types.EpochSnarkData{}); err != errNoSyncActive { t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) } if err := tester.downloader.DeliverReceipts("bad peer", [][]*types.Receipt{}); err != errNoSyncActive { @@ -771,9 +823,9 @@ } }   // Tests that a canceled download wipes all previously accumulated state. -func TestCancel66Full(t *testing.T) { testCancel(t, eth.ETH66, FullSync) } -func TestCancel66Fast(t *testing.T) { testCancel(t, eth.ETH66, FastSync) } -func TestCancel66Light(t *testing.T) { testCancel(t, eth.ETH66, LightSync) } +func TestCancel67Full(t *testing.T) { testCancel(t, istanbul.Celo67, FullSync) } +func TestCancel67Fast(t *testing.T) { testCancel(t, istanbul.Celo67, FastSync) } +func TestCancel67Light(t *testing.T) { testCancel(t, istanbul.Celo67, LightSync) }   func testCancel(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -800,9 +852,15 @@ } }   // Tests that synchronisation from multiple peers works as intended (multi thread sanity test). -func TestMultiSynchronisation66Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FullSync) } -func TestMultiSynchronisation66Fast(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FastSync) } -func TestMultiSynchronisation66Light(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, LightSync) } +func TestMultiSynchronisation67Full(t *testing.T) { + testMultiSynchronisation(t, istanbul.Celo67, FullSync) +} +func TestMultiSynchronisation67Fast(t *testing.T) { + testMultiSynchronisation(t, istanbul.Celo67, FastSync) +} +func TestMultiSynchronisation67Light(t *testing.T) { + testMultiSynchronisation(t, istanbul.Celo67, LightSync) +}   func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -826,9 +884,15 @@ }   // Tests that synchronisations behave well in multi-version protocol environments // and not wreak havoc on other nodes in the network. -func TestMultiProtoSynchronisation66Full(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FullSync) } -func TestMultiProtoSynchronisation66Fast(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FastSync) } -func TestMultiProtoSynchronisation66Light(t *testing.T) { testMultiProtoSync(t, eth.ETH66, LightSync) } +func TestMultiProtoSynchronisation67Full(t *testing.T) { + testMultiProtoSync(t, istanbul.Celo67, FullSync) +} +func TestMultiProtoSynchronisation67Fast(t *testing.T) { + testMultiProtoSync(t, istanbul.Celo67, FastSync) +} +func TestMultiProtoSynchronisation67Light(t *testing.T) { + testMultiProtoSync(t, istanbul.Celo67, LightSync) +}   func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -840,8 +904,7 @@ // Create a small enough block chain to download chain := testChainBase.shorten(blockCacheMaxItems - 15)   // Create peers of every type - tester.newPeer("peer 66", eth.ETH66, chain) - //tester.newPeer("peer 65", eth.ETH67, chain) + tester.newPeer("peer 67", istanbul.Celo67, chain)   // Synchronise with the requested peer and make sure all blocks were retrieved if err := tester.sync(fmt.Sprintf("peer %d", protocol), nil, mode); err != nil { @@ -850,7 +913,7 @@ } assertOwnChain(t, tester, chain.len())   // Check that no peers have been dropped off - for _, version := range []int{66} { + for _, version := range []int{67} { peer := fmt.Sprintf("peer %d", version) if _, ok := tester.peers[peer]; !ok { t.Errorf("%s dropped", peer) @@ -858,61 +921,17 @@ } } }   -// Tests that if a block is empty (e.g. header only), no body request should be -// made, and instead the header should be assembled into a whole block in itself. -func TestEmptyShortCircuit66Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FullSync) } -func TestEmptyShortCircuit66Fast(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FastSync) } -func TestEmptyShortCircuit66Light(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, LightSync) } - -func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) { - t.Parallel() - - tester := newTester() - defer tester.terminate() - - // Create a block chain to download - chain := testChainBase - tester.newPeer("peer", protocol, chain) - - // Instrument the downloader to signal body requests - bodiesHave, receiptsHave := int32(0), int32(0) - tester.downloader.bodyFetchHook = func(headers []*types.Header) { - atomic.AddInt32(&bodiesHave, int32(len(headers))) - } - tester.downloader.receiptFetchHook = func(headers []*types.Header) { - atomic.AddInt32(&receiptsHave, int32(len(headers))) - } - // Synchronise with the peer and make sure all blocks were retrieved - if err := tester.sync("peer", nil, mode); err != nil { - t.Fatalf("failed to synchronise blocks: %v", err) - } - assertOwnChain(t, tester, chain.len()) - - // Validate the number of block bodies that should have been requested - bodiesNeeded, receiptsNeeded := 0, 0 - for _, block := range chain.blockm { - if mode != LightSync && block != tester.genesis && (len(block.Transactions()) > 0 || len(block.Uncles()) > 0) { - bodiesNeeded++ - } - } - for _, receipt := range chain.receiptm { - if mode == FastSync && len(receipt) > 0 { - receiptsNeeded++ - } - } - if int(bodiesHave) != bodiesNeeded { - t.Errorf("body retrieval count mismatch: have %v, want %v", bodiesHave, bodiesNeeded) - } - if int(receiptsHave) != receiptsNeeded { - t.Errorf("receipt retrieval count mismatch: have %v, want %v", receiptsHave, receiptsNeeded) - } -} - // Tests that headers are enqueued continuously, preventing malicious nodes from // stalling the downloader by feeding gapped header chains. -func TestMissingHeaderAttack66Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FullSync) } -func TestMissingHeaderAttack66Fast(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FastSync) } -func TestMissingHeaderAttack66Light(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, LightSync) } +func TestMissingHeaderAttack67Full(t *testing.T) { + testMissingHeaderAttack(t, istanbul.Celo67, FullSync) +} +func TestMissingHeaderAttack67Fast(t *testing.T) { + testMissingHeaderAttack(t, istanbul.Celo67, FastSync) +} +func TestMissingHeaderAttack67Light(t *testing.T) { + testMissingHeaderAttack(t, istanbul.Celo67, LightSync) +}   func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -938,9 +957,15 @@ }   // Tests that if requested headers are shifted (i.e. first is missing), the queue // detects the invalid numbering. -func TestShiftedHeaderAttack66Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FullSync) } -func TestShiftedHeaderAttack66Fast(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FastSync) } -func TestShiftedHeaderAttack66Light(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, LightSync) } +func TestShiftedHeaderAttack67Full(t *testing.T) { + testShiftedHeaderAttack(t, istanbul.Celo67, FullSync) +} +func TestShiftedHeaderAttack67Fast(t *testing.T) { + testShiftedHeaderAttack(t, istanbul.Celo67, FastSync) +} +func TestShiftedHeaderAttack67Light(t *testing.T) { + testShiftedHeaderAttack(t, istanbul.Celo67, LightSync) +}   func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -971,7 +996,9 @@ // Tests that upon detecting an invalid header, the recent ones are rolled back // for various failure scenarios. Afterwards a full sync is attempted to make // sure no state was corrupted. -func TestInvalidHeaderRollback66Fast(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH66, FastSync) } +func TestInvalidHeaderRollback67Fast(t *testing.T) { + testInvalidHeaderRollback(t, istanbul.Celo67, FastSync) +}   func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -979,7 +1006,7 @@ tester := newTester()   // Create a small enough block chain to download - targetBlocks := 3*fsHeaderSafetyNet + 256 + fsMinFullBlocks + targetBlocks := 3*fsHeaderSafetyNet + 256 + int(fsMinFullBlocks) chain := testChainBase.shorten(targetBlocks)   // Attempt to sync with an attacker that feeds junk during the fast sync phase. @@ -1061,14 +1088,14 @@ }   // Tests that a peer advertising a high TD doesn't get to stall the downloader // afterwards by not sending any useful hashes. -func TestHighTDStarvationAttack66Full(t *testing.T) { - testHighTDStarvationAttack(t, eth.ETH66, FullSync) +func TestHighTDStarvationAttack67Full(t *testing.T) { + testHighTDStarvationAttack(t, istanbul.Celo67, FullSync) } -func TestHighTDStarvationAttack66Fast(t *testing.T) { - testHighTDStarvationAttack(t, eth.ETH66, FastSync) +func TestHighTDStarvationAttack67Fast(t *testing.T) { + testHighTDStarvationAttack(t, istanbul.Celo67, FastSync) } -func TestHighTDStarvationAttack66Light(t *testing.T) { - testHighTDStarvationAttack(t, eth.ETH66, LightSync) +func TestHighTDStarvationAttack67Light(t *testing.T) { + testHighTDStarvationAttack(t, istanbul.Celo67, LightSync) }   func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) { @@ -1085,7 +1112,9 @@ tester.terminate() }   // Tests that misbehaving peers are disconnected, whilst behaving ones are not. -func TestBlockHeaderAttackerDropping66(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH66) } +func TestBlockHeaderAttackerDropping67(t *testing.T) { + testBlockHeaderAttackerDropping(t, istanbul.Celo67) +}   func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) { t.Parallel() @@ -1137,9 +1166,9 @@ }   // Tests that synchronisation progress (origin block number, current block number // and highest block number) is tracked and updated correctly. -func TestSyncProgress66Full(t *testing.T) { testSyncProgress(t, eth.ETH66, FullSync) } -func TestSyncProgress66Fast(t *testing.T) { testSyncProgress(t, eth.ETH66, FastSync) } -func TestSyncProgress66Light(t *testing.T) { testSyncProgress(t, eth.ETH66, LightSync) } +func TestSyncProgress67Full(t *testing.T) { testSyncProgress(t, istanbul.Celo67, FullSync) } +func TestSyncProgress67Fast(t *testing.T) { testSyncProgress(t, istanbul.Celo67, FastSync) } +func TestSyncProgress67Light(t *testing.T) { testSyncProgress(t, istanbul.Celo67, LightSync) }   func testSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -1217,9 +1246,11 @@ // Tests that synchronisation progress (origin block number and highest block // number) is tracked and updated correctly in case of a fork (or manual head // revertal). -func TestForkedSyncProgress66Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FullSync) } -func TestForkedSyncProgress66Fast(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FastSync) } -func TestForkedSyncProgress66Light(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, LightSync) } +func TestForkedSyncProgress67Full(t *testing.T) { testForkedSyncProgress(t, istanbul.Celo67, FullSync) } +func TestForkedSyncProgress67Fast(t *testing.T) { testForkedSyncProgress(t, istanbul.Celo67, FastSync) } +func TestForkedSyncProgress67Light(t *testing.T) { + testForkedSyncProgress(t, istanbul.Celo67, LightSync) +}   func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -1289,9 +1320,11 @@ // Tests that if synchronisation is aborted due to some failure, then the progress // origin is not updated in the next sync cycle, as it should be considered the // continuation of the previous sync and not a new instance. -func TestFailedSyncProgress66Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FullSync) } -func TestFailedSyncProgress66Fast(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FastSync) } -func TestFailedSyncProgress66Light(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, LightSync) } +func TestFailedSyncProgress67Full(t *testing.T) { testFailedSyncProgress(t, istanbul.Celo67, FullSync) } +func TestFailedSyncProgress67Fast(t *testing.T) { testFailedSyncProgress(t, istanbul.Celo67, FastSync) } +func TestFailedSyncProgress67Light(t *testing.T) { + testFailedSyncProgress(t, istanbul.Celo67, LightSync) +}   func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -1358,9 +1391,9 @@ }   // Tests that if an attacker fakes a chain height, after the attack is detected, // the progress height is successfully reduced at the next sync invocation. -func TestFakedSyncProgress66Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FullSync) } -func TestFakedSyncProgress66Fast(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FastSync) } -func TestFakedSyncProgress66Light(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, LightSync) } +func TestFakedSyncProgress67Full(t *testing.T) { testFakedSyncProgress(t, istanbul.Celo67, FullSync) } +func TestFakedSyncProgress67Fast(t *testing.T) { testFakedSyncProgress(t, istanbul.Celo67, FastSync) } +func TestFakedSyncProgress67Light(t *testing.T) { testFakedSyncProgress(t, istanbul.Celo67, LightSync) }   func testFakedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -1431,9 +1464,11 @@ }   // This test reproduces an issue where unexpected deliveries would // block indefinitely if they arrived at the right time. -func TestDeliverHeadersHang66Full(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FullSync) } -func TestDeliverHeadersHang66Fast(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FastSync) } -func TestDeliverHeadersHang66Light(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, LightSync) } +func TestDeliverHeadersHang67Full(t *testing.T) { testDeliverHeadersHang(t, istanbul.Celo67, FullSync) } +func TestDeliverHeadersHang67Fast(t *testing.T) { testDeliverHeadersHang(t, istanbul.Celo67, FastSync) } +func TestDeliverHeadersHang67Light(t *testing.T) { + testDeliverHeadersHang(t, istanbul.Celo67, LightSync) +}   func testDeliverHeadersHang(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -1588,10 +1623,14 @@ }   // Tests that peers below a pre-configured checkpoint block are prevented from // being fast-synced from, avoiding potential cheap eclipse attacks. -func TestCheckpointEnforcement66Full(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FullSync) } -func TestCheckpointEnforcement66Fast(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FastSync) } -func TestCheckpointEnforcement66Light(t *testing.T) { - testCheckpointEnforcement(t, eth.ETH66, LightSync) +func TestCheckpointEnforcement67Full(t *testing.T) { + testCheckpointEnforcement(t, istanbul.Celo67, FullSync) +} +func TestCheckpointEnforcement67Fast(t *testing.T) { + testCheckpointEnforcement(t, istanbul.Celo67, FastSync) +} +func TestCheckpointEnforcement67Light(t *testing.T) { + testCheckpointEnforcement(t, istanbul.Celo67, LightSync) }   func testCheckpointEnforcement(t *testing.T, protocol uint, mode SyncMode) { @@ -1601,7 +1640,7 @@ // Create a new tester with a particular hard coded checkpoint block tester := newTester() defer tester.terminate()   - tester.downloader.checkpoint = uint64(fsMinFullBlocks) + 256 + tester.downloader.checkpoint = fsMinFullBlocks + 256 chain := testChainBase.shorten(int(tester.downloader.checkpoint) - 1)   // Attempt to sync with the peer and validate the result @@ -1620,3 +1659,24 @@ } else { assertOwnChain(t, tester, chain.len()) } } + +func TestPivot(t *testing.T) { + testCases := []struct { + height uint64 + epoch uint64 + expected uint64 + }{ + {0, 0, 0}, + {172, 17280, 0}, + {17280, 17280, 0}, + {17280*10 + 1000, 17280, 17280*10 + 1}, + {17280*10 + 10, 17280, 17280*9 + 1}, + {17280 * 10, 17280, 17280*9 + 1}, + {17280*10 - 1000, 17280, 17280*9 + 1}, + } + for _, tt := range testCases { + if res := computePivot(tt.height, tt.epoch); res != tt.expected { + t.Errorf("Got %v expected %v for value %v", res, tt.expected, tt.height) + } + } +}
diff --git go-ethereum/les/downloader/modes.go celo/les/downloader/modes.go index 103af4f0b30f02165bdd706aa2b184243d98e695..f9ce672aa1e1eea55243f01b14fc01f09609b9b1 100644 --- go-ethereum/les/downloader/modes.go +++ celo/les/downloader/modes.go @@ -24,13 +24,31 @@ type SyncMode uint32   const ( FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks - FastSync // Quickly download the headers, full sync only at the chain + FastSync // Quickly download the headers, full sync only at the chain head SnapSync // Download the chain and the state via compact snapshots LightSync // Download only the headers and terminate afterwards + LightestSync // Synchronise one block per Epoch (Celo-specific mode) ) + +func FromString(syncModeStr string) SyncMode { + switch syncModeStr { + case "full": + return FullSync + case "fast": + return FastSync + // case "snap": + // return SnapSync + case "light": + return LightSync + case "lightest": + return LightestSync + default: + panic("Unknown syncModeStr: " + syncModeStr) + } +}   func (mode SyncMode) IsValid() bool { - return mode >= FullSync && mode <= LightSync + return mode >= FullSync && mode <= LightestSync }   // String implements the stringer interface. @@ -40,10 +58,12 @@ case FullSync: return "full" case FastSync: return "fast" - case SnapSync: - return "snap" + // case SnapSync: + // return "snap" case LightSync: return "light" + case LightestSync: + return "lightest" default: return "unknown" } @@ -55,10 +75,13 @@ case FullSync: return []byte("full"), nil case FastSync: return []byte("fast"), nil - case SnapSync: - return []byte("snap"), nil + // case SnapSync: + // return []byte("snap"), nil + // TODO: Implement snap sync case LightSync: return []byte("light"), nil + case LightestSync: + return []byte("lightest"), nil default: return nil, fmt.Errorf("unknown sync mode %d", mode) } @@ -70,12 +93,50 @@ case "full": *mode = FullSync case "fast": *mode = FastSync - case "snap": - *mode = SnapSync + // case "snap": + // *mode = SnapSync + // TODO: Implement snap sync case "light": *mode = LightSync + case "lightest": + *mode = LightestSync default: - return fmt.Errorf(`unknown sync mode %q, want "full", "fast" or "light"`, text) + return fmt.Errorf(`unknown sync mode %q, want "full", "fast", "light", or "lightest"`, text) } return nil } + +// TODO: Enable snap sync mode here. (https://github.com/ethereum/go-ethereum/issues/1735) + +// Returns true if the all headers and not just some a small, discontinuous, set of headers are fetched. +func (mode SyncMode) SyncFullHeaderChain() bool { + switch mode { + case FullSync: + return true + case FastSync: + return true + case LightSync: + return true + case LightestSync: + return false + default: + panic(fmt.Errorf("unknown sync mode %d", mode)) + } +} + +// Returns true if the full blocks (and not just headers) are fetched. +// If a mode returns true here then it will return true for `SyncFullHeaderChain` as well. +func (mode SyncMode) SyncFullBlockChain() bool { + switch mode { + case FullSync: + return true + case FastSync: + return true + case LightSync: + return false + case LightestSync: + return false + default: + panic(fmt.Errorf("unknown sync mode %d", mode)) + } +}
diff --git go-ethereum/les/downloader/testchain_test.go celo/les/downloader/testchain_test.go index 9420b8f57a349ce2a657e65a54c056c016e9f907..22dbb5a3e9205e0ae56ec96ebb2b6e67968c9880 100644 --- go-ethereum/les/downloader/testchain_test.go +++ celo/les/downloader/testchain_test.go @@ -22,7 +22,7 @@ "math/big" "sync"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -69,7 +69,7 @@ tc := new(testChain).copy(length) tc.genesis = genesis tc.chain = append(tc.chain, genesis.Hash()) tc.headerm[tc.genesis.Hash()] = tc.genesis.Header() - tc.tdm[tc.genesis.Hash()] = tc.genesis.Difficulty() + tc.tdm[tc.genesis.Hash()] = tc.genesis.TotalDifficulty() tc.blockm[tc.genesis.Hash()] = tc.genesis tc.generate(length-1, 0, genesis, false) return tc @@ -112,13 +112,12 @@ }   // generate creates a chain of n blocks starting at and including parent. // the returned hash chain is ordered head->parent. In addition, every 22th block -// contains a transaction and every 5th an uncle to allow testing correct block -// reassembly. +// contains a transaction to allow testing correct block reassembly. func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool) { // start := time.Now() // defer func() { fmt.Printf("test chain generated in %v\n", time.Since(start)) }()   - blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testDB, n, func(i int, block *core.BlockGen) { + blocks, receipts := core.GenerateChain(params.IstanbulTestChainConfig, parent, mockEngine.NewFaker(), testDB, n, func(i int, block *core.BlockGen) { block.SetCoinbase(common.Address{seed}) // If a heavy chain is requested, delay blocks to raise difficulty if heavy { @@ -126,26 +125,18 @@ block.OffsetTime(-1) } // Include transactions to the miner to make blocks more interesting. if parent == tc.genesis && i%22 == 0 { - signer := types.MakeSigner(params.TestChainConfig, block.Number()) - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey) + signer := types.MakeSigner(params.IstanbulTestChainConfig, block.Number()) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, testKey) if err != nil { panic(err) } block.AddTx(tx) } - // if the block number is a multiple of 5, add a bonus uncle to the block - if i > 0 && i%5 == 0 { - block.AddUncle(&types.Header{ - ParentHash: block.PrevBlock(i - 1).Hash(), - Number: big.NewInt(block.Number().Int64() - 1), - }) - } })   // Convert the block-chain into a hash-chain and header/block maps - td := new(big.Int).Set(tc.td(parent.Hash())) for i, b := range blocks { - td := td.Add(td, b.Difficulty()) + td := b.TotalDifficulty() hash := b.Hash() tc.chain = append(tc.chain, hash) tc.blockm[hash] = b @@ -208,16 +199,18 @@ return results }   // bodies returns the block bodies of the given block hashes. -func (tc *testChain) bodies(hashes []common.Hash) ([][]*types.Transaction, [][]*types.Header) { +func (tc *testChain) bodies(hashes []common.Hash) ([][]*types.Transaction, []*types.Randomness, []*types.EpochSnarkData) { transactions := make([][]*types.Transaction, 0, len(hashes)) - uncles := make([][]*types.Header, 0, len(hashes)) + randomness := make([]*types.Randomness, 0, len(hashes)) + epochSnarkData := make([]*types.EpochSnarkData, 0, len(hashes)) for _, hash := range hashes { if block, ok := tc.blockm[hash]; ok { transactions = append(transactions, block.Transactions()) - uncles = append(uncles, block.Uncles()) + randomness = append(randomness, block.Randomness()) + epochSnarkData = append(epochSnarkData, block.EpochSnarkData()) } } - return transactions, uncles + return transactions, randomness, epochSnarkData }   func (tc *testChain) hashToNumber(target common.Hash) (uint64, bool) {
diff --git go-ethereum/les/downloader/downloader.go celo/les/downloader/downloader.go index ac159ede421abef793e1150fb1e5e1ff00b07b78..c87c48a91bb82df9509d1d7bc4dc6e26e721a2bc 100644 --- go-ethereum/les/downloader/downloader.go +++ celo/les/downloader/downloader.go @@ -14,15 +14,13 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.   -// This is a temporary package whilst working on the eth/66 blocking refactors. -// After that work is done, les needs to be refactored to use the new package, -// or alternatively use a stripped down version of it. Either way, we need to -// keep the changes scoped so duplicating temporarily seems the sanest. +// Package downloader contains the manual full chain synchronisation. package downloader   import ( "errors" "fmt" + "math" "math/big" "sync" "sync/atomic" @@ -30,10 +28,10 @@ "time"   "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" @@ -46,11 +44,12 @@ var ( MaxBlockFetch = 128 // Amount of blocks to be fetched per retrieval request MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request + MaxEpochHeaderFetch = 192 // Number of epoch block headers to fetch (only used in IBFT consensus + Lightest sync mode) MaxSkeletonSize = 128 // Number of header fetches to need for a skeleton assembly MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request MaxStateFetch = 384 // Amount of node state values to allow fetching per request   - maxQueuedHeaders = 32 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection) + maxQueuedHeaders = 32 * 1024 // [celo/63|eth/62] Maximum number of headers to queue for import (DOS protection) maxHeadersProcess = 2048 // Number of header download results to import at once into the chain maxResultsProcess = 2048 // Number of content download results to import at once into the chain fullMaxForkAncestry uint64 = params.FullImmutabilityThreshold // Maximum chain reorganisation (locally redeclared so tests can reduce it) @@ -63,7 +62,7 @@ fsHeaderCheckFrequency = 100 // Verification frequency of the downloaded headers during fast sync fsHeaderSafetyNet = 2048 // Number of headers to discard in case a chain violation is detected fsHeaderForceVerify = 24 // Number of headers to verify before and after the pivot to accept it fsHeaderContCheck = 3 * time.Second // Time interval to check for header continuations during state download - fsMinFullBlocks = 64 // Number of blocks to retrieve fully even in fast sync + fsMinFullBlocks uint64 = 64 // Number of blocks to retrieve fully even in fast sync )   var ( @@ -87,6 +86,10 @@ errNoSyncActive = errors.New("no sync active") errTooOld = errors.New("peer's protocol version too old") errNoAncestorFound = errors.New("no common ancestor found") ) + +// If you adding a new variable add it at the bottom. Otherwise, you can end up making some uint64 unaligned to 8-byte +// boundary. That seems fine with ARM but on X86 (emulator), atomic loading of 64-bit variables causes a confusing crash. +// Some variables like rttEstimate are loaded atomically with atomic.LoadUint64()   type Downloader struct { mode uint32 // Synchronisation mode defining the strategy used (per sync cycle), use d.getMode() to get the SyncMode @@ -118,6 +121,7 @@ synchronising int32 notified int32 committed int32 ancientLimit uint64 // The maximum block number which can be regarded as ancient data. + ancientLimitMu sync.Mutex   // Channels headerCh chan dataPack // Channel receiving inbound block headers @@ -145,6 +149,8 @@ cancelWg sync.WaitGroup // Make sure all fetcher goroutines have exited.   quitCh chan struct{} // Quit channel to signal termination quitLock sync.Mutex // Lock to prevent double closes + epoch uint64 // Epoch value is useful in IBFT consensus + ibftConsensus bool // True if we are in IBFT consensus mode   // Testing hooks syncInitHook func(uint64, uint64) // Method to call upon initiating a new sync run @@ -160,6 +166,9 @@ HasHeader(common.Hash, uint64) bool   // GetHeaderByHash retrieves a header from the local chain. GetHeaderByHash(common.Hash) *types.Header + + // GetHeaderByHash retrieves a header from the local chain by number. + GetHeaderByNumber(uint64) *types.Header   // CurrentHeader retrieves the head header from the local chain. CurrentHeader() *types.Header @@ -168,8 +177,9 @@ // GetTd returns the total difficulty of a local block. GetTd(common.Hash, uint64) *big.Int   // InsertHeaderChain inserts a batch of headers into the local chain. - InsertHeaderChain([]*types.Header, int) (int, error) + InsertHeaderChain([]*types.Header, int, bool) (int, error)   + Config() *params.ChainConfig // SetHead rewinds the local chain to a new head. SetHead(uint64) error } @@ -202,15 +212,34 @@ // InsertReceiptChain inserts a batch of receipts into the local chain. InsertReceiptChain(types.Blocks, []types.Receipts, uint64) (int, error)   + // GetBlockByNumber retrieves a block from the database by number. + GetBlockByNumber(uint64) *types.Block + // Snapshots returns the blockchain snapshot tree to paused it during sync. Snapshots() *snapshot.Tree } + +// TODO(tim) previously passing mode here!   // New creates a new downloader to fetch hashes and blocks from remote peers. func New(checkpoint uint64, stateDb ethdb.Database, stateBloom *trie.SyncBloom, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn) *Downloader { if lightchain == nil { lightchain = chain } + + ibftConsensus := false + epoch := uint64(0) + if chain != nil && chain.Config() != nil && chain.Config().Istanbul != nil { + epoch = chain.Config().Istanbul.Epoch + ibftConsensus = true + } else if lightchain != nil && lightchain.Config() != nil && lightchain.Config().Istanbul != nil { + epoch = lightchain.Config().Istanbul.Epoch + ibftConsensus = true + } + if epoch > math.MaxInt32 { + panic(fmt.Sprintf("epoch is too big(%d), the code to fetch epoch headers casts epoch to an int to calculate value for skip variable", epoch)) + } + dl := &Downloader{ stateDB: stateDb, stateBloom: stateBloom, @@ -235,6 +264,8 @@ syncStatsState: stateSyncStats{ processed: rawdb.ReadFastTrieProgress(stateDb), }, trackStateReq: make(chan *stateReq), + ibftConsensus: ibftConsensus, + epoch: epoch, } go dl.stateFetcher() return dl @@ -264,6 +295,7 @@ current = d.lightchain.CurrentHeader().Number.Uint64() default: log.Error("Unknown downloader chain/mode combo", "light", d.lightchain != nil, "full", d.blockchain != nil, "mode", mode) } + log.Debug(fmt.Sprintf("Current head is %v", current)) return ethereum.SyncProgress{ StartingBlock: d.syncStatsChainOrigin, CurrentBlock: current, @@ -451,8 +483,8 @@ latest := d.lightchain.CurrentHeader() d.mux.Post(DoneEvent{latest}) } }() - if p.version < eth.ETH66 { - return fmt.Errorf("%w: advertized %d < required %d", errTooOld, p.version, eth.ETH66) + if p.version < istanbul.Celo67 { + return fmt.Errorf("%w: advertized %d < required %d", errTooOld, p.version, istanbul.Celo67) } mode := d.getMode()   @@ -483,21 +515,20 @@ d.syncStatsLock.Lock() if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin { d.syncStatsChainOrigin = origin } + log.Debug(fmt.Sprintf("After the check origin is %d height is %d", origin, height)) d.syncStatsChainHeight = height d.syncStatsLock.Unlock()   // Ensure our origin point is below any fast sync pivot point if mode == FastSync { - if height <= uint64(fsMinFullBlocks) { + pivotNumber := pivot.Number.Uint64() + // Write out the pivot into the database so a rollback beyond it will + // reenable fast sync + rawdb.WriteLastPivotNumber(d.stateDB, pivotNumber) + if pivotNumber == 0 { origin = 0 - } else { - pivotNumber := pivot.Number.Uint64() - if pivotNumber <= origin { - origin = pivotNumber - 1 - } - // Write out the pivot into the database so a rollback beyond it will - // reenable fast sync - rawdb.WriteLastPivotNumber(d.stateDB, pivotNumber) + } else if pivotNumber <= origin { + origin = pivotNumber - 1 } } d.committed = 1 @@ -549,7 +580,7 @@ if d.syncInitHook != nil { d.syncInitHook(origin, height) } fetchers := []func() error{ - func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved + func() error { return d.fetchHeaders(p, origin+1, height) }, // Headers are always retrieved func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync func() error { return d.processHeaders(origin+1, td) }, @@ -616,6 +647,10 @@ // finish before returning. func (d *Downloader) Cancel() { d.cancel() d.cancelWg.Wait() + d.ancientLimitMu.Lock() + defer d.ancientLimitMu.Unlock() + d.ancientLimit = 0 + log.Debug("Reset ancient limit to zero") }   // Terminate interrupts the downloader, canceling all pending operations. @@ -644,12 +679,18 @@ p.log.Debug("Retrieving remote chain head") mode := d.getMode()   // Request the advertised remote head block and wait for the response - latest, _ := p.peer.Head() + latest, td := p.peer.Head() fetch := 1 if mode == FastSync { fetch = 2 // head + pivot headers } - go p.peer.RequestHeadersByHash(latest, fetch, fsMinFullBlocks-1, true) + height := td.Uint64() - 1 // height == TD - 1 + beginningEpochBlockNumber := d.calcPivot(height) + // NOTE: the beginningEpochBlockNumber is subtracting fsMinFullBlocks to the height, + // so, height and beginningEpochBlockNumber will be the same ONLY if the head is the genesis block + blocksFromHeightToEpochBlock := height - beginningEpochBlockNumber + + go p.peer.RequestHeadersByHash(latest, fetch, int(blocksFromHeightToEpochBlock-1), true)   ttl := d.peers.rates.TargetTimeout() timeout := time.After(ttl) @@ -677,7 +718,7 @@ if (mode == FastSync || mode == LightSync) && head.Number.Uint64() < d.checkpoint { return nil, nil, fmt.Errorf("%w: remote head %d below checkpoint %d", errUnsyncedPeer, head.Number, d.checkpoint) } if len(headers) == 1 { - if mode == FastSync && head.Number.Uint64() > uint64(fsMinFullBlocks) { + if mode == FastSync && head.Number.Uint64() > blocksFromHeightToEpochBlock { return nil, nil, fmt.Errorf("%w: no pivot included along head header", errBadPeer) } p.log.Debug("Remote head identified, no pivot", "number", head.Number, "hash", head.Hash()) @@ -686,8 +727,8 @@ } // At this point we have 2 headers in total and the first is the // validated head of the chain. Check the pivot number and return, pivot := headers[1] - if pivot.Number.Uint64() != head.Number.Uint64()-uint64(fsMinFullBlocks) { - return nil, nil, fmt.Errorf("%w: remote pivot %d != requested %d", errInvalidChain, pivot.Number, head.Number.Uint64()-uint64(fsMinFullBlocks)) + if pivot.Number.Uint64() != beginningEpochBlockNumber { + return nil, nil, fmt.Errorf("%w: remote pivot %d != requested %d", errInvalidChain, pivot.Number, beginningEpochBlockNumber) } return head, pivot, nil   @@ -786,9 +827,12 @@ if localHeight >= maxForkAncestry { // We're above the max reorg threshold, find the earliest fork point floor = int64(localHeight - maxForkAncestry) } + + // TODO(tim) TODO(ashishb) see https://github.com/ethereum/go-ethereum/commit/6c312a24b6041385c33eca066ff5604af315a41e + // If we're doing a light sync, ensure the floor doesn't go below the CHT, as // all headers before that point will be missing. - if mode == LightSync { + if !mode.SyncFullBlockChain() { // If we don't know the current CHT position, find it if d.genesis == 0 { header := d.lightchain.CurrentHeader() @@ -858,7 +902,7 @@ // Make sure the peer's reply conforms to the request for i, header := range headers { expectNumber := from + int64(i)*int64(skip+1) if number := header.Number.Int64(); number != expectNumber { - p.log.Warn("Head headers broke chain ordering", "index", i, "requested", expectNumber, "received", number) + p.log.Warn("Head headers broke chain ordering", "index", i, "requested", expectNumber, "received", number, "localHeight", localHeight, "remoteHeight", remoteHeight) return 0, fmt.Errorf("%w: %v", errInvalidChain, errors.New("head headers broke chain ordering")) } } @@ -1000,8 +1044,9 @@ // syncing with, and fill in the missing headers using anyone else. Headers from // other peers are only accepted if they map cleanly to the skeleton. If no one // can fill in the skeleton - not even the origin peer - it's assumed invalid and // the origin is dropped. -func (d *Downloader) fetchHeaders(p *peerConnection, from uint64) error { - p.log.Debug("Directing header downloads", "origin", from) +// height = latest block announced by the peers. +func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, height uint64) error { + p.log.Debug("fetchHeaders", "origin", from, "height", height) defer p.log.Debug("Header download terminated")   // Create a timeout timer, and the associated header fetcher @@ -1011,6 +1056,7 @@ request := time.Now() // time of the last skeleton fetch request timeout := time.NewTimer(0) // timer to dump a non-responsive active peer <-timeout.C // timeout channel should be initially empty defer timeout.Stop() + epoch := d.epoch   var ttl time.Duration getHeaders := func(from uint64) { @@ -1023,29 +1069,91 @@ if skeleton { p.log.Trace("Fetching skeleton headers", "count", MaxHeaderFetch, "from", from) go p.peer.RequestHeadersByNumber(from+uint64(MaxHeaderFetch)-1, MaxSkeletonSize, MaxHeaderFetch-1, false) } else { - p.log.Trace("Fetching full headers", "count", MaxHeaderFetch, "from", from) - go p.peer.RequestHeadersByNumber(from, MaxHeaderFetch, 0, false) + count := MaxHeaderFetch + skip := 0 + p.log.Trace("Fetching full headers", "count", count, "from", from) + go p.peer.RequestHeadersByNumber(from, MaxHeaderFetch, skip, false) } } - getNextPivot := func() { - pivoting = true + + mode := d.getMode() + getEpochHeaders := func(fromEpochBlock uint64) { + if mode != LightestSync { + panic("This method should be called only in LightestSync mode") + } + if fromEpochBlock%epoch != 0 { + panic(fmt.Sprintf( + "Logic error: getEpochHeaders received a request to fetch non-epoch block %d with epoch %d", + fromEpochBlock, epoch)) + } + request = time.Now()   ttl = d.peers.rates.TargetTimeout() timeout.Reset(ttl)   - d.pivotLock.RLock() - pivot := d.pivotHeader.Number.Uint64() - d.pivotLock.RUnlock() + // if epoch is 100 and we fetch from=1000 and skip=100 then we will get + // 1000, 1101, 1202, 1303 ... + // So, skip has to be epoch - 1 to get the right set of blocks. + skip := int(epoch - 1) + count := MaxEpochHeaderFetch + log.Trace("getEpochHeaders", "from", fromEpochBlock, "count", count, "skip", skip) + p.log.Trace("Fetching full headers", "count", count, "from", fromEpochBlock) + go p.peer.RequestHeadersByNumber(fromEpochBlock, count, skip, false) + }   - p.log.Trace("Fetching next pivot header", "number", pivot+uint64(fsMinFullBlocks)) - go p.peer.RequestHeadersByNumber(pivot+uint64(fsMinFullBlocks), 2, fsMinFullBlocks-9, false) // move +64 when it's 2x64-8 deep + // Returns true if a header(s) fetch request was made, false if the syncing is finished. + getEpochOrNormalHeaders := func(from uint64) bool { + // Download the epoch headers including and beyond the current head. + nextEpochBlock := (from-1)/epoch*epoch + epoch + // If we're still not synced up to the latest epoch, sync only epoch headers. + // Otherwise, sync block headers as we would normally in light sync. + log.Trace("Getting headers in lightest sync mode", "from", from, "height", height, "nextEpochBlock", nextEpochBlock, "epoch", epoch) + if nextEpochBlock < height { + getEpochHeaders(nextEpochBlock) + return true + } else if from <= height { + getHeaders(height) + return true + } else { + // During repeated invocations, "from" can be more than height since the blocks could have + // created after this method was invoked and in that case, the from which is one beyond the + // last fetched header number can be more than the height. + // If we have already fetched a block header >= height block header then we declare that the sync + // is finished and stop. + return false + } } + // TODO(ponti): Re add the "moving" pivot, after changing the way we calculate the uptimeScore + // getNextPivot := func() { + // pivoting = true + // request = time.Now() + + // ttl = d.requestTTL() + // timeout.Reset(ttl) + + // d.pivotLock.RLock() + // pivot := d.pivotHeader.Number.Uint64() + // d.pivotLock.RUnlock() + + // p.log.Trace("Fetching next pivot header", "number", pivot+fsMinFullBlocks) + // go p.peer.RequestHeadersByNumber(pivot+fsMinFullBlocks, 2, int(fsMinFullBlocks-9), false) // move +64 when it's 2x64-8 deep + // } // Start pulling the header chain skeleton until all is done ancestor := from - getHeaders(from)   - mode := d.getMode() + if mode == LightestSync { + if epoch == 0 { + panic("Epoch cannot be 0 in IBFT + LightestSync") + } + // Don't fetch skeleton, only fetch the headers. + skeleton = false + getEpochOrNormalHeaders(from) + } else { + log.Trace("getHeaders#initialHeaderDownload", "from", from) + getHeaders(from) + } + for { select { case <-d.cancelCh: @@ -1074,11 +1182,11 @@ if packet.Items() == 2 { // Retrieve the headers and do some sanity checks, just in case headers := packet.(*headerPack).headers   - if have, want := headers[0].Number.Uint64(), pivot+uint64(fsMinFullBlocks); have != want { + if have, want := headers[0].Number.Uint64(), pivot+fsMinFullBlocks; have != want { log.Warn("Peer sent invalid next pivot", "have", have, "want", want) return fmt.Errorf("%w: next pivot number %d != requested %d", errInvalidChain, have, want) } - if have, want := headers[1].Number.Uint64(), pivot+2*uint64(fsMinFullBlocks)-8; have != want { + if have, want := headers[1].Number.Uint64(), pivot+2*fsMinFullBlocks-8; have != want { log.Warn("Peer sent invalid pivot confirmer", "have", have, "want", want) return fmt.Errorf("%w: next pivot confirmer number %d != requested %d", errInvalidChain, have, want) } @@ -1101,6 +1209,7 @@ } // If the skeleton's finished, pull any remaining head headers directly from the origin if skeleton && packet.Items() == 0 { skeleton = false + log.Trace("getHeaders, skeleton finished, download remaining headers") getHeaders(from) continue } @@ -1126,6 +1235,7 @@ case <-d.cancelCh: return errCanceled } } + // Received headers headers := packet.(*headerPack).headers   // If we received a skeleton batch, resolve internals concurrently @@ -1141,30 +1251,34 @@ } else { // If we're closing in on the chain head, but haven't yet reached it, delay // the last few headers so mini reorgs on the head don't cause invalid hash // chain errors. - if n := len(headers); n > 0 { - // Retrieve the current head we're at - var head uint64 - if mode == LightSync { - head = d.lightchain.CurrentHeader().Number.Uint64() - } else { - head = d.blockchain.CurrentFastBlock().NumberU64() - if full := d.blockchain.CurrentBlock().NumberU64(); head < full { - head = full + + // Don't delay last few headers in IBFT since we are not expecting chain reorgs in IBFT + if !d.ibftConsensus { + if n := len(headers); n > 0 { + // Retrieve the current head we're at + var head uint64 + if mode == LightSync { + head = d.lightchain.CurrentHeader().Number.Uint64() + } else { + head = d.blockchain.CurrentFastBlock().NumberU64() + if full := d.blockchain.CurrentBlock().NumberU64(); head < full { + head = full + } } - } - // If the head is below the common ancestor, we're actually deduplicating - // already existing chain segments, so use the ancestor as the fake head. - // Otherwise we might end up delaying header deliveries pointlessly. - if head < ancestor { - head = ancestor - } - // If the head is way older than this batch, delay the last few headers - if head+uint64(reorgProtThreshold) < headers[n-1].Number.Uint64() { - delay := reorgProtHeaderDelay - if delay > n { - delay = n + // If the head is below the common ancestor, we're actually deduplicating + // already existing chain segments, so use the ancestor as the fake head. + // Otherwise we might end up delaying header deliveries pointlessly. + if head < ancestor { + head = ancestor + } + // If the head is way older than this batch, delay the last few headers + if head+uint64(reorgProtThreshold) < headers[n-1].Number.Uint64() { + delay := reorgProtHeaderDelay + if delay > n { + delay = n + } + headers = headers[:n-delay] } - headers = headers[:n-delay] } } } @@ -1176,21 +1290,43 @@ case d.headerProcCh <- headers: case <-d.cancelCh: return errCanceled } - from += uint64(len(headers)) - - // If we're still skeleton filling fast sync, check pivot staleness - // before continuing to the next skeleton filling - if skeleton && pivot > 0 { - getNextPivot() + // In all other sync modes, we fetch the block immediately after the current block. + // In the lightest sync mode, increment the value by epoch instead. + if mode == LightestSync { + lastFetchedHeaderNumber := headers[len(headers)-1].Number.Uint64() + moreHeaderFetchesPending := getEpochOrNormalHeaders(lastFetchedHeaderNumber + 1) + if !moreHeaderFetchesPending { + p.log.Debug("No more headers available") + select { + case d.headerProcCh <- nil: + return nil + case <-d.cancelCh: + return errCanceled + } + } } else { + from += uint64(len(headers)) + log.Trace("getHeaders#downloadMoreHeaders", "from", from) + // If we're still skeleton filling fast sync, check pivot staleness + // before continuing to the next skeleton filling + + // TODO(ponti): Re add the "moving" pivot, after changing the way we calculate the uptimeScore + // if skeleton && pivot > 0 { + // getNextPivot() + // } else { getHeaders(from) + // } } } else { // No headers delivered, or all of them being delayed, sleep a bit and retry p.log.Trace("All headers delayed, waiting") select { case <-time.After(fsHeaderContCheck): - getHeaders(from) + if mode == LightestSync { + getEpochOrNormalHeaders(from) + } else { + getHeaders(from) + } continue case <-d.cancelCh: return errCanceled @@ -1205,7 +1341,7 @@ p.log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", p.id) break } // Header retrieval timed out, consider the peer bad and drop - p.log.Debug("Header request timed out", "elapsed", ttl) + p.log.Warn("Header request timed out, dropping peer", "elapsed", ttl) headerTimeoutMeter.Mark(1) d.dropPeer(p.id)   @@ -1272,7 +1408,7 @@ var ( deliver = func(packet dataPack) (int, error) { pack := packet.(*bodyPack) - return d.queue.DeliverBodies(pack.peerID, pack.transactions, pack.uncles) + return d.queue.DeliverBodies(pack.peerID, pack.transactions, pack.randomness, pack.epochSnarkData) } expire = func() map[string]int { return d.queue.ExpireBodies(d.peers.rates.TargetTimeout()) } fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchBodies(req) } @@ -1425,7 +1561,7 @@ if fails > 2 { peer.log.Trace("Data delivery timed out", "type", kind) setIdle(peer, 0, time.Now()) } else { - peer.log.Debug("Stalling delivery, dropping", "type", kind) + peer.log.Warn("Stalling delivery, dropping", "type", kind)   if d.dropPeer == nil { // The dropPeer method is nil when `--copydb` is used for a local copy. @@ -1523,7 +1659,7 @@ ) defer func() { if rollback > 0 { lastHeader, lastFastBlock, lastBlock := d.lightchain.CurrentHeader().Number, common.Big0, common.Big0 - if mode != LightSync { + if mode.SyncFullBlockChain() { lastFastBlock = d.blockchain.CurrentFastBlock().Number() lastBlock = d.blockchain.CurrentBlock().Number() } @@ -1532,7 +1668,7 @@ // We're already unwinding the stack, only print the error to make it more visible log.Error("Failed to roll back chain segment", "head", rollback-1, "err", err) } curFastBlock, curBlock := common.Big0, common.Big0 - if mode != LightSync { + if mode.SyncFullBlockChain() { curFastBlock = d.blockchain.CurrentFastBlock().Number() curBlock = d.blockchain.CurrentBlock().Number() } @@ -1573,9 +1709,10 @@ // L: Import of block 11 finishes // L: Sync begins, and finds common ancestor at 11 // L: Request new headers up from 11 (R's TD was higher, it must have something) // R: Nothing to give - if mode != LightSync { + if mode.SyncFullBlockChain() { head := d.blockchain.CurrentBlock() if !gotHeaders && td.Cmp(d.blockchain.GetTd(head.Hash(), head.NumberU64())) > 0 { + rollbackErr = errStallingPeer return errStallingPeer } } @@ -1589,6 +1726,7 @@ // peer gave us something useful, we're already happy/progressed (above check). if mode == FastSync || mode == LightSync { head := d.lightchain.CurrentHeader() if td.Cmp(d.lightchain.GetTd(head.Hash(), head.Number.Uint64())) > 0 { + rollbackErr = errStallingPeer return errStallingPeer } } @@ -1614,7 +1752,7 @@ } chunk := headers[:limit]   // In case of header only syncing, validate the chunk immediately - if mode == FastSync || mode == LightSync { + if mode == FastSync || !mode.SyncFullBlockChain() { // mode != FullSync ? // If we're importing pure headers, verify based on their recentness var pivot uint64   @@ -1628,7 +1766,7 @@ frequency := fsHeaderCheckFrequency if chunk[len(chunk)-1].Number.Uint64()+uint64(fsHeaderForceVerify) > pivot { frequency = 1 } - if n, err := d.lightchain.InsertHeaderChain(chunk, frequency); err != nil { + if n, err := d.lightchain.InsertHeaderChain(chunk, frequency, mode.SyncFullHeaderChain()); err != nil { rollbackErr = err   // If some headers were inserted, track them as uncertain @@ -1649,7 +1787,7 @@ } } } // Unless we're doing light chains, schedule the headers for associated content retrieval - if mode == FullSync || mode == FastSync { + if mode.SyncFullBlockChain() { // If we've reached the allowed number of pending headers, stall a bit for d.queue.PendingBlocks() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders { select { @@ -1662,6 +1800,7 @@ } // Otherwise insert the headers for content retrieval inserts := d.queue.Schedule(chunk, origin) if len(inserts) != len(chunk) { + log.Debug("Stale headers") rollbackErr = fmt.Errorf("stale headers: len inserts %v len(chunk) %v", len(inserts), len(chunk)) return fmt.Errorf("%w: stale headers", errBadPeer) } @@ -1721,7 +1860,7 @@ "lastnum", last.Number, "lasthash", last.Hash(), ) blocks := make([]*types.Block, len(results)) for i, result := range results { - blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles) + blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Randomness, result.EpochSnarkData) } if index, err := d.blockchain.InsertChain(blocks); err != nil { if index < len(results) { @@ -1738,6 +1877,42 @@ } return nil }   +func max(a uint64, b uint64) uint64 { + if a < b { + return b + } + return a +} + +func computePivot(height uint64, epochSize uint64) uint64 { + if height <= fsMinFullBlocks { + return 0 + } + target := height - fsMinFullBlocks + targetEpoch := istanbul.GetEpochNumber(target, epochSize) + + // if target is on first epoch start on genesis + if targetEpoch <= 1 { + return 0 + } + + // else start on first block of the epoch + pivot, _ := istanbul.GetEpochFirstBlockNumber(targetEpoch, epochSize) + return pivot + +} + +func (d *Downloader) calcPivot(height uint64) uint64 { + // If epoch is not set (not IBFT) use old logic + if d.epoch == 0 { + if fsMinFullBlocks > height { + return 0 + } + return height - fsMinFullBlocks + } + return computePivot(height, d.epoch) +} + // processFastSyncContent takes fetch results from the queue and writes them to the // database. It also controls the synchronisation of state nodes of the pivot block. func (d *Downloader) processFastSyncContent() error { @@ -1760,7 +1935,6 @@ d.queue.Close() // wake up Results } } go closeOnErr(sync) - // To cater for moving pivot points, track the pivot block and subsequently // accumulated download results separately. var ( @@ -1813,17 +1987,17 @@ // // Note, we have `reorgProtHeaderDelay` number of blocks withheld, Those // need to be taken into account, otherwise we're detecting the pivot move // late and will drop peers due to unavailable state!!! - if height := latest.Number.Uint64(); height >= pivot.Number.Uint64()+2*uint64(fsMinFullBlocks)-uint64(reorgProtHeaderDelay) { - log.Warn("Pivot became stale, moving", "old", pivot.Number.Uint64(), "new", height-uint64(fsMinFullBlocks)+uint64(reorgProtHeaderDelay)) - pivot = results[len(results)-1-fsMinFullBlocks+reorgProtHeaderDelay].Header // must exist as lower old pivot is uncommitted + if height := latest.Number.Uint64(); height > pivot.Number.Uint64()+2*max(d.epoch, fsMinFullBlocks)-uint64(reorgProtHeaderDelay) { + newPivot := d.calcPivot(height) + log.Warn("Pivot became stale, moving", "old", pivot, "new", newPivot)   + pivot = d.lightchain.GetHeaderByNumber(newPivot) d.pivotLock.Lock() d.pivotHeader = pivot d.pivotLock.Unlock() - // Write out the pivot into the database so a rollback beyond it will // reenable fast sync - rawdb.WriteLastPivotNumber(d.stateDB, pivot.Number.Uint64()) + rawdb.WriteLastPivotNumber(d.stateDB, newPivot) } } P, beforeP, afterP := splitAroundPivot(pivot.Number.Uint64(), results) @@ -1908,7 +2082,7 @@ ) blocks := make([]*types.Block, len(results)) receipts := make([]types.Receipts, len(results)) for i, result := range results { - blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles) + blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Randomness, result.EpochSnarkData) receipts[i] = result.Receipts } if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil { @@ -1919,7 +2093,7 @@ return nil }   func (d *Downloader) commitPivotBlock(result *fetchResult) error { - block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles) + block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Randomness, result.EpochSnarkData) log.Debug("Committing fast sync pivot as new head", "number", block.Number(), "hash", block.Hash())   // Commit the pivot block as the new head, will require full sync from here on @@ -1949,8 +2123,8 @@ return d.deliver(d.headerCh, &headerPack{id, headers}, headerInMeter, headerDropMeter) }   // DeliverBodies injects a new batch of block bodies received from a remote node. -func (d *Downloader) DeliverBodies(id string, transactions [][]*types.Transaction, uncles [][]*types.Header) error { - return d.deliver(d.bodyCh, &bodyPack{id, transactions, uncles}, bodyInMeter, bodyDropMeter) +func (d *Downloader) DeliverBodies(id string, transactions [][]*types.Transaction, randomness []*types.Randomness, epochSnarkData []*types.EpochSnarkData) (err error) { + return d.deliver(d.bodyCh, &bodyPack{id, transactions, randomness, epochSnarkData}, bodyInMeter, bodyDropMeter) }   // DeliverReceipts injects a new batch of receipts received from a remote node.
diff --git go-ethereum/les/downloader/types.go celo/les/downloader/types.go index ab653d7416b68ae5967cbdbb7460321341e8639d..ae97debcc82d79504699d39aad9629b44b2eb1ad 100644 --- go-ethereum/les/downloader/types.go +++ celo/les/downloader/types.go @@ -46,17 +46,15 @@ // bodyPack is a batch of block bodies returned by a peer. type bodyPack struct { peerID string transactions [][]*types.Transaction - uncles [][]*types.Header + randomness []*types.Randomness + epochSnarkData []*types.EpochSnarkData }   func (p *bodyPack) PeerId() string { return p.peerID } func (p *bodyPack) Items() int { - if len(p.transactions) <= len(p.uncles) { - return len(p.transactions) - } - return len(p.uncles) + return len(p.transactions) } -func (p *bodyPack) Stats() string { return fmt.Sprintf("%d:%d", len(p.transactions), len(p.uncles)) } +func (p *bodyPack) Stats() string { return fmt.Sprintf("%d", len(p.transactions)) }   // receiptPack is a batch of receipts returned by a peer. type receiptPack struct {
diff --git go-ethereum/les/downloader/queue_test.go celo/les/downloader/queue_test.go index 321872d7442d232d797a513ab8c77dbbf9ed1ff5..9eeee4e6716052079e7ab7eaec4a475b497cb22b 100644 --- go-ethereum/les/downloader/queue_test.go +++ celo/les/downloader/queue_test.go @@ -25,7 +25,7 @@ "testing" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -43,12 +43,12 @@ // the returned hash chain is ordered head->parent. In addition, every 3rd block // contains a transaction and every 5th an uncle to allow testing correct block // reassembly. func makeChain(n int, seed byte, parent *types.Block, empty bool) ([]*types.Block, []types.Receipts) { - blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, func(i int, block *core.BlockGen) { + blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, mockEngine.NewFaker(), testdb, n, func(i int, block *core.BlockGen) { block.SetCoinbase(common.Address{seed}) // Add one tx to every secondblock if !empty && i%2 == 0 { signer := types.MakeSigner(params.TestChainConfig, block.Number()) - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil, nil, nil, nil), signer, testKey) if err != nil { panic(err) } @@ -131,7 +131,7 @@ // queue size is only 10, so throttling should occur t.Fatal("should throttle") } // But we should still get the first things to fetch - if got, exp := len(fetchReq.Headers), 5; got != exp { + if got, exp := len(fetchReq.Headers), 10; got != exp { t.Fatalf("expected %d requests, got %d", exp, got) } if got, exp := fetchReq.Headers[0].Number.Uint64(), uint64(1); got != exp { @@ -223,9 +223,9 @@ // Reserve blocks peer := dummyPeer("peer-1") fetchReq, _, _ := q.ReserveBodies(peer, 50)   - // there should be nothing to fetch, blocks are empty - if fetchReq != nil { - t.Fatal("there should be no body fetch tasks remaining") + // blocks are empty, but must be fetched the bodies so that the random beacon can be updated correctly + if fetchReq == nil { + t.Fatal("there should be body fetch tasks remaining") }   } @@ -250,16 +250,17 @@ } if q.receiptTaskQueue.Size() != 0 { t.Errorf("expected receipt task queue to be %d, got %d", 0, q.receiptTaskQueue.Size()) } - if got, exp := q.resultCache.countCompleted(), 10; got != exp { + if got, exp := q.resultCache.countCompleted(), 0; got != exp { t.Errorf("wrong processable count, got %d, exp %d", got, exp) } }   -// XTestDelivery does some more extensive testing of events that happen, +// TestDelivery does some more extensive testing of events that happen, // blocks that become known and peers that make reservations and deliveries. // disabled since it's not really a unit-test, but can be executed to test // some more advanced scenarios -func XTestDelivery(t *testing.T) { +func TestDelivery(t *testing.T) { + t.Skip("not really a unit-test, but can be executed to test some more advanced scenarios") // the outside network, holding blocks blo, rec := makeChain(128, 0, genesis, false) world := newNetwork() @@ -311,16 +312,17 @@ for { peer := dummyPeer(fmt.Sprintf("peer-%d", i)) f, _, _ := q.ReserveBodies(peer, rand.Intn(30)) if f != nil { - var emptyList []*types.Header var txs [][]*types.Transaction - var uncles [][]*types.Header + var randomnessList []*types.Randomness + var epochSnarkDataList []*types.EpochSnarkData numToSkip := rand.Intn(len(f.Headers)) for _, hdr := range f.Headers[0 : len(f.Headers)-numToSkip] { txs = append(txs, world.getTransactions(hdr.Number.Uint64())) - uncles = append(uncles, emptyList) + randomnessList = append(randomnessList, &types.Randomness{}) + epochSnarkDataList = append(epochSnarkDataList, &types.EpochSnarkData{}) } time.Sleep(100 * time.Millisecond) - _, err := q.DeliverBodies(peer.id, txs, uncles) + _, err := q.DeliverBodies(peer.id, txs, randomnessList, epochSnarkDataList) if err != nil { fmt.Printf("delivered %d bodies %v\n", len(txs), err) }
diff --git go-ethereum/les/flowcontrol/manager.go celo/les/flowcontrol/manager.go index fe19a11ac4946801a2514e521b91c1f187a7f0a9..85cc470c0cc2333f63ed2db67137d3b26da7b589 100644 --- go-ethereum/les/flowcontrol/manager.go +++ celo/les/flowcontrol/manager.go @@ -60,7 +60,6 @@ // to be returned with each reply. type ClientManager struct { clock mclock.Clock lock sync.Mutex - enabledCh chan struct{} stop chan chan struct{}   curve PieceWiseLinear
diff --git go-ethereum/light/odr.go celo/light/odr.go index 1a0a8abe8b68b49a3fa22aa0fc17fa4b96d7f36f..ec679869519e5653f27fccd1cced11a60f656436 100644 --- go-ethereum/light/odr.go +++ celo/light/odr.go @@ -118,6 +118,30 @@ func (req *BlockRequest) StoreResult(db ethdb.Database) { rawdb.WriteBodyRLP(db, req.Hash, req.Number, req.Rlp) }   +type blockHashOrNumber struct { + Hash common.Hash + Number *uint64 +} + +type HeaderRequest struct { + OdrRequest + Origin blockHashOrNumber + Header *types.Header +} + +// StoreResult handles storing the canonical hash if `InsertHeaderChain` has not already +// This occurs if the total difficulty of the requested header is less than the current known TD. +func (req *HeaderRequest) StoreResult(db ethdb.Database) { + if req.Header == nil { + // A nil header is a valid response when request the header by hash, as it indicates + // no known block with this hash. In this case, there is nothing for us to do here. + return + } + if rawdb.ReadCanonicalHash(db, req.Header.Number.Uint64()) == (common.Hash{}) { + rawdb.WriteCanonicalHash(db, req.Header.Hash(), req.Header.Number.Uint64()) + } +} + // ReceiptsRequest is the ODR request type for retrieving receipts. type ReceiptsRequest struct { Untrusted bool // Indicator whether the result retrieved is trusted or not @@ -147,8 +171,9 @@ // StoreResult stores the retrieved data in local database func (req *ChtRequest) StoreResult(db ethdb.Database) { hash, num := req.Header.Hash(), req.Header.Number.Uint64() + rawdb.WriteHeader(db, req.Header) - rawdb.WriteTd(db, hash, num, req.Td) + rawdb.WriteTd(db, hash, num, big.NewInt(int64(req.BlockNum+1))) rawdb.WriteCanonicalHash(db, hash, num) }
diff --git go-ethereum/light/txpool.go celo/light/txpool.go index 2825b86af1d3d1e3c41322b7f7ea6517c54e1c60..d301cc35bc732d6a1517e0662422b6ed7931d202 100644 --- go-ethereum/light/txpool.go +++ celo/light/txpool.go @@ -18,12 +18,14 @@ package light   import ( "context" + "errors" "fmt" "math/big" "sync" "time"   "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/blockchain_parameters" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -67,8 +69,9 @@ pending map[common.Hash]*types.Transaction // pending transactions by tx hash mined map[common.Hash][]*types.Transaction // mined transactions by block hash clearIdx uint64 // earliest block nr that can contain mined tx info   - istanbul bool // Fork indicator whether we are in the istanbul stage. - eip2718 bool // Fork indicator whether we are in the eip2718 stage. + istanbul bool // Fork indicator whether we are in the istanbul stage + donut bool // Fork indicator whether Donut has been activated + espresso bool // Fork indicator whether Espresso has been activated }   // TxRelayBackend provides an interface to the mechanism that forwards transacions @@ -84,6 +87,7 @@ type TxRelayBackend interface { Send(txs types.Transactions) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) Discard(hashes []common.Hash) + CanRelayTransaction(txs *types.Transaction) bool }   // NewTxPool creates a new light transaction pool @@ -222,6 +226,10 @@ // possible to continue checking the missing blocks at the next chain head event func (pool *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) { txc := make(txStateChanges) oldh := pool.chain.GetHeaderByHash(pool.head) + if oldh == nil { + pool.head = newHeader.Hash() + return nil, errors.New("Old header lost") + } newh := newHeader // find common ancestor, create list of rolled back and new block hashes var oldHashes, newHashes []common.Hash @@ -307,14 +315,18 @@ ctx, cancel := context.WithTimeout(context.Background(), blockCheckTimeout) defer cancel()   - txc, _ := pool.reorgOnNewHead(ctx, head) + txc, err := pool.reorgOnNewHead(ctx, head) + if err != nil { + log.Warn("Cannot reorg", "err", err) + } m, r := txc.getLists() pool.relay.NewHead(pool.head, m, r)   // Update fork indicator by next pending block number next := new(big.Int).Add(head.Number, big.NewInt(1)) pool.istanbul = pool.config.IsIstanbul(next) - pool.eip2718 = pool.config.IsBerlin(next) + pool.donut = pool.config.IsDonut(next) + pool.espresso = pool.config.IsEspresso(next) }   // Stop stops the light transaction pool @@ -342,7 +354,7 @@ pending = len(pool.pending) return }   -// validateTx checks whether a transaction is valid according to the consensus rules. +// validateTx checks whether a transaction is valid according to the consensus rules and will be broadcast. func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error { // Validate sender var ( @@ -350,6 +362,16 @@ from common.Address err error )   + if pool.donut && !pool.espresso && !tx.Protected() { + return core.ErrUnprotectedTransaction + } + if tx.EthCompatible() && !pool.donut { + return core.ErrEthCompatibleTransactionsNotSupported + } + if err := tx.CheckEthCompatibility(); err != nil { + return err + } + // Validate the transaction sender and it's sig. Throw // if the from fields is invalid. if from, err = types.Sender(pool.signer, tx); err != nil { @@ -359,13 +381,6 @@ // Last but not least check for nonce errors currentState := pool.currentState(ctx) if n := currentState.GetNonce(from); n > tx.Nonce() { return core.ErrNonceTooLow - } - - // Check the transaction doesn't exceed the current - // block limit gas. - header := pool.chain.GetHeaderByHash(pool.head) - if header.GasLimit < tx.Gas() { - return core.ErrGasLimit }   // Transactions can't be negative. This may never happen @@ -375,20 +390,30 @@ if tx.Value().Sign() < 0 { return core.ErrNegativeValue }   + vmRunner := pool.chain.NewEVMRunner(pool.chain.CurrentHeader(), currentState) // Transactor should have enough funds to cover the costs - // cost == V + GP * GL - if b := currentState.GetBalance(from); b.Cmp(tx.Cost()) < 0 { - return core.ErrInsufficientFunds + err = core.ValidateTransactorBalanceCoversTx(tx, from, currentState, vmRunner, pool.espresso) + if err != nil { + return err }   + gasForAlternativeCurrency := uint64(0) + // If the fee currency is nil, do not retrieve the intrinsic gas adjustment from the chain state, as it will not be used. + if tx.FeeCurrency() != nil { + gasForAlternativeCurrency = blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(vmRunner) + } // Should supply enough intrinsic gas - gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul) + gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, tx.FeeCurrency(), gasForAlternativeCurrency, pool.istanbul) if err != nil { return err } if tx.Gas() < gas { return core.ErrIntrinsicGas } + + if !pool.relay.CanRelayTransaction(tx) { + return ErrNoPeers + } return currentState.Error() }   @@ -398,7 +423,7 @@ func (pool *TxPool) add(ctx context.Context, tx *types.Transaction) error { hash := tx.Hash()   if pool.pending[hash] != nil { - return fmt.Errorf("Known transaction (%x)", hash[:4]) + return fmt.Errorf("known transaction (%x)", hash[:4]) } err := pool.validateTx(ctx, tx) if err != nil { @@ -439,7 +464,7 @@ if err := pool.add(ctx, tx); err != nil { return err } - //fmt.Println("Send", tx.Hash()) + pool.relay.Send(types.Transactions{tx})   pool.chainDb.Put(tx.Hash().Bytes(), data)
diff --git go-ethereum/light/postprocess.go celo/light/postprocess.go index 1e5bccf4d242f102417b8c4699ef2c467a222c68..8ce6e558849645a8f8f77bb1bfc57eec4ba6157a 100644 --- go-ethereum/light/postprocess.go +++ celo/light/postprocess.go @@ -141,7 +141,7 @@ trie *trie.Trie }   // NewChtIndexer creates a Cht chain indexer -func NewChtIndexer(db ethdb.Database, odr OdrBackend, size, confirms uint64, disablePruning bool) *core.ChainIndexer { +func NewChtIndexer(db ethdb.Database, odr OdrBackend, size, confirms uint64, disablePruning bool, fullChainAvailable bool) *core.ChainIndexer { trieTable := rawdb.NewTable(db, ChtTablePrefix) backend := &ChtIndexerBackend{ diskdb: db, @@ -152,7 +152,7 @@ trieset: mapset.NewSet(), sectionSize: size, disablePruning: disablePruning, } - return core.NewChainIndexer(db, rawdb.NewTable(db, "chtIndexV2-"), backend, size, confirms, time.Millisecond*100, "cht") + return core.NewChainIndexer(db, rawdb.NewTable(db, "chtIndex-"), backend, size, confirms, time.Millisecond*100, "cht", fullChainAvailable) }   // fetchMissingNodes tries to retrieve the last entry of the latest trusted CHT from the @@ -208,6 +208,7 @@ td := rawdb.ReadTd(c.diskdb, hash, num) if td == nil { panic(nil) } + var encNumber [8]byte binary.BigEndian.PutUint64(encNumber[:], num) data, _ := rlp.EncodeToBytes(ChtNode{hash, td}) @@ -334,7 +335,7 @@ sectionHeads []common.Hash }   // NewBloomTrieIndexer creates a BloomTrie chain indexer -func NewBloomTrieIndexer(db ethdb.Database, odr OdrBackend, parentSize, size uint64, disablePruning bool) *core.ChainIndexer { +func NewBloomTrieIndexer(db ethdb.Database, odr OdrBackend, parentSize, size uint64, disablePruning bool, fullChainAvailable bool) *core.ChainIndexer { trieTable := rawdb.NewTable(db, BloomTrieTablePrefix) backend := &BloomTrieIndexerBackend{ diskdb: db, @@ -348,7 +349,7 @@ disablePruning: disablePruning, } backend.bloomTrieRatio = size / parentSize backend.sectionHeads = make([]common.Hash, backend.bloomTrieRatio) - return core.NewChainIndexer(db, rawdb.NewTable(db, "bltIndex-"), backend, size, 0, time.Millisecond*100, "bloomtrie") + return core.NewChainIndexer(db, rawdb.NewTable(db, "bltIndex-"), backend, size, 0, time.Millisecond*100, "bloomtrie", fullChainAvailable) }   // fetchMissingNodes tries to retrieve the last entries of the latest trusted bloom trie from the
diff --git go-ethereum/light/lightchain.go celo/light/lightchain.go index a0d9386f3e221a944387c0213a1c601855aeba4b..70ed2b49180d57b5611bc6156f617fff0882466e 100644 --- go-ethereum/light/lightchain.go +++ celo/light/lightchain.go @@ -21,6 +21,7 @@ import ( "context" "errors" + "fmt" "math/big" "sync" "sync/atomic" @@ -32,6 +33,8 @@ "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/vmcontext" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -175,14 +178,8 @@ // head will be deleted and the new one set. func (lc *LightChain) SetHead(head uint64) error { lc.chainmu.Lock() defer lc.chainmu.Unlock() - lc.hc.SetHead(head, nil, nil) return lc.loadLastState() -} - -// GasLimit returns the gas limit of the current HEAD block. -func (lc *LightChain) GasLimit() uint64 { - return lc.hc.CurrentHeader().GasLimit }   // Reset purges the entire blockchain, restoring it to its genesis state. @@ -201,7 +198,7 @@ defer lc.chainmu.Unlock()   // Prepare the genesis block and reinitialise the chain batch := lc.chainDb.NewBatch() - rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()) + rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.TotalDifficulty()) rawdb.WriteBlock(batch, genesis) rawdb.WriteHeadHeaderHash(batch, genesis.Hash()) if err := batch.Write(); err != nil { @@ -222,11 +219,16 @@ func (lc *LightChain) Genesis() *types.Block { return lc.genesisBlock }   +// State returns a new mutable state based on the current HEAD block. +func (bc *LightChain) State() (*state.StateDB, error) { + return NewState(context.Background(), bc.CurrentHeader(), bc.odr), nil // TODO: Any issues with using context.Background() here? +} + func (lc *LightChain) StateCache() state.Database { panic("not implemented") }   -// GetBody retrieves a block body (transactions and uncles) from the database +// GetBody retrieves a block body (transactions) from the database // or ODR service by hash, caching it if found. func (lc *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) { // Short circuit if the body's already in the cache, retrieve otherwise @@ -295,7 +297,13 @@ // caching it if found. func (lc *LightChain) GetBlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { number := lc.hc.GetBlockNumber(hash) if number == nil { - return nil, errors.New("unknown block") + header, err := GetHeaderByHash(ctx, lc.odr, hash) + // Header may be nil, indicating the hash doesn't match any known blocks, + // which is a valid response to the ODR request. + if err != nil || header == nil { + return nil, errors.New("unknown block") + } + return lc.GetBlock(ctx, hash, header.Number.Uint64()) } return lc.GetBlock(ctx, hash, *number) } @@ -331,7 +339,8 @@ }   // Rollback is designed to remove a chain of links from the database that aren't // certain enough to be valid. -func (lc *LightChain) Rollback(chain []common.Hash) { +func (lc *LightChain) Rollback(chain []common.Hash, fullHeaderChainAvailable bool) { + log.Warn(fmt.Sprintf("Rollback %v", chain)) lc.chainmu.Lock() defer lc.chainmu.Unlock()   @@ -344,8 +353,15 @@ // In theory we should update all in-memory markers in the // last step, however the direction of rollback is from high // to low, so it's safe the update in-memory markers directly. if head := lc.hc.CurrentHeader(); head.Hash() == hash { - rawdb.WriteHeadHeaderHash(batch, head.ParentHash) - lc.hc.SetCurrentHeader(lc.GetHeader(head.ParentHash, head.Number.Uint64()-1)) + parentHeader := lc.GetHeader(head.ParentHash, head.Number.Uint64()-1) + // In all sync modes except LightestSync, a complete header chain is available. + // Maintain the old behavior in those cases. + if parentHeader != nil { + rawdb.WriteHeadHeaderHash(batch, head.ParentHash) + lc.hc.SetCurrentHeader(parentHeader) + } else { + log.Warn(fmt.Sprintf("Cannot rollback current head %v, parent block is missing", head)) + } } } if err := batch.Write(); err != nil { @@ -380,12 +396,13 @@ // because nonces can be verified sparsely, not needing to check each. // // In the case of a light chain, InsertHeaderChain also creates and posts light // chain events when necessary. -func (lc *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) { +func (lc *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int, contiguousHeaders bool) (int, error) { if atomic.LoadInt32(&lc.disableCheckFreq) == 1 { checkFreq = 0 } start := time.Now() - if i, err := lc.hc.ValidateHeaderChain(chain, checkFreq); err != nil { + if i, err := lc.hc.ValidateHeaderChain(chain, checkFreq, contiguousHeaders); err != nil { + log.Error(fmt.Sprintf("Failed to validate the header chain at %d due to \"%v\"", i, err)) return i, err }   @@ -500,22 +517,32 @@ } return GetHeaderByNumber(ctx, lc.odr, number) }   +func (lc *LightChain) GetVMConfig() *vm.Config { + return &vm.Config{} +} + +// NewEVMRunner creates the System's EVMRunner for given header & sttate +func (lc *LightChain) NewEVMRunner(header *types.Header, state vm.StateDB) vm.EVMRunner { + return vmcontext.NewEVMRunner(lc, header, state) +} + +// NewEVMRunnerForCurrentBlock creates the System's EVMRunner for current block & state +func (lc *LightChain) NewEVMRunnerForCurrentBlock() (vm.EVMRunner, error) { + header := lc.CurrentHeader() + state := NewState(context.Background(), header, lc.odr) + return vmcontext.NewEVMRunner(lc, header, state), nil +} + // Config retrieves the header chain's chain configuration. func (lc *LightChain) Config() *params.ChainConfig { return lc.hc.Config() }   // SyncCheckpoint fetches the checkpoint point block header according to // the checkpoint provided by the remote peer. -// -// Note if we are running the clique, fetches the last epoch snapshot header -// which covered by checkpoint. func (lc *LightChain) SyncCheckpoint(ctx context.Context, checkpoint *params.TrustedCheckpoint) bool { // Ensure the remote checkpoint head is ahead of us head := lc.CurrentHeader().Number.Uint64()   latest := (checkpoint.SectionIndex+1)*lc.indexerConfig.ChtSize - 1 - if clique := lc.hc.Config().Clique; clique != nil { - latest -= latest % clique.Epoch // epoch snapshot for clique - } if head >= latest { return true }
diff --git go-ethereum/light/odr_test.go celo/light/odr_test.go index b4b774b92226eeffb2f88247834f18aa2d709ceb..0d9ac1be40a327505ccb53622f427639bc2e7402 100644 --- go-ethereum/light/odr_test.go +++ celo/light/odr_test.go @@ -26,7 +26,8 @@ "time"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" + "github.com/ethereum/go-ethereum/contracts/testutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -60,6 +61,10 @@ sdb, ldb ethdb.Database disable bool }   +func (odr *testOdr) ChtIndexer() *core.ChainIndexer { + return nil +} + func (odr *testOdr) Database() ethdb.Database { return odr.ldb } @@ -76,6 +81,17 @@ number := rawdb.ReadHeaderNumber(odr.sdb, req.Hash) if number != nil { req.Rlp = rawdb.ReadBodyRLP(odr.sdb, req.Hash, *number) } + case *HeaderRequest: + if req.Origin.Hash != (common.Hash{}) { + number := rawdb.ReadHeaderNumber(odr.sdb, req.Origin.Hash) + req.Header = rawdb.ReadHeader(odr.sdb, req.Origin.Hash, *number) + } else { + hash := rawdb.ReadCanonicalHash(odr.sdb, *req.Origin.Number) + req.Header = rawdb.ReadHeader(odr.sdb, hash, *req.Origin.Number) + } + // Simulate `InsertHeaderChain` call that would be done in the real `odr` + rawdb.WriteHeader(odr.ldb, req.Header) + rawdb.WriteTd(odr.ldb, req.Header.Hash(), req.Header.Number.Uint64(), big.NewInt(int64(req.Header.Number.Uint64()+1))) case *ReceiptsRequest: number := rawdb.ReadHeaderNumber(odr.sdb, req.Hash) if number != nil { @@ -98,15 +114,48 @@ return odr.indexerConfig }   type odrTestFn func(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) +type odrTestFnNum func(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, origin blockHashOrNumber) ([]byte, error)   func TestOdrGetBlockLes2(t *testing.T) { testChainOdr(t, 1, odrGetBlock) } + +func TestOdrGetBlockLightest(t *testing.T) { testLightestChainOdr(t, 1, odrGetBlockHashOrNumber) }   func odrGetBlock(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { var block *types.Block + var err error if bc != nil { block = bc.GetBlockByHash(bhash) } else { - block, _ = lc.GetBlockByHash(ctx, bhash) + block, err = lc.GetBlockByHash(ctx, bhash) + if err != nil { + return nil, err + } + } + if block == nil { + return nil, nil + } + rlp, _ := rlp.EncodeToBytes(block) + return rlp, nil +} + +func odrGetBlockHashOrNumber(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, origin blockHashOrNumber) ([]byte, error) { + var block *types.Block + var err error + if bc != nil { + if origin.Hash != (common.Hash{}) { + block = bc.GetBlockByHash(origin.Hash) + } else { + block = bc.GetBlockByNumber(*origin.Number) + } + } else { + if origin.Hash != (common.Hash{}) { + block, err = lc.GetBlockByHash(ctx, origin.Hash) + } else { + block, err = lc.GetBlockByNumber(ctx, *origin.Number) + } + if err != nil { + return nil, err + } } if block == nil { return nil, nil @@ -171,7 +220,7 @@ func (callmsg) CheckNonce() bool { return false }   func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") - config := params.TestChainConfig + config := params.IstanbulTestChainConfig   var res []byte for i := 0; i < 3; i++ { @@ -194,12 +243,14 @@ }   // Perform read-only call. st.SetBalance(testBankAddress, math.MaxBig256) - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)} + msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), new(big.Int), new(big.Int), nil, nil, nil, data, nil, false, true)} txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(header, chain, nil) vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) - result, _ := core.ApplyMessage(vmenv, msg, gp) + + celoMock := testutil.NewCeloMock() + result, _ := core.ApplyMessage(vmenv, msg, gp, celoMock.Runner, nil) res = append(res, result.Return()...) if st.Error() != nil { return res, st.Error() @@ -213,17 +264,17 @@ signer := types.HomesteadSigner{} switch i { case 0: // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testBankKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, testBankKey) block.AddTx(tx) case 1: // In block 2, the test bank sends some more ether to account #1. // acc1Addr passes it on to account #2. // acc1Addr creates a test contract. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testBankKey) + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, testBankKey) nonce := block.TxNonce(acc1Addr) - tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, acc1Key) + tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, nil, nil, nil, nil, nil), signer, acc1Key) nonce++ - tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, block.BaseFee(), testContractCode), signer, acc1Key) + tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), nil, nil, nil, testContractCode), signer, acc1Key) testContractAddr = crypto.CreateAddress(acc1Addr, nonce) block.AddTx(tx1) block.AddTx(tx2) @@ -233,18 +284,7 @@ // Block 3 is empty but was mined by account #2. block.SetCoinbase(acc2Addr) block.SetExtra([]byte("yeehaw")) data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001") - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, block.BaseFee(), data), signer, testBankKey) - block.AddTx(tx) - case 3: - // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). - b2 := block.PrevBlock(1).Header() - b2.Extra = []byte("foo") - block.AddUncle(b2) - b3 := block.PrevBlock(2).Header() - b3.Extra = []byte("foo") - block.AddUncle(b3) - data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002") - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, block.BaseFee(), data), signer, testBankKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, nil, nil, nil, nil, data), signer, testBankKey) block.AddTx(tx) } } @@ -255,20 +295,19 @@ sdb = rawdb.NewMemoryDatabase() ldb = rawdb.NewMemoryDatabase() gspec = core.Genesis{ Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, - BaseFee: big.NewInt(params.InitialBaseFee), } genesis = gspec.MustCommit(sdb) ) gspec.MustCommit(ldb) // Assemble the test environment - blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil) - gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, 4, testChainGen) + blockchain, _ := core.NewBlockChain(sdb, nil, params.IstanbulTestChainConfig, mockEngine.NewFullFaker(), vm.Config{}, nil, nil) + gchain, _ := core.GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), sdb, 4, testChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { t.Fatal(err) }   odr := &testOdr{sdb: sdb, ldb: ldb, indexerConfig: TestClientIndexerConfig} - lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil) + lightchain, err := NewLightChain(odr, params.IstanbulTestChainConfig, mockEngine.NewFullFaker(), nil) if err != nil { t.Fatal(err) } @@ -276,7 +315,7 @@ headers := make([]*types.Header, len(gchain)) for i, block := range gchain { headers[i] = block.Header() } - if _, err := lightchain.InsertHeaderChain(headers, 1); err != nil { + if _, err := lightchain.InsertHeaderChain(headers, 1, true); err != nil { t.Fatal(err) }   @@ -319,3 +358,90 @@ t.Log("checking without ODR, should be cached") odr.disable = true test(len(gchain)) } + +func testLightestChainOdr(t *testing.T, protocol int, fn odrTestFnNum) { + var ( + sdb = rawdb.NewMemoryDatabase() + ldb = rawdb.NewMemoryDatabase() + gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} + genesis = gspec.MustCommit(sdb) + ) + gspec.MustCommit(ldb) + // Assemble the test environment + blockchain, _ := core.NewBlockChain(sdb, nil, params.IstanbulTestChainConfig, mockEngine.NewFullFaker(), vm.Config{}, nil, nil) + gchain, _ := core.GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), sdb, 4, testChainGen) + if _, err := blockchain.InsertChain(gchain); err != nil { + t.Fatal(err) + } + + odr := &testOdr{sdb: sdb, ldb: ldb, indexerConfig: TestClientIndexerConfig} + lightchain, err := NewLightChain(odr, params.IstanbulTestChainConfig, mockEngine.NewFullFaker(), nil) + if err != nil { + t.Fatal(err) + } + + test := func(expFail int, tryHash bool) { + for i := uint64(0); i <= blockchain.CurrentHeader().Number.Uint64(); i++ { + bhash := rawdb.ReadCanonicalHash(sdb, i) + var origin blockHashOrNumber + if tryHash { + origin = blockHashOrNumber{Hash: bhash} + } else { + origin = blockHashOrNumber{Number: &i} + } + b1, err := fn(NoOdr, sdb, blockchain, nil, origin) + if err != nil { + t.Fatalf("error in full-node test for block %d: %v", i, err) + } + + ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) + defer cancel() + + exp := i < uint64(expFail) + b2, err := fn(ctx, ldb, nil, lightchain, origin) + if err != nil && exp { + t.Errorf("error in ODR test for block %d: %v", i, err) + } + + eq := bytes.Equal(b1, b2) + if exp && !eq { + t.Errorf("ODR test output for tryHash %t block %d doesn't match full node", tryHash, i) + } + + header := rawdb.ReadHeader(odr.ldb, bhash, i) + if exp { + if header == nil { + t.Errorf("ODR test for tryHash %t block %d did not properly receive/store header", tryHash, i) + } + headers := []*types.Header{header} + + externTd := lightchain.GetTd(header.Hash(), header.Number.Uint64()) + localTd := lightchain.GetTd(lightchain.CurrentHeader().Hash(), lightchain.CurrentHeader().Number.Uint64()) + if externTd != nil && externTd.Cmp(localTd) <= 0 { + // If it has no difficulty, it wasn't stored properly (ignored) + continue + } + if _, err := lightchain.InsertHeaderChain(headers, 1, true); err != nil { + t.Errorf("ODR test for tryHash %t block %d could not insert header to headerchain", tryHash, i) + } + } + } + } + // expect retrievals to fail (except genesis block) without a les peer + t.Log("checking without ODR") + odr.disable = true + test(1, true) + test(1, false) + + // expect all retrievals to pass with ODR enabled + t.Log("checking with ODR") + odr.disable = false + test(len(gchain), true) + test(len(gchain), false) + + // still expect all retrievals to pass, now data should be cached locally + t.Log("checking without ODR, should be cached") + odr.disable = true + test(len(gchain), true) + test(len(gchain), false) +}
diff --git go-ethereum/light/txpool_test.go celo/light/txpool_test.go index b2f65732e848db2a788894e69d1fbc3c27dcfb4d..77aaab22931db05b6b49fa80506da3f3367ef1c9 100644 --- go-ethereum/light/txpool_test.go +++ celo/light/txpool_test.go @@ -24,7 +24,7 @@ "testing" "time"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -51,6 +51,10 @@ func (self *testTxRelay) Discard(hashes []common.Hash) { self.discard <- len(hashes) }   +func (self *testTxRelay) CanRelayTransaction(tx *types.Transaction) bool { + return true +} + const poolTestTxs = 1000 const poolTestBlocks = 100   @@ -77,7 +81,7 @@ }   func TestTxPool(t *testing.T) { for i := range testTx { - testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), types.HomesteadSigner{}, testBankKey) + testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil, nil, nil, nil), types.HomesteadSigner{}, testBankKey) }   var ( @@ -85,14 +89,13 @@ sdb = rawdb.NewMemoryDatabase() ldb = rawdb.NewMemoryDatabase() gspec = core.Genesis{ Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, - BaseFee: big.NewInt(params.InitialBaseFee), } genesis = gspec.MustCommit(sdb) ) gspec.MustCommit(ldb) // Assemble the test environment - blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil) - gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen) + blockchain, _ := core.NewBlockChain(sdb, nil, params.IstanbulTestChainConfig, mockEngine.NewFullFaker(), vm.Config{}, nil, nil) + gchain, _ := core.GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) } @@ -103,9 +106,9 @@ send: make(chan int, 1), discard: make(chan int, 1), mined: make(chan int, 1), } - lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil) + lightchain, _ := NewLightChain(odr, params.IstanbulTestChainConfig, mockEngine.NewFullFaker(), nil) txPermanent = 50 - pool := NewTxPool(params.TestChainConfig, lightchain, relay) + pool := NewTxPool(params.IstanbulTestChainConfig, lightchain, relay) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel()   @@ -122,7 +125,7 @@ t.Errorf("relay.Send expected len = %d, got %d", exp, got) } }   - if _, err := lightchain.InsertHeaderChain([]*types.Header{block.Header()}, 1); err != nil { + if _, err := lightchain.InsertHeaderChain([]*types.Header{block.Header()}, 1, true); err != nil { panic(err) }
diff --git go-ethereum/light/lightchain_test.go celo/light/lightchain_test.go index b97590719a674356b6d7308d9696f7702e347248..8d0d92a2235f30be01354a9faee3c1aecb709212 100644 --- go-ethereum/light/lightchain_test.go +++ celo/light/lightchain_test.go @@ -23,7 +23,7 @@ "math/big" "testing"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -39,7 +39,7 @@ )   // makeHeaderChain creates a deterministic chain of headers rooted at parent. func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header { - blocks, _ := core.GenerateChain(params.TestChainConfig, types.NewBlockWithHeader(parent), ethash.NewFaker(), db, n, func(i int, b *core.BlockGen) { + blocks, _ := core.GenerateChain(params.IstanbulTestChainConfig, types.NewBlockWithHeader(parent), mockEngine.NewFaker(), db, n, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) }) headers := make([]*types.Header, len(blocks)) @@ -54,9 +54,9 @@ // chain. Depending on the full flag, if creates either a full block chain or a // header only chain. func newCanonical(n int) (ethdb.Database, *LightChain, error) { db := rawdb.NewMemoryDatabase() - gspec := core.Genesis{Config: params.TestChainConfig} + gspec := core.Genesis{Config: params.IstanbulTestChainConfig} genesis := gspec.MustCommit(db) - blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, ethash.NewFaker(), nil) + blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, mockEngine.NewFaker(), nil)   // Create and inject the requested chain if n == 0 { @@ -64,7 +64,7 @@ return db, blockchain, nil } // Header-only chain requested headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed) - _, err := blockchain.InsertHeaderChain(headers, 1) + _, err := blockchain.InsertHeaderChain(headers, 1, true) return db, blockchain, err }   @@ -72,11 +72,10 @@ // newTestLightChain creates a LightChain that doesn't validate anything. func newTestLightChain() *LightChain { db := rawdb.NewMemoryDatabase() gspec := &core.Genesis{ - Difficulty: big.NewInt(1), - Config: params.TestChainConfig, + Config: params.IstanbulTestChainConfig, } gspec.MustCommit(db) - lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker(), nil) + lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, mockEngine.NewFullFaker(), nil) if err != nil { panic(err) } @@ -99,7 +98,7 @@ t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1) } // Extend the newly created chain headerChainB := makeHeaderChain(LightChain2.CurrentHeader(), n, db, forkSeed) - if _, err := LightChain2.InsertHeaderChain(headerChainB, 1); err != nil { + if _, err := LightChain2.InsertHeaderChain(headerChainB, 1, true); err != nil { t.Fatalf("failed to insert forking chain: %v", err) } // Sanity check that the forked chain can be imported into the original @@ -124,7 +123,7 @@ return err } // Manually insert the header into the database, but don't reorganize (allows subsequent testing) lightchain.chainmu.Lock() - rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash))) + rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Number, big.NewInt(1))) rawdb.WriteHeader(lightchain.chainDb, header) lightchain.chainmu.Unlock() } @@ -245,12 +244,10 @@ }   func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header { var chain []*types.Header - for i, difficulty := range d { + for i := range d { header := &types.Header{ Coinbase: common.Address{seed}, Number: big.NewInt(int64(i + 1)), - Difficulty: big.NewInt(int64(difficulty)), - UncleHash: types.EmptyUncleHash, TxHash: types.EmptyRootHash, ReceiptHash: types.EmptyRootHash, } @@ -285,21 +282,21 @@ // Tests that reorganizing a long difficult chain after a short easy one // overwrites the canonical numbers and links in the database. func TestReorgLongHeaders(t *testing.T) { - testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 10) + testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 5) }   // Tests that reorganizing a short difficult chain after a long easy one // overwrites the canonical numbers and links in the database. func TestReorgShortHeaders(t *testing.T) { - testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 11) + testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 5) }   func testReorg(t *testing.T, first, second []int, td int64) { bc := newTestLightChain()   // Insert an easy and a difficult chain afterwards - bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1) - bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1) + bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1, true) + bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1, true) // Check that the chain is valid number and link wise prev := bc.CurrentHeader() for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) { @@ -308,7 +305,7 @@ t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash()) } } // Make sure the chain total difficulty is the correct one - want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td)) + want := big.NewInt(td) if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 { t.Errorf("total difficulty mismatch: have %v, want %v", have, want) } @@ -322,7 +319,8 @@ // Create a chain, ban a hash and try to import var err error headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10) core.BadHashes[headers[2].Hash()] = true - if _, err = bc.InsertHeaderChain(headers, 1); !errors.Is(err, core.ErrBannedHash) { + defer func() { delete(core.BadHashes, headers[2].Hash()) }() + if _, err = bc.InsertHeaderChain(headers, 1, true); !errors.Is(err, core.ErrBannedHash) { t.Errorf("error mismatch: have: %v, want %v", err, core.ErrBannedHash) } } @@ -335,7 +333,7 @@ // Create a chain, import and ban afterwards headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)   - if _, err := bc.InsertHeaderChain(headers, 1); err != nil { + if _, err := bc.InsertHeaderChain(headers, 1, true); err != nil { t.Fatalf("failed to import headers: %v", err) } if bc.CurrentHeader().Hash() != headers[3].Hash() { @@ -345,7 +343,7 @@ core.BadHashes[headers[3].Hash()] = true defer func() { delete(core.BadHashes, headers[3].Hash()) }()   // Create a new LightChain and check that it rolled back the state. - ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker(), nil) + ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.IstanbulTestChainConfig, mockEngine.NewFaker(), nil) if err != nil { t.Fatalf("failed to create new chain manager: %v", err) }
diff --git go-ethereum/light/odr_util.go celo/light/odr_util.go index eb51f777fea53f15188af6940edbfb147dd1760c..0f9ac4e4c600ec6d3d01e0ba471c942f9161183e 100644 --- go-ethereum/light/odr_util.go +++ celo/light/odr_util.go @@ -26,6 +26,7 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" )   @@ -48,6 +49,17 @@ if header := rawdb.ReadHeader(db, hash, number); header != nil { return header, nil } } + // if check merged into the refactor from upstream + // on upstream commit (6eef141) + if odr.ChtIndexer() == nil { + // If `ChtIndexer` is `nil` then we are effectively in `lightest` mode + r := &HeaderRequest{Origin: blockHashOrNumber{Number: &number}} + if err := odr.Retrieve(ctx, r); err != nil { + log.Error("Error after retrieve", "Err", err) + return nil, err + } + return r.Header, nil + } // Retrieve the header via ODR, ensure the requested header is covered // by local trusted CHT. chts, _, chtHead := odr.ChtIndexer().Sections() @@ -66,6 +78,15 @@ } return r.Header, nil }   +func GetHeaderByHash(ctx context.Context, odr OdrBackend, hash common.Hash) (*types.Header, error) { + r := &HeaderRequest{Origin: blockHashOrNumber{Hash: hash}} + if err := odr.Retrieve(ctx, r); err != nil { + log.Error("Error after retrieve", "Err", err) + return nil, err + } + return r.Header, nil +} + // GetCanonicalHash retrieves the canonical block hash corresponding to the number. func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) { hash := rawdb.ReadCanonicalHash(odr.Database(), number) @@ -97,7 +118,7 @@ // <hash, number> -> td mapping already be stored in db, get it. return rawdb.ReadTd(odr.Database(), hash, number), nil }   -// GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. +// GetBodyRLP retrieves the block body (transactions) in RLP encoding. func GetBodyRLP(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (rlp.RawValue, error) { if data := rawdb.ReadBodyRLP(odr.Database(), hash, number); data != nil { return data, nil @@ -117,8 +138,7 @@ } return r.Rlp, nil }   -// GetBody retrieves the block body (transactions, uncles) corresponding to the -// hash. +// GetBody retrieves the block body (transactions) corresponding to the hash. func GetBody(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Body, error) { data, err := GetBodyRLP(ctx, odr, hash, number) if err != nil { @@ -144,7 +164,7 @@ if err != nil { return nil, err } // Reassemble the block and return - return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles), nil + return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Randomness, body.EpochSnarkData), nil }   // GetBlockReceipts retrieves the receipts generated by the transactions included
diff --git go-ethereum/light/trie_test.go celo/light/trie_test.go index 5db842867cedb8db4e35378fabd82306deb4e313..1e6b8bd2c28f207fe18d5e670e276c0329f98209 100644 --- go-ethereum/light/trie_test.go +++ celo/light/trie_test.go @@ -20,11 +20,10 @@ import ( "bytes" "context" "fmt" - "math/big" "testing"   "github.com/davecgh/go-spew/spew" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -39,13 +38,12 @@ fulldb = rawdb.NewMemoryDatabase() lightdb = rawdb.NewMemoryDatabase() gspec = core.Genesis{ Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, - BaseFee: big.NewInt(params.InitialBaseFee), } genesis = gspec.MustCommit(fulldb) ) gspec.MustCommit(lightdb) - blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil) - gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), fulldb, 4, testChainGen) + blockchain, _ := core.NewBlockChain(fulldb, nil, params.IstanbulTestChainConfig, mockEngine.NewFullFaker(), vm.Config{}, nil, nil) + gchain, _ := core.GenerateChain(params.IstanbulTestChainConfig, genesis, mockEngine.NewFaker(), fulldb, 4, testChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) }
diff --git go-ethereum/metrics/metrics.go celo/metrics/metrics.go index e96a1eafb804e4504dc942896370306797811a28..35a74ade0599ff28d06c98377d31e956f2bd9925 100644 --- go-ethereum/metrics/metrics.go +++ celo/metrics/metrics.go @@ -8,17 +8,25 @@ import ( "os" "runtime" + "strconv" "strings" "time"   "github.com/ethereum/go-ethereum/log" )   +// This is required to be able of adding it as a ldflag at building time +// ldflags -X only sets string values +var EnabledDefaultValue = "false" + // Enabled is checked by the constructor functions for all of the // standard metrics. If it is true, the metric returned is a stub. // // This global kill-switch helps quantify the observer effect and makes // for less cluttered pprof profiles. +// +// This boolean will be override with the value of Boolean(EnabledDefaultValue) +// at the beginning of the init var Enabled = false   // EnabledExpensive is a soft-flag meant for external packages to check if costly @@ -36,6 +44,11 @@ // Init enables or disables the metrics system. Since we need this to run before // any other code gets to create meters and timers, we'll actually do an ugly hack // and peek into the command line args for the metrics flag. func init() { + var err error + Enabled, err = strconv.ParseBool(EnabledDefaultValue) + if err != nil { + log.Error("The EnabledDefaultValue set is not a string representing a boolean") + } for _, arg := range os.Args { flag := strings.TrimLeft(arg, "-")
diff --git go-ethereum/metrics/csv_metrics.go celo/metrics/csv_metrics.go new file mode 100644 index 0000000000000000000000000000000000000000..b2398699fe6b110fad1f08dbffd1c85f288a54ce --- /dev/null +++ celo/metrics/csv_metrics.go @@ -0,0 +1,73 @@ +// Copyright 2021 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see <http://www.gnu.org/licenses/>. + +package metrics + +import ( + "encoding/csv" + "fmt" + "io" + "sync" +) + +type WriterCloser interface { + io.Writer + io.Closer +} + +// A CSVRecorder enables easy writing of CSV data a specified writer. +// The header is written on creation. Writing is thread safe. +type CSVRecorder struct { + writer csv.Writer + backingWC WriterCloser + writeMu sync.Mutex +} + +// NewCSVRecorder creates a CSV recorder that writes to the supplied writer. +// The writer is retained and can be closed by calling CSVRecorder.Close() +// The header is immediately written upon construction. +func NewCSVRecorder(wc WriterCloser, fields ...string) *CSVRecorder { + c := &CSVRecorder{ + writer: *csv.NewWriter(wc), + backingWC: wc, + } + c.writer.Write(fields) + return c +} + +// WriteRow writes out as csv row. Will convert the values to a string using "%v". +func (c *CSVRecorder) Write(values ...interface{}) { + if c == nil { + return + } + strs := make([]string, 0, len(values)) + for _, v := range values { + strs = append(strs, fmt.Sprintf("%v", v)) + } + + c.writeMu.Lock() + defer c.writeMu.Unlock() + c.writer.Write(strs) +} + +// Close closes the writer. This is a no-op for a nil receiver. +func (c *CSVRecorder) Close() error { + if c == nil { + return nil + } + c.writer.Flush() + return c.backingWC.Close() +}
diff --git go-ethereum/mobile/logger.go celo/mobile/logger.go deleted file mode 100644 index 7078c4fd2c83a8aa9ab8d35b18b3f342087887e2..0000000000000000000000000000000000000000 --- go-ethereum/mobile/logger.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package geth - -import ( - "os" - - "github.com/ethereum/go-ethereum/log" -) - -// SetVerbosity sets the global verbosity level (between 0 and 6 - see logger/verbosity.go). -func SetVerbosity(level int) { - log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(level), log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) -}
diff --git go-ethereum/mobile/vm.go celo/mobile/vm.go deleted file mode 100644 index b9039a83f6c911eaac291f311ea06c5e4b36d8e7..0000000000000000000000000000000000000000 --- go-ethereum/mobile/vm.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains all the wrappers from the core/types package. - -package geth - -import ( - "errors" - - "github.com/ethereum/go-ethereum/core/types" -) - -// Log represents a contract log event. These events are generated by the LOG -// opcode and stored/indexed by the node. -type Log struct { - log *types.Log -} - -func (l *Log) GetAddress() *Address { return &Address{l.log.Address} } -func (l *Log) GetTopics() *Hashes { return &Hashes{l.log.Topics} } -func (l *Log) GetData() []byte { return l.log.Data } -func (l *Log) GetBlockNumber() int64 { return int64(l.log.BlockNumber) } -func (l *Log) GetTxHash() *Hash { return &Hash{l.log.TxHash} } -func (l *Log) GetTxIndex() int { return int(l.log.TxIndex) } -func (l *Log) GetBlockHash() *Hash { return &Hash{l.log.BlockHash} } -func (l *Log) GetIndex() int { return int(l.log.Index) } - -// Logs represents a slice of VM logs. -type Logs struct{ logs []*types.Log } - -// Size returns the number of logs in the slice. -func (l *Logs) Size() int { - return len(l.logs) -} - -// Get returns the log at the given index from the slice. -func (l *Logs) Get(index int) (log *Log, _ error) { - if index < 0 || index >= len(l.logs) { - return nil, errors.New("index out of bounds") - } - return &Log{l.logs[index]}, nil -}
diff --git go-ethereum/mobile/android_test.go celo/mobile/android_test.go deleted file mode 100644 index 8aa6429127c8e74b68d5667ebe8b09652a643e34..0000000000000000000000000000000000000000 --- go-ethereum/mobile/android_test.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package geth - -import ( - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "testing" - "time" - - "github.com/cespare/cp" -) - -// androidTestClass is a Java class to do some lightweight tests against the Android -// bindings. The goal is not to test each individual functionality, rather just to -// catch breaking API and/or implementation changes. -const androidTestClass = ` -package go; - -import android.test.InstrumentationTestCase; -import android.test.MoreAsserts; - -import java.math.BigInteger; -import java.util.Arrays; - -import org.ethereum.geth.*; - -public class AndroidTest extends InstrumentationTestCase { - public AndroidTest() {} - - public void testAccountManagement() { - // Create an encrypted keystore with light crypto parameters. - KeyStore ks = new KeyStore(getInstrumentation().getContext().getFilesDir() + "/keystore", Geth.LightScryptN, Geth.LightScryptP); - - try { - // Create a new account with the specified encryption passphrase. - Account newAcc = ks.newAccount("Creation password"); - - // Export the newly created account with a different passphrase. The returned - // data from this method invocation is a JSON encoded, encrypted key-file. - byte[] jsonAcc = ks.exportKey(newAcc, "Creation password", "Export password"); - - // Update the passphrase on the account created above inside the local keystore. - ks.updateAccount(newAcc, "Creation password", "Update password"); - - // Delete the account updated above from the local keystore. - ks.deleteAccount(newAcc, "Update password"); - - // Import back the account we've exported (and then deleted) above with yet - // again a fresh passphrase. - Account impAcc = ks.importKey(jsonAcc, "Export password", "Import password"); - - // Create a new account to sign transactions with - Account signer = ks.newAccount("Signer password"); - - Transaction tx = new Transaction( - 1, new Address("0x0000000000000000000000000000000000000000"), - new BigInt(0), 0, new BigInt(1), null); // Random empty transaction - BigInt chain = new BigInt(1); // Chain identifier of the main net - - // Sign a transaction with a single authorization - Transaction signed = ks.signTxPassphrase(signer, "Signer password", tx, chain); - - // Sign a transaction with multiple manually cancelled authorizations - ks.unlock(signer, "Signer password"); - signed = ks.signTx(signer, tx, chain); - ks.lock(signer.getAddress()); - - // Sign a transaction with multiple automatically cancelled authorizations - ks.timedUnlock(signer, "Signer password", 1000000000); - signed = ks.signTx(signer, tx, chain); - } catch (Exception e) { - fail(e.toString()); - } - } - - public void testInprocNode() { - Context ctx = new Context(); - - try { - // Start up a new inprocess node - Node node = new Node(getInstrumentation().getContext().getFilesDir() + "/.ethereum", new NodeConfig()); - node.start(); - - // Retrieve some data via function calls (we don't really care about the results) - NodeInfo info = node.getNodeInfo(); - info.getName(); - info.getListenerAddress(); - info.getProtocols(); - - // Retrieve some data via the APIs (we don't really care about the results) - EthereumClient ec = node.getEthereumClient(); - ec.getBlockByNumber(ctx, -1).getNumber(); - - NewHeadHandler handler = new NewHeadHandler() { - @Override public void onError(String error) {} - @Override public void onNewHead(final Header header) {} - }; - ec.subscribeNewHead(ctx, handler, 16); - } catch (Exception e) { - fail(e.toString()); - } - } - - // Tests that recovering transaction signers works for both Homestead and EIP155 - // signatures too. Regression test for go-ethereum issue #14599. - public void testIssue14599() { - try { - byte[] preEIP155RLP = new BigInteger("f901fc8032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b561ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884", 16).toByteArray(); - preEIP155RLP = Arrays.copyOfRange(preEIP155RLP, 1, preEIP155RLP.length); - - byte[] postEIP155RLP = new BigInteger("f86b80847735940082520894ef5bbb9bba2e1ca69ef81b23a8727d889f3ef0a1880de0b6b3a7640000802ba06fef16c44726a102e6d55a651740636ef8aec6df3ebf009e7b0c1f29e4ac114aa057e7fbc69760b522a78bb568cfc37a58bfdcf6ea86cb8f9b550263f58074b9cc", 16).toByteArray(); - postEIP155RLP = Arrays.copyOfRange(postEIP155RLP, 1, postEIP155RLP.length); - - Transaction preEIP155 = new Transaction(preEIP155RLP); - Transaction postEIP155 = new Transaction(postEIP155RLP); - - preEIP155.getFrom(null); // Homestead should accept homestead - preEIP155.getFrom(new BigInt(4)); // EIP155 should accept homestead (missing chain ID) - postEIP155.getFrom(new BigInt(4)); // EIP155 should accept EIP 155 - - try { - postEIP155.getFrom(null); - fail("EIP155 transaction accepted by Homestead"); - } catch (Exception e) {} - } catch (Exception e) { - fail(e.toString()); - } - } -} -` - -// TestAndroid runs the Android java test class specified above. -// -// This requires the gradle command in PATH and the Android SDK whose path is available -// through ANDROID_HOME environment variable. To successfully run the tests, an Android -// device must also be available with debugging enabled. -// -// This method has been adapted from golang.org/x/mobile/bind/java/seq_test.go/runTest -func TestAndroid(t *testing.T) { - // Skip tests on Windows altogether - if runtime.GOOS == "windows" { - t.Skip("cannot test Android bindings on Windows, skipping") - } - // Make sure all the Android tools are installed - if _, err := exec.Command("which", "gradle").CombinedOutput(); err != nil { - t.Skip("command gradle not found, skipping") - } - if sdk := os.Getenv("ANDROID_HOME"); sdk == "" { - // Android SDK not explicitly given, try to auto-resolve - autopath := filepath.Join(os.Getenv("HOME"), "Android", "Sdk") - if _, err := os.Stat(autopath); err != nil { - t.Skip("ANDROID_HOME environment var not set, skipping") - } - os.Setenv("ANDROID_HOME", autopath) - } - if _, err := exec.Command("which", "gomobile").CombinedOutput(); err != nil { - t.Log("gomobile missing, installing it...") - if out, err := exec.Command("go", "get", "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil { - t.Fatalf("install failed: %v\n%s", err, string(out)) - } - t.Log("initializing gomobile...") - start := time.Now() - if _, err := exec.Command("gomobile", "init").CombinedOutput(); err != nil { - t.Fatalf("initialization failed: %v", err) - } - t.Logf("initialization took %v", time.Since(start)) - } - // Create and switch to a temporary workspace - workspace, err := ioutil.TempDir("", "geth-android-") - if err != nil { - t.Fatalf("failed to create temporary workspace: %v", err) - } - defer os.RemoveAll(workspace) - - pwd, err := os.Getwd() - if err != nil { - t.Fatalf("failed to get current working directory: %v", err) - } - if err := os.Chdir(workspace); err != nil { - t.Fatalf("failed to switch to temporary workspace: %v", err) - } - defer os.Chdir(pwd) - - // Create the skeleton of the Android project - for _, dir := range []string{"src/main", "src/androidTest/java/org/ethereum/gethtest", "libs"} { - err = os.MkdirAll(dir, os.ModePerm) - if err != nil { - t.Fatal(err) - } - } - // Generate the mobile bindings for Geth and add the tester class - gobind := exec.Command("gomobile", "bind", "-javapkg", "org.ethereum", "github.com/ethereum/go-ethereum/mobile") - if output, err := gobind.CombinedOutput(); err != nil { - t.Logf("%s", output) - t.Fatalf("failed to run gomobile bind: %v", err) - } - cp.CopyFile(filepath.Join("libs", "geth.aar"), "geth.aar") - - if err = ioutil.WriteFile(filepath.Join("src", "androidTest", "java", "org", "ethereum", "gethtest", "AndroidTest.java"), []byte(androidTestClass), os.ModePerm); err != nil { - t.Fatalf("failed to write Android test class: %v", err) - } - // Finish creating the project and run the tests via gradle - if err = ioutil.WriteFile(filepath.Join("src", "main", "AndroidManifest.xml"), []byte(androidManifest), os.ModePerm); err != nil { - t.Fatalf("failed to write Android manifest: %v", err) - } - if err = ioutil.WriteFile("build.gradle", []byte(gradleConfig), os.ModePerm); err != nil { - t.Fatalf("failed to write gradle build file: %v", err) - } - if output, err := exec.Command("gradle", "connectedAndroidTest").CombinedOutput(); err != nil { - t.Logf("%s", output) - t.Errorf("failed to run gradle test: %v", err) - } -} - -const androidManifest = `<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.ethereum.gethtest" - android:versionCode="1" - android:versionName="1.0"> - - <uses-permission android:name="android.permission.INTERNET" /> -</manifest>` - -const gradleConfig = `buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' - } -} -allprojects { - repositories { jcenter() } -} -apply plugin: 'com.android.library' -android { - compileSdkVersion 'android-19' - buildToolsVersion '21.1.2' - defaultConfig { minSdkVersion 15 } -} -repositories { - flatDir { dirs 'libs' } -} -dependencies { - compile 'com.android.support:appcompat-v7:19.0.0' - compile(name: "geth", ext: "aar") -} -`
diff --git go-ethereum/mobile/context.go celo/mobile/context.go deleted file mode 100644 index 463b36eacad5b4c928f82144fea40df4726cd891..0000000000000000000000000000000000000000 --- go-ethereum/mobile/context.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains all the wrappers from the golang.org/x/net/context package to support -// client side context management on mobile platforms. - -package geth - -import ( - "context" - "time" -) - -// Context carries a deadline, a cancellation signal, and other values across API -// boundaries. -type Context struct { - context context.Context - cancel context.CancelFunc -} - -// NewContext returns a non-nil, empty Context. It is never canceled, has no -// values, and has no deadline. It is typically used by the main function, -// initialization, and tests, and as the top-level Context for incoming requests. -func NewContext() *Context { - return &Context{ - context: context.Background(), - } -} - -// WithCancel returns a copy of the original context with cancellation mechanism -// included. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func (c *Context) WithCancel() *Context { - child, cancel := context.WithCancel(c.context) - return &Context{ - context: child, - cancel: cancel, - } -} - -// WithDeadline returns a copy of the original context with the deadline adjusted -// to be no later than the specified time. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func (c *Context) WithDeadline(sec int64, nsec int64) *Context { - child, cancel := context.WithDeadline(c.context, time.Unix(sec, nsec)) - return &Context{ - context: child, - cancel: cancel, - } -} - -// WithTimeout returns a copy of the original context with the deadline adjusted -// to be no later than now + the duration specified. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func (c *Context) WithTimeout(nsec int64) *Context { - child, cancel := context.WithTimeout(c.context, time.Duration(nsec)) - return &Context{ - context: child, - cancel: cancel, - } -}
diff --git go-ethereum/mobile/discover.go celo/mobile/discover.go deleted file mode 100644 index 541e4ab7a651158e8b301162b0e28efaa08ecac3..0000000000000000000000000000000000000000 --- go-ethereum/mobile/discover.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains all the wrappers from the accounts package to support client side enode -// management on mobile platforms. - -package geth - -import ( - "errors" - - "github.com/ethereum/go-ethereum/p2p/enode" -) - -// Enode represents a host on the network. -type Enode struct { - node *enode.Node -} - -// NewEnode parses a node designator. -// -// There are two basic forms of node designators -// - incomplete nodes, which only have the public key (node ID) -// - complete nodes, which contain the public key and IP/Port information -// -// For incomplete nodes, the designator must look like one of these -// -// enode://<hex node id> -// <hex node id> -// -// For complete nodes, the node ID is encoded in the username portion -// of the URL, separated from the host by an @ sign. The hostname can -// only be given as an IP address, DNS domain names are not allowed. -// The port in the host name section is the TCP listening port. If the -// TCP and UDP (discovery) ports differ, the UDP port is specified as -// query parameter "discport". -// -// In the following example, the node URL describes -// a node with IP address 10.3.58.6, TCP listening port 30303 -// and UDP discovery port 30301. -// -// enode://<hex node id>@10.3.58.6:30303?discport=30301 -func NewEnode(rawurl string) (*Enode, error) { - node, err := enode.Parse(enode.ValidSchemes, rawurl) - if err != nil { - return nil, err - } - return &Enode{node}, nil -} - -// Enodes represents a slice of accounts. -type Enodes struct{ nodes []*enode.Node } - -// NewEnodes creates a slice of uninitialized enodes. -func NewEnodes(size int) *Enodes { - return &Enodes{ - nodes: make([]*enode.Node, size), - } -} - -// NewEnodesEmpty creates an empty slice of Enode values. -func NewEnodesEmpty() *Enodes { - return NewEnodes(0) -} - -// Size returns the number of enodes in the slice. -func (e *Enodes) Size() int { - return len(e.nodes) -} - -// Get returns the enode at the given index from the slice. -func (e *Enodes) Get(index int) (enode *Enode, _ error) { - if index < 0 || index >= len(e.nodes) { - return nil, errors.New("index out of bounds") - } - return &Enode{e.nodes[index]}, nil -} - -// Set sets the enode at the given index in the slice. -func (e *Enodes) Set(index int, enode *Enode) error { - if index < 0 || index >= len(e.nodes) { - return errors.New("index out of bounds") - } - e.nodes[index] = enode.node - return nil -} - -// Append adds a new enode element to the end of the slice. -func (e *Enodes) Append(enode *Enode) { - e.nodes = append(e.nodes, enode.node) -}
diff --git go-ethereum/mobile/geth.go celo/mobile/geth.go deleted file mode 100644 index 5a33470914a8c102c21291189f8cc342613d21a0..0000000000000000000000000000000000000000 --- go-ethereum/mobile/geth.go +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains all the wrappers from the node package to support client side node -// management on mobile platforms. - -package geth - -import ( - "encoding/json" - "fmt" - "path/filepath" - - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/ethstats" - "github.com/ethereum/go-ethereum/internal/debug" - "github.com/ethereum/go-ethereum/les" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/nat" - "github.com/ethereum/go-ethereum/params" -) - -// NodeConfig represents the collection of configuration values to fine tune the Geth -// node embedded into a mobile process. The available values are a subset of the -// entire API provided by go-ethereum to reduce the maintenance surface and dev -// complexity. -type NodeConfig struct { - // Bootstrap nodes used to establish connectivity with the rest of the network. - BootstrapNodes *Enodes - - // MaxPeers is the maximum number of peers that can be connected. If this is - // set to zero, then only the configured static and trusted peers can connect. - MaxPeers int - - // EthereumEnabled specifies whether the node should run the Ethereum protocol. - EthereumEnabled bool - - // EthereumNetworkID is the network identifier used by the Ethereum protocol to - // decide if remote peers should be accepted or not. - EthereumNetworkID int64 // uint64 in truth, but Java can't handle that... - - // EthereumGenesis is the genesis JSON to use to seed the blockchain with. An - // empty genesis state is equivalent to using the mainnet's state. - EthereumGenesis string - - // EthereumDatabaseCache is the system memory in MB to allocate for database caching. - // A minimum of 16MB is always reserved. - EthereumDatabaseCache int - - // EthereumNetStats is a netstats connection string to use to report various - // chain, transaction and node stats to a monitoring server. - // - // It has the form "nodename:secret@host:port" - EthereumNetStats string - - // Listening address of pprof server. - PprofAddress string -} - -// defaultNodeConfig contains the default node configuration values to use if all -// or some fields are missing from the user's specified list. -var defaultNodeConfig = &NodeConfig{ - BootstrapNodes: FoundationBootnodes(), - MaxPeers: 25, - EthereumEnabled: true, - EthereumNetworkID: 1, - EthereumDatabaseCache: 16, -} - -// NewNodeConfig creates a new node option set, initialized to the default values. -func NewNodeConfig() *NodeConfig { - config := *defaultNodeConfig - return &config -} - -// AddBootstrapNode adds an additional bootstrap node to the node config. -func (conf *NodeConfig) AddBootstrapNode(node *Enode) { - conf.BootstrapNodes.Append(node) -} - -// EncodeJSON encodes a NodeConfig into a JSON data dump. -func (conf *NodeConfig) EncodeJSON() (string, error) { - data, err := json.Marshal(conf) - return string(data), err -} - -// String returns a printable representation of the node config. -func (conf *NodeConfig) String() string { - return encodeOrError(conf) -} - -// Node represents a Geth Ethereum node instance. -type Node struct { - node *node.Node -} - -// NewNode creates and configures a new Geth node. -func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) { - // If no or partial configurations were specified, use defaults - if config == nil { - config = NewNodeConfig() - } - if config.MaxPeers == 0 { - config.MaxPeers = defaultNodeConfig.MaxPeers - } - if config.BootstrapNodes == nil || config.BootstrapNodes.Size() == 0 { - config.BootstrapNodes = defaultNodeConfig.BootstrapNodes - } - - if config.PprofAddress != "" { - debug.StartPProf(config.PprofAddress, true) - } - - // Create the empty networking stack - nodeConf := &node.Config{ - Name: clientIdentifier, - Version: params.VersionWithMeta, - DataDir: datadir, - KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores! - P2P: p2p.Config{ - NoDiscovery: true, - DiscoveryV5: true, - BootstrapNodesV5: config.BootstrapNodes.nodes, - ListenAddr: ":0", - NAT: nat.Any(), - MaxPeers: config.MaxPeers, - }, - } - - rawStack, err := node.New(nodeConf) - if err != nil { - return nil, err - } - - debug.Memsize.Add("node", rawStack) - - var genesis *core.Genesis - if config.EthereumGenesis != "" { - // Parse the user supplied genesis spec if not mainnet - genesis = new(core.Genesis) - if err := json.Unmarshal([]byte(config.EthereumGenesis), genesis); err != nil { - return nil, fmt.Errorf("invalid genesis spec: %v", err) - } - // If we have the Ropsten testnet, hard code the chain configs too - if config.EthereumGenesis == RopstenGenesis() { - genesis.Config = params.RopstenChainConfig - if config.EthereumNetworkID == 1 { - config.EthereumNetworkID = 3 - } - } - // If we have the Rinkeby testnet, hard code the chain configs too - if config.EthereumGenesis == RinkebyGenesis() { - genesis.Config = params.RinkebyChainConfig - if config.EthereumNetworkID == 1 { - config.EthereumNetworkID = 4 - } - } - // If we have the Goerli testnet, hard code the chain configs too - if config.EthereumGenesis == GoerliGenesis() { - genesis.Config = params.GoerliChainConfig - if config.EthereumNetworkID == 1 { - config.EthereumNetworkID = 5 - } - } - } - // Register the Ethereum protocol if requested - if config.EthereumEnabled { - ethConf := ethconfig.Defaults - ethConf.Genesis = genesis - ethConf.SyncMode = downloader.LightSync - ethConf.NetworkId = uint64(config.EthereumNetworkID) - ethConf.DatabaseCache = config.EthereumDatabaseCache - lesBackend, err := les.New(rawStack, &ethConf) - if err != nil { - return nil, fmt.Errorf("ethereum init: %v", err) - } - // If netstats reporting is requested, do it - if config.EthereumNetStats != "" { - if err := ethstats.New(rawStack, lesBackend.ApiBackend, lesBackend.Engine(), config.EthereumNetStats); err != nil { - return nil, fmt.Errorf("netstats init: %v", err) - } - } - } - return &Node{rawStack}, nil -} - -// Close terminates a running node along with all it's services, tearing internal state -// down. It is not possible to restart a closed node. -func (n *Node) Close() error { - return n.node.Close() -} - -// Start creates a live P2P node and starts running it. -func (n *Node) Start() error { - // TODO: recreate the node so it can be started multiple times - return n.node.Start() -} - -// Stop terminates a running node along with all its services. If the node was not started, -// an error is returned. It is not possible to restart a stopped node. -// -// Deprecated: use Close() -func (n *Node) Stop() error { - return n.node.Close() -} - -// GetEthereumClient retrieves a client to access the Ethereum subsystem. -func (n *Node) GetEthereumClient() (client *EthereumClient, _ error) { - rpc, err := n.node.Attach() - if err != nil { - return nil, err - } - return &EthereumClient{ethclient.NewClient(rpc)}, nil -} - -// GetNodeInfo gathers and returns a collection of metadata known about the host. -func (n *Node) GetNodeInfo() *NodeInfo { - return &NodeInfo{n.node.Server().NodeInfo()} -} - -// GetPeersInfo returns an array of metadata objects describing connected peers. -func (n *Node) GetPeersInfo() *PeerInfos { - return &PeerInfos{n.node.Server().PeersInfo()} -}
diff --git go-ethereum/mobile/common.go celo/mobile/common.go deleted file mode 100644 index 124712b4b1f26c871da16eeb118e14cb554607d4..0000000000000000000000000000000000000000 --- go-ethereum/mobile/common.go +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains all the wrappers from the common package. - -package geth - -import ( - "encoding/hex" - "errors" - "fmt" - "strings" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -// Hash represents the 32 byte Keccak256 hash of arbitrary data. -type Hash struct { - hash common.Hash -} - -// NewHashFromBytes converts a slice of bytes to a hash value. -func NewHashFromBytes(binary []byte) (hash *Hash, _ error) { - h := new(Hash) - if err := h.SetBytes(common.CopyBytes(binary)); err != nil { - return nil, err - } - return h, nil -} - -// NewHashFromHex converts a hex string to a hash value. -func NewHashFromHex(hex string) (hash *Hash, _ error) { - h := new(Hash) - if err := h.SetHex(hex); err != nil { - return nil, err - } - return h, nil -} - -// SetBytes sets the specified slice of bytes as the hash value. -func (h *Hash) SetBytes(hash []byte) error { - if length := len(hash); length != common.HashLength { - return fmt.Errorf("invalid hash length: %v != %v", length, common.HashLength) - } - copy(h.hash[:], hash) - return nil -} - -// GetBytes retrieves the byte representation of the hash. -func (h *Hash) GetBytes() []byte { - return h.hash[:] -} - -// SetHex sets the specified hex string as the hash value. -func (h *Hash) SetHex(hash string) error { - hash = strings.ToLower(hash) - if len(hash) >= 2 && hash[:2] == "0x" { - hash = hash[2:] - } - if length := len(hash); length != 2*common.HashLength { - return fmt.Errorf("invalid hash hex length: %v != %v", length, 2*common.HashLength) - } - bin, err := hex.DecodeString(hash) - if err != nil { - return err - } - copy(h.hash[:], bin) - return nil -} - -// GetHex retrieves the hex string representation of the hash. -func (h *Hash) GetHex() string { - return h.hash.Hex() -} - -// String implements Stringer interface for printable representation of the hash. -func (h *Hash) String() string { - return h.GetHex() -} - -// Hashes represents a slice of hashes. -type Hashes struct{ hashes []common.Hash } - -// NewHashes creates a slice of uninitialized Hashes. -func NewHashes(size int) *Hashes { - return &Hashes{ - hashes: make([]common.Hash, size), - } -} - -// NewHashesEmpty creates an empty slice of Hashes values. -func NewHashesEmpty() *Hashes { - return NewHashes(0) -} - -// Size returns the number of hashes in the slice. -func (h *Hashes) Size() int { - return len(h.hashes) -} - -// Get returns the hash at the given index from the slice. -func (h *Hashes) Get(index int) (hash *Hash, _ error) { - if index < 0 || index >= len(h.hashes) { - return nil, errors.New("index out of bounds") - } - return &Hash{h.hashes[index]}, nil -} - -// Set sets the Hash at the given index in the slice. -func (h *Hashes) Set(index int, hash *Hash) error { - if index < 0 || index >= len(h.hashes) { - return errors.New("index out of bounds") - } - h.hashes[index] = hash.hash - return nil -} - -// Append adds a new Hash element to the end of the slice. -func (h *Hashes) Append(hash *Hash) { - h.hashes = append(h.hashes, hash.hash) -} - -// Address represents the 20 byte address of an Ethereum account. -type Address struct { - address common.Address -} - -// NewAddressFromBytes converts a slice of bytes to a hash value. -func NewAddressFromBytes(binary []byte) (address *Address, _ error) { - a := new(Address) - if err := a.SetBytes(common.CopyBytes(binary)); err != nil { - return nil, err - } - return a, nil -} - -// NewAddressFromHex converts a hex string to a address value. -func NewAddressFromHex(hex string) (address *Address, _ error) { - a := new(Address) - if err := a.SetHex(hex); err != nil { - return nil, err - } - return a, nil -} - -// SetBytes sets the specified slice of bytes as the address value. -func (a *Address) SetBytes(address []byte) error { - if length := len(address); length != common.AddressLength { - return fmt.Errorf("invalid address length: %v != %v", length, common.AddressLength) - } - copy(a.address[:], address) - return nil -} - -// GetBytes retrieves the byte representation of the address. -func (a *Address) GetBytes() []byte { - return a.address[:] -} - -// SetHex sets the specified hex string as the address value. -func (a *Address) SetHex(address string) error { - address = strings.ToLower(address) - if len(address) >= 2 && address[:2] == "0x" { - address = address[2:] - } - if length := len(address); length != 2*common.AddressLength { - return fmt.Errorf("invalid address hex length: %v != %v", length, 2*common.AddressLength) - } - bin, err := hex.DecodeString(address) - if err != nil { - return err - } - copy(a.address[:], bin) - return nil -} - -// GetHex retrieves the hex string representation of the address. -func (a *Address) GetHex() string { - return a.address.Hex() -} - -// String returns a printable representation of the address. -func (a *Address) String() string { - return a.GetHex() -} - -// Addresses represents a slice of addresses. -type Addresses struct{ addresses []common.Address } - -// NewAddresses creates a slice of uninitialized addresses. -func NewAddresses(size int) *Addresses { - return &Addresses{ - addresses: make([]common.Address, size), - } -} - -// NewAddressesEmpty creates an empty slice of Addresses values. -func NewAddressesEmpty() *Addresses { - return NewAddresses(0) -} - -// Size returns the number of addresses in the slice. -func (a *Addresses) Size() int { - return len(a.addresses) -} - -// Get returns the address at the given index from the slice. -func (a *Addresses) Get(index int) (address *Address, _ error) { - if index < 0 || index >= len(a.addresses) { - return nil, errors.New("index out of bounds") - } - return &Address{a.addresses[index]}, nil -} - -// Set sets the address at the given index in the slice. -func (a *Addresses) Set(index int, address *Address) error { - if index < 0 || index >= len(a.addresses) { - return errors.New("index out of bounds") - } - a.addresses[index] = address.address - return nil -} - -// Append adds a new address element to the end of the slice. -func (a *Addresses) Append(address *Address) { - a.addresses = append(a.addresses, address.address) -} - -// EncodeToHex encodes b as a hex string with 0x prefix. -func EncodeToHex(b []byte) string { - return hexutil.Encode(b) -} - -// DecodeFromHex decodes a hex string with 0x prefix. -func DecodeFromHex(s string) ([]byte, error) { - return hexutil.Decode(s) -}
diff --git go-ethereum/mobile/init.go celo/mobile/init.go deleted file mode 100644 index 2025d85edc92540c481a92f46ab2640e0952c13c..0000000000000000000000000000000000000000 --- go-ethereum/mobile/init.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains initialization code for the mbile library. - -package geth - -import ( - "os" - "runtime" - - "github.com/ethereum/go-ethereum/log" -) - -func init() { - // Initialize the logger - log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) - - // Initialize the goroutine count - runtime.GOMAXPROCS(runtime.NumCPU()) -}
diff --git go-ethereum/mobile/ethclient.go celo/mobile/ethclient.go deleted file mode 100644 index 662125c4adeb0b4ed1d56b85f943320876317bd1..0000000000000000000000000000000000000000 --- go-ethereum/mobile/ethclient.go +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains a wrapper for the Ethereum client. - -package geth - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" -) - -// EthereumClient provides access to the Ethereum APIs. -type EthereumClient struct { - client *ethclient.Client -} - -// NewEthereumClient connects a client to the given URL. -func NewEthereumClient(rawurl string) (client *EthereumClient, _ error) { - rawClient, err := ethclient.Dial(rawurl) - return &EthereumClient{rawClient}, err -} - -// GetBlockByHash returns the given full block. -func (ec *EthereumClient) GetBlockByHash(ctx *Context, hash *Hash) (block *Block, _ error) { - rawBlock, err := ec.client.BlockByHash(ctx.context, hash.hash) - return &Block{rawBlock}, err -} - -// GetBlockByNumber returns a block from the current canonical chain. If number is <0, the -// latest known block is returned. -func (ec *EthereumClient) GetBlockByNumber(ctx *Context, number int64) (block *Block, _ error) { - if number < 0 { - rawBlock, err := ec.client.BlockByNumber(ctx.context, nil) - return &Block{rawBlock}, err - } - rawBlock, err := ec.client.BlockByNumber(ctx.context, big.NewInt(number)) - return &Block{rawBlock}, err -} - -// GetHeaderByHash returns the block header with the given hash. -func (ec *EthereumClient) GetHeaderByHash(ctx *Context, hash *Hash) (header *Header, _ error) { - rawHeader, err := ec.client.HeaderByHash(ctx.context, hash.hash) - return &Header{rawHeader}, err -} - -// GetHeaderByNumber returns a block header from the current canonical chain. If number is <0, -// the latest known header is returned. -func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (header *Header, _ error) { - if number < 0 { - rawHeader, err := ec.client.HeaderByNumber(ctx.context, nil) - return &Header{rawHeader}, err - } - rawHeader, err := ec.client.HeaderByNumber(ctx.context, big.NewInt(number)) - return &Header{rawHeader}, err -} - -// GetTransactionByHash returns the transaction with the given hash. -func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (tx *Transaction, _ error) { - // TODO(karalabe): handle isPending - rawTx, _, err := ec.client.TransactionByHash(ctx.context, hash.hash) - return &Transaction{rawTx}, err -} - -// GetTransactionSender returns the sender address of a transaction. The transaction must -// be included in blockchain at the given block and index. -func (ec *EthereumClient) GetTransactionSender(ctx *Context, tx *Transaction, blockhash *Hash, index int) (sender *Address, _ error) { - addr, err := ec.client.TransactionSender(ctx.context, tx.tx, blockhash.hash, uint(index)) - return &Address{addr}, err -} - -// GetTransactionCount returns the total number of transactions in the given block. -func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (count int, _ error) { - rawCount, err := ec.client.TransactionCount(ctx.context, hash.hash) - return int(rawCount), err -} - -// GetTransactionInBlock returns a single transaction at index in the given block. -func (ec *EthereumClient) GetTransactionInBlock(ctx *Context, hash *Hash, index int) (tx *Transaction, _ error) { - rawTx, err := ec.client.TransactionInBlock(ctx.context, hash.hash, uint(index)) - return &Transaction{rawTx}, err - -} - -// GetTransactionReceipt returns the receipt of a transaction by transaction hash. -// Note that the receipt is not available for pending transactions. -func (ec *EthereumClient) GetTransactionReceipt(ctx *Context, hash *Hash) (receipt *Receipt, _ error) { - rawReceipt, err := ec.client.TransactionReceipt(ctx.context, hash.hash) - return &Receipt{rawReceipt}, err -} - -// SyncProgress retrieves the current progress of the sync algorithm. If there's -// no sync currently running, it returns nil. -func (ec *EthereumClient) SyncProgress(ctx *Context) (progress *SyncProgress, _ error) { - rawProgress, err := ec.client.SyncProgress(ctx.context) - if rawProgress == nil { - return nil, err - } - return &SyncProgress{*rawProgress}, err -} - -// NewHeadHandler is a client-side subscription callback to invoke on events and -// subscription failure. -type NewHeadHandler interface { - OnNewHead(header *Header) - OnError(failure string) -} - -// SubscribeNewHead subscribes to notifications about the current blockchain head -// on the given channel. -func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler, buffer int) (sub *Subscription, _ error) { - // Subscribe to the event internally - ch := make(chan *types.Header, buffer) - rawSub, err := ec.client.SubscribeNewHead(ctx.context, ch) - if err != nil { - return nil, err - } - // Start up a dispatcher to feed into the callback - go func() { - for { - select { - case header := <-ch: - handler.OnNewHead(&Header{header}) - - case err := <-rawSub.Err(): - if err != nil { - handler.OnError(err.Error()) - } - return - } - } - }() - return &Subscription{rawSub}, nil -} - -// State Access - -// GetBalanceAt returns the wei balance of the given account. -// The block number can be <0, in which case the balance is taken from the latest known block. -func (ec *EthereumClient) GetBalanceAt(ctx *Context, account *Address, number int64) (balance *BigInt, _ error) { - if number < 0 { - rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, nil) - return &BigInt{rawBalance}, err - } - rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, big.NewInt(number)) - return &BigInt{rawBalance}, err -} - -// GetStorageAt returns the value of key in the contract storage of the given account. -// The block number can be <0, in which case the value is taken from the latest known block. -func (ec *EthereumClient) GetStorageAt(ctx *Context, account *Address, key *Hash, number int64) (storage []byte, _ error) { - if number < 0 { - return ec.client.StorageAt(ctx.context, account.address, key.hash, nil) - } - return ec.client.StorageAt(ctx.context, account.address, key.hash, big.NewInt(number)) -} - -// GetCodeAt returns the contract code of the given account. -// The block number can be <0, in which case the code is taken from the latest known block. -func (ec *EthereumClient) GetCodeAt(ctx *Context, account *Address, number int64) (code []byte, _ error) { - if number < 0 { - return ec.client.CodeAt(ctx.context, account.address, nil) - } - return ec.client.CodeAt(ctx.context, account.address, big.NewInt(number)) -} - -// GetNonceAt returns the account nonce of the given account. -// The block number can be <0, in which case the nonce is taken from the latest known block. -func (ec *EthereumClient) GetNonceAt(ctx *Context, account *Address, number int64) (nonce int64, _ error) { - if number < 0 { - rawNonce, err := ec.client.NonceAt(ctx.context, account.address, nil) - return int64(rawNonce), err - } - rawNonce, err := ec.client.NonceAt(ctx.context, account.address, big.NewInt(number)) - return int64(rawNonce), err -} - -// Filters - -// FilterLogs executes a filter query. -func (ec *EthereumClient) FilterLogs(ctx *Context, query *FilterQuery) (logs *Logs, _ error) { - rawLogs, err := ec.client.FilterLogs(ctx.context, query.query) - if err != nil { - return nil, err - } - // Temp hack due to vm.Logs being []*vm.Log - res := make([]*types.Log, len(rawLogs)) - for i := range rawLogs { - res[i] = &rawLogs[i] - } - return &Logs{res}, nil -} - -// FilterLogsHandler is a client-side subscription callback to invoke on events and -// subscription failure. -type FilterLogsHandler interface { - OnFilterLogs(log *Log) - OnError(failure string) -} - -// SubscribeFilterLogs subscribes to the results of a streaming filter query. -func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery, handler FilterLogsHandler, buffer int) (sub *Subscription, _ error) { - // Subscribe to the event internally - ch := make(chan types.Log, buffer) - rawSub, err := ec.client.SubscribeFilterLogs(ctx.context, query.query, ch) - if err != nil { - return nil, err - } - // Start up a dispatcher to feed into the callback - go func() { - for { - select { - case log := <-ch: - handler.OnFilterLogs(&Log{&log}) - - case err := <-rawSub.Err(): - if err != nil { - handler.OnError(err.Error()) - } - return - } - } - }() - return &Subscription{rawSub}, nil -} - -// Pending State - -// GetPendingBalanceAt returns the wei balance of the given account in the pending state. -func (ec *EthereumClient) GetPendingBalanceAt(ctx *Context, account *Address) (balance *BigInt, _ error) { - rawBalance, err := ec.client.PendingBalanceAt(ctx.context, account.address) - return &BigInt{rawBalance}, err -} - -// GetPendingStorageAt returns the value of key in the contract storage of the given account in the pending state. -func (ec *EthereumClient) GetPendingStorageAt(ctx *Context, account *Address, key *Hash) (storage []byte, _ error) { - return ec.client.PendingStorageAt(ctx.context, account.address, key.hash) -} - -// GetPendingCodeAt returns the contract code of the given account in the pending state. -func (ec *EthereumClient) GetPendingCodeAt(ctx *Context, account *Address) (code []byte, _ error) { - return ec.client.PendingCodeAt(ctx.context, account.address) -} - -// GetPendingNonceAt returns the account nonce of the given account in the pending state. -// This is the nonce that should be used for the next transaction. -func (ec *EthereumClient) GetPendingNonceAt(ctx *Context, account *Address) (nonce int64, _ error) { - rawNonce, err := ec.client.PendingNonceAt(ctx.context, account.address) - return int64(rawNonce), err -} - -// GetPendingTransactionCount returns the total number of transactions in the pending state. -func (ec *EthereumClient) GetPendingTransactionCount(ctx *Context) (count int, _ error) { - rawCount, err := ec.client.PendingTransactionCount(ctx.context) - return int(rawCount), err -} - -// Contract Calling - -// CallContract executes a message call transaction, which is directly executed in the VM -// of the node, but never mined into the blockchain. -// -// blockNumber selects the block height at which the call runs. It can be <0, in which -// case the code is taken from the latest known block. Note that state from very old -// blocks might not be available. -func (ec *EthereumClient) CallContract(ctx *Context, msg *CallMsg, number int64) (output []byte, _ error) { - if number < 0 { - return ec.client.CallContract(ctx.context, msg.msg, nil) - } - return ec.client.CallContract(ctx.context, msg.msg, big.NewInt(number)) -} - -// PendingCallContract executes a message call transaction using the EVM. -// The state seen by the contract call is the pending state. -func (ec *EthereumClient) PendingCallContract(ctx *Context, msg *CallMsg) (output []byte, _ error) { - return ec.client.PendingCallContract(ctx.context, msg.msg) -} - -// SuggestGasPrice retrieves the currently suggested gas price to allow a timely -// execution of a transaction. -func (ec *EthereumClient) SuggestGasPrice(ctx *Context) (price *BigInt, _ error) { - rawPrice, err := ec.client.SuggestGasPrice(ctx.context) - return &BigInt{rawPrice}, err -} - -// EstimateGas tries to estimate the gas needed to execute a specific transaction based on -// the current pending state of the backend blockchain. There is no guarantee that this is -// the true gas limit requirement as other transactions may be added or removed by miners, -// but it should provide a basis for setting a reasonable default. -func (ec *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (gas int64, _ error) { - rawGas, err := ec.client.EstimateGas(ctx.context, msg.msg) - return int64(rawGas), err -} - -// SendTransaction injects a signed transaction into the pending pool for execution. -// -// If the transaction was a contract creation use the TransactionReceipt method to get the -// contract address after the transaction has been mined. -func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error { - return ec.client.SendTransaction(ctx.context, tx.tx) -}
diff --git go-ethereum/mobile/doc.go celo/mobile/doc.go deleted file mode 100644 index 20131afc2ee004af9a7f6c773a3dd155eb006b40..0000000000000000000000000000000000000000 --- go-ethereum/mobile/doc.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Package geth contains the simplified mobile APIs to go-ethereum. -// -// The scope of this package is *not* to allow writing a custom Ethereum client -// with pieces plucked from go-ethereum, rather to allow writing native dapps on -// mobile platforms. Keep this in mind when using or extending this package! -// -// API limitations -// -// Since gomobile cannot bridge arbitrary types between Go and Android/iOS, the -// exposed APIs need to be manually wrapped into simplified types, with custom -// constructors and getters/setters to ensure that they can be meaningfully used -// from Java/ObjC too. -// -// With this in mind, please try to limit the scope of this package and only add -// essentials without which mobile support cannot work, especially since manually -// syncing the code will be unwieldy otherwise. In the long term we might consider -// writing custom library generators, but those are out of scope now. -// -// Content wise each file in this package corresponds to an entire Go package -// from the go-ethereum repository. Please adhere to this scoping to prevent this -// package getting unmaintainable. -// -// Wrapping guidelines: -// -// Every type that is to be exposed should be wrapped into its own plain struct, -// which internally contains a single field: the original go-ethereum version. -// This is needed because gomobile cannot expose named types for now. -// -// Whenever a method argument or a return type is a custom struct, the pointer -// variant should always be used as value types crossing over between language -// boundaries might have strange behaviors. -// -// Slices of types should be converted into a single multiplicative type wrapping -// a go slice with the methods `Size`, `Get` and `Set`. Further slice operations -// should not be provided to limit the remote code complexity. Arrays should be -// avoided as much as possible since they complicate bounds checking. -// -// If a method has multiple return values (e.g. some return + an error), those -// are generated as output arguments in ObjC. To avoid weird generated names like -// ret_0 for them, please always assign names to output variables if tuples. -// -// Note, a panic *cannot* cross over language boundaries, instead will result in -// an undebuggable SEGFAULT in the process. For error handling only ever use error -// returns, which may be the only or the second return. -package geth
diff --git go-ethereum/mobile/bind.go celo/mobile/bind.go deleted file mode 100644 index 0072a31237a38d31c0afc675b1e788d1ddbc5366..0000000000000000000000000000000000000000 --- go-ethereum/mobile/bind.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains all the wrappers from the bind package. - -package geth - -import ( - "math/big" - "strings" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - -// Signer is an interface defining the callback when a contract requires a -// method to sign the transaction before submission. -type Signer interface { - Sign(addr *Address, unsignedTx *Transaction) (tx *Transaction, _ error) -} - -type MobileSigner struct { - sign bind.SignerFn -} - -func (s *MobileSigner) Sign(addr *Address, unsignedTx *Transaction) (signedTx *Transaction, _ error) { - sig, err := s.sign(addr.address, unsignedTx.tx) - if err != nil { - return nil, err - } - return &Transaction{sig}, nil -} - -// CallOpts is the collection of options to fine tune a contract call request. -type CallOpts struct { - opts bind.CallOpts -} - -// NewCallOpts creates a new option set for contract calls. -func NewCallOpts() *CallOpts { - return new(CallOpts) -} - -func (opts *CallOpts) IsPending() bool { return opts.opts.Pending } -func (opts *CallOpts) GetGasLimit() int64 { return 0 /* TODO(karalabe) */ } - -// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) -// Even then it's awkward to unpack the subtleties of a Go context out to Java. -// func (opts *CallOpts) GetContext() *Context { return &Context{opts.opts.Context} } - -func (opts *CallOpts) SetPending(pending bool) { opts.opts.Pending = pending } -func (opts *CallOpts) SetGasLimit(limit int64) { /* TODO(karalabe) */ } -func (opts *CallOpts) SetContext(context *Context) { opts.opts.Context = context.context } -func (opts *CallOpts) SetFrom(addr *Address) { opts.opts.From = addr.address } - -// TransactOpts is the collection of authorization data required to create a -// valid Ethereum transaction. -type TransactOpts struct { - opts bind.TransactOpts -} - -// NewTransactOpts creates a new option set for contract transaction. -func NewTransactOpts() *TransactOpts { - return new(TransactOpts) -} - -// NewKeyedTransactOpts is a utility method to easily create a transaction signer -// from a single private key. -func NewKeyedTransactOpts(keyJson []byte, passphrase string, chainID *big.Int) (*TransactOpts, error) { - key, err := keystore.DecryptKey(keyJson, passphrase) - if err != nil { - return nil, err - } - auth, err := bind.NewKeyedTransactorWithChainID(key.PrivateKey, chainID) - if err != nil { - return nil, err - } - return &TransactOpts{*auth}, nil -} - -func (opts *TransactOpts) GetFrom() *Address { return &Address{opts.opts.From} } -func (opts *TransactOpts) GetNonce() int64 { return opts.opts.Nonce.Int64() } -func (opts *TransactOpts) GetValue() *BigInt { return &BigInt{opts.opts.Value} } -func (opts *TransactOpts) GetGasPrice() *BigInt { return &BigInt{opts.opts.GasPrice} } -func (opts *TransactOpts) GetGasLimit() int64 { return int64(opts.opts.GasLimit) } - -// GetSigner cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) -// func (opts *TransactOpts) GetSigner() Signer { return &signer{opts.opts.Signer} } - -// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) -// Even then it's awkward to unpack the subtleties of a Go context out to Java. -//func (opts *TransactOpts) GetContext() *Context { return &Context{opts.opts.Context} } - -func (opts *TransactOpts) SetFrom(from *Address) { opts.opts.From = from.address } -func (opts *TransactOpts) SetNonce(nonce int64) { opts.opts.Nonce = big.NewInt(nonce) } -func (opts *TransactOpts) SetSigner(s Signer) { - opts.opts.Signer = func(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { - sig, err := s.Sign(&Address{addr}, &Transaction{tx}) - if err != nil { - return nil, err - } - return sig.tx, nil - } -} -func (opts *TransactOpts) SetValue(value *BigInt) { opts.opts.Value = value.bigint } -func (opts *TransactOpts) SetGasPrice(price *BigInt) { opts.opts.GasPrice = price.bigint } -func (opts *TransactOpts) SetGasLimit(limit int64) { opts.opts.GasLimit = uint64(limit) } -func (opts *TransactOpts) SetContext(context *Context) { opts.opts.Context = context.context } - -// BoundContract is the base wrapper object that reflects a contract on the -// Ethereum network. It contains a collection of methods that are used by the -// higher level contract bindings to operate. -type BoundContract struct { - contract *bind.BoundContract - address common.Address - deployer *types.Transaction -} - -// DeployContract deploys a contract onto the Ethereum blockchain and binds the -// deployment address with a wrapper. -func DeployContract(opts *TransactOpts, abiJSON string, bytecode []byte, client *EthereumClient, args *Interfaces) (contract *BoundContract, _ error) { - // Deploy the contract to the network - parsed, err := abi.JSON(strings.NewReader(abiJSON)) - if err != nil { - return nil, err - } - addr, tx, bound, err := bind.DeployContract(&opts.opts, parsed, common.CopyBytes(bytecode), client.client, args.objects...) - if err != nil { - return nil, err - } - return &BoundContract{ - contract: bound, - address: addr, - deployer: tx, - }, nil -} - -// BindContract creates a low level contract interface through which calls and -// transactions may be made through. -func BindContract(address *Address, abiJSON string, client *EthereumClient) (contract *BoundContract, _ error) { - parsed, err := abi.JSON(strings.NewReader(abiJSON)) - if err != nil { - return nil, err - } - return &BoundContract{ - contract: bind.NewBoundContract(address.address, parsed, client.client, client.client, client.client), - address: address.address, - }, nil -} - -func (c *BoundContract) GetAddress() *Address { return &Address{c.address} } -func (c *BoundContract) GetDeployer() *Transaction { - if c.deployer == nil { - return nil - } - return &Transaction{c.deployer} -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. -func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error { - results := make([]interface{}, len(out.objects)) - copy(results, out.objects) - if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil { - return err - } - copy(out.objects, results) - return nil -} - -// Transact invokes the (paid) contract method with params as input values. -func (c *BoundContract) Transact(opts *TransactOpts, method string, args *Interfaces) (tx *Transaction, _ error) { - rawTx, err := c.contract.Transact(&opts.opts, method, args.objects...) - if err != nil { - return nil, err - } - return &Transaction{rawTx}, nil -} - -// RawTransact invokes the (paid) contract method with raw calldata as input values. -func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (tx *Transaction, _ error) { - rawTx, err := c.contract.RawTransact(&opts.opts, calldata) - if err != nil { - return nil, err - } - return &Transaction{rawTx}, nil -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (c *BoundContract) Transfer(opts *TransactOpts) (tx *Transaction, _ error) { - rawTx, err := c.contract.Transfer(&opts.opts) - if err != nil { - return nil, err - } - return &Transaction{rawTx}, nil -}
diff --git go-ethereum/mobile/interface.go celo/mobile/interface.go deleted file mode 100644 index 93e2ee4424ec33f8cb9a95c94c67010197c157a0..0000000000000000000000000000000000000000 --- go-ethereum/mobile/interface.go +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains perverted wrappers to allow crossing over empty interfaces. - -package geth - -import ( - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/common" -) - -// Interface represents a wrapped version of Go's interface{}, with the capacity -// to store arbitrary data types. -// -// Since it's impossible to get the arbitrary-ness converted between Go and mobile -// platforms, we're using explicit getters and setters for the conversions. There -// is of course no point in enumerating everything, just enough to support the -// contract bindins requiring client side generated code. -type Interface struct { - object interface{} -} - -// NewInterface creates a new empty interface that can be used to pass around -// generic types. -func NewInterface() *Interface { - return new(Interface) -} - -func (i *Interface) SetBool(b bool) { i.object = &b } -func (i *Interface) SetBools(bs *Bools) { i.object = &bs.bools } -func (i *Interface) SetString(str string) { i.object = &str } -func (i *Interface) SetStrings(strs *Strings) { i.object = &strs.strs } -func (i *Interface) SetBinary(binary []byte) { b := common.CopyBytes(binary); i.object = &b } -func (i *Interface) SetBinaries(binaries *Binaries) { i.object = &binaries.binaries } -func (i *Interface) SetAddress(address *Address) { i.object = &address.address } -func (i *Interface) SetAddresses(addrs *Addresses) { i.object = &addrs.addresses } -func (i *Interface) SetHash(hash *Hash) { i.object = &hash.hash } -func (i *Interface) SetHashes(hashes *Hashes) { i.object = &hashes.hashes } -func (i *Interface) SetInt8(n int8) { i.object = &n } -func (i *Interface) SetInt16(n int16) { i.object = &n } -func (i *Interface) SetInt32(n int32) { i.object = &n } -func (i *Interface) SetInt64(n int64) { i.object = &n } -func (i *Interface) SetInt8s(bigints *BigInts) { - ints := make([]int8, 0, bigints.Size()) - for _, bi := range bigints.bigints { - ints = append(ints, int8(bi.Int64())) - } - i.object = &ints -} -func (i *Interface) SetInt16s(bigints *BigInts) { - ints := make([]int16, 0, bigints.Size()) - for _, bi := range bigints.bigints { - ints = append(ints, int16(bi.Int64())) - } - i.object = &ints -} -func (i *Interface) SetInt32s(bigints *BigInts) { - ints := make([]int32, 0, bigints.Size()) - for _, bi := range bigints.bigints { - ints = append(ints, int32(bi.Int64())) - } - i.object = &ints -} -func (i *Interface) SetInt64s(bigints *BigInts) { - ints := make([]int64, 0, bigints.Size()) - for _, bi := range bigints.bigints { - ints = append(ints, bi.Int64()) - } - i.object = &ints -} -func (i *Interface) SetUint8(bigint *BigInt) { n := uint8(bigint.bigint.Uint64()); i.object = &n } -func (i *Interface) SetUint16(bigint *BigInt) { n := uint16(bigint.bigint.Uint64()); i.object = &n } -func (i *Interface) SetUint32(bigint *BigInt) { n := uint32(bigint.bigint.Uint64()); i.object = &n } -func (i *Interface) SetUint64(bigint *BigInt) { n := bigint.bigint.Uint64(); i.object = &n } -func (i *Interface) SetUint8s(bigints *BigInts) { - ints := make([]uint8, 0, bigints.Size()) - for _, bi := range bigints.bigints { - ints = append(ints, uint8(bi.Uint64())) - } - i.object = &ints -} -func (i *Interface) SetUint16s(bigints *BigInts) { - ints := make([]uint16, 0, bigints.Size()) - for _, bi := range bigints.bigints { - ints = append(ints, uint16(bi.Uint64())) - } - i.object = &ints -} -func (i *Interface) SetUint32s(bigints *BigInts) { - ints := make([]uint32, 0, bigints.Size()) - for _, bi := range bigints.bigints { - ints = append(ints, uint32(bi.Uint64())) - } - i.object = &ints -} -func (i *Interface) SetUint64s(bigints *BigInts) { - ints := make([]uint64, 0, bigints.Size()) - for _, bi := range bigints.bigints { - ints = append(ints, bi.Uint64()) - } - i.object = &ints -} -func (i *Interface) SetBigInt(bigint *BigInt) { i.object = &bigint.bigint } -func (i *Interface) SetBigInts(bigints *BigInts) { i.object = &bigints.bigints } - -func (i *Interface) SetDefaultBool() { i.object = new(bool) } -func (i *Interface) SetDefaultBools() { i.object = new([]bool) } -func (i *Interface) SetDefaultString() { i.object = new(string) } -func (i *Interface) SetDefaultStrings() { i.object = new([]string) } -func (i *Interface) SetDefaultBinary() { i.object = new([]byte) } -func (i *Interface) SetDefaultBinaries() { i.object = new([][]byte) } -func (i *Interface) SetDefaultAddress() { i.object = new(common.Address) } -func (i *Interface) SetDefaultAddresses() { i.object = new([]common.Address) } -func (i *Interface) SetDefaultHash() { i.object = new(common.Hash) } -func (i *Interface) SetDefaultHashes() { i.object = new([]common.Hash) } -func (i *Interface) SetDefaultInt8() { i.object = new(int8) } -func (i *Interface) SetDefaultInt8s() { i.object = new([]int8) } -func (i *Interface) SetDefaultInt16() { i.object = new(int16) } -func (i *Interface) SetDefaultInt16s() { i.object = new([]int16) } -func (i *Interface) SetDefaultInt32() { i.object = new(int32) } -func (i *Interface) SetDefaultInt32s() { i.object = new([]int32) } -func (i *Interface) SetDefaultInt64() { i.object = new(int64) } -func (i *Interface) SetDefaultInt64s() { i.object = new([]int64) } -func (i *Interface) SetDefaultUint8() { i.object = new(uint8) } -func (i *Interface) SetDefaultUint8s() { i.object = new([]uint8) } -func (i *Interface) SetDefaultUint16() { i.object = new(uint16) } -func (i *Interface) SetDefaultUint16s() { i.object = new([]uint16) } -func (i *Interface) SetDefaultUint32() { i.object = new(uint32) } -func (i *Interface) SetDefaultUint32s() { i.object = new([]uint32) } -func (i *Interface) SetDefaultUint64() { i.object = new(uint64) } -func (i *Interface) SetDefaultUint64s() { i.object = new([]uint64) } -func (i *Interface) SetDefaultBigInt() { i.object = new(*big.Int) } -func (i *Interface) SetDefaultBigInts() { i.object = new([]*big.Int) } - -func (i *Interface) GetBool() bool { return *i.object.(*bool) } -func (i *Interface) GetBools() *Bools { return &Bools{*i.object.(*[]bool)} } -func (i *Interface) GetString() string { return *i.object.(*string) } -func (i *Interface) GetStrings() *Strings { return &Strings{*i.object.(*[]string)} } -func (i *Interface) GetBinary() []byte { return *i.object.(*[]byte) } -func (i *Interface) GetBinaries() *Binaries { return &Binaries{*i.object.(*[][]byte)} } -func (i *Interface) GetAddress() *Address { return &Address{*i.object.(*common.Address)} } -func (i *Interface) GetAddresses() *Addresses { return &Addresses{*i.object.(*[]common.Address)} } -func (i *Interface) GetHash() *Hash { return &Hash{*i.object.(*common.Hash)} } -func (i *Interface) GetHashes() *Hashes { return &Hashes{*i.object.(*[]common.Hash)} } -func (i *Interface) GetInt8() int8 { return *i.object.(*int8) } -func (i *Interface) GetInt16() int16 { return *i.object.(*int16) } -func (i *Interface) GetInt32() int32 { return *i.object.(*int32) } -func (i *Interface) GetInt64() int64 { return *i.object.(*int64) } -func (i *Interface) GetInt8s() *BigInts { - val := i.object.(*[]int8) - bigints := NewBigInts(len(*val)) - for i, v := range *val { - bigints.Set(i, &BigInt{new(big.Int).SetInt64(int64(v))}) - } - return bigints -} -func (i *Interface) GetInt16s() *BigInts { - val := i.object.(*[]int16) - bigints := NewBigInts(len(*val)) - for i, v := range *val { - bigints.Set(i, &BigInt{new(big.Int).SetInt64(int64(v))}) - } - return bigints -} -func (i *Interface) GetInt32s() *BigInts { - val := i.object.(*[]int32) - bigints := NewBigInts(len(*val)) - for i, v := range *val { - bigints.Set(i, &BigInt{new(big.Int).SetInt64(int64(v))}) - } - return bigints -} -func (i *Interface) GetInt64s() *BigInts { - val := i.object.(*[]int64) - bigints := NewBigInts(len(*val)) - for i, v := range *val { - bigints.Set(i, &BigInt{new(big.Int).SetInt64(v)}) - } - return bigints -} -func (i *Interface) GetUint8() *BigInt { - return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint8)))} -} -func (i *Interface) GetUint16() *BigInt { - return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint16)))} -} -func (i *Interface) GetUint32() *BigInt { - return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint32)))} -} -func (i *Interface) GetUint64() *BigInt { - return &BigInt{new(big.Int).SetUint64(*i.object.(*uint64))} -} -func (i *Interface) GetUint8s() *BigInts { - val := i.object.(*[]uint8) - bigints := NewBigInts(len(*val)) - for i, v := range *val { - bigints.Set(i, &BigInt{new(big.Int).SetUint64(uint64(v))}) - } - return bigints -} -func (i *Interface) GetUint16s() *BigInts { - val := i.object.(*[]uint16) - bigints := NewBigInts(len(*val)) - for i, v := range *val { - bigints.Set(i, &BigInt{new(big.Int).SetUint64(uint64(v))}) - } - return bigints -} -func (i *Interface) GetUint32s() *BigInts { - val := i.object.(*[]uint32) - bigints := NewBigInts(len(*val)) - for i, v := range *val { - bigints.Set(i, &BigInt{new(big.Int).SetUint64(uint64(v))}) - } - return bigints -} -func (i *Interface) GetUint64s() *BigInts { - val := i.object.(*[]uint64) - bigints := NewBigInts(len(*val)) - for i, v := range *val { - bigints.Set(i, &BigInt{new(big.Int).SetUint64(v)}) - } - return bigints -} -func (i *Interface) GetBigInt() *BigInt { return &BigInt{*i.object.(**big.Int)} } -func (i *Interface) GetBigInts() *BigInts { return &BigInts{*i.object.(*[]*big.Int)} } - -// Interfaces is a slices of wrapped generic objects. -type Interfaces struct { - objects []interface{} -} - -// NewInterfaces creates a slice of uninitialized interfaces. -func NewInterfaces(size int) *Interfaces { - return &Interfaces{objects: make([]interface{}, size)} -} - -// Size returns the number of interfaces in the slice. -func (i *Interfaces) Size() int { - return len(i.objects) -} - -// Get returns the bigint at the given index from the slice. -// Notably the returned value can be changed without affecting the -// interfaces itself. -func (i *Interfaces) Get(index int) (iface *Interface, _ error) { - if index < 0 || index >= len(i.objects) { - return nil, errors.New("index out of bounds") - } - return &Interface{object: i.objects[index]}, nil -} - -// Set sets the big int at the given index in the slice. -func (i *Interfaces) Set(index int, object *Interface) error { - if index < 0 || index >= len(i.objects) { - return errors.New("index out of bounds") - } - i.objects[index] = object.object - return nil -}
diff --git go-ethereum/mobile/accounts.go celo/mobile/accounts.go deleted file mode 100644 index 4d979bffff5dae63968c19401e85d7274e929905..0000000000000000000000000000000000000000 --- go-ethereum/mobile/accounts.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains all the wrappers from the accounts package to support client side key -// management on mobile platforms. - -package geth - -import ( - "errors" - "time" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -const ( - // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB - // memory and taking approximately 1s CPU time on a modern processor. - StandardScryptN = int(keystore.StandardScryptN) - - // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB - // memory and taking approximately 1s CPU time on a modern processor. - StandardScryptP = int(keystore.StandardScryptP) - - // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB - // memory and taking approximately 100ms CPU time on a modern processor. - LightScryptN = int(keystore.LightScryptN) - - // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB - // memory and taking approximately 100ms CPU time on a modern processor. - LightScryptP = int(keystore.LightScryptP) -) - -// Account represents a stored key. -type Account struct{ account accounts.Account } - -// Accounts represents a slice of accounts. -type Accounts struct{ accounts []accounts.Account } - -// Size returns the number of accounts in the slice. -func (a *Accounts) Size() int { - return len(a.accounts) -} - -// Get returns the account at the given index from the slice. -func (a *Accounts) Get(index int) (account *Account, _ error) { - if index < 0 || index >= len(a.accounts) { - return nil, errors.New("index out of bounds") - } - return &Account{a.accounts[index]}, nil -} - -// Set sets the account at the given index in the slice. -func (a *Accounts) Set(index int, account *Account) error { - if index < 0 || index >= len(a.accounts) { - return errors.New("index out of bounds") - } - a.accounts[index] = account.account - return nil -} - -// GetAddress retrieves the address associated with the account. -func (a *Account) GetAddress() *Address { - return &Address{a.account.Address} -} - -// GetURL retrieves the canonical URL of the account. -func (a *Account) GetURL() string { - return a.account.URL.String() -} - -// KeyStore manages a key storage directory on disk. -type KeyStore struct{ keystore *keystore.KeyStore } - -// NewKeyStore creates a keystore for the given directory. -func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore { - return &KeyStore{keystore: keystore.NewKeyStore(keydir, scryptN, scryptP)} -} - -// HasAddress reports whether a key with the given address is present. -func (ks *KeyStore) HasAddress(address *Address) bool { - return ks.keystore.HasAddress(address.address) -} - -// GetAccounts returns all key files present in the directory. -func (ks *KeyStore) GetAccounts() *Accounts { - return &Accounts{ks.keystore.Accounts()} -} - -// DeleteAccount deletes the key matched by account if the passphrase is correct. -// If a contains no filename, the address must match a unique key. -func (ks *KeyStore) DeleteAccount(account *Account, passphrase string) error { - return ks.keystore.Delete(account.account, passphrase) -} - -// SignHash calculates a ECDSA signature for the given hash. The produced signature -// is in the [R || S || V] format where V is 0 or 1. -func (ks *KeyStore) SignHash(address *Address, hash []byte) (signature []byte, _ error) { - return ks.keystore.SignHash(accounts.Account{Address: address.address}, common.CopyBytes(hash)) -} - -// SignTx signs the given transaction with the requested account. -func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (*Transaction, error) { - if chainID == nil { // Null passed from mobile app - chainID = new(BigInt) - } - signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint) - if err != nil { - return nil, err - } - return &Transaction{signed}, nil -} - -// SignHashPassphrase signs hash if the private key matching the given address can -// be decrypted with the given passphrase. The produced signature is in the -// [R || S || V] format where V is 0 or 1. -func (ks *KeyStore) SignHashPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) { - return ks.keystore.SignHashWithPassphrase(account.account, passphrase, common.CopyBytes(hash)) -} - -// SignTxPassphrase signs the transaction if the private key matching the -// given address can be decrypted with the given passphrase. -func (ks *KeyStore) SignTxPassphrase(account *Account, passphrase string, tx *Transaction, chainID *BigInt) (*Transaction, error) { - if chainID == nil { // Null passed from mobile app - chainID = new(BigInt) - } - signed, err := ks.keystore.SignTxWithPassphrase(account.account, passphrase, tx.tx, chainID.bigint) - if err != nil { - return nil, err - } - return &Transaction{signed}, nil -} - -// Unlock unlocks the given account indefinitely. -func (ks *KeyStore) Unlock(account *Account, passphrase string) error { - return ks.keystore.TimedUnlock(account.account, passphrase, 0) -} - -// Lock removes the private key with the given address from memory. -func (ks *KeyStore) Lock(address *Address) error { - return ks.keystore.Lock(address.address) -} - -// TimedUnlock unlocks the given account with the passphrase. The account stays -// unlocked for the duration of timeout (nanoseconds). A timeout of 0 unlocks the -// account until the program exits. The account must match a unique key file. -// -// If the account address is already unlocked for a duration, TimedUnlock extends or -// shortens the active unlock timeout. If the address was previously unlocked -// indefinitely the timeout is not altered. -func (ks *KeyStore) TimedUnlock(account *Account, passphrase string, timeout int64) error { - return ks.keystore.TimedUnlock(account.account, passphrase, time.Duration(timeout)) -} - -// NewAccount generates a new key and stores it into the key directory, -// encrypting it with the passphrase. -func (ks *KeyStore) NewAccount(passphrase string) (*Account, error) { - account, err := ks.keystore.NewAccount(passphrase) - if err != nil { - return nil, err - } - return &Account{account}, nil -} - -// UpdateAccount changes the passphrase of an existing account. -func (ks *KeyStore) UpdateAccount(account *Account, passphrase, newPassphrase string) error { - return ks.keystore.Update(account.account, passphrase, newPassphrase) -} - -// ExportKey exports as a JSON key, encrypted with newPassphrase. -func (ks *KeyStore) ExportKey(account *Account, passphrase, newPassphrase string) (key []byte, _ error) { - return ks.keystore.Export(account.account, passphrase, newPassphrase) -} - -// ImportKey stores the given encrypted JSON key into the key directory. -func (ks *KeyStore) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (account *Account, _ error) { - acc, err := ks.keystore.Import(common.CopyBytes(keyJSON), passphrase, newPassphrase) - if err != nil { - return nil, err - } - return &Account{acc}, nil -} - -// ImportECDSAKey stores the given encrypted JSON key into the key directory. -func (ks *KeyStore) ImportECDSAKey(key []byte, passphrase string) (account *Account, _ error) { - privkey, err := crypto.ToECDSA(common.CopyBytes(key)) - if err != nil { - return nil, err - } - acc, err := ks.keystore.ImportECDSA(privkey, passphrase) - if err != nil { - return nil, err - } - return &Account{acc}, nil -} - -// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores -// a key file in the key directory. The key file is encrypted with the same passphrase. -func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (ccount *Account, _ error) { - account, err := ks.keystore.ImportPreSaleKey(common.CopyBytes(keyJSON), passphrase) - if err != nil { - return nil, err - } - return &Account{account}, nil -}
diff --git go-ethereum/mobile/p2p.go celo/mobile/p2p.go deleted file mode 100644 index 04fb4e23206c81789fe449dcb6daed489ba2cfc4..0000000000000000000000000000000000000000 --- go-ethereum/mobile/p2p.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains wrappers for the p2p package. - -package geth - -import ( - "errors" - - "github.com/ethereum/go-ethereum/p2p" -) - -// NodeInfo represents pi short summary of the information known about the host. -type NodeInfo struct { - info *p2p.NodeInfo -} - -func (ni *NodeInfo) GetID() string { return ni.info.ID } -func (ni *NodeInfo) GetName() string { return ni.info.Name } -func (ni *NodeInfo) GetEnode() string { return ni.info.Enode } -func (ni *NodeInfo) GetIP() string { return ni.info.IP } -func (ni *NodeInfo) GetDiscoveryPort() int { return ni.info.Ports.Discovery } -func (ni *NodeInfo) GetListenerPort() int { return ni.info.Ports.Listener } -func (ni *NodeInfo) GetListenerAddress() string { return ni.info.ListenAddr } -func (ni *NodeInfo) GetProtocols() *Strings { - protos := []string{} - for proto := range ni.info.Protocols { - protos = append(protos, proto) - } - return &Strings{protos} -} - -// PeerInfo represents pi short summary of the information known about pi connected peer. -type PeerInfo struct { - info *p2p.PeerInfo -} - -func (pi *PeerInfo) GetID() string { return pi.info.ID } -func (pi *PeerInfo) GetName() string { return pi.info.Name } -func (pi *PeerInfo) GetCaps() *Strings { return &Strings{pi.info.Caps} } -func (pi *PeerInfo) GetLocalAddress() string { return pi.info.Network.LocalAddress } -func (pi *PeerInfo) GetRemoteAddress() string { return pi.info.Network.RemoteAddress } - -// PeerInfos represents a slice of infos about remote peers. -type PeerInfos struct { - infos []*p2p.PeerInfo -} - -// Size returns the number of peer info entries in the slice. -func (pi *PeerInfos) Size() int { - return len(pi.infos) -} - -// Get returns the peer info at the given index from the slice. -func (pi *PeerInfos) Get(index int) (info *PeerInfo, _ error) { - if index < 0 || index >= len(pi.infos) { - return nil, errors.New("index out of bounds") - } - return &PeerInfo{pi.infos[index]}, nil -}
diff --git go-ethereum/mobile/primitives.go celo/mobile/primitives.go deleted file mode 100644 index 7e1ab26ef03974993442c47575c11a97824af009..0000000000000000000000000000000000000000 --- go-ethereum/mobile/primitives.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains various wrappers for primitive types. - -package geth - -import ( - "errors" - "fmt" - - "github.com/ethereum/go-ethereum/common" -) - -// Strings represents s slice of strs. -type Strings struct{ strs []string } - -// Size returns the number of strs in the slice. -func (s *Strings) Size() int { - return len(s.strs) -} - -// Get returns the string at the given index from the slice. -func (s *Strings) Get(index int) (str string, _ error) { - if index < 0 || index >= len(s.strs) { - return "", errors.New("index out of bounds") - } - return s.strs[index], nil -} - -// Set sets the string at the given index in the slice. -func (s *Strings) Set(index int, str string) error { - if index < 0 || index >= len(s.strs) { - return errors.New("index out of bounds") - } - s.strs[index] = str - return nil -} - -// String implements the Stringer interface. -func (s *Strings) String() string { - return fmt.Sprintf("%v", s.strs) -} - -// Bools represents a slice of bool. -type Bools struct{ bools []bool } - -// Size returns the number of bool in the slice. -func (bs *Bools) Size() int { - return len(bs.bools) -} - -// Get returns the bool at the given index from the slice. -func (bs *Bools) Get(index int) (b bool, _ error) { - if index < 0 || index >= len(bs.bools) { - return false, errors.New("index out of bounds") - } - return bs.bools[index], nil -} - -// Set sets the bool at the given index in the slice. -func (bs *Bools) Set(index int, b bool) error { - if index < 0 || index >= len(bs.bools) { - return errors.New("index out of bounds") - } - bs.bools[index] = b - return nil -} - -// String implements the Stringer interface. -func (bs *Bools) String() string { - return fmt.Sprintf("%v", bs.bools) -} - -// Binaries represents a slice of byte slice -type Binaries struct{ binaries [][]byte } - -// Size returns the number of byte slice in the slice. -func (bs *Binaries) Size() int { - return len(bs.binaries) -} - -// Get returns the byte slice at the given index from the slice. -func (bs *Binaries) Get(index int) (binary []byte, _ error) { - if index < 0 || index >= len(bs.binaries) { - return nil, errors.New("index out of bounds") - } - return common.CopyBytes(bs.binaries[index]), nil -} - -// Set sets the byte slice at the given index in the slice. -func (bs *Binaries) Set(index int, binary []byte) error { - if index < 0 || index >= len(bs.binaries) { - return errors.New("index out of bounds") - } - bs.binaries[index] = common.CopyBytes(binary) - return nil -} - -// String implements the Stringer interface. -func (bs *Binaries) String() string { - return fmt.Sprintf("%v", bs.binaries) -}
diff --git go-ethereum/mobile/ethereum.go celo/mobile/ethereum.go deleted file mode 100644 index 2b2da57d0e26b4acd659358b864e90076920b141..0000000000000000000000000000000000000000 --- go-ethereum/mobile/ethereum.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains all the wrappers from the go-ethereum root package. - -package geth - -import ( - "errors" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" -) - -// Subscription represents an event subscription where events are -// delivered on a data channel. -type Subscription struct { - sub ethereum.Subscription -} - -// Unsubscribe cancels the sending of events to the data channel -// and closes the error channel. -func (s *Subscription) Unsubscribe() { - s.sub.Unsubscribe() -} - -// CallMsg contains parameters for contract calls. -type CallMsg struct { - msg ethereum.CallMsg -} - -// NewCallMsg creates an empty contract call parameter list. -func NewCallMsg() *CallMsg { - return new(CallMsg) -} - -func (msg *CallMsg) GetFrom() *Address { return &Address{msg.msg.From} } -func (msg *CallMsg) GetGas() int64 { return int64(msg.msg.Gas) } -func (msg *CallMsg) GetGasPrice() *BigInt { return &BigInt{msg.msg.GasPrice} } -func (msg *CallMsg) GetValue() *BigInt { return &BigInt{msg.msg.Value} } -func (msg *CallMsg) GetData() []byte { return msg.msg.Data } -func (msg *CallMsg) GetTo() *Address { - if to := msg.msg.To; to != nil { - return &Address{*msg.msg.To} - } - return nil -} - -func (msg *CallMsg) SetFrom(address *Address) { msg.msg.From = address.address } -func (msg *CallMsg) SetGas(gas int64) { msg.msg.Gas = uint64(gas) } -func (msg *CallMsg) SetGasPrice(price *BigInt) { msg.msg.GasPrice = price.bigint } -func (msg *CallMsg) SetValue(value *BigInt) { msg.msg.Value = value.bigint } -func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = common.CopyBytes(data) } -func (msg *CallMsg) SetTo(address *Address) { - if address == nil { - msg.msg.To = nil - return - } - msg.msg.To = &address.address -} - -// SyncProgress gives progress indications when the node is synchronising with -// the Ethereum network. -type SyncProgress struct { - progress ethereum.SyncProgress -} - -func (p *SyncProgress) GetStartingBlock() int64 { return int64(p.progress.StartingBlock) } -func (p *SyncProgress) GetCurrentBlock() int64 { return int64(p.progress.CurrentBlock) } -func (p *SyncProgress) GetHighestBlock() int64 { return int64(p.progress.HighestBlock) } -func (p *SyncProgress) GetPulledStates() int64 { return int64(p.progress.PulledStates) } -func (p *SyncProgress) GetKnownStates() int64 { return int64(p.progress.KnownStates) } - -// Topics is a set of topic lists to filter events with. -type Topics struct{ topics [][]common.Hash } - -// NewTopics creates a slice of uninitialized Topics. -func NewTopics(size int) *Topics { - return &Topics{ - topics: make([][]common.Hash, size), - } -} - -// NewTopicsEmpty creates an empty slice of Topics values. -func NewTopicsEmpty() *Topics { - return NewTopics(0) -} - -// Size returns the number of topic lists inside the set -func (t *Topics) Size() int { - return len(t.topics) -} - -// Get returns the topic list at the given index from the slice. -func (t *Topics) Get(index int) (hashes *Hashes, _ error) { - if index < 0 || index >= len(t.topics) { - return nil, errors.New("index out of bounds") - } - return &Hashes{t.topics[index]}, nil -} - -// Set sets the topic list at the given index in the slice. -func (t *Topics) Set(index int, topics *Hashes) error { - if index < 0 || index >= len(t.topics) { - return errors.New("index out of bounds") - } - t.topics[index] = topics.hashes - return nil -} - -// Append adds a new topic list to the end of the slice. -func (t *Topics) Append(topics *Hashes) { - t.topics = append(t.topics, topics.hashes) -} - -// FilterQuery contains options for contract log filtering. -type FilterQuery struct { - query ethereum.FilterQuery -} - -// NewFilterQuery creates an empty filter query for contract log filtering. -func NewFilterQuery() *FilterQuery { - return new(FilterQuery) -} - -func (fq *FilterQuery) GetFromBlock() *BigInt { return &BigInt{fq.query.FromBlock} } -func (fq *FilterQuery) GetToBlock() *BigInt { return &BigInt{fq.query.ToBlock} } -func (fq *FilterQuery) GetAddresses() *Addresses { return &Addresses{fq.query.Addresses} } -func (fq *FilterQuery) GetTopics() *Topics { return &Topics{fq.query.Topics} } - -func (fq *FilterQuery) SetFromBlock(fromBlock *BigInt) { fq.query.FromBlock = fromBlock.bigint } -func (fq *FilterQuery) SetToBlock(toBlock *BigInt) { fq.query.ToBlock = toBlock.bigint } -func (fq *FilterQuery) SetAddresses(addresses *Addresses) { fq.query.Addresses = addresses.addresses } -func (fq *FilterQuery) SetTopics(topics *Topics) { fq.query.Topics = topics.topics }
diff --git go-ethereum/mobile/interface_test.go celo/mobile/interface_test.go deleted file mode 100644 index 8ef9298ffa7ffe5f14e1be4e73789220b391b277..0000000000000000000000000000000000000000 --- go-ethereum/mobile/interface_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package geth - -import ( - "fmt" - "math/big" - "reflect" - "testing" - - "github.com/ethereum/go-ethereum/common" -) - -func TestInterfaceGetSet(t *testing.T) { - var tests = []struct { - method string - input interface{} - expect interface{} - }{ - {"Bool", true, true}, - {"Bool", false, false}, - {"Bools", &Bools{[]bool{false, true}}, &Bools{[]bool{false, true}}}, - {"String", "go-ethereum", "go-ethereum"}, - {"Strings", &Strings{strs: []string{"hello", "world"}}, &Strings{strs: []string{"hello", "world"}}}, - {"Binary", []byte{0x01, 0x02}, []byte{0x01, 0x02}}, - {"Binaries", &Binaries{[][]byte{{0x01, 0x02}, {0x03, 0x04}}}, &Binaries{[][]byte{{0x01, 0x02}, {0x03, 0x04}}}}, - {"Address", &Address{common.HexToAddress("deadbeef")}, &Address{common.HexToAddress("deadbeef")}}, - {"Addresses", &Addresses{[]common.Address{common.HexToAddress("deadbeef"), common.HexToAddress("cafebabe")}}, &Addresses{[]common.Address{common.HexToAddress("deadbeef"), common.HexToAddress("cafebabe")}}}, - {"Hash", &Hash{common.HexToHash("deadbeef")}, &Hash{common.HexToHash("deadbeef")}}, - {"Hashes", &Hashes{[]common.Hash{common.HexToHash("deadbeef"), common.HexToHash("cafebabe")}}, &Hashes{[]common.Hash{common.HexToHash("deadbeef"), common.HexToHash("cafebabe")}}}, - {"Int8", int8(1), int8(1)}, - {"Int16", int16(1), int16(1)}, - {"Int32", int32(1), int32(1)}, - {"Int64", int64(1), int64(1)}, - {"Int8s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}}, - {"Int16s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}}, - {"Int32s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}}, - {"Int64s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}}, - {"Uint8", NewBigInt(1), NewBigInt(1)}, - {"Uint16", NewBigInt(1), NewBigInt(1)}, - {"Uint32", NewBigInt(1), NewBigInt(1)}, - {"Uint64", NewBigInt(1), NewBigInt(1)}, - {"Uint8s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}}, - {"Uint16s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}}, - {"Uint32s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}}, - {"Uint64s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}}, - {"BigInt", NewBigInt(1), NewBigInt(1)}, - {"BigInts", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}}, - } - - args := NewInterfaces(len(tests)) - - callFn := func(receiver interface{}, method string, arg interface{}) interface{} { - rval := reflect.ValueOf(receiver) - rval.MethodByName(fmt.Sprintf("Set%s", method)).Call([]reflect.Value{reflect.ValueOf(arg)}) - res := rval.MethodByName(fmt.Sprintf("Get%s", method)).Call(nil) - if len(res) > 0 { - return res[0].Interface() - } - return nil - } - - for index, c := range tests { - // In theory the change of iface shouldn't effect the args value - iface, _ := args.Get(index) - result := callFn(iface, c.method, c.input) - if !reflect.DeepEqual(result, c.expect) { - t.Errorf("Interface get/set mismatch, want %v, got %v", c.expect, result) - } - // Check whether the underlying value in args is still zero - iface, _ = args.Get(index) - if iface.object != nil { - t.Error("Get operation is not write safe") - } - } -}
diff --git go-ethereum/mobile/params.go celo/mobile/params.go deleted file mode 100644 index 0fc197c9e5089cb252e38c784234a1b0c8ad2812..0000000000000000000000000000000000000000 --- go-ethereum/mobile/params.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains all the wrappers from the params package. - -package geth - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/params" -) - -// MainnetGenesis returns the JSON spec to use for the main Ethereum network. It -// is actually empty since that defaults to the hard coded binary genesis block. -func MainnetGenesis() string { - return "" -} - -// RopstenGenesis returns the JSON spec to use for the Ropsten test network. -func RopstenGenesis() string { - enc, err := json.Marshal(core.DefaultRopstenGenesisBlock()) - if err != nil { - panic(err) - } - return string(enc) -} - -// RinkebyGenesis returns the JSON spec to use for the Rinkeby test network -func RinkebyGenesis() string { - enc, err := json.Marshal(core.DefaultRinkebyGenesisBlock()) - if err != nil { - panic(err) - } - return string(enc) -} - -// GoerliGenesis returns the JSON spec to use for the Goerli test network -func GoerliGenesis() string { - enc, err := json.Marshal(core.DefaultGoerliGenesisBlock()) - if err != nil { - panic(err) - } - return string(enc) -} - -// FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated -// by the foundation running the V5 discovery protocol. -func FoundationBootnodes() *Enodes { - nodes := &Enodes{nodes: make([]*enode.Node, len(params.MainnetBootnodes))} - for i, url := range params.MainnetBootnodes { - var err error - nodes.nodes[i], err = enode.Parse(enode.ValidSchemes, url) - if err != nil { - panic("invalid node URL: " + err.Error()) - } - } - return nodes -}
diff --git go-ethereum/mobile/types.go celo/mobile/types.go deleted file mode 100644 index 133f63d09fa51f17a974aeee6051a5ea3da70335..0000000000000000000000000000000000000000 --- go-ethereum/mobile/types.go +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains all the wrappers from the core/types package. - -package geth - -import ( - "encoding/json" - "errors" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" -) - -type jsonEncoder interface { - EncodeJSON() (string, error) -} - -// encodeOrError tries to encode the object into json. -// If the encoding fails the resulting error is returned. -func encodeOrError(encoder jsonEncoder) string { - enc, err := encoder.EncodeJSON() - if err != nil { - return err.Error() - } - return enc -} - -// A Nonce is a 64-bit hash which proves (combined with the mix-hash) that -// a sufficient amount of computation has been carried out on a block. -type Nonce struct { - nonce types.BlockNonce -} - -// GetBytes retrieves the byte representation of the block nonce. -func (n *Nonce) GetBytes() []byte { - return n.nonce[:] -} - -// GetHex retrieves the hex string representation of the block nonce. -func (n *Nonce) GetHex() string { - return fmt.Sprintf("0x%x", n.nonce[:]) -} - -// String returns a printable representation of the nonce. -func (n *Nonce) String() string { - return n.GetHex() -} - -// Bloom represents a 256 bit bloom filter. -type Bloom struct { - bloom types.Bloom -} - -// GetBytes retrieves the byte representation of the bloom filter. -func (b *Bloom) GetBytes() []byte { - return b.bloom[:] -} - -// GetHex retrieves the hex string representation of the bloom filter. -func (b *Bloom) GetHex() string { - return fmt.Sprintf("0x%x", b.bloom[:]) -} - -// String returns a printable representation of the bloom filter. -func (b *Bloom) String() string { - return b.GetHex() -} - -// Header represents a block header in the Ethereum blockchain. -type Header struct { - header *types.Header -} - -// NewHeaderFromRLP parses a header from an RLP data dump. -func NewHeaderFromRLP(data []byte) (*Header, error) { - h := &Header{ - header: new(types.Header), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), h.header); err != nil { - return nil, err - } - return h, nil -} - -// EncodeRLP encodes a header into an RLP data dump. -func (h *Header) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(h.header) -} - -// NewHeaderFromJSON parses a header from a JSON data dump. -func NewHeaderFromJSON(data string) (*Header, error) { - h := &Header{ - header: new(types.Header), - } - if err := json.Unmarshal([]byte(data), h.header); err != nil { - return nil, err - } - return h, nil -} - -// EncodeJSON encodes a header into a JSON data dump. -func (h *Header) EncodeJSON() (string, error) { - data, err := json.Marshal(h.header) - return string(data), err -} - -// String returns a printable representation of the header. -func (h *Header) String() string { - return encodeOrError(h) -} - -func (h *Header) GetParentHash() *Hash { return &Hash{h.header.ParentHash} } -func (h *Header) GetUncleHash() *Hash { return &Hash{h.header.UncleHash} } -func (h *Header) GetCoinbase() *Address { return &Address{h.header.Coinbase} } -func (h *Header) GetRoot() *Hash { return &Hash{h.header.Root} } -func (h *Header) GetTxHash() *Hash { return &Hash{h.header.TxHash} } -func (h *Header) GetReceiptHash() *Hash { return &Hash{h.header.ReceiptHash} } -func (h *Header) GetBloom() *Bloom { return &Bloom{h.header.Bloom} } -func (h *Header) GetDifficulty() *BigInt { return &BigInt{h.header.Difficulty} } -func (h *Header) GetNumber() int64 { return h.header.Number.Int64() } -func (h *Header) GetGasLimit() int64 { return int64(h.header.GasLimit) } -func (h *Header) GetGasUsed() int64 { return int64(h.header.GasUsed) } -func (h *Header) GetTime() int64 { return int64(h.header.Time) } -func (h *Header) GetExtra() []byte { return h.header.Extra } -func (h *Header) GetMixDigest() *Hash { return &Hash{h.header.MixDigest} } -func (h *Header) GetNonce() *Nonce { return &Nonce{h.header.Nonce} } -func (h *Header) GetHash() *Hash { return &Hash{h.header.Hash()} } - -// Headers represents a slice of headers. -type Headers struct{ headers []*types.Header } - -// Size returns the number of headers in the slice. -func (h *Headers) Size() int { - return len(h.headers) -} - -// Get returns the header at the given index from the slice. -func (h *Headers) Get(index int) (header *Header, _ error) { - if index < 0 || index >= len(h.headers) { - return nil, errors.New("index out of bounds") - } - return &Header{h.headers[index]}, nil -} - -// Block represents an entire block in the Ethereum blockchain. -type Block struct { - block *types.Block -} - -// NewBlockFromRLP parses a block from an RLP data dump. -func NewBlockFromRLP(data []byte) (*Block, error) { - b := &Block{ - block: new(types.Block), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), b.block); err != nil { - return nil, err - } - return b, nil -} - -// EncodeRLP encodes a block into an RLP data dump. -func (b *Block) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(b.block) -} - -// NewBlockFromJSON parses a block from a JSON data dump. -func NewBlockFromJSON(data string) (*Block, error) { - b := &Block{ - block: new(types.Block), - } - if err := json.Unmarshal([]byte(data), b.block); err != nil { - return nil, err - } - return b, nil -} - -// EncodeJSON encodes a block into a JSON data dump. -func (b *Block) EncodeJSON() (string, error) { - data, err := json.Marshal(b.block) - return string(data), err -} - -// String returns a printable representation of the block. -func (b *Block) String() string { - return encodeOrError(b) -} - -func (b *Block) GetParentHash() *Hash { return &Hash{b.block.ParentHash()} } -func (b *Block) GetUncleHash() *Hash { return &Hash{b.block.UncleHash()} } -func (b *Block) GetCoinbase() *Address { return &Address{b.block.Coinbase()} } -func (b *Block) GetRoot() *Hash { return &Hash{b.block.Root()} } -func (b *Block) GetTxHash() *Hash { return &Hash{b.block.TxHash()} } -func (b *Block) GetReceiptHash() *Hash { return &Hash{b.block.ReceiptHash()} } -func (b *Block) GetBloom() *Bloom { return &Bloom{b.block.Bloom()} } -func (b *Block) GetDifficulty() *BigInt { return &BigInt{b.block.Difficulty()} } -func (b *Block) GetNumber() int64 { return b.block.Number().Int64() } -func (b *Block) GetGasLimit() int64 { return int64(b.block.GasLimit()) } -func (b *Block) GetGasUsed() int64 { return int64(b.block.GasUsed()) } -func (b *Block) GetTime() int64 { return int64(b.block.Time()) } -func (b *Block) GetExtra() []byte { return b.block.Extra() } -func (b *Block) GetMixDigest() *Hash { return &Hash{b.block.MixDigest()} } -func (b *Block) GetNonce() int64 { return int64(b.block.Nonce()) } -func (b *Block) GetHash() *Hash { return &Hash{b.block.Hash()} } -func (b *Block) GetHeader() *Header { return &Header{b.block.Header()} } -func (b *Block) GetUncles() *Headers { return &Headers{b.block.Uncles()} } -func (b *Block) GetTransactions() *Transactions { return &Transactions{b.block.Transactions()} } -func (b *Block) GetTransaction(hash *Hash) *Transaction { - return &Transaction{b.block.Transaction(hash.hash)} -} - -// Transaction represents a single Ethereum transaction. -type Transaction struct { - tx *types.Transaction -} - -// NewContractCreation creates a new transaction for deploying a new contract with -// the given properties. -func NewContractCreation(nonce int64, amount *BigInt, gasLimit int64, gasPrice *BigInt, data []byte) *Transaction { - return &Transaction{types.NewContractCreation(uint64(nonce), amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data))} -} - -// NewTransaction creates a new transaction with the given properties. Contracts -// can be created by transacting with a nil recipient. -func NewTransaction(nonce int64, to *Address, amount *BigInt, gasLimit int64, gasPrice *BigInt, data []byte) *Transaction { - if to == nil { - return &Transaction{types.NewContractCreation(uint64(nonce), amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data))} - } - return &Transaction{types.NewTransaction(uint64(nonce), to.address, amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data))} -} - -// NewTransactionFromRLP parses a transaction from an RLP data dump. -func NewTransactionFromRLP(data []byte) (*Transaction, error) { - tx := &Transaction{ - tx: new(types.Transaction), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), tx.tx); err != nil { - return nil, err - } - return tx, nil -} - -// EncodeRLP encodes a transaction into an RLP data dump. -func (tx *Transaction) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(tx.tx) -} - -// NewTransactionFromJSON parses a transaction from a JSON data dump. -func NewTransactionFromJSON(data string) (*Transaction, error) { - tx := &Transaction{ - tx: new(types.Transaction), - } - if err := json.Unmarshal([]byte(data), tx.tx); err != nil { - return nil, err - } - return tx, nil -} - -// EncodeJSON encodes a transaction into a JSON data dump. -func (tx *Transaction) EncodeJSON() (string, error) { - data, err := json.Marshal(tx.tx) - return string(data), err -} - -// String returns a printable representation of the transaction. -func (tx *Transaction) String() string { - return encodeOrError(tx) -} - -func (tx *Transaction) GetData() []byte { return tx.tx.Data() } -func (tx *Transaction) GetGas() int64 { return int64(tx.tx.Gas()) } -func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} } -func (tx *Transaction) GetValue() *BigInt { return &BigInt{tx.tx.Value()} } -func (tx *Transaction) GetNonce() int64 { return int64(tx.tx.Nonce()) } - -func (tx *Transaction) GetHash() *Hash { return &Hash{tx.tx.Hash()} } -func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} } - -func (tx *Transaction) GetTo() *Address { - if to := tx.tx.To(); to != nil { - return &Address{*to} - } - return nil -} - -func (tx *Transaction) WithSignature(sig []byte, chainID *BigInt) (signedTx *Transaction, _ error) { - var signer types.Signer = types.HomesteadSigner{} - if chainID != nil { - signer = types.NewEIP155Signer(chainID.bigint) - } - rawTx, err := tx.tx.WithSignature(signer, common.CopyBytes(sig)) - return &Transaction{rawTx}, err -} - -// Transactions represents a slice of transactions. -type Transactions struct{ txs types.Transactions } - -// Size returns the number of transactions in the slice. -func (txs *Transactions) Size() int { - return len(txs.txs) -} - -// Get returns the transaction at the given index from the slice. -func (txs *Transactions) Get(index int) (tx *Transaction, _ error) { - if index < 0 || index >= len(txs.txs) { - return nil, errors.New("index out of bounds") - } - return &Transaction{txs.txs[index]}, nil -} - -// Receipt represents the results of a transaction. -type Receipt struct { - receipt *types.Receipt -} - -// NewReceiptFromRLP parses a transaction receipt from an RLP data dump. -func NewReceiptFromRLP(data []byte) (*Receipt, error) { - r := &Receipt{ - receipt: new(types.Receipt), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), r.receipt); err != nil { - return nil, err - } - return r, nil -} - -// EncodeRLP encodes a transaction receipt into an RLP data dump. -func (r *Receipt) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(r.receipt) -} - -// NewReceiptFromJSON parses a transaction receipt from a JSON data dump. -func NewReceiptFromJSON(data string) (*Receipt, error) { - r := &Receipt{ - receipt: new(types.Receipt), - } - if err := json.Unmarshal([]byte(data), r.receipt); err != nil { - return nil, err - } - return r, nil -} - -// EncodeJSON encodes a transaction receipt into a JSON data dump. -func (r *Receipt) EncodeJSON() (string, error) { - data, err := rlp.EncodeToBytes(r.receipt) - return string(data), err -} - -// String returns a printable representation of the receipt. -func (r *Receipt) String() string { - return encodeOrError(r) -} - -func (r *Receipt) GetStatus() int { return int(r.receipt.Status) } -func (r *Receipt) GetPostState() []byte { return r.receipt.PostState } -func (r *Receipt) GetCumulativeGasUsed() int64 { return int64(r.receipt.CumulativeGasUsed) } -func (r *Receipt) GetBloom() *Bloom { return &Bloom{r.receipt.Bloom} } -func (r *Receipt) GetLogs() *Logs { return &Logs{r.receipt.Logs} } -func (r *Receipt) GetTxHash() *Hash { return &Hash{r.receipt.TxHash} } -func (r *Receipt) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} } -func (r *Receipt) GetGasUsed() int64 { return int64(r.receipt.GasUsed) }
(deleted)
+0
-129
diff --git go-ethereum/mobile/big.go celo/mobile/big.go deleted file mode 100644 index 408f0773076e76365721b48a495e626818273485..0000000000000000000000000000000000000000 --- go-ethereum/mobile/big.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// Contains all the wrappers from the math/big package. - -package geth - -import ( - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/common" -) - -// A BigInt represents a signed multi-precision integer. -type BigInt struct { - bigint *big.Int -} - -// NewBigInt allocates and returns a new BigInt set to x. -func NewBigInt(x int64) *BigInt { - return &BigInt{big.NewInt(x)} -} - -// NewBigIntFromString allocates and returns a new BigInt set to x -// interpreted in the provided base. -func NewBigIntFromString(x string, base int) *BigInt { - b, success := new(big.Int).SetString(x, base) - if !success { - return nil - } - return &BigInt{b} -} - -// GetBytes returns the absolute value of x as a big-endian byte slice. -func (bi *BigInt) GetBytes() []byte { - return bi.bigint.Bytes() -} - -// String returns the value of x as a formatted decimal string. -func (bi *BigInt) String() string { - return bi.bigint.String() -} - -// GetInt64 returns the int64 representation of x. If x cannot be represented in -// an int64, the result is undefined. -func (bi *BigInt) GetInt64() int64 { - return bi.bigint.Int64() -} - -// SetBytes interprets buf as the bytes of a big-endian unsigned integer and sets -// the big int to that value. -func (bi *BigInt) SetBytes(buf []byte) { - bi.bigint.SetBytes(common.CopyBytes(buf)) -} - -// SetInt64 sets the big int to x. -func (bi *BigInt) SetInt64(x int64) { - bi.bigint.SetInt64(x) -} - -// Sign returns: -// -// -1 if x < 0 -// 0 if x == 0 -// +1 if x > 0 -// -func (bi *BigInt) Sign() int { - return bi.bigint.Sign() -} - -// SetString sets the big int to x. -// -// The string prefix determines the actual conversion base. A prefix of "0x" or -// "0X" selects base 16; the "0" prefix selects base 8, and a "0b" or "0B" prefix -// selects base 2. Otherwise the selected base is 10. -func (bi *BigInt) SetString(x string, base int) { - bi.bigint.SetString(x, base) -} - -// BigInts represents a slice of big ints. -type BigInts struct{ bigints []*big.Int } - -// NewBigInts creates a slice of uninitialized big numbers. -func NewBigInts(size int) *BigInts { - return &BigInts{ - bigints: make([]*big.Int, size), - } -} - -// Size returns the number of big ints in the slice. -func (bi *BigInts) Size() int { - return len(bi.bigints) -} - -// Get returns the bigint at the given index from the slice. -func (bi *BigInts) Get(index int) (bigint *BigInt, _ error) { - if index < 0 || index >= len(bi.bigints) { - return nil, errors.New("index out of bounds") - } - return &BigInt{bi.bigints[index]}, nil -} - -// Set sets the big int at the given index in the slice. -func (bi *BigInts) Set(index int, bigint *BigInt) error { - if index < 0 || index >= len(bi.bigints) { - return errors.New("index out of bounds") - } - bi.bigints[index] = bigint.bigint - return nil -} - -// GetString returns the value of x as a formatted string in some number base. -func (bi *BigInt) GetString(base int) string { - return bi.bigint.Text(base) -}

mycelo is a developer tool to run blockchain testnets with Celo Core Contracts already deployed. This is entirely specific to the Celo blockchain.

diff --git go-ethereum/mycelo/genesis/gen_governance_parameters_json.go celo/mycelo/genesis/gen_governance_parameters_json.go new file mode 100644 index 0000000000000000000000000000000000000000..1a310b6f6422573c2363d43750a54e58c0ee9c30 --- /dev/null +++ celo/mycelo/genesis/gen_governance_parameters_json.go @@ -0,0 +1,104 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package genesis + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/decimal/bigintstr" + "github.com/ethereum/go-ethereum/common/decimal/fixed" +) + +var _ = (*GovernanceParametersMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (g GovernanceParameters) MarshalJSON() ([]byte, error) { + type GovernanceParameters struct { + UseMultiSig bool `json:"useMultiSig"` + ConcurrentProposals uint64 `json:"concurrentProposals"` + MinDeposit *bigintstr.BigIntStr `json:"minDeposit"` + QueueExpiry uint64 `json:"queueExpiry"` + DequeueFrequency uint64 `json:"dequeueFrequency"` + ApprovalStageDuration uint64 `json:"approvalStageDuration"` + ReferendumStageDuration uint64 `json:"referendumStageDuration"` + ExecutionStageDuration uint64 `json:"executionStageDuration"` + ParticipationBaseline *fixed.Fixed `json:"participationBaseline"` + ParticipationFloor *fixed.Fixed `json:"participationFloor"` + BaselineUpdateFactor *fixed.Fixed `json:"baselineUpdateFactor"` + BaselineQuorumFactor *fixed.Fixed `json:"baselineQuorumFactor"` + } + var enc GovernanceParameters + enc.UseMultiSig = g.UseMultiSig + enc.ConcurrentProposals = g.ConcurrentProposals + enc.MinDeposit = (*bigintstr.BigIntStr)(g.MinDeposit) + enc.QueueExpiry = g.QueueExpiry + enc.DequeueFrequency = g.DequeueFrequency + enc.ApprovalStageDuration = g.ApprovalStageDuration + enc.ReferendumStageDuration = g.ReferendumStageDuration + enc.ExecutionStageDuration = g.ExecutionStageDuration + enc.ParticipationBaseline = g.ParticipationBaseline + enc.ParticipationFloor = g.ParticipationFloor + enc.BaselineUpdateFactor = g.BaselineUpdateFactor + enc.BaselineQuorumFactor = g.BaselineQuorumFactor + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (g *GovernanceParameters) UnmarshalJSON(input []byte) error { + type GovernanceParameters struct { + UseMultiSig *bool `json:"useMultiSig"` + ConcurrentProposals *uint64 `json:"concurrentProposals"` + MinDeposit *bigintstr.BigIntStr `json:"minDeposit"` + QueueExpiry *uint64 `json:"queueExpiry"` + DequeueFrequency *uint64 `json:"dequeueFrequency"` + ApprovalStageDuration *uint64 `json:"approvalStageDuration"` + ReferendumStageDuration *uint64 `json:"referendumStageDuration"` + ExecutionStageDuration *uint64 `json:"executionStageDuration"` + ParticipationBaseline *fixed.Fixed `json:"participationBaseline"` + ParticipationFloor *fixed.Fixed `json:"participationFloor"` + BaselineUpdateFactor *fixed.Fixed `json:"baselineUpdateFactor"` + BaselineQuorumFactor *fixed.Fixed `json:"baselineQuorumFactor"` + } + var dec GovernanceParameters + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.UseMultiSig != nil { + g.UseMultiSig = *dec.UseMultiSig + } + if dec.ConcurrentProposals != nil { + g.ConcurrentProposals = *dec.ConcurrentProposals + } + if dec.MinDeposit != nil { + g.MinDeposit = (*big.Int)(dec.MinDeposit) + } + if dec.QueueExpiry != nil { + g.QueueExpiry = *dec.QueueExpiry + } + if dec.DequeueFrequency != nil { + g.DequeueFrequency = *dec.DequeueFrequency + } + if dec.ApprovalStageDuration != nil { + g.ApprovalStageDuration = *dec.ApprovalStageDuration + } + if dec.ReferendumStageDuration != nil { + g.ReferendumStageDuration = *dec.ReferendumStageDuration + } + if dec.ExecutionStageDuration != nil { + g.ExecutionStageDuration = *dec.ExecutionStageDuration + } + if dec.ParticipationBaseline != nil { + g.ParticipationBaseline = dec.ParticipationBaseline + } + if dec.ParticipationFloor != nil { + g.ParticipationFloor = dec.ParticipationFloor + } + if dec.BaselineUpdateFactor != nil { + g.BaselineUpdateFactor = dec.BaselineUpdateFactor + } + if dec.BaselineQuorumFactor != nil { + g.BaselineQuorumFactor = dec.BaselineQuorumFactor + } + return nil +}
diff --git go-ethereum/mycelo/genesis/gen_stable_token_exchange_limit_json.go celo/mycelo/genesis/gen_stable_token_exchange_limit_json.go new file mode 100644 index 0000000000000000000000000000000000000000..1c5982b5e20e923e10078de7f950f376532a0f8c --- /dev/null +++ celo/mycelo/genesis/gen_stable_token_exchange_limit_json.go @@ -0,0 +1,49 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package genesis + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/decimal/bigintstr" +) + +var _ = (*StableTokenExchangeLimitsMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s StableTokenExchangeLimit) MarshalJSON() ([]byte, error) { + type StableTokenExchangeLimit struct { + StableToken string `json:"stableToken"` + MinExchangeAmount *bigintstr.BigIntStr `json:"minExchangeAmount"` + MaxExchangeAmount *bigintstr.BigIntStr `json:"maxExchangeAmount"` + } + var enc StableTokenExchangeLimit + enc.StableToken = s.StableToken + enc.MinExchangeAmount = (*bigintstr.BigIntStr)(s.MinExchangeAmount) + enc.MaxExchangeAmount = (*bigintstr.BigIntStr)(s.MaxExchangeAmount) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *StableTokenExchangeLimit) UnmarshalJSON(input []byte) error { + type StableTokenExchangeLimit struct { + StableToken *string `json:"stableToken"` + MinExchangeAmount *bigintstr.BigIntStr `json:"minExchangeAmount"` + MaxExchangeAmount *bigintstr.BigIntStr `json:"maxExchangeAmount"` + } + var dec StableTokenExchangeLimit + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.StableToken != nil { + s.StableToken = *dec.StableToken + } + if dec.MinExchangeAmount != nil { + s.MinExchangeAmount = (*big.Int)(dec.MinExchangeAmount) + } + if dec.MaxExchangeAmount != nil { + s.MaxExchangeAmount = (*big.Int)(dec.MaxExchangeAmount) + } + return nil +}
diff --git go-ethereum/mycelo/env/accounts.go celo/mycelo/env/accounts.go new file mode 100644 index 0000000000000000000000000000000000000000..7109b6bd90cd82054970c434aa7342700782e13b --- /dev/null +++ celo/mycelo/env/accounts.go @@ -0,0 +1,331 @@ +package env + +import ( + "crypto/ecdsa" + "encoding/hex" + "encoding/json" + "fmt" + "log" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/celo-org/celo-bls-go/bls" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/mycelo/hdwallet" + "github.com/tyler-smith/go-bip39" +) + +// MustNewMnemonic creates a new mnemonic (panics on error) +func MustNewMnemonic() string { + res, err := NewMnemonic() + if err != nil { + panic(err) + } + return res +} + +// NewMnemonic creates a new mnemonic +func NewMnemonic() (string, error) { + entropy, err := bip39.NewEntropy(128) + if err != nil { + return "", err + } + return bip39.NewMnemonic(entropy) +} + +// Account represents a Celo Account +type Account struct { + Address common.Address + PrivateKey *ecdsa.PrivateKey +} + +// MarshalJSON implements json.Marshaler +func (a Account) MarshalJSON() ([]byte, error) { + var data = struct { + PrivateKey string + Address common.Address + }{ + PrivateKey: hex.EncodeToString(crypto.FromECDSA(a.PrivateKey)), + Address: a.Address, + } + + return json.Marshal(data) +} + +// MustBLSProofOfPossession variant of BLSProofOfPossession that panics on error +func (a *Account) MustBLSProofOfPossession() []byte { + pop, err := a.BLSProofOfPossession() + if err != nil { + panic(err) + } + return pop +} + +// BLSProofOfPossession generates bls proof of possession +func (a *Account) BLSProofOfPossession() ([]byte, error) { + privateKeyBytes, err := blscrypto.ECDSAToBLS(a.PrivateKey) + if err != nil { + return nil, err + } + + privateKey, err := bls.DeserializePrivateKey(privateKeyBytes) + if err != nil { + return nil, err + } + defer privateKey.Destroy() + + signature, err := privateKey.SignPoP(a.Address.Bytes()) + if err != nil { + return nil, err + } + defer signature.Destroy() + + signatureBytes, err := signature.Serialize() + if err != nil { + return nil, err + } + return signatureBytes, nil +} + +// BLSPublicKey returns the bls public key +func (a *Account) BLSPublicKey() (blscrypto.SerializedPublicKey, error) { + privateKey, err := blscrypto.ECDSAToBLS(a.PrivateKey) + if err != nil { + return blscrypto.SerializedPublicKey{}, err + } + + return blscrypto.PrivateToPublic(privateKey) +} + +// PublicKeyHex hex representation of the public key +func (a *Account) PublicKey() []byte { + return crypto.FromECDSAPub(&a.PrivateKey.PublicKey) +} + +// PrivateKeyHex hex representation of the private key +func (a *Account) PrivateKeyHex() string { + return common.Bytes2Hex(crypto.FromECDSA(a.PrivateKey)) +} + +func (a *Account) String() string { + return fmt.Sprintf("{ address: %s\tprivateKey: %s }", + a.Address.Hex(), + a.PrivateKeyHex(), + ) +} + +// AccountType represents the different account types for the generator +type AccountType int + +// The difference account types for the generator +var ( + ValidatorAT AccountType = 0 + DeveloperAT AccountType = 1 // load test + TxNodeAT AccountType = 2 + BootnodeAT AccountType = 3 + FaucetAT AccountType = 4 + AttestationAT AccountType = 5 + PriceOracleAT AccountType = 6 + ProxyAT AccountType = 7 + AttestationBotAT AccountType = 8 + VotingBotAT AccountType = 9 + TxNodePrivateAT AccountType = 10 + ValidatorGroupAT AccountType = 11 // Not in celotool (yet) + AdminAT AccountType = 12 // Not in celotool (yet) + TxFeeRecipientAT AccountType = 13 // Not in celotool (yet) +) + +// String implements the stringer interface. +func (accountType AccountType) String() string { + switch accountType { + case ValidatorAT: + return "validator" + case DeveloperAT: + return "developer" + case TxNodeAT: + return "txNode" + case FaucetAT: + return "faucet" + case AttestationAT: + return "attestation" + case PriceOracleAT: + return "priceOracle" + case ProxyAT: + return "proxy" + case AttestationBotAT: + return "attestationBot" + case VotingBotAT: + return "votingBot" + case TxNodePrivateAT: + return "txNodePrivate" + case ValidatorGroupAT: + return "validatorGroup" + case AdminAT: + return "admin" + default: + return "unknown" + } +} + +// MarshalText marshall account type into text +func (accountType AccountType) MarshalText() ([]byte, error) { + switch accountType { + case ValidatorAT: + return []byte("validator"), nil + case DeveloperAT: + return []byte("developer"), nil + case TxNodeAT: + return []byte("txNode"), nil + case FaucetAT: + return []byte("faucet"), nil + case AttestationAT: + return []byte("attestation"), nil + case PriceOracleAT: + return []byte("priceOracle"), nil + case ProxyAT: + return []byte("proxy"), nil + case AttestationBotAT: + return []byte("attestationBot"), nil + case VotingBotAT: + return []byte("votingBot"), nil + case TxNodePrivateAT: + return []byte("txNodePrivate"), nil + case ValidatorGroupAT: + return []byte("validatorGroup"), nil + case AdminAT: + return []byte("admin"), nil + default: + return nil, fmt.Errorf("unknown account type %d", accountType) + } +} + +// UnmarshalText creates AccountType from string +func (accountType *AccountType) UnmarshalText(text []byte) error { + switch string(text) { + case "validator": + *accountType = ValidatorAT + case "developer": + *accountType = DeveloperAT + case "txNode": + *accountType = TxNodeAT + case "faucet": + *accountType = FaucetAT + case "attestation": + *accountType = AttestationAT + case "priceOracle": + *accountType = PriceOracleAT + case "proxy": + *accountType = ProxyAT + case "attestationBot": + *accountType = AttestationBotAT + case "votingBot": + *accountType = VotingBotAT + case "txNodePrivate": + *accountType = TxNodePrivateAT + case "validatorGroup": + *accountType = ValidatorGroupAT + case "admin": + *accountType = AdminAT + default: + return fmt.Errorf(`unknown account type %q, want "validator", "developer", "txNode", "faucet", "attestation", "priceOracle", "proxy", "attestationBot", "votingBot", "txNodePrivate", "validatorGroup", "admin"`, text) + } + return nil +} + +func mustDerivationPath(accountType AccountType, idx int) accounts.DerivationPath { + return hdwallet.MustParseDerivationPath(fmt.Sprintf("m/%d/%d", int(accountType), idx)) +} + +// DeriveAccount will derive the account corresponding to (accountType, idx) using the +// given mnemonic +func DeriveAccount(mnemonic string, accountType AccountType, idx int) (*Account, error) { + wallet, err := hdwallet.NewFromMnemonic(mnemonic) + if err != nil { + return nil, err + } + account, err := wallet.Derive(mustDerivationPath(accountType, idx), false) + if err != nil { + return nil, err + } + pk, err := wallet.PrivateKey(account) + if err != nil { + return nil, err + } + + return &Account{ + Address: account.Address, + PrivateKey: pk, + }, nil +} + +// DeriveAccountList will generate the desired number of accounts using mnemonic & accountType +func DeriveAccountList(mnemonic string, accountType AccountType, qty int) ([]Account, error) { + wallet, err := hdwallet.NewFromMnemonic(mnemonic) + if err != nil { + log.Fatal(err) + } + + accounts := make([]Account, qty) + + for i := 0; i < qty; i++ { + account, err := wallet.Derive(mustDerivationPath(accountType, i), false) + if err != nil { + return nil, err + } + pk, err := wallet.PrivateKey(account) + if err != nil { + return nil, err + } + accounts[i] = Account{ + Address: account.Address, + PrivateKey: pk, + } + } + + return accounts, nil +} + +// MustGenerateRandomAccount creates new account or panics +func MustGenerateRandomAccount() Account { + acc, err := GenerateRandomAccount() + if err != nil { + panic(err) + } + return acc +} + +// GenerateRandomAccount creates a random new account +func GenerateRandomAccount() (Account, error) { + privateKey, err := crypto.GenerateKey() + if err != nil { + return Account{}, err + } + return NewAccount(privateKey), nil +} + +// NewAccount creates a new account for the specified private key +func NewAccount(key *ecdsa.PrivateKey) Account { + return Account{ + PrivateKey: key, + Address: crypto.PubkeyToAddress(key.PublicKey), + } +} + +// UnmarshalJSON implements json.Unmarshaler +func (a *Account) UnmarshalJSON(b []byte) error { + var data struct { + PrivateKey string + Address common.Address + } + if err := json.Unmarshal(b, &data); err != nil { + return err + } + a.Address = data.Address + key, err := crypto.HexToECDSA(data.PrivateKey) + if err != nil { + return err + } + a.PrivateKey = key + return nil +}
diff --git go-ethereum/mycelo/genesis/genesis.go celo/mycelo/genesis/genesis.go new file mode 100644 index 0000000000000000000000000000000000000000..52a12b49c1c00f7a1c6dec1d6f9b58eb569a6bdb --- /dev/null +++ celo/mycelo/genesis/genesis.go @@ -0,0 +1,124 @@ +package genesis + +import ( + "math/big" + "time" + + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/decimal/token" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + + "github.com/ethereum/go-ethereum/mycelo/env" + "github.com/ethereum/go-ethereum/rlp" +) + +// Keccak256 of "The Times 09/Apr/2020 With $2.3 Trillion Injection, Fed’s Plan Far Exceeds Its 2008 Rescue" +var genesisMsgHash = common.HexToHash("ecc833a7747eaa8327335e8e0c6b6d8aa3a38d0063591e43ce116ccf5c89753e") + +// CreateCommonGenesisConfig generates a config starting point which templates can then customize further +func CreateCommonGenesisConfig(chainID *big.Int, adminAccountAddress common.Address, istanbulConfig params.IstanbulConfig) *Config { + genesisConfig := BaseConfig() + genesisConfig.ChainID = chainID + genesisConfig.GenesisTimestamp = uint64(time.Now().Unix()) + genesisConfig.Istanbul = istanbulConfig + genesisConfig.Hardforks = HardforkConfig{ + ChurritoBlock: common.Big0, + DonutBlock: common.Big0, + EspressoBlock: common.Big0, + } + + // Make admin account manager of Governance & Reserve + adminMultisig := MultiSigParameters{ + Signatories: []common.Address{adminAccountAddress}, + NumRequiredConfirmations: 1, + NumInternalRequiredConfirmations: 1, + } + + genesisConfig.ReserveSpenderMultiSig = adminMultisig + genesisConfig.GovernanceApproverMultiSig = adminMultisig + + // Ensure nothing is frozen + genesisConfig.GoldToken.Frozen = false + genesisConfig.StableToken.Frozen = false + genesisConfig.Exchange.Frozen = false + genesisConfig.Reserve.FrozenAssetsDays = 0 + genesisConfig.EpochRewards.Frozen = false + + return genesisConfig +} + +func FundAccounts(genesisConfig *Config, accounts []env.Account) { + cusdBalances := make([]Balance, len(accounts)) + ceurBalances := make([]Balance, len(accounts)) + crealBalances := make([]Balance, len(accounts)) + goldBalances := make([]Balance, len(accounts)) + for i, acc := range accounts { + cusdBalances[i] = Balance{Account: acc.Address, Amount: (*big.Int)(token.MustNew("50000"))} // 50k cUSD + ceurBalances[i] = Balance{Account: acc.Address, Amount: (*big.Int)(token.MustNew("50000"))} // 50k cEUR + crealBalances[i] = Balance{Account: acc.Address, Amount: (*big.Int)(token.MustNew("50000"))} // 50k cREAL + goldBalances[i] = Balance{Account: acc.Address, Amount: (*big.Int)(token.MustNew("50000"))} // 50k CELO + } + genesisConfig.StableToken.InitialBalances = cusdBalances + genesisConfig.StableTokenEUR.InitialBalances = ceurBalances + genesisConfig.StableTokenBRL.InitialBalances = crealBalances + genesisConfig.GoldToken.InitialBalances = goldBalances +} + +// GenerateGenesis will create a new genesis block with full celo blockchain already configured +func GenerateGenesis(accounts *env.AccountsConfig, cfg *Config, contractsBuildPath string) (*core.Genesis, error) { + + extraData, err := generateGenesisExtraData(accounts.ValidatorAccounts()) + if err != nil { + return nil, err + } + + genesisAlloc, err := generateGenesisState(accounts, cfg, contractsBuildPath) + if err != nil { + return nil, err + } + + return &core.Genesis{ + Config: cfg.ChainConfig(), + ExtraData: extraData, + Coinbase: accounts.AdminAccount().Address, + Timestamp: cfg.GenesisTimestamp, + Alloc: genesisAlloc, + }, nil +} + +func generateGenesisExtraData(validatorAccounts []env.Account) ([]byte, error) { + addresses := make([]common.Address, len(validatorAccounts)) + blsKeys := make([]blscrypto.SerializedPublicKey, len(validatorAccounts)) + + for i := 0; i < len(validatorAccounts); i++ { + var err error + addresses[i] = validatorAccounts[i].Address + blsKeys[i], err = validatorAccounts[i].BLSPublicKey() + if err != nil { + return nil, err + } + } + + istExtra := types.IstanbulExtra{ + AddedValidators: addresses, + AddedValidatorsPublicKeys: blsKeys, + RemovedValidators: big.NewInt(0), + Seal: []byte{}, + AggregatedSeal: types.IstanbulAggregatedSeal{}, + ParentAggregatedSeal: types.IstanbulAggregatedSeal{}, + } + + payload, err := rlp.EncodeToBytes(&istExtra) + if err != nil { + return nil, err + } + + var extraBytes []byte + extraBytes = append(extraBytes, genesisMsgHash.Bytes()...) + extraBytes = append(extraBytes, payload...) + + return extraBytes, nil +}
diff --git go-ethereum/mycelo/loadbot/bot.go celo/mycelo/loadbot/bot.go new file mode 100644 index 0000000000000000000000000000000000000000..7e80466c3bac7c85c8200f6f1f73be703214ee6a --- /dev/null +++ celo/mycelo/loadbot/bot.go @@ -0,0 +1,176 @@ +package loadbot + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "math/rand" + "sync" + "time" + + bind "github.com/ethereum/go-ethereum/accounts/abi/bind_v2" + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/mycelo/contract" + "github.com/ethereum/go-ethereum/mycelo/env" + "golang.org/x/sync/errgroup" +) + +// 110k gas for stable token transfer is pretty reasonable. It's just under 100k in practice +const GasForTransferWithComment = 110000 + +// LoadGenerator keeps track of in-flight transactions +type LoadGenerator struct { + MaxPending uint64 + Pending uint64 + PendingMu sync.Mutex +} + +// TxConfig contains the options for a transaction +type txConfig struct { + Acc env.Account + Nonce uint64 + Recipient common.Address + Value *big.Int + Verbose bool + SkipGasEstimation bool + MixFeeCurrency bool +} + +// Config represent the load bot run configuration +type Config struct { + ChainID *big.Int + Accounts []env.Account + Amount *big.Int + TransactionsPerSecond int + Clients []*ethclient.Client + Verbose bool + MaxPending uint64 + SkipGasEstimation bool + MixFeeCurrency bool +} + +// Start will start loads bots +func Start(ctx context.Context, cfg *Config) error { + // Set up nonces, we have to manage nonces because calling PendingNonceAt + // is racy and often results in using the same nonce more than once when + // applying heavy load. + nonces := make([]uint64, len(cfg.Accounts)) + for i, a := range cfg.Accounts { + nonce, err := cfg.Clients[0].PendingNonceAt(ctx, a.Address) + if err != nil { + return fmt.Errorf("failed to retrieve pending nonce for account %s: %v", a.Address.String(), err) + } + nonces[i] = nonce + } + + // Offset the receiver from the sender so that they are different + recvIdx := len(cfg.Accounts) / 2 + sendIdx := 0 + clientIdx := 0 + + // Fire off transactions + period := 1 * time.Second / time.Duration(cfg.TransactionsPerSecond) + ticker := time.NewTicker(period) + group, ctx := errgroup.WithContext(ctx) + lg := &LoadGenerator{ + MaxPending: cfg.MaxPending, + } + for { + select { + case <-ticker.C: + lg.PendingMu.Lock() + if lg.MaxPending != 0 && lg.Pending > lg.MaxPending { + lg.PendingMu.Unlock() + continue + } else { + lg.Pending++ + lg.PendingMu.Unlock() + } + // We use round robin selectors that rollover + recvIdx++ + recipient := cfg.Accounts[recvIdx%len(cfg.Accounts)].Address + + sendIdx++ + sender := cfg.Accounts[sendIdx%len(cfg.Accounts)] + nonce := nonces[sendIdx%len(cfg.Accounts)] + nonces[sendIdx%len(cfg.Accounts)]++ + + clientIdx++ + client := cfg.Clients[clientIdx%len(cfg.Clients)] + group.Go(func() error { + txCfg := txConfig{ + Acc: sender, + Nonce: nonce, + Recipient: recipient, + Value: cfg.Amount, + Verbose: cfg.Verbose, + SkipGasEstimation: cfg.SkipGasEstimation, + MixFeeCurrency: cfg.MixFeeCurrency, + } + return runTransaction(ctx, client, cfg.ChainID, lg, txCfg) + }) + case <-ctx.Done(): + return group.Wait() + } + } +} + +func runTransaction(ctx context.Context, client *ethclient.Client, chainID *big.Int, lg *LoadGenerator, txCfg txConfig) error { + defer func() { + lg.PendingMu.Lock() + if lg.MaxPending != 0 { + lg.Pending-- + } + lg.PendingMu.Unlock() + }() + + abi := contract.AbiFor("StableToken") + stableToken := bind.NewBoundContract(env.MustProxyAddressFor("StableToken"), *abi, client) + + transactor, _ := bind.NewKeyedTransactorWithChainID(txCfg.Acc.PrivateKey, chainID) + transactor.Context = ctx + transactor.ChainID = chainID + transactor.Nonce = new(big.Int).SetUint64(txCfg.Nonce) + + stableTokenAddress := env.MustProxyAddressFor("StableToken") + + if n := rand.Intn(2); txCfg.MixFeeCurrency && n == 0 { + transactor.FeeCurrency = nil + + } else { + transactor.FeeCurrency = &stableTokenAddress + } + if txCfg.SkipGasEstimation { + transactor.GasLimit = GasForTransferWithComment + } + + tx, err := stableToken.TxObj(transactor, "transferWithComment", txCfg.Recipient, txCfg.Value, "need to proivde some long comment to make it similar to an encrypted comment").Send() + if err != nil { + if err != context.Canceled { + fmt.Printf("Error sending transaction: %v\n", err) + } + return fmt.Errorf("Error sending transaction: %w", err) + } + if txCfg.Verbose { + fmt.Printf("cusd transfer generated: from: %s to: %s amount: %s\ttxhash: %s\n", txCfg.Acc.Address.Hex(), txCfg.Recipient.Hex(), txCfg.Value.String(), tx.Transaction.Hash().Hex()) + printJSON(tx) + } + + _, err = tx.WaitMined(ctx) + + if err != nil { + if err != context.Canceled { + fmt.Printf("Error waiting for tx: %v\n", err) + } + return fmt.Errorf("Error waiting for tx: %w", err) + } + return err +} + +func printJSON(obj interface{}) { + b, _ := json.MarshalIndent(obj, " ", " ") + fmt.Println(string(b)) +}
diff --git go-ethereum/mycelo/genesis/gen_gas_price_minimum_parameters_json.go celo/mycelo/genesis/gen_gas_price_minimum_parameters_json.go new file mode 100644 index 0000000000000000000000000000000000000000..60b0fcf2ce11822eea8cf66c83792cc548eab525 --- /dev/null +++ celo/mycelo/genesis/gen_gas_price_minimum_parameters_json.go @@ -0,0 +1,50 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package genesis + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/decimal/bigintstr" + "github.com/ethereum/go-ethereum/common/decimal/fixed" +) + +var _ = (*GasPriceMinimumParametersMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (g GasPriceMinimumParameters) MarshalJSON() ([]byte, error) { + type GasPriceMinimumParameters struct { + MinimumFloor *bigintstr.BigIntStr `json:"minimumFloor"` + TargetDensity *fixed.Fixed `json:"targetDensity"` + AdjustmentSpeed *fixed.Fixed `json:"adjustmentSpeed"` + } + var enc GasPriceMinimumParameters + enc.MinimumFloor = (*bigintstr.BigIntStr)(g.MinimumFloor) + enc.TargetDensity = g.TargetDensity + enc.AdjustmentSpeed = g.AdjustmentSpeed + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (g *GasPriceMinimumParameters) UnmarshalJSON(input []byte) error { + type GasPriceMinimumParameters struct { + MinimumFloor *bigintstr.BigIntStr `json:"minimumFloor"` + TargetDensity *fixed.Fixed `json:"targetDensity"` + AdjustmentSpeed *fixed.Fixed `json:"adjustmentSpeed"` + } + var dec GasPriceMinimumParameters + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.MinimumFloor != nil { + g.MinimumFloor = (*big.Int)(dec.MinimumFloor) + } + if dec.TargetDensity != nil { + g.TargetDensity = dec.TargetDensity + } + if dec.AdjustmentSpeed != nil { + g.AdjustmentSpeed = dec.AdjustmentSpeed + } + return nil +}
diff --git go-ethereum/mycelo/genesis/config.go celo/mycelo/genesis/config.go new file mode 100644 index 0000000000000000000000000000000000000000..c77ae862793bf7996a597d9e1d2f212c8d08d2e7 --- /dev/null +++ celo/mycelo/genesis/config.go @@ -0,0 +1,419 @@ +package genesis + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/decimal/bigintstr" + "github.com/ethereum/go-ethereum/common/decimal/fixed" + "github.com/ethereum/go-ethereum/mycelo/internal/utils" + "github.com/ethereum/go-ethereum/params" + "github.com/shopspring/decimal" +) + +// durations in seconds +const ( + Second = 1 + Minute = 60 * Second + Hour = 60 * Minute + Day = 24 * Hour + Week = 7 * Day + Year = 365 * Day +) + +// Config represent all celo-blockchain configuration options for the genesis block +type Config struct { + ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection + Istanbul params.IstanbulConfig `json:"istanbul"` + Hardforks HardforkConfig `json:"hardforks"` + GenesisTimestamp uint64 `json:"genesisTimestamp"` + + SortedOracles SortedOraclesParameters + GasPriceMinimum GasPriceMinimumParameters + Reserve ReserveParameters + StableToken StableTokenParameters + StableTokenEUR StableTokenParameters + StableTokenBRL StableTokenParameters + Exchange ExchangeParameters + ExchangeEUR ExchangeParameters + ExchangeBRL ExchangeParameters + LockedGold LockedGoldParameters + GoldToken GoldTokenParameters + Validators ValidatorsParameters + Election ElectionParameters + EpochRewards EpochRewardsParameters + Blockchain BlockchainParameters + Random RandomParameters + Attestations AttestationsParameters + TransferWhitelist TransferWhitelistParameters + ReserveSpenderMultiSig MultiSigParameters + GovernanceApproverMultiSig MultiSigParameters + DoubleSigningSlasher DoubleSigningSlasherParameters + DowntimeSlasher DowntimeSlasherParameters + Governance GovernanceParameters + GrandaMento GrandaMentoParameters +} + +// Save will write config into a json file +func (cfg *Config) Save(filepath string) error { + return utils.WriteJson(cfg, filepath) +} + +// LoadConfig will read config from a json file +func LoadConfig(filepath string) (*Config, error) { + var cfg Config + if err := utils.ReadJson(&cfg, filepath); err != nil { + return nil, err + } + return &cfg, nil +} + +// ChainConfig returns the chain config objt for the blockchain +func (cfg *Config) ChainConfig() *params.ChainConfig { + return &params.ChainConfig{ + ChainID: cfg.ChainID, + HomesteadBlock: common.Big0, + EIP150Block: common.Big0, + EIP150Hash: common.Hash{}, + EIP155Block: common.Big0, + EIP158Block: common.Big0, + ByzantiumBlock: common.Big0, + ConstantinopleBlock: common.Big0, + PetersburgBlock: common.Big0, + IstanbulBlock: common.Big0, + + ChurritoBlock: cfg.Hardforks.ChurritoBlock, + DonutBlock: cfg.Hardforks.DonutBlock, + EspressoBlock: cfg.Hardforks.EspressoBlock, + GForkBlock: cfg.Hardforks.GForkBlock, + + Istanbul: &params.IstanbulConfig{ + Epoch: cfg.Istanbul.Epoch, + ProposerPolicy: cfg.Istanbul.ProposerPolicy, + BlockPeriod: cfg.Istanbul.BlockPeriod, + RequestTimeout: cfg.Istanbul.RequestTimeout, + }, + } +} + +// HardforkConfig contains celo hardforks activation blocks +type HardforkConfig struct { + ChurritoBlock *big.Int `json:"churritoBlock"` + DonutBlock *big.Int `json:"donutBlock"` + EspressoBlock *big.Int `json:"espressoBlock"` + GForkBlock *big.Int `json:"gForkBlock"` +} + +// MultiSigParameters are the initial configuration parameters for a MultiSig contract +type MultiSigParameters struct { + Signatories []common.Address `json:"signatories"` + NumRequiredConfirmations uint64 `json:"numRequiredConfirmations"` + NumInternalRequiredConfirmations uint64 `json:"numInternalRequiredConfirmations"` +} + +//go:generate gencodec -type LockedGoldRequirements -field-override LockedgoldRequirementsMarshaling -out gen_locked_gold_requirements_json.go + +// LockedGoldRequirements represents value/duration requirments on locked gold +type LockedGoldRequirements struct { + Value *big.Int `json:"value"` + Duration uint64 `json:"duration"` +} + +type LockedgoldRequirementsMarshaling struct { + Value *bigintstr.BigIntStr `json:"value"` +} + +//go:generate gencodec -type ElectionParameters -field-override ElectionParametersMarshaling -out gen_election_parameters_json.go + +// ElectionParameters are the initial configuration parameters for Elections +type ElectionParameters struct { + MinElectableValidators uint64 `json:"minElectableValidators"` + MaxElectableValidators uint64 `json:"maxElectableValidators"` + MaxVotesPerAccount *big.Int `json:"maxVotesPerAccount"` + ElectabilityThreshold *fixed.Fixed `json:"electabilityThreshold"` +} + +type ElectionParametersMarshaling struct { + MaxVotesPerAccount *bigintstr.BigIntStr `json:"maxVotesPerAccount"` +} + +// Version represents an artifact version number +type Version struct { + Major int64 `json:"major"` + Minor int64 `json:"minor"` + Patch int64 `json:"patch"` +} + +// BlockchainParameters are the initial configuration parameters for Blockchain +type BlockchainParameters struct { + Version Version `json:"version"` + GasForNonGoldCurrencies uint64 `json:"gasForNonGoldCurrencies"` + BlockGasLimit uint64 `json:"blockGasLimit"` +} + +//go:generate gencodec -type DoubleSigningSlasherParameters -field-override DoubleSigningSlasherParametersMarshaling -out gen_double_signing_slasher_parameters_json.go + +// DoubleSigningSlasherParameters are the initial configuration parameters for DoubleSigningSlasher +type DoubleSigningSlasherParameters struct { + Penalty *big.Int `json:"penalty"` + Reward *big.Int `json:"reward"` +} + +type DoubleSigningSlasherParametersMarshaling struct { + Penalty *bigintstr.BigIntStr `json:"penalty"` + Reward *bigintstr.BigIntStr `json:"reward"` +} + +//go:generate gencodec -type DowntimeSlasherParameters -field-override DowntimeSlasherParametersMarshaling -out gen_downtime_slasher_parameters_json.go + +// DowntimeSlasherParameters are the initial configuration parameters for DowntimeSlasher +type DowntimeSlasherParameters struct { + Penalty *big.Int `json:"penalty"` + Reward *big.Int `json:"reward"` + SlashableDowntime uint64 `json:"slashableDowntime"` +} + +type DowntimeSlasherParametersMarshaling struct { + Penalty *bigintstr.BigIntStr `json:"penalty"` + Reward *bigintstr.BigIntStr `json:"reward"` +} + +//go:generate gencodec -type GovernanceParameters -field-override GovernanceParametersMarshaling -out gen_governance_parameters_json.go + +// GovernanceParameters are the initial configuration parameters for Governance +type GovernanceParameters struct { + UseMultiSig bool `json:"useMultiSig"` // whether the approver should be the multisig (otherwise it's the admin) + ConcurrentProposals uint64 `json:"concurrentProposals"` + MinDeposit *big.Int `json:"minDeposit"` + QueueExpiry uint64 `json:"queueExpiry"` + DequeueFrequency uint64 `json:"dequeueFrequency"` + ApprovalStageDuration uint64 `json:"approvalStageDuration"` + ReferendumStageDuration uint64 `json:"referendumStageDuration"` + ExecutionStageDuration uint64 `json:"executionStageDuration"` + ParticipationBaseline *fixed.Fixed `json:"participationBaseline"` + ParticipationFloor *fixed.Fixed `json:"participationFloor"` + BaselineUpdateFactor *fixed.Fixed `json:"baselineUpdateFactor"` + BaselineQuorumFactor *fixed.Fixed `json:"baselineQuorumFactor"` +} + +type GovernanceParametersMarshaling struct { + MinDeposit *bigintstr.BigIntStr `json:"minDeposit"` +} + +// ValidatorsParameters are the initial configuration parameters for Validators +type ValidatorsParameters struct { + GroupLockedGoldRequirements LockedGoldRequirements `json:"groupLockedGoldRequirements"` + ValidatorLockedGoldRequirements LockedGoldRequirements `json:"validatorLockedGoldRequirements"` + ValidatorScoreExponent uint64 `json:"validatorScoreExponent"` + ValidatorScoreAdjustmentSpeed *fixed.Fixed `json:"validatorScoreAdjustmentSpeed"` + MembershipHistoryLength uint64 `json:"membershipHistoryLength"` + SlashingPenaltyResetPeriod uint64 `json:"slashingPenaltyResetPeriod"` + MaxGroupSize uint64 `json:"maxGroupSize"` + CommissionUpdateDelay uint64 `json:"commissionUpdateDelay"` + DowntimeGracePeriod uint64 `json:"downtimeGracePeriod"` + + Commission *fixed.Fixed `json:"commission"` // commission for genesis registered validator groups +} + +//go:generate gencodec -type EpochRewardsParameters -field-override EpochRewardsParametersMarshaling -out gen_epoch_rewards_parameters_json.go + +// EpochRewardsParameters are the initial configuration parameters for EpochRewards +type EpochRewardsParameters struct { + TargetVotingYieldInitial *fixed.Fixed `json:"targetVotingYieldInitial"` + TargetVotingYieldMax *fixed.Fixed `json:"targetVotingYieldMax"` + TargetVotingYieldAdjustmentFactor *fixed.Fixed `json:"targetVotingYieldAdjustmentFactor"` + RewardsMultiplierMax *fixed.Fixed `json:"rewardsMultiplierMax"` + RewardsMultiplierAdjustmentFactorsUnderspend *fixed.Fixed `json:"rewardsMultiplierAdjustmentFactorsUnderspend"` + RewardsMultiplierAdjustmentFactorsOverspend *fixed.Fixed `json:"rewardsMultiplierAdjustmentFactorsOverspend"` + TargetVotingGoldFraction *fixed.Fixed `json:"targetVotingGoldFraction"` + MaxValidatorEpochPayment *big.Int `json:"maxValidatorEpochPayment"` + CommunityRewardFraction *fixed.Fixed `json:"communityRewardFraction"` + CarbonOffsettingPartner common.Address `json:"carbonOffsettingPartner"` + CarbonOffsettingFraction *fixed.Fixed `json:"carbonOffsettingFraction"` + Frozen bool `json:"frozen"` +} + +type EpochRewardsParametersMarshaling struct { + MaxValidatorEpochPayment *bigintstr.BigIntStr `json:"maxValidatorEpochPayment"` +} + +// TransferWhitelistParameters are the initial configuration parameters for TransferWhitelist +type TransferWhitelistParameters struct { + Addresses []common.Address `json:"addresses"` + RegistryIDs []common.Hash `json:"registryIds"` +} + +// GoldTokenParameters are the initial configuration parameters for GoldToken +type GoldTokenParameters struct { + Frozen bool `json:"frozen"` + InitialBalances BalanceList `json:"initialBalances"` +} + +// RandomParameters are the initial configuration parameters for Random +type RandomParameters struct { + RandomnessBlockRetentionWindow uint64 `json:"randomnessBlockRetentionWindow"` +} + +// AttestationsParameters are the initial configuration parameters for Attestations +type AttestationsParameters struct { + AttestationExpiryBlocks uint64 `json:"attestationExpiryBlocks"` + SelectIssuersWaitBlocks uint64 `json:"selectIssuersWaitBlocks"` + MaxAttestations uint64 `json:"maxAttestations"` + AttestationRequestFeeInDollars decimal.Decimal `json:"AttestationRequestFeeInDollars"` +} + +// SortedOraclesParameters are the initial configuration parameters for SortedOracles +type SortedOraclesParameters struct { + ReportExpirySeconds uint64 `json:"reportExpirySeconds"` +} + +//go:generate gencodec -type GasPriceMinimumParameters -field-override GasPriceMinimumParametersMarshaling -out gen_gas_price_minimum_parameters_json.go + +// GasPriceMinimumParameters are the initial configuration parameters for GasPriceMinimum +type GasPriceMinimumParameters struct { + MinimumFloor *big.Int `json:"minimumFloor"` + TargetDensity *fixed.Fixed `json:"targetDensity"` + AdjustmentSpeed *fixed.Fixed `json:"adjustmentSpeed"` +} + +type GasPriceMinimumParametersMarshaling struct { + MinimumFloor *bigintstr.BigIntStr `json:"minimumFloor"` +} + +// GrandaMentoParameters are the initial configuration parameters for GrandaMento +type GrandaMentoParameters struct { + Approver common.Address `json:"approver"` + MaxApprovalExchangeRateChange *fixed.Fixed `json:"maxApprovalExchangeRateChange"` + Spread *fixed.Fixed `json:"spread"` + VetoPeriodSeconds uint64 `json:"vetoPeriodSeconds"` + StableTokenExchangeLimits StableTokenExchangeLimitsList `json:"stableTokenExchangeLimits"` +} + +//go:generate gencodec -type StableTokenExchangeLimit -field-override StableTokenExchangeLimitsMarshaling -out gen_stable_token_exchange_limit_json.go + +// StableTokenExchangeLimit represents the granda mento's exchange limit for a specific stable +type StableTokenExchangeLimit struct { + StableToken string `json:"stableToken"` + MinExchangeAmount *big.Int `json:"minExchangeAmount"` + MaxExchangeAmount *big.Int `json:"maxExchangeAmount"` +} + +type StableTokenExchangeLimitsMarshaling struct { + MinExchangeAmount *bigintstr.BigIntStr `json:"minExchangeAmount"` + MaxExchangeAmount *bigintstr.BigIntStr `json:"maxExchangeAmount"` +} + +// BalanceList list of balances +type StableTokenExchangeLimitsList []StableTokenExchangeLimit + +//go:generate gencodec -type ReserveParameters -field-override ReserveParametersMarshaling -out gen_reserve_parameters_json.go + +// ReserveParameters are the initial configuration parameters for Reserve +type ReserveParameters struct { + TobinTaxStalenessThreshold uint64 `json:"tobinTaxStalenessThreshold"` + DailySpendingRatio *fixed.Fixed `json:"dailySpendingRatio"` + AssetAllocations AssetAllocationList `json:"assetAllocations"` + TobinTax *fixed.Fixed `json:"tobinTax"` + TobinTaxReserveRatio *fixed.Fixed `json:"tobinTaxReserveRatio"` + + // Other parameters + Spenders []common.Address `json:"spenders"` + OtherAddresses []common.Address `json:"otherAddresses"` + InitialBalance *big.Int `json:"initialBalance"` + FrozenAssetsStartBalance *big.Int `json:"frozenAssetsStartBalance"` + FrozenAssetsDays uint64 `json:"frozenAssetsDays"` +} + +type ReserveParametersMarshaling struct { + InitialBalance *bigintstr.BigIntStr `json:"initialBalance"` + FrozenAssetsStartBalance *bigintstr.BigIntStr `json:"frozenAssetsStartBalance"` +} + +// StableTokenParameters are the initial configuration parameters for StableToken +type StableTokenParameters struct { + Name string `json:"name"` + Symbol string `json:"symbol"` + Decimals uint8 `json:"decimals"` + Rate *fixed.Fixed `json:"rate"` + InflationFactorUpdatePeriod uint64 `json:"inflationFactorUpdatePeriod"` // How often the inflation factor is updated. + InitialBalances BalanceList `json:"initialBalances"` + Frozen bool `json:"frozen"` + Oracles []common.Address `json:"oracles"` + GoldPrice *fixed.Fixed `json:"goldPrice"` + ExchangeIdentifier string `json:"exchangeIdentifier"` +} + +// ExchangeParameters are the initial configuration parameters for Exchange +type ExchangeParameters struct { + Frozen bool `json:"frozen"` + Spread *fixed.Fixed `json:"spread"` + ReserveFraction *fixed.Fixed `json:"reserveFraction"` + UpdateFrequency uint64 `json:"updateFrequency"` + MinimumReports uint64 `json:"minimumReports"` +} + +// LockedGoldParameters are the initial configuration parameters for LockedGold +type LockedGoldParameters struct { + UnlockingPeriod uint64 `json:"unlockingPeriod"` +} + +//go:generate gencodec -type Balance -field-override BalanceMarshaling -out gen_balance_json.go + +// Balance represents an account and it's initial balance in wei +type Balance struct { + Account common.Address `json:"account"` + Amount *big.Int `json:"amount"` +} + +type BalanceMarshaling struct { + Amount *bigintstr.BigIntStr `json:"amount"` +} + +// BalanceList list of balances +type BalanceList []Balance + +// Accounts returns all the addresses +func (bl BalanceList) Accounts() []common.Address { + res := make([]common.Address, len(bl)) + for i, x := range bl { + res[i] = x.Account + } + return res +} + +// Amounts returns all the amounts +func (bl BalanceList) Amounts() []*big.Int { + res := make([]*big.Int, len(bl)) + for i, x := range bl { + res[i] = x.Amount + } + return res +} + +// AssetAllocation config for Reserve +type AssetAllocation struct { + Symbol string `json:"symbol"` + Weight *fixed.Fixed `json:"weight"` +} + +// AssetAllocationList list of AssetAllocation +type AssetAllocationList []AssetAllocation + +// SymbolsABI returns symbols in ABI format for assets in list +func (aa AssetAllocationList) SymbolsABI() []common.Hash { + res := make([]common.Hash, len(aa)) + for i, x := range aa { + + res[i] = common.BytesToHash(common.RightPadBytes([]byte(x.Symbol), 32)) + } + return res +} + +// Weights returns weights for assets in list +func (aa AssetAllocationList) Weights() []*big.Int { + res := make([]*big.Int, len(aa)) + for i, x := range aa { + res[i] = x.Weight.BigInt() + } + return res +}
diff --git go-ethereum/mycelo/genesis/gen_locked_gold_requirements_json.go celo/mycelo/genesis/gen_locked_gold_requirements_json.go new file mode 100644 index 0000000000000000000000000000000000000000..c20dfbdf22df7d10147eaf42aba98698867db032 --- /dev/null +++ celo/mycelo/genesis/gen_locked_gold_requirements_json.go @@ -0,0 +1,43 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package genesis + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/decimal/bigintstr" +) + +var _ = (*LockedgoldRequirementsMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (l LockedGoldRequirements) MarshalJSON() ([]byte, error) { + type LockedGoldRequirements struct { + Value *bigintstr.BigIntStr `json:"value"` + Duration uint64 `json:"duration"` + } + var enc LockedGoldRequirements + enc.Value = (*bigintstr.BigIntStr)(l.Value) + enc.Duration = l.Duration + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (l *LockedGoldRequirements) UnmarshalJSON(input []byte) error { + type LockedGoldRequirements struct { + Value *bigintstr.BigIntStr `json:"value"` + Duration *uint64 `json:"duration"` + } + var dec LockedGoldRequirements + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Value != nil { + l.Value = (*big.Int)(dec.Value) + } + if dec.Duration != nil { + l.Duration = *dec.Duration + } + return nil +}
diff --git go-ethereum/mycelo/hdwallet/hdwallet.go celo/mycelo/hdwallet/hdwallet.go new file mode 100644 index 0000000000000000000000000000000000000000..f36fc015fac365a66ea7468a462ddd987caef9ce --- /dev/null +++ celo/mycelo/hdwallet/hdwallet.go @@ -0,0 +1,541 @@ +package hdwallet + +// Taken from https://github.com/miguelmota/go-ethereum-hdwallet + +import ( + "crypto/ecdsa" + "crypto/rand" + "errors" + "fmt" + "math/big" + "sync" + + "github.com/btcsuite/btcd/btcutil/hdkeychain" + "github.com/btcsuite/btcd/chaincfg" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/tyler-smith/go-bip39" +) + +// DefaultRootDerivationPath is the root path to which custom derivation endpoints +// are appended. As such, the first account will be at m/44'/60'/0'/0, the second +// at m/44'/60'/0'/1, etc. +var DefaultRootDerivationPath = accounts.DefaultRootDerivationPath + +// DefaultBaseDerivationPath is the base path from which custom derivation endpoints +// are incremented. As such, the first account will be at m/44'/60'/0'/0, the second +// at m/44'/60'/0'/1, etc +var DefaultBaseDerivationPath = accounts.DefaultBaseDerivationPath + +// Wallet is the underlying wallet struct. +type Wallet struct { + mnemonic string + masterKey *hdkeychain.ExtendedKey + seed []byte + url accounts.URL + paths map[common.Address]accounts.DerivationPath + accounts []accounts.Account + stateLock sync.RWMutex +} + +func newWallet(seed []byte) (*Wallet, error) { + masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) + if err != nil { + return nil, err + } + + return &Wallet{ + masterKey: masterKey, + seed: seed, + accounts: []accounts.Account{}, + paths: map[common.Address]accounts.DerivationPath{}, + }, nil +} + +// NewFromMnemonic returns a new wallet from a BIP-39 mnemonic. +func NewFromMnemonic(mnemonic string) (*Wallet, error) { + if mnemonic == "" { + return nil, errors.New("mnemonic is required") + } + + if !bip39.IsMnemonicValid(mnemonic) { + return nil, errors.New("mnemonic is invalid") + } + + seed, err := NewSeedFromMnemonic(mnemonic) + if err != nil { + return nil, err + } + + wallet, err := newWallet(seed) + if err != nil { + return nil, err + } + wallet.mnemonic = mnemonic + + return wallet, nil +} + +// NewFromSeed returns a new wallet from a BIP-39 seed. +func NewFromSeed(seed []byte) (*Wallet, error) { + if len(seed) == 0 { + return nil, errors.New("seed is required") + } + + return newWallet(seed) +} + +// URL implements accounts.Wallet, returning the URL of the device that +// the wallet is on, however this does nothing since this is not a hardware device. +func (w *Wallet) URL() accounts.URL { + return w.url +} + +// Status implements accounts.Wallet, returning a custom status message +// from the underlying vendor-specific hardware wallet implementation, +// however this does nothing since this is not a hardware device. +func (w *Wallet) Status() (string, error) { + return "ok", nil +} + +// Open implements accounts.Wallet, however this does nothing since this +// is not a hardware device. +func (w *Wallet) Open(passphrase string) error { + return nil +} + +// Close implements accounts.Wallet, however this does nothing since this +// is not a hardware device. +func (w *Wallet) Close() error { + return nil +} + +// Accounts implements accounts.Wallet, returning the list of accounts pinned to +// the wallet. If self-derivation was enabled, the account list is +// periodically expanded based on current chain state. +func (w *Wallet) Accounts() []accounts.Account { + // Attempt self-derivation if it's running + // Return whatever account list we ended up with + w.stateLock.RLock() + defer w.stateLock.RUnlock() + + cpy := make([]accounts.Account, len(w.accounts)) + copy(cpy, w.accounts) + return cpy +} + +// Contains implements accounts.Wallet, returning whether a particular account is +// or is not pinned into this wallet instance. +func (w *Wallet) Contains(account accounts.Account) bool { + w.stateLock.RLock() + defer w.stateLock.RUnlock() + + _, exists := w.paths[account.Address] + return exists +} + +// Unpin unpins account from list of pinned accounts. +func (w *Wallet) Unpin(account accounts.Account) error { + w.stateLock.RLock() + defer w.stateLock.RUnlock() + + for i, acct := range w.accounts { + if acct.Address.String() == account.Address.String() { + w.accounts = removeAtIndex(w.accounts, i) + delete(w.paths, account.Address) + return nil + } + } + + return errors.New("account not found") +} + +// Derive implements accounts.Wallet, deriving a new account at the specific +// derivation path. If pin is set to true, the account will be added to the list +// of tracked accounts. +func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { + // Try to derive the actual account and update its URL if successful + w.stateLock.RLock() // Avoid device disappearing during derivation + + address, err := w.deriveAddress(path) + + w.stateLock.RUnlock() + + // If an error occurred or no pinning was requested, return + if err != nil { + return accounts.Account{}, err + } + + account := accounts.Account{ + Address: address, + URL: accounts.URL{ + Scheme: "", + Path: path.String(), + }, + } + + if !pin { + return account, nil + } + + // Pinning needs to modify the state + w.stateLock.Lock() + defer w.stateLock.Unlock() + + if _, ok := w.paths[address]; !ok { + w.accounts = append(w.accounts, account) + w.paths[address] = path + } + + return account, nil +} + +// SelfDerive implements accounts.Wallet, trying to discover accounts that the +// user used previously (based on the chain state), but ones that he/she did not +// explicitly pin to the wallet manually. To avoid chain head monitoring, self +// derivation only runs during account listing (and even then throttled). +func (w *Wallet) SelfDerive(base []accounts.DerivationPath, chain ethereum.ChainStateReader) { + // TODO: self derivation +} + +// SignHash implements accounts.Wallet, which allows signing arbitrary data. +func (w *Wallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) { + // Make sure the requested account is contained within + path, ok := w.paths[account.Address] + if !ok { + return nil, accounts.ErrUnknownAccount + } + + privateKey, err := w.derivePrivateKey(path) + if err != nil { + return nil, err + } + + return crypto.Sign(hash, privateKey) +} + +// SignTxEIP155 implements accounts.Wallet, which allows the account to sign an ERC-20 transaction. +func (w *Wallet) SignTxEIP155(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { + w.stateLock.RLock() // Comms have own mutex, this is for the state fields + defer w.stateLock.RUnlock() + + // Make sure the requested account is contained within + path, ok := w.paths[account.Address] + if !ok { + return nil, accounts.ErrUnknownAccount + } + + privateKey, err := w.derivePrivateKey(path) + if err != nil { + return nil, err + } + + // Sign the transaction and verify the sender to avoid hardware fault surprises + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey) + if err != nil { + return nil, err + } + + msg, err := signedTx.AsMessage(types.NewEIP155Signer(chainID), nil) + if err != nil { + return nil, err + } + + sender := msg.From() + if sender != account.Address { + return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex()) + } + + return signedTx, nil +} + +// SignTx implements accounts.Wallet, which allows the account to sign an Ethereum transaction. +func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { + w.stateLock.RLock() // Comms have own mutex, this is for the state fields + defer w.stateLock.RUnlock() + + // Make sure the requested account is contained within + path, ok := w.paths[account.Address] + if !ok { + return nil, accounts.ErrUnknownAccount + } + + privateKey, err := w.derivePrivateKey(path) + if err != nil { + return nil, err + } + + // Sign the transaction and verify the sender to avoid hardware fault surprises + signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, privateKey) + if err != nil { + return nil, err + } + + msg, err := signedTx.AsMessage(types.HomesteadSigner{}, nil) + if err != nil { + return nil, err + } + + sender := msg.From() + if sender != account.Address { + return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex()) + } + + return signedTx, nil +} + +// SignHashWithPassphrase implements accounts.Wallet, attempting +// to sign the given hash with the given account using the +// passphrase as extra authentication. +func (w *Wallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { + return w.SignHash(account, hash) +} + +// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given +// transaction with the given account using passphrase as extra authentication. +func (w *Wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { + return w.SignTx(account, tx, chainID) +} + +// PrivateKey returns the ECDSA private key of the account. +func (w *Wallet) PrivateKey(account accounts.Account) (*ecdsa.PrivateKey, error) { + path, err := ParseDerivationPath(account.URL.Path) + if err != nil { + return nil, err + } + + return w.derivePrivateKey(path) +} + +// PrivateKeyBytes returns the ECDSA private key in bytes format of the account. +func (w *Wallet) PrivateKeyBytes(account accounts.Account) ([]byte, error) { + privateKey, err := w.PrivateKey(account) + if err != nil { + return nil, err + } + + return crypto.FromECDSA(privateKey), nil +} + +// PrivateKeyHex return the ECDSA private key in hex string format of the account. +func (w *Wallet) PrivateKeyHex(account accounts.Account) (string, error) { + privateKeyBytes, err := w.PrivateKeyBytes(account) + if err != nil { + return "", err + } + + return hexutil.Encode(privateKeyBytes)[2:], nil +} + +// PublicKey returns the ECDSA public key of the account. +func (w *Wallet) PublicKey(account accounts.Account) (*ecdsa.PublicKey, error) { + path, err := ParseDerivationPath(account.URL.Path) + if err != nil { + return nil, err + } + + return w.derivePublicKey(path) +} + +// PublicKeyBytes returns the ECDSA public key in bytes format of the account. +func (w *Wallet) PublicKeyBytes(account accounts.Account) ([]byte, error) { + publicKey, err := w.PublicKey(account) + if err != nil { + return nil, err + } + + return crypto.FromECDSAPub(publicKey), nil +} + +// PublicKeyHex return the ECDSA public key in hex string format of the account. +func (w *Wallet) PublicKeyHex(account accounts.Account) (string, error) { + publicKeyBytes, err := w.PublicKeyBytes(account) + if err != nil { + return "", err + } + + return hexutil.Encode(publicKeyBytes)[4:], nil +} + +// Address returns the address of the account. +func (w *Wallet) Address(account accounts.Account) (common.Address, error) { + publicKey, err := w.PublicKey(account) + if err != nil { + return common.Address{}, err + } + + return crypto.PubkeyToAddress(*publicKey), nil +} + +// AddressBytes returns the address in bytes format of the account. +func (w *Wallet) AddressBytes(account accounts.Account) ([]byte, error) { + address, err := w.Address(account) + if err != nil { + return nil, err + } + return address.Bytes(), nil +} + +// AddressHex returns the address in hex string format of the account. +func (w *Wallet) AddressHex(account accounts.Account) (string, error) { + address, err := w.Address(account) + if err != nil { + return "", err + } + return address.Hex(), nil +} + +// Path return the derivation path of the account. +func (w *Wallet) Path(account accounts.Account) (string, error) { + return account.URL.Path, nil +} + +// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed +func (w *Wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) { + // Make sure the requested account is contained within + if !w.Contains(account) { + return nil, accounts.ErrUnknownAccount + } + + return w.SignHash(account, crypto.Keccak256(data)) +} + +// SignDataWithPassphrase signs keccak256(data). The mimetype parameter describes the type of data being signed +func (w *Wallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) { + // Make sure the requested account is contained within + if !w.Contains(account) { + return nil, accounts.ErrUnknownAccount + } + + return w.SignHashWithPassphrase(account, passphrase, crypto.Keccak256(data)) +} + +// SignText requests the wallet to sign the hash of a given piece of data, prefixed +// the needed details via SignHashWithPassphrase, or by other means (e.g. unlock +// the account in a keystore). +func (w *Wallet) SignText(account accounts.Account, text []byte) ([]byte, error) { + // Make sure the requested account is contained within + if !w.Contains(account) { + return nil, accounts.ErrUnknownAccount + } + + return w.SignHash(account, accounts.TextHash(text)) +} + +// SignTextWithPassphrase implements accounts.Wallet, attempting to sign the +// given text (which is hashed) with the given account using passphrase as extra authentication. +func (w *Wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) { + // Make sure the requested account is contained within + if !w.Contains(account) { + return nil, accounts.ErrUnknownAccount + } + + return w.SignHashWithPassphrase(account, passphrase, accounts.TextHash(text)) +} + +// ParseDerivationPath parses the derivation path in string format into []uint32 +func ParseDerivationPath(path string) (accounts.DerivationPath, error) { + return accounts.ParseDerivationPath(path) +} + +// MustParseDerivationPath parses the derivation path in string format into +// []uint32 but will panic if it can't parse it. +func MustParseDerivationPath(path string) accounts.DerivationPath { + parsed, err := accounts.ParseDerivationPath(path) + if err != nil { + panic(err) + } + + return parsed +} + +// NewMnemonic returns a randomly generated BIP-39 mnemonic using 128-256 bits of entropy. +func NewMnemonic(bits int) (string, error) { + entropy, err := bip39.NewEntropy(bits) + if err != nil { + return "", err + } + return bip39.NewMnemonic(entropy) +} + +// NewMnemonicFromEntropy returns a BIP-39 mnemonic from entropy. +func NewMnemonicFromEntropy(entropy []byte) (string, error) { + return bip39.NewMnemonic(entropy) +} + +// NewEntropy returns a randomly generated entropy. +func NewEntropy(bits int) ([]byte, error) { + return bip39.NewEntropy(bits) +} + +// NewSeed returns a randomly generated BIP-39 seed. +func NewSeed() ([]byte, error) { + b := make([]byte, 64) + _, err := rand.Read(b) + return b, err +} + +// NewSeedFromMnemonic returns a BIP-39 seed based on a BIP-39 mnemonic. +func NewSeedFromMnemonic(mnemonic string) ([]byte, error) { + if mnemonic == "" { + return nil, errors.New("mnemonic is required") + } + + return bip39.NewSeedWithErrorChecking(mnemonic, "") +} + +// DerivePrivateKey derives the private key of the derivation path. +func (w *Wallet) derivePrivateKey(path accounts.DerivationPath) (*ecdsa.PrivateKey, error) { + var err error + key := w.masterKey + for _, n := range path { + key, err = key.Derive(n) + if err != nil { + return nil, err + } + } + + privateKey, err := key.ECPrivKey() + privateKeyECDSA := privateKey.ToECDSA() + if err != nil { + return nil, err + } + + return privateKeyECDSA, nil +} + +// DerivePublicKey derives the public key of the derivation path. +func (w *Wallet) derivePublicKey(path accounts.DerivationPath) (*ecdsa.PublicKey, error) { + privateKeyECDSA, err := w.derivePrivateKey(path) + if err != nil { + return nil, err + } + + publicKey := privateKeyECDSA.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return nil, errors.New("failed to get public key") + } + + return publicKeyECDSA, nil +} + +// DeriveAddress derives the account address of the derivation path. +func (w *Wallet) deriveAddress(path accounts.DerivationPath) (common.Address, error) { + publicKeyECDSA, err := w.derivePublicKey(path) + if err != nil { + return common.Address{}, err + } + + address := crypto.PubkeyToAddress(*publicKeyECDSA) + return address, nil +} + +// removeAtIndex removes an account at index. +func removeAtIndex(accts []accounts.Account, index int) []accounts.Account { + return append(accts[:index], accts[index+1:]...) +}
diff --git go-ethereum/mycelo/genesis/genesis_state.go celo/mycelo/genesis/genesis_state.go new file mode 100644 index 0000000000000000000000000000000000000000..7c1487d321e174532f2d21e43d0df02931a50dff --- /dev/null +++ celo/mycelo/genesis/genesis_state.go @@ -0,0 +1,1085 @@ +package genesis + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/decimal/token" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/runtime" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/mycelo/contract" + "github.com/ethereum/go-ethereum/mycelo/env" + "github.com/shopspring/decimal" +) + +var ( + proxyOwnerStorageLocation = common.HexToHash("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103") + proxyByteCode = common.Hex2Bytes("60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029") + adminGoldBalance = token.MustNew("100000").BigInt() // 100k CELO +) + +// deployContext context for deployment +type deployContext struct { + genesisConfig *Config + accounts *env.AccountsConfig + statedb *state.StateDB + runtimeConfig *runtime.Config + truffleReader contract.TruffleReader + logger log.Logger +} + +// Helper function to reduce boilerplate, limited to this package on purpose +// Like big.NewInt() except it takes uint64 instead of int64 +func newBigInt(x uint64) *big.Int { return new(big.Int).SetUint64(x) } + +func generateGenesisState(accounts *env.AccountsConfig, cfg *Config, buildPath string) (core.GenesisAlloc, error) { + deployment := newDeployment(cfg, accounts, buildPath) + return deployment.deploy() +} + +// NewDeployment generates a new deployment +func newDeployment(genesisConfig *Config, accounts *env.AccountsConfig, buildPath string) *deployContext { + logger := log.New("obj", "deployment") + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + + adminAddress := accounts.AdminAccount().Address + + logger.Info("New deployment", "admin_address", adminAddress.Hex()) + return &deployContext{ + genesisConfig: genesisConfig, + accounts: accounts, + logger: logger, + statedb: statedb, + truffleReader: contract.NewTruffleReader(buildPath), + runtimeConfig: &runtime.Config{ + ChainConfig: genesisConfig.ChainConfig(), + Origin: adminAddress, + State: statedb, + GasLimit: 1000000000000000, + GasPrice: big.NewInt(0), + Value: big.NewInt(0), + Time: newBigInt(genesisConfig.GenesisTimestamp), + Coinbase: adminAddress, + BlockNumber: newBigInt(0), + EVMConfig: vm.Config{ + Tracer: nil, + Debug: false, + }, + }, + } + +} + +// Deploy runs the deployment +func (ctx *deployContext) deploy() (core.GenesisAlloc, error) { + ctx.fundAdminAccount() + + deploySteps := [](func() error){ + // X, Y => X is the number in this list, Y is the migration number in the protocol folder for the core contracts + + // i:00, migr:01 Libraries + ctx.deployLibraries, + + // i:01, migr:02 Registry + ctx.deployRegistry, + + // i:02, migr:03 Freezer + ctx.deployFreezer, + + // i:03, migr:03 TransferWhitelist + ctx.deployTransferWhitelist, + + // i:04, migr:03 FeeCurrencyWhitelist + ctx.deployFeeCurrencyWhitelist, + + // i:05, migr:04 GoldToken + ctx.deployGoldToken, + + // i:06, migr:05 SortedOracles + ctx.deploySortedOracles, + + // i:07, migr:06 GasPriceMinimum + ctx.deployGasPriceMinimum, + + // i:08, migr:07 Reserve + ctx.deployReserve, + + // i:09, migr:08 ReserveSpenderMultisig (requires reserve to work) + ctx.deployReserveSpenderMultisig, + + // i:10, migr:09 StableToken, StableTokenEUR and StableTokenBRL + ctx.deployStableTokens, + + // i:11, migr:10 Exchange, ExchangeEUR and ExchangeBRL + ctx.deployExchanges, + + // i:12, migr:11 Accounts + ctx.deployAccounts, + + // i:13, migr:12 LockedGold + ctx.deployLockedGold, + + // i:14, migr:13 Validators + ctx.deployValidators, + + // i:15, migr:14 Election + ctx.deployElection, + + // i:16, migr:15 EpochRewards + ctx.deployEpochRewards, + + // i:17, migr:16 Random + ctx.deployRandom, + + // i:18, migr17 Attestations + ctx.deployAttestations, + + // 1:19, migr:18 Escrow + ctx.deployEscrow, + + // i:20, migr:19 BlockchainParameters + ctx.deployBlockchainParameters, + + // i:21, migr:20 GovernanceSlasher + ctx.deployGovernanceSlasher, + + // i:22, migr:21 DoubleSigningSlasher + ctx.deployDoubleSigningSlasher, + + // i:23, migr:22 DowntimeSlasher + ctx.deployDowntimeSlasher, + + // i:24, migr:23 GovernanceApproverMultiSig + ctx.deployGovernanceApproverMultiSig, + + // i:25, migr:24 GrandaMento + ctx.deployGrandaMento, + + // i:26, migr:25 FederatedAttestations + ctx.deployFederatedAttestations, + + // i:27, migr:26 OdisPayment + ctx.deployOdisPayments, + + // i:28, migr:27 Governance + ctx.deployGovernance, + + // i:29, migr:28 Elect Validators + ctx.electValidators, + } + + logger := ctx.logger.New() + + for i, step := range deploySteps { + logger.Info("Running deploy step", "number", i) + if err := step(); err != nil { + return nil, err + } + } + + // Flush Changes + _, err := ctx.statedb.Commit(true) + if err != nil { + return nil, err + } + ctx.statedb.IntermediateRoot(true) + + if err = ctx.verifyState(); err != nil { + return nil, err + } + + dumpConfig := state.DumpConfig{ + SkipCode: false, + SkipStorage: false, + OnlyWithAddresses: true, + Start: nil, + Max: 0, + } + dump := ctx.statedb.RawDump(&dumpConfig).Accounts + genesisAlloc := make(map[common.Address]core.GenesisAccount) + for acc, dumpAcc := range dump { + var account core.GenesisAccount + + if dumpAcc.Balance != "" { + account.Balance, _ = new(big.Int).SetString(dumpAcc.Balance, 10) + } + + account.Code = dumpAcc.Code + + if len(dumpAcc.Storage) > 0 { + account.Storage = make(map[common.Hash]common.Hash) + for k, v := range dumpAcc.Storage { + account.Storage[k] = common.HexToHash(v) + } + } + + genesisAlloc[acc] = account + + } + + return genesisAlloc, nil +} + +// Initialize Admin +func (ctx *deployContext) fundAdminAccount() { + ctx.statedb.SetBalance(ctx.accounts.AdminAccount().Address, new(big.Int).Set(adminGoldBalance)) +} + +func (ctx *deployContext) deployLibraries() error { + for _, name := range env.Libraries() { + bytecode := ctx.truffleReader.MustReadDeployedBytecodeFor(name) + ctx.statedb.SetCode(env.MustLibraryAddressFor(name), bytecode) + } + return nil +} + +// deployProxiedContract will deploy proxied contract +// It will deploy the proxy contract, the impl contract, and initialize both +func (ctx *deployContext) deployProxiedContract(name string, initialize func(contract *contract.EVMBackend) error) error { + proxyAddress := env.MustProxyAddressFor(name) + implAddress := env.MustImplAddressFor(name) + bytecode := ctx.truffleReader.MustReadDeployedBytecodeFor(name) + + logger := ctx.logger.New("contract", name) + logger.Info("Start Deploy of Proxied Contract", "proxyAddress", proxyAddress.Hex(), "implAddress", implAddress.Hex()) + + logger.Info("Deploy Proxy") + ctx.statedb.SetCode(proxyAddress, proxyByteCode) + ctx.statedb.SetState(proxyAddress, proxyOwnerStorageLocation, ctx.accounts.AdminAccount().Address.Hash()) + + logger.Info("Deploy Implementation") + ctx.statedb.SetCode(implAddress, bytecode) + + logger.Info("Set proxy implementation") + proxyContract := ctx.proxyContract(name) + + if err := proxyContract.SimpleCall("_setImplementation", implAddress); err != nil { + return err + } + + logger.Info("Initialize Contract") + if err := initialize(ctx.contract(name)); err != nil { + return err + } + + return nil +} + +// deployCoreContract will deploy a contract + proxy, and add it to the registry +func (ctx *deployContext) deployCoreContract(name string, initialize func(contract *contract.EVMBackend) error) error { + if err := ctx.deployProxiedContract(name, initialize); err != nil { + return err + } + + proxyAddress := env.MustProxyAddressFor(name) + ctx.logger.Info("Add entry to registry", "name", name, "address", proxyAddress) + if err := ctx.contract("Registry").SimpleCall("setAddressFor", name, proxyAddress); err != nil { + return err + } + + return nil +} + +func (ctx *deployContext) deployTransferWhitelist() error { + name := "TransferWhitelist" + logger := ctx.logger.New("contract", name) + + contract, err := contract.DeployCoreContract( + ctx.runtimeConfig, + "TransferWhitelist", + ctx.truffleReader.MustReadBytecodeFor("TransferWhitelist"), + env.MustProxyAddressFor("Registry"), + ) + if err != nil { + return err + } + logger.Info("Contract deployed", "address", contract.Address) + + logger.Debug("setDirectlyWhitelistedAddresses") + err = contract.SimpleCall("setDirectlyWhitelistedAddresses", ctx.genesisConfig.TransferWhitelist.Addresses) + if err != nil { + return err + } + + logger.Debug("setWhitelistedContractIdentifiers") + err = contract.SimpleCall("setWhitelistedContractIdentifiers", ctx.genesisConfig.TransferWhitelist.RegistryIDs) + if err != nil { + return err + } + + logger.Info("Add to Registry") + if err := ctx.contract("Registry").SimpleCall("setAddressFor", name, contract.Address); err != nil { + return err + } + + return nil +} + +func (ctx *deployContext) deployMultiSig(name string, params MultiSigParameters) (common.Address, error) { + err := ctx.deployProxiedContract(name, func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + params.Signatories, + newBigInt(params.NumRequiredConfirmations), + newBigInt(params.NumInternalRequiredConfirmations), + ) + }) + if err != nil { + return common.ZeroAddress, err + } + return env.MustProxyAddressFor(name), nil +} + +func (ctx *deployContext) deployReserveSpenderMultisig() error { + multiSigAddr, err := ctx.deployMultiSig("ReserveSpenderMultiSig", ctx.genesisConfig.ReserveSpenderMultiSig) + if err != nil { + return err + } + + if err := ctx.contract("Reserve").SimpleCall("addSpender", multiSigAddr); err != nil { + return err + } + return nil +} + +func (ctx *deployContext) deployGovernanceApproverMultiSig() error { + _, err := ctx.deployMultiSig("GovernanceApproverMultiSig", ctx.genesisConfig.GovernanceApproverMultiSig) + return err +} + +func (ctx *deployContext) deployGovernance() error { + approver := ctx.accounts.AdminAccount().Address + if ctx.genesisConfig.Governance.UseMultiSig { + approver = env.MustProxyAddressFor("GovernanceApproverMultiSig") + } + err := ctx.deployCoreContract("Governance", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + approver, + newBigInt(ctx.genesisConfig.Governance.ConcurrentProposals), + ctx.genesisConfig.Governance.MinDeposit, + newBigInt(ctx.genesisConfig.Governance.QueueExpiry), + newBigInt(ctx.genesisConfig.Governance.DequeueFrequency), + newBigInt(ctx.genesisConfig.Governance.ApprovalStageDuration), + newBigInt(ctx.genesisConfig.Governance.ReferendumStageDuration), + newBigInt(ctx.genesisConfig.Governance.ExecutionStageDuration), + ctx.genesisConfig.Governance.ParticipationBaseline.BigInt(), + ctx.genesisConfig.Governance.ParticipationFloor.BigInt(), + ctx.genesisConfig.Governance.BaselineUpdateFactor.BigInt(), + ctx.genesisConfig.Governance.BaselineQuorumFactor.BigInt(), + ) + }) + if err != nil { + return err + } + // We are skipping two steps for now: + // 1. Setting the governance thresholds from a constitution + // 2. Transferring ownership of the core contracts to governance + // While the monorepo migrations code does support them, in the configurations it's always + // set to skip them, so we can skip supporting them until it's needed. + return nil +} + +func (ctx *deployContext) deployRegistry() error { + return ctx.deployCoreContract("Registry", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize") + }) +} + +func (ctx *deployContext) deployBlockchainParameters() error { + return ctx.deployCoreContract("BlockchainParameters", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + big.NewInt(ctx.genesisConfig.Blockchain.Version.Major), + big.NewInt(ctx.genesisConfig.Blockchain.Version.Minor), + big.NewInt(ctx.genesisConfig.Blockchain.Version.Patch), + newBigInt(ctx.genesisConfig.Blockchain.GasForNonGoldCurrencies), + newBigInt(ctx.genesisConfig.Blockchain.BlockGasLimit), + newBigInt(3), + ) + }) +} + +func (ctx *deployContext) deployFreezer() error { + return ctx.deployCoreContract("Freezer", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize") + }) +} + +func (ctx *deployContext) deployGovernanceSlasher() error { + err := ctx.deployCoreContract("GovernanceSlasher", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + ) + }) + if err != nil { + return err + } + + return ctx.addSlasher("GovernanceSlasher") +} + +func (ctx *deployContext) addSlasher(slasherName string) error { + ctx.logger.Info("Adding new slasher", "slasher", slasherName) + return ctx.contract("LockedGold").SimpleCall("addSlasher", slasherName) +} + +func (ctx *deployContext) deployDoubleSigningSlasher() error { + err := ctx.deployCoreContract("DoubleSigningSlasher", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + ctx.genesisConfig.DoubleSigningSlasher.Penalty, + ctx.genesisConfig.DoubleSigningSlasher.Reward, + ) + }) + if err != nil { + return err + } + + return ctx.addSlasher("DoubleSigningSlasher") +} + +func (ctx *deployContext) deployDowntimeSlasher() error { + err := ctx.deployCoreContract("DowntimeSlasher", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + ctx.genesisConfig.DowntimeSlasher.Penalty, + ctx.genesisConfig.DowntimeSlasher.Reward, + newBigInt(ctx.genesisConfig.DowntimeSlasher.SlashableDowntime), + ) + }) + if err != nil { + return err + } + + return ctx.addSlasher("DowntimeSlasher") +} + +func (ctx *deployContext) deployAttestations() error { + return ctx.deployCoreContract("Attestations", func(contract *contract.EVMBackend) error { + dollar := decimal.NewFromBigInt(common.Big1, int32(ctx.genesisConfig.StableToken.Decimals)) + fee := dollar.Mul(ctx.genesisConfig.Attestations.AttestationRequestFeeInDollars) + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + newBigInt(ctx.genesisConfig.Attestations.AttestationExpiryBlocks), + newBigInt(ctx.genesisConfig.Attestations.SelectIssuersWaitBlocks), + newBigInt(ctx.genesisConfig.Attestations.MaxAttestations), + []common.Address{env.MustProxyAddressFor("StableToken")}, + []*big.Int{fee.BigInt()}, + ) + }) +} + +func (ctx *deployContext) deployEscrow() error { + return ctx.deployCoreContract("Escrow", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize") + }) +} + +func (ctx *deployContext) deployFeeCurrencyWhitelist() error { + return ctx.deployCoreContract("FeeCurrencyWhitelist", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize") + }) +} + +func (ctx *deployContext) deployGrandaMento() error { + approver := ctx.accounts.AdminAccount().Address + + err := ctx.deployCoreContract("GrandaMento", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + approver, + ctx.genesisConfig.GrandaMento.MaxApprovalExchangeRateChange.BigInt(), + ctx.genesisConfig.GrandaMento.Spread.BigInt(), + newBigInt(ctx.genesisConfig.GrandaMento.VetoPeriodSeconds), + ) + }) + if err != nil { + return err + } + + ctx.logger.Info("Adding GrandaMento as a new exchange spender to the reserve", "GrandaMento", env.MustProxyAddressFor("GrandaMento")) + ctx.contract("Reserve").SimpleCall("addExchangeSpender", env.MustProxyAddressFor("GrandaMento")) + + for _, exchangeLimit := range ctx.genesisConfig.GrandaMento.StableTokenExchangeLimits { + err = ctx.contract("GrandaMento").SimpleCall("setStableTokenExchangeLimits", exchangeLimit.StableToken, exchangeLimit.MinExchangeAmount, exchangeLimit.MaxExchangeAmount) + if err != nil { + return err + } + } + return nil +} + +func (ctx *deployContext) deployFederatedAttestations() error { + return ctx.deployCoreContract("FederatedAttestations", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize") + }) +} + +func (ctx *deployContext) deployOdisPayments() error { + return ctx.deployCoreContract("OdisPayments", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize") + }) +} + +func (ctx *deployContext) deployGoldToken() error { + err := ctx.deployCoreContract("GoldToken", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", env.MustProxyAddressFor("Registry")) + }) + if err != nil { + return err + } + + if ctx.genesisConfig.GoldToken.Frozen { + ctx.logger.Info("Freezing GoldToken") + err = ctx.contract("Freezer").SimpleCall("freeze", env.MustProxyAddressFor("GoldToken")) + if err != nil { + return err + } + } + + for _, bal := range ctx.genesisConfig.GoldToken.InitialBalances { + ctx.statedb.SetBalance(bal.Account, bal.Amount) + } + + return nil +} + +func (ctx *deployContext) deployExchanges() error { + type ExchangeConfig struct { + contract string + stableTokenContract string + cfg ExchangeParameters + } + exchanges := []ExchangeConfig{ + {"Exchange", "StableToken", ctx.genesisConfig.Exchange}, + {"ExchangeEUR", "StableTokenEUR", ctx.genesisConfig.ExchangeEUR}, + {"ExchangeBRL", "StableTokenBRL", ctx.genesisConfig.ExchangeBRL}, + } + for _, exchange := range exchanges { + err := ctx.deployCoreContract(exchange.contract, func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + exchange.stableTokenContract, + exchange.cfg.Spread.BigInt(), + exchange.cfg.ReserveFraction.BigInt(), + newBigInt(exchange.cfg.UpdateFrequency), + newBigInt(exchange.cfg.MinimumReports), + ) + }) + if err != nil { + return err + } + + if exchange.cfg.Frozen { + ctx.logger.Info("Freezing Exchange", "contract", exchange.contract) + err = ctx.contract("Freezer").SimpleCall("freeze", env.MustProxyAddressFor("Exchange")) + if err != nil { + return err + } + } + } + return nil +} + +func (ctx *deployContext) deployEpochRewards() error { + err := ctx.deployCoreContract("EpochRewards", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + ctx.genesisConfig.EpochRewards.TargetVotingYieldInitial.BigInt(), + ctx.genesisConfig.EpochRewards.TargetVotingYieldMax.BigInt(), + ctx.genesisConfig.EpochRewards.TargetVotingYieldAdjustmentFactor.BigInt(), + ctx.genesisConfig.EpochRewards.RewardsMultiplierMax.BigInt(), + ctx.genesisConfig.EpochRewards.RewardsMultiplierAdjustmentFactorsUnderspend.BigInt(), + ctx.genesisConfig.EpochRewards.RewardsMultiplierAdjustmentFactorsOverspend.BigInt(), + ctx.genesisConfig.EpochRewards.TargetVotingGoldFraction.BigInt(), + ctx.genesisConfig.EpochRewards.MaxValidatorEpochPayment, + ctx.genesisConfig.EpochRewards.CommunityRewardFraction.BigInt(), + ctx.genesisConfig.EpochRewards.CarbonOffsettingPartner, + ctx.genesisConfig.EpochRewards.CarbonOffsettingFraction.BigInt(), + ) + }) + if err != nil { + return err + } + + if ctx.genesisConfig.EpochRewards.Frozen { + ctx.logger.Info("Freezing EpochRewards") + err = ctx.contract("Freezer").SimpleCall("freeze", env.MustProxyAddressFor("EpochRewards")) + if err != nil { + return err + } + } + return nil +} + +func (ctx *deployContext) deployAccounts() error { + return ctx.deployCoreContract("Accounts", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", env.MustProxyAddressFor("Registry")) + }) +} + +func (ctx *deployContext) deployRandom() error { + return ctx.deployCoreContract("Random", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + newBigInt(ctx.genesisConfig.Random.RandomnessBlockRetentionWindow), + ) + }) +} + +func (ctx *deployContext) deployLockedGold() error { + return ctx.deployCoreContract("LockedGold", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + newBigInt(ctx.genesisConfig.LockedGold.UnlockingPeriod), + ) + }) +} + +func (ctx *deployContext) deployValidators() error { + return ctx.deployCoreContract("Validators", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + ctx.genesisConfig.Validators.GroupLockedGoldRequirements.Value, + newBigInt(ctx.genesisConfig.Validators.GroupLockedGoldRequirements.Duration), + ctx.genesisConfig.Validators.ValidatorLockedGoldRequirements.Value, + newBigInt(ctx.genesisConfig.Validators.ValidatorLockedGoldRequirements.Duration), + newBigInt(ctx.genesisConfig.Validators.ValidatorScoreExponent), + ctx.genesisConfig.Validators.ValidatorScoreAdjustmentSpeed.BigInt(), + newBigInt(ctx.genesisConfig.Validators.MembershipHistoryLength), + newBigInt(ctx.genesisConfig.Validators.SlashingPenaltyResetPeriod), + newBigInt(ctx.genesisConfig.Validators.MaxGroupSize), + newBigInt(ctx.genesisConfig.Validators.CommissionUpdateDelay), + newBigInt(ctx.genesisConfig.Validators.DowntimeGracePeriod), + ) + }) +} + +func (ctx *deployContext) deployElection() error { + return ctx.deployCoreContract("Election", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + newBigInt(ctx.genesisConfig.Election.MinElectableValidators), + newBigInt(ctx.genesisConfig.Election.MaxElectableValidators), + ctx.genesisConfig.Election.MaxVotesPerAccount, + ctx.genesisConfig.Election.ElectabilityThreshold.BigInt(), + ) + }) +} + +func (ctx *deployContext) deploySortedOracles() error { + return ctx.deployCoreContract("SortedOracles", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + newBigInt(ctx.genesisConfig.SortedOracles.ReportExpirySeconds), + ) + }) +} + +func (ctx *deployContext) deployGasPriceMinimum() error { + return ctx.deployCoreContract("GasPriceMinimum", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + ctx.genesisConfig.GasPriceMinimum.MinimumFloor, + ctx.genesisConfig.GasPriceMinimum.TargetDensity.BigInt(), + ctx.genesisConfig.GasPriceMinimum.AdjustmentSpeed.BigInt(), + ) + }) +} + +func (ctx *deployContext) deployReserve() error { + err := ctx.deployCoreContract("Reserve", func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + env.MustProxyAddressFor("Registry"), + newBigInt(ctx.genesisConfig.Reserve.TobinTaxStalenessThreshold), + ctx.genesisConfig.Reserve.DailySpendingRatio.BigInt(), + big.NewInt(0), + big.NewInt(0), + ctx.genesisConfig.Reserve.AssetAllocations.SymbolsABI(), + ctx.genesisConfig.Reserve.AssetAllocations.Weights(), + ctx.genesisConfig.Reserve.TobinTax.BigInt(), + ctx.genesisConfig.Reserve.TobinTaxReserveRatio.BigInt(), + ) + }) + if err != nil { + return err + } + + logger := ctx.logger.New("contract", "Reserve") + contract := ctx.contract("Reserve") + + if ctx.genesisConfig.Reserve.InitialBalance != nil && ctx.genesisConfig.Reserve.InitialBalance.Cmp(big.NewInt(0)) > 0 { + logger.Info("Setting Initial Balance") + ctx.statedb.SetBalance(contract.Address, ctx.genesisConfig.Reserve.InitialBalance) + + if ctx.genesisConfig.Reserve.FrozenAssetsDays > 0 && ctx.genesisConfig.Reserve.FrozenAssetsStartBalance.Cmp(big.NewInt(0)) > 0 { + err := contract.SimpleCall("setFrozenGold", + ctx.genesisConfig.Reserve.FrozenAssetsStartBalance, + newBigInt(ctx.genesisConfig.Reserve.FrozenAssetsDays), + ) + if err != nil { + return err + } + } + } + + for _, spender := range ctx.genesisConfig.Reserve.Spenders { + if err := contract.SimpleCall("addSpender", spender); err != nil { + return err + } + } + + for _, otherAddress := range ctx.genesisConfig.Reserve.OtherAddresses { + if err := contract.SimpleCall("addOtherReserveAddress", otherAddress); err != nil { + return err + } + } + + return nil +} + +func (ctx *deployContext) deployStableTokens() error { + type StableTokenConfig struct { + contract string + cfg StableTokenParameters + } + tokens := []StableTokenConfig{ + {"StableToken", ctx.genesisConfig.StableToken}, + {"StableTokenEUR", ctx.genesisConfig.StableTokenEUR}, + {"StableTokenBRL", ctx.genesisConfig.StableTokenBRL}, + } + for _, token := range tokens { + err := ctx.deployCoreContract(token.contract, func(contract *contract.EVMBackend) error { + return contract.SimpleCall("initialize", + token.cfg.Name, + token.cfg.Symbol, + token.cfg.Decimals, + env.MustProxyAddressFor("Registry"), + token.cfg.Rate.BigInt(), + newBigInt(token.cfg.InflationFactorUpdatePeriod), + token.cfg.InitialBalances.Accounts(), + token.cfg.InitialBalances.Amounts(), + token.cfg.ExchangeIdentifier, + ) + }) + if err != nil { + return err + } + + stableTokenAddress := env.MustProxyAddressFor(token.contract) + + if token.cfg.Frozen { + ctx.logger.Info("Freezing StableToken", "contract", token.contract) + err = ctx.contract("Freezer").SimpleCall("freeze", stableTokenAddress) + if err != nil { + return err + } + } + + // Configure StableToken Oracles + for _, oracleAddress := range token.cfg.Oracles { + ctx.logger.Info("Adding oracle for StableToken", "contract", token.contract, "oracle", oracleAddress) + err = ctx.contract("SortedOracles").SimpleCall("addOracle", stableTokenAddress, oracleAddress) + if err != nil { + return err + } + } + + // If requested, fix goldPrice of stable token + if token.cfg.GoldPrice != nil { + ctx.logger.Info("Fixing StableToken goldPrice", "contract", token.contract) + + // first check if the admin is an authorized oracle + authorized := false + for _, oracleAddress := range token.cfg.Oracles { + if oracleAddress == ctx.accounts.AdminAccount().Address { + authorized = true + break + } + } + + if !authorized { + ctx.logger.Warn("Fixing StableToken goldprice requires setting admin as oracle", "admin", ctx.accounts.AdminAccount().Address) + err = ctx.contract("SortedOracles").SimpleCall("addOracle", stableTokenAddress, ctx.accounts.AdminAccount().Address) + if err != nil { + return err + } + } + + ctx.logger.Info("Reporting price of StableToken to oracle", "contract", token.contract) + err = ctx.contract("SortedOracles").SimpleCall("report", + stableTokenAddress, + token.cfg.GoldPrice.BigInt(), + common.ZeroAddress, + common.ZeroAddress, + ) + if err != nil { + return err + } + + ctx.logger.Info("Add StableToken to the reserve", "contract", token.contract) + err = ctx.contract("Reserve").SimpleCall("addToken", stableTokenAddress) + if err != nil { + return err + } + } + + ctx.logger.Info("Whitelisting StableToken as a fee currency", "contract", token.contract) + err = ctx.contract("FeeCurrencyWhitelist").SimpleCall("addToken", stableTokenAddress) + if err != nil { + return err + } + } + return nil +} + +func (ctx *deployContext) createAccounts(accs []env.Account, namePrefix string) error { + accounts := ctx.contract("Accounts") + + for i, acc := range accs { + name := fmt.Sprintf("%s %03d", namePrefix, i) + ctx.logger.Info("Create account", "address", acc.Address, "name", name) + + if err := accounts.SimpleCallFrom(acc.Address, "createAccount"); err != nil { + return err + } + + if err := accounts.SimpleCallFrom(acc.Address, "setName", name); err != nil { + return err + } + + if err := accounts.SimpleCallFrom(acc.Address, "setAccountDataEncryptionKey", acc.PublicKey()); err != nil { + return err + } + + // Missing: authorizeAttestationSigner + } + return nil +} + +func (ctx *deployContext) registerValidators() error { + validatorAccounts := ctx.accounts.ValidatorAccounts() + requiredAmount := ctx.genesisConfig.Validators.ValidatorLockedGoldRequirements.Value + + if err := ctx.createAccounts(validatorAccounts, "validator"); err != nil { + return err + } + + lockedGold := ctx.contract("LockedGold") + validators := ctx.contract("Validators") + + for _, validator := range validatorAccounts { + address := validator.Address + logger := ctx.logger.New("validator", address) + + ctx.statedb.AddBalance(address, requiredAmount) + + logger.Info("Lock validator gold", "amount", requiredAmount) + if _, err := lockedGold.Call(contract.CallOpts{Origin: address, Value: requiredAmount}, "lock"); err != nil { + return err + } + + logger.Info("Register validator") + blsPub, err := validator.BLSPublicKey() + if err != nil { + return err + } + + // remove the 0x04 prefix from the pub key (we need the 64 bytes variant) + pubKey := validator.PublicKey()[1:] + err = validators.SimpleCallFrom(address, "registerValidator", pubKey, blsPub[:], validator.MustBLSProofOfPossession()) + if err != nil { + return err + } + } + return nil +} + +func (ctx *deployContext) registerValidatorGroups() error { + validatorGroupsAccounts := ctx.accounts.ValidatorGroupAccounts() + + if err := ctx.createAccounts(validatorGroupsAccounts, "group"); err != nil { + return err + } + + lockedGold := ctx.contract("LockedGold") + validators := ctx.contract("Validators") + + groupRequiredGold := new(big.Int).Mul( + ctx.genesisConfig.Validators.GroupLockedGoldRequirements.Value, + big.NewInt(int64(ctx.accounts.ValidatorsPerGroup)), + ) + groupCommission := ctx.genesisConfig.Validators.Commission.BigInt() + + for _, group := range validatorGroupsAccounts { + address := group.Address + logger := ctx.logger.New("group", address) + + ctx.statedb.AddBalance(address, groupRequiredGold) + + logger.Info("Lock group gold", "amount", groupRequiredGold) + if _, err := lockedGold.Call(contract.CallOpts{Origin: address, Value: groupRequiredGold}, "lock"); err != nil { + return err + } + + logger.Info("Register group") + if err := validators.SimpleCallFrom(address, "registerValidatorGroup", groupCommission); err != nil { + return err + } + } + + return nil +} + +func (ctx *deployContext) addValidatorsToGroups() error { + validators := ctx.contract("Validators") + + validatorGroups := ctx.accounts.ValidatorGroups() + for groupIdx, group := range validatorGroups { + groupAddress := group.Address + prevGroupAddress := common.ZeroAddress + if groupIdx > 0 { + prevGroupAddress = validatorGroups[groupIdx-1].Address + } + + for i, validator := range group.Validators { + ctx.logger.Info("Add validator to group", "validator", validator.Address, "group", groupAddress) + + // affiliate validators to group + if err := validators.SimpleCallFrom(validator.Address, "affiliate", groupAddress); err != nil { + return err + } + + // accept validator as group member + if i == 0 { + // when adding first member, we define group voting order + // since every group start with zero votes, we just use the prevGroup Address as the greater address + // thus ending group order is: + // [ groupZero, groupOne, ..., lastgroup] + + if err := validators.SimpleCallFrom(groupAddress, "addFirstMember", validator.Address, common.ZeroAddress, prevGroupAddress); err != nil { + return err + } + } else { + if err := validators.SimpleCallFrom(groupAddress, "addMember", validator.Address); err != nil { + return err + } + } + } + } + + return nil +} + +func (ctx *deployContext) voteForGroups() error { + election := ctx.contract("Election") + + validatorGroups := ctx.accounts.ValidatorGroupAccounts() + + // value previously locked on registerValidatorGroups() + lockedGoldOnGroup := new(big.Int).Mul( + ctx.genesisConfig.Validators.GroupLockedGoldRequirements.Value, + big.NewInt(int64(ctx.accounts.ValidatorsPerGroup)), + ) + + // current group order (see `addFirstMember` on addValidatorsToGroup) is: + // [ groupZero, groupOne, ..., lastgroup] + + // each group votes for themselves. + // each group votes the SAME AMOUNT + // each group starts with 0 votes + + // so, everytime we vote, that group becomes the one with most votes (most or equal) + // hence, we use: + // greater = zero (we become the one with most votes) + // lesser = currentLeader + + // special case: only one group (no lesser or greater) + if len(validatorGroups) == 1 { + groupAddress := validatorGroups[0].Address + ctx.logger.Info("Vote for group", "group", groupAddress, "amount", lockedGoldOnGroup) + return election.SimpleCallFrom(groupAddress, "vote", groupAddress, lockedGoldOnGroup, common.ZeroAddress, common.ZeroAddress) + } + + // first to vote is group 0, which is already the leader. Hence lesser should go to group 1 + currentLeader := validatorGroups[1].Address + for _, group := range validatorGroups { + groupAddress := group.Address + + ctx.logger.Info("Vote for group", "group", groupAddress, "amount", lockedGoldOnGroup) + if err := election.SimpleCallFrom(groupAddress, "vote", groupAddress, lockedGoldOnGroup, currentLeader, common.ZeroAddress); err != nil { + return err + } + + // we now become the currentLeader + currentLeader = groupAddress + } + + return nil +} + +func (ctx *deployContext) electValidators() error { + if err := ctx.registerValidators(); err != nil { + return err + } + + if err := ctx.registerValidatorGroups(); err != nil { + return err + } + + if err := ctx.addValidatorsToGroups(); err != nil { + return err + } + + if err := ctx.voteForGroups(); err != nil { + return err + } + + // TODO: Config checks + // check that we have enough validators (validators >= election.minElectableValidators) + // check that validatorsPerGroup is <= valdiators.maxGroupSize + + return nil +} + +func (ctx *deployContext) contract(contractName string) *contract.EVMBackend { + return contract.CoreContract(ctx.runtimeConfig, contractName, env.MustProxyAddressFor(contractName)) +} + +func (ctx *deployContext) proxyContract(contractName string) *contract.EVMBackend { + return contract.ProxyContract(ctx.runtimeConfig, contractName, env.MustProxyAddressFor(contractName)) +} + +func (ctx *deployContext) verifyState() error { + snapshotVersion := ctx.statedb.Snapshot() + defer ctx.statedb.RevertToSnapshot(snapshotVersion) + + var reserveSpenders []common.Address + if _, err := ctx.contract("Reserve").Query(&reserveSpenders, "getExchangeSpenders"); err != nil { + return err + } + fmt.Printf("Checking getExchangeSpenders. spenders = %s\n", reserveSpenders) + + var ( + numerator = new(*big.Int) + denominator = new(*big.Int) + ) + out := &[]interface{}{ + numerator, + denominator, + } + if _, err := ctx.contract("SortedOracles").Query(out, "medianRate", env.MustProxyAddressFor("StableToken")); err != nil { + return err + } + fmt.Printf("Checking medianRate. numerator = %s denominator = %s \n", (*numerator).String(), (*denominator).String()) + + var gasPrice *big.Int + if _, err := ctx.contract("GasPriceMinimum").Query(&gasPrice, "getGasPriceMinimum", env.MustProxyAddressFor("StableToken")); err != nil { + return err + } + fmt.Printf("Checking gas price minimum. cusdValue = %s\n", gasPrice.String()) + + return nil +}
diff --git go-ethereum/mycelo/contract/gen_abis.go celo/mycelo/contract/gen_abis.go new file mode 100644 index 0000000000000000000000000000000000000000..bc5fafa86e57805cb4f2790f285870f0d18fab0d --- /dev/null +++ celo/mycelo/contract/gen_abis.go @@ -0,0 +1,28775 @@ +// Code generated by go generate; DO NOT EDIT. +// 2023-02-14 09:28:52.792897 -0300 -03 m=+0.298628811 +package contract + +import "github.com/ethereum/go-ethereum/accounts/abi" + +var abis map[string]*abi.ABI + +func init() { + abis = make(map[string]*abi.ABI) + + // Accounts ABI + abis["Accounts"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "AccountCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "dataEncryptionKey", + "type": "bytes" + } + ], + "name": "AccountDataEncryptionKeySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "metadataURL", + "type": "string" + } + ], + "name": "AccountMetadataURLSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "name": "AccountNameSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "walletAddress", + "type": "address" + } + ], + "name": "AccountWalletAddressSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "AttestationSignerAuthorized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "oldSigner", + "type": "address" + } + ], + "name": "AttestationSignerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "oldSigner", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "DefaultSignerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "DefaultSignerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "oldSigner", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "IndexedSignerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "IndexedSignerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "oldSigner", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "LegacySignerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "LegacySignerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "url", + "type": "bytes" + } + ], + "name": "OffchainStorageRootAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "url", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "OffchainStorageRootRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fraction", + "type": "uint256" + } + ], + "name": "PaymentDelegationSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "SignerAuthorizationCompleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "SignerAuthorizationStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "SignerAuthorized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "oldSigner", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "SignerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "ValidatorSignerAuthorized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "oldSigner", + "type": "address" + } + ], + "name": "ValidatorSignerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "VoteSignerAuthorized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "oldSigner", + "type": "address" + } + ], + "name": "VoteSignerRemoved", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "EIP712_AUTHORIZE_SIGNER_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "authorizedBy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "eip712DomainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "offchainStorageRoots", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "setEip712DomainSeparator", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "bytes", + "name": "dataEncryptionKey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "walletAddress", + "type": "address" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "setAccount", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "createAccount", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "name": "setName", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "walletAddress", + "type": "address" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "setWalletAddress", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes", + "name": "dataEncryptionKey", + "type": "bytes" + } + ], + "name": "setAccountDataEncryptionKey", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "metadataURL", + "type": "string" + } + ], + "name": "setMetadataURL", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes", + "name": "url", + "type": "bytes" + } + ], + "name": "addStorageRoot", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "removeStorageRoot", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getOffchainStorageRoots", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fraction", + "type": "uint256" + } + ], + "name": "setPaymentDelegation", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getPaymentDelegation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "setIndexedSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "authorizeSignerWithSignature", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "authorizeVoteSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "authorizeValidatorSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "ecdsaPublicKey", + "type": "bytes" + } + ], + "name": "authorizeValidatorSignerWithPublicKey", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "ecdsaPublicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPublicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "authorizeValidatorSignerWithKeys", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "authorizeAttestationSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "authorizeSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "completeSignerAuthorization", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "isLegacySigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "isDefaultSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "isIndexedSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "isSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "removeDefaultSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "removeIndexedSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "removeSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "removeVoteSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "removeValidatorSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "removeAttestationSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "attestationSignerToAccount", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "validatorSignerToAccount", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "voteSignerToAccount", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "signerToAccount", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "isLegacyRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getLegacySigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getDefaultSigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getIndexedSigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getVoteSigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getValidatorSigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAttestationSigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "hasLegacySigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "hasDefaultSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "hasIndexedSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "string", + "name": "role", + "type": "string" + } + ], + "name": "hasAuthorizedSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasAuthorizedVoteSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasAuthorizedValidatorSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasAuthorizedAttestationSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getName", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getMetadataURL", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address[]", + "name": "accountsToQuery", + "type": "address[]" + } + ], + "name": "batchGetMetadataURL", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getDataEncryptionKey", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getWalletAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "isAccount", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "isAuthorizedSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "getRoleAuthorizationSigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // Attestations ABI + abis["Attestations"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "issuer", + "type": "address" + } + ], + "name": "AttestationCompleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "AttestationExpiryBlocksSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "issuer", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "attestationRequestFeeToken", + "type": "address" + } + ], + "name": "AttestationIssuerSelected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "AttestationRequestFeeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "attestationsRequested", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "attestationRequestFeeToken", + "type": "address" + } + ], + "name": "AttestationsRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "fromAccount", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "toAccount", + "type": "address" + } + ], + "name": "AttestationsTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "MaxAttestationsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "SelectIssuersWaitBlocksSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "approver", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "indentifier", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "TransferApproval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "attestationExpiryBlocks", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "attestationRequestFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "maxAttestations", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "pendingWithdrawals", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "selectIssuersWaitBlocks", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "transferApprovals", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_attestationExpiryBlocks", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_selectIssuersWaitBlocks", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxAttestations", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "attestationRequestFeeTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "attestationRequestFeeValues", + "type": "uint256[]" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "attestationsRequested", + "type": "uint256" + }, + { + "internalType": "address", + "name": "attestationRequestFeeToken", + "type": "address" + } + ], + "name": "request", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + } + ], + "name": "selectIssuers", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "complete", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "revoke", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getUnselectedRequest", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAttestationIssuers", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAttestationStats", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32[]", + "name": "identifiersToLookup", + "type": "bytes32[]" + } + ], + "name": "batchGetAttestationStats", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "", + "type": "address[]" + }, + { + "internalType": "uint64[]", + "name": "", + "type": "uint64[]" + }, + { + "internalType": "uint64[]", + "name": "", + "type": "uint64[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "issuer", + "type": "address" + } + ], + "name": "getAttestationState", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getCompletableAttestations", + "outputs": [ + { + "internalType": "uint32[]", + "name": "", + "type": "uint32[]" + }, + { + "internalType": "address[]", + "name": "", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getAttestationRequestFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "setAttestationRequestFee", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_attestationExpiryBlocks", + "type": "uint256" + } + ], + "name": "setAttestationExpiryBlocks", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_selectIssuersWaitBlocks", + "type": "uint256" + } + ], + "name": "setSelectIssuersWaitBlocks", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_maxAttestations", + "type": "uint256" + } + ], + "name": "setMaxAttestations", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getMaxAttestations", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "validateAttestationCode", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + } + ], + "name": "lookupAccountsForIdentifier", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint32", + "name": "expected", + "type": "uint32" + } + ], + "name": "requireNAttestationsRequested", + "outputs": [], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bool", + "name": "status", + "type": "bool" + } + ], + "name": "approveTransfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // BlockchainParameters ABI + abis["BlockchainParameters"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "BlockGasLimitSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "gas", + "type": "uint256" + } + ], + "name": "IntrinsicGasForAlternativeFeeCurrencySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "major", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "minor", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "patch", + "type": "uint256" + } + ], + "name": "MinimumClientVersionSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "window", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "activationEpoch", + "type": "uint256" + } + ], + "name": "UptimeLookbackWindowSet", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "blockGasLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "intrinsicGasForAlternativeFeeCurrency", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "uptimeLookbackWindow", + "outputs": [ + { + "internalType": "uint256", + "name": "oldValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nextValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nextValueActivationEpoch", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "major", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "patch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_gasForNonGoldCurrencies", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lookbackWindow", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "major", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "patch", + "type": "uint256" + } + ], + "name": "setMinimumClientVersion", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "setBlockGasLimit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "gas", + "type": "uint256" + } + ], + "name": "setIntrinsicGasForAlternativeFeeCurrency", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "window", + "type": "uint256" + } + ], + "name": "setUptimeLookbackWindow", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getUptimeLookbackWindow", + "outputs": [ + { + "internalType": "uint256", + "name": "lookbackWindow", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getMinimumClientVersion", + "outputs": [ + { + "internalType": "uint256", + "name": "major", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "patch", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // DoubleSigningSlasher ABI + abis["DoubleSigningSlasher"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "DoubleSigningSlashPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "penalty", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "SlashingIncentivesSet", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "groupMembershipHistoryIndex", + "type": "uint256" + } + ], + "name": "groupMembershipAtBlock", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "penalty", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "setSlashingIncentives", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "slashingIncentives", + "outputs": [ + { + "internalType": "uint256", + "name": "penalty", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_penalty", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_reward", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "headerA", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "headerB", + "type": "bytes" + } + ], + "name": "checkForDoubleSigning", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "headerA", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "headerB", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "groupMembershipHistoryIndex", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "validatorElectionLessers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "validatorElectionGreaters", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "validatorElectionIndices", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "groupElectionLessers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "groupElectionGreaters", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "groupElectionIndices", + "type": "uint256[]" + } + ], + "name": "slash", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // DowntimeSlasher ABI + abis["DowntimeSlasher"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "startBlock", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "endBlock", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "bitmap", + "type": "bytes32" + } + ], + "name": "BitmapSetForInterval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "startBlock", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "endBlock", + "type": "uint256" + } + ], + "name": "DowntimeSlashPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "interval", + "type": "uint256" + } + ], + "name": "SlashableDowntimeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "penalty", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "SlashingIncentivesSet", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "bitmaps", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "groupMembershipHistoryIndex", + "type": "uint256" + } + ], + "name": "groupMembershipAtBlock", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "lastSlashedBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "penalty", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "setSlashingIncentives", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "slashableDowntime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "slashingIncentives", + "outputs": [ + { + "internalType": "uint256", + "name": "penalty", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_penalty", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_reward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_slashableDowntime", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "interval", + "type": "uint256" + } + ], + "name": "setSlashableDowntime", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "startBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endBlock", + "type": "uint256" + } + ], + "name": "getBitmapForInterval", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "startBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endBlock", + "type": "uint256" + } + ], + "name": "setBitmapForInterval", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "startBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "signerIndex", + "type": "uint256" + } + ], + "name": "wasDownForInterval", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "startBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endBlock", + "type": "uint256" + } + ], + "name": "isBitmapSetForInterval", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256[]", + "name": "startBlocks", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "endBlocks", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "signerIndices", + "type": "uint256[]" + } + ], + "name": "wasDownForIntervals", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256[]", + "name": "startBlocks", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "endBlocks", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "signerIndices", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "groupMembershipHistoryIndex", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "validatorElectionLessers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "validatorElectionGreaters", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "validatorElectionIndices", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "groupElectionLessers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "groupElectionGreaters", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "groupElectionIndices", + "type": "uint256[]" + } + ], + "name": "slash", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // Election ABI + abis["Election"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "electabilityThreshold", + "type": "uint256" + } + ], + "name": "ElectabilityThresholdSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "name": "ElectableValidatorsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "EpochRewardsDistributedToVoters", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "maxNumGroupsVotedFor", + "type": "uint256" + } + ], + "name": "MaxNumGroupsVotedForSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "units", + "type": "uint256" + } + ], + "name": "ValidatorGroupActiveVoteRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "ValidatorGroupMarkedEligible", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "ValidatorGroupMarkedIneligible", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "ValidatorGroupPendingVoteRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "units", + "type": "uint256" + } + ], + "name": "ValidatorGroupVoteActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "ValidatorGroupVoteCast", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "electabilityThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "electableValidators", + "outputs": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "maxNumGroupsVotedFor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minElectableValidators", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxElectableValidators", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxNumGroupsVotedFor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_electabilityThreshold", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "name": "setElectableValidators", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getElectableValidators", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_maxNumGroupsVotedFor", + "type": "uint256" + } + ], + "name": "setMaxNumGroupsVotedFor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + } + ], + "name": "setElectabilityThreshold", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getElectabilityThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "address", + "name": "lesser", + "type": "address" + }, + { + "internalType": "address", + "name": "greater", + "type": "address" + } + ], + "name": "vote", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "activate", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "activateForAccount", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "hasActivatablePendingVotes", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "address", + "name": "lesser", + "type": "address" + }, + { + "internalType": "address", + "name": "greater", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "revokePending", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "address", + "name": "lesser", + "type": "address" + }, + { + "internalType": "address", + "name": "greater", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "revokeAllActive", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "address", + "name": "lesser", + "type": "address" + }, + { + "internalType": "address", + "name": "greater", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "revokeActive", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getTotalVotesByAccount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getPendingVotesForGroupByAccount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getActiveVotesForGroupByAccount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getTotalVotesForGroupByAccount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getActiveVoteUnitsForGroupByAccount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "getActiveVoteUnitsForGroup", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "getTotalVotesForGroup", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "getActiveVotesForGroup", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "getPendingVotesForGroup", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "getGroupEligibility", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "uint256", + "name": "totalEpochRewards", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "uptimes", + "type": "uint256[]" + } + ], + "name": "getGroupEpochRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "address", + "name": "lesser", + "type": "address" + }, + { + "internalType": "address", + "name": "greater", + "type": "address" + } + ], + "name": "distributeEpochRewards", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "markGroupIneligible", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "address", + "name": "lesser", + "type": "address" + }, + { + "internalType": "address", + "name": "greater", + "type": "address" + } + ], + "name": "markGroupEligible", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getGroupsVotedForByAccount", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "canReceiveVotes", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "getNumVotesReceivable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTotalVotes", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getActiveVotes", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEligibleValidatorGroups", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTotalVotesForEligibleValidatorGroups", + "outputs": [ + { + "internalType": "address[]", + "name": "groups", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "electValidatorSigners", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "minElectableValidators", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxElectableValidators", + "type": "uint256" + } + ], + "name": "electNValidatorSigners", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getCurrentValidatorSigners", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "lessers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "greaters", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "indices", + "type": "uint256[]" + } + ], + "name": "forceDecrementVotes", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // EpochRewards ABI + abis["EpochRewards"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "partner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fraction", + "type": "uint256" + } + ], + "name": "CarbonOffsettingFundSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "fraction", + "type": "uint256" + } + ], + "name": "CommunityRewardFractionSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "max", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "underspendAdjustmentFactor", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "overspendAdjustmentFactor", + "type": "uint256" + } + ], + "name": "RewardsMultiplierParametersSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "payment", + "type": "uint256" + } + ], + "name": "TargetValidatorEpochPaymentSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "fraction", + "type": "uint256" + } + ], + "name": "TargetVotingGoldFractionSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "max", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "adjustmentFactor", + "type": "uint256" + } + ], + "name": "TargetVotingYieldParametersSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "target", + "type": "uint256" + } + ], + "name": "TargetVotingYieldSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "fraction", + "type": "uint256" + } + ], + "name": "TargetVotingYieldUpdated", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "carbonOffsettingPartner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "startTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "targetValidatorEpochPayment", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "targetVotingYieldInitial", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "targetVotingYieldMax", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "targetVotingYieldAdjustmentFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardsMultiplierMax", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardsMultiplierUnderspendAdjustmentFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardsMultiplierOverspendAdjustmentFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_targetVotingGoldFraction", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_targetValidatorEpochPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_communityRewardFraction", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_carbonOffsettingPartner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_carbonOffsettingFraction", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTargetVotingYieldParameters", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getRewardsMultiplierParameters", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setCommunityRewardFraction", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getCommunityRewardFraction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "partner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setCarbonOffsettingFund", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getCarbonOffsettingFraction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setTargetVotingGoldFraction", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTargetVotingGoldFraction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setTargetValidatorEpochPayment", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "underspendAdjustmentFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "overspendAdjustmentFactor", + "type": "uint256" + } + ], + "name": "setRewardsMultiplierParameters", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustmentFactor", + "type": "uint256" + } + ], + "name": "setTargetVotingYieldParameters", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "targetVotingYield", + "type": "uint256" + } + ], + "name": "setTargetVotingYield", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTargetGoldTotalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTargetVoterRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTargetTotalEpochPaymentsInGold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getRewardsMultiplier", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVotingGoldFraction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "updateTargetVotingYield", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isReserveLow", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "calculateTargetEpochRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // Escrow ABI + abis["Escrow"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "trustedIssuer", + "type": "address" + } + ], + "name": "DefaultTrustedIssuerAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "trustedIssuer", + "type": "address" + } + ], + "name": "DefaultTrustedIssuerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "by", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "paymentId", + "type": "address" + } + ], + "name": "Revocation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "paymentId", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "minAttestations", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "paymentId", + "type": "address" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "trustedIssuers", + "type": "address[]" + } + ], + "name": "TrustedIssuersSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "paymentId", + "type": "address" + } + ], + "name": "TrustedIssuersUnset", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "paymentId", + "type": "address" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_TRUSTED_ISSUERS_PER_PAYMENT", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "defaultTrustedIssuers", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "escrowedPayments", + "outputs": [ + { + "internalType": "bytes32", + "name": "recipientIdentifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "sentIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "receivedIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expirySeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAttestations", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "receivedPaymentIds", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registryContract", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "sentPaymentIds", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "trustedIssuersPerPayment", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "trustedIssuer", + "type": "address" + } + ], + "name": "addDefaultTrustedIssuer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "trustedIssuer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "removeDefaultTrustedIssuer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expirySeconds", + "type": "uint256" + }, + { + "internalType": "address", + "name": "paymentId", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minAttestations", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expirySeconds", + "type": "uint256" + }, + { + "internalType": "address", + "name": "paymentId", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minAttestations", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "trustedIssuers", + "type": "address[]" + } + ], + "name": "transferWithTrustedIssuers", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "paymentId", + "type": "address" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "withdraw", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "paymentId", + "type": "address" + } + ], + "name": "revoke", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + } + ], + "name": "getReceivedPaymentIds", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "getSentPaymentIds", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "paymentId", + "type": "address" + } + ], + "name": "getTrustedIssuersPerPayment", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getDefaultTrustedIssuers", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // Exchange ABI + abis["Exchange"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "goldBucket", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "stableBucket", + "type": "uint256" + } + ], + "name": "BucketsUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "exchanger", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "soldGold", + "type": "bool" + } + ], + "name": "Exchanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "minimumReports", + "type": "uint256" + } + ], + "name": "MinimumReportsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "reserveFraction", + "type": "uint256" + } + ], + "name": "ReserveFractionSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "spread", + "type": "uint256" + } + ], + "name": "SpreadSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "stable", + "type": "address" + } + ], + "name": "StableTokenSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "updateFrequency", + "type": "uint256" + } + ], + "name": "UpdateFrequencySet", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "goldBucket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lastBucketUpdate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minimumReports", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "reserveFraction", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "spread", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stable", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stableBucket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stableTokenRegistryId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "updateFrequency", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "string", + "name": "stableTokenIdentifier", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_spread", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_reserveFraction", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_updateFrequency", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minimumReports", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "activateStable", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minBuyAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "sell", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minBuyAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "exchange", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSellAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "buyGold", + "type": "bool" + } + ], + "name": "buy", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "getBuyTokenAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "getSellTokenAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "getBuyAndSellBuckets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newUpdateFrequency", + "type": "uint256" + } + ], + "name": "setUpdateFrequency", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newMininumReports", + "type": "uint256" + } + ], + "name": "setMinimumReports", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newStableToken", + "type": "address" + } + ], + "name": "setStableToken", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newSpread", + "type": "uint256" + } + ], + "name": "setSpread", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newReserveFraction", + "type": "uint256" + } + ], + "name": "setReserveFraction", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // ExchangeBRL ABI + abis["ExchangeBRL"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "goldBucket", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "stableBucket", + "type": "uint256" + } + ], + "name": "BucketsUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "exchanger", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "soldGold", + "type": "bool" + } + ], + "name": "Exchanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "minimumReports", + "type": "uint256" + } + ], + "name": "MinimumReportsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "reserveFraction", + "type": "uint256" + } + ], + "name": "ReserveFractionSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "spread", + "type": "uint256" + } + ], + "name": "SpreadSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "stable", + "type": "address" + } + ], + "name": "StableTokenSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "updateFrequency", + "type": "uint256" + } + ], + "name": "UpdateFrequencySet", + "type": "event" + }, + { + "constant": false, + "inputs": [], + "name": "activateStable", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSellAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "buyGold", + "type": "bool" + } + ], + "name": "buy", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minBuyAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "exchange", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "getBuyAndSellBuckets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "getBuyTokenAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "getSellTokenAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "goldBucket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "string", + "name": "stableTokenIdentifier", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_spread", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_reserveFraction", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_updateFrequency", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minimumReports", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lastBucketUpdate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minimumReports", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "reserveFraction", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minBuyAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "sell", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newMininumReports", + "type": "uint256" + } + ], + "name": "setMinimumReports", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newReserveFraction", + "type": "uint256" + } + ], + "name": "setReserveFraction", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newSpread", + "type": "uint256" + } + ], + "name": "setSpread", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newStableToken", + "type": "address" + } + ], + "name": "setStableToken", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newUpdateFrequency", + "type": "uint256" + } + ], + "name": "setUpdateFrequency", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "spread", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stable", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stableBucket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stableTokenRegistryId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "updateFrequency", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + } + ]`) // ExchangeEUR ABI + abis["ExchangeEUR"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "goldBucket", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "stableBucket", + "type": "uint256" + } + ], + "name": "BucketsUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "exchanger", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "soldGold", + "type": "bool" + } + ], + "name": "Exchanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "minimumReports", + "type": "uint256" + } + ], + "name": "MinimumReportsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "reserveFraction", + "type": "uint256" + } + ], + "name": "ReserveFractionSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "spread", + "type": "uint256" + } + ], + "name": "SpreadSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "stable", + "type": "address" + } + ], + "name": "StableTokenSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "updateFrequency", + "type": "uint256" + } + ], + "name": "UpdateFrequencySet", + "type": "event" + }, + { + "constant": false, + "inputs": [], + "name": "activateStable", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSellAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "buyGold", + "type": "bool" + } + ], + "name": "buy", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minBuyAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "exchange", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "getBuyAndSellBuckets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "getBuyTokenAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "getSellTokenAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "goldBucket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "string", + "name": "stableTokenIdentifier", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_spread", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_reserveFraction", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_updateFrequency", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minimumReports", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lastBucketUpdate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minimumReports", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "reserveFraction", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minBuyAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellGold", + "type": "bool" + } + ], + "name": "sell", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newMininumReports", + "type": "uint256" + } + ], + "name": "setMinimumReports", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newReserveFraction", + "type": "uint256" + } + ], + "name": "setReserveFraction", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newSpread", + "type": "uint256" + } + ], + "name": "setSpread", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newStableToken", + "type": "address" + } + ], + "name": "setStableToken", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newUpdateFrequency", + "type": "uint256" + } + ], + "name": "setUpdateFrequency", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "spread", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stable", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stableBucket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stableTokenRegistryId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "updateFrequency", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + } + ]`) // FederatedAttestations ABI + abis["FederatedAttestations"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "issuer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "issuedOn", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "publishedOn", + "type": "uint64" + } + ], + "name": "AttestationRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "issuer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "issuedOn", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "publishedOn", + "type": "uint64" + } + ], + "name": "AttestationRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "eip712DomainSeparator", + "type": "bytes32" + } + ], + "name": "EIP712DomainSeparatorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "EIP712_OWNERSHIP_ATTESTATION_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_ATTESTATIONS_PER_IDENTIFIER", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_IDENTIFIERS_PER_ADDRESS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "addressToIdentifiers", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "eip712DomainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "identifierToAttestations", + "outputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint64", + "name": "issuedOn", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "publishedOn", + "type": "uint64" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registryContract", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "revokedAttestations", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint64", + "name": "issuedOn", + "type": "uint64" + } + ], + "name": "registerAttestationAsIssuer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "issuer", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint64", + "name": "issuedOn", + "type": "uint64" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "registerAttestation", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "issuer", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeAttestation", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "issuer", + "type": "address" + }, + { + "internalType": "bytes32[]", + "name": "identifiers", + "type": "bytes32[]" + }, + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + } + ], + "name": "batchRevokeAttestations", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address[]", + "name": "trustedIssuers", + "type": "address[]" + } + ], + "name": "lookupAttestations", + "outputs": [ + { + "internalType": "uint256[]", + "name": "countsPerIssuer", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "signers", + "type": "address[]" + }, + { + "internalType": "uint64[]", + "name": "issuedOns", + "type": "uint64[]" + }, + { + "internalType": "uint64[]", + "name": "publishedOns", + "type": "uint64[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address[]", + "name": "trustedIssuers", + "type": "address[]" + } + ], + "name": "lookupIdentifiers", + "outputs": [ + { + "internalType": "uint256[]", + "name": "countsPerIssuer", + "type": "uint256[]" + }, + { + "internalType": "bytes32[]", + "name": "identifiers", + "type": "bytes32[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "issuer", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint64", + "name": "issuedOn", + "type": "uint64" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "validateAttestationSig", + "outputs": [], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifier", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "issuer", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint64", + "name": "issuedOn", + "type": "uint64" + } + ], + "name": "getUniqueAttestationHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + } + ]`) // FeeCurrencyWhitelist ABI + abis["FeeCurrencyWhitelist"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "whitelist", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "addToken", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getWhitelist", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // Freezer ABI + abis["Freezer"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isFrozen", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "freeze", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "unfreeze", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // GasPriceMinimum ABI + abis["GasPriceMinimum"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "adjustmentSpeed", + "type": "uint256" + } + ], + "name": "AdjustmentSpeedSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "gasPriceMinimumFloor", + "type": "uint256" + } + ], + "name": "GasPriceMinimumFloorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "gasPriceMinimum", + "type": "uint256" + } + ], + "name": "GasPriceMinimumUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "targetDensity", + "type": "uint256" + } + ], + "name": "TargetDensitySet", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "adjustmentSpeed", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "gasPriceMinimum", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "gasPriceMinimumFloor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "targetDensity", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_gasPriceMinimumFloor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_targetDensity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_adjustmentSpeed", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_adjustmentSpeed", + "type": "uint256" + } + ], + "name": "setAdjustmentSpeed", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_targetDensity", + "type": "uint256" + } + ], + "name": "setTargetDensity", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_gasPriceMinimumFloor", + "type": "uint256" + } + ], + "name": "setGasPriceMinimumFloor", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "getGasPriceMinimum", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "blockGasTotal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockGasLimit", + "type": "uint256" + } + ], + "name": "updateGasPriceMinimum", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockGasTotal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockGasLimit", + "type": "uint256" + } + ], + "name": "getUpdatedGasPriceMinimum", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // GoldToken ABI + abis["GoldToken"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "comment", + "type": "string" + } + ], + "name": "TransferComment", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "string", + "name": "comment", + "type": "string" + } + ], + "name": "transferWithComment", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "increaseSupply", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // Governance ABI + abis["Governance"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "approvalStageDuration", + "type": "uint256" + } + ], + "name": "ApprovalStageDurationSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ApproverSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "concurrentProposals", + "type": "uint256" + } + ], + "name": "ConcurrentProposalsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "destination", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes4", + "name": "functionId", + "type": "bytes4" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + } + ], + "name": "ConstitutionSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "dequeueFrequency", + "type": "uint256" + } + ], + "name": "DequeueFrequencySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "executionStageDuration", + "type": "uint256" + } + ], + "name": "ExecutionStageDurationSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "HotfixApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "HotfixExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "HotfixPrepared", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "whitelister", + "type": "address" + } + ], + "name": "HotfixWhitelisted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "minDeposit", + "type": "uint256" + } + ], + "name": "MinDepositSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "baselineQuorumFactor", + "type": "uint256" + } + ], + "name": "ParticipationBaselineQuorumFactorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "baselineUpdateFactor", + "type": "uint256" + } + ], + "name": "ParticipationBaselineUpdateFactorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "participationBaseline", + "type": "uint256" + } + ], + "name": "ParticipationBaselineUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "participationFloor", + "type": "uint256" + } + ], + "name": "ParticipationFloorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "ProposalApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "ProposalDequeued", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "ProposalExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "ProposalExpired", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "proposer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "transactionCount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "deposit", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "ProposalQueued", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "revokedUpvotes", + "type": "uint256" + } + ], + "name": "ProposalUpvoteRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "upvotes", + "type": "uint256" + } + ], + "name": "ProposalUpvoted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "weight", + "type": "uint256" + } + ], + "name": "ProposalVoteRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "weight", + "type": "uint256" + } + ], + "name": "ProposalVoted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "queueExpiry", + "type": "uint256" + } + ], + "name": "QueueExpirySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "referendumStageDuration", + "type": "uint256" + } + ], + "name": "ReferendumStageDurationSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "constant": true, + "inputs": [], + "name": "approver", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "concurrentProposals", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "dequeueFrequency", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "dequeued", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "emptyIndices", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "hotfixes", + "outputs": [ + { + "internalType": "bool", + "name": "executed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "preparedEpoch", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lastDequeue", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "proposalCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "queueExpiry", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "refundedDeposits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stageDurations", + "outputs": [ + { + "internalType": "uint256", + "name": "approval", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referendum", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "execution", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_approver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_concurrentProposals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_queueExpiry", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_dequeueFrequency", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "approvalStageDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referendumStageDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionStageDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "participationBaseline", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "participationFloor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baselineUpdateFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baselineQuorumFactor", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_approver", + "type": "address" + } + ], + "name": "setApprover", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_concurrentProposals", + "type": "uint256" + } + ], + "name": "setConcurrentProposals", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_minDeposit", + "type": "uint256" + } + ], + "name": "setMinDeposit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_queueExpiry", + "type": "uint256" + } + ], + "name": "setQueueExpiry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_dequeueFrequency", + "type": "uint256" + } + ], + "name": "setDequeueFrequency", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "approvalStageDuration", + "type": "uint256" + } + ], + "name": "setApprovalStageDuration", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "referendumStageDuration", + "type": "uint256" + } + ], + "name": "setReferendumStageDuration", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "executionStageDuration", + "type": "uint256" + } + ], + "name": "setExecutionStageDuration", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "participationBaseline", + "type": "uint256" + } + ], + "name": "setParticipationBaseline", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "participationFloor", + "type": "uint256" + } + ], + "name": "setParticipationFloor", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "baselineUpdateFactor", + "type": "uint256" + } + ], + "name": "setBaselineUpdateFactor", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "baselineQuorumFactor", + "type": "uint256" + } + ], + "name": "setBaselineQuorumFactor", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "destination", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "functionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + } + ], + "name": "setConstitution", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "destinations", + "type": "address[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "uint256[]", + "name": "dataLengths", + "type": "uint256[]" + }, + { + "internalType": "string", + "name": "descriptionUrl", + "type": "string" + } + ], + "name": "propose", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lesser", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "greater", + "type": "uint256" + } + ], + "name": "upvote", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "getProposalStage", + "outputs": [ + { + "internalType": "enum Proposals.Stage", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "lesser", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "greater", + "type": "uint256" + } + ], + "name": "revokeUpvote", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "enum Proposals.VoteValue", + "name": "value", + "type": "uint8" + } + ], + "name": "vote", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "revokeVotes", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "execute", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "approveHotfix", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "whitelister", + "type": "address" + } + ], + "name": "isHotfixWhitelistedBy", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "whitelistHotfix", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "prepareHotfix", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "destinations", + "type": "address[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "uint256[]", + "name": "dataLengths", + "type": "uint256[]" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "executeHotfix", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "withdraw", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "isVoting", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getApprovalStageDuration", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getReferendumStageDuration", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getExecutionStageDuration", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getParticipationParameters", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "proposalExists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "getProposal", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getProposalTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "isApproved", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "getVoteTotals", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getVoteRecord", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getQueueLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "getUpvotes", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getQueue", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getDequeue", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getUpvoteRecord", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getMostRecentReferendumProposal", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "hotfixWhitelistValidatorTally", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "isHotfixPassing", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "getHotfixRecord", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "dequeueProposalsIfReady", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "isQueued", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "isProposalPassing", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "isDequeuedProposal", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "isDequeuedProposalExpired", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "isQueuedProposalExpired", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "destination", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "functionId", + "type": "bytes4" + } + ], + "name": "getConstitution", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // GovernanceApproverMultiSig ABI + abis["GovernanceApproverMultiSig"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "Confirmation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "name": "Execution", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "internalRequired", + "type": "uint256" + } + ], + "name": "InternalRequirementChange", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnerAddition", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnerRemoval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "required", + "type": "uint256" + } + ], + "name": "RequirementChange", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "Revocation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "Submission", + "type": "event" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_OWNER_COUNT", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "addOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_internalRequired", + "type": "uint256" + } + ], + "name": "changeInternalRequirement", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_required", + "type": "uint256" + } + ], + "name": "changeRequirement", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "confirmTransaction", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "confirmations", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "executeTransaction", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "getConfirmationCount", + "outputs": [ + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "getConfirmations", + "outputs": [ + { + "internalType": "address[]", + "name": "_confirmations", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getOwners", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bool", + "name": "pending", + "type": "bool" + }, + { + "internalType": "bool", + "name": "executed", + "type": "bool" + } + ], + "name": "getTransactionCount", + "outputs": [ + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "pending", + "type": "bool" + }, + { + "internalType": "bool", + "name": "executed", + "type": "bool" + } + ], + "name": "getTransactionIds", + "outputs": [ + { + "internalType": "uint256[]", + "name": "_transactionIds", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address[]", + "name": "_owners", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "_required", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_internalRequired", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "internalRequired", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "isConfirmed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "owners", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "removeOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "replaceOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "required", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "revokeConfirmation", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "destination", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "submitTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "transactionCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "transactions", + "outputs": [ + { + "internalType": "address", + "name": "destination", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "executed", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // GovernanceSlasher ABI + abis["GovernanceSlasher"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "GovernanceSlashPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "SlashingApproved", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "penalty", + "type": "uint256" + } + ], + "name": "approveSlashing", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getApprovedSlashing", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address[]", + "name": "electionLessers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "electionGreaters", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "electionIndices", + "type": "uint256[]" + } + ], + "name": "slash", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // GrandaMento ABI + abis["GrandaMento"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ApproverSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "ExchangeProposalApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "ExchangeProposalCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "exchanger", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "stableTokenRegistryId", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "sellCelo", + "type": "bool" + } + ], + "name": "ExchangeProposalCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "ExchangeProposalExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "maxApprovalExchangeRateChange", + "type": "uint256" + } + ], + "name": "MaxApprovalExchangeRateChangeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "spread", + "type": "uint256" + } + ], + "name": "SpreadSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "stableTokenRegistryId", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "minExchangeAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "maxExchangeAmount", + "type": "uint256" + } + ], + "name": "StableTokenExchangeLimitsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "vetoPeriodSeconds", + "type": "uint256" + } + ], + "name": "VetoPeriodSecondsSet", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "activeProposalIdsSuperset", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "approver", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "exchangeProposalCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "exchangeProposals", + "outputs": [ + { + "internalType": "address payable", + "name": "exchanger", + "type": "address" + }, + { + "internalType": "address", + "name": "stableToken", + "type": "address" + }, + { + "internalType": "enum GrandaMento.ExchangeProposalState", + "name": "state", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "sellCelo", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "celoStableTokenExchangeRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vetoPeriodSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "approvalTimestamp", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "maxApprovalExchangeRateChange", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "spread", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "name": "stableTokenExchangeLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "minExchangeAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxExchangeAmount", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "vetoPeriodSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_registry", + "type": "address" + }, + { + "internalType": "address", + "name": "_approver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_maxApprovalExchangeRateChange", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_spread", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_vetoPeriodSeconds", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "stableTokenRegistryId", + "type": "string" + }, + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellCelo", + "type": "bool" + } + ], + "name": "createExchangeProposal", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "approveExchangeProposal", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "cancelExchangeProposal", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "executeExchangeProposal", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "celoStableTokenExchangeRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "sellCelo", + "type": "bool" + } + ], + "name": "getBuyAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "removeFromActiveProposalIdsSuperset", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getActiveProposalIds", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "string", + "name": "stableTokenRegistryId", + "type": "string" + } + ], + "name": "getStableTokenExchangeLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newApprover", + "type": "address" + } + ], + "name": "setApprover", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newMaxApprovalExchangeRateChange", + "type": "uint256" + } + ], + "name": "setMaxApprovalExchangeRateChange", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newSpread", + "type": "uint256" + } + ], + "name": "setSpread", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "stableTokenRegistryId", + "type": "string" + }, + { + "internalType": "uint256", + "name": "minExchangeAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxExchangeAmount", + "type": "uint256" + } + ], + "name": "setStableTokenExchangeLimits", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "newVetoPeriodSeconds", + "type": "uint256" + } + ], + "name": "setVetoPeriodSeconds", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // LockedGold ABI + abis["LockedGold"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "slashed", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "penalty", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "reporter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "AccountSlashed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "GoldLocked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "GoldRelocked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "available", + "type": "uint256" + } + ], + "name": "GoldUnlocked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "GoldWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "string", + "name": "slasherIdentifier", + "type": "string" + } + ], + "name": "SlasherWhitelistAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "string", + "name": "slasherIdentifier", + "type": "string" + } + ], + "name": "SlasherWhitelistRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "period", + "type": "uint256" + } + ], + "name": "UnlockingPeriodSet", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "slashingWhitelist", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalNonvoting", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "unlockingPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "slasher", + "type": "address" + } + ], + "name": "isSlasher", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_unlockingPeriod", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setUnlockingPeriod", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "lock", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "incrementNonvotingAccountBalance", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "decrementNonvotingAccountBalance", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "unlock", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "relock", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTotalLockedGold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getNonvotingLockedGold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountTotalLockedGold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountNonvotingLockedGold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getPendingWithdrawals", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getPendingWithdrawal", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getTotalPendingWithdrawals", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getSlashingWhitelist", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "slasherIdentifier", + "type": "string" + } + ], + "name": "addSlasher", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "slasherIdentifier", + "type": "string" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "removeSlasher", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "penalty", + "type": "uint256" + }, + { + "internalType": "address", + "name": "reporter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "lessers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "greaters", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "indices", + "type": "uint256[]" + } + ], + "name": "slash", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // OdisPayments ABI + abis["OdisPayments"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "valueInCUSD", + "type": "uint256" + } + ], + "name": "PaymentMade", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registryContract", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "totalPaidCUSD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "payInCUSD", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // Proxy ABI + abis["Proxy"] = mustParseABI(`[ + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ImplementationSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnerSet", + "type": "event" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "_transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "callbackData", + "type": "bytes" + } + ], + "name": "_setAndInitializeImplementation", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "_getImplementation", + "outputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "_setImplementation", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "_getOwner", + "outputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // Random ABI + abis["Random"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "RandomnessBlockRetentionWindowSet", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "commitments", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "randomnessBlockRetentionWindow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_randomnessBlockRetentionWindow", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setRandomnessBlockRetentionWindow", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "randomness", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "newCommitment", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "proposer", + "type": "address" + } + ], + "name": "revealAndCommit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "randomness", + "type": "bytes32" + } + ], + "name": "computeCommitment", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "random", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getBlockRandomness", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // Registry ABI + abis["Registry"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "identifier", + "type": "string" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "identifierHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "RegistryUpdated", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "registry", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "identifier", + "type": "string" + }, + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setAddressFor", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifierHash", + "type": "bytes32" + } + ], + "name": "getAddressForOrDie", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "identifierHash", + "type": "bytes32" + } + ], + "name": "getAddressFor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "string", + "name": "identifier", + "type": "string" + } + ], + "name": "getAddressForStringOrDie", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "string", + "name": "identifier", + "type": "string" + } + ], + "name": "getAddressForString", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32[]", + "name": "identifierHashes", + "type": "bytes32[]" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "isOneOf", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // Reserve ABI + abis["Reserve"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32[]", + "name": "symbols", + "type": "bytes32[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "weights", + "type": "uint256[]" + } + ], + "name": "AssetAllocationSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "ratio", + "type": "uint256" + } + ], + "name": "DailySpendingRatioSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "exchangeSpender", + "type": "address" + } + ], + "name": "ExchangeSpenderAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "exchangeSpender", + "type": "address" + } + ], + "name": "ExchangeSpenderRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "otherReserveAddress", + "type": "address" + } + ], + "name": "OtherReserveAddressAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "otherReserveAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "OtherReserveAddressRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "ReserveGoldTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "SpenderAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "SpenderRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TobinTaxReserveRatioSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TobinTaxSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TobinTaxStalenessThresholdSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "TokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "TokenRemoved", + "type": "event" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "assetAllocationSymbols", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "assetAllocationWeights", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "exchangeSpenderAddresses", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "frozenReserveGoldDays", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "frozenReserveGoldStartBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "frozenReserveGoldStartDay", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isExchangeSpender", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isOtherReserveAddress", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isSpender", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isToken", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lastSpendingDay", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "otherReserveAddresses", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "spendingLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "tobinTax", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "tobinTaxCache", + "outputs": [ + { + "internalType": "uint128", + "name": "numerator", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "timestamp", + "type": "uint128" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "tobinTaxReserveRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "tobinTaxStalenessThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tobinTaxStalenessThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_spendingRatio", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_frozenGold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_frozenDays", + "type": "uint256" + }, + { + "internalType": "bytes32[]", + "name": "_assetAllocationSymbols", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "_assetAllocationWeights", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_tobinTax", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_tobinTaxReserveRatio", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setTobinTaxStalenessThreshold", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setTobinTax", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setTobinTaxReserveRatio", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "ratio", + "type": "uint256" + } + ], + "name": "setDailySpendingRatio", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getDailySpendingRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "frozenGold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "frozenDays", + "type": "uint256" + } + ], + "name": "setFrozenGold", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32[]", + "name": "symbols", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "weights", + "type": "uint256[]" + } + ], + "name": "setAssetAllocations", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "addToken", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "removeToken", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "reserveAddress", + "type": "address" + } + ], + "name": "addOtherReserveAddress", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "reserveAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "removeOtherReserveAddress", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "addSpender", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "removeSpender", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "addExchangeSpender", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "removeExchangeSpender", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getExchangeSpenders", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address payable", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferGold", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address payable", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferExchangeGold", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "getOrComputeTobinTax", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getOtherReserveAddresses", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getAssetAllocationSymbols", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getAssetAllocationWeights", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getUnfrozenBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getReserveGoldBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getOtherReserveAddressesGoldBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getUnfrozenReserveGoldBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getFrozenReserveGoldBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getReserveRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // ReserveSpenderMultiSig ABI + abis["ReserveSpenderMultiSig"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "Confirmation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "name": "Execution", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "internalRequired", + "type": "uint256" + } + ], + "name": "InternalRequirementChange", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnerAddition", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnerRemoval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "required", + "type": "uint256" + } + ], + "name": "RequirementChange", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "Revocation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "Submission", + "type": "event" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_OWNER_COUNT", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "addOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_internalRequired", + "type": "uint256" + } + ], + "name": "changeInternalRequirement", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_required", + "type": "uint256" + } + ], + "name": "changeRequirement", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "confirmTransaction", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "confirmations", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "executeTransaction", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "getConfirmationCount", + "outputs": [ + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "getConfirmations", + "outputs": [ + { + "internalType": "address[]", + "name": "_confirmations", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getOwners", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bool", + "name": "pending", + "type": "bool" + }, + { + "internalType": "bool", + "name": "executed", + "type": "bool" + } + ], + "name": "getTransactionCount", + "outputs": [ + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "pending", + "type": "bool" + }, + { + "internalType": "bool", + "name": "executed", + "type": "bool" + } + ], + "name": "getTransactionIds", + "outputs": [ + { + "internalType": "uint256[]", + "name": "_transactionIds", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address[]", + "name": "_owners", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "_required", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_internalRequired", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "internalRequired", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "isConfirmed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "owners", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "removeOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "replaceOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "required", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "name": "revokeConfirmation", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "destination", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "submitTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "transactionId", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "transactionCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "transactions", + "outputs": [ + { + "internalType": "address", + "name": "destination", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "executed", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // SortedOracles ABI + abis["SortedOracles"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "MedianUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "oracleAddress", + "type": "address" + } + ], + "name": "OracleAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "oracleAddress", + "type": "address" + } + ], + "name": "OracleRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "oracle", + "type": "address" + } + ], + "name": "OracleReportRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "OracleReported", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "reportExpiry", + "type": "uint256" + } + ], + "name": "ReportExpirySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reportExpiry", + "type": "uint256" + } + ], + "name": "TokenReportExpirySet", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isOracle", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "oracles", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "reportExpirySeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenReportExpirySeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_reportExpirySeconds", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "_reportExpirySeconds", + "type": "uint256" + } + ], + "name": "setReportExpiry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_reportExpirySeconds", + "type": "uint256" + } + ], + "name": "setTokenReportExpiry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "oracleAddress", + "type": "address" + } + ], + "name": "addOracle", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "oracleAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "removeOracle", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + } + ], + "name": "removeExpiredReports", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "isOldestReportExpired", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "address", + "name": "lesserKey", + "type": "address" + }, + { + "internalType": "address", + "name": "greaterKey", + "type": "address" + } + ], + "name": "report", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "numRates", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "medianRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getRates", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "enum SortedLinkedListWithMedian.MedianRelation[]", + "name": "", + "type": "uint8[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "numTimestamps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "medianTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTimestamps", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "enum SortedLinkedListWithMedian.MedianRelation[]", + "name": "", + "type": "uint8[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getOracles", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTokenReportExpirySeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) // StableToken ABI + abis["StableToken"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "factor", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lastUpdated", + "type": "uint256" + } + ], + "name": "InflationFactorUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "rate", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatePeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lastUpdated", + "type": "uint256" + } + ], + "name": "InflationParametersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "comment", + "type": "string" + } + ], + "name": "TransferComment", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + }, + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "inflationRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "inflationFactorUpdatePeriod", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "initialBalanceAddresses", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "initialBalanceValues", + "type": "uint256[]" + }, + { + "internalType": "string", + "name": "exchangeIdentifier", + "type": "string" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatePeriod", + "type": "uint256" + } + ], + "name": "setInflationParameters", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "string", + "name": "comment", + "type": "string" + } + ], + "name": "transferWithComment", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "accountOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "accountOwner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getInflationParameters", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "valueToUnits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getExchangeRegistryId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "units", + "type": "uint256" + } + ], + "name": "unitsToValue", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "debitGasFees", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "internalType": "address", + "name": "gatewayFeeRecipient", + "type": "address" + }, + { + "internalType": "address", + "name": "communityFund", + "type": "address" + }, + { + "internalType": "uint256", + "name": "refund", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tipTxFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gatewayFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseTxFee", + "type": "uint256" + } + ], + "name": "creditGasFees", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // StableTokenBRL ABI + abis["StableTokenBRL"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "factor", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lastUpdated", + "type": "uint256" + } + ], + "name": "InflationFactorUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "rate", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatePeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lastUpdated", + "type": "uint256" + } + ], + "name": "InflationParametersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "comment", + "type": "string" + } + ], + "name": "TransferComment", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "accountOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "accountOwner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "internalType": "address", + "name": "gatewayFeeRecipient", + "type": "address" + }, + { + "internalType": "address", + "name": "communityFund", + "type": "address" + }, + { + "internalType": "uint256", + "name": "refund", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tipTxFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gatewayFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseTxFee", + "type": "uint256" + } + ], + "name": "creditGasFees", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "debitGasFees", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getExchangeRegistryId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getInflationParameters", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + }, + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "inflationRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "inflationFactorUpdatePeriod", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "initialBalanceAddresses", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "initialBalanceValues", + "type": "uint256[]" + }, + { + "internalType": "string", + "name": "exchangeIdentifier", + "type": "string" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatePeriod", + "type": "uint256" + } + ], + "name": "setInflationParameters", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "string", + "name": "comment", + "type": "string" + } + ], + "name": "transferWithComment", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "units", + "type": "uint256" + } + ], + "name": "unitsToValue", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "valueToUnits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + } + ]`) // StableTokenEUR ABI + abis["StableTokenEUR"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "factor", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lastUpdated", + "type": "uint256" + } + ], + "name": "InflationFactorUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "rate", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatePeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lastUpdated", + "type": "uint256" + } + ], + "name": "InflationParametersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "comment", + "type": "string" + } + ], + "name": "TransferComment", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "accountOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "accountOwner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "internalType": "address", + "name": "gatewayFeeRecipient", + "type": "address" + }, + { + "internalType": "address", + "name": "communityFund", + "type": "address" + }, + { + "internalType": "uint256", + "name": "refund", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tipTxFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gatewayFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseTxFee", + "type": "uint256" + } + ], + "name": "creditGasFees", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "debitGasFees", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getExchangeRegistryId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getInflationParameters", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + }, + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "inflationRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "inflationFactorUpdatePeriod", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "initialBalanceAddresses", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "initialBalanceValues", + "type": "uint256[]" + }, + { + "internalType": "string", + "name": "exchangeIdentifier", + "type": "string" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatePeriod", + "type": "uint256" + } + ], + "name": "setInflationParameters", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "string", + "name": "comment", + "type": "string" + } + ], + "name": "transferWithComment", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "units", + "type": "uint256" + } + ], + "name": "unitsToValue", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "valueToUnits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + } + ]`) // TransferWhitelist ABI + abis["TransferWhitelist"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "WhitelistedAddress", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "WhitelistedAddressRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "contractIdentifier", + "type": "bytes32" + } + ], + "name": "WhitelistedContractIdentifier", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "whitelistedContractIdentifiers", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newAddress", + "type": "address" + } + ], + "name": "whitelistAddress", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "removedAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "removeAddress", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "contractIdentifier", + "type": "bytes32" + } + ], + "name": "whitelistRegisteredContract", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getNumberOfWhitelistedContractIdentifiers", + "outputs": [ + { + "internalType": "uint256", + "name": "length", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address[]", + "name": "_whitelist", + "type": "address[]" + } + ], + "name": "setDirectlyWhitelistedAddresses", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32[]", + "name": "_registeredContracts", + "type": "bytes32[]" + } + ], + "name": "setWhitelistedContractIdentifiers", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getWhitelist", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "selfDestruct", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`) // Validators ABI + abis["Validators"] = mustParseABI(`[ + { + "inputs": [ + { + "internalType": "bool", + "name": "test", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "delay", + "type": "uint256" + } + ], + "name": "CommissionUpdateDelaySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "name": "GroupLockedGoldRequirementsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "size", + "type": "uint256" + } + ], + "name": "MaxGroupSizeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "length", + "type": "uint256" + } + ], + "name": "MembershipHistoryLengthSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "RegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "ValidatorAffiliated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "blsPublicKey", + "type": "bytes" + } + ], + "name": "ValidatorBlsPublicKeyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "ValidatorDeaffiliated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "ValidatorDeregistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "ecdsaPublicKey", + "type": "bytes" + } + ], + "name": "ValidatorEcdsaPublicKeyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "validatorPayment", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "groupPayment", + "type": "uint256" + } + ], + "name": "ValidatorEpochPaymentDistributed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "commission", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + } + ], + "name": "ValidatorGroupCommissionUpdateQueued", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "commission", + "type": "uint256" + } + ], + "name": "ValidatorGroupCommissionUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "ValidatorGroupDeregistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "ValidatorGroupMemberAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "ValidatorGroupMemberRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "ValidatorGroupMemberReordered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "group", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "commission", + "type": "uint256" + } + ], + "name": "ValidatorGroupRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "name": "ValidatorLockedGoldRequirementsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "ValidatorRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "adjustmentSpeed", + "type": "uint256" + } + ], + "name": "ValidatorScoreParametersSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "score", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "epochScore", + "type": "uint256" + } + ], + "name": "ValidatorScoreUpdated", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "checkProofOfPossession", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "commissionUpdateDelay", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "downtimeGracePeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "aNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bNumerator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bDenominator", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "fractionMulExp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getBlockNumberFromHeader", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getEpochNumberOfBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEpochSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getParentSealBitmap", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "getVerifiedSealBitmapFromHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "groupLockedGoldRequirements", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "hashHeader", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "maxGroupSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "membershipHistoryLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "minQuorumSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minQuorumSizeInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numberValidatorsInCurrentSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "numberValidatorsInSet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "registry", + "outputs": [ + { + "internalType": "contract IRegistry", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "name": "setRegistry", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "slashingMultiplierResetPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "validatorLockedGoldRequirements", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromCurrentSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "validatorSignerAddressFromSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getVersionNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "groupRequirementValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "groupRequirementDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorRequirementValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorRequirementDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorScoreExponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorScoreAdjustmentSpeed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_membershipHistoryLength", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_slashingMultiplierResetPeriod", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxGroupSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_commissionUpdateDelay", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_downtimeGracePeriod", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "delay", + "type": "uint256" + } + ], + "name": "setCommissionUpdateDelay", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "size", + "type": "uint256" + } + ], + "name": "setMaxGroupSize", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "length", + "type": "uint256" + } + ], + "name": "setMembershipHistoryLength", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustmentSpeed", + "type": "uint256" + } + ], + "name": "setValidatorScoreParameters", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getMaxGroupSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getCommissionUpdateDelay", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "name": "setGroupLockedGoldRequirements", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "name": "setValidatorLockedGoldRequirements", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes", + "name": "ecdsaPublicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPublicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "registerValidator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getValidatorScoreParameters", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getMembershipHistory", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "uptime", + "type": "uint256" + } + ], + "name": "calculateEpochScore", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256[]", + "name": "uptimes", + "type": "uint256[]" + } + ], + "name": "calculateGroupEpochScore", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "uptime", + "type": "uint256" + } + ], + "name": "updateValidatorScoreFromSigner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxPayment", + "type": "uint256" + } + ], + "name": "distributeEpochPaymentsFromSigner", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "deregisterValidator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "group", + "type": "address" + } + ], + "name": "affiliate", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "deaffiliate", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes", + "name": "blsPublicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "updateBlsPublicKey", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes", + "name": "ecdsaPublicKey", + "type": "bytes" + } + ], + "name": "updateEcdsaPublicKey", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes", + "name": "ecdsaPublicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPublicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPop", + "type": "bytes" + } + ], + "name": "updatePublicKeys", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "commission", + "type": "uint256" + } + ], + "name": "registerValidatorGroup", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "deregisterValidatorGroup", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "addMember", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "address", + "name": "lesser", + "type": "address" + }, + { + "internalType": "address", + "name": "greater", + "type": "address" + } + ], + "name": "addFirstMember", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "removeMember", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "address", + "name": "lesserMember", + "type": "address" + }, + { + "internalType": "address", + "name": "greaterMember", + "type": "address" + } + ], + "name": "reorderMember", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "commission", + "type": "uint256" + } + ], + "name": "setNextCommissionUpdate", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "updateCommission", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountLockedGoldRequirement", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "meetsAccountLockedGoldRequirements", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "getValidatorBlsPublicKeyFromSigner", + "outputs": [ + { + "internalType": "bytes", + "name": "blsPublicKey", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getValidator", + "outputs": [ + { + "internalType": "bytes", + "name": "ecdsaPublicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blsPublicKey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "affiliation", + "type": "address" + }, + { + "internalType": "uint256", + "name": "score", + "type": "uint256" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getValidatorGroup", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getGroupNumMembers", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + } + ], + "name": "getTopGroupValidators", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + } + ], + "name": "getGroupsNumMembers", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getNumRegisteredValidators", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getValidatorLockedGoldRequirements", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getGroupLockedGoldRequirements", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getRegisteredValidators", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getRegisteredValidatorSigners", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getRegisteredValidatorGroups", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "isValidatorGroup", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "isValidator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "getMembershipInLastEpochFromSigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getMembershipInLastEpoch", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "validatorAccount", + "type": "address" + } + ], + "name": "forceDeaffiliateIfValidator", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setSlashingMultiplierResetPeriod", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setDowntimeGracePeriod", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "resetSlashingMultiplier", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "halveSlashingMultiplier", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getValidatorGroupSlashingMultiplier", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "epochNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "groupMembershipInEpoch", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ]`) +}
diff --git go-ethereum/mycelo/hdwallet/hdwallet_test.go celo/mycelo/hdwallet/hdwallet_test.go new file mode 100644 index 0000000000000000000000000000000000000000..850e736116f6d8a7c48c4531828b52316f44a9cb --- /dev/null +++ celo/mycelo/hdwallet/hdwallet_test.go @@ -0,0 +1,23 @@ +package hdwallet + +import ( + "fmt" + "testing" +) + +func Test(t *testing.T) { + mnemonic := "tag volcano eight thank tide danger coast health above argue embrace heavy" + wallet, err := NewFromMnemonic(mnemonic) + if err != nil { + t.Fatal(err) + } + + path := MustParseDerivationPath("m/1/0") + account, err := wallet.Derive(path, false) + if err != nil { + t.Fatal(err) + } + + fmt.Println(account.Address.Hex()) // 0xC49926C4124cEe1cbA0Ea94Ea31a6c12318df947 + +}
diff --git go-ethereum/mycelo/genesis/gen_epoch_rewards_parameters_json.go celo/mycelo/genesis/gen_epoch_rewards_parameters_json.go new file mode 100644 index 0000000000000000000000000000000000000000..c7d673ca303784ffd725a38c70c261da4c9aef32 --- /dev/null +++ celo/mycelo/genesis/gen_epoch_rewards_parameters_json.go @@ -0,0 +1,105 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package genesis + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/decimal/bigintstr" + "github.com/ethereum/go-ethereum/common/decimal/fixed" +) + +var _ = (*EpochRewardsParametersMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (e EpochRewardsParameters) MarshalJSON() ([]byte, error) { + type EpochRewardsParameters struct { + TargetVotingYieldInitial *fixed.Fixed `json:"targetVotingYieldInitial"` + TargetVotingYieldMax *fixed.Fixed `json:"targetVotingYieldMax"` + TargetVotingYieldAdjustmentFactor *fixed.Fixed `json:"targetVotingYieldAdjustmentFactor"` + RewardsMultiplierMax *fixed.Fixed `json:"rewardsMultiplierMax"` + RewardsMultiplierAdjustmentFactorsUnderspend *fixed.Fixed `json:"rewardsMultiplierAdjustmentFactorsUnderspend"` + RewardsMultiplierAdjustmentFactorsOverspend *fixed.Fixed `json:"rewardsMultiplierAdjustmentFactorsOverspend"` + TargetVotingGoldFraction *fixed.Fixed `json:"targetVotingGoldFraction"` + MaxValidatorEpochPayment *bigintstr.BigIntStr `json:"maxValidatorEpochPayment"` + CommunityRewardFraction *fixed.Fixed `json:"communityRewardFraction"` + CarbonOffsettingPartner common.Address `json:"carbonOffsettingPartner"` + CarbonOffsettingFraction *fixed.Fixed `json:"carbonOffsettingFraction"` + Frozen bool `json:"frozen"` + } + var enc EpochRewardsParameters + enc.TargetVotingYieldInitial = e.TargetVotingYieldInitial + enc.TargetVotingYieldMax = e.TargetVotingYieldMax + enc.TargetVotingYieldAdjustmentFactor = e.TargetVotingYieldAdjustmentFactor + enc.RewardsMultiplierMax = e.RewardsMultiplierMax + enc.RewardsMultiplierAdjustmentFactorsUnderspend = e.RewardsMultiplierAdjustmentFactorsUnderspend + enc.RewardsMultiplierAdjustmentFactorsOverspend = e.RewardsMultiplierAdjustmentFactorsOverspend + enc.TargetVotingGoldFraction = e.TargetVotingGoldFraction + enc.MaxValidatorEpochPayment = (*bigintstr.BigIntStr)(e.MaxValidatorEpochPayment) + enc.CommunityRewardFraction = e.CommunityRewardFraction + enc.CarbonOffsettingPartner = e.CarbonOffsettingPartner + enc.CarbonOffsettingFraction = e.CarbonOffsettingFraction + enc.Frozen = e.Frozen + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (e *EpochRewardsParameters) UnmarshalJSON(input []byte) error { + type EpochRewardsParameters struct { + TargetVotingYieldInitial *fixed.Fixed `json:"targetVotingYieldInitial"` + TargetVotingYieldMax *fixed.Fixed `json:"targetVotingYieldMax"` + TargetVotingYieldAdjustmentFactor *fixed.Fixed `json:"targetVotingYieldAdjustmentFactor"` + RewardsMultiplierMax *fixed.Fixed `json:"rewardsMultiplierMax"` + RewardsMultiplierAdjustmentFactorsUnderspend *fixed.Fixed `json:"rewardsMultiplierAdjustmentFactorsUnderspend"` + RewardsMultiplierAdjustmentFactorsOverspend *fixed.Fixed `json:"rewardsMultiplierAdjustmentFactorsOverspend"` + TargetVotingGoldFraction *fixed.Fixed `json:"targetVotingGoldFraction"` + MaxValidatorEpochPayment *bigintstr.BigIntStr `json:"maxValidatorEpochPayment"` + CommunityRewardFraction *fixed.Fixed `json:"communityRewardFraction"` + CarbonOffsettingPartner *common.Address `json:"carbonOffsettingPartner"` + CarbonOffsettingFraction *fixed.Fixed `json:"carbonOffsettingFraction"` + Frozen *bool `json:"frozen"` + } + var dec EpochRewardsParameters + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.TargetVotingYieldInitial != nil { + e.TargetVotingYieldInitial = dec.TargetVotingYieldInitial + } + if dec.TargetVotingYieldMax != nil { + e.TargetVotingYieldMax = dec.TargetVotingYieldMax + } + if dec.TargetVotingYieldAdjustmentFactor != nil { + e.TargetVotingYieldAdjustmentFactor = dec.TargetVotingYieldAdjustmentFactor + } + if dec.RewardsMultiplierMax != nil { + e.RewardsMultiplierMax = dec.RewardsMultiplierMax + } + if dec.RewardsMultiplierAdjustmentFactorsUnderspend != nil { + e.RewardsMultiplierAdjustmentFactorsUnderspend = dec.RewardsMultiplierAdjustmentFactorsUnderspend + } + if dec.RewardsMultiplierAdjustmentFactorsOverspend != nil { + e.RewardsMultiplierAdjustmentFactorsOverspend = dec.RewardsMultiplierAdjustmentFactorsOverspend + } + if dec.TargetVotingGoldFraction != nil { + e.TargetVotingGoldFraction = dec.TargetVotingGoldFraction + } + if dec.MaxValidatorEpochPayment != nil { + e.MaxValidatorEpochPayment = (*big.Int)(dec.MaxValidatorEpochPayment) + } + if dec.CommunityRewardFraction != nil { + e.CommunityRewardFraction = dec.CommunityRewardFraction + } + if dec.CarbonOffsettingPartner != nil { + e.CarbonOffsettingPartner = *dec.CarbonOffsettingPartner + } + if dec.CarbonOffsettingFraction != nil { + e.CarbonOffsettingFraction = dec.CarbonOffsettingFraction + } + if dec.Frozen != nil { + e.Frozen = *dec.Frozen + } + return nil +}
diff --git go-ethereum/mycelo/genesis/gen_election_parameters_json.go celo/mycelo/genesis/gen_election_parameters_json.go new file mode 100644 index 0000000000000000000000000000000000000000..66a2685db1084c45b86cc9f6a8dcd174bc9de0d4 --- /dev/null +++ celo/mycelo/genesis/gen_election_parameters_json.go @@ -0,0 +1,56 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package genesis + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/decimal/bigintstr" + "github.com/ethereum/go-ethereum/common/decimal/fixed" +) + +var _ = (*ElectionParametersMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (e ElectionParameters) MarshalJSON() ([]byte, error) { + type ElectionParameters struct { + MinElectableValidators uint64 `json:"minElectableValidators"` + MaxElectableValidators uint64 `json:"maxElectableValidators"` + MaxVotesPerAccount *bigintstr.BigIntStr `json:"maxVotesPerAccount"` + ElectabilityThreshold *fixed.Fixed `json:"electabilityThreshold"` + } + var enc ElectionParameters + enc.MinElectableValidators = e.MinElectableValidators + enc.MaxElectableValidators = e.MaxElectableValidators + enc.MaxVotesPerAccount = (*bigintstr.BigIntStr)(e.MaxVotesPerAccount) + enc.ElectabilityThreshold = e.ElectabilityThreshold + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (e *ElectionParameters) UnmarshalJSON(input []byte) error { + type ElectionParameters struct { + MinElectableValidators *uint64 `json:"minElectableValidators"` + MaxElectableValidators *uint64 `json:"maxElectableValidators"` + MaxVotesPerAccount *bigintstr.BigIntStr `json:"maxVotesPerAccount"` + ElectabilityThreshold *fixed.Fixed `json:"electabilityThreshold"` + } + var dec ElectionParameters + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.MinElectableValidators != nil { + e.MinElectableValidators = *dec.MinElectableValidators + } + if dec.MaxElectableValidators != nil { + e.MaxElectableValidators = *dec.MaxElectableValidators + } + if dec.MaxVotesPerAccount != nil { + e.MaxVotesPerAccount = (*big.Int)(dec.MaxVotesPerAccount) + } + if dec.ElectabilityThreshold != nil { + e.ElectabilityThreshold = dec.ElectabilityThreshold + } + return nil +}
diff --git go-ethereum/mycelo/contract/contracts.go celo/mycelo/contract/contracts.go new file mode 100644 index 0000000000000000000000000000000000000000..3dcef9a2d1ed5a0e6b686817611ab2753c4252b0 --- /dev/null +++ celo/mycelo/contract/contracts.go @@ -0,0 +1,127 @@ +package contract + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "path" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/mycelo/env" +) + +type TruffleReader interface { + ReadBytecodeFor(name string) ([]byte, error) + ReadDeployedBytecodeFor(name string) ([]byte, error) + MustReadBytecodeFor(name string) []byte + MustReadDeployedBytecodeFor(name string) []byte +} + +type truffleReader struct { + buildPath string + libraries map[string]common.Address +} + +func NewTruffleReader(buildPath string) TruffleReader { + + librariesMapping := make(map[string]common.Address, len(env.Libraries())) + for _, name := range env.Libraries() { + librariesMapping[name] = env.MustLibraryAddressFor(name) + } + + return &truffleReader{ + buildPath: buildPath, + libraries: librariesMapping, + } + +} + +func (tr *truffleReader) jsonFileFor(name string) string { + return path.Join(tr.buildPath, name+".json") +} + +func (tr *truffleReader) ReadDeployedBytecodeFor(name string) ([]byte, error) { + c, err := readContractBuildFile(tr.jsonFileFor(name), tr.libraries) + if err != nil { + return nil, err + } + return c.deployedBytecode, nil +} + +func (tr *truffleReader) ReadBytecodeFor(name string) ([]byte, error) { + c, err := readContractBuildFile(tr.jsonFileFor(name), tr.libraries) + if err != nil { + return nil, err + } + return c.bytecode, nil +} + +func (tr *truffleReader) MustReadBytecodeFor(name string) []byte { + ret, err := tr.ReadBytecodeFor(name) + if err != nil { + panic(err) + } + return ret +} + +func (tr *truffleReader) MustReadDeployedBytecodeFor(name string) []byte { + ret, err := tr.ReadDeployedBytecodeFor(name) + if err != nil { + panic(err) + } + return ret +} + +func replaceLibrariesInBytecode(mappings map[string]common.Address, bytecode string) string { + for name, addr := range mappings { + pattern := "__" + name + strings.Repeat("_", 40-4-len(name)) + "__" + bytecode = strings.ReplaceAll(bytecode, pattern, addr.Hex()[2:]) + } + return bytecode +} + +type truflleFile struct { + bytecode []byte + deployedBytecode []byte +} + +func readContractBuildFile(truffleJSONFile string, libraries map[string]common.Address) (*truflleFile, error) { + jsonData, err := ioutil.ReadFile(truffleJSONFile) + if err != nil { + return nil, fmt.Errorf("Can't read bytecode for %s: %w", truffleJSONFile, err) + } + + var data struct { + Bytecode string `json:"bytecode"` + DeployedBytecode string `json:"deployedBytecode"` + } + + err = json.Unmarshal(jsonData, &data) + if err != nil { + return nil, fmt.Errorf("Can't read bytecode for %s: %w", truffleJSONFile, err) + } + + if libraries != nil { + data.DeployedBytecode = replaceLibrariesInBytecode(libraries, data.DeployedBytecode) + data.Bytecode = replaceLibrariesInBytecode(libraries, data.Bytecode) + } + + deployedBytecode, err := hexutil.Decode(data.DeployedBytecode) + if err != nil { + fmt.Println(data.DeployedBytecode) + return nil, fmt.Errorf("Can't read bytecode for %s: %w", truffleJSONFile, err) + } + + bytecode, err := hexutil.Decode(data.Bytecode) + if err != nil { + fmt.Println(data.Bytecode) + return nil, fmt.Errorf("Can't read bytecode for %s: %w", truffleJSONFile, err) + } + + return &truflleFile{ + bytecode: bytecode, + deployedBytecode: deployedBytecode, + }, nil +}
diff --git go-ethereum/mycelo/env/core_contracts_test.go celo/mycelo/env/core_contracts_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4feb0328095e80e5646f917351c1a3c103bcf530 --- /dev/null +++ celo/mycelo/env/core_contracts_test.go @@ -0,0 +1,25 @@ +package env + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func TestUniqueContractAddresses(t *testing.T) { + addresses := make(map[common.Address]bool) + + for name, addr := range genesisAddresses { + if addresses[addr] { + t.Errorf("Duplicated use of address. %s - %s", addr.Hex(), name) + } + addresses[addr] = true + } + + for name, addr := range libraryAddresses { + if addresses[addr] { + t.Errorf("Duplicated use of address. %s - %s", addr.Hex(), name) + } + addresses[addr] = true + } +}
diff --git go-ethereum/mycelo/env/paths.go celo/mycelo/env/paths.go new file mode 100644 index 0000000000000000000000000000000000000000..bd770a3fad17df75e2b1403f042577012e37c94c --- /dev/null +++ celo/mycelo/env/paths.go @@ -0,0 +1,27 @@ +package env + +import ( + "fmt" + "path" +) + +type paths struct { + Workdir string + Geth string +} + +func (p paths) genesisJSON() string { + return path.Join(p.Workdir, "genesis.json") +} + +func (p paths) envJSON() string { + return path.Join(p.Workdir, "env.json") +} + +func (p paths) validatorDatadir(idx int) string { + return path.Join(p.Workdir, fmt.Sprintf("validator-%02d", idx)) +} + +func (p paths) validatorIPC(idx int) string { + return path.Join(p.Workdir, fmt.Sprintf("validator-%02d/geth.ipc", idx)) +}
diff --git go-ethereum/mycelo/genesis/gen_balance_json.go celo/mycelo/genesis/gen_balance_json.go new file mode 100644 index 0000000000000000000000000000000000000000..f6f60b9e5c26d1d8fa28b41156d2497eacdd4cb2 --- /dev/null +++ celo/mycelo/genesis/gen_balance_json.go @@ -0,0 +1,44 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package genesis + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/decimal/bigintstr" +) + +var _ = (*BalanceMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (b Balance) MarshalJSON() ([]byte, error) { + type Balance struct { + Account common.Address `json:"account"` + Amount *bigintstr.BigIntStr `json:"amount"` + } + var enc Balance + enc.Account = b.Account + enc.Amount = (*bigintstr.BigIntStr)(b.Amount) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (b *Balance) UnmarshalJSON(input []byte) error { + type Balance struct { + Account *common.Address `json:"account"` + Amount *bigintstr.BigIntStr `json:"amount"` + } + var dec Balance + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Account != nil { + b.Account = *dec.Account + } + if dec.Amount != nil { + b.Amount = (*big.Int)(dec.Amount) + } + return nil +}
diff --git go-ethereum/mycelo/genesis/gen_downtime_slasher_parameters_json.go celo/mycelo/genesis/gen_downtime_slasher_parameters_json.go new file mode 100644 index 0000000000000000000000000000000000000000..25a91a0b0ef507a1249953d19fa9bb0d1d011ed0 --- /dev/null +++ celo/mycelo/genesis/gen_downtime_slasher_parameters_json.go @@ -0,0 +1,49 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package genesis + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/decimal/bigintstr" +) + +var _ = (*DowntimeSlasherParametersMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (d DowntimeSlasherParameters) MarshalJSON() ([]byte, error) { + type DowntimeSlasherParameters struct { + Penalty *bigintstr.BigIntStr `json:"penalty"` + Reward *bigintstr.BigIntStr `json:"reward"` + SlashableDowntime uint64 `json:"slashableDowntime"` + } + var enc DowntimeSlasherParameters + enc.Penalty = (*bigintstr.BigIntStr)(d.Penalty) + enc.Reward = (*bigintstr.BigIntStr)(d.Reward) + enc.SlashableDowntime = d.SlashableDowntime + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (d *DowntimeSlasherParameters) UnmarshalJSON(input []byte) error { + type DowntimeSlasherParameters struct { + Penalty *bigintstr.BigIntStr `json:"penalty"` + Reward *bigintstr.BigIntStr `json:"reward"` + SlashableDowntime *uint64 `json:"slashableDowntime"` + } + var dec DowntimeSlasherParameters + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Penalty != nil { + d.Penalty = (*big.Int)(dec.Penalty) + } + if dec.Reward != nil { + d.Reward = (*big.Int)(dec.Reward) + } + if dec.SlashableDowntime != nil { + d.SlashableDowntime = *dec.SlashableDowntime + } + return nil +}
diff --git go-ethereum/mycelo/genesis/base_config.go celo/mycelo/genesis/base_config.go new file mode 100644 index 0000000000000000000000000000000000000000..9ee0b221c80344c1f3050571edb6fe3ab0bd2162 --- /dev/null +++ celo/mycelo/genesis/base_config.go @@ -0,0 +1,204 @@ +package genesis + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/decimal/fixed" + "github.com/shopspring/decimal" +) + +// BaseConfig creates base parameters for celo +// Callers must complete missing pieces +func BaseConfig() *Config { + bigInt := big.NewInt + bigIntStr := common.MustBigInt + fixed := fixed.MustNew + decimal := decimal.RequireFromString + + return &Config{ + SortedOracles: SortedOraclesParameters{ + ReportExpirySeconds: 5 * Minute, + }, + GasPriceMinimum: GasPriceMinimumParameters{ + MinimumFloor: bigInt(100000000), + AdjustmentSpeed: fixed("0.5"), + TargetDensity: fixed("0.5"), + }, + Reserve: ReserveParameters{ + TobinTaxStalenessThreshold: 3153600000, + TobinTax: fixed("0"), + TobinTaxReserveRatio: fixed("0"), + DailySpendingRatio: fixed("0.05"), + AssetAllocations: AssetAllocationList{ + {"cGLD", fixed("0.5")}, + {"BTC", fixed("0.3")}, + {"ETH", fixed("0.15")}, + {"DAI", fixed("0.05")}, + }, + }, + StableToken: StableTokenParameters{ + Name: "Celo Dollar", + Symbol: "cUSD", + Decimals: 18, + Rate: fixed("1"), + InflationFactorUpdatePeriod: 2 * Year, + GoldPrice: fixed("1"), + ExchangeIdentifier: "Exchange", + }, + StableTokenEUR: StableTokenParameters{ + Name: "Celo Euro", + Symbol: "cEUR", + Decimals: 18, + Rate: fixed("1"), + InflationFactorUpdatePeriod: 2 * Year, + GoldPrice: fixed("1"), + ExchangeIdentifier: "ExchangeEUR", + }, + StableTokenBRL: StableTokenParameters{ + Name: "Celo Brazilian Real", + Symbol: "cREAL", + Decimals: 18, + Rate: fixed("1"), + InflationFactorUpdatePeriod: 2 * Year, + GoldPrice: fixed("1"), + ExchangeIdentifier: "ExchangeBRL", + }, + Validators: ValidatorsParameters{ + GroupLockedGoldRequirements: LockedGoldRequirements{ + Value: bigIntStr("10000000000000000000000"), // 10k CELO per validator + Duration: 180 * Day, + }, + ValidatorLockedGoldRequirements: LockedGoldRequirements{ + Value: bigIntStr("10000000000000000000000"), // 10k CELO + // MUST BE KEPT IN SYNC WITH MEMBERSHIP HISTORY LENGTH + Duration: 60 * Day, + }, + ValidatorScoreExponent: 10, + ValidatorScoreAdjustmentSpeed: fixed("0.1"), + + // MUST BE KEPT IN SYNC WITH VALIDATOR LOCKED GOLD DURATION + MembershipHistoryLength: 60, + + CommissionUpdateDelay: (3 * Day) / 5, // Approximately 3 days with 5s block times + MaxGroupSize: 5, + + SlashingPenaltyResetPeriod: 30 * Day, + + DowntimeGracePeriod: 0, + + Commission: fixed("0.1"), + }, + Election: ElectionParameters{ + MinElectableValidators: 1, + MaxElectableValidators: 100, + MaxVotesPerAccount: bigInt(10), + ElectabilityThreshold: fixed("0.001"), + }, + Exchange: ExchangeParameters{ + Spread: fixed("0.005"), + ReserveFraction: fixed("0.01"), + UpdateFrequency: 5 * Minute, + MinimumReports: 1, + Frozen: false, + }, + ExchangeEUR: ExchangeParameters{ + Spread: fixed("0.005"), + ReserveFraction: fixed("0.01"), + UpdateFrequency: 5 * Minute, + MinimumReports: 1, + Frozen: false, + }, + ExchangeBRL: ExchangeParameters{ + Spread: fixed("0.005"), + ReserveFraction: fixed("0.01"), + UpdateFrequency: 5 * Minute, + MinimumReports: 1, + Frozen: false, + }, + EpochRewards: EpochRewardsParameters{ + TargetVotingYieldInitial: fixed("0"), // Change to (x + 1) ^ 365 = 1.06 once Mainnet activated. + TargetVotingYieldAdjustmentFactor: fixed("0"), // Change to 1 / 3650 once Mainnet activated., + TargetVotingYieldMax: fixed("0.0005"), // (x + 1) ^ 365 = 1.20 + RewardsMultiplierMax: fixed("2"), + RewardsMultiplierAdjustmentFactorsUnderspend: fixed("0.5"), + RewardsMultiplierAdjustmentFactorsOverspend: fixed("5"), + + // Intentionally set lower than the expected value at steady state to account for the fact that + // users may take some time to start voting with their cGLD. + TargetVotingGoldFraction: fixed("0.5"), + MaxValidatorEpochPayment: bigIntStr("205479452054794520547"), // (75,000 / 365) * 10 ^ 18 + CommunityRewardFraction: fixed("0.25"), + CarbonOffsettingPartner: common.Address{}, + CarbonOffsettingFraction: fixed("0.001"), + + Frozen: false, + }, + LockedGold: LockedGoldParameters{ + UnlockingPeriod: 259200, + }, + Random: RandomParameters{ + RandomnessBlockRetentionWindow: 720, + }, + Attestations: AttestationsParameters{ + AttestationExpiryBlocks: Hour / 5, // 1 hour assuming 5 second blocks, but ok anyway + SelectIssuersWaitBlocks: 4, + MaxAttestations: 100, + AttestationRequestFeeInDollars: decimal("0.05"), // use decimal rather than fixed, since we use this to multiply by + }, + TransferWhitelist: TransferWhitelistParameters{}, + GoldToken: GoldTokenParameters{ + Frozen: false, + }, + Blockchain: BlockchainParameters{ + Version: Version{1, 0, 0}, + GasForNonGoldCurrencies: 50000, + BlockGasLimit: 13000000, + }, + DoubleSigningSlasher: DoubleSigningSlasherParameters{ + Reward: bigIntStr("1000000000000000000000"), // 1000 cGLD + Penalty: bigIntStr("9000000000000000000000"), // 9000 cGLD + }, + DowntimeSlasher: DowntimeSlasherParameters{ + Reward: bigIntStr("10000000000000000000"), // 10 cGLD + Penalty: bigIntStr("100000000000000000000"), // 100 cGLD + SlashableDowntime: 4, // make it small so it works with small epoch sizes, e.g. 10 + }, + Governance: GovernanceParameters{ + UseMultiSig: true, + ConcurrentProposals: 3, + MinDeposit: bigIntStr("100000000000000000000"), // 100 cGLD + QueueExpiry: 4 * Week, + DequeueFrequency: 30 * Minute, + ApprovalStageDuration: 30 * Minute, + ReferendumStageDuration: Hour, + ExecutionStageDuration: Day, + ParticipationBaseline: fixed("0.005"), + ParticipationFloor: fixed("0.01"), + BaselineUpdateFactor: fixed("0.2"), + BaselineQuorumFactor: fixed("1"), + }, + GrandaMento: GrandaMentoParameters{ + MaxApprovalExchangeRateChange: fixed("0.3"), + Spread: fixed("0.005"), + VetoPeriodSeconds: 10, + StableTokenExchangeLimits: []StableTokenExchangeLimit{ + { + StableToken: "StableToken", + MinExchangeAmount: bigIntStr("50000000000000000000000"), + MaxExchangeAmount: bigIntStr("50000000000000000000000000"), + }, + { + StableToken: "StableTokenEUR", + MinExchangeAmount: bigIntStr("40000000000000000000000"), + MaxExchangeAmount: bigIntStr("40000000000000000000000000"), + }, + { + StableToken: "StableTokenBRL", + MinExchangeAmount: bigIntStr("40000000000000000000000"), + MaxExchangeAmount: bigIntStr("40000000000000000000000000"), + }, + }, + }, + } +}
diff --git go-ethereum/mycelo/contract/abi.go celo/mycelo/contract/abi.go new file mode 100644 index 0000000000000000000000000000000000000000..1aeacacfa6341c31eabbe89ea6af31cd4d7f0781 --- /dev/null +++ celo/mycelo/contract/abi.go @@ -0,0 +1,41 @@ +package contract + +import ( + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm/runtime" +) + +func mustParseABI(abiStr string) *abi.ABI { + parsed, err := abi.JSON(strings.NewReader(abiStr)) + if err != nil { + panic(err) + } + return &parsed +} + +// AbiFor returns the ABI for one of the core contracts +func AbiFor(name string) *abi.ABI { + abi, ok := abis[name] + if !ok { + panic("No ABI for " + name) + } + return abi +} + +// DeployCoreContract deploys one of celo's core contracts +func DeployCoreContract(cfg *runtime.Config, contractName string, code []byte, params ...interface{}) (*EVMBackend, error) { + return DeployEVMBackend(AbiFor(contractName), cfg, code, params...) +} + +// CoreContract returns a contractBackend for a core contract +func CoreContract(cfg *runtime.Config, contractName string, address common.Address) *EVMBackend { + return NewEVMBackend(AbiFor(contractName), cfg, address) +} + +// ProxyContract returns a contractBackend for a core contract's proxy +func ProxyContract(cfg *runtime.Config, contractName string, address common.Address) *EVMBackend { + return NewEVMBackend(AbiFor("Proxy"), cfg, address) +}
diff --git go-ethereum/mycelo/hdwallet/README.md celo/mycelo/hdwallet/README.md new file mode 100644 index 0000000000000000000000000000000000000000..99fd15b7db4708ab0ff61af1227d5509d8054d5b --- /dev/null +++ celo/mycelo/hdwallet/README.md @@ -0,0 +1,6 @@ + +## Origin + +This module is a copy/adaptation of https://github.com/miguelmota/go-ethereum-hdwallet. All credit goes to them. + +The reason for copy vs dependency is that we want to depend on celo-blockchain module, not on go-ethereum \ No newline at end of file
diff --git go-ethereum/mycelo/genesis/README.md celo/mycelo/genesis/README.md new file mode 100644 index 0000000000000000000000000000000000000000..137ca15a241339f1d88d984e0ccd434468fbaedb --- /dev/null +++ celo/mycelo/genesis/README.md @@ -0,0 +1,5 @@ +To generate the *_parameters_json files: + +``` +go generate mycelo/genesis/config.g +```
diff --git go-ethereum/mycelo/genesis/gen_reserve_parameters_json.go celo/mycelo/genesis/gen_reserve_parameters_json.go new file mode 100644 index 0000000000000000000000000000000000000000..dc06c09f7a8c98de6b38767de1003f8925543784 --- /dev/null +++ celo/mycelo/genesis/gen_reserve_parameters_json.go @@ -0,0 +1,93 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package genesis + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/decimal/bigintstr" + "github.com/ethereum/go-ethereum/common/decimal/fixed" +) + +var _ = (*ReserveParametersMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (r ReserveParameters) MarshalJSON() ([]byte, error) { + type ReserveParameters struct { + TobinTaxStalenessThreshold uint64 `json:"tobinTaxStalenessThreshold"` + DailySpendingRatio *fixed.Fixed `json:"dailySpendingRatio"` + AssetAllocations AssetAllocationList `json:"assetAllocations"` + TobinTax *fixed.Fixed `json:"tobinTax"` + TobinTaxReserveRatio *fixed.Fixed `json:"tobinTaxReserveRatio"` + Spenders []common.Address `json:"spenders"` + OtherAddresses []common.Address `json:"otherAddresses"` + InitialBalance *bigintstr.BigIntStr `json:"initialBalance"` + FrozenAssetsStartBalance *bigintstr.BigIntStr `json:"frozenAssetsStartBalance"` + FrozenAssetsDays uint64 `json:"frozenAssetsDays"` + } + var enc ReserveParameters + enc.TobinTaxStalenessThreshold = r.TobinTaxStalenessThreshold + enc.DailySpendingRatio = r.DailySpendingRatio + enc.AssetAllocations = r.AssetAllocations + enc.TobinTax = r.TobinTax + enc.TobinTaxReserveRatio = r.TobinTaxReserveRatio + enc.Spenders = r.Spenders + enc.OtherAddresses = r.OtherAddresses + enc.InitialBalance = (*bigintstr.BigIntStr)(r.InitialBalance) + enc.FrozenAssetsStartBalance = (*bigintstr.BigIntStr)(r.FrozenAssetsStartBalance) + enc.FrozenAssetsDays = r.FrozenAssetsDays + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (r *ReserveParameters) UnmarshalJSON(input []byte) error { + type ReserveParameters struct { + TobinTaxStalenessThreshold *uint64 `json:"tobinTaxStalenessThreshold"` + DailySpendingRatio *fixed.Fixed `json:"dailySpendingRatio"` + AssetAllocations *AssetAllocationList `json:"assetAllocations"` + TobinTax *fixed.Fixed `json:"tobinTax"` + TobinTaxReserveRatio *fixed.Fixed `json:"tobinTaxReserveRatio"` + Spenders []common.Address `json:"spenders"` + OtherAddresses []common.Address `json:"otherAddresses"` + InitialBalance *bigintstr.BigIntStr `json:"initialBalance"` + FrozenAssetsStartBalance *bigintstr.BigIntStr `json:"frozenAssetsStartBalance"` + FrozenAssetsDays *uint64 `json:"frozenAssetsDays"` + } + var dec ReserveParameters + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.TobinTaxStalenessThreshold != nil { + r.TobinTaxStalenessThreshold = *dec.TobinTaxStalenessThreshold + } + if dec.DailySpendingRatio != nil { + r.DailySpendingRatio = dec.DailySpendingRatio + } + if dec.AssetAllocations != nil { + r.AssetAllocations = *dec.AssetAllocations + } + if dec.TobinTax != nil { + r.TobinTax = dec.TobinTax + } + if dec.TobinTaxReserveRatio != nil { + r.TobinTaxReserveRatio = dec.TobinTaxReserveRatio + } + if dec.Spenders != nil { + r.Spenders = dec.Spenders + } + if dec.OtherAddresses != nil { + r.OtherAddresses = dec.OtherAddresses + } + if dec.InitialBalance != nil { + r.InitialBalance = (*big.Int)(dec.InitialBalance) + } + if dec.FrozenAssetsStartBalance != nil { + r.FrozenAssetsStartBalance = (*big.Int)(dec.FrozenAssetsStartBalance) + } + if dec.FrozenAssetsDays != nil { + r.FrozenAssetsDays = *dec.FrozenAssetsDays + } + return nil +}
diff --git go-ethereum/mycelo/env/types_test.go celo/mycelo/env/types_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7f3ba4ef4ea9c009b361248f5b746f48455f6636 --- /dev/null +++ celo/mycelo/env/types_test.go @@ -0,0 +1,64 @@ +package env + +import ( + "encoding/json" + "math/big" + "testing" + + . "github.com/onsi/gomega" +) + +func TestConfigMarhalling(t *testing.T) { + RegisterTestingT(t) + + cfg := Config{ + ChainID: big.NewInt(1500), + Accounts: AccountsConfig{ + Mnemonic: "aloha hawai", + NumValidators: 6, + ValidatorsPerGroup: 2, + }, + } + + raw, err := json.Marshal(cfg) + Ω(err).ShouldNot(HaveOccurred()) + + // raw2, err := json.MarshalIndent(cfg, " ", " ") + // Ω(err).ShouldNot(HaveOccurred()) + // fmt.Println(string(raw2)) + + var resultCfg Config + err = json.Unmarshal(raw, &resultCfg) + Ω(err).ShouldNot(HaveOccurred()) + + Ω(resultCfg).Should(Equal(cfg)) +} +func TestConfigReadJson(t *testing.T) { + RegisterTestingT(t) + + jsonStr := []byte(`{ + "chainId": 1500, + "accounts": { + "mnemonic": "aloha hawai", + "validators": 6, + "validatorsPerGroup": 2 + } + }`) + + expectedCfg := Config{ + ChainID: big.NewInt(1500), + Accounts: AccountsConfig{ + Mnemonic: "aloha hawai", + NumValidators: 6, + ValidatorsPerGroup: 2, + }, + } + + var resultCfg Config + err := json.Unmarshal(jsonStr, &resultCfg) + Ω(err).ShouldNot(HaveOccurred()) + Ω(err).ShouldNot(HaveOccurred()) + + Ω(resultCfg).Should(Equal(expectedCfg)) + +}
diff --git go-ethereum/mycelo/contract/README.md celo/mycelo/contract/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2c678b9559204f0b6a9aefc59f73859572aae2ee --- /dev/null +++ celo/mycelo/contract/README.md @@ -0,0 +1,12 @@ + +To generate gen_abis.go run + +Using the monorepo_commit file: +``` +go run ./mycelo/internal/scripts/generate -buildpath ./compiled-system-contracts +``` + +For a custom one: +``` +go run ./mycelo/internal/scripts/generate -buildpath $CELO_MONOREPO/packages/protocol/build/contracts +``` \ No newline at end of file
diff --git go-ethereum/mycelo/env/core_contracts.go celo/mycelo/env/core_contracts.go new file mode 100644 index 0000000000000000000000000000000000000000..4d8fe2b2556eee151eb13af61e07798175821a62 --- /dev/null +++ celo/mycelo/env/core_contracts.go @@ -0,0 +1,163 @@ +package env + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" +) + +var addr = common.HexToAddress + +var libraryAddresses = map[string]common.Address{ + "FixidityLib": addr("0xa001"), + "Proposals": addr("0xa002"), + "LinkedList": addr("0xa003"), + "SortedLinkedList": addr("0xa004"), + "SortedLinkedListWithMedian": addr("0xa051"), + "AddressLinkedList": addr("0xa006"), + "AddressSortedLinkedList": addr("0xa007"), + "IntegerSortedLinkedList": addr("0xa008"), + "AddressSortedLinkedListWithMedian": addr("0xa009"), + "Signatures": addr("0xa010"), +} + +var genesisAddresses = map[string]common.Address{ + // Contract implementations + "Registry": addr("0xce11"), + "Freezer": addr("0xf001"), + "FeeCurrencyWhitelist": addr("0xf002"), + "GoldToken": addr("0xf003"), + "SortedOracles": addr("0xf004"), + "GasPriceMinimum": addr("0xf005"), + "ReserveSpenderMultiSig": addr("0xf006"), + "Reserve": addr("0xf007"), + "StableToken": addr("0xf008"), + "Exchange": addr("0xf009"), + "Accounts": addr("0xf010"), + "LockedGold": addr("0xf011"), + "Validators": addr("0xf012"), + "Election": addr("0xf013"), + "EpochRewards": addr("0xf014"), + "Random": addr("0xf015"), + "Attestations": addr("0xf016"), + "Escrow": addr("0xf017"), + "BlockchainParameters": addr("0xf018"), + "GovernanceSlasher": addr("0xf019"), + "DoubleSigningSlasher": addr("0xf020"), + "DowntimeSlasher": addr("0xf021"), + "GovernanceApproverMultiSig": addr("0xf022"), + "Governance": addr("0xf023"), + "StableTokenEUR": addr("0xf024"), + "ExchangeEUR": addr("0xf025"), + "StableTokenBRL": addr("0xf026"), + "ExchangeBRL": addr("0xf027"), + "GrandaMento": addr("0xf028"), + "FederatedAttestations": addr("0xf029"), + "OdisPayments": addr("0xf030"), + + // Contract Proxies + "RegistryProxy": addr("0xce10"), + "FreezerProxy": addr("0xd001"), + "FeeCurrencyWhitelistProxy": addr("0xd002"), + "GoldTokenProxy": addr("0xd003"), + "SortedOraclesProxy": addr("0xd004"), + "GasPriceMinimumProxy": addr("0xd005"), + "ReserveSpenderMultiSigProxy": addr("0xd006"), + "ReserveProxy": addr("0xd007"), + "StableTokenProxy": addr("0xd008"), + "ExchangeProxy": addr("0xd009"), + "AccountsProxy": addr("0xd010"), + "LockedGoldProxy": addr("0xd011"), + "ValidatorsProxy": addr("0xd012"), + "ElectionProxy": addr("0xd013"), + "EpochRewardsProxy": addr("0xd014"), + "RandomProxy": addr("0xd015"), + "AttestationsProxy": addr("0xd016"), + "EscrowProxy": addr("0xd017"), + "BlockchainParametersProxy": addr("0xd018"), + "GovernanceSlasherProxy": addr("0xd019"), + "DoubleSigningSlasherProxy": addr("0xd020"), + "DowntimeSlasherProxy": addr("0xd021"), + "GovernanceApproverMultiSigProxy": addr("0xd022"), + "GovernanceProxy": addr("0xd023"), + "StableTokenEURProxy": addr("0xd024"), + "ExchangeEURProxy": addr("0xd025"), + "StableTokenBRLProxy": addr("0xd026"), + "ExchangeBRLProxy": addr("0xd027"), + "GrandaMentoProxy": addr("0xd028"), + "FederatedAttestationsProxy": addr("0xd029"), + "OdisPaymentsProxy": addr("0xd030"), +} + +var libraries = []string{ + "FixidityLib", + "Proposals", + "LinkedList", + "SortedLinkedList", + "SortedLinkedListWithMedian", + "AddressLinkedList", + "AddressSortedLinkedList", + "IntegerSortedLinkedList", + "AddressSortedLinkedListWithMedian", + "Signatures", +} + +// Libraries returns all celo-blockchain library names +func Libraries() []string { return libraries } + +// LibraryAddressFor obtains the address for a core contract +func LibraryAddressFor(name string) (common.Address, error) { + address, ok := libraryAddresses[name] + if !ok { + return common.ZeroAddress, fmt.Errorf("can't find genesis address for %s", name) + } + return address, nil +} + +// ImplAddressFor obtains the address for a core contract +func ImplAddressFor(name string) (common.Address, error) { + address, ok := genesisAddresses[name] + if !ok { + return common.ZeroAddress, fmt.Errorf("can't find genesis address for %s", name) + } + return address, nil +} + +// ProxyAddressFor obtains the address for a core contract proxy +func ProxyAddressFor(name string) (common.Address, error) { + address, ok := genesisAddresses[name+"Proxy"] + if !ok { + return common.ZeroAddress, fmt.Errorf("can't find genesis address for %sProxy", name) + } + return address, nil +} + +// MustLibraryAddressFor obtains the address for a core contract +// this variant panics on error +func MustLibraryAddressFor(name string) common.Address { + address, err := LibraryAddressFor(name) + if err != nil { + panic(err) + } + return address +} + +// MustImplAddressFor obtains the address for a core contract +// this variant panics on error +func MustImplAddressFor(name string) common.Address { + address, err := ImplAddressFor(name) + if err != nil { + panic(err) + } + return address +} + +// MustProxyAddressFor obtains the address for a core contract proxy +// this variant panics on error +func MustProxyAddressFor(name string) common.Address { + address, err := ProxyAddressFor(name) + if err != nil { + panic(err) + } + return address +}
diff --git go-ethereum/mycelo/contract/evm_backend.go celo/mycelo/contract/evm_backend.go new file mode 100644 index 0000000000000000000000000000000000000000..417c95ec95e1db0afd5c944236d35199de626e4e --- /dev/null +++ celo/mycelo/contract/evm_backend.go @@ -0,0 +1,117 @@ +package contract + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm/runtime" + "github.com/ethereum/go-ethereum/log" +) + +// EVMBackend represents a contract interface that talks directly to an EVM +type EVMBackend struct { + abi *abi.ABI + runtimeConfigTemplate *runtime.Config + Address common.Address + defaultCallOpts CallOpts +} + +type CallOpts struct { + Origin common.Address + Value *big.Int +} + +func DeployEVMBackend(abi *abi.ABI, runtimeConfig *runtime.Config, code []byte, params ...interface{}) (*EVMBackend, error) { + constructorArgs, err := abi.Pack("", params...) + if err != nil { + return nil, err + } + + _, address, _, err := runtime.Create(append(code, constructorArgs...), runtimeConfig) + if err != nil { + return nil, fmt.Errorf("error creating contract: %w", err) + } + + contract := NewEVMBackend(abi, runtimeConfig, address) + return contract, nil +} + +// NewEVMBackend creates a new EVM based contract +func NewEVMBackend(abi *abi.ABI, runtimeConfig *runtime.Config, receiver common.Address) *EVMBackend { + return &EVMBackend{ + abi: abi, + runtimeConfigTemplate: runtimeConfig, + Address: receiver, + defaultCallOpts: CallOpts{ + Origin: runtimeConfig.Origin, + Value: common.Big0, + }, + } +} + +// SimpleCallFrom makes an evm call with given sender address and just returns the error status +func (ecb *EVMBackend) SimpleCallFrom(origin common.Address, method string, args ...interface{}) error { + _, err := ecb.Call(CallOpts{Origin: origin}, method, args...) + return err +} + +// SimpleCall makes an evm call and just returns the error status +func (ecb *EVMBackend) SimpleCall(method string, args ...interface{}) error { + _, err := ecb.Call(ecb.defaultCallOpts, method, args...) + return err +} + +// Call makes an evm call and returns error and gasLeft +func (ecb *EVMBackend) Call(opts CallOpts, method string, args ...interface{}) (uint64, error) { + return ecb.call(opts, method, args...) +} + +func (ecb *EVMBackend) call(opts CallOpts, method string, args ...interface{}) (uint64, error) { + log.Trace("SmartContract Call", "from", opts.Origin, "to", ecb.Address, "method", method, "arguments", args) + calldata, err := ecb.abi.Pack(method, args...) + if err != nil { + return 0, err + } + + runtimeCfg := *ecb.runtimeConfigTemplate + runtimeCfg.Origin = opts.Origin + runtimeCfg.Value = opts.Value + + ret, gasLeft, err := runtime.Call(ecb.Address, calldata, &runtimeCfg) + + if err != nil { + // try unpacking the revert (if it is one) + revertReason, err2 := abi.UnpackRevert(ret) + if err2 == nil { + return gasLeft, fmt.Errorf("Revert: %s", revertReason) + } + } + + return gasLeft, err +} + +// Query makes an evm call, populates the result into returnValue and returns error and gasLeft +func (ecb *EVMBackend) Query(returnValue interface{}, method string, args ...interface{}) (uint64, error) { + calldata, err := ecb.abi.Pack(method, args...) + if err != nil { + return 0, err + } + + runtimeCfg := *ecb.runtimeConfigTemplate + log.Debug("method query", "method", method, "to", ecb.Address, "data", common.Bytes2Hex(calldata)) + ret, gasLeft, err := runtime.Call(ecb.Address, calldata, &runtimeCfg) + + if err != nil { + // try unpacking the revert (if it is one) + revertReason, err2 := abi.UnpackRevert(ret) + if err2 == nil { + return gasLeft, fmt.Errorf("Revert: %s", revertReason) + } + } + + err = ecb.abi.UnpackIntoInterface(returnValue, method, ret) + + return gasLeft, err +}
diff --git go-ethereum/mycelo/env/types.go celo/mycelo/env/types.go new file mode 100644 index 0000000000000000000000000000000000000000..fda41dc61973ef699446a6757bd5f607a06b5817 --- /dev/null +++ celo/mycelo/env/types.go @@ -0,0 +1,112 @@ +package env + +import ( + "math/big" +) + +// Config represents mycelo environment parameters +type Config struct { + ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection + Accounts AccountsConfig `json:"accounts"` // Accounts configuration for the environment +} + +// AccountsConfig represents accounts configuration for the environment +type AccountsConfig struct { + Mnemonic string `json:"mnemonic"` // Accounts mnemonic + NumValidators int `json:"validators"` // Number of initial validators + ValidatorsPerGroup int `json:"validatorsPerGroup"` // Number of validators per group in the initial set + NumDeveloperAccounts int `json:"developerAccounts"` // Number of developers accounts + UseValidatorAsAdmin bool `json:"useValidatorAsAdmin"` // Whether to use the first validator as the admin (for compatibility with monorepo) +} + +// ValidatorGroup represents a group plus its validators members +type ValidatorGroup struct { + Account + Validators []Account +} + +// NumValidatorGroups retrieves the number of validator groups for the genesis +func (ac *AccountsConfig) NumValidatorGroups() int { + if (ac.NumValidators % ac.ValidatorsPerGroup) > 0 { + return (ac.NumValidators / ac.ValidatorsPerGroup) + 1 + } + return ac.NumValidators / ac.ValidatorsPerGroup +} + +// AdminAccount returns the environment's admin account +func (ac *AccountsConfig) AdminAccount() *Account { + at := AdminAT + if ac.UseValidatorAsAdmin { + at = ValidatorAT + } + acc, err := DeriveAccount(ac.Mnemonic, at, 0) + if err != nil { + panic(err) + } + return acc +} + +// DeveloperAccounts returns the environment's developers accounts +func (ac *AccountsConfig) DeveloperAccounts() []Account { + accounts, err := DeriveAccountList(ac.Mnemonic, DeveloperAT, ac.NumDeveloperAccounts) + if err != nil { + panic(err) + } + return accounts +} + +// Account retrieves the account corresponding to the (accountType, idx) +func (ac *AccountsConfig) Account(accType AccountType, idx int) (*Account, error) { + return DeriveAccount(ac.Mnemonic, accType, idx) +} + +// ValidatorAccounts returns the environment's validators accounts +func (ac *AccountsConfig) ValidatorAccounts() []Account { + accounts, err := DeriveAccountList(ac.Mnemonic, ValidatorAT, ac.NumValidators) + if err != nil { + panic(err) + } + return accounts +} + +// ValidatorAccounts returns the environment's validators accounts +func (ac *AccountsConfig) TxFeeRecipientAccounts() []Account { + accounts, err := DeriveAccountList(ac.Mnemonic, TxFeeRecipientAT, ac.NumValidators) + if err != nil { + panic(err) + } + return accounts +} + +// ValidatorGroupAccounts returns the environment's validators group accounts +func (ac *AccountsConfig) ValidatorGroupAccounts() []Account { + accounts, err := DeriveAccountList(ac.Mnemonic, ValidatorGroupAT, ac.NumValidatorGroups()) + if err != nil { + panic(err) + } + return accounts +} + +// ValidatorGroups return the list of validator groups on genesis +func (ac *AccountsConfig) ValidatorGroups() []ValidatorGroup { + groups := make([]ValidatorGroup, ac.NumValidatorGroups()) + + groupAccounts := ac.ValidatorGroupAccounts() + validatorAccounts := ac.ValidatorAccounts() + + for i := 0; i < (len(groups) - 1); i++ { + groups[i] = ValidatorGroup{ + Account: groupAccounts[i], + Validators: validatorAccounts[ac.ValidatorsPerGroup*i : ac.ValidatorsPerGroup*(i+1)], + } + } + + // last group might not be full, use an open slice for validators + i := len(groups) - 1 + groups[i] = ValidatorGroup{ + Account: groupAccounts[i], + Validators: validatorAccounts[ac.ValidatorsPerGroup*i:], + } + + return groups +}
diff --git go-ethereum/mycelo/genesis/gen_double_signing_slasher_parameters_json.go celo/mycelo/genesis/gen_double_signing_slasher_parameters_json.go new file mode 100644 index 0000000000000000000000000000000000000000..7bdbf72f64930bf363bf7d49f26e146bacea0544 --- /dev/null +++ celo/mycelo/genesis/gen_double_signing_slasher_parameters_json.go @@ -0,0 +1,43 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package genesis + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/decimal/bigintstr" +) + +var _ = (*DoubleSigningSlasherParametersMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (d DoubleSigningSlasherParameters) MarshalJSON() ([]byte, error) { + type DoubleSigningSlasherParameters struct { + Penalty *bigintstr.BigIntStr `json:"penalty"` + Reward *bigintstr.BigIntStr `json:"reward"` + } + var enc DoubleSigningSlasherParameters + enc.Penalty = (*bigintstr.BigIntStr)(d.Penalty) + enc.Reward = (*bigintstr.BigIntStr)(d.Reward) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (d *DoubleSigningSlasherParameters) UnmarshalJSON(input []byte) error { + type DoubleSigningSlasherParameters struct { + Penalty *bigintstr.BigIntStr `json:"penalty"` + Reward *bigintstr.BigIntStr `json:"reward"` + } + var dec DoubleSigningSlasherParameters + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Penalty != nil { + d.Penalty = (*big.Int)(dec.Penalty) + } + if dec.Reward != nil { + d.Reward = (*big.Int)(dec.Reward) + } + return nil +}
diff --git go-ethereum/mycelo/cluster/node.go celo/mycelo/cluster/node.go new file mode 100644 index 0000000000000000000000000000000000000000..62760e0f4ee2227bb7e821c098ae3743e0f818a1 --- /dev/null +++ celo/mycelo/cluster/node.go @@ -0,0 +1,242 @@ +package cluster + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "math/big" + "net" + "os" + "os/exec" + "path" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/internal/fileutils" + "github.com/ethereum/go-ethereum/mycelo/env" + "github.com/ethereum/go-ethereum/p2p/enode" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" +) + +// NodeConfig represents the configuration of a celo-blockchain node runner +type NodeConfig struct { + GethPath string + ExtraFlags string + ChainID *big.Int + Number int + Account env.Account + TxFeeRecipientAccount env.Account + OtherAccounts []env.Account + Datadir string +} + +// RPCPort is the rpc port this node will use +func (nc *NodeConfig) RPCPort() int64 { + return int64(8545 + nc.Number) +} + +// RPCPort is the rpc port this node will use +func (nc *NodeConfig) WebsocketPort() int64 { + return int64(9545 + nc.Number) +} + +// NodePort is the node port this node will use +func (nc *NodeConfig) NodePort() int64 { + return int64(30303 + nc.Number) +} + +// Node represents a Node runner +type Node struct { + *NodeConfig +} + +// NewNode creates a node runner +func NewNode(cfg *NodeConfig) *Node { + return &Node{ + NodeConfig: cfg, + } +} + +// SetStaticNodes configures static nodes to be used on the node +func (n *Node) SetStaticNodes(enodeUrls ...string) error { + var staticNodesRaw []byte + var err error + + if staticNodesRaw, err = json.Marshal(enodeUrls); err != nil { + return fmt.Errorf("Can't serialize static nodes: %w", err) + } + //nolint:gosec + if err = ioutil.WriteFile(n.staticNodesFile(), staticNodesRaw, 0644); err != nil { + return fmt.Errorf("Can't serialize static nodes: %w", err) + } + + return nil +} + +// EnodeURL returns the enode url used by the node +func (n *Node) EnodeURL() (string, error) { + nodekey, err := crypto.LoadECDSA(n.keyFile()) + if err != nil { + return "", err + } + ip := net.IP{127, 0, 0, 1} + en := enode.NewV4(&nodekey.PublicKey, ip, int(n.NodePort()), int(n.NodePort())) + return en.URLv4(), nil +} + +// AccountAddresses retrieves the list of accounts currently configured in the node +func (n *Node) AccountAddresses() []common.Address { + ks := keystore.NewKeyStore(path.Join(n.Datadir, "keystore"), keystore.LightScryptN, keystore.LightScryptP) + addresses := make([]common.Address, 0) + for _, acc := range ks.Accounts() { + addresses = append(addresses, acc.Address) + } + return addresses +} + +// Init will run `geth init` on the node along other initialization procedures +// that need to happen before we run the node +func (n *Node) Init(GenesisJSON string) error { + if fileutils.FileExists(n.Datadir) { + os.RemoveAll(n.Datadir) + } + os.MkdirAll(n.Datadir, os.ModePerm) + + // Write password file + if err := ioutil.WriteFile(n.pwdFile(), []byte{}, os.ModePerm); err != nil { + return err + } + + // Run geth init + if out, err := n.runSync("init", GenesisJSON); err != nil { + os.Stderr.Write(out) + return err + } + + // Generate nodekey file (enode private key) + if err := n.generateNodeKey(); err != nil { + return err + } + + // Add Accounts + ks := keystore.NewKeyStore(path.Join(n.Datadir, "keystore"), keystore.LightScryptN, keystore.LightScryptP) + if _, err := ks.ImportECDSA(n.Account.PrivateKey, ""); err != nil { + return err + } + for _, acc := range n.OtherAccounts { + if _, err := ks.ImportECDSA(acc.PrivateKey, ""); err != nil { + return err + } + } + + return nil +} + +func (n *Node) generateNodeKey() error { + nodeKey, err := crypto.GenerateKey() + if err != nil { + return err + } + if err = crypto.SaveECDSA(n.keyFile(), nodeKey); err != nil { + return err + } + return nil +} + +// Run will run the node +func (n *Node) Run(ctx context.Context) error { + + var addressToUnlock string + for _, addr := range n.AccountAddresses() { + addressToUnlock += "," + addr.Hex() + } + + args := []string{ + "--datadir", n.Datadir, + "--verbosity", "4", + "--networkid", n.ChainID.String(), + "--syncmode", "full", + "--mine", + "--allow-insecure-unlock", + "--nodiscover", + "--nat", "extip:127.0.0.1", + "--port", strconv.FormatInt(n.NodePort(), 10), + "--http", + "--http.addr", "127.0.0.1", + "--http.port", strconv.FormatInt(n.RPCPort(), 10), + "--http.api", "eth,net,web3,debug,admin,personal,istanbul,txpool", + "--ws", + "--ws.addr", "127.0.0.1", + "--ws.port", strconv.FormatInt(n.WebsocketPort(), 10), + "--ws.api", "eth,net,web3,debug,admin,personal,istanbul,txpool", + // "--nodiscover", "--nousb ", + "--unlock", addressToUnlock, + "--password", n.pwdFile(), + } + + // Once we're sure we won't run v1.2.x and older, can get rid of this check + // and just use the new options + helpBytes, _ := exec.Command(n.GethPath, "--help").Output() // #nosec G204 + useTxFeeRecipient := strings.Contains(string(helpBytes), "miner.validator") + if useTxFeeRecipient { + args = append(args, + "--miner.validator", n.Account.Address.Hex(), + "--tx-fee-recipient", n.TxFeeRecipientAccount.Address.Hex(), + ) + } else { + args = append(args, + "--etherbase", n.Account.Address.Hex(), + ) + } + + if n.ExtraFlags != "" { + args = append(args, strings.Fields(n.ExtraFlags)...) + } + cmd := exec.Command(n.GethPath, args...) // #nosec G204 + + log.Println(n.GethPath, strings.Join(args, " ")) + + logfile, err := os.OpenFile(n.logFile(), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer logfile.Close() + cmd.Stderr = logfile + cmd.Stdout = os.Stdout + // cmd.Stderr = os.Stderr + + if err := cmd.Start(); err != nil { + return err + } + + // rpc, err := rpc.Dial(fmt.Sprintf("http://localhost:%d", n.RPCPort())) + // if err != nil { + // return err + // } + // rpc.CallContext(ctx, nil, "personal_unlock", ) + + go func() { + <-ctx.Done() + if err := cmd.Process.Signal(os.Interrupt); err != nil { + log.Fatal("Failed to send interrupt signal to geth cmd") + } + }() + + return cmd.Wait() +} + +func (n *Node) pwdFile() string { return path.Join(n.Datadir, "password") } +func (n *Node) logFile() string { return path.Join(n.Datadir, "geth.log") } +func (n *Node) keyFile() string { return path.Join(n.Datadir, "celo/nodekey") } +func (n *Node) staticNodesFile() string { return path.Join(n.Datadir, "/celo/static-nodes.json") } + +func (n *Node) runSync(args ...string) ([]byte, error) { + args = append([]string{"--datadir", n.Datadir}, args...) + cmd := exec.Command(n.GethPath, args...) // #nosec G204 + return cmd.CombinedOutput() +}
diff --git go-ethereum/mycelo/cluster/cluster.go celo/mycelo/cluster/cluster.go new file mode 100644 index 0000000000000000000000000000000000000000..9238a32e190d148b30a0393a8b4e31d5b6cb311d --- /dev/null +++ celo/mycelo/cluster/cluster.go @@ -0,0 +1,115 @@ +package cluster + +import ( + "context" + "fmt" + "log" + + "github.com/ethereum/go-ethereum/mycelo/env" + "github.com/ethereum/go-ethereum/mycelo/internal/console" + "golang.org/x/sync/errgroup" +) + +// Cluster represent a set of nodes (validators) +// that are managed together +type Cluster struct { + env *env.Environment + config Config + + nodes []*Node +} + +type Config struct { + GethPath string + ExtraFlags string +} + +// New creates a new cluster instance +func New(env *env.Environment, cfg Config) *Cluster { + return &Cluster{ + env: env, + config: cfg, + } +} + +// Init will initialize the nodes +// This implies running `geth init` but also +// configuring static nodes and node accounts +func (cl *Cluster) Init() error { + var err error + + nodes := cl.ensureNodes() + enodeUrls := make([]string, len(nodes)) + console.Info("Initializing validator nodes") + for i, node := range nodes { + console.Infof("validator-%d> geth init", i) + if err := node.Init(cl.env.GenesisPath()); err != nil { + return err + } + + enodeUrls[i], err = node.EnodeURL() + if err != nil { + return err + } + } + + // Connect each validator to each other + for i, node := range nodes { + var urls []string + urls = append(urls, enodeUrls[:i]...) + urls = append(urls, enodeUrls[i+1:]...) + err = node.SetStaticNodes(urls...) + if err != nil { + return err + } + } + + return nil +} + +func (cl *Cluster) ensureNodes() []*Node { + + if cl.nodes == nil { + validators := cl.env.Accounts().ValidatorAccounts() + txFeeRecipients := cl.env.Accounts().TxFeeRecipientAccounts() + cl.nodes = make([]*Node, len(validators)) + for i, validator := range validators { + nodeConfig := &NodeConfig{ + GethPath: cl.config.GethPath, + ExtraFlags: cl.config.ExtraFlags, + Number: i, + Account: validator, + TxFeeRecipientAccount: txFeeRecipients[i], + Datadir: cl.env.ValidatorDatadir(i), + ChainID: cl.env.Config.ChainID, + } + cl.nodes[i] = NewNode(nodeConfig) + } + } + return cl.nodes +} + +// PrintNodeInfo prints debug information about nodes +func (cl *Cluster) PrintNodeInfo() error { + for i, node := range cl.ensureNodes() { + endoreURL, err := node.EnodeURL() + if err != nil { + return err + } + fmt.Printf("validator-%d: %s\n", i, endoreURL) + } + return nil +} + +// Run will run all the cluster nodes +func (cl *Cluster) Run(ctx context.Context) error { + group, ctx := errgroup.WithContext(ctx) + log.Printf("Starting cluster") + for i, node := range cl.ensureNodes() { + node := node + i := i + log.Printf("Starting validator%02d...", i) + group.Go(func() error { return node.Run(ctx) }) + } + return group.Wait() +}
diff --git go-ethereum/mycelo/env/environment.go celo/mycelo/env/environment.go new file mode 100644 index 0000000000000000000000000000000000000000..a846989d70d95f49a849f14161c0f5924bb2df84 --- /dev/null +++ celo/mycelo/env/environment.go @@ -0,0 +1,79 @@ +package env + +import ( + "os" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/internal/fileutils" + "github.com/ethereum/go-ethereum/mycelo/internal/utils" +) + +// Environment represents a mycelo environment +// which is the home to any mycelo operation within a computer +type Environment struct { + paths paths + Config Config +} + +// New creates a new environment +func New(envpath string, cfg *Config) (*Environment, error) { + env := &Environment{ + paths: paths{Workdir: envpath}, + Config: *cfg, + } + + return env, nil +} + +// Load will load an environment located in envpath folder +func Load(envpath string) (*Environment, error) { + env := &Environment{ + paths: paths{Workdir: envpath}, + } + + if err := utils.ReadJson(&env.Config, env.paths.envJSON()); err != nil { + return nil, err + } + + return env, nil +} + +// Save will save environment's config into the environment folder +func (env *Environment) Save() error { + env.ensureWorkdir() + + if err := utils.WriteJson(env.Config, env.paths.envJSON()); err != nil { + return err + } + return nil +} + +// Accounts retrieves accounts config +func (env *Environment) Accounts() *AccountsConfig { return &env.Config.Accounts } + +// GenesisPath returns the paths to the genesis.json file (if present on the environment) +func (env *Environment) GenesisPath() string { return env.paths.genesisJSON() } + +// ValidatorDatadir returns the datadir that mycelo uses to run the validator-[idx] +func (env *Environment) ValidatorDatadir(idx int) string { return env.paths.validatorDatadir(idx) } + +// ValidatorIPC returns the ipc path to validator-[idx] +func (env *Environment) ValidatorIPC(idx int) string { return env.paths.validatorIPC(idx) } + +// IPC returns the IPC path to the first validator +func (env *Environment) IPC() string { return env.paths.validatorIPC(0) } + +// SaveGenesis writes genesis.json within the env.json +func (env *Environment) SaveGenesis(genesis *core.Genesis) error { + env.ensureWorkdir() + if err := utils.WriteJson(genesis, env.paths.genesisJSON()); err != nil { + return err + } + return nil +} + +func (env *Environment) ensureWorkdir() { + if !fileutils.FileExists(env.paths.Workdir) { + os.MkdirAll(env.paths.Workdir, os.ModePerm) + } +}
diff --git go-ethereum/mycelo/internal/console/console.go celo/mycelo/internal/console/console.go new file mode 100644 index 0000000000000000000000000000000000000000..d0b029a67ee4826bbe157f203206b9632b15cf1b --- /dev/null +++ celo/mycelo/internal/console/console.go @@ -0,0 +1,40 @@ +package console + +import ( + "fmt" + "os" + + . "github.com/logrusorgru/aurora" +) + +// Error logs en error +func Error(a interface{}) { + fmt.Println(Bold(Red(a))) +} + +// Errorf logs an error (printf format) +func Errorf(format string, a ...interface{}) { + Error(fmt.Sprintf(format, a...)) +} + +// Fatalf logs an error, then exits (printf format) +func Fatalf(format string, a ...interface{}) { + Errorf(format, a...) + os.Exit(1) +} + +// Fatal logs an error, then exits +func Fatal(a interface{}) { + Error(a) + os.Exit(1) +} + +// Info render informational message +func Info(a interface{}) { + fmt.Println(Blue(a)) +} + +// Infof render informational message (printf version) +func Infof(format string, a ...interface{}) { + Info(fmt.Sprintf(format, a...)) +}
diff --git go-ethereum/mycelo/internal/utils/jsonfile.go celo/mycelo/internal/utils/jsonfile.go new file mode 100644 index 0000000000000000000000000000000000000000..02e7b0d0ac21baa9fd37d911b8322001ff45e4aa --- /dev/null +++ celo/mycelo/internal/utils/jsonfile.go @@ -0,0 +1,24 @@ +package utils + +import ( + "encoding/json" + "io/ioutil" +) + +func ReadJson(out interface{}, filepath string) error { + byteValue, err := ioutil.ReadFile(filepath) + if err != nil { + return err + } + + return json.Unmarshal(byteValue, out) +} + +func WriteJson(in interface{}, filepath string) error { + byteValue, err := json.MarshalIndent(in, " ", " ") + if err != nil { + return err + } + + return ioutil.WriteFile(filepath, byteValue, 0644) +}
diff --git go-ethereum/mycelo/internal/scripts/generate/main.go celo/mycelo/internal/scripts/generate/main.go new file mode 100644 index 0000000000000000000000000000000000000000..b29cba3079ed2e9a2d4ca5b57d4eb2d841b9e375 --- /dev/null +++ celo/mycelo/internal/scripts/generate/main.go @@ -0,0 +1,134 @@ +package main + +// The following directive is necessary to make the package coherent: + +// This program generates contributors.go. It can be invoked by running +// go generate + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "path" + "text/template" + "time" +) + +func readABI(truffleJsonFile string) (string, error) { + jsonData, err := ioutil.ReadFile(truffleJsonFile) + if err != nil { + return "", fmt.Errorf("Can't read build fild for %s: %w", truffleJsonFile, err) + } + + var data struct { + ABI json.RawMessage `json:"abi"` + } + + err = json.Unmarshal(jsonData, &data) + if err != nil { + return "", fmt.Errorf("Can't read ABI for %s: %w", truffleJsonFile, err) + } + + return string(data.ABI), nil +} + +var contractNames = []string{ + "Proxy", + "Registry", + "Freezer", + "TransferWhitelist", + "FeeCurrencyWhitelist", + "GoldToken", + "SortedOracles", + "GasPriceMinimum", + "ReserveSpenderMultiSig", + "Reserve", + "StableToken", + "StableTokenEUR", + "StableTokenBRL", + "Exchange", + "ExchangeEUR", + "ExchangeBRL", + "Accounts", + "LockedGold", + "Validators", + "Election", + "EpochRewards", + "Random", + "Attestations", + "Escrow", + "BlockchainParameters", + "GovernanceSlasher", + "DoubleSigningSlasher", + "DowntimeSlasher", + "GovernanceApproverMultiSig", + "Governance", + "GrandaMento", + "FederatedAttestations", + "OdisPayments", +} + +var buildPath = flag.String("buildpath", "", "the folder where truffle contract build live (on monorepo ./packages/protocol/build/contracts )") +var outPath = flag.String("outpath", "./mycelo/contract", "relative path to mycelo/contract package") + +func main() { + flag.Parse() + + if buildPath == nil || *buildPath == "" { + fmt.Println("Missing --buildpath variable") + flag.PrintDefaults() + os.Exit(1) + } + + outfile := path.Join(*outPath, "gen_abis.go") + + fmt.Printf("Generating abi mappings on %s.\nReading contracts from %s\n", outfile, *buildPath) + + abis := make(map[string]string) + for _, name := range contractNames { + abi, err := readABI(path.Join(*buildPath, name+".json")) + die(err) + abis[name] = abi + } + + f, err := os.Create(outfile) + die(err) + defer f.Close() + + fileTemplate.Execute(f, struct { + Timestamp time.Time + ABIs map[string]string + }{ + Timestamp: time.Now(), + ABIs: abis, + }) +} + +func die(err error) { + if err != nil { + log.Fatal(err) + } +} + +var fileTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT. +// {{ .Timestamp }} +package contract + +import "github.com/ethereum/go-ethereum/accounts/abi" + +var abis map[string]*abi.ABI + +func init() { + abis = make(map[string]*abi.ABI) + + {{range $name, $abi := .ABIs -}} + // {{$name}} ABI + abis["{{$name}}"] = mustParseABI(` + "`{{$abi}}`" + `) + + + {{- end }} +} +`))
diff --git go-ethereum/cmd/mycelo/env_commands.go celo/cmd/mycelo/env_commands.go new file mode 100644 index 0000000000000000000000000000000000000000..5a0ce02a117bf3fe8b9da1fdd8105f8563417a65 --- /dev/null +++ celo/cmd/mycelo/env_commands.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/mycelo/env" + "gopkg.in/urfave/cli.v1" +) + +var envCommand = cli.Command{ + Name: "env", + Usage: "Environment utility commands", + Subcommands: []cli.Command{ + getAccountCommand, + }, +} + +var getAccountCommand = cli.Command{ + Name: "account", + Action: getAccount, + Flags: []cli.Flag{ + idxFlag, + accountTypeFlag, + }, +} + +var ( + idxFlag = cli.IntFlag{ + Name: "idx", + Usage: "account index", + Value: 0, + } + accountTypeFlag = utils.TextMarshalerFlag{ + Name: "type", + Usage: `Account type (validator, developer, txNode, faucet, attestation, priceOracle, proxy, attestationBot, votingBot, txNodePrivate, validatorGroup, admin)`, + Value: &env.DeveloperAT, + } +) + +func getAccount(ctx *cli.Context) error { + myceloEnv, err := readEnv(ctx) + if err != nil { + return err + } + + idx := ctx.Int(idxFlag.Name) + accountType := *utils.LocalTextMarshaler(ctx, accountTypeFlag.Name).(*env.AccountType) + + account, err := myceloEnv.Accounts().Account(accountType, idx) + if err != nil { + return err + } + + fmt.Printf("AccountType: %s\nIndex:%d\nAddress: %s\nPrivateKey: %s\n", accountType, idx, account.Address.Hex(), account.PrivateKeyHex()) + return nil +}
diff --git go-ethereum/cmd/mycelo/templates.go celo/cmd/mycelo/templates.go new file mode 100644 index 0000000000000000000000000000000000000000..2b0db288003b62aa15e11e5b12be455fe5427659 --- /dev/null +++ celo/cmd/mycelo/templates.go @@ -0,0 +1,148 @@ +package main + +import ( + "math/big" + "math/rand" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/decimal/fixed" + "github.com/ethereum/go-ethereum/mycelo/env" + "github.com/ethereum/go-ethereum/mycelo/genesis" + "github.com/ethereum/go-ethereum/params" +) + +type template interface { + createEnv(workdir string) (*env.Environment, error) + createGenesisConfig(*env.Environment) (*genesis.Config, error) +} + +func templateFromString(templateStr string) template { + switch templateStr { + case "local": + return localEnv{} + case "loadtest": + return loadtestEnv{} + case "monorepo": + return monorepoEnv{} + } + return localEnv{} +} + +type localEnv struct{} + +func (e localEnv) createEnv(workdir string) (*env.Environment, error) { + envCfg := &env.Config{ + Accounts: env.AccountsConfig{ + Mnemonic: env.MustNewMnemonic(), + NumValidators: 3, + ValidatorsPerGroup: 1, + NumDeveloperAccounts: 10, + }, + ChainID: big.NewInt(1000 * (1 + rand.Int63n(9999))), + } + env, err := env.New(workdir, envCfg) + if err != nil { + return nil, err + } + + return env, nil +} + +func (e localEnv) createGenesisConfig(env *env.Environment) (*genesis.Config, error) { + + genesisConfig := genesis.CreateCommonGenesisConfig(env.Config.ChainID, env.Accounts().AdminAccount().Address, params.IstanbulConfig{ + Epoch: 10, + ProposerPolicy: 2, + BlockPeriod: 1, + RequestTimeout: 3000, + }) + + // Add balances to developer accounts + genesis.FundAccounts(genesisConfig, env.Accounts().DeveloperAccounts()) + + return genesisConfig, nil +} + +type loadtestEnv struct{} + +func (e loadtestEnv) createEnv(workdir string) (*env.Environment, error) { + envCfg := &env.Config{ + Accounts: env.AccountsConfig{ + Mnemonic: "miss fire behind decide egg buyer honey seven advance uniform profit renew", + NumValidators: 1, + ValidatorsPerGroup: 1, + NumDeveloperAccounts: 10000, + }, + ChainID: big.NewInt(9099000), + } + + env, err := env.New(workdir, envCfg) + if err != nil { + return nil, err + } + + return env, nil +} + +func (e loadtestEnv) createGenesisConfig(env *env.Environment) (*genesis.Config, error) { + genesisConfig := genesis.CreateCommonGenesisConfig(env.Config.ChainID, env.Accounts().AdminAccount().Address, params.IstanbulConfig{ + Epoch: 1000, + ProposerPolicy: 2, + BlockPeriod: 5, + RequestTimeout: 3000, + }) + + // 10 billion gas limit, set super high on purpose + genesisConfig.Blockchain.BlockGasLimit = 1000000000 + + // Add balances to developer accounts + genesis.FundAccounts(genesisConfig, env.Accounts().DeveloperAccounts()) + + genesisConfig.StableToken.InflationFactorUpdatePeriod = 1 * genesis.Year + + // Disable gas price min being updated + genesisConfig.GasPriceMinimum.TargetDensity = fixed.MustNew("0.9999") + genesisConfig.GasPriceMinimum.AdjustmentSpeed = fixed.MustNew("0") + + return genesisConfig, nil +} + +type monorepoEnv struct{} + +func (e monorepoEnv) createEnv(workdir string) (*env.Environment, error) { + envCfg := &env.Config{ + Accounts: env.AccountsConfig{ + Mnemonic: env.MustNewMnemonic(), + NumValidators: 3, + ValidatorsPerGroup: 5, // monorepo uses a single validator group, max group size is 5 + NumDeveloperAccounts: 0, + UseValidatorAsAdmin: true, // monorepo doesn't use the admin account type, uses first validator instead + }, + ChainID: big.NewInt(1000 * (1 + rand.Int63n(9999))), + } + env, err := env.New(workdir, envCfg) + if err != nil { + return nil, err + } + + return env, nil +} + +func (e monorepoEnv) createGenesisConfig(env *env.Environment) (*genesis.Config, error) { + genesisConfig := genesis.CreateCommonGenesisConfig(env.Config.ChainID, env.Accounts().AdminAccount().Address, params.IstanbulConfig{ + Epoch: 10, + ProposerPolicy: 2, + BlockPeriod: 1, + RequestTimeout: 3000, + }) + // To match the 'testing' config in monorepo + genesisConfig.EpochRewards.TargetVotingYieldInitial = fixed.MustNew("0.00016") + genesisConfig.EpochRewards.TargetVotingYieldMax = fixed.MustNew("0.0005") + genesisConfig.EpochRewards.TargetVotingYieldAdjustmentFactor = fixed.MustNew("0.1") + genesisConfig.Reserve.InitialBalance = common.MustBigInt("100000000000000000000000000") // 100M CELO + + // Add balances to validator accounts instead of developer accounts + genesis.FundAccounts(genesisConfig, env.Accounts().ValidatorAccounts()) + + return genesisConfig, nil +}
diff --git go-ethereum/cmd/mycelo/main.go celo/cmd/mycelo/main.go new file mode 100644 index 0000000000000000000000000000000000000000..3271ac569874d9d981555810abca3b532ae919a2 --- /dev/null +++ celo/cmd/mycelo/main.go @@ -0,0 +1,303 @@ +package main + +import ( + "context" + "fmt" + "math/big" + "os" + "path" + "path/filepath" + + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/internal/fileutils" + "github.com/ethereum/go-ethereum/internal/flags" + "golang.org/x/sync/errgroup" + + "github.com/ethereum/go-ethereum/internal/debug" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/mycelo/cluster" + "github.com/ethereum/go-ethereum/mycelo/env" + "github.com/ethereum/go-ethereum/mycelo/loadbot" + "github.com/ethereum/go-ethereum/params" + "gopkg.in/urfave/cli.v1" +) + +var ( + // Git information set by linker when building with ci.go. + gitCommit string + gitDate string + app = &cli.App{ + Name: filepath.Base(os.Args[0]), + Usage: "mycelo", + Version: params.VersionWithCommit(gitCommit, gitDate), + Writer: os.Stdout, + HideVersion: true, + EnableBashCompletion: true, + } +) + +func init() { + // Set up the CLI app. + app.Flags = append(app.Flags, debug.Flags...) + app.Before = func(ctx *cli.Context) error { + return debug.Setup(ctx) + } + app.After = func(ctx *cli.Context) error { + debug.Exit() + return nil + } + app.CommandNotFound = func(ctx *cli.Context, cmd string) { + fmt.Fprintf(os.Stderr, "No such command: %s\n", cmd) + os.Exit(1) + } + // Add subcommands. + app.Commands = []cli.Command{ + createGenesisCommand, + createGenesisConfigCommand, + createGenesisFromConfigCommand, + initValidatorsCommand, + runValidatorsCommand, + // initNodesCommand, + // runNodesCommand, + loadBotCommand, + envCommand, + } + cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate +} + +func main() { + exit(app.Run(os.Args)) +} + +var gethPathFlag = cli.StringFlag{ + Name: "geth", + Usage: "Path to geth binary", +} + +var gethExtraFlagsFlag = cli.StringFlag{ + Name: "extraflags", + Usage: "extra flags to pass to the validators", +} + +var loadTestTPSFlag = cli.IntFlag{ + Name: "tps", + Usage: "Transactions per second to target in the load test", + Value: 20, +} + +var loadTestMaxPendingFlag = cli.UintFlag{ + Name: "maxpending", + Usage: "Maximum number of in flight txs. Set to 0 to disable.", + Value: 200, +} + +var loadTestSkipGasEstimationFlag = cli.BoolFlag{ + Name: "skipgasestimation", + Usage: "Skips estimating gas if true and instead hardcodes a value for the cUSD transfer", +} + +var loadTestMixFeeCurrencyFlag = cli.BoolFlag{ + Name: "mixfeecurrency", + Usage: "Switches between paying for gas in cUSD and CELO", +} + +var initValidatorsCommand = cli.Command{ + Name: "validator-init", + Usage: "Setup all validators nodes", + ArgsUsage: "[envdir]", + Action: validatorInit, + Flags: []cli.Flag{gethPathFlag}, +} + +var runValidatorsCommand = cli.Command{ + Name: "validator-run", + Usage: "Runs the testnet", + ArgsUsage: "[envdir]", + Action: validatorRun, + Flags: []cli.Flag{ + gethPathFlag, + gethExtraFlagsFlag, + cli.BoolFlag{Name: "init", Usage: "Init nodes before running them"}, + }, +} + +// var initNodesCommand = cli.Command{ +// Name: "node-init", +// Usage: "Setup all tx nodes", +// ArgsUsage: "[envdir]", +// Action: nodeInit, +// Flags: []cli.Flag{gethPathFlag}, +// } + +// var runNodesCommand = cli.Command{ +// Name: "run", +// Usage: "Runs the tx nodes", +// ArgsUsage: "[envdir]", +// Action: nodeRun, +// Flags: []cli.Flag{gethPathFlag}, +// } + +var loadBotCommand = cli.Command{ + Name: "load-bot", + Usage: "Runs the load bot on the environment", + ArgsUsage: "[envdir]", + Action: loadBot, + Flags: []cli.Flag{ + loadTestTPSFlag, + loadTestMaxPendingFlag, + loadTestSkipGasEstimationFlag, + loadTestMixFeeCurrencyFlag}, +} + +func readWorkdir(ctx *cli.Context) (string, error) { + if ctx.NArg() != 1 { + fmt.Println("Using current directory as workdir") + dir, err := os.Getwd() + if err != nil { + return "", err + } + return dir, err + } + return ctx.Args().Get(0), nil +} + +func readGethPath(ctx *cli.Context) (string, error) { + gethPath := ctx.String(gethPathFlag.Name) + if gethPath == "" { + gethPath = path.Join(os.Getenv("CELO_BLOCKCHAIN"), "build/bin/geth") + if fileutils.FileExists(gethPath) { + log.Info("Missing --geth flag, using CELO_BLOCKCHAIN derived path", "geth", gethPath) + } else { + return "", fmt.Errorf("Missing --geth flag") + } + } + return gethPath, nil +} + +func readEnv(ctx *cli.Context) (*env.Environment, error) { + workdir, err := readWorkdir(ctx) + if err != nil { + return nil, err + } + return env.Load(workdir) +} + +func validatorInit(ctx *cli.Context) error { + env, err := readEnv(ctx) + if err != nil { + return err + } + + gethPath, err := readGethPath(ctx) + if err != nil { + return err + } + cfg := cluster.Config{GethPath: gethPath} + + cluster := cluster.New(env, cfg) + return cluster.Init() +} + +func validatorRun(ctx *cli.Context) error { + env, err := readEnv(ctx) + if err != nil { + return err + } + + gethPath, err := readGethPath(ctx) + if err != nil { + return err + } + extra := "" + if ctx.IsSet(gethExtraFlagsFlag.Name) { + extra = ctx.String(gethExtraFlagsFlag.Name) + } + cfg := cluster.Config{ + GethPath: gethPath, + ExtraFlags: extra, + } + + cluster := cluster.New(env, cfg) + + if ctx.IsSet("init") { + if err := cluster.Init(); err != nil { + return fmt.Errorf("error running init: %w", err) + } + } + + group, runCtx := errgroup.WithContext(withExitSignals(context.Background())) + + group.Go(func() error { return cluster.Run(runCtx) }) + return group.Wait() +} + +// // TODO: Make this run a full node, not a validator node +// func nodeInit(ctx *cli.Context) error { +// env, err := readEnv(ctx) +// if err != nil { +// return err +// } + +// gethPath, err := readGethPath(ctx) +// if err != nil { +// return err +// } + +// cluster := cluster.New(env, gethPath) +// return cluster.Init() +// } + +// // TODO: Make this run a full node, not a validator node +// func nodeRun(ctx *cli.Context) error { +// env, err := readEnv(ctx) +// if err != nil { +// return err +// } + +// gethPath, err := readGethPath(ctx) +// if err != nil { +// return err +// } + +// cluster := cluster.New(env, gethPath) + +// group, runCtx := errgroup.WithContext(withExitSignals(context.Background())) + +// group.Go(func() error { return cluster.Run(runCtx) }) + +// return group.Wait() +// } + +func loadBot(ctx *cli.Context) error { + env, err := readEnv(ctx) + if err != nil { + return err + } + + verbosityLevel := ctx.GlobalInt("verbosity") + verbose := verbosityLevel >= 4 + + runCtx := context.Background() + + var clients []*ethclient.Client + for i := 0; i < env.Accounts().NumValidators; i++ { + // TODO: Pull all of these values from env.json + client, err := ethclient.Dial(env.ValidatorIPC(i)) + if err != nil { + return err + } + clients = append(clients, client) + } + + return loadbot.Start(runCtx, &loadbot.Config{ + ChainID: env.Config.ChainID, + Accounts: env.Accounts().DeveloperAccounts(), + Amount: big.NewInt(10000000), + TransactionsPerSecond: ctx.Int(loadTestTPSFlag.Name), + Clients: clients, + Verbose: verbose, + MaxPending: ctx.Uint64(loadTestMaxPendingFlag.Name), + SkipGasEstimation: ctx.GlobalBool(loadTestSkipGasEstimationFlag.Name), + MixFeeCurrency: ctx.GlobalBool(loadTestMixFeeCurrencyFlag.Name), + }) +}
diff --git go-ethereum/cmd/mycelo/genesis_commands.go celo/cmd/mycelo/genesis_commands.go new file mode 100644 index 0000000000000000000000000000000000000000..e1e0d71675ec84da89018fb5d07acfa0589fb846 --- /dev/null +++ celo/cmd/mycelo/genesis_commands.go @@ -0,0 +1,270 @@ +package main + +import ( + "fmt" + "math/big" + "os" + "path" + + "github.com/ethereum/go-ethereum/internal/fileutils" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/mycelo/env" + "github.com/ethereum/go-ethereum/mycelo/genesis" + "gopkg.in/urfave/cli.v1" +) + +var templateFlags = []cli.Flag{ + cli.StringFlag{ + Name: "template", + Usage: "Optional template to use (default: local)", + }, + cli.IntFlag{ + Name: "validators", + Usage: "Number of Validators", + }, + cli.IntFlag{ + Name: "dev.accounts", + Usage: "Number of developer accounts", + }, + cli.Uint64Flag{ + Name: "blockperiod", + Usage: "Seconds between each block", + }, + cli.Uint64Flag{ + Name: "epoch", + Usage: "Epoch size", + }, + cli.Int64Flag{ + Name: "blockgaslimit", + Usage: "Block gas limit", + }, + cli.StringFlag{ + Name: "mnemonic", + Usage: "Mnemonic to generate accounts", + }, + cli.Int64Flag{ + Name: "forks.churrito", + Usage: "Optional flag to allow churrito fork overwritting (default: 0, disable: -1)", + }, + cli.Int64Flag{ + Name: "forks.donut", + Usage: "Optional flag to allow donut fork overwritting (default: 0, disable: -1)", + }, + cli.Int64Flag{ + Name: "forks.espresso", + Usage: "Optional flag to allow espresso fork overwritting (default: 0, disable: -1)", + }, + cli.Int64Flag{ + Name: "forks.gfork", + Usage: "Optional flag to allow gfork fork overwritting (default: 0, disable: -1)", + }, +} + +var buildpathFlag = cli.StringFlag{ + Name: "buildpath", + Usage: "Directory where smartcontract truffle build file live", +} + +var newEnvFlag = cli.StringFlag{ + Name: "newenv", + Usage: "Creates a new env in desired folder", +} + +var createGenesisCommand = cli.Command{ + Name: "genesis", + Usage: "Creates genesis.json from a template and overrides", + Action: createGenesis, + ArgsUsage: "", + Flags: append( + []cli.Flag{buildpathFlag, newEnvFlag}, + templateFlags...), +} + +var createGenesisConfigCommand = cli.Command{ + Name: "genesis-config", + Usage: "Creates genesis-config.json from a template and overrides", + Action: createGenesisConfig, + ArgsUsage: "[envdir]", + Flags: append([]cli.Flag{}, templateFlags...), +} + +var createGenesisFromConfigCommand = cli.Command{ + Name: "genesis-from-config", + Usage: "Creates genesis.json from a genesis-config.json and env.json", + ArgsUsage: "[envdir]", + Action: createGenesisFromConfig, + Flags: []cli.Flag{buildpathFlag}, +} + +func readBuildPath(ctx *cli.Context) (string, error) { + buildpath := ctx.String(buildpathFlag.Name) + if buildpath == "" { + buildpath = path.Join(os.Getenv("CELO_MONOREPO"), "packages/protocol/build/contracts") + if fileutils.FileExists(buildpath) { + log.Info("Missing --buildpath flag, using CELO_MONOREPO derived path", "buildpath", buildpath) + } else { + return "", fmt.Errorf("Missing --buildpath flag") + } + } + return buildpath, nil +} + +func envFromTemplate(ctx *cli.Context, workdir string) (*env.Environment, *genesis.Config, error) { + templateString := ctx.String("template") + template := templateFromString(templateString) + env, err := template.createEnv(workdir) + if err != nil { + return nil, nil, err + } + // Env overrides + if ctx.IsSet("validators") { + env.Accounts().NumValidators = ctx.Int("validators") + } + if ctx.IsSet("dev.accounts") { + env.Accounts().NumDeveloperAccounts = ctx.Int("dev.accounts") + } + if ctx.IsSet("mnemonic") { + env.Accounts().Mnemonic = ctx.String("mnemonic") + } + + // Genesis config + genesisConfig, err := template.createGenesisConfig(env) + if err != nil { + return nil, nil, err + } + + // Overrides + if ctx.IsSet("epoch") { + genesisConfig.Istanbul.Epoch = ctx.Uint64("epoch") + } + if ctx.IsSet("blockperiod") { + genesisConfig.Istanbul.BlockPeriod = ctx.Uint64("blockperiod") + } + if ctx.IsSet("blockgaslimit") { + genesisConfig.Blockchain.BlockGasLimit = ctx.Uint64("blockgaslimit") + } + + if ctx.IsSet("forks.churrito") { + churritoBlockNumber := ctx.Int64("forks.churrito") + if churritoBlockNumber < 0 { + genesisConfig.Hardforks.ChurritoBlock = nil + } else { + genesisConfig.Hardforks.ChurritoBlock = big.NewInt(churritoBlockNumber) + } + } + + if ctx.IsSet("forks.donut") { + donutBlockNumber := ctx.Int64("forks.donut") + if donutBlockNumber < 0 { + genesisConfig.Hardforks.DonutBlock = nil + } else { + genesisConfig.Hardforks.DonutBlock = big.NewInt(donutBlockNumber) + } + } + + if ctx.IsSet("forks.espresso") { + espressoBlockNumber := ctx.Int64("forks.espresso") + if espressoBlockNumber < 0 { + genesisConfig.Hardforks.EspressoBlock = nil + } else { + genesisConfig.Hardforks.EspressoBlock = big.NewInt(espressoBlockNumber) + } + } + + if ctx.IsSet("forks.gFork") { + gForkBlockNumber := ctx.Int64("forks.gFork") + if gForkBlockNumber < 0 { + genesisConfig.Hardforks.GForkBlock = nil + } else { + genesisConfig.Hardforks.GForkBlock = big.NewInt(gForkBlockNumber) + } + } + + return env, genesisConfig, nil +} + +func createGenesis(ctx *cli.Context) error { + var workdir string + var err error + if ctx.IsSet(newEnvFlag.Name) { + workdir = ctx.String(newEnvFlag.Name) + if !fileutils.FileExists(workdir) { + os.MkdirAll(workdir, os.ModePerm) + } + } else { + workdir, err = os.Getwd() + if err != nil { + return err + } + } + + env, genesisConfig, err := envFromTemplate(ctx, workdir) + if err != nil { + return err + } + + buildpath, err := readBuildPath(ctx) + if err != nil { + return err + } + + generatedGenesis, err := genesis.GenerateGenesis(env.Accounts(), genesisConfig, buildpath) + if err != nil { + return err + } + + if ctx.IsSet(newEnvFlag.Name) { + if err = env.Save(); err != nil { + return err + } + } + + return env.SaveGenesis(generatedGenesis) +} + +func createGenesisConfig(ctx *cli.Context) error { + workdir, err := readWorkdir(ctx) + if err != nil { + return err + } + + env, genesisConfig, err := envFromTemplate(ctx, workdir) + if err != nil { + return err + } + + err = env.Save() + if err != nil { + return err + } + + return genesisConfig.Save(path.Join(workdir, "genesis-config.json")) +} + +func createGenesisFromConfig(ctx *cli.Context) error { + workdir, err := readWorkdir(ctx) + if err != nil { + return err + } + env, err := env.Load(workdir) + if err != nil { + return err + } + + genesisConfig, err := genesis.LoadConfig(path.Join(workdir, "genesis-config.json")) + if err != nil { + return err + } + + buildpath, err := readBuildPath(ctx) + if err != nil { + return err + } + + genesis, err := genesis.GenerateGenesis(env.Accounts(), genesisConfig, buildpath) + if err != nil { + return err + } + + return env.SaveGenesis(genesis) +}
diff --git go-ethereum/cmd/mycelo/README.md celo/cmd/mycelo/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c645a7d2d14e6fee0cf87ec99e6a3155d1d9031f --- /dev/null +++ celo/cmd/mycelo/README.md @@ -0,0 +1,115 @@ + +# Mycelo + +`mycelo` is a developer utility to easy running celo blockchain testnets and related jobs around testnets. + +Its main advantage over previous solutions is that it's able to create a `genesis.json` where all core conctracts are already deployed in it. Eventually it can be extended to support other cases, like e2e tests, load tests, and other operations. + +## Using mycelo + +There are 2 main use cases for mycelo: + + 1. Run a local tesnet + 2. Create a genesis.json to be used in another testnet that will be run on a CloudProvider/Kubernetes + +### Generating a genesis.json + +Both cases share the need to create the `genesis.json`; to do so run: + +```bash +mycelo genesis --buildpath path/to/protocol/build +``` + +Where `buildpath` is the path to truffle compile output folder. By default it will use `CELO_MONOREPO` environment variable as `$CELO_MONOREPO/packages/protocol/build/contracts`. + +This will create a `genesis.json`. + +If you want to run a local testnet, you'll want to create an environment, for that use: + +```bash +mycelo genesis --newenv path/to/envfolder +``` + +This command will create folder `path/to/envfolder` and write there `genesis.json` and `env.json` + +### Configuring Genesis + +Genesis creation has many configuration options, for that `mycelo` use the concept of templates. + +```bash +mycelo genesis --template=[local|loadtest|monorepo] +``` + +Additionally, you can override template options via command line, chedk `mycelo genesis --help` for options: + +```bash + --validators value Number of Validators (default: 0) + --dev.accounts value Number of developer accounts (default: 0) + --blockperiod value Seconds between each block (default: 0) + --epoch value Epoch size (default: 0) + --mnemonic value Mnemonic to generate accounts +``` + +### Configuring Genesis (Advanced) + +If that's not enough, you can ask mycelo to generate a genesis-config file that you can then customize and use to generate genesis + +```bash +mycelo genesis-config path/to/env +``` + +This will create `path/to/env/env.json` & `path/to/env/genesis-config.json`. + +Next step is to customize those files with your desired options, and then run: + +```bash +mycelo genesis-from-config path/to/env +``` + +This command will read those file, and generate a `genesis.json` on the env folder + + +### Running a local testnet + +Once you've created an environment, and the `genesis.json` you can now run your own local testnet. + +First, you need to configure the nodes (this will run `geth init`, configure `static-nodes`, add validator accounts to the node, etc): + +```bash +mycelo validator-init --geth path/to/geth/binary path/to/env +``` + +**NOTE**: If you don't specify geth binary path. It will attempt to use `$CELO_BLOCKCHAIN/build/bin/geth` + + +And then, run the nodes: + +```bash +mycelo validator-run --geth path/to/geth/binary path/to/env +``` + +This command will run one geth node for each validator as subprocesses. + + +### Running a load bot (Experimental) + +You can run a simple load bot with: + +```bash +mycelo load-bot path/to/env +``` + +This will generate cUSD transfer on each of the developers account of the enviroment. + +This feature is still experimental and needs more work, but it's already usable. + + +## What's missing? + +You tell us! + +Happy Coding!!!!! + + + +
diff --git go-ethereum/cmd/mycelo/utils.go celo/cmd/mycelo/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..99c14d7a98eefd831132edd606b2ae0cce6baa70 --- /dev/null +++ celo/cmd/mycelo/utils.go @@ -0,0 +1,37 @@ +package main + +import ( + "context" + "fmt" + "os" + "os/signal" + "syscall" +) + +func exit(err interface{}) { + if err == nil { + os.Exit(0) + } + fmt.Fprintln(os.Stderr, err) + os.Exit(1) +} + +// withExitSignals returns a context that will stop whenever +// the process receive a SIGINT / SIGTERM +func withExitSignals(parentCtx context.Context) context.Context { + ctx, stop := context.WithCancel(parentCtx) + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + fmt.Println("Press CTRL-C to stop the process") + go func() { + select { + case <-parentCtx.Done(): + case sig := <-sigs: + fmt.Printf("Got Signal, Shutting down... signal: %s", sig.String()) + } + stop() + }() + return ctx +}
diff --git go-ethereum/node/defaults.go celo/node/defaults.go index b1b3e4302e55785dc54daae6683932ca0a35d59a..ed91242e272ad6cfd9f3185028ebdc922e99b9f8 100644 --- go-ethereum/node/defaults.go +++ celo/node/defaults.go @@ -46,10 +46,19 @@ HTTPTimeouts: rpc.DefaultHTTPTimeouts, WSPort: DefaultWSPort, WSModules: []string{"net", "web3"}, GraphQLVirtualHosts: []string{"localhost"}, + Proxy: false, P2P: p2p.Config{ ListenAddr: ":30303", - MaxPeers: 50, + MaxPeers: 175, NAT: nat.Any(), + NetworkId: 1, + }, + ProxyP2P: p2p.Config{ + ListenAddr: ":30503", + MaxPeers: 1, + NetworkId: 1, + NoDiscovery: true, + UseInMemoryNodeDatabase: true, }, }   @@ -61,19 +70,19 @@ home := homeDir() if home != "" { switch runtime.GOOS { case "darwin": - return filepath.Join(home, "Library", "Ethereum") + return filepath.Join(home, "Library", "Celo") case "windows": // We used to put everything in %HOME%\AppData\Roaming, but this caused // problems with non-typical setups. If this fallback location exists and // is non-empty, use it, otherwise DTRT and check %LOCALAPPDATA%. - fallback := filepath.Join(home, "AppData", "Roaming", "Ethereum") + fallback := filepath.Join(home, "AppData", "Roaming", "Celo") appdata := windowsAppData() if appdata == "" || isNonEmptyDir(fallback) { return fallback } - return filepath.Join(appdata, "Ethereum") + return filepath.Join(appdata, "Celo") default: - return filepath.Join(home, ".ethereum") + return filepath.Join(home, ".celo") } } // As we cannot guess a stable location, return empty and handle later
diff --git go-ethereum/node/config.go celo/node/config.go index f6c8547601f079657be374cfa9aac19f72513f79..44197b6e8fa193f510acfb90d2b498671d57264f 100644 --- go-ethereum/node/config.go +++ celo/node/config.go @@ -26,6 +26,7 @@ "runtime" "strings" "sync"   + "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" @@ -40,6 +41,7 @@ datadirDefaultKeyStore = "keystore" // Path within the datadir to the keystore datadirStaticNodes = "static-nodes.json" // Path within the datadir to the static node list datadirTrustedNodes = "trusted-nodes.json" // Path within the datadir to the trusted node list datadirNodeDatabase = "nodes" // Path within the datadir to store the node infos + datadirProxiedNodeDatabase = "proxied-nodes" )   // Config represents a small collection of configuration values to fine tune the @@ -65,8 +67,12 @@ // databases or flat files. This enables ephemeral nodes which can fully reside // in memory. DataDir string   + // Specifies if this node is a proxy + Proxy bool + // Configuration of peer-to-peer networking. P2P p2p.Config + ProxyP2P p2p.Config   // KeyStoreDir is the file system folder that contains private keys. The directory can // be specified as a relative path, in which case it is resolved relative to the @@ -84,6 +90,10 @@ // UseLightweightKDF lowers the memory and CPU requirements of the key store // scrypt KDF at the expense of security. UseLightweightKDF bool `toml:",omitempty"`   + // UsePlaintextKeystore stores keys in plain text without any encryption, + // this should only be used for testing. + UsePlaintextKeystore bool `toml:",omitempty"` + // InsecureUnlockAllowed allows user to unlock accounts in unsafe http environment. InsecureUnlockAllowed bool `toml:",omitempty"`   @@ -91,11 +101,9 @@ // NoUSB disables hardware wallet monitoring and connectivity. // Deprecated: USB monitoring is disabled by default and must be enabled explicitly. NoUSB bool `toml:",omitempty"`   + // TODO: USB hw wallet is no. Does Celo suppor this? // USB enables hardware wallet monitoring and connectivity. USB bool `toml:",omitempty"` - - // SmartCardDaemonPath is the path to the smartcard daemon's socket - SmartCardDaemonPath string `toml:",omitempty"`   // IPCPath is the requested location to place the IPC endpoint. If the path is // a simple file name, it is placed inside the data directory (or on the root @@ -188,8 +196,8 @@ staticNodesWarning bool trustedNodesWarning bool oldGethResourceWarning bool   - // AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC. - AllowUnprotectedTxs bool `toml:",omitempty"` + // // AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC. + // AllowUnprotectedTxs bool `toml:",omitempty"` }   // IPCEndpoint resolves an IPC endpoint based on a configured value, taking into @@ -219,12 +227,20 @@ }   // NodeDB returns the path to the discovery node database. func (c *Config) NodeDB() string { - if c.DataDir == "" { + if c.DataDir == "" || c.P2P.UseInMemoryNodeDatabase { return "" // ephemeral } return c.ResolvePath(datadirNodeDatabase) }   +// NodeDB returns the path to the proxy discovery node database. +func (c *Config) ProxiedNodeDB() string { + if c.DataDir == "" { + return "" // ephemeral + } + return c.ResolvePath(datadirProxiedNodeDatabase) +} + // DefaultIPCEndpoint returns the IPC path used by default. func DefaultIPCEndpoint(clientIdentifier string) string { if clientIdentifier == "" { @@ -323,7 +339,7 @@ // Backwards-compatibility: ensure that data directory files created // by geth 1.4 are used if they exist. if warn, isOld := isOldGethResource[path]; isOld { oldpath := "" - if c.name() == "geth" { + if c.name() == "celo" { oldpath = filepath.Join(c.DataDir, path) } if oldpath != "" && common.FileExist(oldpath) { @@ -406,8 +422,7 @@ // Load the nodes from the config file. var nodelist []string if err := common.LoadJSON(path, &nodelist); err != nil { - log.Error(fmt.Sprintf("Can't load node list file: %v", err)) - return nil + panic(fmt.Sprintf("Can't load node list file: %v", err)) } // Interpret the list as a discovery node array var nodes []*enode.Node @@ -417,15 +432,49 @@ continue } node, err := enode.Parse(enode.ValidSchemes, url) if err != nil { - log.Error(fmt.Sprintf("Node URL %s: %v\n", url, err)) - continue + panic(fmt.Sprintf("Node URL %s: %v\n", url, err)) } nodes = append(nodes, node) } return nodes }   +// GetKeyStore returns a keystore, and if no keystore path was provided in the +// config, it returns the ephemeral path that has been generated for this keystore. +func (c *Config) GetKeyStore() (store *keystore.KeyStore, ephemeralPath string, err error) { + path, err := c.KeyDirConfig() + if err != nil { + return nil, "", fmt.Errorf("failed to get the path for the keystore: %v", err) + } + if path == "" { + // There is no datadir, make an ephemeral one + path, err = ioutil.TempDir("", "go-ethereum-keystore") + if err != nil { + return nil, "", fmt.Errorf("failed make the keystore directory: %v", err) + } + ephemeralPath = path + } else { + err = os.MkdirAll(path, 0700) + if err != nil { + return nil, "", fmt.Errorf("failed make the keystore directory: %v", err) + } + } + + switch { + case c.UseLightweightKDF: + return keystore.NewKeyStore(path, keystore.LightScryptN, keystore.LightScryptP), ephemeralPath, nil + case c.UsePlaintextKeystore: + return keystore.NewPlaintextKeyStore(path), ephemeralPath, nil + } + return keystore.NewKeyStore(path, keystore.StandardScryptN, keystore.StandardScryptP), ephemeralPath, nil + +} + // KeyDirConfig determines the settings for keydirectory +// +// returns the absolute path of the keystore, if KeyStoreDir is +// not set this function will generae a path from the data dir. If neither are +// set this function will return the empty string. func (c *Config) KeyDirConfig() (string, error) { var ( keydir string
diff --git go-ethereum/node/node.go celo/node/node.go index 49623513b5f46d1b8f51848b78c9511a22adb03d..6d8bc5f549688d0dec94347d3a9ba63735589dd7 100644 --- go-ethereum/node/node.go +++ celo/node/node.go @@ -38,7 +38,7 @@ )   // Node is a container on which services can be registered. type Node struct { - eventmux *event.TypeMux + eventmux *event.TypeMux // Event multiplexer used between the services of a stack config *Config accman *accounts.Manager log log.Logger @@ -47,6 +47,7 @@ keyDirTemp bool // If true, key directory will be removed by Stop dirLock fileutil.Releaser // prevents concurrent use of instance directory stop chan struct{} // Channel to wait for termination notifications server *p2p.Server // Currently running P2P networking layer + proxyServer *p2p.Server startStopLock sync.Mutex // Start/Stop are protected by an additional lock state int // Tracks state of node lifecycle   @@ -135,6 +136,22 @@ node.server.Config.TrustedNodes = node.config.TrustedNodes() } if node.server.Config.NodeDatabase == "" { node.server.Config.NodeDatabase = node.config.NodeDB() + } + + if node.config.Proxy { + // Initialize the proxy p2p server. This creates the node key and + // discovery databases. + node.proxyServer = &p2p.Server{Config: conf.ProxyP2P} + node.proxyServer.Config.NoDiscovery = true + // There can only up to 10 peers within the internal network + node.proxyServer.Config.MaxPeers = 10 + node.proxyServer.Config.PrivateKey = node.config.NodeKey() + node.proxyServer.Config.Name = node.config.NodeName() + node.proxyServer.Config.Logger = node.log + if node.proxyServer.Config.NodeDatabase == "" { + node.proxyServer.Config.NodeDatabase = node.config.ProxiedNodeDB() + } + node.log.Info("Proxy Server Config", "node.proxyServer.Config", node.proxyServer.Config.ListenAddr) }   // Check HTTP/WS prefixes are valid. @@ -265,11 +282,22 @@ n.log.Info("Starting peer-to-peer node", "instance", n.server.Name) if err := n.server.Start(); err != nil { return convertFileLockError(err) } + if n.proxyServer != nil { + n.log.Info("Starting proxy peer-to-peer node", "instance", n.proxyServer.Name) + if err := n.proxyServer.Start(); err != nil { + n.server.Stop() + return convertFileLockError(err) + } + } + // start RPC endpoints err := n.startRPC() if err != nil { n.stopRPC() n.server.Stop() + if n.proxyServer != nil { + n.proxyServer.Stop() + } } return err } @@ -299,6 +327,9 @@ }   // Stop p2p networking. n.server.Stop() + if n.proxyServer != nil { + n.proxyServer.Stop() + }   if len(failure.Services) > 0 { return failure @@ -445,6 +476,9 @@ if n.state != initializingState { panic("can't register protocols on running/stopped node") } n.server.Protocols = append(n.server.Protocols, protocols...) + if n.proxyServer != nil { + n.proxyServer.Protocols = append(n.proxyServer.Protocols, protocols...) + } }   // RegisterAPIs registers the APIs a service provides on the node. @@ -503,6 +537,16 @@ n.lock.Lock() defer n.lock.Unlock()   return n.server +} + +// ProxyServer retrieves the currently running proxy P2P network layer. This method is meant +// only to inspect fields of the currently running server. Callers should not +// start or stop the returned server. +func (n *Node) ProxyServer() *p2p.Server { + n.lock.Lock() + defer n.lock.Unlock() + + return n.proxyServer }   // DataDir retrieves the current datadir used by the protocol stack.
diff --git go-ethereum/node/rpcstack_test.go celo/node/rpcstack_test.go index 59d312b259057a499a4fc6d6b9d652c26fa2dbb9..a171fa1e4acea50c56e2ff2d922f4f1c5fef44b2 100644 --- go-ethereum/node/rpcstack_test.go +++ celo/node/rpcstack_test.go @@ -159,21 +159,6 @@ srv.stop() } }   -// TestIsWebsocket tests if an incoming websocket upgrade request is handled properly. -func TestIsWebsocket(t *testing.T) { - r, _ := http.NewRequest("GET", "/", nil) - - assert.False(t, isWebsocket(r)) - r.Header.Set("upgrade", "websocket") - assert.False(t, isWebsocket(r)) - r.Header.Set("connection", "upgrade") - assert.True(t, isWebsocket(r)) - r.Header.Set("connection", "upgrade,keep-alive") - assert.True(t, isWebsocket(r)) - r.Header.Set("connection", " UPGRADE,keep-alive") - assert.True(t, isWebsocket(r)) -} - func Test_checkPath(t *testing.T) { tests := []struct { req *http.Request
diff --git go-ethereum/node/utils_test.go celo/node/utils_test.go index 3da9338f4248725f59b5721f3e107a3bc5cb8e78..eb1402cb1e14366b83e8140a0e27f8a42ab1c4f9 100644 --- go-ethereum/node/utils_test.go +++ celo/node/utils_test.go @@ -47,8 +47,6 @@ stop error   startHook func() stopHook func() - - protocols []p2p.Protocol }   func (s *InstrumentedService) Start() error {
diff --git go-ethereum/node/api.go celo/node/api.go index 0fd297a9f3b68d5ae8b7aedcda85da88f05f7c21..44bfb8fb02b54c3b8fd40fde17f7101ba0e9d5d4 100644 --- go-ethereum/node/api.go +++ celo/node/api.go @@ -26,6 +26,7 @@ "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rpc" ) @@ -74,7 +75,7 @@ node, err := enode.Parse(enode.ValidSchemes, url) if err != nil { return false, fmt.Errorf("invalid enode: %v", err) } - server.AddPeer(node) + server.AddPeer(node, p2p.ExplicitStaticPurpose) return true, nil }   @@ -90,7 +91,7 @@ node, err := enode.Parse(enode.ValidSchemes, url) if err != nil { return false, fmt.Errorf("invalid enode: %v", err) } - server.RemovePeer(node) + server.RemovePeer(node, p2p.ExplicitStaticPurpose) return true, nil }   @@ -105,7 +106,7 @@ node, err := enode.Parse(enode.ValidSchemes, url) if err != nil { return false, fmt.Errorf("invalid enode: %v", err) } - server.AddTrustedPeer(node) + server.AddTrustedPeer(node, p2p.ExplicitTrustedPurpose) return true, nil }   @@ -121,7 +122,7 @@ node, err := enode.Parse(enode.ValidSchemes, url) if err != nil { return false, fmt.Errorf("invalid enode: %v", err) } - server.RemoveTrustedPeer(node) + server.RemoveTrustedPeer(node, p2p.ExplicitTrustedPurpose) return true, nil }   @@ -293,6 +294,17 @@ func (api *privateAdminAPI) StopWS() (bool, error) { api.node.http.stopWS() api.node.ws.stop() return true, nil +} + +// DiscoverTableInfo gives the content of all buckets and ips in the p2p +// discover table +func (api *privateAdminAPI) DiscoverTableInfo() (*discover.TableInfo, error) { + // Make sure the server is running, fail otherwise + server := api.node.Server() + if server == nil { + return nil, ErrNodeStopped + } + return server.DiscoverTableInfo(), nil }   // publicAdminAPI is the collection of administrative API methods exposed over
diff --git go-ethereum/params/config.go celo/params/config.go index 60f16a3c6c6c1392a69bf20cac4dfb88a7c56624..c2e370abb8045b6f7f67970c0777d9a445d8b741 100644 --- go-ethereum/params/config.go +++ celo/params/config.go @@ -27,159 +27,81 @@ )   // Genesis hashes to enforce below configs on. var ( - MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - RopstenGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") - RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177") - GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a") + MainnetGenesisHash = common.HexToHash("0x19ea3339d3c8cda97235bc8293240d5b9dadcdfbb5d4b0b90ee731cac1bd11c3") + AlfajoresGenesisHash = common.HexToHash("0xe423b034e7f0282c1b621f7bbc1cea4316a2a80b1600490769eae77777e4b67e") + BaklavaGenesisHash = common.HexToHash("0xbd1db1803638c0c151cdd10179c0117fedfffa4a3f0f88a8334708a4ea1a5fda") +) + +var ( + MainnetNetworkId = uint64(42220) + BaklavaNetworkId = uint64(62320) + AlfajoresNetworkId = uint64(44787) )   +var NetworkIdHelp = fmt.Sprintf("Mainnet=%v, Baklava=%v, Alfajores=%v", MainnetNetworkId, BaklavaNetworkId, AlfajoresNetworkId) + // TrustedCheckpoints associates each known checkpoint with the genesis hash of // the chain it belongs to. -var TrustedCheckpoints = map[common.Hash]*TrustedCheckpoint{ - MainnetGenesisHash: MainnetTrustedCheckpoint, - RopstenGenesisHash: RopstenTrustedCheckpoint, - RinkebyGenesisHash: RinkebyTrustedCheckpoint, - GoerliGenesisHash: GoerliTrustedCheckpoint, -} +var TrustedCheckpoints = map[common.Hash]*TrustedCheckpoint{}   // CheckpointOracles associates each known checkpoint oracles with the genesis hash of // the chain it belongs to. -var CheckpointOracles = map[common.Hash]*CheckpointOracleConfig{ - MainnetGenesisHash: MainnetCheckpointOracle, - RopstenGenesisHash: RopstenCheckpointOracle, - RinkebyGenesisHash: RinkebyCheckpointOracle, - GoerliGenesisHash: GoerliCheckpointOracle, -} +var CheckpointOracles = map[common.Hash]*CheckpointOracleConfig{}   var ( // MainnetChainConfig is the chain parameters to run a node on the main network. MainnetChainConfig = &ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(1_150_000), - DAOForkBlock: big.NewInt(1_920_000), - DAOForkSupport: true, - EIP150Block: big.NewInt(2_463_000), - EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - EIP155Block: big.NewInt(2_675_000), - EIP158Block: big.NewInt(2_675_000), - ByzantiumBlock: big.NewInt(4_370_000), - ConstantinopleBlock: big.NewInt(7_280_000), - PetersburgBlock: big.NewInt(7_280_000), - IstanbulBlock: big.NewInt(9_069_000), - MuirGlacierBlock: big.NewInt(9_200_000), - BerlinBlock: big.NewInt(12_244_000), - LondonBlock: big.NewInt(12_965_000), - Ethash: new(EthashConfig), - } - - // MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network. - MainnetTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 395, - SectionHead: common.HexToHash("0xbfca95b8c1de014e252288e9c32029825fadbff58285f5b54556525e480dbb5b"), - CHTRoot: common.HexToHash("0x2ccf3dbb58eb6375e037fdd981ca5778359e4b8fa0270c2878b14361e64161e7"), - BloomRoot: common.HexToHash("0x2d46ec65a6941a2dc1e682f8f81f3d24192021f492fdf6ef0fdd51acb0f4ba0f"), - } - - // MainnetCheckpointOracle contains a set of configs for the main network oracle. - MainnetCheckpointOracle = &CheckpointOracleConfig{ - Address: common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), - Signers: []common.Address{ - common.HexToAddress("0x1b2C260efc720BE89101890E4Db589b44E950527"), // Peter - common.HexToAddress("0x78d1aD571A1A09D60D9BBf25894b44e4C8859595"), // Martin - common.HexToAddress("0x286834935f4A8Cfb4FF4C77D5770C2775aE2b0E7"), // Zsolt - common.HexToAddress("0xb86e2B0Ab5A4B1373e40c51A7C712c70Ba2f9f8E"), // Gary - common.HexToAddress("0x0DF8fa387C602AE62559cC4aFa4972A7045d6707"), // Guillaume - }, - Threshold: 2, - } - - // RopstenChainConfig contains the chain parameters to run a node on the Ropsten test network. - RopstenChainConfig = &ChainConfig{ - ChainID: big.NewInt(3), + ChainID: big.NewInt(int64(MainnetNetworkId)), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: true, EIP150Block: big.NewInt(0), - EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"), - EIP155Block: big.NewInt(10), - EIP158Block: big.NewInt(10), - ByzantiumBlock: big.NewInt(1_700_000), - ConstantinopleBlock: big.NewInt(4_230_000), - PetersburgBlock: big.NewInt(4_939_394), - IstanbulBlock: big.NewInt(6_485_846), - MuirGlacierBlock: big.NewInt(7_117_117), - BerlinBlock: big.NewInt(9_812_189), - LondonBlock: big.NewInt(10_499_401), - Ethash: new(EthashConfig), - } - - // RopstenTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network. - RopstenTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 329, - SectionHead: common.HexToHash("0xe66f7038333a01fb95dc9ea03e5a2bdaf4b833cdcb9e393b9127e013bd64d39b"), - CHTRoot: common.HexToHash("0x1b0c883338ac0d032122800c155a2e73105fbfebfaa50436893282bc2d9feec5"), - BloomRoot: common.HexToHash("0x3cc98c88d283bf002378246f22c653007655cbcea6ed89f98d739f73bd341a01"), - } - - // RopstenCheckpointOracle contains a set of configs for the Ropsten test network oracle. - RopstenCheckpointOracle = &CheckpointOracleConfig{ - Address: common.HexToAddress("0xEF79475013f154E6A65b54cB2742867791bf0B84"), - Signers: []common.Address{ - common.HexToAddress("0x32162F3581E88a5f62e8A61892B42C46E2c18f7b"), // Peter - common.HexToAddress("0x78d1aD571A1A09D60D9BBf25894b44e4C8859595"), // Martin - common.HexToAddress("0x286834935f4A8Cfb4FF4C77D5770C2775aE2b0E7"), // Zsolt - common.HexToAddress("0xb86e2B0Ab5A4B1373e40c51A7C712c70Ba2f9f8E"), // Gary - common.HexToAddress("0x0DF8fa387C602AE62559cC4aFa4972A7045d6707"), // Guillaume + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + ChurritoBlock: big.NewInt(6774000), + DonutBlock: big.NewInt(6774000), + EspressoBlock: big.NewInt(11838440), + GForkBlock: nil, + Istanbul: &IstanbulConfig{ + Epoch: 17280, + ProposerPolicy: 2, + BlockPeriod: 5, + RequestTimeout: 3000, }, - Threshold: 2, }   - // RinkebyChainConfig contains the chain parameters to run a node on the Rinkeby test network. - RinkebyChainConfig = &ChainConfig{ - ChainID: big.NewInt(4), - HomesteadBlock: big.NewInt(1), + // BaklavaChainConfig contains the chain parameters to run a node on the Baklava test network. + BaklavaChainConfig = &ChainConfig{ + ChainID: big.NewInt(int64(BaklavaNetworkId)), + HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: true, - EIP150Block: big.NewInt(2), - EIP150Hash: common.HexToHash("0x9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"), - EIP155Block: big.NewInt(3), - EIP158Block: big.NewInt(3), - ByzantiumBlock: big.NewInt(1_035_301), - ConstantinopleBlock: big.NewInt(3_660_663), - PetersburgBlock: big.NewInt(4_321_234), - IstanbulBlock: big.NewInt(5_435_345), - MuirGlacierBlock: nil, - BerlinBlock: big.NewInt(8_290_928), - LondonBlock: big.NewInt(8_897_988), - Clique: &CliqueConfig{ - Period: 15, - Epoch: 30000, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + ChurritoBlock: big.NewInt(2719099), + DonutBlock: big.NewInt(5002000), + EspressoBlock: big.NewInt(9195000), + GForkBlock: nil, + Istanbul: &IstanbulConfig{ + Epoch: 17280, + ProposerPolicy: 2, + BlockPeriod: 5, + RequestTimeout: 3000, }, }   - // RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network. - RinkebyTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 276, - SectionHead: common.HexToHash("0xea89a4b04e3da9bd688e316f8de669396b6d4a38a19d2cd96a00b70d58b836aa"), - CHTRoot: common.HexToHash("0xd6889d0bf6673c0d2c1cf6e9098a6fe5b30888a115b6112796aa8ee8efc4a723"), - BloomRoot: common.HexToHash("0x6009a9256b34b8bde3a3f094afb647ba5d73237546017b9025d64ac1ff54c47c"), - } - - // RinkebyCheckpointOracle contains a set of configs for the Rinkeby test network oracle. - RinkebyCheckpointOracle = &CheckpointOracleConfig{ - Address: common.HexToAddress("0xebe8eFA441B9302A0d7eaECc277c09d20D684540"), - Signers: []common.Address{ - common.HexToAddress("0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3"), // Peter - common.HexToAddress("0x78d1aD571A1A09D60D9BBf25894b44e4C8859595"), // Martin - common.HexToAddress("0x286834935f4A8Cfb4FF4C77D5770C2775aE2b0E7"), // Zsolt - common.HexToAddress("0xb86e2B0Ab5A4B1373e40c51A7C712c70Ba2f9f8E"), // Gary - }, - Threshold: 2, - } - - // GoerliChainConfig contains the chain parameters to run a node on the Görli test network. - GoerliChainConfig = &ChainConfig{ - ChainID: big.NewInt(5), + // AlfajoresChainConfig contains the chain parameters to run a node on the Alfajores test network. + AlfajoresChainConfig = &ChainConfig{ + ChainID: big.NewInt(int64(AlfajoresNetworkId)), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: true, @@ -189,52 +111,44 @@ EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(1_561_651), - MuirGlacierBlock: nil, - BerlinBlock: big.NewInt(4_460_644), - LondonBlock: big.NewInt(5_062_605), - Clique: &CliqueConfig{ - Period: 15, - Epoch: 30000, + IstanbulBlock: big.NewInt(0), + ChurritoBlock: big.NewInt(4960000), + DonutBlock: big.NewInt(4960000), + EspressoBlock: big.NewInt(9472000), + GForkBlock: nil, + Istanbul: &IstanbulConfig{ + Epoch: 17280, + ProposerPolicy: 2, + BlockPeriod: 5, + RequestTimeout: 10000, }, }   - // GoerliTrustedCheckpoint contains the light client trusted checkpoint for the Görli test network. - GoerliTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 160, - SectionHead: common.HexToHash("0xb5a666c790dc35a5613d04ebba8ba47a850b45a15d9b95ad7745c35ae034b5a5"), - CHTRoot: common.HexToHash("0x6b4e00df52bdc38fa6c26c8ef595c2ad6184963ea36ab08ee744af460aa735e1"), - BloomRoot: common.HexToHash("0x8fa88f5e50190cb25243aeee262a1a9e4434a06f8d455885dcc1b5fc48c33836"), - } + DeveloperChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &IstanbulConfig{ + Epoch: 300, + ProposerPolicy: 0, + RequestTimeout: 1000, + BlockPeriod: 1, + }, true, false}   - // GoerliCheckpointOracle contains a set of configs for the Goerli test network oracle. - GoerliCheckpointOracle = &CheckpointOracleConfig{ - Address: common.HexToAddress("0x18CA0E045F0D772a851BC7e48357Bcaab0a0795D"), - Signers: []common.Address{ - common.HexToAddress("0x4769bcaD07e3b938B7f43EB7D278Bc7Cb9efFb38"), // Peter - common.HexToAddress("0x78d1aD571A1A09D60D9BBf25894b44e4C8859595"), // Martin - common.HexToAddress("0x286834935f4A8Cfb4FF4C77D5770C2775aE2b0E7"), // Zsolt - common.HexToAddress("0xb86e2B0Ab5A4B1373e40c51A7C712c70Ba2f9f8E"), // Gary - common.HexToAddress("0x0DF8fa387C602AE62559cC4aFa4972A7045d6707"), // Guillaume - }, - Threshold: 2, - } + IstanbulTestChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &IstanbulConfig{ + Epoch: 300, + ProposerPolicy: 0, + RequestTimeout: 1000, + BlockPeriod: 1, + }, true, false}   - // AllEthashProtocolChanges contains every protocol change (EIPs) introduced - // and accepted by the Ethereum core developers into the Ethash consensus. - // - // This configuration is intentionally not using keyed fields to force anyone - // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil} + IstanbulEHFTestChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), &IstanbulConfig{ + Epoch: 300, + ProposerPolicy: 0, + RequestTimeout: 1000, + BlockPeriod: 1, + }, true, false}   - // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced - // and accepted by the Ethereum core developers into the Clique consensus. - // - // This configuration is intentionally not using keyed fields to force anyone - // adding flags to the config to also have to set these fields. - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} - - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &IstanbulConfig{ + Epoch: 30000, + ProposerPolicy: 0, + }, true, true} TestRules = TestChainConfig.Rules(new(big.Int)) )   @@ -291,6 +205,8 @@ // // ChainConfig is stored in the database on a per block basis. This means // that any network, identified by its genesis block, can have its own // set of configuration options. +// This configuration is intentionally not using keyed fields to force anyone +// adding flags to the config to also have to set these fields. type ChainConfig struct { ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection   @@ -310,48 +226,54 @@ ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated) PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople) IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) - MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated) - BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin) - LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london) - - CatalystBlock *big.Int `json:"catalystBlock,omitempty"` // Catalyst switch block (nil = no fork, 0 = already on catalyst) - - // Various consensus engines - Ethash *EthashConfig `json:"ethash,omitempty"` - Clique *CliqueConfig `json:"clique,omitempty"` -} + // MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated) + // BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin) + // LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london) + ChurritoBlock *big.Int `json:"churritoBlock,omitempty"` // Churrito switch block (nil = no fork, 0 = already activated) + DonutBlock *big.Int `json:"donutBlock,omitempty"` // Donut switch block (nil = no fork, 0 = already activated) + EspressoBlock *big.Int `json:"espressoBlock,omitempty"` // Espresso switch block (nil = no fork, 0 = already activated) + GForkBlock *big.Int `json:"gForkBlock,omitempty"` // G Fork switch block (nil = no fork, 0 = already activated)   -// EthashConfig is the consensus engine configs for proof-of-work based sealing. -type EthashConfig struct{} + Istanbul *IstanbulConfig `json:"istanbul,omitempty"` + // This does not belong here but passing it to every function is not possible since that breaks + // some implemented interfaces and introduces churn across the geth codebase. + FullHeaderChainAvailable bool // False for lightest Sync mode, true otherwise   -// String implements the stringer interface, returning the consensus engine details. -func (c *EthashConfig) String() string { - return "ethash" + // Requests mock engine if true + Faker bool `json:"faker,omitempty"` }   -// CliqueConfig is the consensus engine configs for proof-of-authority based sealing. -type CliqueConfig struct { - Period uint64 `json:"period"` // Number of seconds between blocks to enforce +// IstanbulConfig is the consensus engine configs for Istanbul based sealing. +type IstanbulConfig struct { Epoch uint64 `json:"epoch"` // Epoch length to reset votes and checkpoint + ProposerPolicy uint64 `json:"policy"` // The policy for proposer selection + BlockPeriod uint64 `json:"blockperiod,omitempty"` // Default minimum difference between two consecutive block's timestamps in second + + // The base timeout for each Istanbul round in milliseconds. The first + // round will have a timeout of exactly this and subsequent rounds will + // have timeouts of this + additional time that increases with round + // number. + RequestTimeout uint64 `json:"requesttimeout,omitempty"` }   // String implements the stringer interface, returning the consensus engine details. -func (c *CliqueConfig) String() string { - return "clique" +func (c *IstanbulConfig) String() string { + return "istanbul" }   // String implements the fmt.Stringer interface. func (c *ChainConfig) String() string { var engine interface{} - switch { - case c.Ethash != nil: - engine = c.Ethash - case c.Clique != nil: - engine = c.Clique - default: - engine = "unknown" + if !c.Faker { + if c.Istanbul != nil { + engine = c.Istanbul + } else { + engine = "unknown" + } + } else { + engine = "MockEngine" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v Churrito: %v, Donut: %v, Espresso: %v, GFork: %v, Engine: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -363,9 +285,13 @@ c.ByzantiumBlock, c.ConstantinopleBlock, c.PetersburgBlock, c.IstanbulBlock, - c.MuirGlacierBlock, - c.BerlinBlock, - c.LondonBlock, + // c.MuirGlacierBlock, + // c.BerlinBlock, + // c.LondonBlock, + c.ChurritoBlock, + c.DonutBlock, + c.EspressoBlock, + c.GForkBlock, engine, ) } @@ -405,11 +331,6 @@ func (c *ChainConfig) IsConstantinople(num *big.Int) bool { return isForked(c.ConstantinopleBlock, num) }   -// IsMuirGlacier returns whether num is either equal to the Muir Glacier (EIP-2384) fork block or greater. -func (c *ChainConfig) IsMuirGlacier(num *big.Int) bool { - return isForked(c.MuirGlacierBlock, num) -} - // IsPetersburg returns whether num is either // - equal to or greater than the PetersburgBlock fork block, // - OR is nil, and Constantinople is active @@ -422,19 +343,24 @@ func (c *ChainConfig) IsIstanbul(num *big.Int) bool { return isForked(c.IstanbulBlock, num) }   -// IsBerlin returns whether num is either equal to the Berlin fork block or greater. -func (c *ChainConfig) IsBerlin(num *big.Int) bool { - return isForked(c.BerlinBlock, num) +// IsChurrito returns whether num represents a block number after the Churrito fork +func (c *ChainConfig) IsChurrito(num *big.Int) bool { + return isForked(c.ChurritoBlock, num) }   -// IsLondon returns whether num is either equal to the London fork block or greater. -func (c *ChainConfig) IsLondon(num *big.Int) bool { - return isForked(c.LondonBlock, num) +// IsDonut returns whether num represents a block number after the Donut fork +func (c *ChainConfig) IsDonut(num *big.Int) bool { + return isForked(c.DonutBlock, num) +} + +// IsEspresso returns whether num represents a block number after the Espresso fork +func (c *ChainConfig) IsEspresso(num *big.Int) bool { + return isForked(c.EspressoBlock, num) }   -// IsCatalyst returns whether num is either equal to the Merge fork block or greater. -func (c *ChainConfig) IsCatalyst(num *big.Int) bool { - return isForked(c.CatalystBlock, num) +// IsGFork returns whether num represents a block number after the G fork +func (c *ChainConfig) IsGFork(num *big.Int) bool { + return isForked(c.GForkBlock, num) }   // CheckCompatible checks whether scheduled fork transitions have been imported @@ -466,7 +392,6 @@ } var lastFork fork for _, cur := range []fork{ {name: "homesteadBlock", block: c.HomesteadBlock}, - {name: "daoForkBlock", block: c.DAOForkBlock, optional: true}, {name: "eip150Block", block: c.EIP150Block}, {name: "eip155Block", block: c.EIP155Block}, {name: "eip158Block", block: c.EIP158Block}, @@ -474,9 +399,10 @@ {name: "byzantiumBlock", block: c.ByzantiumBlock}, {name: "constantinopleBlock", block: c.ConstantinopleBlock}, {name: "petersburgBlock", block: c.PetersburgBlock}, {name: "istanbulBlock", block: c.IstanbulBlock}, - {name: "muirGlacierBlock", block: c.MuirGlacierBlock, optional: true}, - {name: "berlinBlock", block: c.BerlinBlock}, - {name: "londonBlock", block: c.LondonBlock}, + {name: "churritoBlock", block: c.ChurritoBlock}, + {name: "donutBlock", block: c.DonutBlock}, + {name: "espressoBlock", block: c.EspressoBlock}, + {name: "gForkBlock", block: c.GForkBlock}, } { if lastFork.name != "" { // Next one must be higher number @@ -537,14 +463,22 @@ } if isForkIncompatible(c.IstanbulBlock, newcfg.IstanbulBlock, head) { return newCompatError("Istanbul fork block", c.IstanbulBlock, newcfg.IstanbulBlock) } - if isForkIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, head) { - return newCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock) + + return c.checkCeloCompatible(newcfg, head) +} + +func (c *ChainConfig) checkCeloCompatible(newcfg *ChainConfig, head *big.Int) *ConfigCompatError { + if isForkIncompatible(c.ChurritoBlock, newcfg.ChurritoBlock, head) { + return newCompatError("Churrito fork block", c.ChurritoBlock, newcfg.ChurritoBlock) + } + if isForkIncompatible(c.DonutBlock, newcfg.DonutBlock, head) { + return newCompatError("Donut fork block", c.DonutBlock, newcfg.DonutBlock) } - if isForkIncompatible(c.BerlinBlock, newcfg.BerlinBlock, head) { - return newCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock) + if isForkIncompatible(c.EspressoBlock, newcfg.EspressoBlock, head) { + return newCompatError("Espresso fork block", c.EspressoBlock, newcfg.EspressoBlock) } - if isForkIncompatible(c.LondonBlock, newcfg.LondonBlock, head) { - return newCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock) + if isForkIncompatible(c.GForkBlock, newcfg.GForkBlock, head) { + return newCompatError("G fork block", c.GForkBlock, newcfg.GForkBlock) } return nil } @@ -613,7 +547,8 @@ type Rules struct { ChainID *big.Int IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool - IsBerlin, IsLondon, IsCatalyst bool + // IsBerlin, IsLondon, IsCatalyst bool + IsChurrito, IsDonut, IsEspresso, IsGFork bool }   // Rules ensures c's ChainID is not nil. @@ -632,8 +567,12 @@ IsByzantium: c.IsByzantium(num), IsConstantinople: c.IsConstantinople(num), IsPetersburg: c.IsPetersburg(num), IsIstanbul: c.IsIstanbul(num), - IsBerlin: c.IsBerlin(num), - IsLondon: c.IsLondon(num), - IsCatalyst: c.IsCatalyst(num), + // IsBerlin: c.IsBerlin(num), + // IsLondon: c.IsLondon(num), + // IsCatalyst: c.IsCatalyst(num), + IsChurrito: c.IsChurrito(num), + IsDonut: c.IsDonut(num), + IsEspresso: c.IsEspresso(num), + IsGFork: c.IsGFork(num), } }
diff --git go-ethereum/params/bootnodes.go celo/params/bootnodes.go index b3f7fa93ee36c28d03d494ab3908caf42f0deff5..a90516eccb4be6889defa2696e39b657753f4f46 100644 --- go-ethereum/params/bootnodes.go +++ celo/params/bootnodes.go @@ -20,89 +20,31 @@ import "github.com/ethereum/go-ethereum/common"   // MainnetBootnodes are the enode URLs of the P2P bootstrap nodes running on // the main Ethereum network. -var MainnetBootnodes = []string{ - // Ethereum Foundation Go Bootnodes - "enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303", // bootnode-aws-ap-southeast-1-001 - "enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303", // bootnode-aws-us-east-1-001 - "enode://ca6de62fce278f96aea6ec5a2daadb877e51651247cb96ee310a318def462913b653963c155a0ef6c7d50048bba6e6cea881130857413d9f50a621546b590758@34.255.23.113:30303", // bootnode-aws-eu-west-1-001 - "enode://279944d8dcd428dffaa7436f25ca0ca43ae19e7bcf94a8fb7d1641651f92d121e972ac2e8f381414b80cc8e5555811c2ec6e1a99bb009b3f53c4c69923e11bd8@35.158.244.151:30303", // bootnode-aws-eu-central-1-001 - "enode://8499da03c47d637b20eee24eec3c356c9a2e6148d6fe25ca195c7949ab8ec2c03e3556126b0d7ed644675e78c4318b08691b7b57de10e5f0d40d05b09238fa0a@52.187.207.27:30303", // bootnode-azure-australiaeast-001 - "enode://103858bdb88756c71f15e9b5e09b56dc1be52f0a5021d46301dbbfb7e130029cc9d0d6f73f693bc29b665770fff7da4d34f3c6379fe12721b5d7a0bcb5ca1fc1@191.234.162.198:30303", // bootnode-azure-brazilsouth-001 - "enode://715171f50508aba88aecd1250af392a45a330af91d7b90701c436b618c86aaa1589c9184561907bebbb56439b8f8787bc01f49a7c77276c58c1b09822d75e8e8@52.231.165.108:30303", // bootnode-azure-koreasouth-001 - "enode://5d6d7cd20d6da4bb83a1d28cadb5d409b64edf314c0335df658c1a54e32c7c4a7ab7823d57c39b6a757556e68ff1df17c748b698544a55cb488b52479a92b60f@104.42.217.25:30303", // bootnode-azure-westus-001 -} - -// RopstenBootnodes are the enode URLs of the P2P bootstrap nodes running on the -// Ropsten test network. -var RopstenBootnodes = []string{ - "enode://30b7ab30a01c124a6cceca36863ece12c4f5fa68e3ba9b0b51407ccc002eeed3b3102d20a88f1c1d3c3154e2449317b8ef95090e77b312d5cc39354f86d5d606@52.176.7.10:30303", // US-Azure geth - "enode://865a63255b3bb68023b6bffd5095118fcc13e79dcf014fe4e47e065c350c7cc72af2e53eff895f11ba1bbb6a2b33271c1116ee870f266618eadfc2e78aa7349c@52.176.100.77:30303", // US-Azure parity - "enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303", // Parity - "enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303", // @gpip -} - -// RinkebyBootnodes are the enode URLs of the P2P bootstrap nodes running on the -// Rinkeby test network. -var RinkebyBootnodes = []string{ - "enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303", // IE - "enode://343149e4feefa15d882d9fe4ac7d88f885bd05ebb735e547f12e12080a9fa07c8014ca6fd7f373123488102fe5e34111f8509cf0b7de3f5b44339c9f25e87cb8@52.3.158.184:30303", // INFURA - "enode://b6b28890b006743680c52e64e0d16db57f28124885595fa03a562be1d2bf0f3a1da297d56b13da25fb992888fd556d4c1a27b1f39d531bde7de1921c90061cc6@159.89.28.211:30303", // AKASHA -} - -// GoerliBootnodes are the enode URLs of the P2P bootstrap nodes running on the -// Görli test network. -var GoerliBootnodes = []string{ - // Upstream bootnodes - "enode://011f758e6552d105183b1761c5e2dea0111bc20fd5f6422bc7f91e0fabbec9a6595caf6239b37feb773dddd3f87240d99d859431891e4a642cf2a0a9e6cbb98a@51.141.78.53:30303", - "enode://176b9417f511d05b6b2cf3e34b756cf0a7096b3094572a8f6ef4cdcb9d1f9d00683bf0f83347eebdf3b81c3521c2332086d9592802230bf528eaf606a1d9677b@13.93.54.137:30303", - "enode://46add44b9f13965f7b9875ac6b85f016f341012d84f975377573800a863526f4da19ae2c620ec73d11591fa9510e992ecc03ad0751f53cc02f7c7ed6d55c7291@94.237.54.114:30313", - "enode://b5948a2d3e9d486c4d75bf32713221c2bd6cf86463302339299bd227dc2e276cd5a1c7ca4f43a0e9122fe9af884efed563bd2a1fd28661f3b5f5ad7bf1de5949@18.218.250.66:30303", - - // Ethereum Foundation bootnode - "enode://a61215641fb8714a373c80edbfa0ea8878243193f57c96eeb44d0bc019ef295abd4e044fd619bfc4c59731a73fb79afe84e9ab6da0c743ceb479cbb6d263fa91@3.11.147.67:30303", +var MainnetBootnodes = []string{"enode://5c9a3afb564b48cc2fa2e06b76d0c5d8f6910e1930ea7d0930213a0cbc20450434cd442f6483688eff436ad14dc29cb90c9592cc5c1d27ca62f28d4d8475d932@34.82.79.155:30301"}   - // Goerli Initiative bootnodes - "enode://a869b02cec167211fb4815a82941db2e7ed2936fd90e78619c53eb17753fcf0207463e3419c264e2a1dd8786de0df7e68cf99571ab8aeb7c4e51367ef186b1dd@51.15.116.226:30303", - "enode://807b37ee4816ecf407e9112224494b74dd5933625f655962d892f2f0f02d7fbbb3e2a94cf87a96609526f30c998fd71e93e2f53015c558ffc8b03eceaf30ee33@51.15.119.157:30303", - "enode://a59e33ccd2b3e52d578f1fbd70c6f9babda2650f0760d6ff3b37742fdcdfdb3defba5d56d315b40c46b70198c7621e63ffa3f987389c7118634b0fefbbdfa7fd@51.15.119.157:40303", -} +// BaklavaBootnodes are the enode URLs of the P2P bootstrap nodes running on the +// Baklava test network. +var BaklavaBootnodes = []string{"enode://5aaf10664b12431c250597e980aacd7d5373cae00f128be5b00364344bb96bce7555b50973664bddebd1cb7a6d3fb927bec81527f80e22a26fa373c375fcdefc@35.247.103.141:30301"}   -var V5Bootnodes = []string{ - // Teku team's bootnode - "enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2Gxb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNlY3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA", - "enr:-KG4QDyytgmE4f7AnvW-ZaUOIi9i79qX4JwjRAiXBZCU65wOfBu-3Nb5I7b_Rmg3KCOcZM_C3y5pg7EBU5XGrcLTduQEhGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQ2_DUbiXNlY3AyNTZrMaEDKnz_-ps3UUOfHWVYaskI5kWYO_vtYMGYCQRAR3gHDouDdGNwgiMog3VkcIIjKA", - // Prylab team's bootnodes - "enr:-Ku4QImhMc1z8yCiNJ1TyUxdcfNucje3BGwEHzodEZUan8PherEo4sF7pPHPSIB1NNuSg5fZy7qFsjmUKs2ea1Whi0EBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQOVphkDqal4QzPMksc5wnpuC3gvSC8AfbFOnZY_On34wIN1ZHCCIyg", - "enr:-Ku4QP2xDnEtUXIjzJ_DhlCRN9SN99RYQPJL92TMlSv7U5C1YnYLjwOQHgZIUXw6c-BvRg2Yc2QsZxxoS_pPRVe0yK8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMeFF5GrS7UZpAH2Ly84aLK-TyvH-dRo0JM1i8yygH50YN1ZHCCJxA", - "enr:-Ku4QPp9z1W4tAO8Ber_NQierYaOStqhDqQdOPY3bB3jDgkjcbk6YrEnVYIiCBbTxuar3CzS528d2iE7TdJsrL-dEKoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMw5fqqkw2hHC4F5HZZDPsNmPdB1Gi8JPQK7pRc9XHh-oN1ZHCCKvg", - // Lighthouse team's bootnodes - "enr:-IS4QLkKqDMy_ExrpOEWa59NiClemOnor-krjp4qoeZwIw2QduPC-q7Kz4u1IOWf3DDbdxqQIgC4fejavBOuUPy-HE4BgmlkgnY0gmlwhCLzAHqJc2VjcDI1NmsxoQLQSJfEAHZApkm5edTCZ_4qps_1k_ub2CxHFxi-gr2JMIN1ZHCCIyg", - "enr:-IS4QDAyibHCzYZmIYZCjXwU9BqpotWmv2BsFlIq1V31BwDDMJPFEbox1ijT5c2Ou3kvieOKejxuaCqIcjxBjJ_3j_cBgmlkgnY0gmlwhAMaHiCJc2VjcDI1NmsxoQJIdpj_foZ02MXz4It8xKD7yUHTBx7lVFn3oeRP21KRV4N1ZHCCIyg", - // EF bootnodes - "enr:-Ku4QHqVeJ8PPICcWk1vSn_XcSkjOkNiTg6Fmii5j6vUQgvzMc9L1goFnLKgXqBJspJjIsB91LTOleFmyWWrFVATGngBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhAMRHkWJc2VjcDI1NmsxoQKLVXFOhp2uX6jeT0DvvDpPcU8FWMjQdR4wMuORMhpX24N1ZHCCIyg", - "enr:-Ku4QG-2_Md3sZIAUebGYT6g0SMskIml77l6yR-M_JXc-UdNHCmHQeOiMLbylPejyJsdAPsTHJyjJB2sYGDLe0dn8uYBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhBLY-NyJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyg", - "enr:-Ku4QPn5eVhcoF1opaFEvg1b6JNFD2rqVkHQ8HApOKK61OIcIXD127bKWgAtbwI7pnxx6cDyk_nI88TrZKQaGMZj0q0Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDayLMaJc2VjcDI1NmsxoQK2sBOLGcUb4AwuYzFuAVCaNHA-dy24UuEKkeFNgCVCsIN1ZHCCIyg", - "enr:-Ku4QEWzdnVtXc2Q0ZVigfCGggOVB2Vc1ZCPEc6j21NIFLODSJbvNaef1g4PxhPwl_3kax86YPheFUSLXPRs98vvYsoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDZBrP2Jc2VjcDI1NmsxoQM6jr8Rb1ktLEsVcKAPa08wCsKUmvoQ8khiOl_SLozf9IN1ZHCCIyg", +// AlfajoresBootnodes are the enode URLs of the P2P bootstrap nodes running on the +// Alfajores test network. +var AlfajoresBootnodes = []string{ + "enode://e99a883d0b7d0bacb84cde98c4729933b49adbc94e718b77fdb31779c7ed9da6c49236330a9ae096f42bcbf6e803394229120201704b7a4a3ae8004993fa0876@34.83.92.243:30303", + "enode://b3b42a9a6ef1125006f39b95850c624422eadb6340ac86e4023e47d5452914bb3d17340f9455cd1cdd0d246058b1fec2d3c500eeddbeafa49abd71f8f144b04e@35.199.145.251:30303", + "enode://af5677afe5bf99a00bdb86d0f80f948b2e25f8978867b38cba8e860a6426507cbc37e15900f798305ceff6b7ac7f4057195827274d6b5f6a7e547ce512ff26ba@35.230.21.97:30303", + "enode://02d18a52c4e097c12412ac3da9a9af24745cff182306e21fb1d3d691fe0c25f65c60586302b933bb9ec300b7fb00ed719d26a4d57af91d447691bac2b2d778af@35.247.59.117:30303", + "enode://05977f6b7d3e16a99d27b714f8a029a006e41ec7732167d373dd920d31f72b3a1776650798d8763560854369d36867e9564dad13b4b60a90c347feeb491d83a9@34.83.42.50:30303", + "enode://822366c6b9f80c3f3fdf7748209399ddd888bd54882958f6c75d05b8156c6274f54c8a1a6b468c3e85cade93a7dee2a2b701ccfc820b7669507d4bee855ebbf1@35.247.105.237:30303", + "enode://5bb26439782fb6f9d0d997f907968f4ada618d49d83a2e6d202a107d7b17c67c680877ee733e2f92656714697e6f5eb0da25f26180c3e13d5dc39dc037160651@34.83.234.156:30303", + "enode://29373f661cbea23f4f566d54470fae6ef5419c2a88aa52f306628df2d143f86807c02fd19b3d2d6d2e2a98d99a2db44643c6274e3aadd3632bd744a8be498768@34.105.6.12:30303", + "enode://cc11ee6b035c8948dfaca5b09d676e28b0986585dac7a819376f12a8ee8b6b0ffd31907bb0d8bda27ef0a2296ee614d31773c7c5ea4333a121d80e0ba9bae801@34.83.51.187:30303", + "enode://703cf979becdc501c4221090296fe75299cb9520f19a344098154c14c7133ebf6b649dad7f3f42947ad96312930bea5380a8ff86faa5a3795b0b6cc483adcfc8@35.230.23.131:30303", }   -const dnsPrefix = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@" - // KnownDNSNetwork returns the address of a public DNS-based node list for the given // genesis hash and protocol. See https://github.com/ethereum/discv4-dns-lists for more // information. func KnownDNSNetwork(genesis common.Hash, protocol string) string { - var net string - switch genesis { - case MainnetGenesisHash: - net = "mainnet" - case RopstenGenesisHash: - net = "ropsten" - case RinkebyGenesisHash: - net = "rinkeby" - case GoerliGenesisHash: - net = "goerli" - default: - return "" - } - return dnsPrefix + protocol + "." + net + ".ethdisco.net" + // For now, Celo doesn't use DNS discovery, so urls are blank + return "" }
(deleted)
+0
-158
diff --git go-ethereum/params/dao.go celo/params/dao.go deleted file mode 100644 index 1428ce9f8970320348fdf17bb95930452a860df9..0000000000000000000000000000000000000000 --- go-ethereum/params/dao.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package params - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" -) - -// DAOForkBlockExtra is the block header extra-data field to set for the DAO fork -// point and a number of consecutive blocks to allow fast/light syncers to correctly -// pick the side they want ("dao-hard-fork"). -var DAOForkBlockExtra = common.FromHex("0x64616f2d686172642d666f726b") - -// DAOForkExtraRange is the number of consecutive blocks from the DAO fork point -// to override the extra-data in to prevent no-fork attacks. -var DAOForkExtraRange = big.NewInt(10) - -// DAORefundContract is the address of the refund contract to send DAO balances to. -var DAORefundContract = common.HexToAddress("0xbf4ed7b27f1d666546e30d74d50d173d20bca754") - -// DAODrainList is the list of accounts whose full balances will be moved into a -// refund contract at the beginning of the dao-fork block. -func DAODrainList() []common.Address { - return []common.Address{ - common.HexToAddress("0xd4fe7bc31cedb7bfb8a345f31e668033056b2728"), - common.HexToAddress("0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425"), - common.HexToAddress("0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f"), - common.HexToAddress("0xecd135fa4f61a655311e86238c92adcd779555d2"), - common.HexToAddress("0x1975bd06d486162d5dc297798dfc41edd5d160a7"), - common.HexToAddress("0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6"), - common.HexToAddress("0x319f70bab6845585f412ec7724b744fec6095c85"), - common.HexToAddress("0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936"), - common.HexToAddress("0x5c8536898fbb74fc7445814902fd08422eac56d0"), - common.HexToAddress("0x6966ab0d485353095148a2155858910e0965b6f9"), - common.HexToAddress("0x779543a0491a837ca36ce8c635d6154e3c4911a6"), - common.HexToAddress("0x2a5ed960395e2a49b1c758cef4aa15213cfd874c"), - common.HexToAddress("0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5"), - common.HexToAddress("0x9c50426be05db97f5d64fc54bf89eff947f0a321"), - common.HexToAddress("0x200450f06520bdd6c527622a273333384d870efb"), - common.HexToAddress("0xbe8539bfe837b67d1282b2b1d61c3f723966f049"), - common.HexToAddress("0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb"), - common.HexToAddress("0xf1385fb24aad0cd7432824085e42aff90886fef5"), - common.HexToAddress("0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091"), - common.HexToAddress("0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd"), - common.HexToAddress("0x51e0ddd9998364a2eb38588679f0d2c42653e4a6"), - common.HexToAddress("0x627a0a960c079c21c34f7612d5d230e01b4ad4c7"), - common.HexToAddress("0xf0b1aa0eb660754448a7937c022e30aa692fe0c5"), - common.HexToAddress("0x24c4d950dfd4dd1902bbed3508144a54542bba94"), - common.HexToAddress("0x9f27daea7aca0aa0446220b98d028715e3bc803d"), - common.HexToAddress("0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90"), - common.HexToAddress("0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b"), - common.HexToAddress("0x63ed5a272de2f6d968408b4acb9024f4cc208ebf"), - common.HexToAddress("0x6f6704e5a10332af6672e50b3d9754dc460dfa4d"), - common.HexToAddress("0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6"), - common.HexToAddress("0x492ea3bb0f3315521c31f273e565b868fc090f17"), - common.HexToAddress("0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00"), - common.HexToAddress("0x9ea779f907f0b315b364b0cfc39a0fde5b02a416"), - common.HexToAddress("0xceaeb481747ca6c540a000c1f3641f8cef161fa7"), - common.HexToAddress("0xcc34673c6c40e791051898567a1222daf90be287"), - common.HexToAddress("0x579a80d909f346fbfb1189493f521d7f48d52238"), - common.HexToAddress("0xe308bd1ac5fda103967359b2712dd89deffb7973"), - common.HexToAddress("0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c"), - common.HexToAddress("0xac1ecab32727358dba8962a0f3b261731aad9723"), - common.HexToAddress("0x4fd6ace747f06ece9c49699c7cabc62d02211f75"), - common.HexToAddress("0x440c59b325d2997a134c2c7c60a8c61611212bad"), - common.HexToAddress("0x4486a3d68fac6967006d7a517b889fd3f98c102b"), - common.HexToAddress("0x9c15b54878ba618f494b38f0ae7443db6af648ba"), - common.HexToAddress("0x27b137a85656544b1ccb5a0f2e561a5703c6a68f"), - common.HexToAddress("0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241"), - common.HexToAddress("0x23b75c2f6791eef49c69684db4c6c1f93bf49a50"), - common.HexToAddress("0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b"), - common.HexToAddress("0xb9637156d330c0d605a791f1c31ba5890582fe1c"), - common.HexToAddress("0x6131c42fa982e56929107413a9d526fd99405560"), - common.HexToAddress("0x1591fc0f688c81fbeb17f5426a162a7024d430c2"), - common.HexToAddress("0x542a9515200d14b68e934e9830d91645a980dd7a"), - common.HexToAddress("0xc4bbd073882dd2add2424cf47d35213405b01324"), - common.HexToAddress("0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4"), - common.HexToAddress("0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb"), - common.HexToAddress("0x3ba4d81db016dc2890c81f3acec2454bff5aada5"), - common.HexToAddress("0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab"), - common.HexToAddress("0xe4ae1efdfc53b73893af49113d8694a057b9c0d1"), - common.HexToAddress("0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5"), - common.HexToAddress("0x0737a6b837f97f46ebade41b9bc3e1c509c85c53"), - common.HexToAddress("0x97f43a37f595ab5dd318fb46e7a155eae057317a"), - common.HexToAddress("0x52c5317c848ba20c7504cb2c8052abd1fde29d03"), - common.HexToAddress("0x4863226780fe7c0356454236d3b1c8792785748d"), - common.HexToAddress("0x5d2b2e6fcbe3b11d26b525e085ff818dae332479"), - common.HexToAddress("0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c"), - common.HexToAddress("0x057b56736d32b86616a10f619859c6cd6f59092a"), - common.HexToAddress("0x9aa008f65de0b923a2a4f02012ad034a5e2e2192"), - common.HexToAddress("0x304a554a310c7e546dfe434669c62820b7d83490"), - common.HexToAddress("0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79"), - common.HexToAddress("0x4deb0033bb26bc534b197e61d19e0733e5679784"), - common.HexToAddress("0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a"), - common.HexToAddress("0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b"), - common.HexToAddress("0x4fa802324e929786dbda3b8820dc7834e9134a2a"), - common.HexToAddress("0x9da397b9e80755301a3b32173283a91c0ef6c87e"), - common.HexToAddress("0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6"), - common.HexToAddress("0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9"), - common.HexToAddress("0x5dc28b15dffed94048d73806ce4b7a4612a1d48f"), - common.HexToAddress("0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76"), - common.HexToAddress("0x12e626b0eebfe86a56d633b9864e389b45dcb260"), - common.HexToAddress("0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7"), - common.HexToAddress("0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5"), - common.HexToAddress("0xd164b088bd9108b60d0ca3751da4bceb207b0782"), - common.HexToAddress("0x6231b6d0d5e77fe001c2a460bd9584fee60d409b"), - common.HexToAddress("0x1cba23d343a983e9b5cfd19496b9a9701ada385f"), - common.HexToAddress("0xa82f360a8d3455c5c41366975bde739c37bfeb8a"), - common.HexToAddress("0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339"), - common.HexToAddress("0x005f5cee7a43331d5a3d3eec71305925a62f34b6"), - common.HexToAddress("0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d"), - common.HexToAddress("0xd131637d5275fd1a68a3200f4ad25c71a2a9522e"), - common.HexToAddress("0xbc07118b9ac290e4622f5e77a0853539789effbe"), - common.HexToAddress("0x47e7aa56d6bdf3f36be34619660de61275420af8"), - common.HexToAddress("0xacd87e28b0c9d1254e868b81cba4cc20d9a32225"), - common.HexToAddress("0xadf80daec7ba8dcf15392f1ac611fff65d94f880"), - common.HexToAddress("0x5524c55fb03cf21f549444ccbecb664d0acad706"), - common.HexToAddress("0x40b803a9abce16f50f36a77ba41180eb90023925"), - common.HexToAddress("0xfe24cdd8648121a43a7c86d289be4dd2951ed49f"), - common.HexToAddress("0x17802f43a0137c506ba92291391a8a8f207f487d"), - common.HexToAddress("0x253488078a4edf4d6f42f113d1e62836a942cf1a"), - common.HexToAddress("0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915"), - common.HexToAddress("0xb136707642a4ea12fb4bae820f03d2562ebff487"), - common.HexToAddress("0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940"), - common.HexToAddress("0xf14c14075d6c4ed84b86798af0956deef67365b5"), - common.HexToAddress("0xca544e5c4687d109611d0f8f928b53a25af72448"), - common.HexToAddress("0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c"), - common.HexToAddress("0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7"), - common.HexToAddress("0x6d87578288b6cb5549d5076a207456a1f6a63dc0"), - common.HexToAddress("0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e"), - common.HexToAddress("0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6"), - common.HexToAddress("0x2b3455ec7fedf16e646268bf88846bd7a2319bb2"), - common.HexToAddress("0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a"), - common.HexToAddress("0xd343b217de44030afaa275f54d31a9317c7f441e"), - common.HexToAddress("0x84ef4b2357079cd7a7c69fd7a37cd0609a679106"), - common.HexToAddress("0xda2fef9e4a3230988ff17df2165440f37e8b1708"), - common.HexToAddress("0xf4c64518ea10f995918a454158c6b61407ea345c"), - common.HexToAddress("0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97"), - common.HexToAddress("0xbb9bc244d798123fde783fcc1c72d3bb8c189413"), - common.HexToAddress("0x807640a13483f8ac783c557fcdf27be11ea4ac7a"), - } -}
diff --git go-ethereum/params/celo.go celo/params/celo.go new file mode 100644 index 0000000000000000000000000000000000000000..27fbeef3c4b98999f64d19c13cf3567e180d43f5 --- /dev/null +++ celo/params/celo.go @@ -0,0 +1,22 @@ +package params + +import "github.com/ethereum/go-ethereum/common/math" + +// Scale factor for the solidity fixidity library +var Fixidity1 = math.BigPow(10, 24) + +const ( + DefaultGasLimit uint64 = 20000000 // Gas limit of the blocks before BlockchainParams contract is loaded. +) + +// Celo precompiled contracts +const ( + GetValidatorGas uint64 = 1000 // Cost of reading a validator's address. + GetValidatorBLSGas uint64 = 1000 // Cost of reading a validator's BLS public key. + GetEpochSizeGas uint64 = 10 // Cost of querying the number of blocks in an epoch. + GetBlockNumberFromHeaderGas uint64 = 10 // Cost of decoding a block header. + HashHeaderGas uint64 = 10 // Cost of hashing a block header. + GetParentSealBitmapGas uint64 = 100 // Cost of reading the parent seal bitmap from the chain. + // May take a bit more time with 100 validators, need to bench that + GetVerifiedSealBitmapGas uint64 = 350000 // Cost of verifying the seal on a given RLP encoded header. +)
diff --git go-ethereum/params/config_test.go celo/params/config_test.go index af1e33a1a4ac4170457509a58fc6aa50028982a5..378c4571fcf79df8fcc8b293688a02d494dc9e31 100644 --- go-ethereum/params/config_test.go +++ celo/params/config_test.go @@ -29,8 +29,8 @@ head uint64 wantErr *ConfigCompatError } tests := []test{ - {stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, head: 0, wantErr: nil}, - {stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, head: 100, wantErr: nil}, + {stored: IstanbulTestChainConfig, new: IstanbulTestChainConfig, head: 0, wantErr: nil}, + {stored: IstanbulTestChainConfig, new: IstanbulTestChainConfig, head: 100, wantErr: nil}, { stored: &ChainConfig{EIP150Block: big.NewInt(10)}, new: &ChainConfig{EIP150Block: big.NewInt(20)}, @@ -38,7 +38,7 @@ head: 9, wantErr: nil, }, { - stored: AllEthashProtocolChanges, + stored: IstanbulTestChainConfig, new: &ChainConfig{HomesteadBlock: nil}, head: 3, wantErr: &ConfigCompatError{ @@ -49,7 +49,7 @@ RewindTo: 0, }, }, { - stored: AllEthashProtocolChanges, + stored: IstanbulTestChainConfig, new: &ChainConfig{HomesteadBlock: big.NewInt(1)}, head: 3, wantErr: &ConfigCompatError{
diff --git go-ethereum/params/version.go celo/params/version.go index 4e25d0d37970a337be50f65b3ec2db3b8493bc9b..7f822245d01195b981fe2763bc86c9037aaf4aa3 100644 --- go-ethereum/params/version.go +++ celo/params/version.go @@ -20,11 +20,15 @@ import ( "fmt" )   +// On master, the version should be the UPCOMING one and "unstable" +// e.g. if the latest release was v1.3.2, master should be 1.4.0-unstable +// On release branches, it should be a beta or stable. For example: +// "1.3.0-beta", "1.3.0-beta.2", etc. and then "1.3.0-stable", "1.3.1-stable", etc. const ( VersionMajor = 1 // Major version component of the current release - VersionMinor = 10 // Minor version component of the current release - VersionPatch = 9 // Patch version component of the current release - VersionMeta = "stable" // Version metadata to append to the version string + VersionMinor = 6 // Minor version component of the current release + VersionPatch = 0 // Patch version component of the current release + VersionMeta = "unstable" // Version metadata to append to the version string )   // Version holds the textual version string.
diff --git go-ethereum/params/protocol_params.go celo/params/protocol_params.go index 572955091e3b4f68ed3a641f30fca2c87570dea9..d0cf6981c02166e0dc7686ed31c5a23c3eb97158 100644 --- go-ethereum/params/protocol_params.go +++ celo/params/protocol_params.go @@ -16,13 +16,10 @@ // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.   package params   -import "math/big" - const ( - GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations. - MinGasLimit uint64 = 5000 // Minimum the gas limit may ever be. - GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block. - + // GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations. + // MinGasLimit uint64 = 5000 // Minimum the gas limit may ever be. + // GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block. MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis. ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added. @@ -57,14 +54,17 @@ SstoreSetGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero SstoreResetGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else SstoreClearsScheduleRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot   - ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST - ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST + // ColdAccountAccessCostEIP2929 (2600 -> 900), ColdSloadCostEIP2929 (2100 -> 800) are modified according to CIP-0048 + // Links: https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0048.md + ColdAccountAccessCostEIP2929 = uint64(900) // COLD_ACCOUNT_ACCESS_COST + ColdSloadCostEIP2929 = uint64(800) // COLD_SLOAD_COST WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST   // In EIP-2200: SstoreResetGas was 5000. // In EIP-2929: SstoreResetGas was changed to '5000 - COLD_SLOAD_COST'. // In EIP-3529: SSTORE_CLEARS_SCHEDULE is defined as SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST - // Which becomes: 5000 - 2100 + 1900 = 4800 + // Which becomes: 5000 - 2100 + 1900 = 4800 (ethereum) + // Which becomes: 5000 - 800 + 1900 = 6100 (celo) SstoreClearsScheduleRefundEIP3529 uint64 = SstoreResetGasEIP2200 - ColdSloadCostEIP2929 + TxAccessListStorageKeyGas   JumpdestGas uint64 = 1 // Once per JUMPDEST operation. @@ -118,11 +118,11 @@ // not exist. This logic is similar to call. // Introduced in Tangerine Whistle (Eip 150) CreateBySelfdestructGas uint64 = 25000   - BaseFeeChangeDenominator = 8 // Bounds the amount the base fee can change between blocks. - ElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have. - InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks. + // BaseFeeChangeDenominator = 8 // Bounds the amount the base fee can change between blocks. + // ElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have. + // InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.   - MaxCodeSize = 24576 // Maximum bytecode to permit for a contract + MaxCodeSize = 65536 // Maximum bytecode to permit for a contract (2^16)   // Precompiled contract gas prices   @@ -145,13 +145,12 @@ Bn256PairingPerPointGasIstanbul uint64 = 34000 // Per-point price for an elliptic curve pairing check   Bls12381G1AddGas uint64 = 600 // Price for BLS12-381 elliptic curve G1 point addition Bls12381G1MulGas uint64 = 12000 // Price for BLS12-381 elliptic curve G1 point scalar multiplication - Bls12381G2AddGas uint64 = 4500 // Price for BLS12-381 elliptic curve G2 point addition - Bls12381G2MulGas uint64 = 55000 // Price for BLS12-381 elliptic curve G2 point scalar multiplication - Bls12381PairingBaseGas uint64 = 115000 // Base gas price for BLS12-381 elliptic curve pairing check - Bls12381PairingPerPairGas uint64 = 23000 // Per-point pair gas price for BLS12-381 elliptic curve pairing check + Bls12381G2AddGas uint64 = 800 // Price for BLS12-381 elliptic curve G2 point addition + Bls12381G2MulGas uint64 = 45000 // Price for BLS12-381 elliptic curve G2 point scalar multiplication + Bls12381PairingBaseGas uint64 = 65000 // Base gas price for BLS12-381 elliptic curve pairing check + Bls12381PairingPerPairGas uint64 = 43000 // Per-point pair gas price for BLS12-381 elliptic curve pairing check Bls12381MapG1Gas uint64 = 5500 // Gas price for BLS12-381 mapping field element to G1 operation - Bls12381MapG2Gas uint64 = 110000 // Gas price for BLS12-381 mapping field element to G2 operation - + Bls12381MapG2Gas uint64 = 75000 // Gas price for BLS12-381 mapping field element to G2 operation // The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529, // up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529 RefundQuotient uint64 = 2 @@ -161,9 +160,9 @@ // Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations var Bls12381MultiExpDiscountTable = [128]uint64{1200, 888, 764, 641, 594, 547, 500, 453, 438, 423, 408, 394, 379, 364, 349, 334, 330, 326, 322, 318, 314, 310, 306, 302, 298, 294, 289, 285, 281, 277, 273, 269, 268, 266, 265, 263, 262, 260, 259, 257, 256, 254, 253, 251, 250, 248, 247, 245, 244, 242, 241, 239, 238, 236, 235, 233, 232, 231, 229, 228, 226, 225, 223, 222, 221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213, 212, 211, 211, 210, 209, 208, 208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199, 198, 197, 196, 196, 195, 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185, 184, 183, 182, 182, 181, 180, 179, 179, 178, 177, 176, 176, 175, 174}   -var ( - DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations. - GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block. - MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. - DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. -) +// var ( +// DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations. +// GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block. +// MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. +// DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. +// )
diff --git go-ethereum/rpc/celo_additions.go celo/rpc/celo_additions.go new file mode 100644 index 0000000000000000000000000000000000000000..07a5ed56f4fbb09dda0b2a64575c7813f8cb1606 --- /dev/null +++ celo/rpc/celo_additions.go @@ -0,0 +1,16 @@ +package rpc + +import ( + "fmt" +) + +func (bnh *BlockNumberOrHash) String() string { + if bnh.BlockHash != nil { + return bnh.BlockHash.String() + } + canonical := "" + if bnh.RequireCanonical { + canonical = " (canonical)" + } + return fmt.Sprintf("%d%s", bnh.BlockNumber, canonical) +}
diff --git go-ethereum/rpc/http.go celo/rpc/http.go index 7a57fabac9b42e0c238842b5780f772b917f4950..b332a04516b041e1fe7fe6ce6d1337bcb12f724a 100644 --- go-ethereum/rpc/http.go +++ celo/rpc/http.go @@ -169,11 +169,17 @@ body, err := json.Marshal(msg) if err != nil { return nil, err } + req, err := http.NewRequestWithContext(ctx, "POST", hc.url, ioutil.NopCloser(bytes.NewReader(body))) if err != nil { return nil, err } req.ContentLength = int64(len(body)) + + // And also add the same thing to `Request.GetBody`, which allows + // `net/http` to get a new body in cases like a redirect. + getBodyReader := func() io.ReadCloser { return ioutil.NopCloser(bytes.NewReader(body)) } + req.GetBody = func() (io.ReadCloser, error) { return getBodyReader(), nil }   // set headers hc.mu.Lock() @@ -240,9 +246,9 @@ // All checks passed, create a codec that reads directly from the request body // until EOF, writes the response to w, and orders the server to process a // single request. ctx := r.Context() - ctx = context.WithValue(ctx, "remote", r.RemoteAddr) - ctx = context.WithValue(ctx, "scheme", r.Proto) - ctx = context.WithValue(ctx, "local", r.Host) + ctx = context.WithValue(ctx, "remote", r.RemoteAddr) //lint:ignore SA1029 TODO + ctx = context.WithValue(ctx, "scheme", r.Proto) //lint:ignore SA1029 TODO + ctx = context.WithValue(ctx, "local", r.Host) //lint:ignore SA1029 TODO if ua := r.Header.Get("User-Agent"); ua != "" { ctx = context.WithValue(ctx, "User-Agent", ua) }
diff --git go-ethereum/rpc/types.go celo/rpc/types.go index 12a972097f2336158df05827bad023591870a513..eebb935f5ffba56102f8c132b09e4beaadf0309f 100644 --- go-ethereum/rpc/types.go +++ celo/rpc/types.go @@ -63,6 +63,27 @@ LatestBlockNumber = BlockNumber(-1) EarliestBlockNumber = BlockNumber(0) )   +type RPCTransaction struct { + BlockHash *common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From common.Address `json:"from"` + Gas hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + FeeCurrency *common.Address `json:"feeCurrency"` + GatewayFeeRecipient *common.Address `json:"gatewayFeeRecipient"` + GatewayFee *hexutil.Big `json:"gatewayFee"` + Hash common.Hash `json:"hash"` + Input hexutil.Bytes `json:"input"` + Nonce hexutil.Uint64 `json:"nonce"` + To *common.Address `json:"to"` + TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` + Value *hexutil.Big `json:"value"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` + EthCompatible bool `json:"ethCompatible"` +} + // UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: // - "latest", "earliest" or "pending" as string arguments // - the block number
diff --git go-ethereum/shared/signer/signed_data.go celo/shared/signer/signed_data.go new file mode 100644 index 0000000000000000000000000000000000000000..3656e1cb5ddbfb76f52d6e2e3fd7387e888824a2 --- /dev/null +++ celo/shared/signer/signed_data.go @@ -0,0 +1,709 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package signer + +import ( + "bytes" + "errors" + "fmt" + "math/big" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "unicode" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" +) + +type SigFormat struct { + Mime string + ByteVersion byte +} + +type TypedData struct { + Types Types `json:"types"` + PrimaryType string `json:"primaryType"` + Domain TypedDataDomain `json:"domain"` + Message TypedDataMessage `json:"message"` +} + +type Type struct { + Name string `json:"name"` + Type string `json:"type"` +} + +func (t *Type) isArray() bool { + return strings.HasSuffix(t.Type, "[]") +} + +// typeName returns the canonical name of the type. If the type is 'Person[]', then +// this method returns 'Person' +func (t *Type) typeName() string { + if strings.HasSuffix(t.Type, "[]") { + return strings.TrimSuffix(t.Type, "[]") + } + return t.Type +} + +func (t *Type) isReferenceType() bool { + if len(t.Type) == 0 { + return false + } + // Reference types must have a leading uppercase character + return unicode.IsUpper([]rune(t.Type)[0]) +} + +type Types map[string][]Type + +type TypedDataMessage = map[string]interface{} + +type TypedDataDomain struct { + Name string `json:"name"` + Version string `json:"version"` + ChainId *math.HexOrDecimal256 `json:"chainId"` + VerifyingContract string `json:"verifyingContract"` + Salt string `json:"salt"` +} + +var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`) + +// HashStruct generates a keccak256 hash of the encoding of the provided data +func (typedData *TypedData) HashStruct(primaryType string, data TypedDataMessage) (hexutil.Bytes, error) { + encodedData, err := typedData.EncodeData(primaryType, data, 1) + if err != nil { + return nil, err + } + return crypto.Keccak256(encodedData), nil +} + +// Dependencies returns an array of custom types ordered by their hierarchical reference tree +func (typedData *TypedData) Dependencies(primaryType string, found []string) []string { + includes := func(arr []string, str string) bool { + for _, obj := range arr { + if obj == str { + return true + } + } + return false + } + + if includes(found, primaryType) { + return found + } + if typedData.Types[primaryType] == nil { + return found + } + found = append(found, primaryType) + for _, field := range typedData.Types[primaryType] { + for _, dep := range typedData.Dependencies(field.Type, found) { + if !includes(found, dep) { + found = append(found, dep) + } + } + } + return found +} + +// EncodeType generates the following encoding: +// `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"` +// +// each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name +func (typedData *TypedData) EncodeType(primaryType string) hexutil.Bytes { + // Get dependencies primary first, then alphabetical + deps := typedData.Dependencies(primaryType, []string{}) + if len(deps) > 0 { + slicedDeps := deps[1:] + sort.Strings(slicedDeps) + deps = append([]string{primaryType}, slicedDeps...) + } + + // Format as a string with fields + var buffer bytes.Buffer + for _, dep := range deps { + buffer.WriteString(dep) + buffer.WriteString("(") + for _, obj := range typedData.Types[dep] { + buffer.WriteString(obj.Type) + buffer.WriteString(" ") + buffer.WriteString(obj.Name) + buffer.WriteString(",") + } + buffer.Truncate(buffer.Len() - 1) + buffer.WriteString(")") + } + return buffer.Bytes() +} + +// TypeHash creates the keccak256 hash of the data +func (typedData *TypedData) TypeHash(primaryType string) hexutil.Bytes { + return crypto.Keccak256(typedData.EncodeType(primaryType)) +} + +// EncodeData generates the following encoding: +// `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)` +// +// each encoded member is 32-byte long +func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}, depth int) (hexutil.Bytes, error) { + if err := typedData.validate(); err != nil { + return nil, err + } + + buffer := bytes.Buffer{} + + // Verify extra data + if exp, got := len(typedData.Types[primaryType]), len(data); exp < got { + return nil, fmt.Errorf("there is extra data provided in the message (%d < %d)", exp, got) + } + + // Add typehash + buffer.Write(typedData.TypeHash(primaryType)) + + // Add field contents. Structs and arrays have special handlers. + for _, field := range typedData.Types[primaryType] { + encType := field.Type + encValue := data[field.Name] + if encType[len(encType)-1:] == "]" { + arrayValue, ok := encValue.([]interface{}) + if !ok { + return nil, dataMismatchError(encType, encValue) + } + + arrayBuffer := bytes.Buffer{} + parsedType := strings.Split(encType, "[")[0] + for _, item := range arrayValue { + if typedData.Types[parsedType] != nil { + mapValue, ok := item.(map[string]interface{}) + if !ok { + return nil, dataMismatchError(parsedType, item) + } + encodedData, err := typedData.EncodeData(parsedType, mapValue, depth+1) + if err != nil { + return nil, err + } + arrayBuffer.Write(encodedData) + } else { + bytesValue, err := typedData.EncodePrimitiveValue(parsedType, item, depth) + if err != nil { + return nil, err + } + arrayBuffer.Write(bytesValue) + } + } + + buffer.Write(crypto.Keccak256(arrayBuffer.Bytes())) + } else if typedData.Types[field.Type] != nil { + mapValue, ok := encValue.(map[string]interface{}) + if !ok { + return nil, dataMismatchError(encType, encValue) + } + encodedData, err := typedData.EncodeData(field.Type, mapValue, depth+1) + if err != nil { + return nil, err + } + buffer.Write(crypto.Keccak256(encodedData)) + } else { + byteValue, err := typedData.EncodePrimitiveValue(encType, encValue, depth) + if err != nil { + return nil, err + } + buffer.Write(byteValue) + } + } + return buffer.Bytes(), nil +} + +// Attempt to parse bytes in different formats: byte array, hex string, hexutil.Bytes. +func parseBytes(encType interface{}) ([]byte, bool) { + switch v := encType.(type) { + case []byte: + return v, true + case hexutil.Bytes: + return v, true + case string: + bytes, err := hexutil.Decode(v) + if err != nil { + return nil, false + } + return bytes, true + default: + return nil, false + } +} + +func parseInteger(encType string, encValue interface{}) (*big.Int, error) { + var ( + length int + signed = strings.HasPrefix(encType, "int") + b *big.Int + ) + if encType == "int" || encType == "uint" { + length = 256 + } else { + lengthStr := "" + if strings.HasPrefix(encType, "uint") { + lengthStr = strings.TrimPrefix(encType, "uint") + } else { + lengthStr = strings.TrimPrefix(encType, "int") + } + atoiSize, err := strconv.Atoi(lengthStr) + if err != nil { + return nil, fmt.Errorf("invalid size on integer: %v", lengthStr) + } + length = atoiSize + } + switch v := encValue.(type) { + case *math.HexOrDecimal256: + b = (*big.Int)(v) + case string: + var hexIntValue math.HexOrDecimal256 + if err := hexIntValue.UnmarshalText([]byte(v)); err != nil { + return nil, err + } + b = (*big.Int)(&hexIntValue) + case float64: + // JSON parses non-strings as float64. Fail if we cannot + // convert it losslessly + if float64(int64(v)) == v { + b = big.NewInt(int64(v)) + } else { + return nil, fmt.Errorf("invalid float value %v for type %v", v, encType) + } + } + if b == nil { + return nil, fmt.Errorf("invalid integer value %v/%v for type %v", encValue, reflect.TypeOf(encValue), encType) + } + if b.BitLen() > length { + return nil, fmt.Errorf("integer larger than '%v'", encType) + } + if !signed && b.Sign() == -1 { + return nil, fmt.Errorf("invalid negative value for unsigned type %v", encType) + } + return b, nil +} + +// EncodePrimitiveValue deals with the primitive values found +// while searching through the typed data +func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interface{}, depth int) ([]byte, error) { + switch encType { + case "address": + stringValue, ok := encValue.(string) + if !ok || !common.IsHexAddress(stringValue) { + return nil, dataMismatchError(encType, encValue) + } + retval := make([]byte, 32) + copy(retval[12:], common.HexToAddress(stringValue).Bytes()) + return retval, nil + case "bool": + boolValue, ok := encValue.(bool) + if !ok { + return nil, dataMismatchError(encType, encValue) + } + if boolValue { + return math.PaddedBigBytes(common.Big1, 32), nil + } + return math.PaddedBigBytes(common.Big0, 32), nil + case "string": + strVal, ok := encValue.(string) + if !ok { + return nil, dataMismatchError(encType, encValue) + } + return crypto.Keccak256([]byte(strVal)), nil + case "bytes": + bytesValue, ok := parseBytes(encValue) + if !ok { + return nil, dataMismatchError(encType, encValue) + } + return crypto.Keccak256(bytesValue), nil + } + if strings.HasPrefix(encType, "bytes") { + lengthStr := strings.TrimPrefix(encType, "bytes") + length, err := strconv.Atoi(lengthStr) + if err != nil { + return nil, fmt.Errorf("invalid size on bytes: %v", lengthStr) + } + if length < 0 || length > 32 { + return nil, fmt.Errorf("invalid size on bytes: %d", length) + } + if byteValue, ok := parseBytes(encValue); !ok || len(byteValue) != length { + return nil, dataMismatchError(encType, encValue) + } else { + // Right-pad the bits + dst := make([]byte, 32) + copy(dst, byteValue) + return dst, nil + } + } + if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") { + b, err := parseInteger(encType, encValue) + if err != nil { + return nil, err + } + return math.U256Bytes(b), nil + } + return nil, fmt.Errorf("unrecognized type '%s'", encType) + +} + +// dataMismatchError generates an error for a mismatch between +// the provided type and data +func dataMismatchError(encType string, encValue interface{}) error { + return fmt.Errorf("provided data '%v' doesn't match type '%s'", encValue, encType) +} + +// validate makes sure the types are sound +func (typedData *TypedData) validate() error { + if err := typedData.Types.validate(); err != nil { + return err + } + if err := typedData.Domain.validate(); err != nil { + return err + } + return nil +} + +// Map generates a map version of the typed data +func (typedData *TypedData) Map() map[string]interface{} { + dataMap := map[string]interface{}{ + "types": typedData.Types, + "domain": typedData.Domain.Map(), + "primaryType": typedData.PrimaryType, + "message": typedData.Message, + } + return dataMap +} + +// Format returns a representation of typedData, which can be easily displayed by a user-interface +// without in-depth knowledge about 712 rules +func (typedData *TypedData) Format() ([]*NameValueType, error) { + domain, err := typedData.formatData("EIP712Domain", typedData.Domain.Map()) + if err != nil { + return nil, err + } + ptype, err := typedData.formatData(typedData.PrimaryType, typedData.Message) + if err != nil { + return nil, err + } + var nvts []*NameValueType + nvts = append(nvts, &NameValueType{ + Name: "EIP712Domain", + Value: domain, + Typ: "domain", + }) + nvts = append(nvts, &NameValueType{ + Name: typedData.PrimaryType, + Value: ptype, + Typ: "primary type", + }) + return nvts, nil +} + +func (typedData *TypedData) formatData(primaryType string, data map[string]interface{}) ([]*NameValueType, error) { + var output []*NameValueType + + // Add field contents. Structs and arrays have special handlers. + for _, field := range typedData.Types[primaryType] { + encName := field.Name + encValue := data[encName] + item := &NameValueType{ + Name: encName, + Typ: field.Type, + } + if field.isArray() { + arrayValue, _ := encValue.([]interface{}) + parsedType := field.typeName() + for _, v := range arrayValue { + if typedData.Types[parsedType] != nil { + mapValue, _ := v.(map[string]interface{}) + mapOutput, err := typedData.formatData(parsedType, mapValue) + if err != nil { + return nil, err + } + item.Value = mapOutput + } else { + primitiveOutput, err := formatPrimitiveValue(field.Type, encValue) + if err != nil { + return nil, err + } + item.Value = primitiveOutput + } + } + } else if typedData.Types[field.Type] != nil { + if mapValue, ok := encValue.(map[string]interface{}); ok { + mapOutput, err := typedData.formatData(field.Type, mapValue) + if err != nil { + return nil, err + } + item.Value = mapOutput + } else { + item.Value = "<nil>" + } + } else { + primitiveOutput, err := formatPrimitiveValue(field.Type, encValue) + if err != nil { + return nil, err + } + item.Value = primitiveOutput + } + output = append(output, item) + } + return output, nil +} + +func formatPrimitiveValue(encType string, encValue interface{}) (string, error) { + switch encType { + case "address": + if stringValue, ok := encValue.(string); !ok { + return "", fmt.Errorf("could not format value %v as address", encValue) + } else { + return common.HexToAddress(stringValue).String(), nil + } + case "bool": + if boolValue, ok := encValue.(bool); !ok { + return "", fmt.Errorf("could not format value %v as bool", encValue) + } else { + return fmt.Sprintf("%t", boolValue), nil + } + case "bytes", "string": + return fmt.Sprintf("%s", encValue), nil + } + if strings.HasPrefix(encType, "bytes") { + return fmt.Sprintf("%s", encValue), nil + + } + if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") { + if b, err := parseInteger(encType, encValue); err != nil { + return "", err + } else { + return fmt.Sprintf("%d (0x%x)", b, b), nil + } + } + return "", fmt.Errorf("unhandled type %v", encType) +} + +// NameValueType is a very simple struct with Name, Value and Type. It's meant for simple +// json structures used to communicate signing-info about typed data with the UI +type NameValueType struct { + Name string `json:"name"` + Value interface{} `json:"value"` + Typ string `json:"type"` +} + +// Pprint returns a pretty-printed version of nvt +func (nvt *NameValueType) Pprint(depth int) string { + output := bytes.Buffer{} + output.WriteString(strings.Repeat("\u00a0", depth*2)) + output.WriteString(fmt.Sprintf("%s [%s]: ", nvt.Name, nvt.Typ)) + if nvts, ok := nvt.Value.([]*NameValueType); ok { + output.WriteString("\n") + for _, next := range nvts { + sublevel := next.Pprint(depth + 1) + output.WriteString(sublevel) + } + } else { + if nvt.Value != nil { + output.WriteString(fmt.Sprintf("%q\n", nvt.Value)) + } else { + output.WriteString("\n") + } + } + return output.String() +} + +// Validate checks if the types object is conformant to the specs +func (t Types) validate() error { + for typeKey, typeArr := range t { + if len(typeKey) == 0 { + return fmt.Errorf("empty type key") + } + for i, typeObj := range typeArr { + if len(typeObj.Type) == 0 { + return fmt.Errorf("type %q:%d: empty Type", typeKey, i) + } + if len(typeObj.Name) == 0 { + return fmt.Errorf("type %q:%d: empty Name", typeKey, i) + } + if typeKey == typeObj.Type { + return fmt.Errorf("type '%q' cannot reference itself", typeObj.Type) + } + if typeObj.isReferenceType() { + if _, exist := t[typeObj.typeName()]; !exist { + return fmt.Errorf("reference type '%q' is undefined", typeObj.Type) + } + if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) { + return fmt.Errorf("unknown reference type '%q", typeObj.Type) + } + } else if !isPrimitiveTypeValid(typeObj.Type) { + return fmt.Errorf("unknown type '%q'", typeObj.Type) + } + } + } + return nil +} + +// Checks if the primitive value is valid +func isPrimitiveTypeValid(primitiveType string) bool { + if primitiveType == "address" || + primitiveType == "address[]" || + primitiveType == "bool" || + primitiveType == "bool[]" || + primitiveType == "string" || + primitiveType == "string[]" { + return true + } + if primitiveType == "bytes" || + primitiveType == "bytes[]" || + primitiveType == "bytes1" || + primitiveType == "bytes1[]" || + primitiveType == "bytes2" || + primitiveType == "bytes2[]" || + primitiveType == "bytes3" || + primitiveType == "bytes3[]" || + primitiveType == "bytes4" || + primitiveType == "bytes4[]" || + primitiveType == "bytes5" || + primitiveType == "bytes5[]" || + primitiveType == "bytes6" || + primitiveType == "bytes6[]" || + primitiveType == "bytes7" || + primitiveType == "bytes7[]" || + primitiveType == "bytes8" || + primitiveType == "bytes8[]" || + primitiveType == "bytes9" || + primitiveType == "bytes9[]" || + primitiveType == "bytes10" || + primitiveType == "bytes10[]" || + primitiveType == "bytes11" || + primitiveType == "bytes11[]" || + primitiveType == "bytes12" || + primitiveType == "bytes12[]" || + primitiveType == "bytes13" || + primitiveType == "bytes13[]" || + primitiveType == "bytes14" || + primitiveType == "bytes14[]" || + primitiveType == "bytes15" || + primitiveType == "bytes15[]" || + primitiveType == "bytes16" || + primitiveType == "bytes16[]" || + primitiveType == "bytes17" || + primitiveType == "bytes17[]" || + primitiveType == "bytes18" || + primitiveType == "bytes18[]" || + primitiveType == "bytes19" || + primitiveType == "bytes19[]" || + primitiveType == "bytes20" || + primitiveType == "bytes20[]" || + primitiveType == "bytes21" || + primitiveType == "bytes21[]" || + primitiveType == "bytes22" || + primitiveType == "bytes22[]" || + primitiveType == "bytes23" || + primitiveType == "bytes23[]" || + primitiveType == "bytes24" || + primitiveType == "bytes24[]" || + primitiveType == "bytes25" || + primitiveType == "bytes25[]" || + primitiveType == "bytes26" || + primitiveType == "bytes26[]" || + primitiveType == "bytes27" || + primitiveType == "bytes27[]" || + primitiveType == "bytes28" || + primitiveType == "bytes28[]" || + primitiveType == "bytes29" || + primitiveType == "bytes29[]" || + primitiveType == "bytes30" || + primitiveType == "bytes30[]" || + primitiveType == "bytes31" || + primitiveType == "bytes31[]" || + primitiveType == "bytes32" || + primitiveType == "bytes32[]" { + return true + } + if primitiveType == "int" || + primitiveType == "int[]" || + primitiveType == "int8" || + primitiveType == "int8[]" || + primitiveType == "int16" || + primitiveType == "int16[]" || + primitiveType == "int32" || + primitiveType == "int32[]" || + primitiveType == "int64" || + primitiveType == "int64[]" || + primitiveType == "int128" || + primitiveType == "int128[]" || + primitiveType == "int256" || + primitiveType == "int256[]" { + return true + } + if primitiveType == "uint" || + primitiveType == "uint[]" || + primitiveType == "uint8" || + primitiveType == "uint8[]" || + primitiveType == "uint16" || + primitiveType == "uint16[]" || + primitiveType == "uint32" || + primitiveType == "uint32[]" || + primitiveType == "uint64" || + primitiveType == "uint64[]" || + primitiveType == "uint128" || + primitiveType == "uint128[]" || + primitiveType == "uint256" || + primitiveType == "uint256[]" { + return true + } + return false +} + +// validate checks if the given domain is valid, i.e. contains at least +// the minimum viable keys and values +func (domain *TypedDataDomain) validate() error { + if domain.ChainId == nil && len(domain.Name) == 0 && len(domain.Version) == 0 && len(domain.VerifyingContract) == 0 && len(domain.Salt) == 0 { + return errors.New("domain is undefined") + } + + return nil +} + +// Map is a helper function to generate a map version of the domain +func (domain *TypedDataDomain) Map() map[string]interface{} { + dataMap := map[string]interface{}{} + + if domain.ChainId != nil { + dataMap["chainId"] = domain.ChainId + } + + if len(domain.Name) > 0 { + dataMap["name"] = domain.Name + } + + if len(domain.Version) > 0 { + dataMap["version"] = domain.Version + } + + if len(domain.VerifyingContract) > 0 { + dataMap["verifyingContract"] = domain.VerifyingContract + } + + if len(domain.Salt) > 0 { + dataMap["salt"] = domain.Salt + } + return dataMap +}
diff --git go-ethereum/shared/signer/signed_data_test.go celo/shared/signer/signed_data_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fa07ead156b7b25b4e0b3e25c7696769cdb2579e --- /dev/null +++ celo/shared/signer/signed_data_test.go @@ -0,0 +1,480 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package signer_test + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "path" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/shared/signer" + "github.com/ethereum/go-ethereum/signer/core" +) + +var typesStandard = signer.Types{ + "EIP712Domain": { + { + Name: "name", + Type: "string", + }, + { + Name: "version", + Type: "string", + }, + { + Name: "chainId", + Type: "uint256", + }, + { + Name: "verifyingContract", + Type: "address", + }, + }, + "Person": { + { + Name: "name", + Type: "string", + }, + { + Name: "wallet", + Type: "address", + }, + }, + "Mail": { + { + Name: "from", + Type: "Person", + }, + { + Name: "to", + Type: "Person", + }, + { + Name: "contents", + Type: "string", + }, + }, +} + +var jsonTypedData = ` + { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "test", + "type": "uint8" + }, + { + "name": "wallet", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person" + }, + { + "name": "contents", + "type": "string" + } + ] + }, + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": "1", + "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Cow", + "test": 3, + "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" + } + } +` + +const primaryType = "Mail" + +var domainStandard = signer.TypedDataDomain{ + "Ether Mail", + "1", + math.NewHexOrDecimal256(1), + "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "", +} + +var messageStandard = map[string]interface{}{ + "from": map[string]interface{}{ + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + "to": map[string]interface{}{ + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + "contents": "Hello, Bob!", +} + +var typedData = signer.TypedData{ + Types: typesStandard, + PrimaryType: primaryType, + Domain: domainStandard, + Message: messageStandard, +} + +func TestDomainChainId(t *testing.T) { + withoutChainID := signer.TypedData{ + Types: signer.Types{ + "EIP712Domain": []signer.Type{ + {Name: "name", Type: "string"}, + }, + }, + Domain: signer.TypedDataDomain{ + Name: "test", + }, + } + + if _, ok := withoutChainID.Domain.Map()["chainId"]; ok { + t.Errorf("Expected the chainId key to not be present in the domain map") + } + // should encode successfully + if _, err := withoutChainID.HashStruct("EIP712Domain", withoutChainID.Domain.Map()); err != nil { + t.Errorf("Expected the typedData to encode the domain successfully, got %v", err) + } + withChainID := signer.TypedData{ + Types: signer.Types{ + "EIP712Domain": []signer.Type{ + {Name: "name", Type: "string"}, + {Name: "chainId", Type: "uint256"}, + }, + }, + Domain: signer.TypedDataDomain{ + Name: "test", + ChainId: math.NewHexOrDecimal256(1), + }, + } + + if _, ok := withChainID.Domain.Map()["chainId"]; !ok { + t.Errorf("Expected the chainId key be present in the domain map") + } + // should encode successfully + if _, err := withChainID.HashStruct("EIP712Domain", withChainID.Domain.Map()); err != nil { + t.Errorf("Expected the typedData to encode the domain successfully, got %v", err) + } +} + +func TestHashStruct(t *testing.T) { + hash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) + if err != nil { + t.Fatal(err) + } + mainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) + if mainHash != "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e" { + t.Errorf("Expected different hashStruct result (got %s)", mainHash) + } + + hash, err = typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) + if err != nil { + t.Error(err) + } + domainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) + if domainHash != "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f" { + t.Errorf("Expected different domain hashStruct result (got %s)", domainHash) + } +} + +func TestEncodeType(t *testing.T) { + domainTypeEncoding := string(typedData.EncodeType("EIP712Domain")) + if domainTypeEncoding != "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" { + t.Errorf("Expected different encodeType result (got %s)", domainTypeEncoding) + } + + mailTypeEncoding := string(typedData.EncodeType(typedData.PrimaryType)) + if mailTypeEncoding != "Mail(Person from,Person to,string contents)Person(string name,address wallet)" { + t.Errorf("Expected different encodeType result (got %s)", mailTypeEncoding) + } +} + +func TestTypeHash(t *testing.T) { + mailTypeHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.TypeHash(typedData.PrimaryType))) + if mailTypeHash != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2" { + t.Errorf("Expected different typeHash result (got %s)", mailTypeHash) + } +} + +func TestEncodeData(t *testing.T) { + hash, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0) + if err != nil { + t.Fatal(err) + } + dataEncoding := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) + if dataEncoding != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8" { + t.Errorf("Expected different encodeData result (got %s)", dataEncoding) + } +} + +func TestFormatter(t *testing.T) { + var d signer.TypedData + err := json.Unmarshal([]byte(jsonTypedData), &d) + if err != nil { + t.Fatalf("unmarshalling failed '%v'", err) + } + formatted, _ := d.Format() + for _, item := range formatted { + t.Logf("'%v'\n", item.Pprint(0)) + } + + j, _ := json.Marshal(formatted) + t.Logf("'%v'\n", string(j)) +} + +func sign(typedData signer.TypedData) ([]byte, []byte, error) { + domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) + if err != nil { + return nil, nil, err + } + typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) + if err != nil { + return nil, nil, err + } + rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) + sighash := crypto.Keccak256(rawData) + return typedDataHash, sighash, nil +} + +func TestJsonFiles(t *testing.T) { + testfiles, err := ioutil.ReadDir("testdata/") + if err != nil { + t.Fatalf("failed reading files: %v", err) + } + for i, fInfo := range testfiles { + if !strings.HasSuffix(fInfo.Name(), "json") { + continue + } + expectedFailure := strings.HasPrefix(fInfo.Name(), "expfail") + data, err := ioutil.ReadFile(path.Join("testdata", fInfo.Name())) + if err != nil { + t.Errorf("Failed to read file %v: %v", fInfo.Name(), err) + continue + } + var typedData signer.TypedData + err = json.Unmarshal(data, &typedData) + if err != nil { + t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err) + continue + } + _, _, err = sign(typedData) + t.Logf("Error %v\n", err) + if err != nil && !expectedFailure { + t.Errorf("Test %d failed, file %v: %v", i, fInfo.Name(), err) + } + if expectedFailure && err == nil { + t.Errorf("Test %d succeeded (expected failure), file %v: %v", i, fInfo.Name(), err) + } + } +} + +// TestFuzzerFiles tests some files that have been found by fuzzing to cause +// crashes or hangs. +func TestFuzzerFiles(t *testing.T) { + corpusdir := path.Join("testdata", "fuzzing") + testfiles, err := ioutil.ReadDir(corpusdir) + if err != nil { + t.Fatalf("failed reading files: %v", err) + } + verbose := false + for i, fInfo := range testfiles { + data, err := ioutil.ReadFile(path.Join(corpusdir, fInfo.Name())) + if err != nil { + t.Errorf("Failed to read file %v: %v", fInfo.Name(), err) + continue + } + var typedData signer.TypedData + err = json.Unmarshal(data, &typedData) + if err != nil { + t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err) + continue + } + _, err = typedData.EncodeData("EIP712Domain", typedData.Domain.Map(), 1) + if verbose && err != nil { + t.Logf("%d, EncodeData[1] err: %v\n", i, err) + } + _, err = typedData.EncodeData(typedData.PrimaryType, typedData.Message, 1) + if verbose && err != nil { + t.Logf("%d, EncodeData[2] err: %v\n", i, err) + } + typedData.Format() + } +} + +var gnosisTypedData = ` +{ + "types": { + "EIP712Domain": [ + { "type": "address", "name": "verifyingContract" } + ], + "SafeTx": [ + { "type": "address", "name": "to" }, + { "type": "uint256", "name": "value" }, + { "type": "bytes", "name": "data" }, + { "type": "uint8", "name": "operation" }, + { "type": "uint256", "name": "safeTxGas" }, + { "type": "uint256", "name": "baseGas" }, + { "type": "uint256", "name": "gasPrice" }, + { "type": "address", "name": "gasToken" }, + { "type": "address", "name": "refundReceiver" }, + { "type": "uint256", "name": "nonce" } + ] + }, + "domain": { + "verifyingContract": "0x25a6c4BBd32B2424A9c99aEB0584Ad12045382B3" + }, + "primaryType": "SafeTx", + "message": { + "to": "0x9eE457023bB3De16D51A003a247BaEaD7fce313D", + "value": "20000000000000000", + "data": "0x", + "operation": 0, + "safeTxGas": 27845, + "baseGas": 0, + "gasPrice": "0", + "gasToken": "0x0000000000000000000000000000000000000000", + "refundReceiver": "0x0000000000000000000000000000000000000000", + "nonce": 3 + } +}` + +var gnosisTx = ` +{ + "safe": "0x25a6c4BBd32B2424A9c99aEB0584Ad12045382B3", + "to": "0x9eE457023bB3De16D51A003a247BaEaD7fce313D", + "value": "20000000000000000", + "data": null, + "operation": 0, + "gasToken": "0x0000000000000000000000000000000000000000", + "safeTxGas": 27845, + "baseGas": 0, + "gasPrice": "0", + "refundReceiver": "0x0000000000000000000000000000000000000000", + "nonce": 3, + "executionDate": null, + "submissionDate": "2020-09-15T21:59:23.815748Z", + "modified": "2020-09-15T21:59:23.815748Z", + "blockNumber": null, + "transactionHash": null, + "safeTxHash": "0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f", + "executor": null, + "isExecuted": false, + "isSuccessful": null, + "ethGasPrice": null, + "gasUsed": null, + "fee": null, + "origin": null, + "dataDecoded": null, + "confirmationsRequired": null, + "confirmations": [ + { + "owner": "0xAd2e180019FCa9e55CADe76E4487F126Fd08DA34", + "submissionDate": "2020-09-15T21:59:28.281243Z", + "transactionHash": null, + "confirmationType": "CONFIRMATION", + "signature": "0x5e562065a0cb15d766dac0cd49eb6d196a41183af302c4ecad45f1a81958d7797753f04424a9b0aa1cb0448e4ec8e189540fbcdda7530ef9b9d95dfc2d36cb521b", + "signatureType": "EOA" + } + ], + "signatures": null + } +` + +// TestGnosisTypedData tests the scenario where a user submits a full EIP-712 +// struct without using the gnosis-specific endpoint +func TestGnosisTypedData(t *testing.T) { + var td signer.TypedData + err := json.Unmarshal([]byte(gnosisTypedData), &td) + if err != nil { + t.Fatalf("unmarshalling failed '%v'", err) + } + _, sighash, err := sign(td) + if err != nil { + t.Fatal(err) + } + expSigHash := common.FromHex("0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f") + if !bytes.Equal(expSigHash, sighash) { + t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) + } +} + +// TestGnosisCustomData tests the scenario where a user submits only the gnosis-safe +// specific data, and we fill the TypedData struct on our side +func TestGnosisCustomData(t *testing.T) { + var tx core.GnosisSafeTx + err := json.Unmarshal([]byte(gnosisTx), &tx) + if err != nil { + t.Fatal(err) + } + var td = tx.ToTypedData() + _, sighash, err := sign(td) + if err != nil { + t.Fatal(err) + } + expSigHash := common.FromHex("0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f") + if !bytes.Equal(expSigHash, sighash) { + t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) + } +}
diff --git go-ethereum/signer/core/signed_data_internal_test.go celo/shared/signer/signed_data_internal_test.go rename from signer/core/signed_data_internal_test.go rename to shared/signer/signed_data_internal_test.go index 49976f1e21e5332813f46ee823ef00527937ec11..6968edc8a68060c43490700cd7236121f3662d2d 100644 --- go-ethereum/signer/core/signed_data_internal_test.go +++ celo/shared/signer/signed_data_internal_test.go @@ -14,7 +14,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.   -package core +package signer   import ( "bytes"
diff --git go-ethereum/signer/core/testdata/expfail_extradata-1.json celo/shared/signer/testdata/expfail_extradata-1.json rename from signer/core/testdata/expfail_extradata-1.json rename to shared/signer/testdata/expfail_extradata-1.json
diff --git go-ethereum/signer/core/testdata/eip712.json celo/shared/signer/testdata/eip712.json rename from signer/core/testdata/eip712.json rename to shared/signer/testdata/eip712.json
diff --git go-ethereum/signer/core/testdata/arrays-1.json celo/shared/signer/testdata/arrays-1.json rename from signer/core/testdata/arrays-1.json rename to shared/signer/testdata/arrays-1.json
diff --git go-ethereum/signer/core/testdata/expfail_datamismatch_1.json celo/shared/signer/testdata/expfail_datamismatch_1.json rename from signer/core/testdata/expfail_datamismatch_1.json rename to shared/signer/testdata/expfail_datamismatch_1.json
diff --git go-ethereum/signer/core/testdata/custom_arraytype.json celo/shared/signer/testdata/custom_arraytype.json rename from signer/core/testdata/custom_arraytype.json rename to shared/signer/testdata/custom_arraytype.json
diff --git go-ethereum/signer/core/testdata/expfail_toolargeuint.json celo/shared/signer/testdata/expfail_toolargeuint.json rename from signer/core/testdata/expfail_toolargeuint.json rename to shared/signer/testdata/expfail_toolargeuint.json
diff --git go-ethereum/signer/core/testdata/expfail_unconvertiblefloat2.json celo/shared/signer/testdata/expfail_unconvertiblefloat2.json rename from signer/core/testdata/expfail_unconvertiblefloat2.json rename to shared/signer/testdata/expfail_unconvertiblefloat2.json
diff --git go-ethereum/signer/core/testdata/README.md celo/shared/signer/testdata/README.md rename from signer/core/testdata/README.md rename to shared/signer/testdata/README.md
diff --git go-ethereum/signer/core/testdata/expfail_unconvertiblefloat.json celo/shared/signer/testdata/expfail_unconvertiblefloat.json rename from signer/core/testdata/expfail_unconvertiblefloat.json rename to shared/signer/testdata/expfail_unconvertiblefloat.json
diff --git go-ethereum/signer/core/testdata/expfail_nonexistant_type.json celo/shared/signer/testdata/expfail_nonexistant_type.json rename from signer/core/testdata/expfail_nonexistant_type.json rename to shared/signer/testdata/expfail_nonexistant_type.json
diff --git go-ethereum/signer/core/testdata/expfail_extradata-2.json celo/shared/signer/testdata/expfail_extradata-2.json rename from signer/core/testdata/expfail_extradata-2.json rename to shared/signer/testdata/expfail_extradata-2.json
diff --git go-ethereum/signer/core/testdata/expfail_toolargeuint2.json celo/shared/signer/testdata/expfail_toolargeuint2.json rename from signer/core/testdata/expfail_toolargeuint2.json rename to shared/signer/testdata/expfail_toolargeuint2.json
diff --git go-ethereum/signer/core/testdata/expfail_arraytype_overload.json celo/shared/signer/testdata/expfail_arraytype_overload.json rename from signer/core/testdata/expfail_arraytype_overload.json rename to shared/signer/testdata/expfail_arraytype_overload.json
diff --git go-ethereum/signer/core/testdata/expfail_malformeddomainkeys.json celo/shared/signer/testdata/expfail_malformeddomainkeys.json rename from signer/core/testdata/expfail_malformeddomainkeys.json rename to shared/signer/testdata/expfail_malformeddomainkeys.json
diff --git go-ethereum/signer/core/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921 celo/shared/signer/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921 rename from signer/core/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921 rename to shared/signer/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921
diff --git go-ethereum/signer/core/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f celo/shared/signer/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f rename from signer/core/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f rename to shared/signer/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f
diff --git go-ethereum/signer/core/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92 celo/shared/signer/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92 rename from signer/core/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92 rename to shared/signer/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92
diff --git go-ethereum/signer/core/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d celo/shared/signer/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d rename from signer/core/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d rename to shared/signer/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d
diff --git go-ethereum/signer/core/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1 celo/shared/signer/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1 rename from signer/core/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1 rename to shared/signer/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1
diff --git go-ethereum/signer/core/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d celo/shared/signer/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d rename from signer/core/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d rename to shared/signer/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d
diff --git go-ethereum/signer/core/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8 celo/shared/signer/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8 rename from signer/core/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8 rename to shared/signer/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8
diff --git go-ethereum/signer/core/api.go celo/signer/core/api.go index 9cd7c84307aa5ee51b71c011ccd452cad6c24174..97d16b9495c305632b2abe1477e9626f0de51691 100644 --- go-ethereum/signer/core/api.go +++ celo/signer/core/api.go @@ -22,17 +22,16 @@ "encoding/json" "errors" "fmt" "math/big" - "os" "reflect"   "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/accounts/scwallet" "github.com/ethereum/go-ethereum/accounts/usbwallet" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/shared/signer" "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/ethereum/go-ethereum/signer/storage" ) @@ -57,7 +56,7 @@ SignTransaction(ctx context.Context, args apitypes.SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) // SignData - request to sign the given data (plus prefix) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error) // SignTypedData - request to sign the given structured data (plus prefix) - SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data TypedData) (hexutil.Bytes, error) + SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data signer.TypedData) (hexutil.Bytes, error) // EcRecover - recover public key from given message and signature EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) // Version info about the APIs @@ -127,7 +126,7 @@ UserAgent string `json:"User-Agent"` Origin string `json:"Origin"` }   -func StartClefAccountManager(ksLocation string, nousb, lightKDF bool, scpath string) *accounts.Manager { +func StartClefAccountManager(ksLocation string, nousb, lightKDF bool) *accounts.Manager { var ( backends []accounts.Backend n, p = keystore.StandardScryptN, keystore.StandardScryptP @@ -163,25 +162,6 @@ log.Debug("Trezor support enabled via WebUSB") } }   - // Start a smart card hub - if len(scpath) > 0 { - // Sanity check that the smartcard path is valid - fi, err := os.Stat(scpath) - if err != nil { - log.Info("Smartcard socket file missing, disabling", "err", err) - } else { - if fi.Mode()&os.ModeType != os.ModeSocket { - log.Error("Invalid smartcard socket file type", "path", scpath, "type", fi.Mode().String()) - } else { - if schub, err := scwallet.NewHub(scpath, scwallet.Scheme, ksLocation); err != nil { - log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err)) - } else { - backends = append(backends, schub) - } - } - } - } - // Clef doesn't allow insecure http account unlock. return accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: false}, backends...) } @@ -235,7 +215,7 @@ SignDataRequest struct { ContentType string `json:"content_type"` Address common.MixedcaseAddress `json:"address"` Rawdata []byte `json:"raw_data"` - Messages []*NameValueType `json:"messages"` + Messages []*signer.NameValueType `json:"messages"` Callinfo []apitypes.ValidationInfo `json:"call_info"` Hash hexutil.Bytes `json:"hash"` Meta Metadata `json:"meta"`
diff --git go-ethereum/signer/core/uiapi.go celo/signer/core/uiapi.go index 7d8b72187fb762471a655f0897d11cc10602b972..aed733167921b7e0401670ceec58059a3f34f27f 100644 --- go-ethereum/signer/core/uiapi.go +++ celo/signer/core/uiapi.go @@ -93,7 +93,7 @@ // DeriveAccount requests a HD wallet to derive a new account, optionally pinning // it for later reuse. // Example call -// {"jsonrpc":"2.0","method":"clef_deriveAccount","params":["ledger://","m/44'/60'/0'", false], "id":6} +// {"jsonrpc":"2.0","method":"clef_deriveAccount","params":["ledger://","m/44'/52752'/0'", false], "id":6} func (s *UIServerAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) { wallet, err := s.am.Wallet(url) if err != nil {
diff --git go-ethereum/signer/core/gnosis_safe.go celo/signer/core/gnosis_safe.go index 1ac437582dc01528ea528064118a6e9bc31a69a4..41c0be8fc44aed0e8c24cdba989bb523e4cd3ddc 100644 --- go-ethereum/signer/core/gnosis_safe.go +++ celo/signer/core/gnosis_safe.go @@ -7,6 +7,7 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/shared/signer" "github.com/ethereum/go-ethereum/signer/core/apitypes" )   @@ -34,15 +35,15 @@ InputExpHash common.Hash `json:"safeTxHash"` }   // ToTypedData converts the tx to a EIP-712 Typed Data structure for signing -func (tx *GnosisSafeTx) ToTypedData() TypedData { +func (tx *GnosisSafeTx) ToTypedData() signer.TypedData { var data hexutil.Bytes if tx.Data != nil { data = *tx.Data } - gnosisTypedData := TypedData{ - Types: Types{ - "EIP712Domain": []Type{{Name: "verifyingContract", Type: "address"}}, - "SafeTx": []Type{ + gnosisTypedData := signer.TypedData{ + Types: signer.Types{ + "EIP712Domain": []signer.Type{{Name: "verifyingContract", Type: "address"}}, + "SafeTx": []signer.Type{ {Name: "to", Type: "address"}, {Name: "value", Type: "uint256"}, {Name: "data", Type: "bytes"}, @@ -55,11 +56,11 @@ {Name: "refundReceiver", Type: "address"}, {Name: "nonce", Type: "uint256"}, }, }, - Domain: TypedDataDomain{ + Domain: signer.TypedDataDomain{ VerifyingContract: tx.Safe.Address().Hex(), }, PrimaryType: "SafeTx", - Message: TypedDataMessage{ + Message: signer.TypedDataMessage{ "to": tx.To.Address().Hex(), "value": tx.Value.String(), "data": data,
diff --git go-ethereum/signer/core/api_test.go celo/signer/core/api_test.go index d28d92bc4c72aec6f964b1a92639019f59737b44..d9b447c605545bd88106c3a98cec03b88c782991 100644 --- go-ethereum/signer/core/api_test.go +++ celo/signer/core/api_test.go @@ -126,7 +126,7 @@ if err != nil { t.Fatal(err.Error()) } ui := &headlessUi{make(chan string, 20), make(chan string, 20)} - am := core.StartClefAccountManager(tmpDirName(t), true, true, "") + am := core.StartClefAccountManager(tmpDirName(t), true, true) api := core.NewSignerAPI(am, 1337, true, ui, db, true, &storage.NoStorage{}) return api, ui
diff --git go-ethereum/signer/core/signed_data.go celo/signer/core/signed_data.go index 6f67cadccbbfcfb33cd67adbf05529b39bc99aac..42fbe2f3df9622ed2a9a32e4138ec7934348e890 100644 --- go-ethereum/signer/core/signed_data.go +++ celo/signer/core/signed_data.go @@ -17,51 +17,31 @@ package core   import ( - "bytes" "context" "errors" "fmt" - "math/big" "mime" - "reflect" - "regexp" - "sort" - "strconv" - "strings" - "unicode"   "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/consensus/clique" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/shared/signer" "github.com/ethereum/go-ethereum/signer/core/apitypes" )   -type SigFormat struct { - Mime string - ByteVersion byte -} - var ( - IntendedValidator = SigFormat{ - accounts.MimetypeDataWithValidator, - 0x00, + IntendedValidator = signer.SigFormat{ + Mime: accounts.MimetypeDataWithValidator, + ByteVersion: 0x00, } - DataTyped = SigFormat{ - accounts.MimetypeTypedData, - 0x01, - } - ApplicationClique = SigFormat{ - accounts.MimetypeClique, - 0x02, + DataTyped = signer.SigFormat{ + Mime: accounts.MimetypeTypedData, + ByteVersion: 0x01, } - TextPlain = SigFormat{ - accounts.MimetypeTextPlain, - 0x45, + TextPlain = signer.SigFormat{ + Mime: accounts.MimetypeTextPlain, + ByteVersion: 0x45, } )   @@ -70,58 +50,6 @@ Address common.Address Message hexutil.Bytes }   -type TypedData struct { - Types Types `json:"types"` - PrimaryType string `json:"primaryType"` - Domain TypedDataDomain `json:"domain"` - Message TypedDataMessage `json:"message"` -} - -type Type struct { - Name string `json:"name"` - Type string `json:"type"` -} - -func (t *Type) isArray() bool { - return strings.HasSuffix(t.Type, "[]") -} - -// typeName returns the canonical name of the type. If the type is 'Person[]', then -// this method returns 'Person' -func (t *Type) typeName() string { - if strings.HasSuffix(t.Type, "[]") { - return strings.TrimSuffix(t.Type, "[]") - } - return t.Type -} - -func (t *Type) isReferenceType() bool { - if len(t.Type) == 0 { - return false - } - // Reference types must have a leading uppercase character - return unicode.IsUpper([]rune(t.Type)[0]) -} - -type Types map[string][]Type - -type TypePriority struct { - Type string - Value uint -} - -type TypedDataMessage = map[string]interface{} - -type TypedDataDomain struct { - Name string `json:"name"` - Version string `json:"version"` - ChainId *math.HexOrDecimal256 `json:"chainId"` - VerifyingContract string `json:"verifyingContract"` - Salt string `json:"salt"` -} - -var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`) - // sign receives a request and produces a signature // // Note, the produced signature conforms to the secp256k1 curve R, S and V values, @@ -200,7 +128,7 @@ if err != nil { return nil, useEthereumV, err } sighash, msg := SignTextValidator(validatorData) - messages := []*NameValueType{ + messages := []*signer.NameValueType{ { Name: "This is a request to sign data intended for a particular validator (see EIP 191 version 0)", Typ: "description", @@ -223,42 +151,6 @@ Value: fmt.Sprintf("0x%x", msg), }, } req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash} - case ApplicationClique.Mime: - // Clique is the Ethereum PoA standard - stringData, ok := data.(string) - if !ok { - return nil, useEthereumV, fmt.Errorf("input for %v must be an hex-encoded string", ApplicationClique.Mime) - } - cliqueData, err := hexutil.Decode(stringData) - if err != nil { - return nil, useEthereumV, err - } - header := &types.Header{} - if err := rlp.DecodeBytes(cliqueData, header); err != nil { - return nil, useEthereumV, err - } - // The incoming clique header is already truncated, sent to us with a extradata already shortened - if len(header.Extra) < 65 { - // Need to add it back, to get a suitable length for hashing - newExtra := make([]byte, len(header.Extra)+65) - copy(newExtra, header.Extra) - header.Extra = newExtra - } - // Get back the rlp data, encoded by us - sighash, cliqueRlp, err := cliqueHeaderHashAndRlp(header) - if err != nil { - return nil, useEthereumV, err - } - messages := []*NameValueType{ - { - Name: "Clique header", - Typ: "clique", - Value: fmt.Sprintf("clique header %d [0x%x]", header.Number, header.Hash()), - }, - } - // Clique uses V on the form 0 or 1 - useEthereumV = false - req = &SignDataRequest{ContentType: mediaType, Rawdata: cliqueRlp, Messages: messages, Hash: sighash} default: // also case TextPlain.Mime: // Calculates an Ethereum ECDSA signature for: // hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}") @@ -270,7 +162,7 @@ if textData, err := hexutil.Decode(stringData); err != nil { return nil, useEthereumV, err } else { sighash, msg := accounts.TextAndHash(textData) - messages := []*NameValueType{ + messages := []*signer.NameValueType{ { Name: "message", Typ: accounts.MimetypeTextPlain, @@ -294,29 +186,12 @@ msg := fmt.Sprintf("\x19\x00%s%s", string(validatorData.Address.Bytes()), string(validatorData.Message)) return crypto.Keccak256([]byte(msg)), msg }   -// cliqueHeaderHashAndRlp returns the hash which is used as input for the proof-of-authority -// signing. It is the hash of the entire header apart from the 65 byte signature -// contained at the end of the extra data. -// -// The method requires the extra data to be at least 65 bytes -- the original implementation -// in clique.go panics if this is the case, thus it's been reimplemented here to avoid the panic -// and simply return an error instead -func cliqueHeaderHashAndRlp(header *types.Header) (hash, rlp []byte, err error) { - if len(header.Extra) < 65 { - err = fmt.Errorf("clique header extradata too short, %d < 65", len(header.Extra)) - return - } - rlp = clique.CliqueRLP(header) - hash = clique.SealHash(header).Bytes() - return hash, rlp, err -} - // SignTypedData signs EIP-712 conformant typed data // hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}") // It returns // - the signature, // - and/or any error -func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData TypedData) (hexutil.Bytes, error) { +func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData signer.TypedData) (hexutil.Bytes, error) { signature, _, err := api.signTypedData(ctx, addr, typedData, nil) return signature, err } @@ -324,7 +199,7 @@ // signTypedData is identical to the capitalized version, except that it also returns the hash (preimage) // - the signature preimage (hash) func (api *SignerAPI) signTypedData(ctx context.Context, addr common.MixedcaseAddress, - typedData TypedData, validationMessages *apitypes.ValidationMessages) (hexutil.Bytes, hexutil.Bytes, error) { + typedData signer.TypedData, validationMessages *apitypes.ValidationMessages) (hexutil.Bytes, hexutil.Bytes, error) { domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) if err != nil { return nil, nil, err @@ -356,289 +231,6 @@ } return signature, sighash, nil }   -// HashStruct generates a keccak256 hash of the encoding of the provided data -func (typedData *TypedData) HashStruct(primaryType string, data TypedDataMessage) (hexutil.Bytes, error) { - encodedData, err := typedData.EncodeData(primaryType, data, 1) - if err != nil { - return nil, err - } - return crypto.Keccak256(encodedData), nil -} - -// Dependencies returns an array of custom types ordered by their hierarchical reference tree -func (typedData *TypedData) Dependencies(primaryType string, found []string) []string { - includes := func(arr []string, str string) bool { - for _, obj := range arr { - if obj == str { - return true - } - } - return false - } - - if includes(found, primaryType) { - return found - } - if typedData.Types[primaryType] == nil { - return found - } - found = append(found, primaryType) - for _, field := range typedData.Types[primaryType] { - for _, dep := range typedData.Dependencies(field.Type, found) { - if !includes(found, dep) { - found = append(found, dep) - } - } - } - return found -} - -// EncodeType generates the following encoding: -// `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"` -// -// each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name -func (typedData *TypedData) EncodeType(primaryType string) hexutil.Bytes { - // Get dependencies primary first, then alphabetical - deps := typedData.Dependencies(primaryType, []string{}) - if len(deps) > 0 { - slicedDeps := deps[1:] - sort.Strings(slicedDeps) - deps = append([]string{primaryType}, slicedDeps...) - } - - // Format as a string with fields - var buffer bytes.Buffer - for _, dep := range deps { - buffer.WriteString(dep) - buffer.WriteString("(") - for _, obj := range typedData.Types[dep] { - buffer.WriteString(obj.Type) - buffer.WriteString(" ") - buffer.WriteString(obj.Name) - buffer.WriteString(",") - } - buffer.Truncate(buffer.Len() - 1) - buffer.WriteString(")") - } - return buffer.Bytes() -} - -// TypeHash creates the keccak256 hash of the data -func (typedData *TypedData) TypeHash(primaryType string) hexutil.Bytes { - return crypto.Keccak256(typedData.EncodeType(primaryType)) -} - -// EncodeData generates the following encoding: -// `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)` -// -// each encoded member is 32-byte long -func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}, depth int) (hexutil.Bytes, error) { - if err := typedData.validate(); err != nil { - return nil, err - } - - buffer := bytes.Buffer{} - - // Verify extra data - if exp, got := len(typedData.Types[primaryType]), len(data); exp < got { - return nil, fmt.Errorf("there is extra data provided in the message (%d < %d)", exp, got) - } - - // Add typehash - buffer.Write(typedData.TypeHash(primaryType)) - - // Add field contents. Structs and arrays have special handlers. - for _, field := range typedData.Types[primaryType] { - encType := field.Type - encValue := data[field.Name] - if encType[len(encType)-1:] == "]" { - arrayValue, ok := encValue.([]interface{}) - if !ok { - return nil, dataMismatchError(encType, encValue) - } - - arrayBuffer := bytes.Buffer{} - parsedType := strings.Split(encType, "[")[0] - for _, item := range arrayValue { - if typedData.Types[parsedType] != nil { - mapValue, ok := item.(map[string]interface{}) - if !ok { - return nil, dataMismatchError(parsedType, item) - } - encodedData, err := typedData.EncodeData(parsedType, mapValue, depth+1) - if err != nil { - return nil, err - } - arrayBuffer.Write(encodedData) - } else { - bytesValue, err := typedData.EncodePrimitiveValue(parsedType, item, depth) - if err != nil { - return nil, err - } - arrayBuffer.Write(bytesValue) - } - } - - buffer.Write(crypto.Keccak256(arrayBuffer.Bytes())) - } else if typedData.Types[field.Type] != nil { - mapValue, ok := encValue.(map[string]interface{}) - if !ok { - return nil, dataMismatchError(encType, encValue) - } - encodedData, err := typedData.EncodeData(field.Type, mapValue, depth+1) - if err != nil { - return nil, err - } - buffer.Write(crypto.Keccak256(encodedData)) - } else { - byteValue, err := typedData.EncodePrimitiveValue(encType, encValue, depth) - if err != nil { - return nil, err - } - buffer.Write(byteValue) - } - } - return buffer.Bytes(), nil -} - -// Attempt to parse bytes in different formats: byte array, hex string, hexutil.Bytes. -func parseBytes(encType interface{}) ([]byte, bool) { - switch v := encType.(type) { - case []byte: - return v, true - case hexutil.Bytes: - return v, true - case string: - bytes, err := hexutil.Decode(v) - if err != nil { - return nil, false - } - return bytes, true - default: - return nil, false - } -} - -func parseInteger(encType string, encValue interface{}) (*big.Int, error) { - var ( - length int - signed = strings.HasPrefix(encType, "int") - b *big.Int - ) - if encType == "int" || encType == "uint" { - length = 256 - } else { - lengthStr := "" - if strings.HasPrefix(encType, "uint") { - lengthStr = strings.TrimPrefix(encType, "uint") - } else { - lengthStr = strings.TrimPrefix(encType, "int") - } - atoiSize, err := strconv.Atoi(lengthStr) - if err != nil { - return nil, fmt.Errorf("invalid size on integer: %v", lengthStr) - } - length = atoiSize - } - switch v := encValue.(type) { - case *math.HexOrDecimal256: - b = (*big.Int)(v) - case string: - var hexIntValue math.HexOrDecimal256 - if err := hexIntValue.UnmarshalText([]byte(v)); err != nil { - return nil, err - } - b = (*big.Int)(&hexIntValue) - case float64: - // JSON parses non-strings as float64. Fail if we cannot - // convert it losslessly - if float64(int64(v)) == v { - b = big.NewInt(int64(v)) - } else { - return nil, fmt.Errorf("invalid float value %v for type %v", v, encType) - } - } - if b == nil { - return nil, fmt.Errorf("invalid integer value %v/%v for type %v", encValue, reflect.TypeOf(encValue), encType) - } - if b.BitLen() > length { - return nil, fmt.Errorf("integer larger than '%v'", encType) - } - if !signed && b.Sign() == -1 { - return nil, fmt.Errorf("invalid negative value for unsigned type %v", encType) - } - return b, nil -} - -// EncodePrimitiveValue deals with the primitive values found -// while searching through the typed data -func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interface{}, depth int) ([]byte, error) { - switch encType { - case "address": - stringValue, ok := encValue.(string) - if !ok || !common.IsHexAddress(stringValue) { - return nil, dataMismatchError(encType, encValue) - } - retval := make([]byte, 32) - copy(retval[12:], common.HexToAddress(stringValue).Bytes()) - return retval, nil - case "bool": - boolValue, ok := encValue.(bool) - if !ok { - return nil, dataMismatchError(encType, encValue) - } - if boolValue { - return math.PaddedBigBytes(common.Big1, 32), nil - } - return math.PaddedBigBytes(common.Big0, 32), nil - case "string": - strVal, ok := encValue.(string) - if !ok { - return nil, dataMismatchError(encType, encValue) - } - return crypto.Keccak256([]byte(strVal)), nil - case "bytes": - bytesValue, ok := parseBytes(encValue) - if !ok { - return nil, dataMismatchError(encType, encValue) - } - return crypto.Keccak256(bytesValue), nil - } - if strings.HasPrefix(encType, "bytes") { - lengthStr := strings.TrimPrefix(encType, "bytes") - length, err := strconv.Atoi(lengthStr) - if err != nil { - return nil, fmt.Errorf("invalid size on bytes: %v", lengthStr) - } - if length < 0 || length > 32 { - return nil, fmt.Errorf("invalid size on bytes: %d", length) - } - if byteValue, ok := parseBytes(encValue); !ok || len(byteValue) != length { - return nil, dataMismatchError(encType, encValue) - } else { - // Right-pad the bits - dst := make([]byte, 32) - copy(dst, byteValue) - return dst, nil - } - } - if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") { - b, err := parseInteger(encType, encValue) - if err != nil { - return nil, err - } - return math.U256Bytes(b), nil - } - return nil, fmt.Errorf("unrecognized type '%s'", encType) - -} - -// dataMismatchError generates an error for a mismatch between -// the provided type and data -func dataMismatchError(encType string, encValue interface{}) error { - return fmt.Errorf("provided data '%v' doesn't match type '%s'", encValue, encType) -} - // EcRecover recovers the address associated with the given sig. // Only compatible with `text/plain` func (api *SignerAPI) EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) { @@ -703,342 +295,3 @@ Address: common.BytesToAddress(addrBytes), Message: messageBytes, }, nil } - -// validate makes sure the types are sound -func (typedData *TypedData) validate() error { - if err := typedData.Types.validate(); err != nil { - return err - } - if err := typedData.Domain.validate(); err != nil { - return err - } - return nil -} - -// Map generates a map version of the typed data -func (typedData *TypedData) Map() map[string]interface{} { - dataMap := map[string]interface{}{ - "types": typedData.Types, - "domain": typedData.Domain.Map(), - "primaryType": typedData.PrimaryType, - "message": typedData.Message, - } - return dataMap -} - -// Format returns a representation of typedData, which can be easily displayed by a user-interface -// without in-depth knowledge about 712 rules -func (typedData *TypedData) Format() ([]*NameValueType, error) { - domain, err := typedData.formatData("EIP712Domain", typedData.Domain.Map()) - if err != nil { - return nil, err - } - ptype, err := typedData.formatData(typedData.PrimaryType, typedData.Message) - if err != nil { - return nil, err - } - var nvts []*NameValueType - nvts = append(nvts, &NameValueType{ - Name: "EIP712Domain", - Value: domain, - Typ: "domain", - }) - nvts = append(nvts, &NameValueType{ - Name: typedData.PrimaryType, - Value: ptype, - Typ: "primary type", - }) - return nvts, nil -} - -func (typedData *TypedData) formatData(primaryType string, data map[string]interface{}) ([]*NameValueType, error) { - var output []*NameValueType - - // Add field contents. Structs and arrays have special handlers. - for _, field := range typedData.Types[primaryType] { - encName := field.Name - encValue := data[encName] - item := &NameValueType{ - Name: encName, - Typ: field.Type, - } - if field.isArray() { - arrayValue, _ := encValue.([]interface{}) - parsedType := field.typeName() - for _, v := range arrayValue { - if typedData.Types[parsedType] != nil { - mapValue, _ := v.(map[string]interface{}) - mapOutput, err := typedData.formatData(parsedType, mapValue) - if err != nil { - return nil, err - } - item.Value = mapOutput - } else { - primitiveOutput, err := formatPrimitiveValue(field.Type, encValue) - if err != nil { - return nil, err - } - item.Value = primitiveOutput - } - } - } else if typedData.Types[field.Type] != nil { - if mapValue, ok := encValue.(map[string]interface{}); ok { - mapOutput, err := typedData.formatData(field.Type, mapValue) - if err != nil { - return nil, err - } - item.Value = mapOutput - } else { - item.Value = "<nil>" - } - } else { - primitiveOutput, err := formatPrimitiveValue(field.Type, encValue) - if err != nil { - return nil, err - } - item.Value = primitiveOutput - } - output = append(output, item) - } - return output, nil -} - -func formatPrimitiveValue(encType string, encValue interface{}) (string, error) { - switch encType { - case "address": - if stringValue, ok := encValue.(string); !ok { - return "", fmt.Errorf("could not format value %v as address", encValue) - } else { - return common.HexToAddress(stringValue).String(), nil - } - case "bool": - if boolValue, ok := encValue.(bool); !ok { - return "", fmt.Errorf("could not format value %v as bool", encValue) - } else { - return fmt.Sprintf("%t", boolValue), nil - } - case "bytes", "string": - return fmt.Sprintf("%s", encValue), nil - } - if strings.HasPrefix(encType, "bytes") { - return fmt.Sprintf("%s", encValue), nil - - } - if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") { - if b, err := parseInteger(encType, encValue); err != nil { - return "", err - } else { - return fmt.Sprintf("%d (0x%x)", b, b), nil - } - } - return "", fmt.Errorf("unhandled type %v", encType) -} - -// NameValueType is a very simple struct with Name, Value and Type. It's meant for simple -// json structures used to communicate signing-info about typed data with the UI -type NameValueType struct { - Name string `json:"name"` - Value interface{} `json:"value"` - Typ string `json:"type"` -} - -// Pprint returns a pretty-printed version of nvt -func (nvt *NameValueType) Pprint(depth int) string { - output := bytes.Buffer{} - output.WriteString(strings.Repeat("\u00a0", depth*2)) - output.WriteString(fmt.Sprintf("%s [%s]: ", nvt.Name, nvt.Typ)) - if nvts, ok := nvt.Value.([]*NameValueType); ok { - output.WriteString("\n") - for _, next := range nvts { - sublevel := next.Pprint(depth + 1) - output.WriteString(sublevel) - } - } else { - if nvt.Value != nil { - output.WriteString(fmt.Sprintf("%q\n", nvt.Value)) - } else { - output.WriteString("\n") - } - } - return output.String() -} - -// Validate checks if the types object is conformant to the specs -func (t Types) validate() error { - for typeKey, typeArr := range t { - if len(typeKey) == 0 { - return fmt.Errorf("empty type key") - } - for i, typeObj := range typeArr { - if len(typeObj.Type) == 0 { - return fmt.Errorf("type %q:%d: empty Type", typeKey, i) - } - if len(typeObj.Name) == 0 { - return fmt.Errorf("type %q:%d: empty Name", typeKey, i) - } - if typeKey == typeObj.Type { - return fmt.Errorf("type %q cannot reference itself", typeObj.Type) - } - if typeObj.isReferenceType() { - if _, exist := t[typeObj.typeName()]; !exist { - return fmt.Errorf("reference type %q is undefined", typeObj.Type) - } - if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) { - return fmt.Errorf("unknown reference type %q", typeObj.Type) - } - } else if !isPrimitiveTypeValid(typeObj.Type) { - return fmt.Errorf("unknown type %q", typeObj.Type) - } - } - } - return nil -} - -// Checks if the primitive value is valid -func isPrimitiveTypeValid(primitiveType string) bool { - if primitiveType == "address" || - primitiveType == "address[]" || - primitiveType == "bool" || - primitiveType == "bool[]" || - primitiveType == "string" || - primitiveType == "string[]" { - return true - } - if primitiveType == "bytes" || - primitiveType == "bytes[]" || - primitiveType == "bytes1" || - primitiveType == "bytes1[]" || - primitiveType == "bytes2" || - primitiveType == "bytes2[]" || - primitiveType == "bytes3" || - primitiveType == "bytes3[]" || - primitiveType == "bytes4" || - primitiveType == "bytes4[]" || - primitiveType == "bytes5" || - primitiveType == "bytes5[]" || - primitiveType == "bytes6" || - primitiveType == "bytes6[]" || - primitiveType == "bytes7" || - primitiveType == "bytes7[]" || - primitiveType == "bytes8" || - primitiveType == "bytes8[]" || - primitiveType == "bytes9" || - primitiveType == "bytes9[]" || - primitiveType == "bytes10" || - primitiveType == "bytes10[]" || - primitiveType == "bytes11" || - primitiveType == "bytes11[]" || - primitiveType == "bytes12" || - primitiveType == "bytes12[]" || - primitiveType == "bytes13" || - primitiveType == "bytes13[]" || - primitiveType == "bytes14" || - primitiveType == "bytes14[]" || - primitiveType == "bytes15" || - primitiveType == "bytes15[]" || - primitiveType == "bytes16" || - primitiveType == "bytes16[]" || - primitiveType == "bytes17" || - primitiveType == "bytes17[]" || - primitiveType == "bytes18" || - primitiveType == "bytes18[]" || - primitiveType == "bytes19" || - primitiveType == "bytes19[]" || - primitiveType == "bytes20" || - primitiveType == "bytes20[]" || - primitiveType == "bytes21" || - primitiveType == "bytes21[]" || - primitiveType == "bytes22" || - primitiveType == "bytes22[]" || - primitiveType == "bytes23" || - primitiveType == "bytes23[]" || - primitiveType == "bytes24" || - primitiveType == "bytes24[]" || - primitiveType == "bytes25" || - primitiveType == "bytes25[]" || - primitiveType == "bytes26" || - primitiveType == "bytes26[]" || - primitiveType == "bytes27" || - primitiveType == "bytes27[]" || - primitiveType == "bytes28" || - primitiveType == "bytes28[]" || - primitiveType == "bytes29" || - primitiveType == "bytes29[]" || - primitiveType == "bytes30" || - primitiveType == "bytes30[]" || - primitiveType == "bytes31" || - primitiveType == "bytes31[]" || - primitiveType == "bytes32" || - primitiveType == "bytes32[]" { - return true - } - if primitiveType == "int" || - primitiveType == "int[]" || - primitiveType == "int8" || - primitiveType == "int8[]" || - primitiveType == "int16" || - primitiveType == "int16[]" || - primitiveType == "int32" || - primitiveType == "int32[]" || - primitiveType == "int64" || - primitiveType == "int64[]" || - primitiveType == "int128" || - primitiveType == "int128[]" || - primitiveType == "int256" || - primitiveType == "int256[]" { - return true - } - if primitiveType == "uint" || - primitiveType == "uint[]" || - primitiveType == "uint8" || - primitiveType == "uint8[]" || - primitiveType == "uint16" || - primitiveType == "uint16[]" || - primitiveType == "uint32" || - primitiveType == "uint32[]" || - primitiveType == "uint64" || - primitiveType == "uint64[]" || - primitiveType == "uint128" || - primitiveType == "uint128[]" || - primitiveType == "uint256" || - primitiveType == "uint256[]" { - return true - } - return false -} - -// validate checks if the given domain is valid, i.e. contains at least -// the minimum viable keys and values -func (domain *TypedDataDomain) validate() error { - if domain.ChainId == nil && len(domain.Name) == 0 && len(domain.Version) == 0 && len(domain.VerifyingContract) == 0 && len(domain.Salt) == 0 { - return errors.New("domain is undefined") - } - - return nil -} - -// Map is a helper function to generate a map version of the domain -func (domain *TypedDataDomain) Map() map[string]interface{} { - dataMap := map[string]interface{}{} - - if domain.ChainId != nil { - dataMap["chainId"] = domain.ChainId - } - - if len(domain.Name) > 0 { - dataMap["name"] = domain.Name - } - - if len(domain.Version) > 0 { - dataMap["version"] = domain.Version - } - - if len(domain.VerifyingContract) > 0 { - dataMap["verifyingContract"] = domain.VerifyingContract - } - - if len(domain.Salt) > 0 { - dataMap["salt"] = domain.Salt - } - return dataMap -}
diff --git go-ethereum/signer/core/signed_data_test.go celo/signer/core/signed_data_test.go index 0a955632b3c43dd73f8bec64867697b89a12899b..682abbdc9fe24f6494dfcd7c5c6adbd6b67f936f 100644 --- go-ethereum/signer/core/signed_data_test.go +++ celo/signer/core/signed_data_test.go @@ -17,24 +17,18 @@ package core_test   import ( - "bytes" "context" - "encoding/json" - "fmt" - "io/ioutil" - "path" - "strings" "testing"   "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/shared/signer" "github.com/ethereum/go-ethereum/signer/core" )   -var typesStandard = core.Types{ +var typesStandard = signer.Types{ "EIP712Domain": { { Name: "name", @@ -79,86 +73,14 @@ }, }, }   -var jsonTypedData = ` - { - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "test", - "type": "uint8" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "string" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "test": 3, - "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } - } -` - const primaryType = "Mail"   -var domainStandard = core.TypedDataDomain{ - "Ether Mail", - "1", - math.NewHexOrDecimal256(1), - "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", - "", +var domainStandard = signer.TypedDataDomain{ + Name: "Ether Mail", + Version: "1", + ChainId: math.NewHexOrDecimal256(1), + VerifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + Salt: "", }   var messageStandard = map[string]interface{}{ @@ -173,7 +95,7 @@ }, "contents": "Hello, Bob!", }   -var typedData = core.TypedData{ +var typedData = signer.TypedData{ Types: typesStandard, PrimaryType: primaryType, Domain: domainStandard, @@ -230,304 +152,3 @@ if signature == nil || len(signature) != 65 { t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature)) } } - -func TestDomainChainId(t *testing.T) { - withoutChainID := core.TypedData{ - Types: core.Types{ - "EIP712Domain": []core.Type{ - {Name: "name", Type: "string"}, - }, - }, - Domain: core.TypedDataDomain{ - Name: "test", - }, - } - - if _, ok := withoutChainID.Domain.Map()["chainId"]; ok { - t.Errorf("Expected the chainId key to not be present in the domain map") - } - // should encode successfully - if _, err := withoutChainID.HashStruct("EIP712Domain", withoutChainID.Domain.Map()); err != nil { - t.Errorf("Expected the typedData to encode the domain successfully, got %v", err) - } - withChainID := core.TypedData{ - Types: core.Types{ - "EIP712Domain": []core.Type{ - {Name: "name", Type: "string"}, - {Name: "chainId", Type: "uint256"}, - }, - }, - Domain: core.TypedDataDomain{ - Name: "test", - ChainId: math.NewHexOrDecimal256(1), - }, - } - - if _, ok := withChainID.Domain.Map()["chainId"]; !ok { - t.Errorf("Expected the chainId key be present in the domain map") - } - // should encode successfully - if _, err := withChainID.HashStruct("EIP712Domain", withChainID.Domain.Map()); err != nil { - t.Errorf("Expected the typedData to encode the domain successfully, got %v", err) - } -} - -func TestHashStruct(t *testing.T) { - hash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) - if err != nil { - t.Fatal(err) - } - mainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) - if mainHash != "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e" { - t.Errorf("Expected different hashStruct result (got %s)", mainHash) - } - - hash, err = typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) - if err != nil { - t.Error(err) - } - domainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) - if domainHash != "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f" { - t.Errorf("Expected different domain hashStruct result (got %s)", domainHash) - } -} - -func TestEncodeType(t *testing.T) { - domainTypeEncoding := string(typedData.EncodeType("EIP712Domain")) - if domainTypeEncoding != "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" { - t.Errorf("Expected different encodeType result (got %s)", domainTypeEncoding) - } - - mailTypeEncoding := string(typedData.EncodeType(typedData.PrimaryType)) - if mailTypeEncoding != "Mail(Person from,Person to,string contents)Person(string name,address wallet)" { - t.Errorf("Expected different encodeType result (got %s)", mailTypeEncoding) - } -} - -func TestTypeHash(t *testing.T) { - mailTypeHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.TypeHash(typedData.PrimaryType))) - if mailTypeHash != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2" { - t.Errorf("Expected different typeHash result (got %s)", mailTypeHash) - } -} - -func TestEncodeData(t *testing.T) { - hash, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0) - if err != nil { - t.Fatal(err) - } - dataEncoding := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) - if dataEncoding != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8" { - t.Errorf("Expected different encodeData result (got %s)", dataEncoding) - } -} - -func TestFormatter(t *testing.T) { - var d core.TypedData - err := json.Unmarshal([]byte(jsonTypedData), &d) - if err != nil { - t.Fatalf("unmarshalling failed '%v'", err) - } - formatted, _ := d.Format() - for _, item := range formatted { - t.Logf("'%v'\n", item.Pprint(0)) - } - - j, _ := json.Marshal(formatted) - t.Logf("'%v'\n", string(j)) -} - -func sign(typedData core.TypedData) ([]byte, []byte, error) { - domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) - if err != nil { - return nil, nil, err - } - typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) - if err != nil { - return nil, nil, err - } - rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) - sighash := crypto.Keccak256(rawData) - return typedDataHash, sighash, nil -} - -func TestJsonFiles(t *testing.T) { - testfiles, err := ioutil.ReadDir("testdata/") - if err != nil { - t.Fatalf("failed reading files: %v", err) - } - for i, fInfo := range testfiles { - if !strings.HasSuffix(fInfo.Name(), "json") { - continue - } - expectedFailure := strings.HasPrefix(fInfo.Name(), "expfail") - data, err := ioutil.ReadFile(path.Join("testdata", fInfo.Name())) - if err != nil { - t.Errorf("Failed to read file %v: %v", fInfo.Name(), err) - continue - } - var typedData core.TypedData - err = json.Unmarshal(data, &typedData) - if err != nil { - t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err) - continue - } - _, _, err = sign(typedData) - t.Logf("Error %v\n", err) - if err != nil && !expectedFailure { - t.Errorf("Test %d failed, file %v: %v", i, fInfo.Name(), err) - } - if expectedFailure && err == nil { - t.Errorf("Test %d succeeded (expected failure), file %v: %v", i, fInfo.Name(), err) - } - } -} - -// TestFuzzerFiles tests some files that have been found by fuzzing to cause -// crashes or hangs. -func TestFuzzerFiles(t *testing.T) { - corpusdir := path.Join("testdata", "fuzzing") - testfiles, err := ioutil.ReadDir(corpusdir) - if err != nil { - t.Fatalf("failed reading files: %v", err) - } - verbose := false - for i, fInfo := range testfiles { - data, err := ioutil.ReadFile(path.Join(corpusdir, fInfo.Name())) - if err != nil { - t.Errorf("Failed to read file %v: %v", fInfo.Name(), err) - continue - } - var typedData core.TypedData - err = json.Unmarshal(data, &typedData) - if err != nil { - t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err) - continue - } - _, err = typedData.EncodeData("EIP712Domain", typedData.Domain.Map(), 1) - if verbose && err != nil { - t.Logf("%d, EncodeData[1] err: %v\n", i, err) - } - _, err = typedData.EncodeData(typedData.PrimaryType, typedData.Message, 1) - if verbose && err != nil { - t.Logf("%d, EncodeData[2] err: %v\n", i, err) - } - typedData.Format() - } -} - -var gnosisTypedData = ` -{ - "types": { - "EIP712Domain": [ - { "type": "address", "name": "verifyingContract" } - ], - "SafeTx": [ - { "type": "address", "name": "to" }, - { "type": "uint256", "name": "value" }, - { "type": "bytes", "name": "data" }, - { "type": "uint8", "name": "operation" }, - { "type": "uint256", "name": "safeTxGas" }, - { "type": "uint256", "name": "baseGas" }, - { "type": "uint256", "name": "gasPrice" }, - { "type": "address", "name": "gasToken" }, - { "type": "address", "name": "refundReceiver" }, - { "type": "uint256", "name": "nonce" } - ] - }, - "domain": { - "verifyingContract": "0x25a6c4BBd32B2424A9c99aEB0584Ad12045382B3" - }, - "primaryType": "SafeTx", - "message": { - "to": "0x9eE457023bB3De16D51A003a247BaEaD7fce313D", - "value": "20000000000000000", - "data": "0x", - "operation": 0, - "safeTxGas": 27845, - "baseGas": 0, - "gasPrice": "0", - "gasToken": "0x0000000000000000000000000000000000000000", - "refundReceiver": "0x0000000000000000000000000000000000000000", - "nonce": 3 - } -}` - -var gnosisTx = ` -{ - "safe": "0x25a6c4BBd32B2424A9c99aEB0584Ad12045382B3", - "to": "0x9eE457023bB3De16D51A003a247BaEaD7fce313D", - "value": "20000000000000000", - "data": null, - "operation": 0, - "gasToken": "0x0000000000000000000000000000000000000000", - "safeTxGas": 27845, - "baseGas": 0, - "gasPrice": "0", - "refundReceiver": "0x0000000000000000000000000000000000000000", - "nonce": 3, - "executionDate": null, - "submissionDate": "2020-09-15T21:59:23.815748Z", - "modified": "2020-09-15T21:59:23.815748Z", - "blockNumber": null, - "transactionHash": null, - "safeTxHash": "0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f", - "executor": null, - "isExecuted": false, - "isSuccessful": null, - "ethGasPrice": null, - "gasUsed": null, - "fee": null, - "origin": null, - "dataDecoded": null, - "confirmationsRequired": null, - "confirmations": [ - { - "owner": "0xAd2e180019FCa9e55CADe76E4487F126Fd08DA34", - "submissionDate": "2020-09-15T21:59:28.281243Z", - "transactionHash": null, - "confirmationType": "CONFIRMATION", - "signature": "0x5e562065a0cb15d766dac0cd49eb6d196a41183af302c4ecad45f1a81958d7797753f04424a9b0aa1cb0448e4ec8e189540fbcdda7530ef9b9d95dfc2d36cb521b", - "signatureType": "EOA" - } - ], - "signatures": null - } -` - -// TestGnosisTypedData tests the scenario where a user submits a full EIP-712 -// struct without using the gnosis-specific endpoint -func TestGnosisTypedData(t *testing.T) { - var td core.TypedData - err := json.Unmarshal([]byte(gnosisTypedData), &td) - if err != nil { - t.Fatalf("unmarshalling failed '%v'", err) - } - _, sighash, err := sign(td) - if err != nil { - t.Fatal(err) - } - expSigHash := common.FromHex("0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f") - if !bytes.Equal(expSigHash, sighash) { - t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) - } -} - -// TestGnosisCustomData tests the scenario where a user submits only the gnosis-safe -// specific data, and we fill the TypedData struct on our side -func TestGnosisCustomData(t *testing.T) { - var tx core.GnosisSafeTx - err := json.Unmarshal([]byte(gnosisTx), &tx) - if err != nil { - t.Fatal(err) - } - var td = tx.ToTypedData() - _, sighash, err := sign(td) - if err != nil { - t.Fatal(err) - } - expSigHash := common.FromHex("0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f") - if !bytes.Equal(expSigHash, sighash) { - t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) - } -}
diff --git go-ethereum/signer/core/auditlog.go celo/signer/core/auditlog.go index 84877ee712cf43b71fbe91651939b48e0368af0c..dff2697c73d8338996e739541ae51ba51b16ffdd 100644 --- go-ethereum/signer/core/auditlog.go +++ celo/signer/core/auditlog.go @@ -24,6 +24,7 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/shared/signer" "github.com/ethereum/go-ethereum/signer/core/apitypes" )   @@ -89,7 +90,7 @@ } return res, e }   -func (l *AuditLogger) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data TypedData) (hexutil.Bytes, error) { +func (l *AuditLogger) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data signer.TypedData) (hexutil.Bytes, error) { l.log.Info("SignTypedData", "type", "request", "metadata", MetadataFromContext(ctx).String(), "addr", addr.String(), "data", data) b, e := l.api.SignTypedData(ctx, addr, data)
diff --git go-ethereum/signer/fourbyte/validation.go celo/signer/fourbyte/validation.go index 58111e8e00c82adbf19667805cda1c27ac5482f1..e765dcf6ffabf8c9dff46be8ae5ba848d2868011 100644 --- go-ethereum/signer/fourbyte/validation.go +++ celo/signer/fourbyte/validation.go @@ -32,6 +32,10 @@ // should be immediately rejected). func (db *Database) ValidateTransaction(selector *string, tx *apitypes.SendTxArgs) (*apitypes.ValidationMessages, error) { messages := new(apitypes.ValidationMessages)   + // Reject if EthCompatible is true but the transaction has non-nil-or-0 values for the non-eth-compatible fields + if err := tx.CheckEthCompatibility(); err != nil { + return nil, err + } // Prevent accidental erroneous usage of both 'input' and 'data' (show stopper) if tx.Data != nil && tx.Input != nil && !bytes.Equal(*tx.Data, *tx.Input) { return nil, errors.New(`ambiguous request: both "data" and "input" are set and are not identical`)
diff --git go-ethereum/signer/rules/rules_test.go celo/signer/rules/rules_test.go index 506693790029295c48dd368ae0208b7269f088f6..f2c55a539538a453af66ed05d8d3855d44a1bb39 100644 --- go-ethereum/signer/rules/rules_test.go +++ celo/signer/rules/rules_test.go @@ -27,6 +27,7 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/shared/signer" "github.com/ethereum/go-ethereum/signer/core" "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/ethereum/go-ethereum/signer/storage" @@ -459,7 +460,8 @@ to := common.HexToAddress("000000000000000000000000000000000000dead") gas := uint64(21000) gasPrice := big.NewInt(2000000) data := make([]byte, 0) - return types.NewTransaction(3, to, value, gas, gasPrice, data) + return types.NewTransaction(3, to, value, gas, gasPrice, nil, nil, nil, data) + }   func TestLimitWindow(t *testing.T) { @@ -605,7 +607,7 @@ addr, _ := mixAddr("0x694267f14675d7e1b9494fd8d72fefe1755710fa")   t.Logf("address %v %v\n", addr.String(), addr.Original())   - nvt := []*core.NameValueType{ + nvt := []*signer.NameValueType{ { Name: "message", Typ: "text/plain",
diff --git go-ethereum/signer/core/apitypes/types.go celo/signer/core/apitypes/types.go index 4763b71c959b0803867b5c89880bd36fef3e3238..ccedadd380382e2931678c2c7a9f71ce974796df 100644 --- go-ethereum/signer/core/apitypes/types.go +++ celo/signer/core/apitypes/types.go @@ -19,12 +19,12 @@ import ( "encoding/json" "fmt" - "math/big" "strings"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/internal/ethapi" )   type ValidationInfo struct { @@ -75,9 +75,12 @@ Gas hexutil.Uint64 `json:"gas"` GasPrice *hexutil.Big `json:"gasPrice"` MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` + FeeCurrency *common.MixedcaseAddress `json:"feeCurrency"` + GatewayFeeRecipient *common.MixedcaseAddress `json:"gatewayFeeRecipient"` + GatewayFee hexutil.Big `json:"gatewayFee"` Value hexutil.Big `json:"value"` Nonce hexutil.Uint64 `json:"nonce"` - + EthCompatible bool `json:"ethCompatible"` // We accept "data" and "input" for backwards-compatibility reasons. // "input" is the newer name and should be preferred by clients. // Issue detail: https://github.com/ethereum/go-ethereum/issues/15628 @@ -97,60 +100,40 @@ } return err.Error() }   -// ToTransaction converts the arguments to a transaction. +func (args SendTxArgs) CheckEthCompatibility() error { + return args.ToTransaction().CheckEthCompatibility() +} + func (args *SendTxArgs) ToTransaction() *types.Transaction { + // Upstream refactored this method to copy what txArgs.ToTransaction is doing in + // bb1f7ebf203f40dae714a3b8445918cfcfc9a7db in order to be able to compile the code to + // WebAssembly. Duplicating this logic right now does not seem to be worth it for celo + // use case, so the same version as before is maintained. + txArgs := ethapi.TransactionArgs{ + Gas: &args.Gas, + GasPrice: args.GasPrice, + MaxFeePerGas: args.MaxFeePerGas, + MaxPriorityFeePerGas: args.MaxPriorityFeePerGas, + GatewayFee: &args.GatewayFee, + Value: &args.Value, + Nonce: &args.Nonce, + Data: args.Data, + Input: args.Input, + AccessList: args.AccessList, + ChainID: args.ChainID, + } + if args.FeeCurrency != nil { + feeCurrency := args.FeeCurrency.Address() + txArgs.FeeCurrency = &feeCurrency + } + if args.GatewayFeeRecipient != nil { + gatewayFeeRecipient := args.GatewayFeeRecipient.Address() + txArgs.GatewayFeeRecipient = &gatewayFeeRecipient + } // Add the To-field, if specified - var to *common.Address if args.To != nil { - dstAddr := args.To.Address() - to = &dstAddr + to := args.To.Address() + txArgs.To = &to } - - var input []byte - if args.Input != nil { - input = *args.Input - } else if args.Data != nil { - input = *args.Data - } - - var data types.TxData - switch { - case args.MaxFeePerGas != nil: - al := types.AccessList{} - if args.AccessList != nil { - al = *args.AccessList - } - data = &types.DynamicFeeTx{ - To: to, - ChainID: (*big.Int)(args.ChainID), - Nonce: uint64(args.Nonce), - Gas: uint64(args.Gas), - GasFeeCap: (*big.Int)(args.MaxFeePerGas), - GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas), - Value: (*big.Int)(&args.Value), - Data: input, - AccessList: al, - } - case args.AccessList != nil: - data = &types.AccessListTx{ - To: to, - ChainID: (*big.Int)(args.ChainID), - Nonce: uint64(args.Nonce), - Gas: uint64(args.Gas), - GasPrice: (*big.Int)(args.GasPrice), - Value: (*big.Int)(&args.Value), - Data: input, - AccessList: *args.AccessList, - } - default: - data = &types.LegacyTx{ - To: to, - Nonce: uint64(args.Nonce), - Gas: uint64(args.Gas), - GasPrice: (*big.Int)(args.GasPrice), - Value: (*big.Int)(&args.Value), - Data: input, - } - } - return types.NewTx(data) + return txArgs.ToTransaction() }
diff --git go-ethereum/test/account.go celo/test/account.go new file mode 100644 index 0000000000000000000000000000000000000000..971948e298cda628acddd8c0fd2d90a073b1856d --- /dev/null +++ celo/test/account.go @@ -0,0 +1,130 @@ +package test + +import ( + "context" + "crypto/ecdsa" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/mycelo/env" + "github.com/ethereum/go-ethereum/params" +) + +type Account struct { + Address common.Address + Key *ecdsa.PrivateKey + ChainConfig *params.ChainConfig + Nonce *uint64 +} + +func NewAccount(key *ecdsa.PrivateKey, address common.Address, chainConfig *params.ChainConfig) *Account { + return &Account{ + Address: address, + Key: key, + ChainConfig: chainConfig, + } +} + +// SendCelo submits a value transfer transaction via the provided Node to send +// celo to the recipient. The submitted transaction is returned. +func (a *Account) SendCelo(ctx context.Context, recipient common.Address, value int64, node *Node) (*types.Transaction, error) { + var err error + // Lazy set nonce + if a.Nonce == nil { + a.Nonce = new(uint64) + *a.Nonce, err = node.WsClient.PendingNonceAt(ctx, a.Address) + if err != nil { + return nil, err + } + } + num, err := node.WsClient.BlockNumber(ctx) + if err != nil { + return nil, err + } + + signer := types.MakeSigner(a.ChainConfig, new(big.Int).SetUint64(num)) + tx, err := BuildSignedTransaction( + node.WsClient, + a.Key, + a.Address, + recipient, + *a.Nonce, + big.NewInt(value), + signer, + nil, + ) + + if err != nil { + return nil, err + } + err = node.WsClient.SendTransaction(ctx, tx) + if err != nil { + return nil, err + } + *a.Nonce++ + return tx, nil +} + +// SendCelo submits a value transfer transaction via the provided Node to send +// celo to the recipient. The submitted transaction is returned. +func (a *Account) SendCeloWithDynamicFee(ctx context.Context, recipient common.Address, value int64, gasFeeCap *big.Int, gasTipCap *big.Int, node *Node) (*types.Transaction, error) { + var err error + // Lazy set nonce + if a.Nonce == nil { + a.Nonce = new(uint64) + *a.Nonce, err = node.WsClient.PendingNonceAt(ctx, a.Address) + if err != nil { + return nil, err + } + } + num, err := node.WsClient.BlockNumber(ctx) + if err != nil { + return nil, err + } + + signer := types.MakeSigner(a.ChainConfig, new(big.Int).SetUint64(num)) + tx, err := ValueTransferTransactionWithDynamicFee( + node.WsClient, + a.Key, + a.Address, + recipient, + *a.Nonce, + big.NewInt(value), + gasFeeCap, + gasTipCap, + signer) + + if err != nil { + return nil, err + } + err = node.WsClient.SendTransaction(ctx, tx) + if err != nil { + return nil, err + } + *a.Nonce++ + return tx, nil +} + +// SendCeloTracked functions like SendCelo but also waits for the transaction +// to be processed by the sending node. +func (a *Account) SendCeloTracked(ctx context.Context, recipient common.Address, value int64, node *Node) (*types.Transaction, error) { + tx, err := a.SendCelo(ctx, recipient, value, node) + if err != nil { + return nil, err + } + err = node.AwaitTransactions(ctx, tx) + if err != nil { + return nil, err + } + return node.Tracker.GetProcessedTx(tx.Hash()), nil +} + +// Accounts converts a slice of env.Account objects to Account objects. +func Accounts(accts []env.Account, chainConfig *params.ChainConfig) []*Account { + accounts := make([]*Account, 0, len(accts)) + for _, a := range accts { + accounts = append(accounts, NewAccount(a.PrivateKey, a.Address, chainConfig)) + } + return accounts +}
(new)
+607
-0
diff --git go-ethereum/test/node.go celo/test/node.go new file mode 100644 index 0000000000000000000000000000000000000000..80c670d0927770139dfc4c827b6ac41028d31746 --- /dev/null +++ celo/test/node.go @@ -0,0 +1,607 @@ +package test + +import ( + "context" + "crypto/ecdsa" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/eth/ethconfig" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/backend" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/mycelo/env" + "github.com/ethereum/go-ethereum/mycelo/genesis" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + allModules = []string{"admin", "debug", "web3", "eth", "txpool", "personal", "istanbul", "miner", "net"} + baseNodeConfig *node.Config = &node.Config{ + Name: "celo", + Version: params.Version, + P2P: p2p.Config{ + MaxPeers: 100, + NoDiscovery: true, + ListenAddr: "0.0.0.0:0", + InboundThrottleTime: 200 * time.Millisecond, + DialHistoryExpiration: 210 * time.Millisecond, + }, + NoUSB: true, + // It is important that HTTPHost and WSHost remain the same. This + // ensures that only one server is started up on the HTTPHost. That one + // server can still handle both http and ws connectons. + HTTPHost: "0.0.0.0", + WSHost: "0.0.0.0", + UsePlaintextKeystore: true, + WSModules: allModules, + HTTPModules: allModules, + } + + BaseEthConfig = &eth.Config{ + SyncMode: downloader.FullSync, + MinSyncPeers: 1, + DatabaseCache: 256, + DatabaseHandles: 256, + TxPool: core.DefaultTxPoolConfig, + RPCEthCompatibility: true, + Istanbul: istanbul.Config{ + Validator: true, + // Set announce gossip period to 1 minute, if not set this results + // in a panic when trying to set a timer with a zero value. We + // don't actually rely on this mechanism in the tests because we + // pre share all the enode certificates. + AnnounceQueryEnodeGossipPeriod: 60, + + // 50ms is a really low timeout, we set this here because nodes + // fail the first round of consensus and so setting this higher + // makes tests run slower. + RequestTimeout: 200, + TimeoutBackoffFactor: 200, + MinResendRoundChangeTimeout: 200, + MaxResendRoundChangeTimeout: 10000, + Epoch: 20, + ProposerPolicy: istanbul.ShuffledRoundRobin, + BlockPeriod: 0, + }, + } +) + +// Node provides an enhanced interface to node.Node with useful additions, the +// *node.Node is embedded so that its api is available through Node. +type Node struct { + *node.Node + Config *node.Config + P2PListenAddr string + Enode *enode.Node + Eth *eth.Ethereum + EthConfig *eth.Config + WsClient *ethclient.Client + Key *ecdsa.PrivateKey + Address common.Address + Tracker *Tracker + // The transactions that this node has sent. + SentTxs []*types.Transaction +} + +// NewNode creates a new running node with the provided config. +func NewNode( + validatorAccount *env.Account, + nc *node.Config, + ec *eth.Config, + genesis *core.Genesis, +) (*Node, error) { + + // Copy the node config so we can modify it without damaging the original + ncCopy := *nc + + // p2p key and address, this is not the same as the validator key. + p2pKey, err := crypto.GenerateKey() + if err != nil { + return nil, err + } + ncCopy.P2P.PrivateKey = p2pKey + + // Make temp datadir + datadir, err := ioutil.TempDir("", "celo_datadir") + if err != nil { + return nil, err + } + ncCopy.DataDir = datadir + + // copy the base eth config, so we can modify it without damaging the + // original. + ecCopy := &eth.Config{} + err = copyObject(ec, ecCopy) + if err != nil { + return nil, err + } + ecCopy.Genesis = genesis + ecCopy.NetworkId = genesis.Config.ChainID.Uint64() + ecCopy.Miner.Validator = validatorAccount.Address + ecCopy.TxFeeRecipient = validatorAccount.Address + + node := &Node{ + Config: &ncCopy, + EthConfig: ecCopy, + Key: validatorAccount.PrivateKey, + Address: validatorAccount.Address, + Tracker: NewTracker(), + } + + return node, node.Start() +} + +// Start creates the node.Node and eth.Ethereum and starts the node.Node and +// starts eth.Ethereum mining. +func (n *Node) Start() error { + // Provide a copy of the config to node.New, so that we can rely on + // Node.Config field not being manipulated by node and hence use our copy + // for black box testing. + nodeConfigCopy := &node.Config{} + err := copyNodeConfig(n.Config, nodeConfigCopy) + if err != nil { + return err + } + + n.Node, err = node.New(nodeConfigCopy) + if err != nil { + return err + } + + // This registers the ethereum service on n.Node, so that calling + // n.Node.Stop will also close the eth service. Again we provide a copy of + // the EthConfig so that we can use our copy for black box testing. + ethConfigCopy := &eth.Config{} + err = copyObject(n.EthConfig, ethConfigCopy) + if err != nil { + return err + } + + // Register eth service + n.Eth, err = eth.New(n.Node, ethConfigCopy) + if err != nil { + return err + } + // This manual step is required to enable tracing, it's messy but this is the + // approach taken by geth in cmd/utils.RegisterEthService. + n.Node.RegisterAPIs(tracers.APIs(n.Eth.APIBackend)) + + err = n.Node.Start() + if err != nil { + return err + } + + // The ListenAddr is set at p2p server startup, save it here. + n.P2PListenAddr = n.Node.Server().ListenAddr + + // Import the node key into the keystore and then unlock it, the keystore + // is the interface used for signing operations so the node key needs to be + // inside it. + ks, _, err := n.Config.GetKeyStore() + if err != nil { + return err + } + n.AccountManager().AddBackend(ks) + account, err := ks.ImportECDSA(n.Key, "") + if err != nil { + return err + } + err = ks.TimedUnlock(account, "", 0) + if err != nil { + return err + } + + _, _, err = core.SetupGenesisBlock(n.Eth.ChainDb(), n.EthConfig.Genesis) + if err != nil { + return err + } + n.WsClient, err = ethclient.Dial(n.WSEndpoint()) + if err != nil { + return err + } + err = n.Tracker.StartTracking(n.WsClient) + if err != nil { + return err + } + err = n.Eth.StartMining() + if err != nil { + return err + } + + // Note we need to use the LocalNode from the p2p server because that is + // what is also used by the announce protocol when building enode + // certificates, so that is what is used by the announce protocol to check + // if a validator enode has changed. If we constructed the enode ourselves + // here we could not be sure it matches the enode from the p2p.Server. + n.Enode = n.Server().LocalNode().Node() + + return nil +} + +func (n *Node) AddPeers(nodes ...*Node) { + // Add the given nodes as peers. Although this means that nodes can reach + // each other nodes don't start sending consensus messages to another node + // until they have received an enode certificate from that node. + for _, no := range nodes { + n.Server().AddPeer(no.Enode, p2p.ValidatorPurpose) + } +} + +// GossipEnodeCertificatge gossips this nodes enode certificates to the rest of +// the network. +func (n *Node) GossipEnodeCertificatge() error { + enodeCertificate := &istanbul.EnodeCertificate{ + EnodeURL: n.Enode.URLv4(), + Version: uint(time.Now().Unix()), + } + enodeCertificateBytes, err := rlp.EncodeToBytes(enodeCertificate) + if err != nil { + return err + } + msg := &istanbul.Message{ + Code: istanbul.EnodeCertificateMsg, + Address: n.Address, + Msg: enodeCertificateBytes, + } + b := n.Eth.Engine().(*backend.Backend) + if err := msg.Sign(b.Sign); err != nil { + return err + } + payload, err := msg.Payload() + if err != nil { + return err + } + // Share enode certificates to the other nodes, nodes wont consider other + // nodes valid validators without seeing an enode certificate message from + // them. + return b.Gossip(payload, istanbul.EnodeCertificateMsg) +} + +// Close shuts down the node and releases all resources and removes the datadir +// unless an error is returned, in which case there is no guarantee that all +// resources are released. It is assumed that this is only called after calling +// Start otherwise it will panic. +func (n *Node) Close() error { + err := n.Tracker.StopTracking() + if err != nil { + return err + } + n.WsClient.Close() + err = n.Node.Close() // This also shuts down the Eth service + if err != nil { + return err + } + return os.RemoveAll(n.Config.DataDir) +} + +// AwaitTransactions awaits all the provided transactions. +func (n *Node) AwaitTransactions(ctx context.Context, txs ...*types.Transaction) error { + sentHashes := make([]common.Hash, len(txs)) + for i, tx := range txs { + sentHashes[i] = tx.Hash() + } + return n.Tracker.AwaitTransactions(ctx, sentHashes) +} + +// ProcessedTxBlock returns the block that the given transaction was processed +// in, nil will be retuned if the transaction has not been processed by this node. +func (n *Node) ProcessedTxBlock(tx *types.Transaction) *types.Block { + return n.Tracker.GetProcessedBlockForTx(tx.Hash()) +} + +// TxFee returns the gas fee for the given transaction. +func (n *Node) TxFee(ctx context.Context, tx *types.Transaction) (*big.Int, error) { + r, err := n.WsClient.TransactionReceipt(ctx, tx.Hash()) + if err != nil { + return nil, err + } + return big.NewInt(0).Mul(new(big.Int).SetUint64(r.GasUsed), tx.GasPrice()), nil +} + +// Network represents a network of nodes and provides functionality to easily +// create, start and stop a collection of nodes. +type Network []*Node + +func AccountConfig(numValidators, numExternal int) *env.AccountsConfig { + return &env.AccountsConfig{ + Mnemonic: env.MustNewMnemonic(), + NumValidators: numValidators, + ValidatorsPerGroup: 1, + NumDeveloperAccounts: numExternal, + } +} + +// BuildConfig generates genesis and eth config instances, that can be modified +// before passing to NewNetwork or NewNode. +// +// NOTE: Do not edit the Istanbul field of the returned genesis config it will +// be overwritten with the corresponding config from the Istanbul field of the +// returned eth config. +func BuildConfig(accounts *env.AccountsConfig) (*genesis.Config, *ethconfig.Config, error) { + gc := genesis.CreateCommonGenesisConfig( + big.NewInt(1), + accounts.AdminAccount().Address, + params.IstanbulConfig{}, + ) + gc.Hardforks.EspressoBlock = common.Big0 + + genesis.FundAccounts(gc, accounts.DeveloperAccounts()) + + // copy the base eth config, so we can modify it without damaging the + // original. + ec := &eth.Config{} + err := copyObject(BaseEthConfig, ec) + return gc, ec, err +} + +// GenerateGenesis checks that the contractsBuildPath exists and if so proceeds to generate the genesis. +func GenerateGenesis(accounts *env.AccountsConfig, gc *genesis.Config, contractsBuildPath string) (*core.Genesis, error) { + // Check for the existence of the compiled-system-contracts dir + _, err := os.Stat(contractsBuildPath) + if errors.Is(err, os.ErrNotExist) { + abs, err := filepath.Abs(contractsBuildPath) + if err != nil { + panic(fmt.Sprintf("failed to get abs path for %s, error: %v", contractsBuildPath, err)) + } + return nil, fmt.Errorf("Could not find dir %s, try running 'make prepare-system-contracts' and then re-running the test", abs) + + } + return genesis.GenerateGenesis(accounts, gc, contractsBuildPath) +} + +// NewNetwork generates a network of nodes that are running and mining. For +// each provided validator account a corresponding node is created and each +// node is also assigned a developer account, there must be at least as many +// developer accounts provided as validator accounts. A shutdown function is +// also returned which will shutdown and clean up the network when called. In +// the case that an error is returned the shutdown function will be nil and so +// no attempt should be made to call it. +func NewNetwork(accounts *env.AccountsConfig, gc *genesis.Config, ec *eth.Config) (Network, func(), error) { + + // Copy eth istanbul config fields to the genesis istanbul config. + // There is a ticket to remove this duplication of config. + // https://github.com/ethereum/go-ethereum/issues/1693 + gc.Istanbul = params.IstanbulConfig{ + Epoch: ec.Istanbul.Epoch, + ProposerPolicy: uint64(ec.Istanbul.ProposerPolicy), + BlockPeriod: ec.Istanbul.BlockPeriod, + RequestTimeout: ec.Istanbul.RequestTimeout, + } + + genesis, err := GenerateGenesis(accounts, gc, "../compiled-system-contracts") + if err != nil { + return nil, nil, fmt.Errorf("failed to generate genesis: %v", err) + } + + va := accounts.ValidatorAccounts() + var network Network = make([]*Node, len(va)) + + for i := range va { + n, err := NewNode(&va[i], baseNodeConfig, ec, genesis) + if err != nil { + return nil, nil, fmt.Errorf("failed to build node for network: %v", err) + } + network[i] = n + } + + // Connect nodes to each other, although this means that nodes can reach + // each other nodes don't start sending consensus messages to another node + // until they have received an enode certificate from that node. + for i := range network { + network[i].AddPeers(network[i+1:]...) + } + + // Give nodes some time to connect. Also there is a race condition in + // miner.worker its field snapshotBlock is set only when new transactions + // are received or commitNewWork is called. But both of these happen in + // goroutines separate to the call to miner.Start and miner.Start does not + // wait for snapshotBlock to be set. Therefore there is currently no way to + // know when it is safe to call estimate gas. What we do here is sleep a + // bit and cross our fingers. + time.Sleep(25 * time.Millisecond) + + for i := range network { + err := network[i].GossipEnodeCertificatge() + if err != nil { + network.Shutdown() + return nil, nil, err + } + } + + shutdown := func() { + for _, err := range network.Shutdown() { + fmt.Println(err.Error()) + } + } + + return network, shutdown, nil +} + +// AwaitTransactions ensures that the entire network has processed the provided transactions. +func (n Network) AwaitTransactions(ctx context.Context, txs ...*types.Transaction) error { + for _, node := range n { + err := node.AwaitTransactions(ctx, txs...) + if err != nil { + return err + } + } + return nil +} + +// AwaitBlock ensures that the entire network has processed a block with the given num. +func (n Network) AwaitBlock(ctx context.Context, num uint64) error { + for _, node := range n { + err := node.Tracker.AwaitBlock(ctx, num) + if err != nil { + return err + } + } + return nil +} + +// Shutdown closes all nodes in the network, any errors encountered when +// shutting down nodes are returned in a slice. +func (n Network) Shutdown() []error { + var errors []error + for i, node := range n { + if node != nil { + err := node.Close() + if err != nil { + errors = append(errors, fmt.Errorf("error shutting down node %v index %d: %w", node.Address.String(), i, err)) + } + } + } + return errors +} + +// Uses the client to suggest a gas price and to estimate the gas. +func BuildSignedTransaction( + client *ethclient.Client, + senderKey *ecdsa.PrivateKey, + sender, + recipient common.Address, + nonce uint64, + value *big.Int, + signer types.Signer, + data []byte, +) (*types.Transaction, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + defer cancel() + // Figure out the gas allowance and gas price values + gasPrice, err := client.SuggestGasPrice(ctx) + if err != nil { + return nil, fmt.Errorf("failed to suggest gas price: %v", err) + } + + msg := ethereum.CallMsg{From: sender, To: &recipient, GasPrice: gasPrice, Value: value, Data: data} + gasLimit, err := client.EstimateGas(ctx, msg) + if err != nil { + return nil, fmt.Errorf("failed to estimate gas needed: %v", err) + } + // Create the transaction and sign it + rawTx := types.NewTransactionEthCompatible(nonce, recipient, value, gasLimit, gasPrice, data) + signed, err := types.SignTx(rawTx, signer, senderKey) + if err != nil { + return nil, err + } + return signed, nil +} + +// ValueTransferTransactionWithDynamicFee builds a signed value transfer transaction +// from the sender to the recipient with the given value, nonce, gasFeeCap and gasTipCap. +func ValueTransferTransactionWithDynamicFee( + client *ethclient.Client, + senderKey *ecdsa.PrivateKey, + sender, + recipient common.Address, + nonce uint64, + value *big.Int, + gasFeeCap *big.Int, + gasTipCap *big.Int, + signer types.Signer, +) (*types.Transaction, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + defer cancel() + + msg := ethereum.CallMsg{From: sender, To: &recipient, Value: value} + gasLimit, err := client.EstimateGas(ctx, msg) + if err != nil { + return nil, fmt.Errorf("failed to estimate gas needed: %v", err) + } + // Create the transaction and sign it + rawTx := types.NewTx(&types.DynamicFeeTx{ + Nonce: nonce, + To: &recipient, + Value: value, + Gas: gasLimit, + GasFeeCap: gasFeeCap, + GasTipCap: gasTipCap, + }) + signed, err := types.SignTx(rawTx, signer, senderKey) + if err != nil { + return nil, err + } + return signed, nil +} + +// Since the node config is not marshalable by default we construct a +// marshalable struct which we marshal and unmarshal and then unpack into the +// original struct type. +func copyNodeConfig(source, dest *node.Config) error { + s := &MarshalableNodeConfig{} + s.Config = *source + p := MarshalableP2PConfig{} + p.Config = source.P2P + + p.PrivateKey = (*MarshalableECDSAPrivateKey)(source.P2P.PrivateKey) + s.P2P = p + data, err := json.Marshal(s) + if err != nil { + return err + } + u := new(MarshalableNodeConfig) + err = json.Unmarshal(data, u) + if err != nil { + return err + } + *dest = u.Config + dest.P2P = u.P2P.Config + dest.P2P.PrivateKey = (*ecdsa.PrivateKey)(u.P2P.PrivateKey) + return nil +} + +type MarshalableNodeConfig struct { + node.Config + P2P MarshalableP2PConfig +} + +type MarshalableP2PConfig struct { + p2p.Config + PrivateKey *MarshalableECDSAPrivateKey +} + +type MarshalableECDSAPrivateKey ecdsa.PrivateKey + +func (k *MarshalableECDSAPrivateKey) UnmarshalJSON(b []byte) error { + key, err := crypto.PrivECDSAFromHex(b[1 : len(b)-1]) + if err != nil { + return err + } + *k = MarshalableECDSAPrivateKey(*key) + return nil +} + +func (k *MarshalableECDSAPrivateKey) MarshalJSON() ([]byte, error) { + return []byte(`"` + hex.EncodeToString(crypto.FromECDSA((*ecdsa.PrivateKey)(k))) + `"`), nil +} + +// copyObject copies an object so that the copy shares no memory with the +// original. +func copyObject(source, dest interface{}) error { + data, err := json.Marshal(source) + if err != nil { + return err + } + return json.Unmarshal(data, dest) +}
diff --git go-ethereum/test/utils.go celo/test/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..4f12bc04242a13d8dd8e48b18eafd57baa0a47e9 --- /dev/null +++ celo/test/utils.go @@ -0,0 +1,79 @@ +package test + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" +) + +type Token int + +type Balances map[common.Address]*big.Int + +// BalanceWatcher is a helper to watch balance changes over addresses. +type BalanceWatcher struct { + accounts []common.Address + client *ethclient.Client + initial Balances + current Balances +} + +// NewBalanceWatcher creates a BalanceWatcher, records initial balances of the accounts. +func NewBalanceWatcher(client *ethclient.Client, addresses []common.Address) *BalanceWatcher { + watcher := &BalanceWatcher{ + client: client, + accounts: addresses, + } + if initial, err := fetch(client, addresses); err != nil { + panic("Unable to initialize Balances") + } else { + watcher.initial = initial + watcher.current = initial + } + + return watcher +} + +// Initial returns initial balance of given address, or nil if address is not found. +func (w *BalanceWatcher) Initial(address common.Address) *big.Int { + if _, ok := w.initial[address]; ok { + if balance, ok := w.initial[address]; ok { + return balance + } + } + return nil +} + +// Current returns current balance of given address. +func (w *BalanceWatcher) Current(address common.Address) *big.Int { + return w.current[address] // TODO: support stable coin +} + +// Delta returns difference between current and initial balance of given address for given token. +func (w *BalanceWatcher) Delta(address common.Address) *big.Int { + return new(big.Int).Sub(w.current[address], w.initial[address]) // TODO: support stable coin +} + +// Update updates current balances in the BalanceWatcher +func (w *BalanceWatcher) Update() { + if current, err := fetch(w.client, w.accounts); err != nil { + panic("Unable to fetch current balances") + } else { + w.current = current + } +} + +// fetch retrieves balances of given addresses. +func fetch(client *ethclient.Client, addresses []common.Address) (Balances, error) { + balances := make(Balances) + for _, address := range addresses { + balance, err := client.BalanceAt(context.Background(), address, nil) + if err != nil { + return nil, err + } + balances[address] = balance // TODO: support stable coin + } + return balances, nil +}
diff --git go-ethereum/test/tracker.go celo/test/tracker.go new file mode 100644 index 0000000000000000000000000000000000000000..d81bee0b6a24de2affead192312f6976913ab7c9 --- /dev/null +++ celo/test/tracker.go @@ -0,0 +1,206 @@ +package test + +import ( + "context" + "errors" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/event" +) + +var ( + errStopped = errors.New("transaction tracker closed") + ErrTrackerAlreadyStopped = errors.New("attempted to stop already stopped tracker") +) + +// Tracker tracks processed blocks and transactions through a subscription with +// an ethclient. It provides the ability to check whether blocks or +// transactions have been processed and to wait till those blocks or +// transactions have been processed. +type Tracker struct { + client *ethclient.Client + heads chan *types.Header + sub ethereum.Subscription + wg sync.WaitGroup + // processedTxs maps transaction hashes to the block they were processed in. + processedTxs map[common.Hash]*types.Block + // processedBlocks maps block number to processed blocks. + processedBlocks map[uint64]*types.Block + processedMu sync.Mutex + stopCh chan struct{} + newBlock event.Feed +} + +// NewTracker creates a new tracker. +func NewTracker() *Tracker { + return &Tracker{ + heads: make(chan *types.Header, 10), + processedTxs: make(map[common.Hash]*types.Block), + processedBlocks: make(map[uint64]*types.Block), + } +} + +// GetProcessedTx returns the processed transaction with the given hash or nil +// if the tracker has not seen a processed transaction with the given hash. +func (tr *Tracker) GetProcessedTx(hash common.Hash) *types.Transaction { + tr.processedMu.Lock() + defer tr.processedMu.Unlock() + return tr.processedTxs[hash].Transaction(hash) +} + +// GetProcessedBlockForTx returns the block that a transaction with the given +// hash was processed in or nil if the tracker has not seen a processed +// transaction with the given hash. +func (tr *Tracker) GetProcessedBlockForTx(hash common.Hash) *types.Block { + tr.processedMu.Lock() + defer tr.processedMu.Unlock() + return tr.processedTxs[hash] +} + +// GetProcessedBlock returns processed block with the given num or nil if the +// tracker has not seen a processed block with that num. +func (tr *Tracker) GetProcessedBlock(num uint64) *types.Block { + tr.processedMu.Lock() + defer tr.processedMu.Unlock() + return tr.processedBlocks[num] +} + +// StartTracking subscribes to new head events on the client and starts +// processing the events in a goroutine. +func (tr *Tracker) StartTracking(client *ethclient.Client) error { + if tr.sub != nil { + return errors.New("attempted to start already started tracker") + } + // The subscription client will buffer 20000 notifications before closing + // the subscription, if that happens the Err() chan will return + // ErrSubscriptionQueueOverflow + sub, err := client.SubscribeNewHead(context.Background(), tr.heads) + if err != nil { + return err + } + tr.client = client + tr.sub = sub + tr.stopCh = make(chan struct{}) + + tr.wg.Add(1) + go func() { + defer tr.wg.Done() + err := tr.track() + if err != nil { + fmt.Printf("track failed with error: %v\n", err) + } + }() + return nil +} + +// track reads new heads from the heads channel and for each head retrieves the +// block, places the block in processedBlocks and places the transactions into +// processedTxs. It signals the sub Subscription for each retrieved block. +func (tr *Tracker) track() error { + for { + select { + case h := <-tr.heads: + b, err := tr.client.BlockByHash(context.Background(), h.Hash()) + if err != nil { + return err + } + tr.processedMu.Lock() + tr.processedBlocks[b.NumberU64()] = b + // If we have transactions then process them + if len(b.Transactions()) > 0 { + for _, t := range b.Transactions() { + tr.processedTxs[t.Hash()] = b + } + } + tr.processedMu.Unlock() + // signal + tr.newBlock.Send(struct{}{}) + case err := <-tr.sub.Err(): + // Will be nil if closed by calling Unsubscribe() + return err + case <-tr.stopCh: + return nil + } + } +} + +// AwaitTransactions waits for the transactions listed in hashes to be +// processed, it will return the ctx.Err() if ctx expires before all the +// transactions in hashes were processed or ErrStopped if StopTracking is +// called before all the transactions in hashes were processed. +func (tr *Tracker) AwaitTransactions(ctx context.Context, hashes []common.Hash) error { + hashmap := make(map[common.Hash]struct{}, len(hashes)) + for i := range hashes { + hashmap[hashes[i]] = struct{}{} + } + condition := func() bool { + for hash := range hashmap { + _, ok := tr.processedTxs[hash] + if ok { + delete(hashmap, hash) + } + } + // If there are no transactions left then they have all been processed. + return len(hashmap) == 0 + } + return tr.await(ctx, condition) +} + +// AwaitBlock waits for a block with the given num to be processed, it will +// return the ctx.Err() if ctx expires before a block with that number has been +// processed or ErrStopped if StopTracking is called before a block with that +// number is processed. +func (tr *Tracker) AwaitBlock(ctx context.Context, num uint64) error { + condition := func() bool { + return tr.processedBlocks[num] != nil + } + return tr.await(ctx, condition) +} + +// await waits for the provided condition to return true, it rechecks the +// condition every time a new block is received by the Tracker. Await returns +// nil when the condition returns true, otherwise it will return ctx.Err() if +// ctx expires before the condition returns true or ErrStopped if StopTracking +// is called before the condition returns true. +func (tr *Tracker) await(ctx context.Context, condition func() bool) error { + ch := make(chan struct{}, 10) + sub := tr.newBlock.Subscribe(ch) + defer sub.Unsubscribe() + for { + tr.processedMu.Lock() + found := condition() + tr.processedMu.Unlock() + // If we found what we are looking for then return. + if found { + return nil + } + select { + case <-ch: + continue + case <-ctx.Done(): + return ctx.Err() + case <-tr.stopCh: + return errStopped + } + } +} + +// StopTracking shuts down all the goroutines in the tracker. +func (tr *Tracker) StopTracking() error { + if tr.sub == nil { + return ErrTrackerAlreadyStopped + } + tr.sub.Unsubscribe() + close(tr.stopCh) + tr.wg.Wait() + // Set this to nil to mark the tracker as stopped. This must be done after + // waiting for wg, to avoid a data race in trackTransactions. + tr.sub = nil + tr.wg = sync.WaitGroup{} + return nil +}
diff --git go-ethereum/tests/gen_stenv.go celo/tests/gen_stenv.go index 0083d1d9257ccbc4afdd66f64f0a0e696e2d9c47..903f7e97fc5573529d0afdfe7277219ad8bfe325 100644 --- go-ethereum/tests/gen_stenv.go +++ celo/tests/gen_stenv.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" - "math/big"   "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" @@ -17,19 +16,15 @@ // MarshalJSON marshals as JSON. func (s stEnv) MarshalJSON() ([]byte, error) { type stEnv struct { Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` } var enc stEnv enc.Coinbase = common.UnprefixedAddress(s.Coinbase) - enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty) enc.GasLimit = math.HexOrDecimal64(s.GasLimit) enc.Number = math.HexOrDecimal64(s.Number) enc.Timestamp = math.HexOrDecimal64(s.Timestamp) - enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee) return json.Marshal(&enc) }   @@ -37,11 +32,9 @@ // UnmarshalJSON unmarshals from JSON. func (s *stEnv) UnmarshalJSON(input []byte) error { type stEnv struct { Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` } var dec stEnv if err := json.Unmarshal(input, &dec); err != nil { @@ -51,10 +44,6 @@ if dec.Coinbase == nil { return errors.New("missing required field 'currentCoinbase' for stEnv") } s.Coinbase = common.Address(*dec.Coinbase) - if dec.Difficulty == nil { - return errors.New("missing required field 'currentDifficulty' for stEnv") - } - s.Difficulty = (*big.Int)(dec.Difficulty) if dec.GasLimit == nil { return errors.New("missing required field 'currentGasLimit' for stEnv") } @@ -67,8 +56,5 @@ if dec.Timestamp == nil { return errors.New("missing required field 'currentTimestamp' for stEnv") } s.Timestamp = uint64(*dec.Timestamp) - if dec.BaseFee != nil { - s.BaseFee = (*big.Int)(dec.BaseFee) - } return nil }
diff --git go-ethereum/tests/gen_difficultytest.go celo/tests/gen_difficultytest.go deleted file mode 100644 index 2131fc4498e3c60f081a89e86d41e71633e8f9c5..0000000000000000000000000000000000000000 --- go-ethereum/tests/gen_difficultytest.go +++ /dev/null @@ -1,68 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package tests - -import ( - "encoding/json" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" -) - -var _ = (*difficultyTestMarshaling)(nil) - -// MarshalJSON marshals as JSON. -func (d DifficultyTest) MarshalJSON() ([]byte, error) { - type DifficultyTest struct { - ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp"` - ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` - UncleHash common.Hash `json:"parentUncles"` - CurrentTimestamp math.HexOrDecimal64 `json:"currentTimestamp"` - CurrentBlockNumber math.HexOrDecimal64 `json:"currentBlockNumber"` - CurrentDifficulty *math.HexOrDecimal256 `json:"currentDifficulty"` - } - var enc DifficultyTest - enc.ParentTimestamp = math.HexOrDecimal64(d.ParentTimestamp) - enc.ParentDifficulty = (*math.HexOrDecimal256)(d.ParentDifficulty) - enc.UncleHash = d.UncleHash - enc.CurrentTimestamp = math.HexOrDecimal64(d.CurrentTimestamp) - enc.CurrentBlockNumber = math.HexOrDecimal64(d.CurrentBlockNumber) - enc.CurrentDifficulty = (*math.HexOrDecimal256)(d.CurrentDifficulty) - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals from JSON. -func (d *DifficultyTest) UnmarshalJSON(input []byte) error { - type DifficultyTest struct { - ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp"` - ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` - UncleHash *common.Hash `json:"parentUncles"` - CurrentTimestamp *math.HexOrDecimal64 `json:"currentTimestamp"` - CurrentBlockNumber *math.HexOrDecimal64 `json:"currentBlockNumber"` - CurrentDifficulty *math.HexOrDecimal256 `json:"currentDifficulty"` - } - var dec DifficultyTest - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.ParentTimestamp != nil { - d.ParentTimestamp = uint64(*dec.ParentTimestamp) - } - if dec.ParentDifficulty != nil { - d.ParentDifficulty = (*big.Int)(dec.ParentDifficulty) - } - if dec.UncleHash != nil { - d.UncleHash = *dec.UncleHash - } - if dec.CurrentTimestamp != nil { - d.CurrentTimestamp = uint64(*dec.CurrentTimestamp) - } - if dec.CurrentBlockNumber != nil { - d.CurrentBlockNumber = uint64(*dec.CurrentBlockNumber) - } - if dec.CurrentDifficulty != nil { - d.CurrentDifficulty = (*big.Int)(dec.CurrentDifficulty) - } - return nil -}
diff --git go-ethereum/tests/init.go celo/tests/init.go index 192519c2e81e20588099d4a066aa7ec652ac2ecd..272651c14202459ac9321e5832847f0e20868619 100644 --- go-ethereum/tests/init.go +++ celo/tests/init.go @@ -141,7 +141,8 @@ ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(5), }, - "Berlin": { + + "Espresso": { ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), EIP150Block: big.NewInt(0), @@ -151,9 +152,11 @@ ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), + ChurritoBlock: big.NewInt(0), + DonutBlock: big.NewInt(0), + EspressoBlock: big.NewInt(0), }, - "BerlinToLondonAt5": { + "DonutToEspressoAt5": { ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), EIP150Block: big.NewInt(0), @@ -163,10 +166,11 @@ ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(5), + ChurritoBlock: big.NewInt(0), + DonutBlock: big.NewInt(0), + EspressoBlock: big.NewInt(5), }, - "London": { + "GFork": { ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), EIP150Block: big.NewInt(0), @@ -176,21 +180,10 @@ ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - }, - "Aleut": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), + ChurritoBlock: big.NewInt(0), + DonutBlock: big.NewInt(0), + EspressoBlock: big.NewInt(0), + GForkBlock: big.NewInt(0), }, }
diff --git go-ethereum/tests/gen_btheader.go celo/tests/gen_btheader.go index 95e2cd80d5217e26900fd9d7063d3ec0c119e79b..68f477e779467a66bbcff11dc327367439cabfb3 100644 --- go-ethereum/tests/gen_btheader.go +++ celo/tests/gen_btheader.go @@ -19,18 +19,13 @@ func (b btHeader) MarshalJSON() ([]byte, error) { type btHeader struct { Bloom types.Bloom Coinbase common.Address - MixHash common.Hash - Nonce types.BlockNonce Number *math.HexOrDecimal256 Hash common.Hash ParentHash common.Hash ReceiptTrie common.Hash StateRoot common.Hash TransactionsTrie common.Hash - UncleHash common.Hash ExtraData hexutil.Bytes - Difficulty *math.HexOrDecimal256 - GasLimit math.HexOrDecimal64 GasUsed math.HexOrDecimal64 Timestamp math.HexOrDecimal64 BaseFeePerGas *math.HexOrDecimal256 @@ -38,18 +33,13 @@ } var enc btHeader enc.Bloom = b.Bloom enc.Coinbase = b.Coinbase - enc.MixHash = b.MixHash - enc.Nonce = b.Nonce enc.Number = (*math.HexOrDecimal256)(b.Number) enc.Hash = b.Hash enc.ParentHash = b.ParentHash enc.ReceiptTrie = b.ReceiptTrie enc.StateRoot = b.StateRoot enc.TransactionsTrie = b.TransactionsTrie - enc.UncleHash = b.UncleHash enc.ExtraData = b.ExtraData - enc.Difficulty = (*math.HexOrDecimal256)(b.Difficulty) - enc.GasLimit = math.HexOrDecimal64(b.GasLimit) enc.GasUsed = math.HexOrDecimal64(b.GasUsed) enc.Timestamp = math.HexOrDecimal64(b.Timestamp) enc.BaseFeePerGas = (*math.HexOrDecimal256)(b.BaseFeePerGas) @@ -61,18 +51,13 @@ func (b *btHeader) UnmarshalJSON(input []byte) error { type btHeader struct { Bloom *types.Bloom Coinbase *common.Address - MixHash *common.Hash - Nonce *types.BlockNonce Number *math.HexOrDecimal256 Hash *common.Hash ParentHash *common.Hash ReceiptTrie *common.Hash StateRoot *common.Hash TransactionsTrie *common.Hash - UncleHash *common.Hash ExtraData *hexutil.Bytes - Difficulty *math.HexOrDecimal256 - GasLimit *math.HexOrDecimal64 GasUsed *math.HexOrDecimal64 Timestamp *math.HexOrDecimal64 BaseFeePerGas *math.HexOrDecimal256 @@ -87,12 +72,6 @@ } if dec.Coinbase != nil { b.Coinbase = *dec.Coinbase } - if dec.MixHash != nil { - b.MixHash = *dec.MixHash - } - if dec.Nonce != nil { - b.Nonce = *dec.Nonce - } if dec.Number != nil { b.Number = (*big.Int)(dec.Number) } @@ -111,17 +90,8 @@ } if dec.TransactionsTrie != nil { b.TransactionsTrie = *dec.TransactionsTrie } - if dec.UncleHash != nil { - b.UncleHash = *dec.UncleHash - } if dec.ExtraData != nil { b.ExtraData = *dec.ExtraData - } - if dec.Difficulty != nil { - b.Difficulty = (*big.Int)(dec.Difficulty) - } - if dec.GasLimit != nil { - b.GasLimit = uint64(*dec.GasLimit) } if dec.GasUsed != nil { b.GasUsed = uint64(*dec.GasUsed)
diff --git go-ethereum/tests/block_test_util.go celo/tests/block_test_util.go index 995a109e4b0c5071391f571f0a343e68d54ed6e5..4fa8a15acf0f53cbce2afc4e437455ec92d2fec3 100644 --- go-ethereum/tests/block_test_util.go +++ celo/tests/block_test_util.go @@ -28,8 +28,7 @@ "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -63,7 +62,6 @@ type btBlock struct { BlockHeader *btHeader ExpectException string Rlp string - UncleHeaders []*btHeader }   //go:generate gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go @@ -71,18 +69,13 @@ type btHeader struct { Bloom types.Bloom Coinbase common.Address - MixHash common.Hash - Nonce types.BlockNonce Number *big.Int Hash common.Hash ParentHash common.Hash ReceiptTrie common.Hash StateRoot common.Hash TransactionsTrie common.Hash - UncleHash common.Hash ExtraData []byte - Difficulty *big.Int - GasLimit uint64 GasUsed uint64 Timestamp uint64 BaseFeePerGas *big.Int @@ -91,11 +84,8 @@ type btHeaderMarshaling struct { ExtraData hexutil.Bytes Number *math.HexOrDecimal256 - Difficulty *math.HexOrDecimal256 - GasLimit math.HexOrDecimal64 GasUsed math.HexOrDecimal64 Timestamp math.HexOrDecimal64 - BaseFeePerGas *math.HexOrDecimal256 }   func (t *BlockTest) Run(snapshotter bool) error { @@ -116,12 +106,7 @@ } if gblock.Root() != t.json.Genesis.StateRoot { return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6]) } - var engine consensus.Engine - if t.json.SealEngine == "NoProof" { - engine = ethash.NewFaker() - } else { - engine = ethash.NewShared() - } + engine := mockEngine.NewFaker() cache := &core.CacheConfig{TrieCleanLimit: 0} if snapshotter { cache.SnapshotLimit = 1 @@ -160,24 +145,19 @@ func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis { return &core.Genesis{ Config: config, - Nonce: t.json.Genesis.Nonce.Uint64(), Timestamp: t.json.Genesis.Timestamp, ParentHash: t.json.Genesis.ParentHash, ExtraData: t.json.Genesis.ExtraData, - GasLimit: t.json.Genesis.GasLimit, GasUsed: t.json.Genesis.GasUsed, - Difficulty: t.json.Genesis.Difficulty, - Mixhash: t.json.Genesis.MixHash, Coinbase: t.json.Genesis.Coinbase, Alloc: t.json.Pre, - BaseFee: t.json.Genesis.BaseFeePerGas, } }   /* See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II   Whether a block is valid or not is a bit subtle, it's defined by presence of - blockHeader, transactions and uncleHeaders fields. If they are missing, the block is + blockHeader and transactions. If they are missing, the block is invalid and we must verify that we do not accept it.   Since some tests mix valid and invalid blocks we need to check this for every block. @@ -233,12 +213,6 @@ } if h.Coinbase != h2.Coinbase { return fmt.Errorf("coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase) } - if h.MixHash != h2.MixDigest { - return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest) - } - if h.Nonce != h2.Nonce { - return fmt.Errorf("nonce: want: %x have: %x", h.Nonce, h2.Nonce) - } if h.Number.Cmp(h2.Number) != 0 { return fmt.Errorf("number: want: %v have: %v", h.Number, h2.Number) } @@ -254,17 +228,8 @@ } if h.StateRoot != h2.Root { return fmt.Errorf("state hash: want: %x have: %x", h.StateRoot, h2.Root) } - if h.UncleHash != h2.UncleHash { - return fmt.Errorf("uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash) - } if !bytes.Equal(h.ExtraData, h2.Extra) { return fmt.Errorf("extra data: want: %x have: %x", h.ExtraData, h2.Extra) - } - if h.Difficulty.Cmp(h2.Difficulty) != 0 { - return fmt.Errorf("difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty) - } - if h.GasLimit != h2.GasLimit { - return fmt.Errorf("gasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit) } if h.GasUsed != h2.GasUsed { return fmt.Errorf("gasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed)
diff --git go-ethereum/tests/state_test_util.go celo/tests/state_test_util.go index 2edb32d1908e120a840e8eb0edbbef4ffabfa740..b55375b0af502a44b6fd16625e30ce833a47083a 100644 --- go-ethereum/tests/state_test_util.go +++ celo/tests/state_test_util.go @@ -33,6 +33,7 @@ "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/vmcontext" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" @@ -80,20 +81,16 @@ //go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go   type stEnv struct { Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` - Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"` GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` Number uint64 `json:"currentNumber" gencodec:"required"` Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` - BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"` }   type stEnvMarshaling struct { Coinbase common.UnprefixedAddress - Difficulty *math.HexOrDecimal256 GasLimit math.HexOrDecimal64 Number math.HexOrDecimal64 Timestamp math.HexOrDecimal64 - BaseFee *math.HexOrDecimal256 }   //go:generate gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go @@ -186,8 +183,8 @@ block := t.genesis(config).ToBlock(nil) snaps, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter)   var baseFee *big.Int - if config.IsLondon(new(big.Int)) { - baseFee = t.json.Env.BaseFee + if config.IsEspresso(new(big.Int)) { + // baseFee = t.json.Env.BaseFee ? if baseFee == nil { // Retesteth uses `0x10` for genesis baseFee. Therefore, it defaults to // parent - 2 : 0xa as the basefee for 'this' context. @@ -223,8 +220,9 @@ // Execute the message. snapshot := statedb.Snapshot() gaspool := new(core.GasPool) - gaspool.AddGas(block.GasLimit()) - if _, err := core.ApplyMessage(evm, msg, gaspool); err != nil { + gaspool.AddGas(params.DefaultGasLimit) + vmRunner := vmcontext.NewEVMRunner(nil, block.Header(), statedb) + if _, err := core.ApplyMessage(evm, msg, gaspool, vmRunner, nil); err != nil { statedb.RevertToSnapshot(snapshot) }   @@ -271,8 +269,6 @@ func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { return &core.Genesis{ Config: config, Coinbase: t.json.Env.Coinbase, - Difficulty: t.json.Env.Difficulty, - GasLimit: t.json.Env.GasLimit, Number: t.json.Env.Number, Timestamp: t.json.Env.Timestamp, Alloc: t.json.Pre, @@ -348,7 +344,7 @@ return nil, fmt.Errorf("no gas price provided") }   msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, gasPrice, - tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, data, accessList, false) + tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, nil, nil, nil, data, accessList, false, false) return msg, nil }
diff --git go-ethereum/tests/difficulty_test.go celo/tests/difficulty_test.go deleted file mode 100644 index bb49b0d5028a4b9d1faafe13790190cf30a5c1a7..0000000000000000000000000000000000000000 --- go-ethereum/tests/difficulty_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package tests - -import ( - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/params" -) - -var ( - mainnetChainConfig = params.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(1150000), - DAOForkBlock: big.NewInt(1920000), - DAOForkSupport: true, - EIP150Block: big.NewInt(2463000), - EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - EIP155Block: big.NewInt(2675000), - EIP158Block: big.NewInt(2675000), - ByzantiumBlock: big.NewInt(4370000), - } -) - -func TestDifficulty(t *testing.T) { - t.Parallel() - - dt := new(testMatcher) - // Not difficulty-tests - dt.skipLoad("hexencodetest.*") - dt.skipLoad("crypto.*") - dt.skipLoad("blockgenesistest\\.json") - dt.skipLoad("genesishashestest\\.json") - dt.skipLoad("keyaddrtest\\.json") - dt.skipLoad("txtest\\.json") - - // files are 2 years old, contains strange values - dt.skipLoad("difficultyCustomHomestead\\.json") - dt.skipLoad("difficultyMorden\\.json") - dt.skipLoad("difficultyOlimpic\\.json") - - dt.config("Ropsten", *params.RopstenChainConfig) - dt.config("Morden", *params.RopstenChainConfig) - dt.config("Frontier", params.ChainConfig{}) - - dt.config("Homestead", params.ChainConfig{ - HomesteadBlock: big.NewInt(0), - }) - - dt.config("Byzantium", params.ChainConfig{ - ByzantiumBlock: big.NewInt(0), - }) - - dt.config("Frontier", *params.RopstenChainConfig) - dt.config("MainNetwork", mainnetChainConfig) - dt.config("CustomMainNetwork", mainnetChainConfig) - dt.config("Constantinople", params.ChainConfig{ - ConstantinopleBlock: big.NewInt(0), - }) - dt.config("EIP2384", params.ChainConfig{ - MuirGlacierBlock: big.NewInt(0), - }) - dt.config("difficulty.json", mainnetChainConfig) - - dt.walk(t, difficultyTestDir, func(t *testing.T, name string, test *DifficultyTest) { - cfg := dt.findConfig(t) - if test.ParentDifficulty.Cmp(params.MinimumDifficulty) < 0 { - t.Skip("difficulty below minimum") - return - } - if err := dt.checkFailure(t, test.Run(cfg)); err != nil { - t.Error(err) - } - }) -}
diff --git go-ethereum/tests/init_test.go celo/tests/init_test.go index 86d7554e1f1cc64609a92050c5351cd00dfffd89..cc79b7caa06e72ff0e10109d064d0f8ff8e22120 100644 --- go-ethereum/tests/init_test.go +++ celo/tests/init_test.go @@ -40,7 +40,6 @@ stateTestDir = filepath.Join(baseDir, "GeneralStateTests") legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests") transactionTestDir = filepath.Join(baseDir, "TransactionTests") rlpTestDir = filepath.Join(baseDir, "RLPTests") - difficultyTestDir = filepath.Join(baseDir, "BasicTests") )   func readJSON(reader io.Reader, value interface{}) error { @@ -87,6 +86,7 @@ return }   // testMatcher controls skipping and chain config assignment to tests. + type testMatcher struct { configpat []testConfig failpat []testFailure @@ -128,6 +128,7 @@ tm.runonlylistpat = regexp.MustCompile(pattern) }   // config defines chain config for tests matching the pattern. + func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) { tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg}) } @@ -154,7 +155,9 @@ return "", false }   // findConfig returns the chain config matching defined patterns. + func (tm *testMatcher) findConfig(t *testing.T) *params.ChainConfig { + for _, m := range tm.configpat { if m.p.MatchString(t.Name()) { return &m.config
diff --git go-ethereum/tests/difficulty_test_util.go celo/tests/difficulty_test_util.go deleted file mode 100644 index 7c415608f788701a5aa37ec4918255f84df54416..0000000000000000000000000000000000000000 --- go-ethereum/tests/difficulty_test_util.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package tests - -import ( - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" -) - -//go:generate gencodec -type DifficultyTest -field-override difficultyTestMarshaling -out gen_difficultytest.go - -type DifficultyTest struct { - ParentTimestamp uint64 `json:"parentTimestamp"` - ParentDifficulty *big.Int `json:"parentDifficulty"` - UncleHash common.Hash `json:"parentUncles"` - CurrentTimestamp uint64 `json:"currentTimestamp"` - CurrentBlockNumber uint64 `json:"currentBlockNumber"` - CurrentDifficulty *big.Int `json:"currentDifficulty"` -} - -type difficultyTestMarshaling struct { - ParentTimestamp math.HexOrDecimal64 - ParentDifficulty *math.HexOrDecimal256 - CurrentTimestamp math.HexOrDecimal64 - CurrentDifficulty *math.HexOrDecimal256 - UncleHash common.Hash - CurrentBlockNumber math.HexOrDecimal64 -} - -func (test *DifficultyTest) Run(config *params.ChainConfig) error { - parentNumber := big.NewInt(int64(test.CurrentBlockNumber - 1)) - parent := &types.Header{ - Difficulty: test.ParentDifficulty, - Time: test.ParentTimestamp, - Number: parentNumber, - UncleHash: test.UncleHash, - } - - actual := ethash.CalcDifficulty(config, test.CurrentTimestamp, parent) - exp := test.CurrentDifficulty - - if actual.Cmp(exp) != 0 { - return fmt.Errorf("parent[time %v diff %v unclehash:%x] child[time %v number %v] diff %v != expected %v", - test.ParentTimestamp, test.ParentDifficulty, test.UncleHash, - test.CurrentTimestamp, test.CurrentBlockNumber, actual, exp) - } - return nil - -}
diff --git go-ethereum/tests/transaction_test_util.go celo/tests/transaction_test_util.go index 5fb9f9fd979dcb548cc2988c1e02743205dcd34a..5152a2fb33e4157766f6eb4cf23a7cf81cf5a8ff 100644 --- go-ethereum/tests/transaction_test_util.go +++ celo/tests/transaction_test_util.go @@ -55,7 +55,7 @@ if err != nil { return nil, nil, err } // Intrinsic gas - requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul) + requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, nil, 0, false) if err != nil { return nil, nil, err }
diff --git go-ethereum/tests/fuzzers/les/les-fuzzer.go celo/tests/fuzzers/les/les-fuzzer.go index 40cfb427ea006b225e19752a794b1fde0f5b90eb..19377c49f309ceb855e34a4dd97ac7c5909baef0 100644 --- go-ethereum/tests/fuzzers/les/les-fuzzer.go +++ celo/tests/fuzzers/les/les-fuzzer.go @@ -23,7 +23,7 @@ "io" "math/big"   "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + mockEngine "github.com/ethereum/go-ethereum/consensus/consensustest" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -58,11 +58,10 @@ db := rawdb.NewMemoryDatabase() gspec := core.Genesis{ Config: params.TestChainConfig, Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}}, - GasLimit: 100000000, } genesis := gspec.MustCommit(db) signer := types.HomesteadSigner{} - blocks, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, testChainLen, + blocks, _ := core.GenerateChain(gspec.Config, genesis, mockEngine.NewFaker(), db, testChainLen, func(i int, gen *core.BlockGen) { var ( tx *types.Transaction @@ -70,17 +69,17 @@ addr common.Address ) nonce := uint64(i) if i%4 == 0 { - tx, _ = types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, bankKey) + tx, _ = types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 200000, big.NewInt(0), nil, nil, nil, testContractCode), signer, bankKey) addr = crypto.CreateAddress(bankAddr, nonce) } else { addr = common.BigToAddress(big.NewInt(int64(i))) - tx, _ = types.SignTx(types.NewTransaction(nonce, addr, big.NewInt(10000), params.TxGas, big.NewInt(params.GWei), nil), signer, bankKey) + tx, _ = types.SignTx(types.NewTransaction(nonce, addr, big.NewInt(10000), params.TxGas, big.NewInt(params.GWei), nil, nil, nil, nil), signer, bankKey) } gen.AddTx(tx) addrHashes = append(addrHashes, crypto.Keccak256Hash(addr[:])) txHashes = append(txHashes, tx.Hash()) }) - bc, _ = core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + bc, _ = core.NewBlockChain(db, nil, gspec.Config, mockEngine.NewFaker(), vm.Config{}, nil, nil) if _, err := bc.InsertChain(blocks); err != nil { panic(err) } @@ -252,6 +251,18 @@ } return nil }   +func (f *fuzzer) GetEtherbase() common.Address { + return common.Address{} +} + +func (h *fuzzer) GetGatewayFee() *big.Int { + return big.NewInt(1) +} + +func (h *fuzzer) VerifyGatewayFee(gatewayFeeRecipient *common.Address, gatewayFee *big.Int) error { + return nil +} + type dummyMsg struct { data []byte } @@ -391,7 +402,7 @@ } else { nonce = f.nonce f.nonce += 1 } - req.Txs[i], _ = types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(10000), params.TxGas, big.NewInt(1000000000*int64(f.randomByte())), nil), signer, bankKey) + req.Txs[i], _ = types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(10000), params.TxGas, big.NewInt(1000000000*int64(f.randomByte())), nil, nil, nil, nil), signer, bankKey) } f.doFuzz(l.SendTxV2Msg, req)
diff --git go-ethereum/tests/solidity/contracts/OpCodes.sol celo/tests/solidity/contracts/OpCodes.sol index 9e3a0ebb020228526013839d3174387a94263343..bd45cd7780ed2be6acd5678404a90e991d4e15f4 100644 --- go-ethereum/tests/solidity/contracts/OpCodes.sol +++ celo/tests/solidity/contracts/OpCodes.sol @@ -193,12 +193,6 @@ //number assembly { pop(number())}   - //difficulty - assembly { pop(difficulty())} - - //gaslimit - assembly { pop(gaslimit())} - //call address contractAddr = address(test1); bytes4 sig = bytes4(keccak256("isSameAddress(address,address)")); //Function signature
diff --git go-ethereum/tests/fuzzers/txfetcher/txfetcher_fuzzer.go celo/tests/fuzzers/txfetcher/txfetcher_fuzzer.go index 1003342c4c71e86e31d532659c1fd029bda09f36..75b8a9f0fc8635b1c89d8e7f4b134968f2783987 100644 --- go-ethereum/tests/fuzzers/txfetcher/txfetcher_fuzzer.go +++ celo/tests/fuzzers/txfetcher/txfetcher_fuzzer.go @@ -44,7 +44,7 @@ peers[i] = fmt.Sprintf("Peer #%d", i) } txs = make([]*types.Transaction, 65536) // We need to bump enough to hit all the limits for i := 0; i < len(txs); i++ { - txs[i] = types.NewTransaction(rand.Uint64(), common.Address{byte(rand.Intn(256))}, new(big.Int), 0, new(big.Int), nil) + txs[i] = types.NewTransaction(rand.Uint64(), common.Address{byte(rand.Intn(256))}, new(big.Int), 0, new(big.Int), nil, nil, nil, nil) } }
diff --git go-ethereum/tests/fuzzers/bls12381/precompile_fuzzer.go celo/tests/fuzzers/bls12381/precompile_fuzzer.go index 0b08cfdb48404af8b0366ac8af9ec3a907fed2e8..075db837713cb69f0a1803c410285d353eeba972 100644 --- go-ethereum/tests/fuzzers/bls12381/precompile_fuzzer.go +++ celo/tests/fuzzers/bls12381/precompile_fuzzer.go @@ -19,9 +19,18 @@ import ( "bytes" "fmt" + "math/big"   + blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" + "github.com/ethereum/go-ethereum/consensus/istanbul/validator" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/crypto/sha3" )   const ( @@ -36,6 +45,88 @@ blsMapG1 = byte(17) blsMapG2 = byte(18) )   +func getValidators(number *big.Int, _ common.Hash) []istanbul.Validator { + preimage := append([]byte("fakevalidators"), common.LeftPadBytes(number.Bytes()[:], 32)...) + hash := sha3.Sum256(preimage) + var validators []istanbul.Validator + for i := 0; i < 16; i, hash = i+1, sha3.Sum256(hash[:]) { + key, _ := crypto.ToECDSA(hash[:]) + blsPrivateKey, _ := blscrypto.ECDSAToBLS(key) + blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) + addr := crypto.PubkeyToAddress(key.PublicKey) + validators = append(validators, validator.New(addr, blsPublicKey)) + } + return validators +} + +func makeTestSeal(number *big.Int) types.IstanbulAggregatedSeal { + preimage := append([]byte("fakeseal"), common.LeftPadBytes(number.Bytes()[:], 32)...) + hash := sha3.Sum256(preimage) + return types.IstanbulAggregatedSeal{Bitmap: new(big.Int).SetBytes(hash[:2])} +} + +func makeTestHeaderHash(number *big.Int) common.Hash { + preimage := append([]byte("fakeheader"), common.LeftPadBytes(number.Bytes()[:], 32)...) + return common.Hash(sha3.Sum256(preimage)) +} + +func makeTestHeaderExtra(number *big.Int) *types.IstanbulExtra { + return &types.IstanbulExtra{ + AggregatedSeal: makeTestSeal(number), + ParentAggregatedSeal: makeTestSeal(new(big.Int).Sub(number, common.Big1)), + } +} + +func makeTestHeader(number *big.Int) *types.Header { + extra, err := rlp.EncodeToBytes(makeTestHeaderExtra(number)) + if err != nil { + panic(err) + } + return &types.Header{ + ParentHash: makeTestHeaderHash(new(big.Int).Sub(number, common.Big1)), + Number: number, + GasUsed: params.DefaultGasLimit / 2, + Extra: append(make([]byte, types.IstanbulExtraVanity), extra...), + Time: number.Uint64() * 5, + } +} + +func getHeaderByNumber(number uint64) *types.Header { + return makeTestHeader(new(big.Int).SetUint64(number)) +} + +var testHeader = makeTestHeader(big.NewInt(10000)) + +var vmBlockCtx = vm.BlockContext{ + CanTransfer: func(db vm.StateDB, addr common.Address, amount *big.Int) bool { + return db.GetBalance(addr).Cmp(amount) >= 0 + }, + Transfer: func(s vm.StateDB, a1, a2 common.Address, i *big.Int) { panic("transfer: not implemented") }, + GetHash: func(u uint64) common.Hash { panic("getHash: not implemented") }, + VerifySeal: func(header *types.Header) bool { + // If the block is later than the unsealed reference block, return false. + return !(header.Number.Cmp(testHeader.Number) > 0) + }, + Coinbase: common.Address{}, + BlockNumber: new(big.Int).Set(testHeader.Number), + Time: new(big.Int).SetUint64(testHeader.Time), + + EpochSize: 100, + GetValidators: getValidators, + GetHeaderByNumber: getHeaderByNumber, +} + +var vmTxCtx = vm.TxContext{ + GasPrice: common.Big1, + Origin: common.HexToAddress("a11ce"), +} + +// Create a global mock EVM for use in the following tests. +var mockEVM = &vm.EVM{ + Context: vmBlockCtx, + TxContext: vmTxCtx, +} + func FuzzG1Add(data []byte) int { return fuzz(blsG1Add, data) } func FuzzG1Mul(data []byte) int { return fuzz(blsG1Mul, data) } func FuzzG1MultiExp(data []byte) int { return fuzz(blsG1MultiExp, data) } @@ -79,7 +170,7 @@ // 0 otherwise // other values are reserved for future use. func fuzz(id byte, data []byte) int { // Even on bad input, it should not crash, so we still test the gas calc - precompile := vm.PrecompiledContractsBLS[common.BytesToAddress([]byte{id})] + precompile := vm.PrecompiledContractsE[common.BytesToAddress([]byte{id})] gas := precompile.RequiredGas(data) if !checkInput(id, len(data)) { return 0 @@ -90,7 +181,7 @@ return 0 } cpy := make([]byte, len(data)) copy(cpy, data) - _, err := precompile.Run(cpy) + _, err := precompile.Run(cpy, vm.NewContext(common.HexToAddress("1337"), mockEVM)) if !bytes.Equal(cpy, data) { panic(fmt.Sprintf("input data modified, precompile %d: %x %x", id, data, cpy)) }
diff --git go-ethereum/tests/fuzzers/difficulty/difficulty-fuzz.go celo/tests/fuzzers/difficulty/difficulty-fuzz.go deleted file mode 100644 index fe6570e8f59773af3265fc2526b732462bba1edb..0000000000000000000000000000000000000000 --- go-ethereum/tests/fuzzers/difficulty/difficulty-fuzz.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package difficulty - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - "math/big" - - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core/types" -) - -type fuzzer struct { - input io.Reader - exhausted bool - debugging bool -} - -func (f *fuzzer) read(size int) []byte { - out := make([]byte, size) - if _, err := f.input.Read(out); err != nil { - f.exhausted = true - } - return out -} - -func (f *fuzzer) readSlice(min, max int) []byte { - var a uint16 - binary.Read(f.input, binary.LittleEndian, &a) - size := min + int(a)%(max-min) - out := make([]byte, size) - if _, err := f.input.Read(out); err != nil { - f.exhausted = true - } - return out -} - -func (f *fuzzer) readUint64(min, max uint64) uint64 { - if min == max { - return min - } - var a uint64 - if err := binary.Read(f.input, binary.LittleEndian, &a); err != nil { - f.exhausted = true - } - a = min + a%(max-min) - return a -} -func (f *fuzzer) readBool() bool { - return f.read(1)[0]&0x1 == 0 -} - -// The function must return -// 1 if the fuzzer should increase priority of the -// given input during subsequent fuzzing (for example, the input is lexically -// correct and was parsed successfully); -// -1 if the input must not be added to corpus even if gives new coverage; and -// 0 otherwise -// other values are reserved for future use. -func Fuzz(data []byte) int { - f := fuzzer{ - input: bytes.NewReader(data), - exhausted: false, - } - return f.fuzz() -} - -var minDifficulty = big.NewInt(0x2000) - -type calculator func(time uint64, parent *types.Header) *big.Int - -func (f *fuzzer) fuzz() int { - // A parent header - header := &types.Header{} - if f.readBool() { - header.UncleHash = types.EmptyUncleHash - } - // Difficulty can range between 0x2000 (2 bytes) and up to 32 bytes - { - diff := new(big.Int).SetBytes(f.readSlice(2, 32)) - if diff.Cmp(minDifficulty) < 0 { - diff.Set(minDifficulty) - } - header.Difficulty = diff - } - // Number can range between 0 and up to 32 bytes (but not so that the child exceeds it) - { - // However, if we use astronomic numbers, then the bomb exp karatsuba calculation - // in the legacy methods) - // times out, so we limit it to fit within reasonable bounds - number := new(big.Int).SetBytes(f.readSlice(0, 4)) // 4 bytes: 32 bits: block num max 4 billion - header.Number = number - } - // Both parent and child time must fit within uint64 - var time uint64 - { - childTime := f.readUint64(1, 0xFFFFFFFFFFFFFFFF) - //fmt.Printf("childTime: %x\n",childTime) - delta := f.readUint64(1, childTime) - //fmt.Printf("delta: %v\n", delta) - pTime := childTime - delta - header.Time = pTime - time = childTime - } - // Bomb delay will never exceed uint64 - bombDelay := new(big.Int).SetUint64(f.readUint64(1, 0xFFFFFFFFFFFFFFFe)) - - if f.exhausted { - return 0 - } - - for i, pair := range []struct { - bigFn calculator - u256Fn calculator - }{ - {ethash.FrontierDifficultyCalulator, ethash.CalcDifficultyFrontierU256}, - {ethash.HomesteadDifficultyCalulator, ethash.CalcDifficultyHomesteadU256}, - {ethash.DynamicDifficultyCalculator(bombDelay), ethash.MakeDifficultyCalculatorU256(bombDelay)}, - } { - want := pair.bigFn(time, header) - have := pair.u256Fn(time, header) - if want.Cmp(have) != 0 { - panic(fmt.Sprintf("pair %d: want %x have %x\nparent.Number: %x\np.Time: %x\nc.Time: %x\nBombdelay: %v\n", i, want, have, - header.Number, header.Time, time, bombDelay)) - } - } - return 1 -}
diff --git go-ethereum/tests/fuzzers/secp256k1/secp_fuzzer.go celo/tests/fuzzers/secp256k1/secp_fuzzer.go index 53845b64334504520b8c3814e06f0157ca13039e..47083d5fe3a9c3bb2b578c0e95f3f1e3ccb013be 100644 --- go-ethereum/tests/fuzzers/secp256k1/secp_fuzzer.go +++ celo/tests/fuzzers/secp256k1/secp_fuzzer.go @@ -21,7 +21,7 @@ import ( "fmt"   - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" "github.com/ethereum/go-ethereum/crypto/secp256k1" fuzz "github.com/google/gofuzz" )
diff --git go-ethereum/tests/fuzzers/bls12381/bls12381_fuzz.go celo/tests/fuzzers/bls12381/bls12381_fuzz.go index ed2df43d2d31ff31876edea294a2110cb4375ab6..4726a3036196dea3b41473621fef3abccecfb50e 100644 --- go-ethereum/tests/fuzzers/bls12381/bls12381_fuzz.go +++ celo/tests/fuzzers/bls12381/bls12381_fuzz.go @@ -26,10 +26,10 @@ "fmt" "io" "math/big"   + "github.com/celo-org/celo-blockchain/crypto/bls12381" gnark "github.com/consensys/gnark-crypto/ecc/bls12-381" "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" - "github.com/ethereum/go-ethereum/crypto/bls12381" )   func FuzzCrossPairing(data []byte) int {
diff --git go-ethereum/tests/fuzzers/difficulty/debug/main.go celo/tests/fuzzers/difficulty/debug/main.go deleted file mode 100644 index 23516b3a0dfa966377a3b2fbbc386e925adf48fa..0000000000000000000000000000000000000000 --- go-ethereum/tests/fuzzers/difficulty/debug/main.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "os" - - "github.com/ethereum/go-ethereum/tests/fuzzers/difficulty" -) - -func main() { - if len(os.Args) != 2 { - fmt.Fprintf(os.Stderr, "Usage: debug <file>") - os.Exit(1) - } - crasher := os.Args[1] - data, err := ioutil.ReadFile(crasher) - if err != nil { - fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err) - os.Exit(1) - } - difficulty.Fuzz(data) -}
diff --git go-ethereum/e2e_test/e2e_test.go celo/e2e_test/e2e_test.go new file mode 100644 index 0000000000000000000000000000000000000000..208f8eca286ee5e3ebc06f49e0e1882d27229542 --- /dev/null +++ celo/e2e_test/e2e_test.go @@ -0,0 +1,589 @@ +package e2e_test + +import ( + "context" + "errors" + "fmt" + "math/big" + "os/exec" + "strings" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func init() { + // This statement is commented out but left here since its very useful for + // debugging problems and its non trivial to construct. + // + // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stdout, log.TerminalFormat(true)))) + + // This disables all logging which in general we want, because there is a lot + log.Root().SetHandler(log.DiscardHandler()) +} + +// This test starts a network submits a transaction and waits for the whole +// network to process the transaction. +func TestSendCelo(t *testing.T) { + ac := test.AccountConfig(3, 2) + gc, ec, err := test.BuildConfig(ac) + require.NoError(t, err) + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + accounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig()) + + // Send one celo from external account 0 to 1 via node 0. + tx, err := accounts[0].SendCelo(ctx, accounts[1].Address, 1, network[0]) + require.NoError(t, err) + + // Wait for the whole network to process the transaction. + err = network.AwaitTransactions(ctx, tx) + require.NoError(t, err) +} + +// This test verifies correct behavior in a network of size one, in the case that +// this fails we know that the problem does not lie with our network code. +func TestSingleNodeNetworkManyTxs(t *testing.T) { + iterations := 5 + txsPerIteration := 5 + ac := test.AccountConfig(1, 1) + gc, ec, err := test.BuildConfig(ac) + require.NoError(t, err) + gc.Istanbul.Epoch = uint64(iterations) * 50 // avoid the epoch for this test + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + accounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig()) + for r := 0; r < iterations; r++ { + txs := make([]*types.Transaction, 0, txsPerIteration) + for j := 0; j < txsPerIteration; j++ { + tx, err := accounts[0].SendCelo(ctx, common.Address{}, 1, network[0]) + require.NoError(t, err) + require.NotNil(t, tx) + } + err = network.AwaitTransactions(ctx, txs...) + require.NoError(t, err) + } +} + +// This test is intended to ensure that epoch blocks can be correctly marshalled. +// We previously had an open bug for this https://github.com/ethereum/go-ethereum/issues/1574 +func TestEpochBlockMarshaling(t *testing.T) { + accounts := test.AccountConfig(1, 0) + gc, ec, err := test.BuildConfig(accounts) + require.NoError(t, err) + + // Configure the shortest possible epoch, uptimeLookbackWindow minimum is 3 + // and it needs to be < (epoch -2). + ec.Istanbul.Epoch = 6 + network, shutdown, err := test.NewNetwork(accounts, gc, ec) + require.NoError(t, err) + defer shutdown() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + // Wait for the whole network to process the transaction. + err = network.AwaitBlock(ctx, 6) + require.NoError(t, err) + b := network[0].Tracker.GetProcessedBlock(6) + + // Check that epoch snark data was actually unmarshalled, I.E there was + // something there. + assert.True(t, len(b.EpochSnarkData().Signature) > 0) + assert.True(t, b.EpochSnarkData().Bitmap.Uint64() > 0) +} + +// This test checks that a network can have validators shut down mid operation +// and that it can continue to function, it also checks that if more than f +// validators are shut down, when they restart the network is able to continue. +func TestStartStopValidators(t *testing.T) { + ac := test.AccountConfig(4, 2) + gc, ec, err := test.BuildConfig(ac) + require.NoError(t, err) + network, _, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + + // We define our own shutdown function because we don't want to print + // errors about already stopped nodes. Since this test can fail while we + // have stopped nodes. + defer func() { + for _, err := range network.Shutdown() { + if !errors.Is(err, test.ErrTrackerAlreadyStopped) && !errors.Is(err, node.ErrNodeStopped) { + fmt.Println(err.Error()) + } + } + }() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*40) + defer cancel() + + var txs []*types.Transaction + + accounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig()) + + // Send one celo from external account 0 to 1 via node 0. + tx, err := accounts[0].SendCelo(ctx, accounts[1].Address, 1, network[0]) + require.NoError(t, err) + txs = append(txs, tx) + + // Wait for the whole network to process the transaction. + err = network.AwaitTransactions(ctx, txs...) + require.NoError(t, err) + + // Stop one node, the rest of the network should still be able to progress + err = network[3].Close() + require.NoError(t, err) + + // Send one celo from external account 0 to 1 via node 0. + tx, err = accounts[0].SendCelo(ctx, accounts[1].Address, 1, network[0]) + require.NoError(t, err) + txs = append(txs, tx) + + // Check that the remaining network can still process this transction. + err = network[:3].AwaitTransactions(ctx, txs...) + require.NoError(t, err) + + // Stop another node, the network should now be stuck + err = network[2].Close() + require.NoError(t, err) + + // Now we will check that the network does not process transactions in this + // state, by waiting for a reasonable amount of time for it to process a + // transaction and assuming it is not processing transactions if we time out. + shortCtx, cancel := context.WithTimeout(context.Background(), time.Second*2) + defer cancel() + // Send one celo from external account 0 to 1 via node 0. + tx, err = accounts[0].SendCelo(ctx, accounts[1].Address, 1, network[0]) + require.NoError(t, err) + txs = append(txs, tx) + + err = network[:2].AwaitTransactions(shortCtx, txs...) + // Expect DeadlineExceeded error + if !errors.Is(err, context.DeadlineExceeded) { + t.Fatalf("expecting %q, instead got: %v ", context.DeadlineExceeded.Error(), err) + } + + // Start the last stopped node + err = network[2].Start() + require.NoError(t, err) + + // We need to wait here to allow the call to "Backend.RefreshValPeers" to + // complete before adding peers. This is because "Backend.RefreshValPeers" + // deletes all peers and then re-adds any peers from the cached + // connections, but in the case that peers were recently added there may + // not have been enough time to connect to them and populate the connection + // cache, and in that case "Backend.RefreshValPeers" simply removes all the + // peers. + time.Sleep(250 * time.Millisecond) + // Connect last stopped node to running nodes + network[2].AddPeers(network[:2]...) + time.Sleep(25 * time.Millisecond) + for _, n := range network[:3] { + err = n.GossipEnodeCertificatge() + require.NoError(t, err) + } + + // Check that the network now processes the previous transaction. + err = network[:3].AwaitTransactions(ctx, txs...) + require.NoError(t, err) + + // Check that the network now quickly processes incoming transactions. + // Send one celo from external account 0 to 1 via node 0. + tx, err = accounts[0].SendCelo(ctx, accounts[1].Address, 1, network[0]) + require.NoError(t, err) + txs = append(txs, tx) + + err = network[:3].AwaitTransactions(ctx, txs...) + require.NoError(t, err) + + // Start the first stopped node + err = network[3].Start() + require.NoError(t, err) + + // We need to wait here to allow the call to "Backend.RefreshValPeers" to + // complete before adding peers. This is because "Backend.RefreshValPeers" + // deletes all peers and then re-adds any peers from the cached + // connections, but in the case that peers were recently added there may + // not have been enough time to connect to them and populate the connection + // cache, and in that case "Backend.RefreshValPeers" simply removes all the + // peers. + time.Sleep(250 * time.Millisecond) + // Connect final node to rest of network + network[3].AddPeers(network[:3]...) + time.Sleep(25 * time.Millisecond) + for _, n := range network { + err = n.GossipEnodeCertificatge() + require.NoError(t, err) + } + + // Check that the network continues to quickly processes incoming transactions. + // Send one celo from external account 0 to 1 via node 0. + tx, err = accounts[0].SendCelo(ctx, accounts[1].Address, 1, network[0]) + require.NoError(t, err) + txs = append(txs, tx) + + err = network.AwaitTransactions(ctx, txs...) + require.NoError(t, err) + +} + +// This test was created to reproduce the concurrent map access error in +// https://github.com/ethereum/go-ethereum/issues/1799 +// +// It does this by calling debug_traceBlockByNumber a number of times since the +// trace block code was the source of the concurrent map access. +func TestBlockTracingConcurrentMapAccess(t *testing.T) { + ac := test.AccountConfig(1, 2) + gc, ec, err := test.BuildConfig(ac) + require.NoError(t, err) + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) + defer cancel() + + n := network[0] + + accounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig()) + + var txs []*types.Transaction + // Send one celo from external account 0 to 1 via node 0. + for i := 0; i < 10; i++ { + tx, err := accounts[0].SendCelo(ctx, accounts[1].Address, 1, n) + require.NoError(t, err) + txs = append(txs, tx) + } + + // Wait for the whole network to process the transactions. + err = network.AwaitTransactions(ctx, txs...) + require.NoError(t, err) + + lastTx := txs[len(txs)-1] + + b := n.Tracker.GetProcessedBlockForTx(lastTx.Hash()) + + var wg sync.WaitGroup + for i := 1; i < +int(b.NumberU64()); i++ { + wg.Add(1) + num := i + go func() { + defer wg.Done() + c, err := rpc.DialContext(ctx, n.WSEndpoint()) + require.NoError(t, err) + + var result []interface{} + err = c.CallContext(ctx, &result, "debug_traceBlockByNumber", hexutil.EncodeUint64(uint64(num))) + require.NoError(t, err) + }() + + } + wg.Wait() +} + +// Sends and traces a single native transfer. +// Helpful for debugging issues in TestBlockTracingConcurrentMapAccess. +func TestBlockTracingSequentialAccess(t *testing.T) { + ac := test.AccountConfig(1, 2) + gc, ec, err := test.BuildConfig(ac) + require.NoError(t, err) + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*300) + defer cancel() + n := network[0] + accounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig()) + // Send one celo from external account 0 to 1 via node 0. + tx, err := accounts[0].SendCelo(ctx, accounts[1].Address, 1, n) + require.NoError(t, err) + // Wait for the whole network to process the transaction. + err = network.AwaitTransactions(ctx, tx) + require.NoError(t, err) + b := n.Tracker.GetProcessedBlockForTx(tx.Hash()) + c, err := rpc.DialContext(ctx, n.WSEndpoint()) + require.NoError(t, err) + var result []interface{} + err = c.CallContext(ctx, &result, "debug_traceBlockByNumber", hexutil.EncodeUint64(uint64(int(b.NumberU64())))) + require.NoError(t, err) +} + +type rpcCustomTransaction struct { + BlockNumber *hexutil.Big `json:"blockNumber"` + BlockHash *common.Hash `json:"blockHash"` + GasPrice *hexutil.Big `json:"gasPrice"` +} + +// TestRPCDynamicTxGasPriceWithBigFeeCap test that after a dynamic tx +// was added to a block, the rpc sends in the gasPrice the actual consumed +// price by the tx, which could be less than the feeCap (as in this example) +func TestRPCDynamicTxGasPriceWithBigFeeCap(t *testing.T) { + ac := test.AccountConfig(3, 2) + gc, ec, err := test.BuildConfig(ac) + require.NoError(t, err) + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + accounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig()) + + suggestedGasPrice, err := network[0].WsClient.SuggestGasPrice(ctx) + require.NoError(t, err) + gasFeeCap := big.NewInt(0).Mul(suggestedGasPrice, big.NewInt(90)) + gasTipCap := big.NewInt(0).Mul(suggestedGasPrice, big.NewInt(2)) + + // Send one celo from external account 0 to 1 via node 0. + tx, err := accounts[0].SendCeloWithDynamicFee(ctx, accounts[1].Address, 1, gasFeeCap, gasTipCap, network[0]) + require.NoError(t, err) + + // Wait for the whole network to process the transaction. + err = network.AwaitTransactions(ctx, tx) + require.NoError(t, err) + + var json *rpcCustomTransaction + err = network[0].WsClient.GetRPCClient().CallContext(ctx, &json, "eth_getTransactionByHash", tx.Hash()) + require.NoError(t, err) + require.NotNil(t, json.BlockNumber) + gasPrice := json.GasPrice.ToInt() + require.NotNil(t, json.GasPrice) + require.Greater(t, gasPrice.Int64(), gasTipCap.Int64()) + require.Less(t, gasPrice.Int64(), gasFeeCap.Int64()) +} + +// TestRPCDynamicTxGasPriceWithState aims to test the scenario where a +// an old dynamic tx is requested via rpc, to an archive node. +// As right now on Celo, we are not storing the baseFee in the header (as ethereum does), +// to know the exactly gasPrice expent in a dynamic tx, depends on consuming the +// GasPriceMinimum contract +func TestRPCDynamicTxGasPriceWithState(t *testing.T) { + ac := test.AccountConfig(3, 2) + gc, ec, err := test.BuildConfig(ac) + require.NoError(t, err) + ec.TxLookupLimit = 0 + ec.NoPruning = true + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + accounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig()) + + suggestedGasPrice, err := network[0].WsClient.SuggestGasPrice(ctx) + require.NoError(t, err) + gasFeeCap := big.NewInt(0).Mul(suggestedGasPrice, big.NewInt(90)) + gasTipCap := big.NewInt(0).Mul(suggestedGasPrice, big.NewInt(2)) + + // Send one celo from external account 0 to 1 via node 0. + tx, err := accounts[0].SendCeloWithDynamicFee(ctx, accounts[1].Address, 1, gasFeeCap, gasTipCap, network[0]) + require.NoError(t, err) + + // Wait for the whole network to process the transaction. + err = network.AwaitTransactions(ctx, tx) + require.NoError(t, err) + + var json *rpcCustomTransaction + // Check that the transaction can be retrieved via the rpc api + err = network[0].WsClient.GetRPCClient().CallContext(ctx, &json, "eth_getTransactionByHash", tx.Hash()) + require.NoError(t, err) + // Blocknumber != nil it means that it eas already processed + require.NotNil(t, json.BlockNumber) + + // Create a block containing a transaction, we will prune the state of this block. + _, err = accounts[0].SendCeloTracked(ctx, accounts[1].Address, 1, network[0]) + require.NoError(t, err) + + // Prune state + err = pruneStateOfBlock(ctx, network[0], *json.BlockHash) + require.NoError(t, err) + + var json2 *rpcCustomTransaction + // Check that the transaction can still be retrieved via the rpc api + err = network[0].WsClient.GetRPCClient().CallContext(ctx, &json2, "eth_getTransactionByHash", tx.Hash()) + require.NoError(t, err) + // if the object is nil, it means that was not found + require.NotNil(t, json2) + // Blocknumber != nil it means that it eas already processed + require.NotNil(t, json2.BlockNumber) + require.Equal(t, json.GasPrice, json2.GasPrice) +} + +// TestRPCDynamicTxGasPriceWithoutState aims to test the scenario where a +// an old dynamic tx is requested via rpc, to a full node that does not have +// the state anymore. +// As right now on Celo, we are not storing the baseFee in the header (as ethereum does), +// to know the exactly gasPrice expent in a dynamic tx, depends on consuming the +// GasPriceMinimum contract +func TestRPCDynamicTxGasPriceWithoutState(t *testing.T) { + ac := test.AccountConfig(3, 2) + gc, ec, err := test.BuildConfig(ac) + ec.TrieDirtyCache = 5 + require.NoError(t, err) + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + + accounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig()) + + suggestedGasPrice, err := network[0].WsClient.SuggestGasPrice(ctx) + require.NoError(t, err) + gasFeeCap := big.NewInt(0).Mul(suggestedGasPrice, big.NewInt(90)) + gasTipCap := big.NewInt(0).Mul(suggestedGasPrice, big.NewInt(2)) + + // Send one celo from external account 0 to 1 via node 0. + tx, err := accounts[0].SendCeloWithDynamicFee(ctx, accounts[1].Address, 1, gasFeeCap, gasTipCap, network[0]) + require.NoError(t, err) + + // Wait for the whole network to process the transaction. + err = network.AwaitTransactions(ctx, tx) + require.NoError(t, err) + + var json *rpcCustomTransaction + // Check that the transaction can be retrieved via the rpc api + err = network[0].WsClient.GetRPCClient().CallContext(ctx, &json, "eth_getTransactionByHash", tx.Hash()) + require.NoError(t, err) + // Blocknumber != nil it means that it eas already processed + require.NotNil(t, json.BlockNumber) + + // Create one block to be able to prune the last state + _, err = accounts[0].SendCeloTracked(ctx, accounts[1].Address, 1, network[0]) + require.NoError(t, err) + + // Prune state + err = pruneStateOfBlock(ctx, network[0], *json.BlockHash) + require.NoError(t, err) + + var json2 *rpcCustomTransaction + // Check that the transaction can still be retrieved via the rpc api + err = network[0].WsClient.GetRPCClient().CallContext(ctx, &json2, "eth_getTransactionByHash", tx.Hash()) + require.NoError(t, err) + // if the object is nil, it means that was not found + require.NotNil(t, json2) + // Blocknumber != nil it means that it eas already processed + require.NotNil(t, json2.BlockNumber) + + require.Nil(t, json2.GasPrice) +} + +func pruneStateOfBlock(ctx context.Context, node *test.Node, blockHash common.Hash) error { + var block *types.Block + block, err := node.WsClient.BlockByHash(ctx, blockHash) + if err != nil { + return err + } + root := block.Root() + node.Eth.BlockChain().StateCache().TrieDB().Dereference(root) + + return nil +} + +func TestEthersJSCompatibility(t *testing.T) { + ac := test.AccountConfig(1, 1) + gc, ec, err := test.BuildConfig(ac) + require.NoError(t, err) + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) + defer cancel() + + num, err := network[0].WsClient.BlockNumber(ctx) + require.NoError(t, err) + + // Execute typescript tests to check ethers.js compatibility. + // + // The '--networkaddr' and '--blocknum' flags are npm config variables, the + // values become available under 'process.env.npm_config_networkaddr' and + // 'process.env.npm_config_blocknum' in typescript test. Everything after + // '--' are flags that are passed to mocha and these flags are controlling + // which tests to run. + + // The tests don't seem to work on CI with IPV6 addresses so we convert to IPV4 here + addr := strings.Replace(network[0].Node.HTTPEndpoint(), "[::]", "127.0.0.1", 1) + + cmd := exec.Command("npm", "run", "test", "--networkaddr="+addr, "--blocknum="+hexutil.Uint64(num).String(), "--", "--grep", "ethers.js compatibility tests with state") + cmd.Dir = "./ethersjs-api-check/" + println("executing mocha test with", cmd.String()) + output, err := cmd.CombinedOutput() + println(string(output)) + require.NoError(t, err) + + err = network[0].Tracker.AwaitBlock(ctx, num+1) + require.NoError(t, err) + block := network[0].Tracker.GetProcessedBlock(num) + + // Prune state + err = pruneStateOfBlock(ctx, network[0], block.Hash()) + require.NoError(t, err) + + // Execute typescript tests to check what happens with a pruned block. + cmd = exec.Command("npm", "run", "test", "--networkaddr="+addr, "--blocknum="+hexutil.Uint64(num).String(), "--", "--grep", "ethers.js compatibility tests with no state") + cmd.Dir = "./ethersjs-api-check/" + println("executing mocha test with", cmd.String()) + output, err = cmd.CombinedOutput() + println(string(output)) + require.NoError(t, err) +} + +// This test checks the functionality of the configuration to enable/disable +// returning the 'gasLimit' and 'baseFeePerGas' fields on RPC blocks. +func TestEthersJSCompatibilityDisable(t *testing.T) { + ac := test.AccountConfig(1, 1) + gc, ec, err := test.BuildConfig(ac) + require.NoError(t, err) + + // Check fields present (compatibility set by default) + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) + defer cancel() + + result := make(map[string]interface{}) + err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true) + require.NoError(t, err) + + _, ok := result["gasLimit"] + assert.True(t, ok, "gasLimit field should be present on RPC block") + _, ok = result["baseFeePerGas"] + assert.True(t, ok, "baseFeePerGas field should be present on RPC block") + + // Turn of compatibility and check fields are not present + ec.RPCEthCompatibility = false + network, shutdown, err = test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + + ctx, cancel = context.WithTimeout(context.Background(), time.Second*20) + defer cancel() + + result = make(map[string]interface{}) + err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true) + require.NoError(t, err) + + _, ok = result["gasLimit"] + assert.False(t, ok, "gasLimit field should not be present on RPC block") + _, ok = result["baseFeePerGas"] + assert.False(t, ok, "baseFeePerGas field should not be present on RPC block") +}
diff --git go-ethereum/e2e_test/e2e_transfer_test.go celo/e2e_test/e2e_transfer_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b7d4ebaf9d38f5b5e242a0a8e8f664f431b1463d --- /dev/null +++ celo/e2e_test/e2e_transfer_test.go @@ -0,0 +1,201 @@ +package e2e + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + oneCelo = 1000000000000000000 +) + +// TestTransferCELO checks following accounts: +// - Sender account has transfer value, transaction fee deducted +// - Receiver account has transfer value added. +// - Governance account has base fee added. +// - validator account has tip fee added. +func TestTransferCELO(t *testing.T) { + ac := test.AccountConfig(1, 3) + gc, ec, err := test.BuildConfig(ac) + gc.Hardforks.EspressoBlock = big.NewInt(0) + require.NoError(t, err) + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + node := network[0] // validator node + client := node.WsClient + devAccounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig()) + sender := devAccounts[0] + recipient := devAccounts[1] + gateWayFeeRecipient := devAccounts[2] + + // Get datum to set GasPrice/MaxFeePerGas/MaxPriorityFeePerGas to sensible values + header, err := network[0].WsClient.HeaderByNumber(ctx, common.Big0) + require.NoError(t, err) + datum, err := network[0].Eth.APIBackend.GasPriceMinimumForHeader(ctx, nil, header) + require.NoError(t, err) + + testCases := []struct { + name string + txArgs *ethapi.TransactionArgs + }{ + { + name: "eth compatible LegacyTxType", + txArgs: &ethapi.TransactionArgs{ + To: &recipient.Address, + Value: (*hexutil.Big)(new(big.Int).SetInt64(oneCelo)), + GasPrice: (*hexutil.Big)(datum.Mul(datum, new(big.Int).SetInt64(4))), + }, + }, + { + name: "eth incompatible LegacyTxType", + txArgs: &ethapi.TransactionArgs{ + To: &recipient.Address, + Value: (*hexutil.Big)(new(big.Int).SetInt64(oneCelo)), + GasPrice: (*hexutil.Big)(datum.Mul(datum, new(big.Int).SetInt64(4))), + GatewayFee: (*hexutil.Big)(new(big.Int).SetInt64(oneCelo / 10)), + GatewayFeeRecipient: &gateWayFeeRecipient.Address, + }, + }, + { + name: "AccessListTxType", + txArgs: &ethapi.TransactionArgs{ + To: &recipient.Address, + Value: (*hexutil.Big)(new(big.Int).SetInt64(oneCelo)), + GasPrice: (*hexutil.Big)(datum.Mul(datum, new(big.Int).SetInt64(4))), + AccessList: &types.AccessList{}, + }, + }, + { + name: "DynamicFeeTxType - tip = MaxFeePerGas - BaseFee", + txArgs: &ethapi.TransactionArgs{ + To: &recipient.Address, + Value: (*hexutil.Big)(new(big.Int).SetInt64(oneCelo)), + MaxFeePerGas: (*hexutil.Big)(datum.Mul(datum, new(big.Int).SetInt64(4))), + MaxPriorityFeePerGas: (*hexutil.Big)(datum.Mul(datum, new(big.Int).SetInt64(4))), + }, + }, + { + name: "DynamicFeeTxType - tip = MaxPriorityFeePerGas", + txArgs: &ethapi.TransactionArgs{ + To: &recipient.Address, + Value: (*hexutil.Big)(new(big.Int).SetInt64(oneCelo)), + MaxFeePerGas: (*hexutil.Big)(datum.Mul(datum, new(big.Int).SetInt64(4))), + MaxPriorityFeePerGas: (*hexutil.Big)(datum), + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + watcher := test.NewBalanceWatcher(client, []common.Address{sender.Address, recipient.Address, gateWayFeeRecipient.Address, node.Address}) + blockNum, err := client.BlockNumber(ctx) + require.NoError(t, err) + signer := types.MakeSigner(devAccounts[0].ChainConfig, new(big.Int).SetUint64(blockNum)) + tx, err := prepareTransaction(*tc.txArgs, sender.Key, sender.Address, signer, client) + require.NoError(t, err) + err = client.SendTransaction(ctx, tx) + require.NoError(t, err, "SendTransaction failed", "tx", *tx) + err = network.AwaitTransactions(ctx, tx) + require.NoError(t, err) + watcher.Update() + receipt, err := client.TransactionReceipt(ctx, tx.Hash()) + require.NoError(t, err) + + // check value goes to recipient + expected := tx.Value() + actual := watcher.Delta(recipient.Address) + assert.Equal(t, expected, actual, "Recipient's balance increase unexpected", "expected", expected.Int64(), "actual", actual.Int64()) + + // Check tip goes to validator + header, err := network[0].WsClient.HeaderByNumber(ctx, receipt.BlockNumber) + require.NoError(t, err) + gpm, err := network[0].Eth.APIBackend.GasPriceMinimumForHeader(ctx, nil, header) + require.NoError(t, err) + baseFee := new(big.Int).Mul(gpm, new(big.Int).SetUint64(receipt.GasUsed)) + switch tx.Type() { + case types.LegacyTxType, types.AccessListTxType: + fee := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(receipt.GasUsed)) + expected = new(big.Int).Sub(fee, baseFee) + case types.DynamicFeeTxType: + expected = tx.EffectiveGasTipValue(gpm) + expected.Mul(expected, new(big.Int).SetUint64(receipt.GasUsed)) + } + actual = watcher.Delta(node.Address) + assert.Equal(t, expected, actual, "Validator's balance increase unexpected", "expected", expected.Int64(), "actual", actual.Int64()) + + // check value + tx fee + gateway fee are subtracted from sender + var fee *big.Int + switch tx.Type() { + case types.LegacyTxType, types.AccessListTxType: + fee = new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(receipt.GasUsed)) + case types.DynamicFeeTxType: + tip := tx.EffectiveGasTipValue(gpm) + tip.Mul(tip, new(big.Int).SetUint64(receipt.GasUsed)) + fee = new(big.Int).Add(tip, baseFee) + } + consumed := new(big.Int).Add(tx.Value(), fee) + if tx.GatewayFeeRecipient() != nil && tx.GatewayFee() != nil { + consumed.Add(consumed, tx.GatewayFee()) + } + expected = new(big.Int).Neg(consumed) + actual = watcher.Delta(sender.Address) + assert.Equal(t, expected, actual, "Sender's balance decrease unexpected", "expected", expected.Int64(), "actual", expected.Int64()) + + // Check gateway fee + if tx.GatewayFeeRecipient() != nil && tx.GatewayFee() != nil { + expected = tx.GatewayFee() + actual = watcher.Delta(gateWayFeeRecipient.Address) + assert.Equal(t, expected, actual, "gateWayFeeRecipient's balance increase unexpected", "expected", expected.Int64(), "actual", actual.Int64()) + } + }) + } +} + +// prepareTransaction prepares gasPrice, gasLimit and sign the transaction. +func prepareTransaction(txArgs ethapi.TransactionArgs, senderKey *ecdsa.PrivateKey, sender common.Address, signer types.Signer, client *ethclient.Client) (*types.Transaction, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + defer cancel() + + // Set nonce + nonce, err := client.PendingNonceAt(ctx, sender) + if err != nil { + return nil, err + } + txArgs.Nonce = (*hexutil.Uint64)(&nonce) + + // Set gasLimit + if txArgs.Gas == nil { + msg := ethereum.CallMsg{From: sender, To: txArgs.To, GasPrice: txArgs.GasPrice.ToInt(), Value: txArgs.Value.ToInt()} + gasLimit, err := client.EstimateGas(ctx, msg) + if err != nil { + return nil, fmt.Errorf("failed to estimate gas needed: %v", err) + } + txArgs.Gas = (*hexutil.Uint64)(&gasLimit) + } + + // Create the transaction and sign it + rawTx := txArgs.ToTransaction() + signed, err := types.SignTx(rawTx, signer, senderKey) + if err != nil { + return nil, err + } + return signed, nil +}
diff --git go-ethereum/e2e_test/e2e_bench_test.go celo/e2e_test/e2e_bench_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c5f61413164c948b71d66a69b03c526481abd58d --- /dev/null +++ celo/e2e_test/e2e_bench_test.go @@ -0,0 +1,73 @@ +package e2e_test + +import ( + "context" + "fmt" + "math/rand" + "testing" + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/test" + "github.com/stretchr/testify/require" +) + +func BenchmarkNet100EmptyBlocks(b *testing.B) { + for _, n := range []int{1, 3, 9} { + b.Run(fmt.Sprintf("%dNodes", n), func(b *testing.B) { + for i := 0; i < b.N; i++ { + ac := test.AccountConfig(n, 0) + gc, ec, err := test.BuildConfig(ac) + require.NoError(b, err) + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(b, err) + defer shutdown() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*60*time.Duration(n)) + defer cancel() + b.ResetTimer() + err = network.AwaitBlock(ctx, 100) + require.NoError(b, err) + } + }) + } +} + +func BenchmarkNet1000Txs(b *testing.B) { + b.Skip() // Flaky + // Seed the random number generator so that the generated numbers are + // different on each run. + rand.Seed(time.Now().UnixNano()) + for _, n := range []int{1, 3, 9} { + b.Run(fmt.Sprintf("%dNodes", n), func(b *testing.B) { + for i := 0; i < b.N; i++ { + + ac := test.AccountConfig(n, n) + gc, ec, err := test.BuildConfig(ac) + require.NoError(b, err) + accounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig()) + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(b, err) + defer shutdown() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*60*time.Duration(n)) + defer cancel() + b.ResetTimer() + + // Send 1000 txs randomly between nodes accounts sending via a + // random node. + txs := make([]*types.Transaction, 1000) + for i := range txs { + sender := accounts[rand.Intn(n)] + receiver := accounts[rand.Intn(n)] + node := network[rand.Intn(n)] + tx, err := sender.SendCelo(ctx, receiver.Address, 1, node) + require.NoError(b, err) + txs[i] = tx + } + err = network.AwaitTransactions(ctx, txs...) + require.NoError(b, err) + block := network[0].Tracker.GetProcessedBlockForTx(txs[len(txs)-1].Hash()) + fmt.Printf("Processed 1000 txs in %d blocks\n", block.NumberU64()) + } + }) + } +}
diff --git go-ethereum/e2e_test/ethersjs-api-check/test/test.ts celo/e2e_test/ethersjs-api-check/test/test.ts new file mode 100644 index 0000000000000000000000000000000000000000..77e7aeee33d9eadf503dc335e040a5d8e962aa4d --- /dev/null +++ celo/e2e_test/ethersjs-api-check/test/test.ts @@ -0,0 +1,88 @@ +/* + * Note these tests are intended to be invoked only by our e2e tests, they + * should not be executed in a standalone fashion. + * See e2e_test.TestEthersJSCompatibility + */ +import {ethers} from 'ethers'; +import {assert} from 'chai'; +import 'mocha'; + +describe('ethers.js compatibility tests with state', () => { + + it('provider.getBlock works (block has gasLimit set)', async () => { + let provider = new ethers.providers.JsonRpcProvider(process.env.npm_config_networkaddr); + let block = await provider.getBlock(process.env.npm_config_blocknum as string); + + // These assertions trigger on undefined or null + assert.notEqual(block, null); + assert.notEqual(block.gasLimit, null); + }); + + it('EIP-1559 transactions supported (can get feeData)', async () => { + let provider = new ethers.providers.JsonRpcProvider(process.env.npm_config_networkaddr); + + // The fee data is the construct used to determine if EIP-1559 transactions are supported, if it contains max + let feeData = await provider.getFeeData(); + + // These assertions trigger on undefined or null + assert.notEqual(feeData, null); + // If the following 2 fields are set then the network is assumed to support EIP-1559 transactions. + assert.notEqual(feeData.maxFeePerGas, null); + assert.notEqual(feeData.maxPriorityFeePerGas, null); + // We check the other 2 fields for completeness, they should also be set. + assert.notEqual(feeData.gasPrice, null); + assert.notEqual(feeData.lastBaseFeePerGas, null); + }); + + it('block has gasLimit', async () => { + let provider = new ethers.providers.JsonRpcProvider(process.env.npm_config_networkaddr); + const fullBlock = await provider.send( + 'eth_getBlockByNumber', + [ethers.utils.hexValue(process.env.npm_config_blocknum as string), true] + ) + assert.isTrue(fullBlock.hasOwnProperty('gasLimit')) + }); + + it('block has baseFeePerGas', async () => { + let provider = new ethers.providers.JsonRpcProvider(process.env.npm_config_networkaddr); + const fullBlock = await provider.send( + 'eth_getBlockByNumber', + [ethers.utils.hexValue(process.env.npm_config_blocknum as string), true] + ) + assert.isTrue(fullBlock.hasOwnProperty('baseFeePerGas')) + }); + +}); + +describe('ethers.js compatibility tests with no state', () => { + + it('provider.getBlock throws exception (no gasLimit)', async () => { + let provider = new ethers.providers.JsonRpcProvider(process.env.npm_config_networkaddr); + try { + await provider.getBlock(process.env.npm_config_blocknum as string); + } catch (e) { + return + } + assert.fail("Expecting exception to be thrown when getting block") + }); + + it('block has no gasLimit', async () => { + let provider = new ethers.providers.JsonRpcProvider(process.env.npm_config_networkaddr); + const fullBlock = await provider.send( + 'eth_getBlockByNumber', + [ethers.utils.hexValue(process.env.npm_config_blocknum as string), true] + ) + assert.isFalse(fullBlock.hasOwnProperty('gasLimit')) + }); + + it('block has no baseFeePerGas', async () => { + let provider = new ethers.providers.JsonRpcProvider(process.env.npm_config_networkaddr); + const fullBlock = await provider.send( + 'eth_getBlockByNumber', + [ethers.utils.hexValue(process.env.npm_config_blocknum as string), true] + ) + assert.isFalse(fullBlock.hasOwnProperty('baseFeePerGas')) + }); + + +});
diff --git go-ethereum/build/ci.go celo/build/ci.go index 1c06d48b14153f8f367a268257130e5f94edd909..3906e2f2e291a0dc1385a09e6475bc5298836f80 100644 --- go-ethereum/build/ci.go +++ celo/build/ci.go @@ -27,13 +27,14 @@ install [ -arch architecture ] [ -cc compiler ] [ packages... ] -- builds packages and executables test [ -coverage ] [ packages... ] -- runs the tests lint -- runs certain pre-selected linters - archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -signify key-envvar ] [ -upload dest ] -- archives build artifacts + archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -upload dest ] -- archives build artifacts importkeys -- imports signing keys from env debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package nsis -- creates a Windows NSIS installer aar [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive xcode [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework xgo [ -alltools ] [ options ] -- cross builds according to options + xgo-archive [ -targets linux/amd64,linux/386... ][ -type zip|tar ][ -in dir ][ -out dir ] -- archives build artifacts from cross-compilation purge [ -store blobstore ] [ -days threshold ] -- purges old archives from the blobstore   For all commands, -n prevents execution of external programs (dry run mode). @@ -79,9 +80,9 @@ executablePath("abigen"), executablePath("bootnode"), executablePath("evm"), executablePath("geth"), - executablePath("puppeth"), executablePath("rlpdump"), executablePath("clef"), + executablePath("blspopchecker"), }   // A debian package is created for all executables listed here. @@ -101,10 +102,6 @@ }, { BinaryName: "geth", Description: "Ethereum CLI client.", - }, - { - BinaryName: "puppeth", - Description: "Ethereum private network manager.", }, { BinaryName: "rlpdump", @@ -114,6 +111,10 @@ { BinaryName: "clef", Description: "Ethereum account management tool.", }, + { + BinaryName: "blspopchecker", + Description: "Developer utility tool checks BLS PoP signatures in genesis.", + }, }   // A debian package is created for all executables listed here. @@ -174,6 +175,8 @@ case "install": doInstall(os.Args[2:]) case "test": doTest(os.Args[2:]) + case "ensure-linter": + doEnsureLinter(os.Args[2:]) case "lint": doLint(os.Args[2:]) case "archive": @@ -190,6 +193,8 @@ case "xcode": doXCodeFramework(os.Args[2:]) case "xgo": doXgo(os.Args[2:]) + case "xgo-archive": + doXgoArchive(os.Args[2:]) case "purge": doPurge(os.Args[2:]) default: @@ -251,6 +256,9 @@ // buildFlags returns the go tool flags for building. func buildFlags(env build.Environment) (flags []string) { var ld []string + if env.IsMusl { + flags = append(flags, []string{"-tags", "musl"}...) + } if env.Commit != "" { ld = append(ld, "-X", "main.gitCommit="+env.Commit) ld = append(ld, "-X", "main.gitDate="+env.Date) @@ -259,10 +267,17 @@ // Strip DWARF on darwin. This used to be required for certain things, // and there is no downside to this, so we just keep doing it. if runtime.GOOS == "darwin" { ld = append(ld, "-s") + } else { + ld = append(ld, "-extldflags") + ld = append(ld, "-pthread") + } + if env.MetricsDefault { + ld = append(ld, "-X", "'github.com/ethereum/go-ethereum/metrics.EnabledDefaultValue=true'") } if len(ld) > 0 { flags = append(flags, "-ldflags", strings.Join(ld, " ")) } + return flags }   @@ -292,7 +307,7 @@ // Test a single package at a time. CI builders are slow // and some tests run into timeouts under load. gotest.Args = append(gotest.Args, "-p", "1") if *coverage { - gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover") + gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover", "-coverprofile=coverage.out") } if *verbose { gotest.Args = append(gotest.Args, "-v") @@ -306,6 +321,16 @@ gotest.Args = append(gotest.Args, packages...) build.MustRun(gotest) }   +// doEnsureLinter ensure golangci-lint is properly installed +func doEnsureLinter(cmdline []string) { + var ( + cachedir = flag.String("cachedir", "./build/cache", "directory for caching golangci-lint binary.") + ) + flag.CommandLine.Parse(cmdline) + linter := downloadLinter(*cachedir) + fmt.Printf("LinterPath=%s", linter) +} + // doLint runs golangci-lint on requested packages. func doLint(cmdline []string) { var ( @@ -444,8 +469,8 @@ if env.IsCronJob { log.Printf("skipping archive creation because this is a cron job") os.Exit(0) } - if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") { - log.Printf("skipping archive creation because branch %q, tag %q is not on the inclusion list", env.Branch, env.Tag) + if env.Branch != "master" && !strings.HasPrefix(env.Branch, "release/") { + log.Printf("skipping archive creation because branch %q is not on the whitelist", env.Branch) os.Exit(0) } } @@ -939,10 +964,10 @@ build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools) build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil) build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil) if err := cp.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll"); err != nil { - log.Fatal("Failed to copy SimpleFC.dll: %v", err) + log.Fatalf("Failed to copy SimpleFC.dll: %v", err) } if err := cp.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING"); err != nil { - log.Fatal("Failed to copy copyright note: %v", err) + log.Fatalf("Failed to copy copyright note: %v", err) } // Build the installer. This assumes that all the needed files have been previously // built (don't mix building and packaging to keep cross compilation complexity to a @@ -975,10 +1000,15 @@ signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`) signify = flag.String("signify", "", `Environment variable holding the signify signing key (e.g. ANDROID_SIGNIFY_KEY)`) deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`) upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`) + dlgo = flag.Bool("dlgo", false, "Download Go and build with it") ) flag.CommandLine.Parse(cmdline) env := build.Env() tc := new(build.GoToolchain) + if *dlgo { + csdb := build.MustLoadChecksums("build/checksums.txt") + tc.Root = build.DownloadGo(csdb, dlgoVersion) + }   // Sanity check that the SDK and NDK are installed and set if os.Getenv("ANDROID_HOME") == "" { @@ -995,7 +1025,12 @@ // gomobile bind work because it expects go.sum to contain all checksums. build.MustRun(tc.Go("mod", "download"))   // Build the Android archive and Maven resources - build.MustRun(gomobileTool("bind", "-ldflags", "-s -w", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile")) + // build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind")) + ldflags := "-s -w" + if env.MetricsDefault { + ldflags = ldflags + " -X 'github.com/ethereum/go-ethereum/metrics.EnabledDefaultValue=true'" + } + build.MustRun(gomobileTool("bind", "-ldflags", ldflags, "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))   if *local { // If we're building locally, copy bundle to build dir and skip Maven @@ -1124,7 +1159,13 @@ // gomobile bind work because it expects go.sum to contain all checksums. build.MustRun(tc.Go("mod", "download"))   // Build the iOS XCode framework - bind := gomobileTool("bind", "-ldflags", "-s -w", "--target", "ios", "-v", "github.com/ethereum/go-ethereum/mobile") + // build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind")) + ldflags := "-s -w" + if env.MetricsDefault { + ldflags = ldflags + " -X 'github.com/ethereum/go-ethereum/metrics.EnabledDefaultValue=true'" + } + bind := gomobileTool("bind", "-ldflags", ldflags, "--target", "ios,iossimulator", "-v", "github.com/ethereum/go-ethereum/mobile") + bind.Env = append(bind.Env, "CGO_ENABLED=1")   if *local { // If we're building locally, use the build folder and stop afterwards @@ -1240,6 +1281,80 @@ cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...) cmd.Env = os.Environ() cmd.Env = append(cmd.Env, []string{"GOBIN=" + GOBIN}...) return cmd +} + +// Archive cross compilation artifacts + +func doXgoArchive(cmdline []string) { + var ( + targetsFlag = flag.String("targets", "", `List of targets int the same format as xgo`) + archiveType = flag.String("type", "tar", `Archive type tar|zip`) + inputDir = flag.String("in", "", `Directory with all build binaries`) + outputDir = flag.String("out", "", `Output directory`) + ext string + ) + + flag.CommandLine.Parse(cmdline) + targets := strings.Split(*targetsFlag, ",") + switch *archiveType { + case "zip": + ext = ".zip" + case "tar": + ext = ".tar.gz" + default: + log.Fatal("unknown archive type: ", archiveType) + } + + var ( + env = build.Env() + ) + maybeSkipArchive(env) + + for _, target := range targets { + // linux/amd64 => geth-linux-amd64 + var ( + targetBinSegment = strings.Replace(target, "/", "-", 1) + basegeth = targetBinSegment + "-" + params.ArchiveVersion(env.Commit) + geth = filepath.Join(*outputDir, "geth-"+basegeth+ext) + alltools = filepath.Join(*outputDir, "geth-alltools-"+basegeth+ext) + ) + + if err := build.WriteArchive(geth, xgoGethArchiveFiles(targetBinSegment, *inputDir)); err != nil { + log.Fatal(err) + } + if err := build.WriteArchive(alltools, xgoAllToolsArchiveFiles(targetBinSegment, *inputDir)); err != nil { + log.Fatal(err) + } + } +} + +func xgoGethArchiveFiles(target string, dir string) []string { + return []string{ + "COPYING", + executableXgoPath("geth", target, dir), + } +} + +func xgoAllToolsArchiveFiles(target string, dir string) []string { + return []string{ + "COPYING", + executableXgoPath("abigen", target, dir), + executableXgoPath("bootnode", target, dir), + executableXgoPath("evm", target, dir), + executableXgoPath("geth", target, dir), + executableXgoPath("rlpdump", target, dir), + executableXgoPath("clef", target, dir), + executableXgoPath("blspopchecker", target, dir), + } +} + +func executableXgoPath(exec, target, dir string) string { + filename := exec + "-" + target + if strings.HasPrefix(target, "windows") { + filename = filename + ".exe" + } + + return filepath.Join(dir, filename) }   // Binary distribution cleanups
diff --git go-ethereum/cmd/checkpoint-admin/README.md celo/cmd/checkpoint-admin/README.md deleted file mode 100644 index 43e3785ec2fad670ca159d20e9ad63ba9be33f5b..0000000000000000000000000000000000000000 --- go-ethereum/cmd/checkpoint-admin/README.md +++ /dev/null @@ -1,103 +0,0 @@ -## Checkpoint-admin - -Checkpoint-admin is a tool for updating checkpoint oracle status. It provides a series of functions including deploying checkpoint oracle contract, signing for new checkpoints, and updating checkpoints in the checkpoint oracle contract. - -### Checkpoint - -In the LES protocol, there is an important concept called checkpoint. In simple terms, whenever a certain number of blocks are generated on the blockchain, a new checkpoint is generated which contains some important information such as - -* Block hash at checkpoint -* Canonical hash trie root at checkpoint -* Bloom trie root at checkpoint - -*For a more detailed introduction to checkpoint, please see the LES [spec](https://github.com/ethereum/devp2p/blob/master/caps/les.md).* - -Using this information, light clients can skip all historical block headers when synchronizing data and start synchronization from this checkpoint. Therefore, as long as the light client can obtain some latest and correct checkpoints, the amount of data and time for synchronization will be greatly reduced. - -However, from a security perspective, the most critical step in a synchronization algorithm based on checkpoints is to determine whether the checkpoint used by the light client is correct. Otherwise, all blockchain data synchronized based on this checkpoint may be wrong. For this we provide two different ways to ensure the correctness of the checkpoint used by the light client. - -#### Hardcoded checkpoint - -There are several hardcoded checkpoints in the [source code](https://github.com/ethereum/go-ethereum/blob/master/params/config.go#L38) of the go-ethereum project. These checkpoints are updated by go-ethereum developers when new versions of software are released. Because light client users trust Geth developers to some extent, hardcoded checkpoints in the code can also be considered correct. - -#### Checkpoint oracle - -Hardcoded checkpoints can solve the problem of verifying the correctness of checkpoints (although this is a more centralized solution). But the pain point of this solution is that developers can only update checkpoints when a new version of software is released. In addition, light client users usually do not keep the Geth version they use always up to date. So hardcoded checkpoints used by users are generally stale. Therefore, it still needs to download a large amount of blockchain data during synchronization. - -Checkpoint oracle is a more flexible solution. In simple terms, this is a smart contract that is deployed on the blockchain. The smart contract records several designated trusted signers. Whenever enough trusted signers have issued their signatures for the same checkpoint, it can be considered that the checkpoint has been authenticated by the signers. Checkpoints authenticated by trusted signers can be considered correct. - -So this way, even without updating the software version, as long as the trusted signers regularly update the checkpoint in oracle on time, the light client can always use the latest and verified checkpoint for data synchronization. - -### Usage - -Checkpoint-admin is a command line tool designed for checkpoint oracle. Users can easily deploy contracts and update checkpoints through this tool. - -#### Install - -```shell -go get github.com/ethereum/go-ethereum/cmd/checkpoint-admin -``` - -#### Deploy - -Deploy checkpoint oracle contract. `--signers` indicates the specified trusted signer, and `--threshold` indicates the minimum number of signatures required by trusted signers to update a checkpoint. - -```shell -checkpoint-admin deploy --rpc <NODE_RPC_ENDPOINT> --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_TX> --signers <TRUSTED_SIGNER_LIST> --threshold 1 -``` - -It is worth noting that checkpoint-admin only supports clef as a signer for transactions and plain text(checkpoint). For more clef usage, please see the clef [tutorial](https://geth.ethereum.org/docs/clef/tutorial) . - -#### Sign - -Checkpoint-admin provides two different modes of signing. You can automatically obtain the current stable checkpoint and sign it interactively, and you can also use the information provided by the command line flags to sign checkpoint offline. - -**Interactive mode** - -```shell -checkpoint-admin sign --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_CHECKPOINT> --rpc <NODE_RPC_ENDPOINT> -``` - -*It is worth noting that the connected Geth node can be a fullnode or a light client. If it is fullnode, you must enable the LES protocol. E.G. add `--light.serv 50` to the startup command line flags*. - -**Offline mode** - -```shell -checkpoint-admin sign --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_CHECKPOINT> --index <CHECKPOINT_INDEX> --hash <CHECKPOINT_HASH> --oracle <CHECKPOINT_ORACLE_ADDRESS> -``` - -*CHECKPOINT_HASH is obtained based on this [calculation method](https://github.com/ethereum/go-ethereum/blob/master/params/config.go#L251).* - -#### Publish - -Collect enough signatures from different trusted signers for the same checkpoint and submit them to oracle to update the "authenticated" checkpoint in the contract. - -```shell -checkpoint-admin publish --clef <CLEF_ENDPOINT> --rpc <NODE_RPC_ENDPOINT> --signer <SIGNER_TO_SIGN_TX> --index <CHECKPOINT_INDEX> --signatures <CHECKPOINT_SIGNATURE_LIST> -``` - -#### Status query - -Check the latest status of checkpoint oracle. - -```shell -checkpoint-admin status --rpc <NODE_RPC_ENDPOINT> -``` - -### Enable checkpoint oracle in your private network - -Currently, only the Ethereum mainnet and the default supported test networks (ropsten, rinkeby, goerli) activate this feature. If you want to activate this feature in your private network, you can overwrite the relevant checkpoint oracle settings through the configuration file after deploying the oracle contract. - -* Get your node configuration file `geth dumpconfig OTHER_COMMAND_LINE_OPTIONS > config.toml` -* Edit the configuration file and add the following information - -```toml -[Eth.CheckpointOracle] -Address = CHECKPOINT_ORACLE_ADDRESS -Signers = [TRUSTED_SIGNER_1, ..., TRUSTED_SIGNER_N] -Threshold = THRESHOLD -``` - -* Start geth with the modified configuration file - -*In the private network, all fullnodes and light clients need to be started using the same checkpoint oracle settings.* \ No newline at end of file
diff --git go-ethereum/cmd/checkpoint-admin/common.go celo/cmd/checkpoint-admin/common.go deleted file mode 100644 index 5e25bafaeb0caa98d67bf78b8ac60e7e79aba9e3..0000000000000000000000000000000000000000 --- go-ethereum/cmd/checkpoint-admin/common.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "strconv" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/accounts/external" - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/contracts/checkpointoracle" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" - "gopkg.in/urfave/cli.v1" -) - -// newClient creates a client with specified remote URL. -func newClient(ctx *cli.Context) *ethclient.Client { - client, err := ethclient.Dial(ctx.GlobalString(nodeURLFlag.Name)) - if err != nil { - utils.Fatalf("Failed to connect to Ethereum node: %v", err) - } - return client -} - -// newRPCClient creates a rpc client with specified node URL. -func newRPCClient(url string) *rpc.Client { - client, err := rpc.Dial(url) - if err != nil { - utils.Fatalf("Failed to connect to Ethereum node: %v", err) - } - return client -} - -// getContractAddr retrieves the register contract address through -// rpc request. -func getContractAddr(client *rpc.Client) common.Address { - var addr string - if err := client.Call(&addr, "les_getCheckpointContractAddress"); err != nil { - utils.Fatalf("Failed to fetch checkpoint oracle address: %v", err) - } - return common.HexToAddress(addr) -} - -// getCheckpoint retrieves the specified checkpoint or the latest one -// through rpc request. -func getCheckpoint(ctx *cli.Context, client *rpc.Client) *params.TrustedCheckpoint { - var checkpoint *params.TrustedCheckpoint - - if ctx.GlobalIsSet(indexFlag.Name) { - var result [3]string - index := uint64(ctx.GlobalInt64(indexFlag.Name)) - if err := client.Call(&result, "les_getCheckpoint", index); err != nil { - utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err) - } - checkpoint = &params.TrustedCheckpoint{ - SectionIndex: index, - SectionHead: common.HexToHash(result[0]), - CHTRoot: common.HexToHash(result[1]), - BloomRoot: common.HexToHash(result[2]), - } - } else { - var result [4]string - err := client.Call(&result, "les_latestCheckpoint") - if err != nil { - utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err) - } - index, err := strconv.ParseUint(result[0], 0, 64) - if err != nil { - utils.Fatalf("Failed to parse checkpoint index %v", err) - } - checkpoint = &params.TrustedCheckpoint{ - SectionIndex: index, - SectionHead: common.HexToHash(result[1]), - CHTRoot: common.HexToHash(result[2]), - BloomRoot: common.HexToHash(result[3]), - } - } - return checkpoint -} - -// newContract creates a registrar contract instance with specified -// contract address or the default contracts for mainnet or testnet. -func newContract(client *rpc.Client) (common.Address, *checkpointoracle.CheckpointOracle) { - addr := getContractAddr(client) - if addr == (common.Address{}) { - utils.Fatalf("No specified registrar contract address") - } - contract, err := checkpointoracle.NewCheckpointOracle(addr, ethclient.NewClient(client)) - if err != nil { - utils.Fatalf("Failed to setup registrar contract %s: %v", addr, err) - } - return addr, contract -} - -// newClefSigner sets up a clef backend and returns a clef transaction signer. -func newClefSigner(ctx *cli.Context) *bind.TransactOpts { - clef, err := external.NewExternalSigner(ctx.String(clefURLFlag.Name)) - if err != nil { - utils.Fatalf("Failed to create clef signer %v", err) - } - return bind.NewClefTransactor(clef, accounts.Account{Address: common.HexToAddress(ctx.String(signerFlag.Name))}) -}
diff --git go-ethereum/cmd/checkpoint-admin/exec.go celo/cmd/checkpoint-admin/exec.go deleted file mode 100644 index 0c60eafe0a7cfb006874578d72debca3a664728d..0000000000000000000000000000000000000000 --- go-ethereum/cmd/checkpoint-admin/exec.go +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "bytes" - "context" - "encoding/binary" - "fmt" - "math/big" - "strings" - "time" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/contracts/checkpointoracle" - "github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" - "gopkg.in/urfave/cli.v1" -) - -var commandDeploy = cli.Command{ - Name: "deploy", - Usage: "Deploy a new checkpoint oracle contract", - Flags: []cli.Flag{ - nodeURLFlag, - clefURLFlag, - signerFlag, - signersFlag, - thresholdFlag, - }, - Action: utils.MigrateFlags(deploy), -} - -var commandSign = cli.Command{ - Name: "sign", - Usage: "Sign the checkpoint with the specified key", - Flags: []cli.Flag{ - nodeURLFlag, - clefURLFlag, - signerFlag, - indexFlag, - hashFlag, - oracleFlag, - }, - Action: utils.MigrateFlags(sign), -} - -var commandPublish = cli.Command{ - Name: "publish", - Usage: "Publish a checkpoint into the oracle", - Flags: []cli.Flag{ - nodeURLFlag, - clefURLFlag, - signerFlag, - indexFlag, - signaturesFlag, - }, - Action: utils.MigrateFlags(publish), -} - -// deploy deploys the checkpoint registrar contract. -// -// Note the network where the contract is deployed depends on -// the network where the connected node is located. -func deploy(ctx *cli.Context) error { - // Gather all the addresses that should be permitted to sign - var addrs []common.Address - for _, account := range strings.Split(ctx.String(signersFlag.Name), ",") { - if trimmed := strings.TrimSpace(account); !common.IsHexAddress(trimmed) { - utils.Fatalf("Invalid account in --signers: '%s'", trimmed) - } - addrs = append(addrs, common.HexToAddress(account)) - } - // Retrieve and validate the signing threshold - needed := ctx.Int(thresholdFlag.Name) - if needed == 0 || needed > len(addrs) { - utils.Fatalf("Invalid signature threshold %d", needed) - } - // Print a summary to ensure the user understands what they're signing - fmt.Printf("Deploying new checkpoint oracle:\n\n") - for i, addr := range addrs { - fmt.Printf("Admin %d => %s\n", i+1, addr.Hex()) - } - fmt.Printf("\nSignatures needed to publish: %d\n", needed) - - // setup clef signer, create an abigen transactor and an RPC client - transactor, client := newClefSigner(ctx), newClient(ctx) - - // Deploy the checkpoint oracle - fmt.Println("Sending deploy request to Clef...") - oracle, tx, _, err := contract.DeployCheckpointOracle(transactor, client, addrs, big.NewInt(int64(params.CheckpointFrequency)), - big.NewInt(int64(params.CheckpointProcessConfirmations)), big.NewInt(int64(needed))) - if err != nil { - utils.Fatalf("Failed to deploy checkpoint oracle %v", err) - } - log.Info("Deployed checkpoint oracle", "address", oracle, "tx", tx.Hash().Hex()) - - return nil -} - -// sign creates the signature for specific checkpoint -// with local key. Only contract admins have the permission to -// sign checkpoint. -func sign(ctx *cli.Context) error { - var ( - offline bool // The indicator whether we sign checkpoint by offline. - chash common.Hash - cindex uint64 - address common.Address - - node *rpc.Client - oracle *checkpointoracle.CheckpointOracle - ) - if !ctx.GlobalIsSet(nodeURLFlag.Name) { - // Offline mode signing - offline = true - if !ctx.IsSet(hashFlag.Name) { - utils.Fatalf("Please specify the checkpoint hash (--hash) to sign in offline mode") - } - chash = common.HexToHash(ctx.String(hashFlag.Name)) - - if !ctx.IsSet(indexFlag.Name) { - utils.Fatalf("Please specify checkpoint index (--index) to sign in offline mode") - } - cindex = ctx.Uint64(indexFlag.Name) - - if !ctx.IsSet(oracleFlag.Name) { - utils.Fatalf("Please specify oracle address (--oracle) to sign in offline mode") - } - address = common.HexToAddress(ctx.String(oracleFlag.Name)) - } else { - // Interactive mode signing, retrieve the data from the remote node - node = newRPCClient(ctx.GlobalString(nodeURLFlag.Name)) - - checkpoint := getCheckpoint(ctx, node) - chash, cindex, address = checkpoint.Hash(), checkpoint.SectionIndex, getContractAddr(node) - - // Check the validity of checkpoint - reqCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second) - defer cancelFn() - - head, err := ethclient.NewClient(node).HeaderByNumber(reqCtx, nil) - if err != nil { - return err - } - num := head.Number.Uint64() - if num < ((cindex+1)*params.CheckpointFrequency + params.CheckpointProcessConfirmations) { - utils.Fatalf("Invalid future checkpoint") - } - _, oracle = newContract(node) - latest, _, h, err := oracle.Contract().GetLatestCheckpoint(nil) - if err != nil { - return err - } - if cindex < latest { - utils.Fatalf("Checkpoint is too old") - } - if cindex == latest && (latest != 0 || h.Uint64() != 0) { - utils.Fatalf("Stale checkpoint, latest registered %d, given %d", latest, cindex) - } - } - var ( - signature string - signer string - ) - // isAdmin checks whether the specified signer is admin. - isAdmin := func(addr common.Address) error { - signers, err := oracle.Contract().GetAllAdmin(nil) - if err != nil { - return err - } - for _, s := range signers { - if s == addr { - return nil - } - } - return fmt.Errorf("signer %v is not the admin", addr.Hex()) - } - // Print to the user the data thy are about to sign - fmt.Printf("Oracle => %s\n", address.Hex()) - fmt.Printf("Index %4d => %s\n", cindex, chash.Hex()) - - // Sign checkpoint in clef mode. - signer = ctx.String(signerFlag.Name) - - if !offline { - if err := isAdmin(common.HexToAddress(signer)); err != nil { - return err - } - } - clef := newRPCClient(ctx.String(clefURLFlag.Name)) - p := make(map[string]string) - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, cindex) - p["address"] = address.Hex() - p["message"] = hexutil.Encode(append(buf, chash.Bytes()...)) - - fmt.Println("Sending signing request to Clef...") - if err := clef.Call(&signature, "account_signData", accounts.MimetypeDataWithValidator, signer, p); err != nil { - utils.Fatalf("Failed to sign checkpoint, err %v", err) - } - fmt.Printf("Signer => %s\n", signer) - fmt.Printf("Signature => %s\n", signature) - return nil -} - -// sighash calculates the hash of the data to sign for the checkpoint oracle. -func sighash(index uint64, oracle common.Address, hash common.Hash) []byte { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, index) - - data := append([]byte{0x19, 0x00}, append(oracle[:], append(buf, hash[:]...)...)...) - return crypto.Keccak256(data) -} - -// ecrecover calculates the sender address from a sighash and signature combo. -func ecrecover(sighash []byte, sig []byte) common.Address { - sig[64] -= 27 - defer func() { sig[64] += 27 }() - - signer, err := crypto.SigToPub(sighash, sig) - if err != nil { - utils.Fatalf("Failed to recover sender from signature %x: %v", sig, err) - } - return crypto.PubkeyToAddress(*signer) -} - -// publish registers the specified checkpoint which generated by connected node -// with a authorised private key. -func publish(ctx *cli.Context) error { - // Print the checkpoint oracle's current status to make sure we're interacting - // with the correct network and contract. - status(ctx) - - // Gather the signatures from the CLI - var sigs [][]byte - for _, sig := range strings.Split(ctx.String(signaturesFlag.Name), ",") { - trimmed := strings.TrimPrefix(strings.TrimSpace(sig), "0x") - if len(trimmed) != 130 { - utils.Fatalf("Invalid signature in --signature: '%s'", trimmed) - } else { - sigs = append(sigs, common.Hex2Bytes(trimmed)) - } - } - // Retrieve the checkpoint we want to sign to sort the signatures - var ( - client = newRPCClient(ctx.GlobalString(nodeURLFlag.Name)) - addr, oracle = newContract(client) - checkpoint = getCheckpoint(ctx, client) - sighash = sighash(checkpoint.SectionIndex, addr, checkpoint.Hash()) - ) - for i := 0; i < len(sigs); i++ { - for j := i + 1; j < len(sigs); j++ { - signerA := ecrecover(sighash, sigs[i]) - signerB := ecrecover(sighash, sigs[j]) - if bytes.Compare(signerA.Bytes(), signerB.Bytes()) > 0 { - sigs[i], sigs[j] = sigs[j], sigs[i] - } - } - } - // Retrieve recent header info to protect replay attack - reqCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second) - defer cancelFn() - - head, err := ethclient.NewClient(client).HeaderByNumber(reqCtx, nil) - if err != nil { - return err - } - num := head.Number.Uint64() - recent, err := ethclient.NewClient(client).HeaderByNumber(reqCtx, big.NewInt(int64(num-128))) - if err != nil { - return err - } - // Print a summary of the operation that's going to be performed - fmt.Printf("Publishing %d => %s:\n\n", checkpoint.SectionIndex, checkpoint.Hash().Hex()) - for i, sig := range sigs { - fmt.Printf("Signer %d => %s\n", i+1, ecrecover(sighash, sig).Hex()) - } - fmt.Println() - fmt.Printf("Sentry number => %d\nSentry hash => %s\n", recent.Number, recent.Hash().Hex()) - - // Publish the checkpoint into the oracle - fmt.Println("Sending publish request to Clef...") - tx, err := oracle.RegisterCheckpoint(newClefSigner(ctx), checkpoint.SectionIndex, checkpoint.Hash().Bytes(), recent.Number, recent.Hash(), sigs) - if err != nil { - utils.Fatalf("Register contract failed %v", err) - } - log.Info("Successfully registered checkpoint", "tx", tx.Hash().Hex()) - return nil -}
diff --git go-ethereum/cmd/checkpoint-admin/main.go celo/cmd/checkpoint-admin/main.go deleted file mode 100644 index 8870eadf46cb4f7d2855451b4eb58da4df51686f..0000000000000000000000000000000000000000 --- go-ethereum/cmd/checkpoint-admin/main.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -// checkpoint-admin is a utility that can be used to query checkpoint information -// and register stable checkpoints into an oracle contract. -package main - -import ( - "fmt" - "os" - - "github.com/ethereum/go-ethereum/common/fdlimit" - "github.com/ethereum/go-ethereum/internal/flags" - "github.com/ethereum/go-ethereum/log" - "gopkg.in/urfave/cli.v1" -) - -var ( - // Git SHA1 commit hash of the release (set via linker flags) - gitCommit = "" - gitDate = "" -) - -var app *cli.App - -func init() { - app = flags.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool") - app.Commands = []cli.Command{ - commandStatus, - commandDeploy, - commandSign, - commandPublish, - } - app.Flags = []cli.Flag{ - oracleFlag, - nodeURLFlag, - } - cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate -} - -// Commonly used command line flags. -var ( - indexFlag = cli.Int64Flag{ - Name: "index", - Usage: "Checkpoint index (query latest from remote node if not specified)", - } - hashFlag = cli.StringFlag{ - Name: "hash", - Usage: "Checkpoint hash (query latest from remote node if not specified)", - } - oracleFlag = cli.StringFlag{ - Name: "oracle", - Usage: "Checkpoint oracle address (query from remote node if not specified)", - } - thresholdFlag = cli.Int64Flag{ - Name: "threshold", - Usage: "Minimal number of signatures required to approve a checkpoint", - } - nodeURLFlag = cli.StringFlag{ - Name: "rpc", - Value: "http://localhost:8545", - Usage: "The rpc endpoint of a local or remote geth node", - } - clefURLFlag = cli.StringFlag{ - Name: "clef", - Value: "http://localhost:8550", - Usage: "The rpc endpoint of clef", - } - signerFlag = cli.StringFlag{ - Name: "signer", - Usage: "Signer address for clef signing", - } - signersFlag = cli.StringFlag{ - Name: "signers", - Usage: "Comma separated accounts of trusted checkpoint signers", - } - signaturesFlag = cli.StringFlag{ - Name: "signatures", - Usage: "Comma separated checkpoint signatures to submit", - } -) - -func main() { - log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) - fdlimit.Raise(2048) - - if err := app.Run(os.Args); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } -}
diff --git go-ethereum/cmd/checkpoint-admin/status.go celo/cmd/checkpoint-admin/status.go deleted file mode 100644 index c6649bce50eb7780108ae994b17a881d9b0a7813..0000000000000000000000000000000000000000 --- go-ethereum/cmd/checkpoint-admin/status.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. - -package main - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "gopkg.in/urfave/cli.v1" -) - -var commandStatus = cli.Command{ - Name: "status", - Usage: "Fetches the signers and checkpoint status of the oracle contract", - Flags: []cli.Flag{ - nodeURLFlag, - }, - Action: utils.MigrateFlags(status), -} - -// status fetches the admin list of specified registrar contract. -func status(ctx *cli.Context) error { - // Create a wrapper around the checkpoint oracle contract - addr, oracle := newContract(newRPCClient(ctx.GlobalString(nodeURLFlag.Name))) - fmt.Printf("Oracle => %s\n", addr.Hex()) - fmt.Println() - - // Retrieve the list of authorized signers (admins) - admins, err := oracle.Contract().GetAllAdmin(nil) - if err != nil { - return err - } - for i, admin := range admins { - fmt.Printf("Admin %d => %s\n", i+1, admin.Hex()) - } - fmt.Println() - - // Retrieve the latest checkpoint - index, checkpoint, height, err := oracle.Contract().GetLatestCheckpoint(nil) - if err != nil { - return err - } - fmt.Printf("Checkpoint (published at #%d) %d => %s\n", height, index, common.Hash(checkpoint).Hex()) - - return nil -}
diff --git go-ethereum/contracts/config/gascosts.go celo/contracts/config/gascosts.go new file mode 100644 index 0000000000000000000000000000000000000000..3be27f1bf5639808cf54fe5d52074e14f4c7440f --- /dev/null +++ celo/contracts/config/gascosts.go @@ -0,0 +1,9 @@ +package config + +import "github.com/ethereum/go-ethereum/contracts/internal/n" + +const ( + // Default intrinsic gas cost of transactions paying for gas in alternative currencies. + // Calculated to estimate 1 balance read, 1 debit, and 4 credit transactions. + IntrinsicGasForAlternativeFeeCurrency uint64 = 50 * n.Thousand +)
diff --git go-ethereum/contracts/config/registry_ids.go celo/contracts/config/registry_ids.go new file mode 100644 index 0000000000000000000000000000000000000000..931dcc26223cc7de0b9b3dcd17fc04ab35df2d4f --- /dev/null +++ celo/contracts/config/registry_ids.go @@ -0,0 +1,32 @@ +package config + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + RegistrySmartContractAddress = common.HexToAddress("0x000000000000000000000000000000000000ce10") + + // Celo registered contract IDs. + // The names are taken from celo-monorepo/packages/protocol/lib/registry-utils.ts + AttestationsRegistryId = makeRegistryId("Attestations") + BlockchainParametersRegistryId = makeRegistryId("BlockchainParameters") + ElectionRegistryId = makeRegistryId("Election") + FeeCurrencyWhitelistRegistryId = makeRegistryId("FeeCurrencyWhitelist") + GasPriceMinimumRegistryId = makeRegistryId("GasPriceMinimum") + GovernanceRegistryId = makeRegistryId("Governance") + RandomRegistryId = makeRegistryId("Random") + SortedOraclesRegistryId = makeRegistryId("SortedOracles") + StableTokenRegistryId = makeRegistryId("StableToken") + TransferWhitelistRegistryId = makeRegistryId("TransferWhitelist") + ValidatorsRegistryId = makeRegistryId("Validators") +) + +func makeRegistryId(contractName string) [32]byte { + hash := crypto.Keccak256([]byte(contractName)) + var id [32]byte + copy(id[:], hash) + + return id +}
diff --git go-ethereum/contracts/config/version.go celo/contracts/config/version.go new file mode 100644 index 0000000000000000000000000000000000000000..992da4935659530a1cc52c984fa858e6c3cb16cd --- /dev/null +++ celo/contracts/config/version.go @@ -0,0 +1,39 @@ +package config + +import "github.com/ethereum/go-ethereum/params" + +type VersionInfo struct { + Major uint64 + Minor uint64 + Patch uint64 +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +func cmp(x uint64, y uint64) int { + if x < y { + return -1 + } + if x > y { + return 1 + } + return 0 +} + +func (v *VersionInfo) Cmp(version *VersionInfo) int { + if v.Major == version.Major { + if v.Minor == version.Minor { + return cmp(v.Patch, version.Patch) + } + return cmp(v.Minor, version.Minor) + } + return cmp(v.Major, version.Major) +} + +var CurrentVersionInfo = func() *VersionInfo { + return &VersionInfo{params.VersionMajor, params.VersionMinor, params.VersionPatch} +}()
diff --git go-ethereum/contracts/erc20gas/erc20gas.go celo/contracts/erc20gas/erc20gas.go new file mode 100644 index 0000000000000000000000000000000000000000..e8bf2447186534380402baf3ccb84007b1bbb32e --- /dev/null +++ celo/contracts/erc20gas/erc20gas.go @@ -0,0 +1,71 @@ +package erc20gas + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/contracts/internal/n" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" +) + +const ( + maxGasForDebitGasFeesTransactions uint64 = 1 * n.Million + maxGasForCreditGasFeesTransactions uint64 = 1 * n.Million +) + +func DebitFees(evm *vm.EVM, address common.Address, amount *big.Int, feeCurrency *common.Address) error { + if amount.Cmp(big.NewInt(0)) == 0 { + return nil + } + // Function is "debitGasFees(address from, uint256 value)" + // selector is first 4 bytes of keccak256 of "debitGasFees(address,uint256)" + // Source: + // pip3 install pyethereum + // python3 -c 'from ethereum.utils import sha3; print(sha3("debitGasFees(address,uint256)")[0:4].hex())' + functionSelector := hexutil.MustDecode("0x58cf9672") + transactionData := common.GetEncodedAbi(functionSelector, [][]byte{common.AddressToAbi(address), common.AmountToAbi(amount)}) + + // Run only primary evm.Call() with tracer + if evm.GetDebug() { + evm.SetDebug(false) + defer func() { evm.SetDebug(true) }() + } + + rootCaller := vm.AccountRef(common.HexToAddress("0x0")) + // The caller was already charged for the cost of this operation via IntrinsicGas. + _, leftoverGas, err := evm.Call(rootCaller, *feeCurrency, transactionData, maxGasForDebitGasFeesTransactions, big.NewInt(0)) + gasUsed := maxGasForDebitGasFeesTransactions - leftoverGas + log.Trace("debitGasFees called", "feeCurrency", *feeCurrency, "gasUsed", gasUsed) + return err +} + +func CreditFees( + evm *vm.EVM, + from common.Address, + feeRecipient common.Address, + gatewayFeeRecipient *common.Address, + communityFund common.Address, + refund *big.Int, + tipTxFee *big.Int, + gatewayFee *big.Int, + baseTxFee *big.Int, + feeCurrency *common.Address) error { + // Function is "creditGasFees(address,address,address,address,uint256,uint256,uint256,uint256)" + functionSelector := hexutil.MustDecode("0x6a30b253") + transactionData := common.GetEncodedAbi(functionSelector, [][]byte{common.AddressToAbi(from), common.AddressToAbi(feeRecipient), common.AddressToAbi(*gatewayFeeRecipient), common.AddressToAbi(communityFund), common.AmountToAbi(refund), common.AmountToAbi(tipTxFee), common.AmountToAbi(gatewayFee), common.AmountToAbi(baseTxFee)}) + + // Run only primary evm.Call() with tracer + if evm.GetDebug() { + evm.SetDebug(false) + defer func() { evm.SetDebug(true) }() + } + + rootCaller := vm.AccountRef(common.HexToAddress("0x0")) + // The caller was already charged for the cost of this operation via IntrinsicGas. + _, leftoverGas, err := evm.Call(rootCaller, *feeCurrency, transactionData, maxGasForCreditGasFeesTransactions, big.NewInt(0)) + gasUsed := maxGasForCreditGasFeesTransactions - leftoverGas + log.Trace("creditGas called", "feeCurrency", *feeCurrency, "gasUsed", gasUsed) + return err +}
diff --git go-ethereum/contracts/internal/n/numbers.go celo/contracts/internal/n/numbers.go new file mode 100644 index 0000000000000000000000000000000000000000..adccdac26ab4e2e0437466a7cba46b20e580839d --- /dev/null +++ celo/contracts/internal/n/numbers.go @@ -0,0 +1,6 @@ +package n + +const ( + Thousand = 1000 + Million = 1000 * 1000 +)
diff --git go-ethereum/interfaces.go celo/interfaces.go index 9917e76b9b93c310a2897ef7284d990c54914007..b0f8fcb9836517941ebf30d096cf74f3d9a62331 100644 --- go-ethereum/interfaces.go +++ celo/interfaces.go @@ -14,8 +14,8 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.   -// Package ethereum defines interfaces for interacting with Ethereum. -package ethereum +// Package celo defines interfaces for interacting with Celo. +package celo   import ( "context" @@ -116,11 +116,15 @@ type CallMsg struct { From common.Address // the sender of the 'transaction' To *common.Address // the destination contract (nil for contract creation) Gas uint64 // if 0, the call executes with near-infinite gas + FeeCurrency *common.Address // 0 for the native currency + GatewayFeeRecipient *common.Address // 0 for no gateway fee + GatewayFee *big.Int // 0 for no gateway fee GasPrice *big.Int // wei <-> gas exchange ratio GasFeeCap *big.Int // EIP-1559 fee cap per gas. GasTipCap *big.Int // EIP-1559 tip per gas. Value *big.Int // amount of wei sent along with the call Data []byte // input data, usually an ABI-encoded contract method invocation + EthCompatible bool // Whether the 3 Celo-only fields (FeeCurrency & co.) were omitted   AccessList types.AccessList // EIP-2930 access list. }
diff --git go-ethereum/log/logger.go celo/log/logger.go index 161eb9b5b292d1c189fe02a83e026c77c989f439..dea96004f9334d4aaa75400fe043e269af8bbfd3 100644 --- go-ethereum/log/logger.go +++ celo/log/logger.go @@ -8,8 +8,8 @@ "github.com/go-stack/stack" )   -const timeKey = "t" -const lvlKey = "lvl" +const timeKey = "timestamp" +const lvlKey = "severity" const msgKey = "msg" const ctxKey = "ctx" const errorKey = "LOG15_ERROR" @@ -50,17 +50,17 @@ // Strings returns the name of a Lvl. func (l Lvl) String() string { switch l { case LvlTrace: - return "trce" + return "debug" case LvlDebug: - return "dbug" + return "debug" case LvlInfo: return "info" case LvlWarn: - return "warn" + return "warning" case LvlError: - return "eror" + return "error" case LvlCrit: - return "crit" + return "critical" default: panic("bad level") }
diff --git go-ethereum/patches/mobileLibsForBuild.patch celo/patches/mobileLibsForBuild.patch new file mode 100644 index 0000000000000000000000000000000000000000..ba1b81083aae085a43b07e8b36772762114f7b04 --- /dev/null +++ celo/patches/mobileLibsForBuild.patch @@ -0,0 +1,10 @@ +diff --git c/go.mod i/go.mod +index b6989868d..9947b3aa4 100644 +--- c/go.mod ++++ i/go.mod +@@ -75,3 +75,5 @@ require ( + + // Use our fork which disables bitcode + replace golang.org/x/mobile => github.com/celo-org/mobile v0.0.0-20210324213558-66ac87d7fb95 ++ ++replace golang.org/x/sys => github.com/celo-org/sys v0.0.0-20200816232032-1d4c3212eaf2
diff --git go-ethereum/rlp/decode_test.go celo/rlp/decode_test.go index 1037a1f2eedf1e303cb5e72365f67e1c9b694e58..3d1475c81941f7466775737b518b18b4f8ca0da8 100644 --- go-ethereum/rlp/decode_test.go +++ celo/rlp/decode_test.go @@ -361,7 +361,7 @@ type tailPrivateFields struct { A uint Tail []uint `rlp:"tail"` - x, y bool //lint:ignore U1000 unused fields required for testing purposes. + x, y bool //nolint:unused // fields required for testing purposes }   type nilListUint struct {
diff --git go-ethereum/tools/parsecov/main.go celo/tools/parsecov/main.go new file mode 100644 index 0000000000000000000000000000000000000000..211d1f204a3348bed38134a0a2096ff3e234a52b --- /dev/null +++ celo/tools/parsecov/main.go @@ -0,0 +1,125 @@ +package main + +import ( + "bufio" + "fmt" + "log" + "os" + "path" + "sort" + "strconv" + "strings" + + "gopkg.in/urfave/cli.v1" +) + +var ( + packagePrefixFlagName = "packagePrefix" +) + +type count struct { + totalStatements int + coveredStatements int +} + +func (c count) percentage() float32 { + return 100 * float32(c.coveredStatements) / float32(c.totalStatements) +} + +func parse(c *cli.Context) error { + packageCoverage := make(map[string]count) + profile, err := os.Open(c.Args()[0]) + if err != nil { + return err + } + defer profile.Close() + + // First line is "mode: foo", where foo is "set", "count", or "atomic". + // Rest of file is in the format + // encoding/base64/base64.go:34.44,37.40 3 1 + // where the fields are: name.go:line.column,line.column numberOfStatements count + + // This program only handles set mode + s := bufio.NewScanner(profile) + s.Scan() + line := s.Text() + if line != "mode: set" { + return fmt.Errorf("invalid coverage mode, expecting 'mode: set' as the first line, but got %q", line) + } + linenum := 1 + for s.Scan() { + line := s.Text() + linenum++ + lastColon := strings.LastIndex(line, ":") + if lastColon == -1 { + return fmt.Errorf("line %d invalid: %q", linenum, line) + } + + packageName := path.Dir(line[:lastColon]) + numStatements, covered, err := getStats(line[lastColon+1:]) + if err != nil { + return fmt.Errorf("line %d invalid: %q", linenum, line) + } + c := packageCoverage[packageName] + c.totalStatements += numStatements + if covered { + c.coveredStatements += numStatements + } + packageCoverage[packageName] = c + } + + packageNames := make([]string, 0, len(packageCoverage)) + for name := range packageCoverage { + packageNames = append(packageNames, name) + } + sort.Strings(packageNames) + + var totalCount count + for _, v := range packageCoverage { + totalCount.totalStatements += v.totalStatements + totalCount.coveredStatements += v.coveredStatements + } + fmt.Printf("coverage: %5.1f%% of statements across all listed packages\n", totalCount.percentage()) + + for _, name := range packageNames { + cov := packageCoverage[name].percentage() + fmt.Printf("coverage: %5.1f%% of statements in %s\n", cov, strings.TrimPrefix(name, c.String(packagePrefixFlagName))) + } + + return nil +} + +// Gets the stats from the end of the line, how many statements and were they +// covered? +// +// E.G line -> encoding/base64/base64.go:34.44,37.40 3 1 +// End of line -> 34.44,37.40 3 1 +// Would return 3 true +func getStats(lineEnd string) (numStatements int, covered bool, err error) { + parts := strings.Split(lineEnd, " ") + if len(parts) != 3 { + return 0, false, fmt.Errorf("invalid line end") + } + numStatements, err = strconv.Atoi(parts[1]) + return numStatements, parts[2] == "1", err +} + +func main() { + app := cli.NewApp() + app.Name = "parsecov" + app.Usage = `parses coverage files outputting a per package breakdown + of coverage as well as a total for all the packages.` + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: packagePrefixFlagName, + Usage: `a common prefix that is stripped from the front + of all packages in order to make output more concise.`, + }, + } + app.Action = parse + + err := app.Run(os.Args) + if err != nil { + log.Fatalf("Failed to parse coverage: %v", err) + } +}
diff --git go-ethereum/trie/proof.go celo/trie/proof.go index 5ca6783c4f6226fffed7351aba6a582fc56a5305..d86ab96879ef127222676dd12a259c324f2701a4 100644 --- go-ethereum/trie/proof.go +++ celo/trie/proof.go @@ -369,11 +369,12 @@ // (it belongs to the range), unset the entrie // branch. The parent must be a fullnode. fn := parent.(*fullNode) fn.Children[key[pos-1]] = nil - } else { - // The key of fork shortnode is greater than the - // path(it doesn't belong to the range), keep - // it with the cached hash available. } + // else { + // The key of fork shortnode is greater than the + // path(it doesn't belong to the range), keep + // it with the cached hash available. + // } } else { if bytes.Compare(cld.Key, key[pos:]) > 0 { // The key of fork shortnode is greater than the @@ -381,11 +382,12 @@ // path(it belongs to the range), unset the entrie // branch. The parent must be a fullnode. fn := parent.(*fullNode) fn.Children[key[pos-1]] = nil - } else { - // The key of fork shortnode is less than the - // path(it doesn't belong to the range), keep - // it with the cached hash available. } + // else { + // The key of fork shortnode is less than the + // path(it doesn't belong to the range), keep + // it with the cached hash available. + // } } return nil }
diff --git go-ethereum/trie/stacktrie.go celo/trie/stacktrie.go index 0e1fe4e7f8b266b9a3bc0f9c94f646150ecbb803..a6890ea046f99eedd849fcc7538fb613947aa1c8 100644 --- go-ethereum/trie/stacktrie.go +++ celo/trie/stacktrie.go @@ -365,6 +365,7 @@ // Possible outcomes: // 1. The rlp-encoded value was >= 32 bytes: // - Then the 32-byte `hash` will be accessible in `st.val`. // - And the 'st.type' will be 'hashedNode' +// // 2. The rlp-encoded value was < 32 bytes // - Then the <32 byte rlp-encoded value will be accessible in 'st.val'. // - And the 'st.type' will be 'hashedNode' AGAIN
diff --git go-ethereum/.codecov.yml celo/.codecov.yml new file mode 100644 index 0000000000000000000000000000000000000000..1f7cd8ce94e2f5306ab795b5e3599ecf23cb1716 --- /dev/null +++ celo/.codecov.yml @@ -0,0 +1,8 @@ +coverage: + status: + project: + default: + threshold: null + target: auto + patch: + default: off
diff --git go-ethereum/.dockerignore celo/.dockerignore index 0c013d18b13f26adba32df14f3642b90e048e0d3..f2f6a19c1628925527952a80fbfeb5f1e19b3a96 100644 --- go-ethereum/.dockerignore +++ celo/.dockerignore @@ -1,5 +1,8 @@ **/*_test.go +.git +/build/_workspace +/build/bin +tests/testdata   -build/_workspace -build/_bin -tests/testdata +/crypto/bls/bls-zexe/bls/target +/crypto/bls/bls-zexe/target
diff --git go-ethereum/.github/CODEOWNERS celo/.github/CODEOWNERS deleted file mode 100644 index 2015604e646dcdaa5b4c476b0d1ab648f48c8b55..0000000000000000000000000000000000000000 --- go-ethereum/.github/CODEOWNERS +++ /dev/null @@ -1,23 +0,0 @@ -# Lines starting with '#' are comments. -# Each line is a file pattern followed by one or more owners. - -accounts/usbwallet @karalabe -accounts/scwallet @gballet -accounts/abi @gballet @MariusVanDerWijden -cmd/clef @holiman -cmd/puppeth @karalabe -consensus @karalabe -core/ @karalabe @holiman @rjl493456442 -eth/ @karalabe @holiman @rjl493456442 -eth/catalyst/ @gballet -graphql/ @gballet -les/ @zsfelfoldi @rjl493456442 -light/ @zsfelfoldi @rjl493456442 -mobile/ @karalabe @ligi -node/ @fjl @renaynay -p2p/ @fjl @zsfelfoldi -rpc/ @fjl @holiman -p2p/simulations @fjl -p2p/protocols @fjl -p2p/testing @fjl -signer/ @holiman
diff --git go-ethereum/.github/CONTRIBUTING.md celo/.github/CONTRIBUTING.md index a08542df25553564ea96570479ce2b754770d34a..51d0f28518cd37f32b518a485e8509fa8896c7f3 100644 --- go-ethereum/.github/CONTRIBUTING.md +++ celo/.github/CONTRIBUTING.md @@ -1,40 +1,13 @@ # Contributing   -Thank you for considering to help out with the source code! We welcome -contributions from anyone on the internet, and are grateful for even the -smallest of fixes! - -If you'd like to contribute to go-ethereum, please fork, fix, commit and send a -pull request for the maintainers to review and merge into the main code base. If -you wish to submit more complex changes though, please check up with the core -devs first on [our gitter channel](https://gitter.im/ethereum/go-ethereum) to -ensure those changes are in line with the general philosophy of the project -and/or get some early feedback which can make both your efforts much lighter as -well as our review and merge procedures quick and simple. +Thank you for considering making a contribution to the Celo community! +Everyone is encouraged to contribute, even the smallest fixes are welcome.   -## Coding guidelines +If you'd like to contribute to Celo, please fork, fix, commit and send a +pull request for the maintainers to review.   -Please make sure your contributions adhere to our coding guidelines: +If you wish to submit more complex changes, please sync with a core developer first. +This will help ensure those changes are in line with the general philosophy of the project +and enable you to get some early feedback.   - * Code must adhere to the official Go -[formatting](https://golang.org/doc/effective_go.html#formatting) guidelines -(i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). - * Code must be documented adhering to the official Go -[commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. - * Pull requests need to be based on and opened against the `master` branch. - * Commit messages should be prefixed with the package(s) they modify. - * E.g. "eth, rpc: make trace configs optional" - -## Can I have feature X - -Before you submit a feature request, please check and make sure that it isn't -possible through some other means. The JavaScript-enabled console is a powerful -feature in the right hands. Please check our -[Geth documentation page](https://geth.ethereum.org/docs/) for more info -and help. - -## Configuration, dependencies, and tests - -Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/devguide) -for more details on configuring your environment, managing project dependencies -and testing procedures. +See the [contributing guide](https://docs.celo.org/community/contributing) for details on how to participate.
diff --git go-ethereum/.github/DISCUSSION_TEMPLATE/feature-requests-needs.yml celo/.github/DISCUSSION_TEMPLATE/feature-requests-needs.yml new file mode 100644 index 0000000000000000000000000000000000000000..5af13543e886815c8a5167cd8aae188e6f09fd58 --- /dev/null +++ celo/.github/DISCUSSION_TEMPLATE/feature-requests-needs.yml @@ -0,0 +1,27 @@ +title: "[Feature] " +body: + - type: markdown + attributes: + value: | + Let us know what feature is missing and why you think it should exist! + - type: textarea + id: rationale + attributes: + label: Rationale + description: "Why do you think it should exist?" + value: | + Why should this feature exist? + What are the use-cases? + validations: + required: true + - type: textarea + id: implementation + attributes: + label: Implementation + description: "Your thoughts about the implementation" + value: | + Do you have ideas regarding the implementation of this feature? + Are you willing to implement it? + validations: + required: true +
diff --git go-ethereum/.github/DISCUSSION_TEMPLATE/help-q-a.yml celo/.github/DISCUSSION_TEMPLATE/help-q-a.yml new file mode 100644 index 0000000000000000000000000000000000000000..a9befe4fb92ad524e868834db2b9714db65f8111 --- /dev/null +++ celo/.github/DISCUSSION_TEMPLATE/help-q-a.yml @@ -0,0 +1,34 @@ +title: "[Help] " +body: + - type: markdown + attributes: + value: | + The more specific you can be, the easier it will be to help you! + - type: textarea + id: context + attributes: + label: Context + description: "What is the context of your question?" + value: | + Describe what was the result you were trying to achieve + validations: + required: true + - type: textarea + id: problem + attributes: + label: Problem + description: "What is the problem you're facing?" + value: | + Describe the specific problem you have + validations: + required: true + - type: textarea + id: asks + attributes: + label: Asks + description: "What kind of help you're looking for?" + value: | + How can we help you? + validations: + required: true +
diff --git go-ethereum/.github/ISSUE_TEMPLATE/bug.md celo/.github/ISSUE_TEMPLATE/bug.md index 2aa2c48a600b0e31ecb6c8e4844993289c9260c3..b739ba03da9fb2b759c3a0bf40204b0a103c47ef 100644 --- go-ethereum/.github/ISSUE_TEMPLATE/bug.md +++ celo/.github/ISSUE_TEMPLATE/bug.md @@ -1,30 +1,43 @@ --- name: Report a bug -about: Something with go-ethereum is not working as expected +about: When something is not working as expected title: '' -labels: 'type:bug' +labels: triage, type:bug assignees: '' + ---   -#### System information +## Expected Behavior + +_Explain the expected behavior._ + +## Actual Behavior + +_Explain what the current behavior is._   -Geth version: `geth version` -OS & Version: Windows/Linux/OSX -Commit hash : (if `develop`) +## Steps to reproduce the behavior   -#### Expected behaviour +1. Specific steps to reproduce the bug.   +## Backtrace   -#### Actual behaviour +``` + error output with backtrace +```   +## System Information   -#### Steps to reproduce the behaviour +Client version:   +```bash +./geth version +```   -#### Backtrace +geth command with flags:   -```` -[backtrace] -```` +```bash +./geth %flags% + +```   -When submitting logs: please submit them as text and not screenshots. \ No newline at end of file +Chain/Network: `Alfajores/Baklava/Mainnet`
diff --git go-ethereum/.github/ISSUE_TEMPLATE/feature.md celo/.github/ISSUE_TEMPLATE/feature.md deleted file mode 100644 index aacd885f9e5ef7de4eaa833c9e67297db24a85e2..0000000000000000000000000000000000000000 --- go-ethereum/.github/ISSUE_TEMPLATE/feature.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Request a feature -about: Report a missing feature - e.g. as a step before submitting a PR -title: '' -labels: 'type:feature' -assignees: '' ---- - -# Rationale - -Why should this feature exist? -What are the use-cases? - -# Implementation - -Do you have ideas regarding the implementation of this feature? -Are you willing to implement this feature? \ No newline at end of file
diff --git go-ethereum/.github/ISSUE_TEMPLATE/question.md celo/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 8f460ab558ecc6930b0f1c348c08bdde31fe2b2b..0000000000000000000000000000000000000000 --- go-ethereum/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: Ask a question -about: Something is unclear -title: '' -labels: 'type:docs' -assignees: '' ---- - -This should only be used in very rare cases e.g. if you are not 100% sure if something is a bug or asking a question that leads to improving the documentation. For general questions please use [discord](https://discord.gg/nthXNEv) or the Ethereum stack exchange at https://ethereum.stackexchange.com.
diff --git go-ethereum/.github/fork.yaml celo/.github/fork.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f9a0a1ad3416d079f54a63badf7d208429d4ce37 --- /dev/null +++ celo/.github/fork.yaml @@ -0,0 +1,351 @@ +title: "celo - go-ethereum fork diff overview" +footer: | + Fork-diff overview of [`celo`](https://github.com/celo-org/celo-blockchain), a fork of [`go-ethereum`](https://github.com/ethereum/go-ethereum). +base: + name: go-ethereum + url: https://github.com/ethereum/go-ethereum + ref: HEAD # the specific forked commit is checked out as part of the GitHub workflow +fork: + name: celo + url: https://github.com/celo-org/celo-blockchain + ref: HEAD +def: + title: "celo-blockchain" + description: | + This is an overview of the changes in [`celo-blockchain`](https://github.com/celo-org/celo-blockchain), + a fork of [`go-ethereum`](https://github.com/ethereum/go-ethereum). + + To minimize changes in this diff, we have replaced `github.com/celo-org/celo-blockchain` + with `github.com/ethereum/go-ethereum` in imports, removed "ethereum" named imports, + and applied `gofmt` to both codebases. + When using individual diffs, double check any changes in the original codebases. + + Since the diffs between the two codebases are currently so large, the categories + below often reflect different packages as opposed to broader features or logical units. + sub: + - title: "accounts" + globs: + - "accounts/accounts.go" + - "accounts/manager.go" + sub: + - title: "external" + globs: + - "accounts/external/*" + - title: "keystore" + globs: + - "accounts/keystore/*" + - title: "hd" + globs: + - "accounts/hd.go" + - "accounts/hd_test.go" + - title: "usbwallet" + globs: + - "accounts/usbwallet/*" + - "accounts/usbwallet/*/*" + - title: "abi" + globs: + - "accounts/abi/*" + - "accounts/abi/bind/*" + - "accounts/abi/bind/*/*" + - "accounts/abi/bind_v2/*" + - title: "scwallet" + globs: + - "accounts/scwallet/*" + - title: "cmd" + sub: + - title: "geth" + globs: + - "cmd/geth/*" + - "cmd/geth/*/*" + - "cmd/geth/*/*/*" + - title: "bootnode" + globs: + - "cmd/bootnode/*" + - title: "utils" + globs: + - "cmd/utils/*" + - title: "abigen" + globs: + - "cmd/abigen/*" + - title: "uptime" + globs: + - "cmd/uptime/*" + - title: "clef" + globs: + - "cmd/clef/*" + - "cmd/clef/*/*" + - "cmd/clef/*/*/*" + - title: "evm" + globs: + - "cmd/evm/*" + - "cmd/evm/*/*" + - "cmd/evm/*/*/*" + - "cmd/evm/*/*/*/*" + - title: "blspopchecker" + globs: + - "cmd/blspopchecker/*" + - title: "devp2p" + globs: + - "cmd/devp2p/*" + - "cmd/devp2p/*/*" + - "cmd/devp2p/*/*/*" + - "cmd/devp2p/*/*/*/*" + - title: "faucet" + globs: + - "cmd/faucet/*" + - title: "puppeth" + globs: + - "cmd/puppeth/*" + - "cmd/puppeth/*/*" + - title: "common" + globs: + - "common/*" + - "common/*/*" + - "common/*/*/*" + - "common/*/*/*/*" + - title: "consensus" + globs: + - "consensus/*" + - "consensus/consensustest/*" + sub: + - title: "istanbul" + description: | + Celo implements Istanbul BFT, specified in the [Celo IBFT Spec](https://github.com/celo-org/celo-blockchain/blob/dbd09899efecd39651f6546a2a42a76bf1667ec3/consensus/istanbul/celo-ibft-spec.md), and loosely based off of [EIP 650](https://github.com/ethereum/EIPs/issues/650). + This section is almost entirely Celo-specific. + globs: + - "consensus/istanbul/*" + - "consensus/istanbul/*/*" + - "consensus/istanbul/*/*/*" + - "consensus/istanbul/*/*/*/*" + - title: "misc" + globs: + - "consensus/misc/*" + - title: "ethash" + globs: + - "consensus/ethash/*" + - title: "clique" + globs: + - "consensus/clique/*" + - title: "console" + globs: + - "console/*" + - title: "contracts" + globs: + - "contracts/*" + - "contracts/abis/*" + - "contracts/blockchain_parameters/*" + - "contracts/checkpointoracle/*" + - "contracts/checkpointoracle/contract/*" + - "contracts/currency/*" + - "contracts/election/*" + - "contracts/epoch_rewards/*" + - "contracts/freezer/*" + - "contracts/gasprice_minimum/*" + - "contracts/gold_token/*" + - "contracts/random/*" + - "contracts/reserve/*" + - "contracts/testutil/*" + - "contracts/validators/*" + - title: "core" + globs: + - "core/*" + - "core/forkid/*" + - "core/asm/*" + - "core/state/*" + sub: + - title: "rawDB" + globs: + - "core/rawdb/*" + - title: "types" + globs: + - "core/types/*" + - title: "vm" + globs: + - "core/vm/*" + - "core/vm/*/*" + sub: + - title: "testdata" + globs: + - "core/vm/testdata/*/*" + - title: "miner" + globs: + - "miner/*" + sub: + - title: "Miner stress tests" + globs: + - "miner/stress/*/*" + - title: "p2p" + globs: + - "p2p/*" + - "p2p/*/*" + - "p2p/*/*/*" + - title: "crypto" + globs: + - "crypto/*" + sub: + - title: "bls" + globs: + - "crypto/bls/*" + - title: "bls12377" + globs: + - "crypto/bls12377/*" + - title: "bls12381" + globs: + - "crypto/bls12381/*" + - title: "blake2b" + globs: + - "crypto/blake2b/*" + - title: "bn256" + globs: + - "crypto/bn256/*" + - "crypto/bn256/*/*" + - title: "blake2s" + globs: + - "crypto/blake2s/*" + - title: "eth" + globs: + - "eth/*" + sub: + - title: "catalyst" + globs: + - "eth/catalyst/*" + - title: "downloader" + globs: + - "eth/downloader/*" + - title: "ethconfig" + globs: + - "eth/ethconfig/*" + - title: "fetcher" + globs: + - "eth/fetcher/*" + - title: "filters" + globs: + - "eth/filters/*" + - title: "gasprice" + globs: + - "eth/gasprice/*" + - title: "protocols" + globs: + - "eth/protocols/*" + - "eth/protocols/*/*" + - "eth/protocols/*/*/*" + - title: "tracers" + globs: + - "eth/tracers/*" + - "eth/tracers/*/*" + - "eth/tracers/*/*/*" + - title: "ethclient" + globs: + - "ethclient/*" + - "ethclient/*/*" + - title: "ethstats" + globs: + - "ethstats/*" + - title: "graphql" + globs: + - "graphql/*" + - title: "internal" + globs: + - "internal/*" + - "internal/*/*" + - "internal/*/*/*" + - title: "les" + globs: + - "les/*" + sub: + - title: "fetcher" + globs: + - "les/fetcher/*" + - title: "downloader" + globs: + - "les/downloader/*" + - title: "flowcontrol" + globs: + - "les/flowcontrol/*" + - title: "light" + globs: + - "light/*" + - title: "metrics" + globs: + - "metrics/*" + - title: "mobile" + globs: + - "mobile/*" + - title: "mycelo" + description: | + [mycelo](https://github.com/celo-org/celo-blockchain/blob/dbd09899efecd39651f6546a2a42a76bf1667ec3/cmd/mycelo/README.md) is a developer tool to run blockchain testnets with Celo Core Contracts already deployed. + This is entirely specific to the Celo blockchain. + globs: + - "mycelo/*" + - "mycelo/*/*" + - "mycelo/*/*/*" + - "mycelo/*/*/*/*" + - "cmd/mycelo/*" + - title: "node" + globs: + - "node/*" + - title: "params" + globs: + - "params/*" + - title: "rpc" + globs: + - "rpc/*" + - title: "shared signer" + globs: + - "shared/signer/*" + sub: + - title: "testdata" + globs: + - "shared/signer/testdata/*" + - "shared/signer/testdata/*/*" + - title: "signer" + globs: + - "signer/*" + - "signer/*/*" + - "signer/*/*/*" + - "signer/*/*/*/*" + - title: "tests, e2e_test" + globs: + - "test/*" + - "tests/*" + - "tests/*/*" + - "tests/*/*/*" + - "tests/*/*/*/*" + - "e2e_test/*" + - "e2e_test/*/*/*" +# ignored globally, does not count towards line count +ignore: + # Double ** globbing is not currently available + - ".circleci/*" + - ".github/*" + - ".github/*/*" + # Configuration files in root + - ".git*" + - "*.yaml" + - "*.yml" + - "*.md" + - "Dockerfile*" + - ".dockerignore" + - "*.sum" + - "go.mod" + - ".mailmap" + - "AUTHORS" + - "COPYING" + - "COPYING.LESSER" + - "Makefile" + - "default.profraw" + - "geth-sources.jar" + - "monorepo_commit" + - "oss-fuzz.sh" + - "scripts/*" + - "scripts/*/*" + # Docs + - "docs/*" + - "docs/*/*" + - "docs/*/*/*" + - "docs/*/*/*/*" + # This is one layer deeper than currently exists + - "docs/*/*/*/*/*" + - "e2e_test/ethersjs-api-check/*.json" + - "e2e_test/ethersjs-api-check/*/*.json" + - "e2e_test/ethersjs-api-check/.gitignore"
diff --git go-ethereum/.github/no-response.yml celo/.github/no-response.yml deleted file mode 100644 index 903d4ce85f350f737c2049d272fae414a9ac148d..0000000000000000000000000000000000000000 --- go-ethereum/.github/no-response.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Number of days of inactivity before an Issue is closed for lack of response -daysUntilClose: 30 -# Label requiring a response -responseRequiredLabel: "need:more-information" -# Comment to post when closing an Issue for lack of response. Set to `false` to disable -closeComment: > - This issue has been automatically closed because there has been no response - to our request for more information from the original author. With only the - information that is currently in the issue, we don't have enough information - to take action. Please reach out if you have more relevant information or - answers to our questions so that we can investigate further.
diff --git go-ethereum/.github/pull_request_template.md celo/.github/pull_request_template.md new file mode 100644 index 0000000000000000000000000000000000000000..24ec3774ca9d64b2c74a36a3ad9e0d59602e89d3 --- /dev/null +++ celo/.github/pull_request_template.md @@ -0,0 +1,20 @@ +### Description + +_A few sentences describing the overall effects and goals of the pull request's commits. +What is the current behavior, and what is the updated/expected behavior with this PR?_ + +### Other changes + +_Describe any minor or "drive-by" changes here._ + +### Tested + +_An explanation of how the changes were tested or an explanation as to why they don't need to be._ + +### Related issues + +- Fixes #[issue number here] + +### Backwards compatibility + +_Brief explanation of why these changes are/are not backwards compatible._
diff --git go-ethereum/.github/stale.yml celo/.github/stale.yml deleted file mode 100644 index 6d921cc795ff45352aafb16efb6130f50e470b54..0000000000000000000000000000000000000000 --- go-ethereum/.github/stale.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 366 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 42 -# Issues with these labels will never be considered stale -exemptLabels: - - pinned - - security -# Label to use when marking an issue as stale -staleLabel: "status:inactive" -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: false
diff --git go-ethereum/.github/workflows/add-docker-tag.yaml celo/.github/workflows/add-docker-tag.yaml new file mode 100644 index 0000000000000000000000000000000000000000..08c4e55fabab5624fe6b04869d8aee3a76e3aaa3 --- /dev/null +++ celo/.github/workflows/add-docker-tag.yaml @@ -0,0 +1,50 @@ +name: Add Tags to Blockchain Release Images + +on: + workflow_dispatch: + inputs: + origin-tag: + description: 'Original tag' + required: true + type: string + destination-tag: + description: 'Tag to add' + required: true + type: choice + options: + - alfajores + - mainnet + workflow_call: + inputs: + origin-tag: + description: 'Original tag' + required: true + type: string + destination-tag: + description: 'Tag to add' + required: true + default: 'baklava' + type: string + +jobs: + Add-Tag: + runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' + steps: + - id: 'auth-gcp-master' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@v1' + with: + workload_identity_provider: 'projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-blockchain-add-tag/providers/github-by-repos' + service_account: 'celo-blockchain@devopsre.iam.gserviceaccount.com' + access_token_lifetime: '20m' + + - name: 'Set up Cloud SDK' + uses: 'google-github-actions/setup-gcloud@v1' + + - id: add-tag + run: | + gcloud container images add-tag us-west1-docker.pkg.dev/devopsre/celo-blockchain-public/geth:${{ inputs.origin-tag }} us-west1-docker.pkg.dev/devopsre/celo-blockchain-public/geth:${{ inputs.destination-tag }} + gcloud container images add-tag us-west1-docker.pkg.dev/devopsre/celo-blockchain-public/geth-all:${{ inputs.origin-tag }} us-west1-docker.pkg.dev/devopsre/celo-blockchain-public/geth-all:${{ inputs.destination-tag }}
diff --git go-ethereum/.github/workflows/build-sign-binaries.yaml celo/.github/workflows/build-sign-binaries.yaml new file mode 100644 index 0000000000000000000000000000000000000000..49a5f114d94955236e04304e3bd51662000ae4b3 --- /dev/null +++ celo/.github/workflows/build-sign-binaries.yaml @@ -0,0 +1,71 @@ +name: Build and Sign Blockchain Binaries + +on: + push: + branches: + - master + - 'release/[0-9]+.[0-9]+' + workflow_dispatch: + +jobs: + Build-Container-Blockchain-Binaries: + uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 + with: + workload-id-provider: 'projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-blockchain/providers/github-by-repos' + service-account: 'celo-blockchain@devopsre.iam.gserviceaccount.com' + artifact-registry: us-west1-docker.pkg.dev/devopsre/celo-blockchain-public/geth-xgo-builder + tag: latest + context: . + file: Dockerfile.binaries + + Upload-Cloud-Storage: + runs-on: ubuntu-latest + needs: + - Build-Container-Blockchain-Binaries + permissions: + contents: 'read' + id-token: 'write' + env: + BUILD_TARGETS: linux/amd64,linux/arm64 + TAG_NAME: ${{ github.ref_name }} + BRANCH_NAME: ${{ github.ref_name }} + REPO_NAME: ${{ github.repository }} + COMMIT_SHA: ${{ github.sha }} + steps: + + - name: 'Checkout' + uses: actions/checkout@v3 + + - id: 'auth-gcp-master' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@v1' + with: + workload_identity_provider: 'projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-blockchain/providers/github-by-repos' + service_account: 'celo-blockchain@devopsre.iam.gserviceaccount.com' + access_token_lifetime: '40m' + + - name: 'Set up Cloud SDK' + uses: 'google-github-actions/setup-gcloud@877d4953d2c70a0ba7ef3290ae968eb24af233bb' + + - name: 'Configure docker for gcloud auth' + run: 'gcloud auth configure-docker us-west1-docker.pkg.dev' + + - name: 'Get timestamp' + run: | + timestamp=$(date +%s) + echo "COMMIT_TIMESTAMP=${timestamp}" >> $GITHUB_ENV + + - name: 'Go Run all-tools' + run: | + docker run --rm -v $(pwd)/build/bin:/build -v $(pwd)/build/archives:/archives -v $(pwd):/go/src/github.com/ethereum/go-ethereum --entrypoint /bin/sh --env BUILD_TARGETS=$_BUILD_TARGETS --env TAG_NAME=$TAG_NAME --env BRANCH_NAME=$BRANCH_NAME --env REPO_NAME=$REPO_NAME --env COMMIT_SHA=$COMMIT_SHA --env COMMIT_TIMESTAMP=${{ env.COMMIT_TIMESTAMP }} --env CLOUDBUILD=True --env CI=True us-west1-docker.pkg.dev/devopsre/celo-blockchain-public/geth-xgo-builder:$COMMIT_SHA -c "go run build/ci.go xgo --alltools -- -targets=$BUILD_TARGETS -v -dest /build" + + - name: 'Go Run xgo-archive' + run: | + docker run --rm -v $(pwd)/build/bin:/build -v $(pwd)/build/archives:/archives -v $(pwd):/go/src/github.com/ethereum/go-ethereum --entrypoint /bin/sh --env BUILD_TARGETS=$_BUILD_TARGETS --env TAG_NAME=$TAG_NAME --env BRANCH_NAME=$BRANCH_NAME --env REPO_NAME=$REPO_NAME --env COMMIT_SHA=$COMMIT_SHA --env COMMIT_TIMESTAMP=${{ env.COMMIT_TIMESTAMP }} --env CLOUDBUILD=True --env CI=True us-west1-docker.pkg.dev/devopsre/celo-blockchain-public/geth-xgo-builder:$COMMIT_SHA -c "go run build/ci.go xgo-archive -targets=$BUILD_TARGETS -in /build -out /archives" + + - name: 'Upload to storage bucket' + uses: 'google-github-actions/upload-cloud-storage@v1' + with: + path: './build/archives' + destination: celo-blockchain-binaries/binaries/${{ env.BRANCH_NAME }} + parent: false
diff --git go-ethereum/.github/workflows/build-sign-commit-images.yaml celo/.github/workflows/build-sign-commit-images.yaml new file mode 100644 index 0000000000000000000000000000000000000000..18deb85a7baff9d9582ae86f1f2907daed800781 --- /dev/null +++ celo/.github/workflows/build-sign-commit-images.yaml @@ -0,0 +1,58 @@ +name: Build and Sign Blockchain Commit Images + +on: + push: + branches-ignore: + - 'release/[0-9]+.[0-9]+' + workflow_dispatch: + +jobs: + # From Cloud Build trigger ff28b668-43bf-4c3a-8967-fbf6640fdf5f in celo-testnet + # https://console.cloud.google.com/cloud-build/triggers;region=global/edit/ff28b668-43bf-4c3a-8967-fbf6640fdf5f?project=celo-testnet&supportedpurview=project + Build-Container-geth-Anycommit: + uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 + if: github.ref != 'refs/heads/master' + with: + workload-id-provider: 'projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-blockchain-dev/providers/github-by-repos' + service-account: 'celo-blockchain-dev@devopsre.iam.gserviceaccount.com' + artifact-registry: us-west1-docker.pkg.dev/devopsre/dev-images/geth + tag: testing + context: . + + # From Cloud Build trigger ff28b668-43bf-4c3a-8967-fbf6640fdf5f in celo-testnet + # https://console.cloud.google.com/cloud-build/triggers;region=global/edit/ff28b668-43bf-4c3a-8967-fbf6640fdf5f?project=celo-testnet&supportedpurview=project + Build-Container-geth-all-Anycommit: + uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 + if: github.ref != 'refs/heads/master' + with: + workload-id-provider: 'projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-blockchain-dev/providers/github-by-repos' + service-account: 'celo-blockchain-dev@devopsre.iam.gserviceaccount.com' + artifact-registry: us-west1-docker.pkg.dev/devopsre/dev-images/geth-all + tag: testing + context: . + file: Dockerfile.alltools + + # From Cloud Build trigger docker-branchname-tagged in celo-testnet + # https://console.cloud.google.com/cloud-build/triggers;region=global/edit/dcf76f6d-b05b-4289-bb3b-de3e9b62efa5?project=celo-testnet&supportedpurview=project + Build-Container-geth-master: + uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 + if: github.ref == 'refs/heads/master' + with: + workload-id-provider: 'projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-blockchain/providers/github-by-repos' + service-account: 'celo-blockchain@devopsre.iam.gserviceaccount.com' + artifact-registry: us-west1-docker.pkg.dev/devopsre/celo-blockchain-public/geth + tag: master + context: . + + # From Cloud Build trigger docker-branchname-tagged in celo-testnet + # https://console.cloud.google.com/cloud-build/triggers;region=global/edit/dcf76f6d-b05b-4289-bb3b-de3e9b62efa5?project=celo-testnet&supportedpurview=project + Build-Container-geth-all-master: + uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 + if: github.ref == 'refs/heads/master' + with: + workload-id-provider: 'projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-blockchain/providers/github-by-repos' + service-account: 'celo-blockchain@devopsre.iam.gserviceaccount.com' + artifact-registry: us-west1-docker.pkg.dev/devopsre/celo-blockchain-public/geth-all + tag: master + context: . + file: Dockerfile.alltools
diff --git go-ethereum/.github/workflows/build-sign-release-images.yaml celo/.github/workflows/build-sign-release-images.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8feb72475f545d7f9d626a5ce419060d4bcc472c --- /dev/null +++ celo/.github/workflows/build-sign-release-images.yaml @@ -0,0 +1,125 @@ +name: Build and Sign Blockchain Release Images + +on: + push: + branches: + - 'release/[0-9]+.[0-9]+' + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + workflow_dispatch: + +jobs: + Replace-Branch-Name: + runs-on: ubuntu-latest + # Map a step output to a job output + outputs: + replaced-branch: ${{ steps.replace.outputs.value }} + if: startsWith(github.ref, 'refs/heads/release') + steps: + - id: replace + run: | + branch_name=$(echo ${{ github.ref_name }} | sed 's/\///' ) + echo "value=${branch_name}" >> $GITHUB_OUTPUT + + Replace-Tag-V: + runs-on: ubuntu-latest + # Map a step output to a job output + outputs: + replaced-tag: ${{ steps.replace.outputs.value }} + major: ${{ steps.major-minor.outputs.MAJOR }} + major-minor: ${{ steps.major-minor.outputs.MAJOR_MINOR }} + if: startsWith(github.ref, 'refs/tags/v') + steps: + - id: replace + run: | + version=$(echo ${{ github.ref_name }} | sed 's/^v//' ) + echo "value=${version}" >> $GITHUB_OUTPUT + - id: major-minor + run: | + version=${{ steps.replace.outputs.value }} + semver=( ${version//./ } ) + echo "MAJOR=${semver[0]}" >> $GITHUB_OUTPUT + echo "MAJOR_MINOR=${semver[0]}.${semver[1]}" >> $GITHUB_OUTPUT + + Build-Container-geth-dev: + uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 + if: startsWith(github.ref, 'refs/heads/release') + needs: + - Replace-Branch-Name + with: + workload-id-provider: 'projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-blockchain-dev/providers/github-by-repos' + service-account: 'celo-blockchain-dev@devopsre.iam.gserviceaccount.com' + artifact-registry: us-west1-docker.pkg.dev/devopsre/dev-images/geth + tag: ${{needs.Replace-Branch-Name.outputs.replaced-branch}} + context: . + + Build-Container-geth-all-dev: + uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 + if: startsWith(github.ref, 'refs/heads/release') + needs: + - Replace-Branch-Name + with: + workload-id-provider: 'projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-blockchain-dev/providers/github-by-repos' + service-account: 'celo-blockchain-dev@devopsre.iam.gserviceaccount.com' + artifact-registry: us-west1-docker.pkg.dev/devopsre/dev-images/geth-all + tag: ${{needs.Replace-Branch-Name.outputs.replaced-branch}} + context: . + file: Dockerfile.alltools + + Build-Container-geth: + uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 + if: startsWith(github.ref, 'refs/tags/v') + needs: + - Replace-Tag-V + with: + workload-id-provider: 'projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-blockchain/providers/github-by-repos' + service-account: 'celo-blockchain@devopsre.iam.gserviceaccount.com' + artifact-registry: us-west1-docker.pkg.dev/devopsre/celo-blockchain-public/geth + tag: ${{needs.Replace-Tag-V.outputs.replaced-tag}} + context: . + + Build-Container-geth-all: + uses: celo-org/reusable-workflows/.github/workflows/container-cicd.yaml@v1.8 + if: startsWith(github.ref, 'refs/tags/v') + needs: + - Replace-Tag-V + with: + workload-id-provider: 'projects/1094498259535/locations/global/workloadIdentityPools/gh-celo-blockchain/providers/github-by-repos' + service-account: 'celo-blockchain@devopsre.iam.gserviceaccount.com' + artifact-registry: us-west1-docker.pkg.dev/devopsre/celo-blockchain-public/geth-all + tag: ${{needs.Replace-Tag-V.outputs.replaced-tag}} + context: . + file: Dockerfile.alltools + + Add-Baklava-tag: + if: startsWith(github.ref, 'refs/tags/v') + uses: ./.github/workflows/add-docker-tag.yaml + needs: + - Replace-Tag-V + - Build-Container + - Build-Container-all + with: + origin-tag: ${{needs.Replace-Tag-V.outputs.replaced-tag}} + destination-tag: baklava + + Add-Major-tag: + if: startsWith(github.ref, 'refs/tags/v') + uses: ./.github/workflows/add-docker-tag.yaml + needs: + - Replace-Tag-V + - Build-Container + - Build-Container-all + with: + origin-tag: ${{needs.Replace-Tag-V.outputs.replaced-tag}} + destination-tag: ${{needs.Replace-Tag-V.outputs.major}} + + Add-Major-Minor-tag: + if: startsWith(github.ref, 'refs/tags/v') + uses: ./.github/workflows/add-docker-tag.yaml + needs: + - Replace-Tag-V + - Build-Container + - Build-Container-all + with: + origin-tag: ${{needs.Replace-Tag-V.outputs.replaced-tag}} + destination-tag: ${{needs.Replace-Tag-V.outputs.major-minor}}
diff --git go-ethereum/.github/workflows/config.yml celo/.github/workflows/config.yml new file mode 100644 index 0000000000000000000000000000000000000000..da0a85806a45ead3a30f988d32333aec4b7de013 --- /dev/null +++ celo/.github/workflows/config.yml @@ -0,0 +1,728 @@ +name: celo-blockchain + +on: + push: + branches: + - master + - feature-split + + pull_request: + branches: + - master + - feature-split + +concurrency: + group: ${{ github.head_ref }} + cancel-in-progress: true + +env: + # Increment these to force cache rebuilding + SYSTEM_CONTRACTS_CACHE_VERSION: 3 + CHECKOUT_MONOREPO_CACHE_VERSION: 5 + GO_VERSION: '1.17.5' + # Location where compiled system contracts are stored under the root of this + # repo. + SYSTEM_CONTRACTS_PATH: "compiled-system-contracts" + +jobs: + prepare-system-contracts: + name: Build celo-monorepo + runs-on: [self-hosted, blockchain, 8-cpu] + env: + NODE_VERSION: 12 + PYTHON_VERSION: '2.7' + CONTRACTS_BUILD_PATH: packages/protocol/build/contracts + + steps: + - name: Checkout celo-blockchain repo + uses: actions/checkout@v3 + with: + path: geth + - name: Get monorepo commit + run: echo "MONOREPO_COMMIT=$(cat ./geth/monorepo_commit)" >> $GITHUB_ENV + - name: Get monorepo commit id + run: echo "MONOREPO_COMMIT_ID=${{ hashFiles('./geth/monorepo_commit') }}" >> $GITHUB_ENV + - name: Restore celo-monorepo cache + id: celo-monorepo-cache + uses: actions/cache@v3 + with: + path: celo-monorepo + key: celo-monorepo-cache-${{ env.MONOREPO_COMMIT_ID }}-${{ env.CHECKOUT_MONOREPO_CACHE_VERSION }} + restore-keys: | + celo-monorepo-cache- + # If there is a cached version, there is no need to rebuild it + - name: Conditionally checkout monorepo + if: steps.celo-monorepo-cache.outputs.cache-hit != 'true' + uses: actions/checkout@v3 + with: + repository: celo-org/celo-monorepo + path: celo-monorepo + ref: ${{ env.MONOREPO_COMMIT }} + - name: Conditionally setup Node.js + if: steps.celo-monorepo-cache.outputs.cache-hit != 'true' + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + cache-dependency-path: celo-monorepo/yarn.lock + # Python is required for node-gyp rebuild + - name: Conditionally setup python + if: steps.celo-monorepo-cache.outputs.cache-hit != 'true' + uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + - name: Conditionally build monorepo + if: steps.celo-monorepo-cache.outputs.cache-hit != 'true' + run: | + cd celo-monorepo + yarn + yarn build + cd packages/protocol + yarn run build:sol + - name: Upload built system contracts + if: always() + uses: actions/upload-artifact@v3 + with: + name: system-contracts + path: 'celo-monorepo/${{ env.CONTRACTS_BUILD_PATH }}' + + go-modules: + name: Setup go + runs-on: [self-hosted, blockchain, 8-cpu] + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + + check-imports: + name: Check imports + needs: go-modules + runs-on: [self-hosted, blockchain, 8-cpu] + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + - name: Check imports to ensure we are using github.com/ethereum/go-ethereum + run: ./scripts/check_imports.sh + + lint: + name: Lint code + runs-on: [self-hosted, blockchain, 8-cpu] + needs: go-modules + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + - name: Run linter + run: make lint + + unit-tests: + name: Unit tests + runs-on: [self-hosted, blockchain, 8-cpu] + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + - name: Restore system contracts + uses: actions/download-artifact@v3 + with: + name: system-contracts + path: ${{ env.SYSTEM_CONTRACTS_PATH }} + - name: Prepare ethersjs project + run: make prepare-ethersjs-project + - name: Run tests + run: | + go install github.com/jstemmer/go-junit-report@latest + CI=true go test -v -cover -coverprofile=coverage.out ./... | tee go-test.out + - name: Generate test report + if: always() + run: | + go-junit-report -set-exit-code < go-test.out | tee -a report.xml + - name: Test summary + id: unit-tests-summary + if: always() + uses: test-summary/action@v2 + with: + paths: | + report.xml + output: test-summary.md + show: "fail" + - name: Post test summary comment on PR + if: always() + uses: mshick/add-pr-comment@v2 + with: + message-path: test-summary.md + message-id: test-summary + # TODO: figure out if we need a token + - name: Upload coverage to codecov + if: always() + uses: codecov/codecov-action@v3 + + race: + name: Data race detector + if: ${{ github.ref == 'refs/heads/master' || contains(github.ref, 'release') }} + runs-on: [self-hosted, blockchain, 8-cpu] + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + - name: Restore system contracts + uses: actions/download-artifact@v3 + with: + name: system-contracts + path: ${{ env.SYSTEM_CONTRACTS_PATH }} + - name: Prepare ethersjs project + run: make prepare-ethersjs-project + - name: Install JUnit report + run: go get github.com/jstemmer/go-junit-report + - name: Run tests + run: | + set -o pipefail + mkdir -p /tmp/test-results + trap "go-junit-report < /tmp/test-results/go-test.out > /tmp/test-results/go-test-report.xml" EXIT + go test -p 1 -v -race ./... | tee /tmp/test-results/go-test.out + - name: Upload test report + uses: actions/upload-artifact@v3 + with: + name: data-race-detector-report + path: /tmp/test-results + + build-and-store-binaries: + name: Build and store binaries + runs-on: [self-hosted, blockchain, 8-cpu] + needs: + - go-modules + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + - name: Run and compress executables + run: | + make all + - name: Upload binaries + uses: actions/upload-artifact@v3 + with: + name: binaries + path: ./build/bin + + istanbul-e2e-coverage: + name: Istanbul consensus coverage + runs-on: [self-hosted, blockchain, 8-cpu] + # Needed to publish the summary PR comment + permissions: + pull-requests: write + + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + - name: Prepare ethersjs project + run: make prepare-ethersjs-project + - name: Restore system contracts + uses: actions/download-artifact@v3 + with: + name: system-contracts + path: ${{ env.SYSTEM_CONTRACTS_PATH }} + - name: Run tests and print coverage summary + run: | + go test -v -coverprofile cov.out -coverpkg ./consensus/istanbul/... ./e2e_test + go run tools/parsecov/main.go -packagePrefix github.com/ethereum/go-ethereum/ cov.out > summary + cat summary + - name: Build summary message + run: | + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + echo "PR_SUMMARY_MESSAGE<<$EOF" >> $GITHUB_ENV + echo "Coverage from tests in \`./e2e_test/...\` for \`./consensus/istanbul/...\` at commit $GITHUB_SHA" >> $GITHUB_ENV + echo "<details><summary>" >> $GITHUB_ENV + head -n 1 summary >> $GITHUB_ENV + echo "</summary><br><pre>" >> $GITHUB_ENV + tail -n +2 summary >> $GITHUB_ENV + echo "</pre>" >> $GITHUB_ENV + echo "$EOF" >> $GITHUB_ENV + - name: Post summary comment on PR + uses: mshick/add-pr-comment@v2 + with: + message: "${{ env.PR_SUMMARY_MESSAGE }}" + message-id: istanbul-cov-summary + + lightest-sync-test: + name: Lightest sync test + runs-on: [self-hosted, blockchain, 8-cpu] + + needs: + - build-and-store-binaries + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + - name: Download pre-built binaries + uses: actions/download-artifact@v3 + with: + name: binaries + path: ./build/bin + - name: Fix permissions + run: | + chmod +x ./build/bin/* + - name: Run sync test + run: | + DATADIR=/tmp/lightest_sync_test_data MODE=lightest ./scripts/sync_test.sh + + e2e-benchmarks: + name: End-to-end benchmarks + runs-on: [self-hosted, blockchain, 8-cpu] + + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + - name: Restore system contracts + uses: actions/download-artifact@v3 + with: + name: system-contracts + path: ${{ env.SYSTEM_CONTRACTS_PATH }} + - name: Run benchmarks + run: | + set -o pipefail + mkdir -p /tmp/bench-results + trap "grep -E 'goos: |^goarch: |^pkg: |^cpu: |^Benchmark' /tmp/bench-results/go-bench.out > /tmp/bench-results/go-bench-results.txt" EXIT + go test ./e2e_test -run Bench -bench . | tee /tmp/bench-results/go-bench.out + - name: Upload results + uses: actions/upload-artifact@v3 + with: + name: e2e-benchmarks + path: /tmp/bench-results + + end-to-end-blockchain-parameters-test: + name: End-to-end blockchain parameters test + runs-on: [self-hosted, blockchain, 8-cpu] + env: + NODE_VERSION: 12 + + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + path: geth + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + cache-dependency-path: geth/go.sum + - name: Get monorepo commit + run: echo "MONOREPO_COMMIT=$(cat ./geth/monorepo_commit)" >> $GITHUB_ENV + - name: Get monorepo commit id + run: echo "MONOREPO_COMMIT_ID=${{ hashFiles('./geth/monorepo_commit') }}" >> $GITHUB_ENV + - name: Restore celo-monorepo cache + id: celo-monorepo-cache + uses: actions/cache@v3 + with: + path: celo-monorepo + key: celo-monorepo-cache-${{ env.MONOREPO_COMMIT_ID }}-${{ env.CHECKOUT_MONOREPO_CACHE_VERSION }} + restore-keys: | + celo-monorepo-cache- + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + cache-dependency-path: celo-monorepo/yarn.lock + - name: Run e2e test + run: | + export E2E_TESTS_FORCE_USE_MYCELO=true + cd celo-monorepo/packages/celotool + ./ci_test_blockchain_parameters.sh local "$GITHUB_WORKSPACE/geth" + + end-to-end-governance-test: + name: End-to-end governance test + runs-on: [self-hosted, blockchain, 8-cpu] + env: + NODE_VERSION: 12 + + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + path: geth + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + cache-dependency-path: geth/go.sum + - name: Get monorepo commit + run: echo "MONOREPO_COMMIT=$(cat ./geth/monorepo_commit)" >> $GITHUB_ENV + - name: Get monorepo commit id + run: echo "MONOREPO_COMMIT_ID=${{ hashFiles('./geth/monorepo_commit') }}" >> $GITHUB_ENV + - name: Restore celo-monorepo cache + id: celo-monorepo-cache + uses: actions/cache@v3 + with: + path: celo-monorepo + key: celo-monorepo-cache-${{ env.MONOREPO_COMMIT_ID }}-${{ env.CHECKOUT_MONOREPO_CACHE_VERSION }} + restore-keys: | + celo-monorepo-cache- + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + cache-dependency-path: celo-monorepo/yarn.lock + - name: Run e2e test + run: | + export E2E_TESTS_FORCE_USE_MYCELO=true + cd celo-monorepo/packages/celotool + ./ci_test_governance.sh local "$GITHUB_WORKSPACE/geth" + + + end-to-end-sync-test: + name: End-to-end sync test + runs-on: [self-hosted, blockchain, 8-cpu] + env: + NODE_VERSION: 12 + + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + path: geth + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + cache-dependency-path: geth/go.sum + - name: Get monorepo commit + run: echo "MONOREPO_COMMIT=$(cat ./geth/monorepo_commit)" >> $GITHUB_ENV + - name: Get monorepo commit id + run: echo "MONOREPO_COMMIT_ID=${{ hashFiles('./geth/monorepo_commit') }}" >> $GITHUB_ENV + - name: Restore celo-monorepo cache + id: celo-monorepo-cache + uses: actions/cache@v3 + with: + path: celo-monorepo + key: celo-monorepo-cache-${{ env.MONOREPO_COMMIT_ID }}-${{ env.CHECKOUT_MONOREPO_CACHE_VERSION }} + restore-keys: | + celo-monorepo-cache- + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + cache-dependency-path: celo-monorepo/yarn.lock + - name: Run e2e test + run: | + export E2E_TESTS_FORCE_USE_MYCELO=true + cd celo-monorepo/packages/celotool + ./ci_test_sync.sh local "$GITHUB_WORKSPACE/geth" + + end-to-end-slashing-test: + name: End-to-end slashing test + runs-on: [self-hosted, blockchain, 8-cpu] + env: + NODE_VERSION: 12 + + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + path: geth + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + cache-dependency-path: geth/go.sum + - name: Get monorepo commit + run: echo "MONOREPO_COMMIT=$(cat ./geth/monorepo_commit)" >> $GITHUB_ENV + - name: Get monorepo commit id + run: echo "MONOREPO_COMMIT_ID=${{ hashFiles('./geth/monorepo_commit') }}" >> $GITHUB_ENV + - name: Restore celo-monorepo cache + id: celo-monorepo-cache + uses: actions/cache@v3 + with: + path: celo-monorepo + key: celo-monorepo-cache-${{ env.MONOREPO_COMMIT_ID }}-${{ env.CHECKOUT_MONOREPO_CACHE_VERSION }} + restore-keys: | + celo-monorepo-cache- + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + cache-dependency-path: celo-monorepo/yarn.lock + - name: Run e2e test + run: | + export E2E_TESTS_FORCE_USE_MYCELO=true + cd celo-monorepo/packages/celotool + ./ci_test_slashing.sh local "$GITHUB_WORKSPACE/geth" + + end-to-end-transfers-test: + name: End-to-end transfers test + runs-on: [self-hosted, blockchain, 8-cpu] + env: + NODE_VERSION: 12 + + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + path: geth + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + cache-dependency-path: geth/go.sum + - name: Get monorepo commit + run: echo "MONOREPO_COMMIT=$(cat ./geth/monorepo_commit)" >> $GITHUB_ENV + - name: Get monorepo commit id + run: echo "MONOREPO_COMMIT_ID=${{ hashFiles('./geth/monorepo_commit') }}" >> $GITHUB_ENV + - name: Restore celo-monorepo cache + id: celo-monorepo-cache + uses: actions/cache@v3 + with: + path: celo-monorepo + key: celo-monorepo-cache-${{ env.MONOREPO_COMMIT_ID }}-${{ env.CHECKOUT_MONOREPO_CACHE_VERSION }} + restore-keys: | + celo-monorepo-cache- + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + cache-dependency-path: celo-monorepo/yarn.lock + - name: Run e2e test + run: | + export E2E_TESTS_FORCE_USE_MYCELO=true + cd celo-monorepo/packages/celotool + ./ci_test_transfers.sh local "$GITHUB_WORKSPACE/geth" + + end-to-end-validator-order-test: + name: End-to-end validator order test + runs-on: [self-hosted, blockchain, 8-cpu] + env: + NODE_VERSION: 12 + + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + path: geth + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + cache-dependency-path: geth/go.sum + - name: Get monorepo commit + run: echo "MONOREPO_COMMIT=$(cat ./geth/monorepo_commit)" >> $GITHUB_ENV + - name: Get monorepo commit id + run: echo "MONOREPO_COMMIT_ID=${{ hashFiles('./geth/monorepo_commit') }}" >> $GITHUB_ENV + - name: Restore celo-monorepo cache + id: celo-monorepo-cache + uses: actions/cache@v3 + with: + path: celo-monorepo + key: celo-monorepo-cache-${{ env.MONOREPO_COMMIT_ID }}-${{ env.CHECKOUT_MONOREPO_CACHE_VERSION }} + restore-keys: | + celo-monorepo-cache- + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + cache-dependency-path: celo-monorepo/yarn.lock + - name: Run e2e test + run: | + export E2E_TESTS_FORCE_USE_MYCELO=true + cd celo-monorepo/packages/celotool + ./ci_test_validator_order.sh local "$GITHUB_WORKSPACE/geth" + + end-to-end-cip35-eth-compatibility-test: + name: End-to-end CIP35-eth compatibility test + runs-on: [self-hosted, blockchain, 8-cpu] + env: + NODE_VERSION: 12 + + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + path: geth + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + cache-dependency-path: geth/go.sum + - name: Get monorepo commit + run: echo "MONOREPO_COMMIT=$(cat ./geth/monorepo_commit)" >> $GITHUB_ENV + - name: Get monorepo commit id + run: echo "MONOREPO_COMMIT_ID=${{ hashFiles('./geth/monorepo_commit') }}" >> $GITHUB_ENV + - name: Restore celo-monorepo cache + id: celo-monorepo-cache + uses: actions/cache@v3 + with: + path: celo-monorepo + key: celo-monorepo-cache-${{ env.MONOREPO_COMMIT_ID }}-${{ env.CHECKOUT_MONOREPO_CACHE_VERSION }} + restore-keys: | + celo-monorepo-cache- + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + cache-dependency-path: celo-monorepo/yarn.lock + - name: Run e2e test + run: | + export E2E_TESTS_FORCE_USE_MYCELO=true + cd celo-monorepo/packages/celotool + ./ci_test_cip35.sh local "$GITHUB_WORKSPACE/geth" + + end-to-end-replica-test: + name: End-to-end replica test + runs-on: [self-hosted, blockchain, 8-cpu] + env: + NODE_VERSION: 12 + + needs: + - go-modules + - prepare-system-contracts + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + path: geth + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + cache: true + cache-dependency-path: geth/go.sum + - name: Get monorepo commit + run: echo "MONOREPO_COMMIT=$(cat ./geth/monorepo_commit)" >> $GITHUB_ENV + - name: Get monorepo commit id + run: echo "MONOREPO_COMMIT_ID=${{ hashFiles('./geth/monorepo_commit') }}" >> $GITHUB_ENV + - name: Restore celo-monorepo cache + id: celo-monorepo-cache + uses: actions/cache@v3 + with: + path: celo-monorepo + key: celo-monorepo-cache-${{ env.MONOREPO_COMMIT_ID }}-${{ env.CHECKOUT_MONOREPO_CACHE_VERSION }} + restore-keys: | + celo-monorepo-cache- + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + cache-dependency-path: celo-monorepo/yarn.lock + - name: Run e2e test + run: | + export E2E_TESTS_FORCE_USE_MYCELO=true + cd celo-monorepo/packages/celotool + ./ci_test_replicas.sh local "$GITHUB_WORKSPACE/geth"
diff --git go-ethereum/.github/workflows/pages.yaml celo/.github/workflows/pages.yaml new file mode 100644 index 0000000000000000000000000000000000000000..df6fba1491f6fd09fe711dc5b02c164b2b2a7f4a --- /dev/null +++ celo/.github/workflows/pages.yaml @@ -0,0 +1,112 @@ +name: Build and publish forkdiff github-pages +permissions: + contents: write +on: + push: + branches: + - feature-split +env: + GO_VERSION: '1.17.5' + CELO_BLOCKCHAIN_PATH: 'celo-blockchain' + GO_ETHEREUM_PATH: 'go-ethereum' + +jobs: + create-forkdiff: + runs-on: ubuntu-latest + steps: + - name: Checkout celo-blockchain + uses: actions/checkout@v3 + with: + path: ${{ env.CELO_BLOCKCHAIN_PATH }} + ref: feature-split + + - name: Checkout go-ethereum + uses: actions/checkout@v3 + with: + path: ${{ env.GO_ETHEREUM_PATH }} + repository: ethereum/go-ethereum + ref: eae3b1946a276ac099e0018fc792d9e8c3bfda6d + + - name: Setup golang + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: false + + - name: Install goimports + run: | + go install golang.org/x/tools/cmd/goimports@latest + + - name: Minimize unhelpful differences (formatting, imports) + # Replace github.com/ethereum/go-ethereum -> github.com/ethereum/go-ethereum + # Re-replaces crypto/bls as this is specific to Celo, to avoid confusing diffs + # Replaces '"github.com/ethereum/go-ethereum"' imports (Celo renaming) + # Applies gofmt to both repositories to minimize formatting differences + # The last steps replace 2+ white spaces with a single space, since git diff --ignore-all-space + # is not available. If this causes other issues down the line, delete those two lines. + run: | + grep -rl "//\s*nolint:\s*unused$" ${{ env.CELO_BLOCKCHAIN_PATH }}/. | xargs sed -i "s#//\s*nolint:\s*unused\$##" + grep -rl "github.com/ethereum/go-ethereum" ${{ env.CELO_BLOCKCHAIN_PATH }}/. --exclude "*fork.yaml"| xargs sed -i "s#github.com/celo-org/celo-blockchain#github.com/ethereum/go-ethereum#" + grep -rl "github.com/celo-org/celo-blockchain/crypto/bls" ${{ env.CELO_BLOCKCHAIN_PATH }}/. --exclude "*fork.yaml"| xargs sed -i "s#github.com/ethereum/go-ethereum/crypto/bls#github.com/ethereum/go-ethereum/crypto/bls#" + grep -rl '"github.com/ethereum/go-ethereum"' . --exclude "*fork.yaml" | xargs sed -i 's#ethereum "github.com/ethereum/go-ethereum"#"github.com/ethereum/go-ethereum"#' + gofmt -w ./${{ env.CELO_BLOCKCHAIN_PATH }} ./${{ env.GO_ETHEREUM_PATH }} + goimports -w ./${{ env.CELO_BLOCKCHAIN_PATH }} ./${{ env.GO_ETHEREUM_PATH }} + grep -rl " \{2,\}" ${{ env.CELO_BLOCKCHAIN_PATH }}/. --include \*.go --exclude "*fork.yaml" | xargs sed -i "s# \{2,\}# #g" + grep -rl " \{2,\}" ${{ env.GO_ETHEREUM_PATH }}/. --include \*.go | xargs sed -i "s# \{2,\}# #g" + + - name: Commit changes locally (${{ env.CELO_BLOCKCHAIN_PATH }}) + uses: EndBug/add-and-commit@v9 + with: + message: "Replace github.com/ethereum/go-ethereum and run gofmt locally" + add: '-u' + push: false + cwd: './${{ env.CELO_BLOCKCHAIN_PATH }}' + + - name: Commit changes locally (${{ env.GO_ETHEREUM_PATH }}) + uses: EndBug/add-and-commit@v9 + with: + message: "Run gofmt locally" + add: '-u' + push: false + cwd: './${{ env.GO_ETHEREUM_PATH }}' + + - name: Build forkdiff + uses: "docker://eelaclabs/forkdiff:latest" + with: + args: -repo=/github/workspace/${{ env.CELO_BLOCKCHAIN_PATH }} -upstream-repo=/github/workspace/${{ env.GO_ETHEREUM_PATH }} -fork=/github/workspace/${{ env.CELO_BLOCKCHAIN_PATH }}/.github/fork.yaml -out=/github/workspace/index.html + + - name: Upload forkdiff index.html + uses: actions/upload-artifact@v3 + with: + name: forkdiff-output + path: ./index.html + + # Split into a separate step since JamesIves/github-pages-deploy-action@v4 + # requires running actions/checkout from the main workspace, + # which we do not do above as we need to checkout two repositories. + + deploy: + needs: + - create-forkdiff + concurrency: ci-${{ github.ref }} + runs-on: ubuntu-latest + steps: + - name: Checkout celo-blockchain + uses: actions/checkout@v3 + + - name: Build pages directory + run: | + mkdir -p /tmp/pages + touch /tmp/pages/.nojekyll + + - name: Restore forkdiff index.html output + uses: actions/download-artifact@v3 + with: + name: forkdiff-output + path: /tmp/pages/index.html + + - name: Deploy + uses: JamesIves/github-pages-deploy-action@v4 + with: + folder: /tmp/pages + clean: true
diff --git go-ethereum/.gitignore celo/.gitignore index 1ee8b83022efe585f890e9104913de9cf40bfacf..5dc0074a147f7e45fb8543898c6b164d0584165e 100644 --- go-ethereum/.gitignore +++ celo/.gitignore @@ -14,6 +14,14 @@ .ethtest */**/*tx_database* */**/*dapps* build/_vendor/pkg +*.swp +target + +# created when running tests +/miner/validatorenodes/ +/graphql/roundstates/ +/graphql/validatorenodes/ +/graphql/versioncertificates/   #* .#* @@ -26,7 +34,9 @@ # used by the Makefile /build/_workspace/ /build/cache/ /build/bin/ +!/build/bin/geth.aar /geth*.zip +ndk_bundle   # travis profile.tmp @@ -47,3 +57,13 @@ /dashboard/assets/bundle.js.map /dashboard/assets/package-lock.json   **/yarn-error.log + +vendor/**/Cargo.lock + +# Docs +docs/Gemfile.lock +docs/_site +docs/.jekyll-metadata + +# Ignore compiled contracts +compiled-system-contracts
diff --git go-ethereum/.golangci.yml celo/.golangci.yml index 395a91fe1bc8877e689eb00b7da572369c43d514..c8018a86c54af9e4f52f44d35ba4dc2ee0ab0f1e 100644 --- go-ethereum/.golangci.yml +++ celo/.golangci.yml @@ -12,7 +12,6 @@ linters: disable-all: true enable: - - deadcode - goconst - goimports - gosimple @@ -22,7 +21,11 @@ - misspell # - staticcheck - unconvert # - unused - - varcheck + + # - gosec + # - errcheck + # - prealloc + # - unparam   linters-settings: gofmt: @@ -31,8 +34,22 @@ goconst: min-len: 3 # minimum length of string constant min-occurrences: 6 # minimum number of occurrences   + issues: exclude-rules: + # ignore deprecation warnings for now + - linters: + - staticcheck + text: "SA1019:" + # genesis has a really long string + - path: core/genesis_alloc.go + linters: + - misspell + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - errcheck + - gosec - path: crypto/blake2b/ linters: - deadcode
(deleted)
+0
-265
diff --git go-ethereum/.travis.yml celo/.travis.yml deleted file mode 100644 index f1018cc11edd4d272f38f849b1b98316924ab23c..0000000000000000000000000000000000000000 --- go-ethereum/.travis.yml +++ /dev/null @@ -1,265 +0,0 @@ -language: go -go_import_path: github.com/ethereum/go-ethereum -sudo: false -jobs: - allow_failures: - - stage: build - os: osx - go: 1.17.x - env: - - azure-osx - - azure-ios - - cocoapods-ios - - include: - # This builder only tests code linters on latest version of Go - - stage: lint - os: linux - dist: bionic - go: 1.17.x - env: - - lint - git: - submodules: false # avoid cloning ethereum/tests - script: - - go run build/ci.go lint - - # These builders create the Docker sub-images for multi-arch push and each - # will attempt to push the multi-arch image if they are the last builder - - stage: build - if: type = push - os: linux - arch: amd64 - dist: bionic - go: 1.17.x - env: - - docker - services: - - docker - git: - submodules: false # avoid cloning ethereum/tests - before_install: - - export DOCKER_CLI_EXPERIMENTAL=enabled - script: - - go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go - - - stage: build - if: type = push - os: linux - arch: arm64 - dist: bionic - go: 1.17.x - env: - - docker - services: - - docker - git: - submodules: false # avoid cloning ethereum/tests - before_install: - - export DOCKER_CLI_EXPERIMENTAL=enabled - script: - - go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go - - # This builder does the Ubuntu PPA upload - - stage: build - if: type = push - os: linux - dist: bionic - go: 1.17.x - env: - - ubuntu-ppa - - GO111MODULE=on - git: - submodules: false # avoid cloning ethereum/tests - addons: - apt: - packages: - - devscripts - - debhelper - - dput - - fakeroot - - python-bzrlib - - python-paramiko - script: - - echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts - - go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>" - - # This builder does the Linux Azure uploads - - stage: build - if: type = push - os: linux - dist: bionic - sudo: required - go: 1.17.x - env: - - azure-linux - - GO111MODULE=on - git: - submodules: false # avoid cloning ethereum/tests - addons: - apt: - packages: - - gcc-multilib - script: - # Build for the primary platforms that Trusty can manage - - go run build/ci.go install -dlgo - - go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - go run build/ci.go install -dlgo -arch 386 - - go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - # Switch over GCC to cross compilation (breaks 386, hence why do it here only) - - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross - - sudo ln -s /usr/include/asm-generic /usr/include/asm - - - GOARM=5 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc - - GOARM=5 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - GOARM=6 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc - - GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - GOARM=7 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabihf-gcc - - GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc - - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - # This builder does the Linux Azure MIPS xgo uploads - - stage: build - if: type = push - os: linux - dist: bionic - services: - - docker - go: 1.17.x - env: - - azure-linux-mips - - GO111MODULE=on - git: - submodules: false # avoid cloning ethereum/tests - script: - - go run build/ci.go xgo --alltools -- --targets=linux/mips --ldflags '-extldflags "-static"' -v - - for bin in build/bin/*-linux-mips; do mv -f "${bin}" "${bin/-linux-mips/}"; done - - go run build/ci.go archive -arch mips -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - - go run build/ci.go xgo --alltools -- --targets=linux/mipsle --ldflags '-extldflags "-static"' -v - - for bin in build/bin/*-linux-mipsle; do mv -f "${bin}" "${bin/-linux-mipsle/}"; done - - go run build/ci.go archive -arch mipsle -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - - go run build/ci.go xgo --alltools -- --targets=linux/mips64 --ldflags '-extldflags "-static"' -v - - for bin in build/bin/*-linux-mips64; do mv -f "${bin}" "${bin/-linux-mips64/}"; done - - go run build/ci.go archive -arch mips64 -type tar -signer LINUX_SIGNING_KEY signify SIGNIFY_KEY -upload gethstore/builds - - - go run build/ci.go xgo --alltools -- --targets=linux/mips64le --ldflags '-extldflags "-static"' -v - - for bin in build/bin/*-linux-mips64le; do mv -f "${bin}" "${bin/-linux-mips64le/}"; done - - go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - # This builder does the Android Maven and Azure uploads - - stage: build - if: type = push - os: linux - dist: bionic - addons: - apt: - packages: - - openjdk-8-jdk - env: - - azure-android - - maven-android - - GO111MODULE=on - git: - submodules: false # avoid cloning ethereum/tests - before_install: - # Install Android and it's dependencies manually, Travis is stale - - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 - - curl https://dl.google.com/android/repository/commandlinetools-linux-6858069_latest.zip -o android.zip - - unzip -q android.zip -d $HOME/sdk && rm android.zip - - mv $HOME/sdk/cmdline-tools $HOME/sdk/latest && mkdir $HOME/sdk/cmdline-tools && mv $HOME/sdk/latest $HOME/sdk/cmdline-tools - - export PATH=$PATH:$HOME/sdk/cmdline-tools/latest/bin - - export ANDROID_HOME=$HOME/sdk - - - yes | sdkmanager --licenses >/dev/null - - sdkmanager "platform-tools" "platforms;android-15" "platforms;android-19" "platforms;android-24" "ndk-bundle" - - # Install Go to allow building with - - curl https://dl.google.com/go/go1.16.linux-amd64.tar.gz | tar -xz - - export PATH=`pwd`/go/bin:$PATH - - export GOROOT=`pwd`/go - - export GOPATH=$HOME/go - script: - # Build the Android archive and upload it to Maven Central and Azure - - mkdir -p $GOPATH/src/github.com/ethereum - - ln -s `pwd` $GOPATH/src/github.com/ethereum/go-ethereum - - go run build/ci.go aar -signer ANDROID_SIGNING_KEY -signify SIGNIFY_KEY -deploy https://oss.sonatype.org -upload gethstore/builds - - # This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads - - stage: build - if: type = push - os: osx - go: 1.17.x - env: - - azure-osx - - azure-ios - - cocoapods-ios - - GO111MODULE=on - git: - submodules: false # avoid cloning ethereum/tests - script: - - go run build/ci.go install -dlgo - - go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - # Build the iOS framework and upload it to CocoaPods and Azure - - gem uninstall cocoapods -a -x - - gem install cocoapods - - - mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak - - sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb - - if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git clone --depth=1 https://github.com/CocoaPods/Specs.git ~/.cocoapods/repos/master && pod setup --verbose; fi - - - xctool -version - - xcrun simctl list - - # Workaround for https://github.com/golang/go/issues/23749 - - export CGO_CFLAGS_ALLOW='-fmodules|-fblocks|-fobjc-arc' - - go run build/ci.go xcode -signer IOS_SIGNING_KEY -signify SIGNIFY_KEY -deploy trunk -upload gethstore/builds - - # These builders run the tests - - stage: build - os: linux - arch: amd64 - dist: bionic - go: 1.17.x - env: - - GO111MODULE=on - script: - - go run build/ci.go test -coverage $TEST_PACKAGES - - - stage: build - if: type = pull_request - os: linux - arch: arm64 - dist: bionic - go: 1.17.x - env: - - GO111MODULE=on - script: - - go run build/ci.go test -coverage $TEST_PACKAGES - - - stage: build - os: linux - dist: bionic - go: 1.16.x - env: - - GO111MODULE=on - script: - - go run build/ci.go test -coverage $TEST_PACKAGES - - # This builder does the Azure archive purges to avoid accumulating junk - - stage: build - if: type = cron - os: linux - dist: bionic - go: 1.17.x - env: - - azure-purge - - GO111MODULE=on - git: - submodules: false # avoid cloning ethereum/tests - script: - - go run build/ci.go purge -store gethstore/builds -days 14
diff --git go-ethereum/Dockerfile celo/Dockerfile index 7badbc1320a4ea4f32ede9a852b9c771d186c5e0..88e2e21a696d684f994e865232e2cabb8ed53e8c 100644 --- go-ethereum/Dockerfile +++ celo/Dockerfile @@ -1,24 +1,38 @@ -# Support setting various labels on the final image -ARG COMMIT="" -ARG VERSION="" -ARG BUILDNUM="" +# How to test changes to this file +# docker build -f Dockerfile -t gcr.io/celo-testnet/geth:$USER . +# To locally run that image +# docker rm geth_container ; docker run --name geth_container gcr.io/celo-testnet/geth:$USER +# and connect to it with +# docker exec -t -i geth_container /bin/sh +# +# Once you are satisfied, build the image using +# export COMMIT_SHA=$(git rev-parse HEAD) +# docker build -f Dockerfile --build-arg COMMIT_SHA=$COMMIT_SHA -t gcr.io/celo-testnet/geth:$COMMIT_SHA . +# +# push the image to the cloud +# docker push gcr.io/celo-testnet/geth:$COMMIT_SHA +# +# To use this image for testing, modify GETH_NODE_DOCKER_IMAGE_TAG in celo-monorepo/.env file   # Build Geth in a stock Go builder container FROM golang:1.17-alpine as builder   -RUN apk add --no-cache gcc musl-dev linux-headers git +RUN apk add --no-cache make gcc musl-dev linux-headers git   ADD . /go-ethereum -RUN cd /go-ethereum && go run build/ci.go install ./cmd/geth +RUN cd /go-ethereum && make geth-musl   # Pull Geth into a second stage deploy alpine container FROM alpine:latest +ARG COMMIT_SHA   RUN apk add --no-cache ca-certificates COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/ +RUN echo $COMMIT_SHA > /version.txt +ADD scripts/run_geth_in_docker.sh /   EXPOSE 8545 8546 30303 30303/udp -ENTRYPOINT ["geth"] +ENTRYPOINT ["sh", "/run_geth_in_docker.sh"]   # Add some metadata labels to help programatic image consumption ARG COMMIT=""
diff --git go-ethereum/Dockerfile.alltools celo/Dockerfile.alltools index 3ae5377e4f3669449b662cb1a8314f095b8823d3..dd31bbf833750afad1887678260a95ca205c50a0 100644 --- go-ethereum/Dockerfile.alltools +++ celo/Dockerfile.alltools @@ -1,21 +1,18 @@ -# Support setting various labels on the final image -ARG COMMIT="" -ARG VERSION="" -ARG BUILDNUM="" - # Build Geth in a stock Go builder container FROM golang:1.17-alpine as builder   -RUN apk add --no-cache gcc musl-dev linux-headers git +RUN apk add --no-cache make gcc musl-dev linux-headers git   ADD . /go-ethereum -RUN cd /go-ethereum && go run build/ci.go install +RUN cd /go-ethereum && make all-musl   # Pull all binaries into a second stage deploy alpine container FROM alpine:latest +ARG COMMIT_SHA   RUN apk add --no-cache ca-certificates COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/ +RUN echo $COMMIT_SHA > /version.txt   EXPOSE 8545 8546 30303 30303/udp
diff --git go-ethereum/Dockerfile.android celo/Dockerfile.android new file mode 100644 index 0000000000000000000000000000000000000000..3a10e0f3e85f361f370aca0abe7ea625326c5790 --- /dev/null +++ celo/Dockerfile.android @@ -0,0 +1,111 @@ +# celohq/circleci:android-v3 +# Note: Please build and push this image this image as +# us.gcr.io/celo-testnet/android:vx after updating the dockerfile. +# It is not automatically built. + +FROM circleci/android:api-28 + +# NDK see also: https://github.com/CircleCI-Public/circleci-dockerfiles/blob/cb8bda793023d3e919ea5553e2f2c04b71f53c49/android/images/api-28-ndk/Dockerfile#L181 + +ARG ndk_version=android-ndk-r19c +ARG android_ndk_home=/opt/android/${ndk_version} + +# Install NDK +RUN curl --silent --show-error --location --fail --retry 3 --output /tmp/${ndk_version}.zip \ + https://dl.google.com/android/repository/${ndk_version}-linux-x86_64.zip && \ + sudo unzip -q /tmp/${ndk_version}.zip -d /opt/android && \ + rm /tmp/${ndk_version}.zip && \ + sudo chown -R circleci:circleci ${android_ndk_home} + +ENV ANDROID_NDK_HOME ${android_ndk_home} +ENV ANDROID_NDK ${android_ndk_home} + +# Install rust + +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.41.0 +ENV PATH=$PATH:$HOME/.cargo/bin +RUN rustup target add aarch64-linux-android +RUN rustup target add armv7-linux-androideabi +RUN rustup target add i686-linux-android +RUN rustup target add x86_64-linux-android + +# go and node installations command expect to run as root +USER root + +# Install go. See instructions at top when updating the go version. +RUN curl https://dl.google.com/go/go1.16.4.linux-amd64.tar.gz | tar -xz +ENV PATH=/go/bin:$PATH +ENV GOROOT=/go +ENV GOPATH=$HOME/go + +## Using node installation from https://raw.githubusercontent.com/nodejs/docker-node/dc340d0bf2119dee534106ef012e85861cda8b84/12/stretch/Dockerfile + +RUN groupadd --gid 1000 node \ + && useradd --uid 1000 --gid node --shell /bin/bash --create-home node + +ENV NODE_VERSION 12.22.6 + +RUN ARCH= && dpkgArch="$(dpkg --print-architecture)" \ + && case "${dpkgArch##*-}" in \ + amd64) ARCH='x64';; \ + ppc64el) ARCH='ppc64le';; \ + s390x) ARCH='s390x';; \ + arm64) ARCH='arm64';; \ + armhf) ARCH='armv7l';; \ + i386) ARCH='x86';; \ + *) echo "unsupported architecture"; exit 1 ;; \ + esac \ + # gpg keys listed at https://github.com/nodejs/node#release-keys + && set -ex \ + && for key in \ + 4ED778F539E3634C779C87C6D7062848A1AB005C \ + 94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \ + 74F12602B6F1C4E913FAA37AD3A89613643B6201 \ + 71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \ + 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \ + C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \ + C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C \ + DD8F2338BAE7501E3DD5AC78C273792F7D83545D \ + A48C2BEE680E841632CD4E44F07496B3EB3C1762 \ + 108F52B48DB57BB0CC439B2997B01419BD92F80A \ + B9E2F5981AA6E0CD28160D9FF13993A75599653C \ + ; do \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" || \ + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" ; \ + done \ + && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.xz" \ + && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \ + && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ + && grep " node-v$NODE_VERSION-linux-$ARCH.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ + && tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \ + && rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \ + && ln -s /usr/local/bin/node /usr/local/bin/nodejs \ + # smoke tests + && node --version \ + && npm --version + +ENV YARN_VERSION 1.22.5 + +RUN set -ex \ + && for key in \ + 6A010C5166006599AA17F08146C2130DFD2497F5 \ + ; do \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" || \ + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" ; \ + done \ + && curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \ + && curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz.asc" \ + && gpg --batch --verify yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \ + && mkdir -p /opt \ + && tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \ + && ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \ + && ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \ + && rm yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \ + # smoke test + && yarn --version + + +# Basic smoke test +RUN node --version + +USER circleci
diff --git go-ethereum/Dockerfile.arm64 celo/Dockerfile.arm64 new file mode 100644 index 0000000000000000000000000000000000000000..3cb8ba678c2711411ec187acfac043219fcae76a --- /dev/null +++ celo/Dockerfile.arm64 @@ -0,0 +1,20 @@ +# celohq/android-client + +FROM circleci/rust:1.41.0-buster + +USER root + +RUN apt update && \ + apt-get install gcc-aarch64-linux-gnu && \ + rm -rf /var/lib/apt/lists/* + +RUN rustup target add aarch64-unknown-linux-gnu +RUN wget https://dl.google.com/go/go1.16.4.linux-amd64.tar.gz && \ + tar xf go1.16.4.linux-amd64.tar.gz -C /usr/local + +COPY . /go-ethereum +WORKDIR /go-ethereum + +ENV PATH $PATH:/usr/local/go/bin + +CMD /bin/bash
diff --git go-ethereum/Dockerfile.binaries celo/Dockerfile.binaries new file mode 100644 index 0000000000000000000000000000000000000000..96e1c3fdab526a128c5c8abb3af29b5565b31d3d --- /dev/null +++ celo/Dockerfile.binaries @@ -0,0 +1,56 @@ +# Purpose: +# ------- +# This docker image is used by the ./cloudbuild-binaries.yaml CI flow to +# cross-compile geth for different platforms. +# At the time of writing this is only linux-{386/amd64} but there are two +# pending update that will add darwin-{386,amd64} and windows-amd64 support. +# +# How to test changes to this image locally: +# ----------------------------------------- +# First build the image: +# docker build -f Dockerfile.binaries -t gcr.io/celo-testnet/geth-xgo-builder:$USER . +# +# Running this image depends on a series of environment variables: +# BUILD_TARGETS Comma separated list of platforms to build for, +# passed as an arg to xgo. eg: "linux/386,linux/amd64" +# TAG_NAME Name of the tag associated with the current commit +# BRANCH_NAME Name of the branch +# REPO_NAME Name of the repo +# COMMIT_SHA Full SHA of the commit +# COMMIT_TIMESTAMP Commit timestamp +# TODO: currently this is not accurately passed see +# discussion in PR celo-blockchain#<number> +# CLOUDBUILD True/False +# CI True/False +# These two are used to comply with how geth handles build +# environments internal/build/env.go where we have added a +# branch for Cloudbuild +# You also need to mount: +# $(pwd)/build/bin:/build - where binaries will be written +# $(pwd)/build/archives:/archives - where the archives (final release artfeact) will be written +# $(pwd):/go/src/github.com/ethereum/go-ethereum - the source code +# +# With all of the above in place the container needs to execute two commands: +# (see ./cloudbuild-binaries.yaml for example of the full command) +# 1. Create the binaries: +# go run build/ci.go xgo --alltools -- -targets=$BUILD_TARGETS -v -dest /build +# 2. Create release archives: +# go run build/ci.go xgo-archive -targets=$_BUILD_TARGETS -in /build -out /archives +# +# This will result in build archives stored in ./build/archives +# In the CI flow these are then uploaded to cloud storage as artefacts. + +# Build Geth binaries in the xgo builder container +FROM techknowlogick/xgo:go-1.16.x +# techknowlogic/xgo is a fork of karalabe/xgo updated to ubunut-18, it is more maintained +# by the community and allows us to backport mingw in order to build for windows +# See discussion in PR celo-blockchain#<number> about downsides of this image + +# We need a newer version of mingw, backported to Bionic +ENV DEBIAN_FRONTEND=noninteractive +RUN apt update && apt install -y --no-install-recommends software-properties-common apt-utils +RUN add-apt-repository -y ppa:mati865/mingw-w64 +RUN apt update && apt -y upgrade + +RUN mkdir -p /go/src/github.com/ethereum/go-ethereum +WORKDIR /go/src/github.com/ethereum/go-ethereum
diff --git go-ethereum/Dockerfile.celo-node celo/Dockerfile.celo-node new file mode 100644 index 0000000000000000000000000000000000000000..ef5add83959a0570c535d91503578883b334dea8 --- /dev/null +++ celo/Dockerfile.celo-node @@ -0,0 +1,25 @@ +# Build a docker image for users to run full nodes/validators +# * celo_env arg is used to pre-download genesis block and static nodes +# * geth_label arg is used specify which geth image to build from +# +# CELO_NETWORK=baklavastaging +# COMMIT_HASH=$(git rev-parse HEAD) +# docker build . -f Dockerfile.celo-node --build-arg celo_env=$CELO_NETWORK --build-arg geth_label=$COMMIT_HASH -t us.gcr.io/celo-testnet/celo-node:$CELO_NETWORK -t us.gcr.io/celo-testnet/celo-node:$COMMIT_HASH +# docker push us.gcr.io/celo-testnet/celo-node:$CELO_NETWORK +# docker push us.gcr.io/celo-testnet/celo-node:$COMMIT_HASH + +ARG geth_label + +FROM us.gcr.io/celo-testnet/geth:${geth_label} + +ARG celo_env + +RUN apk add curl + +RUN mkdir /celo + +RUN curl https://www.googleapis.com/storage/v1/b/genesis_blocks/o/${celo_env}?alt=media > /celo/genesis.json + +RUN curl https://www.googleapis.com/storage/v1/b/static_nodes/o/${celo_env}?alt=media > /celo/static-nodes.json + +RUN curl https://www.googleapis.com/storage/v1/b/env_bootnodes/o/${celo_env}?alt=media > /celo/bootnodes
diff --git go-ethereum/Dockerfile.mimalloc celo/Dockerfile.mimalloc new file mode 100644 index 0000000000000000000000000000000000000000..e5c7c4669a55530b2cd90aafdaaf49f8e92dc329 --- /dev/null +++ celo/Dockerfile.mimalloc @@ -0,0 +1,57 @@ +# How to test changes to this file +# docker build -f Dockerfile -t gcr.io/celo-testnet/geth:$USER . +# To locally run that image +# docker rm geth_container ; docker run --name geth_container gcr.io/celo-testnet/geth:$USER +# and connect to it with +# docker exec -t -i geth_container /bin/sh +# +# Once you are satisfied, build the image using +# export COMMIT_SHA=$(git rev-parse HEAD) +# docker build -f Dockerfile --build-arg COMMIT_SHA=$COMMIT_SHA -t gcr.io/celo-testnet/geth:$COMMIT_SHA . +# +# push the image to the cloud +# docker push gcr.io/celo-testnet/geth:$COMMIT_SHA +# +# To use this image for testing, modify GETH_NODE_DOCKER_IMAGE_TAG in celo-monorepo/.env file + +# Build Geth in a stock Go builder container +FROM golang:1.16-alpine as builder + +RUN apk add --no-cache make gcc musl-dev linux-headers git build-base cmake + +RUN cd / && \ + git clone -b v2.0.3 --depth 1 https://github.com/microsoft/mimalloc && \ + cd mimalloc && \ + mkdir build && \ + cd build && \ + cmake .. && \ + make -j$(nproc) && \ + make install + +ADD . /go-ethereum +RUN cd /go-ethereum && make geth-musl + +# Pull Geth into a second stage deploy alpine container +FROM alpine:latest +ARG COMMIT_SHA + +# Copy over compiled libmimalloc +COPY --from=builder /mimalloc/build/*.so.* /lib +RUN ln -s /lib/libmimalloc.so.* /lib/libmimalloc.so +ENV LD_PRELOAD=/lib/libmimalloc.so +ENV MIMALLOC_LARGE_OS_PAGES=1 + +RUN apk add --no-cache ca-certificates +COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/ +RUN echo $COMMIT_SHA > /version.txt +ADD scripts/run_geth_in_docker.sh / + +EXPOSE 8545 8546 30303 30303/udp +ENTRYPOINT ["sh", "/run_geth_in_docker.sh"] + +# Add some metadata labels to help programatic image consumption +ARG COMMIT="" +ARG VERSION="" +ARG BUILDNUM="" + +LABEL commit="$COMMIT" version="$VERSION" buildnum="$BUILDNUM" \ No newline at end of file
diff --git go-ethereum/Makefile celo/Makefile index cb5a87dad0eea112af6489ebfb3d7e4e55df20c9..b975a53a675f9973e0bd27c77182afcb687d7a51 100644 --- go-ethereum/Makefile +++ celo/Makefile @@ -5,42 +5,177 @@ .PHONY: geth android ios geth-cross evm all test clean .PHONY: geth-linux geth-linux-386 geth-linux-amd64 geth-linux-mips64 geth-linux-mips64le .PHONY: geth-linux-arm geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64 -.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64 +.PHONY: geth-darwin geth-darwin-amd64 .PHONY: geth-windows geth-windows-386 geth-windows-amd64 +.PHONY: prepare prepare-system-contracts prepare-ethersjs-project   GOBIN = ./build/bin GO ?= latest GORUN = env GO111MODULE=on go run   +LSB_exists := $(shell command -v lsb_release 2> /dev/null) + +OS := +ifeq ("$(LSB_exists)","") + OS = darwin +else + OS = linux +endif + +# We checkout the monorepo as a sibling to the celo-blockchain dir because the +# huge amount of files in the monorepo interferes with tooling such as gopls, +# which becomes very slow. +MONOREPO_PATH=../.celo-blockchain-monorepo-checkouts/$(shell cat monorepo_commit) + +# This either evaluates to the contract source files if they exist or NOT_FOUND +# if celo-monorepo has not been checked out yet. +CONTRACT_SOURCE_FILES=$(shell 2>/dev/null find $(MONOREPO_PATH)/packages/protocol \ + -not -path "*/node_modules*" \ + -not -path "$(MONOREPO_PATH)/packages/protocol/test*" \ + -not -path "$(MONOREPO_PATH)/packages/protocol/build*" \ + -not -path "$(MONOREPO_PATH)/packages/protocol/types*" \ + || echo "NOT_FOUND") + +# example NDK values +export NDK_VERSION ?= android-ndk-r19c +export ANDROID_NDK ?= $(PWD)/ndk_bundle/$(NDK_VERSION) + geth: $(GORUN) build/ci.go install ./cmd/geth @echo "Done building." @echo "Run \"$(GOBIN)/geth\" to launch geth."   +prepare: prepare-system-contracts prepare-ethersjs-project + +prepare-ethersjs-project: ./e2e_test/ethersjs-api-check/node_modules + +./e2e_test/ethersjs-api-check/node_modules: ./e2e_test/ethersjs-api-check/package.json ./e2e_test/ethersjs-api-check/package-lock.json + @cd ./e2e_test/ethersjs-api-check && npm ci + +# This rule checks out celo-monorepo under MONOREPO_PATH at the commit contained in +# monorepo_commit and compiles the system solidity contracts. It then copies the +# compiled contracts from the monorepo to the compiled-system-contracts, so +# that this repo can always access the contracts at a consistent path. +prepare-system-contracts: $(MONOREPO_PATH)/packages/protocol/build + @rm -rf compiled-system-contracts + @cp -a $(MONOREPO_PATH)/packages/protocol/build/contracts compiled-system-contracts + +# If any of the source files in CONTRACT_SOURCE_FILES are more recent than the +# build dir or the build dir does not exist then we remove the build dir, yarn +# install and rebuild the contracts. +$(MONOREPO_PATH)/packages/protocol/build: $(CONTRACT_SOURCE_FILES) + @node --version | grep "^v12" || (echo "node v12 is required to build the monorepo (nvm use 12)" && exit 1) + @echo Running yarn install and compiling contracts + @cd $(MONOREPO_PATH) && rm -rf packages/protocol/build && yarn && cd packages/protocol && yarn run build:sol + + +# This target serves as an intermediate step to avoid running the +# $(MONOREPO_PATH) target once per contract source file. This could also be +# achieved by using the group targets separator '&:' instead of just ':', but +# that functionality was added in make version 4.3 which doesn't seem to be +# readily available on most systems yet. So although this rule will be run once +# for each source file, since it is empty that is very quick. $(MONOREPO_PATH) +# as a prerequisite of this will be run at most once. +$(CONTRACT_SOURCE_FILES): $(MONOREPO_PATH) + +# Clone the monorepo at the commit in the file `monorepo_commit`. +# +# The checkouts are kept separate by commit to make switching between commits. Use `make clean-old-monorepos` to remove all checkouts but the once currently indicated by `monorepo_commit`. +$(MONOREPO_PATH): monorepo_commit + @set -e; \ + mc=`cat monorepo_commit`; \ + echo "monorepo_commit is $${mc}"; \ + if git ls-remote --heads --exit-code git@github.com:celo-org/celo-monorepo.git $${mc} > /dev/null 2>&1; \ + then \ + echo "Expected commit hash or tag in 'monorepo_commit' instead found branch name '$${mc}'"; \ + exit 1; \ + fi; \ + if [ ! -e $(MONOREPO_PATH) ]; \ + then \ + echo "Cloning monorepo at $${mc}"; \ + mkdir -p $(MONOREPO_PATH) && cd $(MONOREPO_PATH); \ + git init; \ + git remote add origin https://github.com/celo-org/celo-monorepo.git; \ + git fetch --quiet --depth 1 origin $${mc}; \ + git checkout FETCH_HEAD; \ + fi + + +clean-old-monorepos: + @all_repos_dir=$$(realpath $(MONOREPO_PATH)/..); \ + delete_dirs=$$(ls -d -1 $${all_repos_dir}/*/ | grep -v $$(cat monorepo_commit)); \ + echo Deleting $$delete_dirs; \ + rm -fr $$delete_dirs + + +geth-musl: + $(GORUN) build/ci.go install -musl ./cmd/geth + @echo "Done building with musl." + @echo "Run \"$(GOBIN)/geth\" to launch geth." + +check_android_env: + @test $${ANDROID_NDK?Please set environment variable ANDROID_NDK} + @test $${ANDROID_HOME?Please set environment variable ANDROID_HOME} + +ndk_bundle: check_android_env +ifeq ("$(wildcard $(ANDROID_NDK))","") + @test $${NDK_VERSION?Please set environment variable NDK_VERSION} + curl --silent --show-error --location --fail --retry 3 --output /tmp/$(NDK_VERSION).zip \ + https://dl.google.com/android/repository/$(NDK_VERSION)-$(OS)-x86_64.zip && \ + rm -rf $(ANDROID_NDK) && \ + mkdir -p $(ANDROID_NDK) && \ + unzip -q /tmp/$(NDK_VERSION).zip -d $(ANDROID_NDK)/.. && \ + rm /tmp/$(NDK_VERSION).zip +else +ifeq ("$(wildcard $(ANDROID_NDK)/toolchains/llvm/prebuilt/$(OS)-x86_64)","") + $(error "Android NDK is installed but doesn't contain an llvm cross-compilation toolchain. Delete your current NDK or modify the ANDROID_NDK environment variable to an empty directory download it automatically.") +endif +endif + +swarm: + $(GORUN) build/ci.go install ./cmd/swarm + @echo "Done building." + @echo "Run \"$(GOBIN)/swarm\" to launch swarm." + all: $(GORUN) build/ci.go install   +all-musl: + $(GORUN) build/ci.go install -musl + android: - $(GORUN) build/ci.go aar --local + @echo "Applying patch for mobile libs..." + git apply patches/mobileLibsForBuild.patch + ANDROID_NDK_HOME=$(ANDROID_NDK) $(GORUN) build/ci.go aar --local --metrics-default @echo "Done building." @echo "Import \"$(GOBIN)/geth.aar\" to use the library." @echo "Import \"$(GOBIN)/geth-sources.jar\" to add javadocs" @echo "For more info see https://stackoverflow.com/questions/20994336/android-studio-how-to-attach-javadoc" + @echo "Remove patch for mobile libs..." + git apply -R patches/mobileLibsForBuild.patch +   ios: - $(GORUN) build/ci.go xcode --local + CGO_ENABLED=1 DISABLE_BITCODE=true $(GORUN) build/ci.go xcode --local --metrics-default + pushd "$(GOBIN)"; rm -rf Geth.framework.tgz; tar -czvf Geth.framework.tgz Geth.framework; popd + # Geth.framework is a static framework, so we have to also keep the other static libs it depends on + # in order to link it to the final app + # One day gomobile will probably support xcframework which would solve this ;-) + cp -f "$$(go list -m -f "{{ .Dir }}" github.com/celo-org/celo-bls-go-ios)/libs/universal/libbls_snark_sys.a" . @echo "Done building." @echo "Import \"$(GOBIN)/Geth.framework\" to use the library."   test: all - $(GORUN) build/ci.go test + $(GORUN) build/ci.go test $(TEST_FLAGS)   lint: ## Run linters. $(GORUN) build/ci.go lint   -clean: +clean-geth: env GO111MODULE=on go clean -cache rm -fr build/_workspace/pkg/ $(GOBIN)/* + +clean: clean-geth   # The devtools target installs tools required for 'go generate'. # You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'. @@ -79,6 +214,7 @@ @echo "Linux ARM cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep arm   geth-linux-arm-5: + # requires an arm compiler, on Ubuntu: sudo apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-5 -v ./cmd/geth @echo "Linux ARMv5 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep arm-5 @@ -89,45 +225,48 @@ @echo "Linux ARMv6 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep arm-6   geth-linux-arm-7: - $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-7 -v ./cmd/geth + # requires an arm compiler, on Ubuntu: sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf + $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-7 -v --tags arm7 ./cmd/geth @echo "Linux ARMv7 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep arm-7   geth-linux-arm64: + # requires an arm64 compiler, on Ubuntu: sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm64 -v ./cmd/geth @echo "Linux ARM64 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep arm64   geth-linux-mips: + # requires a mips compiler, on Ubuntu: sudo apt-get install gcc-mips-linux-gnu $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips --ldflags '-extldflags "-static"' -v ./cmd/geth @echo "Linux MIPS cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep mips   geth-linux-mipsle: + # requires a mips compiler, on Ubuntu: sudo apt-get install gcc-mipsel-linux-gnu $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mipsle --ldflags '-extldflags "-static"' -v ./cmd/geth @echo "Linux MIPSle cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep mipsle   geth-linux-mips64: + # requires a mips compiler, on Ubuntu: sudo apt-get install gcc-mips64-linux-gnuabi64 $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips64 --ldflags '-extldflags "-static"' -v ./cmd/geth @echo "Linux MIPS64 cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep mips64   geth-linux-mips64le: + # requires a mips compiler, on Ubuntu: sudo apt-get install gcc-mips64el-linux-gnuabi64 $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips64le --ldflags '-extldflags "-static"' -v ./cmd/geth @echo "Linux MIPS64le cross compilation done:" @ls -ld $(GOBIN)/geth-linux-* | grep mips64le   -geth-darwin: geth-darwin-386 geth-darwin-amd64 +geth-darwin: geth-darwin-amd64 @echo "Darwin cross compilation done:" @ls -ld $(GOBIN)/geth-darwin-*   -geth-darwin-386: - $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=darwin/386 -v ./cmd/geth - @echo "Darwin 386 cross compilation done:" - @ls -ld $(GOBIN)/geth-darwin-* | grep 386 - geth-darwin-amd64: + # needs include files for asm errno, on Ubuntu: sudo apt-get install linux-libc-dev + # currently doesn't compile on Ubuntu $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=darwin/amd64 -v ./cmd/geth @echo "Darwin amd64 cross compilation done:" @ls -ld $(GOBIN)/geth-darwin-* | grep amd64 @@ -137,11 +276,13 @@ @echo "Windows cross compilation done:" @ls -ld $(GOBIN)/geth-windows-*   geth-windows-386: + # currently doesn't compile on Ubuntu, missing libunwind in xgo $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=windows/386 -v ./cmd/geth @echo "Windows 386 cross compilation done:" @ls -ld $(GOBIN)/geth-windows-* | grep 386   geth-windows-amd64: + # currently doesn't compile on Ubuntu, missing libunwind in xgo $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=windows/amd64 -v ./cmd/geth @echo "Windows amd64 cross compilation done:" @ls -ld $(GOBIN)/geth-windows-* | grep amd64
diff --git go-ethereum/README.md celo/README.md index 86f7713fd9166fe1cafa3527c99f18dc65acc289..2ec00f361ccc7dbf609899d4cabbe5a5910e32cf 100644 --- go-ethereum/README.md +++ celo/README.md @@ -1,23 +1,21 @@ -## Go Ethereum +## Celo Blockchain   -Official Golang implementation of the Ethereum protocol. +Official golang implementation of the Celo blockchain, based off of the [official golang implementation of the Ethereum protocol](https://github.com/ethereum/go-ethereum).   -[![API Reference]( -https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667 -)](https://pkg.go.dev/github.com/ethereum/go-ethereum?tab=doc) -[![Go Report Card](https://goreportcard.com/badge/github.com/ethereum/go-ethereum)](https://goreportcard.com/report/github.com/ethereum/go-ethereum) -[![Travis](https://travis-ci.com/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.com/ethereum/go-ethereum) -[![Discord](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://discord.gg/nthXNEv) +[![CircleCI](https://img.shields.io/circleci/build/github/celo-org/celo-blockchain/master)](https://circleci.com/gh/celo-org/celo-blockchain/tree/master) +[![Codecov](https://img.shields.io/codecov/c/github/celo-org/celo-blockchain)](https://codecov.io/gh/celo-org/celo-blockchain) +[![Discord](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://chat.celo.org)   -Automated builds are available for stable releases and the unstable master branch. Binary -archives are published at https://geth.ethereum.org/downloads/. +Prebuilt [Docker](https://en.wikipedia.org/wiki/Docker_\(software\)) images are available for immediate use: [us.gcr.io/celo-org/geth](https://us.gcr.io/celo-org/geth). See [docs.celo.org/getting-started](https://docs.celo.org/getting-started/choosing-a-network) for a guide to the Celo networks and how to get started.   -## Building the source +Documentation for Celo more generally can be found at [docs.celo.org](https://docs.celo.org/)   -For prerequisites and detailed build instructions please read the [Installation Instructions](https://geth.ethereum.org/docs/install-and-build/installing-geth). +Most functionality of this client is similar to `go-ethereum`, also known as `geth`, from which it was forked. If you do not find your question answered by Celo-specific documentation, try searching the [geth wiki](https://github.com/ethereum/go-ethereum/wiki).   -Building `geth` requires both a Go (version 1.14 or later) and a C compiler. You can install -them using your favourite package manager. Once the dependencies are installed, run +## Building the source + +Building `geth` requires both Go (min version 1.15) and a C compiler. +You can install them using your favourite package manager. Once the dependencies are installed, run   ```shell make geth @@ -29,151 +27,145 @@ ```shell make all ```   +### Mobile Clients + +There are two different commands in the `Makefile` to build the `ios` and the `android` clients. + +```shell +make ios +``` + +and + +```shell +make android +``` + +Note: The `android` command it applies a git patch (`patches/mobileLibsForBuild.patch`) required to swap some libs from the `go.mod` for the client to work, installs those libs, builds the client, and then reverts the patch. + ## Executables   -The go-ethereum project comes with several wrappers/executables found in the `cmd` -directory. +The Celo blockchain client comes with several wrappers/executables found in the `cmd` directory.   -| Command | Description | -| :-----------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI page](https://geth.ethereum.org/docs/interface/command-line-options) for command line options. | -| `clef` | Stand-alone signing tool, which can be used as a backend signer for `geth`. | -| `devp2p` | Utilities to interact with nodes on the networking layer, without running a full blockchain. | -| `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://geth.ethereum.org/docs/dapp/native-bindings) page for details. | -| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. | -| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). | -| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://eth.wiki/en/fundamentals/rlp)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). | -| `puppeth` | a CLI wizard that aids in creating a new Ethereum network. | +| Command | Description | +|:----------:|-------------| +| **`geth`** | The main Celo Blockchain client. It is the entry point into the Celo network, capable of running as a full node (default), archive node (retaining all historical state), light node (retrieving data live), or lightest node (retrieving minimum number of block headers to verify existing validator set). It can be used by other processes as a gateway into the Celo network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [Ethereum CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) for command line options. | +| `abigen` | Source code generator to convert Celo contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) with expanded functionality if the contract bytecode is also available. However it also accepts Solidity source files, making development much more streamlined. Please see [Ethereum's Native DApps](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts) wiki page for details. | +| `bootnode` | Stripped down version of the Celo client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. | +| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). | +| `gethrpctest` | Developer utility tool to support the [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [ethereum test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. | +| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Celo protocol both network as well as consensus wise) to user friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |   -## Running `geth` +## Running tests   -Going through all the possible command line flags is out of scope here (please consult our -[CLI Wiki page](https://geth.ethereum.org/docs/interface/command-line-options)), -but we've enumerated a few common parameter combos to get you up to speed quickly -on how you can run your own `geth` instance. +Prior to running tests you will need to run `make prepare`, this will run two sub rules.   -### Full node on the main Ethereum network +Without first running this `make prepare`, certain tests will fail.   -By far the most common scenario is people wanting to simply interact with the Ethereum -network: create accounts; transfer funds; deploy and interact with contracts. For this -particular use-case the user doesn't care about years-old historical data, so we can -fast-sync quickly to the current state of the network. To do so: +### prepare-system-contracts + +This will shallow checkout the +[celo-monorepo](https://github.com/celo-org/celo-monorepo) under +`../.celo-blockchain-monorepo-checkout` relative to this project's root at the +commit defined in the file `monorepo_commit`. Then it will compile the system +contracts for use in full network tests. The rule will copy the compiled +contracts from celo-monorepo to `compiled-system-contracts`. If you +subsequently edit the system contracts source or `monorepo_commit`, running the +make rule again will re-checkout the monorepo, re-compile the contracts and +copy them into place. + +`monorepo_commit` may contain a commit hash or a tag, branch names are +forbidden. + +In the case that you would like to change the default monorepo checkout +location, or that you would like to have multiple checkouts of the monorepo (at +different versions) you can set the `MONOREPO_PATH` variable in the make +command, for example: + +``` +make prepare-system-contracts MONOREPO_PATH=../alt-monorepo + +``` +Note that `MONOREPO_PATH` should not be set to point at checkouts other than +those checked out by the `prepare-system-contracts` rule, and the checkouts +created by the `prepare-system-contracts` rule should not be manually modified, +aside from changing the contract source. + +### prepare-ethersjs-project +This will install dependencies for the `ethersjs-api-check` typescript project. + +## Running Celo + +Please see the [docs.celo.org/getting-started](https://docs.celo.org/getting-started/choosing-a-network) for instructions on how to run a node connected to the Celo network using the prebuilt Docker image. + +Going through all the possible command line flags is out of scope here, please consult `geth --help` for more complete information. +We've enumerated a few common parameter combos to get you up to speed quickly on how you can run your own Celo blockchain client instance. + +### Full node on the main Celo network + +By default, the Celo client will connect to the Mainnet. +Running the following command will create a full node that will sync with the Celo network and allow access to all of its functionality.   ```shell $ geth console ```   This command will: - * Start `geth` in snap sync mode (default, can be changed with the `--syncmode` flag), - causing it to download more data in exchange for avoiding processing the entire history - of the Ethereum network, which is very CPU intensive. - * Start up `geth`'s built-in interactive [JavaScript console](https://geth.ethereum.org/docs/interface/javascript-console), - (via the trailing `console` subcommand) through which you can interact using [`web3` methods](https://web3js.readthedocs.io/en/) - (note: the `web3` version bundled within `geth` is very old, and not up to date with official docs), - as well as `geth`'s own [management APIs](https://geth.ethereum.org/docs/rpc/server). + * Start `geth` in full sync mode which will download and execute all historical block information. + * Start up `geth`'s built-in interactive [JavaScript console](https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console), + (via the trailing `console` subcommand) through which you can invoke all official [`web3` methods](https://github.com/ethereum/wiki/wiki/JavaScript-API) + as well as `geth`'s own [management APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs). This tool is optional and if you leave it out you can always attach to an already running `geth` instance with `geth attach`.   -### A Full node on the Görli test network +### A Full node on the Alfajores test network   -Transitioning towards developers, if you'd like to play around with creating Ethereum -contracts, you almost certainly would like to do that without any real money involved until -you get the hang of the entire system. In other words, instead of attaching to the main -network, you want to join the **test** network with your node, which is fully equivalent to -the main network, but with play-Ether only. +Smart contract developers will be most interested in the Alfajores testnet. +On Alfajores, you can receive testnet Celo through the [Alfajores faucet](https://celo.org/developers/faucet) and deploy smart contracts in an environment very similar to Mainnet. +More information about the Alfajores testnet can be found on [docs.celo.org](https://docs.celo.org/getting-started/alfajores-testnet).   ```shell -$ geth --goerli console +$ geth --alfajores console ```   -The `console` subcommand has the exact same meaning as above and they are equally -useful on the testnet too. Please, see above for their explanations if you've skipped here. - -Specifying the `--goerli` flag, however, will reconfigure your `geth` instance a bit: - - * Instead of connecting the main Ethereum network, the client will connect to the Görli - test network, which uses different P2P bootnodes, different network IDs and genesis - states. - * Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth` - will nest itself one level deeper into a `goerli` subfolder (`~/.ethereum/goerli` on - Linux). Note, on OSX and Linux this also means that attaching to a running testnet node - requires the use of a custom endpoint since `geth attach` will try to attach to a - production node endpoint by default, e.g., - `geth attach <datadir>/goerli/geth.ipc`. Windows users are not affected by - this. - *Note: Although there are some internal protective measures to prevent transactions from crossing over between the main network and test network, you should make sure to always -use separate accounts for play-money and real-money. Unless you manually move +use separate accounts for testnet-tokens and real-tokens. Unless you manually move accounts, `geth` will by default correctly separate the two networks and will not make any accounts available between them.*   -### Full node on the Rinkeby test network +### Full node on the Baklava test network   -Go Ethereum also supports connecting to the older proof-of-authority based test network -called [*Rinkeby*](https://www.rinkeby.io) which is operated by members of the community. - -```shell -$ geth --rinkeby console -``` - -### Full node on the Ropsten test network - -In addition to Görli and Rinkeby, Geth also supports the ancient Ropsten testnet. The -Ropsten test network is based on the Ethash proof-of-work consensus algorithm. As such, -it has certain extra overhead and is more susceptible to reorganization attacks due to the -network's low difficulty/security. +Validators and full node operators will be most interested in the Baklava testnet. +On Baklava, you can receive a distribution of testnet Celo Gold to become a validator on the network and test out running a validator for the first time, or try out new infrastructure. +More information about the Baklava testnet can be found on [docs.celo.org](https://docs.celo.org/getting-started/baklava-testnet). +A full guide to getting started as a validator on Baklava can be found in the [Getting Started guides](https://docs.celo.org/getting-started/baklava-testnet/running-a-validator-in-baklava)   ```shell -$ geth --ropsten console +$ geth --baklava console ```   -*Note: Older Geth configurations store the Ropsten database in the `testnet` subdirectory.* - ### Configuration   -As an alternative to passing the numerous flags to the `geth` binary, you can also pass a -configuration file via: +As an alternative to passing the numerous flags to the `Celo` binary, you can also pass a configuration file via:   ```shell $ geth --config /path/to/your_config.toml ```   -To get an idea how the file should look like you can use the `dumpconfig` subcommand to +To get an idea of how the file should look like you can use the `dumpconfig` subcommand to export your existing configuration:   ```shell $ geth --your-favourite-flags dumpconfig ```   -*Note: This works only with `geth` v1.6.0 and above.* - -#### Docker quick start - -One of the quickest ways to get Ethereum up and running on your machine is by using -Docker: - -```shell -docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \ - -p 8545:8545 -p 30303:30303 \ - ethereum/client-go -``` - -This will start `geth` in fast-sync mode with a DB memory allowance of 1GB just as the -above command does. It will also create a persistent volume in your home directory for -saving your blockchain as well as map the default ports. There is also an `alpine` tag -available for a slim version of the image. - -Do not forget `--http.addr 0.0.0.0`, if you want to access RPC from other containers -and/or hosts. By default, `geth` binds to the local interface and RPC endpoints is not -accessible from the outside. - ### Programmatically interfacing `geth` nodes   As a developer, sooner rather than later you'll want to start interacting with `geth` and the -Ethereum network via your own programs and not manually through the console. To aid -this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://eth.wiki/json-rpc/API) -and [`geth` specific APIs](https://geth.ethereum.org/docs/rpc/server)). +Celo network via your own programs and not manually through the console. To aid +this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://github.com/ethereum/wiki/wiki/JSON-RPC) +and [`geth` specific APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs)). These can be exposed via HTTP, WebSockets and IPC (UNIX sockets on UNIX based platforms, and named pipes on Windows).   @@ -193,7 +185,10 @@ * `--ws` Enable the WS-RPC server * `--ws.addr` WS-RPC server listening interface (default: `localhost`) * `--ws.port` WS-RPC server listening port (default: `8546`) * `--ws.api` API's offered over the WS-RPC interface (default: `eth,net,web3`) - * `--ws.origins` Origins from which to accept websockets requests + * `--ws.origins value` Origins from which to accept websockets requests + * `--graphql` Enable GraphQL on the HTTP-RPC server. Note that GraphQL can only be started if an HTTP server is started as well. + * `--graphql.corsdomain value` Comma separated list of domains from which to accept cross origin requests (browser enforced) + * `--graphql.vhosts value` Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost") * `--ipcdisable` Disable the IPC-RPC server * `--ipcapi` API's offered over the IPC-RPC interface (default: `admin,debug,eth,miner,net,personal,shh,txpool,web3`) * `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it) @@ -205,159 +200,77 @@ can reuse the same connection for multiple requests!   **Note: Please understand the security implications of opening up an HTTP/WS based transport before doing so! Hackers on the internet are actively trying to subvert -Ethereum nodes with exposed APIs! Further, all browser tabs can access locally +Celo nodes with exposed APIs! Further, all browser tabs can access locally running web servers, so malicious web pages could try to subvert locally available APIs!**   -### Operating a private network   -Maintaining your own private network is more involved as a lot of configurations taken for -granted in the official networks need to be manually set up. +## Contribution   -#### Defining the private genesis state +Thank you for considering to help out with the source code! We welcome contributions +from anyone on the internet, and are grateful for even the smallest of fixes!   -First, you'll need to create the genesis state of your networks, which all nodes need to be -aware of and agree upon. This consists of a small JSON file (e.g. call it `genesis.json`): +If you'd like to contribute to celo-blockchain, please fork, fix, commit and send a pull request +for the maintainers to review and merge into the main code base. If you wish to submit more +complex changes though, please check up with the core devs first on [the official Celo forum](https://forum.celo.org/c/protocol) +to ensure those changes are in line with the general philosophy of the project and/or get some +early feedback which can make both your efforts much lighter as well as our review and merge +procedures quick and simple.   -```json -{ - "config": { - "chainId": <arbitrary positive integer>, - "homesteadBlock": 0, - "eip150Block": 0, - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, - "constantinopleBlock": 0, - "petersburgBlock": 0, - "istanbulBlock": 0, - "berlinBlock": 0, - "londonBlock": 0 - }, - "alloc": {}, - "coinbase": "0x0000000000000000000000000000000000000000", - "difficulty": "0x20000", - "extraData": "", - "gasLimit": "0x2fefd8", - "nonce": "0x0000000000000042", - "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "timestamp": "0x00" -} -``` +Please make sure your contributions adhere to our coding guidelines:   -The above fields should be fine for most purposes, although we'd recommend changing -the `nonce` to some random value so you prevent unknown remote nodes from being able -to connect to you. If you'd like to pre-fund some accounts for easier testing, create -the accounts and populate the `alloc` field with their addresses. + * Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) + guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). + * Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) + guidelines. + * Pull requests need to be based on and opened against the `master` branch. + * Commit messages should be prefixed with the package(s) they modify. + * E.g. "eth, rpc: make trace configs optional"   -```json -"alloc": { - "0x0000000000000000000000000000000000000001": { - "balance": "111111111" - }, - "0x0000000000000000000000000000000000000002": { - "balance": "222222222" - } -} -``` +### Submitting an issue   -With the genesis state defined in the above JSON file, you'll need to initialize **every** -`geth` node with it prior to starting it up to ensure all blockchain parameters are correctly -set: +If you come across a bug, please open a [GitHub issue](https://github.com/ethereum/go-ethereum/issues/new) with information about your build and what happened.   -```shell -$ geth init path/to/genesis.json -``` +### CI Testing and automerge   -#### Creating the rendezvous point +We run a circle CI test suite on each PR. The following tests are required to +merge a PR. + * Unit tests: `make test` or `./build/env.sh go run build/ci.go test` + * Lint: `make lint` (Fix go format errors with `gofmt -s`) + * Build: `make` + * End to end sync and transfer tests + * Check imports: `./scripts/check_imports.sh`   -With all nodes that you want to run initialized to the desired genesis state, you'll need to -start a bootstrap node that others can use to find each other in your network and/or over -the internet. The clean way is to configure and run a dedicated bootnode: + `celo-blockchain` is based on `go-ethereum`, but the import path has been renamed from `github.com/ethereum/go-ethereum` to `github.com/ethereum/go-ethereum`. + Developers are encouraged to run `./scripts/setup_git_hooks.sh` to enable checking that import path has been changed to `celo-org` on `git merge` and `git commit`. + Imports can automatically be renamed with `./scripts/rename_imports.sh`.   -```shell -$ bootnode --genkey=boot.key -$ bootnode --nodekey=boot.key -```   -With the bootnode online, it will display an [`enode` URL](https://eth.wiki/en/fundamentals/enode-url-format) -that other nodes can use to connect to it and exchange peer information. Make sure to -replace the displayed IP address information (most probably `[::]`) with your externally -accessible IP to get the actual `enode` URL. +Individual package tests can be run with +`./build/env.sh go test github.com/ethereum/go-ethereum/$(PATH_TO_GO_PACKAGE)` +if you don't have `GOPATH` set-up.   -*Note: You could also use a full-fledged `geth` node as a bootnode, but it's the less -recommended way.*   -#### Starting up your member nodes +Once a PR is approved, adding on the `automerge` label will keep it up to date +and do a squash merge once all the required tests have passed.   -With the bootnode operational and externally reachable (you can try -`telnet <ip> <port>` to ensure it's indeed reachable), start every subsequent `geth` -node pointed to the bootnode for peer discovery via the `--bootnodes` flag. It will -probably also be desirable to keep the data directory of your private network separated, so -do also specify a custom `--datadir` flag. +### Benchmarking   -```shell -$ geth --datadir=path/to/custom/data/folder --bootnodes=<bootnode-enode-url-from-above> -``` +Golang has built-in support for running benchmarks with go tool +`go test -run=ThisIsNotATestName -bench=. ./$PACKAGE_NAME` will run all benchmarks in a package.   -*Note: Since your network will be completely cut off from the main and test networks, you'll -also need to configure a miner to process transactions and create new blocks for you.* +One note around running benchmarks is that `BenchmarkHandlePreprepare` is quite takes a while to run, particularly when testing with a larger number of validators. +Substituting `-bench=REGEX` for `-bench=.` will specify which tests to run. Adding `-cpuprofile=cpu.out` which can be visualized with `go tool pprof -html:8080 cpu.out` if `graphviz` is installed.   -#### Running a private miner +See the [go testing flags](https://golang.org/cmd/go/#hdr-Testing_flags) and [go docs](https://golang.org/pkg/testing/#hdr-Benchmarks) for more information on benchmarking.   -Mining on the public Ethereum network is a complex task as it's only feasible using GPUs, -requiring an OpenCL or CUDA enabled `ethminer` instance. For information on such a -setup, please consult the [EtherMining subreddit](https://www.reddit.com/r/EtherMining/) -and the [ethminer](https://github.com/ethereum-mining/ethminer) repository. - -In a private network setting, however a single CPU miner instance is more than enough for -practical purposes as it can produce a stable stream of blocks at the correct intervals -without needing heavy resources (consider running on a single thread, no need for multiple -ones either). To start a `geth` instance for mining, run it with all your usual flags, extended -by: - -```shell -$ geth <usual-flags> --mine --miner.threads=1 --miner.etherbase=0x0000000000000000000000000000000000000000 -``` - -Which will start mining blocks and transactions on a single CPU thread, crediting all -proceedings to the account specified by `--miner.etherbase`. You can further tune the mining -by changing the default gas limit blocks converge to (`--miner.targetgaslimit`) and the price -transactions are accepted at (`--miner.gasprice`). - -## Contribution - -Thank you for considering to help out with the source code! We welcome contributions -from anyone on the internet, and are grateful for even the smallest of fixes! - -If you'd like to contribute to go-ethereum, please fork, fix, commit and send a pull request -for the maintainers to review and merge into the main code base. If you wish to submit -more complex changes though, please check up with the core devs first on [our Discord Server](https://discord.gg/invite/nthXNEv) -to ensure those changes are in line with the general philosophy of the project and/or get -some early feedback which can make both your efforts much lighter as well as our review -and merge procedures quick and simple. - -Please make sure your contributions adhere to our coding guidelines: - - * Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) - guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). - * Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) - guidelines. - * Pull requests need to be based on and opened against the `master` branch. - * Commit messages should be prefixed with the package(s) they modify. - * E.g. "eth, rpc: make trace configs optional" - -Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/devguide) -for more details on configuring your environment, managing project dependencies, and -testing procedures.   ## License   -The go-ethereum library (i.e. all code outside of the `cmd` directory) is licensed under the -[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html), -also included in our repository in the `COPYING.LESSER` file. +The celo-blockchain library (i.e. all code outside of the `cmd` directory) is licensed under the +[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html), also +included in our repository in the `COPYING.LESSER` file.   -The go-ethereum binaries (i.e. all code inside of the `cmd` directory) is licensed under the -[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also -included in our repository in the `COPYING` file. +The celo-blockchain binaries (i.e. all code inside of the `cmd` directory) is licensed under the +[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also included +in our repository in the `COPYING` file.
diff --git go-ethereum/SECURITY.md celo/SECURITY.md index 635c0869fc93ae74c2e15e70dbefa585eeccdc72..8292174d663543c012101f3d71d8a8c1c087dbf0 100644 --- go-ethereum/SECURITY.md +++ celo/SECURITY.md @@ -1,120 +1,78 @@ -# Security Policy +# Security   -## Supported Versions +## Security Announcements + +Public announcements of new releases with security fixes and of disclosure of any vulnerabilities will be made in the Celo Forum's [Security Announcements](https://forum.celo.org/c/security-announcements/) channel.   -Please see [Releases](https://github.com/ethereum/go-ethereum/releases). We recommend using the [most recently released version](https://github.com/ethereum/go-ethereum/releases/latest).   -## Audit reports +## Reporting a Vulnerability   -Audit reports are published in the `docs` folder: https://github.com/ethereum/go-ethereum/tree/master/docs/audits +We’re extremely grateful for security researchers and users that report vulnerabilities to the Celo community. All reports are thoroughly investigated.   -| Scope | Date | Report Link | -| ------- | ------- | ----------- | -| `geth` | 20170425 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2017-04-25_Geth-audit_Truesec.pdf) | -| `clef` | 20180914 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2018-09-14_Clef-audit_NCC.pdf) | -| `Discv5` | 20191015 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2019-10-15_Discv5_audit_LeastAuthority.pdf) | -| `Discv5` | 20200124 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2020-01-24_DiscV5_audit_Cure53.pdf) | +**Please do not file a public ticket** mentioning any vulnerability.   -## Reporting a Vulnerability   -**Please do not file a public ticket** mentioning the vulnerability. +The Celo community asks that all suspected vulnerabilities be privately and responsibly disclosed.   -To find out how to disclose a vulnerability in Ethereum visit [https://bounty.ethereum.org](https://bounty.ethereum.org) or email bounty@ethereum.org. Please read the [disclosure page](https://github.com/ethereum/go-ethereum/security/advisories?state=published) for more information about publically disclosed security vulnerabilities. +To make a report, submit your vulnerability to [Celo on HackerOne](https://hackerone.com/celo).   -Use the built-in `geth version-check` feature to check whether the software is affected by any known vulnerability. This command will fetch the latest [`vulnerabilities.json`](https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities.json) file which contains known security vulnerabilities concerning `geth`, and cross-check the data against its own version number. +You can also email the [security@celo.org](mailto:security@celo.org) list with the details of reproducing the vulnerability as well as the usual details expected for all bug reports.   -The following key may be used to communicate sensitive information to developers. +While the primary focus of this disclosure program is the Celo protocol and the Celo wallet, the team may be able to assist in coordinating a response to a vulnerability in the third-party apps or tools in the Celo ecosystem.   -Fingerprint: `AE96 ED96 9E47 9B00 84F3 E17F E88D 3334 FA5F 6A0A` +You may encrypt your email to this list using this GPG key (but encryption using GPG is NOT required to make a disclosure):   ``` -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1   -mQINBFgl3tgBEAC8A1tUBkD9YV+eLrOmtgy+/JS/H9RoZvkg3K1WZ8IYfj6iIRaY -neAk3Bp182GUPVz/zhKr2g0tMXIScDR3EnaDsY+Qg+JqQl8NOG+Cikr1nnkG2on9 -L8c8yiqry1ZTCmYMqCa2acTFqnyuXJ482aZNtB4QG2BpzfhW4k8YThpegk/EoRUi -m+y7buJDtoNf7YILlhDQXN8qlHB02DWOVUihph9tUIFsPK6BvTr9SIr/eG6j6k0b -fUo9pexOn7LS4SojoJmsm/5dp6AoKlac48cZU5zwR9AYcq/nvkrfmf2WkObg/xRd -EvKZzn05jRopmAIwmoC3CiLmqCHPmT5a29vEob/yPFE335k+ujjZCPOu7OwjzDk7 -M0zMSfnNfDq8bXh16nn+ueBxJ0NzgD1oC6c2PhM+XRQCXChoyI8vbfp4dGvCvYqv -QAE1bWjqnumZ/7vUPgZN6gDfiAzG2mUxC2SeFBhacgzDvtQls+uuvm+FnQOUgg2H -h8x2zgoZ7kqV29wjaUPFREuew7e+Th5BxielnzOfVycVXeSuvvIn6cd3g/s8mX1c -2kLSXJR7+KdWDrIrR5Az0kwAqFZt6B6QTlDrPswu3mxsm5TzMbny0PsbL/HBM+GZ -EZCjMXxB8bqV2eSaktjnSlUNX1VXxyOxXA+ZG2jwpr51egi57riVRXokrQARAQAB -tDlFdGhlcmV1bSBGb3VuZGF0aW9uIFNlY3VyaXR5IFRlYW0gPHNlY3VyaXR5QGV0 -aGVyZXVtLm9yZz6JAj4EEwECACgCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheA -BQJaCWH6BQkFo2BYAAoJEOiNMzT6X2oK+DEP/3H6dxkm0hvHZKoHLVuuxcu3EHYo -k5sd3MMWPrZSN8qzZnY7ayEDMxnarWOizc+2jfOxfJlzX/g8lR1/fsHdWPFPhPoV -Qk8ygrHn1H8U8+rpw/U03BqmqHpYCDzJ+CIis9UWROniqXw1nuqu/FtWOsdWxNKh -jUo6k/0EsaXsxRPzgJv7fEUcVcQ7as/C3x9sy3muc2gvgA4/BKoGPb1/U0GuA8lV -fDIDshAggmnSUAg+TuYSAAdoFQ1sKwFMPigcLJF2eyKuK3iUyixJrec/c4LSf3wA -cGghbeuqI8INP0Y2zvXDQN2cByxsFAuoZG+m0cyKGaDH2MVUvOKKYqn/03qvrf15 -AWAsW0l0yQwOTCo3FbsNzemClm5Bj/xH0E4XuwXwChcMCMOWJrFoxyvCEI+keoQc -c08/a8/MtS7vBAABXwOziSmm6CNqmzpWrh/fDrjlJlba9U3MxzvqU3IFlTdMratv -6V+SgX+L25lCzW4NxxUavoB8fAlvo8lxpHKo24FP+RcLQ8XqkU3RiUsgRjQRFOqQ -TaJcsp8mimmiYyf24mNu6b48pi+a5c/eQR9w59emeEUZqsJU+nqv8BWIIp7o4Agh -NYnKjkhPlY5e1fLVfAHIADZFynWwRPkPMJSrBiP5EtcOFxQGHGjRxU/KjXkvE0hV -xYb1PB8pWMTu/beeiQI+BBMBAgAoBQJYJd7YAhsDBQkB4TOABgsJCAcDAgYVCAIJ -CgsEFgIDAQIeAQIXgAAKCRDojTM0+l9qCplDD/9IZ2i+m1cnqQKtiyHbyFGx32oL -fzqPylX2bOG5DPsSTorSUdJMGVfT04oVxXc4S/2DVnNvi7RAbSiLapCWSplgtBOj -j1xlblOoXxT3m7s1XHGCX5tENxI9fVSSPVKJn+fQaWpPB2MhBA+1lUI6GJ+11T7K -J8LrP/fiw1/nOb7rW61HW44Gtyox23sA/d1+DsFVaF8hxJlNj5coPKr8xWzQ8pQl -juzdjHDukjevuw4rRmRq9vozvj9keEU9XJ5dldyEVXFmdDk7KT0p0Rla9nxYhzf/ -r/Bv8Bzy0HCWRb2D31BjXXGG05oVnYmNGxGFxYja4MwgrMmne3ilEVjfUJsapsqi -w41BAyQgIdfREulYN7ahsF5PrjVAqBd9IGtE8ULelF2SQxEBQBngEkP0ahP6tRAL -i7/CBjPKOyKijtqVny7qrGOnU2ygcA88/WDibexDhrjz0Gx8WmErU7rIWZiZ5u4Y -vJYVRo0+6rBCXRPeSJfiP5h1p17Anr2l42boAYslfcrzquB8MHtrNcyn650OLtHG -nbxgIdniKrpuzGN6Opw+O2id2JhD1/1p4SOemwAmthplr1MIyOHNP3q93rEj2J7h -5zPS/AJuKkMDFUpslPNLQjCOwPXtdzL7/kUZGBSyez1T3TaW1uY6l9XaJJRaSn+v -1zPgfp4GJ3lPs4AlAbQ0RXRoZXJldW0gRm91bmRhdGlvbiBCdWcgQm91bnR5IDxi -b3VudHlAZXRoZXJldW0ub3JnPokCPgQTAQIAKAIbAwYLCQgHAwIGFQgCCQoLBBYC -AwECHgECF4AFAloJYfoFCQWjYFgACgkQ6I0zNPpfagoENg/+LnSaVeMxiGVtcjWl -b7Xd73yrEy4uxiESS1AalW9mMf7oZzfI05f7QIQlaLAkNac74vZDJbPKjtb7tpMO -RFhRZMCveq6CPKU6pd1SI8IUVUKwpEe6AJP3lHdVP57dquieFE2HlYKm6uHbCGWU -0cjyTA+uu2KbgCHGmofsPY/xOcZLGEHTHqa5w60JJAQm+BSDKnw8wTyrxGvA3EK/ -ePSvOZMYa+iw6vYuZeBIMbdiXR/A2keBi3GuvqB8tDMj7P22TrH5mVDm3zNqGYD6 -amDPeiWp4cztY3aZyLcgYotqXPpDceZzDn+HopBPzAb/llCdE7bVswKRhphVMw4b -bhL0R/TQY7Sf6TK2LKSBrjv0DWOSijikE71SJcBnJvHU7EpKrQQ0lMGclm3ynyji -Nf0YTPXQt4I+fwTmOew2GFeK3UytNWbWI7oXX7Nm4bj9bhf3IJ0kmZb/Gs73+xII -e7Rz52Mby436tWyQIQiF9ITYNGvNf53TwBBZMn0pKPiTyr3Ur7FHEotkEOFNh1// -4zQY10XxuBdLrYGyZ4V8xHJM+oKre8Eg2R9qHXVbjvErHE+7CvgnV7YUip0criPr -BlKRvuoJaSliH2JFhSjWVrkPmFGrWN0BAx10yIqMnEplfKeHf4P9Elek3oInS8WP -G1zJG6s/t5+hQK0X37+TB+6rd3GJAj4EEwECACgFAlgl4TsCGwMFCQHhM4AGCwkI -BwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEOiNMzT6X2oKzf8P/iIKd77WHTbp4pMN -8h52HyZJtDJmjA1DPZrbGl1TesW/Z9uTd12txlgqZnbG2GfN9+LSP6EOPzR6v2xC -OVhR+RdWhZDJJuQCVS7lJIqQrZgmeTZG0TyQPZdLjVFBOrrhVwYX+HXbu429IzHr -URf5InyR1QgqOXyElDYS6e28HFqvaoA0DWTWDDqOLPVl+U5fuceIE2XXdv3AGLeP -Yf8J5MPobjPiZtBqI6S6iENY2Yn35qLX+axeC/iYSCHVtFuCCIdb/QYR1ZZV8Ps/ -aI9DwC7LU+YfPw7iqCIoqxSeA3o1PORkdSigEg3jtfRv5UqVo9a0oBb9jdoADsat -F/gW0E7mto3XGOiaR0eB9SSdsM3x7Bz4A0HIGNaxpZo1RWqlO91leP4c13Px7ISv -5OGXfLg+M8qb+qxbGd1HpitGi9s1y1aVfEj1kOtZ0tN8eu+Upg5WKwPNBDX3ar7J -9NCULgVSL+E79FG+zXw62gxiQrLfKzm4wU/9L5wVkwQnm29hLJ0tokrSBZFnc/1l -7OC+GM63tYicKkY4rqmoWUeYx7IwFH9mtDtvR1RxO85RbQhZizwpZpdpRkH0DqZu -ZJRmRa5r7rPqmfa7d+VIFhz2Xs8pJMLVqxTsLKcLglmjw7aOrYG0SWeH7YraXWGD -N3SlvSBiVwcK7QUKzLLvpadLwxfsuQINBFgl3tgBEACbgq6HTN5gEBi0lkD/MafI -nmNi+59U5gRGYqk46WlfRjhHudXjDpgD0lolGb4hYontkMaKRlCg2Rvgjvk3Zve0 -PKWjKw7gr8YBa9fMFY8BhAXI32OdyI9rFhxEZFfWAfwKVmT19BdeAQRFvcfd+8w8 -f1XVc+zddULMJFBTr+xKDlIRWwTkdLPQeWbjo0eHl/g4tuLiLrTxVbnj26bf+2+1 -DbM/w5VavzPrkviHqvKe/QP/gay4QDViWvFgLb90idfAHIdsPgflp0VDS5rVHFL6 -D73rSRdIRo3I8c8mYoNjSR4XDuvgOkAKW9LR3pvouFHHjp6Fr0GesRbrbb2EG66i -PsR99MQ7FqIL9VMHPm2mtR+XvbnKkH2rYyEqaMbSdk29jGapkAWle4sIhSKk749A -4tGkHl08KZ2N9o6GrfUehP/V2eJLaph2DioFL1HxRryrKy80QQKLMJRekxigq8gr -eW8xB4zuf9Mkuou+RHNmo8PebHjFstLigiD6/zP2e+4tUmrT0/JTGOShoGMl8Rt0 -VRxdPImKun+4LOXbfOxArOSkY6i35+gsgkkSy1gTJE0BY3S9auT6+YrglY/TWPQ9 -IJxWVOKlT+3WIp5wJu2bBKQ420VLqDYzkoWytel/bM1ACUtipMiIVeUs2uFiRjpz -A1Wy0QHKPTdSuGlJPRrfcQARAQABiQIlBBgBAgAPAhsMBQJaCWIIBQkFo2BYAAoJ -EOiNMzT6X2oKgSwQAKKs7BGF8TyZeIEO2EUK7R2bdQDCdSGZY06tqLFg3IHMGxDM -b/7FVoa2AEsFgv6xpoebxBB5zkhUk7lslgxvKiSLYjxfNjTBltfiFJ+eQnf+OTs8 -KeR51lLa66rvIH2qUzkNDCCTF45H4wIDpV05AXhBjKYkrDCrtey1rQyFp5fxI+0I -Q1UKKXvzZK4GdxhxDbOUSd38MYy93nqcmclGSGK/gF8XiyuVjeifDCM6+T1NQTX0 -K9lneidcqtBDvlggJTLJtQPO33o5EHzXSiud+dKth1uUhZOFEaYRZoye1YE3yB0T -NOOE8fXlvu8iuIAMBSDL9ep6sEIaXYwoD60I2gHdWD0lkP0DOjGQpi4ouXM3Edsd -5MTi0MDRNTij431kn8T/D0LCgmoUmYYMBgbwFhXr67axPZlKjrqR0z3F/Elv0ZPP -cVg1tNznsALYQ9Ovl6b5M3cJ5GapbbvNWC7yEE1qScl9HiMxjt/H6aPastH63/7w -cN0TslW+zRBy05VNJvpWGStQXcngsSUeJtI1Gd992YNjUJq4/Lih6Z1TlwcFVap+ -cTcDptoUvXYGg/9mRNNPZwErSfIJ0Ibnx9wPVuRN6NiCLOt2mtKp2F1pM6AOQPpZ -85vEh6I8i6OaO0w/Z0UHBwvpY6jDUliaROsWUQsqz78Z34CVj4cy6vPW2EF4 -=r6KK +mQINBF5Vg0MBEADNmoPEf2HiSGGqJZOE8liv783KZVKRle5oTwI9VNF7rnHUq0ub +/jf6B/saUliO8JbYTyfbUXfPjaeIRxA1zvbHPMtWdj6coPUFwvZ77okDHeXGAnFl +6ZcKs/q8mpcNP8E4ATtvrNUW3aRkDvud2e+ysIHyjaae1mf29cWMGInxjm3YUyMr +5/YnJEzSiVN+krtTDDVg4N2qZbR1gX7uvVlXytzD92vKWNurWi2ZXhwWhC0BbcCK +HlHnEhok2njMqmKlY1rzj35hNwzxwj8fZi3JGgTPQAUZP6vHqvo7GxmUYPQqo/f0 +2y7dshL3An7AM3OMIWbtwsh72PX+SqeKTF9Y00TsYz4raVv4ub2HT/0TtOwBlNJD +fBr3XgRMkUtBGhaGWe8D4P6UNUM/imz8EQLCbFa6qhYr+asrYzvCGHHNy9v3OeJF +fyYyqn/k+44zMTCZx7FGR/SFjEDnROqjFmOYio+Tuv5U7ycJu8Bhu2qqCCxYApR6 +NyVZQ1U7cTWTLLWkFfe359pSM2KhK9ftuRm2Jf1CmkgTsxYxpzyuFYf37FvI8LFK +06h1R5yr7lTNY0EfKG6UWTHmJo5oYSTJ1tWEItxT0H4cjCRVyJxs2ze9sdPc1a6f +hP05fk+VLyN29WgmVuKtrMHUjtWwVhjbmOe9fCMHe4+N/4jPZ/ivvT5c2QARAQAB +tDJDZWxvIFZ1bG5lcmFiaWxpdHkgRGlzY2xvc3VyZXMgPHNlY3VyaXR5QGNlbG8u +b3JnPokCTgQTAQgAOBYhBPXFzhvm4XlmxS2jdaoCJpJIGPpJBQJeVYNDAhsDBQsJ +CAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEKoCJpJIGPpJqqcQAKWp35OEhP/Je8Xg +XmMuBeaOqdjdWoBQD7lYIBwd6I+Y1vF6mk69yWLhWqIqgSaTf8BtHyZmuKPAs8rX +mSwdQY9X/cy3ECLBFP5TABGwZx6F6cYNXH6b/drSo6Mh/ZEJE/M4rwuHImEIVO9f +jljA66HaNOZp3C9CBVqUlsJkezXR7JCWcKwrlzXyG7NeP/6/yqlCADX8zgGEjBLa +bsFrSLnBgxncSmxEmSjlT3BxpUv6T5QGu/QxfhmgqgmwzUKA5Ak1aZ4jxZQ0W5ZB +bT2jyifJXszg1qeZ+bbyFSINqiuDB9/gJTNwZ5WTtf2Q8HBJOS6i0uRpHaXqY841 +4srFeu7bOM35FCUL6kZ2snMQSVuw9cnvWtcby/rJZ3QUFn0Sffh4b/c7zS+/aUOM +8e2MrF76veHa4uqA1gHOtsT2LKtyaVn6HdImeMH0TQJVvAm8u8B0jrcgB1z8BjrR +qbJeJnqjz9pGQo1AMuuSJ5pj3BemfGlUyDx4KuJVJBFDqzcP8DjOEYY9ylcqS7MO +lZS36e00npd2sHcty0FmHpYKMODNfZ+XW6OAfOMX9eZlispOV5JmT78FTr5HLHjG +oTOBGeOTDB4Y16d4EO47AZvOBPMuXsw2SJ4aqyyxhMVlgdnp+0A3CMpkv0BnvVFT +ewaQUMIrvaTKyF5B4Mf3E0pTDQ5OuQINBF5Vg0MBEACzQa+flDsevx9Utb5hQN+S +mKPQpkno7AN1PdLA23DAiXoV2L3Mwa7lpqSKrwo8yRnlq7aNo6j4/G1xnmgFyd6c +oYHWOwSfF/nkio2Stjf6UQSe8WlHNwKTOyXA2ABZ9Xr4BpeuTh+tZIf02VEfdDEd +tTfCcq/iA/UScUDG2LmPcFt62shjJ5+bP0HHalIn28kgaTJIWgp8tWkIFvt7TRYt +UrBxcjgmVnpb2eC1AI9UoQ04+7hV4mXb19GfM0WdssmOPArtmHo2daGg/WwcbgJ8 +oqSBe/DEqmjrn7ETNn1wbCgsA8nPS7NCrBxl0pFEiav+2ZJ0B6jOA1m8UI4FQUPS +oXnZMyq7jd8liBeWQrrK8OpUUKcyaBDnM31wNgmWJAq9Ck42JnnLijYoESbpOk9r +e0RnDv2H86oIBXpwnVRZebJgbw2Nuv9GsvCB4hYsBbL0UVrxRGl7pOnRnEMd94RY +P4KzB6lELVxUDt3NCs/PFXSKId9NpLYxomox6B+9SDh9e0MnFTn8vT8hOEkCBYMW +bZldMAengb7jagC0Z2TfwukiDWMcVHObhxjR6fl03ACnt+EXqAZjr5x2QOko7CXU +xBo+wTDZTmuD6S8LI0q9v0BrIszZiJxWti7BSqYGnDPOLA84d0p8DIEy6W9GSqAD +CBfYi5r24BkLaslh6Uyb2QARAQABiQI2BBgBCAAgFiEE9cXOG+bheWbFLaN1qgIm +kkgY+kkFAl5Vg0MCGwwACgkQqgImkkgY+kkhXw//YtV8tNpmDo2oZfUltYE0sZrr +YN/0wchkHMs3rUt6K8/5Lbbzi3GcLVtG5PSbkGs6eTfbCYzJkhQO+vdA+T5CgZld +HrEQ4nPXHBcr7BFBRPQ4LCM7q4ygVldRw2qusWvf/YdcMmLk7pg/F1wSSkWZHUpp +a0BNbZBeCZ1xWlu/+VUlCpsT2m5Ak1gdm58zwJ4uZwTc4hRPxS7q4GuSQBNrvNx8 +Os6Grt6lcZyJ6zGYr73/5PraZyQnprQ4FzJjwSLb7doVfhUoGVf4wiECsVhoNByu +/ojfERTErUzhw9Wu44EmY0Imot99SbbJ9ifchF69TnSMcb17PNnbV10VbbQuNLun +7GiTfF3wd80MhI+KpDAlFpy08M5i+kyk97uzMzFcOZn68KUbNIsn/JFaKc1orMmy +b8mhFCXTeX1UJsfSwRodSmKRrKSPq6MflNQYPwpKOU5hOHaQ71gavNunQVEbEoXO +ny+/RyALrlDt0ffuKoVBHHQjThIGXnb1ItTjuCI5d6yiuFuRz3AvHmhqm/4sqX96 +t1yroLhsk8x7HaLuVdKB/SW+DhTAxPqFJzEw09KpZci5m9VMyJxplI3uh0rJb6pz +HssYE9a+LjVUEzu3sZxvKhDzG7v4Nn7NZ4Ve8Q5tRZJEXvwEi6KKBxrDKV9wKdqY +ztmP6RvHPG6jYvyqofo= +=Sy1W -----END PGP PUBLIC KEY BLOCK----- ```
diff --git go-ethereum/appveyor.yml celo/appveyor.yml index 65b5f96841e206076b287ae3afcf4f4469a517f2..535de98dccdef1bbea51c69a684c0ac9981ebb87 100644 --- go-ethereum/appveyor.yml +++ celo/appveyor.yml @@ -13,7 +13,10 @@ - GETH_ARCH: 386 GETH_MINGW: 'C:\msys64\mingw32'   install: - - git submodule update --init --depth 1 + - git submodule update --init + - rmdir C:\go /s /q + - appveyor DownloadFile https://dl.google.com/go/go1.16.windows-%GETH_ARCH%.zip + - 7z x go1.16.windows-%GETH_ARCH%.zip -y -oC:\ > NUL - go version   for:
(deleted)
+0
-32
diff --git go-ethereum/circle.yml celo/circle.yml deleted file mode 100644 index 39ff5d83c68e6329c33b4de6190c45334408ccff..0000000000000000000000000000000000000000 --- go-ethereum/circle.yml +++ /dev/null @@ -1,32 +0,0 @@ -machine: - services: - - docker - -dependencies: - cache_directories: - - "~/.ethash" # Cache the ethash DAG generated by hive for consecutive builds - - "~/.docker" # Cache all docker images manually to avoid lengthy rebuilds - override: - # Restore all previously cached docker images - - mkdir -p ~/.docker - - for img in `ls ~/.docker`; do docker load -i ~/.docker/$img; done - - # Pull in and hive, restore cached ethash DAGs and do a dry run - - go get -u github.com/karalabe/hive - - (cd ~/.go_workspace/src/github.com/karalabe/hive && mkdir -p workspace/ethash/ ~/.ethash) - - (cd ~/.go_workspace/src/github.com/karalabe/hive && cp -r ~/.ethash/. workspace/ethash/) - - (cd ~/.go_workspace/src/github.com/karalabe/hive && hive --docker-noshell --client=NONE --test=. --sim=. --loglevel=6) - - # Cache all the docker images and the ethash DAGs - - for img in `docker images | grep -v "^<none>" | tail -n +2 | awk '{print $1}'`; do docker save $img > ~/.docker/`echo $img | tr '/' ':'`.tar; done - - cp -r ~/.go_workspace/src/github.com/karalabe/hive/workspace/ethash/. ~/.ethash - -test: - override: - # Build Geth and move into a known folder - - make geth - - cp ./build/bin/geth $HOME/geth - - # Run hive and move all generated logs into the public artifacts folder - - (cd ~/.go_workspace/src/github.com/karalabe/hive && hive --docker-noshell --client=go-ethereum:local --override=$HOME/geth --test=. --sim=.) - - cp -r ~/.go_workspace/src/github.com/karalabe/hive/workspace/logs/* $CIRCLE_ARTIFACTS
diff --git go-ethereum/cloudbuild-binaries.yaml celo/cloudbuild-binaries.yaml new file mode 100644 index 0000000000000000000000000000000000000000..766caca4096bd4be73afa34b76c244f278ff6835 --- /dev/null +++ celo/cloudbuild-binaries.yaml @@ -0,0 +1,62 @@ +# See ./Dockerfile.binaries for more information w.r.t this CI flow +steps: +- name: 'gcr.io/cloud-builders/docker' + entrypoint: 'sh' + args: + - '-c' + - 'docker pull us.gcr.io/$PROJECT_ID/geth-xgo-builder:latest || exit 0' +- name: 'gcr.io/cloud-builders/docker' + entrypoint: 'sh' + args: + - '-c' + - 'docker build . + --cache-from us.gcr.io/$PROJECT_ID/geth-xgo-builder:latest + -t us.gcr.io/$PROJECT_ID/geth-xgo-builder:$COMMIT_SHA + -t us.gcr.io/$PROJECT_ID/geth-xgo-builder:latest + -f Dockerfile.binaries' +- name: 'gcr.io/cloud-builders/docker' + entrypoint: 'sh' + args: + - '-c' + - 'docker run --rm + -v $(pwd)/build/bin:/build + -v $(pwd)/build/archives:/archives + -v $(pwd):/go/src/github.com/ethereum/go-ethereum + --entrypoint /bin/sh + --env BUILD_TARGETS=$_BUILD_TARGETS + --env TAG_NAME=$TAG_NAME + --env BRANCH_NAME=$BRANCH_NAME + --env REPO_NAME=$REPO_NAME + --env COMMIT_SHA=$COMMIT_SHA + --env COMMIT_TIMESTAMP=$(date +%s) + --env CLOUDBUILD=True + --env CI=True + us.gcr.io/$PROJECT_ID/geth-xgo-builder:$COMMIT_SHA + -c "go run build/ci.go xgo --alltools -- -targets=$_BUILD_TARGETS -v -dest /build"' +- name: 'gcr.io/cloud-builders/docker' + entrypoint: 'sh' + args: + - '-c' + - 'docker run --rm + -v $(pwd)/build/bin:/build + -v $(pwd)/build/archives:/archives + -v $(pwd):/go/src/github.com/ethereum/go-ethereum + --entrypoint /bin/sh + --env BUILD_TARGETS=$_BUILD_TARGETS + --env TAG_NAME=$TAG_NAME + --env BRANCH_NAME=$BRANCH_NAME + --env REPO_NAME=$REPO_NAME + --env COMMIT_SHA=$COMMIT_SHA + --env COMMIT_TIMESTAMP=$(date +%s) + --env CLOUDBUILD=True + --env CI=True + us.gcr.io/$PROJECT_ID/geth-xgo-builder:$COMMIT_SHA + -c "go run build/ci.go xgo-archive -targets=$_BUILD_TARGETS -in /build -out /archives"' +artifacts: + objects: + location: 'gs://$_BUCKET/$BRANCH_NAME/' + paths: ['./build/archives/*'] +images: +- 'us.gcr.io/$PROJECT_ID/geth-xgo-builder:$COMMIT_SHA' +- 'us.gcr.io/$PROJECT_ID/geth-xgo-builder:latest' +timeout: 2700s
diff --git go-ethereum/cloudbuild-branchname.yaml celo/cloudbuild-branchname.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3564535c22f3ed8527b16aec9f174c15ec88ed72 --- /dev/null +++ celo/cloudbuild-branchname.yaml @@ -0,0 +1,11 @@ +steps: +- name: 'gcr.io/cloud-builders/docker' + args: [ 'build', '-t', 'us.gcr.io/$PROJECT_ID/geth:$BRANCH_NAME', '--build-arg', 'COMMIT_SHA=$COMMIT_SHA', '.' ] + waitFor: ["-"] +- name: 'gcr.io/cloud-builders/docker' + args: [ 'build', '-t', 'us.gcr.io/$PROJECT_ID/geth-all:$BRANCH_NAME', '--build-arg', 'COMMIT_SHA=$COMMIT_SHA', '-f', 'Dockerfile.alltools', '.' ] + waitFor: ["-"] +images: +- 'us.gcr.io/$PROJECT_ID/geth:$BRANCH_NAME' +- 'us.gcr.io/$PROJECT_ID/geth-all:$BRANCH_NAME' +timeout: 2700s
diff --git go-ethereum/cloudbuild.yaml celo/cloudbuild.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b99262e3bdada1d8d6aa377ebd1b871c8cd6f821 --- /dev/null +++ celo/cloudbuild.yaml @@ -0,0 +1,11 @@ +steps: +- name: 'gcr.io/cloud-builders/docker' + args: [ 'build', '-t', 'us.gcr.io/$PROJECT_ID/geth:$COMMIT_SHA', '--build-arg', 'COMMIT_SHA=$COMMIT_SHA', '.' ] + waitFor: ["-"] +- name: 'gcr.io/cloud-builders/docker' + args: [ 'build', '-t', 'us.gcr.io/$PROJECT_ID/geth-all:$COMMIT_SHA', '--build-arg', 'COMMIT_SHA=$COMMIT_SHA', '-f', 'Dockerfile.alltools', '.' ] + waitFor: ["-"] +images: +- 'us.gcr.io/$PROJECT_ID/geth:$COMMIT_SHA' +- 'us.gcr.io/$PROJECT_ID/geth-all:$COMMIT_SHA' +timeout: 2700s
(new)
(binary file)
diff --git go-ethereum/default.profraw celo/default.profraw new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 Binary files /dev/null and celo/default.profraw differ
diff --git go-ethereum/disclosures.md celo/disclosures.md new file mode 100644 index 0000000000000000000000000000000000000000..84b93546cb187010fe13bda9e2ce4f668f341086 --- /dev/null +++ celo/disclosures.md @@ -0,0 +1,85 @@ +# Vulnerability Disclosure Policy + +## Introduction + +We at Celo are committed to ensuring the security of the platfomr by protecting their information. This policy is intended to give security researchers +clear guidelines for conducting vulnerability discovery activities and to convey our preferences in how to submit discovered vulnerabilities to us. + +We encourage you to contact us to report potential vulnerabilities in our systems. + + +Under this policy, “research” means activities in which you: + + Notify us as soon as possible after you discover a real or potential security issue. + + Make every effort to avoid privacy violations, degradation of user experience, disruption to production systems, and destruction or manipulation of data. + + Only use exploits to the extent necessary to confirm a vulnerability’s presence. Do not use an exploit to compromise or exfiltrate data, + establish persistent command line access, or use the exploit to pivot to other systems. + + Provide us a reasonable amount of time to resolve the issue before you disclose it publicly. + + Do not submit a high volume of low-quality reports. + +## How to contact us + +Please send an email to disclosures@celo.org you can use our PGP key 7283 BC81 D343 8FB7 3FBC DD4C AFEA 4E05 3F5C E709 + +### Public key + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: Mailvelope v4.3.2 +Comment: https://www.mailvelope.com + +xsFNBF+AjgABEAC+b6VFJ88EohrqyNnkaukBoZU6bW09gX1beEtoTzBDXXcR +E6GVvtCLPOt364Q+jQ+vxnB5iin4wYSny74zIu+LRfm/bo050sWyO9VlvDqN +/I+F+Ch86ltF2YKJU06fiEPsj30wJLCCsRTWiN0Qnumsza00ocsTLYT1KMyb +bsQzRDNck+VTp3ughv+7fX9/GkKwXJKx7h4xLSJrGp9YgEKjLKjs96KeFn+U +LjsjqtjrjI5Oli5gpWW1NDIDJqwkA2dj1O8uD/lplIYFni54S963Vtjql0j9 +bX9yoIZHixY4hDAm595eNv2jyEaYhjFLrD8qyfPoje2DOFQCX6o4t2OWQzCw +kpy+IY46HX+mdzGWPb2kk/+uxsHG3wOc3Uesh94iC+ybHLW+HRMqocrVMe13 +alIXxMfIX871mCG6O/l+IBsF0hyw4baqiVzniuFB0tzFYNgdiJ/SiUAOpW6T +cgUG6dKq+zB72Nu9Hj8hVrlaMVnRYHPa8bjDb0kfiIIXRFTahwQloJ2eqJsg +1ODXVi3MpusL34D/3c0nTOxMFYv21TFePz+1kDJcYABactyV9BzYGlL+hqIK +putUNUSuh2TZRXw7Iw/fkdHtYRn0+jInhBC9yakya4rbKCAzxSo2cQJnfQEM +lxAnHo/o/RxfqTn0ft4COO0oXd9g71jCedex/QARAQABzStkaXNjbG9zdXJl +cyBjZWxvLm9yZyA8ZGlzY2xvc3VyZXNAY2Vsby5vcmc+wsF1BBABCAAfBQJf +gI4ABgsJBwgDAgQVCAoCAxYCAQIZAQIbAwIeAQAKCRCv6k4FP1znCTywEACs +/13WT8sEadJdeVzhjephXenbxiMSDZDu29HqIbCjZ4q6Jr3UP47hgPmTC5qC +89cewmjd8GMVFYrMES1k7BFHxEaSeITvbXj5eC707PJThV8zn9qv8/oBknfo +4IQcbbFmJ/5qJIGeOPFqpGZK1u923PCeGobWNMGh2G53ahrgz5uvm+TQqGj3 +onu2UNJ+i6lSGCJCouStj+sUaAClDOt5xKkWu/5QBYz2rmVjptMbFKV6Kswu +PEsrA32LISP6+1EpjCF3pJ+xnIFJ7OZszGdIfmreujajoq2tqYEcVgx5fQ4p +nR+ej2J7hfiuLXE4N1DY37Pfd1jBuWpbtIX/nbms9TlQCRGzXQjQh4dj+9KU +h0XfjAe/YGPBova6uBrc4x+m1LXEkIsBiZhPHpylED2JTPTGXYN2qQzJkMXl +j2eQAKKvq/QqJEG4Roq5OgsSoap8CTffWO2FyLM+92Guz1t/D2hug4MB8Z// +Xgc2JFAaWQ6ugK4W6Sa8vVrWqKymhMK0rRe/1PL8FMmxdXfNuCow3qb2Rlmd +HxRcyl4KB5f9yryeGG+r/ibTtkdm6i2gYNkhyGXnP+U7gHcWnVY6xnh+uD0t +piS4urm/YRDFP0mTxj6Pc8cgjwUWPwAHcLkzDQM0B8b/JFDeHugNt4SK5kDJ +DBYMY5bDCc/Cylx/ArjBR87BTQRfgI4AARAAv1O3k9qoCPnQB3CglXSxIhTX +GJJ77y76pM6CMdhYJgni0hwKR/9aPqEP7mejT4qwviHSPw93Phj8+TmDKNk3 +uq5ASSB6cWGQZ/apoMi4AKTaUbnY2RHi1Nw67M/igP+fKvg0iHTBqX7x30+x +02OTYegXywRuZ2eN/b4ZZKIvfqK6YLp9E3ezNwdGcyG9JqcTmqkRsaiZa6kM +AU71JOpnwBOUfcJ2nFOThU0+MwJo1MxUhsojcrzkSGb8/aSvMBveCaDeAmQq +yEHsONjsEO8UOZ/RxexQbB7FfoeNYkGe1g/CTiL8jHJeAd737SdtSYpcgKTh +nWV+tOM9hmy67MkNJm1HPvI4dh99nwQBPag2kfNHtgZOPf9W/3x473JE+Fmt +qYXvxbDfmPaIXlGsbuEHvIFu+7fTJt2Gi9gkzlI23E+F5Zoy+mmcDJxxBBZ6 +IIfLKMRoZiL2uzE401OZRnkVthDrltkAJmVhYmJ22GrwKDuTqnaukEDx+KUM +XUmDaEItr2E3FWQ0sPbndxRwNoy0r5xR3/TFFhFmhl6ZDUdDzPyal0xiJU9J +SqWy9fF0k+hhOh1YYLF0iro5FnYfkm2q3snjqbck3wyad6d1CmNr46hZ0CIo +uFe1CpLOIHwEFdYxAtvnpojITXZgTBAOvpg+zGJDTF5PztcIWuQ+H+Yg2D8A +EQEAAcLBXwQYAQgACQUCX4COAAIbDAAKCRCv6k4FP1znCUC5D/0VvOkfflE2 +f1VdEjwwE1PzWoLnp/LK9hy9MR14qbfnmw2opesBUjhHteoW8+kjh8MV6PBs ++yYahhr3bFlPR5FK3R+9EdfEvZSjwDOrqwsqujTTnBvGUYRjPA93KGmdnrpa +O8WzRSY63B8Oxt3a+KdOglutvTW1FV3waqpnN8zL6i2M9pfCox+dJK7XJKQY +OAitSN6g1C4dVRnXExomcvJ+w5DY5YCuu4w9kJGxRDouqq2uU2i+A3XAfl0t +nIoFoogwAUguCC2YQXq10nKNMYmJUrB/Diwnxqkfa3fmdMGWb/X0vxc8A3wV +J/wzICnzn7J2s/TkVtOQsnNYJ5NVVS+8GQEVpPjs1NAuvcI9tRcYLiMNPzjX +kKOtNq6uaIvQ/L3z5bCy3e5gxfnMeGU55ZjR22WCTsKPIY9G/bFi/DbEPR3V +LydsLrd6cPFq9j2Wirww0jOdpdrJADpR9hKq2b9eMps36+Z88LnvKxF8U+hF +mCClYViH/1E+NoD/UuswmIwkupWmNYylSTn5cnBx7gNOnXK5tnFPwrptYJ7n +VQIdAwvoDkU29KUwbIgRCdrA+BU6yYMGxM/nPBNDsgOP4QNNmhhB+u8jNcgT +R1M9CATkqyw3d4ww314Xc3GLJRftYeUaJkCMDlYkd16OvOuj9xoEUx6ahimx +W2grzwCRcXkUDA== +=S96l +-----END PGP PUBLIC KEY BLOCK-----
(new)
+33
-0
diff --git go-ethereum/docs/Gemfile celo/docs/Gemfile new file mode 100644 index 0000000000000000000000000000000000000000..eef36123dd11068f7bb11754e300cf322217449b --- /dev/null +++ celo/docs/Gemfile @@ -0,0 +1,34 @@ +source "https://rubygems.org" + +# Hello! This is where you manage which Jekyll version is used to run. +# When you want to use a different version, change it below, save the +# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: +# +# bundle exec jekyll serve +# +# This will help ensure the proper Jekyll version is running. +# Happy Jekylling! +#gem "jekyll", "~> 3.8.5" + +# This is the default theme for new Jekyll sites. You may change this to anything you like. +gem "minima", "~> 2.0" + +# If you want to use GitHub Pages, remove the "gem "jekyll"" above and +# uncomment the line below. To upgrade, run `bundle update github-pages`. +gem "github-pages", group: :jekyll_plugins + +# If you have any plugins, put them here! +group :jekyll_plugins do + gem "jekyll-feed", "~> 0.6" +end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] + +# Performance-booster for watching directories on Windows +gem "wdm", "~> 0.1.0" if Gem.win_platform? + +group :development, :test do + gem "pry" + gem "html-proofer" +end \ No newline at end of file
diff --git go-ethereum/docs/README.md celo/docs/README.md new file mode 100644 index 0000000000000000000000000000000000000000..75e5069c1ce0b92a841e3348fb6d6a4dff306734 --- /dev/null +++ celo/docs/README.md @@ -0,0 +1,39 @@ +### Docs + +The Celo blockchain client documentation is forked from the [go-ethereum](https://github.com/ethereum/go-ethereum) documentation, modified as needed to match changes made to the client. +These are work in progress and might not always be accurate. Please [let us know](https://github.com/ethereum/go-ethereum/issues/new) if there are any issues. + +Links: +- [geth docs](https://geth.ethereum.org/) + +See also: +- [general celo docs](https://docs.celo.org) + +### Building the docs locally + +#### With Docker + +If you can take advantage of Docker, run: +``` +$ docker-compose up +``` + +Navigate to [localhost:4000](http://localhost:4000) to browse the docs. + +#### With Ruby + +In order to build the docs you need to have `ruby` installed. We recommend using [asdf](https://asdf-vm.com/) or [rvm](https://rvm.io/). +After that you need to install dependencies: + +``` +$ bundle install +``` + +And run jekyll: + +``` +$ bundle exec jekyll server +``` + + +Navigate to [localhost:4000](http://localhost:4000) to browse the docs.
diff --git go-ethereum/docs/_config.yml celo/docs/_config.yml new file mode 100644 index 0000000000000000000000000000000000000000..e3c0fda6f28bee88496f9daa14d0bfc4deb68ebc --- /dev/null +++ celo/docs/_config.yml @@ -0,0 +1,68 @@ +repository: celo-org/celo-blockchain + +theme: minima + +keep_files: + - static + +favicon: /static/images/favicon.ico + +defaults: + - scope: + path: "" + values: + layout: sidebar + - scope: + path: "*" + values: + root: "../.." + - scope: + path: "*/*" + values: + root: "../.." + +default_root: "../.." + +collections_dir: docs +collections: + install-and-build: + output: true + permalink: docs/:collection/:slug + caption: Install and Build + sidebar_index: 2 + frontpage: _install-and-build/Installing-Geth.md + interface: + output: true + permalink: docs/:collection/:slug + caption: Using Geth + sidebar_index: 3 + dapp: + output: true + permalink: docs/:collection/:slug + caption: For dApp Developers + frontpage: _dapp/native.md + sidebar_index: 4 + rpc: + output: true + permalink: docs/:collection/:slug + caption: JSON RPC APIs + frontpage: _rpc/server.md + sidebar_index: 5 + developers: + output: true + permalink: docs/:collection/:slug + caption: For Geth Developers + frontpage: _developers/devguide.md + sidebar_index: 6 + clef: + output: true + permalink: docs/:collection/:slug + caption: Clef + sidebar_index: 7 + frontpage: _clef/Tutorial.md + whisper: + output: true + permalink: docs/:collection/:slug + caption: Whisper + sidebar_index: 8 + frontpage: _whisper/Whisper-Overview.md
diff --git go-ethereum/docs/_data/navbar.yml celo/docs/_data/navbar.yml new file mode 100644 index 0000000000000000000000000000000000000000..8dd4c8659ee8df2ea3b0d154c7f358c1a53b5a5f --- /dev/null +++ celo/docs/_data/navbar.yml @@ -0,0 +1,2 @@ +Install: /docs/install-and-build/installing-celo-blockchain +Documentation: /docs
diff --git go-ethereum/docs/_data/sidebar.yml celo/docs/_data/sidebar.yml new file mode 100644 index 0000000000000000000000000000000000000000..b46b588cad58315f3e38bac421c47104cc11f383 --- /dev/null +++ celo/docs/_data/sidebar.yml @@ -0,0 +1,25 @@ +Getting started: + - Dev mode: /docs/getting-started/dev-mode +Install and build: + - Downloads: /downloads + - Installing Ethereum: /docs/Building-Ethereum + - Developers' Guide: /docs/Developers-Guide +How to use: + - Managing Accounts: /docs/Managing-your-accounts + - Mining: /docs/Mining + - Contract Tutorial: /docs/Contract-Tutorial +Interface Documentation: + - Command Line Options: /docs/Command-Line-Options + - JavaScript Console: /docs/JavaScript-Console + - Management API: /docs/Management-APIs + - JSON-RPC server: https://github.com/ethereum/wiki/wiki/JSON-RPC +Issues and support: + - FAQ: /docs/FAQ + - Reporting issues: /docs/geth#reporting + - Community and support: /docs/geth#community-and-support + - Issue handling workflow: /docs/Issue-handling-workflow +Developers: + - Mobile Clients: /docs/Mobile-Clients/Libraries-and-Inproc-Ethereum-Nodes + - Native DApps: /docs/Native-DApps/Go-bindings-to-Ethereum-contracts + - Active go-ethereum projects: /docs/Active-go-ethereum-projects + - Other documents: /docs/other-documents
diff --git go-ethereum/docs/_includes/head.html celo/docs/_includes/head.html new file mode 100644 index 0000000000000000000000000000000000000000..6aaf2997702d38c324a85e3f3d64dd21f1cce193 --- /dev/null +++ celo/docs/_includes/head.html @@ -0,0 +1,43 @@ +<head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <title>{% if page.title %}{{ page.title }} | {% endif %}Celo Blockchain</title> + <!-- debug root:{% include link.html url="/debug-path" %} --> + + <link rel="icon" type="image/png" href="{% include link.html url=site.favicon %}" /> +{% for x in layout.common-css %}{% capture target %}{% include link.html url=x %}{% endcapture %} + <link rel="stylesheet" href="{{ target |strip }}" /> +{% endfor %} + +{% for x in page.css %}{% capture target %}{% include link.html url=x %}{% endcapture %} + <link rel="stylesheet" href="{{ target |strip }}" /> +{% endfor %} + +{% for x in layout.common-js %}{% capture target %}{% include link.html url=x %}{% endcapture %} + <script src="{{ target | strip }}"></script> +{% endfor %} + +{% for x in page.js %}{% capture target %}{% include link.html url=x %}{% endcapture %} + <script src="{{ target | strip }}"></script> +{% endfor %} + <!-- Matomo --> + <!-- TODO: What analytics do we want? + <script type="text/javascript"> + var _paq = window._paq || []; + /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ + _paq.push(['trackPageView']); + _paq.push(['enableLinkTracking']); + (function() { + var u="https://matomo.ethereum.org/"; + _paq.push(['setTrackerUrl', u+'matomo.php']); + _paq.push(['setSiteId', '22']); + var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; + g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); + })(); + </script> + <noscript><p><img src="https://matomo.ethereum.org/matomo.php?idsite=22&amp;rec=1" style="border:0;" alt="" /></p></noscript> + --> + <!-- End Matomo Code --> +</head>
diff --git go-ethereum/docs/_includes/link.html celo/docs/_includes/link.html new file mode 100644 index 0000000000000000000000000000000000000000..b66e4a17e1851117a47a551896dfe15dba5d90b5 --- /dev/null +++ celo/docs/_includes/link.html @@ -0,0 +1 @@ +{% if page.root %}{% assign root=page.root %}{% else %}{% assign root=site.default_root %}{% endif %}{{ include.url | remove: ".html" | prepend: root | replace: '//', '/' }} \ No newline at end of file
diff --git go-ethereum/docs/_includes/title.html celo/docs/_includes/title.html new file mode 100644 index 0000000000000000000000000000000000000000..09d443c62937061faa4193846a3e4060dae9a4bf --- /dev/null +++ celo/docs/_includes/title.html @@ -0,0 +1 @@ +{{ include.doc.title | remove_first: include.coll.label | remove_first: "/ " }} \ No newline at end of file
diff --git go-ethereum/docs/_layouts/default.html celo/docs/_layouts/default.html new file mode 100644 index 0000000000000000000000000000000000000000..8ebc7baec62bd5b9aa69c4e187e071c1057a818b --- /dev/null +++ celo/docs/_layouts/default.html @@ -0,0 +1,67 @@ +--- +common-css: +- /static/styles/bootstrap.min.css +- /static/styles/flatly.min.css +- /static/styles/font-awesome.min.css +- /static/styles/custom/common.css +common-js: +- /static/scripts/jquery.min.js +- /static/scripts/bootstrap.min.js +- /static/scripts/moment.min.js +- /static/scripts/marked.min.js +- /static/scripts/emojify.min.js +- /static/scripts/custom/polyfills.js +--- +<!DOCTYPE html> +<html lang="en"> + {% include head.html %} + + <body> + <nav class="navbar navbar-default navbar-fixed-top"> + <div class="container"> + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" href="{% include link.html url='' %}">Celo Blockchain</a> + </div> + <div id="navbar" class="navbar-collapse collapse"> + <ul class="nav navbar-nav"> + {% for link in site.data.navbar %} + {% capture target %}{% include link.html url=link.last %}{% endcapture %} + <li><a href="{{target |strip }}">{{ link.first }}</a></li> + {% endfor %} + </ul> + <!-- TODO: Re-enable search when we have client docs served from somewhere --> + <!-- + <form class="navbar-form pull-right" role="search" method="get" action="https://duckduckgo.com/"> + <input type="hidden" name="sites" value="geth.ethereum.org"> + <input type="hidden" name="kz" value="-1"> + <div class="input-group navbar-input-group-fixup"> + <input class="input-sm" type="text" name="q" placeholder="Search site..."> + <span class="input-group-btn"> + <button type="submit" class="btn btn-default btn-sm"><i class="fa fa-search" aria-hidden="true"></i></button> + </span> + </div> + </form> + --> + </div> + </div> + </nav> + {{ content }} + <hr/> + <footer class="container"> + {%- capture gh_link -%} + https://github.com/ethereum/go-ethereum/blob/master/docs + {%- if page.collection -%}{{ site.collections_dir }}/{%- endif -%} + {{- page.path -}} + {%- endcapture -%} + + <p>&copy; 2015–2020. The celo-blockchain Authors. <a href="{{ gh_link }}">Edit this page.</a></p> + <p>&copy; 2013–2019. The go-ethereum Authors.</p> + </footer> + </body> +</html>
diff --git go-ethereum/docs/_layouts/sidebar.html celo/docs/_layouts/sidebar.html new file mode 100644 index 0000000000000000000000000000000000000000..615fd2c6b76546e041dcc4ab3c9d4dcc1b6addcc --- /dev/null +++ celo/docs/_layouts/sidebar.html @@ -0,0 +1,41 @@ +--- +layout: default +--- + +{% assign toplevels = site.collections | where_exp: "item", "item.sidebar_index" | sort:"sidebar_index" %} +<div class="container" style="padding-top: 24px;"> + <div class="row"> + <div class="col-md-3" id="toc" style="padding-top: 16px;"> + {% for collection in toplevels %} + {% assign frontdoc = collection.docs | where_exp: "doc","doc.path == collection.frontpage" | first %} + {% unless frontdoc %} + {% assign frontdoc = collection.docs[0] %} + {% endunless %} + {% capture target %}{% include link.html url=frontdoc.url %}{% endcapture %} + <div class="list-group"> + <a class="list-group-item active" href="{{ target |strip }}">{{ collection.caption }}</a> + {% if page.collection == collection.label %} + {% assign docs_by_sort_key = collection.docs | group_by:"sort_key" | sort:"name", "last" %} + {% for group in docs_by_sort_key %} + {% assign docs_sorted = group.items | sort:"title" %} + {% for doc in docs_sorted %} + {% assign classmodifier="" %} + {% if doc.url == page.url %} + {% assign classmodifier="disabled" %} + {% endif %} + {% capture target %}{% include link.html url=doc.url %}{% endcapture %} + <a class="list-group-item {{classmodifier}}" href="{{ target |strip }}">{% include title.html doc=doc coll=collection %}</a> + {% endfor %} + {% endfor %} + {% endif %} + </div> + {% endfor %} + </div> + <div class="col-md-9" id="content"> + {% if page.title %} + <h1 class="featurette-heading">{% include title.html doc=page coll=page.collection %}</h1> + {% endif %} + {{ content }} + </div> + </div> +</div>
diff --git go-ethereum/docs/bin/check_redirect.sh celo/docs/bin/check_redirect.sh new file mode 100755 index 0000000000000000000000000000000000000000..16c0634326aee7d99db4b9b48e58462397bb6130 --- /dev/null +++ celo/docs/bin/check_redirect.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +urls=( + "http://geth.ethereum.org" + "https://geth.ethereum.org" + "http://geth.ethereum.org/" + "https://geth.ethereum.org/" + "http://geth.ethereum.org/install" + "https://geth.ethereum.org/install" + "http://geth.ethereum.org/install/" + "https://geth.ethereum.org/install/" + "http://ethereum.github.io/go-ethereum" + "https://ethereum.github.io/go-ethereum" + "http://ethereum.github.io/go-ethereum/" + "https://ethereum.github.io/go-ethereum/" + "http://ethereum.github.io/go-ethereum/install" + "https://ethereum.github.io/go-ethereum/install" + "http://ethereum.github.io/go-ethereum/install/" + "https://ethereum.github.io/go-ethereum/install/" +) +for u in "${urls[@]}" +do + echo "$u -> $(curl $u -w --silent -I 2>&1 | grep Location)" +done
(new)
+8
-0
diff --git go-ethereum/docs/doc.md celo/docs/doc.md new file mode 100644 index 0000000000000000000000000000000000000000..10c2acdd34a80037a8385e8b11ee6d445ed1bcd4 --- /dev/null +++ celo/docs/doc.md @@ -0,0 +1,8 @@ +--- +title: Other documents +root: .. +permalink: /doc/ +--- +{% for p in site.doc %} +* [{{ p.title }}]({% include link.html url=p.url %}) +{% endfor %}
diff --git go-ethereum/docs/docker-compose.yml celo/docs/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..30403fab50dbacd56a4ff0e5904f2b144cc73893 --- /dev/null +++ celo/docs/docker-compose.yml @@ -0,0 +1,7 @@ +jekyll: + image: jekyll/jekyll:latest + command: jekyll serve --watch --incremental + ports: + - 4000:4000 + volumes: + - .:/srv/jekyll \ No newline at end of file
diff --git go-ethereum/cmd/clef/rules.md celo/docs/docs/_clef/Rules.md rename from cmd/clef/rules.md rename to docs/docs/_clef/Rules.md index 112dae65122072c9863db16e2682c5d5e3337914..9441181716199f6a6645d0dd4dff4f1a6afaecb2 100644 --- go-ethereum/cmd/clef/rules.md +++ celo/docs/docs/_clef/Rules.md @@ -1,4 +1,7 @@ -# Rules +--- +title: Rules +sort_key: B +---   The `signer` binary contains a ruleset engine, implemented with [OttoVM](https://github.com/robertkrimen/otto)
diff --git go-ethereum/cmd/clef/docs/setup.md celo/docs/docs/_clef/Setup.md rename from cmd/clef/docs/setup.md rename to docs/docs/_clef/Setup.md index 6cc7a4120d97becbe84388f82572b87d3c6d6f71..297f8ddea3af3efbda95680fe2dab28f28c899f4 100644 --- go-ethereum/cmd/clef/docs/setup.md +++ celo/docs/docs/_clef/Setup.md @@ -1,4 +1,8 @@ -# Setting up Clef +--- +title: Advanced setup +sort_key: B +--- +   This document describes how Clef can be used in a more secure manner than executing it from your everyday laptop, in order to ensure that the keys remain safe in the event that your computer should get compromised. @@ -34,10 +38,10 @@   #### 1. Qubes Integrated   -Qubes provides a facility for inter-qubes communication via `qrexec`. A qube can request to make a cross-qube RPC request +Qubes provdes a facility for inter-qubes communication via `qrexec`. A qube can request to make a cross-qube RPC request to another qube. The OS then asks the user if the call is permitted.   -![Example](qubes/qrexec-example.png) +![Example](qrexec-example.png)   A policy-file can be created to allow such interaction. On the `target` domain, a service is invoked which can read the `stdin` from the `client` qube. @@ -46,11 +50,11 @@ This is how [Split GPG](https://www.qubes-os.org/doc/split-gpg/) is implemented. We can set up Clef the same way:   ##### Server   -![Clef via qrexec](qubes/clef_qubes_qrexec.png) +![Clef via qrexec](clef_qubes_qrexec.png)   -On the `target` qubes, we need to define the RPC service. +On the `target` qubes, we need to define the rpc service.   -[qubes.Clefsign](qubes/qubes.Clefsign): +[qubes.Clefsign](qubes.Clefsign):   ```bash #!/bin/bash @@ -94,7 +98,7 @@ On the `client` qube, we need to create a listener which will receive the request from the Dapp, and proxy it.   -[qubes-client.py](qubes/qubes-client.py): +[qubes-client.py](qubes-client.py):   ```python   @@ -135,13 +139,13 @@ $ cat newaccnt.json| qrexec-client-vm debian-work qubes.Clefsign ```   -A dialog should pop up first to allow the IPC call: +This should pop up first a dialog to allow the IPC call:   -![one](qubes/qubes_newaccount-1.png) +![one](qubes_newaccount-1.png)   -Followed by a GTK-dialog to approve the operation: +Followed by a GTK-dialog to approve the operation   -![two](qubes/qubes_newaccount-2.png) +![two](qubes_newaccount-2.png)   To test the full flow, we use the client wrapper. Start it on the `client` qube: ``` @@ -169,7 +173,7 @@ - The `Host` header is most likely `localhost` - The `Origin` header must be forwarded - Information about the remote ip must be added as a `X-Forwarded-For`. However, Clef cannot always trust an `XFF` header, since malicious clients may lie about `XFF` in order to fool the http server into believing it comes from another address. -- Even with a policy in place to allow RPC calls between `caller` and `target`, there will be several popups: +- Even with a policy in place to allow rpc-calls between `caller` and `target`, there will be several popups: - One qubes-specific where the user specifies the `target` vm - One clef-specific to approve the transaction   @@ -177,22 +181,22 @@ #### 2. Network integrated   The second way to set up Clef on a qubes system is to allow networking, and have Clef listen to a port which is accessible -from other qubes. +form other qubes.   -![Clef via http](qubes/clef_qubes_http.png) +![Clef via http](clef_qubes_http.png)     ## USBArmory   -The [USB armory](https://inversepath.com/usbarmory) is an open source hardware design with an 800 MHz ARM processor. It is a pocket-size +The [USB armory](https://inversepath.com/usbarmory) is an open source hardware design with an 800 Mhz ARM processor. It is a pocket-size computer. When inserted into a laptop, it identifies itself as a USB network interface, basically adding another network to your computer. Over this new network interface, you can SSH into the device.   Running Clef off a USB armory means that you can use the armory as a very versatile offline computer, which only ever connects to a local network between your computer and the device itself.   -Needless to say, while this model should be fairly secure against remote attacks, an attacker with physical access +Needless to say, the while this model should be fairly secure against remote attacks, an attacker with physical access to the USB Armory would trivially be able to extract the contents of the device filesystem.
diff --git go-ethereum/cmd/clef/tutorial.md celo/docs/docs/_clef/Tutorial.md rename from cmd/clef/tutorial.md rename to docs/docs/_clef/Tutorial.md index 3ea662b5d4c70643759bc653da10ccb86d16e89e..4b2c9551e1c7da66d3bb185d8eba6ee25eb30668 100644 --- go-ethereum/cmd/clef/tutorial.md +++ celo/docs/docs/_clef/Tutorial.md @@ -1,6 +1,11 @@ +--- +title: Tutorial +sort_key: A +--- + ## Initializing Clef   -First things first, Clef needs to store some data itself. Since that data might be sensitive (passwords, signing rules, accounts), Clef's entire storage is encrypted. To support encrypting data, the first step is to initialize Clef with a random master seed, itself too encrypted with your chosen password: +First thing's first, Clef needs to store some data itself. Since that data might be sensitive (passwords, signing rules, accounts), Clef's entire storage is encrypted. To support encrypting data, the first step is to initialize Clef with a random master seed, itself too encrypted with your chosen password:   ```text $ clef init @@ -22,8 +27,8 @@ > ok   The master seed of clef will be locked with a password. Please specify a password. Do not forget this password! -Password: -Repeat password: +Passphrase: +Repeat passphrase:   A master seed has been generated into /home/martin/.clef/masterseed.json   @@ -100,7 +105,7 @@ {"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"Request denied"}} ```   -Apart from listing accounts, you can also *request* creating a new account; signing transactions and data; and recovering signatures. You can find the available methods in the Clef [External API Spec](https://github.com/ethereum/go-ethereum/tree/master/cmd/clef#external-api-1) and the [External API Changelog](https://github.com/ethereum/go-ethereum/blob/master/cmd/clef/extapi_changelog.md). +Apart from listing accounts, you can also *request* creating a new account; signing transactions and data; and recovering signatures. You can find the available methods in the Clef [External API Spec](https://github.com/ethereum/go-ethereum/tree/master/cmd/clef#external-api-1) and the [External API Changelog](https://github.com/celo-org/celo-blockchain/blob/master/cmd/clef/extapi_changelog.md).   *Note, the number of things you can do from the External API is deliberately small, since we want to limit the power of remote calls by as much as possible! Clef has an [Internal API](https://github.com/ethereum/go-ethereum/tree/master/cmd/clef#ui-api-1) too for the UI (User Interface) which is much richer and can support custom interfaces on top. But that's out of scope here.*   @@ -124,7 +129,7 @@ 645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c rules.js   $ clef attest 645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c Decrypt master seed of clef -Password: +Passphrase: INFO [07-01|13:25:03.290] Ruleset attestation updated sha256=645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c ```   @@ -193,12 +198,12 @@ ```text $ clef setpw 0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3   -Please enter a password to store for this address: -Password: -Repeat password: +Please enter a passphrase to store for this address: +Passphrase: +Repeat passphrase:   Decrypt master seed of clef -Password: +Passphrase: INFO [07-01|14:05:56.031] Credential store updated key=0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3 ```   @@ -237,7 +242,7 @@ f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178 rules.js   $ clef attest f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178 Decrypt master seed of clef -Password: +Passphrase: INFO [07-01|14:11:28.509] Ruleset attestation updated sha256=f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178 ```
diff --git go-ethereum/docs/docs/_clef/apis.md celo/docs/docs/_clef/apis.md new file mode 100644 index 0000000000000000000000000000000000000000..1932a87fd7f663d03278d56b620f5b13b0adcb77 --- /dev/null +++ celo/docs/docs/_clef/apis.md @@ -0,0 +1,817 @@ +--- +title: Communication APIs +sort_key: C +--- + +### External API + +The signer listens to HTTP requests on `rpcaddr`:`rpcport`, with the same JSONRPC standard as Geth. The messages are +expected to be JSON [jsonrpc 2.0 standard](http://www.jsonrpc.org/specification). + +Some of these call can require user interaction. Clients must be aware that responses +may be delayed significantly or may never be received if a users decides to ignore the confirmation request. + +The External API is **untrusted** : it does not accept credentials over this api, nor does it expect +that requests have any authority. + +### UI API + +The signer has one native console-based UI, for operation without any standalone tools. +However, there is also an API to communicate with an external UI. To enable that UI, +the signer needs to be executed with the `--stdio-ui` option, which allocates the +`stdin`/`stdout` for the UI-api. + +An example (insecure) proof-of-concept of has been implemented in `pythonsigner.py`. + +The model is as follows: + +* The user starts the UI app (`pythonsigner.py`). +* The UI app starts the `signer` with `--stdio-ui`, and listens to the +process output for confirmation-requests. +* The `signer` opens the external http api. +* When the `signer` receives requests, it sends a `jsonrpc` request via `stdout`. +* The UI app prompts the user accordingly, and responds to the `signer` +* The `signer` signs (or not), and responds to the original request. + +### More resoruces + + +* Changelog for [External API](https://github.com/ethereum/go-ethereum/blob/master/cmd/clef/extapi_changelog.md) +* Changelog for [UI API](https://github.com/ethereum/go-ethereum/blob/master/cmd/clef/intapi_changelog.md) +* Documentation about [Datatypes](datatypes) + + +## External API + +See the [external api changelog](https://github.com/ethereum/go-ethereum/blob/master/cmd/clef/extapi_changelog.md) for information about changes to this API. + + +### Encoding +- number: positive integers that are hex encoded +- data: hex encoded data +- string: ASCII string + +All hex encoded values must be prefixed with `0x`. + +## Methods + +### account_new + +#### Create new password protected account + +The signer will generate a new private key, encrypts it according to [web3 keystore spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) and stores it in the keystore directory. +The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving lost accounts. + +#### Arguments + +None + +#### Result + - address [string]: account address that is derived from the generated key + - url [string]: location of the keyfile + +#### Sample call +```json +{ + "id": 0, + "jsonrpc": "2.0", + "method": "account_new", + "params": [] +} +``` +Response +``` +{ + "id": 0, + "jsonrpc": "2.0", + "result": { + "address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133", + "url": "keystore:///my/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133" + } +} +``` + +### account_list + +#### List available accounts + List all accounts that this signer currently manages + +#### Arguments + +None + +#### Result + - array with account records: + - account.address [string]: account address that is derived from the generated key + - account.type [string]: type of the + - account.url [string]: location of the account + +#### Sample call +```json +{ + "id": 1, + "jsonrpc": "2.0", + "method": "account_list" +} +``` +Response +``` +{ + "id": 1, + "jsonrpc": "2.0", + "result": [ + { + "address": "0xafb2f771f58513609765698f65d3f2f0224a956f", + "type": "account", + "url": "keystore:///tmp/keystore/UTC--2017-08-24T07-26-47.162109726Z--afb2f771f58513609765698f65d3f2f0224a956f" + }, + { + "address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133", + "type": "account", + "url": "keystore:///tmp/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133" + } + ] +} +``` + +### account_signTransaction + +#### Sign transactions + Signs a transactions and responds with the signed transaction in RLP encoded form. + +#### Arguments + 2. transaction object: + - `from` [address]: account to send the transaction from + - `to` [address]: receiver account. If omitted or `0x`, will cause contract creation. + - `gas` [number]: maximum amount of gas to burn + - `gasPrice` [number]: gas price + - `value` [number:optional]: amount of Wei to send with the transaction + - `data` [data:optional]: input data + - `nonce` [number]: account nonce + 3. method signature [string:optional] + - The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected. + + +#### Result + - signed transaction in RLP encoded form [data] + +#### Sample call +```json +{ + "id": 2, + "jsonrpc": "2.0", + "method": "account_signTransaction", + "params": [ + { + "from": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", + "gas": "0x55555", + "gasPrice": "0x1234", + "input": "0xabcd", + "nonce": "0x0", + "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "value": "0x1234" + } + ] +} +``` +Response + +```json +{ + "id": 2, + "jsonrpc": "2.0", + "error": { + "code": -32000, + "message": "Request denied" + } +} +``` +#### Sample call with ABI-data + + +```json +{ + "id": 67, + "jsonrpc": "2.0", + "method": "account_signTransaction", + "params": [ + { + "from": "0x694267f14675d7e1b9494fd8d72fefe1755710fa", + "gas": "0x333", + "gasPrice": "0x1", + "nonce": "0x0", + "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "value": "0x0", + "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012" + }, + "safeSend(address)" + ] +} +``` +Response + +```json +{ + "jsonrpc": "2.0", + "id": 67, + "result": { + "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", + "tx": { + "nonce": "0x0", + "gasPrice": "0x1", + "gas": "0x333", + "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "value": "0x0", + "input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", + "v": "0x26", + "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e", + "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", + "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e" + } + } +} +``` + +Bash example: +```bash +#curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/ + +{"jsonrpc":"2.0","id":67,"result":{"raw":"0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","tx":{"nonce":"0x0","gasPrice":"0x1","gas":"0x333","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0","value":"0x0","input":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012","v":"0x26","r":"0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e","s":"0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","hash":"0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"}}} +``` + +### account_signData + +#### Sign data + Signs a chunk of data and returns the calculated signature. + +#### Arguments + - content type [string]: type of signed data + - `text/validator`: hex data with custom validator defined in a contract + - `application/clique`: [clique](https://github.com/ethereum/EIPs/issues/225) headers + - `text/plain`: simple hex data validated by `account_ecRecover` + - account [address]: account to sign with + - data [object]: data to sign + +#### Result + - calculated signature [data] + +#### Sample call +```json +{ + "id": 3, + "jsonrpc": "2.0", + "method": "account_signData", + "params": [ + "data/plain", + "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", + "0xaabbccdd" + ] +} +``` +Response + +```json +{ + "id": 3, + "jsonrpc": "2.0", + "result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c" +} +``` + +### account_signTypedData + +#### Sign data + Signs a chunk of structured data conformant to [EIP712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) and returns the calculated signature. + +#### Arguments + - account [address]: account to sign with + - data [object]: data to sign + +#### Result + - calculated signature [data] + +#### Sample call +```json +{ + "id": 68, + "jsonrpc": "2.0", + "method": "account_signTypedData", + "params": [ + "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", + { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person" + }, + { + "name": "contents", + "type": "string" + } + ] + }, + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" + } + } + ] +} +``` +Response + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c" +} +``` + +### account_ecRecover + +#### Sign data + +Derive the address from the account that was used to sign data with content type `text/plain` and the signature. + +#### Arguments + - data [data]: data that was signed + - signature [data]: the signature to verify + +#### Result + - derived account [address] + +#### Sample call +```json +{ + "id": 4, + "jsonrpc": "2.0", + "method": "account_ecRecover", + "params": [ + "data/plain", + "0xaabbccdd", + "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c" + ] +} +``` +Response + +```json +{ + "id": 4, + "jsonrpc": "2.0", + "result": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db" +} +``` + +### account_import + +#### Import account + Import a private key into the keystore. The imported key is expected to be encrypted according to the web3 keystore + format. + +#### Arguments + - account [object]: key in [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) (retrieved with account_export) + +#### Result + - imported key [object]: + - key.address [address]: address of the imported key + - key.type [string]: type of the account + - key.url [string]: key URL + +#### Sample call +```json +{ + "id": 6, + "jsonrpc": "2.0", + "method": "account_import", + "params": [ + { + "address": "c7412fc59930fd90099c917a50e5f11d0934b2f5", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "401c39a7c7af0388491c3d3ecb39f532" + }, + "ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 262144, + "p": 1, + "r": 8, + "salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a" + }, + "mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806" + }, + "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9", + "version": 3 + } + ] +} +``` +Response + +```json +{ + "id": 6, + "jsonrpc": "2.0", + "result": { + "address": "0xc7412fc59930fd90099c917a50e5f11d0934b2f5", + "type": "account", + "url": "keystore:///tmp/keystore/UTC--2017-08-24T11-00-42.032024108Z--c7412fc59930fd90099c917a50e5f11d0934b2f5" + } +} +``` + +### account_export + +#### Export account from keystore + Export a private key from the keystore. The exported private key is encrypted with the original passphrase. When the + key is imported later this passphrase is required. + +#### Arguments + - account [address]: export private key that is associated with this account + +#### Result + - exported key, see [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) for + more information + +#### Sample call +```json +{ + "id": 5, + "jsonrpc": "2.0", + "method": "account_export", + "params": [ + "0xc7412fc59930fd90099c917a50e5f11d0934b2f5" + ] +} +``` +Response + +```json +{ + "id": 5, + "jsonrpc": "2.0", + "result": { + "address": "c7412fc59930fd90099c917a50e5f11d0934b2f5", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "401c39a7c7af0388491c3d3ecb39f532" + }, + "ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 262144, + "p": 1, + "r": 8, + "salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a" + }, + "mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806" + }, + "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9", + "version": 3 + } +} +``` + + + +## UI API + +These methods needs to be implemented by a UI listener. + +By starting the signer with the switch `--stdio-ui-test`, the signer will invoke all known methods, and expect the UI to respond with +denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented. +See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'. + +All methods in this API uses object-based parameters, so that there can be no mixups of parameters: each piece of data is accessed by key. + +See the [ui api changelog](https://github.com/ethereum/go-ethereum/blob/master/cmd/clef/intapi_changelog.md) for information about changes to this API. + +OBS! A slight deviation from `json` standard is in place: every request and response should be confined to a single line. +Whereas the `json` specification allows for linebreaks, linebreaks __should not__ be used in this communication channel, to make +things simpler for both parties. + +### ApproveTx / `ui_approveTx` + +Invoked when there's a transaction for approval. + + +#### Sample call + +Here's a method invocation: +```bash + +curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/ +``` +Results in the following invocation on the UI: +```json + +{ + "jsonrpc": "2.0", + "id": 1, + "method": "ui_approveTx", + "params": [ + { + "transaction": { + "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", + "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "gas": "0x333", + "gasPrice": "0x1", + "value": "0x0", + "nonce": "0x0", + "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", + "input": null + }, + "call_info": [ + { + "type": "WARNING", + "message": "Invalid checksum on to-address" + }, + { + "type": "Info", + "message": "safeSend(address: 0x0000000000000000000000000000000000000012)" + } + ], + "meta": { + "remote": "127.0.0.1:48486", + "local": "localhost:8550", + "scheme": "HTTP/1.1" + } + } + ] +} + +``` + +The same method invocation, but with invalid data: +```bash + +curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000002000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/ +``` + +```json + +{ + "jsonrpc": "2.0", + "id": 1, + "method": "ui_approveTx", + "params": [ + { + "transaction": { + "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", + "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "gas": "0x333", + "gasPrice": "0x1", + "value": "0x0", + "nonce": "0x0", + "data": "0x4401a6e40000000000000002000000000000000000000000000000000000000000000012", + "input": null + }, + "call_info": [ + { + "type": "WARNING", + "message": "Invalid checksum on to-address" + }, + { + "type": "WARNING", + "message": "Transaction data did not match ABI-interface: WARNING: Supplied data is stuffed with extra data. \nWant 0000000000000002000000000000000000000000000000000000000000000012\nHave 0000000000000000000000000000000000000000000000000000000000000012\nfor method safeSend(address)" + } + ], + "meta": { + "remote": "127.0.0.1:48492", + "local": "localhost:8550", + "scheme": "HTTP/1.1" + } + } + ] +} + + +``` + +One which has missing `to`, but with no `data`: + + +```json + +{ + "jsonrpc": "2.0", + "id": 3, + "method": "ui_approveTx", + "params": [ + { + "transaction": { + "from": "", + "to": null, + "gas": "0x0", + "gasPrice": "0x0", + "value": "0x0", + "nonce": "0x0", + "data": null, + "input": null + }, + "call_info": [ + { + "type": "CRITICAL", + "message": "Tx will create contract with empty code!" + } + ], + "meta": { + "remote": "signer binary", + "local": "main", + "scheme": "in-proc" + } + } + ] +} +``` + +### ApproveListing / `ui_approveListing` + +Invoked when a request for account listing has been made. + +#### Sample call + +```json + +{ + "jsonrpc": "2.0", + "id": 5, + "method": "ui_approveListing", + "params": [ + { + "accounts": [ + { + "type": "Account", + "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42", + "address": "0x123409812340981234098123409812deadbeef42" + }, + { + "type": "Account", + "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42", + "address": "0xcafebabedeadbeef34098123409812deadbeef42" + } + ], + "meta": { + "remote": "signer binary", + "local": "main", + "scheme": "in-proc" + } + } + ] +} + +``` + + +### ApproveSignData / `ui_approveSignData` + +#### Sample call + +```json +{ + "jsonrpc": "2.0", + "id": 4, + "method": "ui_approveSignData", + "params": [ + { + "address": "0x123409812340981234098123409812deadbeef42", + "raw_data": "0x01020304", + "message": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004", + "hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310", + "meta": { + "remote": "signer binary", + "local": "main", + "scheme": "in-proc" + } + } + ] +} + +``` + +### ShowInfo / `ui_showInfo` + +The UI should show the info to the user. Does not expect response. + +#### Sample call + +```json +{ + "jsonrpc": "2.0", + "id": 9, + "method": "ui_showInfo", + "params": [ + { + "text": "Tests completed" + } + ] +} + +``` + +### ShowError / `ui_showError` + +The UI should show the info to the user. Does not expect response. + +```json + +{ + "jsonrpc": "2.0", + "id": 2, + "method": "ShowError", + "params": [ + { + "text": "Testing 'ShowError'" + } + ] +} + +``` + +### OnApprovedTx / `ui_onApprovedTx` + +`OnApprovedTx` is called when a transaction has been approved and signed. The call contains the return value that will be sent to the external caller. The return value from this method is ignored - the reason for having this callback is to allow the ruleset to keep track of approved transactions. + +When implementing rate-limited rules, this callback should be used. + +TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`. + +### OnSignerStartup / `ui_onSignerStartup` + +This method provide the UI with information about what API version the signer uses (both internal and external) aswell as build-info and external api, +in k/v-form. + +Example call: +```json + +{ + "jsonrpc": "2.0", + "id": 1, + "method": "ui_onSignerStartup", + "params": [ + { + "info": { + "extapi_http": "http://localhost:8550", + "extapi_ipc": null, + "extapi_version": "2.0.0", + "intapi_version": "1.2.0" + } + } + ] +} + +``` +
diff --git go-ethereum/cmd/clef/docs/clef_architecture_pt1.png celo/docs/docs/_clef/clef_architecture_pt1.png rename from cmd/clef/docs/clef_architecture_pt1.png rename to docs/docs/_clef/clef_architecture_pt1.png
diff --git go-ethereum/cmd/clef/docs/clef_architecture_pt2.png celo/docs/docs/_clef/clef_architecture_pt2.png rename from cmd/clef/docs/clef_architecture_pt2.png rename to docs/docs/_clef/clef_architecture_pt2.png
diff --git go-ethereum/cmd/clef/docs/clef_architecture_pt3.png celo/docs/docs/_clef/clef_architecture_pt3.png rename from cmd/clef/docs/clef_architecture_pt3.png rename to docs/docs/_clef/clef_architecture_pt3.png
diff --git go-ethereum/cmd/clef/docs/clef_architecture_pt4.png celo/docs/docs/_clef/clef_architecture_pt4.png rename from cmd/clef/docs/clef_architecture_pt4.png rename to docs/docs/_clef/clef_architecture_pt4.png
diff --git go-ethereum/cmd/clef/datatypes.md celo/docs/docs/_clef/datatypes.md rename from cmd/clef/datatypes.md rename to docs/docs/_clef/datatypes.md index dd8cda584649e107d7fe1bdb6539f85145b74063..75f7e1914f3e3de9a2ee1485325ae79ffbb3a0ef 100644 --- go-ethereum/cmd/clef/datatypes.md +++ celo/docs/docs/_clef/datatypes.md @@ -1,9 +1,14 @@ +--- +title: Communication data types +sort_key: C +--- + ## UI Client interface   These data types are defined in the channel between clef and the UI ### SignDataRequest   -SignDataRequest contains information about a pending request to sign some data. The data to be signed can be of various types, defined by content-type. Clef has done most of the work in canonicalizing and making sense of the data, and it's up to the UI to present the user with the contents of the `message` +SignDataRequest contains information about a pending request to sign some data. The data to be signed can be of various types, defined by content-type. Clef has done most of the work in canonicalizing and making sense of the data, and it's up to the UI to presentthe user with the contents of the `message`   Example: ```json
diff --git go-ethereum/cmd/clef/docs/qubes/clef_qubes_http.png celo/docs/docs/_clef/qubes/clef_qubes_http.png rename from cmd/clef/docs/qubes/clef_qubes_http.png rename to docs/docs/_clef/qubes/clef_qubes_http.png
diff --git go-ethereum/cmd/clef/docs/qubes/clef_qubes_qrexec.png celo/docs/docs/_clef/qubes/clef_qubes_qrexec.png rename from cmd/clef/docs/qubes/clef_qubes_qrexec.png rename to docs/docs/_clef/qubes/clef_qubes_qrexec.png
diff --git go-ethereum/cmd/clef/docs/qubes/qrexec-example.png celo/docs/docs/_clef/qubes/qrexec-example.png rename from cmd/clef/docs/qubes/qrexec-example.png rename to docs/docs/_clef/qubes/qrexec-example.png
diff --git go-ethereum/cmd/clef/docs/qubes/qubes-client.py celo/docs/docs/_clef/qubes/qubes-client.py rename from cmd/clef/docs/qubes/qubes-client.py rename to docs/docs/_clef/qubes/qubes-client.py
diff --git go-ethereum/cmd/clef/docs/qubes/qubes.Clefsign celo/docs/docs/_clef/qubes/qubes.Clefsign rename from cmd/clef/docs/qubes/qubes.Clefsign rename to docs/docs/_clef/qubes/qubes.Clefsign
diff --git go-ethereum/cmd/clef/docs/qubes/qubes_newaccount-1.png celo/docs/docs/_clef/qubes/qubes_newaccount-1.png rename from cmd/clef/docs/qubes/qubes_newaccount-1.png rename to docs/docs/_clef/qubes/qubes_newaccount-1.png
diff --git go-ethereum/cmd/clef/docs/qubes/qubes_newaccount-2.png celo/docs/docs/_clef/qubes/qubes_newaccount-2.png rename from cmd/clef/docs/qubes/qubes_newaccount-2.png rename to docs/docs/_clef/qubes/qubes_newaccount-2.png
diff --git go-ethereum/docs/docs/_clef/sign_flow.png celo/docs/docs/_clef/sign_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..93ef81a32e8ee72d68f59dbad9db6b03ac7fb782 Binary files /dev/null and celo/docs/docs/_clef/sign_flow.png differ
diff --git go-ethereum/docs/docs/_dapp/mobile-accounts.md celo/docs/docs/_dapp/mobile-accounts.md new file mode 100644 index 0000000000000000000000000000000000000000..1b9bbd2d07b7081f60c3408c86190d7287277a89 --- /dev/null +++ celo/docs/docs/_dapp/mobile-accounts.md @@ -0,0 +1,324 @@ +--- +title: Mobile Account Management +--- + +To provide Ethereum integration for your mobile applications, the very first thing you +should be interested in doing is account management. + +Although all current leading Ethereum implementations provide account management built in, +it is ill advised to keep accounts in any location that is shared between multiple +applications and/or multiple people. The same way you do not entrust your ISP (who is +after all your gateway into the internet) with your login credentials; you should not +entrust an Ethereum node (who is your gateway into the Ethereum network) with your +credentials either. + +The proper way to handle user accounts in your mobile applications is to do client side +account management, everything self-contained within your own application. This way you +can ensure as fine grained (or as coarse) access permissions to the sensitive data as +deemed necessary, without relying on any third party application's functionality and/or +vulnerabilities. + +To support this, `go-ethereum` provides a simple, yet thorough accounts library that gives +you all the tools to do properly secured account management via encrypted keystores and +passphrase protected accounts. You can leverage all the security of the `go-ethereum` +crypto implementation while at the same time running everything in your own application. + +## Encrypted keystores + +Although handling your users' accounts locally on their own mobile device does provide +certain security guarantees, access keys to Ethereum accounts should never lay around in +clear-text form. As such, we provide an encrypted keystore that provides the proper +security guarantees for you without requiring a thorough understanding from your part of +the associated cryptographic primitives. + +The important thing to know when using the encrypted keystore is that the cryptographic +primitives used within can operate either in *standard* or *light* mode. The former +provides a higher level of security at the cost of increased computational burden and +resource consumption: + + * *standard* needs 256MB memory and 1 second processing on a modern CPU to access a key + * *light* needs 4MB memory and 100 millisecond processing on a modern CPU to access a key + +As such, *light* is more suitable for mobile applications, but you should be aware of the +trade-offs nonetheless. + +*For those interested in the cryptographic and/or implementation details, the key-store +uses the `secp256k1` elliptic curve as defined in the [Standards for Efficient +Cryptography](sec2), implemented by the [`libsecp256k`](secp256k1) library and wrapped by +[`github.com/ethereum/go-ethereum/accounts`](accounts-go). Accounts are stored on disk in +the [Web3 Secret Storage](secstore) format.* + +[sec2]: http://www.secg.org/sec2-v2.pdf +[accounts-go]: https://godoc.org/github.com/ethereum/go-ethereum/accounts +[secp256k1]: https://github.com/bitcoin-core/secp256k1 +[secstore]: https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition + +### Keystores on Android (Java) + +The encrypted keystore on Android is implemented by the `KeyStore` class from the +`org.ethereum.geth` package. The configuration constants (for the *standard* or *light* +security modes described above) are located in the `Geth` abstract class, similarly from +the `org.ethereum.geth` package. Hence to do client side account management on Android, +you'll need to import two classes into your Java code: + +```java +import org.ethereum.geth.Geth; +import org.ethereum.geth.KeyStore; +``` + +Afterwards you can create a new encrypted keystore via: + +```java +KeyStore ks = new KeyStore("/path/to/keystore", Geth.LightScryptN, Geth.LightScryptP); +``` + +The path to the keystore folder needs to be a location that is writable by the local +mobile application but non-readable for other installed applications (for security reasons +obviously), so we'd recommend placing it inside your app's data directory. If you are +creating the `KeyStore` from within a class extending an Android object, you will most +probably have access to the `Context.getFilesDir()` method via `this.getFilesDir()`, so +you could set the keystore path to `this.getFilesDir() + "/keystore"`. + +The last two arguments of the `KeyStore` constructor are the crypto parameters defining +how resource-intensive the keystore encryption should be. You can choose between +`Geth.StandardScryptN, Geth.StandardScryptP`, `Geth.LightScryptN, Geth.LightScryptP` or +specify your own numbers (please make sure you understand the underlying cryptography for +this). We recommend using the *light* version. + +### Keystores on iOS (Swift 3) + +The encrypted keystore on iOS is implemented by the `GethKeyStore` class from the `Geth` +framework. The configuration constants (for the *standard* or *light* security modes +described above) are located in the same namespace as global variables. Hence to do client +side account management on iOS, you'll need to import the framework into your Swift code: + +```swift +import Geth +``` + +Afterwards you can create a new encrypted account manager via: + +```swift +let ks = GethNewKeyStore("/path/to/keystore", GethLightScryptN, GethLightScryptP); +``` + +The path to the keystore folder needs to be a location that is writable by the local +mobile application but non-readable for other installed applications (for security reasons +obviously), so we'd recommend placing it inside your app's document directory. You should +be able to retrieve the document directory via `let datadir = +NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]`, so you +could set the keystore path to `datadir + "/keystore"`. + +The last two arguments of the `GethNewKeyStore` factory method are the crypto parameters +defining how resource-intensive the keystore encryption should be. You can choose between +`GethStandardScryptN, GethStandardScryptP`, `GethLightScryptN, GethLightScryptP` or +specify your own numbers (please make sure you understand the underlying cryptography for +this). We recommend using the *light* version. + +## Account lifecycle + +Having created an encrypted keystore for your Ethereum accounts, you can use this for the +entire account lifecycle requirements of your mobile application. This includes the basic +functionality of creating new accounts and deleting existing ones; as well as the more +advanced functionality of updating access credentials, exporting existing accounts, and +importing them on another device. + +Although the keystore defines the encryption strength it uses to store your accounts, +there is no global master password that can grant access to all of them. Rather each +account is maintained individually, and stored on disk in its [encrypted +format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) +individually, ensuring a much cleaner and stricter separation of credentials. + +This individuality however means that any operation requiring access to an account will +need to provide the necessary authentication credentials for that particular account in +the form of a passphrase: + + * When creating a new account, the caller must supply a passphrase to encrypt the account + with. This passphrase will be required for any subsequent access, the lack of which + will forever forfeit using the newly created account. + * When deleting an existing account, the caller must supply a passphrase to verify + ownership of the account. This isn't cryptographically necessary, rather a protective + measure against accidental loss of accounts. + * When updating an existing account, the caller must supply both current and new + passphrases. After completing the operation, the account will not be accessible via the + old passphrase any more. + * When exporting an existing account, the caller must supply both the current passphrase + to decrypt the account, as well as an export passphrase to re-encrypt it with before + returning the key-file to the user. This is required to allow moving accounts between + devices without sharing original credentials. + * When importing a new account, the caller must supply both the encryption passphrase of + the key-file being imported, as well as a new passhprase with which to store the + account. This is required to allow storing account with different credentials than used + for moving them around. + +*Please note, there is no recovery mechanisms for losing the passphrases. The +cryptographic properties of the encrypted keystore (if using the provided parameters) +guarantee that account credentials cannot be brute forced in any meaningful time.* + +### Accounts on Android (Java) + +An Ethereum account on Android is implemented by the `Account` class from the +`org.ethereum.geth` package. Assuming we already have an instance of a `KeyStore` called +`ks` from the previous section, we can easily execute all of the described lifecycle +operations with a handful of function calls. + +```java +// Create a new account with the specified encryption passphrase. +Account newAcc = ksm.newAccount("Creation password"); + +// Export the newly created account with a different passphrase. The returned +// data from this method invocation is a JSON encoded, encrypted key-file. +byte[] jsonAcc = ks.exportKey(newAcc, "Creation password", "Export password"); + +// Update the passphrase on the account created above inside the local keystore. +ks.updateAccount(newAcc, "Creation password", "Update password"); + +// Delete the account updated above from the local keystore. +ks.deleteAccount(newAcc, "Update password"); + +// Import back the account we've exported (and then deleted) above with yet +// again a fresh passphrase. +Account impAcc = ks.importKey(jsonAcc, "Export password", "Import password"); +``` + +*Although instances of `Account` can be used to access various information about specific +Ethereum accounts, they do not contain any sensitive data (such as passphrases or private +keys), rather act solely as identifiers for client code and the keystore.* + +### Accounts on iOS (Swift 3) + +An Ethereum account on iOS is implemented by the `GethAccount` class from the `Geth` +framework. Assuming we already have an instance of a `GethKeyStore` called `ks` from the +previous section, we can easily execute all of the described lifecycle operations with a +handful of function calls. + +```swift +// Create a new account with the specified encryption passphrase. +let newAcc = try! ks?.newAccount("Creation password") + +// Export the newly created account with a different passphrase. The returned +// data from this method invocation is a JSON encoded, encrypted key-file. +let jsonKey = try! ks?.exportKey(newAcc!, passphrase: "Creation password", newPassphrase: "Export password") + +// Update the passphrase on the account created above inside the local keystore. +try! ks?.update(newAcc, passphrase: "Creation password", newPassphrase: "Update password") + +// Delete the account updated above from the local keystore. +try! ks?.delete(newAcc, passphrase: "Update password") + +// Import back the account we've exported (and then deleted) above with yet +// again a fresh passphrase. +let impAcc = try! ks?.importKey(jsonKey, passphrase: "Export password", newPassphrase: "Import password") +``` + +*Although instances of `GethAccount` can be used to access various information about +specific Ethereum accounts, they do not contain any sensitive data (such as passphrases or +private keys), rather act solely as identifiers for client code and the keystore.* + +## Signing authorization + +As mentioned above, account objects do not hold the sensitive private keys of the +associated Ethereum accounts, but are merely placeholders to identify the cryptographic +keys with. All operations that require authorization (e.g. transaction signing) are +performed by the account manager after granting it access to the private keys. + +There are a few different ways one can authorize the account manager to execute signing +operations, each having its advantages and drawbacks. Since the different methods have +wildly different security guarantees, it is essential to be clear on how each works: + + * **Single authorization**: The simplest way to sign a transaction via the keystore is to + provide the passphrase of the account every time something needs to be signed, which + will ephemerally decrypt the private key, execute the signing operation and immediately + throw away the decrypted key. The drawbacks are that the passphrase needs to be queried + from the user every time, which can become annoying if done frequently; or the + application needs to keep the passphrase in memory, which can have security + consequences if not done properly; and depending on the keystore's configured strength, + constantly decrypting keys can result in non-negligible resource requirements. + * **Multiple authorizations**: A more complex way of signing transactions via the + keystore is to unlock the account via its passphrase once, and allow the account + manager to cache the decrypted private key, enabling all subsequent signing requests to + complete without the passphrase. The lifetime of the cached private key may be managed + manually (by explicitly locking the account back up) or automatically (by providing a + timeout during unlock). This mechanism is useful for scenarios where the user may need + to sign many transactions or the application would need to do so without requiring user + input. The crucial aspect to remember is that **anyone with access to the account + manager can sign transactions while a particular account is unlocked** (e.g. device + left unattended; application running untrusted code). + +*Note, creating transactions is out of scope here, so the remainder of this section will +assume we already have a transaction to sign, and will focus only on creating an +authorized version of it. Creating an actually meaningful transaction will be covered +later.* + +### Signing on Android (Java) + +Assuming we already have an instance of a `KeyStore` called `ks` from the previous +sections, we can create a new account to sign transactions with via it's already +demonstrated `newAccount` method; and to avoid going into transaction creation for now, we +can hard-code a random transaction to sign instead. + +```java +// Create a new account to sign transactions with +Account signer = ks.newAccount("Signer password"); +Transaction tx = new Transaction( + 1, new Address("0x0000000000000000000000000000000000000000"), + new BigInt(0), new BigInt(0), new BigInt(1), null); // Random empty transaction +BigInt chain = new BigInt(1); // Chain identifier of the main net +``` + +With the boilerplate out of the way, we can now sign transaction using the authorization +mechanisms described above: + +```java +// Sign a transaction with a single authorization +Transaction signed = ks.signTxPassphrase(signer, "Signer password", tx, chain); + +// Sign a transaction with multiple manually cancelled authorizations +ks.unlock(signer, "Signer password"); +signed = ks.signTx(signer, tx, chain); +ks.lock(signer.getAddress()); + +// Sign a transaction with multiple automatically cancelled authorizations +ks.timedUnlock(signer, "Signer password", 1000000000); +signed = ks.signTx(signer, tx, chain); +``` + +### Signing on iOS (Swift 3) + +Assuming we already have an instance of a `GethKeyStore` called `ks` from the previous +sections, we can create a new account to sign transactions with via it's already +demonstrated `newAccount` method; and to avoid going into transaction creation for now, we +can hard-code a random transaction to sign instead. + +```swift +// Create a new account to sign transactions with +var error: NSError? +let signer = try! ks?.newAccount("Signer password") + +let to = GethNewAddressFromHex("0x0000000000000000000000000000000000000000", &error) +let tx = GethNewTransaction(1, to, GethNewBigInt(0), GethNewBigInt(0), GethNewBigInt(0), nil) // Random empty transaction +let chain = GethNewBigInt(1) // Chain identifier of the main net +``` + +*Note, although Swift usually rewrites `NSError` returns to throws, this particular +instance seems to have been missed for some reason (possibly due to it being a +constructor). It will be fixed in a later version of the iOS bindings when the appropriate +fixed are implemented upstream in the `gomobile` project.* + +With the boilerplate out of the way, we can now sign transaction using the authorization +methods described above: + +```swift +// Sign a transaction with a single authorization +var signed = try! ks?.signTxPassphrase(signer, passphrase: "Signer password", tx: tx, chainID: chain) + +// Sign a transaction with multiple manually cancelled authorizations +try! ks?.unlock(signer, passphrase: "Signer password") +signed = try! ks?.signTx(signer, tx: tx, chainID: chain) +try! ks?.lock(signer?.getAddress()) + +// Sign a transaction with multiple automatically cancelled authorizations +try! ks?.timedUnlock(signer, passphrase: "Signer password", timeout: 1000000000) +signed = try! ks?.signTx(signer, tx: tx, chainID: chain) +``` +
diff --git go-ethereum/docs/docs/_dapp/mobile.md celo/docs/docs/_dapp/mobile.md new file mode 100644 index 0000000000000000000000000000000000000000..2f550d5286bcb18ce6ce3982fb829ac1a4dcad02 --- /dev/null +++ celo/docs/docs/_dapp/mobile.md @@ -0,0 +1,179 @@ +--- +title: Mobile API +--- + +The Ethereum blockchain along with its two extension protocols Whisper and Swarm was +originally conceptualized to become the supporting pillar of web3, providing the +consensus, messaging and storage backbone for a new generation of distributed (actually, +decentralized) applications called DApps. + +The first incarnation towards this dream of web3 was a command line client providing an +RPC interface into the peer-to-peer protocols. The client was soon enough extended with a +web-browser-like graphical user interface, permitting developers to write DApps based on +the tried and proven HTML/CSS/JS technologies. + +As many DApps have more complex requirements than what a browser environment can handle, +it became apparent that providing programmatic access to the web3 pillars would open the +door towards a new class of applications. As such, the second incarnation of the web +dream is to open up all our technologies for other projects as reusable components. + +Starting with the 1.5 release family of `go-ethereum`, we transitioned away from providing +only a full blown Ethereum client and started shipping official Go packages that could be +embedded into third party desktop and server applications. It took only a small leap from +here to begin porting our code to mobile platforms. + +## Quick overview + +Similarly to our reusable Go libraries, the mobile wrappers also focus on four main usage +areas: + +- Simplified client side account management +- Remote node interfacing via different transports +- Contract interactions through auto-generated bindings +- In-process Ethereum, Whisper and Swarm peer-to-peer node + +You can watch a quick overview about these in Peter's (@karalabe) talk titled "Import +Geth: Ethereum from Go and beyond", presented at the Ethereum Devcon2 developer conference +in September, 2016 (Shanghai). Slides are [available +here](https://ethereum.karalabe.com/talks/2016-devcon.html). + +[![Peter's Devcon2 talk](https://img.youtube.com/vi/R0Ia1U9Gxjg/0.jpg)](https://www.youtube.com/watch?v=R0Ia1U9Gxjg) + +## Library bundles + +The `go-ethereum` mobile library is distributed either as an Android `.aar` archive +(containing binaries for `arm-7`, `arm64`, `x86` and `x64`); or as an iOS XCode framework +(containing binaries for `arm-7`, `arm64` and `x86`). We do not provide library bundles +for Windows phone the moment. + +### Android archive + +The simplest way to use `go-ethereum` in your Android project is through a Maven +dependency. We provide bundles of all our stable releases (starting from v1.5.0) through +Maven Central, and also provide the latest develop bundle through the Sonatype OSS +repository. + +#### Stable dependency (Maven Central) + +To add an Android dependency to the **stable** library release of `go-ethereum`, you'll +need to ensure that the Maven Central repository is enabled in your Android project, and +that the `go-ethereum` code is listed as a required dependency of your application. You +can do both of these by editing the `build.gradle` script in your Android app's folder: + +```gradle +repositories { + mavenCentral() +} + +dependencies { + // All your previous dependencies + compile 'org.ethereum:geth:1.5.2' // Change the version to the latest release +} +``` + +#### Develop dependency (Sonatype) + +To add an Android dependency to the current version of `go-ethereum`, you'll need to +ensure that the Sonatype snapshot repository is enabled in your Android project, and that +the `go-ethereum` code is listed as a required `SNAPSHOT` dependency of your application. +You can do both of these by editing the `build.gradle` script in your Android app's +folder: + +```gradle +repositories { + maven { + url "https://oss.sonatype.org/content/groups/public" + } +} + +dependencies { + // All your previous dependencies + compile 'org.ethereum:geth:1.5.3-SNAPSHOT' // Change the version to the latest release +} +``` + +#### Custom dependency + +If you prefer not to depend on Maven Central or Sonatype; or would like to access an older +develop build not available any more as an online dependency, you can download any bundle +directly from [our website](https://geth.ethereum.org/downloads/) and insert it into your +project in Android Studio via `File -> New -> New module... -> Import .JAR/.AAR Package`. + +You will also need to configure `gradle` to link the mobile library bundle to your +application. This can be done by adding a new entry to the `dependencies` section of your +`build.gradle` script, pointing it to the module you just added (named `geth` by default). + +```gradle +dependencies { + // All your previous dependencies + compile project(':geth') +} +``` + +#### Manual builds + +Lastly, if you would like to make modifications to the `go-ethereum` mobile code and/or +build it yourself locally instead of downloading a pre-built bundle, you can do so using a +`make` command. This will create an Android archive called `geth.aar` in the `build/bin` +folder that you can import into your Android Studio as described above. + +```bash +$ make android +[...] +Done building. +Import "build/bin/geth.aar" to use the library. +``` + +### iOS framework + +The simplest way to use `go-ethereum` in your iOS project is through a +[CocoaPods](https://cocoapods.org/) dependency. We provide bundles of all our stable +releases (starting from v1.5.3) and also latest develop versions. + +#### Automatic dependency + +To add an iOS dependency to the current stable or latest develop version of `go-ethereum`, +you'll need to ensure that your iOS XCode project is configured to use CocoaPods. +Detailing that is out of scope in this document, but you can find a guide in the upstream +[Using CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html) page. +Afterwards you can edit your `Podfile` to list `go-ethereum` as a dependency: + +```ruby +target 'MyApp' do + # All your previous dependencies + pod 'Geth', '1.5.4' # Change the version to the latest release +end +``` + +Alternatively, if you'd like to use the latest develop version, replace the package +version `1.5.4` with `~> 1.5.5-unstable` to switch to pre-releases and to always pull in +the latest bundle from a particular release family. + +#### Custom dependency + +If you prefer not to depend on CocoaPods; or would like to access an older develop build +not available any more as an online dependency, you can download any bundle directly from +[our website](https://geth.ethereum.org/downloads/) and insert it into your project in +XCode via `Project Settings -> Build Phases -> Link Binary With Libraries`. + +Do not forget to extract the framework from the compressed `.tar.gz` archive. You can do +that either using a GUI tool or from the command line via (replace the archive with your +downloaded file): + +``` +tar -zxvf geth-ios-all-1.5.3-unstable-e05d35e6.tar.gz +``` + +#### Manual builds + +Lastly, if you would like to make modifications to the `go-ethereum` mobile code and/or +build it yourself locally instead of downloading a pre-built bundle, you can do so using a +`make` command. This will create an iOS XCode framework called `Geth.framework` in the +`build/bin` folder that you can import into XCode as described above. + +```bash +$ make ios +[...] +Done building. +Import "build/bin/Geth.framework" to use the library. +```
diff --git go-ethereum/docs/docs/_dapp/native-accounts.md celo/docs/docs/_dapp/native-accounts.md new file mode 100644 index 0000000000000000000000000000000000000000..9f672c926e2199a82ba6d9b2ef1c908ddc36dd87 --- /dev/null +++ celo/docs/docs/_dapp/native-accounts.md @@ -0,0 +1,252 @@ +--- +title: Go Account Management +--- + +To provide Ethereum integration for your native applications, the very first thing you +should be interested in doing is account management. + +Although all current leading Ethereum implementations provide account management built in, +it is ill advised to keep accounts in any location that is shared between multiple +applications and/or multiple people. The same way you do not entrust your ISP (who is +after all your gateway into the internet) with your login credentials; you should not +entrust an Ethereum node (who is your gateway into the Ethereum network) with your +credentials either. + +The proper way to handle user accounts in your native applications is to do client side +account management, everything self-contained within your own application. This way you +can ensure as fine grained (or as coarse) access permissions to the sensitive data as +deemed necessary, without relying on any third party application's functionality and/or +vulnerabilities. + +To support this, `go-ethereum` provides a simple, yet thorough accounts package that gives +you all the tools to do properly secured account management via encrypted keystores and +passphrase protected accounts. You can leverage all the security of the `go-ethereum` +crypto implementation while at the same time running everything in your own application. + +## Encrypted keystores + +Although handling accounts locally to an application does provide certain security +guarantees, access keys to Ethereum accounts should never lay around in clear-text form. +As such, we provide an encrypted keystore that provides the proper security guarantees for +you without requiring a thorough understanding from your part of the associated +cryptographic primitives. + +The important thing to know when using the encrypted keystore is that the cryptographic +primitives used within can operate either in *standard* or *light* mode. The former +provides a higher level of security at the cost of increased computational burden and +resource consumption: + + * *standard* needs 256MB memory and 1 second processing on a modern CPU to access a key + * *light* needs 4MB memory and 100 millisecond processing on a modern CPU to access a key + +As such, *standard* is more suitable for native applications, but you should be aware of +the trade-offs nonetheless in case you you're targeting more resource constrained +environments. + +*For those interested in the cryptographic and/or implementation details, the key-store +uses the `secp256k1` elliptic curve as defined in the [Standards for Efficient +Cryptography](sec2), implemented by the [`libsecp256k`](secp256k1) library and wrapped by +[`github.com/ethereum/go-ethereum/accounts`](accounts-go). Accounts are stored on disk in +the [Web3 Secret Storage](secstore) format.* + +[sec2]: http://www.secg.org/sec2-v2.pdf +[accounts-go]: https://godoc.org/github.com/ethereum/go-ethereum/accounts +[secp256k1]: https://github.com/bitcoin-core/secp256k1 +[secstore]: https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition + +### Keystores from Go + +The encrypted keystore is implemented by the +[`accounts.Manager`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Manager) +struct from the +[`github.com/ethereum/go-ethereum/accounts`](https://godoc.org/github.com/celo-org/celo-blockchain/accounts) +package, which also contains the configuration constants for the *standard* or *light* +security modes described above. Hence to do client side account management from Go, you'll +need to import only the `accounts` package into your code: + +```go +import "github.com/ethereum/go-ethereum/accounts" +``` + +Afterwards you can create a new encrypted account manager via: + +```go +am := accounts.NewManager("/path/to/keystore", accounts.StandardScryptN, accounts.StandardScryptP); +``` + +The path to the keystore folder needs to be a location that is writable by the local user +but non-readable for other system users (for security reasons obviously), so we'd +recommend placing it either inside your user's home directory or even more locked down for +backend applications. + +The last two arguments of +[`accounts.NewManager`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#NewManager) +are the crypto parameters defining how resource-intensive the keystore encryption should +be. You can choose between [`accounts.StandardScryptN, accounts.StandardScryptP`, +`accounts.LightScryptN, +accounts.LightScryptP`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#pkg-constants) +or specify your own numbers (please make sure you understand the underlying cryptography +for this). We recommend using the *standard* version. + +## Account lifecycle + +Having created an encrypted keystore for your Ethereum accounts, you can use this account +manager for the entire account lifecycle requirements of your native application. This +includes the basic functionality of creating new accounts and deleting existing ones; as +well as the more advanced functionality of updating access credentials, exporting existing +accounts, and importing them on another device. + +Although the keystore defines the encryption strength it uses to store your accounts, +there is no global master password that can grant access to all of them. Rather each +account is maintained individually, and stored on disk in its [encrypted +format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) +individually, ensuring a much cleaner and stricter separation of credentials. + +This individuality however means that any operation requiring access to an account will +need to provide the necessary authentication credentials for that particular account in +the form of a passphrase: + + * When creating a new account, the caller must supply a passphrase to encrypt the account + with. This passphrase will be required for any subsequent access, the lack of which + will forever forfeit using the newly created account. + * When deleting an existing account, the caller must supply a passphrase to verify + ownership of the account. This isn't cryptographically necessary, rather a protective + measure against accidental loss of accounts. + * When updating an existing account, the caller must supply both current and new + passphrases. After completing the operation, the account will not be accessible via the + old passphrase any more. + * When exporting an existing account, the caller must supply both the current passphrase + to decrypt the account, as well as an export passphrase to re-encrypt it with before + returning the key-file to the user. This is required to allow moving accounts between + machines and applications without sharing original credentials. + * When importing a new account, the caller must supply both the encryption passphrase of + the key-file being imported, as well as a new passhprase with which to store the + account. This is required to allow storing account with different credentials than used + for moving them around. + +*Please note, there is no recovery mechanisms for losing the passphrases. The +cryptographic properties of the encrypted keystore (if using the provided parameters) +guarantee that account credentials cannot be brute forced in any meaningful time.* + +### Accounts from Go + +An Ethereum account is implemented by the +[`accounts.Account`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Account) +struct from the +[`github.com/ethereum/go-ethereum/accounts`](https://godoc.org/github.com/celo-org/celo-blockchain/accounts) +package. Assuming we already have an instance of an +[`accounts.Manager`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Manager) +called `am` from the previous section, we can easily execute all of the described +lifecycle operations with a handful of function calls (error handling omitted). + +```go +// Create a new account with the specified encryption passphrase. +newAcc, _ := am.NewAccount("Creation password"); + +// Export the newly created account with a different passphrase. The returned +// data from this method invocation is a JSON encoded, encrypted key-file. +jsonAcc, _ := am.Export(newAcc, "Creation password", "Export password"); + +// Update the passphrase on the account created above inside the local keystore. +am.Update(newAcc, "Creation password", "Update password"); + +// Delete the account updated above from the local keystore. +am.Delete(newAcc, "Update password"); + +// Import back the account we've exported (and then deleted) above with yet +// again a fresh passphrase. +impAcc, _ := am.Import(jsonAcc, "Export password", "Import password"); +``` + +*Although instances of +[`accounts.Account`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Account) +can be used to access various information about specific Ethereum accounts, they do not +contain any sensitive data (such as passphrases or private keys), rather act solely as +identifiers for client code and the keystore.* + +## Signing authorization + +As mentioned above, account objects do not hold the sensitive private keys of the +associated Ethereum accounts, but are merely placeholders to identify the cryptographic +keys with. All operations that require authorization (e.g. transaction signing) are +performed by the account manager after granting it access to the private keys. + +There are a few different ways one can authorize the account manager to execute signing +operations, each having its advantages and drawbacks. Since the different methods have +wildly different security guarantees, it is essential to be clear on how each works: + + * **Single authorization**: The simplest way to sign a transaction via the account + manager is to provide the passphrase of the account every time something needs to be + signed, which will ephemerally decrypt the private key, execute the signing operation + and immediately throw away the decrypted key. The drawbacks are that the passphrase + needs to be queried from the user every time, which can become annoying if done + frequently; or the application needs to keep the passphrase in memory, which can have + security consequences if not done properly; and depending on the keystore's configured + strength, constantly decrypting keys can result in non-negligible resource + requirements. + * **Multiple authorizations**: A more complex way of signing transactions via the account + manager is to unlock the account via its passphrase once, and allow the account manager + to cache the decrypted private key, enabling all subsequent signing requests to + complete without the passphrase. The lifetime of the cached private key may be managed + manually (by explicitly locking the account back up) or automatically (by providing a + timeout during unlock). This mechanism is useful for scenarios where the user may need + to sign many transactions or the application would need to do so without requiring user + input. The crucial aspect to remember is that **anyone with access to the account + manager can sign transactions while a particular account is unlocked** (e.g. + application running untrusted code). + +*Note, creating transactions is out of scope here, so the remainder of this section will +assume we already have a transaction hash to sign, and will focus only on creating a +cryptographic signature authorizing it. Creating an actual transaction and injecting the +authorization signature into it will be covered later.* + +### Signing from Go + +Assuming we already have an instance of an +[`accounts.Manager`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Manager) +called `am` from the previous sections, we can create a new account to sign transactions +with via it's already demonstrated +[`NewAccount`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Manager.NewAccount) +method; and to avoid going into transaction creation for now, we can hard-code a random +[`common.Hash`](https://godoc.org/github.com/ethereum/go-ethereum/common#Hash) to sign +instead. + +```go +// Create a new account to sign transactions with +signer, _ := am.NewAccount("Signer password"); +txHash := common.HexToHash("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"); +``` + +With the boilerplate out of the way, we can now sign transaction using the authorization +mechanisms described above: + +```go +// Sign a transaction with a single authorization +signature, _ := am.SignWithPassphrase(signer, "Signer password", txHash.Bytes()); + +// Sign a transaction with multiple manually cancelled authorizations +am.Unlock(signer, "Signer password"); +signature, _ = am.Sign(signer.Address, txHash.Bytes()); +am.Lock(signer.Address); + +// Sign a transaction with multiple automatically cancelled authorizations +am.TimedUnlock(signer, "Signer password", time.Second); +signature, _ = am.Sign(signer.Address, txHash.Bytes()); +``` + +You may wonder why +[`SignWithPassphrase`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Manager.SignWithPassphrase) +takes an +[`accounts.Account`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Account) +as the signer, whereas +[`Sign`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Manager.Sign) takes +only a +[`common.Address`](https://godoc.org/github.com/ethereum/go-ethereum/common#Address). The +reason is that an +[`accounts.Account`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Account) +object may also contain a custom key-path, allowing +[`SignWithPassphrase`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Manager.SignWithPassphrase) +to sign using accounts outside of the keystore; however +[`Sign`](https://godoc.org/github.com/ethereum/go-ethereum/accounts#Manager.Sign) relies +on accounts already unlocked within the keystore, so it cannot specify custom paths. +
diff --git go-ethereum/docs/docs/_dapp/native-bindings.md celo/docs/docs/_dapp/native-bindings.md new file mode 100644 index 0000000000000000000000000000000000000000..b22b99d3bd22cf23e97c460682a6540905f77cab --- /dev/null +++ celo/docs/docs/_dapp/native-bindings.md @@ -0,0 +1,442 @@ +--- +title: Go Contract Bindings +--- + +**[Please note, events are not yet implemented as they need some RPC subscription +features that are still under review.]** + +The original roadmap and/or dream of the Ethereum platform was to provide a solid, high +performing client implementation of the consensus protocol in various languages, which +would provide an RPC interface for JavaScript DApps to communicate with, pushing towards +the direction of the Mist browser, through which users can interact with the blockchain. + +Although this was a solid plan for mainstream adoption and does cover quite a lot of use +cases that people come up with (mostly where people manually interact with the blockchain), +it eludes the server side (backend, fully automated, devops) use cases where JavaScript is +usually not the language of choice given its dynamic nature. + +This page introduces the concept of server side native Dapps: Go language bindings to any +Ethereum contract that is compile time type safe, highly performant and best of all, can +be generated fully automatically from a contract ABI and optionally the EVM bytecode. + +*This page is written in a more beginner friendly tutorial style to make it easier for +people to start out with writing Go native Dapps. The used concepts will be introduced +gradually as a developer would need/encounter them. However, we do assume the reader +is familiar with Ethereum in general, has a fair understanding of Solidity and can code +Go.* + +## Token contract + +To avoid falling into the fallacy of useless academic examples, we're going to take the +official Token contract as the base for introducing the Go +native bindings. If you're unfamiliar with the contract, skimming the linked page should +probably be enough, the details aren't relevant for now. *In short the contract implements +a custom token that can be deployed on top of Ethereum.* To make sure this tutorial doesn't +go stale if the linked website changes, the Solidity source code of the Token contract is +also available at [`token.sol`](https://gist.github.com/karalabe/08f4b780e01c8452d989). + +### Go binding generator + +Interacting with a contract on the Ethereum blockchain from Go (or any other language for +a matter of fact) is already possible via the RPC interfaces exposed by Ethereum clients. +However, writing the boilerplate code that translates decent Go language constructs into +RPC calls and back is extremely time consuming and also extremely brittle: implementation +bugs can only be detected during runtime and it's almost impossible to evolve a contract +as even a tiny change in Solidity can be painful to port over to Go. + +To avoid all this mess, the go-ethereum implementation introduces a source code generator +that can convert Ethereum ABI definitions into easy to use, type-safe Go packages. Assuming +you have a valid Go development environment set up, `godep` installed and the go-ethereum +repository checked out correctly, you can build the generator with: + +``` +$ cd $GOPATH/src/github.com/ethereum/go-ethereum +$ godep go install ./cmd/abigen +``` + +### Generating the bindings + +The single essential thing needed to generate a Go binding to an Ethereum contract is the +contract's ABI definition `JSON` file. For our `Token` contract tutorial you can obtain this +either by compiling the Solidity code yourself (e.g. via @chriseth's [online Solidity compiler](https://chriseth.github.io/browser-solidity/)), or you can download our pre-compiled [`token.abi`](https://gist.github.com/karalabe/b8dfdb6d301660f56c1b). + +To generate a binding, simply call: + +``` +$ abigen --abi token.abi --pkg main --type Token --out token.go +``` + +Where the flags are: + + * `--abi`: Mandatory path to the contract ABI to bind to + * `--pgk`: Mandatory Go package name to place the Go code into + * `--type`: Optional Go type name to assign to the binding struct + * `--out`: Optional output path for the generated Go source file (not set = stdout) + +This will generate a type-safe Go binding for the Token contract. The generated code will +look something like [`token.go`](https://gist.github.com/karalabe/5839509295afa4f7e2215bc4116c7a8f), +but please generate your own as this will change as more work is put into the generator. + +### Accessing an Ethereum contract + +To interact with a contract deployed on the blockchain, you'll need to know the `address` +of the contract itself, and need to specify a `backend` through which to access Ethereum. +The binding generator provides out of the box an RPC backend through which you can attach +to an existing Ethereum node via IPC, HTTP or WebSockets. + +We'll use the foundation's Unicorn token contract deployed +on the testnet to demonstrate calling contract methods. It is deployed at the address +`0x21e6fc92f93c8a1bb41e2be64b4e1f88a54d3576`. + +To run the snippet below, please ensure a Geth instance is running and attached to the +Morden test network where the above mentioned contract was deployed. Also please update +the path to the IPC socket below to the one reported by your own local Geth node. + +```go +package main + +import ( + "fmt" + "log" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" +) + +func main() { + // Create an IPC based RPC connection to a remote node + conn, err := ethclient.Dial("/home/karalabe/.ethereum/testnet/geth.ipc") + if err != nil { + log.Fatalf("Failed to connect to the Ethereum client: %v", err) + } + // Instantiate the contract and display its name + token, err := NewToken(common.HexToAddress("0x21e6fc92f93c8a1bb41e2be64b4e1f88a54d3576"), conn) + if err != nil { + log.Fatalf("Failed to instantiate a Token contract: %v", err) + } + name, err := token.Name(nil) + if err != nil { + log.Fatalf("Failed to retrieve token name: %v", err) + } + fmt.Println("Token name:", name) +} +``` + +And the output (yay): + +``` +Token name: Testnet Unicorn +``` + +If you look at the method invoked to read the token name `token.Name(nil)`, it required +a parameter to be passed, even though the original Solidity contract requires none. This +is a `*bind.CallOpts` type, which can be used to fine tune the call. + + * `Pending`: Whether to access pending contract state or the current stable one + * `GasLimit`: Place a limit on the computing resources the call might consume + +### Transacting with an Ethereum contract + +Invoking a method that changes contract state (i.e. transacting) is a bit more involved, +as a live transaction needs to be authorized and broadcast into the network. **Opposed +to the conventional way of storing accounts and keys in the node we attach to, Go bindings +require signing transactions locally and do not delegate this to a remote node.** This is +done so to facilitate the general direction of the Ethereum community where accounts are +kept private to DApps, and not shared (by default) between them. + +Thus to allow transacting with a contract, your code needs to implement a method that +given an input transaction, signs it and returns an authorized output transaction. Since +most users have their keys in the [Web3 Secret Storage](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) format, the `bind` package contains a small utility method +(`bind.NewTransactor(keyjson, passphrase)`) that can create an authorized transactor from +a key file and associated password, without the user needing to implement key signing himself. + +Changing the previous code snippet to send one unicorn to the zero address: + +```go +package main + +import ( + "fmt" + "log" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" +) + +const key = `paste the contents of your *testnet* key json here` + +func main() { + // Create an IPC based RPC connection to a remote node and instantiate a contract binding + conn, err := ethclient.Dial("/home/karalabe/.ethereum/testnet/geth.ipc") + if err != nil { + log.Fatalf("Failed to connect to the Ethereum client: %v", err) + } + token, err := NewToken(common.HexToAddress("0x21e6fc92f93c8a1bb41e2be64b4e1f88a54d3576"), conn) + if err != nil { + log.Fatalf("Failed to instantiate a Token contract: %v", err) + } + // Create an authorized transactor and spend 1 unicorn + auth, err := bind.NewTransactor(strings.NewReader(key), "my awesome super secret password") + if err != nil { + log.Fatalf("Failed to create authorized transactor: %v", err) + } + tx, err := token.Transfer(auth, common.HexToAddress("0x0000000000000000000000000000000000000000"), big.NewInt(1)) + if err != nil { + log.Fatalf("Failed to request token transfer: %v", err) + } + fmt.Printf("Transfer pending: 0x%x\n", tx.Hash()) +} +``` + +And the output (yay): + +``` +Transfer pending: 0x4f4aaeb29ed48e88dd653a81f0b05d4df64a86c99d4e83b5bfeb0f0006b0e55b +``` + +*Note, with high probability you won't have any testnet unicorns available to spend, so the +above program will fail with an error. Send at least 2.014 testnet(!) Ethers to the foundation +testnet tipjar `0xDf7D0030bfed998Db43288C190b63470c2d18F50` to receive a unicorn token and +you'll be able to see the above code run without an error!* + +Similar to the method invocations in the previous section which only read contract state, +transacting methods also require a mandatory first parameter, a `*bind.TransactOpts` type, +which authorizes the transaction and potentially fine tunes it: + + * `From`: Address of the account to invoke the method with (mandatory) + * `Signer`: Method to sign a transaction locally before broadcasting it (mandatory) + * `Nonce`: Account nonce to use for the transaction ordering (optional) + * `GasLimit`: Place a limit on the computing resources the call might consume (optional) + * `GasPrice`: Explicitly set the gas price to run the transaction with (optional) + * `Value`: Any funds to transfer along with the method call (optional) + +The two mandatory fields are automatically set by the `bind` package if the auth options are +constructed using `bind.NewTransactor`. The nonce and gas related fields are automatically +derived by the binding if they are not set. An unset value is assumed to be zero. + +### Pre-configured contract sessions + +As mentioned in the previous two sections, both reading as well as state modifying contract +calls require a mandatory first parameter which can both authorize as well as fine tune some +of the internal parameters. However, most of the time we want to use the same parameters and +issue transactions with the same account, so always constructing the call/transact options or +passing them along with the binding can become unwieldy. + +To avoid these scenarios, the generator also creates specialized wrappers that can be pre- +configured with tuning and authorization parameters, allowing all the Solidity defined methods +to be invoked without needing an extra parameter. + +These are named analogous to the original contract type name, just suffixed with `Sessions`: + +```go +// Wrap the Token contract instance into a session +session := &TokenSession{ + Contract: token, + CallOpts: bind.CallOpts{ + Pending: true, + }, + TransactOpts: bind.TransactOpts{ + From: auth.From, + Signer: auth.Signer, + GasLimit: big.NewInt(3141592), + }, +} +// Call the previous methods without the option parameters +session.Name() +session.Transfer("0x0000000000000000000000000000000000000000"), big.NewInt(1)) +``` + +### Deploying contracts to Ethereum + +Interacting with existing contracts is nice, but let's take it up a notch and deploy +a brand new contract onto the Ethereum blockchain! To do so however, the contract ABI +we used to generate the binding is not enough. We need the compiled bytecode too to +allow deploying it. + +To get the bytecode, either go back to the online compiler with which you may generate it, +or alternatively download our [`token.bin`](https://gist.github.com/karalabe/026548f6a5f5f97b54de). +You'll need to rerun the Go generator with the bytecode included for it to create deploy +code too: + +``` +$ abigen --abi token.abi --pkg main --type Token --out token.go --bin token.bin +``` + +This will generate something similar to [`token.go`](https://gist.github.com/karalabe/2153b087c1f80f651fd87dd4c439fac4). +If you quickly skim this file, you'll find an extra `DeployToken` function that was just +injected compared to the previous code. Beside all the parameters specified by Solidity, +it also needs the usual authorization options to deploy the contract with and the Ethereum +backend to deploy the contract through. + +Putting it all together would result in: + +```go +package main + +import ( + "fmt" + "log" + "math/big" + "strings" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/ethclient" +) + +const key = `paste the contents of your *testnet* key json here` + +func main() { + // Create an IPC based RPC connection to a remote node and an authorized transactor + conn, err := rpc.NewIPCClient("/home/karalabe/.ethereum/testnet/geth.ipc") + if err != nil { + log.Fatalf("Failed to connect to the Ethereum client: %v", err) + } + auth, err := bind.NewTransactor(strings.NewReader(key), "my awesome super secret password") + if err != nil { + log.Fatalf("Failed to create authorized transactor: %v", err) + } + // Deploy a new awesome contract for the binding demo + address, tx, token, err := DeployToken(auth, conn), new(big.Int), "Contracts in Go!!!", 0, "Go!") + if err != nil { + log.Fatalf("Failed to deploy new token contract: %v", err) + } + fmt.Printf("Contract pending deploy: 0x%x\n", address) + fmt.Printf("Transaction waiting to be mined: 0x%x\n\n", tx.Hash()) + + // Don't even wait, check its presence in the local pending state + time.Sleep(250 * time.Millisecond) // Allow it to be processed by the local node :P + + name, err := token.Name(&bind.CallOpts{Pending: true}) + if err != nil { + log.Fatalf("Failed to retrieve pending name: %v", err) + } + fmt.Println("Pending name:", name) +} +``` + +And the code performs as expected: it requests the creation of a brand new Token contract +on the Ethereum blockchain, which we can either wait for to be mined or as in the above code +start calling methods on it in the pending state :) + +``` +Contract pending deploy: 0x46506d900559ad005feb4645dcbb2dbbf65e19cc +Transaction waiting to be mined: 0x6a81231874edd2461879b7280ddde1a857162a744e3658ca7ec276984802183b + +Pending name: Contracts in Go!!! +``` + +## Bind Solidity directly + +If you've followed the tutorial along until this point you've probably realized that +every contract modification needs to be recompiled, the produced ABIs and bytecodes +(especially if you need multiple contracts) individually saved to files and then the +binding executed for them. This can become a quite bothersome after the Nth iteration, +so the `abigen` command supports binding from Solidity source files directly (`--sol`), +which first compiles the source code (via `--solc`, defaulting to `solc`) into it's +constituent components and binds using that. + +Binding the official Token contract [`token.sol`](https://gist.github.com/karalabe/08f4b780e01c8452d989) +would then entail to running: + +``` +$ abigen --sol token.sol --pkg main --out token.go +``` + +*Note: Building from Solidity (`--sol`) is mutually exclusive with individually setting +the bind components (`--abi`, `--bin` and `--type`), as all of them are extracted from +the Solidity code and produced build results directly.* + +Building a contract directly from Solidity has the nice side effect that all contracts +contained within a Solidity source file are built and bound, so if your file contains many +contract sources, each and every one of them will be available from Go code. The sample +Token solidity file results in [`token.go`](https://gist.github.com/karalabe/c22aab73194ba7da834ab5b379621031). + +### Project integration (i.e. `go generate`) + +The `abigen` command was made in such a way as to play beautifully together with existing +Go toolchains: instead of having to remember the exact command needed to bind an Ethereum +contract into a Go project, we can leverage `go generate` to remember all the nitty-gritty +details. + +Place the binding generation command into a Go source file before the package definition: + +``` +//go:generate abigen --sol token.sol --pkg main --out token.go +``` + +After which whenever the Solidity contract is modified, instead of needing to remember and +run the above command, we can simply call `go generate` on the package (or even the entire +source tree via `go generate ./...`), and it will correctly generate the new bindings for us. + +## Blockchain simulator + +Being able to deploy and access already deployed Ethereum contracts from within native Go +code is an extremely powerful feature, but there is one facet with developing native code +that not even the testnet lends itself well to: *automatic unit testing*. Using go-ethereum +internal constructs it's possible to create test chains and verify them, but it is unfeasible +to do high level contract testing with such low level mechanisms. + +To sort out this last issue that would make it hard to run (and test) native DApps, we've also +implemented a *simulated blockchain*, that can be set as a backend to native contracts the same +way as a live RPC backend could be: `backends.NewSimulatedBackend(genesisAccounts)`. + +```go +package main + +import ( + "fmt" + "log" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/crypto" +) + +func main() { + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth := bind.NewKeyedTransactor(key) + + sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + + // Deploy a token contract on the simulated blockchain + _, _, token, err := DeployMyToken(auth, sim, new(big.Int), "Simulated blockchain tokens", 0, "SBT") + if err != nil { + log.Fatalf("Failed to deploy new token contract: %v", err) + } + // Print the current (non existent) and pending name of the contract + name, _ := token.Name(nil) + fmt.Println("Pre-mining name:", name) + + name, _ = token.Name(&bind.CallOpts{Pending: true}) + fmt.Println("Pre-mining pending name:", name) + + // Commit all pending transactions in the simulator and print the names again + sim.Commit() + + name, _ = token.Name(nil) + fmt.Println("Post-mining name:", name) + + name, _ = token.Name(&bind.CallOpts{Pending: true}) + fmt.Println("Post-mining pending name:", name) +} +``` + +And the output (yay): + +``` +Pre-mining name: +Pre-mining pending name: Simulated blockchain tokens +Post-mining name: Simulated blockchain tokens +Post-mining pending name: Simulated blockchain tokens +``` + +Note, that we don't have to wait for a local private chain miner, or testnet miner to +integrate the currently pending transactions. When we decide to mine the next block, +we simply `Commit()` the simulator.
diff --git go-ethereum/docs/docs/_dapp/native.md celo/docs/docs/_dapp/native.md new file mode 100644 index 0000000000000000000000000000000000000000..c6c769fa72ca67d3003b276742abdd8f69544069 --- /dev/null +++ celo/docs/docs/_dapp/native.md @@ -0,0 +1,72 @@ +--- +title: Go API +--- + +The Ethereum blockchain along with its two extension protocols Whisper and Swarm was +originally conceptualized to become the supporting pillar of web3, providing the +consensus, messaging and storage backbone for a new generation of distributed (actually, +decentralized) applications called DApps. + +The first incarnation towards this dream of web3 was a command line client providing an +RPC interface into the peer-to-peer protocols. The client was soon enough extended with a +web-browser-like graphical user interface, permitting developers to write DApps based on +the tried and proven HTML/CSS/JS technologies. + +As many DApps have more complex requirements than what a browser environment can handle, +it became apparent that providing programmatic access to the web3 pillars would open the +door towards a new class of applications. As such, the second incarnation of the web3 +dream is to open up all our technologies for other projects as reusable components. + +Starting with the 1.5 release family of `go-ethereum`, we transitioned away from providing +only a full blown Ethereum client and started shipping official Go packages that could be +embedded into third party desktop and server applications. + +*Note, this guide will assume you are familiar with Go development. It will make no +attempts to cover general topics about Go project layouts, import paths or any other +standard methodologies. If you are new to Go, consider reading its [getting started +guides](https://github.com/golang/go/wiki#getting-started-with-go) first.* + +## Quick overview + +Our reusable Go libraries focus on four main usage areas: + +- Simplified client side account management +- Remote node interfacing via different transports +- Contract interactions through auto-generated bindings +- In-process Ethereum, Whisper and Swarm peer-to-peer node + +You can watch a quick overview about these in Peter's (@karalabe) talk titled "Import +Geth: Ethereum from Go and beyond", presented at the Ethereum Devcon2 developer conference +in September, 2016 (Shanghai). Slides are [available +here](https://ethereum.karalabe.com/talks/2016-devcon.html). + +[![Peter's Devcon2 talk](https://img.youtube.com/vi/R0Ia1U9Gxjg/0.jpg)](https://www.youtube.com/watch?v=R0Ia1U9Gxjg) + +## Go packages + +The `go-ethereum` library is distributed as a collection of standard Go packages straight +from our GitHub repository. The packages can be used directly via the official Go toolkit, +without needing any third party tools. External dependencies are vendored locally into +`vendor`, ensuring both self-containment as well as code stability. If you reuse +`go-ethereum` in your own project, please follow these best practices and vendor it +yourself too to avoid any accidental API breakages! + +The canonical import path for `go-ethereum` is `github.com/ethereum/go-ethereum`, with all +packages residing underneath. Although there are [quite a +number](https://godoc.org/github.com/ethereum/go-ethereum#pkg-subdirectories) of them, +you'll only need to care about a limited subset, each of which will be properly introduced +in their relevant section. + +You can download all our packages via: + +``` +$ go get -d github.com/ethereum/go-ethereum/... +``` + +You may also need Go's original context package. Although this was moved into the official +Go SDK in Go 1.7, `go-ethereum` will depend on the original `golang.org/x/net/context` +package until we officially drop support for Go 1.5 and Go 1.6. + +``` +$ go get -u golang.org/x/net/context +```
diff --git go-ethereum/docs/docs/_dapp/tracing.md celo/docs/docs/_dapp/tracing.md new file mode 100644 index 0000000000000000000000000000000000000000..0b0a2a9a2ccdab6841618bd85d2eea7e942f6d61 --- /dev/null +++ celo/docs/docs/_dapp/tracing.md @@ -0,0 +1,216 @@ +--- +title: EVM Tracing +--- + +There are two different types of transactions in Ethereum: plain value transfers and +contract executions. A plain value transfer just moves Ether from one account to another +and as such is uninteresting from this guide's perspective. If however the recipient of a +transaction is a contract account with associated EVM (Ethereum Virtual Machine) +bytecode - beside transferring any Ether - the code will also be executed as part of the +transaction. + +Having code associated with Ethereum accounts permits transactions to do arbitrarilly +complex data storage and enables them to act on the previously stored data by further +transacting internally with outside accounts and contracts. This creates an intertwined +ecosystem of contracts, where a single transaction can interact with tens or hunderds of +accounts. + +The downside of contract execution is that it is very hard to say what a transaction +actually did. A transaction receipt does contain a status code to check whether execution +succeeded or not, but there's no way to see what data was modified, nor what external +contracts where invoked. In order to introspect a transaction, we need to trace its +execution. + +## Tracing prerequisites + +In its simplest form, tracing a transaction entails requesting the Ethereum node to +reexecute the desired transaction with varying degrees of data collection and have it +return the aggregated summary for post processing. Reexecuting a transaction however has a +few prerequisites to be met. + +In order for an Ethereum node to reexecute a transaction, it needs to have available all +historical state accessed by the transaction: + + * Balance, nonce, bytecode and storage of both the recipient as well as all internally invoked contracts. + * Block metadata referenced during execution of both the outer as well as all internally created transactions. + * Intermediate state generated by all preceding transactions contained in the same block as the one being traced. + +Depending on your node's mode of synchronization and pruning, different configurations +result in different capabilities: + + * An **archive** node retaining **all historical data** can trace arbitrary transactions + at any point in time. Tracing a single transaction also entails reexecuting all + preceding transactions in the same block. + * A **full synced** node retaining **all historical data** after initial sync can only + trace transactions from blocks following the initial sync point. Tracing a single + transaction also entails reexecuting all preceding transactions in the same block. + * A **fast synced** node retaining only **periodic state data** after initial sync can + only trace transactions from blocks following the initial sync point. Tracing a single + transaction entails reexecuting all preceding transactions **both** in the same block, + as well as all preceding blocks until the previous stored snapshot. + * A **light synced** node retrieving data **on demand** can in theory trace transactions + for which all required historical state is readily available in the network. In + practice, data availability is **not** a feasible assumption. + +*There are exceptions to the above rules when running batch traces of entire blocks or +chain segments. Those will be detailed later.* + +## Basic traces + +The simplest type of transaction trace that `go-ethereum` can generate are raw EVM opcode +traces. For every VM instruction the transaction executes, a structured log entry is +emitted, containing all contextual metadata deemed useful. This includes the *program +counter*, *opcode name*, *opcode cost*, *remaining gas*, *execution depth* and any +*occurred error*. The structured logs can optionally also contain the content of the +*execution stack*, *execution memory* and *contract storage*. + +An example log entry for a single opcode looks like: + +```json +{ + "pc": 48, + "op": "DIV", + "gasCost": 5, + "gas": 64532, + "depth": 1, + "error": null, + "stack": [ + "00000000000000000000000000000000000000000000000000000000ffffffff", + "0000000100000000000000000000000000000000000000000000000000000000", + "2df07fbaabbe40e3244445af30759352e348ec8bebd4dd75467a9f29ec55d98d" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000060" + ], + "storage": { + } +} +``` + +The entire output of an raw EVM opcode trace is a JSON object having a few metadata +fields: *consumed gas*, *failure status*, *return value*; and a list of *opcode entries* +that take the above form: + +```json +{ + "gas": 25523, + "failed": false, + "returnValue": "", + "structLogs": [] +} +``` + +### Generating basic traces + +To generate a raw EVM opcode trace, `go-ethereum` provides a few [RPC API +endpoints](debug-api), out of which the most commonly used is +[`debug_traceTransaction`](trace-tx). + +In its simplest form, `traceTransaction` accepts a transaction hash as its sole argument, +traces the transaction, aggregates all the generated data and returns it as a **large** +JSON object. A sample invocation from the Geth console would be: + +```js +debug.traceTransaction("0xfc9359e49278b7ba99f59edac0e3de49956e46e530a53c15aa71226b7aa92c6f") +``` + +The same call can of course be invoked from outside the node too via HTTP RPC. In this +case, please make sure the HTTP endpoint is enabled via `--rpc` and the `debug` API +namespace exposed via `--rpcapi=debug`. + +``` +$ curl -H "Content-Type: application/json" -d '{"id": 1, "method": "debug_traceTransaction", "params": ["0xfc9359e49278b7ba99f59edac0e3de49956e46e530a53c15aa71226b7aa92c6f"]}' localhost:8545 +``` + +Running the above operation on the Rinkeby network (with a node retaining enough history) +will result in this [trace dump](rinkeby-example-trace-big). + +### Tuning basic traces + +By default the raw opcode tracer emits all relevant events that occur within the EVM while +processing a transaction, such as *EVM stack*, *EVM memory* and *updated storage slots*. +Certain use cases however may not need some of these data fields reported. To cater for +those use cases, these massive fields may be omitted using a second *options* parameter +for the tracer: + +```json +{ + "disableStack": true, + "disableMemory": true, + "disableStorage": true +} +``` + +Running the previous tracer invocation from the Geth console with the data fields +disabled: + +```js +debug.traceTransaction("0xfc9359e49278b7ba99f59edac0e3de49956e46e530a53c15aa71226b7aa92c6f", {disableStack: true, disableMemory: true, disableStorage: true}) +``` + +Analogously running the filtered tracer from outside the node too via HTTP RPC: + +``` +$ curl -H "Content-Type: application/json" -d '{"id": 1, "method": "debug_traceTransaction", "params": ["0xfc9359e49278b7ba99f59edac0e3de49956e46e530a53c15aa71226b7aa92c6f", {"disableStack": true, "disableMemory": true, "disableStorage": true}]}' localhost:8545 +``` + +Running the above operation on the Rinkeby network will result in this significantly +shorter [trace dump](rinkeby-example-trace). + +### Limits of basic traces + +Although the raw opcode traces we've generated above have their use, this basic way of +tracing is problematic in the real world. Having an individual log entry for every single +opcode is too low level for most use cases, and will require developers to create +additional tools to post-process the traces. Additionally, a full opcode trace can easily +go into the hundreds of megabytes, making them very resource intensive to get out of the +node and process externally. + +To avoid all of the previously mentioned issues, `go-ethereum` supports running custom +JavaScript tracers *within* the Ethereum node, which have full access to the EVM stack, +memory and contract storage. This permits developers to only gather the data they need, +and do any processing **at** the data. Please see the next section for our *custom in-node +tracers*. + +### Pruning + +Geth by default does in-memory pruning of state, discarding state entries that it deems is +no longer necessary to maintain. This is configured via the `--gcmode` option. Often, +people run into the error that state is not available. + +Say you want to do a trace on block `B`. Now there are a couple of cases: + +1. You have done a fast-sync, pivot block `P` where `P <= B`. +2. You have done a fast-sync, pivot block `P` where `P > B`. +3. You have done a full-sync, with pruning +4. You have done a full-sync, without pruning (`--gcmode=archive`) + +Here's what happens in each respective case: + +1. Geth will regenerate the desired state by replaying blocks from the closest point ina + time before `B` where it has full state. This defaults to `128` blocks max, but you can + specify more in the actual call `... "reexec":1000 .. }` to the tracer. +2. Sorry, can't be done without replaying from genesis. +3. Same as 1) +4. Does not need to replay anything, can immediately load up the state and serve the request. + +There is one other option available to you, which may or may not suit your needs. That is +to use [Evmlab](evmlab). + + docker pull holiman/evmlab && docker run -it holiman/evmlab + +There you can use the reproducer. The reproducer will incrementally fetch data from infura +until it has all the information required to create the trace locally on an evm which is +bundled with the image. It will create a custom genesis containing the state that the +transaction touches (balances, code, nonce etc). It should be mentioned that the evmlab +reproducer is strictly guaranteed to be totally exact with regards to gascosts incurred by +the outer transaction, as evmlab does not fully calculate the gascosts for nonzero data +etc, but is usually sufficient to analyze contracts and events. + +[evmlab]: https://github.com/holiman/evmlab +[rinkeby-example-trace]: https://gist.github.com/karalabe/d74a7cb33a70f2af75e7824fc772c5b4 +[rinkeby-example-trace-big]: https://gist.github.com/karalabe/c91f95ac57f5e57f8b950ec65ecc697f +[debug-api]: ../rpc/ns-debug +[trace-tx]: ../rpc/ns-debug#debug_tracetransaction
diff --git go-ethereum/docs/docs/_developers/Code-Review-Guidelines.md celo/docs/docs/_developers/Code-Review-Guidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..cfe6ae85d300c21ca0909ba3cd8b49b8d9f42a29 --- /dev/null +++ celo/docs/docs/_developers/Code-Review-Guidelines.md @@ -0,0 +1,103 @@ +--- +title: Code Review Guidelines +sort_key: B +--- + +The only way to get code into go-ethereum is to send a pull request. Those pull requests +need to be reviewed by someone. This document is a guide that explains our expectations +around PRs for both authors and reviewers. + +## Terminology + +* The **author** of a pull request is the entity who wrote the diff and submitted it to + GitHub. +* The **team** consists of people with commit rights on the go-ethereum repository. +* The **reviewer** is the person assigned to review the diff. The reviewer must be a team + member. +* The **code owner** is the person responsible for the subsystem being modified by the PR. + +## The Process + +The first decision to make for any PR is whether it's worth including at all. This +decision lies primarily with the code owner, but may be negotiated with team members. + +To make the decision we must understand what the PR is about. If there isn't enough +description content or the diff is too large, request an explanation. Anyone can do this +part. + +We expect that reviewers check the style and functionality of the PR, providing comments +to the author using the GitHub review system. Reviewers should follow up with the PR until +it is in good shape, then **approve** the PR. Approved PRs can be merged by any code owner. + +When communicating with authors, be polite and respectful. + +### Code Style + +We expect `gofmt`ed code. For contributions of significant size, we expect authors to +understand and use the guidelines in [Effective Go][effgo]. Authors should avoid common +mistakes explained in the [Go Code Review Comments][revcomment] page. + +### Functional Checks + +For PRs that fix an issue, reviewers should try reproduce the issue and verify that the +pull request actually fixes it. Authors can help with this by including a unit test that +fails without (and passes with) the change. + +For PRs adding new features, reviewers should attempt to use the feature and comment on +how it feels to use it. Example: if a PR adds a new command line flag, use the program +with the flag and comment on whether the flag feels useful. + +We expect appropriate unit test coverage. Reviewers should verify that new code is covered +by unit tests. + +### CI + +Code submitted must pass all unit tests and static analysis ("lint") checks. We use Travis +CI to test code on Linux, macOS and AppVeyor to test code on Microsoft Windows. + +For failing CI builds, the issue may not be related to the PR itself. Such failures are +usually related to flakey tests. These failures can be ignored (authors don't need to fix +unrelated issues), but please file a GH issue so the test gets fixed eventually. + +### Commit Messages + +Commit messages on the master branch should follow the rule below. PR authors are not +required to use any particular style because the message can be modified at merge time. +Enforcing commit message style is the responsibility of the person merging the PR. + +The commit message style we use is similar to the style used by the Go project: + +The first line of the change description is conventionally a one-line summary of the +change, prefixed by the primary affected Go package. It should complete the sentence "This +change modifies go-ethereum to _____." The rest of the description elaborates and should +provide context for the change and explain what it does. + +Template: + +```text +package/path: change XYZ + +Longer explanation of the change in the commit. You can use +multiple sentences here. It's usually best to include content +from the PR description in the final commit message. + +issue notices, e.g. "Fixes #42353". +``` + +### Special Situations And How To Deal With Them + +As a reviewer, you may find yourself in one of the sitations below. Here's how to deal +with those: + +* The author doesn't follow up: ping them after a while (i.e. after a few days). If there + is no further response, close the PR or complete the work yourself. + +* Author insists on including refactoring changes alongside bug fix: We can tolerate small + refactorings alongside any change. If you feel lost in the diff, ask the author to + submit the refactoring as an independent PR, or at least as an independent commit in the + same PR. + +* Author keeps rejecting your feedback: reviewers have authority to reject any change for technical reasons. If you're unsure, ask the team for a second opinion. You may close the PR if no consensus can be reached. + +[effgo]: https://golang.org/doc/effective_go.html +[revcomment]: https://github.com/golang/go/wiki/CodeReviewComments
diff --git go-ethereum/docs/docs/_developers/devguide.md celo/docs/docs/_developers/devguide.md new file mode 100644 index 0000000000000000000000000000000000000000..8d5167a99f0c0e2fd899338063689c86044cd1c4 --- /dev/null +++ celo/docs/docs/_developers/devguide.md @@ -0,0 +1,122 @@ +--- +title: Developer Guide +sort_key: A +--- + +**NOTE: These instructions are for people who want to contribute Go source code changes. +If you just want to run ethereum, use the regular [Installation Instructions][install-guide].** + +This document is the entry point for developers of the Go implementation of Ethereum. +Developers here refer to the hands-on: who are interested in build, develop, debug, submit +a bug report or pull request or contribute code to go-ethereum. + +## Contributing + +Thank you for considering to help out with the source code! We welcome contributions from +anyone on the internet, and are grateful for even the smallest of fixes! + +GitHub is used to track issues and contribute code, suggestions, feature requests or +documentation. + +If you'd like to contribute to go-ethereum, please fork, fix, commit and send a pull +request (PR) for the maintainers to review and merge into the main code base. If you wish +to submit more complex changes though, please check up with the core devs in the +go-ethereum [Discord Server][discord]. to ensure those changes are in line with the +general philosophy of the project and/or get some early feedback. This can reduce your +effort as well as speeding up our review and merge procedures. + +PRs need to be based on and opened against the `master` branch (unless by explicit +agreement, you contribute to a complex feature branch). + +Your PR will be reviewed according to the [Code Review guidelines][code-review]. + +We encourage a PR early approach, meaning you create the PR the earliest even without the +fix/feature. This will let core devs and other volunteers know you picked up an issue. +These early PRs should indicate 'in progress' status. + +## Building and Testing + +We assume that you have Go installed. Please use Go version 1.14 or later. We use the gc +toolchain for development, which you can get from the [Go downloads page][go-install]. + +go-ethereum is a Go module, and uses the [Go modules system][go-modules] to manage +dependencies. Using `GOPATH` is not required to build go-ethereum. + +### Building Executables + +Switch to the go-ethereum repository root directory. + +You can build all code using the go tool, placing the resulting binary in `$GOPATH/bin`. + +```text +go install -v ./... +``` + +go-ethereum exectuables can be built individually. To build just geth, use: + +```text +go install -v ./cmd/geth +``` + +If you want to compile geth for an architecture that differs from your host, please +consult our [cross compilation guide][cross-compile]. + +### Testing + +Testing a package: + +``` +go test -v ./eth +``` + +Running an individual test: + +``` +go test -v ./eth -run TestMethod +``` + +**Note**: here all tests with prefix _TestMethod_ will be run, so if you got TestMethod, +TestMethod1, then both tests will run. + +Running benchmarks, eg.: + +``` +go test -v -bench . -run BenchmarkJoin +``` + +For more information, see the [go test flags][testflag] documentation. + +### Getting Stack Traces + +If `geth` is started with the `--pprof` option, a debugging HTTP server is made available +on port 6060. You can bring up <http://localhost:6060/debug/pprof> to see the heap, +running routines etc. By clicking "full goroutine stack dump" you can generate a trace +that is useful for debugging. + +Note that if you run multiple instances of `geth`, this port will only work for the first +instance that was launched. If you want to generate stacktraces for these other instances, +you need to start them up choosing an alternative pprof port. Make sure you are +redirecting stderr to a logfile. + +``` +geth -port=30300 -verbosity 5 --pprof --pprofport 6060 2>> /tmp/00.glog +geth -port=30301 -verbosity 5 --pprof --pprofport 6061 2>> /tmp/01.glog +geth -port=30302 -verbosity 5 --pprof --pprofport 6062 2>> /tmp/02.glog +``` + +Alternatively if you want to kill the clients (in case they hang or stalled syncing, etc) +and have the stacktrace too, you can use the `-QUIT` signal with `kill`: + +``` +killall -QUIT geth +``` + +This will dump stack traces for each instance to their respective log file. + +[install-guide]: ../install-and-build/installing-geth +[code-review]: ../developers/code-review-guidelines +[cross-compile]: ../install-and-build/cross-compile +[go-modules]: https://github.com/golang/go/wiki/Modules +[discord]: https://discord.gg/nthXNEv +[go-install]: https://golang.org/doc/install +[testflag]: https://golang.org/cmd/go/#hdr-Description_of_testing_flags
diff --git go-ethereum/docs/docs/_developers/dns-discovery-setup.md celo/docs/docs/_developers/dns-discovery-setup.md new file mode 100644 index 0000000000000000000000000000000000000000..aa3f3b9688c3c1b398a4069fb0287b100398c9d6 --- /dev/null +++ celo/docs/docs/_developers/dns-discovery-setup.md @@ -0,0 +1,125 @@ +--- +title: DNS Discovery Setup Guide +sort_key: C +--- + +This document explains how to set up an [EIP 1459][dns-eip] node list using the devp2p +developer tool. The focus of this guide is creating a public list for the Ethereum mainnet +and public testnets, but you may also find this helpful if you want to set up DNS-based +discovery for a private network. + +DNS-based node lists can serve as a fallback option when connectivity to the discovery DHT +is unavailable. In this guide, we'll create node lists by crawling the discovery DHT, then +publishing the resulting node sets under chosen DNS names. + +### Installing the devp2p command + +cmd/devp2p is a developer utility and is not included in the Geth distribution. You can +install this command using `go get`: + +```shell +go get -u github.com/ethereum/go-ethereum/cmd/devp2p +``` + +To create a signing key, you might also need the `ethkey` utility. + +```shell +go get -u github.com/ethereum/go-ethereum/cmd/ethkey +``` + +### Crawling the v4 DHT + +Our first step is to compile a list of all reachable nodes. The DHT crawler in cmd/devp2p +is a batch process which runs for a set amount of time. You should should schedule this command +to run at a regular interval. To create a node list, run + +```shell +devp2p discv4 crawl -timeout 30m all-nodes.json +``` + +This walks the DHT and stores the set of all found nodes in the `all-nodes.json` file. +Subsequent runs of the same command will revalidate previously discovered node records, +add newly-found nodes to the set, and remove nodes which are no longer alive. The quality +of the node set improves with each run because the number of revalidations is tracked +alongside each node in the set. + +### Creating sub-lists through filtering + +Once `all-nodes.json` has been created and the set contains a sizeable number of nodes, +useful sub-sets of nodes can be extracted using the `devp2p nodeset filter` command. This +command takes a node set file as argument and applies filters given as command-line flags. + +To create a filtered node set, first create a new directory to hold the output set. You +can use any directory name, though it's good practice to use the DNS domain name as the +name of this directory. + +```shell +mkdir mainnet.nodes.example.org +``` + +Then, to create the output set containing Ethereum mainnet nodes only, run + +```shell +devp2p nodeset filter all-nodes.json -eth-network mainnet > mainnet.nodes.example.org/nodes.json +``` + +The following filter flags are available: + +* `-eth-network ( mainnet | ropsten | rinkeby | goerli )` selects an Ethereum network. +* `-les-server` selects LES server nodes. +* `-ip <mask>` restricts nodes to the given IP range. +* `-min-age <duration>` restricts the result to nodes which have been live for the + given duration. + +### Creating DNS trees + +To turn a node list into a DNS node tree, the list needs to be signed. To do this, you +need a key pair. To create the key file in the correct format, you can use the cmd/ethkey +utility. Please choose a good password to encrypt the key on disk. + +```shell +ethkey generate dnskey.json +``` + +Now use `devp2p dns sign` to update the signature of the node list. If your list's +directory name differs from the name you want to publish it at, please specify the DNS +name the using the `-domain` flag. This command will prompt for the key file password and +update the tree signature. + +```shell +devp2p dns sign mainnet.nodes.example.org dnskey.json +``` + +The resulting DNS tree metadata is stored in the +`mainnet.nodes.example.org/enrtree-info.json` file. + +### Publishing DNS trees + +Now that the tree is signed, it can be published to a DNS provider. cmd/devp2p currently +supports publishing to CloudFlare DNS and Amazon Route53. You can also export TXT records +as a JSON file and publish them yourself. + +To publish to CloudFlare, first create an API token in the management console. cmd/devp2p +expects the API token in the `CLOUDFLARE_API_TOKEN` environment variable. Now use the +following command to upload DNS TXT records via the CloudFlare API: + +```shell +devp2p dns to-cloudflare mainnet.nodes.example.org +``` + +Note that this command uses the domain name specified during signing. Any existing records +below this name will be erased by cmd/devp2p. + +### Using DNS trees with Geth + +Once your tree is available through a DNS name, you can tell geth to use it with the +`--discovery.dns` command line flag. Node trees are referenced using the `enrtree://` URL +scheme. You can find the URL of your tree in the `enrtree-info.json` file created by +`devp2p dns sign`. Just pass the URL as an argument to the flag in order to make use of +the published tree. + +```shell +geth --discovery.dns "enrtree://AMBMWDM3J6UY3M32TMMROUNLX6Y3YTLVC3DC6HN2AVG5NHNSAXDW6@mainnet.nodes.example.org" +``` + +[dns-eip]: https://eips.ethereum.org/EIPS/eip-1459
diff --git go-ethereum/docs/docs/_developers/issue-handling-workflow.md celo/docs/docs/_developers/issue-handling-workflow.md new file mode 100644 index 0000000000000000000000000000000000000000..412a2e959fe573960accfbbee861b0481d2a1eb3 --- /dev/null +++ celo/docs/docs/_developers/issue-handling-workflow.md @@ -0,0 +1,57 @@ +--- +title: Issue Handling Workflow +sort_key: B +--- + +### (Draft proposal) + +Keep the number of open issues under 820 + +Keep the ratio of open issues per all issues under 13% + +Have 50 issues labelled [help wanted](https://github.com/ethereum/go-ethereum/labels/help%20wanted) and 50 [good first issue](https://github.com/celo-org/celo-blockchain/labels/good%20first%20issue). + +Use structured labels of the form `<category>:<label>` or if need be `<category>:<main>/<sub>`, for example `area: plugins/foobuzzer`. + +Use the following labels. Areas and statuses depend on the application and workflow. +- area + - `area: android` + - `area: clef` + - `area: network` + - `area: swarm` + - `area: whisper` +- type + - `type: bug` + - `type: feature` + - `type: documentation` + - `type: discussion` +- status + - `status: PR review` + - `status: community working on it` +- need + - `need: more info` + - `need: steps to reproduce` + - `need: investigation` + - `need: decision` + +Use these milestones +- [Future](https://github.com/ethereum/go-ethereum/milestone/80) - Maybe implement one day +- [Coming soon](https://github.com/ethereum/go-ethereum/milestone/81) - Not assigned to a specific release, but to be delivered in one of the upcoming releases +- \<next version\> - Next release with a version number +- \<next-next version\> - The version after the next release with a version number +- \<next major release\> - Optional. + +It's ok to not set a due date for a milestone, but once you release it, close it. If you have a few issues dangling, consider moving them to the next milestone, and close this one. + +Optionally, use a project board to collect issues of a larger effort that has an end state and overarches multiple releases. + +## Workflow +We have a weekly or bi-weekly triage meeting. Issues are preselected by [labelling them "status:triage" and sorted the oldest ones first](https://github.com/ethereum/go-ethereum/issues?q=is%3Aopen+is%3Aissue+label%3Astatus%3Atriage+sort%3Acreated-asc). This is when we go through the new issues and do one of the following +1. Close it. +1. Assign it to "Coming soon" milestone which doesn't have an end date. +1. Move it to the "Future" milestone. +1. Change its status to "Need:\<what-is-needed\>". + +Optional further activities: +* Label the issue with the appropriate area/component. +* Add a section to the FAQ or add a wiki page. Link to it from the issue.
diff --git go-ethereum/docs/docs/_getting-started/dev-mode.md celo/docs/docs/_getting-started/dev-mode.md new file mode 100644 index 0000000000000000000000000000000000000000..8f94f4333f437e33175938d5ddcab5998900f514 --- /dev/null +++ celo/docs/docs/_getting-started/dev-mode.md @@ -0,0 +1,69 @@ +--- +title: Dev mode +sort_key: B +--- + +Geth has a development mode that sets up a single node Ethereum test network with options optimized for developing on local machines. You enable it with the `--dev` argument. + +Starting geth in dev mode does the following: + +- Initializes the data directory with a testing genesis block +- Sets max peers to 0 +- Turns off discovery by other nodes +- Sets the gas price to 0 +- Uses the Clique PoA consensus engine with which allows blocks to be mined as-needed without excessive CPU and memory consumption +- By default produces a block per second + - To instead produce blocks on-demand when transactions are waiting to be mined, set `--dev.period 0` + +## Start Geth in Dev Mode + +You can specify a data directory to maintain state between runs using the `--datadir` option, otherwise, databases are ephemeral and in-memory: + +```shell +geth --dev +``` + +Optionally specify `--dev.period` if a block period other than 1 per second is desired. + +Whether run ephemerally or with a persistent data directory, this uses the hard-coded developer account `0x47e172f6cfb6c7d01c1574fa3e2be7cc73269d95` to configure the validator and tx fee recipient. Note that currently dev mode does not function properly when setting the validator and tx fee recipient params to accounts other than the hard-coded developer account. + +### Example with Remix + +For this guide, start geth in dev mode as described above, and enable [RPC](../_rpc/server.md) so you can connect other applications to geth. For this guide, we use Remix, the web-based Ethereum IDE, so also allow its domains to accept cross-origin requests. + +```shell +geth --dev --datadir test-chain-dir +geth --dev --datadir test-chain-dir --http --http.corsdomain "https://remix.ethereum.org,http://remix.ethereum.org" +``` + +Connect to the IPC console on the node from another terminal window: + +```shell +geth attach <IPC_LOCATION> +``` + +Once geth is running in dev mode, you can interact with it in the same way as when geth is running in other ways. + +For example, create a test account: + +```shell +> personal.newAccount() +``` + +And transfer ether from the coinbase to the new account: + +```shell +> eth.sendTransaction({from:eth.coinbase, to:eth.accounts[1], value: web3.toWei(0.05, "ether")}) +``` + +And check the balance of the account: + +```shell +> eth.getBalance(eth.accounts[1]) +``` + +If you want to test your dapps with a realistic block time use the `--dev.period` option when you start dev mode with the `--dev.period 14` argument. + +#### Connect Remix to Geth + +With geth now running, open <https://remix.ethereum.org>. Compile the contract as normal, but when you deploy and run a contract, select _Custom -- External Http Provider_ from the _Environment_ dropdown, and add "http://127.0.0.1:8545" to the popup box. Click _Deploy_, and interact with the contract. You should see contract creation, mining, and transaction activity. \ No newline at end of file
diff --git go-ethereum/docs/docs/_getting-started/index.md celo/docs/docs/_getting-started/index.md new file mode 100644 index 0000000000000000000000000000000000000000..dc0fa97e1acba66a8c1efa5409f16a17506bda94 --- /dev/null +++ celo/docs/docs/_getting-started/index.md @@ -0,0 +1,98 @@ +--- +title: Getting Started with Geth +permalink: docs/getting-started/geth +sort_key: A +--- + +To use Geth, you need to install it first. You can install the geth software in a variety +of ways. These include installing it via your favorite package manager; downloading a +standalone pre-built binary; running as a docker container; or building it yourself. + +For this tutorial, we'll assume you are comfortable with downloading a pre-built binary. +Head over to the [install and build](./install-and-build/installing-geth) section and +follow the instructions for your operating system if you wish to use a different +installation method. + +### Downloading Geth + +We distribute all our stable releases and development builds as standalone binaries. These +are useful for scenarios where you'd like to: a) install a specific version of our code +(e.g., for reproducible environments); b) install on machines without internet access +(e.g., air gapped computers); or c) do not like automatic updates and would rather +manually install software. + +To download Geth, go to the [Downloads page](https://geth.ethereum.org/downloads) and +select the latest stable release matching your platform. + +We create the following archives: + +- 32bit, 64bit, ARMv5, ARMv6, ARMv7 and ARM64 archives (`.tar.gz`) on Linux +- 64bit archives (`.tar.gz`) on macOS +- 32bit and 64bit archives (`.zip`) and installers (`.exe`) on Windows + +For all binaries we provide two options, one containing only Geth, and another containing +Geth along with all the developer tools from our repository (`abigen`, `bootnode`, +`disasm`, `evm`, `rlpdump`). Read our +[`README`](https://github.com/ethereum/go-ethereum#executables) for more information about +these executables. + +### Creating an account + +Before starting Geth you first need to create an account that represents a key pair. Use +the following command to create a new account and set a password for that account: + +```shell +geth account new +``` + +_[Read this guide](./interface/managing-your-accounts) for more details on importing +existing Ethereum accounts and other uses of the `account` command._ + +### Sync modes + +Running Geth starts an Ethereum node that can join any existing network, or create a new +one. You can start Geth in one of three different sync modes using the `--syncmode "{mode}"` +argument that determines what sort of node it is in the network. + +These are: + +- **Full**: Downloads all blocks (including headers, transactions and receipts) and + generates the state of the blockchain incrementally by executing every block. +- **Fast** (Default): Downloads all blocks (including headers, transactions and + receipts), verifies all headers, and downloads the state and verifies it against the + headers. +- **Light**: Downloads all block headers, block data, and verifies some randomly. + +For example: + +```shell +geth --syncmode "light" +``` + +### Javascript Console + +Once you have an account and Geth is running, you can interact with it by opening another +terminal and using the following command to open a JavaScript console: + +```shell +geth attach +``` + +If you get the error 'unable to attach to remote geth', try connecting via HTTP as shown below: + +```shell +geth attach http://127.0.0.1:8545 +``` + +In the console you can issue any of the Geth commands, for example, to list all the +accounts on the node, use: + +```js +> eth.accounts +``` + +You can also enter the console directly when you start the node with the `console` command: + +```shell +geth console --syncmode "light" +```
diff --git go-ethereum/docs/docs/_getting-started/private-net.md celo/docs/docs/_getting-started/private-net.md new file mode 100644 index 0000000000000000000000000000000000000000..dd778b0aefffb074f098735819aa60c03bbd291f --- /dev/null +++ celo/docs/docs/_getting-started/private-net.md @@ -0,0 +1,125 @@ +--- +title: Private Network Tutorial +sort_key: B +--- + +This page describes how to set up a local cluster of nodes, advise how to make it private, +and how to hook up your nodes on the eth-netstat network monitoring app. A fully +controlled ethereum network is useful as a backend for network integration testing (core +developers working on issues related to networking/blockchain synching/message +propagation, etc or DAPP developers testing multi-block and multi-user scenarios). + +We assume you are able to build `geth` following the [build instructions](../install-and-build/build-from-source) + +## Setting up multiple nodes + +In order to run multiple ethereum nodes locally, you have to make sure: + +- each instance has a separate data directory (`--datadir`) +- each instance runs on a different port (both eth and rpc) (`--port and --rpcport`) +- in case of a cluster the instances must know about each other +- the ipc endpoint is unique or the ipc interface is disabled (`--ipcpath or --ipcdisable`) + +You start the first node (let's make port explicit and disable ipc interface) + + geth --datadir="/tmp/eth/60/01" -verbosity 6 --ipcdisable --port 30301 --rpcport 8101 console 2>> /tmp/eth/60/01.log + +We started the node with the console, so that we can grab the enode url for instance: + + > admin.nodeInfo.enode + enode://8c544b4a07da02a9ee024def6f3ba24b2747272b64e16ec5dd6b17b55992f8980b77938155169d9d33807e501729ecb42f5c0a61018898c32799ced152e9f0d7@9[::]:30301 + +`[::]` will be parsed as localhost (`127.0.0.1`). If your nodes are on a local network +check each individual host machine and find your ip with `ifconfig` (on Linux and MacOS): + + $ ifconfig|grep netmask|awk '{print $2}' + 127.0.0.1 + 192.168.1.97 + +If your peers are not on the local network, you need to know your external IP address (use +a service) to construct the enode url. + +Now you can launch a second node with: + + geth --datadir="/tmp/eth/60/02" --verbosity 6 --ipcdisable --port 30302 --rpcport 8102 console 2>> /tmp/eth/60/02.log + +If you want to connect this instance to the previously started node you can add it as a +peer from the console with `admin.addPeer(enodeUrlOfFirstInstance)`. + +You can test the connection by typing in geth console: + + > net.listening + true + > net.peerCount + 1 + > admin.peers + ... + +## Local cluster + +As an extention of the above, you can spawn a local cluster of nodes easily. It can also +be scripted including account creation which is needed for mining. See +[`gethcluster.sh`](https://github.com/ethersphere/eth-utils) script, and the README there +for usage and examples. + +## Private network + +See [[the Private Network Page|Private network]] for more information. + +### Setup bootnode + +The first time a node connects to the network it uses one of the predefined +[bootnodes](https://github.com/ethereum/go-ethereum/blob/master/params/bootnodes.go). +Through these bootnodes a node can join the network and find other nodes. In the case of a +private cluster these predefined bootnodes are not of much use. Therefore go-ethereum +offers a bootnode implementation that can be configured and run in your private network. + +It can be run through the command. + + > bootnode + Fatal: Use -nodekey or -nodekeyhex to specify a private key + +As can be seen the bootnode asks for a key. Each ethereum node, including a bootnode is +identified by an enode identifier. These identifiers are derived from a key. Therefore you +will need to give the bootnode such key. Since we currently don't have one we can instruct +the bootnode to generate a key (and store it in a file) before it starts. + + > bootnode -genkey bootnode.key + I0216 09:53:08.076155 p2p/discover/udp.go:227] Listening, enode://890b6b5367ef6072455fedbd7a24ebac239d442b18c5ab9d26f58a349dad35ee5783a0dd543e4f454fed22db9772efe28a3ed6f21e75674ef6203e47803da682@ + + (exit with CTRL-C) + +The stored key can be seen with: + + > cat bootnode.key + dc90f8f7324f1cc7ba52c4077721c939f98a628ed17e51266d01c9cd0294033a + +To instruct geth nodes to use our own bootnode(s) use the `--bootnodes` flag. This is a +comma separated list of bootnode enode identifiers. + + geth --bootnodes "enode://890b6b5367ef6072455fedbd7a24ebac239d442b18c5ab9d26f58a349dad35ee5783a0dd543e4f454fed22db9772efe28a3ed6f21e75674ef6203e47803da682@[::]:30301" + +(what `[::]` means is explained previously) + +Since it is convenient to start the bootnode each time with the same enode we can give the +bootnode program the just generated key on the next time it is started. + + bootnode -nodekey bootnode.key + I0216 10:01:19.125600 p2p/discover/udp.go:227] Listening, enode://890b6b5367ef6072455fedbd7a24ebac239d442b18c5ab9d26f58a349dad35ee5783a0dd543e4f454fed22db9772efe28a3ed6f21e75674ef6203e47803da682@[::]:30301 + +or + + bootnode -nodekeyhex dc90f8f7324f1cc7ba52c4077721c939f98a628ed17e51266d01c9cd0294033a + I0216 10:01:40.094089 p2p/discover/udp.go:227] Listening, enode://890b6b5367ef6072455fedbd7a24ebac239d442b18c5ab9d26f58a349dad35ee5783a0dd543e4f454fed22db9772efe28a3ed6f21e75674ef6203e47803da682@[::]:30301 + +## Monitoring your nodes + +[This page](https://github.com/ethereum/wiki/wiki/Network-Status) describes how to use the +[The Ethereum (centralised) network status monitor](eth-stats). to monitor your nodes. + +[This page](monitoring) or [this README](https://github.com/ethersphere/eth-utils) +describes how you set up your own monitoring service for a (private or public) local +cluster. + +[eth-stats]: https://ethstats.org +[monitoring]: ../doc/setting-up-monitoring-on-local-cluster
diff --git go-ethereum/docs/docs/_install-and-build/Backup--restore.md celo/docs/docs/_install-and-build/Backup--restore.md new file mode 100644 index 0000000000000000000000000000000000000000..d717f50236aad2f8b0bbc80400430fe84938efb2 --- /dev/null +++ celo/docs/docs/_install-and-build/Backup--restore.md @@ -0,0 +1,65 @@ +--- +title: Backup & Restore +sort_key: C +--- + +Most important info first: **REMEMBER YOUR PASSWORD** and **BACKUP YOUR KEYSTORE**. + +## Data Directory + +Everything `geth` persists gets written inside its data directory. The default data +directory locations are platform specific: + +* Mac: `~/Library/Ethereum` +* Linux: `~/.ethereum` +* Windows: `%APPDATA%\Ethereum` + +Accounts are stored in the `keystore` subdirectory. The contents of this directories +should be transportable between nodes, platforms, implementations (C++, Go, Python). + +To configure the location of the data directory, the `--datadir` parameter can be +specified. See [CLI Options](../interface/command-line-options) for more details. + +Note the [ethash dag](../interface/mining) is stored at `~/.ethash` (Mac/Linux) or +`%APPDATA%\Ethash` (Windows) so that it can be reused by all clients. You can store this +in a different location by using a symbolic link. + +## Cleanup + +Geth's blockchain and state databases can be removed with: + +``` +geth removedb +``` + +This is useful for deleting an old chain and sync'ing to a new one. It only affects data +directories that can be re-created on synchronisation and does not touch the keystore. + +## Blockchain Import/Export + +Export the blockchain in binary format with: + +``` +geth export <filename> +``` + +Or if you want to back up portions of the chain over time, a first and last block can be +specified. For example, to back up the first epoch: + +``` +geth export <filename> 0 29999 +``` + +Note that when backing up a partial chain, the file will be appended rather than +truncated. + +Import binary-format blockchain exports with: + +``` +geth import <filename> +``` + +_See https://github.com/ethereum/wiki/wiki/Blockchain-import-export for more info_ + + +And finally: **REMEMBER YOUR PASSWORD** and **BACKUP YOUR KEYSTORE**
diff --git go-ethereum/docs/docs/_install-and-build/Build-from-Source.md celo/docs/docs/_install-and-build/Build-from-Source.md new file mode 100644 index 0000000000000000000000000000000000000000..6e575a6b09cce472c07ce3e748da23b715597c36 --- /dev/null +++ celo/docs/docs/_install-and-build/Build-from-Source.md @@ -0,0 +1,29 @@ +--- +title: Build from source +sort_key: B +--- +<!-- TODO: Maybe move? --> + +Go Ethereum (as its name implies) is written in [Go](https://golang.org), and as such to build from source code you need to have a Go version as recent as possible. This guide does not go into details on how to install Go itself, for that please read the [Go installation instructions](https://golang.org/doc/install). + +Assuming you have Go installed, you can download our project via: + +```shell +go get -d github.com/ethereum/go-ethereum +``` + +The above command will checkout the default version of Go Ethereum into your local `GOPATH` work space, but it will not build any executables for you. To do that you can either build one specifically: + + go install github.com/ethereum/go-ethereum/cmd/geth + +Or you can also build the entire project and install `geth` along with all developer tools by running `go install ./...` in the repository root inside your `GOPATH` work space. + +### Building without a Go workflow + +If you do not want to set up Go work spaces on your machine, only build `geth` and forget about the build process, you can clone our repository directly into a folder of your choosing and invoke `make`, which will configure everything for a temporary build and clean up after itself: + + git clone https://github.com/ethereum/go-ethereum.git + cd go-ethereum + make geth + +This will create a `geth` (or `geth.exe` on Windows) executable file in the `go-ethereum/build/bin` folder that you can move wherever you want to run from. The binary is standalone and doesn't require any additional files.
diff --git go-ethereum/docs/docs/_install-and-build/Installing-Celo-Blockchain.md celo/docs/docs/_install-and-build/Installing-Celo-Blockchain.md new file mode 100644 index 0000000000000000000000000000000000000000..895bf444e09dcc4457a10e3f2131dc23d43d0c20 --- /dev/null +++ celo/docs/docs/_install-and-build/Installing-Celo-Blockchain.md @@ -0,0 +1,248 @@ +--- +title: Installing Celo Blockchain +sort_key: A +--- + +You can install the Celo Blockchain client using a variety of ways. These include installing it via your favorite package manager; downloading a standalone pre-built bundle; running as a docker container; or building it yourself. This document details all of the possibilities to get you joining the Ethereum network using whatever means you prefer. + +{:toc} + +- this will be removed by the toc + +## Install from a package manager + +### Install on macOS via Homebrew + +The easiest way to install go-ethereum is to use our +Homebrew tap. If you don't have Homebrew, [install it first](http://brew.sh). + +Run the following commands to add the tap and install `geth`: + +```shell +brew tap ethereum/ethereum +brew install ethereum +``` + +You can install the master branch using the `--devel` parameter: + +```shell +brew install ethereum --devel +``` + +The `abigen`, `bootnode`, `checkpoint-admin`, `clef`, `devp2p`, `ethkey`, `evm`, `faucet`, `geth`, `p2psim`, `puppeth`, `rlpdump`, and `wnode` commands are then available on your system in `/usr/local/bin/`. + +Find the different options and commands available with `geth --help`. + +### Install on Ubuntu via PPAs + +The easiest way to install go-ethereum on Ubuntu-based distributions is with the built-in launchpad PPAs (Personal Package Archives). We provide a single PPA repository that contains both our stable and development releases for Ubuntu versions `trusty`, `xenial`, `zesty` and `artful`. + +To enable our launchpad repository run: + +```shell +sudo add-apt-repository -y ppa:ethereum/ethereum +``` + +Then install the stable version of go-ethereum: + +```shell +sudo apt-get update +sudo apt-get install ethereum +``` + +Or the develop version via: + +```shell +sudo apt-get update +sudo apt-get install ethereum-unstable +``` + +The `abigen`, `bootnode`, `clef`, `evm`, `geth`, `puppeth`, `rlpdump`, and `wnode` commands are then available on your system in `/usr/bin/`. + +Find the different options and commands available with `geth --help`. + +### Install on Windows + +The easiest way to install go-ethereum is to download a pre-compiled binary from the [downloads](https://geth.ethereum.org/downloads/) page. The page provides an installer as well as a zip file. The installer puts `geth` into your `PATH` automatically. The zip file contains the command `.exe` files that you can use without installing by runnning it from the command prompt. + +### Install on FreeBSD via pkg + +```shell +pkg install go-ethereum +``` + +The `geth` command is then available on your system in `/usr/local/bin/`. You can create a new account on your node with: + +```shell +geth account new +``` + +Find the different options and commands available with `geth --help`. + +### Install on FreeBSD via ports + +Go to the `net-p2p/go-ethereum` ports directory: + +```shell +cd /usr/ports/net-p2p/go-ethereum +``` + +Then build it the standard way (as root): + +```shell +make install +``` + +The `abigen`, `bootnode`, `clef`, `evm`, `geth`, `puppeth`, `rlpdump`, and `wnode` commands are then available on your system in `/usr/local/bin/`. + +Find the different options and commands available with `geth --help`. + +### Install on Arch Linux via `pacman` + +The `geth` package is available from the [community repo](https://www.archlinux.org/packages/community/x86_64/geth/). + +You can install it using: + +```shell +pacman -S geth +``` + +The `abigen`, `bootnode`, `clef`, `evm`, `geth`, `puppeth`, `rlpdump`, and `wnode` commands are then available on your system in `/usr/bin/`. + +Find the different options and commands available with `geth --help`. + +## Download standalone bundle + +We distribute our stable releases and development builds as standalone bundles. These are useful when you'd like to: a) install a specific version of our code (e.g., for reproducible environments); b) install on machines without internet access (e.g., air-gapped computers); or c) do not like automatic updates and would rather manually install software. + +We create the following standalone bundles: + +- 32bit, 64bit, ARMv5, ARMv6, ARMv7 and ARM64 archives (`.tar.gz`) on Linux +- 64bit archives (`.tar.gz`) on macOS +- 32bit and 64bit archives (`.zip`) and installers (`.exe`) on Windows + +We provide archives containing only Geth, and archives containing Geth along with the developer tools from our repository (`abigen`, `bootnode`, `disasm`, `evm`, `rlpdump`). Read our [`README`](https://github.com/ethereum/go-ethereum#executables) for more information about these executables. + +Download these bundles from the [Go Ethereum Downloads](https://geth.ethereum.org/downloads) page. + +## Run inside Docker container + +If you prefer containerized processes, we maintain a Docker image with recent snapshot builds from our `develop` branch on DockerHub. We maintain four different Docker images for running the latest stable or development versions of Geth. + +- `ethereum/client-go:latest` is the latest development version of Geth (default) +- `ethereum/client-go:stable` is the latest stable version of Geth +- `ethereum/client-go:{version}` is the stable version of Geth at a specific version number +- `ethereum/client-go:release-{version}` is the latest stable version of Geth at a specific version family + +To pull an image and start a node, run these commands: + +```shell +docker pull ethereum/client-go +docker run -it -p 30303:30303 ethereum/client-go +``` + +We also maintain four different Docker images for running the latest stable or development versions of miscellaneous Ethereum tools. + +- `ethereum/client-go:alltools-latest` is the latest development version of the Ethereum tools +- `ethereum/client-go:alltools-stable` is the latest stable version of the Ethereum tools +- `ethereum/client-go:alltools-{version}` is the stable version of the Ethereum tools at a specific version number +- `ethereum/client-go:alltools-release-{version}` is the latest stable version of the Ethereum tools at a specific version family + +The image has the following ports automatically exposed: + +- `8545` TCP, used by the HTTP based JSON RPC API +- `8546` TCP, used by the WebSocket based JSON RPC API +- `8547` TCP, used by the GraphQL API +- `30303` TCP and UDP, used by the P2P protocol running the network + +_Note, if you are running an Ethereum client inside a Docker container, you should mount a data volume as the client's data directory (located at `/root/.ethereum` inside the container) to ensure that downloaded data is preserved between restarts and/or container life-cycles._ + +## Build go-ethereum from source code + +### Most Linux systems and macOS + +Go Ethereum is written in [Go](https://golang.org), so to build from source code you need the most recent version of Go. This guide doesn't cover how to install Go itself, for details read the [Go installation instructions](https://golang.org/doc/install) and grab any needed bundles from the [Go download page](https://golang.org/dl/). + +With Go installed, you can download the project into you `GOPATH` workspace via: + +```shell +go get -d github.com/ethereum/go-ethereum +``` + +The above command does not build any executables. To do that you can either build one specifically: + +```shell +go install github.com/ethereum/go-ethereum/cmd/geth +``` + +Or you can build the entire project and install `geth` along with all developer tools by running `go install ./...` in the `celo-org/celo-blockchain` repository root inside your `GOPATH` workspace. + +If you are using macOS and see errors related to macOS header files, install XCode Command Line Tools with `xcode-select --install`, and try again. + +### Windows + +The Chocolatey package manager provides an easy way to get the required build tools installed. If you don't have chocolatey, [follow the instructions](https://chocolatey.org) to install it first. + +Then open an Administrator command prompt and install the build tools you need: + +```shell +C:\Windows\system32> choco install git +C:\Windows\system32> choco install golang +C:\Windows\system32> choco install mingw +``` + +Installing these packages sets up the path environment variables, you need to open a new command prompt to get the new path. + +The following steps don't need Administrator privileges. First create and set up a Go workspace directory layout, then clone the source and build it. + +```shell +C:\Users\xxx> mkdir src\github.com\ethereum +C:\Users\xxx> git clone https://github.com/ethereum/go-ethereum src\github.com\ethereum\go-ethereum +C:\Users\xxx> cd src\github.com\ethereum\go-ethereum +C:\Users\xxx> go get -u -v golang.org/x/net/context +C:\Users\xxx\src\github.com\ethereum\go-ethereum> go install -v ./cmd/... +``` + +### FreeBSD + +Ports are slightly more up to date (1.8.14 at the time of writing) + +Clone the repository to a directory of your choosing: + +```shell +git clone https://github.com/ethereum/go-ethereum +``` + +Building `geth` requires the Go compiler: + +```shell +pkg install go +``` + +If your golang version is >= 1.5, build the `geth` program using the following command: + +```shell +cd go-ethereum +make geth +``` + +If your golang version is &lt; 1.5 (quarterly packages, for example), use the following command instead: + +```shell +cd go-ethereum +CC=clang make geth +``` + +You can now run `build/bin/geth` to start your node. + +### Building without a Go workflow + +If you do not want to set up Go workspaces on your machine, but only build `geth` and forget about the build process, you can clone our repository and use the `make` command, which configures everything for a temporary build and cleans up afterwards. This method of building only works on UNIX-like operating systems, and you still need Go installed. + +```shell +git clone https://github.com/ethereum/go-ethereum.git +cd go-ethereum +make geth +``` + +These commands create a `geth` executable file in the `go-ethereum/build/bin` folder that you can move wherever you want to run from. The binary is standalone and doesn't require any additional files.
diff --git go-ethereum/docs/docs/_install-and-build/cross-compile.md celo/docs/docs/_install-and-build/cross-compile.md new file mode 100644 index 0000000000000000000000000000000000000000..55af4e6652b8c9a0b829129ef238c36606357c1e --- /dev/null +++ celo/docs/docs/_install-and-build/cross-compile.md @@ -0,0 +1,167 @@ +--- +title: Cross-Compiling Geth +sort_key: C +--- + +**Note: All of these and much more have been merged into the project Makefile. You can +cross build via `make geth-<os>-<platform>` without needing to know any of these details +from below.** + +Developers usually have a preferred platform that they feel most comfortable working in, +with all the necessary tools, libraries and environments set up for an optimal workflow. +However, there's often need to build for either a different CPU architecture, or an +entirely different operating system; but maintaining a development environment for each +and switching between the them quickly becomes unwieldy. + +Here we present a very simple way to cross compile Ethereum to various operating systems +and architectures using a minimal set of prerequisites and a completely containerized +approach, guaranteeing that your development environment remains clean even after the +complex requirements and mechanisms of a cross compilation. + +The currently supported target platforms are: + + - ARMv7 Android and iOS + - 32 bit, 64 bit and ARMv5 Linux + - 32 bit and 64 bit Mac OSX + - 32 bit and 64 bit Windows + +Please note, that cross compilation does not replace a release build. Although resulting +binaries can usually run perfectly on the desired platform, compiling on a native system +with the specialized tools provided by the official vendor can often result in more a +finely optimized code. + +## Cross compilation environment + +Although the `go-ethereum` project is written in Go, it does include a bit of C code +shared between all implementations to ensure that all perform equally well, including a +dependency to the GNU Multiple Precision Arithmetic Library. Because of these, Go cannot +by itself compile to a different platform than the host. To overcome this limitation, we +will use [`xgo`](https://github.com/karalabe/xgo), a Go cross compiler package based on +Docker containers that has been architected specifically to allow both embedded C snippets +as well as simpler external C dependencies during compilation. + +The `xgo` project has two simple dependencies: Docker (to ensure that the build +environment is completely contained) and Go. On most platforms these should be available +from the official package repositories. For manually installing them, please consult their +install guides at [Docker](https://docs.docker.com/installation/) and +[Go](https://golang.org/doc/install) respectively. This guide assumes that these two +dependencies are met. + +To install and/or update xgo, simply type: + + $ go get -u github.com/karalabe/xgo + +You can test whether `xgo` is functioning correctly by requesting it to cross +compile itself and verifying that all cross compilations succeeded or not. + + $ xgo github.com/karalabe/xgo + ... + + $ ls -al + -rwxr-xr-x 1 root root 2792436 Sep 14 16:45 xgo-android-21-arm + -rwxr-xr-x 1 root root 2353212 Sep 14 16:45 xgo-darwin-386 + -rwxr-xr-x 1 root root 2906128 Sep 14 16:45 xgo-darwin-amd64 + -rwxr-xr-x 1 root root 2388288 Sep 14 16:45 xgo-linux-386 + -rwxr-xr-x 1 root root 2960560 Sep 14 16:45 xgo-linux-amd64 + -rwxr-xr-x 1 root root 2437864 Sep 14 16:45 xgo-linux-arm + -rwxr-xr-x 1 root root 2551808 Sep 14 16:45 xgo-windows-386.exe + -rwxr-xr-x 1 root root 3130368 Sep 14 16:45 xgo-windows-amd64.exe + + +## Building Ethereum + +Cross compiling Ethereum is analogous to the above example, but an additional flags is +required to satisfy the dependencies: + + - `--deps` is used to inject arbitrary C dependency packages and pre-build them + +Injecting the GNU Arithmetic Library dependency and selecting `geth` would be: + + $ xgo --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 \ + github.com/ethereum/go-ethereum/cmd/geth + ... + + $ ls -al + -rwxr-xr-x 1 root root 23213372 Sep 14 17:59 geth-android-21-arm + -rwxr-xr-x 1 root root 14373980 Sep 14 17:59 geth-darwin-386 + -rwxr-xr-x 1 root root 17373676 Sep 14 17:59 geth-darwin-amd64 + -rwxr-xr-x 1 root root 21098910 Sep 14 17:59 geth-linux-386 + -rwxr-xr-x 1 root root 25049693 Sep 14 17:59 geth-linux-amd64 + -rwxr-xr-x 1 root root 20578535 Sep 14 17:59 geth-linux-arm + -rwxr-xr-x 1 root root 16351260 Sep 14 17:59 geth-windows-386.exe + -rwxr-xr-x 1 root root 19418071 Sep 14 17:59 geth-windows-amd64.exe + + +As the cross compiler needs to build all the dependencies as well as the main project +itself for each platform, it may take a while for the build to complete (approximately 3-4 +minutes on a Core i7 3770K machine). + +### Fine tuning the build + +By default Go, and inherently `xgo`, checks out and tries to build the master branch of a +source repository. However, more often than not, you'll probably want to build a different +branch from possibly an entirely different remote repository. These can be controlled via +the `--remote` and `--branch` flags. + +To build the `develop` branch of the official `go-ethereum` repository instead of the +default `master` branch, you just need to specify it as an additional command line flag +(`--branch`): + + $ xgo --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 \ + --branch=develop \ + github.com/ethereum/go-ethereum/cmd/geth + +Additionally, during development you will most probably want to not only build a custom +branch, but also one originating from your own fork of the repository instead of the +upstream one. This can be done via the `--remote` flag: + + $ xgo --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 \ + --remote=https://github.com/karalabe/go-ethereum \ + --branch=rpi-staging \ + github.com/ethereum/go-ethereum/cmd/geth + +By default `xgo` builds binaries for all supported platforms and architectures, with +Android binaries defaulting to the highest released Android NDK platform. To limit the +build targets or compile to a different Android platform, use the `--targets` CLI +parameter. + + $ xgo --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 \ + --targets=android-16/arm,windows/* \ + github.com/ethereum/go-ethereum/cmd/geth + +### Building locally + +If you would like to cross compile your local development version, simply specify a local +path (starting with `.` or `/`), and `xgo` will use all local code from `GOPATH`, only +downloading missing dependencies. In such a case of course, the `--branch`, `--remote` and +`--pkg` arguments are no-op: + + $ xgo --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 \ + ./cmd/geth + +## Using the Makefile + +Having understood the gist of `xgo` based cross compilation, you do not need to actually +memorize and maintain these commands, as they have been incorporated into the official +[Makefile](https://github.com/ethereum/go-ethereum/blob/master/Makefile) and can be +invoked with a trivial `make` request: + + * `make geth-cross`: Cross compiles to every supported OS and architecture + * `make geth-<os>`: Cross compiles supported architectures of a particular OS (e.g. `linux`) + * `make geth-<os>-<arch>`: Cross compiles to a specific OS/architecture (e.g. `linux`, `arm`) + +We advise using the `make` based commands opposed to manually invoking `xgo` as we do +maintain the Makefile actively whereas we cannot guarantee that this document will be +always readily updated to latest advancements. + +### Tuning the cross builds + +A few of the `xgo` build options have also been surfaced directly into the Makefile to +allow fine tuning builds to work around either upstream Go issues, or to enable some +fancier mechanics. + + - `make ... GO=<go>`: Use a specific Go runtime (e.g. `1.5.1`, `1.5-develop`, `develop`) + - `make ... MODE=<mode>`: Build a specific target type (e.g. `exe`, `c-archive`). + +Please note that these are not yet fully finalized, so they may or may not change in the +future as our code and the Go runtime features change.
diff --git go-ethereum/docs/docs/_interface/Command-Line-Options.md celo/docs/docs/_interface/Command-Line-Options.md new file mode 100644 index 0000000000000000000000000000000000000000..b81eb98c8aea68ed76f5a499972c6a55caa219bc --- /dev/null +++ celo/docs/docs/_interface/Command-Line-Options.md @@ -0,0 +1,212 @@ +--- +title: Command-line Options +sort_key: A +--- + +``` +$ geth --help +NAME: + geth - the go-ethereum command line interface + + Copyright 2013-2019 The go-ethereum Authors + +USAGE: + geth [options] command [command options] [arguments...] + +VERSION: + 1.9.6-stable + +COMMANDS: + account Manage accounts + attach Start an interactive JavaScript environment (connect to node) + console Start an interactive JavaScript environment + copydb Create a local chain from a target chaindata folder + dump Dump a specific block from storage + dumpconfig Show configuration values + export Export blockchain into file + export-preimages Export the preimage database into an RLP stream + import Import a blockchain file + import-preimages Import the preimage database from an RLP stream + init Bootstrap and initialize a new genesis block + inspect Inspect the storage size for each type of data in the database + js Execute the specified JavaScript files + license Display license information + makecache Generate ethash verification cache (for testing) + makedag Generate ethash mining DAG (for testing) + removedb Remove blockchain and state databases + retesteth Launches geth in retesteth mode + version Print version numbers + wallet Manage Ethereum presale wallets + help, h Shows a list of commands or help for one command + +ETHEREUM OPTIONS: + --config value TOML configuration file + --datadir value Data directory for the databases and keystore (default: "~/Library/Ethereum") + --datadir.ancient value Data directory for ancient chain segments (default = inside chaindata) + --keystore value Directory for the keystore (default = inside the datadir) + --nousb Disables monitoring for and managing USB hardware wallets + --networkid value Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby) (default: 1) + --testnet Ropsten network: pre-configured proof-of-work test network + --rinkeby Rinkeby network: pre-configured proof-of-authority test network + --goerli Görli network: pre-configured proof-of-authority test network + --syncmode value Blockchain sync mode ("fast", "full", or "light") (default: fast) + --exitwhensynced Exits after block synchronisation completes + --gcmode value Blockchain garbage collection mode ("full", "archive") (default: "full") + --ethstats value Reporting URL of a ethstats service (nodename:secret@host:port) + --identity value Custom node name + --lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength + --whitelist value Comma separated block number-to-hash mappings to enforce (<number>=<hash>) + +LIGHT CLIENT OPTIONS: + --light.serve value Maximum percentage of time allowed for serving LES requests (multi-threaded processing allows values over 100) (default: 0) + --light.ingress value Incoming bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited) (default: 0) + --light.egress value Outgoing bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited) (default: 0) + --light.maxpeers value Maximum number of light clients to serve, or light servers to attach to (default: 100) + --ulc.servers value List of trusted ultra-light servers + --ulc.fraction value Minimum % of trusted ultra-light servers required to announce a new head (default: 75) + --ulc.onlyannounce Ultra light server sends announcements only + +DEVELOPER CHAIN OPTIONS: + --dev Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled + --dev.period value Block period to use in developer mode (0 = mine only if transaction pending) (default: 1) + +ETHASH OPTIONS: + --ethash.cachedir value Directory to store the ethash verification caches (default = inside the datadir) + --ethash.cachesinmem value Number of recent ethash caches to keep in memory (16MB each) (default: 2) + --ethash.cachesondisk value Number of recent ethash caches to keep on disk (16MB each) (default: 3) + --ethash.dagdir value Directory to store the ethash mining DAGs (default: "~/Library/Ethash") + --ethash.dagsinmem value Number of recent ethash mining DAGs to keep in memory (1+GB each) (default: 1) + --ethash.dagsondisk value Number of recent ethash mining DAGs to keep on disk (1+GB each) (default: 2) + +TRANSACTION POOL OPTIONS: + --txpool.locals value Comma separated accounts to treat as locals (no flush, priority inclusion) + --txpool.nolocals Disables price exemptions for locally submitted transactions + --txpool.journal value Disk journal for local transaction to survive node restarts (default: "transactions.rlp") + --txpool.rejournal value Time interval to regenerate the local transaction journal (default: 1h0m0s) + --txpool.pricelimit value Minimum gas price limit to enforce for acceptance into the pool (default: 1) + --txpool.pricebump value Price bump percentage to replace an already existing transaction (default: 10) + --txpool.accountslots value Minimum number of executable transaction slots guaranteed per account (default: 16) + --txpool.globalslots value Maximum number of executable transaction slots for all accounts (default: 4096) + --txpool.accountqueue value Maximum number of non-executable transaction slots permitted per account (default: 64) + --txpool.globalqueue value Maximum number of non-executable transaction slots for all accounts (default: 1024) + --txpool.lifetime value Maximum amount of time non-executable transaction are queued (default: 3h0m0s) + +PERFORMANCE TUNING OPTIONS: + --cache value Megabytes of memory allocated to internal caching (default = 4096 mainnet full node, 128 light mode) (default: 1024) + --cache.database value Percentage of cache memory allowance to use for database io (default: 50) + --cache.trie value Percentage of cache memory allowance to use for trie caching (default = 25% full mode, 50% archive mode) (default: 25) + --cache.gc value Percentage of cache memory allowance to use for trie pruning (default = 25% full mode, 0% archive mode) (default: 25) + --cache.noprefetch Disable heuristic state prefetch during block import (less CPU and disk IO, more time waiting for data) + +ACCOUNT OPTIONS: + --unlock value Comma separated list of accounts to unlock + --password value Password file to use for non-interactive password input + --signer value External signer (url or path to ipc file) + --allow-insecure-unlock Allow insecure account unlocking when account-related RPCs are exposed by http + +API AND CONSOLE OPTIONS: + --ipcdisable Disable the IPC-RPC server + --ipcpath value Filename for IPC socket/pipe within the datadir (explicit paths escape it) + --rpc Enable the HTTP-RPC server + --rpcaddr value HTTP-RPC server listening interface (default: "localhost") + --rpcport value HTTP-RPC server listening port (default: 8545) + --rpcapi value API's offered over the HTTP-RPC interface + --rpc.gascap value Sets a cap on gas that can be used in eth_call/estimateGas (default: 0) + --rpccorsdomain value Comma separated list of domains from which to accept cross origin requests (browser enforced) + --rpcvhosts value Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost") + --ws Enable the WS-RPC server + --wsaddr value WS-RPC server listening interface (default: "localhost") + --wsport value WS-RPC server listening port (default: 8546) + --wsapi value API's offered over the WS-RPC interface + --wsorigins value Origins from which to accept websockets requests + --graphql Enable the GraphQL server + --graphql.addr value GraphQL server listening interface (default: "localhost") + --graphql.port value GraphQL server listening port (default: 8547) + --graphql.corsdomain value Comma separated list of domains from which to accept cross origin requests (browser enforced) + --graphql.vhosts value Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost") + --jspath loadScript JavaScript root path for loadScript (default: ".") + --exec value Execute JavaScript statement + --preload value Comma separated list of JavaScript files to preload into the console + +NETWORKING OPTIONS: + --bootnodes value Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers) + --bootnodesv4 value Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes) + --bootnodesv5 value Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes) + --port value Network listening port (default: 30303) + --maxpeers value Maximum number of network peers (network disabled if set to 0) (default: 50) + --maxpendpeers value Maximum number of pending connection attempts (defaults used if set to 0) (default: 0) + --nat value NAT port mapping mechanism (any|none|upnp|pmp|extip:<IP>) (default: "any") + --nodiscover Disables the peer discovery mechanism (manual peer addition) + --v5disc Enables the experimental RLPx V5 (Topic Discovery) mechanism + --netrestrict value Restricts network communication to the given IP networks (CIDR masks) + --nodekey value P2P node key file + --nodekeyhex value P2P node key as hex (for testing) + +MINER OPTIONS: + --mine Enable mining + --miner.threads value Number of CPU threads to use for mining (default: 0) + --miner.notify value Comma separated HTTP URL list to notify of new work packages + --miner.gasprice value Minimum gas price for mining a transaction (default: 1000000000) + --miner.gastarget value Target gas floor for mined blocks (default: 8000000) + --miner.gaslimit value Target gas ceiling for mined blocks (default: 8000000) + --miner.etherbase value Public address for block mining rewards (default = first account) (default: "0") + --miner.extradata value Block extra data set by the miner (default = client version) + --miner.recommit value Time interval to recreate the block being mined (default: 3s) + --miner.noverify Disable remote sealing verification + +GAS PRICE ORACLE OPTIONS: + --gpoblocks value Number of recent blocks to check for gas prices (default: 20) + --gpopercentile value Suggested gas price is the given percentile of a set of recent transaction gas prices (default: 60) + +VIRTUAL MACHINE OPTIONS: + --vmdebug Record information useful for VM and contract debugging + --vm.evm value External EVM configuration (default = built-in interpreter) + --vm.ewasm value External ewasm configuration (default = built-in interpreter) + +LOGGING AND DEBUGGING OPTIONS: + --nocompaction Disables db compaction after import + --verbosity value Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail (default: 3) + --vmodule value Per-module verbosity: comma-separated list of <pattern>=<level> (e.g. eth/*=5,p2p=4) + --backtrace value Request a stack trace at a specific logging statement (e.g. "block.go:271") + --debug Prepends log messages with call-site location (file and line number) + --pprof Enable the pprof HTTP server + --pprofaddr value pprof HTTP server listening interface (default: "127.0.0.1") + --pprofport value pprof HTTP server listening port (default: 6060) + --memprofilerate value Turn on memory profiling with the given rate (default: 524288) + --blockprofilerate value Turn on block profiling with the given rate (default: 0) + --cpuprofile value Write CPU profile to the given file + --trace value Write execution trace to the given file + +METRICS AND STATS OPTIONS: + --metrics Enable metrics collection and reporting + --metrics.expensive Enable expensive metrics collection and reporting + --metrics.influxdb Enable metrics export/push to an external InfluxDB database + --metrics.influxdb.endpoint value InfluxDB API endpoint to report metrics to (default: "http://localhost:8086") + --metrics.influxdb.database value InfluxDB database name to push reported metrics to (default: "geth") + --metrics.influxdb.username value Username to authorize access to the database (default: "test") + --metrics.influxdb.password value Password to authorize access to the database (default: "test") + --metrics.influxdb.tags value Comma-separated InfluxDB tags (key/values) attached to all measurements (default: "host=localhost") + +WHISPER (EXPERIMENTAL) OPTIONS: + --shh Enable Whisper + --shh.maxmessagesize value Max message size accepted (default: 1048576) + --shh.pow value Minimum POW accepted (default: 0.2) + --shh.restrict-light Restrict connection between two whisper light clients + +DEPRECATED OPTIONS: + --lightserv value Maximum percentage of time allowed for serving LES requests (deprecated, use --light.serve) (default: 0) + --lightpeers value Maximum number of light clients to serve, or light servers to attach to (deprecated, use --light.maxpeers) (default: 100) + --minerthreads value Number of CPU threads to use for mining (deprecated, use --miner.threads) (default: 0) + --targetgaslimit value Target gas floor for mined blocks (deprecated, use --miner.gastarget) (default: 8000000) + --gasprice value Minimum gas price for mining a transaction (deprecated, use --miner.gasprice) (default: 1000000000) + --etherbase value Public address for block mining rewards (default = first account, deprecated, use --miner.etherbase) (default: "0") + --extradata value Block extra data set by the miner (default = client version, deprecated, use --miner.extradata) + +MISC OPTIONS: + --override.istanbul value Manually specify Istanbul fork-block, overriding the bundled setting (default: 0) + --help, -h show help + + +COPYRIGHT: + Copyright 2013-2019 The go-ethereum Authors +```
diff --git go-ethereum/docs/docs/_interface/FAQ.md celo/docs/docs/_interface/FAQ.md new file mode 100644 index 0000000000000000000000000000000000000000..29acd3d9082908e18e33f38a84564399c80824de --- /dev/null +++ celo/docs/docs/_interface/FAQ.md @@ -0,0 +1,68 @@ +--- +title: FAQ +permalink: docs/faq +sort_key: C +--- + +**Q.** I noticed my peercount slowly decrease, and now it is at 0. Restarting doesn't get any peers. + +**A.** Check and sync your clock with ntp. [Example](http://askubuntu.com/questions/254826/how-to-force-a-clock-update-using-ntp) `sudo ntpdate -s time.nist.gov` + +--- + +**Q.** I would like to run multiple geth instances but got the error "Fatal: blockchain db err: resource temporarily unavailable". + +**A.** Geth uses a datadir to store the blockchain, accounts and some additional information. This directory cannot be shared between running instances. If you would like to run multiple instances follow [these](../doc/setting-up-private-network-or-local-cluster) instructions. + +--- + +**Q.** How do Ethereum syncing work? + +**A.** The current default mode of sync for Geth is called fast sync. Instead of starting from the genesis block and reprocessing all the transactions that ever occurred (which could take weeks), fast sync downloads the blocks, and only verifies the associated proof-of-works. Downloading all the blocks is a straightforward and fast procedure and will relatively quickly reassemble the entire chain. + +Many people falsely assume that because they have the blocks, they are in sync. Unfortunately this is not the case, since no transaction was executed, so we do not have any account state available (ie. balances, nonces, smart contract code and data). These need to be downloaded separately and cross checked with the latest blocks. This phase is called the state trie download and it actually runs concurrently with the block downloads; alas it take a lot longer nowadays than downloading the blocks. + +So, what's the state trie? In the Ethereum mainnet, there are a ton of accounts already, which track the balance, nonce, etc of each user/contract. The accounts themselves are however insufficient to run a node, they need to be cryptographically linked to each block so that nodes can actually verify that the account's are not tampered with. This cryptographic linking is done by creating a tree data structure above the accounts, each level aggregating the layer below it into an ever smaller layer, until you reach the single root. This gigantic data structure containing all the accounts and the intermediate cryptographic proofs is called the state trie. + +Ok, so why does this pose a problem? This trie data structure is an intricate interlink of hundreds of millions of tiny cryptographic proofs (trie nodes). To truly have a synchronized node, you need to download all the account data, as well as all the tiny cryptographic proofs to verify that noone in the network is trying to cheat you. This itself is already a crazy number of data items. The part where it gets even messier is that this data is constantly morphing: at every block (15s), about 1000 nodes are deleted from this trie and about 2000 new ones are added. This means your node needs to synchronize a dataset that is changing 200 times per second. The worst part is that while you are synchronizing, the network is moving forward, and state that you begun to download might disappear while you're downloading, so your node needs to constantly follow the network while trying to gather all the recent data. But until you actually do gather all the data, your local node is not usable since it cannot cryptographically prove anything about any accounts. + +If you see that you are 64 blocks behind mainnet, you aren't yet synchronized, not even close. You are just done with the block download phase and still running the state downloads. You can see this yourself via the seemingly endless `Imported state entries [...]` stream of logs. You'll need to wait that out too before your node comes truly online. + +--- + +**Q: The node just hangs on importing state enties?!** + +**A**: The node doesn't hang, it just doesn't know how large the state trie is in advance so it keeps on going and going and going until it discovers and downloads the entire thing. + +The reason is that a block in Ethereum only contains the state root, a single hash of the root node. When the node begins synchronizing, it knows about exactly 1 node and tries to download it. That node, can refer up to 16 new nodes, so in the next step, we'll know about 16 new nodes and try to download those. As we go along the download, most of the nodes will reference new ones that we didn't know about until then. This is why you might be tempted to think it's stuck on the same numbers. It is not, rather it's discovering and downloading the trie as it goes along. + +--- + +**Q: I'm stuck at 64 blocks behind mainnet?!** + +*A:* As explained above, you are not stuck, just finished with the block download phase, waiting for the state download phase to complete too. This latter phase nowadays take a lot longer than just getting the blocks. + +--- + +**Q: Why does downloading the state take so long, I have good bandwidth?** + +**A:** State sync is mostly limited by disk IO, not bandwidth. + +The state trie in Ethereum contains hundreds of millions of nodes, most of which take the form of a single hash referencing up to 16 other hashes. This is a horrible way to store data on a disk, because there's almost no structure in it, just random numbers referencing even more random numbers. This makes any underlying database weep, as it cannot optimize storing and looking up the data in any meaningful way. + +Not only is storing the data very suboptimal, but due to the 200 modification / second and pruning of past data, we cannot even download it is a properly pre-processed way to make it import faster without the underlying database shuffling it around too much. The end result is that even a fast sync nowadays incurs a huge disk IO cost, which is too much for a mechanical hard drive. + +--- + +**Q: Wait, so I can't run a full node on an HDD?** + +**A:** Unfortunately not. Doing a fast sync on an HDD will take more time than you're willing to wait with the current data schema. Even if you do wait it out, an HDD will not be able to keep up with the read/write requirements of transaction processing on mainnet. + +You however should be able to run a light client on an HDD with minimal impact on system resources. If you wish to run a full node however, an SSD is your only option. + + +--- + +**Q: When I try to use the --password command line flag, I get the error "Could not decrypt key with given passphrase" but the password is correct. Why does this error appear?** + +**A:** Especially if the password file was created on Windows, it may have a Byte Order Mark or other special encoding that the go-ethereum client doesn't currently recognize. You can change this behavior with a PowerShell command like `echo "mypasswordhere" | out-file test.txt -encoding ASCII`. Additional details and/or any updates on more robust handling are at <https://github.com/ethereum/go-ethereum/issues/19905>.
diff --git go-ethereum/docs/docs/_interface/JavaScript-Console.md celo/docs/docs/_interface/JavaScript-Console.md new file mode 100644 index 0000000000000000000000000000000000000000..cd4633fc1c5a51b4ecf71d40fa664dd9a9e5d70a --- /dev/null +++ celo/docs/docs/_interface/JavaScript-Console.md @@ -0,0 +1,84 @@ +--- +title: JavaScript Console +sort_key: B +--- + +The Geth JavaScript console exposes the full [web3 JavaScript Dapp +API](https://github.com/ethereum/wiki/wiki/JavaScript-API) and further administrative +APIs. + +## Interactive Use: The Console + +The geth JavaScript console is started with the `console` or `attach` geth sub-commands. +The `console` subcommands starts the geth node and then opens the console. The `attach` +subcommand attaches to the console to an already-running geth instance. + + geth console + geth attach + +Attach mode accepts an endpoint in case the geth node is running with a non default +ipc endpoint or you would like to connect over the rpc interface. + + geth attach /some/custom/path.ipc + geth attach http://191.168.1.1:8545 + geth attach ws://191.168.1.1:8546 + +Note that by default the geth node doesn't start the HTTP and WebSocket servers and not +all functionality is provided over these interfaces for security reasons. These defaults +can be overridden with the `--rpcapi` and `--wsapi` arguments when the geth node is +started, or with [admin.startRPC](../rpc/ns-admin#admin_startrpc) and +[admin.startWS](../rpc/ns-admin#admin_startws). + +If you need log information, start with: + + geth console --verbosity 5 2>> /tmp/eth.log + +Otherwise mute your logs, so that it does not pollute your console: + + geth console 2> /dev/null + +Geth has support to load custom JavaScript files into the console through the `--preload` +option. This can be used to load often used functions, or to setup web3 contract objects. + + geth console --preload "/my/scripts/folder/utils.js,/my/scripts/folder/contracts.js" + +## Non-interactive Use: Script Mode + +It's also possible to execute files to the JavaScript interpreter. The `console` and +`attach` subcommand accept the `--exec` argument which is a javascript statement. + + geth attach --exec "eth.blockNumber" + +This prints the current block number of a running geth instance. + +Or execute a local script with more complex statements on a remote node over http: + + geth attach http://geth.example.org:8545 --exec 'loadScript("/tmp/checkbalances.js")' + geth attach http://geth.example.org:8545 --jspath "/tmp" --exec 'loadScript("checkbalances.js")' + +Use the `--jspath <path/to/my/js/root>` to set a library directory for your js scripts. +Parameters to `loadScript()` with no absolute path will be understood relative to this +directory. + +You can exit the console by typing `exit` or simply with `CTRL-C`. + +## Caveats + +go-ethereum uses the [Otto JS VM](https://github.com/robertkrimen/otto) which has some +limitations: + +* `"use strict"` will parse, but does nothing. +* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 + specification. + +`web3.js` uses the [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) library. +This library is auto-loaded into the console. + +### Timers + +In addition to the full functionality of JS (as per ECMA5), the ethereum JSRE is augmented +with various timers. It implements `setInterval`, `clearInterval`, `setTimeout`, +`clearTimeout` you may be used to using in browser windows. It also provides +implementation for `admin.sleep(seconds)` and a block based timer, `admin.sleepBlocks(n)` +which sleeps till the number of new blocks added is equal to or greater than `n`, think +"wait for n confirmations".
diff --git go-ethereum/docs/docs/_interface/Managing-your-accounts.md celo/docs/docs/_interface/Managing-your-accounts.md new file mode 100644 index 0000000000000000000000000000000000000000..7b2352387e6379413faed3da8d976027b83e28aa --- /dev/null +++ celo/docs/docs/_interface/Managing-your-accounts.md @@ -0,0 +1,352 @@ +--- +title: Managing Your Accounts +sort_key: B +--- + +**WARNING** +Remember your password. + +If you lose the password you use to encrypt your account, you will not be able to access that account. +Repeat: It is NOT possible to access your account without a password and there is no _forgot my password_ option here. Do not forget it. + +The ethereum CLI `geth` provides account management via the `account` command: + +``` +$ geth account <command> [options...] [arguments...] +``` + +Manage accounts lets you create new accounts, list all existing accounts, import a private +key into a new account, migrate to newest key format and change your password. + +It supports interactive mode, when you are prompted for password as well as +non-interactive mode where passwords are supplied via a given password file. +Non-interactive mode is only meant for scripted use on test networks or known safe +environments. + +Make sure you remember the password you gave when creating a new account (with new, update +or import). Without it you are not able to unlock your account. + +Note that exporting your key in unencrypted format is NOT supported. + +Keys are stored under `<DATADIR>/keystore`. Make sure you backup your keys regularly! See +[DATADIR backup & restore](../install-and-build/backup-restore) +for more information. If a custom datadir and keystore option are given the keystore +option takes preference over the datadir option. + +The newest format of the keyfiles is: `UTC--<created_at UTC ISO8601>-<address hex>`. The +order of accounts when listing, is lexicographic, but as a consequence of the timespamp +format, it is actually order of creation + +It is safe to transfer the entire directory or the individual keys therein between +ethereum nodes. Note that in case you are adding keys to your node from a different node, +the order of accounts may change. So make sure you do not rely or change the index in your +scripts or code snippets. + +And again. **DO NOT FORGET YOUR PASSWORD** + +``` +COMMANDS: + list Print summary of existing accounts + new Create a new account + update Update an existing account + import Import a private key into a new account +``` + +You can get info about subcommands by `geth account <command> --help`. +``` +$ geth account list --help +list [command options] [arguments...] + +Print a short summary of all accounts + +OPTIONS: + --datadir "/home/bas/.ethereum" Data directory for the databases and keystore + --keystore Directory for the keystore (default = inside the datadir) +``` + +Accounts can also be managed via the [Javascript Console](../interface/javascript-console) + +## Examples +### Interactive use + +#### creating an account + +``` +$ geth account new +Your new account is locked with a password. Please give a password. Do not forget this password. +Passphrase: +Repeat Passphrase: +Address: {168bc315a2ee09042d83d7c5811b533620531f67} +``` + +#### Listing accounts in a custom keystore directory + +``` +$ geth account list --keystore /tmp/mykeystore/ +Account #0: {5afdd78bdacb56ab1dad28741ea2a0e47fe41331} keystore:///tmp/mykeystore/UTC--2017-04-28T08-46-27.437847599Z--5afdd78bdacb56ab1dad28741ea2a0e47fe41331 +Account #1: {9acb9ff906641a434803efb474c96a837756287f} keystore:///tmp/mykeystore/UTC--2017-04-28T08-46-52.180688336Z--9acb9ff906641a434803efb474c96a837756287f + +``` + +#### Import private key into a node with a custom datadir + +``` +$ geth account import --datadir /someOtherEthDataDir ./key.prv +The new account will be encrypted with a passphrase. +Please enter a passphrase now. +Passphrase: +Repeat Passphrase: +Address: {7f444580bfef4b9bc7e14eb7fb2a029336b07c9d} +``` + +#### Account update + +``` +$ geth account update a94f5374fce5edbc8e2a8697c15331677e6ebf0b +Unlocking account a94f5374fce5edbc8e2a8697c15331677e6ebf0b | Attempt 1/3 +Passphrase: +0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b +Account 'a94f5374fce5edbc8e2a8697c15331677e6ebf0b' unlocked. +Please give a new password. Do not forget this password. +Passphrase: +Repeat Passphrase: +0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b +``` + +### Non-interactive use + +You supply a plaintext password file as argument to the `--password` flag. The data in the +file consists of the raw characters of the password, followed by a single newline. + +**Note**: Supplying the password directly as part of the command line is not recommended, +but you can always use shell trickery to get round this restriction. + +``` +$ geth account new --password /path/to/password + +$ geth account import --datadir /someOtherEthDataDir --password /path/to/anotherpassword ./key.prv +``` + +# Creating accounts + +## Creating a new account + +``` +$ geth account new +$ geth account new --password /path/to/passwdfile +$ geth account new --password <(echo $mypassword) + +``` + +Creates a new account and prints the address. + +On the console, use: + +``` +> personal.newAccount() +... you will be prompted for a password ... + +or + +> personal.newAccount("passphrase") + +``` + +The account is saved in encrypted format. You **must** remember this passphrase to unlock +your account in the future. + +For non-interactive use the passphrase can be specified with the `--password` flag: + +``` +geth account new --password <passwordfile> +``` + +Note, this is meant to be used for testing only, it is a bad idea to save your +password to file or expose in any other way. + +## Creating an account by importing a private key + +``` + geth account import <keyfile> +``` + +Imports an unencrypted private key from `<keyfile>` and creates a new account and prints +the address. + +The keyfile is assumed to contain an unencrypted private key as canonical EC raw bytes +encoded into hex. + +The account is saved in encrypted format, you are prompted for a passphrase. + +You must remember this passphrase to unlock your account in the future. + +For non-interactive use the passphrase can be specified with the `--password` flag: + +``` +geth account import --password <passwordfile> <keyfile> +``` + +**Note**: Since you can directly copy your encrypted accounts to another ethereum +instance, this import/export mechanism is not needed when you transfer an account between +nodes. + +**Warning:** when you copy keys into an existing node's keystore, the order of accounts +you are used to may change. Therefore you make sure you either do not rely on the account +order or doublecheck and update the indexes used in your scripts. + +**Warning:** If you use the password flag with a password file, best to make sure the file +is not readable or even listable for anyone but you. You achieve this with: + +``` +touch /path/to/password +chmod 700 /path/to/password +cat > /path/to/password +>I type my pass here^D +``` + +## Updating an existing account + +You can update an existing account on the command line with the `update` subcommand with +the account address or index as parameter. You can specify multiple accounts at once. + +``` +geth account update 5afdd78bdacb56ab1dad28741ea2a0e47fe41331 9acb9ff906641a434803efb474c96a837756287f +geth account update 0 1 2 +``` + +The account is saved in the newest version in encrypted format, you are prompted +for a passphrase to unlock the account and another to save the updated file. + +This same command can therefore be used to migrate an account of a deprecated +format to the newest format or change the password for an account. + +After a successful update, all previous formats/versions of that same key are removed! + +# Importing your presale wallet + +Importing your presale wallet is very easy. If you remember your password that is: + +``` +geth wallet import /path/to/my/presale.wallet +``` + +will prompt for your password and imports your ether presale account. It can be used +non-interactively with the --password option taking a passwordfile as argument containing +the wallet password in cleartext. + +# Listing accounts and checking balances + +### Listing your current accounts + +From the command line, call the CLI with: + +``` +$ geth account list +Account #0: {5afdd78bdacb56ab1dad28741ea2a0e47fe41331} keystore:///tmp/mykeystore/UTC--2017-04-28T08-46-27.437847599Z--5afdd78bdacb56ab1dad28741ea2a0e47fe41331 +Account #1: {9acb9ff906641a434803efb474c96a837756287f} keystore:///tmp/mykeystore/UTC--2017-04-28T08-46-52.180688336Z--9acb9ff906641a434803efb474c96a837756287f +``` + +to list your accounts in order of creation. + +**Note**: +This order can change if you copy keyfiles from other nodes, so make sure you either do not rely on indexes or make sure if you copy keys you check and update your account indexes in your scripts. + +When using the console: +``` +> eth.accounts +["0x5afdd78bdacb56ab1dad28741ea2a0e47fe41331", "0x9acb9ff906641a434803efb474c96a837756287f"] +``` + +or via RPC: +``` +# Request +$ curl -X POST --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":1} http://127.0.0.1:8545' + +# Result +{ + "id":1, + "jsonrpc": "2.0", + "result": ["0x5afdd78bdacb56ab1dad28741ea2a0e47fe41331", "0x9acb9ff906641a434803efb474c96a837756287f"] +} +``` + +If you want to use an account non-interactively, you need to unlock it. You can do this on +the command line with the `--unlock` option which takes a comma separated list of accounts +(in hex or index) as argument so you can unlock the accounts programmatically for one +session. This is useful if you want to use your account from Dapps via RPC. `--unlock ` +will unlock the first account. This is useful when you created your account +programmatically, you do not need to know the actual account to unlock it. + +Create account and start node with account unlocked: +``` +geth account new --password <(echo this is not secret!) +geth --password <(echo this is not secret!) --unlock primary --rpccorsdomain localhost --verbosity 6 2>> geth.log +``` + +Instead of the account address, you can use integer indexes which refers to the address +position in the account listing (and corresponds to order of creation) + +The command line allows you to unlock multiple accounts. In this case the argument to +unlock is a comma delimited list of accounts addresses or indexes. + +``` +geth --unlock "0x407d73d8a49eeb85d32cf465507dd71d507100c1,0,5,e470b1a7d2c9c5c6f03bbaa8fa20db6d404a0c32" +``` + +If this construction is used non-interactively, your password file will need to contain +the respective passwords for the accounts in question, one per line. + +On the console you can also unlock accounts (one at a time) for a duration (in seconds). + +``` +personal.unlockAccount(address, "password", 300) +``` + +Note that we do NOT recommend using the password argument here, since the console history +is logged, so you may compromise your account. You have been warned. + +### Checking account balances + +To check your the etherbase account balance: +``` +> web3.fromWei(eth.getBalance(eth.coinbase), "ether") +6.5 +``` + +Print all balances with a JavaScript function: +``` +function checkAllBalances() { + var totalBal = 0; + for (var acctNum in eth.accounts) { + var acct = eth.accounts[acctNum]; + var acctBal = web3.fromWei(eth.getBalance(acct), "ether"); + totalBal += parseFloat(acctBal); + console.log(" eth.accounts[" + acctNum + "]: \t" + acct + " \tbalance: " + acctBal + " ether"); + } + console.log(" Total balance: " + totalBal + " ether"); +}; +``` +That can then be executed with: +``` +> checkAllBalances(); + eth.accounts[0]: 0xd1ade25ccd3d550a7eb532ac759cac7be09c2719 balance: 63.11848 ether + eth.accounts[1]: 0xda65665fc30803cb1fb7e6d86691e20b1826dee0 balance: 0 ether + eth.accounts[2]: 0xe470b1a7d2c9c5c6f03bbaa8fa20db6d404a0c32 balance: 1 ether + eth.accounts[3]: 0xf4dd5c3794f1fd0cdc0327a83aa472609c806e99 balance: 6 ether +``` + +Since this function will disappear after restarting geth, it can be helpful to store +commonly used functions to be recalled later. The +[loadScript](../interface/javascript-console) +function makes this very easy. + +First, save the `checkAllBalances()` function definition to a file on your computer. For +example, `/Users/username/gethload.js`. Then load the file from the interactive console: + +``` +> loadScript("/Users/username/gethload.js") +true +``` + +The file will modify your JavaScript environment as if you has typed the commands +manually. Feel free to experiment!
diff --git go-ethereum/docs/docs/_interface/Private-Network.md celo/docs/docs/_interface/Private-Network.md new file mode 100644 index 0000000000000000000000000000000000000000..bf9899dbe0cd10e61df5111ffc2f960986e4a22e --- /dev/null +++ celo/docs/docs/_interface/Private-Network.md @@ -0,0 +1,298 @@ +--- +title: Private Networks +sort_key: B +--- + +This guide explains how to set up a private network of multiple Geth nodes. An Ethereum +network is a private network if the nodes are not connected to the main network. In this +context private only means reserved or isolated, rather than protected or secure. + +### Choosing A Network ID + +The network ID is an integer number which isolates Ethereum peer-to-peer networks. +Connections between blockchain nodes will occur only if both peers use the same genesis +block and network ID. Use the `--networkid` command line option to set the network ID used +by geth. + +The main network has ID 1. If you supply your own custom network ID which is different +than the main network, your nodes will not connect to other nodes and form a private +network. If you're planning to connect to your private chain on the Internet, it's best to +choose a network ID that isn't already used. You can find a community-run registry of +Ethereum networks at <https://chainid.network>. + +### Choosing A Consensus Algorithm + +While the main network uses proof-of-work to secure the blockchain, Geth also supports the +the 'clique' proof-of-authority consensus algorithm as an alternative for private +networks. We strongly recommend 'clique' for new private network deployments because it is +much less resource intensive than proof-of-work. The clique system is also used for +several public Ethereum testnets such as [Rinkeby](https://rinkeby.io) and +[Görli](https://goerli.net). + +Here are the key differences between the two consensus algorithms available in Geth: + +Ethash consensus, being a proof-of-work algorithm, is a system that allows open +participation by anyone willing to dedicate resources to mining. While this is a great +property to have for a public network, the overall security of the blockchain strictly +depends on the total amount of resources used to secure it. As such, proof-of-work is a +poor choice for private networks with few miners. The Ethash mining 'difficulty' is +adjusted automatically so that new blocks are created approximately 12 seconds apart. As +more mining resources are deployed on the network, creating a new block becomes harder so +that the average block time matches the target block time. + +Clique consensus is a proof-of-authority system where new blocks can be created by +authorized 'signers' only. The clique consenus protocol is specified in +[EIP-225](clique-eip). The initial set of authorized signers is configured in the genesis +block. Signers can be authorized and de-authorized using a voting mechanism, thus allowing +the set of signers to change while the blockchain operates. Clique can be configured to +target any block time (within reasonable limits) since it isn't tied to the difficulty +adjustment. + +[clique-eip]: https://eips.ethereum.org/EIPS/eip-225 + +### Creating The Genesis Block + +Every blockchain starts with the genesis block. When you run Geth with default settings +for the first time, it commits the main net genesis to the database. For a private +network, you usually want a different genesis block. + +The genesis block is configured using the _genesis.json_ file. When creating a genesis +block, you need to decide on a few initial parameters for your blockchain: + +- Ethereum platform features enabled at launch (`config`). Enabling protocol features + while the blockchain is running requires scheduling a hard fork. +- Initial block gas limit (`gasLimit`). Your choice here impacts how much EVM computation + can happen within a single block. We recommend using the main Ethereum network as a + [guideline to find a good amount](gaslimit-char). The block gas limit can be adjusted + after launch using the `--targetgaslimit` command-line flag. +- Initial allocation of ether (`alloc`). This determines how much ether is available to + the addresses you list in the genesis block. Additional ether can be created through + mining as the chain progresses. + +[gaslimit-chart]: https://etherscan.io/chart/gaslimit + +#### Clique Example + +This is an example of a genesis.json file for a proof-of-authority network. The `config` +section ensures that all known protocol changes are available and configures the 'clique' +engine to be used for consensus. + +Note that the initial signer set must be configured through the `extradata` field. This +field is required for clique to work. + +First create the signer account keys using the [geth account](./managing-your-accounts) +command (run this command multiple times to create more than one signer key). + +```shell +geth account new --datadir data +``` + +Take note of the Ethereum address printed by this command. + +To create the initial extradata for your network, collect the signer addresses and encode +`extradata` as the concatenation of 32 zero bytes, all signer addresses, and 65 further +zero bytes. In the example below, `extradata` contains a single initial signer address, +`0x7df9a875a174b3bc565e6424a0050ebc1b2d1d82`. + +You can use the `period` configuration option to set the target block time of the chain. + +```json +{ + "config": { + "chainId": 15, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "clique": { + "period": 5, + "epoch": 30000 + } + }, + "difficulty": "1", + "gasLimit": "8000000", + "extradata": "0x00000000000000000000000000000000000000000000000000000000000000007df9a875a174b3bc565e6424a0050ebc1b2d1d820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "alloc": { + "7df9a875a174b3bc565e6424a0050ebc1b2d1d82": { "balance": "300000" }, + "f41c74c9ae680c1aa78f42e5647a62f353b7bdde": { "balance": "400000" } + } +} +``` + +#### Ethash Example + +Since ethash is the default consensus algorithm, no additional parameters need to be +configured in order to use it. You can influence the initial mining difficulty using the +`difficulty` parameter, but note that the difficulty adjustment algorithm will quickly +adapt to the amount of mining resources you deploy on the chain. + +```json +{ + "config": { + "chainId": 15, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "ethash": {} + }, + "difficulty": "1", + "gasLimit": "8000000", + "alloc": { + "7df9a875a174b3bc565e6424a0050ebc1b2d1d82": { "balance": "300000" }, + "f41c74c9ae680c1aa78f42e5647a62f353b7bdde": { "balance": "400000" } + } +} +``` + +### Initializing the Geth Database + +To create a blockchain node that uses this genesis block, run the following command. This +imports and sets the canonical genesis block for your chain. + +```shell +geth init --datadir data genesis.json +``` + +Future runs of geth using this data directory will use the genesis block you have defined. + +```shell +geth --datadir data --networkid 15 +``` + +### Scheduling Hard Forks + +As Ethereum protocol development progresses, new Ethereum features become available. To +enable these features on your private network, you must schedule a hard fork. + +First, choose any future block number where the hard fork will activate. Continuing from +the genesis.json example above, let's assume your network is running and its current block +number is 35421. To schedule the 'Istanbul' fork, we pick block 40000 as the activation +block number and modify our genesis.json file to set it: + +```json +{ + "config": { + ... + "istanbulBlock": 40000, + ... + }, + ... +} +``` + +In order to update to the new fork, first ensure that all Geth instances on your private +network actually support the Istanbul fork (i.e. ensure you have the latest version of +Geth installed). Now shut down all nodes and re-run the `init` command to enable the new +chain configuration: + +```shell +geth init --datadir data genesis.json +``` + +### Setting Up Networking + +Once your node is initialized to the desired genesis state, it is time to set up the +peer-to-peer network. Any node can be used as an entry point. We recommend dedicating a +single node as the rendezvous point which all other nodes use to join. This node is called +the 'bootstrap node'. + +First, determine the IP address of the machine your bootstrap node will run on. If you are +using a cloud service such as Amazon EC2, you'll find the IP of the virtual machine in the +management console. Please also ensure that your firewall configuration allows both UDP +and TCP traffic on port 30303. + +The bootstrap node needs to know about its own IP address in order to be able to relay it +others. The IP is set using the `--nat` flag (insert your own IP instead of the example +address below). + +```shell +geth --datadir data --networkid 15 --nat extip:172.16.254.4 +``` + +Now extract the 'node record' of the bootnode using the JS console. + +```shell +geth attach data/geth.ipc --exec admin.nodeInfo.enr +``` + +This command should print a base64 string such as the following example. Other nodes will +use the information contained in the bootstrap node record to connect to your peer-to-peer +network. + +```text +"enr:-Je4QEiMeOxy_h0aweL2DtZmxnUMy-XPQcZllrMt_2V1lzynOwSx7GnjCf1k8BAsZD5dvHOBLuldzLYxpoD5UcqISiwDg2V0aMfGhGlQhqmAgmlkgnY0gmlwhKwQ_gSJc2VjcDI1NmsxoQKX_WLWgDKONsGvxtp9OeSIv2fRoGwu5vMtxfNGdut4cIN0Y3CCdl-DdWRwgnZf" +``` + +Setting up peer-to-peer networking depends on your requirements. If you connect nodes +across the Internet, please ensure that your bootnode and all other nodes have public IP +addresses assigned, and both TCP and UDP traffic can pass the firewall. + +If Internet connectivity is not required or all member nodes connect using well-known IPs, +we strongly recommend setting up Geth to restrict peer-to-peer connectivity to an IP +subnet. Doing so will further isolate your network and prevents cross-connecting with +other blockchain networks in case your nodes are reachable from the Internet. Use the +`--netrestrict` flag to configure a whitelist of IP networks: + +```shell +geth <other-flags> --netrestrict 172.16.254.0/24 +``` + +With the above setting, Geth will only allow connections from the 172.16.254.0/24 subnet, +and will not attempt to connect to other nodes outside of the set IP range. + +### Running Member Nodes + +Before running a member node, you have to initialize it with the same genesis file as +used for the bootstrap node. + +With the bootnode operational and externally reachable (you can try `telnet <ip> <port>` +to ensure it's indeed reachable), you can start more Geth nodes and connect them via the +bootstrap node using the `--bootnodes` flag. + +To create a member node running on the same machine as the bootstrap node, choose a +separate data directory (example: `data-2`) and listening port (example: `30305`): + +```shell +geth --datadir data-2 --networkid 15 --port 30305 --bootnodes <bootstrap-node-record> +``` + +With the member node running, you can check whether it is connected to the bootstrap node +or any other node in your network by attaching a console and running `admin.peers`. It may +take up to a few seconds for the nodes to get connected. + +```shell +geth attach data-2/geth.ipc --exec admin.peers +``` + +### Clique: Running A Signer + +To set up Geth for signing blocks in proof-of-authority mode, a signer account must be +available. The account must be unlocked to mine blocks. The following command will prompt +for the account password, then start signing blocks: + +```shell +geth <other-flags> --unlock 0x7df9a875a174b3bc565e6424a0050ebc1b2d1d82 --mine +``` + +You can further configure mining by changing the default gas limit blocks converge to +(with `--targetgaslimit`) and the price transactions are accepted at (with `--gasprice`). + +### Ethash: Running A Miner + +For proof-of-work in a simple private network, a single CPU miner instance is enough to +create a stable stream of blocks at regular intervals. To start a Geth instance for +mining, run it with all the usual flags and add the following to configure mining: + +```shell +geth <other-flags> --mine --minerthreads=1 --etherbase=0x0000000000000000000000000000000000000000 +``` + +This will start mining bocks and transactions on a single CPU thread, crediting all block +rewards to the account specified by `--etherbase`.
diff --git go-ethereum/docs/docs/_interface/mining.md celo/docs/docs/_interface/mining.md new file mode 100644 index 0000000000000000000000000000000000000000..7c72f9e92d23b995dd76699fff79d29fd7eaac1a --- /dev/null +++ celo/docs/docs/_interface/mining.md @@ -0,0 +1,160 @@ +--- +title: Mining +sort_key: B +--- + +This document explains how to set up geth for mining. The Ethereum wiki also has a [page +about mining](eth-wiki-mining), be sure to check that one as well. + +Mining is the process through which new blocks are created. Geth actually creates new +blocks all the time, but these blocks need to be secured through proof-of-work so they +will be accepted by other nodes. Mining is all about creating these proof-of-work values. + +The proof-of-work computation can be performed in multiple ways. Geth includes a CPU +miner, which does mining within the geth process. We discourage using the CPU miner with +the Ethereum mainnet. If you want to mine real ether, use GPU mining. Your best option for +doing that is the [ethminer](ethminer) software. + +Always ensure your blockchain is fully synchronised with the chain before starting to +mine, otherwise you will not be mining on the correct chain and your block rewards will +not be valueable. + +## GPU mining + +The ethash algorithm is memory hard and in order to fit the DAG into memory, it needs +1-2GB of RAM on each GPU. If you get `Error GPU mining. GPU memory fragmentation?` you +don't have enough memory. + +### Installing ethminer + +To get ethminer, you need to install the ethminer binary package or build it from source. +See <https://github.com/ethereum-mining/ethminer/#build> for the official ethminer +build/install instructions. At the time of writing, ethminer only provides a binary for +Microsoft Windows. + +### Using ethminer with geth + +First create an account to hold your block rewards. + + geth account new + +Follow the prompts and enter a good password. **DO NOT FORGET YOUR PASSWORD**. Also take +note of the public Ethereum address which is printed at the end of the account creation +process. In the following examples, we will use 0xC95767AC46EA2A9162F0734651d6cF17e5BfcF10 +as the example address. + +Now start geth and wait for it to sync the blockchain. This will take quite a while. + + geth --rpc --etherbase 0xC95767AC46EA2A9162F0734651d6cF17e5BfcF10 + +Now we're ready to start mining. In a new terminal session, run ethminer and connect it to geth: + + ethminer -G -P http://127.0.0.1:8545 + +`ethminer` communicates with geth on port 8545 (the default RPC port in geth). You can +change this by giving the [`--rpcport` option](../rpc/index) to `geth`. Ethminer will find +get on any port. You also need to set the port on `ethminer` with `-P +http://127.0.0.1:3301`. Setting up custom ports is necessary if you want several instances +mining on the same computer. If you are testing on a private cluster, we recommend you use +CPU mining instead. + +If the default for `ethminer` does not work try to specify the OpenCL device with: +`--opencl-device X` where X is 0, 1, 2, etc. When running `ethminer` with `-M` +(benchmark), you should see something like: + + Benchmarking on platform: { "platform": "NVIDIA CUDA", "device": "GeForce GTX 750 Ti", "version": "OpenCL 1.1 CUDA" } + + Benchmarking on platform: { "platform": "Apple", "device": "Intel(R) Xeon(R) CPU E5-1620 v2 @ 3.70GHz", "version": "OpenCL 1.2 " } + +**Note** hashrate info is not available in `geth` when GPU mining. Check your hashrate +with `ethminer`, `miner.hashrate` will always report 0. + +## CPU Mining with Geth + +When you start up your ethereum node with `geth` it is not mining by default. To start it +in mining mode, you use the `--mine` command-line flag. The `--minerthreads` parameter can +be used to set the number parallel mining threads (defaulting to the total number of +processor cores). + + geth --mine --minerthreads=4 + +You can also start and stop CPU mining at runtime using the +[console](../interface/javascript-console). `miner.start` takes an optional parameter for +the number of miner threads. + + > miner.start(8) + true + > miner.stop() + true + +Note that mining for real ether only makes sense if you are in sync with the network +(since you mine on top of the consensus block). Therefore the eth blockchain +downloader/synchroniser will delay mining until syncing is complete, and after that mining +automatically starts unless you cancel your intention with `miner.stop()`. + +In order to earn ether you must have your **etherbase** (or **coinbase**) address set. +This etherbase defaults to your [primary account](../interface/managing-your-accounts). If +you don't have an etherbase address, then `geth --mine` will not start up. + +You can set your etherbase on the command line: + + geth --etherbase '0xC95767AC46EA2A9162F0734651d6cF17e5BfcF10' --mine 2>> geth.log + +You can reset your etherbase on the console too: + + > miner.setEtherbase(eth.accounts[2]) + +Note that your etherbase does not need to be an address of a local account, just an +existing one. + +There is an option [to add extra data](../interface/javascript-console) (32 bytes only) to +your mined blocks. By convention this is interpreted as a unicode string, so you can set +your short vanity tag. + + > miner.setExtra("ΞTHΞЯSPHΞЯΞ") + +You can check your hashrate with [miner.hashrate](../interface/javascript-console), the +result is in H/s (Hash operations per second). + + > miner.hashrate + 712000 + +After you successfully mined some blocks, you can check the ether balance of your +etherbase account. Now assuming your etherbase is a local account: + + > eth.getBalance(eth.coinbase).toNumber(); + '34698870000000' + +You can check which blocks are mined by a particular miner (address) with the following +code snippet on the console: + + > function minedBlocks(lastn, addr) { + addrs = []; + if (!addr) { + addr = eth.coinbase + } + limit = eth.blockNumber - lastn + for (i = eth.blockNumber; i >= limit; i--) { + if (eth.getBlock(i).miner == addr) { + addrs.push(i) + } + } + return addrs + } + // scans the last 1000 blocks and returns the blocknumbers of blocks mined by your coinbase + // (more precisely blocks the mining reward for which is sent to your coinbase). + > minedBlocks(1000, eth.coinbase) + [352708, 352655, 352559] + +Note that it will happen often that you find a block yet it never makes it to the +canonical chain. This means when you locally include your mined block, the current state +will show the mining reward credited to your account, however, after a while, the better +chain is discovered and we switch to a chain in which your block is not included and +therefore no mining reward is credited. Therefore it is quite possible that as a miner +monitoring their coinbase balance will find that it may fluctuate quite a bit. + +The logs show locally mined blocks confirmed after 5 blocks. At the moment you may find it +easier and faster to generate the list of your mined blocks from these logs. + +[eth-wiki-mining]: https://github.com/ethereum/wiki/wiki/Mining +[ethminer]: https://github.com/ethereum-mining/ethminer
diff --git go-ethereum/docs/docs/_interface/peer-to-peer.md celo/docs/docs/_interface/peer-to-peer.md new file mode 100644 index 0000000000000000000000000000000000000000..8d471964343926fd3e6ea9ba9b33db8df3401f88 --- /dev/null +++ celo/docs/docs/_interface/peer-to-peer.md @@ -0,0 +1,154 @@ +--- +title: Connecting To The Network +sort_key: B +--- + +## How Peers Are Found + +Geth continuously attempts to connect to other nodes on the network until it has peers. If +you have UPnP enabled on your router or run ethereum on an Internet-facing server, it will +also accept connections from other nodes. + +Geth finds peers through something called the discovery protocol. In the discovery +protocol, nodes are gossipping with each other to find out about other nodes on the +network. In order to get going initially, geth uses a set of bootstrap nodes whose +endpoints are recorded in the source code. + +To change the bootnodes on startup, use the `--bootnodes` option and separate the nodes by +commas. For example: + + geth --bootnodes enode://pubkey1@ip1:port1,enode://pubkey2@ip2:port2,enode://pubkey3@ip3:port3 + +## Common Problems With Connectivity + +Sometimes you just can't get connected. The most common reasons are as follows: + +- Your local time might be incorrect. An accurate clock is required to participate in the + Ethereum network. Check your OS for how to resync your clock (example sudo ntpdate -s + time.nist.gov) because even 12 seconds too fast can lead to 0 peers. +- Some firewall configurations can prevent UDP traffic from flowing. You can use the + static nodes feature or `admin.addPeer()` on the console to configure connections by + hand. + +To start geth without the discovery protocol, you can use the `--nodiscover` parameter. +You only want this is you are running a test node or an experimental test network with +fixed nodes. + +## Checking Connectivity + +To check how many peers the client is connected to in the interactive console, the `net` +module has two attributes give you info about the number of peers and whether you are a +listening node. + +```js +> net.listening +true +> net.peerCount +4 +``` + +To get more information about the connected peers, such as IP address and port number, +supported protocols, use the `peers()` function of the `admin` object. `admin.peers()` +returns the list of currently connected peers. + +``` +> admin.peers +[{ + ID: 'a4de274d3a159e10c2c9a68c326511236381b84c9ec52e72ad732eb0b2b1a2277938f78593cdbe734e6002bf23114d434a085d260514ab336d4acdc312db671b', + Name: 'Geth/v0.9.14/linux/go1.4.2', + Caps: 'eth/60', + RemoteAddress: '5.9.150.40:30301', + LocalAddress: '192.168.0.28:39219' +}, { + ID: 'a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c', + Name: 'Geth/v0.9.15/linux/go1.4.2', + Caps: 'eth/60', + RemoteAddress: '52.16.188.185:30303', + LocalAddress: '192.168.0.28:50995' +}, { + ID: 'f6ba1f1d9241d48138136ccf5baa6c2c8b008435a1c2bd009ca52fb8edbbc991eba36376beaee9d45f16d5dcbf2ed0bc23006c505d57ffcf70921bd94aa7a172', + Name: 'pyethapp_dd52/v0.9.13/linux2/py2.7.9', + Caps: 'eth/60, p2p/3', + RemoteAddress: '144.76.62.101:30303', + LocalAddress: '192.168.0.28:40454' +}, { + ID: 'f4642fa65af50cfdea8fa7414a5def7bb7991478b768e296f5e4a54e8b995de102e0ceae2e826f293c481b5325f89be6d207b003382e18a8ecba66fbaf6416c0', + Name: '++eth/Zeppelin/Rascal/v0.9.14/Release/Darwin/clang/int', + Caps: 'eth/60, shh/2', + RemoteAddress: '129.16.191.64:30303', + LocalAddress: '192.168.0.28:39705' +} ] + +``` + +To check the ports used by geth and also find your enode URI run: + +``` +> admin.nodeInfo +{ + Name: 'Geth/v0.9.14/darwin/go1.4.2', + NodeUrl: 'enode://3414c01c19aa75a34f2dbd2f8d0898dc79d6b219ad77f8155abf1a287ce2ba60f14998a3a98c0cf14915eabfdacf914a92b27a01769de18fa2d049dbf4c17694@[::]:30303', + NodeID: '3414c01c19aa75a34f2dbd2f8d0898dc79d6b219ad77f8155abf1a287ce2ba60f14998a3a98c0cf14915eabfdacf914a92b27a01769de18fa2d049dbf4c17694', + IP: '::', + DiscPort: 30303, + TCPPort: 30303, + Td: '2044952618444', + ListenAddr: '[::]:30303' +} +``` + +## Custom Networks + +Sometimes you might not need to connect to the live public network, you can instead choose +to create your own private testnet. This is very useful if you don't need to test external +contracts and want just to test the technology, because you won't have to compete with +other miners and will easily generate a lot of test ether to play around (replace 12345 +with any non-negative number): + + geth -—networkid="12345" console + +It is also possible to run geth with a custom genesis block from a JSON file by supplying +the `--genesis` flag. The genesis JSON file should have the following format: + +```js +{ + "alloc": { + "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": { + "balance": "1606938044258990275541962092341162602522202993782792835301376" + }, + "e6716f9544a56c530d868e4bfbacb172315bdead": { + "balance": "1606938044258990275541962092341162602522202993782792835301376" + }, + ... + }, + "nonce": "0x000000000000002a", + "difficulty": "0x020000", + "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2fefd8" +} +``` + +## Static nodes + +Geth also supports a feature called static nodes if you have certain peers you always want +to connect to. Static nodes are re-connected on disconnects. You can configure permanent +static nodes by putting something like the following into +`<datadir>/geth/static-nodes.json`: + +```js +[ + "enode://f4642fa65af50cfdea8fa7414a5def7bb7991478b768e296f5e4a54e8b995de102e0ceae2e826f293c481b5325f89be6d207b003382e18a8ecba66fbaf6416c0@33.4.2.1:30303", + "enode://pubkey@ip:port" +] +``` + +You can also add static nodes at runtime via the js console using +[`admin.addPeer()`](../interface/management-apis#admin_addpeer): + +```js +admin.addPeer("enode://f4642fa65af50cfdea8fa7414a5def7bb7991478b768e296f5e4a54e8b995de102e0ceae2e826f293c481b5325f89be6d207b003382e18a8ecba66fbaf6416c0@33.4.2.1:30303") +```
diff --git go-ethereum/docs/docs/_legacy/Contract-Tutorial.md celo/docs/docs/_legacy/Contract-Tutorial.md new file mode 100644 index 0000000000000000000000000000000000000000..c1128a15379c4c13ac3ab258d246018202a76200 --- /dev/null +++ celo/docs/docs/_legacy/Contract-Tutorial.md @@ -0,0 +1,938 @@ +--- +title: Contract tutorial +--- + +**This page is heavily outdated** + +## Introduction + +Now that you mastered the basics on how to get started and how to send ether, it's time to get your hands dirty in what really makes ethereum stand out of the crowd: smart contracts. Smart contracts are pieces of code that live on the blockchain and execute commands exactly how they were told to. They can read other contracts, make decisions, send ether and execute other contracts. Contracts will exist and run as long as the whole network exists, and will only stop if they run out of gas or if they were programmed to self destruct. + +What can you do with contracts? You can do almost anything really, but for this guide let's do some simple things: You will get funds through a crowdfunding that, if successful, will supply a radically transparent and democratic organization that will only obey its own citizens, will never swerve away from its constitution and cannot be censored or shut down. And all that in less than 300 lines of code. + +So let's start now. + + +## Your first citizen: the greeter + +Now that you’ve mastered the basics of Ethereum, let’s move into your first serious contract. The Frontier is a big open territory and sometimes you might feel lonely, so our first order of business will be to create a little automatic companion to greet you whenever you feel lonely. We’ll call him the “Greeter”. + +The Greeter is an intelligent digital entity that lives on the blockchain and is able to have conversations with anyone who interacts with it, based on its input. It might not be a talker, but it’s a great listener. Here is its code: + +```js +contract mortal { + /* Define variable owner of the type address*/ + address owner; + + /* this function is executed at initialization and sets the owner of the contract */ + function mortal() { owner = msg.sender; } + + /* Function to recover the funds on the contract */ + function kill() { if (msg.sender == owner) suicide(owner); } +} + +contract greeter is mortal { + /* define variable greeting of the type string */ + string greeting; + + /* this runs when the contract is executed */ + function greeter(string _greeting) public { + greeting = _greeting; + } + + /* main function */ + function greet() constant returns (string) { + return greeting; + } +} +``` + +You'll notice that there are two different contracts in this code: _"mortal"_ and _"greeter"_. This is because Solidity (the high level contract language we are using) has *inheritance*, meaning that one contract can inherit characteristics of another. This is very useful to simplify coding as common traits of contracts don't need to be rewritten every time, and all contracts can be written in smaller, more readable chunks. So by just declaring that _greeter is mortal_ you inherited all characteristics from the "mortal" contract and kept the greeter code simple and easy to read. + +The inherited characteristic _"mortal"_ simply means that the greeter contract can be killed by its owner, to clean up the blockchain and recover funds locked into it when the contract is no longer needed. Contracts in ethereum are, by default, immortal and have no owner, meaning that once deployed the author has no special privileges anymore. Consider this before deploying. + +### Installing a compiler + +Before you are able to Deploy it though, you'll need two things: the compiled code, and the Application Binary Interface, which is a sort of reference template that defines how to interact with the contract. + +The first you can get by using a compiler. You should have a solidity compiler built in on your geth console. To test it, use this command: + + eth.getCompilers() + +If you have it installed, it should output something like this: + + ['Solidity' ] + +If instead the command returns an error, then you need to install it. + +#### Using an online compiler + +If you don't have solC installed, we have a [online solidity compiler](https://chriseth.github.io/cpp-ethereum/) available. But be aware that **if the compiler is compromised, your contract is not safe**. For this reason, if you want to use the online compiler we encourage you to [host your own](https://github.com/chriseth/browser-solidity). + +#### Install SolC on Ubuntu + +Press control+c to exit the console (or type _exit_) and go back to the command line. Open the terminal and execute these commands: + + sudo add-apt-repository ppa:ethereum/ethereum + sudo apt-get update + sudo apt-get install solc + which solc + +Take note of the path given by the last line, you'll need it soon. + +#### Install SolC on Mac OSX + +You need [brew](http://brew.sh) in order to install on your mac + + brew tap ethereum/ethereum + brew install solidity + which solc + +Take note of the path given by the last line, you'll need it soon. + +#### Install SolC on Windows + +You need [chocolatey](http://chocolatey.org) in order to install solc. + + cinst -pre solC-stable + +Windows is more complicated than that, you'll need to wait a bit more. + +If you have the SolC Solidity Compiler installed, you need now reformat by removing spaces so it fits into a string variable [(there are some online tools that will do this)](http://www.textfixer.com/tools/remove-line-breaks.php): + +#### Compile from source + + git clone https://github.com/ethereum/cpp-ethereum.git + mkdir cpp-ethereum/build + cd cpp-ethereum/build + cmake -DJSONRPC=OFF -DMINER=OFF -DETHKEY=OFF -DSERPENT=OFF -DGUI=OFF -DTESTS=OFF -DJSCONSOLE=OFF .. + make -j4 + make install + which solc + +#### Linking your compiler in Geth + +Now [go back to the console](../interface/javascript-console) and type this command to install solC, replacing _path/to/solc_ to the path that you got on the last command you did: + + admin.setSolc("path/to/solc") + +Now type again: + + eth.getCompilers() + +If you now have solC installed, then congratulations, you can keep reading. If you don't, then go to our [forums](http://forum.ethereum.org) or [subreddit](http://www.reddit.com/r/ethereum) and berate us on failing to make the process easier. + + +### Compiling your contract + + +If you have the compiler installed, you need now reformat your contract by removing line-breaks so it fits into a string variable [(there are some online tools that will do this)](http://www.textfixer.com/tools/remove-line-breaks.php): + + var greeterSource = 'contract mortal { address owner; function mortal() { owner = msg.sender; } function kill() { if (msg.sender == owner) suicide(owner); } } contract greeter is mortal { string greeting; function greeter(string _greeting) public { greeting = _greeting; } function greet() constant returns (string) { return greeting; } }' + + var greeterCompiled = web3.eth.compile.solidity(greeterSource) + +You have now compiled your code. Now you need to get it ready for deployment, this includes setting some variables up, like what is your greeting. Edit the first line below to something more interesting than 'Hello World!" and execute these commands: + + var _greeting = "Hello World!" + var greeterContract = web3.eth.contract(greeterCompiled.greeter.info.abiDefinition); + + var greeter = greeterContract.new(_greeting,{from:web3.eth.accounts[0], data: greeterCompiled.greeter.code, gas: 1000000}, function(e, contract){ + if(!e) { + + if(!contract.address) { + console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined..."); + + } else { + console.log("Contract mined! Address: " + contract.address); + console.log(contract); + } + + } + }) + +#### Using the online compiler + +If you don't have solC installed, you can simply use the online compiler. Copy the source code above to the [online solidity compiler](https://chriseth.github.io/cpp-ethereum/) and then your compiled code should appear on the left pane. Copy the code on the box labeled **Geth deploy** to a text file. Now change the first line to your greeting: + + var _greeting = "Hello World!" + +Now you can paste the resulting text on your geth window. Wait up to thirty seconds and you'll see a message like this: + + Contract mined! address: 0xdaa24d02bad7e9d6a80106db164bad9399a0423e + +You will probably be asked for the password you picked in the beginning, because you need to pay for the gas costs to deploying your contract. This contract is estimated to need 172 thousand gas to deploy (according to the [online solidity compiler](https://chriseth.github.io/cpp-ethereum/)), at the time of writing, gas on the test net is priced at 1 to 10 microethers per unit of gas (nicknamed "szabo" = 1 followed by 12 zeroes in wei). To know the latest price in ether all you can see the [latest gas prices at the network stats page](https://stats.ethdev.com) and multiply both terms. + +**Notice that the cost is not paid to the [ethereum developers](https://ethereum.org), instead it goes to the _Miners_, people who are running computers who keep the network running. Gas price is set by the market of the current supply and demand of computation. If the gas prices are too high, you can be a miner and lower your asking price.** + + +After less than a minute, you should have a log with the contract address, this means you've sucessfully deployed your contract. You can verify the deployed code (compiled) by using this command: + + eth.getCode(greeter.address) + +If it returns anything other than "0x" then congratulations! Your little Greeter is live! If the contract is created again (by performing another eth.sendTransaction), it will be published to a new address. + + +### Run the Greeter + +In order to call your bot, just type the following command in your terminal: + + greeter.greet(); + +Since this call changes nothing on the blockchain, it returns instantly and without any gas cost. You should see it return your greeting: + + 'Hello World!' + + +### Getting other people to interact with your code + +In order to other people to run your contract they need two things: the address where the contract is located and the ABI (Application Binary Interface) which is a sort of user manual, describing the name of its functions and how to call them. In order to get each of them run these commands: + + greeterCompiled.greeter.info.abiDefinition; + greeter.address; + +Then you can instantiate a javascript object which can be used to call the contract on any machine connected to the network. Replace 'ABI' and 'address' to create a contract object in javascript: + + var greeter = eth.contract(ABI).at(Address); + +This particular example can be instantiated by anyone by simply calling: + + var greeter2 = eth.contract([{constant:false,inputs:[],name:'kill',outputs:[],type:'function'},{constant:true,inputs:[],name:'greet',outputs:[{name:'',type:'string'}],type:'function'},{inputs:[{name:'_greeting',type:'string'}],type:'constructor'}]).at('greeterAddress'); + +Replace _greeterAddress_ with your contract's address. + + +**Tip: if the solidity compiler isn't properly installed in your machine, you can get the ABI from the online compiler. To do so, use the code below carefully replacing _greeterCompiled.greeter.info.abiDefinition_ with the abi from your compiler.** + + +### Cleaning up after yourself: + +You must be very excited to have your first contract live, but this excitement wears off sometimes, when the owners go on to write further contracts, leading to the unpleasant sight of abandoned contracts on the blockchain. In the future, blockchain rent might be implemented in order to increase the scalability of the blockchain but for now, be a good citizen and humanely put down your abandoned bots. + +Unlike last time we will not be making a call as we wish to change something on the blockchain. This requires a transaction be sent to the network and a fee to be paid for the changes made. The suicide is subsidized by the network so it will cost much less than a usual transaction. + + greeter.kill.sendTransaction({from:eth.accounts[0]}) + +You can verify that the deed is done simply seeing if this returns 0: + + eth.getCode(greeter.contractAddress) + +Notice that every contract has to implement its own kill clause. In this particular case only the account that created the contract can kill it. + +If you don't add any kill clause it could potentially live forever (or at least until the frontier contracts are all wiped) independently of you and any earthly borders, so before you put it live check what your local laws say about it, including any possible limitation on technology export, restrictions on speech and maybe any legislation on the civil rights of sentient digital beings. Treat your bots humanely. + + + + + +## The Coin + +What is a coin? Coins are much more interesting and useful than they seem, they are in essence just a tradeable token, but can become much more, depending on how you use them. Its value depends on what you do with it: a token can be used to control access (**an entrance ticket**), can be used for voting rights in an organization (**a share**), can be placeholders for an asset held by a third party (**a certificate of ownership**) or even be simply used as an exchange of value within a community (**a currency**). + +You could do all those things by creating a centralized server, but using an Ethereum token contract comes with some free functionalities: for one, it's a decentralized service and tokens can be still exchanged even if the original service goes down for any reason. The code can guarantee that no tokens will ever be created other than the ones set in the original code. Finally, by having each user hold their own token, this eliminates the scenarios where one single server break-in can result in the loss of funds from thousands of clients. + +You could create your own token on a different blockchain, but creating on ethereum is easier — so you can focus your energy on the innovation that will make your coin stand out - and it's more secure, as your security is provided by all the miners who are supporting the ethereum network. Finally, by creating your token in Ethereum, your coin will be compatible with any other contract running on ethereum. + +### The Code + +This is the code for the contract we're building: + + contract token { + mapping (address => uint) public coinBalanceOf; + event CoinTransfer(address sender, address receiver, uint amount); + + /* Initializes contract with initial supply tokens to the creator of the contract */ + function token(uint supply) { + coinBalanceOf[msg.sender] = supply; + } + + /* Very simple trade function */ + function sendCoin(address receiver, uint amount) returns(bool sufficient) { + if (coinBalanceOf[msg.sender] < amount) return false; + coinBalanceOf[msg.sender] -= amount; + coinBalanceOf[receiver] += amount; + CoinTransfer(msg.sender, receiver, amount); + return true; + } + } + +If you have ever programmed, you won't find it hard to understand what it does: it is a contract that generates 10 thousand tokens to the creator of the contract, and then allows anyone with enough balance to send it to others. These tokens are the minimum tradeable unit and cannot be subdivided, but for the final users could be presented as a 100 units subdividable by 100 subunits, so owning a single token would represent having 0.01% of the total. If your application needs more fine grained atomic divisibility, then just increase the initial issuance amount. + +In this example we declared the variable "coinBalanceOf" to be public, this will automatically create a function that checks any account's balance. + +### Compile and Deploy + +**So let's run it!** + + var tokenSource = ' contract token { mapping (address => uint) public coinBalanceOf; event CoinTransfer(address sender, address receiver, uint amount); /* Initializes contract with initial supply tokens to the creator of the contract */ function token(uint supply) { coinBalanceOf[msg.sender] = supply; } /* Very simple trade function */ function sendCoin(address receiver, uint amount) returns(bool sufficient) { if (coinBalanceOf[msg.sender] < amount) return false; coinBalanceOf[msg.sender] -= amount; coinBalanceOf[receiver] += amount; CoinTransfer(msg.sender, receiver, amount); return true; } }' + + var tokenCompiled = eth.compile.solidity(tokenSource) + +Now let’s set up the contract, just like we did in the previous section. Change the "initial Supply" to the amount of non divisible tokens you want to create. If you want to have divisible units, you should do that on the user frontend but keep them represented in the minimun unit of account. + + var supply = 10000; + var tokenContract = web3.eth.contract(tokenCompiled.token.info.abiDefinition); + var token = tokenContract.new( + supply, + { + from:web3.eth.accounts[0], + data:tokenCompiled.token.code, + gas: 1000000 + }, function(e, contract){ + if(!e) { + + if(!contract.address) { + console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined..."); + + } else { + console.log("Contract mined! Address: " + contract.address); + console.log(contract); + } + + } + }) + +#### Online Compiler + +**If you don't have solC installed, you can simply use the online compiler.** Copy the contract code to the [online solidity compiler](https://chriseth.github.io/cpp-ethereum/), if there are no errors on the contract you should see a text box labeled **Geth Deploy**. Copy the content to a text file so you can change the first line to set the initial supply, like this: + + var supply = 10000; + +Now you can paste the resulting text on your geth window. Wait up to thirty seconds and you'll see a message like this: + + Contract mined! address: 0xdaa24d02bad7e9d6a80106db164bad9399a0423e + + +### Check balance watching coin transfers + +If everything worked correctly, you should be able to check your own balance with: + + token.coinBalanceOf(eth.accounts[0]) + " tokens" + +It should have all the 10 000 tokens that were created once the contract was published. Since there is not any other defined way for new coins to be issued, these are all that will ever exist. + +You can set up a **Watcher** to react whenever anyone sends a coin using your contract. Here's how you do it: + + var event = token.CoinTransfer({}, '', function(error, result){ + if (!error) + console.log("Coin transfer: " + result.args.amount + " tokens were sent. Balances now are as following: \n Sender:\t" + result.args.sender + " \t" + token.coinBalanceOf.call(result.args.sender) + " tokens \n Receiver:\t" + result.args.receiver + " \t" + token.coinBalanceOf.call(result.args.receiver) + " tokens" ) + }); + +### Sending coins + +Now of course those tokens aren't very useful if you hoard them all, so in order to send them to someone else, use this command: + + token.sendCoin.sendTransaction(eth.accounts[1], 1000, {from: eth.accounts[0]}) + +If a friend has registered a name on the registrar you can send it without knowing their address, doing this: + + token.sendCoin.sendTransaction(registrar.addr("Alice"), 2000, {from: eth.accounts[0]}) + + +Note that our first function **coinBalanceOf** was simply called directly on the contract instance and returned a value. This was possible since this was a simple read operation that incurs no state change and which executes locally and synchronously. Our second function **sendCoin** needs a **.sendTransaction()** call. Since this function is meant to change the state (write operation), it is sent as a transaction to the network to be picked up by miners and included in the canonical blockchain. As a result the consensus state of all participant nodes will adequately reflect the state changes resulting from executing the transaction. Sender address needs to be sent as part of the transaction to fund the fuel needed to run the transaction. Now, wait a minute and check both accounts balances: + + token.coinBalanceOf.call(eth.accounts[0])/100 + "% of all tokens" + token.coinBalanceOf.call(eth.accounts[1])/100 + "% of all tokens" + token.coinBalanceOf.call(registrar.addr("Alice"))/100 + "% of all tokens" + + +### Improvement suggestions + +Right now this cryptocurrency is quite limited as there will only ever be 10,000 coins and all are controlled by the coin creator, but you can change that. You could for example reward ethereum miners, by creating a transaction that will reward who found the current block: + + mapping (uint => address) miningReward; + function claimMiningReward() { + if (miningReward[block.number] == 0) { + coinBalanceOf[block.coinbase] += 1; + miningReward[block.number] = block.coinbase; + } + } + +You could modify this to anything else: maybe reward someone who finds a solution for a new puzzle, wins a game of chess, install a solar panel—as long as that can be somehow translated to a contract. Or maybe you want to create a central bank for your personal country, so you can keep track of hours worked, favours owed or control of property. In that case you might want to add a function to allow the bank to remotely freeze funds and destroy tokens if needed. + + +### Register a name for your coin + +The commands mentioned only work because you have token javascript object instantiated on your local machine. If you send tokens to someone they won't be able to move them forward because they don't have the same object and wont know where to look for your contract or call its functions. In fact if you restart your console these objects will be deleted and the contracts you've been working on will be lost forever. So how do you instantiate the contract on a clean machine? + +There are two ways. Let's start with the quick and dirty, providing your friends with a reference to your contract’s ABI: + + token = eth.contract([{constant:false,inputs:[{name:'receiver',type:'address'},{name:'amount',type:'uint256'}],name:'sendCoin',outputs:[{name:'sufficient',type:'bool'}],type:'function'},{constant:true,inputs:[{name:'',type:'address'}],name:'coinBalanceOf',outputs:[{name:'',type:'uint256'}],type:'function'},{inputs:[{name:'supply',type:'uint256'}],type:'constructor'},{anonymous:false,inputs:[{indexed:false,name:'sender',type:'address'},{indexed:false,name:'receiver',type:'address'},{indexed:false,name:'amount',type:'uint256'}],name:'CoinTransfer',type:'event'}]).at('0x4a4ce7844735c4b6fc66392b200ab6fe007cfca8') + +Just replace the address at the end for your own token address, then anyone that uses this snippet will immediately be able to use your contract. Of course this will work only for this specific contract so let's analyze step by step and see how to improve this code so you'll be able to use it anywhere. + +All accounts are referenced in the network by their public address. But addresses are long, difficult to write down, hard to memorize and immutable. The last one is specially important if you want to be able to generate fresh accounts in your name, or upgrade the code of your contract. In order to solve this, there is a default name registrar contract which is used to associate the long addresses with short, human-friendly names. + +Names have to use only alphanumeric characters and, cannot contain blank spaces. In future releases the name registrar will likely implement a bidding process to prevent name squatting but for now, it works on a first come first served basis: as long as no one else registered the name, you can claim it. + + +First, if you register a name, then you won't need the hardcoded address in the end. Select a nice coin name and try to reserve it for yourself. First, select your name: + + var tokenName = "MyFirstCoin" + +Then, check the availability of your name: + + registrar.addr(tokenName) + +If that function returns "0x00..", you can claim it to yourself: + + registrar.reserve.sendTransaction(tokenName, {from: eth.accounts[0]}); + + +Wait for the previous transaction to be picked up. Wait up to thirty seconds and then try: + + registrar.owner(myName) + +If it returns your address, it means you own that name and are able to set your chosen name to any address you want: + + registrar.setAddress.sendTransaction(tokenName, token.address, true,{from: eth.accounts[0]}); + +_You can replace **token.address** for **eth.accounts[0]** if you want to use it as a personal nickname._ + +Wait a little bit for that transaction to be picked up too and test it: + + registrar.addr("MyFirstCoin") + +You can send a transaction to anyone or any contract by name instead of account simply by typing + + eth.sendTransaction({from: eth.accounts[0], to: registrar.addr("MyFirstCoin"), value: web3.toWei(1, "ether")}) + +**Tip: don't mix registrar.addr for registrar.owner. The first is to which address that name is pointed at: anyone can point a name to anywhere else, just like anyone can forward a link to google.com, but only the owner of the name can change and update the link. You can set both to be the same address.** + +This should now return your token address, meaning that now the previous code to instantiate could use a name instead of an address. + + token = eth.contract([{constant:false,inputs:[{name:'receiver',type:'address'},{name:'amount',type:'uint256'}],name:'sendCoin',outputs:[{name:'sufficient',type:'bool'}],type:'function'},{constant:true,inputs:[{name:'',type:'address'}],name:'coinBalanceOf',outputs:[{name:'',type:'uint256'}],type:'function'},{inputs:[{name:'supply',type:'uint256'}],type:'constructor'},{anonymous:false,inputs:[{indexed:false,name:'sender',type:'address'},{indexed:false,name:'receiver',type:'address'},{indexed:false,name:'amount',type:'uint256'}],name:'CoinTransfer',type:'event'}]).at(registrar.addr("MyFirstCoin")) + +This also means that the owner of the coin can update the coin by pointing the registrar to the new contract. This would, of course, require the coin holders trust the owner set at registrar.owner("MyFirstCoin") + +### Learn More + +* [Meta coin standard](https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs) is a proposed standardization of function names for coin and token contracts, to allow them to be automatically added to other ethereum contract that utilizes trading, like exchanges or escrow. + +* [Formal proofing](https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format#documentation-output) is a way where the contract developer will be able to assert some invariant qualities of the contract, like the total cap of the coin. *Not yet implemented*. + + + +# Crowdfund your idea + +Sometimes a good idea takes a lot of funds and collective effort. You could ask for donations, but donors prefer to give to projects they are more certain that will get traction and proper funding. This is an example where a crowdfunding would be ideal: you set up a goal and a deadline for reaching it. If you miss your goal, the donations are returned, therefore reducing the risk for donors. Since the code is open and auditable, there is no need for a centralized trusted platform and therefore the only fees everyone will pay are just the gas fees. + +In a crowdfunding prizes are usually given. This would require you to get everyone's contact information and keep track of who owns what. But since you just created your own token, why not use that to keep track of the prizes? This allows donors to immediately own something after they donated. They can store it safely, but they can also sell or trade it if they realize they don't want the prize anymore. If your idea is something physical, all you have to do after the project is completed is to give the product to everyone who sends you back a token. If the project is digital the token itself can immediately be used for users to participate or get entry on your project. + +### The code + +The way this particular crowdsale contract works is that you set an exchange rate for your token and then the donors will immediately get a proportional amount of tokens in exchange of their ether. You will also choose a funding goal and a deadline: once that deadline is over you can ping the contract and if the goal was reached it will send the ether raised to you, otherwise it goes back to the donors. Donors keep their tokens even if the project doesn't reach its goal, as a proof that they helped. + + + contract token { mapping (address => uint) public coinBalanceOf; function token() {} function sendCoin(address receiver, uint amount) returns(bool sufficient) { } } + + contract Crowdsale { + + address public beneficiary; + uint public fundingGoal; uint public amountRaised; uint public deadline; uint public price; + token public tokenReward; + Funder[] public funders; + event FundTransfer(address backer, uint amount, bool isContribution); + + /* data structure to hold information about campaign contributors */ + struct Funder { + address addr; + uint amount; + } + + /* at initialization, setup the owner */ + function Crowdsale(address _beneficiary, uint _fundingGoal, uint _duration, uint _price, token _reward) { + beneficiary = _beneficiary; + fundingGoal = _fundingGoal; + deadline = now + _duration * 1 minutes; + price = _price; + tokenReward = token(_reward); + } + + /* The function without name is the default function that is called whenever anyone sends funds to a contract */ + function () { + uint amount = msg.value; + funders[funders.length++] = Funder({addr: msg.sender, amount: amount}); + amountRaised += amount; + tokenReward.sendCoin(msg.sender, amount / price); + FundTransfer(msg.sender, amount, true); + } + + modifier afterDeadline() { if (now >= deadline) _ } + + /* checks if the goal or time limit has been reached and ends the campaign */ + function checkGoalReached() afterDeadline { + if (amountRaised >= fundingGoal){ + beneficiary.send(amountRaised); + FundTransfer(beneficiary, amountRaised, false); + } else { + FundTransfer(0, 11, false); + for (uint i = 0; i < funders.length; ++i) { + funders[i].addr.send(funders[i].amount); + FundTransfer(funders[i].addr, funders[i].amount, false); + } + } + suicide(beneficiary); + } + } + +### Set the parameters + +Before we go further, let's start by setting the parameters of the crowdsale: + + var _beneficiary = eth.accounts[1]; // create an account for this + var _fundingGoal = web3.toWei(100, "ether"); // raises 100 ether + var _duration = 30; // number of minutes the campaign will last + var _price = web3.toWei(0.02, "ether"); // the price of the tokens, in ether + var _reward = token.address; // the token contract address. + +On Beneficiary put the new address that will receive the raised funds. The funding goal is the amount of ether to be raised. Deadline is measured in blocktimes which average 12 seconds, so the default is about 4 weeks. The price is tricky: but just change the number 2 for the amount of tokens the contributors will receive for each ether donated. Finally reward should be the address of the token contract you created in the last section. + +In this example you are selling on the crowdsale half of all the tokens that ever existed, in exchange for 100 ether. Decide those parameters very carefully as they will play a very important role in the next part of our guide. + +### Deploy + +You know the drill: if you are using the solC compiler,[remove line breaks](http://www.textfixer.com/tools/remove-line-breaks.php) and copy the following commands on the terminal: + + + var crowdsaleCompiled = eth.compile.solidity(' contract token { mapping (address => uint) public coinBalanceOf; function token() {} function sendCoin(address receiver, uint amount) returns(bool sufficient) { } } contract Crowdsale { address public beneficiary; uint public fundingGoal; uint public amountRaised; uint public deadline; uint public price; token public tokenReward; Funder[] public funders; event FundTransfer(address backer, uint amount, bool isContribution); /* data structure to hold information about campaign contributors */ struct Funder { address addr; uint amount; } /* at initialization, setup the owner */ function Crowdsale(address _beneficiary, uint _fundingGoal, uint _duration, uint _price, token _reward) { beneficiary = _beneficiary; fundingGoal = _fundingGoal; deadline = now + _duration * 1 minutes; price = _price; tokenReward = token(_reward); } /* The function without name is the default function that is called whenever anyone sends funds to a contract */ function () { Funder f = funders[++funders.length]; f.addr = msg.sender; f.amount = msg.value; amountRaised += f.amount; tokenReward.sendCoin(msg.sender, f.amount/price); FundTransfer(f.addr, f.amount, true); } modifier afterDeadline() { if (now >= deadline) _ } /* checks if the goal or time limit has been reached and ends the campaign */ function checkGoalReached() afterDeadline { if (amountRaised >= fundingGoal){ beneficiary.send(amountRaised); FundTransfer(beneficiary, amountRaised, false); } else { FundTransfer(0, 11, false); for (uint i = 0; i < funders.length; ++i) { funders[i].addr.send(funders[i].amount); FundTransfer(funders[i].addr, funders[i].amount, false); } } suicide(beneficiary); } }'); + + var crowdsaleContract = web3.eth.contract(crowdsaleCompiled.Crowdsale.info.abiDefinition); + var crowdsale = crowdsaleContract.new( + _beneficiary, + _fundingGoal, + _duration, + _price, + _reward, + { + from:web3.eth.accounts[0], + data:crowdsaleCompiled.Crowdsale.code, + gas: 1000000 + }, function(e, contract){ + if(!e) { + + if(!contract.address) { + console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined..."); + + } else { + console.log("Contract mined! Address: " + contract.address); + console.log(contract); + } + + } }) + +**If you are using the _online compiler_ Copy the contract code to the [online solidity compiler](https://chriseth.github.io/cpp-ethereum/), and then grab the content of the box labeled **Geth Deploy**. Since you have already set the parameters, you don't need to change anything to that text, simply paste the resulting text on your geth window.** + +Wait up to thirty seconds and you'll see a message like this: + + Contract mined! address: 0xdaa24d02bad7e9d6a80106db164bad9399a0423e + +If you received that alert then your code should be online. You can always double check by doing this: + + eth.getCode(crowdsale.address) + +Now fund your newly created contract with the necessary tokens so it can automatically distribute rewards to the contributors! + + token.sendCoin.sendTransaction(crowdsale.address, 5000,{from: eth.accounts[0]}) + +After the transaction is picked, you can check the amount of tokens the crowdsale address has, and all other variables this way: + + "Current crowdsale must raise " + web3.fromWei(crowdsale.fundingGoal.call(), "ether") + " ether in order to send it to " + crowdsale.beneficiary.call() + "." + + + +### Put some watchers on + +You want to be alerted whenever your crowdsale receives new funds, so paste this code: + + var event = crowdsale.FundTransfer({}, '', function(error, result){ + if (!error) + + if (result.args.isContribution) { + console.log("\n New backer! Received " + web3.fromWei(result.args.amount, "ether") + " ether from " + result.args.backer ) + + console.log( "\n The current funding at " +( 100 * crowdsale.amountRaised.call() / crowdsale.fundingGoal.call()) + "% of its goals. Funders have contributed a total of " + web3.fromWei(crowdsale.amountRaised.call(), "ether") + " ether."); + + var timeleft = Math.floor(Date.now() / 1000)-crowdsale.deadline(); + if (timeleft>3600) { console.log("Deadline has passed, " + Math.floor(timeleft/3600) + " hours ago") + } else if (timeleft>0) { console.log("Deadline has passed, " + Math.floor(timeleft/60) + " minutes ago") + } else if (timeleft>-3600) { console.log(Math.floor(-1*timeleft/60) + " minutes until deadline") + } else { console.log(Math.floor(-1*timeleft/3600) + " hours until deadline") + } + + } else { + console.log("Funds transferred from crowdsale account: " + web3.fromWei(result.args.amount, "ether") + " ether to " + result.args.backer ) + } + + }); + + + + +### Register the contract + +You are now set. Anyone can now contribute by simply sending ether to the crowdsale address, but to make it even simpler, let's register a name for your sale. First, pick a name for your crowdsale: + + var name = "mycrowdsale" + +Check if that's available and register: + + registrar.addr(name) + registrar.reserve.sendTransaction(name, {from: eth.accounts[0]}); + +Wait for the previous transaction to be picked up and then: + + registrar.setAddress.sendTransaction(name, crowdsale.address, true,{from: eth.accounts[0]}); + + +### Contribute to the crowdsale + +Contributing to the crowdsale is very simple, it doesn't even require instantiating the contract. This is because the crowdsale responds to simple ether deposits, so anyone that sends ether to the crowdsale will automatically receive a reward. +Anyone can contribute to it by simply executing this command: + + var amount = web3.toWei(5, "ether") // decide how much to contribute + + eth.sendTransaction({from: eth.accounts[0], to: crowdsale.address, value: amount, gas: 1000000}) + + +Alternatively, if you want someone else to send it, they can even use the name registrar to contribute: + + eth.sendTransaction({from: eth.accounts[0], to: registrar.addr("mycrowdsale"), value: amount, gas: 500000}) + + +Now wait a minute for the blocks to pickup and you can check if the contract received the ether by doing any of these commands: + + web3.fromWei(crowdsale.amountRaised.call(), "ether") + " ether" + token.coinBalanceOf.call(eth.accounts[0]) + " tokens" + token.coinBalanceOf.call(crowdsale.address) + " tokens" + + +### Recover funds + +Once the deadline is passed someone has to wake up the contract to have the funds sent to either the beneficiary or back to the funders (if it failed). This happens because there is no such thing as an active loop or timer on ethereum so any future transactions must be pinged by someone. + + crowdsale.checkGoalReached.sendTransaction({from:eth.accounts[0], gas: 2000000}) + +You can check your accounts with these lines of code: + + web3.fromWei(eth.getBalance(eth.accounts[0]), "ether") + " ether" + web3.fromWei(eth.getBalance(eth.accounts[1]), "ether") + " ether" + token.coinBalanceOf.call(eth.accounts[0]) + " tokens" + token.coinBalanceOf.call(eth.accounts[1]) + " tokens" + +The crowdsale instance is setup to self destruct once it has done its job, so if the deadline is over and everyone got their prizes the contract is no more, as you can see by running this: + + eth.getCode(crowdsale.address) + +So you raised a 100 ethers and successfully distributed your original coin among the crowdsale donors. What could you do next with those things? + + + + + + +# Democracy DAO + + + +So far you have created a tradeable token and you successfully distributed it among all those who were willing to help fundraise a 100 ethers. That's all very interesting but what exactly are those tokens for? Why would anyone want to own or trade it for anything else valuable? If you can convince your new token is the next big money maybe others will want it, but so far your token offers no value per se. We are going to change that, by creating your first decentralized autonomous organization, or DAO. + +Think of the DAO as the constitution of a country, the executive branch of a government or maybe like a robotic manager for an organization. The DAO receives the money that your organization raises, keeps it safe and uses it to fund whatever its members want. The robot is incorruptible, will never defraud the bank, never create secret plans, never use the money for anything other than what its constituents voted on. The DAO will never disappear, never run away and cannot be controlled by anyone other than its own citizens. + +The token we distributed using the crowdsale is the only citizen document needed. Anyone who holds any token is able to create and vote on proposals. Similar to being a shareholder in a company, the token can be traded on the open market and the vote is proportional to amounts of tokens the voter holds. + +Take a moment to dream about the revolutionary possibilities this would allow, and now you can do it yourself, in under a 100 lines of code: + + +### The Code + + + + + contract token { mapping (address => uint) public coinBalanceOf; function token() { } function sendCoin(address receiver, uint amount) returns(bool sufficient) { } } + + + contract Democracy { + + uint public minimumQuorum; + uint public debatingPeriod; + token public voterShare; + address public founder; + Proposal[] public proposals; + uint public numProposals; + + event ProposalAdded(uint proposalID, address recipient, uint amount, bytes32 data, string description); + event Voted(uint proposalID, int position, address voter); + event ProposalTallied(uint proposalID, int result, uint quorum, bool active); + + struct Proposal { + address recipient; + uint amount; + bytes32 data; + string description; + uint creationDate; + bool active; + Vote[] votes; + mapping (address => bool) voted; + } + + struct Vote { + int position; + address voter; + } + + function Democracy(token _voterShareAddress, uint _minimumQuorum, uint _debatingPeriod) { + founder = msg.sender; + voterShare = token(_voterShareAddress); + minimumQuorum = _minimumQuorum || 10; + debatingPeriod = _debatingPeriod * 1 minutes || 30 days; + } + + + function newProposal(address _recipient, uint _amount, bytes32 _data, string _description) returns (uint proposalID) { + if (voterShare.coinBalanceOf(msg.sender)>0) { + proposalID = proposals.length++; + Proposal p = proposals[proposalID]; + p.recipient = _recipient; + p.amount = _amount; + p.data = _data; + p.description = _description; + p.creationDate = now; + p.active = true; + ProposalAdded(proposalID, _recipient, _amount, _data, _description); + numProposals = proposalID+1; + } + } + + function vote(uint _proposalID, int _position) returns (uint voteID){ + if (voterShare.coinBalanceOf(msg.sender)>0 && (_position >= -1 || _position <= 1 )) { + Proposal p = proposals[_proposalID]; + if (p.voted[msg.sender] == true) return; + voteID = p.votes.length++; + p.votes[voteID] = Vote({position: _position, voter: msg.sender}); + p.voted[msg.sender] = true; + Voted(_proposalID, _position, msg.sender); + } + } + + function executeProposal(uint _proposalID) returns (int result) { + Proposal p = proposals[_proposalID]; + /* Check if debating period is over */ + if (now > (p.creationDate + debatingPeriod) && p.active){ + uint quorum = 0; + /* tally the votes */ + for (uint i = 0; i < p.votes.length; ++i) { + Vote v = p.votes[i]; + uint voteWeight = voterShare.coinBalanceOf(v.voter); + quorum += voteWeight; + result += int(voteWeight) * v.position; + } + /* execute result */ + if (quorum > minimumQuorum && result > 0 ) { + p.recipient.call.value(p.amount)(p.data); + p.active = false; + } else if (quorum > minimumQuorum && result < 0) { + p.active = false; + } + ProposalTallied(_proposalID, result, quorum, p.active); + } + } + } + + + + + +There's a lot of going on but it's simpler than it looks. The rules of your organization are very simple: anyone with at least one token can create proposals to send funds from the country's account. After a week of debate and votes, if it has received votes worth a total of 100 tokens or more and has more approvals than rejections, the funds will be sent. If the quorum hasn't been met or it ends on a tie, then voting is kept until it's resolved. Otherwise, the proposal is locked and kept for historical purposes. + +So let's recap what this means: in the last two sections you created 10,000 tokens, sent 1,000 of those to another account you control, 2,000 to a friend named Alice and distributed 5,000 of them via a crowdsale. This means that you no longer control over 50% of the votes in the DAO, and if Alice and the community bands together, they can outvote any spending decision on the 100 ethers raised so far. This is exactly how a democracy should work. If you don't want to be a part of your country anymore the only thing you can do is sell your own tokens on a decentralized exchange and opt out, but you cannot prevent the others from doing so. + + +### Set Up your Organization + +So open your console and let's get ready to finally put your country online. First, let's set the right parameters, pick them with care: + + var _voterShareAddress = token.address; + var _minimumQuorum = 10; // Minimun amount of voter tokens the proposal needs to pass + var _debatingPeriod = 60; // debating period, in minutes; + +With these default parameters anyone with any tokens can make a proposal on how to spend the organization's money. The proposal has 1 hour to be debated and it will pass if it has at least votes from at least 0.1% of the total tokens and has more support than rejections. Pick those parameters with care, as you won't be able to change them in the future. + + var daoCompiled = eth.compile.solidity('contract token { mapping (address => uint) public coinBalanceOf; function token() { } function sendCoin(address receiver, uint amount) returns(bool sufficient) { } } contract Democracy { uint public minimumQuorum; uint public debatingPeriod; token public voterShare; address public founder; Proposal[] public proposals; uint public numProposals; event ProposalAdded(uint proposalID, address recipient, uint amount, bytes32 data, string description); event Voted(uint proposalID, int position, address voter); event ProposalTallied(uint proposalID, int result, uint quorum, bool active); struct Proposal { address recipient; uint amount; bytes32 data; string description; uint creationDate; bool active; Vote[] votes; mapping (address => bool) voted; } struct Vote { int position; address voter; } function Democracy(token _voterShareAddress, uint _minimumQuorum, uint _debatingPeriod) { founder = msg.sender; voterShare = token(_voterShareAddress); minimumQuorum = _minimumQuorum || 10; debatingPeriod = _debatingPeriod * 1 minutes || 30 days; } function newProposal(address _recipient, uint _amount, bytes32 _data, string _description) returns (uint proposalID) { if (voterShare.coinBalanceOf(msg.sender)>0) { proposalID = proposals.length++; Proposal p = proposals[proposalID]; p.recipient = _recipient; p.amount = _amount; p.data = _data; p.description = _description; p.creationDate = now; p.active = true; ProposalAdded(proposalID, _recipient, _amount, _data, _description); numProposals = proposalID+1; } else { return 0; } } function vote(uint _proposalID, int _position) returns (uint voteID){ if (voterShare.coinBalanceOf(msg.sender)>0 && (_position >= -1 || _position <= 1 )) { Proposal p = proposals[_proposalID]; if (p.voted[msg.sender] == true) return; voteID = p.votes.length++; Vote v = p.votes[voteID]; v.position = _position; v.voter = msg.sender; p.voted[msg.sender] = true; Voted(_proposalID, _position, msg.sender); } else { return 0; } } function executeProposal(uint _proposalID) returns (int result) { Proposal p = proposals[_proposalID]; /* Check if debating period is over */ if (now > (p.creationDate + debatingPeriod) && p.active){ uint quorum = 0; /* tally the votes */ for (uint i = 0; i < p.votes.length; ++i) { Vote v = p.votes[i]; uint voteWeight = voterShare.coinBalanceOf(v.voter); quorum += voteWeight; result += int(voteWeight) * v.position; } /* execute result */ if (quorum > minimumQuorum && result > 0 ) { p.recipient.call.value(p.amount)(p.data); p.active = false; } else if (quorum > minimumQuorum && result < 0) { p.active = false; } } ProposalTallied(_proposalID, result, quorum, p.active); } }'); + + var democracyContract = web3.eth.contract(daoCompiled.Democracy.info.abiDefinition); + + var democracy = democracyContract.new( + _voterShareAddress, + _minimumQuorum, + _debatingPeriod, + { + from:web3.eth.accounts[0], + data:daoCompiled.Democracy.code, + gas: 3000000 + }, function(e, contract){ + if(!e) { + + if(!contract.address) { + console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined..."); + + } else { + console.log("Contract mined! Address: " + contract.address); + console.log(contract); + } + + } + }) + +**If you are using the _online compiler_ Copy the contract code to the [online solidity compiler](https://chriseth.github.io/cpp-ethereum/), and then grab the content of the box labeled **Geth Deploy**. Since you have already set the parameters, you don't need to change anything to that text, simply paste the resulting text on your geth window.** + +Wait a minute until the miners pick it up. It will cost you about 850k Gas. Once that is picked up, it's time to instantiate it and set it up, by pointing it to the correct address of the token contract you created previously. + +If everything worked out, you can take a look at the whole organization by executing this string: + + "This organization has " + democracy.numProposals() + " proposals and uses the token at the address " + democracy.voterShare() ; + +If everything is setup then your DAO should return a proposal count of 0 and an address marked as the "founder". While there are still no proposals, the founder of the DAO can change the address of the token to anything it wants. + +### Register your organization name + +Let's also register a name for your contract so it's easily accessible (don't forget to check your name availability with registrar.addr("nameYouWant") before reserving!) + + var name = "MyPersonalDemocracy" + registrar.reserve.sendTransaction(name, {from: eth.accounts[0]}) + var democracy = eth.contract(daoCompiled.Democracy.info.abiDefinition).at(democracy.address); + democracy.setup.sendTransaction(registrar.addr("MyFirstCoin"),{from:eth.accounts[0]}) + +Wait for the previous transactions to be picked up and then: + + registrar.setAddress.sendTransaction(name, democracy.address, true,{from: eth.accounts[0]}); + + +### The Democracy Watchbots + + + var event = democracy.ProposalAdded({}, '', function(error, result){ + if (!error) + console.log("New Proposal #"+ result.args.proposalID +"!\n Send " + web3.fromWei(result.args.amount, "ether") + " ether to " + result.args.recipient.substring(2,8) + "... for " + result.args.description ) + }); + var eventVote = democracy.Voted({}, '', function(error, result){ + if (!error) + var opinion = ""; + if (result.args.position > 0) { + opinion = "in favor" + } else if (result.args.position < 0) { + opinion = "against" + } else { + opinion = "abstaining" + } + + console.log("Vote on Proposal #"+ result.args.proposalID +"!\n " + result.args.voter + " is " + opinion ) + }); + var eventTally = democracy.ProposalTallied({}, '', function(error, result){ + if (!error) + var totalCount = ""; + if (result.args.result > 1) { + totalCount = "passed" + } else if (result.args.result < 1) { + totalCount = "rejected" + } else { + totalCount = "a tie" + } + console.log("Votes counted on Proposal #"+ result.args.proposalID +"!\n With a total of " + Math.abs(result.args.result) + " out of " + result.args.quorum + ", proposal is " + totalCount + ". Proposal is " + (result.args.active? " still on the floor" : "archived") ) + }); + + +### Interacting with the DAO + +After you are satisfied with what you want, it's time to get all that ether you got from the crowdfunding into your new organization: + + eth.sendTransaction({from: eth.accounts[1], to: democracy.address, value: web3.toWei(100, "ether")}) + +This should take only a minute and your country is ready for business! Now, as a first priority, your organisation needs a nice logo, but unless you are a designer, you have no idea how to do that. For the sake of argument let's say you find that your friend Bob is a great designer who's willing to do it for only 10 ethers, so you want to propose to hire him. + + recipient = registrar.addr("bob"); + amount = web3.toWei(10, "ether"); + shortNote = "Logo Design"; + + democracy.newProposal.sendTransaction( recipient, amount, '', shortNote, {from: eth.accounts[0], gas:1000000}) + +After a minute, anyone can check the proposal recipient and amount by executing these commands: + + "This organization has " + (Number(democracy.numProposals())+1) + " pending proposals"; + +### Keep an eye on the organization + +Unlike most governments, your country's government is completely transparent and easily programmable. As a small demonstration here's a snippet of code that goes through all the current proposals and prints what they are and for whom: + + + + function checkAllProposals() { + console.log("Country Balance: " + web3.fromWei( eth.getBalance(democracy.address), "ether") ); + for (i = 0; i< (Number(democracy.numProposals())); i++ ) { + var p = democracy.proposals(i); + var timeleft = Math.floor(((Math.floor(Date.now() / 1000)) - Number(p[4]) - Number(democracy.debatingPeriod()))/60); + console.log("Proposal #" + i + " Send " + web3.fromWei( p[1], "ether") + " ether to address " + p[0].substring(2,6) + " for "+ p[3] + ".\t Deadline:"+ Math.abs(Math.floor(timeleft)) + (timeleft>0?" minutes ago ":" minutes left ") + (p[5]? " Active":" Archived") ); + } + } + + checkAllProposals(); + +A concerned citizen could easily write a bot that periodically pings the blockchain and then publicizes any new proposals that were put forth, guaranteeing total transparency. + +Now of course you want other people to be able to vote on your proposals. You can check the crowdsale tutorial on the best way to register your contract app so that all the user needs is a name, but for now let's use the easier version. Anyone should be able to instantiate a local copy of your country in their computer by using this giant command: + + + democracy = eth.contract( [{ constant: true, inputs: [{ name: '', type: 'uint256' } ], name: 'proposals', outputs: [{ name: 'recipient', type: 'address' }, { name: 'amount', type: 'uint256' }, { name: 'data', type: 'bytes32' }, { name: 'descriptionHash', type: 'bytes32' }, { name: 'creationDate', type: 'uint256' }, { name: 'numVotes', type: 'uint256' }, { name: 'quorum', type: 'uint256' }, { name: 'active', type: 'bool' } ], type: 'function' }, { constant: false, inputs: [{ name: '_proposalID', type: 'uint256' } ], name: 'executeProposal', outputs: [{ name: 'result', type: 'uint256' } ], type: 'function' }, { constant: true, inputs: [ ], name: 'debatingPeriod', outputs: [{ name: '', type: 'uint256' } ], type: 'function' }, { constant: true, inputs: [ ], name: 'numProposals', outputs: [{ name: '', type: 'uint256' } ], type: 'function' }, { constant: true, inputs: [ ], name: 'founder', outputs: [{ name: '', type: 'address' } ], type: 'function' }, { constant: false, inputs: [{ name: '_proposalID', type: 'uint256' }, { name: '_position', type: 'int256' } ], name: 'vote', outputs: [{ name: 'voteID', type: 'uint256' } ], type: 'function' }, { constant: false, inputs: [{ name: '_voterShareAddress', type: 'address' } ], name: 'setup', outputs: [ ], type: 'function' }, { constant: false, inputs: [{ name: '_recipient', type: 'address' }, { name: '_amount', type: 'uint256' }, { name: '_data', type: 'bytes32' }, { name: '_descriptionHash', type: 'bytes32' } ], name: 'newProposal', outputs: [{ name: 'proposalID', type: 'uint256' } ], type: 'function' }, { constant: true, inputs: [ ], name: 'minimumQuorum', outputs: [{ name: '', type: 'uint256' } ], type: 'function' }, { inputs: [ ], type: 'constructor' } ] ).at(registrar.addr('MyPersonalCountry')) + +Then anyone who owns any of your tokens can vote on the proposals by doing this: + + var proposalID = 0; + var position = -1; // +1 for voting yea, -1 for voting nay, 0 abstains but counts as quorum + democracy.vote.sendTransaction(proposalID, position, {from: eth.accounts[0], gas: 1000000}); + + var proposalID = 1; + var position = 1; // +1 for voting yea, -1 for voting nay, 0 abstains but counts as quorum + democracy.vote.sendTransaction(proposalID, position, {from: eth.accounts[0], gas: 1000000}); + + +Unless you changed the basic parameters in the code, any proposal will have to be debated for at least a week until it can be executed. After that anyone—even a non-citizen—can demand the votes to be counted and the proposal to be executed. The votes are tallied and weighted at that moment and if the proposal is accepted then the ether is sent immediately and the proposal is archived. If the votes end in a tie or the minimum quorum hasn’t been reached, the voting is kept open until the stalemate is resolved. If it loses, then it's archived and cannot be voted again. + + var proposalID = 1; + democracy.executeProposal.sendTransaction(proposalID, {from: eth.accounts[0], gas: 1000000}); + + +If the proposal passed then you should be able to see Bob's ethers arriving on his address: + + web3.fromWei(eth.getBalance(democracy.address), "ether") + " ether"; + web3.fromWei(eth.getBalance(registrar.addr("bob")), "ether") + " ether"; + + +**Try for yourself:** This is a very simple democracy contract, which could be vastly improved: currently, all proposals have the same debating time and are won by direct vote and simple majority. Can you change that so it will have some situations, depending on the amount proposed, that the debate might be longer or that it would require a larger majority? Also think about some way where citizens didn't need to vote on every issue and could temporarily delegate their votes to a special representative. You might have also noticed that we added a tiny description for each proposal. This could be used as a title for the proposal or could be a hash of a larger document describing it in detail. + +### Let's go exploring! + +You have reached the end of this tutorial, but it's just the beginning of a great adventure. Look back and see how much you accomplished: you created a living, talking robot, your own cryptocurrency, raised funds through a trustless crowdfunding and used it to kickstart your own personal democratic organization. + +For the sake of simplicity, we only used the democratic organization you created to send ether around, the native currency of ethereum. While that might be good enough for some, this is only scratching the surface of what can be done. In the ethereum network contracts have all the same rights as any normal user, meaning that your organization could do any of the transactions that you executed coming from your own accounts. + + +### What could happen next? + +* The greeter contract you created at the beginning could be improved to charge ether for its services and could funnel those funds into the DAO. + +* The tokens you still control could be sold on a decentralized exchange or traded for goods and services to fund further develop the first contract and grow the organization. + +* Your DAO could own its own name on the name registrar, and then change where it's redirecting in order to update itself if the token holders approved. + +* The organization could hold not only ethers, but any kind of other coin created on ethereum, including assets whose value are tied to the bitcoin or dollar. + +* The DAO could be programmed to allow a proposal with multiple transactions, some scheduled to the future. +It could also own shares of other DAO's, meaning it could vote on larger organization or be a part of a federation of DAO's. + +* The Token Contract could be reprogrammed to hold ether or to hold other tokens and distribute it to the token holders. This would link the value of the token to the value of other assets, so paying dividends could be accomplished by simply moving funds to the token address. + +This all means that this tiny society you created could grow, get funding from third parties, pay recurrent salaries, own any kind of crypto-assets and even use crowdsales to fund its activities. All with full transparency, complete accountability and complete immunity from any human interference. While the network lives the contracts will execute exactly the code they were created to execute, without any exception, forever. + +So what will your contract be? Will it be a country, a company, a non-profit group? What will your code do? + +That's up to you.
diff --git go-ethereum/docs/docs/_legacy/Disclaimer.md celo/docs/docs/_legacy/Disclaimer.md new file mode 100644 index 0000000000000000000000000000000000000000..98e1cf9610e15d2b0c995ae4ce977c1b101d0a43 --- /dev/null +++ celo/docs/docs/_legacy/Disclaimer.md @@ -0,0 +1,86 @@ +--- +title: Disclaimer +--- + +Safety caveats + +## Security warnings + +* **You are responsible for your own computer security.** If your machine is compromised you **will** lose your ether, access to any contracts and maybe more. + +* **You are responsible for your own actions.** If you mess something up or break any laws while using this software, it's your fault, and your fault only. + +* **You are responsible for your own karma.** Don't be a jerk and respect others. + +* This software is open source under a [GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl-3.0.en.html) license. + +## Legal warning: Disclaimer of Liabilites and Warranties + +### Short version + +* **The user expressly knows and agrees that the user is using the ethereum platform at the user’s sole risk.** +* **The user represents that the user has an adequate understanding of the risks, usage and intricacies of cryptographic tokens and blockchain-based open source software, eth platform and eth.** +* **The user acknowledges and agrees that, to the fullest extent permitted by any applicable law, the disclaimers of liability contained herein apply to any and all damages or injury whatsoever caused by or related to risks of, use of, or inability to use, eth or the ethereum platform under any cause or action whatsoever of any kind in any jurisdiction, including, without limitation, actions for breach of warranty, breach of contract or tort (including negligence) and that neither stiftung ethereum nor the ethereum team shall be liable for any indirect, incidental, special, exemplary or consequential damages, including for loss of profits, goodwill or data.** +* **Some jurisdictions do not allow the exclusion of certain warranties or the limitation or exclusion of liability for certain types of damages. therefore, some of the above limitations in this section may not apply to a user. In particular, nothing in these terms shall affect the statutory rights of any user or exclude injury arising from any willful misconduct or fraud of stiftung ethereum.** + +### Long Version: Terms and Conditions + +The following Terms and Conditions (“Terms”) govern the use of the Ethereum open source software platform (“Ethereum Platform”). Prior to any use of the Ethereum Platform, the User confirms to understand and expressly agrees to all of the Terms. All capitalized terms in this agreement will be given the same effect and meaning as in the Terms. The group of developers and other personnel that is now, or will be, employed by, or contracted with, Stiftung Ethereum (“Stiftung Ethereum”) is termed the “Ethereum Team.” The Platform will be developed by persons and entities who support Ethereum, including both volunteers and developers who are paid by nonprofit entities interested in supporting the Ethereum Platform. + +The user acknowledges the following serious risks to any use the Ethereum Platform and ETH and expressly agrees not to hold liable Ethereum Stiftung or Ethereum Team should any of these risks occur: + +#### Risk of Regulatory Actions in One or More Jurisdictions + +The Ethereum Platform and ETH could be impacted by one or more regulatory inquiries or regulatory action, which could impede or limit the ability of Stiftung Ethereum to continue to develop the Ethereum Platform, or which could impeded or limit the ability of User to use Ethereum Platform or ETH. + +#### Risk of Alternative, Unofficial Ethereum Networks + +It is possible that alternative Ethereum-based networks could be established, which utilize the same open source source code and open source protocol underlying the Ethereum Platform. The Ethereum network may compete with these alternative Ethereum-based networks, which could potentially negatively impact the Ethereum Platform and ETH. + +#### Risk of Insufficient Interest in the Ethereum Platform or Distributed Applications + +It is possible that the Ethereum Platform will not be used by a large number of external businesses, individuals, and other organizations and that there will be limited public interest in the creation and development of distributed applications. Such a lack of interest could impact the development of the Ethereum Platform and potential uses of ETH. It cannot predict the success of its own development efforts or the efforts of other third parties. + +#### Risk that the Ethereum Platform, As Developed, Will Not Meet the Expectations of User + +The User recognizes that the Ethereum Platform is under development and may undergo significant changes before release. User acknowledges that any expectations regarding the form and functionality of the Ethereum Platform held by the User may not be met upon release of the Ethereum Platform, for any number of reasons including a change in the design and implementation plans and execution of the implementation of the Ethereum Platform. + +#### Risk of Security Weaknesses in the Ethereum Platform Core Infrastructure Software + +The Ethereum Platform rests on open-source software, and there is a risk that the Ethereum Stiftung or the Ethereum Team, or other third parties not directly affiliated with the Stiftung Ethereum, may introduce weaknesses or bugs into the core infrastructural elements of the Ethereum Platform causing the system to lose ETH stored in one or more User accounts or other accounts or lose sums of other valued tokens issued on the Ethereum Platform. + +#### Risk of Weaknesses or Exploitable Breakthroughs in the Field of Cryptography + +Cryptography is an art, not a science. And the state of the art can advance over time Advances in code cracking, or technical advances such as the development of quantum computers, could present risks to cryptocurrencies and the Ethereum Platform, which could result in the theft or loss of ETH. To the extent possible, Stiftung Ethereum intends to update the protocol underlying the Ethereum Platform to account for any advances in cryptography and to incorporate additional security measures, but cannot it cannot predict the future of cryptography or the success of any future security updates. + +#### Risk of Ether Mining Attacks + +As with other cryptocurrencies, the blockchain used for the Ethereum Platform is susceptible to mining attacks, including but not limited to double-spend attacks, majority mining power attacks, “selfish-mining” attacks, and race condition attacks. Any successful attacks present a risk to the Ethereum Platform, expected proper execution and sequencing of ETH transactions, and expected proper execution and sequencing of contract computations. Despite the efforts of the Ethereum Stiftung and Team, known or novel mining attacks may be successful. + +#### Risk of Rapid Adoption and Increased Demand + +If the Ethereum Platform is rapidly adopted, the demand for ETH could rise dramatically and at a pace that exceeds the rate with which ETH miners can create new ETH tokens. Under such a scenario, the entire Ethereum Platform could become destabilized, due to the increased cost of running distributed applications. In turn, this could dampen interest in the Ethereum Platform and ETH. Instability in the demand of for ETH may lead to a negative change of the economical parameters of an Ethereum based business which could result in the business being unable to continue to operate economically or to cease operation. + +#### Risk of Rapid Adoption and Insufficiency of Computational Application Processing Power on the Ethereum Platform + +If the Ethereum Platform is rapidly adopted, the demand for transaction processing and distributed application computations could rise dramatically and at a pace that exceeds the rate with which ETH miners can bring online additional mining power. Under such a scenario, the entire Ethereum Platform could become destabilized, due to the increased cost of running distributed applications. In turn, this could dampen interest in the Ethereum Platform and ETH. Insufficiency of computational resources and an associated rise in the price of ETH could result in businesses being unable to acquire scarce computational resources to run their distributed applications. This would represent revenue losses to businesses or worst case, cause businesses to cease operations because such operations have become uneconomical due to distortions in the crypto-economy. + +Acknowledgment, Acceptance of all Risks and Disclaimer of Warranties and Liabilities +**THE USER EXPRESSLY KNOWS AND AGREES THAT THE USER IS USING THE ETHEREUM PLATFORM AT THE USER’S SOLE RISK. THE USER REPRESENTS THAT THE USER HAS AN ADEQUATE UNDERSTANDING OF THE RISKS, USAGE AND INTRICACIES OF CRYPTOGRAPHIC TOKENS AND BLOCKCHAIN-BASED OPEN SOURCE SOFTWARE, ETH PLATFORM AND ETH. THE USER ACKNOWLEDGES AND AGREES THAT, TO THE FULLEST EXTENT PERMITTED BY ANY APPLICABLE LAW, THE DISCLAIMERS OF LIABILITY CONTAINED HEREIN APPLY TO ANY AND ALL DAMAGES OR INJURY WHATSOEVER CAUSED BY OR RELATED TO RISKS OF, USE OF, OR INABILITY TO USE, ETH OR THE ETHEREUM PLATFORM UNDER ANY CAUSE OR ACTION WHATSOEVER OF ANY KIND IN ANY JURISDICTION, INCLUDING, WITHOUT LIMITATION, ACTIONS FOR BREACH OF WARRANTY, BREACH OF CONTRACT OR TORT (INCLUDING NEGLIGENCE) AND THAT NEITHER STIFTUNG ETHEREUM NOR ETHERUM TEAM SHALL BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES, INCLUDING FOR LOSS OF PROFITS, GOODWILL OR DATA. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR CERTAIN TYPES OF DAMAGES. THEREFORE, SOME OF THE ABOVE LIMITATIONS IN THIS SECTION MAY NOT APPLY TO A USER. IN PARTICULAR, NOTHING IN THESE TERMS SHALL AFFECT THE STATUTORY RIGHTS OF ANY USER OR EXCLUDE INJURY ARISING FROM ANY WILLFUL MISCONDUCT OR FRAUD OF STIFTUNG ETHEREUM**. + +#### Dispute Resolution + +All disputes or claims arising out of, relating to, or in connection with the Terms, the breach thereof, or use of the Ethereum Platform shall be finally settled under the Rules of Arbitration of the International Chamber of Commerce by one or more arbitrators appointed in accordance with said Rules. All claims between the parties relating to these Terms that are capable of being resolved by arbitration, whether sounding in contract, tort, or otherwise, shall be submitted to ICC arbitration. Prior to commencing arbitration, the parties have a duty to negotiate in good faith and attempt to resolve their dispute in a manner other than by submission to ICC arbitration. The arbitration panel shall consist of one arbitrator only, unless the ICC Court of Arbitration determines that the dispute is such as to warrant three arbitrators. If the Court determines that one arbitrator is sufficient, then such arbitrator shall be Swiss resident. If the Court determines that three arbitrators are necessary, then each party shall have 30 days to nominate an arbitrator of its choice -- in the case of the Claimant, measured from receipt of notification of the ICC Court’s decision to have three arbitrators; in the case of Respondent, measured from receipt of notification of Claimant’s nomination. All nominations must be Swiss resident. If a party fails to nominate an arbitrator, the Court will do so. The Court shall also appoint the chairman. All arbitrators shall be and remain “independent” of the parties involved in the arbitration. The place of arbitration shall be Zug, Switzerland. The language of the arbitration shall be English. In deciding the merits of the dispute, the tribunal shall apply the laws of Switzerland and any discovery shall be limited and shall not involve any depositions or any other examinations outside of a formal hearing. The tribunal shall not assume the powers of amiable compositeur or decide the case ex aequo et bono. In the final award, the tribunal shall fix the costs of the arbitration and decide which of the parties shall bear such costs in what proportion. Every award shall be binding on the parties. The parties undertake to carry out the award without delay and waive their right to any form of recourse against the award in so far as such waiver can validly be made. + +#### Force Majeure + +**STIFTUNG ETHEREUM** is finally not liable for: + +* unavoidable casualty, +* delays in delivery of materials, +* embargoes, +* government orders, +* acts of civil or military authorities, +* lack of energy, or +* any similar unforeseen event that renders performance commercially implausible. + \ No newline at end of file
diff --git go-ethereum/docs/docs/_legacy/Ethereum-Specification.md celo/docs/docs/_legacy/Ethereum-Specification.md new file mode 100644 index 0000000000000000000000000000000000000000..4a55748e658dcf1e4804e975c3a5eea9ef81864f --- /dev/null +++ celo/docs/docs/_legacy/Ethereum-Specification.md @@ -0,0 +1,31 @@ +--- +title: Ethereum specification +--- +Specifications of all ethereum technologies, languages, protocols, etc. + +### Whitepapers and design rationale + +- [Ethereum Whitepaper](https://github.com/ethereum/wiki/wiki/White-Paper) +- [Design Rationale](https://github.com/ethereum/wiki/wiki/Design-Rationale) +- [Ethereum Yellow Paper](http://gavwood.com/Paper.pdf) +- [ÐΞVp2p Whitepaper](https://github.com/ethereum/wiki/wiki/libp2p-Whitepaper) (WiP) +- [Ethash](https://github.com/ethereum/wiki/wiki/Ethash) + +### Specs + +- [JavaScript API](https://github.com/ethereum/wiki/wiki/JavaScript-API#a) +- [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) +- [JSRE admin API](../interface/javascript-console) +- [RLP](https://github.com/ethereum/wiki/wiki/RLP) +- [ÐΞVp2p Wire Protocol](https://github.com/ethereum/wiki/wiki/%C3%90%CE%9EVp2p-Wire-Protocol) +- [Web3 Secret Storage](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) +- [Patricia Tree](https://github.com/ethereum/wiki/wiki/Patricia-Tree) +- [Wire protocol](https://github.com/ethereum/wiki/wiki/Ethereum-Wire-Protocol) +- [Light client protocol](https://github.com/ethereum/wiki/wiki/Light-client-protocol) +- [Solidity, Docs & ABI](https://github.com/ethereum/wiki/wiki/Solidity,-Docs-and-ABI) +- [NatSpec](https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format) +- [Contract ABI](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) +- [Ethash](https://github.com/ethereum/wiki/wiki/Ethash) +- [Ethash C API](https://github.com/ethereum/wiki/wiki/Ethash-C-API) +- [Ethash DAG](https://github.com/ethereum/wiki/wiki/Ethash-DAG) +- [ICAP: Inter-exchange Client Address Protocol](https://github.com/ethereum/wiki/wiki/ICAP:-Inter-exchange-Client-Address-Protocol)
diff --git go-ethereum/docs/docs/_legacy/Ethereum-on-Android.md celo/docs/docs/_legacy/Ethereum-on-Android.md new file mode 100644 index 0000000000000000000000000000000000000000..4184a1cc5e0855b8e88333618191025409de9fa4 --- /dev/null +++ celo/docs/docs/_legacy/Ethereum-on-Android.md @@ -0,0 +1,66 @@ +--- +title: Ethereum on Android +--- +Building Geth for Android is a non trivial task, as it requires cross compiling external C dependencies ([GNU Arithmetic Library](https://gmplib.org/)); internal C dependencies ([ethash](https://github.com/ethereum/ethash)); as well as the entire CGO enabled Go code-base to Android. This is further complicated by the Position Independent Executables (PIE) security feature introduced since Android 4.1 Jelly Bean, requiring different compiler and linker options based on the target Android platform version. + +To cope with all the build issues, the [`xgo`](https://github.com/karalabe/xgo) CGO enabled Go cross compiler is used, which assembles an entire multi-platform cross compiler suite into a single mega docker container. Details about using `xgo` can be found in the project's [README](https://github.com/karalabe/xgo/blob/master/README.md), with Ethereum specifics on the [go-ethereum cross compilation](../developers/cross-compiling-ethereum). + +TL;DR + +``` +$ go get -u github.com/karalabe/xgo +$ xgo --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 \ + --branch=develop \ + --targets=android-16/arm \ + github.com/ethereum/go-ethereum/cmd/geth + +$ ls -al + -rwxr-xr-x 1 root root 23213348 Sep 14 19:35 geth-android-16-arm +``` + +## Deploying a binary + +Currently `xgo` will compile a native Android binary that can be copied onto a device and executed from a terminal emulator. As Ethereum Android support at the moment is mostly a developer feature, there have been no attempts at making it even remotely user friendly (installers, APKs, etc). + +To push a native binary onto an Android device, you'll need an Android SDK installed. The most lightweight solution is the standalone [SDK Tools Only](https://developer.android.com/sdk/index.html#Other) package. Download and extract for your local machine's platform. Since building the binary is already done, we only need the [Android Debug Bridge](http://developer.android.com/tools/help/adb.html) to push it to our device, which can be installed using the SDK's `android` tool `$YOUR_SDK_PATH/tools/android` -> `Android SDK Platform Tools` (deselect everything else). We'll assume `$YOUR_SDK_PATH/platform-tools/adb` is in the PATH environmental variable from now on. + +You can list the available devices via: + +``` +$ adb devices +List of devices attached +0149CBF30201400E device +``` + +Deploying the binary to an Android device can be done in two steps. First copy the binary into the non-executable `sdcard` filesystem on the device. You may be asked the first time by the device to grant developer permission (also developer mode should be enabled on the devices prior). + +``` +$ adb push $PATH_TO_BINARY/geth-android-16-arm /sdcard/ +1984 KB/s (23213348 bytes in 11.421s) +``` + +Then the binary needs to be moved to a file system with executable permissions, and said permissions need to be granted. On an unrooted phone the following path should be accessible with USB developer options. + +``` +$ adb shell +$ cp /sdcard/geth-android-16-arm /data/local/tmp/geth +$ cd /data/local/tmp +$ chmod 751 geth +``` + +## Running the deployed binary + +After pushing the binary to the device and setting the appropriate permissions, you may execute Geth straight from the Android Debug Bridge shell: + +``` +$ ./geth +I0911 11:09:05.329120 1427 cmd.go:125] Starting Geth/v1.1.0/android/go1.5.1 +I0911 11:09:05.466782 1427 server.go:311] Starting Server +I0911 11:09:05.823965 1427 udp.go:207] Listening, enode://824e1a16bd6cb9931bec1ab6268cd76571936d5674505d53c7409b2b860cd9e396a66c7fe4c3ad4e60c43fe42408920e33aaf3e7bbdb6123f8094dbc423c2bb1@[::]:30303 +I0911 11:09:05.832037 1427 backend.go:560] Server started +I0911 11:09:05.848936 1427 server.go:552] Listening on [::]:30303 +``` + +A fancier way would be to start a terminal emulator on the Android device itself and run the binary expressly from it (remember, deployed at `/data/local/tmp/geth`): + +![Geth on Android](http://i.imgur.com/wylOsBL.jpg) \ No newline at end of file
diff --git go-ethereum/docs/docs/_legacy/Gas-Price-Oracle.md celo/docs/docs/_legacy/Gas-Price-Oracle.md new file mode 100644 index 0000000000000000000000000000000000000000..918ae563c935bb9b4732907cc35bbe5469b9a82d --- /dev/null +++ celo/docs/docs/_legacy/Gas-Price-Oracle.md @@ -0,0 +1,44 @@ +--- +title: Gas price oracle +--- + +The gas price oracle is a helper function of the Geth client that tries to find an +appropriate default gas price when sending transactions. It can be parametrized with the +following command line options: + +- `gpomin`: lower limit of suggested gas price. This should be set at least as high as the + `gasprice` setting usually used by miners so that your transactions will not be rejected + automatically because of a too low price. + +- `gpomax`: higher limit of suggested gas price. During load peaks when there is a + competition between transactions to get into the blocks, the price needs to be limited, + otherwise the oracle would eventually try to overbid everyone else at any price. + +- `gpofull`: a block is considered "full" when a certain percentage of the block gas limit + (specified in percents) is used up by transactions. If a block is not "full", that means + that a transaction could have been accepted even with a minimal price offered. + +- `gpobasedown`: an exponential ratio (specified in `1/1000ths`) by which the base price + decreases when the lowest acceptable price of the last block is below the last base + price. + +- `gpobaseup`: an exponential ratio (specified in `1/1000ths`) by which the base price + increases when the lowest acceptable price of the last block is over the last base + price. + +- `gpobasecf`: a correction factor (specified in percents) of the base price. The + suggested price is the corrected base price, limited by `gpomin` and `gpomax`. + +The lowest acceptable price is defined as a price that could have been enough to insert a +transaction into a certain block. Although this value varies slightly with the gas used by +the particular transaction, it is aproximated as follows: if the block is full, it is the +lowest transaction gas price found in that block. If the block is not full, it equals to +gpomin. + +The base price is a moving value that is adjusted from block to block, up if it was lower +than the lowest acceptable price, down otherwise. Note that there is a slight amount of +randomness added to the correction factors so that your client will not behave absolutely +predictable on the market. + +If you want to specify a constant for the default gas price and not use the oracle, set +both `gpomin` and `gpomax` to the same value.
diff --git go-ethereum/docs/docs/_legacy/Metrics-and-Monitoring.md celo/docs/docs/_legacy/Metrics-and-Monitoring.md new file mode 100644 index 0000000000000000000000000000000000000000..03a1923f57251068a77ed1b4a3c1be972ace4336 --- /dev/null +++ celo/docs/docs/_legacy/Metrics-and-Monitoring.md @@ -0,0 +1,116 @@ +--- +title: Metrics and monitoring +--- +Geth has quite a nice logging system, capable of creating leveled log entries tagged with various parts of the system. This helps enormously during debugging to see exactly what the system is doing, what branches it's taking, etc. However, logs are not particularly useful when the system does work correctly, just not very optimally: one - or even a handful - of logged events is not really statistically relevant, and tracing more in log files can quickly become unwieldy. + +The goal of the Geth metrics system is that - similar to logs - we should be able to add arbitrary metric collection to any part of the code without requiring fancy constructs to analyze them (counter variables, public interfaces, crossing over the APIs, console hooks, etc). Instead, we should just "update" metrics whenever and wherever needed, and have them automatically collected, surfaced through the APIs, queryable and visualizable for analysis. + +To that extent, Geth currently implement two types of metrics: + * **Meters**: Analogous to physical meters (electricity, water, etc), they are capable of measuring the *amount* of "things" that pass through and at the *rate* at which they do that. A meter doesn't have a specific unit of measure (byte, block, malloc, etc), it just counts arbitrary *events*. At any point in time it can report: + * *Total number of events* that passed through the meter + * *Mean throughput rate* of the meter since startup (events / second) + * *Weighted throughput rate* in the last *1*, *5* and *15* minutes (events / second) + * (*"weighted" means that recent seconds count more that in older ones*) + * **Timers**: Extension of *meters*, where not only the occurrence of some event is measured, its *duration* is also collected. Similarly to meters, a timer can also measure arbitrary events, but each requires a duration to be assigned individually. Beside **all** the reports a meter can generate, a timer has additionally: + * *Percentiles (5, 20, 50, 80, 95)*, reporting that some percentage of the events took less than the reported time to execute (*e.g. Percentile 20 = 1.5s would mean that 20% of the measured events took less time than 1.5 seconds to execute; inherently 80%(=100%-20%) took more that 1.5s*) + * Percentile 5: minimum durations (this is as fast as it gets) + * Percentile 50: well behaved samples (boring, just to give an idea) + * Percentile 80: general performance (these should be optimised) + * Percentile 95: worst case outliers (rare, just handle gracefully) + +## Creating and updating metrics + +Although the Geth metrics system is based on the [`go-metrics`](https://github.com/rcrowley/go-metrics) library, custom metric constructors are used that take into consideration the CLI flags to enable or disable metrics collection and reporting: + +```go +meter := metrics.NewMeter("system/memory/allocs") +timer := metrics.NewTimer("chain/inserts") +``` + +The name can be any arbitrary string, however since Geth assumes it to be some meaningful sub-system hierarchy, please name accordingly. Metrics can then be updated equally simply: + +```go +meter.Mark(n) // Record the occurrence of `n` events + +timer.Update(duration) // Record an event that took `duration` +timer.UpdateSince(time) // Record an event that started at `time` +timer.Time(function) // Measure and record the execution of `function` +``` + +Note, metrics collection is disabled by default in order not to incur reporting overhead for the average user. To enable it please specify the `--metrics` flag to geth. + +## Querying metrics + +Geth automatically exposes all collected metrics in the `debug` RPC API, through the `metrics` method, hence these can be queried simply from the console in: + +```javascript +> debug.metrics().p2p.InboundTraffic +{ + Avg01Min: '169.12K (2.82K/s)', + Avg05Min: '1.92M (6.42K/s)', + Avg15Min: '3.57M (3.96K/s)', + Total: '5.83M (2.97K/s)' +} +> debug.metrics().chain.inserts +{ + Avg01Min: '10 (0.17/s)', + Avg05Min: '61 (0.20/s)', + Avg15Min: '168 (0.19/s)', + Maximum: '2.157261657s', + Minimum: '2.271716ms', + Percentiles: { + 20: '6.993756ms', + 50: '12.342836ms', + 80: '21.765944ms', + 95: '218.500479ms', + 99: '376.015984ms' + }, + Total: '432 (0.22/s)' +} +``` + +By default, the reported metrics are scaled and formatted in a user friendly way to allow quick inspection. These are however not appropriate for programmatic processing, so the raw values may be retrieved via an optional flag: + +```javascript +> debug.metrics(true).p2p.InboundTraffic +{ + AvgRate01Min: 1599.6190029292586, + AvgRate05Min: 5367.754506658111, + AvgRate15Min: 3761.057607521597, + MeanRate: 2907.3919382272857, + Total: 5901154 +} +``` + +## Monitoring metrics + +Although inspecting metrics via the console is very useful to gain an insight into the internal state of Geth, it falls short of visualizing how these metrics evolve over time, possibly under different circumstances and events. To overcome this limitation, Geth introduces a monitoring tool (`geth monitor`) that periodically queries a node for the requested metrics and plots them on a terminal based UI. + +![Monitoring tool](http://i.imgur.com/Nug0sPG.png) + +Monitoring can be started via: + +``` +geth monitor [--attach=api-endpoint] metric1 metric2 ... metricN +``` + +Where a metric may be: + * Full canonical metric (e.g. `system/memory/allocs/AvgRate05Min`) + * Group of metrics (e.g. `system/memory/allocs` or `system/memory`) + * Multiple branching metrics (e.g. `system/memory/allocs,frees/AvgRate01Min`) + +Not yet supported but planned: + * Wildcard pattern (e.g. `system/memory/*/AvgRate01Min`) + * Exclusion pattern (e.g. `system/memory/allocs/!AvgRate01Min`) + +By default `geth monitor` uses 5 chart rows. This makes comparative charts easy as meters have 5 components, and timers 10 (out of which 5 are throughput and 5 percentiles). For custom layout you can override with `--rows`. + +## Available metrics + +Metrics are a debugging tool, with every developer being free to add, remove or modify them as seen fit. As they can change between commits, the exactly available ones can be queried via `geth monitor` or via `debug.metrics(false)` in the console. A few however may warrant longevity, so feel free to add to the below list if you feel it's worth a more general audience: + + * system/memory/ + * allocs: number of memory allocations made + * frees: number of memory releases made + * inuse: memory currently being used + * pauses: time spent in the garbage collector \ No newline at end of file
diff --git go-ethereum/docs/docs/_legacy/sending-ether.md celo/docs/docs/_legacy/sending-ether.md new file mode 100644 index 0000000000000000000000000000000000000000..c083bb85b873647133b85db4da06f46b240e0b17 --- /dev/null +++ celo/docs/docs/_legacy/sending-ether.md @@ -0,0 +1,33 @@ +--- +title: Sending ether +--- + +The basic way of sending a simple transaction of ether with the console is as follows: +```js +> eth.sendTransaction({from:sender, to:receiver, value: amount}) +``` + +Using the built-in JavaScript, you can easily set variables to hold these values. For example: + +```js +> var sender = eth.accounts[0]; +> var receiver = eth.accounts[1]; +> var amount = web3.toWei(0.01, "ether") +``` + +Alternatively, you can compose a transaction in a single line with: + +```js +> eth.sendTransaction({from:eth.coinbase, to:eth.accounts[1], value: web3.toWei(0.05, "ether")}) +Please unlock account d1ade25ccd3d550a7eb532ac759cac7be09c2719. +Passphrase: +Account is now unlocked for this session. +'0xeeb66b211e7d9be55232ed70c2ebb1bcc5d5fd9ed01d876fac5cff45b5bf8bf4' +``` + +The resulting transaction is `0xeeb66b211e7d9be55232ed70c2ebb1bcc5d5fd9ed01d876fac5cff45b5bf8bf4` + +If the password was incorrect you will instead receive an error: +```js +error: could not unlock sender account +``` \ No newline at end of file
diff --git go-ethereum/docs/docs/_rpc/ns-admin.md celo/docs/docs/_rpc/ns-admin.md new file mode 100644 index 0000000000000000000000000000000000000000..6727913ff4416bbe3b3836022631a87ff7c31008 --- /dev/null +++ celo/docs/docs/_rpc/ns-admin.md @@ -0,0 +1,226 @@ +--- +title: admin Namespace +sort_key: C +--- + +The `admin` API gives you access to several non-standard RPC methods, which will allow you to have +a fine grained control over your Geth instance, including but not limited to network peer and RPC +endpoint management. + +* TOC +{:toc} + +### admin_addPeer + +The `addPeer` administrative method requests adding a new remote node to the list of tracked static +nodes. The node will try to maintain connectivity to these nodes at all times, reconnecting every +once in a while if the remote connection goes down. + +The method accepts a single argument, the [`enode`](https://github.com/ethereum/wiki/wiki/enode-url-format) +URL of the remote peer to start tracking and returns a `BOOL` indicating whether the peer was accepted +for tracking or some error occurred. + +| Client | Method invocation | +|:--------|------------------------------------------------| +| Go | `admin.AddPeer(url string) (bool, error)` | +| Console | `admin.addPeer(url)` | +| RPC | `{"method": "admin_addPeer", "params": [url]}` | + +#### Example + +```javascript +> admin.addPeer("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303") +true +``` + +### admin_datadir + +The `datadir` administrative property can be queried for the absolute path the running Geth node +currently uses to store all its databases. + +| Client | Method invocation | +|:--------|-----------------------------------| +| Go | `admin.Datadir() (string, error`) | +| Console | `admin.datadir` | +| RPC | `{"method": "admin_datadir"}` | + +#### Example + +```javascript +> admin.datadir +"/home/john/.ethereum" +``` + +### admin_nodeInfo + +The `nodeInfo` administrative property can be queried for all the information known about the running +Geth node at the networking granularity. These include general information about the node itself as a +participant of the [ÐΞVp2p](https://github.com/ethereum/wiki/wiki/%C3%90%CE%9EVp2p-Wire-Protocol) P2P +overlay protocol, as well as specialized information added by each of the running application protocols +(e.g. `eth`, `les`, `shh`, `bzz`). + +| Client | Method invocation | +|:--------|-------------------------------------------| +| Go | `admin.NodeInfo() (*p2p.NodeInfo, error`) | +| Console | `admin.nodeInfo` | +| RPC | `{"method": "admin_nodeInfo"}` | + +#### Example + +```javascript +> admin.nodeInfo +{ + enode: "enode://44826a5d6a55f88a18298bca4773fca5749cdc3a5c9f308aa7d810e9b31123f3e7c5fba0b1d70aac5308426f47df2a128a6747040a3815cc7dd7167d03be320d@[::]:30303", + id: "44826a5d6a55f88a18298bca4773fca5749cdc3a5c9f308aa7d810e9b31123f3e7c5fba0b1d70aac5308426f47df2a128a6747040a3815cc7dd7167d03be320d", + ip: "::", + listenAddr: "[::]:30303", + name: "Geth/v1.5.0-unstable/linux/go1.6", + ports: { + discovery: 30303, + listener: 30303 + }, + protocols: { + eth: { + difficulty: 17334254859343145000, + genesis: "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", + head: "0xb83f73fbe6220c111136aefd27b160bf4a34085c65ba89f24246b3162257c36a", + network: 1 + } + } +} +``` + +### admin_peers + +The `peers` administrative property can be queried for all the information known about the connected +remote nodes at the networking granularity. These include general information about the nodes themselves +as participants of the [ÐΞVp2p](https://github.com/ethereum/wiki/wiki/%C3%90%CE%9EVp2p-Wire-Protocol) +P2P overlay protocol, as well as specialized information added by each of the running application +protocols (e.g. `eth`, `les`, `shh`, `bzz`). + +| Client | Method invocation | +|:--------|------------------------------------------| +| Go | `admin.Peers() ([]*p2p.PeerInfo, error`) | +| Console | `admin.peers` | +| RPC | `{"method": "admin_peers"}` | + +#### Example + +```javascript +> admin.peers +[{ + caps: ["eth/61", "eth/62", "eth/63"], + id: "08a6b39263470c78d3e4f58e3c997cd2e7af623afce64656cfc56480babcea7a9138f3d09d7b9879344c2d2e457679e3655d4b56eaff5fd4fd7f147bdb045124", + name: "Geth/v1.5.0-unstable/linux/go1.5.1", + network: { + localAddress: "192.168.0.104:51068", + remoteAddress: "71.62.31.72:30303" + }, + protocols: { + eth: { + difficulty: 17334052235346465000, + head: "5794b768dae6c6ee5366e6ca7662bdff2882576e09609bf778633e470e0e7852", + version: 63 + } + } +}, /* ... */ { + caps: ["eth/61", "eth/62", "eth/63"], + id: "fcad9f6d3faf89a0908a11ddae9d4be3a1039108263b06c96171eb3b0f3ba85a7095a03bb65198c35a04829032d198759edfca9b63a8b69dc47a205d94fce7cc", + name: "Geth/v1.3.5-506c9277/linux/go1.4.2", + network: { + localAddress: "192.168.0.104:55968", + remoteAddress: "121.196.232.205:30303" + }, + protocols: { + eth: { + difficulty: 17335165914080772000, + head: "5794b768dae6c6ee5366e6ca7662bdff2882576e09609bf778633e470e0e7852", + version: 63 + } + } +}] +``` + +### admin_startRPC + +The `startRPC` administrative method starts an HTTP based [JSON RPC](http://www.jsonrpc.org/specification) +API webserver to handle client requests. All the parameters are optional: + +* `host`: network interface to open the listener socket on (defaults to `"localhost"`) +* `port`: network port to open the listener socket on (defaults to `8545`) +* `cors`: [cross-origin resource sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) header to use (defaults to `""`) +* `apis`: API modules to offer over this interface (defaults to `"eth,net,web3"`) + +The method returns a boolean flag specifying whether the HTTP RPC listener was opened or not. Please note, only one HTTP endpoint is allowed to be active at any time. + +| Client | Method invocation | +|:--------|-----------------------------------------------------------------------------------------------| +| Go | `admin.StartRPC(host *string, port *rpc.HexNumber, cors *string, apis *string) (bool, error)` | +| Console | `admin.startRPC(host, port, cors, apis)` | +| RPC | `{"method": "admin_startRPC", "params": [host, port, cors, apis]}` | + +#### Example + +```javascript +> admin.startRPC("127.0.0.1", 8545) +true +``` + +### admin_startWS + +The `startWS` administrative method starts an WebSocket based [JSON RPC](http://www.jsonrpc.org/specification) +API webserver to handle client requests. All the parameters are optional: + +* `host`: network interface to open the listener socket on (defaults to `"localhost"`) +* `port`: network port to open the listener socket on (defaults to `8546`) +* `cors`: [cross-origin resource sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) header to use (defaults to `""`) +* `apis`: API modules to offer over this interface (defaults to `"eth,net,web3"`) + +The method returns a boolean flag specifying whether the WebSocket RPC listener was opened or not. Please note, only one WebSocket endpoint is allowed to be active at any time. + +| Client | Method invocation | +|:--------|-----------------------------------------------------------------------------------------------| +| Go | `admin.StartWS(host *string, port *rpc.HexNumber, cors *string, apis *string) (bool, error)` | +| Console | `admin.startWS(host, port, cors, apis)` | +| RPC | `{"method": "admin_startWS", "params": [host, port, cors, apis]}` | + +#### Example + +```javascript +> admin.startWS("127.0.0.1", 8546) +true +``` + +### admin_stopRPC + +The `stopRPC` administrative method closes the currently open HTTP RPC endpoint. As the node can only have a single HTTP endpoint running, this method takes no parameters, returning a boolean whether the endpoint was closed or not. + +| Client | Method invocation | +|:--------|---------------------------------| +| Go | `admin.StopRPC() (bool, error`) | +| Console | `admin.stopRPC()` | +| RPC | `{"method": "admin_stopRPC"` | + +#### Example + +```javascript +> admin.stopRPC() +true +``` + +### admin_stopWS + +The `stopWS` administrative method closes the currently open WebSocket RPC endpoint. As the node can only have a single WebSocket endpoint running, this method takes no parameters, returning a boolean whether the endpoint was closed or not. + +| Client | Method invocation | +|:--------|--------------------------------| +| Go | `admin.StopWS() (bool, error`) | +| Console | `admin.stopWS()` | +| RPC | `{"method": "admin_stopWS"` | + +#### Example + +```javascript +> admin.stopWS() +true +```
diff --git go-ethereum/docs/docs/_rpc/ns-clique.md celo/docs/docs/_rpc/ns-clique.md new file mode 100644 index 0000000000000000000000000000000000000000..73b9a2540de2b6fcc6bffadcfb2b7f109733ccf2 --- /dev/null +++ celo/docs/docs/_rpc/ns-clique.md @@ -0,0 +1,129 @@ +--- +title: clique Namespace +sort_key: C +--- + +The `clique` API provides access to the state of the clique consensus engine. You can use +this API to manage signer votes and to check the health of a private network. + +* TOC +{:toc} + +### clique_getSnapshot + +Retrieves a snapshot of all clique state at a given block. + +| Client | Method invocation | +|:--------|------------------------------------------------------------| +| Console | `clique.getSnapshot(blockNumber)` | +| RPC | `{"method": "clique_getSnapsnot", "params": [blockNumber]}` | + +Example: + +```javascript +> clique.getSnapshot(5463755) +{ + hash: "0x018194fc50ca62d973e2f85cffef1e6811278ffd2040a4460537f8dbec3d5efc", + number: 5463755, + recents: { + 5463752: "0x42eb768f2244c8811c63729a21a3569731535f06", + 5463753: "0x6635f83421bf059cd8111f180f0727128685bae4", + 5463754: "0x7ffc57839b00206d1ad20c69a1981b489f772031", + 5463755: "0xb279182d99e65703f0076e4812653aab85fca0f0" + }, + signers: { + 0x42eb768f2244c8811c63729a21a3569731535f06: {}, + 0x6635f83421bf059cd8111f180f0727128685bae4: {}, + 0x7ffc57839b00206d1ad20c69a1981b489f772031: {}, + 0xb279182d99e65703f0076e4812653aab85fca0f0: {}, + 0xd6ae8250b8348c94847280928c79fb3b63ca453e: {}, + 0xda35dee8eddeaa556e4c26268463e26fb91ff74f: {}, + 0xfc18cbc391de84dbd87db83b20935d3e89f5dd91: {} + }, + tally: {}, + votes: [] +} +``` + +### clique_getSnapshotAtHash + +Retrieves the state snapshot at a given block. + +| Client | Method invocation | +|:--------|----------------------------------------------------------| +| Console | `clique.getSnapshotAtHash(blockHash)` | +| RPC | `{"method": "clique_getSnapshotAtHash", "params": [blockHash]}` | + +### clique_getSigners + +Retrieves the list of authorized signers at the specified block. + +| Client | Method invocation | +|:--------|------------------------------------------------------------ +| Console | `clique.getSigners(blockNumber)` | +| RPC | `{"method": "clique_getSigners", "params": [blockNumber]}` | + +### clique_proposals + +Returns the current proposals the node is voting on. + +| Client | Method invocation | +|:--------|------------------------------------------------| +| Console | `clique.proposals()` | +| RPC | `{"method": "clique_proposals", "params": []}` | + +### clique_propose + +Adds a new authorization proposal that the signer will attempt to push through. If the +`auth` parameter is true, the local signer votes for the given address to be included in +the set of authorized signers. With `auth` set to `false`, the vote is against the +address. + +| Client | Method invocation | +|:--------|-----------------------------------------------------------| +| Console | `clique.propose(address, auth)` | +| RPC | `{"method": "clique_propose", "params": [address, auth]}` | + +### clique_discard + +This method drops a currently running proposal. The signer will not cast +further votes (either for or against) the address. + +| Client | Method invocation | +|:--------|-----------------------------------------------------| +| Console | `clique.discard(address)` | +| RPC | `{"method": "clique_discard", "params": [address]}` | + +### clique_status + +This is a debugging method which returns statistics about signer activity +for the last 64 blocks. The returned object contains the following fields: + +- `inturnPercent`: percentage of blocks signed in-turn +- `sealerActivity`: object containing signer addresses and the number + of blocks signed by them +- `numBlocks`: number of blocks analyzed + +| Client | Method invocation | +|:--------|-----------------------------------------------------| +| Console | `clique.status()` | +| RPC | `{"method": "clique_status", "params": [}` | + +Example: + +``` +> clique.status() +{ + inturnPercent: 100, + numBlocks: 64, + sealerActivity: { + 0x42eb768f2244c8811c63729a21a3569731535f06: 9, + 0x6635f83421bf059cd8111f180f0727128685bae4: 9, + 0x7ffc57839b00206d1ad20c69a1981b489f772031: 9, + 0xb279182d99e65703f0076e4812653aab85fca0f0: 10, + 0xd6ae8250b8348c94847280928c79fb3b63ca453e: 9, + 0xda35dee8eddeaa556e4c26268463e26fb91ff74f: 9, + 0xfc18cbc391de84dbd87db83b20935d3e89f5dd91: 9 + } +} +```
diff --git go-ethereum/docs/docs/_rpc/ns-debug.md celo/docs/docs/_rpc/ns-debug.md new file mode 100644 index 0000000000000000000000000000000000000000..6ba8fc8b738a1779a8c081cb12f24a6a5e5ed39d --- /dev/null +++ celo/docs/docs/_rpc/ns-debug.md @@ -0,0 +1,564 @@ +--- +title: debug Namespace +sort_key: C +--- + +The `debug` API gives you access to several non-standard RPC methods, which will allow you +to inspect, debug and set certain debugging flags during runtime. + +* TOC +{:toc} + +### debug_backtraceAt + +Sets the logging backtrace location. When a backtrace location +is set and a log message is emitted at that location, the stack +of the goroutine executing the log statement will be printed to stderr. + +The location is specified as `<filename>:<line>`. + +| Client | Method invocation | +|:--------|-------------------------------------------------------| +| Console | `debug.backtraceAt(string)` | +| RPC | `{"method": "debug_backtraceAt", "params": [string]}` | + +Example: + +``` javascript +> debug.backtraceAt("server.go:443") +``` + +### debug_blockProfile + +Turns on block profiling for the given duration and writes +profile data to disk. It uses a profile rate of 1 for most +accurate information. If a different rate is desired, set +the rate and write the profile manually using +`debug_writeBlockProfile`. + +| Client | Method invocation | +|:--------|----------------------------------------------------------------| +| Console | `debug.blockProfile(file, seconds)` | +| RPC | `{"method": "debug_blockProfile", "params": [string, number]}` | + +### debug_cpuProfile + +Turns on CPU profiling for the given duration and writes +profile data to disk. + +| Client | Method invocation | +|:--------|--------------------------------------------------------------| +| Console | `debug.cpuProfile(file, seconds)` | +| RPC | `{"method": "debug_cpuProfile", "params": [string, number]}` | + +### debug_dumpBlock + +Retrieves the state that corresponds to the block number and returns a list of accounts (including +storage and code). + +| Client | Method invocation | +|:--------|-------------------------------------------------------| +| Go | `debug.DumpBlock(number uint64) (state.World, error)` | +| Console | `debug.traceBlockByHash(number, [options])` | +| RPC | `{"method": "debug_dumpBlock", "params": [number]}` | + +#### Example + +```javascript +> debug.dumpBlock(10) +{ + fff7ac99c8e4feb60c9750054bdc14ce1857f181: { + balance: "49358640978154672", + code: "", + codeHash: "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + nonce: 2, + root: "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + storage: {} + }, + fffbca3a38c3c5fcb3adbb8e63c04c3e629aafce: { + balance: "3460945928", + code: "", + codeHash: "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + nonce: 657, + root: "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + storage: {} + } + }, + root: "19f4ed94e188dd9c7eb04226bd240fa6b449401a6c656d6d2816a87ccaf206f1" +} +``` + +### debug_gcStats + +Returns GC statistics. + +See https://golang.org/pkg/runtime/debug/#GCStats for information about +the fields of the returned object. + +| Client | Method invocation | +|:--------|---------------------------------------------------| +| Console | `debug.gcStats()` | +| RPC | `{"method": "debug_gcStats", "params": []}` | + +### debug_getBlockRlp + +Retrieves and returns the RLP encoded block by number. + +| Client | Method invocation | +|:--------|-------------------------------------------------------| +| Go | `debug.GetBlockRlp(number uint64) (string, error)` | +| Console | `debug.getBlockRlp(number, [options])` | +| RPC | `{"method": "debug_getBlockRlp", "params": [number]}` | + +References: [RLP](https://github.com/ethereum/wiki/wiki/RLP) + +### debug_goTrace + +Turns on Go runtime tracing for the given duration and writes +trace data to disk. + +| Client | Method invocation | +|:--------|-----------------------------------------------------------| +| Console | `debug.goTrace(file, seconds)` | +| RPC | `{"method": "debug_goTrace", "params": [string, number]}` | + +### debug_memStats + +Returns detailed runtime memory statistics. + +See https://golang.org/pkg/runtime/#MemStats for information about +the fields of the returned object. + +| Client | Method invocation | +|:--------|---------------------------------------------------| +| Console | `debug.memStats()` | +| RPC | `{"method": "debug_memStats", "params": []}` | + +### debug_seedHash + +Fetches and retrieves the seed hash of the block by number + +| Client | Method invocation | +|:--------|----------------------------------------------------| +| Go | `debug.SeedHash(number uint64) (string, error)` | +| Console | `debug.seedHash(number, [options])` | +| RPC | `{"method": "debug_seedHash", "params": [number]}` | + +### debug_setHead + +Sets the current head of the local chain by block number. **Note**, this is a +destructive action and may severely damage your chain. Use with *extreme* caution. + +| Client | Method invocation | +|:--------|---------------------------------------------------| +| Go | `debug.SetHead(number uint64)` | +| Console | `debug.setHead(number)` | +| RPC | `{"method": "debug_setHead", "params": [number]}` | + +References: +[Ethash](https://github.com/ethereum/wiki/wiki/Mining#the-algorithm) + +### debug_setBlockProfileRate + +Sets the rate (in samples/sec) of goroutine block profile +data collection. A non-zero rate enables block profiling, +setting it to zero stops the profile. Collected profile data +can be written using `debug_writeBlockProfile`. + +| Client | Method invocation | +|:--------|---------------------------------------------------------------| +| Console | `debug.setBlockProfileRate(rate)` | +| RPC | `{"method": "debug_setBlockProfileRate", "params": [number]}` | + +### debug_stacks + +Returns a printed representation of the stacks of all goroutines. +Note that the web3 wrapper for this method takes care of the printing +and does not return the string. + +| Client | Method invocation | +|:--------|---------------------------------------------------| +| Console | `debug.stacks()` | +| RPC | `{"method": "debug_stacks", "params": []}` | + +### debug_startCPUProfile + +Turns on CPU profiling indefinitely, writing to the given file. + +| Client | Method invocation | +|:--------|-----------------------------------------------------------| +| Console | `debug.startCPUProfile(file)` | +| RPC | `{"method": "debug_startCPUProfile", "params": [string]}` | + +### debug_startGoTrace + +Starts writing a Go runtime trace to the given file. + +| Client | Method invocation | +|:--------|--------------------------------------------------------| +| Console | `debug.startGoTrace(file)` | +| RPC | `{"method": "debug_startGoTrace", "params": [string]}` | + +### debug_stopCPUProfile + +Stops an ongoing CPU profile. + +| Client | Method invocation | +|:--------|----------------------------------------------------| +| Console | `debug.stopCPUProfile()` | +| RPC | `{"method": "debug_stopCPUProfile", "params": []}` | + +### debug_stopGoTrace + +Stops writing the Go runtime trace. + +| Client | Method invocation | +|:--------|---------------------------------------------------| +| Console | `debug.startGoTrace(file)` | +| RPC | `{"method": "debug_stopGoTrace", "params": []}` | + +### debug_traceBlock + +The `traceBlock` method will return a full stack trace of all invoked opcodes of all transaction +that were included included in this block. **Note**, the parent of this block must be present or +it will fail. + +| Client | Method invocation | +|:--------|--------------------------------------------------------------------------| +| Go | `debug.TraceBlock(blockRlp []byte, config. *vm.Config) BlockTraceResult` | +| Console | `debug.traceBlock(tblockRlp, [options])` | +| RPC | `{"method": "debug_traceBlock", "params": [blockRlp, {}]}` | + +References: +[RLP](https://github.com/ethereum/wiki/wiki/RLP) + +#### Example + +```javascript +> debug.traceBlock("0xblock_rlp") +{ + gas: 85301, + returnValue: "", + structLogs: [{ + depth: 1, + error: "", + gas: 162106, + gasCost: 3, + memory: null, + op: "PUSH1", + pc: 0, + stack: [], + storage: {} + }, + /* snip */ + { + depth: 1, + error: "", + gas: 100000, + gasCost: 0, + memory: ["0000000000000000000000000000000000000000000000000000000000000006", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000060"], + op: "STOP", + pc: 120, + stack: ["00000000000000000000000000000000000000000000000000000000d67cbec9"], + storage: { + 0000000000000000000000000000000000000000000000000000000000000004: "8241fa522772837f0d05511f20caa6da1d5a3209000000000000000400000001", + 0000000000000000000000000000000000000000000000000000000000000006: "0000000000000000000000000000000000000000000000000000000000000001", + f652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f: "00000000000000000000000002e816afc1b5c0f39852131959d946eb3b07b5ad" + } + }] +``` + +### debug_traceBlockByNumber + +Similar to [debug_traceBlock](#debug_traceblock), `traceBlockByNumber` accepts a block number and will replay the +block that is already present in the database. + +| Client | Method invocation | +|:--------|--------------------------------------------------------------------------------| +| Go | `debug.TraceBlockByNumber(number uint64, config. *vm.Config) BlockTraceResult` | +| Console | `debug.traceBlockByNumber(number, [options])` | +| RPC | `{"method": "debug_traceBlockByNumber", "params": [number, {}]}` | + +References: +[RLP](https://github.com/ethereum/wiki/wiki/RLP) + +### debug_traceBlockByHash + +Similar to [debug_traceBlock](#debug_traceblock), `traceBlockByHash` accepts a block hash and will replay the +block that is already present in the database. + +| Client | Method invocation | +|:--------|---------------------------------------------------------------------------------| +| Go | `debug.TraceBlockByHash(hash common.Hash, config. *vm.Config) BlockTraceResult` | +| Console | `debug.traceBlockByHash(hash, [options])` | +| RPC | `{"method": "debug_traceBlockByHash", "params": [hash {}]}` | + +References: +[RLP](https://github.com/ethereum/wiki/wiki/RLP) + +### debug_traceBlockFromFile + +Similar to [debug_traceBlock](#debug_traceblock), `traceBlockFromFile` accepts a file containing the RLP of the block. + +| Client | Method invocation | +|:--------|----------------------------------------------------------------------------------| +| Go | `debug.TraceBlockFromFile(fileName string, config. *vm.Config) BlockTraceResult` | +| Console | `debug.traceBlockFromFile(fileName, [options])` | +| RPC | `{"method": "debug_traceBlockFromFile", "params": [fileName, {}]}` | + +References: +[RLP](https://github.com/ethereum/wiki/wiki/RLP) + +### debug_standardTraceBlockToFile + + +When JS-based tracing (see below) was first implemented, the intended usecase was to enable long-running tracers that could stream results back via a subscription channel. +This method works a bit differently. (For full details, see [PR](https://github.com/ethereum/go-ethereum/pull/17914)) + +- It streams output to disk during the execution, to not blow up the memory usage on the node +- It uses `jsonl` as output format (to allow streaming) +- Uses a cross-client standardized output, so called 'standard json' + * Uses `op` for string-representation of opcode, instead of `op`/`opName` for numeric/string, and other simlar small differences. + * has `refund` + * Represents memory as a contiguous chunk of data, as opposed to a list of `32`-byte segments like `debug_traceTransaction` + +This means that this method is only 'useful' for callers who control the node -- at least sufficiently to be able to read the artefacts from the filesystem after the fact. + +The method can be used to dump a certain transaction out of a given block: +``` +> debug.standardTraceBlockToFile("0x0bbe9f1484668a2bf159c63f0cf556ed8c8282f99e3ffdb03ad2175a863bca63", {txHash:"0x4049f61ffbb0747bb88dc1c85dd6686ebf225a3c10c282c45a8e0c644739f7e9", disableMemory:true}) +["/tmp/block_0x0bbe9f14-14-0x4049f61f-099048234"] +``` +Or all txs from a block: +``` +> debug.standardTraceBlockToFile("0x0bbe9f1484668a2bf159c63f0cf556ed8c8282f99e3ffdb03ad2175a863bca63", {disableMemory:true}) +["/tmp/block_0x0bbe9f14-0-0xb4502ea7-409046657", "/tmp/block_0x0bbe9f14-1-0xe839be8f-954614764", "/tmp/block_0x0bbe9f14-2-0xc6e2052f-542255195", "/tmp/block_0x0bbe9f14-3-0x01b7f3fe-209673214", "/tmp/block_0x0bbe9f14-4-0x0f290422-320999749", "/tmp/block_0x0bbe9f14-5-0x2dc0fb80-844117472", "/tmp/block_0x0bbe9f14-6-0x35542da1-256306111", "/tmp/block_0x0bbe9f14-7-0x3e199a08-086370834", "/tmp/block_0x0bbe9f14-8-0x87778b88-194603593", "/tmp/block_0x0bbe9f14-9-0xbcb081ba-629580052", "/tmp/block_0x0bbe9f14-10-0xc254381a-578605923", "/tmp/block_0x0bbe9f14-11-0xcc434d58-405931366", "/tmp/block_0x0bbe9f14-12-0xce61967d-874423181", "/tmp/block_0x0bbe9f14-13-0x05a20b35-267153288", "/tmp/block_0x0bbe9f14-14-0x4049f61f-606653767", "/tmp/block_0x0bbe9f14-15-0x46d473d2-614457338", "/tmp/block_0x0bbe9f14-16-0x35cf5500-411906321", "/tmp/block_0x0bbe9f14-17-0x79222961-278569788", "/tmp/block_0x0bbe9f14-18-0xad84e7b1-095032683", "/tmp/block_0x0bbe9f14-19-0x4bd48260-019097038", "/tmp/block_0x0bbe9f14-20-0x1517411d-292624085", "/tmp/block_0x0bbe9f14-21-0x6857e350-971385904", "/tmp/block_0x0bbe9f14-22-0xbe3ae2ca-236639695"] + +``` +Files are created in a temp-location, with the naming standard `block_<blockhash:4>-<txindex>-<txhash:4>-<random suffix>`. Each opcode immediately streams to file, with no in-geth buffering aside from whatever buffering the os normally does. + +On the server side, it also adds some more info when regenerating historical state, namely, the reexec-number if `required historical state is not avaiable` is encountered, so a user can experiment with increasing that setting. It also prints out the remaining block until it reaches target: + +``` +INFO [10-15|13:48:25.263] Regenerating historical state block=2385959 target=2386012 remaining=53 elapsed=3m30.990537767s +INFO [10-15|13:48:33.342] Regenerating historical state block=2386012 target=2386012 remaining=0 elapsed=3m39.070073163s +INFO [10-15|13:48:33.343] Historical state regenerated block=2386012 elapsed=3m39.070454362s nodes=10.03mB preimages=652.08kB +INFO [10-15|13:48:33.352] Wrote trace file=/tmp/block_0x14490c57-0-0xfbbd6d91-715824834 +INFO [10-15|13:48:33.352] Wrote trace file=/tmp/block_0x14490c57-1-0x71076194-187462969 +INFO [10-15|13:48:34.421] Wrote trace file=/tmp/block_0x14490c57-2-0x3f4263fe-056924484 +``` + +The `options` is as follows: +``` +type StdTraceConfig struct { + *vm.LogConfig + Reexec *uint64 + TxHash *common.Hash +} +``` + +### debug_standardTraceBadBlockToFile + +This method is similar to `debug_standardTraceBlockToFile`, but can be used to obtain info about a block which has been _rejected_ as invalid (for some reason). + + +### debug_traceTransaction + +**OBS** In most scenarios, `debug.standardTraceBlockToFile` is better suited for tracing! + +The `traceTransaction` debugging method will attempt to run the transaction in the exact same manner +as it was executed on the network. It will replay any transaction that may have been executed prior +to this one before it will finally attempt to execute the transaction that corresponds to the given +hash. + +In addition to the hash of the transaction you may give it a secondary *optional* argument, which +specifies the options for this specific call. The possible options are: + +* `disableStorage`: `BOOL`. Setting this to true will disable storage capture (default = false). +* `disableMemory`: `BOOL`. Setting this to true will disable memory capture (default = false). +* `disableStack`: `BOOL`. Setting this to true will disable stack capture (default = false). +* `tracer`: `STRING`. Setting this will enable JavaScript-based transaction tracing, described below. If set, the previous four arguments will be ignored. +* `timeout`: `STRING`. Overrides the default timeout of 5 seconds for JavaScript-based tracing calls. Valid values are described [here](https://golang.org/pkg/time/#ParseDuration). + +| Client | Method invocation | +|:--------|----------------------------------------------------------------------------------------------| +| Go | `debug.TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ExecutionResurt, error)` | +| Console | `debug.traceTransaction(txHash, [options])` | +| RPC | `{"method": "debug_traceTransaction", "params": [txHash, {}]}` | + +#### Example + +```javascript +> debug.traceTransaction("0x2059dd53ecac9827faad14d364f9e04b1d5fe5b506e3acc886eff7a6f88a696a") +{ + gas: 85301, + returnValue: "", + structLogs: [{ + depth: 1, + error: "", + gas: 162106, + gasCost: 3, + memory: null, + op: "PUSH1", + pc: 0, + stack: [], + storage: {} + }, + /* snip */ + { + depth: 1, + error: "", + gas: 100000, + gasCost: 0, + memory: ["0000000000000000000000000000000000000000000000000000000000000006", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000060"], + op: "STOP", + pc: 120, + stack: ["00000000000000000000000000000000000000000000000000000000d67cbec9"], + storage: { + 0000000000000000000000000000000000000000000000000000000000000004: "8241fa522772837f0d05511f20caa6da1d5a3209000000000000000400000001", + 0000000000000000000000000000000000000000000000000000000000000006: "0000000000000000000000000000000000000000000000000000000000000001", + f652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f: "00000000000000000000000002e816afc1b5c0f39852131959d946eb3b07b5ad" + } + }] +``` + + +#### JavaScript-based tracing +Specifying the `tracer` option in the second argument enables JavaScript-based tracing. In this mode, `tracer` is interpreted as a JavaScript expression that is expected to evaluate to an object with (at least) two methods, named `step` and `result`. + +`step`is a function that takes two arguments, log and db, and is called for each step of the EVM, or when an error occurs, as the specified transaction is traced. + +`log` has the following fields: + + - `pc`: Number, the current program counter + - `op`: Object, an OpCode object representing the current opcode + - `gas`: Number, the amount of gas remaining + - `gasPrice`: Number, the cost in wei of each unit of gas + - `memory`: Object, a structure representing the contract's memory space + - `stack`: array[big.Int], the EVM execution stack + - `depth`: The execution depth + - `account`: The address of the account executing the current operation + - `err`: If an error occured, information about the error + +If `err` is non-null, all other fields should be ignored. + +For efficiency, the same `log` object is reused on each execution step, updated with current values; make sure to copy values you want to preserve beyond the current call. For instance, this step function will not work: + + function(log) { + this.logs.append(log); + } + +But this step function will: + + function(log) { + this.logs.append({gas: log.gas, pc: log.pc, ...}); + } + +`log.op` has the following methods: + + - `isPush()` - returns true iff the opcode is a PUSHn + - `toString()` - returns the string representation of the opcode + - `toNumber()` - returns the opcode's number + +`log.memory` has the following methods: + + - `slice(start, stop)` - returns the specified segment of memory as a byte slice + - `length()` - returns the length of the memory + +`log.stack` has the following methods: + + - `peek(idx)` - returns the idx-th element from the top of the stack (0 is the topmost element) as a big.Int + - `length()` - returns the number of elements in the stack + +`db` has the following methods: + + - `getBalance(address)` - returns a `big.Int` with the specified account's balance + - `getNonce(address)` - returns a Number with the specified account's nonce + - `getCode(address)` - returns a byte slice with the code for the specified account + - `getState(address, hash)` - returns the state value for the specified account and the specified hash + - `exists(address)` - returns true if the specified address exists + +The second function, 'result', takes no arguments, and is expected to return a JSON-serializable value to return to the RPC caller. + +If the step function throws an exception or executes an illegal operation at any point, it will not be called on any further VM steps, and the error will be returned to the caller. + +Note that several values are Golang big.Int objects, not JavaScript numbers or JS bigints. As such, they have the same interface as described in the godocs. Their default serialization to JSON is as a Javascript number; to serialize large numbers accurately call `.String()` on them. For convenience, `big.NewInt(x)` is provided, and will convert a uint to a Go BigInt. + +Usage example, returns the top element of the stack at each CALL opcode only: + + debug.traceTransaction(txhash, {tracer: '{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == "CALL") this.data.push(log.stack.peek(0)); }, result: function() { return this.data; }}'}); + +### debug_verbosity + +Sets the logging verbosity ceiling. Log messages with level +up to and including the given level will be printed. + +The verbosity of individual packages and source files +can be raised using `debug_vmodule`. + +| Client | Method invocation | +|:--------|---------------------------------------------------| +| Console | `debug.verbosity(level)` | +| RPC | `{"method": "debug_vmodule", "params": [number]}` | + +### debug_vmodule + +Sets the logging verbosity pattern. + +| Client | Method invocation | +|:--------|---------------------------------------------------| +| Console | `debug.vmodule(string)` | +| RPC | `{"method": "debug_vmodule", "params": [string]}` | + + +#### Examples + +If you want to see messages from a particular Go package (directory) +and all subdirectories, use: + +``` javascript +> debug.vmodule("eth/*=6") +``` + +If you want to restrict messages to a particular package (e.g. p2p) +but exclude subdirectories, use: + +``` javascript +> debug.vmodule("p2p=6") +``` + +If you want to see log messages from a particular source file, use + +``` javascript +> debug.vmodule("server.go=6") +``` + +You can compose these basic patterns. If you want to see all +output from peer.go in a package below eth (eth/peer.go, +eth/downloader/peer.go) as well as output from package p2p +at level <= 5, use: + +``` javascript +debug.vmodule("eth/*/peer.go=6,p2p=5") +``` + +### debug_writeBlockProfile + +Writes a goroutine blocking profile to the given file. + +| Client | Method invocation | +|:--------|-------------------------------------------------------------| +| Console | `debug.writeBlockProfile(file)` | +| RPC | `{"method": "debug_writeBlockProfile", "params": [string]}` | + +### debug_writeMemProfile + +Writes an allocation profile to the given file. +Note that the profiling rate cannot be set through the API, +it must be set on the command line using the `--memprofilerate` +flag. + +| Client | Method invocation | +|:--------|-------------------------------------------------------------| +| Console | `debug.writeMemProfile(file string)` | +| RPC | `{"method": "debug_writeBlockProfile", "params": [string]}` |
diff --git go-ethereum/docs/docs/_rpc/ns-eth.md celo/docs/docs/_rpc/ns-eth.md new file mode 100644 index 0000000000000000000000000000000000000000..a0a19f23c3d1dc1a4929aa205b9ebe1d6f9b73e5 --- /dev/null +++ celo/docs/docs/_rpc/ns-eth.md @@ -0,0 +1,186 @@ +--- +title: eth Namespace +sort_key: C +--- + +Geth provides several extensions to the standard "eth" JSON-RPC namespace. + +* TOC +{:toc} + +### eth_subscribe, eth_unsubscribe + +These methods are used for real-time events through subscriptions. See the [subscription +documentation](./pubsub) for more information. + +### eth_call + +Executes a new message call immediately, without creating a transaction on the block +chain. The `eth_call` method can be used to query internal contract state, to execute +validations coded into a contract or even to test what the effect of a transaction would +be without running it live. + +#### Parameters + +The method takes 3 parameters: an unsigned transaction object to execute in read-only +mode; the block number to execute the call against; and an optional state override-set to +allow executing the call against a modified chain state. + +##### 1. `Object` - Transaction call object + +The *transaction call object* is mandatory and contains all the necessary parameters to +execute a read-only EVM contract method. + +| Field | Type | Bytes | Optional | Description | +|:-----------|:-----------|:------|:---------|:------------| +| `from` | `Address` | 20 | Yes | Address the transaction is simulated to have been sent from. Defaults to first account in the local keystore or the `0x00..0` address if no local accounts are available. | +| `to` | `Address` | 20 | No | Address the transaction is sent to. | +| `gas` | `Quantity` | <8 | Yes | Maximum gas allowance for the code execution to avoid infinite loops. Defaults to `2^63` or whatever value the node operator specified via `--rpc.gascap`. | +| `gasPrice` | `Quantity` | <32 | Yes | Number of `wei` to simulate paying for each unit of gas during execution. Defaults to `1 gwei`. | +| `value` | `Quantity` | <32 | Yes | Amount of `wei` to simulate sending along with the transaction. Defaults to `0`. | +| `data` | `Binary` | any | Yes | Binary data to send to the target contract. Generally the 4 byte hash of the method signature followed by the ABI encoded parameters. For details please see the [Ethereum Contract ABI](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI). | + +Example: + +```json +{ + "from": "0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3", + "to": "0xebe8efa441b9302a0d7eaecc277c09d20d684540", + "gas": "0x1bd7c", + "data": "0xd459fc46000000000000000000000000000000000000000000000000000000000046c650dbb5e8cb2bac4d2ed0b1e6475d37361157738801c494ca482f96527eb48f9eec488c2eba92d31baeccfb6968fad5c21a3df93181b43b4cf253b4d572b64172ef000000000000000000000000000000000000000000000000000000000000008c00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002b85c0c828d7a98633b4e1b65eac0c017502da909420aeade9a280675013df36bdc71cffdf420cef3d24ba4b3f9b980bfbb26bd5e2dcf7795b3519a3fd22ffbb2000000000000000000000000000000000000000000000000000000000000000238fb6606dc2b5e42d00c653372c153da8560de77bd9afaba94b4ab6e4aa11d565d858c761320dbf23a94018d843772349bd9d92301b0ca9ca983a22d86a70628", +} +``` + +##### 2. `Quantity | Tag` - Block number or the string `latest` or `pending` + +The *block number* is mandatory and defines the context (state) against which the +specified transaction should be executed. It is not possible to execute calls against +reorged blocks; or blocks older than 128 (unless the node is an archive node). + +##### 3. `Object` - State override set + +The *state override set* is an optional address-to-state mapping, where each entry +specifies some state to be ephemerally overridden prior to executing the call. Each +address maps to an object containing: + +| Field | Type | Bytes | Optional | Description | +|:------------|:-----------|:------|:---------|:------------| +| `balance` | `Quantity` | <32 | Yes | Fake balance to set for the account before executing the call. | +| `nonce` | `Quantity` | <8 | Yes | Fake nonce to set for the account before executing the call. | +| `code` | `Binary` | any | Yes | Fake EVM bytecode to inject into the account before executing the call. | +| `state` | `Object` | any | Yes | Fake key-value mapping to override **all** slots in the account storage before executing the call. | +| `stateDiff` | `Object` | any | Yes | Fake key-value mapping to override **individual** slots in the account storage before executing the call. | + +The goal of the *state override set* is manyfold: + + * It can be used by DApps to reduce the amount of contract code needed to be deployed on + chain. Code that simply returns internal state or does pre-defined validations can be + kept off chain and fed to the node on-demand. + * It can be used for smart contract analysis by extending the code deployed on chain with + custom methods and invoking them. This avoids having to download and reconstruct the + entire state in a sandbox to run custom code against. + * It can be used to debug smart contracts in an already deployed large suite of contracts + by selectively overriding some code or state and seeing how execution changes. + Specialized tooling will probably be necessary. + +Example: + +```json +{ + "0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3": { + "balance": "0xde0b6b3a7640000" + }, + "0xebe8efa441b9302a0d7eaecc277c09d20d684540": { + "code": "0x...", + "state": { + "" + } + } +} +``` + +#### Return Values + +The method returns a single `Binary` consisting the return value of the executed contract +call. + +#### Simple example + +With a synced Rinkeby node with RPC exposed on localhost (`geth --rinkeby --rpc`) we can +make a call against the [Checkpoint +Oracle](https://rinkeby.etherscan.io/address/0xebe8efa441b9302a0d7eaecc277c09d20d684540) +to retrieve the list of administrators: + +``` +$ curl --data '{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},"latest"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8545 +``` + +And the result is an Ethereum ABI encoded list of accounts: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "result": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000d9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f300000000000000000000000078d1ad571a1a09d60d9bbf25894b44e4c8859595000000000000000000000000286834935f4a8cfb4ff4c77d5770c2775ae2b0e7000000000000000000000000b86e2b0ab5a4b1373e40c51a7c712c70ba2f9f8e" +} +``` + +Just for the sake of completeness, decoded the response is: + +``` +0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3, +0x78d1ad571a1a09d60d9bbf25894b44e4c8859595, +0x286834935f4a8cfb4ff4c77d5770c2775ae2b0e7, +0xb86e2b0ab5a4b1373e40c51a7c712c70ba2f9f8e +``` + +#### Override example + +The above *simple example* showed how to call a method already exposed by an on-chain +smart contract. What if we want to access some data not exposed by it? + +We can gut out the +[original](https://github.com/ethereum/go-ethereum/blob/master/contracts/checkpointoracle/contract/oracle.sol) +checkpoint oracle contract with one that retains the same fields (to retain the same +storage layout), but one that includes a different method set: + +``` +pragma solidity ^0.5.10; + +contract CheckpointOracle { + mapping(address => bool) admins; + address[] adminList; + uint64 sectionIndex; + uint height; + bytes32 hash; + uint sectionSize; + uint processConfirms; + uint threshold; + + function VotingThreshold() public view returns (uint) { + return threshold; + } +} +``` + +With a synced Rinkeby node with RPC exposed on localhost (`geth --rinkeby --rpc`) we can +make a call against the live [Checkpoint +Oracle](https://rinkeby.etherscan.io/address/0xebe8efa441b9302a0d7eaecc277c09d20d684540), +but override its byte code with our own version that has an accessor for the voting +threshold field: + +``` +$ curl --data '{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x0be5b6ba"}, "latest", {"0xebe8efa441b9302a0d7eaecc277c09d20d684540": {"code":"0x6080604052348015600f57600080fd5b506004361060285760003560e01c80630be5b6ba14602d575b600080fd5b60336045565b60408051918252519081900360200190f35b6007549056fea265627a7a723058206f26bd0433456354d8d1228d8fe524678a8aeeb0594851395bdbd35efc2a65f164736f6c634300050a0032"}}],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8545 +``` + +And the result is the Ethereum ABI encoded threshold number: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "result": "0x0000000000000000000000000000000000000000000000000000000000000002" +} +``` + +Just for the sake of completeness, decoded the response is: `2`.
diff --git go-ethereum/docs/docs/_rpc/ns-les.md celo/docs/docs/_rpc/ns-les.md new file mode 100644 index 0000000000000000000000000000000000000000..457efc217507ff3ac7bb7fcb68d02223fb599e3f --- /dev/null +++ celo/docs/docs/_rpc/ns-les.md @@ -0,0 +1,318 @@ +--- +title: les Namespace +sort_key: C +--- + +The `les` API allows you to manage LES server settings, including client parameters and payment settings for prioritized clients. It also provides functions to query checkpoint information in both server and client mode. + +* TOC +{:toc} + +### les_serverInfo + +Get information about currently connected and total/individual allowed connection capacity. + +| Client | Method invocation | +|:--------|-------------------------------------------------------------| +| Go | `les.ServerInfo() map[string]interface{}` | +| Console | `les.serverInfo()` | +| RPC | `{"method": "les_serverInfo", "params": []}` | + +#### Example + +```javascript +> les.serverInfo +{ + freeClientCapacity: 16000, + maximumCapacity: 1600000, + minimumCapacity: 16000, + priorityConnectedCapacity: 180000, + totalCapacity: 1600000, + totalConnectedCapacity: 180000 +} +``` + +### les_clientInfo + +Get individual client information (connection, balance, pricing) on the specified list of clients or for all connected clients if the ID list is empty. + +| Client | Method invocation | +|:--------|---------------------------------------------------------------------------| +| Go | `les.ClientInfo(ids []enode.ID) map[enode.ID]map[string]interface{}` | +| Console | `les.clientInfo([id, ...])` | +| RPC | `{"method": "les_clientInfo", "params": [[id, ...]]}` | + +#### Example + +```javascript +> les.clientInfo([]) +{ + 37078bf8ea160a2b3d129bb4f3a930ce002356f83b820f467a07c1fe291531ea: { + capacity: 16000, + connectionTime: 11225.335901136, + isConnected: true, + pricing/balance: 998266395881, + pricing/balanceMeta: "", + pricing/negBalance: 501657912857, + priority: true + }, + 6a47fe7bb23fd335df52ef1690f37ab44265a537b1d18eb616a3e77f898d9e77: { + capacity: 100000, + connectionTime: 9874.839293082, + isConnected: true, + pricing/balance: 2908840710198, + pricing/balanceMeta: "qwerty", + pricing/negBalance: 206242704507, + priority: true + }, + 740c78f7d914e5c763731bc751b513fc2388ffa0b47db080ded3e8b305e68c75: { + capacity: 16000, + connectionTime: 3089.286712188, + isConnected: true, + pricing/balance: 998266400174, + pricing/balanceMeta: "", + pricing/negBalance: 55135348863, + priority: true + }, + 9985ade55b515f79f64274bf2ae440ca8c433cfb0f283fb6010bf46f796b2a3b: { + capacity: 16000, + connectionTime: 11479.335479545, + isConnected: true, + pricing/balance: 998266452203, + pricing/balanceMeta: "", + pricing/negBalance: 564116425655, + priority: true + }, + ce65ada2c3e17d6da00cec0b3cc4c8ed5e74428b60f42fa287eaaec8cca62544: { + capacity: 16000, + connectionTime: 7095.794385419, + isConnected: true, + pricing/balance: 998266448492, + pricing/balanceMeta: "", + pricing/negBalance: 214617753229, + priority: true + }, + e1495ceb6db842f3ee66428d4bb7f4a124b2b17111dae35d141c3d568b869ef1: { + capacity: 16000, + connectionTime: 8614.018237937, + isConnected: true, + pricing/balance: 998266391796, + pricing/balanceMeta: "", + pricing/negBalance: 185964891797, + priority: true + } +} +``` + +### les_priorityClientInfo + +Get individual client information on clients with a positive balance in the specified ID range, `start` included, `stop` excluded. If `stop` is zero then results are returned until the last existing balance entry. `maxCount` limits the number of returned results. If the count limit is reached but there are more IDs in the range then the first missing ID is included in the result with an empty value assigned to it. + +| Client | Method invocation | +|:--------|----------------------------------------------------------------------------------------------------| +| Go | `les.PriorityClientInfo(start, stop enode.ID, maxCount int) map[enode.ID]map[string]interface{}` | +| Console | `les.priorityClientInfo(id, id, number)` | +| RPC | `{"method": "les_priorityClientInfo", "params": [id, id, number]}` | + +#### Example + +```javascript +> les.priorityClientInfo("0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", 100) +{ + 37078bf8ea160a2b3d129bb4f3a930ce002356f83b820f467a07c1fe291531ea: { + capacity: 16000, + connectionTime: 11128.247204027, + isConnected: true, + pricing/balance: 999819815030, + pricing/balanceMeta: "", + pricing/negBalance: 501657912857, + priority: true + }, + 6a47fe7bb23fd335df52ef1690f37ab44265a537b1d18eb616a3e77f898d9e77: { + capacity: 100000, + connectionTime: 9777.750592047, + isConnected: true, + pricing/balance: 2918549830576, + pricing/balanceMeta: "qwerty", + pricing/negBalance: 206242704507, + priority: true + }, + 740c78f7d914e5c763731bc751b513fc2388ffa0b47db080ded3e8b305e68c75: { + capacity: 16000, + connectionTime: 2992.198001116, + isConnected: true, + pricing/balance: 999819845102, + pricing/balanceMeta: "", + pricing/negBalance: 55135348863, + priority: true + }, + 9985ade55b515f79f64274bf2ae440ca8c433cfb0f283fb6010bf46f796b2a3b: { + capacity: 16000, + connectionTime: 11382.246766963, + isConnected: true, + pricing/balance: 999819871598, + pricing/balanceMeta: "", + pricing/negBalance: 564116425655, + priority: true + }, + ce65ada2c3e17d6da00cec0b3cc4c8ed5e74428b60f42fa287eaaec8cca62544: { + capacity: 16000, + connectionTime: 6998.705683407, + isConnected: true, + pricing/balance: 999819882177, + pricing/balanceMeta: "", + pricing/negBalance: 214617753229, + priority: true + }, + e1495ceb6db842f3ee66428d4bb7f4a124b2b17111dae35d141c3d568b869ef1: { + capacity: 16000, + connectionTime: 8516.929533901, + isConnected: true, + pricing/balance: 999819891640, + pricing/balanceMeta: "", + pricing/negBalance: 185964891797, + priority: true + } +} + +> les.priorityClientInfo("0x4000000000000000000000000000000000000000000000000000000000000000", "0xe000000000000000000000000000000000000000000000000000000000000000", 2) +{ + 6a47fe7bb23fd335df52ef1690f37ab44265a537b1d18eb616a3e77f898d9e77: { + capacity: 100000, + connectionTime: 9842.11178361, + isConnected: true, + pricing/balance: 2912113588853, + pricing/balanceMeta: "qwerty", + pricing/negBalance: 206242704507, + priority: true + }, + 740c78f7d914e5c763731bc751b513fc2388ffa0b47db080ded3e8b305e68c75: { + capacity: 16000, + connectionTime: 3056.559199029, + isConnected: true, + pricing/balance: 998790060237, + pricing/balanceMeta: "", + pricing/negBalance: 55135348863, + priority: true + }, + 9985ade55b515f79f64274bf2ae440ca8c433cfb0f283fb6010bf46f796b2a3b: {} +} +``` + +### les_addBalance + +Add signed value to the token balance of the specified client and update its `meta` tag. The balance cannot go below zero or over `2^^63-1`. The balance values before and after the update are returned. The `meta` tag can be used to store a sequence number or reference to the last processed incoming payment, token expiration info, balance in other currencies or any application-specific additional information. + +| Client | Method invocation | +|:--------|-----------------------------------------------------------------------------------| +| Go | `les.AddBalance(id enode.ID, value int64, meta string) ([2]uint64, error)}` | +| Console | `les.addBalance(id, number, string)` | +| RPC | `{"method": "les_addBalance", "params": [id, number, string]}` | + +#### Example + +```javascript +> les.addBalance("0x6a47fe7bb23fd335df52ef1690f37ab44265a537b1d18eb616a3e77f898d9e77", 1000000000, "qwerty") +[968379616, 1968379616] +``` + +### les_setClientParams + +Set capacity and pricing factors for the specified list of connected clients or for all connected clients if the ID list is empty. + +| Client | Method invocation | +|:--------|-----------------------------------------------------------------------------------| +| Go | `les.SetClientParams(ids []enode.ID, params map[string]interface{}) error` | +| Console | `les.setClientParams([id, ...], {string: value, ...})` | +| RPC | `{"method": "les_setClientParams", "params": [[id, ...], {string: value, ...}]}` | + +#### Example + +```javascript +> les.setClientParams(["0x6a47fe7bb23fd335df52ef1690f37ab44265a537b1d18eb616a3e77f898d9e77"], { + "capacity": 100000, + "pricing/timeFactor": 0, + "pricing/capacityFactor": 1000000000, + "pricing/requestCostFactor": 1000000000, + "pricing/negative/timeFactor": 0, + "pricing/negative/capacityFactor": 1000000000, + "pricing/negative/requestCostFactor": 1000000000, +}) +null +``` + +### les_setDefaultParams + +Set default pricing factors for subsequently connected clients. + +| Client | Method invocation | +|:--------|-----------------------------------------------------------------------------------| +| Go | `les.SetDefaultParams(params map[string]interface{}) error` | +| Console | `les.setDefaultParams({string: value, ...})` | +| RPC | `{"method": "les_setDefaultParams", "params": [{string: value, ...}]}` | + +#### Example + +```javascript +> les.setDefaultParams({ + "pricing/timeFactor": 0, + "pricing/capacityFactor": 1000000000, + "pricing/requestCostFactor": 1000000000, + "pricing/negative/timeFactor": 0, + "pricing/negative/capacityFactor": 1000000000, + "pricing/negative/requestCostFactor": 1000000000, +}) +null +``` + +### les_latestCheckpoint + +Get the index and hashes of the latest known checkpoint. + +| Client | Method invocation | +|:--------|-------------------------------------------------------------| +| Go | `les.LatestCheckpoint() ([4]string, error)` | +| Console | `les.latestCheckpoint()` | +| RPC | `{"method": "les_latestCheckpoint", "params": []}` | + +#### Example + +```javascript +> les.latestCheckpoint +["0x110", "0x6eedf8142d06730b391bfcbd32e9bbc369ab0b46ae226287ed5b29505a376164", "0x191bb2265a69c30201a616ae0d65a4ceb5937c2f0c94b125ff55343d707463e5", "0xf58409088a5cb2425350a59d854d546d37b1e7bef8bbf6afee7fd15f943d626a"] +``` + +### les_getCheckpoint + +Get checkpoint hashes by index. + +| Client | Method invocation | +|:--------|-------------------------------------------------------------| +| Go | `les.GetCheckpoint(index uint64) ([3]string, error)` | +| Console | `les.getCheckpoint(number)` | +| RPC | `{"method": "les_getCheckpoint", "params": [number]}` | + +#### Example + +```javascript +> les.getCheckpoint(256) +["0x93eb4af0b224b1097e09181c2e51536fe0a3bf3bb4d93e9a69cab9eb3e28c75f", "0x0eb055e384cf58bc72ca20ca5e2b37d8d4115dce80ab4a19b72b776502c4dd5b", "0xda6c02f7c51f9ecc3eca71331a7eaad724e5a0f4f906ce9251a2f59e3115dd6a"] +``` + +### les_getCheckpointContractAddress + +Get the address of the checkpoint oracle contract. + +| Client | Method invocation | +|:--------|-------------------------------------------------------------------| +| Go | `les.GetCheckpointContractAddress() (string, error)` | +| Console | `les.checkpointContractAddress()` | +| RPC | `{"method": "les_getCheckpointContractAddress", "params": []}` | + +#### Example + +```javascript +> les.checkpointContractAddress +"0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a" +``` +
diff --git go-ethereum/docs/docs/_rpc/ns-miner.md celo/docs/docs/_rpc/ns-miner.md new file mode 100644 index 0000000000000000000000000000000000000000..f8ca0775d52c03341767c17eeba599ce7f333eb9 --- /dev/null +++ celo/docs/docs/_rpc/ns-miner.md @@ -0,0 +1,72 @@ +--- +title: miner Namespace +sort_key: C +--- + +The `miner` API allows you to remote control the node's mining operation and set various +mining specific settings. + +* TOC +{:toc} + +### miner_getHashrate + +Get your hashrate in H/s (Hash operations per second). + +| Client | Method invocation | +|:--------|-------------------------------------------------------------| +| Console | `miner.getHashrate()` | +| RPC | `{"method": "miner_getHashrate", "params": []}` | + +### miner_setExtra + +Sets the extra data a miner can include when miner blocks. This is capped at +32 bytes. + +| Client | Method invocation | +|:--------|----------------------------------------------------| +| Go | `miner.setExtra(extra string) (bool, error)` | +| Console | `miner.setExtra(string)` | +| RPC | `{"method": "miner_setExtra", "params": [string]}` | + +### miner_setGasPrice + +Sets the minimal accepted gas price when mining transactions. Any transactions that are +below this limit are excluded from the mining process. + +| Client | Method invocation | +|:--------|-------------------------------------------------------| +| Go | `miner.setGasPrice(number *rpc.HexNumber) bool` | +| Console | `miner.setGasPrice(number)` | +| RPC | `{"method": "miner_setGasPrice", "params": [number]}` | + +### miner_start + +Start the CPU mining process with the given number of threads and generate a new DAG +if need be. + +| Client | Method invocation | +|:--------|-----------------------------------------------------| +| Go | `miner.Start(threads *rpc.HexNumber) (bool, error)` | +| Console | `miner.start(number)` | +| RPC | `{"method": "miner_start", "params": [number]}` | + +### miner_stop + +Stop the CPU mining operation. + +| Client | Method invocation | +|:--------|----------------------------------------------| +| Go | `miner.Stop() bool` | +| Console | `miner.stop()` | +| RPC | `{"method": "miner_stop", "params": []}` | + +### miner_setEtherbase + +Sets the etherbase, where mining rewards will go. + +| Client | Method invocation | +|:--------|-------------------------------------------------------------| +| Go | `miner.SetEtherbase(common.Address) bool` | +| Console | `miner.setEtherbase(address)` | +| RPC | `{"method": "miner_setEtherbase", "params": [address]}` |
diff --git go-ethereum/docs/docs/_rpc/ns-personal.md celo/docs/docs/_rpc/ns-personal.md new file mode 100644 index 0000000000000000000000000000000000000000..a7a55eeb7ba1f4080f1e9d36c5ad754c6af917a0 --- /dev/null +++ celo/docs/docs/_rpc/ns-personal.md @@ -0,0 +1,187 @@ +--- +title: personal Namespace +sort_key: C +--- + +The personal API manages private keys in the key store. + +* TOC +{:toc} + +### personal_importRawKey + +Imports the given unencrypted private key (hex string) into the key store, +encrypting it with the passphrase. + +Returns the address of the new account. + +| Client | Method invocation | +| :--------| ----------------------------------------------------------------- | +| Console | `personal.importRawKey(keydata, passphrase)` | +| RPC | `{"method": "personal_importRawKey", "params": [string, string]}` | + +### personal_listAccounts + +Returns all the Ethereum account addresses of all keys +in the key store. + +| Client | Method invocation | +| :--------| --------------------------------------------------- | +| Console | `personal.listAccounts` | +| RPC | `{"method": "personal_listAccounts", "params": []}` | + +#### Example + +``` javascript +> personal.listAccounts +["0x5e97870f263700f46aa00d967821199b9bc5a120", "0x3d80b31a78c30fc628f20b2c89d7ddbf6e53cedc"] +``` + +### personal_lockAccount + +Removes the private key with given address from memory. +The account can no longer be used to send transactions. + +| Client | Method invocation | +| :--------| -------------------------------------------------------- | +| Console | `personal.lockAccount(address)` | +| RPC | `{"method": "personal_lockAccount", "params": [string]}` | + +### personal_newAccount + +Generates a new private key and stores it in the key store directory. +The key file is encrypted with the given passphrase. +Returns the address of the new account. + +At the geth console, `newAccount` will prompt for a passphrase when +it is not supplied as the argument. + +| Client | Method invocation | +| :--------| --------------------------------------------------- | +| Console | `personal.newAccount()` | +| RPC | `{"method": "personal_newAccount", "params": [string]}` | + +#### Example + +``` javascript +> personal.newAccount() +Passphrase: +Repeat passphrase: +"0x5e97870f263700f46aa00d967821199b9bc5a120" +``` + +The passphrase can also be supplied as a string. + +``` javascript +> personal.newAccount("h4ck3r") +"0x3d80b31a78c30fc628f20b2c89d7ddbf6e53cedc" +``` + +### personal_unlockAccount + +Decrypts the key with the given address from the key store. + +Both passphrase and unlock duration are optional when using the JavaScript console. +If the passphrase is not supplied as an argument, the console will prompt for +the passphrase interactively. + +The unencrypted key will be held in memory until the unlock duration expires. +If the unlock duration defaults to 300 seconds. An explicit duration +of zero seconds unlocks the key until geth exits. + +The account can be used with `eth_sign` and `eth_sendTransaction` while it is unlocked. + +| Client | Method invocation | +| :--------| -------------------------------------------------------------------------- | +| Console | `personal.unlockAccount(address, passphrase, duration)` | +| RPC | `{"method": "personal_unlockAccount", "params": [string, string, number]}` | + +#### Examples + +``` javascript +> personal.unlockAccount("0x5e97870f263700f46aa00d967821199b9bc5a120") +Unlock account 0x5e97870f263700f46aa00d967821199b9bc5a120 +Passphrase: +true +``` + +Supplying the passphrase and unlock duration as arguments: + +``` javascript +> personal.unlockAccount("0x5e97870f263700f46aa00d967821199b9bc5a120", "foo", 30) +true +``` + +If you want to type in the passphrase and stil override the default unlock duration, +pass `null` as the passphrase. + +``` +> personal.unlockAccount("0x5e97870f263700f46aa00d967821199b9bc5a120", null, 30) +Unlock account 0x5e97870f263700f46aa00d967821199b9bc5a120 +Passphrase: +true +``` + +### personal_sendTransaction + +Validate the given passphrase and submit transaction. + +The transaction is the same argument as for `eth_sendTransaction` and contains the `from` address. If the passphrase can be used to decrypt the private key belogging to `tx.from` the transaction is verified, signed and send onto the network. The account is not unlocked globally in the node and cannot be used in other RPC calls. + +| Client | Method invocation | +| :--------| -----------------------------------------------------------------| +| Console | `personal.sendTransaction(tx, passphrase)` | +| RPC | `{"method": "personal_sendTransaction", "params": [tx, string]}` | + +*Note, prior to Geth 1.5, please use `personal_signAndSendTransaction` as that was the +original introductory name and only later renamed to the current final version.* + +#### Examples + +``` javascript +> var tx = {from: "0x391694e7e0b0cce554cb130d723a9d27458f9298", to: "0xafa3f8684e54059998bc3a7b0d2b0da075154d66", value: web3.toWei(1.23, "ether")} +undefined +> personal.sendTransaction(tx, "passphrase") +0x8474441674cdd47b35b875fd1a530b800b51a5264b9975fb21129eeb8c18582f +``` + +### personal_sign + +The sign method calculates an Ethereum specific signature with: +`sign(keccack256("\x19Ethereum Signed Message:\n" + len(message) + message)))`. + +By adding a prefix to the message makes the calculated signature recognisable as an Ethereum specific signature. This prevents misuse where a malicious DApp can sign arbitrary data (e.g. transaction) and use the signature to impersonate the victim. + +See ecRecover to verify the signature. + +| Client | Method invocation | +|:--------|-------------------------------------------------------| +| Console | `personal.sign(message, account, [password])` | +| RPC | `{"method": "personal_sign", "params": [message, account, password]}` | + + +#### Examples + +``` javascript +> personal.sign("0xdeadbeaf", "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83", "") +"0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" +``` + +### personal_ecRecover + +`ecRecover` returns the address associated with the private key that was used to calculate the signature in `personal_sign`. + +| Client | Method invocation | +|:--------|-------------------------------------------------------| +| Console | `personal.ecRecover(message, signature)` | +| RPC | `{"method": "personal_ecRecover", "params": [message, signature]}` | + + +#### Examples + +``` javascript +> personal.sign("0xdeadbeaf", "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83", "") +"0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" +> personal.ecRecover("0xdeadbeaf", "0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b") +"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83" +```
diff --git go-ethereum/docs/docs/_rpc/ns-txpool.md celo/docs/docs/_rpc/ns-txpool.md new file mode 100644 index 0000000000000000000000000000000000000000..62abc22a183238a8d200ed6f0352b16d28dd7bfc --- /dev/null +++ celo/docs/docs/_rpc/ns-txpool.md @@ -0,0 +1,216 @@ +--- +title: txpool Namespace +sort_key: C +--- + +The `txpool` API gives you access to several non-standard RPC methods to inspect the contents of the +transaction pool containing all the currently pending transactions as well as the ones queued for +future processing. + +* TOC +{:toc} + +### txpool_content + +The `content` inspection property can be queried to list the exact details of all the transactions +currently pending for inclusion in the next block(s), as well as the ones that are being scheduled +for future execution only. + +The result is an object with two fields `pending` and `queued`. Each of these fields are associative +arrays, in which each entry maps an origin-address to a batch of scheduled transactions. These batches +themselves are maps associating nonces with actual transactions. + +Please note, there may be multiple transactions associated with the same account and nonce. This can +happen if the user broadcast mutliple ones with varying gas allowances (or even complerely different +transactions). + +| Client | Method invocation | +|:-------:|-----------------------------------------------------------------------| +| Go | `txpool.Content() (map[string]map[string]map[string]*RPCTransaction)` | +| Console | `txpool.content` | +| RPC | `{"method": "txpool_content"}` | + +#### Example + +```javascript +> txpool.content +{ + pending: { + 0x0216d5032f356960cd3749c31ab34eeff21b3395: { + 806: { + blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", + blockNumber: null, + from: "0x0216d5032f356960cd3749c31ab34eeff21b3395", + gas: "0x5208", + gasPrice: "0xba43b7400", + hash: "0xaf953a2d01f55cfe080c0c94150a60105e8ac3d51153058a1f03dd239dd08586", + input: "0x", + nonce: "0x326", + to: "0x7f69a91a3cf4be60020fb58b893b7cbb65376db8", + transactionIndex: null, + value: "0x19a99f0cf456000" + } + }, + 0x24d407e5a0b506e1cb2fae163100b5de01f5193c: { + 34: { + blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", + blockNumber: null, + from: "0x24d407e5a0b506e1cb2fae163100b5de01f5193c", + gas: "0x44c72", + gasPrice: "0x4a817c800", + hash: "0xb5b8b853af32226755a65ba0602f7ed0e8be2211516153b75e9ed640a7d359fe", + input: "0xb61d27f600000000000000000000000024d407e5a0b506e1cb2fae163100b5de01f5193c00000000000000000000000000000000000000000000000053444835ec580000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + nonce: "0x22", + to: "0x7320785200f74861b69c49e4ab32399a71b34f1a", + transactionIndex: null, + value: "0x0" + } + } + }, + queued: { + 0x976a3fc5d6f7d259ebfb4cc2ae75115475e9867c: { + 3: { + blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", + blockNumber: null, + from: "0x976a3fc5d6f7d259ebfb4cc2ae75115475e9867c", + gas: "0x15f90", + gasPrice: "0x4a817c800", + hash: "0x57b30c59fc39a50e1cba90e3099286dfa5aaf60294a629240b5bbec6e2e66576", + input: "0x", + nonce: "0x3", + to: "0x346fb27de7e7370008f5da379f74dd49f5f2f80f", + transactionIndex: null, + value: "0x1f161421c8e0000" + } + }, + 0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a: { + 2: { + blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", + blockNumber: null, + from: "0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a", + gas: "0x15f90", + gasPrice: "0xba43b7400", + hash: "0x3a3c0698552eec2455ed3190eac3996feccc806970a4a056106deaf6ceb1e5e3", + input: "0x", + nonce: "0x2", + to: "0x24a461f25ee6a318bdef7f33de634a67bb67ac9d", + transactionIndex: null, + value: "0xebec21ee1da40000" + }, + 6: { + blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", + blockNumber: null, + from: "0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a", + gas: "0x15f90", + gasPrice: "0x4a817c800", + hash: "0xbbcd1e45eae3b859203a04be7d6e1d7b03b222ec1d66dfcc8011dd39794b147e", + input: "0x", + nonce: "0x6", + to: "0x6368f3f8c2b42435d6c136757382e4a59436a681", + transactionIndex: null, + value: "0xf9a951af55470000" + } + } + } +} +``` + +### txpool_inspect + +The `inspect` inspection property can be queried to list a textual summary of all the transactions +currently pending for inclusion in the next block(s), as well as the ones that are being scheduled +for future execution only. This is a method specifically tailored to developers to quickly see the +transactions in the pool and find any potential issues. + +The result is an object with two fields `pending` and `queued`. Each of these fields are associative +arrays, in which each entry maps an origin-address to a batch of scheduled transactions. These batches +themselves are maps associating nonces with transactions summary strings. + +Please note, there may be multiple transactions associated with the same account and nonce. This can +happen if the user broadcast mutliple ones with varying gas allowances (or even complerely different +transactions). + +| Client | Method invocation | +|:-------:|--------------------------------------------------------------| +| Go | `txpool.Inspect() (map[string]map[string]map[string]string)` | +| Console | `txpool.inspect` | +| RPC | `{"method": "txpool_inspect"}` | + +#### Example + +```javascript +> txpool.inspect +{ + pending: { + 0x26588a9301b0428d95e6fc3a5024fce8bec12d51: { + 31813: "0x3375ee30428b2a71c428afa5e89e427905f95f7e: 0 wei + 500000 × 20000000000 gas" + }, + 0x2a65aca4d5fc5b5c859090a6c34d164135398226: { + 563662: "0x958c1fa64b34db746925c6f8a3dd81128e40355e: 1051546810000000000 wei + 90000 × 20000000000 gas", + 563663: "0x77517b1491a0299a44d668473411676f94e97e34: 1051190740000000000 wei + 90000 × 20000000000 gas", + 563664: "0x3e2a7fe169c8f8eee251bb00d9fb6d304ce07d3a: 1050828950000000000 wei + 90000 × 20000000000 gas", + 563665: "0xaf6c4695da477f8c663ea2d8b768ad82cb6a8522: 1050544770000000000 wei + 90000 × 20000000000 gas", + 563666: "0x139b148094c50f4d20b01caf21b85edb711574db: 1048598530000000000 wei + 90000 × 20000000000 gas", + 563667: "0x48b3bd66770b0d1eecefce090dafee36257538ae: 1048367260000000000 wei + 90000 × 20000000000 gas", + 563668: "0x468569500925d53e06dd0993014ad166fd7dd381: 1048126690000000000 wei + 90000 × 20000000000 gas", + 563669: "0x3dcb4c90477a4b8ff7190b79b524773cbe3be661: 1047965690000000000 wei + 90000 × 20000000000 gas", + 563670: "0x6dfef5bc94b031407ffe71ae8076ca0fbf190963: 1047859050000000000 wei + 90000 × 20000000000 gas" + }, + 0x9174e688d7de157c5c0583df424eaab2676ac162: { + 3: "0xbb9bc244d798123fde783fcc1c72d3bb8c189413: 30000000000000000000 wei + 85000 × 21000000000 gas" + }, + 0xb18f9d01323e150096650ab989cfecd39d757aec: { + 777: "0xcd79c72690750f079ae6ab6ccd7e7aedc03c7720: 0 wei + 1000000 × 20000000000 gas" + }, + 0xb2916c870cf66967b6510b76c07e9d13a5d23514: { + 2: "0x576f25199d60982a8f31a8dff4da8acb982e6aba: 26000000000000000000 wei + 90000 × 20000000000 gas" + }, + 0xbc0ca4f217e052753614d6b019948824d0d8688b: { + 0: "0x2910543af39aba0cd09dbb2d50200b3e800a63d2: 1000000000000000000 wei + 50000 × 1171602790622 gas" + }, + 0xea674fdde714fd979de3edf0f56aa9716b898ec8: { + 70148: "0xe39c55ead9f997f7fa20ebe40fb4649943d7db66: 1000767667434026200 wei + 90000 × 20000000000 gas" + } + }, + queued: { + 0x0f6000de1578619320aba5e392706b131fb1de6f: { + 6: "0x8383534d0bcd0186d326c993031311c0ac0d9b2d: 9000000000000000000 wei + 21000 × 20000000000 gas" + }, + 0x5b30608c678e1ac464a8994c3b33e5cdf3497112: { + 6: "0x9773547e27f8303c87089dc42d9288aa2b9d8f06: 50000000000000000000 wei + 90000 × 50000000000 gas" + }, + 0x976a3fc5d6f7d259ebfb4cc2ae75115475e9867c: { + 3: "0x346fb27de7e7370008f5da379f74dd49f5f2f80f: 140000000000000000 wei + 90000 × 20000000000 gas" + }, + 0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a: { + 2: "0x24a461f25ee6a318bdef7f33de634a67bb67ac9d: 17000000000000000000 wei + 90000 × 50000000000 gas", + 6: "0x6368f3f8c2b42435d6c136757382e4a59436a681: 17990000000000000000 wei + 90000 × 20000000000 gas", + 7: "0x6368f3f8c2b42435d6c136757382e4a59436a681: 17900000000000000000 wei + 90000 × 20000000000 gas" + } + } +} +``` + +### txpool_status + +The `status` inspection property can be queried for the number of transactions currently pending for +inclusion in the next block(s), as well as the ones that are being scheduled for future execution only. + +The result is an object with two fields `pending` and `queued`, each of which is a counter representing +the number of transactions in that particular state. + +| Client | Method invocation | +|:--------|-----------------------------------------------| +| Go | `txpool.Status() (map[string]*rpc.HexNumber)` | +| Console | `txpool.status` | +| RPC | `{"method": "txpool_status"}` | + +#### Example + +```javascript +> txpool.status +{ + pending: 10, + queued: 7 +} +```
diff --git go-ethereum/docs/docs/_rpc/pubsub.md celo/docs/docs/_rpc/pubsub.md new file mode 100644 index 0000000000000000000000000000000000000000..a6de88b06c00f28a67bcdd496a23c1f2d16e1d35 --- /dev/null +++ celo/docs/docs/_rpc/pubsub.md @@ -0,0 +1,168 @@ +--- +title: Real-time Events +sort_key: B +--- + +Geth v1.4 and later support publish / subscribe using JSON-RPC notifications. This allows +clients to wait for events instead of polling for them. + +It works by subscribing to particular events. The node will return a subscription id. For +each event that matches the subscription a notification with relevant data is send +together with the subscription id. + +Example: + + // create subscription + >> {"id": 1, "method": "eth_subscribe", "params": ["newHeads", {}]} + << {"jsonrpc":"2.0","id":1,"result":"0xcd0c3e8af590364c09d0fa6a1210faf5"} + + // incoming notifications + << {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0xcd0c3e8af590364c09d0fa6a1210faf5","result":{"difficulty":"0xd9263f42a87",<...>, "uncles":[]}}} + << {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0xcd0c3e8af590364c09d0fa6a1210faf5","result":{"difficulty":"0xd90b1a7ad02", <...>, "uncles":["0x80aacd1ea4c9da32efd8c2cc9ab38f8f70578fcd46a1a4ed73f82f3e0957f936"]}}} + + // cancel subscription + >> {"id": 1, "method": "eth_unsubscribe", "params": ["0xcd0c3e8af590364c09d0fa6a1210faf5"]} + << {"jsonrpc":"2.0","id":1,"result":true} + +### Considerations + +1. notifications are send for current events and not for past events. If your use case + requires you not to miss any notifications than subscriptions are probably not the best + option. +2. subscriptions require a full duplex connection. Geth offers such connections in the + form of WebSocket and IPC (enabled by default). +3. subscriptions are coupled to a connection. If the connection is closed all + subscriptions that are created over this connection are removed. +4. notifications are stored in an internal buffer and sent from this buffer to the client. + If the client is unable to keep up and the number of buffered notifications reaches a + limit (currently 10k) the connection is closed. Keep in mind that subscribing to some + events can cause a flood of notifications, e.g. listening for all logs/blocks when the + node starts to synchronize. + +## Create subscription + +Subscriptions are creates with a regular RPC call with `eth_subscribe` as method and the +subscription name as first parameter. If successful it returns the subscription id. + +### Parameters + +1. subscription name +2. optional arguments + +### Example + + >> {"id": 1, "method": "eth_subscribe", "params": ["newHeads", {"includeTransactions": true}]} + << {"id": 1, "jsonrpc": "2.0", "result": "0x9cef478923ff08bf67fde6c64013158d"} + +## Cancel subscription + +Subscriptions are cancelled with a regular RPC call with `eth_unsubscribe` as method and +the subscription id as first parameter. It returns a bool indicating if the subscription +was cancelled successful. + +### Parameters +1. subscription id + +### Example + + >> {"id": 1, "method": "eth_unsubscribe", "params": ["0x9cef478923ff08bf67fde6c64013158d"]} + << {"jsonrpc":"2.0","id":1,"result":true} + +## Supported Subscriptions + +### newHeads + +Fires a notification each time a new header is appended to the chain, including chain reorganizations. Users can use the bloom filter to determine if the block contains logs that are interested to them. + +In case of a chain reorganization the subscription will emit all new headers for the new +chain. Therefore the subscription can emit multiple headers on the same height. + +#### Example + + >> {"id": 1, "method": "eth_subscribe", "params": ["newHeads"]} + << {"jsonrpc":"2.0","id":2,"result":"0x9ce59a13059e417087c02d3236a0b1cc"} + + << { + "jsonrpc": "2.0", + "method": "eth_subscription", + "params": { + "result": { + "difficulty": "0x15d9223a23aa", + "extraData": "0xd983010305844765746887676f312e342e328777696e646f7773", + "gasLimit": "0x47e7c4", + "gasUsed": "0x38658", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069", + "nonce": "0x084149998194cc5f", + "number": "0x1348c9", + "parentHash": "0x7736fab79e05dc611604d22470dadad26f56fe494421b5b333de816ce1f25701", + "receiptRoot": "0x2fab35823ad00c7bb388595cb46652fe7886e00660a01e867824d3dceb1c8d36", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "stateRoot": "0xb3346685172db67de536d8765c43c31009d0eb3bd9c501c9be3229203f15f378", + "timestamp": "0x56ffeff8", + "transactionsRoot": "0x0167ffa60e3ebc0b080cdb95f7c0087dd6c0e61413140e39d94d3468d7c9689f" + }, + "subscription": "0x9ce59a13059e417087c02d3236a0b1cc" + } + } + +### logs + +Returns logs that are included in new imported blocks and match the given filter criteria. + +In case of a chain reorganization previous sent logs that are on the old chain will be resend with the `removed` property set to true. Logs from transactions that ended up in the new chain are emitted. Therefore a subscription can emit logs for the same transaction multiple times. + +#### Parameters + +1. `object` with the following (optional) fields + - **address**, either an address or an array of addresses. Only logs that are created from these addresses are returned (optional) + - **topics**, only logs which match the specified topics (optional) + + +#### Example + + >> {"id": 1, "method": "eth_subscribe", "params": ["logs", {"address": "0x8320fe7702b96808f7bbc0d4a888ed1468216cfd", "topics": ["0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902"]}]} + << {"jsonrpc":"2.0","id":2,"result":"0x4a8a4c0517381924f9838102c5a4dcb7"} + + << {"jsonrpc":"2.0","method":"eth_subscription","params": {"subscription":"0x4a8a4c0517381924f9838102c5a4dcb7","result":{"address":"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd","blockHash":"0x61cdb2a09ab99abf791d474f20c2ea89bf8de2923a2d42bb49944c8c993cbf04","blockNumber":"0x29e87","data":"0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003","logIndex":"0x0","topics":["0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902"],"transactionHash":"0xe044554a0a55067caafd07f8020ab9f2af60bdfe337e395ecd84b4877a3d1ab4","transactionIndex":"0x0"}}} + +### newPendingTransactions + +Returns the hash for all transactions that are added to the pending state and are signed with a key that is available in the node. + +When a transaction that was previously part of the canonical chain isn't part of the new canonical chain after a reogranization its again emitted. + +#### Parameters + +none + +#### Example + + >> {"id": 1, "method": "eth_subscribe", "params": ["newPendingTransactions"]} + << {"jsonrpc":"2.0","id":2,"result":"0xc3b33aa549fb9a60e95d21862596617c"} + << { + "jsonrpc":"2.0", + "method":"eth_subscription", + "params":{ + "subscription":"0xc3b33aa549fb9a60e95d21862596617c", + "result":"0xd6fdc5cc41a9959e922f30cb772a9aef46f4daea279307bc5f7024edc4ccd7fa" + } + } + +### syncing + +Indicates when the node starts or stops synchronizing. The result can either be a boolean +indicating that the synchronization has started (true), finished (false) or an object with +various progress indicators. + +#### Parameters + +none + +#### Example + + >> {"id": 1, "method": "eth_subscribe", "params": ["syncing"]} + << {"jsonrpc":"2.0","id":2,"result":"0xe2ffeb2703bcf602d42922385829ce96"} + + << {"subscription":"0xe2ffeb2703bcf602d42922385829ce96","result":{"syncing":true,"status":{"startingBlock":674427,"currentBlock":67400,"highestBlock":674432,"pulledStates":0,"knownStates":0}}}} +
diff --git go-ethereum/docs/docs/_rpc/server.md celo/docs/docs/_rpc/server.md new file mode 100644 index 0000000000000000000000000000000000000000..129ede012ce69696ab3d8cde13f7d9dab245a77a --- /dev/null +++ celo/docs/docs/_rpc/server.md @@ -0,0 +1,89 @@ +--- +title: JSON-RPC Server +sort_key: A +--- + +Geth supports all standard web3 JSON-RPC APIs. You can find documentation for +these APIs on the [Ethereum Wiki JSON-RPC page][web3-rpc]. + +JSON-RPC is provided on multiple transports. Geth supports JSON-RPC over HTTP, +WebSocket and Unix Domain Sockets. Transports must be enabled through +command-line flags. + +Ethereum JSON-RPC APIs use a name-space system. RPC methods are grouped into +several categories depending on their purpose. All method names are composed of +the namespace, an underscore, and the actual method name within the namespace. +For example, the `eth_call` method resides in the `eth` namespace. + +Access to RPC methods can be enabled on a per-namespace basis. Find +documentation for individual namespaces in the sidebar. + +### HTTP Server + +To enable the HTTP server, use the `--rpc` flag. + + geth --rpc + +By default, geth accepts connections from the loopback interface (127.0.0.1). +The default listening port is 8545. You can customize address and port using the +`--rpcport` and `--rpcaddr` flags. + + geth --rpc --rpcport 3334 + +JSON-RPC method namespaces must be whitelisted in order to be available through +the HTTP server. An RPC error with error code `-32602` is generated if you call a +namespace that isn't whitelisted. The default whitelist allows access to the "eth" +and "shh" namespaces. To enable access to other APIs like account management ("personal") +and debugging ("debug"), they must be configured via the `--rpcapi` flag. We do +not recommend enabling such APIs over HTTP, however, since access to these +methods increases the attack surface. + + geth --rpc --rpcapi personal,eth,net,web3b + +Since the HTTP server is reachable from any local application, additional +protection is built into the server to prevent misuse of the API from web pages. +If you want enable access to the API from a web page, you must configure the +server to accept Cross-Origin requests with the `--rpccorsdomain` flag. + +Example: if you want to use [Remix][remix] with geth, allow requests from the +remix domain. + + geth --rpc --rpccorsdomain https://remix.ethereum.org + +Use `--rpccorsdomain '*'` to enable access from any origin. + +### WebSocket Server + +Configuration of the WebSocket endpoint is similar to the HTTP transport. To +enable WebSocket access, use `--ws` flag. The default WebSocket port is 8546. +The `--wsaddr`, `--wsport` and `--wsapi` flags can be used to customize settings +for the WebSocket server. + + geth --ws --wsport 3334 --wsapi eth,net,web3 + +Cross-Origin request protection also applies to the WebSocket server. Use the +`--wsorigins` flag to allow access to the server from web pages: + + geth --ws --wsorigins http://myapp.example.com + +As with `--rpccorsdomain`, using `--wsorigins '*'` allows access from any origin. + +### IPC Server + +JSON-RPC APIs are also provided on a UNIX domain socket. This server is enabled +by default and has access to all JSON-RPC namespaces. + +The listening socket is placed into the data directory by default. On Linux and macOS, +the default location of the geth socket is + + ~/.ethereum/geth.ipc + +On Windows, IPC is provided via named pipes. The default location of the geth pipe is: + + \\.\pipe\geth.ipc + +You can configure the location of the socket using the `--ipcpath` flag. IPC can +be disabled using the `--ipcdisable` flag. + +[web3-rpc]: https://github.com/ethereum/wiki/wiki/JSON-RPC +[remix]: https://remix.ethereum.org
diff --git go-ethereum/docs/docs/_whisper/Achieving-Darkness.md celo/docs/docs/_whisper/Achieving-Darkness.md new file mode 100644 index 0000000000000000000000000000000000000000..c76ceb3dfc5c790df3369fb36d971e39887cd6ca --- /dev/null +++ celo/docs/docs/_whisper/Achieving-Darkness.md @@ -0,0 +1,54 @@ +--- +title: Achieving Darkness +sort_key: B +--- + +Whisper is designed to be a building block in the next generation of unstoppable ÐApps. It was designed to provide resilience and privacy at considerable expense. At its most secure mode of operation Whisper can theoretically deliver complete darkness. Whisper should also allow the users to configure the level of privacy (how much information it leaks concerning the ÐApp content and ultimately, user activities) as a trade-off for performance. In this article we will discuss the strategy of achieving complete darkness. + +### Adversary + +In the worst case, you are playing against a very powerful adversary, which has a capacity to run a lot of Whisper nodes. In addition to that, it might generate Whisper identities in order to target a specific node. For example, it might generate IDs that will be very close to the ID of the target node, which then fit into Kademlia topology in such a way that will allow a traffic analysis from this node. As a result, it will be possible to see which messages originate from the target node. + +### Recipient vs. Sender + +Even if most nodes of the network belong to adversary, it will be still impossible to identify the recipient of particular message by the means of simple traffic analysis. But the sender might be identified. In order to prevent that, the node might maintain a certain noise level. For example, if the node sends on average several messages per hour, it might still send random messages every minute. Those extra messages should resemble the meaningful messages, but should contain randomly generated bytes and be encrypted with random key, so that adversary will not be able to distinguish the meaningful messages from the noise. It is necessary to encrypt (not only generate) random data because encrypted messages might have some common traits, which distinguish them from noise. E.g. in case of asymmetric encryption some bytes are allowed to have only certain values. + +In some cases recipient might also be probabilistically identified if two nodes send each other messages with common traits, e.g. same Topic or same size. That's why it is important to use different Topics for sending and receiving the messages within the session. + +### Topics + +It is possible to send asymmetrically encrypted messages without any Topic. However, in case of huge traffic it might be not practical to try decrypting all the incoming messages, since decrypt operation is quite expensive. Some filtering might be necessary. Whisper allows to subscribe for messages with particular Topic or specify several possible Topics. It is also possible to subscribe for partial Topic(s), e.g. first one, two, or three bytes of the Topic. + +Topic collisions are part of the plausible deniability strategy. Some messages from different nodes (and encrypted with different keys) should have the same Topic (or partial Topic). Therefore it is recommended to set the Topics dynamically, so that collisions will regulary occur on your node. + +For example, if there are 6000 nodes in the network and each of them on average sends ten messages per minute, it would make sense to set only first byte of the Topic (leaving the other three bytes random). In this case each node will receive on average 1000 messages per second. The node will try to decrypt approximately four messages (those matching the Topic), and one of them will be successfully decrypted. The rest of the messages will be ignored and just forwarded to the other peers. + +### Message Size and Padding + +When sending the messages, another important metric which could potentially leak metainformation is the size. Therefore, depending on the circumstances, it might be useful to send messages only of certain size or always generate random size (not exceeding certain limit). The API allows easy setting of padding. The default implementation of Whisper also uses the padding to align the messages along the 256-byte boundaries. + +### Symmetric vs. Asymmetric + +In case of symmetric encryption, the nodes must have already exchanged the encryption key via some secure channel. So, they should use the same channel to exchange the Topics (or partial Topics), and maybe PoW requirement for individual messages. In case of asymmetric encryption, the public key is exchanged via open channels, and it might be impossible to exchange any other information before they engage in secure communication. In this case different strategies might be employed. + +For example, the first message might be sent without any Topic. This message may contain all the information necessary to start a symmetrically encrypted session, complete with (partial) Topics, PoW requirement and symmetric keys. Because it requires trying to decrypt all the incoming asymmetric messages, the PoW requirement for such messages might be set very high, which might be acceptable since such messages are only sent once per session. + +Alternatively, a partial Topic should be published along with the public key and PoW requirement. E.g., if we use two or three bytes for the partial Topic of symmetrically encrypted messages, we might only use one byte for the asymmetric encryption. + +### Example of Session + +Suppose two nodes want to engage in secure communication. One of them sends an asymmetrically encrypted message of certain (pre-defined) format, which contains two symmetric keys and corresponding partial Topics, all randomly generated. The sender of this message subscribes for the messages with the first Topic (encrypted with the first key), and always encrypts outgoing messages with the second key. The recipient subscribes for the messages with the second Topic (encrypted with the second key), and always encrypts outgoing messages with the first key. They also include the message number in each outgoing message, in order to ensure the correct order of the messages, and detect if some of them did not arrive. After the session is over, the keys are deleted and never used again. + +In case of several participants (e.g. a group chat) they just need one additional key and one additional subscription for any additional participant. + +### Plausible Deniability + +Suppose a user has lost control of his key. It might happen due to hacker attack on his computer, or becuase a hostile entity forces him to reveal the key. In case he signed any message with his private key, it might be used as evidence against him. So, the user might prefer to use his key only to establish the session, and then engage in symmetrically encrypted communication without signing the messages. His peer may be sure about his identity, since the symmetric key was signed with his private key. But these messages can not be used as evidence, since this message could have been sent by anyone in possession of the symmetric key, especially if this key was used in a group chat session. + +### Steganography + +However, in some extreme cases even plausible deniability might not be enough. In those cases users might utilize the padding mechanism for steganographic purposes. It is possible to set arbitrary data as padding. The Whisper API allows easy access to padding for both writing and reading. Since vast majority of the messages will contain some kind of padding (either default or customized), it will be impossible to distinguish between randomly generated and encrypted padding bytes. The users should only use such encryption which does not reveal any metainformation (preferably symmetric stream ciphers), and then add the resulting data as padding to a message with some irrelevant payload. + +### Private Networks + +It is very easy to set up a private Whisper network, since Whisper does not contain any genesis block or indeed any state at all. You just start a bootstrap node to which everybody should connect. It might be useful for organizations with restricted access. Private network may have several advantages. For example, it might be very difficult for any adversary to join the network, which will make the traffic analysis almost impossible. Also, PoW requirement might be dropped, thus allowing instant messaging with very little overhead.
diff --git go-ethereum/docs/docs/_whisper/Diagnostic-Tool-wnode.md celo/docs/docs/_whisper/Diagnostic-Tool-wnode.md new file mode 100644 index 0000000000000000000000000000000000000000..38565f42973b4afd359a2e3f33f07747c1677b2f --- /dev/null +++ celo/docs/docs/_whisper/Diagnostic-Tool-wnode.md @@ -0,0 +1,267 @@ +--- +title: Diagnostic tool wnode +sort_key: B +--- + +Wnode (whisper node) is a command-line diagnostic tool. It does not have a nice user interface, because its main purpose is diagnostic, and it's meant to be very light-weight rather than beautiful. Wnode might be used for different purposes, including: + +- running a standalone bootstrap node, in order to set up a private whisper network +- connecting to particular node for debugging or other purposes +- engaging in command-line chat with another peer in order to test its functionality +- sending and receiving text messages +- sending and receiving files +- running fully functional mail server +- testing functionality of mail client + +## Usage + +``` +> wnode [flags/arguments] +``` + +## Flags & Switches + +In case an argument is missing, `wnode` will either use the default value or prompt the user at startup. For security reasons, it is not possible to provide passwords in command-line arguments. In `test` mode, a hardcoded password ("test") is used. + +`-asym`: use asymmetric encryption in the chat + +`-boot`: A string representing the bootstrap node to connect to. For example: + +``` +-boot=enode://d25474361659861e9e651bc728a17e807a3359ca0d344afd544ed0f11a31faecaf4d74b55db53c6670fd624f08d5c79adfc8da5dd4a11b9213db49a3b750845e@52.178.209.125:30379 +``` + +`-dbpath`: The path to the server's DB directory, for incoming messages to be stored + +`-echo`: prints some arguments for diagnostics + +`-fileexchange`: file exchange mode (send and receive files instead of text messages) + +`-filereader`: load and decrypt messages saved as files, display as plain text + +`-forwarder`: forwarder mode (only forward; neither send nor decrypt messages) + +`-generatekey`: generate and show the private key, and exit + +`-idfile`: file name containing a node ID (private key) in hexadecimal format + +`-ip`: IP address and port of this node (e.g. 127.0.0.1:30303) + +`-mailclient`: request expired messages from the mail server + +`-mailserver`: mail server mode (delivers expired messages on demand) + +`maxsize`: max size of message (default 1048576) + +`-mspow`: PoW requirement for Mail Server request (default 0.2) + +`-pow`: PoW for normal messages in float format (e.g. 2.7) (default 0.2) + +`-pub`: public key for asymmetric encryption. For example: + +``` +-pub=0x07af49cbe6353b8732a8b9eb20dd1472f3d4512cd1a11382ee2817cc6de9453bc07c32c730b93bc83877b11e4f47d718751297f4edcbf35015df2b34ff5fc6a75d +``` + +`-savedir`: directory where all incoming messages will be saved as files. Only big messages are stored there, except in `fileexchange` mode where all files are stored there. + +`-standalone`: don't actively connect to any peers, wait for incoming connections instead + +`-test`: use of predefined parameters for diagnostics, including passwords + +`-topic`: topic in hexadecimal format (e.g. 70a4beef) + +`-ttl`: time-to-live for messages in seconds (default 30) + +`-verbosity`: log verbosity level (default 1) + +`-work`: work time in seconds (default 5) + + + +## Scenarios & Examples + +For simplicity, in these examples we assume that we only use `wnode` to communicate with another wnode. + + +### Start a bootstrap node for test network + +``` +$ wnode -standalone -forwarder -ip=127.0.0.1:30379 +my public key: 0x040ef7acd60781c336c52056b3782f7eae45be2063e591ac6b78472dc27ba770010bde445ffd2f3623ad656f3859e00d11ef518df4916c4d4e258c60b15f34c682 enode://15454fc65bbf0031155f4eee83fa732f1454c314e9f78ade9cba4d4a098d29edbf5431764ee65b200169025c3f900cacc3348a000dda7a8a0d9643d0b7618712@127.0.0.1:30379 +Bootstrap Whisper node started +``` + +### Connecting to a bootstrap node + +After the bootstrap node has started, another local node can connect to it, using the resulting enode: + +``` +$ wnode -test -boot=enode://15454fc65bbf0031155f4eee83fa732f1454c314e9f78ade9cba4d4a098d29edbf5431764ee65b200169025c3f900cacc3348a000dda7a8a0d9643d0b7618712@127.0.0.1:30379 +............................ +Whisper node started +Connected to peer. +............................ +``` + +### Persistent ID + +Upon restarting the bootstrap node, its enode will be different, because the ID is randomly generated. For persistence accross restarts, it is possible to specify an ID stored in a file using the 'idfile' argument. + +Generating ID: + +``` +$ wnode -generatekey > pk1.txt +$ cat pk1.txt +b3651aff593ef395ee7c16f3ca681830f7d8d0b2729cf472b14f2c4ebe833aa0 +``` + +`pk1.txt` now contains the key used to generate the ID. + +Starting the bootstrap node with a persistent ID: + +``` +bootnode $ wnode -forwarder -standalone -ip=127.0.0.1:30379 -idfile=pk1.txt +my public key: 0x04be81a00a90f5c21ead8887eaa254b3f7a37e06f8f2d776dcc46954a228bc50c6fb6dfd155f7e44e6fef9b62fdf6dad041759b864d2cbe4089b6f5c16a817ff46 enode://7d13360f5b1ddcf6947f244639113597a863abba0589d2fa5fffb2816ead0acea6211d5778a8be648e45e81ed881f4c1f5c9bbbf0e79065dfb54bcd97de3beab@127.0.0.1:30379 + Filter is configured for the topic: 5a4ea131 + Bootstrap Whisper node started +``` + +Now you can always use the same command to connect to your bootstrap node: + +``` +othernode $ wnode -test -boot=enode://7d13360f5b1ddcf6947f244639113597a863abba0589d2fa5fffb2816ead0acea6211d5778a8be648e45e81ed881f4c1f5c9bbbf0e79065dfb54bcd97de3beab@127.0.0.1:30379 +``` + +Be aware that the ID is stored unencrypted. This feature should only be used for test purposes. + +In order to set up a bootstrap node on a server with a dedicated IP address, its IP and port need to be specified explicitly: + +``` +bootnode $ wnode -forwarder -standalone -ip=52.178.211.103:30379 +``` + +### Using a mail server + +The mailserver is only provided as an example for people interested in building their own solution. It is not supported. + +``` +$ wnode -forwarder -standalone -ip=127.0.0.1:30381 -idfile=config.txt -mailserver -dbpath=tmp/db +``` + + +### Chat with symmetric encryption + +For two nodes to communicate using symmetric encryption, one of them must assume the role of a bootstrap node, and the second one that of the client. The bootstrap node is started with the `standalone` flag, and the client must connect to it. It is easy to do on the same machine or on a dedicated server. But what if two peers are behind distinct NAT? In that case, you need a third bootstrap node on a dedicated server, which both peers can connect to. At the time of writing we have out test node with the following enode: +`enode://d25474361659861e9e651bc728a17e807a3359ca0d344afd544ed0f11a31faecaf4d74b55db53c6670fd624f08d5c79adfc8da5dd4a11b9213db49a3b750845e@52.178.209.125:30379`, +to which both peers can connect with the following command: + +``` +bootnode $ wnode -boot=enode://d25474361659861e9e651bc728a17e807a3359ca0d344afd544ed0f11a31faecaf4d74b55db53c6670fd624f08d5c79adfc8da5dd4a11b9213db49a3b750845e@52.178.209.125:30379 +``` + +The user is prompted for the symmetric encryption password. The symmetric key is derived from this password. The topic will be derived from the password as well, unless it's provided by the user on the command line (which is strongly encouraged for any meaningful communication): + +``` +othernode $ wnode -topic=a6fcb30d -boot=enode://d25474361659861e9e651bc728a17e807a3359ca0d344afd544ed0f11a31faecaf4d74b55db53c6670fd624f08d5c79adfc8da5dd4a11b9213db49a3b750845e@52.178.209.125:30379 +``` + +The communication is therefore established. Typing message in one console will echo: + +``` +hello world! + +1493061848 <031792461900245c6919c4b23447ef8ba43f79a2>: hello world! +``` + +The first number (1493061848) is UNIX timestamp. This format is useful for Mail Client/Server tests. The number in brackets is the ID with which the message is signed. Seeing an ID with only zeros means the message is not signed, although encrypted with the right key. Another `wnode` peer will show the same output: + +``` +1493061848 [031792461900245c6919c4b23447ef8ba43f79a2]: hello world! +``` + +Only the brackets are different, indicating that this message originated from another identity. + +### Chat with Asymmetric Encryption + +Using asymmetric encryption is as simple as using the `-asym` flag: + +``` +othernode $ wnode -topic=a6fcb30d -asym -boot=enode://d25474361659861e9e651bc728a17e807a3359ca0d344afd544ed0f11a31faecaf4d74b55db53c6670fd624f08d5c79adfc8da5dd4a11b9213db49a3b750845e@52.178.209.125:30379 + +my public key: 0x0405007821171295a716c9d091371e836e98a5206d5b9ce9177df90c83fc308ebae2786a9c7bff999ad83d12be08e597d4b5a5240f3bb0bc366f008b7d0908df8a +enode://efe233263c78482111ba6c058ccc69b7a2ea3372774733def4fd5a357dfbaa67657e665078d573f11876fd2b7d75d41926976f41e257f91b486e9d36f4143c8a@[::]:42562 +Whisper node started +Connected to peer. +Please enter the peer's public key: +``` + +First line of the output contains the key which should be passed to another peer, and vice versa. Once both clients have entered their peer's public key, the chat session is active. + +### Sending and receiving files + +File exchange is activated with the `fileexchange` flags. Examples here use the `-test` flag for simplicity. Assuming that the incoming messages are to be stored in `/home/tester/tmp/msg`, the resulting command line is: + +``` +bootnode $ wnode -standalone -ip=127.0.0.1:30379 -idfile=pk1.txt -fileexchange -savedir=/home/tester/tmp/msg +enode://7d13360f5b1ddcf6947f244639113597a863abba0589d2fa5fffb2816ead0acea6211d5778a8be648e45e81ed881f4c1f5c9bbbf0e79065dfb54bcd97de3beab@127.0.0.1:30379 +``` + +To send a file to this first `wnode`, type: + +``` +othernode $ wnode -test -boot=enode://7d13360f5b1ddcf6947f244639113597a863abba0589d2fa5fffb2816ead0acea6211d5778a8be648e45e81ed881f4c1f5c9bbbf0e79065dfb54bcd97de3beab@127.0.0.1:30379 +``` + +Typing messages in the console of the second node, will cause the first one to display something like this: + +``` +1493124416 {624fdf6983940c7ffa8a4742f76dc78ae9775c47}: message received and saved as 'aa6f339e830c86718ddf4254038dd9fa8da6494e3f3c856af500a5aeaf0df62d' (4 bytes) +``` + +Messages are not displayed, but saved instead. Examine the contents of `/home/tester/tmp/msg/aa6f339e830c86718ddf4254038dd9fa8da6494e3f3c856af500a5aeaf0df62d` to confirm that the message is saved there. + +It is possible to send a file directly by typing its path. For example: + +``` +> /home/tester/tmp/msg/aa6f339e830c86718ddf4254038dd9fa8da6494e3f3c856af500a5aeaf0df62d +``` + +Asymmetric encryption is also available in file exchange mode by providing the `asym` flag. + +### Mail Server & Client + +Whisper protocol allows you to exchange messages with other peers only if you are online. But what if you go offline? Will important messages be lost forever? The golang implementation of Whisper v6 has a built-in support for Mail Client/Server functionality, which allows to create very secure (and even dark) anonymous email-like system. Wnode is designed to demonstrate the viability of such project. + +Mail Server and Client must have direct connection, since they exchange special kind of messages, which are not propagated any further. The reason for that is simple: if you receive the old (expired) message from the Server, and try to send it to other peers, they will recognise the message as expired, and drop connection with you. + +Starting Mail Server: + + +``` +bootnode $ wnode -mailserver -forwarder -standalone -test -ip=127.0.0.1:30381 -idfile=pk1.txt -dbpath=/home/tester/tmp/arj +``` + +Now start another node, connect to the Server, and send some test messages to fill the database: + +``` +othernode $ wnode -test -boot=enode://7d13360f5b1ddcf6947f244639113597a863abba0589d2fa5fffb2816ead0acea6211d5778a8be648e45e81ed881f4c1f5c9bbbf0e79065dfb54bcd97de3beab@127.0.0.1:30381 +``` + +Note the UNIX time of the messages. For example: `1493127055`. +Now start the Mail Client and connect to the Server: + +``` +othernode $ wnode -mailclient -test -boot=enode://7d13360f5b1ddcf6947f244639113597a863abba0589d2fa5fffb2816ead0acea6211d5778a8be648e45e81ed881f4c1f5c9bbbf0e79065dfb54bcd97de3beab@127.0.0.1:30381 +``` + +You will be prompted to enter the time range of the archived messages you want to receive: + +``` +Please enter the lower limit of the time range (unix timestamp): 1493127000 +Please enter the upper limit of the time range (unix timestamp): 1493127099 +Please enter the topic (hexadecimal): +``` + +You can leave the topic empty for now, in which case all the messages will be delivered, regardless of the topic. +The message should be delivered by the the Server, decrypted by the Client and displayed on the screen.
diff --git go-ethereum/docs/docs/_whisper/How-to-Whisper.md celo/docs/docs/_whisper/How-to-Whisper.md new file mode 100644 index 0000000000000000000000000000000000000000..c385a7b899af0e9e3af9a2902fcf001a52b114d7 --- /dev/null +++ celo/docs/docs/_whisper/How-to-Whisper.md @@ -0,0 +1,102 @@ +--- +title: How to Whisper +sort_key: B +--- + +Whisper is a pure identity-based messaging system. Whisper provides a low-level (non-application-specific) but easily-accessible API without being based upon or prejudiced by the low-level hardware attributes and characteristics, particularly the notion of singular endpoints. + +This tutorial will guide you to setting up a full p2p server with whisper capabilities. + +Let's quickly cover some of whisper's basic functionality and discuss it in greater detail later. + +```go +whisper.Send(myEnvelope) +``` + +The notion of envelopes and messages in whisper is somewhat blurred. An application shouldn't ever need to know the difference between the two and should only care about the information it's interested in. Therefor whisper comes with a subscribing mechanism which allows you watch/listen for specific whisper messages (e.g., to you, with a specific topic, etc). + +```go +whisper.Watch(Filter{ + From: myFriendsPubKey, + Fn: func(msg *whisper.Message) { /* ... */ }, +}) +``` + +## Envelopes & Messages + +Whenever you want to send message over the whisper network you need to prove to network you've done some significant work for sealing the message (such is the cost for sending messages) and thus the more work you put in to sealing the message the higher the priority the message will have when propagating it over the network. + +Whisper's *P*roof *o*f *W*ork consists of a simple SHA3 algorithm in which we try to find the smallest number within a given time frame. Giving the algorithm more time will result in a smaller number which means the message has a higher priority in the network. + +Messages are also sealed with a *T*ime *T*o *L*ive. Whisper peers will automatically flush out messages which have exceeded their time to live (with a maximum up to 2 days). + +Additionally messages may also contain a recipient (public key) and a set of topics. Topics will allow us to specify messages their subject (e.g., "shoes", "laptop", "marketplace", "chat"). Topics are automatically hashed and only the first 4 bytes are used during transmission and as such, topics are not 100% reliable, they should be treated as a probabilistic message filter. + +Sending a whisper message requires you to: + +1. create a new `whisper.Message` +2. `Seal` it (optionally encrypt, sign and supply with topics) +3. `Send` it to your peers + +```go +topics := TopicsFromString("my", "message") +msg := whisper.NewMessage([]byte("hello world")) // 1 +envelope := msg.Seal(whisper.Opts{ // 2 + From: myPrivateKey, // Sign it + Topics: topics, +}) +whisper.Send(envelope) // 3 +``` + +Whenever a message needs to be encrypted for a specific recipient supply the `Opts` struct with an additional `To` parameter which accepts the recipients public key (`ecdsa.PublicKey`). + +## Watching & Listening + +Watching for specific messages on the whisper network can be done using the `Watch` method. You have the option to watch for messages from a specific recipient, with specific topics or messages directly directed to you. + +```go +topics := TopicsFromString("my", "message") +whisper.Watch(Filter{ + Topics: topics + Fn: func(msg *Message) { + fmt.Println(msg) + }, +}) +``` + +## Connecting it all together + +Now if we tie it all together and supply whisper as a sub-protocol to the DEV's P2P service we have whisper including peer handling and message propagation. + +```go +package main + +import ( + "fmt" + "log" + "os" + + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/whisper" + "github.com/obscuren/secp256k1-go" +) + +func main() { + pub, _ := secp256k1.GenerateKeyPair() + + whisper := whisper.New() + + srv := p2p.Server{ + MaxPeers: 10, + Identity: p2p.NewSimpleClientIdentity("my-whisper-app", "1.0", "", string(pub)), + ListenAddr: ":8000", + Protocols: []p2p.Protocol{whisper.Protocol()}, + } + if err := srv.Start(); err != nil { + fmt.Println("could not start server:", err) + os.Exit(1) + } + + select {} +} +```
diff --git go-ethereum/docs/docs/_whisper/Whisper-Overview.md celo/docs/docs/_whisper/Whisper-Overview.md new file mode 100644 index 0000000000000000000000000000000000000000..62cb201075b11f363a9991b50a738bb19bbbe396 --- /dev/null +++ celo/docs/docs/_whisper/Whisper-Overview.md @@ -0,0 +1,172 @@ +--- +title: Overview +sort_key: A +--- + +Whisper is a pure identity-based messaging system. Whisper provides a simple low-level API without being based upon or influenced by the low-level hardware attributes and characteristics. Peer-to-peer communication between the nodes of Whisper network uses the underlying [ÐΞVp2p Wire Protocol](https://github.com/ethereum/wiki/wiki/%C3%90%CE%9EVp2p-Wire-Protocol). Whisper was not designed to provide a connection-oriented system, nor for simply delivering data between a pair of particular network endpoints. However, this might be necessary in some very specific cases (e.g. delivering the expired messages in case they were missed), and Whisper protocol will accommodate for that. Whisper is designed for easy and efficient broadcasting, and also for low-level asynchronous communications. It is designed to be a building block in next generation of unstoppable ÐApps. It was designed to provide resilience and privacy at considerable expense. At its most secure mode of operation, Whisper can theoretically deliver 100% darkness. Whisper should also allow the users to configure the level of privacy (how much information it leaks concerning the ÐApp content and ultimately, user activities) as a trade-off for performance. + +Basically, all Whisper messages are supposed to be sent to every Whisper node. In order to prevent a DDoS attack, proof-of-work (PoW) algorithm is used. Messages will be processed (and forwarded further) only if their PoW exceeds a certain threshold, otherwise they will be dropped. + +### Encryption in version 6 + +All Whisper messages are encrypted and then sent via underlying ÐΞVp2p Protocol, which in turn uses its own encryption, on top of Whisper encryption. Every Whisper message must be encrypted either symmetrically or asymmetrically. Messages could be decrypted by anyone who possesses the corresponding key. + +In previous versions unencrypted messages were allowed, but since it was necessary to exchange the topic (more on that below), the nodes might as well use the same communication channel to exchange encryption key. + +Every node may possess multiple symmetric and asymmetric keys. Upon Envelope receipt, the node should try to decrypt it with each of the keys, depending on Envelope's encryption mode -- symmetric or asymmetric. In case of success, decrypted message is passed to the corresponding Ðapp. In any case, every Envelope should be forwarded to each of the node's peers. + +Asymmetric encryption uses the standard Elliptic Curve Integrated Encryption Scheme with SECP-256k1 public key. Symmetric encryption uses AES GCM algorithm with random 96-bit nonce. If the same nonce will be used twice, then all the previous messages encrypted with the same key will be compromised. Therefore no more than 2^48 messages should be encrypted with the same symmetric key (for detailed explanation please see the Birthday Paradox). However, since Whisper uses proof-of-work, this number could possibly be reached only under very special circumstances (e.g. private network with extremely high performance and reduced PoW). Still, usage of one-time session keys is strongly encouraged for all Ðapps. + +Although we assume these standard encryption algorithms to be reasonably secure, users are encouraged to use their own custom encryption on top of the default Whisper encryption. + +#### Envelopes + +Envelopes are the packets sent and received by Whisper nodes. Envelopes contain the encrypted payload and some metadata in plain format, because these data is essential for decryption. Envelopes are transmitted as RLP-encoded structures of the following format: + + [ Version, Expiry, TTL, Topic, AESNonce, Data, EnvNonce ] + +Version: up to 4 bytes (currently one byte containing zero). Version indicates encryption method. If Version is higher than current, envelope could not be decrypted, and therefore only forwarded to the peers. + +Expiry time: 4 bytes (UNIX time in seconds). + +TTL: 4 bytes (time-to-live in seconds). + +Topic: 4 bytes of arbitrary data. + +AESNonce: 12 bytes of random data (only present in case of symmetric encryption). + +Data: byte array of arbitrary size (contains encrypted message). + +EnvNonce: 8 bytes of arbitrary data (used for PoW calculation). + +Whisper nodes know nothing about content of envelopes which they can not decrypt. The nodes pass envelopes around regardless of their ability to decrypt the message, or their interest in it at all. This is an important component in Whisper's dark communications strategy. + +#### Messages + +Message is the content of Envelope's payload in plain format (unencrypted). + +Plaintext (unencrypted) message is formed as a concatenation of a single byte for flags, additional metadata (as stipulated by the flags) and the actual payload. The message has the following structure: + + flags: 1 byte + optional padding: byte array of arbitrary size + payload: byte array of arbitrary size + optional signature: 65 bytes + +Those unable to decrypt the message data are also unable to access the signature. The signature, if provided, is the ECDSA signature of the Keccak-256 hash of the unencrypted data using the secret key of the originator identity. The signature is serialised as the concatenation of the `R`, `S` and `V` parameters of the SECP-256k1 ECDSA signature, in that order. `R` and `S` are both big-endian encoded, fixed-width 256-bit unsigned. `V` is an 8-bit big-endian encoded, non-normalised and should be either 27 or 28. + +The padding is introduced in order to align the message size, since message size alone might reveal important metainformation. The padding is supposed to contain random data (at least this is the default behaviour in version 6). However, it is possible to set arbitrary padding data, which might even be used for steganographic purposes. The API allows easy access to the padding data. + +Default version 6 implementation ensures the size of the message to be multiple of 256. However, it is possible to set arbitrary padding through Whisper API. It might be useful for private Whisper networks, e.g. in order to ensure that all Whisper messages have the same (arbitrary) size. Incoming Whisper messages might have arbitrary padding size, and still be compatible with version 6. + +The first several bytes of padding (up to four bytes) indicate the total size of padding. E.g. if padding is less than 256 bytes, then one byte is enough; if padding is less than 65536 bytes, then 2 bytes; and so on. + +Flags byte uses only three bits in v.6. First two bits indicate, how many bytes indicate the padding size. The third byte indicates if signature is present. Other bits must be set to zero for backwards compatibility of future versions. + +#### Topics + +It might not be feasible to try to decrypt ALL incoming envelopes, because decryption is quite expensive. In order to facilitate the filtering, Topics were introduced to the Whisper protocol. Topic gives a probabilistic hint about encryption key. Single Topic corresponds to a single key (symmetric or asymmetric). + +Upon receipt of a message, if the node detects a known Topic, it tries to decrypt the message with the corresponding key. In case of failure, the node assumes that Topic collision occurs, e.g. the message was encrypted with another key, and should be just forwarded further. Collisions are not only expected, they are necessary for plausible deniability. + +Any Envelope could be encrypted only with one key, and therefore it contains only one Topic. + +Topic field contains 4 bytes of arbitrary data. It might be generated from the key (e.g. first 4 bytes of the key hash), but we strongly discourage it. In order to avoid any compromise on security, Topics should be completely unrelated to the keys. + +In order to use symmetric encryption, the nodes must exchange symmetric keys via some secure channel anyway. They might use the same channel in order to exchange the corresponding Topics as well. + +In case of asymmetric encryption, it might be more complicated since public keys are meant to be exchanged via the open channels. So, the Ðapp has a choice of either publishing its Topic along with the public key (thus compromising on privacy), or trying to decrypt all asymmetrically encrypted Envelopes (at considerable expense). Alternatively, PoW requirement for asymmetric Envelopes might be set much higher than for symmetric ones, in order to limit the number of futile attempts. + +It is also possible to publish a partial Topic (first bytes), and then filter the incoming messages correspondingly. In this case the sender should set the first bytes as required, and rest should be randomly generated. + +Examples: + + Partial Topic: 0x12 (first byte must be 0x12, the last three bytes - random) + Partial Topic: 0x1234 (first two bytes must be {0x12, 0x34}, the last two bytes - random) + Partial Topic: 0x123456 (first three bytes must be {0x12, 0x34, 0x56}, the last byte - random) + +#### Filters + +Any Ðapp can install multiple Filters utilising the Whisper API. Filters contain the secret key (symmetric or asymmetric), and some conditions, according to which the Filter should try to decrypt the incoming Envelopes. If Envelope does not satisfy those conditions, it should be ignored. Those are: +- array of possible Topics (or partial Topics) +- Sender address +- Recipient address +- PoW requirement +- AcceptP2P: boolean value, indicating whether the node accepts direct messages from trusted peers (reserved for some specific purposes, like Client/MailServer implementation) + +All incoming messages, that have satisfied the Filter conditions AND have been successfully decrypted, will be saved by the corresponding Filter until the Ðapp requests them. Ðapps are expected to poll for incoming messages at regular time intervals. All installed Filters are independent of each other, and their conditions might overlap. If a message satisfies the conditions of multiple Filters, it will be stored in each of the Filters. + +In future versions subscription will be used instead of polling. + +In case of partial Topic, the message will match the Filter if first X bytes of the message Topic are equal to the corresponding bytes of partial Topic in Filter (where X can be 1, 2 or 3). The last bytes of the message Topic are ignored in this case. + +#### Proof of Work + +The purpose of PoW is spam prevention, and also reducing the burden on the network. The cost of computing PoW can be regarded as the price you pay for allocated resources if you want the network to store your message for a specific time (TTL). In terms of resources, it does not matter if the network stores X equal messages for Y seconds, or Y messages for X seconds. Or N messages of Z bytes each versus Z messages of N bytes. So, required PoW should be proportional to both message size and TTL. + +After creating the Envelope, its Nonce should be repeatedly incremented, and then its hash should be calculated. This procedure could be run for a specific predefined time, looking for the lowest hash. Alternatively, the node might run the loop until certain predefined PoW is achieved. + +In version 6, PoW is defined as average number of iterations, required to find the current BestBit (the number of leading zero bits in the hash), divided by message size and TTL: + +<code>PoW = (2^BestBit) / (size * TTL)</code> + +Thus, we can use PoW as a single aggregated parameter for the message rating. In the future versions every node will be able to set its own PoW requirement dynamically and communicate this change to the other nodes via the Whisper protocol. Now it is only possible to set PoW requirement at the Ðapp startup. + +### Packet Codes (ÐΞVp2p level) + +As a sub-protocol of [ÐΞVp2p](https://github.com/ethereum/wiki/wiki/%C3%90%CE%9EVp2p-Wire-Protocol), Whisper sends and receives its messages within ÐΞVp2p packets. +Whisper v6 supports the following packet codes: + +<code>Status (0x0)</code> + +<code>Messages (0x1)</code> + +<code>P2PMessage (0x2)</code> + +<code>P2PRequest (0x3)</code> + +Also, the following codes might be supported in the future: + +<code>PoWRequirement (0x4)</code> + +<code>BloomFilterExchange (0x5)</code> + +### Basic Operation + +Nodes are expected to receive and send envelopes continuously. They should maintain a map of envelopes, indexed by expiry time, and prune accordingly. They should also efficiently deliver messages to the front-end API through maintaining mappings between Ðapps, their filters and envelopes. + +When a node's envelope memory becomes exhausted, a node may drop envelopes it considers unimportant or unlikely to please its peers. Nodes should rate peers higher if they pass them envelopes with higher PoW. Nodes should blacklist peers if they pass invalid envelopes, i.e., expired envelopes or envelopes with an implied insertion time in the future. + +Nodes should always treat messages that its ÐApps have created no different than incoming messages. + +#### Creating and Sending Messages + +To send a message, the node should place the envelope its envelope pool. Then this envelope will be forwarded to the peers in due course along with the other envelopes. Composing an envelope from a basic payload, is done in a few steps: + +- Compose the Envelope data by concatenating the relevant flag byte, padding, payload (randomly generated or provided by user), and an optional signature. +- Encrypt the data symmetrically or asymmetrically. +- Add a Topic. +- Set the TTL attribute. +- Set the expiry as the present Unix time plus TTL. +- Set the nonce which provides the best PoW. + +### Mail Server + +Suppose, a Ðapp waits for messages with certain Topic and suffers an unexpected network failure for certain period of time. As a result, a number of important messages will be lost. Since those messages are expired, there is no way to resend them via the normal Whisper channels, because they will be rejected and the peer punished. + +One possible way to solve this problem is to run a Mail Server, which would store all the messages, and resend them at the request of the known nodes. Even though the server might repack the old messages and provide sufficient PoW, it's not feasible to resend all of them at whim, because it would tantamount to DDoS attack on the entire network. Instead, the Mail Server should engage in peer-to-peer communication with the node, and resend the expired messages directly. The recipient will consume the messages and will not forward them any further. + +In order to facilitate this task, protocol-level support is provided in version 6. New message types are introduced to Whisper v.6: mailRequestCode and p2pCode. + +- mailRequestCode is used by the node to request historic (expired) messages from the Mail Server. The payload of this message should be understood by the Server. The Whisper protocol is entirely agnostic about it. It might contain a time frame, the node's authorization details, filtering information, payment details, etc. + +- p2pCode is a peer-to-peer message, that is not supposed to be forwarded to other peers. It will also bypass the protocol-level checks for expiry and PoW threshold. + +There is MailServer interface defined in the codebase for easy Mail Server implementation. One only needs to implement two functions: + +type MailServer interface { + Archive(env *Envelope) + DeliverMail(whisperPeer *Peer, data []byte) +} + +Archive should just save all incoming messages. +DeliverMail should be able to process the request and send the historic messages to the corresponding peer (with p2pCode), according to additional information in the data parameter. This function will be invoked upon receipt of protocol-level mailRequestCode.
diff --git go-ethereum/docs/docs/_whisper/Whisper-js-example.md celo/docs/docs/_whisper/Whisper-js-example.md new file mode 100644 index 0000000000000000000000000000000000000000..3fb350924f1931776cb782ad0ee56e5fe83f4b08 --- /dev/null +++ celo/docs/docs/_whisper/Whisper-js-example.md @@ -0,0 +1,110 @@ +--- +title: Whisper JavaScript example +sort_key: B +--- + +[This link](https://github.com/gballet/whisper-chat-example) contains a full-fledged example of how to use Whisper in a small chat application. + +The app is a simple Vue single page application that works in several steps. In the first step, the user configures the RPC service that they wish to connect to. Typically, this would be a `geth` client with the `--shh` option enabled. + +Then one can install the application locally by typing: + +``` +$ git clone https://github.com/gballet/whisper-chat-example +$ cd whisper-chat-example +$ npm install +``` + +The application is then started by typing: + +``` +$ npm run dev +``` + +The application will then be available at `http://localhost:8080` (Note the http here, which is because it's a **demo application** and should not be used in production) + +## User workflow + +The app starts by asking the user for their user name and whether they want a symmetric or asymmetric connection. If it's asymmetric, the geth server will propose a public key. If it's symmetric, a key has to be provided. + +Then the user presses "Start" and the conversation begins. + +## The app's state + +```javascript + let data = { + msgs: [], + text: "", + symKeyId: null, + name: "", + asymKeyId: null, + sympw: "", + asym: true, + configured: false, + topic: defaultTopic, + recipientPubKey: defaultRecipientPubKey, + asymPubKey: "" + }; +``` + +This is how the current, transient, state of the application is represented: + + * `msgs` is the list of messages in the current conversation + * `text` contains the text that the current user is typing + * `name` is the name of the current user, which is used to identify them in conversations + * `asymKeyId` and `symKeyId` represent handles to the corresponding keys in `geth`'s memory + * `recipientPubKey` is a hex string representing the public key that an asymmetric message is sent to + * `topic` is a hex string representing the message's topic + * `asymPubKey` is a hex string representing the user's own public key + * `configured` is a flag that is set to true when the user has choosen either a public key or a symmetric key, and a user name + * `sympw` contains the symmetric password + +## The `sendMessage` callback + +The `sendMessage` callback is called every time the user clicks on "Send" or presses the return key. It is responsible for creating the RPC request that instructs the `geth` node to encrypt and send the message. + +```javascript +sendMessage() { + // Start by declaring the message, we picked a JSON format with + // `text` as the content and `name` as the name of the user who + // is sending the message. + let msg = { + text: this.text, + name: this.name + }; + + // (code elided for clarity) + // ... + + // Create the data object that will be sent to the RPC endpoint. + let postData = { + ttl: 7, + topic: '0x07678231', + powTarget: 2.01, + powTime: 100, + payload: encodeToHex(JSON.stringify(msg)), + }; + + // Set the appropriate key id. + if (this.asym) { + postData.pubKey = this.recipientPubKey; + postData.sig = this.asymKeyId; + } else + postData.symKeyID = this.symKeyId; + + // Perform the RPC call that will tell the node to forward + // that message to all its neighboring nodes. + this.shh.post(postData); + + // (code elided for clarity) + // ... +} +``` + +The `msg` object is created. The format chosen for the object is specific to this demo application. It just contains a text and the name of the sender. This is obviously not secure enough for a real-world application. + +That object is converted to a string and then encoded as a hexadecimal string, in the `payload` member of the request's `POST` data object. Other fields include the `topic` of the message, how much work the sending server should do and other parameters. + +Next, depending whether the "asymmetric" checkbox has been ticked, the value of `this.asym` will be true or false. Based on this, the system will update the request object with the relevant information. + +Finally, the request is being sent with `this.shh.post(postData)`, which calls Web3's `shh.post` function to send the message.
diff --git go-ethereum/docs/docs/_whisper/Whisper-v6-RPC-API.md celo/docs/docs/_whisper/Whisper-v6-RPC-API.md new file mode 100644 index 0000000000000000000000000000000000000000..966c367fbd50cb3ef9ea906bae2415aaee643db4 --- /dev/null +++ celo/docs/docs/_whisper/Whisper-v6-RPC-API.md @@ -0,0 +1,786 @@ +--- +title: Whisper RPC API 6.0 +sort_key: C +--- + +This is the proposed API for whisper v6. + +### Specs + +- [shh_version](#shh_version) +- [shh_info](#shh_info) +- [shh_setMaxMessageSize](#shh_setmaxmessagesize) +- [shh_setMinPoW](#shh_setminpow) +- [shh_markTrustedPeer](#shh_marktrustedpeer) +- [shh_newKeyPair](#shh_newkeypair) +- [shh_addPrivateKey](#shh_addprivatekey) +- [shh_deleteKeyPair](#shh_deletekeypair) +- [shh_hasKeyPair](#shh_haskeypair) +- [shh_getPublicKey](#shh_getpublickey) +- [shh_getPrivateKey](#shh_getprivatekey) +- [shh_newSymKey](#shh_newsymkey) +- [shh_addSymKey](#shh_addsymkey) +- [shh_generateSymKeyFromPassword](#shh_generatesymkeyfrompassword) +- [shh_hasSymKey](#shh_hassymkey) +- [shh_getSymKey](#shh_getsymkey) +- [shh_deleteSymKey](#shh_deletesymkey) +- [shh_subscribe](#shh_subscribe) +- [shh_unsubscribe](#shh_unsubscribe) +- [shh_newMessageFilter](#shh_newmessagefilter) +- [shh_deleteMessageFilter](#shh_deletemessagefilter) +- [shh_getFilterMessages](#shh_getfiltermessages) +- [shh_post](#shh_post) + + +*** + +#### shh_version + +Returns the current semver version number. + +##### Parameters + +none + +##### Returns + +`String` - The version number. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_version","params":[],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": "6.0" +} +``` + +*** + +#### shh_info + +Returns diagnostic information about the whisper node. + +##### Parameters + +none + +##### Returns + +`Object` - diagnostic information with the following properties: + - `minPow` - `Number`: current minimum PoW requirement. + - `maxMessageSize` - `Float`: current messgae size limit in bytes. + - `memory` - `Number`: Memory size of the floating messages in bytes. + - `messages` - `Number`: Number of floating messages. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_info","params":[],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": { + "minPow": 12.5, + "maxMessageSize": 20000, + "memory": 10000, + "messages": 20, + } +} +``` + +*** + +#### shh_setMaxMessageSize + +Sets the maximal message size allowed by this node. +Incoming and outgoing messages with a larger size will be rejected. +Whisper message size can never exceed the limit imposed by the underlying P2P protocol (10 Mb). + +##### Parameters + +1. `Number`: Message size in bytes. + +##### Returns + +`Boolean`: (`true`) on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_setMaxMessageSize","params":[234567],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": true +} +``` + +*** + +#### shh_setMinPoW + +Sets the minimal PoW required by this node. + +This experimental function was introduced for the future dynamic adjustment of PoW requirement. If the node is overwhelmed with messages, it should raise the PoW requirement and notify the peers. The new value should be set relative to the old value (e.g. double). The old value could be obtained via shh_info call. + +**Note** This function is currently experimental. + +##### Parameters + +1. `Number`: The new PoW requirement. + +##### Returns + +`Boolean`: `true` on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_setMinPoW","params":[12.3],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": true +} +``` + +*** + +#### shh_markTrustedPeer + +Marks specific peer trusted, which will allow it to send historic (expired) messages. + +**Note** This function is not adding new nodes, the node needs to exists as a peer. + +##### Parameters + +1. `String`: Enode of the trusted peer. + +##### Returns + +`Boolean` (`true`) on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_markTrustedPeer","params":["enode://d25474361659861e9e651bc728a17e807a3359ca0d344afd544ed0f11a31faecaf4d74b55db53c6670fd624f08d5c79adfc8da5dd4a11b9213db49a3b750845e@52.178.209.125:30379"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": true +} +``` + +*** + + +#### shh_newKeyPair + +Generates a new public and private key pair for message decryption and encryption. + +##### Parameters + +none + +##### Returns + +`String`: Key ID on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_newKeyPair","id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": "5e57b9ffc2387e18636e0a3d0c56b023264c16e78a2adcba1303cefc685e610f" +} +``` + +*** + + +#### shh_addPrivateKey + +Stores the key pair, and returns its ID. + +##### Parameters + +1. `String`: private key as HEX bytes. + +##### Returns + +`String`: Key ID on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_addPrivateKey","params":["0x8bda3abeb454847b515fa9b404cede50b1cc63cfdeddd4999d074284b4c21e15"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": "3e22b9ffc2387e18636e0a3d0c56b023264c16e78a2adcba1303cefc685e610f" +} +``` + +*** + +#### shh_deleteKeyPair + +Deletes the specifies key if it exists. + +##### Parameters + +1. `String`: ID of key pair. + +##### Returns + +`Boolean`: `true` on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_deleteKeyPair","params":["5e57b9ffc2387e18636e0a3d0c56b023264c16e78a2adcba1303cefc685e610f"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": true +} +``` + +*** + + +#### shh_hasKeyPair + +Checks if the whisper node has a private key of a key pair matching the given ID. + +##### Parameters + +1. `String`: ID of key pair. + +##### Returns + +`Boolean`: (`true` or `false`) and error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_hasKeyPair","params":["5e57b9ffc2387e18636e0a3d0c56b023264c16e78a2adcba1303cefc685e610f"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": false +} +``` + +*** + +#### shh_getPublicKey + +Returns the public key for identity ID. + +##### Parameters + +1. `String`: ID of key pair. + +##### Returns + +`String`: Public key on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_getPublicKey","params":["86e658cbc6da63120b79b5eec0c67d5dcfb6865a8f983eff08932477282b77bb"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": "0x04d1574d4eab8f3dde4d2dc7ed2c4d699d77cbbdd09167b8fffa099652ce4df00c4c6e0263eafe05007a46fdf0c8d32b11aeabcd3abbc7b2bc2bb967368a68e9c6" +} +``` + +*** + +#### shh_getPrivateKey + +Returns the private key for identity ID. + +##### Parameters + +1. `String`: ID of the key pair. + +##### Returns + +`String`: Private key on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_getPrivateKey","params":["0xc862bf3cf4565d46abcbadaf4712a8940bfea729a91b9b0e338eab5166341ab5"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": "0x234234e22b9ffc2387e18636e0534534a3d0c56b0243567432453264c16e78a2adc" +} +``` + +*** + +#### shh_newSymKey + +Generates a random symmetric key and stores it under an ID, which is then returned. +Can be used encrypting and decrypting messages where the key is known to both parties. + +##### Parameters + +none + +##### Returns + +`String`: Key ID on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_newSymKey", "params": [], "id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": "cec94d139ff51d7df1d228812b90c23ec1f909afa0840ed80f1e04030bb681e4" +} +``` + +*** + +#### shh_addSymKey + +Stores the key, and returns its ID. + +##### Parameters + +1. `String`: The raw key for symmetric encryption as HEX bytes. + +##### Returns + +`String`: Key ID on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_addSymKey","params":["0xf6dcf21ed6a17bd78d8c4c63195ab997b3b65ea683705501eae82d32667adc92"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": "5e57b9ffc2387e18636e0a3d0c56b023264c16e78a2adcba1303cefc685e610f" +} +``` + +*** + +#### shh_generateSymKeyFromPassword + +Generates the key from password, stores it, and returns its ID. + +##### Parameters + +1. `String`: password. + +##### Returns + +`String`: Key ID on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_generateSymKeyFromPassword","params":["test"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": "2e57b9ffc2387e18636e0a3d0c56b023264c16e78a2adcba1303cefc685e610f" +} +``` + +*** + +#### shh_hasSymKey + +Returns true if there is a key associated with the name string. Otherwise, returns false. + +##### Parameters + +1. `String`: key ID. + +##### Returns + +`Boolean` (`true` or `false`) on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_hasSymKey","params":["f6dcf21ed6a17bd78d8c4c63195ab997b3b65ea683705501eae82d32667adc92"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": true +} +``` + + +*** + +#### shh_getSymKey + +Returns the symmetric key associated with the given ID. + +##### Parameters + +1. `String`: key ID. + +##### Returns + +`String`: Raw key on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_getSymKey","params":["f6dcf21ed6a17bd78d8c4c63195ab997b3b65ea683705501eae82d32667adc92"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": "0xa82a520aff70f7a989098376e48ec128f25f767085e84d7fb995a9815eebff0a" +} +``` + + +*** + +#### shh_deleteSymKey + +Deletes the key associated with the name string if it exists. + +##### Parameters + +1. `String`: key ID. + +##### Returns + +`Boolean` (`true` or `false`) on success and an error on failure. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_deleteSymKey","params":["5e57b9ffc2387e18636e0a3d0c56b023264c16e78a2adcba1303cefc685e610f"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": true +} +``` + +*** + +#### shh_subscribe + +Creates and registers a new subscription to receive notifications for inbound whisper messages. Returns the ID of the newly created subscription. + +##### Parameters + +1. `id` - `String`: identifier of function call. In case of Whisper must contain the value "messages". + +2. `Object`. Options object with the following properties: + - `symKeyID` - `String`: ID of symmetric key for message decryption. + - `privateKeyID` - `String`: ID of private (asymmetric) key for message decryption. + - `sig` - `String` (optional): Public key of the signature. + - `minPow` - `Number` (optional): Minimal PoW requirement for incoming messages. + - `topics` - `Array` (optional when asym key): Array of possible topics (or partial topics). + - `allowP2P` - `Boolean` (optional): Indicates if this filter allows processing of direct peer-to-peer messages (which are not to be forwarded any further, because they might be expired). This might be the case in some very rare cases, e.g. if you intend to communicate to MailServers, etc. + +Either `symKeyID` or `privateKeyID` must be present. Can not be both. + +##### Returns + +`String` - The subscription ID on success, the error on failure. + + +##### Notification Return + +`Object`: The whisper message matching the subscription options, with the following parameters: + - `sig` - `String`: Public key who signed this message. + - `recipientPublicKey` - `String`: The recipients public key. + - `ttl` - `Number`: Time-to-live in seconds. + - `timestamp` - `Number`: Unix timestamp of the message genertion. + - `topic` - `String` 4 Bytes: Message topic. + - `payload` - `String`: Decrypted payload. + - `padding` - `String`: Optional padding (byte array of arbitrary length). + - `pow` - `Number`: Proof of work value. + - `hash` - `String`: Hash of the enveloved message. + + +##### Example +``` +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_subscribe","params":["messages", { + topics: ['0x5a4ea131', '0x11223344'], + symKeyID: 'b874f3bbaf031214a567485b703a025cec27d26b2c4457d6b139e56ad8734cea', + sig: '0x048229fb947363cf13bb9f9532e124f08840cd6287ecae6b537cda2947ec2b23dbdc3a07bdf7cd2bfb288c25c4d0d0461d91c719da736a22b7bebbcf912298d1e6', + pow: 12.3(?) + }],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": "02c1f5c953804acee3b68eda6c0afe3f1b4e0bec73c7445e10d45da333616412" +} + + +// Notification Result +{ + "jsonrpc": "2.0", + "method": "shh_subscription", + "params": { + subscription: "02c1f5c953804acee3b68eda6c0afe3f1b4e0bec73c7445e10d45da333616412", + result: { + sig: '0x0498ac1951b9078a0549c93c3f6088ec7c790032b17580dc3c0c9e900899a48d89eaa27471e3071d2de6a1f48716ecad8b88ee022f4321a7c29b6ffcbee65624ff', + recipientPublicKey: null, + ttl: 10, + timestamp: 1498577270, + topic: '0xffaadd11', + payload: '0xffffffdddddd1122', + padding: '0x35d017b66b124dd4c67623ed0e3f23ba68e3428aa500f77aceb0dbf4b63f69ccfc7ae11e39671d7c94f1ed170193aa3e327158dffdd7abb888b3a3cc48f718773dc0a9dcf1a3680d00fe17ecd4e8d5db31eb9a3c8e6e329d181ecb6ab29eb7a2d9889b49201d9923e6fd99f03807b730780a58924870f541a8a97c87533b1362646e5f573dc48382ef1e70fa19788613c9d2334df3b613f6e024cd7aadc67f681fda6b7a84efdea40cb907371cd3735f9326d02854', + pow: 0.6714754098360656, + hash: '0x17f8066f0540210cf67ef400a8a55bcb32a494a47f91a0d26611c5c1d66f8c57' + } + } +} +``` + +*** + +#### shh_unsubscribe + +Cancels and removes an existing subscription. + +##### Parameters + +1. `String`: subscription ID. + +##### Returns + +`Boolean`: `true` or `false`. + +##### Example +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_unsubscribe","params":["02c1f5c953804acee3b68eda6c0afe3f1b4e0bec73c7445e10d45da333616412"],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": true +} +``` + +*** + +#### shh_newMessageFilter + +Create a new filter within the node. This filter can be used to poll for new messages that match the set of criteria. + +##### Parameters + +1. `criteria` - `Object`: see [shh_subscribe](#shh_subscribe) + +##### Returns + +`String`: filter identifier + +##### Example +``` +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_newMessageFilter","params":[{"symKeyID": "3742c75e4232325d54143707e4b73d17c2f86a5e4abe3359021be5653f5b2c81"}],"id":1}' localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"2b47fbafb3cce24570812a82e6e93cd9e2551bbc4823f6548ff0d82d2206b326"} + +``` + + +*** + +#### shh_deleteMessageFilter + +Uninstall a message filter in the node + +##### Parameters + +1. `id` - `String`: filter identifier as returned when the filter was created + +##### Returns + +`Boolean`: `true` on success, error on failure. + +##### Example +``` +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_deleteMessageFilter","params":[{"symKeyID": "3742c75e4232325d54143707e4b73d17c2f86a5e4abe3359021be5653f5b2c81"}],"id":1}' localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result": true} + +``` + + +*** + +#### shh_getFilterMessages + +Retrieve messages that match the filter criteria and are received between the last time this +function was called and now. + +##### Parameters + +1. `id` - `String`: ID of filter that was created with `shh_newMessageFilter` + + +##### Returns + +`Array of messages`: `true` on success and an error on failure. + +`Object`: whisper message: + - `sig` - `String`: Public key who signed this message. + - `ttl` - `Number`: Time-to-live in seconds. + - `timestamp` - `Number`: Unix timestamp of the message generation. + - `topic` - `String` 4 Bytes: Message topic. + - `payload` - `String`: Decrypted payload. + - `padding` - `String`: Optional padding (byte array of arbitrary length). + - `pow` - `Number`: Proof of work value. + - `hash` - `String`: Hash of the enveloped message. + - `recipientPublicKey` - `String`: recipient public key + +##### Example +``` +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_getFilterMessages","params":["2b47fbafb3cce24570812a82e6e93cd9e2551bbc4823f6548ff0d82d2206b326"],"id":1}' + +// Result +{ + "id": 1, + "jsonrpc": "2.0", + "result": [ + { + "hash": "0xe05c4be74d667bd4c57dba2a8dbfb097d6fc2719d5c0d699d2f84a2442a4d8c2", + "padding": "0x6e3e82571c7aa91f2a9e82e20344ede0d1112205555843d9dafffeb1536741a1fca758ff30cc01320bb0775aa5b82b9c9f48deb10bff8b5c61354bf8f95f2ab7289c7894c52e285b1d750ea2ffa78edd374bc7386a901d36a59022d73208c852dedaca8709087693ef6831b861139f42a89263af5931cb2b9253216dc42edc1393afd03940f91c561d20080f7a258aa52d30dcf4b1b921b7c910ad429f73ed9308cb6218537f0444d915e993ba8c9bb00af311aab3574bf1722b5640632bf5bb6b12406e1b89d0c98628117b1d8f55ea6b974e251b34969d4c49dfb6036d40e0d90414c65a8b036ae985396d6a4bf28332676e85dc0a0d352a58680200cc", + "payload": "0xabcd", + "pow": 0.5371803278688525, + "recipientPublicKey": null, + "sig": null, + "timestamp": 1496991875, + "topic": "0x01020304", + "ttl": 50 + }, + { + "hash": "0x4158eb81ad8e30cfcee67f20b1372983d388f1243a96e39f94fd2797b1e9c78e", + "padding": "0xc15f786f34e5cef0fef6ce7c1185d799ecdb5ebca72b3310648c5588db2e99a0d73301c7a8d90115a91213f0bc9c72295fbaf584bf14dc97800550ea53577c9fb57c0249caeb081733b4e605cdb1a6011cee8b6d8fddb972c2b90157e23ba3baae6c68d4f0b5822242bb2c4cd821b9568d3033f10ec1114f641668fc1083bf79ebb9f5c15457b538249a97b22a4bcc4f02f06dec7318c16758f7c008001c2e14eba67d26218ec7502ad6ba81b2402159d7c29b068b8937892e3d4f0d4ad1fb9be5e66fb61d3d21a1c3163bce74c0a9d16891e2573146aa92ecd7b91ea96a6987ece052edc5ffb620a8987a83ac5b8b6140d8df6e92e64251bf3a2cec0cca", + "payload": "0xdeadbeaf", + "pow": 0.5371803278688525, + "recipientPublicKey": null, + "sig": null, + "timestamp": 1496991876, + "topic": "0x01020304", + "ttl": 50 + } + ] +} + +``` + + + + + + + +*** + +#### shh_post + +Creates a whisper message and injects it into the network for distribution. + +##### Parameters + +1. `Object`. Post options object with the following properties: + - `symKeyID` - `String`: ID of symmetric key for message encryption. + - `pubKey` - `String`: public key for message encryption. + - `sig` - `String` (optional): ID of the signing key. + - `ttl` - `Number`: Time-to-live in seconds. + - `topic` - `String` 4 Bytes (mandatory when key is symmetric): Message topic. + - `payload` - `String`: Payload to be encrypted. + - `padding` - `String` (optional): Optional padding (byte array of arbitrary length). + - `powTime` - `Number`: Maximal time in seconds to be spent on proof of work. + - `powTarget` - `Number`: Minimal PoW target required for this message. + - `targetPeer` - `String` (optional): Optional peer ID (for peer-to-peer message only). + +Either `symKeyID` or `pubKey` must be present. Can not be both. + +##### Returns + +`Boolean`: `true` on success and an error on failure. + +##### Example +``` +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"shh_post","params":[{ + pubKey: 'b874f3bbaf031214a567485b703a025cec27d26b2c4457d6b139e56ad8734cea', + ttl: 7, + topic: '0x07678231', + powTarget: 2.01, + powTime: 2, + payload: '0x68656c6c6f' + }],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": true +} +```
diff --git go-ethereum/docs/docs/index.md celo/docs/docs/index.md new file mode 100644 index 0000000000000000000000000000000000000000..ef2f1ff1f3dbba09a7c5515d9c0f8e43b9adf27a --- /dev/null +++ celo/docs/docs/index.md @@ -0,0 +1,17 @@ +--- +title: Celo Documentation +root: .. +--- + +You have found the user manual for celo-blockchain, the Go language implementation of the Celo Protocol, based on geth. + +* [JSON-RPC Server](./rpc/server) +* [JavaScript Console](./interface/javascript-console) + +For the Geth Go API reference and developer documentation head over to +[GoDoc](https://godoc.org/github.com/ethereum/go-ethereum). + +### Other Celo Documentation + +For generic Celo documentation, check the **[Celo docs](https://docs.celo.org)**. +
diff --git go-ethereum/docs/downloads.html celo/docs/downloads.html new file mode 100644 index 0000000000000000000000000000000000000000..8309639fc827149ba43196e36598d9ce926b92e7 --- /dev/null +++ celo/docs/downloads.html @@ -0,0 +1,548 @@ +--- +layout: default +title: Downloads +root: .. +permalink: /downloads/ +css: +- /static/styles/custom/downloads.css +js: +- /static/scripts/filesize.min.js +--- + <div id="loader" class="modal fade" role="dialog" data-backdrop="static" data-keyboard="false"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-body"> + <i class="fa fa-spinner fa-spin" aria-hidden="true"></i> + <span id="loader_label">Retrieving packages from release server...</span> + </div> + </div> + </div> + </div> + + <div class="jumbotron" style="padding-top: 24px; padding-bottom: 24px;"> + <div class="container"> + <h2 style="text-align: center;"> + Download Geth &ndash; <span id="release_title"></span> + <span id="release_notes_toggle"> + &ndash; <a href="#" style='text-decoration: none;'>Release Notes</a> + </span> + </h2> + <div id="release_notes" class="col-md-12 collapse on"></div> + <div style="text-align: center;" class="col-md-12"> + <br/> + <p style="font-size: 23px;">You can download the latest 64-bit stable release of Geth for our primary platforms below. Packages for all supported platforms, as well as develop builds, can be found further down the page. If you're looking to install Geth and/or associated tools via your favorite package manager, please check our <a href="../install">installation</a> guide.</p> + <a id="download_linux" class="btn btn-primary" href="#" role="button" style="margin: 4px;"></a> + <a id="download_darwin" class="btn btn-primary" href="#" role="button" style="margin: 4px;"></a> + <a id="download_windows" class="btn btn-primary" href="#" role="button" style="margin: 4px;"></a> + <a id="download_source" class="btn btn-primary" href="#" role="button" style="margin: 4px;"></a> + </div> + </div> + </div> + + <div class="container"> + <div class="row"> + <div class="col-md-12"> + <h2>Specific Versions</h2> + <p>If you're looking for a specific release, operating system or architecture, below you will find:</p> + <ul> + <li>All stable and develop builds of Geth and tools</li> + <li>Archives for non-primary processor architectures</li> + <li>Android library archives and iOS XCode frameworks</li> + </ul> + <p>Please select your desired platform from the lists below and download your bundle of choice. Please be aware that the <code>MD5</code> checksums are provided by our binary hosting platform (Azure Blobstore) to help check for download errors. <strong>For security guarantees please verify any downloads via the attached PGP signature files</strong> (see <a href="#openpgp_signatures">OpenPGP Signatures</a> for details).</p> + + <h3>Stable releases</h3> + <p>These are the current and previous stable releases of go-ethereum, updated automatically when a new version is tagged in our <a href="https://github.com/ethereum/go-ethereum" targer="_blank">GitHub repository</a>.</p> + + <ul id="dl_stable" class="nav nav-tabs"> + <li><a data-toggle="tab" href="#dl_stable_android">Android</a></li> + <li><a data-toggle="tab" href="#dl_stable_ios">iOS</a></li> + <li><a data-toggle="tab" href="#dl_stable_linux">Linux</a></li> + <li><a data-toggle="tab" href="#dl_stable_darwin">macOS</a></li> + <li><a data-toggle="tab" href="#dl_stable_windows">Windows</a></li> + </ul> + + <div class="tab-content"> + <div id="dl_stable_android" class="table-responsive tab-pane fade"> + <table id="table_stable_android" class="table table-striped table-hover"></table> + </div> + <div id="dl_stable_ios" class="table-responsive tab-pane fade"> + <table id="table_stable_ios" class="table table-striped table-hover"></table> + </div> + <div id="dl_stable_linux" class="table-responsive tab-pane fade in active"> + <table id="table_stable_linux" class="table table-striped table-hover"></table> + </div> + <div id="dl_stable_darwin" class="table-responsive tab-pane fade"> + <table id="table_stable_darwin" class="table table-striped table-hover"></table> + </div> + <div id="dl_stable_windows" class="table-responsive tab-pane fade"> + <table id="table_stable_windows" class="table table-striped table-hover"></table> + </div> + </div> + + <h3>Develop builds</h3> + <p>These are the develop snapshots of go-ethereum, updated automatically when a new commit is pushed into our <a href="https://github.com/ethereum/go-ethereum" targer="_blank">GitHub repository</a>.</p> + + <ul id="dl_develop" class="nav nav-tabs"> + <li><a data-toggle="tab" href="#dl_develop_android">Android</a></li> + <li><a data-toggle="tab" href="#dl_develop_ios">iOS</a></li> + <li><a data-toggle="tab" href="#dl_develop_linux">Linux</a></li> + <li><a data-toggle="tab" href="#dl_develop_darwin">macOS</a></li> + <li><a data-toggle="tab" href="#dl_develop_windows">Windows</a></li> + </ul> + + <div class="tab-content"> + <div id="dl_develop_android" class="table-responsive tab-pane fade"> + <table id="table_develop_android" class="table table-striped table-hover"></table> + </div> + <div id="dl_develop_ios" class="table-responsive tab-pane fade"> + <table id="table_develop_ios" class="table table-striped table-hover"></table> + </div> + <div id="dl_develop_linux" class="table-responsive tab-pane fade in active"> + <table id="table_develop_linux" class="table table-striped table-hover"></table> + </div> + <div id="dl_develop_darwin" class="table-responsive tab-pane fade"> + <table id="table_develop_darwin" class="table table-striped table-hover"></table> + </div> + <div id="dl_develop_windows" class="table-responsive tab-pane fade"> + <table id="table_develop_windows" class="table table-striped table-hover"></table> + </div> + </div> + + <a name="openpgp_signatures"></a> + <h2>OpenPGP Signatures</h2> + <p>All the binaries available from this page are signed via our build server PGP keys:</p> + <div class="table-responsive"> + <table id="table_develop_android" class="table table-striped table-hover"> + <thead><tr> + <th>Build Server</th> + <th>Unique ID</th> + <th>OpenPGP Key</th> + <th>Fingerprint</th> + </tr></thead> + <tbody> + <tr> + <td>Android Builder</td> + <td>Go Ethereum Android Builder &lt;geth-ci@ethereum.org&gt;</td> + <td><a style='font-family: monospace; text-decoration: none;' href="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x70AD154BF9585DE6" target="_blank">F9585DE6</a></td> + <td style='font-family: monospace;'>8272 1824 F4D7 46E0 B5A7 AB95 70AD 154B F958 5DE6</td> + </tr> + <tr> + <td>iOS Builder</td> + <td>Go Ethereum iOS Builder &lt;geth-ci@ethereum.org&gt;</td> + <td><a style='font-family: monospace; text-decoration: none;' href="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xF29DEFAFC2FF8BBF" target="_blank">C2FF8BBF</a></td> + <td style='font-family: monospace;'>70AD EB8F 3BC6 6F69 0256 4D88 F29D EFAF C2FF 8BBF</td> + </tr> + <tr> + <td>Linux Builder</td> + <td>Go Ethereum Linux Builder &lt;geth-ci@ethereum.org&gt;</td> + <td><a style='font-family: monospace; text-decoration: none;' href="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA61A13569BA28146" target="_blank">9BA28146</a></td> + <td style='font-family: monospace;'>FDE5 A1A0 44FA 13D2 F7AD A019 A61A 1356 9BA2 8146</td> + </tr> + <tr> + <td>macOS Builder</td> + <td>Go Ethereum macOS Builder &lt;geth-ci@ethereum.org&gt;</td> + <td><a style='font-family: monospace; text-decoration: none;' href="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x558915E17B9E2481" target="_blank">7B9E2481</a></td> + <td style='font-family: monospace;'>6D1D AF5D 0534 DEA6 1AA7 7AD5 5589 15E1 7B9E 2481</td> + </tr> + <tr> + <td>Windows Builder</td> + <td>Go Ethereum Windows Builder &lt;geth-ci@ethereum.org&gt;</td> + <td><a style='font-family: monospace; text-decoration: none;' href="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x9417309ED2A67EAC" target="_blank">D2A67EAC</a></td> + <td style='font-family: monospace;'>C4B3 2BB1 F603 4241 A9E6 50A1 9417 309E D2A6 7EAC</td> + </tr> + </tbody> + </table> + </div> + <br/> + <div class="table-responsive"> + <table id="table_develop_android" class="table table-striped table-hover"> + <thead><tr> + <th>Developer</th> + <th>Unique ID</th> + <th>OpenPGP Key</th> + <th>Fingerprint</th> + </tr></thead> + </tbody> + <tr> + <td>Felix Lange</td> + <td>Felix Lange &lt;fjl@ethereum.org&gt;</td> + <td><a style='font-family: monospace; text-decoration: none;' href="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x337E68FCE058A81C" target="_blank">E058A81C</a></td> + <td style='font-family: monospace;'>6047 0B71 5865 392D E43D 75A3 337E 68FC E058 A81C</td> + </tr> + <tr> + <td>Jeffrey Wilcke</td> + <td>Jeffrey Wilcke &lt;jeffrey@ethereum.org&gt;</td> + <td><a style='font-family: monospace; text-decoration: none;' href="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6766F7185BE4B0C" target="_blank">85BE4B0C</a></td> + <td style='font-family: monospace;'>EF8F A45D 7698 A065 F1AB 3D5C A676 6F71 85BE 4B0C</td> + </tr> + <tr> + <td>Martin Holst Swende</td> + <td>Martin Holst Swende &lt;martin.swende@ethereum.org&gt;</td> + <td><a style='font-family: monospace; text-decoration: none;' href="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x683B438C05A5DDF0" target="_blank">05A5DDF0</a></td> + <td style='font-family: monospace;'>CA99 ABB5 B36E 24AD 5DA0 FD40 683B 438C 05A5 DDF0</td> + </tr> + <tr> + <td>Nick Johnson</td> + <td>Nick Johnson &lt;nick@ethereum.org&gt;</td> + <td><a style='font-family: monospace; text-decoration: none;' href="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x6D9649932F295D12" target="_blank">2F295D12</a></td> + <td style='font-family: monospace;'>DD7A 2E03 0B2F AF1C 3121 6FE6 6D96 4993 2F29 5D12</td> + </tr> + <tr> + <td>Péter Szilágyi</td> + <td>Péter Szilágyi &lt;peter@ethereum.org&gt;</td> + <td><a style='font-family: monospace; text-decoration: none;' href="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x119A76381CCB7DD2" target="_blank">1CCB7DD2</a></td> + <td style='font-family: monospace;'>4948 43FC E822 1C4C 86AB 5E2F 119A 7638 1CCB 7DD2</td> + </tr> + <tr> + <td>Viktor Trón</td> + <td>Viktor Tron &lt;viktor@ethereum.org&gt;</td> + <td><a style='font-family: monospace; text-decoration: none;' href="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x5219CDE299E23788" target="_blank">99E23788</a></td> + <td style='font-family: monospace;'>446D 939E 45C0 DC1E 2A8E 2F62 5219 CDE2 99E2 3788</td> + </tr> + </tbody> + </table> + </div> + + <h3>Importing keys and verifying builds</h3> + <p>You can import the build server public keys by grabbing the individual keys directly from the keyserver network:</p> + <pre>gpg --recv-keys F9585DE6 C2FF8BBF 9BA28146 7B9E2481 D2A67EAC</pre> + <p>Similarly you can import all the developer public keys by grabbing them directly from the keyserver network:</p> + <pre>gpg --recv-keys E058A81C 85BE4B0C 05A5DDF0 2F295D12 1CCB7DD2 99E23788</pre> + <br/> + <p>From the download listings above you should see a link both to the downloadable archives as well as detached signature files. To verify the authenticity of any downloaded data, grab both files and then run:</p> + <pre>gpg --verify geth-linux-amd64-1.5.0-d0c820ac.tar.gz.asc</pre> + <p>Note that you must use the name of the signature file, and you should use the one that's appropriate to the download you're verifying.</p> + </div> + </div> + </div> + + <script type="text/javascript"> + var myos = "linux"; + if (navigator.appVersion.indexOf("Win") != -1) { + myos = "windows"; + } else if (navigator.appVersion.indexOf("Mac") != -1) { + myos = "darwin"; + } else if (navigator.appVersion.indexOf("Linux") != -1) { + myos = "linux"; + } + $('#dl_stable a[href="#dl_stable_' + myos + '"]').tab('show'); + $('#dl_develop a[href="#dl_develop_' + myos + '"]').tab('show'); + </script> + + <script type="text/javascript"> + // Maintain a set of downloadable resources to cross check URL liveliness later + var resources = {}; + + // Maintain a counter for external requests to show a loader modal during + $('#loader').modal('show'); + $('#loader').attr('data-reqs', '3'); + + var requestDone = function() { + var reqs = $('#loader').data('reqs'); + if (reqs == 1) { + // All requests done, check which primary download buttons can be activated + var prefix = "https://gethstore.blob.core.windows.net/builds/"; + + if (!($('#download_linux').attr("href").slice(prefix.length) in resources)) { + $('#download_linux').addClass("disabled"); + } + if (!($('#download_darwin').attr("href").slice(prefix.length) in resources)) { + $('#download_darwin').addClass("disabled"); + } + if (!($('#download_windows').attr("href").slice(prefix.length) in resources)) { + $('#download_windows').addClass("disabled"); + } + // Finally hide the load screen + $('#loader').modal('hide'); + } + $('#loader').data('reqs', reqs-1); + } + </script> + + <script type="text/javascript"> + // Create the blob retriever that iterates over all the pages of the blobstore. + var blobs = []; + var retrieveBlobs = function(marker, finished) { + // Generate the blob listing URL and request it from Azure + var url = 'https://gethstore.blob.core.windows.net/builds?restype=container&comp=list&maxresults=5000&prefix=geth-'; + if (marker != "") { + url += "&marker=" + marker; + } + $.ajax({ + url: url, + type: 'GET', + error: function() { + alert("Failed to load releases!"); + }, + dataType: 'xml', + success: function(data) { + // List of blobs retrieved, acumulate them first of all + Array.prototype.push.apply(blobs, $(data).find('Blob')); + + // If there are more pages, load them all + marker = $($(data).find('NextMarker')[0]).text(); + if (marker != "") { + retrieveBlobs(marker, finished); + } else { + finished(blobs); + } + } + }); + } + // Start retrieving the release blobs and populate the page on success + retrieveBlobs("", function() { + // Define the release tables + var releases = { + stable: [], + develop: [], + cross: [] + }; + var signatures = {}; + + // Iterate over all the blobs and populate the tables + for (var i = 0; i < blobs.length; i++) { + // Gather all available resources to later inspection + var name = $($(blobs[i]).find('Name')[0]).text(); + resources[name] = true; + + // Skip any signatures, those are assumed implicitly + if (name.endsWith(".asc")) { + signatures[name] = true; + continue; + } + // Otherwise add an entry to one of the release tables + var parts = name.split("-"); + var date = parts[parts.length-1].split(".")[0]; + if (date.length != 14) { + date = parts[parts.length-2]; + } + if (date.length != 14) { + date = new Date($($(blobs[i]).find('Last-Modified')[0]).text()); + } else { + date = moment(date, "YYYYMMDDhhmmss").toDate(); + } + var size = $($(blobs[i]).find('Content-Length')[0]).text(); + + var sum = window.atob($($(blobs[i]).find('Content-MD5')[0]).text()); + sum = sum.split('').map(function (char) { return ('0' + char.charCodeAt(0).toString(16)).slice(-2); }).join(''); + + var parts = name.split("-"); + var commit = parts[parts.length - 1].split(".")[0]; + + if (name.includes("unstable")) { + releases.develop.push({name: name, commit: commit, date: date, size: size, sum: sum}); + } else { + releases.stable.push({name: name, commit: commit, date: date, size: size, sum: sum}); + } + } + // Generate the actual release HTML tables + var groups = ["stable", "develop"]; + var oses = ["android", "ios", "linux", "darwin", "windows"]; + + for (var i = 0; i < groups.length; i++) { + // Fetch and sort the releases in the given group + var group = groups[i]; + + var bins = releases[group]; + bins.sort(function compare(a,b) { return b.date - a.date; }); + + // Split up the releases into different OSes + for (var j = 0; j < oses.length; j++) { + var os = oses[j]; + + // Gather all the releases belonging to this group and operating system, split by version + var versions = []; + + for (var k = 0; k < bins.length; k++) { + if (bins[k].name.includes(os)) { + var parts = bins[k].name.split("-").slice(1); + + // Assemble the name of the download with the version string + var name = "Geth "; + if (parts[0] == "alltools") { + name += "&amp; Tools "; + parts = parts.slice(1); + } + name += parts[2]; + + // Extract a user friendly archive type + var kind = "Archive"; + if (os == "android" || os == "ios") { + kind = "Library"; + } else if (os == "windows" && bins[k].name.split('.').pop() == "exe") { + kind = "Installer"; + } + // Extract the architecture and make it user friendly + var arch = parts[1]; + switch (arch) { + case "386": + arch = "32-bit"; + break; + case "amd64": + arch = "64-bit"; + break; + case "arm5": + arch = "ARMv5"; + break; + case "arm6": + arch = "ARMv6"; + break; + case "arm7": + arch = "ARMv7"; + break; + case "arm64": + arch = "ARM64"; + break; + case "mips": + arch = "MIPS32"; + break; + case "mipsle": + arch = "MIPS32(le)"; + break; + case "mips64": + arch = "MIPS64"; + break; + case "mips64le": + arch = "MIPS64(le)"; + break; + } + var primary = (os == "android" && arch == "all") || (os == "ios" && arch == "all") || + (os == "linux" && arch == "64-bit") || (os == "darwin" && arch == "64-bit") || (os == "windows" && arch == "64-bit" && kind == "Installer"); + + // Extract the commit hash from the download name + parts = parts.slice(3); + if (parts[0] == "unstable") { + parts = parts.slice(1); + } + var commit = parts[0].split(".")[0]; + + // Figure out whether a signature is available + var sig = (bins[k].name+".asc") in signatures; + + if (versions.length == 0 || versions[versions.length - 1].commit != commit) { + versions.push({commit: commit, bins: []}); + } + versions[versions.length - 1].bins.push({ + file: bins[k].name, date: bins[k].date, sum: bins[k].sum, size: bins[k].size, + name: name, commit: commit, kind: kind, arch: arch, primary: primary, sig: sig + }); + } + } + // Look up the HTML table corresponding to the [group || os] and empty it + var table = $('#table_' + group + "_" + os); + table.empty(); + + $("<thead><tr>" + + " <th class='text-center'>Release</th>" + + " <th class='text-center'>Commit</th>" + + " <th class='text-center'>Kind</th>" + + " <th class='text-center'>Arch</th>" + + " <th class='text-center'>Size</th>" + + " <th class='text-center'>Published</th>" + + " <th class='text-center'>Signature</th>" + + " <th class='text-center'>Checksum (MD5)</th>" + + "</tr></thead>").appendTo(table); + var body = $("<tbody></tbody>").appendTo(table); + + // Generate the contents for the HTML table + var collapsed = false; + var entires = 0; + + for (var k = 0; k < versions.length; k++) { + // Sort the downloads in the version group by name and architecture + var verbins = versions[k].bins; + verbins.sort(function(a, b) { + if (a.name < b.name) return 1; + if (a.name > b.name) return -1; + if (a.arch < b.arch) return -1; + if (a.arch > b.arch) return 1; + return 0; + }); + // Iterate over the binaries and display them + for (var l = 0; l < verbins.length; l++) { + // Retrieve the current archive and decide on its recentness + var bin = verbins[l]; + + // Append the archive to the download table + $("<tr style='text-align: center; " + (k == 0 && bin.primary ? " font-weight: bold;" : "") + "' class='" + (group == "develop" && k == 0 ? "latest" : "") + (k >= 2 && entires >= 10 ? " collapse out" : "") + "'>" + + " <td><a href='https://gethstore.blob.core.windows.net/builds/" + bin.file + "' style='text-decoration: none;'>" + bin.name + "</a></td>" + + " <td><a href='https://github.com/ethereum/go-ethereum/tree/" + bin.commit + "' target='_blank' style='text-decoration: none; font-family: monospace;'>" + bin.commit + "&hellip;</a></td>" + + " <td>" + bin.kind + "</td>" + + " <td>" + bin.arch + "</td>" + + " <td>" + filesize(bin.size) + "</td>" + + " <td>" + moment(bin.date).calendar() + "</td>" + + " <td>" + (bin.sig ? "<a href='https://gethstore.blob.core.windows.net/builds/" + bin.file + ".asc' style='text-decoration: none;'>Signature</a>" : "Unavailable") + "</td>" + + " <td style='font-family: monospace;'>" + bin.sum + "</td>" + + "</tr>").appendTo(body); + + // If we've displayed at least 3 versions and the table's getting long, collapse + if (k >= 2 && !collapsed && entires >= 10) { + $("<tr style='text-align: center;'>" + + " <td colspan='8'><a class='btn btn-success btn-xs'>Show older releases</a></td>" + + "</tr>").appendTo(table); + $(table.find('.btn')).click(function(){ + $(this).parent().parent().toggle(); + $(this).parent().parent().parent().find('.collapse').toggle(); + }); + collapsed = true; + } + entires++; + } + } + } + } + // Mark the request done to possibly hide the loading page + requestDone(); + }); + </script> + + <script type="text/javascript"> + // Retrieve the latest release from GitHub for the ribbon and primary buttons + $.ajax({ + url: 'https://api.github.com/repos/celo-org/celo-blockchain/releases/latest', + error: function() { + alert("Failed to load latest release!"); + }, + success: function(release) { + // Render the release notes and the page title + $('#release_title').html(release.name); + $('#release_notes').html("<br/>" + marked(release.body)); + emojify.setConfig({img_dir: '../static/images/emoji'}); + emojify.run(document.getElementById('release_notes')); + + $('#release_notes_toggle').click(function(){ + $('#release_notes_toggle').toggle(); + $('#release_notes').slideToggle(); + }); + $('#release_version').html(release.tag_name); + + // Retrieve the commit of the latest release for the download links + $.ajax({ + url: 'https://api.github.com/repos/celo-org/celo-blockchain/commits/' + release.tag_name, + error: function() { + alert("Failed to load latest release!"); + }, + success: function(commit) { + // Update the primary download buttons + var version = release.tag_name.slice(1); + var commit = commit.sha.slice(0, 8); + + $('#download_linux').attr("href", "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-" + version + "-" + commit + ".tar.gz"); + $('#download_darwin').attr("href", "https://gethstore.blob.core.windows.net/builds/geth-darwin-amd64-" + version + "-" + commit + ".tar.gz"); + $('#download_windows').attr("href", "https://gethstore.blob.core.windows.net/builds/geth-windows-amd64-" + version + "-" + commit + ".exe"); + $('#download_source').attr("href", "https://github.com/ethereum/go-ethereum/archive/" + release.tag_name + ".tar.gz"); + + $('#download_linux').html("<i class='fa fa-linux' aria-hidden='true'></i> Geth " + version + " for Linux"); + $('#download_darwin').html("<i class='fa fa-apple' aria-hidden='true'></i> Geth " + version + " for macOS"); + $('#download_windows').html("<i class='fa fa-windows' aria-hidden='true'></i> Geth " + version + " for Windows"); + $('#download_source').html("<i class='fa fa-code-fork' aria-hidden='true'></i> Geth " + version + " sources"); + + // Mark the request done to possibly hide the loading page + requestDone(); } + }); + // Mark the request done to possibly hide the loading page + requestDone(); + } + }); + </script>
diff --git go-ethereum/docs/guide/index.html celo/docs/guide/index.html new file mode 100644 index 0000000000000000000000000000000000000000..2ab03a4fe7ca443c94c78254ebaa035f62021c3d --- /dev/null +++ celo/docs/guide/index.html @@ -0,0 +1,140 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <title>Go Ethereum Guide</title> + <link rel="icon" type="image/png" href="../static/images/favicon.png" /> + + <link href="../static/styles/bootstrap.min.css" rel="stylesheet" /> + <link href="../static/styles/flatly.min.css" rel="stylesheet" /> + <link href="../static/styles/font-awesome.min.css" rel="stylesheet" /> + <link href="../static/styles/highlight.min.css" rel="stylesheet" /> + + <link href="../static/styles/custom/common.css" rel="stylesheet" /> + + <script src="../static/scripts/jquery.min.js"></script> + <script src="../static/scripts/bootstrap.min.js"></script> + <script src="../static/scripts/moment.min.js"></script> + <script src="../static/scripts/marked.min.js"></script> + <script src="../static/scripts/emojify.min.js"></script> + <script src="../static/scripts/highlight.min.js"></script> + <script src="../static/scripts/highlight-go.min.js"></script> + <script src="../static/scripts/highlight-java.min.js"></script> + <script src="../static/scripts/highlight-gradle.min.js"></script> + <script src="../static/scripts/highlight-swift.min.js"></script> + + <script src="../static/scripts/custom/polyfills.js"></script> + </head> + + <body> + <nav class="navbar navbar-default navbar-fixed-top"> + <div class="container"> + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" href="../">Go Ethereum</a> + </div> + <div id="navbar" class="navbar-collapse collapse"> + <ul class="nav navbar-nav"> + <li><a href="../install/">Install</a></li> + <li><a href="../downloads/">Downloads</a></li> + <li class="active"><a href="../guide/">Guide</a></li> + </ul> + </div> + </div> + </nav> + + <div class="container" style="padding-top: 24px;"> + <div class="row"> + <div class="col-md-3" id="toc" style="padding-top: 16px;"></div> + <div class="col-md-9" id="content"></div> + </div> + <hr/> + <footer> + <p>&copy; 2013-2016. The go-ethereum Authors.</p> + </footer> + </div> + + <script type="text/javascript"> + // Download the developer guide ToC and convert it into a guide sidebar + $.ajax({ + url: 'https://raw.githubusercontent.com/wiki/celo-org/celo-blockchain/Developer-Guide.md', + error: function() { + alert("Failed to load latest release!"); + }, + dataType: 'text', + success: function(data) { + // Iterate over all the lines of the sidebar markdown + var lines = data.split("\n"); + + var toc = $("#toc"); + var menu; + + var head; + for (var i = 0; i < lines.length; i++) { + // Skip any empty lines + var line = lines[i].trim(); + if (line == "") { + continue; + } + // If the line is a heading, add a new menu entry + if (line[0] == "#") { + while (line.length > 0 && line[0] == "#") { + line = line.slice(1); + } + head = line.trim(); + menu = $("<div class='list-group'></div>").appendTo(toc); + $("<a href='#' class='list-group-item active'>" + head + "</a>").appendTo(menu); + continue; + } + // Otherwise if the line is a link, add a new entry to the sub-menu + if (line[0] == "[") { + var name = /\[(.*)\]/g.exec(line)[1]; + var id = (head + "-" + name).split(" ").join("-"); + + var link = /\((.*)\)/g.exec(line)[1]; + link = "https://raw.githubusercontent.com/wiki/" + link.slice("https://github.com/".length).replace("wiki/", "") + ".md"; + + var item = $("<a id='menu-" + id + "' href='#" + id + "' class='list-group-item'>" + name + "</a>").appendTo(menu); + $(item).click(function(id, head, name, link) { + return function() { + $.ajax({ + url: link, + error: function() { + $('#content').html("<div class='alert alert-dismissible alert-danger'>Failed to load guide page \"" + name + "\". Please report this issue on our <a href='https://github.com/ethereum/go-ethereum/issues' class='alert-link'>bug tracker</a>. Thank you!</div>"); + }, + dataType: 'text', + success: function(data) { + // Load the wiki page content into the current page + $('#content').html("<h1>" + head + ": " + name + "</h1>" + marked(data)); + emojify.setConfig({img_dir: '../static/images/emoji'}); + emojify.run(document.getElementById('content')); + + // Run the syntax highlighter on all code blocks + $('pre code').each(function(i, block) { + hljs.highlightBlock(block); + }); + } + }); + }; + }(id, head, name, link)); + } + } + // Menu constructed, load the specified entry, or first if non existent + if (window.location.hash.length > 1) { + $('#menu-' + window.location.hash.split('#')[1])[0].click(); + } else { + $(toc.children()[0]).children()[1].click(); + } + } + }); + </script> + </body> +</html>
diff --git go-ethereum/docs/index.html celo/docs/index.html new file mode 100644 index 0000000000000000000000000000000000000000..306d94a35f1d94966d825b15cd69a4c0bf7cf32f --- /dev/null +++ celo/docs/index.html @@ -0,0 +1,60 @@ +--- +layout: default +root: ./ +css: +- /static/styles/custom/home.css +--- + <div class="jumbotron"> + <div class="container mascot"> + <h1>Celo Blockchain</h1> + <h2>Official Go implementation of the Celo protocol</h2> + <br/> + <a href="https://github.com/ethereum/go-ethereum" target="_blank" class="btn btn-success" style="margin: 4px;"><i class="fa fa-github" aria-hidden="true"></i> View on GitHub</a> + <a href="http://chat.celo.org" target="_blank" class="btn btn-success" style="margin: 4px;"><i class="fa fa-user-circle" aria-hidden="true"></i> Chat on Discord</a> + </div> + </div> + + <div class="container mascot-mobile"> + <img src="./static/images/mascot.png" alt="Geth mascot" height="200px" /> + </div> + + <div class="container marketing"> + <div class="row featurette"> + <div class="col-md-7"> + <h2 class="featurette-heading">What is Celo?</h2> + <p class="lead">Celo is an open platform that makes financial tools accessible to anyone with a mobile phone</p> + <p class="lead">See <a href="https://celo.org" target="_blank">our website</a> or <a href="https://docs.celo.org/" target="_blank">read the docs</a> for more info!</p> + </div> + <div class="col-md-5 hide-on-small-screen"> + <img class="featurette-image center-block" src="./static/images/celo-logo.png" alt="Celo Logo" height="300px" style="width: 40vw;"> + </div> + </div> + <hr/> + <div class="row featurette"> + <div class="col-md-7 col-md-push-5"> + <h2 class="featurette-heading">What is Celo Blockchain?</h2> + <p class="lead"> + Celo Blockchain is the primary implementaion of the Celo protocol. + It is based on <a href="https://github.com/ethereum/go-ethereum">Geth</a>, the Ethereum client, and it will be refered to as "geth" throught the docs. + + These docs are fork of the go-ethereum documentation, and although much of it will be the same, they may not always be accurate, but we are working on updating them over time. + Please <a href="https://github.com/ethereum/go-ethereum/issues/new">let us know</a> if there are any issues. + </p> + + <p class="lead">See <a href="https://github.com/ethereum/go-ethereum" target="_blank">our repository</a> for the code!</p> + </div> + <div class="col-md-5 col-md-pull-7 hide-on-small-screen"> + <img class="featurette-image center-block" src="./static/images/mascot.png" alt="Geth mascot" height="300px"> + </div> + </div> + <!-- TODO: See if this is still useful + <hr/> + <div class="row featurette"> + <div class="col-md-7"> + <h2 class="featurette-heading">How can I use it?</h2> + <p class="lead">Go Ethereum is available either as a standalone client called Geth that you can install on pretty much any operating system, or as a library that you can embed in your Go, Android or iOS projects.</p> + <p class="lead">See our <a href="./install">installation guide</a> or our <a href="https://github.com/ethereum/go-ethereum/wiki" target="_blank">wiki pages</a> for details!</p> + </div> + </div> + --> + </div>
diff --git go-ethereum/docs/install.md celo/docs/install.md new file mode 100644 index 0000000000000000000000000000000000000000..f67fd4cd43e0b3953e1ae70916f0abd882ee7a0b --- /dev/null +++ celo/docs/install.md @@ -0,0 +1,6 @@ +--- +title: Install +root: .. +permalink: /install/ +--- +See [Installing Geth](../docs/install-and-build/installing-geth). \ No newline at end of file
diff --git go-ethereum/docs/params.json celo/docs/params.json new file mode 100644 index 0000000000000000000000000000000000000000..608c9534a0e92e30f10e393ba95090931ef28def --- /dev/null +++ celo/docs/params.json @@ -0,0 +1 @@ +{"name":"Celo Blockchain","tagline":"Official golang implementation of the Celo protocol","body":"","google":"","note":"Don't delete this file! It's used internally to help with page regeneration."}
diff --git go-ethereum/docs/postmortems/2021-08-22-split-postmortem.md celo/docs/postmortems/2021-08-22-split-postmortem.md deleted file mode 100644 index 429f22d70afb191f577c839289d749376b87983c..0000000000000000000000000000000000000000 --- go-ethereum/docs/postmortems/2021-08-22-split-postmortem.md +++ /dev/null @@ -1,266 +0,0 @@ -# Minority split 2021-08-27 post mortem - -This is a post-mortem concerning the minority split that occurred on Ethereum mainnet on block [13107518](https://etherscan.io/block/13107518), at which a minority chain split occurred. - -## Timeline - - -- 2021-08-17: Guido Vranken submitted bounty report. Investigation started, root cause identified, patch variations discussed. -- 2021-08-18: Made public announcement over twitter about upcoming security release upcoming Tuesday. Downstream projects were also notified about the upcoming patch-release. -- 2021-08-24: Released [v1.10.8](https://github.com/ethereum/go-ethereum/releases/tag/v1.10.8) containing the fix on Tuesday morning (CET). Erigon released [v2021.08.04](https://github.com/ledgerwatch/erigon/releases/tag/v2021.08.04). -- 2021-08-27: At 12:50:07 UTC, issue exploited. Analysis started roughly 30m later, - - - -## Bounty report - -### 2021-08-17 RETURNDATA corruption via datacopy - -On 2021-08-17, Guido Vranken submitted a report to bounty@ethereum.org. This coincided with a geth-meetup in Berlin, so the geth team could fairly quickly analyse the issue. - -He submitted a proof of concept which called the `dataCopy` precompile, where the input slice and output slice were overlapping but shifted. Doing a `copy` where the `src` and `dest` overlaps is not a problem in itself, however, the `returnData`slice was _also_ using the same memory as a backing-array. - -#### Technical details - -During CALL-variants, `geth` does not copy the input. This was changed at one point, to avoid a DoS attack reported by Hubert Ritzdorf, to avoid copying data a lot on repeated `CALL`s -- essentially combating a DoS via `malloc`. Further, the datacopy precompile also does not copy the data, but just returns the same slice. This is fine so far. - -After the execution of `dataCopy`, we copy the `ret` into the designated memory area, and this is what causes a problem. Because we're copying a slice of memory over a slice of memory, and this operation modifies (shifts) the data in the source -- the `ret`. So this means we wind up with corrupted returndata. - - -``` -1. Calling datacopy - - memory: [0, 1, 2, 3, 4] - in (mem[0:4]) : [0,1,2,3] - out (mem[1:5]): [1,2,3,4] - -2. dataCopy returns - - returndata (==in, mem[0:4]): [0,1,2,3] - -3. Copy in -> out - - => memory: [0,0,1,2,3] - => returndata: [0,0,1,2] -``` - - -#### Summary - -A memory-corruption bug within the EVM can cause a consensus error, where vulnerable nodes obtain a different `stateRoot` when processing a maliciously crafted transaction. This, in turn, would lead to the chain being split: mainnet splitting in two forks. - -#### Handling - -On the evening of 17th, we discussed options how to handle it. We made a state test to reproduce the issue, and verified that neither `openethereum`, `nethermind` nor `besu` were affected by the same vulnerability, and started a full-sync with a patched version of `geth`. - -It was decided that in this specific instance, it would be possible to make a public announcement and a patch release: - -- The fix can be made pretty 'generically', e.g. always copying data on input to precompiles. -- The flaw is pretty difficult to find, given a generic fix in the call. The attacker needs to figure out that it concerns the precompiles, specifically the datcopy, and that it concerns the `RETURNDATA` buffer rather than the regular memory, and lastly the special circumstances to trigger it (overlapping but shifted input/output). - -Since we had merged the removal of `ETH65`, if the entire network were to upgrade, then nodes which have not yet implemented `ETH66` would be cut off from the network. After further discussions, we decided to: - -- Announce an upcoming security release on Tuesday (August 24th), via Twitter and official channels, plus reach out to downstream projects. -- Temporarily revert the `ETH65`-removal. -- Place the fix into the PR optimizing the jumpdest analysis [233381](https://github.com/ethereum/go-ethereum/pull/23381). -- After 4-8 weeks, release details about the vulnerability. - - -## Exploit - -At block [13107518](https://etherscan.io/block/13107518), mined at (Aug-27-2021 12:50:07 PM +UTC), a minority chain split occurred. The discord user @AlexSSD7 notified the allcoredevs-channel on the Eth R&D discord, on Aug 27 13:09 UTC. - - -At 14:09 UTC, it was confirmed that the transaction `0x1cb6fb36633d270edefc04d048145b4298e67b8aa82a9e5ec4aa1435dd770ce4` had triggered the bug, leading to a minority-split of the chain. The term 'minority split' means that the majority of miners continued to mine on the correct chain. - -At 14:17 UTC, @mhswende tweeted out about the issue [2]. - -The attack was sent from an account funded from Tornado cash. - -It was also found that the same attack had been carried out on the BSC chain at roughly the same time -- at a block mined [12 minutes earlier](https://bscscan.com/tx/0xf667f820631f6adbd04a4c92274374034a3e41fa9057dc42cb4e787535136dce), at Aug-27-2021 12:38:30 PM +UTC. - -The blocks on the 'bad' chain were investigated, and Tim Beiko reached out to those mining operators on the minority chain who could be identified via block extradata. - - -## Lessons learned - - -### Disclosure decision - -The geth-team have an official policy regarding [vulnerability disclosure](https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities). - -> The primary goal for the Geth team is the health of the Ethereum network as a whole, and the decision whether or not to publish details about a serious vulnerability boils down to minimizing the risk and/or impact of discovery and exploitation. - -In this case, it was decided that public pre-announce + patch would likely lead to sufficient update-window for a critical mass of nodes/miners to upgrade in time before it could be exploited. In hindsight, this was a dangerous decision, and it's unlikely that the same decision would be reached were a similar incident to happen again. - - -### Disclosure path - -Several subprojects were informed about the upcoming security patch: - -- Polygon/Matic -- MEV -- Avalanche -- Erigon -- BSC -- EWF -- Quorum -- ETC -- xDAI - -However, some were 'lost', and only notified later - -- Optimism -- Summa -- Harmony - -Action point: create a low-volume geth-announce@ethereum.org email list where dependent projects/operators can receive public announcements. -- This has been done. If you wish to receive release- and security announcements, sign up [here](https://groups.google.com/a/ethereum.org/g/geth-announce/about) - -### Fork monitoring - -The fork monitor behaved 'ok' during the incident, but had to be restarted during the evening. - -Action point: improve the resiliency of the forkmon, which is currently not performing great when many nodes are connected. - -Action point: enable push-based alerts to be sent from the forkmon, to speed up the fork detection. - - -## Links - -- [1] https://twitter.com/go_ethereum/status/1428051458763763721 -- [2] https://twitter.com/mhswende/status/1431259601530458112 - - -## Appendix - -### Subprojects - - -The projects were sent variations of the following text: -``` -We have identified a security issue with go-ethereum, and will issue a -new release (v1.10.8) on Tuesday next week. - -At this point, we will not disclose details about the issue, but -recommend downstream/dependent projects to be ready to take actions to -upgrade to the latest go-ethereum codebase. More information about the -issue will be disclosed at a later date. - -https://twitter.com/go_ethereum/status/1428051458763763721 - -``` -### Patch - -```diff -diff --git a/core/vm/instructions.go b/core/vm/instructions.go -index f7ef2f900e..6c8c6e6e6f 100644 ---- a/core/vm/instructions.go -+++ b/core/vm/instructions.go -@@ -669,6 +669,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt - } - stack.push(&temp) - if err == nil || err == ErrExecutionReverted { -+ ret = common.CopyBytes(ret) - scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) - } - scope.Contract.Gas += returnGas -@@ -703,6 +704,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ - } - stack.push(&temp) - if err == nil || err == ErrExecutionReverted { -+ ret = common.CopyBytes(ret) - scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) - } - scope.Contract.Gas += returnGas -@@ -730,6 +732,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext - } - stack.push(&temp) - if err == nil || err == ErrExecutionReverted { -+ ret = common.CopyBytes(ret) - scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) - } - scope.Contract.Gas += returnGas -@@ -757,6 +760,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) - } - stack.push(&temp) - if err == nil || err == ErrExecutionReverted { -+ ret = common.CopyBytes(ret) - scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) - } - scope.Contract.Gas += returnGas -diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go -index 9cf0c4e2c1..9fb83799c9 100644 ---- a/core/vm/interpreter.go -+++ b/core/vm/interpreter.go -@@ -262,7 +262,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( - // if the operation clears the return data (e.g. it has returning data) - // set the last return to the result of the operation. - if operation.returns { -- in.returnData = common.CopyBytes(res) -+ in.returnData = res - } - - switch { -``` - -### Statetest to test for the issue - -```json -{ - "trigger-issue": { - "env": { - "currentCoinbase": "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "currentDifficulty": "0x20000", - "currentGasLimit": "0x26e1f476fe1e22", - "currentNumber": "0x1", - "currentTimestamp": "0x3e8", - "previousHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - }, - "pre": { - "0x00000000000000000000000000000000000000bb": { - "code": "0x6001600053600260015360036002536004600353600560045360066005536006600260066000600060047f7ef0367e633852132a0ebbf70eb714015dd44bc82e1e55a96ef1389c999c1bcaf13d600060003e596000208055", - "storage": {}, - "balance": "0x5", - "nonce": "0x0" - }, - "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { - "code": "0x", - "storage": {}, - "balance": "0xffffffff", - "nonce": "0x0" - } - }, - "transaction": { - "gasPrice": "0x1", - "nonce": "0x0", - "to": "0x00000000000000000000000000000000000000bb", - "data": [ - "0x" - ], - "gasLimit": [ - "0x7a1200" - ], - "value": [ - "0x01" - ], - "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" - }, - "out": "0x", - "post": { - "Berlin": [ - { - "hash": "2a38a040bab1e1fa499253d98b2fd363e5756ecc52db47dd59af7116c068368c", - "logs": "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "indexes": { - "data": 0, - "gas": 0, - "value": 0 - } - } - ] - } - } -} -``` -
diff --git go-ethereum/docs/static/fonts/FontAwesome.otf celo/docs/static/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..401ec0f36e4f73b8efa40bd6f604fe80d286db70 Binary files /dev/null and celo/docs/static/fonts/FontAwesome.otf differ
diff --git go-ethereum/docs/static/fonts/fontawesome-webfont.eot celo/docs/static/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..e9f60ca953f93e35eab4108bd414bc02ddcf3928 Binary files /dev/null and celo/docs/static/fonts/fontawesome-webfont.eot differ
diff --git go-ethereum/docs/static/fonts/fontawesome-webfont.svg celo/docs/static/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000000000000000000000000000000000000..855c845e538b65548118279537a04eab2ec6ef0d --- /dev/null +++ celo/docs/static/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg> +<metadata> +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. +</metadata> +<defs> +<font id="FontAwesome" horiz-adv-x="1536" > + <font-face + font-family="FontAwesome" + font-weight="400" + font-stretch="normal" + units-per-em="1792" + panose-1="0 0 0 0 0 0 0 0 0 0" + ascent="1536" + descent="-256" + bbox="-1.02083 -256.962 2304.6 1537.02" + underline-thickness="0" + underline-position="0" + unicode-range="U+0020-F500" + /> +<missing-glyph horiz-adv-x="896" +d="M224 112h448v1312h-448v-1312zM112 0v1536h672v-1536h-672z" /> + <glyph glyph-name=".notdef" horiz-adv-x="896" +d="M224 112h448v1312h-448v-1312zM112 0v1536h672v-1536h-672z" /> + <glyph glyph-name=".null" horiz-adv-x="0" + /> + <glyph glyph-name="nonmarkingreturn" horiz-adv-x="597" + /> + <glyph glyph-name="space" unicode=" " horiz-adv-x="448" + /> + <glyph glyph-name="dieresis" unicode="&#xa8;" horiz-adv-x="1792" + /> + <glyph glyph-name="copyright" unicode="&#xa9;" horiz-adv-x="1792" + /> + <glyph glyph-name="registered" unicode="&#xae;" horiz-adv-x="1792" + /> + <glyph glyph-name="acute" unicode="&#xb4;" horiz-adv-x="1792" + /> + <glyph glyph-name="AE" unicode="&#xc6;" horiz-adv-x="1792" + /> + <glyph glyph-name="Oslash" unicode="&#xd8;" horiz-adv-x="1792" + /> + <glyph glyph-name="trademark" unicode="&#x2122;" horiz-adv-x="1792" + /> + <glyph glyph-name="infinity" unicode="&#x221e;" horiz-adv-x="1792" + /> + <glyph glyph-name="notequal" unicode="&#x2260;" horiz-adv-x="1792" + /> + <glyph glyph-name="glass" unicode="&#xf000;" horiz-adv-x="1792" +d="M1699 1350q0 -35 -43 -78l-632 -632v-768h320q26 0 45 -19t19 -45t-19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45t45 19h320v768l-632 632q-43 43 -43 78q0 23 18 36.5t38 17.5t43 4h1408q23 0 43 -4t38 -17.5t18 -36.5z" /> + <glyph glyph-name="music" unicode="&#xf001;" +d="M1536 1312v-1120q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v537l-768 -237v-709q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89 +t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v967q0 31 19 56.5t49 35.5l832 256q12 4 28 4q40 0 68 -28t28 -68z" /> + <glyph glyph-name="search" unicode="&#xf002;" horiz-adv-x="1664" +d="M1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -52 -38 -90t-90 -38q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5 +t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" /> + <glyph glyph-name="envelope" unicode="&#xf003;" horiz-adv-x="1792" +d="M1664 32v768q-32 -36 -69 -66q-268 -206 -426 -338q-51 -43 -83 -67t-86.5 -48.5t-102.5 -24.5h-1h-1q-48 0 -102.5 24.5t-86.5 48.5t-83 67q-158 132 -426 338q-37 30 -69 66v-768q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1664 1083v11v13.5t-0.5 13 +t-3 12.5t-5.5 9t-9 7.5t-14 2.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5q0 -168 147 -284q193 -152 401 -317q6 -5 35 -29.5t46 -37.5t44.5 -31.5t50.5 -27.5t43 -9h1h1q20 0 43 9t50.5 27.5t44.5 31.5t46 37.5t35 29.5q208 165 401 317q54 43 100.5 115.5t46.5 131.5z +M1792 1120v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" /> + <glyph glyph-name="heart" unicode="&#xf004;" horiz-adv-x="1792" +d="M896 -128q-26 0 -44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124t127 -344q0 -221 -229 -450l-623 -600 +q-18 -18 -44 -18z" /> + <glyph glyph-name="star" unicode="&#xf005;" horiz-adv-x="1664" +d="M1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -21 -10.5 -35.5t-30.5 -14.5q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455 +l502 -73q56 -9 56 -46z" /> + <glyph glyph-name="star_empty" unicode="&#xf006;" horiz-adv-x="1664" +d="M1137 532l306 297l-422 62l-189 382l-189 -382l-422 -62l306 -297l-73 -421l378 199l377 -199zM1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -50 -41 -50q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500 +l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455l502 -73q56 -9 56 -46z" /> + <glyph glyph-name="user" unicode="&#xf007;" horiz-adv-x="1280" +d="M1280 137q0 -109 -62.5 -187t-150.5 -78h-854q-88 0 -150.5 78t-62.5 187q0 85 8.5 160.5t31.5 152t58.5 131t94 89t134.5 34.5q131 -128 313 -128t313 128q76 0 134.5 -34.5t94 -89t58.5 -131t31.5 -152t8.5 -160.5zM1024 1024q0 -159 -112.5 -271.5t-271.5 -112.5 +t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" /> + <glyph glyph-name="film" unicode="&#xf008;" horiz-adv-x="1920" +d="M384 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 320v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 704v128q0 26 -19 45t-45 19h-128 +q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 -64v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM384 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45 +t45 -19h128q26 0 45 19t19 45zM1792 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 704v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1792 320v128 +q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 704v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19 +t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1920 1248v-1344q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1344q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" /> + <glyph glyph-name="th_large" unicode="&#xf009;" horiz-adv-x="1664" +d="M768 512v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM768 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 512v-384q0 -52 -38 -90t-90 -38 +h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" /> + <glyph glyph-name="th" unicode="&#xf00a;" horiz-adv-x="1792" +d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 288v-192q0 -40 -28 -68t-68 -28h-320 +q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 +h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192 +q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68z" /> + <glyph glyph-name="th_list" unicode="&#xf00b;" horiz-adv-x="1792" +d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-960 +q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 +h960q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68z" /> + <glyph glyph-name="ok" unicode="&#xf00c;" horiz-adv-x="1792" +d="M1671 970q0 -40 -28 -68l-724 -724l-136 -136q-28 -28 -68 -28t-68 28l-136 136l-362 362q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -295l656 657q28 28 68 28t68 -28l136 -136q28 -28 28 -68z" /> + <glyph glyph-name="remove" unicode="&#xf00d;" horiz-adv-x="1408" +d="M1298 214q0 -40 -28 -68l-136 -136q-28 -28 -68 -28t-68 28l-294 294l-294 -294q-28 -28 -68 -28t-68 28l-136 136q-28 28 -28 68t28 68l294 294l-294 294q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -294l294 294q28 28 68 28t68 -28l136 -136q28 -28 28 -68 +t-28 -68l-294 -294l294 -294q28 -28 28 -68z" /> + <glyph glyph-name="zoom_in" unicode="&#xf00e;" horiz-adv-x="1664" +d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-224q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v224h-224q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h224v224q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5v-224h224 +q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5 +t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" /> + <glyph glyph-name="zoom_out" unicode="&#xf010;" horiz-adv-x="1664" +d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-576q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h576q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5z +M1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z +" /> + <glyph glyph-name="off" unicode="&#xf011;" +d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61t-298 61t-245 164t-164 245t-61 298q0 182 80.5 343t226.5 270q43 32 95.5 25t83.5 -50q32 -42 24.5 -94.5t-49.5 -84.5q-98 -74 -151.5 -181t-53.5 -228q0 -104 40.5 -198.5t109.5 -163.5t163.5 -109.5 +t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5q0 121 -53.5 228t-151.5 181q-42 32 -49.5 84.5t24.5 94.5q31 43 84 50t95 -25q146 -109 226.5 -270t80.5 -343zM896 1408v-640q0 -52 -38 -90t-90 -38t-90 38t-38 90v640q0 52 38 90t90 38t90 -38t38 -90z" /> + <glyph glyph-name="signal" unicode="&#xf012;" horiz-adv-x="1792" +d="M256 96v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 224v-320q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 480v-576q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 +v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1408 864v-960q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1376v-1472q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1472q0 14 9 23t23 9h192q14 0 23 -9t9 -23z" /> + <glyph glyph-name="cog" unicode="&#xf013;" +d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1536 749v-222q0 -12 -8 -23t-20 -13l-185 -28q-19 -54 -39 -91q35 -50 107 -138q10 -12 10 -25t-9 -23q-27 -37 -99 -108t-94 -71q-12 0 -26 9l-138 108q-44 -23 -91 -38 +q-16 -136 -29 -186q-7 -28 -36 -28h-222q-14 0 -24.5 8.5t-11.5 21.5l-28 184q-49 16 -90 37l-141 -107q-10 -9 -25 -9q-14 0 -25 11q-126 114 -165 168q-7 10 -7 23q0 12 8 23q15 21 51 66.5t54 70.5q-27 50 -41 99l-183 27q-13 2 -21 12.5t-8 23.5v222q0 12 8 23t19 13 +l186 28q14 46 39 92q-40 57 -107 138q-10 12 -10 24q0 10 9 23q26 36 98.5 107.5t94.5 71.5q13 0 26 -10l138 -107q44 23 91 38q16 136 29 186q7 28 36 28h222q14 0 24.5 -8.5t11.5 -21.5l28 -184q49 -16 90 -37l142 107q9 9 24 9q13 0 25 -10q129 -119 165 -170q7 -8 7 -22 +q0 -12 -8 -23q-15 -21 -51 -66.5t-54 -70.5q26 -50 41 -98l183 -28q13 -2 21 -12.5t8 -23.5z" /> + <glyph glyph-name="trash" unicode="&#xf014;" horiz-adv-x="1408" +d="M512 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM768 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1024 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576 +q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1152 76v948h-896v-948q0 -22 7 -40.5t14.5 -27t10.5 -8.5h832q3 0 10.5 8.5t14.5 27t7 40.5zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832 +q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" /> + <glyph glyph-name="home" unicode="&#xf015;" horiz-adv-x="1664" +d="M1408 544v-480q0 -26 -19 -45t-45 -19h-384v384h-256v-384h-384q-26 0 -45 19t-19 45v480q0 1 0.5 3t0.5 3l575 474l575 -474q1 -2 1 -6zM1631 613l-62 -74q-8 -9 -21 -11h-3q-13 0 -21 7l-692 577l-692 -577q-12 -8 -24 -7q-13 2 -21 11l-62 74q-8 10 -7 23.5t11 21.5 +l719 599q32 26 76 26t76 -26l244 -204v195q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-408l219 -182q10 -8 11 -21.5t-7 -23.5z" /> + <glyph glyph-name="file_alt" unicode="&#xf016;" +d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z +" /> + <glyph glyph-name="time" unicode="&#xf017;" +d="M896 992v-448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 +q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="road" unicode="&#xf018;" horiz-adv-x="1920" +d="M1111 540v4l-24 320q-1 13 -11 22.5t-23 9.5h-186q-13 0 -23 -9.5t-11 -22.5l-24 -320v-4q-1 -12 8 -20t21 -8h244q12 0 21 8t8 20zM1870 73q0 -73 -46 -73h-704q13 0 22 9.5t8 22.5l-20 256q-1 13 -11 22.5t-23 9.5h-272q-13 0 -23 -9.5t-11 -22.5l-20 -256 +q-1 -13 8 -22.5t22 -9.5h-704q-46 0 -46 73q0 54 26 116l417 1044q8 19 26 33t38 14h339q-13 0 -23 -9.5t-11 -22.5l-15 -192q-1 -14 8 -23t22 -9h166q13 0 22 9t8 23l-15 192q-1 13 -11 22.5t-23 9.5h339q20 0 38 -14t26 -33l417 -1044q26 -62 26 -116z" /> + <glyph glyph-name="download_alt" unicode="&#xf019;" horiz-adv-x="1664" +d="M1280 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 416v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h465l135 -136 +q58 -56 136 -56t136 56l136 136h464q40 0 68 -28t28 -68zM1339 985q17 -41 -14 -70l-448 -448q-18 -19 -45 -19t-45 19l-448 448q-31 29 -14 70q17 39 59 39h256v448q0 26 19 45t45 19h256q26 0 45 -19t19 -45v-448h256q42 0 59 -39z" /> + <glyph glyph-name="download" unicode="&#xf01a;" +d="M1120 608q0 -12 -10 -24l-319 -319q-11 -9 -23 -9t-23 9l-320 320q-15 16 -7 35q8 20 30 20h192v352q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-352h192q14 0 23 -9t9 -23zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273 +t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="upload" unicode="&#xf01b;" +d="M1118 660q-8 -20 -30 -20h-192v-352q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v352h-192q-14 0 -23 9t-9 23q0 12 10 24l319 319q11 9 23 9t23 -9l320 -320q15 -16 7 -35zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198 +t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="inbox" unicode="&#xf01c;" +d="M1023 576h316q-1 3 -2.5 8.5t-2.5 7.5l-212 496h-708l-212 -496q-1 -3 -2.5 -8.5t-2.5 -7.5h316l95 -192h320zM1536 546v-482q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v482q0 62 25 123l238 552q10 25 36.5 42t52.5 17h832q26 0 52.5 -17t36.5 -42l238 -552 +q25 -61 25 -123z" /> + <glyph glyph-name="play_circle" unicode="&#xf01d;" +d="M1184 640q0 -37 -32 -55l-544 -320q-15 -9 -32 -9q-16 0 -32 8q-32 19 -32 56v640q0 37 32 56q33 18 64 -1l544 -320q32 -18 32 -55zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 +q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="repeat" unicode="&#xf01e;" +d="M1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l138 138q-148 137 -349 137q-104 0 -198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5q119 0 225 52t179 147q7 10 23 12q15 0 25 -9 +l137 -138q9 -8 9.5 -20.5t-7.5 -22.5q-109 -132 -264 -204.5t-327 -72.5q-156 0 -298 61t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q147 0 284.5 -55.5t244.5 -156.5l130 129q29 31 70 14q39 -17 39 -59z" /> + <glyph glyph-name="refresh" unicode="&#xf021;" +d="M1511 480q0 -5 -1 -7q-64 -268 -268 -434.5t-478 -166.5q-146 0 -282.5 55t-243.5 157l-129 -129q-19 -19 -45 -19t-45 19t-19 45v448q0 26 19 45t45 19h448q26 0 45 -19t19 -45t-19 -45l-137 -137q71 -66 161 -102t187 -36q134 0 250 65t186 179q11 17 53 117 +q8 23 30 23h192q13 0 22.5 -9.5t9.5 -22.5zM1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-26 0 -45 19t-19 45t19 45l138 138q-148 137 -349 137q-134 0 -250 -65t-186 -179q-11 -17 -53 -117q-8 -23 -30 -23h-199q-13 0 -22.5 9.5t-9.5 22.5v7q65 268 270 434.5t480 166.5 +q146 0 284 -55.5t245 -156.5l130 129q19 19 45 19t45 -19t19 -45z" /> + <glyph glyph-name="list_alt" unicode="&#xf022;" horiz-adv-x="1792" +d="M384 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M384 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1536 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5z +M1536 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5zM1536 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5 +t9.5 -22.5zM1664 160v832q0 13 -9.5 22.5t-22.5 9.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 1248v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47 +t47 -113z" /> + <glyph glyph-name="lock" unicode="&#xf023;" horiz-adv-x="1152" +d="M320 768h512v192q0 106 -75 181t-181 75t-181 -75t-75 -181v-192zM1152 672v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v192q0 184 132 316t316 132t316 -132t132 -316v-192h32q40 0 68 -28t28 -68z" /> + <glyph glyph-name="flag" unicode="&#xf024;" horiz-adv-x="1792" +d="M320 1280q0 -72 -64 -110v-1266q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v1266q-64 38 -64 110q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -25 -12.5 -38.5t-39.5 -27.5q-215 -116 -369 -116q-61 0 -123.5 22t-108.5 48 +t-115.5 48t-142.5 22q-192 0 -464 -146q-17 -9 -33 -9q-26 0 -45 19t-19 45v742q0 32 31 55q21 14 79 43q236 120 421 120q107 0 200 -29t219 -88q38 -19 88 -19q54 0 117.5 21t110 47t88 47t54.5 21q26 0 45 -19t19 -45z" /> + <glyph glyph-name="headphones" unicode="&#xf025;" horiz-adv-x="1664" +d="M1664 650q0 -166 -60 -314l-20 -49l-185 -33q-22 -83 -90.5 -136.5t-156.5 -53.5v-32q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-32q71 0 130 -35.5t93 -95.5l68 12q29 95 29 193q0 148 -88 279t-236.5 209t-315.5 78 +t-315.5 -78t-236.5 -209t-88 -279q0 -98 29 -193l68 -12q34 60 93 95.5t130 35.5v32q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v32q-88 0 -156.5 53.5t-90.5 136.5l-185 33l-20 49q-60 148 -60 314q0 151 67 291t179 242.5 +t266 163.5t320 61t320 -61t266 -163.5t179 -242.5t67 -291z" /> + <glyph glyph-name="volume_off" unicode="&#xf026;" horiz-adv-x="768" +d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45z" /> + <glyph glyph-name="volume_down" unicode="&#xf027;" horiz-adv-x="1152" +d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 36 +t12 56.5t-12 56.5t-29 36t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142z" /> + <glyph glyph-name="volume_up" unicode="&#xf028;" horiz-adv-x="1664" +d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 36 +t12 56.5t-12 56.5t-29 36t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142zM1408 640q0 -153 -85 -282.5t-225 -188.5q-13 -5 -25 -5q-27 0 -46 19t-19 45q0 39 39 59q56 29 76 44q74 54 115.5 135.5t41.5 173.5t-41.5 173.5 +t-115.5 135.5q-20 15 -76 44q-39 20 -39 59q0 26 19 45t45 19q13 0 26 -5q140 -59 225 -188.5t85 -282.5zM1664 640q0 -230 -127 -422.5t-338 -283.5q-13 -5 -26 -5q-26 0 -45 19t-19 45q0 36 39 59q7 4 22.5 10.5t22.5 10.5q46 25 82 51q123 91 192 227t69 289t-69 289 +t-192 227q-36 26 -82 51q-7 4 -22.5 10.5t-22.5 10.5q-39 23 -39 59q0 26 19 45t45 19q13 0 26 -5q211 -91 338 -283.5t127 -422.5z" /> + <glyph glyph-name="qrcode" unicode="&#xf029;" horiz-adv-x="1408" +d="M384 384v-128h-128v128h128zM384 1152v-128h-128v128h128zM1152 1152v-128h-128v128h128zM128 129h384v383h-384v-383zM128 896h384v384h-384v-384zM896 896h384v384h-384v-384zM640 640v-640h-640v640h640zM1152 128v-128h-128v128h128zM1408 128v-128h-128v128h128z +M1408 640v-384h-384v128h-128v-384h-128v640h384v-128h128v128h128zM640 1408v-640h-640v640h640zM1408 1408v-640h-640v640h640z" /> + <glyph glyph-name="barcode" unicode="&#xf02a;" horiz-adv-x="1792" +d="M63 0h-63v1408h63v-1408zM126 1h-32v1407h32v-1407zM220 1h-31v1407h31v-1407zM377 1h-31v1407h31v-1407zM534 1h-62v1407h62v-1407zM660 1h-31v1407h31v-1407zM723 1h-31v1407h31v-1407zM786 1h-31v1407h31v-1407zM943 1h-63v1407h63v-1407zM1100 1h-63v1407h63v-1407z +M1226 1h-63v1407h63v-1407zM1352 1h-63v1407h63v-1407zM1446 1h-63v1407h63v-1407zM1635 1h-94v1407h94v-1407zM1698 1h-32v1407h32v-1407zM1792 0h-63v1408h63v-1408z" /> + <glyph glyph-name="tag" unicode="&#xf02b;" +d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 +l715 -714q37 -39 37 -91z" /> + <glyph glyph-name="tags" unicode="&#xf02c;" horiz-adv-x="1920" +d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 +l715 -714q37 -39 37 -91zM1899 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-36 0 -59 14t-53 45l470 470q37 37 37 90q0 52 -37 91l-715 714q-38 38 -102 64.5t-117 26.5h224q53 0 117 -26.5t102 -64.5l715 -714q37 -39 37 -91z" /> + <glyph glyph-name="book" unicode="&#xf02d;" horiz-adv-x="1664" +d="M1639 1058q40 -57 18 -129l-275 -906q-19 -64 -76.5 -107.5t-122.5 -43.5h-923q-77 0 -148.5 53.5t-99.5 131.5q-24 67 -2 127q0 4 3 27t4 37q1 8 -3 21.5t-3 19.5q2 11 8 21t16.5 23.5t16.5 23.5q23 38 45 91.5t30 91.5q3 10 0.5 30t-0.5 28q3 11 17 28t17 23 +q21 36 42 92t25 90q1 9 -2.5 32t0.5 28q4 13 22 30.5t22 22.5q19 26 42.5 84.5t27.5 96.5q1 8 -3 25.5t-2 26.5q2 8 9 18t18 23t17 21q8 12 16.5 30.5t15 35t16 36t19.5 32t26.5 23.5t36 11.5t47.5 -5.5l-1 -3q38 9 51 9h761q74 0 114 -56t18 -130l-274 -906 +q-36 -119 -71.5 -153.5t-128.5 -34.5h-869q-27 0 -38 -15q-11 -16 -1 -43q24 -70 144 -70h923q29 0 56 15.5t35 41.5l300 987q7 22 5 57q38 -15 59 -43zM575 1056q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5 +t-16.5 -22.5zM492 800q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5t-16.5 -22.5z" /> + <glyph glyph-name="bookmark" unicode="&#xf02e;" horiz-adv-x="1280" +d="M1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289q0 34 19.5 62t52.5 41q21 9 44 9h1048z" /> + <glyph glyph-name="print" unicode="&#xf02f;" horiz-adv-x="1664" +d="M384 0h896v256h-896v-256zM384 640h896v384h-160q-40 0 -68 28t-28 68v160h-640v-640zM1536 576q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 576v-416q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-160q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68 +v160h-224q-13 0 -22.5 9.5t-9.5 22.5v416q0 79 56.5 135.5t135.5 56.5h64v544q0 40 28 68t68 28h672q40 0 88 -20t76 -48l152 -152q28 -28 48 -76t20 -88v-256h64q79 0 135.5 -56.5t56.5 -135.5z" /> + <glyph glyph-name="camera" unicode="&#xf030;" horiz-adv-x="1920" +d="M960 864q119 0 203.5 -84.5t84.5 -203.5t-84.5 -203.5t-203.5 -84.5t-203.5 84.5t-84.5 203.5t84.5 203.5t203.5 84.5zM1664 1280q106 0 181 -75t75 -181v-896q0 -106 -75 -181t-181 -75h-1408q-106 0 -181 75t-75 181v896q0 106 75 181t181 75h224l51 136 +q19 49 69.5 84.5t103.5 35.5h512q53 0 103.5 -35.5t69.5 -84.5l51 -136h224zM960 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" /> + <glyph glyph-name="font" unicode="&#xf031;" horiz-adv-x="1664" +d="M725 977l-170 -450q33 0 136.5 -2t160.5 -2q19 0 57 2q-87 253 -184 452zM0 -128l2 79q23 7 56 12.5t57 10.5t49.5 14.5t44.5 29t31 50.5l237 616l280 724h75h53q8 -14 11 -21l205 -480q33 -78 106 -257.5t114 -274.5q15 -34 58 -144.5t72 -168.5q20 -45 35 -57 +q19 -15 88 -29.5t84 -20.5q6 -38 6 -57q0 -5 -0.5 -13.5t-0.5 -12.5q-63 0 -190 8t-191 8q-76 0 -215 -7t-178 -8q0 43 4 78l131 28q1 0 12.5 2.5t15.5 3.5t14.5 4.5t15 6.5t11 8t9 11t2.5 14q0 16 -31 96.5t-72 177.5t-42 100l-450 2q-26 -58 -76.5 -195.5t-50.5 -162.5 +q0 -22 14 -37.5t43.5 -24.5t48.5 -13.5t57 -8.5t41 -4q1 -19 1 -58q0 -9 -2 -27q-58 0 -174.5 10t-174.5 10q-8 0 -26.5 -4t-21.5 -4q-80 -14 -188 -14z" /> + <glyph glyph-name="bold" unicode="&#xf032;" horiz-adv-x="1408" +d="M555 15q74 -32 140 -32q376 0 376 335q0 114 -41 180q-27 44 -61.5 74t-67.5 46.5t-80.5 25t-84 10.5t-94.5 2q-73 0 -101 -10q0 -53 -0.5 -159t-0.5 -158q0 -8 -1 -67.5t-0.5 -96.5t4.5 -83.5t12 -66.5zM541 761q42 -7 109 -7q82 0 143 13t110 44.5t74.5 89.5t25.5 142 +q0 70 -29 122.5t-79 82t-108 43.5t-124 14q-50 0 -130 -13q0 -50 4 -151t4 -152q0 -27 -0.5 -80t-0.5 -79q0 -46 1 -69zM0 -128l2 94q15 4 85 16t106 27q7 12 12.5 27t8.5 33.5t5.5 32.5t3 37.5t0.5 34v35.5v30q0 982 -22 1025q-4 8 -22 14.5t-44.5 11t-49.5 7t-48.5 4.5 +t-30.5 3l-4 83q98 2 340 11.5t373 9.5q23 0 68 -0.5t68 -0.5q70 0 136.5 -13t128.5 -42t108 -71t74 -104.5t28 -137.5q0 -52 -16.5 -95.5t-39 -72t-64.5 -57.5t-73 -45t-84 -40q154 -35 256.5 -134t102.5 -248q0 -100 -35 -179.5t-93.5 -130.5t-138 -85.5t-163.5 -48.5 +t-176 -14q-44 0 -132 3t-132 3q-106 0 -307 -11t-231 -12z" /> + <glyph glyph-name="italic" unicode="&#xf033;" horiz-adv-x="1024" +d="M0 -126l17 85q22 7 61.5 16.5t72 19t59.5 23.5q28 35 41 101q1 7 62 289t114 543.5t52 296.5v25q-24 13 -54.5 18.5t-69.5 8t-58 5.5l19 103q33 -2 120 -6.5t149.5 -7t120.5 -2.5q48 0 98.5 2.5t121 7t98.5 6.5q-5 -39 -19 -89q-30 -10 -101.5 -28.5t-108.5 -33.5 +q-8 -19 -14 -42.5t-9 -40t-7.5 -45.5t-6.5 -42q-27 -148 -87.5 -419.5t-77.5 -355.5q-2 -9 -13 -58t-20 -90t-16 -83.5t-6 -57.5l1 -18q17 -4 185 -31q-3 -44 -16 -99q-11 0 -32.5 -1.5t-32.5 -1.5q-29 0 -87 10t-86 10q-138 2 -206 2q-51 0 -143 -9t-121 -11z" /> + <glyph glyph-name="text_height" unicode="&#xf034;" horiz-adv-x="1792" +d="M1744 128q33 0 42 -18.5t-11 -44.5l-126 -162q-20 -26 -49 -26t-49 26l-126 162q-20 26 -11 44.5t42 18.5h80v1024h-80q-33 0 -42 18.5t11 44.5l126 162q20 26 49 26t49 -26l126 -162q20 -26 11 -44.5t-42 -18.5h-80v-1024h80zM81 1407l54 -27q12 -5 211 -5q44 0 132 2 +t132 2q36 0 107.5 -0.5t107.5 -0.5h293q6 0 21 -0.5t20.5 0t16 3t17.5 9t15 17.5l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 48t-14.5 73.5t-7.5 35.5q-6 8 -12 12.5t-15.5 6t-13 2.5t-18 0.5t-16.5 -0.5 +q-17 0 -66.5 0.5t-74.5 0.5t-64 -2t-71 -6q-9 -81 -8 -136q0 -94 2 -388t2 -455q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27 +q19 42 19 383q0 101 -3 303t-3 303v117q0 2 0.5 15.5t0.5 25t-1 25.5t-3 24t-5 14q-11 12 -162 12q-33 0 -93 -12t-80 -26q-19 -13 -34 -72.5t-31.5 -111t-42.5 -53.5q-42 26 -56 44v383z" /> + <glyph glyph-name="text_width" unicode="&#xf035;" +d="M81 1407l54 -27q12 -5 211 -5q44 0 132 2t132 2q70 0 246.5 1t304.5 0.5t247 -4.5q33 -1 56 31l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 47.5t-15 73.5t-7 36q-10 13 -27 19q-5 2 -66 2q-30 0 -93 1t-103 1 +t-94 -2t-96 -7q-9 -81 -8 -136l1 -152v52q0 -55 1 -154t1.5 -180t0.5 -153q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27 +q7 16 11.5 74t6 145.5t1.5 155t-0.5 153.5t-0.5 89q0 7 -2.5 21.5t-2.5 22.5q0 7 0.5 44t1 73t0 76.5t-3 67.5t-6.5 32q-11 12 -162 12q-41 0 -163 -13.5t-138 -24.5q-19 -12 -34 -71.5t-31.5 -111.5t-42.5 -54q-42 26 -56 44v383zM1310 125q12 0 42 -19.5t57.5 -41.5 +t59.5 -49t36 -30q26 -21 26 -49t-26 -49q-4 -3 -36 -30t-59.5 -49t-57.5 -41.5t-42 -19.5q-13 0 -20.5 10.5t-10 28.5t-2.5 33.5t1.5 33t1.5 19.5h-1024q0 -2 1.5 -19.5t1.5 -33t-2.5 -33.5t-10 -28.5t-20.5 -10.5q-12 0 -42 19.5t-57.5 41.5t-59.5 49t-36 30q-26 21 -26 49 +t26 49q4 3 36 30t59.5 49t57.5 41.5t42 19.5q13 0 20.5 -10.5t10 -28.5t2.5 -33.5t-1.5 -33t-1.5 -19.5h1024q0 2 -1.5 19.5t-1.5 33t2.5 33.5t10 28.5t20.5 10.5z" /> + <glyph glyph-name="align_left" unicode="&#xf036;" horiz-adv-x="1792" +d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45 +t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" /> + <glyph glyph-name="align_center" unicode="&#xf037;" horiz-adv-x="1792" +d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h896q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45t-45 -19 +h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h640q26 0 45 -19t19 -45z" /> + <glyph glyph-name="align_right" unicode="&#xf038;" horiz-adv-x="1792" +d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 +t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" /> + <glyph glyph-name="align_justify" unicode="&#xf039;" horiz-adv-x="1792" +d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 +t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" /> + <glyph glyph-name="list" unicode="&#xf03a;" horiz-adv-x="1792" +d="M256 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM256 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5 +t9.5 -22.5zM256 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344 +q13 0 22.5 -9.5t9.5 -22.5zM256 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 +t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192 +q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5z" /> + <glyph glyph-name="indent_left" unicode="&#xf03b;" horiz-adv-x="1792" +d="M384 992v-576q0 -13 -9.5 -22.5t-22.5 -9.5q-14 0 -23 9l-288 288q-9 9 -9 23t9 23l288 288q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 +t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 +q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" /> + <glyph glyph-name="indent_right" unicode="&#xf03c;" horiz-adv-x="1792" +d="M352 704q0 -14 -9 -23l-288 -288q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v576q0 13 9.5 22.5t22.5 9.5q14 0 23 -9l288 -288q9 -9 9 -23zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 +t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 +q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" /> + <glyph glyph-name="facetime_video" unicode="&#xf03d;" horiz-adv-x="1792" +d="M1792 1184v-1088q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-403 403v-166q0 -119 -84.5 -203.5t-203.5 -84.5h-704q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h704q119 0 203.5 -84.5t84.5 -203.5v-165l403 402q18 19 45 19q12 0 25 -5 +q39 -17 39 -59z" /> + <glyph glyph-name="picture" unicode="&#xf03e;" horiz-adv-x="1920" +d="M640 960q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 576v-448h-1408v192l320 320l160 -160l512 512zM1760 1280h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5v1216 +q0 13 -9.5 22.5t-22.5 9.5zM1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" /> + <glyph glyph-name="pencil" unicode="&#xf040;" +d="M363 0l91 91l-235 235l-91 -91v-107h128v-128h107zM886 928q0 22 -22 22q-10 0 -17 -7l-542 -542q-7 -7 -7 -17q0 -22 22 -22q10 0 17 7l542 542q7 7 7 17zM832 1120l416 -416l-832 -832h-416v416zM1515 1024q0 -53 -37 -90l-166 -166l-416 416l166 165q36 38 90 38 +q53 0 91 -38l235 -234q37 -39 37 -91z" /> + <glyph glyph-name="map_marker" unicode="&#xf041;" horiz-adv-x="1024" +d="M768 896q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1024 896q0 -109 -33 -179l-364 -774q-16 -33 -47.5 -52t-67.5 -19t-67.5 19t-46.5 52l-365 774q-33 70 -33 179q0 212 150 362t362 150t362 -150t150 -362z" /> + <glyph glyph-name="adjust" unicode="&#xf042;" +d="M768 96v1088q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="tint" unicode="&#xf043;" horiz-adv-x="1024" +d="M512 384q0 36 -20 69q-1 1 -15.5 22.5t-25.5 38t-25 44t-21 50.5q-4 16 -21 16t-21 -16q-7 -23 -21 -50.5t-25 -44t-25.5 -38t-15.5 -22.5q-20 -33 -20 -69q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 512q0 -212 -150 -362t-362 -150t-362 150t-150 362 +q0 145 81 275q6 9 62.5 90.5t101 151t99.5 178t83 201.5q9 30 34 47t51 17t51.5 -17t33.5 -47q28 -93 83 -201.5t99.5 -178t101 -151t62.5 -90.5q81 -127 81 -275z" /> + <glyph glyph-name="edit" unicode="&#xf044;" horiz-adv-x="1792" +d="M888 352l116 116l-152 152l-116 -116v-56h96v-96h56zM1328 1072q-16 16 -33 -1l-350 -350q-17 -17 -1 -33t33 1l350 350q17 17 1 33zM1408 478v-190q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 +q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-14 -14 -32 -8q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v126q0 13 9 22l64 64q15 15 35 7t20 -29zM1312 1216l288 -288l-672 -672h-288v288zM1756 1084l-92 -92 +l-288 288l92 92q28 28 68 28t68 -28l152 -152q28 -28 28 -68t-28 -68z" /> + <glyph glyph-name="share" unicode="&#xf045;" horiz-adv-x="1664" +d="M1408 547v-259q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h255v0q13 0 22.5 -9.5t9.5 -22.5q0 -27 -26 -32q-77 -26 -133 -60q-10 -4 -16 -4h-112q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832 +q66 0 113 47t47 113v214q0 19 18 29q28 13 54 37q16 16 35 8q21 -9 21 -29zM1645 1043l-384 -384q-18 -19 -45 -19q-12 0 -25 5q-39 17 -39 59v192h-160q-323 0 -438 -131q-119 -137 -74 -473q3 -23 -20 -34q-8 -2 -12 -2q-16 0 -26 13q-10 14 -21 31t-39.5 68.5t-49.5 99.5 +t-38.5 114t-17.5 122q0 49 3.5 91t14 90t28 88t47 81.5t68.5 74t94.5 61.5t124.5 48.5t159.5 30.5t196.5 11h160v192q0 42 39 59q13 5 25 5q26 0 45 -19l384 -384q19 -19 19 -45t-19 -45z" /> + <glyph glyph-name="check" unicode="&#xf046;" horiz-adv-x="1664" +d="M1408 606v-318q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-10 -10 -23 -10q-3 0 -9 2q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832 +q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v254q0 13 9 22l64 64q10 10 23 10q6 0 12 -3q20 -8 20 -29zM1639 1095l-814 -814q-24 -24 -57 -24t-57 24l-430 430q-24 24 -24 57t24 57l110 110q24 24 57 24t57 -24l263 -263l647 647q24 24 57 24t57 -24l110 -110 +q24 -24 24 -57t-24 -57z" /> + <glyph glyph-name="move" unicode="&#xf047;" horiz-adv-x="1792" +d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-384v-384h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v384h-384v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45 +t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h384v384h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45t-19 -45t-45 -19h-128v-384h384v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" /> + <glyph glyph-name="step_backward" unicode="&#xf048;" horiz-adv-x="1024" +d="M979 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 10 13 19z" /> + <glyph glyph-name="fast_backward" unicode="&#xf049;" horiz-adv-x="1792" +d="M1747 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 10 13 19l710 710 +q19 19 32 13t13 -32v-710q4 10 13 19z" /> + <glyph glyph-name="backward" unicode="&#xf04a;" horiz-adv-x="1664" +d="M1619 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-19 19 -19 45t19 45l710 710q19 19 32 13t13 -32v-710q4 10 13 19z" /> + <glyph glyph-name="play" unicode="&#xf04b;" horiz-adv-x="1408" +d="M1384 609l-1328 -738q-23 -13 -39.5 -3t-16.5 36v1472q0 26 16.5 36t39.5 -3l1328 -738q23 -13 23 -31t-23 -31z" /> + <glyph glyph-name="pause" unicode="&#xf04c;" +d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45zM640 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45z" /> + <glyph glyph-name="stop" unicode="&#xf04d;" +d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" /> + <glyph glyph-name="forward" unicode="&#xf04e;" horiz-adv-x="1664" +d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q9 -9 13 -19v710q0 26 13 32t32 -13l710 -710q19 -19 19 -45t-19 -45l-710 -710q-19 -19 -32 -13t-13 32v710q-4 -10 -13 -19z" /> + <glyph glyph-name="fast_forward" unicode="&#xf050;" horiz-adv-x="1792" +d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q9 -9 13 -19v710q0 26 13 32t32 -13l710 -710q9 -9 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-4 -10 -13 -19l-710 -710 +q-19 -19 -32 -13t-13 32v710q-4 -10 -13 -19z" /> + <glyph glyph-name="step_forward" unicode="&#xf051;" horiz-adv-x="1024" +d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q9 -9 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-4 -10 -13 -19z" /> + <glyph glyph-name="eject" unicode="&#xf052;" horiz-adv-x="1538" +d="M14 557l710 710q19 19 45 19t45 -19l710 -710q19 -19 13 -32t-32 -13h-1472q-26 0 -32 13t13 32zM1473 0h-1408q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1408q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19z" /> + <glyph glyph-name="chevron_left" unicode="&#xf053;" horiz-adv-x="1280" +d="M1171 1235l-531 -531l531 -531q19 -19 19 -45t-19 -45l-166 -166q-19 -19 -45 -19t-45 19l-742 742q-19 19 -19 45t19 45l742 742q19 19 45 19t45 -19l166 -166q19 -19 19 -45t-19 -45z" /> + <glyph glyph-name="chevron_right" unicode="&#xf054;" horiz-adv-x="1280" +d="M1107 659l-742 -742q-19 -19 -45 -19t-45 19l-166 166q-19 19 -19 45t19 45l531 531l-531 531q-19 19 -19 45t19 45l166 166q19 19 45 19t45 -19l742 -742q19 -19 19 -45t-19 -45z" /> + <glyph glyph-name="plus_sign" unicode="&#xf055;" +d="M1216 576v128q0 26 -19 45t-45 19h-256v256q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-256h-256q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h256v-256q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v256h256q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5 +t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="minus_sign" unicode="&#xf056;" +d="M1216 576v128q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 +t103 -385.5z" /> + <glyph glyph-name="remove_sign" unicode="&#xf057;" +d="M1149 414q0 26 -19 45l-181 181l181 181q19 19 19 45q0 27 -19 46l-90 90q-19 19 -46 19q-26 0 -45 -19l-181 -181l-181 181q-19 19 -45 19q-27 0 -46 -19l-90 -90q-19 -19 -19 -46q0 -26 19 -45l181 -181l-181 -181q-19 -19 -19 -45q0 -27 19 -46l90 -90q19 -19 46 -19 +q26 0 45 19l181 181l181 -181q19 -19 45 -19q27 0 46 19l90 90q19 19 19 46zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="ok_sign" unicode="&#xf058;" +d="M1284 802q0 28 -18 46l-91 90q-19 19 -45 19t-45 -19l-408 -407l-226 226q-19 19 -45 19t-45 -19l-91 -90q-18 -18 -18 -46q0 -27 18 -45l362 -362q19 -19 45 -19q27 0 46 19l543 543q18 18 18 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 +t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="question_sign" unicode="&#xf059;" +d="M896 160v192q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1152 832q0 88 -55.5 163t-138.5 116t-170 41q-243 0 -371 -213q-15 -24 8 -42l132 -100q7 -6 19 -6q16 0 25 12q53 68 86 92q34 24 86 24q48 0 85.5 -26t37.5 -59 +q0 -38 -20 -61t-68 -45q-63 -28 -115.5 -86.5t-52.5 -125.5v-36q0 -14 9 -23t23 -9h192q14 0 23 9t9 23q0 19 21.5 49.5t54.5 49.5q32 18 49 28.5t46 35t44.5 48t28 60.5t12.5 81zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 +t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="info_sign" unicode="&#xf05a;" +d="M1024 160v160q0 14 -9 23t-23 9h-96v512q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h96v-320h-96q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h448q14 0 23 9t9 23zM896 1056v160q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23 +t23 -9h192q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="screenshot" unicode="&#xf05b;" +d="M1197 512h-109q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h109q-32 108 -112.5 188.5t-188.5 112.5v-109q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v109q-108 -32 -188.5 -112.5t-112.5 -188.5h109q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-109 +q32 -108 112.5 -188.5t188.5 -112.5v109q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-109q108 32 188.5 112.5t112.5 188.5zM1536 704v-128q0 -26 -19 -45t-45 -19h-143q-37 -161 -154.5 -278.5t-278.5 -154.5v-143q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v143 +q-161 37 -278.5 154.5t-154.5 278.5h-143q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h143q37 161 154.5 278.5t278.5 154.5v143q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-143q161 -37 278.5 -154.5t154.5 -278.5h143q26 0 45 -19t19 -45z" /> + <glyph glyph-name="remove_circle" unicode="&#xf05c;" +d="M1097 457l-146 -146q-10 -10 -23 -10t-23 10l-137 137l-137 -137q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l137 137l-137 137q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l137 -137l137 137q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23 +l-137 -137l137 -137q10 -10 10 -23t-10 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5 +t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="ok_circle" unicode="&#xf05d;" +d="M1171 723l-422 -422q-19 -19 -45 -19t-45 19l-294 294q-19 19 -19 45t19 45l102 102q19 19 45 19t45 -19l147 -147l275 275q19 19 45 19t45 -19l102 -102q19 -19 19 -45t-19 -45zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198 +t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="ban_circle" unicode="&#xf05e;" +d="M1312 643q0 161 -87 295l-754 -753q137 -89 297 -89q111 0 211.5 43.5t173.5 116.5t116 174.5t43 212.5zM313 344l755 754q-135 91 -300 91q-148 0 -273 -73t-198 -199t-73 -274q0 -162 89 -299zM1536 643q0 -157 -61 -300t-163.5 -246t-245 -164t-298.5 -61t-298.5 61 +t-245 164t-163.5 246t-61 300t61 299.5t163.5 245.5t245 164t298.5 61t298.5 -61t245 -164t163.5 -245.5t61 -299.5z" /> + <glyph glyph-name="arrow_left" unicode="&#xf060;" +d="M1536 640v-128q0 -53 -32.5 -90.5t-84.5 -37.5h-704l293 -294q38 -36 38 -90t-38 -90l-75 -76q-37 -37 -90 -37q-52 0 -91 37l-651 652q-37 37 -37 90q0 52 37 91l651 650q38 38 91 38q52 0 90 -38l75 -74q38 -38 38 -91t-38 -91l-293 -293h704q52 0 84.5 -37.5 +t32.5 -90.5z" /> + <glyph glyph-name="arrow_right" unicode="&#xf061;" +d="M1472 576q0 -54 -37 -91l-651 -651q-39 -37 -91 -37q-51 0 -90 37l-75 75q-38 38 -38 91t38 91l293 293h-704q-52 0 -84.5 37.5t-32.5 90.5v128q0 53 32.5 90.5t84.5 37.5h704l-293 294q-38 36 -38 90t38 90l75 75q38 38 90 38q53 0 91 -38l651 -651q37 -35 37 -90z" /> + <glyph glyph-name="arrow_up" unicode="&#xf062;" horiz-adv-x="1664" +d="M1611 565q0 -51 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-294 293v-704q0 -52 -37.5 -84.5t-90.5 -32.5h-128q-53 0 -90.5 32.5t-37.5 84.5v704l-294 -293q-36 -38 -90 -38t-90 38l-75 75q-38 38 -38 90q0 53 38 91l651 651q35 37 90 37q54 0 91 -37l651 -651 +q37 -39 37 -91z" /> + <glyph glyph-name="arrow_down" unicode="&#xf063;" horiz-adv-x="1664" +d="M1611 704q0 -53 -37 -90l-651 -652q-39 -37 -91 -37q-53 0 -90 37l-651 652q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l294 -294v704q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-704l294 294q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" /> + <glyph glyph-name="share_alt" unicode="&#xf064;" horiz-adv-x="1792" +d="M1792 896q0 -26 -19 -45l-512 -512q-19 -19 -45 -19t-45 19t-19 45v256h-224q-98 0 -175.5 -6t-154 -21.5t-133 -42.5t-105.5 -69.5t-80 -101t-48.5 -138.5t-17.5 -181q0 -55 5 -123q0 -6 2.5 -23.5t2.5 -26.5q0 -15 -8.5 -25t-23.5 -10q-16 0 -28 17q-7 9 -13 22 +t-13.5 30t-10.5 24q-127 285 -127 451q0 199 53 333q162 403 875 403h224v256q0 26 19 45t45 19t45 -19l512 -512q19 -19 19 -45z" /> + <glyph glyph-name="resize_full" unicode="&#xf065;" +d="M755 480q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23zM1536 1344v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332 +q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45z" /> + <glyph glyph-name="resize_small" unicode="&#xf066;" +d="M768 576v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45zM1523 1248q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45 +t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23z" /> + <glyph glyph-name="plus" unicode="&#xf067;" horiz-adv-x="1408" +d="M1408 800v-192q0 -40 -28 -68t-68 -28h-416v-416q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v416h-416q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h416v416q0 40 28 68t68 28h192q40 0 68 -28t28 -68v-416h416q40 0 68 -28t28 -68z" /> + <glyph glyph-name="minus" unicode="&#xf068;" horiz-adv-x="1408" +d="M1408 800v-192q0 -40 -28 -68t-68 -28h-1216q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h1216q40 0 68 -28t28 -68z" /> + <glyph glyph-name="asterisk" unicode="&#xf069;" horiz-adv-x="1664" +d="M1482 486q46 -26 59.5 -77.5t-12.5 -97.5l-64 -110q-26 -46 -77.5 -59.5t-97.5 12.5l-266 153v-307q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v307l-266 -153q-46 -26 -97.5 -12.5t-77.5 59.5l-64 110q-26 46 -12.5 97.5t59.5 77.5l266 154l-266 154 +q-46 26 -59.5 77.5t12.5 97.5l64 110q26 46 77.5 59.5t97.5 -12.5l266 -153v307q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-307l266 153q46 26 97.5 12.5t77.5 -59.5l64 -110q26 -46 12.5 -97.5t-59.5 -77.5l-266 -154z" /> + <glyph glyph-name="exclamation_sign" unicode="&#xf06a;" +d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM896 161v190q0 14 -9 23.5t-22 9.5h-192q-13 0 -23 -10t-10 -23v-190q0 -13 10 -23t23 -10h192 +q13 0 22 9.5t9 23.5zM894 505l18 621q0 12 -10 18q-10 8 -24 8h-220q-14 0 -24 -8q-10 -6 -10 -18l17 -621q0 -10 10 -17.5t24 -7.5h185q14 0 23.5 7.5t10.5 17.5z" /> + <glyph glyph-name="gift" unicode="&#xf06b;" +d="M928 180v56v468v192h-320v-192v-468v-56q0 -25 18 -38.5t46 -13.5h192q28 0 46 13.5t18 38.5zM472 1024h195l-126 161q-26 31 -69 31q-40 0 -68 -28t-28 -68t28 -68t68 -28zM1160 1120q0 40 -28 68t-68 28q-43 0 -69 -31l-125 -161h194q40 0 68 28t28 68zM1536 864v-320 +q0 -14 -9 -23t-23 -9h-96v-416q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v416h-96q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h440q-93 0 -158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5q107 0 168 -77l128 -165l128 165q61 77 168 77q93 0 158.5 -65.5t65.5 -158.5 +t-65.5 -158.5t-158.5 -65.5h440q14 0 23 -9t9 -23z" /> + <glyph glyph-name="leaf" unicode="&#xf06c;" horiz-adv-x="1792" +d="M1280 832q0 26 -19 45t-45 19q-172 0 -318 -49.5t-259.5 -134t-235.5 -219.5q-19 -21 -19 -45q0 -26 19 -45t45 -19q24 0 45 19q27 24 74 71t67 66q137 124 268.5 176t313.5 52q26 0 45 19t19 45zM1792 1030q0 -95 -20 -193q-46 -224 -184.5 -383t-357.5 -268 +q-214 -108 -438 -108q-148 0 -286 47q-15 5 -88 42t-96 37q-16 0 -39.5 -32t-45 -70t-52.5 -70t-60 -32q-43 0 -63.5 17.5t-45.5 59.5q-2 4 -6 11t-5.5 10t-3 9.5t-1.5 13.5q0 35 31 73.5t68 65.5t68 56t31 48q0 4 -14 38t-16 44q-9 51 -9 104q0 115 43.5 220t119 184.5 +t170.5 139t204 95.5q55 18 145 25.5t179.5 9t178.5 6t163.5 24t113.5 56.5l29.5 29.5t29.5 28t27 20t36.5 16t43.5 4.5q39 0 70.5 -46t47.5 -112t24 -124t8 -96z" /> + <glyph glyph-name="fire" unicode="&#xf06d;" horiz-adv-x="1408" +d="M1408 -160v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1152 896q0 -78 -24.5 -144t-64 -112.5t-87.5 -88t-96 -77.5t-87.5 -72t-64 -81.5t-24.5 -96.5q0 -96 67 -224l-4 1l1 -1 +q-90 41 -160 83t-138.5 100t-113.5 122.5t-72.5 150.5t-27.5 184q0 78 24.5 144t64 112.5t87.5 88t96 77.5t87.5 72t64 81.5t24.5 96.5q0 94 -66 224l3 -1l-1 1q90 -41 160 -83t138.5 -100t113.5 -122.5t72.5 -150.5t27.5 -184z" /> + <glyph glyph-name="eye_open" unicode="&#xf06e;" horiz-adv-x="1792" +d="M1664 576q-152 236 -381 353q61 -104 61 -225q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 121 61 225q-229 -117 -381 -353q133 -205 333.5 -326.5t434.5 -121.5t434.5 121.5t333.5 326.5zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5 +t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1792 576q0 -34 -20 -69q-140 -230 -376.5 -368.5t-499.5 -138.5t-499.5 139t-376.5 368q-20 35 -20 69t20 69q140 229 376.5 368t499.5 139t499.5 -139t376.5 -368q20 -35 20 -69z" /> + <glyph glyph-name="eye_close" unicode="&#xf070;" horiz-adv-x="1792" +d="M555 201l78 141q-87 63 -136 159t-49 203q0 121 61 225q-229 -117 -381 -353q167 -258 427 -375zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1307 1151q0 -7 -1 -9 +q-106 -189 -316 -567t-315 -566l-49 -89q-10 -16 -28 -16q-12 0 -134 70q-16 10 -16 28q0 12 44 87q-143 65 -263.5 173t-208.5 245q-20 31 -20 69t20 69q153 235 380 371t496 136q89 0 180 -17l54 97q10 16 28 16q5 0 18 -6t31 -15.5t33 -18.5t31.5 -18.5t19.5 -11.5 +q16 -10 16 -27zM1344 704q0 -139 -79 -253.5t-209 -164.5l280 502q8 -45 8 -84zM1792 576q0 -35 -20 -69q-39 -64 -109 -145q-150 -172 -347.5 -267t-419.5 -95l74 132q212 18 392.5 137t301.5 307q-115 179 -282 294l63 112q95 -64 182.5 -153t144.5 -184q20 -34 20 -69z +" /> + <glyph glyph-name="warning_sign" unicode="&#xf071;" horiz-adv-x="1792" +d="M1024 161v190q0 14 -9.5 23.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -23.5v-190q0 -14 9.5 -23.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 23.5zM1022 535l18 459q0 12 -10 19q-13 11 -24 11h-220q-11 0 -24 -11q-10 -7 -10 -21l17 -457q0 -10 10 -16.5t24 -6.5h185 +q14 0 23.5 6.5t10.5 16.5zM1008 1469l768 -1408q35 -63 -2 -126q-17 -29 -46.5 -46t-63.5 -17h-1536q-34 0 -63.5 17t-46.5 46q-37 63 -2 126l768 1408q17 31 47 49t65 18t65 -18t47 -49z" /> + <glyph glyph-name="plane" unicode="&#xf072;" horiz-adv-x="1408" +d="M1376 1376q44 -52 12 -148t-108 -172l-161 -161l160 -696q5 -19 -12 -33l-128 -96q-7 -6 -19 -6q-4 0 -7 1q-15 3 -21 16l-279 508l-259 -259l53 -194q5 -17 -8 -31l-96 -96q-9 -9 -23 -9h-2q-15 2 -24 13l-189 252l-252 189q-11 7 -13 23q-1 13 9 25l96 97q9 9 23 9 +q6 0 8 -1l194 -53l259 259l-508 279q-14 8 -17 24q-2 16 9 27l128 128q14 13 30 8l665 -159l160 160q76 76 172 108t148 -12z" /> + <glyph glyph-name="calendar" unicode="&#xf073;" horiz-adv-x="1664" +d="M128 -128h288v288h-288v-288zM480 -128h320v288h-320v-288zM128 224h288v320h-288v-320zM480 224h320v320h-320v-320zM128 608h288v288h-288v-288zM864 -128h320v288h-320v-288zM480 608h320v288h-320v-288zM1248 -128h288v288h-288v-288zM864 224h320v320h-320v-320z +M512 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1248 224h288v320h-288v-320zM864 608h320v288h-320v-288zM1248 608h288v288h-288v-288zM1280 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64 +q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47 +h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" /> + <glyph glyph-name="random" unicode="&#xf074;" horiz-adv-x="1792" +d="M666 1055q-60 -92 -137 -273q-22 45 -37 72.5t-40.5 63.5t-51 56.5t-63 35t-81.5 14.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q250 0 410 -225zM1792 256q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192q-32 0 -85 -0.5t-81 -1t-73 1 +t-71 5t-64 10.5t-63 18.5t-58 28.5t-59 40t-55 53.5t-56 69.5q59 93 136 273q22 -45 37 -72.5t40.5 -63.5t51 -56.5t63 -35t81.5 -14.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1792 1152q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5 +v192h-256q-48 0 -87 -15t-69 -45t-51 -61.5t-45 -77.5q-32 -62 -78 -171q-29 -66 -49.5 -111t-54 -105t-64 -100t-74 -83t-90 -68.5t-106.5 -42t-128 -16.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q48 0 87 15t69 45t51 61.5t45 77.5q32 62 78 171q29 66 49.5 111 +t54 105t64 100t74 83t90 68.5t106.5 42t128 16.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" /> + <glyph glyph-name="comment" unicode="&#xf075;" horiz-adv-x="1792" +d="M1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22q-17 -2 -30.5 9t-17.5 29v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281 +q0 130 71 248.5t191 204.5t286 136.5t348 50.5q244 0 450 -85.5t326 -233t120 -321.5z" /> + <glyph glyph-name="magnet" unicode="&#xf076;" +d="M1536 704v-128q0 -201 -98.5 -362t-274 -251.5t-395.5 -90.5t-395.5 90.5t-274 251.5t-98.5 362v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-128q0 -52 23.5 -90t53.5 -57t71 -30t64 -13t44 -2t44 2t64 13t71 30t53.5 57t23.5 90v128q0 26 19 45t45 19h384 +q26 0 45 -19t19 -45zM512 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45zM1536 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45z" /> + <glyph glyph-name="chevron_up" unicode="&#xf077;" horiz-adv-x="1792" +d="M1683 205l-166 -165q-19 -19 -45 -19t-45 19l-531 531l-531 -531q-19 -19 -45 -19t-45 19l-166 165q-19 19 -19 45.5t19 45.5l742 741q19 19 45 19t45 -19l742 -741q19 -19 19 -45.5t-19 -45.5z" /> + <glyph glyph-name="chevron_down" unicode="&#xf078;" horiz-adv-x="1792" +d="M1683 728l-742 -741q-19 -19 -45 -19t-45 19l-742 741q-19 19 -19 45.5t19 45.5l166 165q19 19 45 19t45 -19l531 -531l531 531q19 19 45 19t45 -19l166 -165q19 -19 19 -45.5t-19 -45.5z" /> + <glyph glyph-name="retweet" unicode="&#xf079;" horiz-adv-x="1920" +d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -10 7 -21 +zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z +" /> + <glyph glyph-name="shopping_cart" unicode="&#xf07a;" horiz-adv-x="1664" +d="M640 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1536 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1664 1088v-512q0 -24 -16.5 -42.5t-40.5 -21.5l-1044 -122q13 -60 13 -70q0 -16 -24 -64h920q26 0 45 -19t19 -45 +t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 11 8 31.5t16 36t21.5 40t15.5 29.5l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t19.5 -15.5t13 -24.5t8 -26t5.5 -29.5t4.5 -26h1201q26 0 45 -19t19 -45z" /> + <glyph glyph-name="folder_close" unicode="&#xf07b;" horiz-adv-x="1664" +d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" /> + <glyph glyph-name="folder_open" unicode="&#xf07c;" horiz-adv-x="1920" +d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5 +t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" /> + <glyph glyph-name="resize_vertical" unicode="&#xf07d;" horiz-adv-x="768" +d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" /> + <glyph glyph-name="resize_horizontal" unicode="&#xf07e;" horiz-adv-x="1792" +d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" /> + <glyph glyph-name="bar_chart" unicode="&#xf080;" horiz-adv-x="2048" +d="M640 640v-512h-256v512h256zM1024 1152v-1024h-256v1024h256zM2048 0v-128h-2048v1536h128v-1408h1920zM1408 896v-768h-256v768h256zM1792 1280v-1152h-256v1152h256z" /> + <glyph glyph-name="twitter_sign" unicode="&#xf081;" +d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4 +q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5 +t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="facebook_sign" unicode="&#xf082;" +d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-188v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-532q-119 0 -203.5 84.5t-84.5 203.5v960 +q0 119 84.5 203.5t203.5 84.5h960z" /> + <glyph glyph-name="camera_retro" unicode="&#xf083;" horiz-adv-x="1792" +d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5 +t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280 +q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" /> + <glyph glyph-name="key" unicode="&#xf084;" horiz-adv-x="1792" +d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26 +l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5 +t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" /> + <glyph glyph-name="cogs" unicode="&#xf085;" horiz-adv-x="1920" +d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5 +t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -11 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5 +l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7 +l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -8 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31 +q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20 +t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68 +q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70 +q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" /> + <glyph glyph-name="comments" unicode="&#xf086;" horiz-adv-x="1792" +d="M1408 768q0 -139 -94 -257t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224 +q0 139 94 257t256.5 186.5t353.5 68.5t353.5 -68.5t256.5 -186.5t94 -257zM1792 512q0 -120 -71 -224.5t-195 -176.5q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7 +q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230z" /> + <glyph glyph-name="thumbs_up_alt" unicode="&#xf087;" +d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 768q0 51 -39 89.5t-89 38.5h-352q0 58 48 159.5t48 160.5q0 98 -32 145t-128 47q-26 -26 -38 -85t-30.5 -125.5t-59.5 -109.5q-22 -23 -77 -91q-4 -5 -23 -30t-31.5 -41t-34.5 -42.5 +t-40 -44t-38.5 -35.5t-40 -27t-35.5 -9h-32v-640h32q13 0 31.5 -3t33 -6.5t38 -11t35 -11.5t35.5 -12.5t29 -10.5q211 -73 342 -73h121q192 0 192 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5q32 1 53.5 47t21.5 81zM1536 769 +q0 -89 -49 -163q9 -33 9 -69q0 -77 -38 -144q3 -21 3 -43q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5h-36h-93q-96 0 -189.5 22.5t-216.5 65.5q-116 40 -138 40h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h274q36 24 137 155q58 75 107 128 +q24 25 35.5 85.5t30.5 126.5t62 108q39 37 90 37q84 0 151 -32.5t102 -101.5t35 -186q0 -93 -48 -192h176q104 0 180 -76t76 -179z" /> + <glyph glyph-name="thumbs_down_alt" unicode="&#xf088;" +d="M256 1088q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 512q0 35 -21.5 81t-53.5 47q15 17 25 47.5t10 55.5q0 69 -53 119q18 31 18 69q0 37 -17.5 73.5t-47.5 52.5q5 30 5 56q0 85 -49 126t-136 41h-128q-131 0 -342 -73q-5 -2 -29 -10.5 +t-35.5 -12.5t-35 -11.5t-38 -11t-33 -6.5t-31.5 -3h-32v-640h32q16 0 35.5 -9t40 -27t38.5 -35.5t40 -44t34.5 -42.5t31.5 -41t23 -30q55 -68 77 -91q41 -43 59.5 -109.5t30.5 -125.5t38 -85q96 0 128 47t32 145q0 59 -48 160.5t-48 159.5h352q50 0 89 38.5t39 89.5z +M1536 511q0 -103 -76 -179t-180 -76h-176q48 -99 48 -192q0 -118 -35 -186q-35 -69 -102 -101.5t-151 -32.5q-51 0 -90 37q-34 33 -54 82t-25.5 90.5t-17.5 84.5t-31 64q-48 50 -107 127q-101 131 -137 155h-274q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5 +h288q22 0 138 40q128 44 223 66t200 22h112q140 0 226.5 -79t85.5 -216v-5q60 -77 60 -178q0 -22 -3 -43q38 -67 38 -144q0 -36 -9 -69q49 -73 49 -163z" /> + <glyph glyph-name="star_half" unicode="&#xf089;" horiz-adv-x="896" +d="M832 1504v-1339l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41z" /> + <glyph glyph-name="heart_empty" unicode="&#xf08a;" horiz-adv-x="1792" +d="M1664 940q0 81 -21.5 143t-55 98.5t-81.5 59.5t-94 31t-98 8t-112 -25.5t-110.5 -64t-86.5 -72t-60 -61.5q-18 -22 -49 -22t-49 22q-24 28 -60 61.5t-86.5 72t-110.5 64t-112 25.5t-98 -8t-94 -31t-81.5 -59.5t-55 -98.5t-21.5 -143q0 -168 187 -355l581 -560l580 559 +q188 188 188 356zM1792 940q0 -221 -229 -450l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5 +q224 0 351 -124t127 -344z" /> + <glyph glyph-name="signout" unicode="&#xf08b;" horiz-adv-x="1664" +d="M640 96q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h320q13 0 22.5 -9.5t9.5 -22.5q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-66 0 -113 -47t-47 -113v-704 +q0 -66 47 -113t113 -47h288h11h13t11.5 -1t11.5 -3t8 -5.5t7 -9t2 -13.5zM1568 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45z" /> + <glyph glyph-name="linkedin_sign" unicode="&#xf08c;" +d="M237 122h231v694h-231v-694zM483 1030q-1 52 -36 86t-93 34t-94.5 -34t-36.5 -86q0 -51 35.5 -85.5t92.5 -34.5h1q59 0 95 34.5t36 85.5zM1068 122h231v398q0 154 -73 233t-193 79q-136 0 -209 -117h2v101h-231q3 -66 0 -694h231v388q0 38 7 56q15 35 45 59.5t74 24.5 +q116 0 116 -157v-371zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="pushpin" unicode="&#xf08d;" horiz-adv-x="1152" +d="M480 672v448q0 14 -9 23t-23 9t-23 -9t-9 -23v-448q0 -14 9 -23t23 -9t23 9t9 23zM1152 320q0 -26 -19 -45t-45 -19h-429l-51 -483q-2 -12 -10.5 -20.5t-20.5 -8.5h-1q-27 0 -32 27l-76 485h-404q-26 0 -45 19t-19 45q0 123 78.5 221.5t177.5 98.5v512q-52 0 -90 38 +t-38 90t38 90t90 38h640q52 0 90 -38t38 -90t-38 -90t-90 -38v-512q99 0 177.5 -98.5t78.5 -221.5z" /> + <glyph glyph-name="external_link" unicode="&#xf08e;" horiz-adv-x="1792" +d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320 +q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" /> + <glyph glyph-name="signin" unicode="&#xf090;" +d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5 +q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="trophy" unicode="&#xf091;" horiz-adv-x="1664" +d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91 +t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96 +q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" /> + <glyph glyph-name="github_sign" unicode="&#xf092;" +d="M519 336q4 6 -3 13q-9 7 -14 2q-4 -6 3 -13q9 -7 14 -2zM491 377q-5 7 -12 4q-6 -4 0 -12q7 -8 12 -5q6 4 0 13zM450 417q2 4 -5 8q-7 2 -8 -2q-3 -5 4 -8q8 -2 9 2zM471 394q2 1 1.5 4.5t-3.5 5.5q-6 7 -10 3t1 -11q6 -6 11 -2zM557 319q2 7 -9 11q-9 3 -13 -4 +q-2 -7 9 -11q9 -3 13 4zM599 316q0 8 -12 8q-10 0 -10 -8t11 -8t11 8zM638 323q-2 7 -13 5t-9 -9q2 -8 12 -6t10 10zM1280 640q0 212 -150 362t-362 150t-362 -150t-150 -362q0 -167 98 -300.5t252 -185.5q18 -3 26.5 5t8.5 20q0 52 -1 95q-6 -1 -15.5 -2.5t-35.5 -2t-48 4 +t-43.5 20t-29.5 41.5q-23 59 -57 74q-2 1 -4.5 3.5l-8 8t-7 9.5t4 7.5t19.5 3.5q6 0 15 -2t30 -15.5t33 -35.5q16 -28 37.5 -42t43.5 -14t38 3.5t30 9.5q7 47 33 69q-49 6 -86 18.5t-73 39t-55.5 76t-19.5 119.5q0 79 53 137q-24 62 5 136q19 6 54.5 -7.5t60.5 -29.5l26 -16 +q58 17 128 17t128 -17q11 7 28.5 18t55.5 26t57 9q29 -74 5 -136q53 -58 53 -137q0 -57 -14 -100.5t-35.5 -70t-53.5 -44.5t-62.5 -26t-68.5 -12q35 -31 35 -95q0 -40 -0.5 -89t-0.5 -51q0 -12 8.5 -20t26.5 -5q154 52 252 185.5t98 300.5zM1536 1120v-960 +q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="upload_alt" unicode="&#xf093;" horiz-adv-x="1664" +d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92 +t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" /> + <glyph glyph-name="lemon" unicode="&#xf094;" +d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5 +q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44 +q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5 +q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -13 2 -25t3.5 -16.5t7.5 -20.5t8 -20q16 -40 25 -118.5t9 -136.5z" /> + <glyph glyph-name="phone" unicode="&#xf095;" horiz-adv-x="1408" +d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -53 3.5t-57.5 12.5t-47 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-127 79 -264 216t-216 264q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47t-12.5 57.5t-3.5 53q0 92 51 186 +q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174q2 -1 19 -11.5t24 -14 +t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" /> + <glyph glyph-name="check_empty" unicode="&#xf096;" horiz-adv-x="1408" +d="M1120 1280h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v832q0 66 -47 113t-113 47zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 +q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="bookmark_empty" unicode="&#xf097;" horiz-adv-x="1280" +d="M1152 1280h-1024v-1242l423 406l89 85l89 -85l423 -406v1242zM1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289 +q0 34 19.5 62t52.5 41q21 9 44 9h1048z" /> + <glyph glyph-name="phone_sign" unicode="&#xf098;" +d="M1280 343q0 11 -2 16t-18 16.5t-40.5 25t-47.5 26.5t-45.5 25t-28.5 15q-5 3 -19 13t-25 15t-21 5q-15 0 -36.5 -20.5t-39.5 -45t-38.5 -45t-33.5 -20.5q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170 126.5t-127 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5 +t-3.5 16.5q0 13 20.5 33.5t45 38.5t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5 +t320.5 -216.5q6 -2 30 -11t33 -12.5t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z +" /> + <glyph glyph-name="twitter" unicode="&#xf099;" horiz-adv-x="1664" +d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41 +q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" /> + <glyph glyph-name="facebook" unicode="&#xf09a;" horiz-adv-x="1024" +d="M959 1524v-264h-157q-86 0 -116 -36t-30 -108v-189h293l-39 -296h-254v-759h-306v759h-255v296h255v218q0 186 104 288.5t277 102.5q147 0 228 -12z" /> + <glyph glyph-name="github" unicode="&#xf09b;" +d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -40 7t-13 30q0 3 0.5 76.5t0.5 134.5q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 119 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24 +q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-85 13.5q-45 -113 -8 -204q-79 -87 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-39 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5 +t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -88.5t0.5 -54.5q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103zM291 305q3 7 -7 12 +q-10 3 -13 -2q-3 -7 7 -12q9 -6 13 2zM322 271q7 5 -2 16q-10 9 -16 3q-7 -5 2 -16q10 -10 16 -3zM352 226q9 7 0 19q-8 13 -17 6q-9 -5 0 -18t17 -7zM394 184q8 8 -4 19q-12 12 -20 3q-9 -8 4 -19q12 -12 20 -3zM451 159q3 11 -13 16q-15 4 -19 -7t13 -15q15 -6 19 6z +M514 154q0 13 -17 11q-16 0 -16 -11q0 -13 17 -11q16 0 16 11zM572 164q-2 11 -18 9q-16 -3 -14 -15t18 -8t14 14z" /> + <glyph glyph-name="unlock" unicode="&#xf09c;" horiz-adv-x="1664" +d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5 +t316.5 -131.5t131.5 -316.5z" /> + <glyph glyph-name="credit_card" unicode="&#xf09d;" horiz-adv-x="1920" +d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608 +q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" /> + <glyph glyph-name="rss" unicode="&#xf09e;" horiz-adv-x="1408" +d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5 +t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294 +q187 -186 294 -425.5t120 -501.5z" /> + <glyph glyph-name="hdd" unicode="&#xf0a0;" +d="M1040 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1296 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1408 160v320q0 13 -9.5 22.5t-22.5 9.5 +h-1216q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5zM178 640h1180l-157 482q-4 13 -16 21.5t-26 8.5h-782q-14 0 -26 -8.5t-16 -21.5zM1536 480v-320q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v320q0 25 16 75 +l197 606q17 53 63 86t101 33h782q55 0 101 -33t63 -86l197 -606q16 -50 16 -75z" /> + <glyph glyph-name="bullhorn" unicode="&#xf0a1;" horiz-adv-x="1792" +d="M1664 896q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5v-384q0 -52 -38 -90t-90 -38q-417 347 -812 380q-58 -19 -91 -66t-31 -100.5t40 -92.5q-20 -33 -23 -65.5t6 -58t33.5 -55t48 -50t61.5 -50.5q-29 -58 -111.5 -83t-168.5 -11.5t-132 55.5q-7 23 -29.5 87.5 +t-32 94.5t-23 89t-15 101t3.5 98.5t22 110.5h-122q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h480q435 0 896 384q52 0 90 -38t38 -90v-384zM1536 292v954q-394 -302 -768 -343v-270q377 -42 768 -341z" /> + <glyph glyph-name="bell" unicode="&#xf0a2;" horiz-adv-x="1792" +d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM246 128h1300q-266 300 -266 832q0 51 -24 105t-69 103t-121.5 80.5t-169.5 31.5t-169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -532 -266 -832z +M1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5 +t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" /> + <glyph glyph-name="certificate" unicode="&#xf0a3;" +d="M1376 640l138 -135q30 -28 20 -70q-12 -41 -52 -51l-188 -48l53 -186q12 -41 -19 -70q-29 -31 -70 -19l-186 53l-48 -188q-10 -40 -51 -52q-12 -2 -19 -2q-31 0 -51 22l-135 138l-135 -138q-28 -30 -70 -20q-41 11 -51 52l-48 188l-186 -53q-41 -12 -70 19q-31 29 -19 70 +l53 186l-188 48q-40 10 -52 51q-10 42 20 70l138 135l-138 135q-30 28 -20 70q12 41 52 51l188 48l-53 186q-12 41 19 70q29 31 70 19l186 -53l48 188q10 41 51 51q41 12 70 -19l135 -139l135 139q29 30 70 19q41 -10 51 -51l48 -188l186 53q41 12 70 -19q31 -29 19 -70 +l-53 -186l188 -48q40 -10 52 -51q10 -42 -20 -70z" /> + <glyph glyph-name="hand_right" unicode="&#xf0a4;" horiz-adv-x="1792" +d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 768q0 51 -39 89.5t-89 38.5h-576q0 20 15 48.5t33 55t33 68t15 84.5q0 67 -44.5 97.5t-115.5 30.5q-24 0 -90 -139q-24 -44 -37 -65q-40 -64 -112 -145q-71 -81 -101 -106 +q-69 -57 -140 -57h-32v-640h32q72 0 167 -32t193.5 -64t179.5 -32q189 0 189 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5h331q52 0 90 38t38 90zM1792 769q0 -105 -75.5 -181t-180.5 -76h-169q-4 -62 -37 -119q3 -21 3 -43 +q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5q-133 0 -322 69q-164 59 -223 59h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h288q10 0 21.5 4.5t23.5 14t22.5 18t24 22.5t20.5 21.5t19 21.5t14 17q65 74 100 129q13 21 33 62t37 72t40.5 63t55 49.5 +t69.5 17.5q125 0 206.5 -67t81.5 -189q0 -68 -22 -128h374q104 0 180 -76t76 -179z" /> + <glyph glyph-name="hand_left" unicode="&#xf0a5;" horiz-adv-x="1792" +d="M1376 128h32v640h-32q-35 0 -67.5 12t-62.5 37t-50 46t-49 54q-8 9 -12 14q-72 81 -112 145q-14 22 -38 68q-1 3 -10.5 22.5t-18.5 36t-20 35.5t-21.5 30.5t-18.5 11.5q-71 0 -115.5 -30.5t-44.5 -97.5q0 -43 15 -84.5t33 -68t33 -55t15 -48.5h-576q-50 0 -89 -38.5 +t-39 -89.5q0 -52 38 -90t90 -38h331q-15 -17 -25 -47.5t-10 -55.5q0 -69 53 -119q-18 -32 -18 -69t17.5 -73.5t47.5 -52.5q-4 -24 -4 -56q0 -85 48.5 -126t135.5 -41q84 0 183 32t194 64t167 32zM1664 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45z +M1792 768v-640q0 -53 -37.5 -90.5t-90.5 -37.5h-288q-59 0 -223 -59q-190 -69 -317 -69q-142 0 -230 77.5t-87 217.5l1 5q-61 76 -61 178q0 22 3 43q-33 57 -37 119h-169q-105 0 -180.5 76t-75.5 181q0 103 76 179t180 76h374q-22 60 -22 128q0 122 81.5 189t206.5 67 +q38 0 69.5 -17.5t55 -49.5t40.5 -63t37 -72t33 -62q35 -55 100 -129q2 -3 14 -17t19 -21.5t20.5 -21.5t24 -22.5t22.5 -18t23.5 -14t21.5 -4.5h288q53 0 90.5 -37.5t37.5 -90.5z" /> + <glyph glyph-name="hand_up" unicode="&#xf0a6;" +d="M1280 -64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 700q0 189 -167 189q-26 0 -56 -5q-16 30 -52.5 47.5t-73.5 17.5t-69 -18q-50 53 -119 53q-25 0 -55.5 -10t-47.5 -25v331q0 52 -38 90t-90 38q-51 0 -89.5 -39t-38.5 -89v-576 +q-20 0 -48.5 15t-55 33t-68 33t-84.5 15q-67 0 -97.5 -44.5t-30.5 -115.5q0 -24 139 -90q44 -24 65 -37q64 -40 145 -112q81 -71 106 -101q57 -69 57 -140v-32h640v32q0 72 32 167t64 193.5t32 179.5zM1536 705q0 -133 -69 -322q-59 -164 -59 -223v-288q0 -53 -37.5 -90.5 +t-90.5 -37.5h-640q-53 0 -90.5 37.5t-37.5 90.5v288q0 10 -4.5 21.5t-14 23.5t-18 22.5t-22.5 24t-21.5 20.5t-21.5 19t-17 14q-74 65 -129 100q-21 13 -62 33t-72 37t-63 40.5t-49.5 55t-17.5 69.5q0 125 67 206.5t189 81.5q68 0 128 -22v374q0 104 76 180t179 76 +q105 0 181 -75.5t76 -180.5v-169q62 -4 119 -37q21 3 43 3q101 0 178 -60q139 1 219.5 -85t80.5 -227z" /> + <glyph glyph-name="hand_down" unicode="&#xf0a7;" +d="M1408 576q0 84 -32 183t-64 194t-32 167v32h-640v-32q0 -35 -12 -67.5t-37 -62.5t-46 -50t-54 -49q-9 -8 -14 -12q-81 -72 -145 -112q-22 -14 -68 -38q-3 -1 -22.5 -10.5t-36 -18.5t-35.5 -20t-30.5 -21.5t-11.5 -18.5q0 -71 30.5 -115.5t97.5 -44.5q43 0 84.5 15t68 33 +t55 33t48.5 15v-576q0 -50 38.5 -89t89.5 -39q52 0 90 38t38 90v331q46 -35 103 -35q69 0 119 53q32 -18 69 -18t73.5 17.5t52.5 47.5q24 -4 56 -4q85 0 126 48.5t41 135.5zM1280 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 580 +q0 -142 -77.5 -230t-217.5 -87l-5 1q-76 -61 -178 -61q-22 0 -43 3q-54 -30 -119 -37v-169q0 -105 -76 -180.5t-181 -75.5q-103 0 -179 76t-76 180v374q-54 -22 -128 -22q-121 0 -188.5 81.5t-67.5 206.5q0 38 17.5 69.5t49.5 55t63 40.5t72 37t62 33q55 35 129 100 +q3 2 17 14t21.5 19t21.5 20.5t22.5 24t18 22.5t14 23.5t4.5 21.5v288q0 53 37.5 90.5t90.5 37.5h640q53 0 90.5 -37.5t37.5 -90.5v-288q0 -59 59 -223q69 -190 69 -317z" /> + <glyph glyph-name="circle_arrow_left" unicode="&#xf0a8;" +d="M1280 576v128q0 26 -19 45t-45 19h-502l189 189q19 19 19 45t-19 45l-91 91q-18 18 -45 18t-45 -18l-362 -362l-91 -91q-18 -18 -18 -45t18 -45l91 -91l362 -362q18 -18 45 -18t45 18l91 91q18 18 18 45t-18 45l-189 189h502q26 0 45 19t19 45zM1536 640 +q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="circle_arrow_right" unicode="&#xf0a9;" +d="M1285 640q0 27 -18 45l-91 91l-362 362q-18 18 -45 18t-45 -18l-91 -91q-18 -18 -18 -45t18 -45l189 -189h-502q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h502l-189 -189q-19 -19 -19 -45t19 -45l91 -91q18 -18 45 -18t45 18l362 362l91 91q18 18 18 45zM1536 640 +q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="circle_arrow_up" unicode="&#xf0aa;" +d="M1284 641q0 27 -18 45l-362 362l-91 91q-18 18 -45 18t-45 -18l-91 -91l-362 -362q-18 -18 -18 -45t18 -45l91 -91q18 -18 45 -18t45 18l189 189v-502q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v502l189 -189q19 -19 45 -19t45 19l91 91q18 18 18 45zM1536 640 +q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="circle_arrow_down" unicode="&#xf0ab;" +d="M1284 639q0 27 -18 45l-91 91q-18 18 -45 18t-45 -18l-189 -189v502q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-502l-189 189q-19 19 -45 19t-45 -19l-91 -91q-18 -18 -18 -45t18 -45l362 -362l91 -91q18 -18 45 -18t45 18l91 91l362 362q18 18 18 45zM1536 640 +q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="globe" unicode="&#xf0ac;" +d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1042 887q-2 -1 -9.5 -9.5t-13.5 -9.5q2 0 4.5 5t5 11t3.5 7q6 7 22 15q14 6 52 12q34 8 51 -11 +q-2 2 9.5 13t14.5 12q3 2 15 4.5t15 7.5l2 22q-12 -1 -17.5 7t-6.5 21q0 -2 -6 -8q0 7 -4.5 8t-11.5 -1t-9 -1q-10 3 -15 7.5t-8 16.5t-4 15q-2 5 -9.5 11t-9.5 10q-1 2 -2.5 5.5t-3 6.5t-4 5.5t-5.5 2.5t-7 -5t-7.5 -10t-4.5 -5q-3 2 -6 1.5t-4.5 -1t-4.5 -3t-5 -3.5 +q-3 -2 -8.5 -3t-8.5 -2q15 5 -1 11q-10 4 -16 3q9 4 7.5 12t-8.5 14h5q-1 4 -8.5 8.5t-17.5 8.5t-13 6q-8 5 -34 9.5t-33 0.5q-5 -6 -4.5 -10.5t4 -14t3.5 -12.5q1 -6 -5.5 -13t-6.5 -12q0 -7 14 -15.5t10 -21.5q-3 -8 -16 -16t-16 -12q-5 -8 -1.5 -18.5t10.5 -16.5 +q2 -2 1.5 -4t-3.5 -4.5t-5.5 -4t-6.5 -3.5l-3 -2q-11 -5 -20.5 6t-13.5 26q-7 25 -16 30q-23 8 -29 -1q-5 13 -41 26q-25 9 -58 4q6 1 0 15q-7 15 -19 12q3 6 4 17.5t1 13.5q3 13 12 23q1 1 7 8.5t9.5 13.5t0.5 6q35 -4 50 11q5 5 11.5 17t10.5 17q9 6 14 5.5t14.5 -5.5 +t14.5 -5q14 -1 15.5 11t-7.5 20q12 -1 3 17q-4 7 -8 9q-12 4 -27 -5q-8 -4 2 -8q-1 1 -9.5 -10.5t-16.5 -17.5t-16 5q-1 1 -5.5 13.5t-9.5 13.5q-8 0 -16 -15q3 8 -11 15t-24 8q19 12 -8 27q-7 4 -20.5 5t-19.5 -4q-5 -7 -5.5 -11.5t5 -8t10.5 -5.5t11.5 -4t8.5 -3 +q14 -10 8 -14q-2 -1 -8.5 -3.5t-11.5 -4.5t-6 -4q-3 -4 0 -14t-2 -14q-5 5 -9 17.5t-7 16.5q7 -9 -25 -6l-10 1q-4 0 -16 -2t-20.5 -1t-13.5 8q-4 8 0 20q1 4 4 2q-4 3 -11 9.5t-10 8.5q-46 -15 -94 -41q6 -1 12 1q5 2 13 6.5t10 5.5q34 14 42 7l5 5q14 -16 20 -25 +q-7 4 -30 1q-20 -6 -22 -12q7 -12 5 -18q-4 3 -11.5 10t-14.5 11t-15 5q-16 0 -22 -1q-146 -80 -235 -222q7 -7 12 -8q4 -1 5 -9t2.5 -11t11.5 3q9 -8 3 -19q1 1 44 -27q19 -17 21 -21q3 -11 -10 -18q-1 2 -9 9t-9 4q-3 -5 0.5 -18.5t10.5 -12.5q-7 0 -9.5 -16t-2.5 -35.5 +t-1 -23.5l2 -1q-3 -12 5.5 -34.5t21.5 -19.5q-13 -3 20 -43q6 -8 8 -9q3 -2 12 -7.5t15 -10t10 -10.5q4 -5 10 -22.5t14 -23.5q-2 -6 9.5 -20t10.5 -23q-1 0 -2.5 -1t-2.5 -1q3 -7 15.5 -14t15.5 -13q1 -3 2 -10t3 -11t8 -2q2 20 -24 62q-15 25 -17 29q-3 5 -5.5 15.5 +t-4.5 14.5q2 0 6 -1.5t8.5 -3.5t7.5 -4t2 -3q-3 -7 2 -17.5t12 -18.5t17 -19t12 -13q6 -6 14 -19.5t0 -13.5q9 0 20 -10.5t17 -19.5q5 -8 8 -26t5 -24q2 -7 8.5 -13.5t12.5 -9.5l16 -8t13 -7q5 -2 18.5 -10.5t21.5 -11.5q10 -4 16 -4t14.5 2.5t13.5 3.5q15 2 29 -15t21 -21 +q36 -19 55 -11q-2 -1 0.5 -7.5t8 -15.5t9 -14.5t5.5 -8.5q5 -6 18 -15t18 -15q6 4 7 9q-3 -8 7 -20t18 -10q14 3 14 32q-31 -15 -49 18q0 1 -2.5 5.5t-4 8.5t-2.5 8.5t0 7.5t5 3q9 0 10 3.5t-2 12.5t-4 13q-1 8 -11 20t-12 15q-5 -9 -16 -8t-16 9q0 -1 -1.5 -5.5t-1.5 -6.5 +q-13 0 -15 1q1 3 2.5 17.5t3.5 22.5q1 4 5.5 12t7.5 14.5t4 12.5t-4.5 9.5t-17.5 2.5q-19 -1 -26 -20q-1 -3 -3 -10.5t-5 -11.5t-9 -7q-7 -3 -24 -2t-24 5q-13 8 -22.5 29t-9.5 37q0 10 2.5 26.5t3 25t-5.5 24.5q3 2 9 9.5t10 10.5q2 1 4.5 1.5t4.5 0t4 1.5t3 6q-1 1 -4 3 +q-3 3 -4 3q7 -3 28.5 1.5t27.5 -1.5q15 -11 22 2q0 1 -2.5 9.5t-0.5 13.5q5 -27 29 -9q3 -3 15.5 -5t17.5 -5q3 -2 7 -5.5t5.5 -4.5t5 0.5t8.5 6.5q10 -14 12 -24q11 -40 19 -44q7 -3 11 -2t4.5 9.5t0 14t-1.5 12.5l-1 8v18l-1 8q-15 3 -18.5 12t1.5 18.5t15 18.5q1 1 8 3.5 +t15.5 6.5t12.5 8q21 19 15 35q7 0 11 9q-1 0 -5 3t-7.5 5t-4.5 2q9 5 2 16q5 3 7.5 11t7.5 10q9 -12 21 -2q8 8 1 16q5 7 20.5 10.5t18.5 9.5q7 -2 8 2t1 12t3 12q4 5 15 9t13 5l17 11q3 4 0 4q18 -2 31 11q10 11 -6 20q3 6 -3 9.5t-15 5.5q3 1 11.5 0.5t10.5 1.5 +q15 10 -7 16q-17 5 -43 -12zM879 10q206 36 351 189q-3 3 -12.5 4.5t-12.5 3.5q-18 7 -24 8q1 7 -2.5 13t-8 9t-12.5 8t-11 7q-2 2 -7 6t-7 5.5t-7.5 4.5t-8.5 2t-10 -1l-3 -1q-3 -1 -5.5 -2.5t-5.5 -3t-4 -3t0 -2.5q-21 17 -36 22q-5 1 -11 5.5t-10.5 7t-10 1.5t-11.5 -7 +q-5 -5 -6 -15t-2 -13q-7 5 0 17.5t2 18.5q-3 6 -10.5 4.5t-12 -4.5t-11.5 -8.5t-9 -6.5t-8.5 -5.5t-8.5 -7.5q-3 -4 -6 -12t-5 -11q-2 4 -11.5 6.5t-9.5 5.5q2 -10 4 -35t5 -38q7 -31 -12 -48q-27 -25 -29 -40q-4 -22 12 -26q0 -7 -8 -20.5t-7 -21.5q0 -6 2 -16z" /> + <glyph glyph-name="wrench" unicode="&#xf0ad;" horiz-adv-x="1664" +d="M384 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1028 484l-682 -682q-37 -37 -90 -37q-52 0 -91 37l-106 108q-38 36 -38 90q0 53 38 91l681 681q39 -98 114.5 -173.5t173.5 -114.5zM1662 919q0 -39 -23 -106q-47 -134 -164.5 -217.5 +t-258.5 -83.5q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q58 0 121.5 -16.5t107.5 -46.5q16 -11 16 -28t-16 -28l-293 -169v-224l193 -107q5 3 79 48.5t135.5 81t70.5 35.5q15 0 23.5 -10t8.5 -25z" /> + <glyph glyph-name="tasks" unicode="&#xf0ae;" horiz-adv-x="1792" +d="M1024 128h640v128h-640v-128zM640 640h1024v128h-1024v-128zM1280 1152h384v128h-384v-128zM1792 320v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 832v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19 +t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" /> + <glyph glyph-name="filter" unicode="&#xf0b0;" horiz-adv-x="1408" +d="M1403 1241q17 -41 -14 -70l-493 -493v-742q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-256 256q-19 19 -19 45v486l-493 493q-31 29 -14 70q17 39 59 39h1280q42 0 59 -39z" /> + <glyph glyph-name="briefcase" unicode="&#xf0b1;" horiz-adv-x="1792" +d="M640 1280h512v128h-512v-128zM1792 640v-480q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v480h672v-160q0 -26 19 -45t45 -19h320q26 0 45 19t19 45v160h672zM1024 640v-128h-256v128h256zM1792 1120v-384h-1792v384q0 66 47 113t113 47h352v160q0 40 28 68 +t68 28h576q40 0 68 -28t28 -68v-160h352q66 0 113 -47t47 -113z" /> + <glyph glyph-name="fullscreen" unicode="&#xf0b2;" +d="M1283 995l-355 -355l355 -355l144 144q29 31 70 14q39 -17 39 -59v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l144 144l-355 355l-355 -355l144 -144q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l144 -144 +l355 355l-355 355l-144 -144q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v448q0 26 19 45t45 19h448q42 0 59 -40q17 -39 -14 -69l-144 -144l355 -355l355 355l-144 144q-31 30 -14 69q17 40 59 40h448q26 0 45 -19t19 -45v-448q0 -42 -39 -59q-13 -5 -25 -5q-26 0 -45 19z +" /> + <glyph glyph-name="group" unicode="&#xf0c0;" horiz-adv-x="1920" +d="M593 640q-162 -5 -265 -128h-134q-82 0 -138 40.5t-56 118.5q0 353 124 353q6 0 43.5 -21t97.5 -42.5t119 -21.5q67 0 133 23q-5 -37 -5 -66q0 -139 81 -256zM1664 3q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5 +t43 97.5t62 81t85.5 53.5t111.5 20q10 0 43 -21.5t73 -48t107 -48t135 -21.5t135 21.5t107 48t73 48t43 21.5q61 0 111.5 -20t85.5 -53.5t62 -81t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM640 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75 +t75 -181zM1344 896q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5zM1920 671q0 -78 -56 -118.5t-138 -40.5h-134q-103 123 -265 128q81 117 81 256q0 29 -5 66q66 -23 133 -23q59 0 119 21.5t97.5 42.5 +t43.5 21q124 0 124 -353zM1792 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181z" /> + <glyph glyph-name="link" unicode="&#xf0c1;" horiz-adv-x="1664" +d="M1456 320q0 40 -28 68l-208 208q-28 28 -68 28q-42 0 -72 -32q3 -3 19 -18.5t21.5 -21.5t15 -19t13 -25.5t3.5 -27.5q0 -40 -28 -68t-68 -28q-15 0 -27.5 3.5t-25.5 13t-19 15t-21.5 21.5t-18.5 19q-33 -31 -33 -73q0 -40 28 -68l206 -207q27 -27 68 -27q40 0 68 26 +l147 146q28 28 28 67zM753 1025q0 40 -28 68l-206 207q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l208 -208q27 -27 68 -27q42 0 72 31q-3 3 -19 18.5t-21.5 21.5t-15 19t-13 25.5t-3.5 27.5q0 40 28 68t68 28q15 0 27.5 -3.5t25.5 -13t19 -15 +t21.5 -21.5t18.5 -19q33 31 33 73zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-206 207q-83 83 -83 203q0 123 88 209l-88 88q-86 -88 -208 -88q-120 0 -204 84l-208 208q-84 84 -84 204t85 203l147 146q83 83 203 83q121 0 204 -85l206 -207 +q83 -83 83 -203q0 -123 -88 -209l88 -88q86 88 208 88q120 0 204 -84l208 -208q84 -84 84 -204z" /> + <glyph glyph-name="cloud" unicode="&#xf0c2;" horiz-adv-x="1920" +d="M1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088q-185 0 -316.5 131.5t-131.5 316.5q0 132 71 241.5t187 163.5q-2 28 -2 43q0 212 150 362t362 150q158 0 286.5 -88t187.5 -230q70 62 166 62q106 0 181 -75t75 -181q0 -75 -41 -138q129 -30 213 -134.5t84 -239.5z +" /> + <glyph glyph-name="beaker" unicode="&#xf0c3;" horiz-adv-x="1664" +d="M1527 88q56 -89 21.5 -152.5t-140.5 -63.5h-1152q-106 0 -140.5 63.5t21.5 152.5l503 793v399h-64q-26 0 -45 19t-19 45t19 45t45 19h512q26 0 45 -19t19 -45t-19 -45t-45 -19h-64v-399zM748 813l-272 -429h712l-272 429l-20 31v37v399h-128v-399v-37z" /> + <glyph glyph-name="cut" unicode="&#xf0c4;" horiz-adv-x="1792" +d="M960 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1260 576l507 -398q28 -20 25 -56q-5 -35 -35 -51l-128 -64q-13 -7 -29 -7q-17 0 -31 8l-690 387l-110 -66q-8 -4 -12 -5q14 -49 10 -97q-7 -77 -56 -147.5t-132 -123.5q-132 -84 -277 -84 +q-136 0 -222 78q-90 84 -79 207q7 76 56 147t131 124q132 84 278 84q83 0 151 -31q9 13 22 22l122 73l-122 73q-13 9 -22 22q-68 -31 -151 -31q-146 0 -278 84q-82 53 -131 124t-56 147q-5 59 15.5 113t63.5 93q85 79 222 79q145 0 277 -84q83 -52 132 -123t56 -148 +q4 -48 -10 -97q4 -1 12 -5l110 -66l690 387q14 8 31 8q16 0 29 -7l128 -64q30 -16 35 -51q3 -36 -25 -56zM579 836q46 42 21 108t-106 117q-92 59 -192 59q-74 0 -113 -36q-46 -42 -21 -108t106 -117q92 -59 192 -59q74 0 113 36zM494 91q81 51 106 117t-21 108 +q-39 36 -113 36q-100 0 -192 -59q-81 -51 -106 -117t21 -108q39 -36 113 -36q100 0 192 59zM672 704l96 -58v11q0 36 33 56l14 8l-79 47l-26 -26q-3 -3 -10 -11t-12 -12q-2 -2 -4 -3.5t-3 -2.5zM896 480l96 -32l736 576l-128 64l-768 -431v-113l-160 -96l9 -8q2 -2 7 -6 +q4 -4 11 -12t11 -12l26 -26zM1600 64l128 64l-520 408l-177 -138q-2 -3 -13 -7z" /> + <glyph glyph-name="copy" unicode="&#xf0c5;" horiz-adv-x="1792" +d="M1696 1152q40 0 68 -28t28 -68v-1216q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v288h-544q-40 0 -68 28t-28 68v672q0 40 20 88t48 76l408 408q28 28 76 48t88 20h416q40 0 68 -28t28 -68v-328q68 40 128 40h416zM1152 939l-299 -299h299v299zM512 1323l-299 -299 +h299v299zM708 676l316 316v416h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h512v256q0 40 20 88t48 76zM1664 -128v1152h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h896z" /> + <glyph glyph-name="paper_clip" unicode="&#xf0c6;" horiz-adv-x="1408" +d="M1404 151q0 -117 -79 -196t-196 -79q-135 0 -235 100l-777 776q-113 115 -113 271q0 159 110 270t269 111q158 0 273 -113l605 -606q10 -10 10 -22q0 -16 -30.5 -46.5t-46.5 -30.5q-13 0 -23 10l-606 607q-79 77 -181 77q-106 0 -179 -75t-73 -181q0 -105 76 -181 +l776 -777q63 -63 145 -63q64 0 106 42t42 106q0 82 -63 145l-581 581q-26 24 -60 24q-29 0 -48 -19t-19 -48q0 -32 25 -59l410 -410q10 -10 10 -22q0 -16 -31 -47t-47 -31q-12 0 -22 10l-410 410q-63 61 -63 149q0 82 57 139t139 57q88 0 149 -63l581 -581q100 -98 100 -235 +z" /> + <glyph glyph-name="save" unicode="&#xf0c7;" +d="M384 0h768v384h-768v-384zM1280 0h128v896q0 14 -10 38.5t-20 34.5l-281 281q-10 10 -34 20t-39 10v-416q0 -40 -28 -68t-68 -28h-576q-40 0 -68 28t-28 68v416h-128v-1280h128v416q0 40 28 68t68 28h832q40 0 68 -28t28 -68v-416zM896 928v320q0 13 -9.5 22.5t-22.5 9.5 +h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5zM1536 896v-928q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h928q40 0 88 -20t76 -48l280 -280q28 -28 48 -76t20 -88z" /> + <glyph glyph-name="sign_blank" unicode="&#xf0c8;" +d="M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="reorder" unicode="&#xf0c9;" +d="M1536 192v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 704v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 1216v-128q0 -26 -19 -45 +t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" /> + <glyph glyph-name="ul" unicode="&#xf0ca;" horiz-adv-x="1792" +d="M384 128q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 640q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 +t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1152q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z +M1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" /> + <glyph glyph-name="ol" unicode="&#xf0cb;" horiz-adv-x="1792" +d="M381 -84q0 -80 -54.5 -126t-135.5 -46q-106 0 -172 66l57 88q49 -45 106 -45q29 0 50.5 14.5t21.5 42.5q0 64 -105 56l-26 56q8 10 32.5 43.5t42.5 54t37 38.5v1q-16 0 -48.5 -1t-48.5 -1v-53h-106v152h333v-88l-95 -115q51 -12 81 -49t30 -88zM383 543v-159h-362 +q-6 36 -6 54q0 51 23.5 93t56.5 68t66 47.5t56.5 43.5t23.5 45q0 25 -14.5 38.5t-39.5 13.5q-46 0 -81 -58l-85 59q24 51 71.5 79.5t105.5 28.5q73 0 123 -41.5t50 -112.5q0 -50 -34 -91.5t-75 -64.5t-75.5 -50.5t-35.5 -52.5h127v60h105zM1792 224v-192q0 -13 -9.5 -22.5 +t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1123v-99h-335v99h107q0 41 0.5 121.5t0.5 121.5v12h-2q-8 -17 -50 -54l-71 76l136 127h106v-404h108zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216 +q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" /> + <glyph glyph-name="strikethrough" unicode="&#xf0cc;" horiz-adv-x="1792" +d="M1760 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h1728zM483 704q-28 35 -51 80q-48 98 -48 188q0 181 134 309q133 127 393 127q50 0 167 -19q66 -12 177 -48q10 -38 21 -118q14 -123 14 -183q0 -18 -5 -45l-12 -3l-84 6 +l-14 2q-50 149 -103 205q-88 91 -210 91q-114 0 -182 -59q-67 -58 -67 -146q0 -73 66 -140t279 -129q69 -20 173 -66q58 -28 95 -52h-743zM990 448h411q7 -39 7 -92q0 -111 -41 -212q-23 -56 -71 -104q-37 -35 -109 -81q-80 -48 -153 -66q-80 -21 -203 -21q-114 0 -195 23 +l-140 40q-57 16 -72 28q-8 8 -8 22v13q0 108 -2 156q-1 30 0 68l2 37v44l102 2q15 -34 30 -71t22.5 -56t12.5 -27q35 -57 80 -94q43 -36 105 -57q59 -22 132 -22q64 0 139 27q77 26 122 86q47 61 47 129q0 84 -81 157q-34 29 -137 71z" /> + <glyph glyph-name="underline" unicode="&#xf0cd;" +d="M48 1313q-37 2 -45 4l-3 88q13 1 40 1q60 0 112 -4q132 -7 166 -7q86 0 168 3q116 4 146 5q56 0 86 2l-1 -14l2 -64v-9q-60 -9 -124 -9q-60 0 -79 -25q-13 -14 -13 -132q0 -13 0.5 -32.5t0.5 -25.5l1 -229l14 -280q6 -124 51 -202q35 -59 96 -92q88 -47 177 -47 +q104 0 191 28q56 18 99 51q48 36 65 64q36 56 53 114q21 73 21 229q0 79 -3.5 128t-11 122.5t-13.5 159.5l-4 59q-5 67 -24 88q-34 35 -77 34l-100 -2l-14 3l2 86h84l205 -10q76 -3 196 10l18 -2q6 -38 6 -51q0 -7 -4 -31q-45 -12 -84 -13q-73 -11 -79 -17q-15 -15 -15 -41 +q0 -7 1.5 -27t1.5 -31q8 -19 22 -396q6 -195 -15 -304q-15 -76 -41 -122q-38 -65 -112 -123q-75 -57 -182 -89q-109 -33 -255 -33q-167 0 -284 46q-119 47 -179 122q-61 76 -83 195q-16 80 -16 237v333q0 188 -17 213q-25 36 -147 39zM1536 -96v64q0 14 -9 23t-23 9h-1472 +q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h1472q14 0 23 9t9 23z" /> + <glyph glyph-name="table" unicode="&#xf0ce;" horiz-adv-x="1664" +d="M512 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23 +v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 160v192 +q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192 +q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1664 1248v-1088q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1344q66 0 113 -47t47 -113 +z" /> + <glyph glyph-name="magic" unicode="&#xf0d0;" horiz-adv-x="1664" +d="M1190 955l293 293l-107 107l-293 -293zM1637 1248q0 -27 -18 -45l-1286 -1286q-18 -18 -45 -18t-45 18l-198 198q-18 18 -18 45t18 45l1286 1286q18 18 45 18t45 -18l198 -198q18 -18 18 -45zM286 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM636 1276 +l196 -60l-196 -60l-60 -196l-60 196l-196 60l196 60l60 196zM1566 798l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM926 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98z" /> + <glyph glyph-name="truck" unicode="&#xf0d1;" horiz-adv-x="1792" +d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 +t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 +t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" /> + <glyph glyph-name="pinterest" unicode="&#xf0d2;" +d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 +q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 +q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="pinterest_sign" unicode="&#xf0d3;" +d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 +t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 +t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" /> + <glyph glyph-name="google_plus_sign" unicode="&#xf0d4;" +d="M917 631q0 26 -6 64h-362v-132h217q-3 -24 -16.5 -50t-37.5 -53t-66.5 -44.5t-96.5 -17.5q-99 0 -169 71t-70 171t70 171t169 71q92 0 153 -59l104 101q-108 100 -257 100q-160 0 -272 -112.5t-112 -271.5t112 -271.5t272 -112.5q165 0 266.5 105t101.5 270zM1262 585 +h109v110h-109v110h-110v-110h-110v-110h110v-110h110v110zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="google_plus" unicode="&#xf0d5;" horiz-adv-x="2304" +d="M1437 623q0 -208 -87 -370.5t-248 -254t-369 -91.5q-149 0 -285 58t-234 156t-156 234t-58 285t58 285t156 234t234 156t285 58q286 0 491 -192l-199 -191q-117 113 -292 113q-123 0 -227.5 -62t-165.5 -168.5t-61 -232.5t61 -232.5t165.5 -168.5t227.5 -62 +q83 0 152.5 23t114.5 57.5t78.5 78.5t49 83t21.5 74h-416v252h692q12 -63 12 -122zM2304 745v-210h-209v-209h-210v209h-209v210h209v209h210v-209h209z" /> + <glyph glyph-name="money" unicode="&#xf0d6;" horiz-adv-x="1920" +d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 +v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" /> + <glyph glyph-name="caret_down" unicode="&#xf0d7;" horiz-adv-x="1024" +d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" /> + <glyph glyph-name="caret_up" unicode="&#xf0d8;" horiz-adv-x="1024" +d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" /> + <glyph glyph-name="caret_left" unicode="&#xf0d9;" horiz-adv-x="640" +d="M640 1088v-896q0 -26 -19 -45t-45 -19t-45 19l-448 448q-19 19 -19 45t19 45l448 448q19 19 45 19t45 -19t19 -45z" /> + <glyph glyph-name="caret_right" unicode="&#xf0da;" horiz-adv-x="640" +d="M576 640q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19t-19 45v896q0 26 19 45t45 19t45 -19l448 -448q19 -19 19 -45z" /> + <glyph glyph-name="columns" unicode="&#xf0db;" horiz-adv-x="1664" +d="M160 0h608v1152h-640v-1120q0 -13 9.5 -22.5t22.5 -9.5zM1536 32v1120h-640v-1152h608q13 0 22.5 9.5t9.5 22.5zM1664 1248v-1216q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1344q66 0 113 -47t47 -113z" /> + <glyph glyph-name="sort" unicode="&#xf0dc;" horiz-adv-x="1024" +d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45zM1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" /> + <glyph glyph-name="sort_down" unicode="&#xf0dd;" horiz-adv-x="1024" +d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" /> + <glyph glyph-name="sort_up" unicode="&#xf0de;" horiz-adv-x="1024" +d="M1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" /> + <glyph glyph-name="envelope_alt" unicode="&#xf0e0;" horiz-adv-x="1792" +d="M1792 826v-794q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v794q44 -49 101 -87q362 -246 497 -345q57 -42 92.5 -65.5t94.5 -48t110 -24.5h1h1q51 0 110 24.5t94.5 48t92.5 65.5q170 123 498 345q57 39 100 87zM1792 1120q0 -79 -49 -151t-122 -123 +q-376 -261 -468 -325q-10 -7 -42.5 -30.5t-54 -38t-52 -32.5t-57.5 -27t-50 -9h-1h-1q-23 0 -50 9t-57.5 27t-52 32.5t-54 38t-42.5 30.5q-91 64 -262 182.5t-205 142.5q-62 42 -117 115.5t-55 136.5q0 78 41.5 130t118.5 52h1472q65 0 112.5 -47t47.5 -113z" /> + <glyph glyph-name="linkedin" unicode="&#xf0e1;" +d="M349 911v-991h-330v991h330zM370 1217q1 -73 -50.5 -122t-135.5 -49h-2q-82 0 -132 49t-50 122q0 74 51.5 122.5t134.5 48.5t133 -48.5t51 -122.5zM1536 488v-568h-329v530q0 105 -40.5 164.5t-126.5 59.5q-63 0 -105.5 -34.5t-63.5 -85.5q-11 -30 -11 -81v-553h-329 +q2 399 2 647t-1 296l-1 48h329v-144h-2q20 32 41 56t56.5 52t87 43.5t114.5 15.5q171 0 275 -113.5t104 -332.5z" /> + <glyph glyph-name="undo" unicode="&#xf0e2;" +d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 +t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298z" /> + <glyph glyph-name="legal" unicode="&#xf0e3;" horiz-adv-x="1792" +d="M1771 0q0 -53 -37 -90l-107 -108q-39 -37 -91 -37q-53 0 -90 37l-363 364q-38 36 -38 90q0 53 43 96l-256 256l-126 -126q-14 -14 -34 -14t-34 14q2 -2 12.5 -12t12.5 -13t10 -11.5t10 -13.5t6 -13.5t5.5 -16.5t1.5 -18q0 -38 -28 -68q-3 -3 -16.5 -18t-19 -20.5 +t-18.5 -16.5t-22 -15.5t-22 -9t-26 -4.5q-40 0 -68 28l-408 408q-28 28 -28 68q0 13 4.5 26t9 22t15.5 22t16.5 18.5t20.5 19t18 16.5q30 28 68 28q10 0 18 -1.5t16.5 -5.5t13.5 -6t13.5 -10t11.5 -10t13 -12.5t12 -12.5q-14 14 -14 34t14 34l348 348q14 14 34 14t34 -14 +q-2 2 -12.5 12t-12.5 13t-10 11.5t-10 13.5t-6 13.5t-5.5 16.5t-1.5 18q0 38 28 68q3 3 16.5 18t19 20.5t18.5 16.5t22 15.5t22 9t26 4.5q40 0 68 -28l408 -408q28 -28 28 -68q0 -13 -4.5 -26t-9 -22t-15.5 -22t-16.5 -18.5t-20.5 -19t-18 -16.5q-30 -28 -68 -28 +q-10 0 -18 1.5t-16.5 5.5t-13.5 6t-13.5 10t-11.5 10t-13 12.5t-12 12.5q14 -14 14 -34t-14 -34l-126 -126l256 -256q43 43 96 43q52 0 91 -37l363 -363q37 -39 37 -91z" /> + <glyph glyph-name="dashboard" unicode="&#xf0e4;" horiz-adv-x="1792" +d="M384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM576 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1004 351l101 382q6 26 -7.5 48.5t-38.5 29.5 +t-48 -6.5t-30 -39.5l-101 -382q-60 -5 -107 -43.5t-63 -98.5q-20 -77 20 -146t117 -89t146 20t89 117q16 60 -6 117t-72 91zM1664 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 1024q0 53 -37.5 90.5 +t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1472 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 384q0 -261 -141 -483q-19 -29 -54 -29h-1402q-35 0 -54 29 +q-141 221 -141 483q0 182 71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" /> + <glyph glyph-name="comment_alt" unicode="&#xf0e5;" horiz-adv-x="1792" +d="M896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640 +q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 174 120 321.5 +t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" /> + <glyph glyph-name="comments_alt" unicode="&#xf0e6;" horiz-adv-x="1792" +d="M704 1152q-153 0 -286 -52t-211.5 -141t-78.5 -191q0 -82 53 -158t149 -132l97 -56l-35 -84q34 20 62 39l44 31l53 -10q78 -14 153 -14q153 0 286 52t211.5 141t78.5 191t-78.5 191t-211.5 141t-286 52zM704 1280q191 0 353.5 -68.5t256.5 -186.5t94 -257t-94 -257 +t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224q0 139 94 257t256.5 186.5 +t353.5 68.5zM1526 111q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129 +q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230q0 -120 -71 -224.5t-195 -176.5z" /> + <glyph glyph-name="bolt" unicode="&#xf0e7;" horiz-adv-x="896" +d="M885 970q18 -20 7 -44l-540 -1157q-13 -25 -42 -25q-4 0 -14 2q-17 5 -25.5 19t-4.5 30l197 808l-406 -101q-4 -1 -12 -1q-18 0 -31 11q-18 15 -13 39l201 825q4 14 16 23t28 9h328q19 0 32 -12.5t13 -29.5q0 -8 -5 -18l-171 -463l396 98q8 2 12 2q19 0 34 -15z" /> + <glyph glyph-name="sitemap" unicode="&#xf0e8;" horiz-adv-x="1792" +d="M1792 288v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320 +q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192q0 52 38 90t90 38h512v192h-96q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h320q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-96v-192h512q52 0 90 -38t38 -90v-192h96q40 0 68 -28t28 -68 +z" /> + <glyph glyph-name="umbrella" unicode="&#xf0e9;" horiz-adv-x="1664" +d="M896 708v-580q0 -104 -76 -180t-180 -76t-180 76t-76 180q0 26 19 45t45 19t45 -19t19 -45q0 -50 39 -89t89 -39t89 39t39 89v580q33 11 64 11t64 -11zM1664 681q0 -13 -9.5 -22.5t-22.5 -9.5q-11 0 -23 10q-49 46 -93 69t-102 23q-68 0 -128 -37t-103 -97 +q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -28 -17q-18 0 -29 17q-4 6 -14.5 24t-17.5 28q-43 60 -102.5 97t-127.5 37t-127.5 -37t-102.5 -97q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -29 -17q-17 0 -28 17q-4 6 -14.5 24t-17.5 28q-43 60 -103 97t-128 37q-58 0 -102 -23t-93 -69 +q-12 -10 -23 -10q-13 0 -22.5 9.5t-9.5 22.5q0 5 1 7q45 183 172.5 319.5t298 204.5t360.5 68q140 0 274.5 -40t246.5 -113.5t194.5 -187t115.5 -251.5q1 -2 1 -7zM896 1408v-98q-42 2 -64 2t-64 -2v98q0 26 19 45t45 19t45 -19t19 -45z" /> + <glyph glyph-name="paste" unicode="&#xf0ea;" horiz-adv-x="1792" +d="M768 -128h896v640h-416q-40 0 -68 28t-28 68v416h-384v-1152zM1024 1312v64q0 13 -9.5 22.5t-22.5 9.5h-704q-13 0 -22.5 -9.5t-9.5 -22.5v-64q0 -13 9.5 -22.5t22.5 -9.5h704q13 0 22.5 9.5t9.5 22.5zM1280 640h299l-299 299v-299zM1792 512v-672q0 -40 -28 -68t-68 -28 +h-960q-40 0 -68 28t-28 68v160h-544q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1088q40 0 68 -28t28 -68v-328q21 -13 36 -28l408 -408q28 -28 48 -76t20 -88z" /> + <glyph glyph-name="light_bulb" unicode="&#xf0eb;" horiz-adv-x="1024" +d="M736 960q0 -13 -9.5 -22.5t-22.5 -9.5t-22.5 9.5t-9.5 22.5q0 46 -54 71t-106 25q-13 0 -22.5 9.5t-9.5 22.5t9.5 22.5t22.5 9.5q50 0 99.5 -16t87 -54t37.5 -90zM896 960q0 72 -34.5 134t-90 101.5t-123 62t-136.5 22.5t-136.5 -22.5t-123 -62t-90 -101.5t-34.5 -134 +q0 -101 68 -180q10 -11 30.5 -33t30.5 -33q128 -153 141 -298h228q13 145 141 298q10 11 30.5 33t30.5 33q68 79 68 180zM1024 960q0 -155 -103 -268q-45 -49 -74.5 -87t-59.5 -95.5t-34 -107.5q47 -28 47 -82q0 -37 -25 -64q25 -27 25 -64q0 -52 -45 -81q13 -23 13 -47 +q0 -46 -31.5 -71t-77.5 -25q-20 -44 -60 -70t-87 -26t-87 26t-60 70q-46 0 -77.5 25t-31.5 71q0 24 13 47q-45 29 -45 81q0 37 25 64q-25 27 -25 64q0 54 47 82q-4 50 -34 107.5t-59.5 95.5t-74.5 87q-103 113 -103 268q0 99 44.5 184.5t117 142t164 89t186.5 32.5 +t186.5 -32.5t164 -89t117 -142t44.5 -184.5z" /> + <glyph glyph-name="exchange" unicode="&#xf0ec;" horiz-adv-x="1792" +d="M1792 352v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5q-12 0 -24 10l-319 320q-9 9 -9 22q0 14 9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h1376q13 0 22.5 -9.5t9.5 -22.5zM1792 896q0 -14 -9 -23l-320 -320q-9 -9 -23 -9 +q-13 0 -22.5 9.5t-9.5 22.5v192h-1376q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1376v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" /> + <glyph glyph-name="cloud_download" unicode="&#xf0ed;" horiz-adv-x="1920" +d="M1280 608q0 14 -9 23t-23 9h-224v352q0 13 -9.5 22.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-352h-224q-13 0 -22.5 -9.5t-9.5 -22.5q0 -14 9 -23l352 -352q9 -9 23 -9t23 9l351 351q10 12 10 24zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 +q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" /> + <glyph glyph-name="cloud_upload" unicode="&#xf0ee;" horiz-adv-x="1920" +d="M1280 672q0 14 -9 23l-352 352q-9 9 -23 9t-23 -9l-351 -351q-10 -12 -10 -24q0 -14 9 -23t23 -9h224v-352q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5v352h224q13 0 22.5 9.5t9.5 22.5zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 +q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" /> + <glyph glyph-name="user_md" unicode="&#xf0f0;" horiz-adv-x="1408" +d="M384 192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 68 5.5 131t24 138t47.5 132.5t81 103t120 60.5q-22 -52 -22 -120v-203q-58 -20 -93 -70t-35 -111q0 -80 56 -136t136 -56 +t136 56t56 136q0 61 -35.5 111t-92.5 70v203q0 62 25 93q132 -104 295 -104t295 104q25 -31 25 -93v-64q-106 0 -181 -75t-75 -181v-89q-32 -29 -32 -71q0 -40 28 -68t68 -28t68 28t28 68q0 42 -32 71v89q0 52 38 90t90 38t90 -38t38 -90v-89q-32 -29 -32 -71q0 -40 28 -68 +t68 -28t68 28t28 68q0 42 -32 71v89q0 68 -34.5 127.5t-93.5 93.5q0 10 0.5 42.5t0 48t-2.5 41.5t-7 47t-13 40q68 -15 120 -60.5t81 -103t47.5 -132.5t24 -138t5.5 -131zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 +t271.5 -112.5t112.5 -271.5z" /> + <glyph glyph-name="stethoscope" unicode="&#xf0f1;" horiz-adv-x="1408" +d="M1280 832q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 832q0 -62 -35.5 -111t-92.5 -70v-395q0 -159 -131.5 -271.5t-316.5 -112.5t-316.5 112.5t-131.5 271.5v132q-164 20 -274 128t-110 252v512q0 26 19 45t45 19q6 0 16 -2q17 30 47 48 +t65 18q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5q-33 0 -64 18v-402q0 -106 94 -181t226 -75t226 75t94 181v402q-31 -18 -64 -18q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5q35 0 65 -18t47 -48q10 2 16 2q26 0 45 -19t19 -45v-512q0 -144 -110 -252 +t-274 -128v-132q0 -106 94 -181t226 -75t226 75t94 181v395q-57 21 -92.5 70t-35.5 111q0 80 56 136t136 56t136 -56t56 -136z" /> + <glyph glyph-name="suitcase" unicode="&#xf0f2;" horiz-adv-x="1792" +d="M640 1152h512v128h-512v-128zM288 1152v-1280h-64q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h64zM1408 1152v-1280h-1024v1280h128v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h128zM1792 928v-832q0 -92 -66 -158t-158 -66h-64v1280h64q92 0 158 -66 +t66 -158z" /> + <glyph glyph-name="bell_alt" unicode="&#xf0f3;" horiz-adv-x="1792" +d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5 +t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" /> + <glyph glyph-name="coffee" unicode="&#xf0f4;" horiz-adv-x="1920" +d="M1664 896q0 80 -56 136t-136 56h-64v-384h64q80 0 136 56t56 136zM0 128h1792q0 -106 -75 -181t-181 -75h-1280q-106 0 -181 75t-75 181zM1856 896q0 -159 -112.5 -271.5t-271.5 -112.5h-64v-32q0 -92 -66 -158t-158 -66h-704q-92 0 -158 66t-66 158v736q0 26 19 45 +t45 19h1152q159 0 271.5 -112.5t112.5 -271.5z" /> + <glyph glyph-name="food" unicode="&#xf0f5;" horiz-adv-x="1408" +d="M640 1472v-640q0 -61 -35.5 -111t-92.5 -70v-779q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v779q-57 20 -92.5 70t-35.5 111v640q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45 +t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45zM1408 1472v-1600q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v512h-224q-13 0 -22.5 9.5t-9.5 22.5v800q0 132 94 226t226 94h256q26 0 45 -19t19 -45z" /> + <glyph glyph-name="file_text_alt" unicode="&#xf0f6;" +d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z +M384 736q0 14 9 23t23 9h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64zM1120 512q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704zM1120 256q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704 +q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704z" /> + <glyph glyph-name="building" unicode="&#xf0f7;" horiz-adv-x="1408" +d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M640 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M640 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M896 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M896 -128h384v1536h-1152v-1536h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM1408 1472v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280q26 0 45 -19t19 -45z" /> + <glyph glyph-name="hospital" unicode="&#xf0f8;" horiz-adv-x="1408" +d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z +M896 -128h384v1152h-256v-32q0 -40 -28 -68t-68 -28h-448q-40 0 -68 28t-28 68v32h-256v-1152h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM896 1056v320q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-96h-128v96q0 13 -9.5 22.5 +t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5v96h128v-96q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1408 1088v-1280q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1280q0 26 19 45t45 19h320 +v288q0 40 28 68t68 28h448q40 0 68 -28t28 -68v-288h320q26 0 45 -19t19 -45z" /> + <glyph glyph-name="ambulance" unicode="&#xf0f9;" horiz-adv-x="1920" +d="M640 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM256 640h384v256h-158q-14 -2 -22 -9l-195 -195q-7 -12 -9 -22v-30zM1536 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5 +t90.5 37.5t37.5 90.5zM1664 800v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM1920 1344v-1152 +q0 -26 -19 -45t-45 -19h-192q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-128q-26 0 -45 19t-19 45t19 45t45 19v416q0 26 13 58t32 51l198 198q19 19 51 32t58 13h160v320q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" /> + <glyph glyph-name="medkit" unicode="&#xf0fa;" horiz-adv-x="1792" +d="M1280 416v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM640 1152h512v128h-512v-128zM256 1152v-1280h-32 +q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h32zM1440 1152v-1280h-1088v1280h160v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h160zM1792 928v-832q0 -92 -66 -158t-158 -66h-32v1280h32q92 0 158 -66t66 -158z" /> + <glyph glyph-name="fighter_jet" unicode="&#xf0fb;" horiz-adv-x="1920" +d="M1920 576q-1 -32 -288 -96l-352 -32l-224 -64h-64l-293 -352h69q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-96h-160h-64v32h64v416h-160l-192 -224h-96l-32 32v192h32v32h128v8l-192 24v128l192 24v8h-128v32h-32v192l32 32h96l192 -224h160v416h-64v32h64h160h96 +q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-69l293 -352h64l224 -64l352 -32q128 -28 200 -52t80 -34z" /> + <glyph glyph-name="beer" unicode="&#xf0fc;" horiz-adv-x="1664" +d="M640 640v384h-256v-256q0 -53 37.5 -90.5t90.5 -37.5h128zM1664 192v-192h-1152v192l128 192h-128q-159 0 -271.5 112.5t-112.5 271.5v320l-64 64l32 128h480l32 128h960l32 -192l-64 -32v-800z" /> + <glyph glyph-name="h_sign" unicode="&#xf0fd;" +d="M1280 192v896q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-512v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-896q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h512v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1536 1120v-960 +q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="f0fe" unicode="&#xf0fe;" +d="M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 +q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="double_angle_left" unicode="&#xf100;" horiz-adv-x="1024" +d="M627 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23zM1011 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 +t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23z" /> + <glyph glyph-name="double_angle_right" unicode="&#xf101;" horiz-adv-x="1024" +d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM979 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23 +l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" /> + <glyph glyph-name="double_angle_up" unicode="&#xf102;" horiz-adv-x="1152" +d="M1075 224q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM1075 608q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393 +q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" /> + <glyph glyph-name="double_angle_down" unicode="&#xf103;" horiz-adv-x="1152" +d="M1075 672q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23zM1075 1056q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 +t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" /> + <glyph glyph-name="angle_left" unicode="&#xf104;" horiz-adv-x="640" +d="M627 992q0 -13 -10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" /> + <glyph glyph-name="angle_right" unicode="&#xf105;" horiz-adv-x="640" +d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" /> + <glyph glyph-name="angle_up" unicode="&#xf106;" horiz-adv-x="1152" +d="M1075 352q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" /> + <glyph glyph-name="angle_down" unicode="&#xf107;" horiz-adv-x="1152" +d="M1075 800q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" /> + <glyph glyph-name="desktop" unicode="&#xf108;" horiz-adv-x="1920" +d="M1792 544v832q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1376v-1088q0 -66 -47 -113t-113 -47h-544q0 -37 16 -77.5t32 -71t16 -43.5q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19 +t-19 45q0 14 16 44t32 70t16 78h-544q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" /> + <glyph glyph-name="laptop" unicode="&#xf109;" horiz-adv-x="1920" +d="M416 256q-66 0 -113 47t-47 113v704q0 66 47 113t113 47h1088q66 0 113 -47t47 -113v-704q0 -66 -47 -113t-113 -47h-1088zM384 1120v-704q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5v704q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5z +M1760 192h160v-96q0 -40 -47 -68t-113 -28h-1600q-66 0 -113 28t-47 68v96h160h1600zM1040 96q16 0 16 16t-16 16h-160q-16 0 -16 -16t16 -16h160z" /> + <glyph glyph-name="tablet" unicode="&#xf10a;" horiz-adv-x="1152" +d="M640 128q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1024 288v960q0 13 -9.5 22.5t-22.5 9.5h-832q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h832q13 0 22.5 9.5t9.5 22.5zM1152 1248v-1088q0 -66 -47 -113t-113 -47h-832 +q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h832q66 0 113 -47t47 -113z" /> + <glyph glyph-name="mobile_phone" unicode="&#xf10b;" horiz-adv-x="768" +d="M464 128q0 33 -23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5t56.5 23.5t23.5 56.5zM672 288v704q0 13 -9.5 22.5t-22.5 9.5h-512q-13 0 -22.5 -9.5t-9.5 -22.5v-704q0 -13 9.5 -22.5t22.5 -9.5h512q13 0 22.5 9.5t9.5 22.5zM480 1136 +q0 16 -16 16h-160q-16 0 -16 -16t16 -16h160q16 0 16 16zM768 1152v-1024q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v1024q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" /> + <glyph glyph-name="circle_blank" unicode="&#xf10c;" +d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103 +t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="quote_left" unicode="&#xf10d;" horiz-adv-x="1664" +d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z +M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" /> + <glyph glyph-name="quote_right" unicode="&#xf10e;" horiz-adv-x="1664" +d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216 +v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" /> + <glyph glyph-name="spinner" unicode="&#xf110;" horiz-adv-x="1792" +d="M526 142q0 -53 -37.5 -90.5t-90.5 -37.5q-52 0 -90 38t-38 90q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 -64q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -53 -37.5 -90.5t-90.5 -37.5 +t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1522 142q0 -52 -38 -90t-90 -38q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM558 1138q0 -66 -47 -113t-113 -47t-113 47t-47 113t47 113t113 47t113 -47t47 -113z +M1728 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1088 1344q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1618 1138q0 -93 -66 -158.5t-158 -65.5q-93 0 -158.5 65.5t-65.5 158.5 +q0 92 65.5 158t158.5 66q92 0 158 -66t66 -158z" /> + <glyph glyph-name="circle" unicode="&#xf111;" +d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="reply" unicode="&#xf112;" horiz-adv-x="1792" +d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19 +l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" /> + <glyph glyph-name="github_alt" unicode="&#xf113;" horiz-adv-x="1664" +d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320 +q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86 +t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218 +q0 -87 -27 -168q136 -160 136 -398z" /> + <glyph glyph-name="folder_close_alt" unicode="&#xf114;" horiz-adv-x="1664" +d="M1536 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68v-960q0 -40 28 -68t68 -28h1216q40 0 68 28t28 68zM1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320 +q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" /> + <glyph glyph-name="folder_open_alt" unicode="&#xf115;" horiz-adv-x="1920" +d="M1781 605q0 35 -53 35h-1088q-40 0 -85.5 -21.5t-71.5 -52.5l-294 -363q-18 -24 -18 -40q0 -35 53 -35h1088q40 0 86 22t71 53l294 363q18 22 18 39zM640 768h768v160q0 40 -28 68t-68 28h-576q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68 +v-853l256 315q44 53 116 87.5t140 34.5zM1909 605q0 -62 -46 -120l-295 -363q-43 -53 -116 -87.5t-140 -34.5h-1088q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158v-160h192q54 0 99 -24.5t67 -70.5q15 -32 15 -68z +" /> + <glyph glyph-name="expand_alt" unicode="&#xf116;" horiz-adv-x="1792" + /> + <glyph glyph-name="collapse_alt" unicode="&#xf117;" horiz-adv-x="1792" + /> + <glyph glyph-name="smile" unicode="&#xf118;" +d="M1134 461q-37 -121 -138 -195t-228 -74t-228 74t-138 195q-8 25 4 48.5t38 31.5q25 8 48.5 -4t31.5 -38q25 -80 92.5 -129.5t151.5 -49.5t151.5 49.5t92.5 129.5q8 26 32 38t49 4t37 -31.5t4 -48.5zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 +t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5 +t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="frown" unicode="&#xf119;" +d="M1134 307q8 -25 -4 -48.5t-37 -31.5t-49 4t-32 38q-25 80 -92.5 129.5t-151.5 49.5t-151.5 -49.5t-92.5 -129.5q-8 -26 -31.5 -38t-48.5 -4q-26 8 -38 31.5t-4 48.5q37 121 138 195t228 74t228 -74t138 -195zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 +t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204 +t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="meh" unicode="&#xf11a;" +d="M1152 448q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h640q26 0 45 -19t19 -45zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 +t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 +q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="gamepad" unicode="&#xf11b;" horiz-adv-x="1920" +d="M832 448v128q0 14 -9 23t-23 9h-192v192q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-192h-192q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h192v-192q0 -14 9 -23t23 -9h128q14 0 23 9t9 23v192h192q14 0 23 9t9 23zM1408 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 +t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1920 512q0 -212 -150 -362t-362 -150q-192 0 -338 128h-220q-146 -128 -338 -128q-212 0 -362 150 +t-150 362t150 362t362 150h896q212 0 362 -150t150 -362z" /> + <glyph glyph-name="keyboard" unicode="&#xf11c;" horiz-adv-x="1920" +d="M384 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM512 624v-96q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h224q16 0 16 -16zM384 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 368v-96q0 -16 -16 -16 +h-864q-16 0 -16 16v96q0 16 16 16h864q16 0 16 -16zM768 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM640 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1024 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16 +h96q16 0 16 -16zM896 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1280 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1152 880v-96 +q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 880v-352q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h112v240q0 16 16 16h96q16 0 16 -16zM1792 128v896h-1664v-896 +h1664zM1920 1024v-896q0 -53 -37.5 -90.5t-90.5 -37.5h-1664q-53 0 -90.5 37.5t-37.5 90.5v896q0 53 37.5 90.5t90.5 37.5h1664q53 0 90.5 -37.5t37.5 -90.5z" /> + <glyph glyph-name="flag_alt" unicode="&#xf11d;" horiz-adv-x="1792" +d="M1664 491v616q-169 -91 -306 -91q-82 0 -145 32q-100 49 -184 76.5t-178 27.5q-173 0 -403 -127v-599q245 113 433 113q55 0 103.5 -7.5t98 -26t77 -31t82.5 -39.5l28 -14q44 -22 101 -22q120 0 293 92zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9 +h-64q-14 0 -23 9t-9 23v1266q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102 +q-15 -9 -33 -9q-16 0 -32 8q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" /> + <glyph glyph-name="flag_checkered" unicode="&#xf11e;" horiz-adv-x="1792" +d="M832 536v192q-181 -16 -384 -117v-185q205 96 384 110zM832 954v197q-172 -8 -384 -126v-189q215 111 384 118zM1664 491v184q-235 -116 -384 -71v224q-20 6 -39 15q-5 3 -33 17t-34.5 17t-31.5 15t-34.5 15.5t-32.5 13t-36 12.5t-35 8.5t-39.5 7.5t-39.5 4t-44 2 +q-23 0 -49 -3v-222h19q102 0 192.5 -29t197.5 -82q19 -9 39 -15v-188q42 -17 91 -17q120 0 293 92zM1664 918v189q-169 -91 -306 -91q-45 0 -78 8v-196q148 -42 384 90zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v1266 +q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102q-15 -9 -33 -9q-16 0 -32 8 +q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" /> + <glyph glyph-name="terminal" unicode="&#xf120;" horiz-adv-x="1664" +d="M585 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23zM1664 96v-64q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h960q14 0 23 -9 +t9 -23z" /> + <glyph glyph-name="code" unicode="&#xf121;" horiz-adv-x="1920" +d="M617 137l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23zM1208 1204l-373 -1291q-4 -13 -15.5 -19.5t-23.5 -2.5l-62 17q-13 4 -19.5 15.5t-2.5 24.5 +l373 1291q4 13 15.5 19.5t23.5 2.5l62 -17q13 -4 19.5 -15.5t2.5 -24.5zM1865 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23z" /> + <glyph glyph-name="reply_all" unicode="&#xf122;" horiz-adv-x="1792" +d="M640 454v-70q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-69l-397 -398q-19 -19 -19 -45t19 -45zM1792 416q0 -58 -17 -133.5t-38.5 -138t-48 -125t-40.5 -90.5l-20 -40q-8 -17 -28 -17q-6 0 -9 1 +q-25 8 -23 34q43 400 -106 565q-64 71 -170.5 110.5t-267.5 52.5v-251q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-262q411 -28 599 -221q169 -173 169 -509z" /> + <glyph glyph-name="star_half_empty" unicode="&#xf123;" horiz-adv-x="1664" +d="M1186 579l257 250l-356 52l-66 10l-30 60l-159 322v-963l59 -31l318 -168l-60 355l-12 66zM1638 841l-363 -354l86 -500q5 -33 -6 -51.5t-34 -18.5q-17 0 -40 12l-449 236l-449 -236q-23 -12 -40 -12q-23 0 -34 18.5t-6 51.5l86 500l-364 354q-32 32 -23 59.5t54 34.5 +l502 73l225 455q20 41 49 41q28 0 49 -41l225 -455l502 -73q45 -7 54 -34.5t-24 -59.5z" /> + <glyph glyph-name="location_arrow" unicode="&#xf124;" horiz-adv-x="1408" +d="M1401 1187l-640 -1280q-17 -35 -57 -35q-5 0 -15 2q-22 5 -35.5 22.5t-13.5 39.5v576h-576q-22 0 -39.5 13.5t-22.5 35.5t4 42t29 30l1280 640q13 7 29 7q27 0 45 -19q15 -14 18.5 -34.5t-6.5 -39.5z" /> + <glyph glyph-name="crop" unicode="&#xf125;" horiz-adv-x="1664" +d="M557 256h595v595zM512 301l595 595h-595v-595zM1664 224v-192q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v224h-864q-14 0 -23 9t-9 23v864h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224v224q0 14 9 23t23 9h192q14 0 23 -9t9 -23 +v-224h851l246 247q10 9 23 9t23 -9q9 -10 9 -23t-9 -23l-247 -246v-851h224q14 0 23 -9t9 -23z" /> + <glyph glyph-name="code_fork" unicode="&#xf126;" horiz-adv-x="1024" +d="M288 64q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM288 1216q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM928 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1024 1088q0 -52 -26 -96.5t-70 -69.5 +q-2 -287 -226 -414q-67 -38 -203 -81q-128 -40 -169.5 -71t-41.5 -100v-26q44 -25 70 -69.5t26 -96.5q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 52 26 96.5t70 69.5v820q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136q0 -52 -26 -96.5t-70 -69.5v-497 +q54 26 154 57q55 17 87.5 29.5t70.5 31t59 39.5t40.5 51t28 69.5t8.5 91.5q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136z" /> + <glyph glyph-name="unlink" unicode="&#xf127;" horiz-adv-x="1664" +d="M439 265l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23zM608 224v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM384 448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23t9 23t23 9h320 +q14 0 23 -9t9 -23zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-334 335q-21 21 -42 56l239 18l273 -274q27 -27 68 -27.5t68 26.5l147 146q28 28 28 67q0 40 -28 68l-274 275l18 239q35 -21 56 -42l336 -336q84 -86 84 -204zM1031 1044l-239 -18 +l-273 274q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l274 -274l-18 -240q-35 21 -56 42l-336 336q-84 86 -84 204q0 120 85 203l147 146q83 83 203 83q121 0 204 -85l334 -335q21 -21 42 -56zM1664 960q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9 +t-9 23t9 23t23 9h320q14 0 23 -9t9 -23zM1120 1504v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM1527 1353l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" /> + <glyph glyph-name="question" unicode="&#xf128;" horiz-adv-x="1024" +d="M704 280v-240q0 -16 -12 -28t-28 -12h-240q-16 0 -28 12t-12 28v240q0 16 12 28t28 12h240q16 0 28 -12t12 -28zM1020 880q0 -54 -15.5 -101t-35 -76.5t-55 -59.5t-57.5 -43.5t-61 -35.5q-41 -23 -68.5 -65t-27.5 -67q0 -17 -12 -32.5t-28 -15.5h-240q-15 0 -25.5 18.5 +t-10.5 37.5v45q0 83 65 156.5t143 108.5q59 27 84 56t25 76q0 42 -46.5 74t-107.5 32q-65 0 -108 -29q-35 -25 -107 -115q-13 -16 -31 -16q-12 0 -25 8l-164 125q-13 10 -15.5 25t5.5 28q160 266 464 266q80 0 161 -31t146 -83t106 -127.5t41 -158.5z" /> + <glyph glyph-name="_279" unicode="&#xf129;" horiz-adv-x="640" +d="M640 192v-128q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64v384h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-576h64q26 0 45 -19t19 -45zM512 1344v-192q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v192 +q0 26 19 45t45 19h256q26 0 45 -19t19 -45z" /> + <glyph glyph-name="exclamation" unicode="&#xf12a;" horiz-adv-x="640" +d="M512 288v-224q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v224q0 26 19 45t45 19h256q26 0 45 -19t19 -45zM542 1344l-28 -768q-1 -26 -20.5 -45t-45.5 -19h-256q-26 0 -45.5 19t-20.5 45l-28 768q-1 26 17.5 45t44.5 19h320q26 0 44.5 -19t17.5 -45z" /> + <glyph glyph-name="superscript" unicode="&#xf12b;" +d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3q-1 -3 -2.5 -6.5t-3.5 -8t-3 -6.5q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109z +M1534 846v-206h-514l-3 27q-4 28 -4 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q83 65 188 65q110 0 178 -59.5t68 -158.5q0 -56 -24.5 -103t-62 -76.5t-81.5 -58.5t-82 -50.5 +t-65.5 -51.5t-30.5 -63h232v80h126z" /> + <glyph glyph-name="subscript" unicode="&#xf12c;" +d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3q-1 -3 -2.5 -6.5t-3.5 -8t-3 -6.5q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109z +M1536 -50v-206h-514l-4 27q-3 45 -3 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q80 65 188 65q110 0 178 -59.5t68 -158.5q0 -66 -34.5 -118.5t-84 -86t-99.5 -62.5t-87 -63t-41 -73 +h232v80h126z" /> + <glyph glyph-name="_283" unicode="&#xf12d;" horiz-adv-x="1920" +d="M896 128l336 384h-768l-336 -384h768zM1909 1205q15 -34 9.5 -71.5t-30.5 -65.5l-896 -1024q-38 -44 -96 -44h-768q-38 0 -69.5 20.5t-47.5 54.5q-15 34 -9.5 71.5t30.5 65.5l896 1024q38 44 96 44h768q38 0 69.5 -20.5t47.5 -54.5z" /> + <glyph glyph-name="puzzle_piece" unicode="&#xf12e;" horiz-adv-x="1664" +d="M1664 438q0 -81 -44.5 -135t-123.5 -54q-41 0 -77.5 17.5t-59 38t-56.5 38t-71 17.5q-110 0 -110 -124q0 -39 16 -115t15 -115v-5q-22 0 -33 -1q-34 -3 -97.5 -11.5t-115.5 -13.5t-98 -5q-61 0 -103 26.5t-42 83.5q0 37 17.5 71t38 56.5t38 59t17.5 77.5q0 79 -54 123.5 +t-135 44.5q-84 0 -143 -45.5t-59 -127.5q0 -43 15 -83t33.5 -64.5t33.5 -53t15 -50.5q0 -45 -46 -89q-37 -35 -117 -35q-95 0 -245 24q-9 2 -27.5 4t-27.5 4l-13 2q-1 0 -3 1q-2 0 -2 1v1024q2 -1 17.5 -3.5t34 -5t21.5 -3.5q150 -24 245 -24q80 0 117 35q46 44 46 89 +q0 22 -15 50.5t-33.5 53t-33.5 64.5t-15 83q0 82 59 127.5t144 45.5q80 0 134 -44.5t54 -123.5q0 -41 -17.5 -77.5t-38 -59t-38 -56.5t-17.5 -71q0 -57 42 -83.5t103 -26.5q64 0 180 15t163 17v-2q-1 -2 -3.5 -17.5t-5 -34t-3.5 -21.5q-24 -150 -24 -245q0 -80 35 -117 +q44 -46 89 -46q22 0 50.5 15t53 33.5t64.5 33.5t83 15q82 0 127.5 -59t45.5 -143z" /> + <glyph glyph-name="microphone" unicode="&#xf130;" horiz-adv-x="1152" +d="M1152 832v-128q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-217 24 -364.5 187.5t-147.5 384.5v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -185 131.5 -316.5t316.5 -131.5 +t316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45zM896 1216v-512q0 -132 -94 -226t-226 -94t-226 94t-94 226v512q0 132 94 226t226 94t226 -94t94 -226z" /> + <glyph glyph-name="microphone_off" unicode="&#xf131;" horiz-adv-x="1408" +d="M271 591l-101 -101q-42 103 -42 214v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -53 15 -113zM1385 1193l-361 -361v-128q0 -132 -94 -226t-226 -94q-55 0 -109 19l-96 -96q97 -51 205 -51q185 0 316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45v-128 +q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-125 13 -235 81l-254 -254q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l1234 1234q10 10 23 10t23 -10l82 -82q10 -10 10 -23 +t-10 -23zM1005 1325l-621 -621v512q0 132 94 226t226 94q102 0 184.5 -59t116.5 -152z" /> + <glyph glyph-name="shield" unicode="&#xf132;" horiz-adv-x="1280" +d="M1088 576v640h-448v-1137q119 63 213 137q235 184 235 360zM1280 1344v-768q0 -86 -33.5 -170.5t-83 -150t-118 -127.5t-126.5 -103t-121 -77.5t-89.5 -49.5t-42.5 -20q-12 -6 -26 -6t-26 6q-16 7 -42.5 20t-89.5 49.5t-121 77.5t-126.5 103t-118 127.5t-83 150 +t-33.5 170.5v768q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" /> + <glyph glyph-name="calendar_empty" unicode="&#xf133;" horiz-adv-x="1664" +d="M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 +q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" /> + <glyph glyph-name="fire_extinguisher" unicode="&#xf134;" horiz-adv-x="1408" +d="M512 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 1376v-320q0 -16 -12 -25q-8 -7 -20 -7q-4 0 -7 1l-448 96q-11 2 -18 11t-7 20h-256v-102q111 -23 183.5 -111t72.5 -203v-800q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v800 +q0 106 62.5 190.5t161.5 114.5v111h-32q-59 0 -115 -23.5t-91.5 -53t-66 -66.5t-40.5 -53.5t-14 -24.5q-17 -35 -57 -35q-16 0 -29 7q-23 12 -31.5 37t3.5 49q5 10 14.5 26t37.5 53.5t60.5 70t85 67t108.5 52.5q-25 42 -25 86q0 66 47 113t113 47t113 -47t47 -113 +q0 -33 -14 -64h302q0 11 7 20t18 11l448 96q3 1 7 1q12 0 20 -7q12 -9 12 -25z" /> + <glyph glyph-name="rocket" unicode="&#xf135;" horiz-adv-x="1664" +d="M1440 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1664 1376q0 -249 -75.5 -430.5t-253.5 -360.5q-81 -80 -195 -176l-20 -379q-2 -16 -16 -26l-384 -224q-7 -4 -16 -4q-12 0 -23 9l-64 64q-13 14 -8 32l85 276l-281 281l-276 -85q-3 -1 -9 -1 +q-14 0 -23 9l-64 64q-17 19 -5 39l224 384q10 14 26 16l379 20q96 114 176 195q188 187 358 258t431 71q14 0 24 -9.5t10 -22.5z" /> + <glyph glyph-name="maxcdn" unicode="&#xf136;" horiz-adv-x="1792" +d="M1745 763l-164 -763h-334l178 832q13 56 -15 88q-27 33 -83 33h-169l-204 -953h-334l204 953h-286l-204 -953h-334l204 953l-153 327h1276q101 0 189.5 -40.5t147.5 -113.5q60 -73 81 -168.5t0 -194.5z" /> + <glyph glyph-name="chevron_sign_left" unicode="&#xf137;" +d="M909 141l102 102q19 19 19 45t-19 45l-307 307l307 307q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 +t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="chevron_sign_right" unicode="&#xf138;" +d="M717 141l454 454q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l307 -307l-307 -307q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 +t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="chevron_sign_up" unicode="&#xf139;" +d="M1165 397l102 102q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l307 307l307 -307q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 +t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="chevron_sign_down" unicode="&#xf13a;" +d="M813 237l454 454q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-307 -307l-307 307q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 +t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="html5" unicode="&#xf13b;" horiz-adv-x="1408" +d="M1130 939l16 175h-884l47 -534h612l-22 -228l-197 -53l-196 53l-13 140h-175l22 -278l362 -100h4v1l359 99l50 544h-644l-15 181h674zM0 1408h1408l-128 -1438l-578 -162l-574 162z" /> + <glyph glyph-name="css3" unicode="&#xf13c;" horiz-adv-x="1792" +d="M275 1408h1505l-266 -1333l-804 -267l-698 267l71 356h297l-29 -147l422 -161l486 161l68 339h-1208l58 297h1209l38 191h-1208z" /> + <glyph glyph-name="anchor" unicode="&#xf13d;" horiz-adv-x="1792" +d="M960 1280q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1792 352v-352q0 -22 -20 -30q-8 -2 -12 -2q-12 0 -23 9l-93 93q-119 -143 -318.5 -226.5t-429.5 -83.5t-429.5 83.5t-318.5 226.5l-93 -93q-9 -9 -23 -9q-4 0 -12 2q-20 8 -20 30v352 +q0 14 9 23t23 9h352q22 0 30 -20q8 -19 -7 -35l-100 -100q67 -91 189.5 -153.5t271.5 -82.5v647h-192q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h192v163q-58 34 -93 92.5t-35 128.5q0 106 75 181t181 75t181 -75t75 -181q0 -70 -35 -128.5t-93 -92.5v-163h192q26 0 45 -19 +t19 -45v-128q0 -26 -19 -45t-45 -19h-192v-647q149 20 271.5 82.5t189.5 153.5l-100 100q-15 16 -7 35q8 20 30 20h352q14 0 23 -9t9 -23z" /> + <glyph glyph-name="unlock_alt" unicode="&#xf13e;" horiz-adv-x="1152" +d="M1056 768q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v320q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45q0 106 -75 181t-181 75t-181 -75t-75 -181 +v-320h736z" /> + <glyph glyph-name="bullseye" unicode="&#xf140;" +d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM1152 640q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1280 640q0 -212 -150 -362t-362 -150t-362 150 +t-150 362t150 362t362 150t362 -150t150 -362zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 +q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="ellipsis_horizontal" unicode="&#xf141;" horiz-adv-x="1408" +d="M384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM896 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM1408 800v-192q0 -40 -28 -68t-68 -28h-192 +q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" /> + <glyph glyph-name="ellipsis_vertical" unicode="&#xf142;" horiz-adv-x="384" +d="M384 288v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 1312v-192q0 -40 -28 -68t-68 -28h-192 +q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" /> + <glyph glyph-name="_303" unicode="&#xf143;" +d="M512 256q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM863 162q-13 233 -176.5 396.5t-396.5 176.5q-14 1 -24 -9t-10 -23v-128q0 -13 8.5 -22t21.5 -10q154 -11 264 -121t121 -264q1 -13 10 -21.5t22 -8.5h128 +q13 0 23 10t9 24zM1247 161q-5 154 -56 297.5t-139.5 260t-205 205t-260 139.5t-297.5 56q-14 1 -23 -9q-10 -10 -10 -23v-128q0 -13 9 -22t22 -10q204 -7 378 -111.5t278.5 -278.5t111.5 -378q1 -13 10 -22t22 -9h128q13 0 23 10q11 9 9 23zM1536 1120v-960 +q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="play_sign" unicode="&#xf144;" +d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1152 585q32 18 32 55t-32 55l-544 320q-31 19 -64 1q-32 -19 -32 -56v-640q0 -37 32 -56 +q16 -8 32 -8q17 0 32 9z" /> + <glyph glyph-name="ticket" unicode="&#xf145;" horiz-adv-x="1792" +d="M1024 1084l316 -316l-572 -572l-316 316zM813 105l618 618q19 19 19 45t-19 45l-362 362q-18 18 -45 18t-45 -18l-618 -618q-19 -19 -19 -45t19 -45l362 -362q18 -18 45 -18t45 18zM1702 742l-907 -908q-37 -37 -90.5 -37t-90.5 37l-126 126q56 56 56 136t-56 136 +t-136 56t-136 -56l-125 126q-37 37 -37 90.5t37 90.5l907 906q37 37 90.5 37t90.5 -37l125 -125q-56 -56 -56 -136t56 -136t136 -56t136 56l126 -125q37 -37 37 -90.5t-37 -90.5z" /> + <glyph glyph-name="minus_sign_alt" unicode="&#xf146;" +d="M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 +t84.5 -203.5z" /> + <glyph glyph-name="check_minus" unicode="&#xf147;" horiz-adv-x="1408" +d="M1152 736v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h832q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5 +t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="level_up" unicode="&#xf148;" horiz-adv-x="1024" +d="M1018 933q-18 -37 -58 -37h-192v-864q0 -14 -9 -23t-23 -9h-704q-21 0 -29 18q-8 20 4 35l160 192q9 11 25 11h320v640h-192q-40 0 -58 37q-17 37 9 68l320 384q18 22 49 22t49 -22l320 -384q27 -32 9 -68z" /> + <glyph glyph-name="level_down" unicode="&#xf149;" horiz-adv-x="1024" +d="M32 1280h704q13 0 22.5 -9.5t9.5 -23.5v-863h192q40 0 58 -37t-9 -69l-320 -384q-18 -22 -49 -22t-49 22l-320 384q-26 31 -9 69q18 37 58 37h192v640h-320q-14 0 -25 11l-160 192q-13 14 -4 34q9 19 29 19z" /> + <glyph glyph-name="check_sign" unicode="&#xf14a;" +d="M685 237l614 614q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-467 -467l-211 211q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l358 -358q19 -19 45 -19t45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5 +t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="edit_sign" unicode="&#xf14b;" +d="M404 428l152 -152l-52 -52h-56v96h-96v56zM818 818q14 -13 -3 -30l-291 -291q-17 -17 -30 -3q-14 13 3 30l291 291q17 17 30 3zM544 128l544 544l-288 288l-544 -544v-288h288zM1152 736l92 92q28 28 28 68t-28 68l-152 152q-28 28 -68 28t-68 -28l-92 -92zM1536 1120 +v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="_312" unicode="&#xf14c;" +d="M1280 608v480q0 26 -19 45t-45 19h-480q-42 0 -59 -39q-17 -41 14 -70l144 -144l-534 -534q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l534 534l144 -144q18 -19 45 -19q12 0 25 5q39 17 39 59zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 +q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="share_sign" unicode="&#xf14d;" +d="M1005 435l352 352q19 19 19 45t-19 45l-352 352q-30 31 -69 14q-40 -17 -40 -59v-160q-119 0 -216 -19.5t-162.5 -51t-114 -79t-76.5 -95.5t-44.5 -109t-21.5 -111.5t-5 -110.5q0 -181 167 -404q11 -12 25 -12q7 0 13 3q22 9 19 33q-44 354 62 473q46 52 130 75.5 +t224 23.5v-160q0 -42 40 -59q12 -5 24 -5q26 0 45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="compass" unicode="&#xf14e;" +d="M640 448l256 128l-256 128v-256zM1024 1039v-542l-512 -256v542zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 +t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="collapse" unicode="&#xf150;" +d="M1145 861q18 -35 -5 -66l-320 -448q-19 -27 -52 -27t-52 27l-320 448q-23 31 -5 66q17 35 57 35h640q40 0 57 -35zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120 +v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="collapse_top" unicode="&#xf151;" +d="M1145 419q-17 -35 -57 -35h-640q-40 0 -57 35q-18 35 5 66l320 448q19 27 52 27t52 -27l320 -448q23 -31 5 -66zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120v-960 +q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="_317" unicode="&#xf152;" +d="M1088 640q0 -33 -27 -52l-448 -320q-31 -23 -66 -5q-35 17 -35 57v640q0 40 35 57q35 18 66 -5l448 -320q27 -19 27 -52zM1280 160v960q0 14 -9 23t-23 9h-960q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h960q14 0 23 9t9 23zM1536 1120v-960q0 -119 -84.5 -203.5 +t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="eur" unicode="&#xf153;" horiz-adv-x="1024" +d="M976 229l35 -159q3 -12 -3 -22.5t-17 -14.5l-5 -1q-4 -2 -10.5 -3.5t-16 -4.5t-21.5 -5.5t-25.5 -5t-30 -5t-33.5 -4.5t-36.5 -3t-38.5 -1q-234 0 -409 130.5t-238 351.5h-95q-13 0 -22.5 9.5t-9.5 22.5v113q0 13 9.5 22.5t22.5 9.5h66q-2 57 1 105h-67q-14 0 -23 9 +t-9 23v114q0 14 9 23t23 9h98q67 210 243.5 338t400.5 128q102 0 194 -23q11 -3 20 -15q6 -11 3 -24l-43 -159q-3 -13 -14 -19.5t-24 -2.5l-4 1q-4 1 -11.5 2.5l-17.5 3.5t-22.5 3.5t-26 3t-29 2.5t-29.5 1q-126 0 -226 -64t-150 -176h468q16 0 25 -12q10 -12 7 -26 +l-24 -114q-5 -26 -32 -26h-488q-3 -37 0 -105h459q15 0 25 -12q9 -12 6 -27l-24 -112q-2 -11 -11 -18.5t-20 -7.5h-387q48 -117 149.5 -185.5t228.5 -68.5q18 0 36 1.5t33.5 3.5t29.5 4.5t24.5 5t18.5 4.5l12 3l5 2q13 5 26 -2q12 -7 15 -21z" /> + <glyph glyph-name="gbp" unicode="&#xf154;" horiz-adv-x="1024" +d="M1020 399v-367q0 -14 -9 -23t-23 -9h-956q-14 0 -23 9t-9 23v150q0 13 9.5 22.5t22.5 9.5h97v383h-95q-14 0 -23 9.5t-9 22.5v131q0 14 9 23t23 9h95v223q0 171 123.5 282t314.5 111q185 0 335 -125q9 -8 10 -20.5t-7 -22.5l-103 -127q-9 -11 -22 -12q-13 -2 -23 7 +q-5 5 -26 19t-69 32t-93 18q-85 0 -137 -47t-52 -123v-215h305q13 0 22.5 -9t9.5 -23v-131q0 -13 -9.5 -22.5t-22.5 -9.5h-305v-379h414v181q0 13 9 22.5t23 9.5h162q14 0 23 -9.5t9 -22.5z" /> + <glyph glyph-name="usd" unicode="&#xf155;" horiz-adv-x="1024" +d="M978 351q0 -153 -99.5 -263.5t-258.5 -136.5v-175q0 -14 -9 -23t-23 -9h-135q-13 0 -22.5 9.5t-9.5 22.5v175q-66 9 -127.5 31t-101.5 44.5t-74 48t-46.5 37.5t-17.5 18q-17 21 -2 41l103 135q7 10 23 12q15 2 24 -9l2 -2q113 -99 243 -125q37 -8 74 -8q81 0 142.5 43 +t61.5 122q0 28 -15 53t-33.5 42t-58.5 37.5t-66 32t-80 32.5q-39 16 -61.5 25t-61.5 26.5t-62.5 31t-56.5 35.5t-53.5 42.5t-43.5 49t-35.5 58t-21 66.5t-8.5 78q0 138 98 242t255 134v180q0 13 9.5 22.5t22.5 9.5h135q14 0 23 -9t9 -23v-176q57 -6 110.5 -23t87 -33.5 +t63.5 -37.5t39 -29t15 -14q17 -18 5 -38l-81 -146q-8 -15 -23 -16q-14 -3 -27 7q-3 3 -14.5 12t-39 26.5t-58.5 32t-74.5 26t-85.5 11.5q-95 0 -155 -43t-60 -111q0 -26 8.5 -48t29.5 -41.5t39.5 -33t56 -31t60.5 -27t70 -27.5q53 -20 81 -31.5t76 -35t75.5 -42.5t62 -50 +t53 -63.5t31.5 -76.5t13 -94z" /> + <glyph glyph-name="inr" unicode="&#xf156;" horiz-adv-x="898" +d="M898 1066v-102q0 -14 -9 -23t-23 -9h-168q-23 -144 -129 -234t-276 -110q167 -178 459 -536q14 -16 4 -34q-8 -18 -29 -18h-195q-16 0 -25 12q-306 367 -498 571q-9 9 -9 22v127q0 13 9.5 22.5t22.5 9.5h112q132 0 212.5 43t102.5 125h-427q-14 0 -23 9t-9 23v102 +q0 14 9 23t23 9h413q-57 113 -268 113h-145q-13 0 -22.5 9.5t-9.5 22.5v133q0 14 9 23t23 9h832q14 0 23 -9t9 -23v-102q0 -14 -9 -23t-23 -9h-233q47 -61 64 -144h171q14 0 23 -9t9 -23z" /> + <glyph glyph-name="jpy" unicode="&#xf157;" horiz-adv-x="1027" +d="M603 0h-172q-13 0 -22.5 9t-9.5 23v330h-288q-13 0 -22.5 9t-9.5 23v103q0 13 9.5 22.5t22.5 9.5h288v85h-288q-13 0 -22.5 9t-9.5 23v104q0 13 9.5 22.5t22.5 9.5h214l-321 578q-8 16 0 32q10 16 28 16h194q19 0 29 -18l215 -425q19 -38 56 -125q10 24 30.5 68t27.5 61 +l191 420q8 19 29 19h191q17 0 27 -16q9 -14 1 -31l-313 -579h215q13 0 22.5 -9.5t9.5 -22.5v-104q0 -14 -9.5 -23t-22.5 -9h-290v-85h290q13 0 22.5 -9.5t9.5 -22.5v-103q0 -14 -9.5 -23t-22.5 -9h-290v-330q0 -13 -9.5 -22.5t-22.5 -9.5z" /> + <glyph glyph-name="rub" unicode="&#xf158;" horiz-adv-x="1280" +d="M1043 971q0 100 -65 162t-171 62h-320v-448h320q106 0 171 62t65 162zM1280 971q0 -193 -126.5 -315t-326.5 -122h-340v-118h505q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-505v-192q0 -14 -9.5 -23t-22.5 -9h-167q-14 0 -23 9t-9 23v192h-224q-14 0 -23 9t-9 23v128 +q0 14 9 23t23 9h224v118h-224q-14 0 -23 9t-9 23v149q0 13 9 22.5t23 9.5h224v629q0 14 9 23t23 9h539q200 0 326.5 -122t126.5 -315z" /> + <glyph glyph-name="krw" unicode="&#xf159;" horiz-adv-x="1792" +d="M514 341l81 299h-159l75 -300q1 -1 1 -3t1 -3q0 1 0.5 3.5t0.5 3.5zM630 768l35 128h-292l32 -128h225zM822 768h139l-35 128h-70zM1271 340l78 300h-162l81 -299q0 -1 0.5 -3.5t1.5 -3.5q0 1 0.5 3t0.5 3zM1382 768l33 128h-297l34 -128h230zM1792 736v-64q0 -14 -9 -23 +t-23 -9h-213l-164 -616q-7 -24 -31 -24h-159q-24 0 -31 24l-166 616h-209l-167 -616q-7 -24 -31 -24h-159q-11 0 -19.5 7t-10.5 17l-160 616h-208q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h175l-33 128h-142q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h109l-89 344q-5 15 5 28 +q10 12 26 12h137q26 0 31 -24l90 -360h359l97 360q7 24 31 24h126q24 0 31 -24l98 -360h365l93 360q5 24 31 24h137q16 0 26 -12q10 -13 5 -28l-91 -344h111q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-145l-34 -128h179q14 0 23 -9t9 -23z" /> + <glyph glyph-name="btc" unicode="&#xf15a;" horiz-adv-x="1280" +d="M1167 896q18 -182 -131 -258q117 -28 175 -103t45 -214q-7 -71 -32.5 -125t-64.5 -89t-97 -58.5t-121.5 -34.5t-145.5 -15v-255h-154v251q-80 0 -122 1v-252h-154v255q-18 0 -54 0.5t-55 0.5h-200l31 183h111q50 0 58 51v402h16q-6 1 -16 1v287q-13 68 -89 68h-111v164 +l212 -1q64 0 97 1v252h154v-247q82 2 122 2v245h154v-252q79 -7 140 -22.5t113 -45t82.5 -78t36.5 -114.5zM952 351q0 36 -15 64t-37 46t-57.5 30.5t-65.5 18.5t-74 9t-69 3t-64.5 -1t-47.5 -1v-338q8 0 37 -0.5t48 -0.5t53 1.5t58.5 4t57 8.5t55.5 14t47.5 21t39.5 30 +t24.5 40t9.5 51zM881 827q0 33 -12.5 58.5t-30.5 42t-48 28t-55 16.5t-61.5 8t-58 2.5t-54 -1t-39.5 -0.5v-307q5 0 34.5 -0.5t46.5 0t50 2t55 5.5t51.5 11t48.5 18.5t37 27t27 38.5t9 51z" /> + <glyph glyph-name="file" unicode="&#xf15b;" +d="M1024 1024v472q22 -14 36 -28l408 -408q14 -14 28 -36h-472zM896 992q0 -40 28 -68t68 -28h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544z" /> + <glyph glyph-name="file_text" unicode="&#xf15c;" +d="M1468 1060q14 -14 28 -36h-472v472q22 -14 36 -28zM992 896h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544q0 -40 28 -68t68 -28zM1152 160v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704 +q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23z" /> + <glyph glyph-name="sort_by_alphabet" unicode="&#xf15d;" horiz-adv-x="1664" +d="M1191 1128h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1572 -23 +v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -11v-2l14 2q9 2 30 2h248v119h121zM1661 874v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162 +l230 -662h70z" /> + <glyph glyph-name="_329" unicode="&#xf15e;" horiz-adv-x="1664" +d="M1191 104h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1661 -150 +v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162l230 -662h70zM1572 1001v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -10v-3l14 3q9 1 30 1h248 +v119h121z" /> + <glyph glyph-name="sort_by_attributes" unicode="&#xf160;" horiz-adv-x="1792" +d="M736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1792 -32v-192q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832 +q14 0 23 -9t9 -23zM1600 480v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1408 992v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1216 1504v-192q0 -14 -9 -23t-23 -9h-256 +q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23z" /> + <glyph glyph-name="sort_by_attributes_alt" unicode="&#xf161;" horiz-adv-x="1792" +d="M1216 -32v-192q0 -14 -9 -23t-23 -9h-256q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192 +q14 0 23 -9t9 -23zM1408 480v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1600 992v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1792 1504v-192q0 -14 -9 -23t-23 -9h-832 +q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832q14 0 23 -9t9 -23z" /> + <glyph glyph-name="sort_by_order" unicode="&#xf162;" +d="M1346 223q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23 +zM1486 165q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5 +t82 -252.5zM1456 882v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165z" /> + <glyph glyph-name="sort_by_order_alt" unicode="&#xf163;" +d="M1346 1247q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9 +t9 -23zM1456 -142v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165zM1486 1189q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13 +q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5t82 -252.5z" /> + <glyph glyph-name="_334" unicode="&#xf164;" horiz-adv-x="1664" +d="M256 192q0 26 -19 45t-45 19q-27 0 -45.5 -19t-18.5 -45q0 -27 18.5 -45.5t45.5 -18.5q26 0 45 18.5t19 45.5zM416 704v-640q0 -26 -19 -45t-45 -19h-288q-26 0 -45 19t-19 45v640q0 26 19 45t45 19h288q26 0 45 -19t19 -45zM1600 704q0 -86 -55 -149q15 -44 15 -76 +q3 -76 -43 -137q17 -56 0 -117q-15 -57 -54 -94q9 -112 -49 -181q-64 -76 -197 -78h-36h-76h-17q-66 0 -144 15.5t-121.5 29t-120.5 39.5q-123 43 -158 44q-26 1 -45 19.5t-19 44.5v641q0 25 18 43.5t43 20.5q24 2 76 59t101 121q68 87 101 120q18 18 31 48t17.5 48.5 +t13.5 60.5q7 39 12.5 61t19.5 52t34 50q19 19 45 19q46 0 82.5 -10.5t60 -26t40 -40.5t24 -45t12 -50t5 -45t0.5 -39q0 -38 -9.5 -76t-19 -60t-27.5 -56q-3 -6 -10 -18t-11 -22t-8 -24h277q78 0 135 -57t57 -135z" /> + <glyph glyph-name="_335" unicode="&#xf165;" horiz-adv-x="1664" +d="M256 960q0 -26 -19 -45t-45 -19q-27 0 -45.5 19t-18.5 45q0 27 18.5 45.5t45.5 18.5q26 0 45 -18.5t19 -45.5zM416 448v640q0 26 -19 45t-45 19h-288q-26 0 -45 -19t-19 -45v-640q0 -26 19 -45t45 -19h288q26 0 45 19t19 45zM1545 597q55 -61 55 -149q-1 -78 -57.5 -135 +t-134.5 -57h-277q4 -14 8 -24t11 -22t10 -18q18 -37 27 -57t19 -58.5t10 -76.5q0 -24 -0.5 -39t-5 -45t-12 -50t-24 -45t-40 -40.5t-60 -26t-82.5 -10.5q-26 0 -45 19q-20 20 -34 50t-19.5 52t-12.5 61q-9 42 -13.5 60.5t-17.5 48.5t-31 48q-33 33 -101 120q-49 64 -101 121 +t-76 59q-25 2 -43 20.5t-18 43.5v641q0 26 19 44.5t45 19.5q35 1 158 44q77 26 120.5 39.5t121.5 29t144 15.5h17h76h36q133 -2 197 -78q58 -69 49 -181q39 -37 54 -94q17 -61 0 -117q46 -61 43 -137q0 -32 -15 -76z" /> + <glyph glyph-name="youtube_sign" unicode="&#xf166;" +d="M919 233v157q0 50 -29 50q-17 0 -33 -16v-224q16 -16 33 -16q29 0 29 49zM1103 355h66v34q0 51 -33 51t-33 -51v-34zM532 621v-70h-80v-423h-74v423h-78v70h232zM733 495v-367h-67v40q-39 -45 -76 -45q-33 0 -42 28q-6 17 -6 54v290h66v-270q0 -24 1 -26q1 -15 15 -15 +q20 0 42 31v280h67zM985 384v-146q0 -52 -7 -73q-12 -42 -53 -42q-35 0 -68 41v-36h-67v493h67v-161q32 40 68 40q41 0 53 -42q7 -21 7 -74zM1236 255v-9q0 -29 -2 -43q-3 -22 -15 -40q-27 -40 -80 -40q-52 0 -81 38q-21 27 -21 86v129q0 59 20 86q29 38 80 38t78 -38 +q21 -29 21 -86v-76h-133v-65q0 -51 34 -51q24 0 30 26q0 1 0.5 7t0.5 16.5v21.5h68zM785 1079v-156q0 -51 -32 -51t-32 51v156q0 52 32 52t32 -52zM1318 366q0 177 -19 260q-10 44 -43 73.5t-76 34.5q-136 15 -412 15q-275 0 -411 -15q-44 -5 -76.5 -34.5t-42.5 -73.5 +q-20 -87 -20 -260q0 -176 20 -260q10 -43 42.5 -73t75.5 -35q137 -15 412 -15t412 15q43 5 75.5 35t42.5 73q20 84 20 260zM563 1017l90 296h-75l-51 -195l-53 195h-78q7 -23 23 -69l24 -69q35 -103 46 -158v-201h74v201zM852 936v130q0 58 -21 87q-29 38 -78 38 +q-51 0 -78 -38q-21 -29 -21 -87v-130q0 -58 21 -87q27 -38 78 -38q49 0 78 38q21 27 21 87zM1033 816h67v370h-67v-283q-22 -31 -42 -31q-15 0 -16 16q-1 2 -1 26v272h-67v-293q0 -37 6 -55q11 -27 43 -27q36 0 77 45v-40zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5 +h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="youtube" unicode="&#xf167;" +d="M971 292v-211q0 -67 -39 -67q-23 0 -45 22v301q22 22 45 22q39 0 39 -67zM1309 291v-46h-90v46q0 68 45 68t45 -68zM343 509h107v94h-312v-94h105v-569h100v569zM631 -60h89v494h-89v-378q-30 -42 -57 -42q-18 0 -21 21q-1 3 -1 35v364h-89v-391q0 -49 8 -73 +q12 -37 58 -37q48 0 102 61v-54zM1060 88v197q0 73 -9 99q-17 56 -71 56q-50 0 -93 -54v217h-89v-663h89v48q45 -55 93 -55q54 0 71 55q9 27 9 100zM1398 98v13h-91q0 -51 -2 -61q-7 -36 -40 -36q-46 0 -46 69v87h179v103q0 79 -27 116q-39 51 -106 51q-68 0 -107 -51 +q-28 -37 -28 -116v-173q0 -79 29 -116q39 -51 108 -51q72 0 108 53q18 27 21 54q2 9 2 58zM790 1011v210q0 69 -43 69t-43 -69v-210q0 -70 43 -70t43 70zM1509 260q0 -234 -26 -350q-14 -59 -58 -99t-102 -46q-184 -21 -555 -21t-555 21q-58 6 -102.5 46t-57.5 99 +q-26 112 -26 350q0 234 26 350q14 59 58 99t103 47q183 20 554 20t555 -20q58 -7 102.5 -47t57.5 -99q26 -112 26 -350zM511 1536h102l-121 -399v-271h-100v271q-14 74 -61 212q-37 103 -65 187h106l71 -263zM881 1203v-175q0 -81 -28 -118q-38 -51 -106 -51q-67 0 -105 51 +q-28 38 -28 118v175q0 80 28 117q38 51 105 51q68 0 106 -51q28 -37 28 -117zM1216 1365v-499h-91v55q-53 -62 -103 -62q-46 0 -59 37q-8 24 -8 75v394h91v-367q0 -33 1 -35q3 -22 21 -22q27 0 57 43v381h91z" /> + <glyph glyph-name="xing" unicode="&#xf168;" horiz-adv-x="1408" +d="M597 869q-10 -18 -257 -456q-27 -46 -65 -46h-239q-21 0 -31 17t0 36l253 448q1 0 0 1l-161 279q-12 22 -1 37q9 15 32 15h239q40 0 66 -45zM1403 1511q11 -16 0 -37l-528 -934v-1l336 -615q11 -20 1 -37q-10 -15 -32 -15h-239q-42 0 -66 45l-339 622q18 32 531 942 +q25 45 64 45h241q22 0 31 -15z" /> + <glyph glyph-name="xing_sign" unicode="&#xf169;" +d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 +l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="youtube_play" unicode="&#xf16a;" horiz-adv-x="1792" +d="M711 408l484 250l-484 253v-503zM896 1270q168 0 324.5 -4.5t229.5 -9.5l73 -4q1 0 17 -1.5t23 -3t23.5 -4.5t28.5 -8t28 -13t31 -19.5t29 -26.5q6 -6 15.5 -18.5t29 -58.5t26.5 -101q8 -64 12.5 -136.5t5.5 -113.5v-40v-136q1 -145 -18 -290q-7 -55 -25 -99.5t-32 -61.5 +l-14 -17q-14 -15 -29 -26.5t-31 -19t-28 -12.5t-28.5 -8t-24 -4.5t-23 -3t-16.5 -1.5q-251 -19 -627 -19q-207 2 -359.5 6.5t-200.5 7.5l-49 4l-36 4q-36 5 -54.5 10t-51 21t-56.5 41q-6 6 -15.5 18.5t-29 58.5t-26.5 101q-8 64 -12.5 136.5t-5.5 113.5v40v136 +q-1 145 18 290q7 55 25 99.5t32 61.5l14 17q14 15 29 26.5t31 19.5t28 13t28.5 8t23.5 4.5t23 3t17 1.5q251 18 627 18z" /> + <glyph glyph-name="dropbox" unicode="&#xf16b;" horiz-adv-x="1792" +d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" /> + <glyph glyph-name="stackexchange" unicode="&#xf16c;" +d="M1289 -96h-1118v480h-160v-640h1438v640h-160v-480zM347 428l33 157l783 -165l-33 -156zM450 802l67 146l725 -339l-67 -145zM651 1158l102 123l614 -513l-102 -123zM1048 1536l477 -641l-128 -96l-477 641zM330 65v159h800v-159h-800z" /> + <glyph glyph-name="instagram" unicode="&#xf16d;" +d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1162 640q0 -164 -115 -279t-279 -115t-279 115t-115 279t115 279t279 115t279 -115t115 -279zM1270 1050q0 -38 -27 -65t-65 -27t-65 27t-27 65t27 65t65 27t65 -27t27 -65zM768 1270 +q-7 0 -76.5 0.5t-105.5 0t-96.5 -3t-103 -10t-71.5 -18.5q-50 -20 -88 -58t-58 -88q-11 -29 -18.5 -71.5t-10 -103t-3 -96.5t0 -105.5t0.5 -76.5t-0.5 -76.5t0 -105.5t3 -96.5t10 -103t18.5 -71.5q20 -50 58 -88t88 -58q29 -11 71.5 -18.5t103 -10t96.5 -3t105.5 0t76.5 0.5 +t76.5 -0.5t105.5 0t96.5 3t103 10t71.5 18.5q50 20 88 58t58 88q11 29 18.5 71.5t10 103t3 96.5t0 105.5t-0.5 76.5t0.5 76.5t0 105.5t-3 96.5t-10 103t-18.5 71.5q-20 50 -58 88t-88 58q-29 11 -71.5 18.5t-103 10t-96.5 3t-105.5 0t-76.5 -0.5zM1536 640q0 -229 -5 -317 +q-10 -208 -124 -322t-322 -124q-88 -5 -317 -5t-317 5q-208 10 -322 124t-124 322q-5 88 -5 317t5 317q10 208 124 322t322 124q88 5 317 5t317 -5q208 -10 322 -124t124 -322q5 -88 5 -317z" /> + <glyph glyph-name="flickr" unicode="&#xf16e;" +d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 +t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" /> + <glyph glyph-name="adn" unicode="&#xf170;" +d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="f171" unicode="&#xf171;" horiz-adv-x="1408" +d="M815 677q8 -63 -50.5 -101t-111.5 -6q-39 17 -53.5 58t-0.5 82t52 58q36 18 72.5 12t64 -35.5t27.5 -67.5zM926 698q-14 107 -113 164t-197 13q-63 -28 -100.5 -88.5t-34.5 -129.5q4 -91 77.5 -155t165.5 -56q91 8 152 84t50 168zM1165 1240q-20 27 -56 44.5t-58 22 +t-71 12.5q-291 47 -566 -2q-43 -7 -66 -12t-55 -22t-50 -43q30 -28 76 -45.5t73.5 -22t87.5 -11.5q228 -29 448 -1q63 8 89.5 12t72.5 21.5t75 46.5zM1222 205q-8 -26 -15.5 -76.5t-14 -84t-28.5 -70t-58 -56.5q-86 -48 -189.5 -71.5t-202 -22t-201.5 18.5q-46 8 -81.5 18 +t-76.5 27t-73 43.5t-52 61.5q-25 96 -57 292l6 16l18 9q223 -148 506.5 -148t507.5 148q21 -6 24 -23t-5 -45t-8 -37zM1403 1166q-26 -167 -111 -655q-5 -30 -27 -56t-43.5 -40t-54.5 -31q-252 -126 -610 -88q-248 27 -394 139q-15 12 -25.5 26.5t-17 35t-9 34t-6 39.5 +t-5.5 35q-9 50 -26.5 150t-28 161.5t-23.5 147.5t-22 158q3 26 17.5 48.5t31.5 37.5t45 30t46 22.5t48 18.5q125 46 313 64q379 37 676 -50q155 -46 215 -122q16 -20 16.5 -51t-5.5 -54z" /> + <glyph glyph-name="bitbucket_sign" unicode="&#xf172;" +d="M848 666q0 43 -41 66t-77 1q-43 -20 -42.5 -72.5t43.5 -70.5q39 -23 81 4t36 72zM928 682q8 -66 -36 -121t-110 -61t-119 40t-56 113q-2 49 25.5 93t72.5 64q70 31 141.5 -10t81.5 -118zM1100 1073q-20 -21 -53.5 -34t-53 -16t-63.5 -8q-155 -20 -324 0q-44 6 -63 9.5 +t-52.5 16t-54.5 32.5q13 19 36 31t40 15.5t47 8.5q198 35 408 1q33 -5 51 -8.5t43 -16t39 -31.5zM1142 327q0 7 5.5 26.5t3 32t-17.5 16.5q-161 -106 -365 -106t-366 106l-12 -6l-5 -12q26 -154 41 -210q47 -81 204 -108q249 -46 428 53q34 19 49 51.5t22.5 85.5t12.5 71z +M1272 1020q9 53 -8 75q-43 55 -155 88q-216 63 -487 36q-132 -12 -226 -46q-38 -15 -59.5 -25t-47 -34t-29.5 -54q8 -68 19 -138t29 -171t24 -137q1 -5 5 -31t7 -36t12 -27t22 -28q105 -80 284 -100q259 -28 440 63q24 13 39.5 23t31 29t19.5 40q48 267 80 473zM1536 1120 +v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="tumblr" unicode="&#xf173;" horiz-adv-x="1024" +d="M944 207l80 -237q-23 -35 -111 -66t-177 -32q-104 -2 -190.5 26t-142.5 74t-95 106t-55.5 120t-16.5 118v544h-168v215q72 26 129 69.5t91 90t58 102t34 99t15 88.5q1 5 4.5 8.5t7.5 3.5h244v-424h333v-252h-334v-518q0 -30 6.5 -56t22.5 -52.5t49.5 -41.5t81.5 -14 +q78 2 134 29z" /> + <glyph glyph-name="tumblr_sign" unicode="&#xf174;" +d="M1136 75l-62 183q-44 -22 -103 -22q-36 -1 -62 10.5t-38.5 31.5t-17.5 40.5t-5 43.5v398h257v194h-256v326h-188q-8 0 -9 -10q-5 -44 -17.5 -87t-39 -95t-77 -95t-118.5 -68v-165h130v-418q0 -57 21.5 -115t65 -111t121 -85.5t176.5 -30.5q69 1 136.5 25t85.5 50z +M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="long_arrow_down" unicode="&#xf175;" horiz-adv-x="768" +d="M765 237q8 -19 -5 -35l-350 -384q-10 -10 -23 -10q-14 0 -24 10l-355 384q-13 16 -5 35q9 19 29 19h224v1248q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1248h224q21 0 29 -19z" /> + <glyph glyph-name="long_arrow_up" unicode="&#xf176;" horiz-adv-x="768" +d="M765 1043q-9 -19 -29 -19h-224v-1248q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1248h-224q-21 0 -29 19t5 35l350 384q10 10 23 10q14 0 24 -10l355 -384q13 -16 5 -35z" /> + <glyph glyph-name="long_arrow_left" unicode="&#xf177;" horiz-adv-x="1792" +d="M1792 736v-192q0 -14 -9 -23t-23 -9h-1248v-224q0 -21 -19 -29t-35 5l-384 350q-10 10 -10 23q0 14 10 24l384 354q16 14 35 6q19 -9 19 -29v-224h1248q14 0 23 -9t9 -23z" /> + <glyph glyph-name="long_arrow_right" unicode="&#xf178;" horiz-adv-x="1792" +d="M1728 643q0 -14 -10 -24l-384 -354q-16 -14 -35 -6q-19 9 -19 29v224h-1248q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h1248v224q0 21 19 29t35 -5l384 -350q10 -10 10 -23z" /> + <glyph glyph-name="apple" unicode="&#xf179;" horiz-adv-x="1408" +d="M1393 321q-39 -125 -123 -250q-129 -196 -257 -196q-49 0 -140 32q-86 32 -151 32q-61 0 -142 -33q-81 -34 -132 -34q-152 0 -301 259q-147 261 -147 503q0 228 113 374q113 144 284 144q72 0 177 -30q104 -30 138 -30q45 0 143 34q102 34 173 34q119 0 213 -65 +q52 -36 104 -100q-79 -67 -114 -118q-65 -94 -65 -207q0 -124 69 -223t158 -126zM1017 1494q0 -61 -29 -136q-30 -75 -93 -138q-54 -54 -108 -72q-37 -11 -104 -17q3 149 78 257q74 107 250 148q1 -3 2.5 -11t2.5 -11q0 -4 0.5 -10t0.5 -10z" /> + <glyph glyph-name="windows" unicode="&#xf17a;" horiz-adv-x="1664" +d="M682 530v-651l-682 94v557h682zM682 1273v-659h-682v565zM1664 530v-786l-907 125v661h907zM1664 1408v-794h-907v669z" /> + <glyph glyph-name="android" unicode="&#xf17b;" horiz-adv-x="1408" +d="M493 1053q16 0 27.5 11.5t11.5 27.5t-11.5 27.5t-27.5 11.5t-27 -11.5t-11 -27.5t11 -27.5t27 -11.5zM915 1053q16 0 27 11.5t11 27.5t-11 27.5t-27 11.5t-27.5 -11.5t-11.5 -27.5t11.5 -27.5t27.5 -11.5zM103 869q42 0 72 -30t30 -72v-430q0 -43 -29.5 -73t-72.5 -30 +t-73 30t-30 73v430q0 42 30 72t73 30zM1163 850v-666q0 -46 -32 -78t-77 -32h-75v-227q0 -43 -30 -73t-73 -30t-73 30t-30 73v227h-138v-227q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73l-1 227h-74q-46 0 -78 32t-32 78v666h918zM931 1255q107 -55 171 -153.5t64 -215.5 +h-925q0 117 64 215.5t172 153.5l-71 131q-7 13 5 20q13 6 20 -6l72 -132q95 42 201 42t201 -42l72 132q7 12 20 6q12 -7 5 -20zM1408 767v-430q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73v430q0 43 30 72.5t72 29.5q43 0 73 -29.5t30 -72.5z" /> + <glyph glyph-name="linux" unicode="&#xf17c;" +d="M663 1125q-11 -1 -15.5 -10.5t-8.5 -9.5q-5 -1 -5 5q0 12 19 15h10zM750 1111q-4 -1 -11.5 6.5t-17.5 4.5q24 11 32 -2q3 -6 -3 -9zM399 684q-4 1 -6 -3t-4.5 -12.5t-5.5 -13.5t-10 -13q-10 -11 -1 -12q4 -1 12.5 7t12.5 18q1 3 2 7t2 6t1.5 4.5t0.5 4v3t-1 2.5t-3 2z +M1254 325q0 18 -55 42q4 15 7.5 27.5t5 26t3 21.5t0.5 22.5t-1 19.5t-3.5 22t-4 20.5t-5 25t-5.5 26.5q-10 48 -47 103t-72 75q24 -20 57 -83q87 -162 54 -278q-11 -40 -50 -42q-31 -4 -38.5 18.5t-8 83.5t-11.5 107q-9 39 -19.5 69t-19.5 45.5t-15.5 24.5t-13 15t-7.5 7 +q-14 62 -31 103t-29.5 56t-23.5 33t-15 40q-4 21 6 53.5t4.5 49.5t-44.5 25q-15 3 -44.5 18t-35.5 16q-8 1 -11 26t8 51t36 27q37 3 51 -30t4 -58q-11 -19 -2 -26.5t30 -0.5q13 4 13 36v37q-5 30 -13.5 50t-21 30.5t-23.5 15t-27 7.5q-107 -8 -89 -134q0 -15 -1 -15 +q-9 9 -29.5 10.5t-33 -0.5t-15.5 5q1 57 -16 90t-45 34q-27 1 -41.5 -27.5t-16.5 -59.5q-1 -15 3.5 -37t13 -37.5t15.5 -13.5q10 3 16 14q4 9 -7 8q-7 0 -15.5 14.5t-9.5 33.5q-1 22 9 37t34 14q17 0 27 -21t9.5 -39t-1.5 -22q-22 -15 -31 -29q-8 -12 -27.5 -23.5 +t-20.5 -12.5q-13 -14 -15.5 -27t7.5 -18q14 -8 25 -19.5t16 -19t18.5 -13t35.5 -6.5q47 -2 102 15q2 1 23 7t34.5 10.5t29.5 13t21 17.5q9 14 20 8q5 -3 6.5 -8.5t-3 -12t-16.5 -9.5q-20 -6 -56.5 -21.5t-45.5 -19.5q-44 -19 -70 -23q-25 -5 -79 2q-10 2 -9 -2t17 -19 +q25 -23 67 -22q17 1 36 7t36 14t33.5 17.5t30 17t24.5 12t17.5 2.5t8.5 -11q0 -2 -1 -4.5t-4 -5t-6 -4.5t-8.5 -5t-9 -4.5t-10 -5t-9.5 -4.5q-28 -14 -67.5 -44t-66.5 -43t-49 -1q-21 11 -63 73q-22 31 -25 22q-1 -3 -1 -10q0 -25 -15 -56.5t-29.5 -55.5t-21 -58t11.5 -63 +q-23 -6 -62.5 -90t-47.5 -141q-2 -18 -1.5 -69t-5.5 -59q-8 -24 -29 -3q-32 31 -36 94q-2 28 4 56q4 19 -1 18q-2 -1 -4 -5q-36 -65 10 -166q5 -12 25 -28t24 -20q20 -23 104 -90.5t93 -76.5q16 -15 17.5 -38t-14 -43t-45.5 -23q8 -15 29 -44.5t28 -54t7 -70.5q46 24 7 92 +q-4 8 -10.5 16t-9.5 12t-2 6q3 5 13 9.5t20 -2.5q46 -52 166 -36q133 15 177 87q23 38 34 30q12 -6 10 -52q-1 -25 -23 -92q-9 -23 -6 -37.5t24 -15.5q3 19 14.5 77t13.5 90q2 21 -6.5 73.5t-7.5 97t23 70.5q15 18 51 18q1 37 34.5 53t72.5 10.5t60 -22.5zM626 1152 +q3 17 -2.5 30t-11.5 15q-9 2 -9 -7q2 -5 5 -6q10 0 7 -15q-3 -20 8 -20q3 0 3 3zM1045 955q-2 8 -6.5 11.5t-13 5t-14.5 5.5q-5 3 -9.5 8t-7 8t-5.5 6.5t-4 4t-4 -1.5q-14 -16 7 -43.5t39 -31.5q9 -1 14.5 8t3.5 20zM867 1168q0 11 -5 19.5t-11 12.5t-9 3q-6 0 -8 -2t0 -4 +t5 -3q14 -4 18 -31q0 -3 8 2q2 2 2 3zM921 1401q0 2 -2.5 5t-9 7t-9.5 6q-15 15 -24 15q-9 -1 -11.5 -7.5t-1 -13t-0.5 -12.5q-1 -4 -6 -10.5t-6 -9t3 -8.5q4 -3 8 0t11 9t15 9q1 1 9 1t15 2t9 7zM1486 60q20 -12 31 -24.5t12 -24t-2.5 -22.5t-15.5 -22t-23.5 -19.5 +t-30 -18.5t-31.5 -16.5t-32 -15.5t-27 -13q-38 -19 -85.5 -56t-75.5 -64q-17 -16 -68 -19.5t-89 14.5q-18 9 -29.5 23.5t-16.5 25.5t-22 19.5t-47 9.5q-44 1 -130 1q-19 0 -57 -1.5t-58 -2.5q-44 -1 -79.5 -15t-53.5 -30t-43.5 -28.5t-53.5 -11.5q-29 1 -111 31t-146 43 +q-19 4 -51 9.5t-50 9t-39.5 9.5t-33.5 14.5t-17 19.5q-10 23 7 66.5t18 54.5q1 16 -4 40t-10 42.5t-4.5 36.5t10.5 27q14 12 57 14t60 12q30 18 42 35t12 51q21 -73 -32 -106q-32 -20 -83 -15q-34 3 -43 -10q-13 -15 5 -57q2 -6 8 -18t8.5 -18t4.5 -17t1 -22q0 -15 -17 -49 +t-14 -48q3 -17 37 -26q20 -6 84.5 -18.5t99.5 -20.5q24 -6 74 -22t82.5 -23t55.5 -4q43 6 64.5 28t23 48t-7.5 58.5t-19 52t-20 36.5q-121 190 -169 242q-68 74 -113 40q-11 -9 -15 15q-3 16 -2 38q1 29 10 52t24 47t22 42q8 21 26.5 72t29.5 78t30 61t39 54 +q110 143 124 195q-12 112 -16 310q-2 90 24 151.5t106 104.5q39 21 104 21q53 1 106 -13.5t89 -41.5q57 -42 91.5 -121.5t29.5 -147.5q-5 -95 30 -214q34 -113 133 -218q55 -59 99.5 -163t59.5 -191q8 -49 5 -84.5t-12 -55.5t-20 -22q-10 -2 -23.5 -19t-27 -35.5 +t-40.5 -33.5t-61 -14q-18 1 -31.5 5t-22.5 13.5t-13.5 15.5t-11.5 20.5t-9 19.5q-22 37 -41 30t-28 -49t7 -97q20 -70 1 -195q-10 -65 18 -100.5t73 -33t85 35.5q59 49 89.5 66.5t103.5 42.5q53 18 77 36.5t18.5 34.5t-25 28.5t-51.5 23.5q-33 11 -49.5 48t-15 72.5 +t15.5 47.5q1 -31 8 -56.5t14.5 -40.5t20.5 -28.5t21 -19t21.5 -13t16.5 -9.5z" /> + <glyph glyph-name="dribble" unicode="&#xf17d;" +d="M1024 36q-42 241 -140 498h-2l-2 -1q-16 -6 -43 -16.5t-101 -49t-137 -82t-131 -114.5t-103 -148l-15 11q184 -150 418 -150q132 0 256 52zM839 643q-21 49 -53 111q-311 -93 -673 -93q-1 -7 -1 -21q0 -124 44 -236.5t124 -201.5q50 89 123.5 166.5t142.5 124.5t130.5 81 +t99.5 48l37 13q4 1 13 3.5t13 4.5zM732 855q-120 213 -244 378q-138 -65 -234 -186t-128 -272q302 0 606 80zM1416 536q-210 60 -409 29q87 -239 128 -469q111 75 185 189.5t96 250.5zM611 1277q-1 0 -2 -1q1 1 2 1zM1201 1132q-185 164 -433 164q-76 0 -155 -19 +q131 -170 246 -382q69 26 130 60.5t96.5 61.5t65.5 57t37.5 40.5zM1424 647q-3 232 -149 410l-1 -1q-9 -12 -19 -24.5t-43.5 -44.5t-71 -60.5t-100 -65t-131.5 -64.5q25 -53 44 -95q2 -5 6.5 -17t7.5 -17q36 5 74.5 7t73.5 2t69 -1.5t64 -4t56.5 -5.5t48 -6.5t36.5 -6 +t25 -4.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="skype" unicode="&#xf17e;" +d="M1173 473q0 50 -19.5 91.5t-48.5 68.5t-73 49t-82.5 34t-87.5 23l-104 24q-30 7 -44 10.5t-35 11.5t-30 16t-16.5 21t-7.5 30q0 77 144 77q43 0 77 -12t54 -28.5t38 -33.5t40 -29t48 -12q47 0 75.5 32t28.5 77q0 55 -56 99.5t-142 67.5t-182 23q-68 0 -132 -15.5 +t-119.5 -47t-89 -87t-33.5 -128.5q0 -61 19 -106.5t56 -75.5t80 -48.5t103 -32.5l146 -36q90 -22 112 -36q32 -20 32 -60q0 -39 -40 -64.5t-105 -25.5q-51 0 -91.5 16t-65 38.5t-45.5 45t-46 38.5t-54 16q-50 0 -75.5 -30t-25.5 -75q0 -92 122 -157.5t291 -65.5 +q73 0 140 18.5t122.5 53.5t88.5 93.5t33 131.5zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5q-130 0 -234 80q-77 -16 -150 -16q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5q0 73 16 150q-80 104 -80 234q0 159 112.5 271.5t271.5 112.5q130 0 234 -80 +q77 16 150 16q143 0 273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -73 -16 -150q80 -104 80 -234z" /> + <glyph glyph-name="foursquare" unicode="&#xf180;" horiz-adv-x="1280" +d="M1000 1102l37 194q5 23 -9 40t-35 17h-712q-23 0 -38.5 -17t-15.5 -37v-1101q0 -7 6 -1l291 352q23 26 38 33.5t48 7.5h239q22 0 37 14.5t18 29.5q24 130 37 191q4 21 -11.5 40t-36.5 19h-294q-29 0 -48 19t-19 48v42q0 29 19 47.5t48 18.5h346q18 0 35 13.5t20 29.5z +M1227 1324q-15 -73 -53.5 -266.5t-69.5 -350t-35 -173.5q-6 -22 -9 -32.5t-14 -32.5t-24.5 -33t-38.5 -21t-58 -10h-271q-13 0 -22 -10q-8 -9 -426 -494q-22 -25 -58.5 -28.5t-48.5 5.5q-55 22 -55 98v1410q0 55 38 102.5t120 47.5h888q95 0 127 -53t10 -159zM1227 1324 +l-158 -790q4 17 35 173.5t69.5 350t53.5 266.5z" /> + <glyph glyph-name="trello" unicode="&#xf181;" +d="M704 192v1024q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-1024q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1376 576v640q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-640q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408 +q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" /> + <glyph glyph-name="female" unicode="&#xf182;" horiz-adv-x="1280" +d="M1280 480q0 -40 -28 -68t-68 -28q-51 0 -80 43l-227 341h-45v-132l247 -411q9 -15 9 -33q0 -26 -19 -45t-45 -19h-192v-272q0 -46 -33 -79t-79 -33h-160q-46 0 -79 33t-33 79v272h-192q-26 0 -45 19t-19 45q0 18 9 33l247 411v132h-45l-227 -341q-29 -43 -80 -43 +q-40 0 -68 28t-28 68q0 29 16 53l256 384q73 107 176 107h384q103 0 176 -107l256 -384q16 -24 16 -53zM864 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" /> + <glyph glyph-name="male" unicode="&#xf183;" horiz-adv-x="1024" +d="M1024 832v-416q0 -40 -28 -68t-68 -28t-68 28t-28 68v352h-64v-912q0 -46 -33 -79t-79 -33t-79 33t-33 79v464h-64v-464q0 -46 -33 -79t-79 -33t-79 33t-33 79v912h-64v-352q0 -40 -28 -68t-68 -28t-68 28t-28 68v416q0 80 56 136t136 56h640q80 0 136 -56t56 -136z +M736 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" /> + <glyph glyph-name="gittip" unicode="&#xf184;" +d="M773 234l350 473q16 22 24.5 59t-6 85t-61.5 79q-40 26 -83 25.5t-73.5 -17.5t-54.5 -45q-36 -40 -96 -40q-59 0 -95 40q-24 28 -54.5 45t-73.5 17.5t-84 -25.5q-46 -31 -60.5 -79t-6 -85t24.5 -59zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 +t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="sun" unicode="&#xf185;" horiz-adv-x="1792" +d="M1472 640q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123t223.5 -45.5t223.5 45.5t184 123t123 184t45.5 223.5zM1748 363q-4 -15 -20 -20l-292 -96v-306q0 -16 -13 -26q-15 -10 -29 -4 +l-292 94l-180 -248q-10 -13 -26 -13t-26 13l-180 248l-292 -94q-14 -6 -29 4q-13 10 -13 26v306l-292 96q-16 5 -20 20q-5 17 4 29l180 248l-180 248q-9 13 -4 29q4 15 20 20l292 96v306q0 16 13 26q15 10 29 4l292 -94l180 248q9 12 26 12t26 -12l180 -248l292 94 +q14 6 29 -4q13 -10 13 -26v-306l292 -96q16 -5 20 -20q5 -16 -4 -29l-180 -248l180 -248q9 -12 4 -29z" /> + <glyph glyph-name="_366" unicode="&#xf186;" +d="M1262 233q-54 -9 -110 -9q-182 0 -337 90t-245 245t-90 337q0 192 104 357q-201 -60 -328.5 -229t-127.5 -384q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51q144 0 273.5 61.5t220.5 171.5zM1465 318q-94 -203 -283.5 -324.5t-413.5 -121.5q-156 0 -298 61 +t-245 164t-164 245t-61 298q0 153 57.5 292.5t156 241.5t235.5 164.5t290 68.5q44 2 61 -39q18 -41 -15 -72q-86 -78 -131.5 -181.5t-45.5 -218.5q0 -148 73 -273t198 -198t273 -73q118 0 228 51q41 18 72 -13q14 -14 17.5 -34t-4.5 -38z" /> + <glyph glyph-name="archive" unicode="&#xf187;" horiz-adv-x="1792" +d="M1088 704q0 26 -19 45t-45 19h-256q-26 0 -45 -19t-19 -45t19 -45t45 -19h256q26 0 45 19t19 45zM1664 896v-960q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v960q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1728 1344v-256q0 -26 -19 -45t-45 -19h-1536 +q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1536q26 0 45 -19t19 -45z" /> + <glyph glyph-name="bug" unicode="&#xf188;" horiz-adv-x="1664" +d="M1632 576q0 -26 -19 -45t-45 -19h-224q0 -171 -67 -290l208 -209q19 -19 19 -45t-19 -45q-18 -19 -45 -19t-45 19l-198 197q-5 -5 -15 -13t-42 -28.5t-65 -36.5t-82 -29t-97 -13v896h-128v-896q-51 0 -101.5 13.5t-87 33t-66 39t-43.5 32.5l-15 14l-183 -207 +q-20 -21 -48 -21q-24 0 -43 16q-19 18 -20.5 44.5t15.5 46.5l202 227q-58 114 -58 274h-224q-26 0 -45 19t-19 45t19 45t45 19h224v294l-173 173q-19 19 -19 45t19 45t45 19t45 -19l173 -173h844l173 173q19 19 45 19t45 -19t19 -45t-19 -45l-173 -173v-294h224q26 0 45 -19 +t19 -45zM1152 1152h-640q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5z" /> + <glyph glyph-name="vk" unicode="&#xf189;" horiz-adv-x="1920" +d="M1917 1016q23 -64 -150 -294q-24 -32 -65 -85q-40 -51 -55 -72t-30.5 -49.5t-12 -42t13 -34.5t32.5 -43t57 -53q4 -2 5 -4q141 -131 191 -221q3 -5 6.5 -12.5t7 -26.5t-0.5 -34t-25 -27.5t-59 -12.5l-256 -4q-24 -5 -56 5t-52 22l-20 12q-30 21 -70 64t-68.5 77.5t-61 58 +t-56.5 15.5q-3 -1 -8 -3.5t-17 -14.5t-21.5 -29.5t-17 -52t-6.5 -77.5q0 -15 -3.5 -27.5t-7.5 -18.5l-4 -5q-18 -19 -53 -22h-115q-71 -4 -146 16.5t-131.5 53t-103 66t-70.5 57.5l-25 24q-10 10 -27.5 30t-71.5 91t-106 151t-122.5 211t-130.5 272q-6 16 -6 27t3 16l4 6 +q15 19 57 19l274 2q12 -2 23 -6.5t16 -8.5l5 -3q16 -11 24 -32q20 -50 46 -103.5t41 -81.5l16 -29q29 -60 56 -104t48.5 -68.5t41.5 -38.5t34 -14t27 5q2 1 5 5t12 22t13.5 47t9.5 81t0 125q-2 40 -9 73t-14 46l-6 12q-25 34 -85 43q-13 2 5 24q16 19 38 30q53 26 239 24 +q82 -1 135 -13q20 -5 33.5 -13.5t20.5 -24t10.5 -32t3.5 -45.5t-1 -55t-2.5 -70.5t-1.5 -82.5q0 -11 -1 -42t-0.5 -48t3.5 -40.5t11.5 -39t22.5 -24.5q8 -2 17 -4t26 11t38 34.5t52 67t68 107.5q60 104 107 225q4 10 10 17.5t11 10.5l4 3l5 2.5t13 3t20 0.5l288 2 +q39 5 64 -2.5t31 -16.5z" /> + <glyph glyph-name="weibo" unicode="&#xf18a;" horiz-adv-x="1792" +d="M675 252q21 34 11 69t-45 50q-34 14 -73 1t-60 -46q-22 -34 -13 -68.5t43 -50.5t74.5 -2.5t62.5 47.5zM769 373q8 13 3.5 26.5t-17.5 18.5q-14 5 -28.5 -0.5t-21.5 -18.5q-17 -31 13 -45q14 -5 29 0.5t22 18.5zM943 266q-45 -102 -158 -150t-224 -12 +q-107 34 -147.5 126.5t6.5 187.5q47 93 151.5 139t210.5 19q111 -29 158.5 -119.5t2.5 -190.5zM1255 426q-9 96 -89 170t-208.5 109t-274.5 21q-223 -23 -369.5 -141.5t-132.5 -264.5q9 -96 89 -170t208.5 -109t274.5 -21q223 23 369.5 141.5t132.5 264.5zM1563 422 +q0 -68 -37 -139.5t-109 -137t-168.5 -117.5t-226 -83t-270.5 -31t-275 33.5t-240.5 93t-171.5 151t-65 199.5q0 115 69.5 245t197.5 258q169 169 341.5 236t246.5 -7q65 -64 20 -209q-4 -14 -1 -20t10 -7t14.5 0.5t13.5 3.5l6 2q139 59 246 59t153 -61q45 -63 0 -178 +q-2 -13 -4.5 -20t4.5 -12.5t12 -7.5t17 -6q57 -18 103 -47t80 -81.5t34 -116.5zM1489 1046q42 -47 54.5 -108.5t-6.5 -117.5q-8 -23 -29.5 -34t-44.5 -4q-23 8 -34 29.5t-4 44.5q20 63 -24 111t-107 35q-24 -5 -45 8t-25 37q-5 24 8 44.5t37 25.5q60 13 119 -5.5t101 -65.5z +M1670 1209q87 -96 112.5 -222.5t-13.5 -241.5q-9 -27 -34 -40t-52 -4t-40 34t-5 52q28 82 10 172t-80 158q-62 69 -148 95.5t-173 8.5q-28 -6 -52 9.5t-30 43.5t9.5 51.5t43.5 29.5q123 26 244 -11.5t208 -134.5z" /> + <glyph glyph-name="renren" unicode="&#xf18b;" +d="M1133 -34q-171 -94 -368 -94q-196 0 -367 94q138 87 235.5 211t131.5 268q35 -144 132.5 -268t235.5 -211zM638 1394v-485q0 -252 -126.5 -459.5t-330.5 -306.5q-181 215 -181 495q0 187 83.5 349.5t229.5 269.5t325 137zM1536 638q0 -280 -181 -495 +q-204 99 -330.5 306.5t-126.5 459.5v485q179 -30 325 -137t229.5 -269.5t83.5 -349.5z" /> + <glyph glyph-name="_372" unicode="&#xf18c;" horiz-adv-x="1408" +d="M1402 433q-32 -80 -76 -138t-91 -88.5t-99 -46.5t-101.5 -14.5t-96.5 8.5t-86.5 22t-69.5 27.5t-46 22.5l-17 10q-113 -228 -289.5 -359.5t-384.5 -132.5q-19 0 -32 13t-13 32t13 31.5t32 12.5q173 1 322.5 107.5t251.5 294.5q-36 -14 -72 -23t-83 -13t-91 2.5t-93 28.5 +t-92 59t-84.5 100t-74.5 146q114 47 214 57t167.5 -7.5t124.5 -56.5t88.5 -77t56.5 -82q53 131 79 291q-7 -1 -18 -2.5t-46.5 -2.5t-69.5 0.5t-81.5 10t-88.5 23t-84 42.5t-75 65t-54.5 94.5t-28.5 127.5q70 28 133.5 36.5t112.5 -1t92 -30t73.5 -50t56 -61t42 -63t27.5 -56 +t16 -39.5l4 -16q12 122 12 195q-8 6 -21.5 16t-49 44.5t-63.5 71.5t-54 93t-33 112.5t12 127t70 138.5q73 -25 127.5 -61.5t84.5 -76.5t48 -85t20.5 -89t-0.5 -85.5t-13 -76.5t-19 -62t-17 -42l-7 -15q1 -4 1 -50t-1 -72q3 7 10 18.5t30.5 43t50.5 58t71 55.5t91.5 44.5 +t112 14.5t132.5 -24q-2 -78 -21.5 -141.5t-50 -104.5t-69.5 -71.5t-81.5 -45.5t-84.5 -24t-80 -9.5t-67.5 1t-46.5 4.5l-17 3q-23 -147 -73 -283q6 7 18 18.5t49.5 41t77.5 52.5t99.5 42t117.5 20t129 -23.5t137 -77.5z" /> + <glyph glyph-name="stack_exchange" unicode="&#xf18d;" horiz-adv-x="1280" +d="M1259 283v-66q0 -85 -57.5 -144.5t-138.5 -59.5h-57l-260 -269v269h-529q-81 0 -138.5 59.5t-57.5 144.5v66h1238zM1259 609v-255h-1238v255h1238zM1259 937v-255h-1238v255h1238zM1259 1077v-67h-1238v67q0 84 57.5 143.5t138.5 59.5h846q81 0 138.5 -59.5t57.5 -143.5z +" /> + <glyph glyph-name="_374" unicode="&#xf18e;" +d="M1152 640q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 +t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="arrow_circle_alt_left" unicode="&#xf190;" +d="M1152 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-192q0 -14 -9 -23t-23 -9q-12 0 -24 10l-319 319q-9 9 -9 23t9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h352q13 0 22.5 -9.5t9.5 -22.5zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 +t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="_376" unicode="&#xf191;" +d="M1024 960v-640q0 -26 -19 -45t-45 -19q-20 0 -37 12l-448 320q-27 19 -27 52t27 52l448 320q17 12 37 12q26 0 45 -19t19 -45zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5z +M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="dot_circle_alt" unicode="&#xf192;" +d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5 +t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="_378" unicode="&#xf193;" horiz-adv-x="1664" +d="M1023 349l102 -204q-58 -179 -210 -290t-339 -111q-156 0 -288.5 77.5t-210 210t-77.5 288.5q0 181 104.5 330t274.5 211l17 -131q-122 -54 -195 -165.5t-73 -244.5q0 -185 131.5 -316.5t316.5 -131.5q126 0 232.5 65t165 175.5t49.5 236.5zM1571 249l58 -114l-256 -128 +q-13 -7 -29 -7q-40 0 -57 35l-239 477h-472q-24 0 -42.5 16.5t-21.5 40.5l-96 779q-2 17 6 42q14 51 57 82.5t97 31.5q66 0 113 -47t47 -113q0 -69 -52 -117.5t-120 -41.5l37 -289h423v-128h-407l16 -128h455q40 0 57 -35l228 -455z" /> + <glyph glyph-name="vimeo_square" unicode="&#xf194;" +d="M1292 898q10 216 -161 222q-231 8 -312 -261q44 19 82 19q85 0 74 -96q-4 -57 -74 -167t-105 -110q-43 0 -82 169q-13 54 -45 255q-30 189 -160 177q-59 -7 -164 -100l-81 -72l-81 -72l52 -67q76 52 87 52q57 0 107 -179q15 -55 45 -164.5t45 -164.5q68 -179 164 -179 +q157 0 383 294q220 283 226 444zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="_380" unicode="&#xf195;" horiz-adv-x="1152" +d="M1152 704q0 -191 -94.5 -353t-256.5 -256.5t-353 -94.5h-160q-14 0 -23 9t-9 23v611l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v93l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v250q0 14 9 23t23 9h160 +q14 0 23 -9t9 -23v-181l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-93l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-487q188 13 318 151t130 328q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" /> + <glyph glyph-name="plus_square_o" unicode="&#xf196;" horiz-adv-x="1408" +d="M1152 736v-64q0 -14 -9 -23t-23 -9h-352v-352q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v352h-352q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h352v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-352h352q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832 +q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="_382" unicode="&#xf197;" horiz-adv-x="2176" +d="M620 416q-110 -64 -268 -64h-128v64h-64q-13 0 -22.5 23.5t-9.5 56.5q0 24 7 49q-58 2 -96.5 10.5t-38.5 20.5t38.5 20.5t96.5 10.5q-7 25 -7 49q0 33 9.5 56.5t22.5 23.5h64v64h128q158 0 268 -64h1113q42 -7 106.5 -18t80.5 -14q89 -15 150 -40.5t83.5 -47.5t22.5 -40 +t-22.5 -40t-83.5 -47.5t-150 -40.5q-16 -3 -80.5 -14t-106.5 -18h-1113zM1739 668q53 -36 53 -92t-53 -92l81 -30q68 48 68 122t-68 122zM625 400h1015q-217 -38 -456 -80q-57 0 -113 -24t-83 -48l-28 -24l-288 -288q-26 -26 -70.5 -45t-89.5 -19h-96l-93 464h29 +q157 0 273 64zM352 816h-29l93 464h96q46 0 90 -19t70 -45l288 -288q4 -4 11 -10.5t30.5 -23t48.5 -29t61.5 -23t72.5 -10.5l456 -80h-1015q-116 64 -273 64z" /> + <glyph glyph-name="_383" unicode="&#xf198;" horiz-adv-x="1664" +d="M1519 760q62 0 103.5 -40.5t41.5 -101.5q0 -97 -93 -130l-172 -59l56 -167q7 -21 7 -47q0 -59 -42 -102t-101 -43q-47 0 -85.5 27t-53.5 72l-55 165l-310 -106l55 -164q8 -24 8 -47q0 -59 -42 -102t-102 -43q-47 0 -85 27t-53 72l-55 163l-153 -53q-29 -9 -50 -9 +q-61 0 -101.5 40t-40.5 101q0 47 27.5 85t71.5 53l156 53l-105 313l-156 -54q-26 -8 -48 -8q-60 0 -101 40.5t-41 100.5q0 47 27.5 85t71.5 53l157 53l-53 159q-8 24 -8 47q0 60 42 102.5t102 42.5q47 0 85 -27t53 -72l54 -160l310 105l-54 160q-8 24 -8 47q0 59 42.5 102 +t101.5 43q47 0 85.5 -27.5t53.5 -71.5l53 -161l162 55q21 6 43 6q60 0 102.5 -39.5t42.5 -98.5q0 -45 -30 -81.5t-74 -51.5l-157 -54l105 -316l164 56q24 8 46 8zM725 498l310 105l-105 315l-310 -107z" /> + <glyph glyph-name="_384" unicode="&#xf199;" +d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM1280 352v436q-31 -35 -64 -55q-34 -22 -132.5 -85t-151.5 -99q-98 -69 -164 -69v0v0q-66 0 -164 69 +q-47 32 -142 92.5t-142 92.5q-12 8 -33 27t-31 27v-436q0 -40 28 -68t68 -28h832q40 0 68 28t28 68zM1280 925q0 41 -27.5 70t-68.5 29h-832q-40 0 -68 -28t-28 -68q0 -37 30.5 -76.5t67.5 -64.5q47 -32 137.5 -89t129.5 -83q3 -2 17 -11.5t21 -14t21 -13t23.5 -13 +t21.5 -9.5t22.5 -7.5t20.5 -2.5t20.5 2.5t22.5 7.5t21.5 9.5t23.5 13t21 13t21 14t17 11.5l267 174q35 23 66.5 62.5t31.5 73.5z" /> + <glyph glyph-name="_385" unicode="&#xf19a;" horiz-adv-x="1792" +d="M127 640q0 163 67 313l367 -1005q-196 95 -315 281t-119 411zM1415 679q0 -19 -2.5 -38.5t-10 -49.5t-11.5 -44t-17.5 -59t-17.5 -58l-76 -256l-278 826q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-75 1 -202 10q-12 1 -20.5 -5t-11.5 -15t-1.5 -18.5t9 -16.5 +t19.5 -8l80 -8l120 -328l-168 -504l-280 832q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-7 0 -23 0.5t-26 0.5q105 160 274.5 253.5t367.5 93.5q147 0 280.5 -53t238.5 -149h-10q-55 0 -92 -40.5t-37 -95.5q0 -12 2 -24t4 -21.5t8 -23t9 -21t12 -22.5t12.5 -21 +t14.5 -24t14 -23q63 -107 63 -212zM909 573l237 -647q1 -6 5 -11q-126 -44 -255 -44q-112 0 -217 32zM1570 1009q95 -174 95 -369q0 -209 -104 -385.5t-279 -278.5l235 678q59 169 59 276q0 42 -6 79zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286 +t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 -215q173 0 331.5 68t273 182.5t182.5 273t68 331.5t-68 331.5t-182.5 273t-273 182.5t-331.5 68t-331.5 -68t-273 -182.5t-182.5 -273t-68 -331.5t68 -331.5t182.5 -273 +t273 -182.5t331.5 -68z" /> + <glyph glyph-name="_386" unicode="&#xf19b;" horiz-adv-x="1792" +d="M1086 1536v-1536l-272 -128q-228 20 -414 102t-293 208.5t-107 272.5q0 140 100.5 263.5t275 205.5t391.5 108v-172q-217 -38 -356.5 -150t-139.5 -255q0 -152 154.5 -267t388.5 -145v1360zM1755 954l37 -390l-525 114l147 83q-119 70 -280 99v172q277 -33 481 -157z" /> + <glyph glyph-name="_387" unicode="&#xf19c;" horiz-adv-x="2048" +d="M960 1536l960 -384v-128h-128q0 -26 -20.5 -45t-48.5 -19h-1526q-28 0 -48.5 19t-20.5 45h-128v128zM256 896h256v-768h128v768h256v-768h128v768h256v-768h128v768h256v-768h59q28 0 48.5 -19t20.5 -45v-64h-1664v64q0 26 20.5 45t48.5 19h59v768zM1851 -64 +q28 0 48.5 -19t20.5 -45v-128h-1920v128q0 26 20.5 45t48.5 19h1782z" /> + <glyph glyph-name="_388" unicode="&#xf19d;" horiz-adv-x="2304" +d="M1774 700l18 -316q4 -69 -82 -128t-235 -93.5t-323 -34.5t-323 34.5t-235 93.5t-82 128l18 316l574 -181q22 -7 48 -7t48 7zM2304 1024q0 -23 -22 -31l-1120 -352q-4 -1 -10 -1t-10 1l-652 206q-43 -34 -71 -111.5t-34 -178.5q63 -36 63 -109q0 -69 -58 -107l58 -433 +q2 -14 -8 -25q-9 -11 -24 -11h-192q-15 0 -24 11q-10 11 -8 25l58 433q-58 38 -58 107q0 73 65 111q11 207 98 330l-333 104q-22 8 -22 31t22 31l1120 352q4 1 10 1t10 -1l1120 -352q22 -8 22 -31z" /> + <glyph glyph-name="_389" unicode="&#xf19e;" +d="M859 579l13 -707q-62 11 -105 11q-41 0 -105 -11l13 707q-40 69 -168.5 295.5t-216.5 374.5t-181 287q58 -15 108 -15q44 0 111 15q63 -111 133.5 -229.5t167 -276.5t138.5 -227q37 61 109.5 177.5t117.5 190t105 176t107 189.5q54 -14 107 -14q56 0 114 14v0 +q-28 -39 -60 -88.5t-49.5 -78.5t-56.5 -96t-49 -84q-146 -248 -353 -610z" /> + <glyph glyph-name="uniF1A0" unicode="&#xf1a0;" +d="M768 750h725q12 -67 12 -128q0 -217 -91 -387.5t-259.5 -266.5t-386.5 -96q-157 0 -299 60.5t-245 163.5t-163.5 245t-60.5 299t60.5 299t163.5 245t245 163.5t299 60.5q300 0 515 -201l-209 -201q-123 119 -306 119q-129 0 -238.5 -65t-173.5 -176.5t-64 -243.5 +t64 -243.5t173.5 -176.5t238.5 -65q87 0 160 24t120 60t82 82t51.5 87t22.5 78h-436v264z" /> + <glyph glyph-name="f1a1" unicode="&#xf1a1;" horiz-adv-x="1792" +d="M1095 369q16 -16 0 -31q-62 -62 -199 -62t-199 62q-16 15 0 31q6 6 15 6t15 -6q48 -49 169 -49q120 0 169 49q6 6 15 6t15 -6zM788 550q0 -37 -26 -63t-63 -26t-63.5 26t-26.5 63q0 38 26.5 64t63.5 26t63 -26.5t26 -63.5zM1183 550q0 -37 -26.5 -63t-63.5 -26t-63 26 +t-26 63t26 63.5t63 26.5t63.5 -26t26.5 -64zM1434 670q0 49 -35 84t-85 35t-86 -36q-130 90 -311 96l63 283l200 -45q0 -37 26 -63t63 -26t63.5 26.5t26.5 63.5t-26.5 63.5t-63.5 26.5q-54 0 -80 -50l-221 49q-19 5 -25 -16l-69 -312q-180 -7 -309 -97q-35 37 -87 37 +q-50 0 -85 -35t-35 -84q0 -35 18.5 -64t49.5 -44q-6 -27 -6 -56q0 -142 140 -243t337 -101q198 0 338 101t140 243q0 32 -7 57q30 15 48 43.5t18 63.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191 +t348 71t348 -71t286 -191t191 -286t71 -348z" /> + <glyph glyph-name="_392" unicode="&#xf1a2;" +d="M939 407q13 -13 0 -26q-53 -53 -171 -53t-171 53q-13 13 0 26q5 6 13 6t13 -6q42 -42 145 -42t145 42q5 6 13 6t13 -6zM676 563q0 -31 -23 -54t-54 -23t-54 23t-23 54q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1014 563q0 -31 -23 -54t-54 -23t-54 23t-23 54 +q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1229 666q0 42 -30 72t-73 30q-42 0 -73 -31q-113 78 -267 82l54 243l171 -39q1 -32 23.5 -54t53.5 -22q32 0 54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5q-48 0 -69 -43l-189 42q-17 5 -21 -13l-60 -268q-154 -6 -265 -83 +q-30 32 -74 32q-43 0 -73 -30t-30 -72q0 -30 16 -55t42 -38q-5 -25 -5 -48q0 -122 120 -208.5t289 -86.5q170 0 290 86.5t120 208.5q0 25 -6 49q25 13 40.5 37.5t15.5 54.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 +q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="_393" unicode="&#xf1a3;" +d="M866 697l90 27v62q0 79 -58 135t-138 56t-138 -55.5t-58 -134.5v-283q0 -20 -14 -33.5t-33 -13.5t-32.5 13.5t-13.5 33.5v120h-151v-122q0 -82 57.5 -139t139.5 -57q81 0 138.5 56.5t57.5 136.5v280q0 19 13.5 33t33.5 14q19 0 32.5 -14t13.5 -33v-54zM1199 502v122h-150 +v-126q0 -20 -13.5 -33.5t-33.5 -13.5q-19 0 -32.5 14t-13.5 33v123l-90 -26l-60 28v-123q0 -80 58 -137t139 -57t138.5 57t57.5 139zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103 +t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="f1a4" unicode="&#xf1a4;" horiz-adv-x="1920" +d="M1062 824v118q0 42 -30 72t-72 30t-72 -30t-30 -72v-612q0 -175 -126 -299t-303 -124q-178 0 -303.5 125.5t-125.5 303.5v266h328v-262q0 -43 30 -72.5t72 -29.5t72 29.5t30 72.5v620q0 171 126.5 292t301.5 121q176 0 302 -122t126 -294v-136l-195 -58zM1592 602h328 +v-266q0 -178 -125.5 -303.5t-303.5 -125.5q-177 0 -303 124.5t-126 300.5v268l131 -61l195 58v-270q0 -42 30 -71.5t72 -29.5t72 29.5t30 71.5v275z" /> + <glyph glyph-name="_395" unicode="&#xf1a5;" +d="M1472 160v480h-704v704h-480q-93 0 -158.5 -65.5t-65.5 -158.5v-480h704v-704h480q93 0 158.5 65.5t65.5 158.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 +t84.5 -203.5z" /> + <glyph glyph-name="_396" unicode="&#xf1a6;" horiz-adv-x="2048" +d="M328 1254h204v-983h-532v697h328v286zM328 435v369h-123v-369h123zM614 968v-697h205v697h-205zM614 1254v-204h205v204h-205zM901 968h533v-942h-533v163h328v82h-328v697zM1229 435v369h-123v-369h123zM1516 968h532v-942h-532v163h327v82h-327v697zM1843 435v369h-123 +v-369h123z" /> + <glyph glyph-name="_397" unicode="&#xf1a7;" +d="M1046 516q0 -64 -38 -109t-91 -45q-43 0 -70 15v277q28 17 70 17q53 0 91 -45.5t38 -109.5zM703 944q0 -64 -38 -109.5t-91 -45.5q-43 0 -70 15v277q28 17 70 17q53 0 91 -45t38 -109zM1265 513q0 134 -88 229t-213 95q-20 0 -39 -3q-23 -78 -78 -136q-87 -95 -211 -101 +v-636l211 41v206q51 -19 117 -19q125 0 213 95t88 229zM922 940q0 134 -88.5 229t-213.5 95q-74 0 -141 -36h-186v-840l211 41v206q55 -19 116 -19q125 0 213.5 95t88.5 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 +q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="_398" unicode="&#xf1a8;" horiz-adv-x="2038" +d="M1222 607q75 3 143.5 -20.5t118 -58.5t101 -94.5t84 -108t75.5 -120.5q33 -56 78.5 -109t75.5 -80.5t99 -88.5q-48 -30 -108.5 -57.5t-138.5 -59t-114 -47.5q-44 37 -74 115t-43.5 164.5t-33 180.5t-42.5 168.5t-72.5 123t-122.5 48.5l-10 -2l-6 -4q4 -5 13 -14 +q6 -5 28 -23.5t25.5 -22t19 -18t18 -20.5t11.5 -21t10.5 -27.5t4.5 -31t4 -40.5l1 -33q1 -26 -2.5 -57.5t-7.5 -52t-12.5 -58.5t-11.5 -53q-35 1 -101 -9.5t-98 -10.5q-39 0 -72 10q-2 16 -2 47q0 74 3 96q2 13 31.5 41.5t57 59t26.5 51.5q-24 2 -43 -24 +q-36 -53 -111.5 -99.5t-136.5 -46.5q-25 0 -75.5 63t-106.5 139.5t-84 96.5q-6 4 -27 30q-482 -112 -513 -112q-16 0 -28 11t-12 27q0 15 8.5 26.5t22.5 14.5l486 106q-8 14 -8 25t5.5 17.5t16 11.5t20 7t23 4.5t18.5 4.5q4 1 15.5 7.5t17.5 6.5q15 0 28 -16t20 -33 +q163 37 172 37q17 0 29.5 -11t12.5 -28q0 -15 -8.5 -26t-23.5 -14l-182 -40l-1 -16q-1 -26 81.5 -117.5t104.5 -91.5q47 0 119 80t72 129q0 36 -23.5 53t-51 18.5t-51 11.5t-23.5 34q0 16 10 34l-68 19q43 44 43 117q0 26 -5 58q82 16 144 16q44 0 71.5 -1.5t48.5 -8.5 +t31 -13.5t20.5 -24.5t15.5 -33.5t17 -47.5t24 -60l50 25q-3 -40 -23 -60t-42.5 -21t-40 -6.5t-16.5 -20.5zM1282 842q-5 5 -13.5 15.5t-12 14.5t-10.5 11.5t-10 10.5l-8 8t-8.5 7.5t-8 5t-8.5 4.5q-7 3 -14.5 5t-20.5 2.5t-22 0.5h-32.5h-37.5q-126 0 -217 -43 +q16 30 36 46.5t54 29.5t65.5 36t46 36.5t50 55t43.5 50.5q12 -9 28 -31.5t32 -36.5t38 -13l12 1v-76l22 -1q247 95 371 190q28 21 50 39t42.5 37.5t33 31t29.5 34t24 31t24.5 37t23 38t27 47.5t29.5 53l7 9q-2 -53 -43 -139q-79 -165 -205 -264t-306 -142q-14 -3 -42 -7.5 +t-50 -9.5t-39 -14q3 -19 24.5 -46t21.5 -34q0 -11 -26 -30zM1061 -79q39 26 131.5 47.5t146.5 21.5q9 0 22.5 -15.5t28 -42.5t26 -50t24 -51t14.5 -33q-121 -45 -244 -45q-61 0 -125 11zM822 568l48 12l109 -177l-73 -48zM1323 51q3 -15 3 -16q0 -7 -17.5 -14.5t-46 -13 +t-54 -9.5t-53.5 -7.5t-32 -4.5l-7 43q21 2 60.5 8.5t72 10t60.5 3.5h14zM866 679l-96 -20l-6 17q10 1 32.5 7t34.5 6q19 0 35 -10zM1061 45h31l10 -83l-41 -12v95zM1950 1535v1v-1zM1950 1535l-1 -5l-2 -2l1 3zM1950 1535l1 1z" /> + <glyph glyph-name="_399" unicode="&#xf1a9;" +d="M1167 -50q-5 19 -24 5q-30 -22 -87 -39t-131 -17q-129 0 -193 49q-5 4 -13 4q-11 0 -26 -12q-7 -6 -7.5 -16t7.5 -20q34 -32 87.5 -46t102.5 -12.5t99 4.5q41 4 84.5 20.5t65 30t28.5 20.5q12 12 7 29zM1128 65q-19 47 -39 61q-23 15 -76 15q-47 0 -71 -10 +q-29 -12 -78 -56q-26 -24 -12 -44q9 -8 17.5 -4.5t31.5 23.5q3 2 10.5 8.5t10.5 8.5t10 7t11.5 7t12.5 5t15 4.5t16.5 2.5t20.5 1q27 0 44.5 -7.5t23 -14.5t13.5 -22q10 -17 12.5 -20t12.5 1q23 12 14 34zM1483 346q0 22 -5 44.5t-16.5 45t-34 36.5t-52.5 14 +q-33 0 -97 -41.5t-129 -83.5t-101 -42q-27 -1 -63.5 19t-76 49t-83.5 58t-100 49t-111 19q-115 -1 -197 -78.5t-84 -178.5q-2 -112 74 -164q29 -20 62.5 -28.5t103.5 -8.5q57 0 132 32.5t134 71t120 70.5t93 31q26 -1 65 -31.5t71.5 -67t68 -67.5t55.5 -32q35 -3 58.5 14 +t55.5 63q28 41 42.5 101t14.5 106zM1536 506q0 -164 -62 -304.5t-166 -236t-242.5 -149.5t-290.5 -54t-293 57.5t-247.5 157t-170.5 241.5t-64 302q0 89 19.5 172.5t49 145.5t70.5 118.5t78.5 94t78.5 69.5t64.5 46.5t42.5 24.5q14 8 51 26.5t54.5 28.5t48 30t60.5 44 +q36 28 58 72.5t30 125.5q129 -155 186 -193q44 -29 130 -68t129 -66q21 -13 39 -25t60.5 -46.5t76 -70.5t75 -95t69 -122t47 -148.5t19.5 -177.5z" /> + <glyph glyph-name="_400" unicode="&#xf1aa;" +d="M1070 463l-160 -160l-151 -152l-30 -30q-65 -64 -151.5 -87t-171.5 -2q-16 -70 -72 -115t-129 -45q-85 0 -145 60.5t-60 145.5q0 72 44.5 128t113.5 72q-22 86 1 173t88 152l12 12l151 -152l-11 -11q-37 -37 -37 -89t37 -90q37 -37 89 -37t89 37l30 30l151 152l161 160z +M729 1145l12 -12l-152 -152l-12 12q-37 37 -89 37t-89 -37t-37 -89.5t37 -89.5l29 -29l152 -152l160 -160l-151 -152l-161 160l-151 152l-30 30q-68 67 -90 159.5t5 179.5q-70 15 -115 71t-45 129q0 85 60 145.5t145 60.5q76 0 133.5 -49t69.5 -123q84 20 169.5 -3.5 +t149.5 -87.5zM1536 78q0 -85 -60 -145.5t-145 -60.5q-74 0 -131 47t-71 118q-86 -28 -179.5 -6t-161.5 90l-11 12l151 152l12 -12q37 -37 89 -37t89 37t37 89t-37 89l-30 30l-152 152l-160 160l152 152l160 -160l152 -152l29 -30q64 -64 87.5 -150.5t2.5 -171.5 +q76 -11 126.5 -68.5t50.5 -134.5zM1534 1202q0 -77 -51 -135t-127 -69q26 -85 3 -176.5t-90 -158.5l-12 -12l-151 152l12 12q37 37 37 89t-37 89t-89 37t-89 -37l-30 -30l-152 -152l-160 -160l-152 152l161 160l152 152l29 30q67 67 159 89.5t178 -3.5q11 75 68.5 126 +t135.5 51q85 0 145 -60.5t60 -145.5z" /> + <glyph glyph-name="f1ab" unicode="&#xf1ab;" +d="M654 458q-1 -3 -12.5 0.5t-31.5 11.5l-20 9q-44 20 -87 49q-7 5 -41 31.5t-38 28.5q-67 -103 -134 -181q-81 -95 -105 -110q-4 -2 -19.5 -4t-18.5 0q6 4 82 92q21 24 85.5 115t78.5 118q17 30 51 98.5t36 77.5q-8 1 -110 -33q-8 -2 -27.5 -7.5t-34.5 -9.5t-17 -5 +q-2 -2 -2 -10.5t-1 -9.5q-5 -10 -31 -15q-23 -7 -47 0q-18 4 -28 21q-4 6 -5 23q6 2 24.5 5t29.5 6q58 16 105 32q100 35 102 35q10 2 43 19.5t44 21.5q9 3 21.5 8t14.5 5.5t6 -0.5q2 -12 -1 -33q0 -2 -12.5 -27t-26.5 -53.5t-17 -33.5q-25 -50 -77 -131l64 -28 +q12 -6 74.5 -32t67.5 -28q4 -1 10.5 -25.5t4.5 -30.5zM449 944q3 -15 -4 -28q-12 -23 -50 -38q-30 -12 -60 -12q-26 3 -49 26q-14 15 -18 41l1 3q3 -3 19.5 -5t26.5 0t58 16q36 12 55 14q17 0 21 -17zM1147 815l63 -227l-139 42zM39 15l694 232v1032l-694 -233v-1031z +M1280 332l102 -31l-181 657l-100 31l-216 -536l102 -31l45 110l211 -65zM777 1294l573 -184v380zM1088 -29l158 -13l-54 -160l-40 66q-130 -83 -276 -108q-58 -12 -91 -12h-84q-79 0 -199.5 39t-183.5 85q-8 7 -8 16q0 8 5 13.5t13 5.5q4 0 18 -7.5t30.5 -16.5t20.5 -11 +q73 -37 159.5 -61.5t157.5 -24.5q95 0 167 14.5t157 50.5q15 7 30.5 15.5t34 19t28.5 16.5zM1536 1050v-1079l-774 246q-14 -6 -375 -127.5t-368 -121.5q-13 0 -18 13q0 1 -1 3v1078q3 9 4 10q5 6 20 11q107 36 149 50v384l558 -198q2 0 160.5 55t316 108.5t161.5 53.5 +q20 0 20 -21v-418z" /> + <glyph glyph-name="_402" unicode="&#xf1ac;" horiz-adv-x="1792" +d="M288 1152q66 0 113 -47t47 -113v-1088q0 -66 -47 -113t-113 -47h-128q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h128zM1664 989q58 -34 93 -93t35 -128v-768q0 -106 -75 -181t-181 -75h-864q-66 0 -113 47t-47 113v1536q0 40 28 68t68 28h672q40 0 88 -20t76 -48 +l152 -152q28 -28 48 -76t20 -88v-163zM928 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 512v128q0 14 -9 23 +t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128 +q14 0 23 9t9 23zM1184 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 256v128q0 14 -9 23t-23 9h-128 +q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1536 896v256h-160q-40 0 -68 28t-28 68v160h-640v-512h896z" /> + <glyph glyph-name="_403" unicode="&#xf1ad;" +d="M1344 1536q26 0 45 -19t19 -45v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280zM512 1248v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 992v-64q0 -14 9 -23t23 -9h64q14 0 23 9 +t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 736v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 480v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 160v64 +q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64 +q14 0 23 9t9 23zM384 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 -96v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9 +t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM896 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 928v64 +q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 160v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64 +q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9 +t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23z" /> + <glyph glyph-name="_404" unicode="&#xf1ae;" horiz-adv-x="1280" +d="M1188 988l-292 -292v-824q0 -46 -33 -79t-79 -33t-79 33t-33 79v384h-64v-384q0 -46 -33 -79t-79 -33t-79 33t-33 79v824l-292 292q-28 28 -28 68t28 68q29 28 68.5 28t67.5 -28l228 -228h368l228 228q28 28 68 28t68 -28q28 -29 28 -68.5t-28 -67.5zM864 1152 +q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" /> + <glyph glyph-name="uniF1B1" unicode="&#xf1b0;" horiz-adv-x="1664" +d="M780 1064q0 -60 -19 -113.5t-63 -92.5t-105 -39q-76 0 -138 57.5t-92 135.5t-30 151q0 60 19 113.5t63 92.5t105 39q77 0 138.5 -57.5t91.5 -135t30 -151.5zM438 581q0 -80 -42 -139t-119 -59q-76 0 -141.5 55.5t-100.5 133.5t-35 152q0 80 42 139.5t119 59.5 +q76 0 141.5 -55.5t100.5 -134t35 -152.5zM832 608q118 0 255 -97.5t229 -237t92 -254.5q0 -46 -17 -76.5t-48.5 -45t-64.5 -20t-76 -5.5q-68 0 -187.5 45t-182.5 45q-66 0 -192.5 -44.5t-200.5 -44.5q-183 0 -183 146q0 86 56 191.5t139.5 192.5t187.5 146t193 59zM1071 819 +q-61 0 -105 39t-63 92.5t-19 113.5q0 74 30 151.5t91.5 135t138.5 57.5q61 0 105 -39t63 -92.5t19 -113.5q0 -73 -30 -151t-92 -135.5t-138 -57.5zM1503 923q77 0 119 -59.5t42 -139.5q0 -74 -35 -152t-100.5 -133.5t-141.5 -55.5q-77 0 -119 59t-42 139q0 74 35 152.5 +t100.5 134t141.5 55.5z" /> + <glyph glyph-name="_406" unicode="&#xf1b1;" horiz-adv-x="768" +d="M704 1008q0 -145 -57 -243.5t-152 -135.5l45 -821q2 -26 -16 -45t-44 -19h-192q-26 0 -44 19t-16 45l45 821q-95 37 -152 135.5t-57 243.5q0 128 42.5 249.5t117.5 200t160 78.5t160 -78.5t117.5 -200t42.5 -249.5z" /> + <glyph glyph-name="_407" unicode="&#xf1b2;" horiz-adv-x="1792" +d="M896 -93l640 349v636l-640 -233v-752zM832 772l698 254l-698 254l-698 -254zM1664 1024v-768q0 -35 -18 -65t-49 -47l-704 -384q-28 -16 -61 -16t-61 16l-704 384q-31 17 -49 47t-18 65v768q0 40 23 73t61 47l704 256q22 8 44 8t44 -8l704 -256q38 -14 61 -47t23 -73z +" /> + <glyph glyph-name="_408" unicode="&#xf1b3;" horiz-adv-x="2304" +d="M640 -96l384 192v314l-384 -164v-342zM576 358l404 173l-404 173l-404 -173zM1664 -96l384 192v314l-384 -164v-342zM1600 358l404 173l-404 173l-404 -173zM1152 651l384 165v266l-384 -164v-267zM1088 1030l441 189l-441 189l-441 -189zM2176 512v-416q0 -36 -19 -67 +t-52 -47l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-4 2 -7 4q-2 -2 -7 -4l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-33 16 -52 47t-19 67v416q0 38 21.5 70t56.5 48l434 186v400q0 38 21.5 70t56.5 48l448 192q23 10 50 10t50 -10l448 -192q35 -16 56.5 -48t21.5 -70 +v-400l434 -186q36 -16 57 -48t21 -70z" /> + <glyph glyph-name="_409" unicode="&#xf1b4;" horiz-adv-x="2048" +d="M1848 1197h-511v-124h511v124zM1596 771q-90 0 -146 -52.5t-62 -142.5h408q-18 195 -200 195zM1612 186q63 0 122 32t76 87h221q-100 -307 -427 -307q-214 0 -340.5 132t-126.5 347q0 208 130.5 345.5t336.5 137.5q138 0 240.5 -68t153 -179t50.5 -248q0 -17 -2 -47h-658 +q0 -111 57.5 -171.5t166.5 -60.5zM277 236h296q205 0 205 167q0 180 -199 180h-302v-347zM277 773h281q78 0 123.5 36.5t45.5 113.5q0 144 -190 144h-260v-294zM0 1282h594q87 0 155 -14t126.5 -47.5t90 -96.5t31.5 -154q0 -181 -172 -263q114 -32 172 -115t58 -204 +q0 -75 -24.5 -136.5t-66 -103.5t-98.5 -71t-121 -42t-134 -13h-611v1260z" /> + <glyph glyph-name="_410" unicode="&#xf1b5;" +d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM499 1041h-371v-787h382q117 0 197 57.5t80 170.5q0 158 -143 200q107 52 107 164q0 57 -19.5 96.5 +t-56.5 60.5t-79 29.5t-97 8.5zM477 723h-176v184h163q119 0 119 -90q0 -94 -106 -94zM486 388h-185v217h189q124 0 124 -113q0 -104 -128 -104zM1136 356q-68 0 -104 38t-36 107h411q1 10 1 30q0 132 -74.5 220.5t-203.5 88.5q-128 0 -210 -86t-82 -216q0 -135 79 -217 +t213 -82q205 0 267 191h-138q-11 -34 -47.5 -54t-75.5 -20zM1126 722q113 0 124 -122h-254q4 56 39 89t91 33zM964 988h319v-77h-319v77z" /> + <glyph glyph-name="_411" unicode="&#xf1b6;" horiz-adv-x="1792" +d="M1582 954q0 -101 -71.5 -172.5t-172.5 -71.5t-172.5 71.5t-71.5 172.5t71.5 172.5t172.5 71.5t172.5 -71.5t71.5 -172.5zM812 212q0 104 -73 177t-177 73q-27 0 -54 -6l104 -42q77 -31 109.5 -106.5t1.5 -151.5q-31 -77 -107 -109t-152 -1q-21 8 -62 24.5t-61 24.5 +q32 -60 91 -96.5t130 -36.5q104 0 177 73t73 177zM1642 953q0 126 -89.5 215.5t-215.5 89.5q-127 0 -216.5 -89.5t-89.5 -215.5q0 -127 89.5 -216t216.5 -89q126 0 215.5 89t89.5 216zM1792 953q0 -189 -133.5 -322t-321.5 -133l-437 -319q-12 -129 -109 -218t-229 -89 +q-121 0 -214 76t-118 192l-230 92v429l389 -157q79 48 173 48q13 0 35 -2l284 407q2 187 135.5 319t320.5 132q188 0 321.5 -133.5t133.5 -321.5z" /> + <glyph glyph-name="_412" unicode="&#xf1b7;" +d="M1242 889q0 80 -57 136.5t-137 56.5t-136.5 -57t-56.5 -136q0 -80 56.5 -136.5t136.5 -56.5t137 56.5t57 136.5zM632 301q0 -83 -58 -140.5t-140 -57.5q-56 0 -103 29t-72 77q52 -20 98 -40q60 -24 120 1.5t85 86.5q24 60 -1.5 120t-86.5 84l-82 33q22 5 42 5 +q82 0 140 -57.5t58 -140.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v153l172 -69q20 -92 93.5 -152t168.5 -60q104 0 181 70t87 173l345 252q150 0 255.5 105.5t105.5 254.5q0 150 -105.5 255.5t-255.5 105.5 +q-148 0 -253 -104.5t-107 -252.5l-225 -322q-9 1 -28 1q-75 0 -137 -37l-297 119v468q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5zM1289 887q0 -100 -71 -170.5t-171 -70.5t-170.5 70.5t-70.5 170.5t70.5 171t170.5 71q101 0 171.5 -70.5t70.5 -171.5z +" /> + <glyph glyph-name="_413" unicode="&#xf1b8;" horiz-adv-x="1792" +d="M836 367l-15 -368l-2 -22l-420 29q-36 3 -67 31.5t-47 65.5q-11 27 -14.5 55t4 65t12 55t21.5 64t19 53q78 -12 509 -28zM449 953l180 -379l-147 92q-63 -72 -111.5 -144.5t-72.5 -125t-39.5 -94.5t-18.5 -63l-4 -21l-190 357q-17 26 -18 56t6 47l8 18q35 63 114 188 +l-140 86zM1680 436l-188 -359q-12 -29 -36.5 -46.5t-43.5 -20.5l-18 -4q-71 -7 -219 -12l8 -164l-230 367l211 362l7 -173q170 -16 283 -5t170 33zM895 1360q-47 -63 -265 -435l-317 187l-19 12l225 356q20 31 60 45t80 10q24 -2 48.5 -12t42 -21t41.5 -33t36 -34.5 +t36 -39.5t32 -35zM1550 1053l212 -363q18 -37 12.5 -76t-27.5 -74q-13 -20 -33 -37t-38 -28t-48.5 -22t-47 -16t-51.5 -14t-46 -12q-34 72 -265 436l313 195zM1407 1279l142 83l-220 -373l-419 20l151 86q-34 89 -75 166t-75.5 123.5t-64.5 80t-47 46.5l-17 13l405 -1 +q31 3 58 -10.5t39 -28.5l11 -15q39 -61 112 -190z" /> + <glyph glyph-name="_414" unicode="&#xf1b9;" horiz-adv-x="2048" +d="M480 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM516 768h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5zM1888 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM2048 544v-384 +q0 -14 -9 -23t-23 -9h-96v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-1024v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5t179 63.5h768q98 0 179 -63.5t104 -157.5 +l105 -419h28q93 0 158.5 -65.5t65.5 -158.5z" /> + <glyph glyph-name="_415" unicode="&#xf1ba;" horiz-adv-x="2048" +d="M1824 640q93 0 158.5 -65.5t65.5 -158.5v-384q0 -14 -9 -23t-23 -9h-96v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-1024v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5 +t179 63.5h128v224q0 14 9 23t23 9h448q14 0 23 -9t9 -23v-224h128q98 0 179 -63.5t104 -157.5l105 -419h28zM320 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM516 640h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5z +M1728 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47z" /> + <glyph glyph-name="_416" unicode="&#xf1bb;" +d="M1504 64q0 -26 -19 -45t-45 -19h-462q1 -17 6 -87.5t5 -108.5q0 -25 -18 -42.5t-43 -17.5h-320q-25 0 -43 17.5t-18 42.5q0 38 5 108.5t6 87.5h-462q-26 0 -45 19t-19 45t19 45l402 403h-229q-26 0 -45 19t-19 45t19 45l402 403h-197q-26 0 -45 19t-19 45t19 45l384 384 +q19 19 45 19t45 -19l384 -384q19 -19 19 -45t-19 -45t-45 -19h-197l402 -403q19 -19 19 -45t-19 -45t-45 -19h-229l402 -403q19 -19 19 -45z" /> + <glyph glyph-name="_417" unicode="&#xf1bc;" +d="M1127 326q0 32 -30 51q-193 115 -447 115q-133 0 -287 -34q-42 -9 -42 -52q0 -20 13.5 -34.5t35.5 -14.5q5 0 37 8q132 27 243 27q226 0 397 -103q19 -11 33 -11q19 0 33 13.5t14 34.5zM1223 541q0 40 -35 61q-237 141 -548 141q-153 0 -303 -42q-48 -13 -48 -64 +q0 -25 17.5 -42.5t42.5 -17.5q7 0 37 8q122 33 251 33q279 0 488 -124q24 -13 38 -13q25 0 42.5 17.5t17.5 42.5zM1331 789q0 47 -40 70q-126 73 -293 110.5t-343 37.5q-204 0 -364 -47q-23 -7 -38.5 -25.5t-15.5 -48.5q0 -31 20.5 -52t51.5 -21q11 0 40 8q133 37 307 37 +q159 0 309.5 -34t253.5 -95q21 -12 40 -12q29 0 50.5 20.5t21.5 51.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="_418" unicode="&#xf1bd;" horiz-adv-x="1024" +d="M1024 1233l-303 -582l24 -31h279v-415h-507l-44 -30l-142 -273l-30 -30h-301v303l303 583l-24 30h-279v415h507l44 30l142 273l30 30h301v-303z" /> + <glyph glyph-name="_419" unicode="&#xf1be;" horiz-adv-x="2304" +d="M784 164l16 241l-16 523q-1 10 -7.5 17t-16.5 7q-9 0 -16 -7t-7 -17l-14 -523l14 -241q1 -10 7.5 -16.5t15.5 -6.5q22 0 24 23zM1080 193l11 211l-12 586q0 16 -13 24q-8 5 -16 5t-16 -5q-13 -8 -13 -24l-1 -6l-10 -579q0 -1 11 -236v-1q0 -10 6 -17q9 -11 23 -11 +q11 0 20 9q9 7 9 20zM35 533l20 -128l-20 -126q-2 -9 -9 -9t-9 9l-17 126l17 128q2 9 9 9t9 -9zM121 612l26 -207l-26 -203q-2 -9 -10 -9q-9 0 -9 10l-23 202l23 207q0 9 9 9q8 0 10 -9zM401 159zM213 650l25 -245l-25 -237q0 -11 -11 -11q-10 0 -12 11l-21 237l21 245 +q2 12 12 12q11 0 11 -12zM307 657l23 -252l-23 -244q-2 -13 -14 -13q-13 0 -13 13l-21 244l21 252q0 13 13 13q12 0 14 -13zM401 639l21 -234l-21 -246q-2 -16 -16 -16q-6 0 -10.5 4.5t-4.5 11.5l-20 246l20 234q0 6 4.5 10.5t10.5 4.5q14 0 16 -15zM784 164zM495 785 +l21 -380l-21 -246q0 -7 -5 -12.5t-12 -5.5q-16 0 -18 18l-18 246l18 380q2 18 18 18q7 0 12 -5.5t5 -12.5zM589 871l19 -468l-19 -244q0 -8 -5.5 -13.5t-13.5 -5.5q-18 0 -20 19l-16 244l16 468q2 19 20 19q8 0 13.5 -5.5t5.5 -13.5zM687 911l18 -506l-18 -242 +q-2 -21 -22 -21q-19 0 -21 21l-16 242l16 506q0 9 6.5 15.5t14.5 6.5q9 0 15 -6.5t7 -15.5zM1079 169v0v0v0zM881 915l15 -510l-15 -239q0 -10 -7.5 -17.5t-17.5 -7.5t-17 7t-8 18l-14 239l14 510q0 11 7.5 18t17.5 7t17.5 -7t7.5 -18zM980 896l14 -492l-14 -236 +q0 -11 -8 -19t-19 -8t-19 8t-9 19l-12 236l12 492q1 12 9 20t19 8t18.5 -8t8.5 -20zM1192 404l-14 -231v0q0 -13 -9 -22t-22 -9t-22 9t-10 22l-6 114l-6 117l12 636v3q2 15 12 24q9 7 20 7q8 0 15 -5q14 -8 16 -26zM2304 423q0 -117 -83 -199.5t-200 -82.5h-786 +q-13 2 -22 11t-9 22v899q0 23 28 33q85 34 181 34q195 0 338 -131.5t160 -323.5q53 22 110 22q117 0 200 -83t83 -201z" /> + <glyph glyph-name="uniF1C0" unicode="&#xf1c0;" +d="M768 768q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 0q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127 +t443 -43zM768 384q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 1536q208 0 385 -34.5t280 -93.5t103 -128v-128q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5 +t-103 128v128q0 69 103 128t280 93.5t385 34.5z" /> + <glyph glyph-name="uniF1C1" unicode="&#xf1c1;" +d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z +M894 465q33 -26 84 -56q59 7 117 7q147 0 177 -49q16 -22 2 -52q0 -1 -1 -2l-2 -2v-1q-6 -38 -71 -38q-48 0 -115 20t-130 53q-221 -24 -392 -83q-153 -262 -242 -262q-15 0 -28 7l-24 12q-1 1 -6 5q-10 10 -6 36q9 40 56 91.5t132 96.5q14 9 23 -6q2 -2 2 -4q52 85 107 197 +q68 136 104 262q-24 82 -30.5 159.5t6.5 127.5q11 40 42 40h21h1q23 0 35 -15q18 -21 9 -68q-2 -6 -4 -8q1 -3 1 -8v-30q-2 -123 -14 -192q55 -164 146 -238zM318 54q52 24 137 158q-51 -40 -87.5 -84t-49.5 -74zM716 974q-15 -42 -2 -132q1 7 7 44q0 3 7 43q1 4 4 8 +q-1 1 -1 2q-1 2 -1 3q-1 22 -13 36q0 -1 -1 -2v-2zM592 313q135 54 284 81q-2 1 -13 9.5t-16 13.5q-76 67 -127 176q-27 -86 -83 -197q-30 -56 -45 -83zM1238 329q-24 24 -140 24q76 -28 124 -28q14 0 18 1q0 1 -2 3z" /> + <glyph glyph-name="_422" unicode="&#xf1c2;" +d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z +M233 768v-107h70l164 -661h159l128 485q7 20 10 46q2 16 2 24h4l3 -24q1 -3 3.5 -20t5.5 -26l128 -485h159l164 661h70v107h-300v-107h90l-99 -438q-5 -20 -7 -46l-2 -21h-4q0 3 -0.5 6.5t-1.5 8t-1 6.5q-1 5 -4 21t-5 25l-144 545h-114l-144 -545q-2 -9 -4.5 -24.5 +t-3.5 -21.5l-4 -21h-4l-2 21q-2 26 -7 46l-99 438h90v107h-300z" /> + <glyph glyph-name="_423" unicode="&#xf1c3;" +d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z +M429 106v-106h281v106h-75l103 161q5 7 10 16.5t7.5 13.5t3.5 4h2q1 -4 5 -10q2 -4 4.5 -7.5t6 -8t6.5 -8.5l107 -161h-76v-106h291v106h-68l-192 273l195 282h67v107h-279v-107h74l-103 -159q-4 -7 -10 -16.5t-9 -13.5l-2 -3h-2q-1 4 -5 10q-6 11 -17 23l-106 159h76v107 +h-290v-107h68l189 -272l-194 -283h-68z" /> + <glyph glyph-name="_424" unicode="&#xf1c4;" +d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z +M416 106v-106h327v106h-93v167h137q76 0 118 15q67 23 106.5 87t39.5 146q0 81 -37 141t-100 87q-48 19 -130 19h-368v-107h92v-555h-92zM769 386h-119v268h120q52 0 83 -18q56 -33 56 -115q0 -89 -62 -120q-31 -15 -78 -15z" /> + <glyph glyph-name="_425" unicode="&#xf1c5;" +d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z +M1280 320v-320h-1024v192l192 192l128 -128l384 384zM448 512q-80 0 -136 56t-56 136t56 136t136 56t136 -56t56 -136t-56 -136t-136 -56z" /> + <glyph glyph-name="_426" unicode="&#xf1c6;" +d="M640 1152v128h-128v-128h128zM768 1024v128h-128v-128h128zM640 896v128h-128v-128h128zM768 768v128h-128v-128h128zM1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400 +v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-128v-128h-128v128h-512v-1536h1280zM781 593l107 -349q8 -27 8 -52q0 -83 -72.5 -137.5t-183.5 -54.5t-183.5 54.5t-72.5 137.5q0 25 8 52q21 63 120 396v128h128v-128h79 +q22 0 39 -13t23 -34zM640 128q53 0 90.5 19t37.5 45t-37.5 45t-90.5 19t-90.5 -19t-37.5 -45t37.5 -45t90.5 -19z" /> + <glyph glyph-name="_427" unicode="&#xf1c7;" +d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z +M620 686q20 -8 20 -30v-544q0 -22 -20 -30q-8 -2 -12 -2q-12 0 -23 9l-166 167h-131q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h131l166 167q16 15 35 7zM1037 -3q31 0 50 24q129 159 129 363t-129 363q-16 21 -43 24t-47 -14q-21 -17 -23.5 -43.5t14.5 -47.5 +q100 -123 100 -282t-100 -282q-17 -21 -14.5 -47.5t23.5 -42.5q18 -15 40 -15zM826 145q27 0 47 20q87 93 87 219t-87 219q-18 19 -45 20t-46 -17t-20 -44.5t18 -46.5q52 -57 52 -131t-52 -131q-19 -20 -18 -46.5t20 -44.5q20 -17 44 -17z" /> + <glyph glyph-name="_428" unicode="&#xf1c8;" +d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z +M768 768q52 0 90 -38t38 -90v-384q0 -52 -38 -90t-90 -38h-384q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h384zM1260 766q20 -8 20 -30v-576q0 -22 -20 -30q-8 -2 -12 -2q-14 0 -23 9l-265 266v90l265 266q9 9 23 9q4 0 12 -2z" /> + <glyph glyph-name="_429" unicode="&#xf1c9;" +d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z +M480 768q8 11 21 12.5t24 -6.5l51 -38q11 -8 12.5 -21t-6.5 -24l-182 -243l182 -243q8 -11 6.5 -24t-12.5 -21l-51 -38q-11 -8 -24 -6.5t-21 12.5l-226 301q-14 19 0 38zM1282 467q14 -19 0 -38l-226 -301q-8 -11 -21 -12.5t-24 6.5l-51 38q-11 8 -12.5 21t6.5 24l182 243 +l-182 243q-8 11 -6.5 24t12.5 21l51 38q11 8 24 6.5t21 -12.5zM662 6q-13 2 -20.5 13t-5.5 24l138 831q2 13 13 20.5t24 5.5l63 -10q13 -2 20.5 -13t5.5 -24l-138 -831q-2 -13 -13 -20.5t-24 -5.5z" /> + <glyph glyph-name="_430" unicode="&#xf1ca;" +d="M1497 709v-198q-101 -23 -198 -23q-65 -136 -165.5 -271t-181.5 -215.5t-128 -106.5q-80 -45 -162 3q-28 17 -60.5 43.5t-85 83.5t-102.5 128.5t-107.5 184t-105.5 244t-91.5 314.5t-70.5 390h283q26 -218 70 -398.5t104.5 -317t121.5 -235.5t140 -195q169 169 287 406 +q-142 72 -223 220t-81 333q0 192 104 314.5t284 122.5q178 0 273 -105.5t95 -297.5q0 -159 -58 -286q-7 -1 -19.5 -3t-46 -2t-63 6t-62 25.5t-50.5 51.5q31 103 31 184q0 87 -29 132t-79 45q-53 0 -85 -49.5t-32 -140.5q0 -186 105 -293.5t267 -107.5q62 0 121 14z" /> + <glyph glyph-name="_431" unicode="&#xf1cb;" horiz-adv-x="1792" +d="M216 367l603 -402v359l-334 223zM154 511l193 129l-193 129v-258zM973 -35l603 402l-269 180l-334 -223v-359zM896 458l272 182l-272 182l-272 -182zM485 733l334 223v359l-603 -402zM1445 640l193 -129v258zM1307 733l269 180l-603 402v-359zM1792 913v-546 +q0 -41 -34 -64l-819 -546q-21 -13 -43 -13t-43 13l-819 546q-34 23 -34 64v546q0 41 34 64l819 546q21 13 43 13t43 -13l819 -546q34 -23 34 -64z" /> + <glyph glyph-name="_432" unicode="&#xf1cc;" horiz-adv-x="2048" +d="M1800 764q111 -46 179.5 -145.5t68.5 -221.5q0 -164 -118 -280.5t-285 -116.5q-4 0 -11.5 0.5t-10.5 0.5h-1209h-1h-2h-5q-170 10 -288 125.5t-118 280.5q0 110 55 203t147 147q-12 39 -12 82q0 115 82 196t199 81q95 0 172 -58q75 154 222.5 248t326.5 94 +q166 0 306 -80.5t221.5 -218.5t81.5 -301q0 -6 -0.5 -18t-0.5 -18zM468 498q0 -122 84 -193t208 -71q137 0 240 99q-16 20 -47.5 56.5t-43.5 50.5q-67 -65 -144 -65q-55 0 -93.5 33.5t-38.5 87.5q0 53 38.5 87t91.5 34q44 0 84.5 -21t73 -55t65 -75t69 -82t77 -75t97 -55 +t121.5 -21q121 0 204.5 71.5t83.5 190.5q0 121 -84 192t-207 71q-143 0 -241 -97l93 -108q66 64 142 64q52 0 92 -33t40 -84q0 -57 -37 -91.5t-94 -34.5q-43 0 -82.5 21t-72 55t-65.5 75t-69.5 82t-77.5 75t-96.5 55t-118.5 21q-122 0 -207 -70.5t-85 -189.5z" /> + <glyph glyph-name="_433" unicode="&#xf1cd;" horiz-adv-x="1792" +d="M896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 1408q-190 0 -361 -90l194 -194q82 28 167 28t167 -28l194 194q-171 90 -361 90zM218 279l194 194 +q-28 82 -28 167t28 167l-194 194q-90 -171 -90 -361t90 -361zM896 -128q190 0 361 90l-194 194q-82 -28 -167 -28t-167 28l-194 -194q171 -90 361 -90zM896 256q159 0 271.5 112.5t112.5 271.5t-112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5 +t271.5 -112.5zM1380 473l194 -194q90 171 90 361t-90 361l-194 -194q28 -82 28 -167t-28 -167z" /> + <glyph glyph-name="_434" unicode="&#xf1ce;" horiz-adv-x="1792" +d="M1760 640q0 -176 -68.5 -336t-184 -275.5t-275.5 -184t-336 -68.5t-336 68.5t-275.5 184t-184 275.5t-68.5 336q0 213 97 398.5t265 305.5t374 151v-228q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5 +t136.5 204t51 248.5q0 230 -145.5 406t-366.5 221v228q206 -31 374 -151t265 -305.5t97 -398.5z" /> + <glyph glyph-name="uniF1D0" unicode="&#xf1d0;" horiz-adv-x="1792" +d="M19 662q8 217 116 406t305 318h5q0 -1 -1 -3q-8 -8 -28 -33.5t-52 -76.5t-60 -110.5t-44.5 -135.5t-14 -150.5t39 -157.5t108.5 -154q50 -50 102 -69.5t90.5 -11.5t69.5 23.5t47 32.5l16 16q39 51 53 116.5t6.5 122.5t-21 107t-26.5 80l-14 29q-10 25 -30.5 49.5t-43 41 +t-43.5 29.5t-35 19l-13 6l104 115q39 -17 78 -52t59 -61l19 -27q1 48 -18.5 103.5t-40.5 87.5l-20 31l161 183l160 -181q-33 -46 -52.5 -102.5t-22.5 -90.5l-4 -33q22 37 61.5 72.5t67.5 52.5l28 17l103 -115q-44 -14 -85 -50t-60 -65l-19 -29q-31 -56 -48 -133.5t-7 -170 +t57 -156.5q33 -45 77.5 -60.5t85 -5.5t76 26.5t57.5 33.5l21 16q60 53 96.5 115t48.5 121.5t10 121.5t-18 118t-37 107.5t-45.5 93t-45 72t-34.5 47.5l-13 17q-14 13 -7 13l10 -3q40 -29 62.5 -46t62 -50t64 -58t58.5 -65t55.5 -77t45.5 -88t38 -103t23.5 -117t10.5 -136 +q3 -259 -108 -465t-312 -321t-456 -115q-185 0 -351 74t-283.5 198t-184 293t-60.5 353z" /> + <glyph glyph-name="uniF1D1" unicode="&#xf1d1;" horiz-adv-x="1792" +d="M874 -102v-66q-208 6 -385 109.5t-283 275.5l58 34q29 -49 73 -99l65 57q148 -168 368 -212l-17 -86q65 -12 121 -13zM276 428l-83 -28q22 -60 49 -112l-57 -33q-98 180 -98 385t98 385l57 -33q-30 -56 -49 -112l82 -28q-35 -100 -35 -212q0 -109 36 -212zM1528 251 +l58 -34q-106 -172 -283 -275.5t-385 -109.5v66q56 1 121 13l-17 86q220 44 368 212l65 -57q44 50 73 99zM1377 805l-233 -80q14 -42 14 -85t-14 -85l232 -80q-31 -92 -98 -169l-185 162q-57 -67 -147 -85l48 -241q-52 -10 -98 -10t-98 10l48 241q-90 18 -147 85l-185 -162 +q-67 77 -98 169l232 80q-14 42 -14 85t14 85l-233 80q33 93 99 169l185 -162q59 68 147 86l-48 240q44 10 98 10t98 -10l-48 -240q88 -18 147 -86l185 162q66 -76 99 -169zM874 1448v-66q-65 -2 -121 -13l17 -86q-220 -42 -368 -211l-65 56q-38 -42 -73 -98l-57 33 +q106 172 282 275.5t385 109.5zM1705 640q0 -205 -98 -385l-57 33q27 52 49 112l-83 28q36 103 36 212q0 112 -35 212l82 28q-19 56 -49 112l57 33q98 -180 98 -385zM1585 1063l-57 -33q-35 56 -73 98l-65 -56q-148 169 -368 211l17 86q-56 11 -121 13v66q209 -6 385 -109.5 +t282 -275.5zM1748 640q0 173 -67.5 331t-181.5 272t-272 181.5t-331 67.5t-331 -67.5t-272 -181.5t-181.5 -272t-67.5 -331t67.5 -331t181.5 -272t272 -181.5t331 -67.5t331 67.5t272 181.5t181.5 272t67.5 331zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71 +t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" /> + <glyph glyph-name="uniF1D2" unicode="&#xf1d2;" +d="M582 228q0 -66 -93 -66q-107 0 -107 63q0 64 98 64q102 0 102 -61zM546 694q0 -85 -74 -85q-77 0 -77 84q0 90 77 90q36 0 55 -25.5t19 -63.5zM712 769v125q-78 -29 -135 -29q-50 29 -110 29q-86 0 -145 -57t-59 -143q0 -50 29.5 -102t73.5 -67v-3q-38 -17 -38 -85 +q0 -53 41 -77v-3q-113 -37 -113 -139q0 -45 20 -78.5t54 -51t72 -25.5t81 -8q224 0 224 188q0 67 -48 99t-126 46q-27 5 -51.5 20.5t-24.5 39.5q0 44 49 52q77 15 122 70t45 134q0 24 -10 52q37 9 49 13zM771 350h137q-2 27 -2 82v387q0 46 2 69h-137q3 -23 3 -71v-392 +q0 -50 -3 -75zM1280 366v121q-30 -21 -68 -21q-53 0 -53 82v225h52q9 0 26.5 -1t26.5 -1v117h-105q0 82 3 102h-140q4 -24 4 -55v-47h-60v-117q36 3 37 3q3 0 11 -0.5t12 -0.5v-2h-2v-217q0 -37 2.5 -64t11.5 -56.5t24.5 -48.5t43.5 -31t66 -12q64 0 108 24zM924 1072 +q0 36 -24 63.5t-60 27.5t-60.5 -27t-24.5 -64q0 -36 25 -62.5t60 -26.5t59.5 27t24.5 62zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="_438" unicode="&#xf1d3;" horiz-adv-x="1792" +d="M595 22q0 100 -165 100q-158 0 -158 -104q0 -101 172 -101q151 0 151 105zM536 777q0 61 -30 102t-89 41q-124 0 -124 -145q0 -135 124 -135q119 0 119 137zM805 1101v-202q-36 -12 -79 -22q16 -43 16 -84q0 -127 -73 -216.5t-197 -112.5q-40 -8 -59.5 -27t-19.5 -58 +q0 -31 22.5 -51.5t58 -32t78.5 -22t86 -25.5t78.5 -37.5t58 -64t22.5 -98.5q0 -304 -363 -304q-69 0 -130 12.5t-116 41t-87.5 82t-32.5 127.5q0 165 182 225v4q-67 41 -67 126q0 109 63 137v4q-72 24 -119.5 108.5t-47.5 165.5q0 139 95 231.5t235 92.5q96 0 178 -47 +q98 0 218 47zM1123 220h-222q4 45 4 134v609q0 94 -4 128h222q-4 -33 -4 -124v-613q0 -89 4 -134zM1724 442v-196q-71 -39 -174 -39q-62 0 -107 20t-70 50t-39.5 78t-18.5 92t-4 103v351h2v4q-7 0 -19 1t-18 1q-21 0 -59 -6v190h96v76q0 54 -6 89h227q-6 -41 -6 -165h171 +v-190q-15 0 -43.5 2t-42.5 2h-85v-365q0 -131 87 -131q61 0 109 33zM1148 1389q0 -58 -39 -101.5t-96 -43.5q-58 0 -98 43.5t-40 101.5q0 59 39.5 103t98.5 44q58 0 96.5 -44.5t38.5 -102.5z" /> + <glyph glyph-name="_439" unicode="&#xf1d4;" +d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="uniF1D5" unicode="&#xf1d5;" horiz-adv-x="1280" +d="M842 964q0 -80 -57 -136.5t-136 -56.5q-60 0 -111 35q-62 -67 -115 -146q-247 -371 -202 -859q1 -22 -12.5 -38.5t-34.5 -18.5h-5q-20 0 -35 13.5t-17 33.5q-14 126 -3.5 247.5t29.5 217t54 186t69 155.5t74 125q61 90 132 165q-16 35 -16 77q0 80 56.5 136.5t136.5 56.5 +t136.5 -56.5t56.5 -136.5zM1223 953q0 -158 -78 -292t-212.5 -212t-292.5 -78q-64 0 -131 14q-21 5 -32.5 23.5t-6.5 39.5q5 20 23 31.5t39 7.5q51 -13 108 -13q97 0 186 38t153 102t102 153t38 186t-38 186t-102 153t-153 102t-186 38t-186 -38t-153 -102t-102 -153 +t-38 -186q0 -114 52 -218q10 -20 3.5 -40t-25.5 -30t-39.5 -3t-30.5 26q-64 123 -64 265q0 119 46.5 227t124.5 186t186 124t226 46q158 0 292.5 -78t212.5 -212.5t78 -292.5z" /> + <glyph glyph-name="uniF1D6" unicode="&#xf1d6;" horiz-adv-x="1792" +d="M270 730q-8 19 -8 52q0 20 11 49t24 45q-1 22 7.5 53t22.5 43q0 139 92.5 288.5t217.5 209.5q139 66 324 66q133 0 266 -55q49 -21 90 -48t71 -56t55 -68t42 -74t32.5 -84.5t25.5 -89.5t22 -98l1 -5q55 -83 55 -150q0 -14 -9 -40t-9 -38q0 -1 1.5 -3.5t3.5 -5t2 -3.5 +q77 -114 120.5 -214.5t43.5 -208.5q0 -43 -19.5 -100t-55.5 -57q-9 0 -19.5 7.5t-19 17.5t-19 26t-16 26.5t-13.5 26t-9 17.5q-1 1 -3 1l-5 -4q-59 -154 -132 -223q20 -20 61.5 -38.5t69 -41.5t35.5 -65q-2 -4 -4 -16t-7 -18q-64 -97 -302 -97q-53 0 -110.5 9t-98 20 +t-104.5 30q-15 5 -23 7q-14 4 -46 4.5t-40 1.5q-41 -45 -127.5 -65t-168.5 -20q-35 0 -69 1.5t-93 9t-101 20.5t-74.5 40t-32.5 64q0 40 10 59.5t41 48.5q11 2 40.5 13t49.5 12q4 0 14 2q2 2 2 4l-2 3q-48 11 -108 105.5t-73 156.5l-5 3q-4 0 -12 -20q-18 -41 -54.5 -74.5 +t-77.5 -37.5h-1q-4 0 -6 4.5t-5 5.5q-23 54 -23 100q0 275 252 466z" /> + <glyph glyph-name="uniF1D7" unicode="&#xf1d7;" horiz-adv-x="2048" +d="M580 1075q0 41 -25 66t-66 25q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 66 24.5t25 65.5zM1323 568q0 28 -25.5 50t-65.5 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q40 0 65.5 22t25.5 51zM1087 1075q0 41 -24.5 66t-65.5 25 +q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 65.5 24.5t24.5 65.5zM1722 568q0 28 -26 50t-65 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q39 0 65 22t26 51zM1456 965q-31 4 -70 4q-169 0 -311 -77t-223.5 -208.5t-81.5 -287.5 +q0 -78 23 -152q-35 -3 -68 -3q-26 0 -50 1.5t-55 6.5t-44.5 7t-54.5 10.5t-50 10.5l-253 -127l72 218q-290 203 -290 490q0 169 97.5 311t264 223.5t363.5 81.5q176 0 332.5 -66t262 -182.5t136.5 -260.5zM2048 404q0 -117 -68.5 -223.5t-185.5 -193.5l55 -181l-199 109 +q-150 -37 -218 -37q-169 0 -311 70.5t-223.5 191.5t-81.5 264t81.5 264t223.5 191.5t311 70.5q161 0 303 -70.5t227.5 -192t85.5 -263.5z" /> + <glyph glyph-name="_443" unicode="&#xf1d8;" horiz-adv-x="1792" +d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-453 185l-242 -295q-18 -23 -49 -23q-13 0 -22 4q-19 7 -30.5 23.5t-11.5 36.5v349l864 1059l-1069 -925l-395 162q-37 14 -40 55q-2 40 32 59l1664 960q15 9 32 9q20 0 36 -11z" /> + <glyph glyph-name="_444" unicode="&#xf1d9;" horiz-adv-x="1792" +d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-527 215l-298 -327q-18 -21 -47 -21q-14 0 -23 4q-19 7 -30 23.5t-11 36.5v452l-472 193q-37 14 -40 55q-3 39 32 59l1664 960q35 21 68 -2zM1422 26l221 1323l-1434 -827l336 -137 +l863 639l-478 -797z" /> + <glyph glyph-name="_445" unicode="&#xf1da;" +d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 +t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298zM896 928v-448q0 -14 -9 -23 +t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23z" /> + <glyph glyph-name="_446" unicode="&#xf1db;" +d="M768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 +t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="_447" unicode="&#xf1dc;" horiz-adv-x="1792" +d="M1682 -128q-44 0 -132.5 3.5t-133.5 3.5q-44 0 -132 -3.5t-132 -3.5q-24 0 -37 20.5t-13 45.5q0 31 17 46t39 17t51 7t45 15q33 21 33 140l-1 391q0 21 -1 31q-13 4 -50 4h-675q-38 0 -51 -4q-1 -10 -1 -31l-1 -371q0 -142 37 -164q16 -10 48 -13t57 -3.5t45 -15 +t20 -45.5q0 -26 -12.5 -48t-36.5 -22q-47 0 -139.5 3.5t-138.5 3.5q-43 0 -128 -3.5t-127 -3.5q-23 0 -35.5 21t-12.5 45q0 30 15.5 45t36 17.5t47.5 7.5t42 15q33 23 33 143l-1 57v813q0 3 0.5 26t0 36.5t-1.5 38.5t-3.5 42t-6.5 36.5t-11 31.5t-16 18q-15 10 -45 12t-53 2 +t-41 14t-18 45q0 26 12 48t36 22q46 0 138.5 -3.5t138.5 -3.5q42 0 126.5 3.5t126.5 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17 -43.5t-38.5 -14.5t-49.5 -4t-43 -13q-35 -21 -35 -160l1 -320q0 -21 1 -32q13 -3 39 -3h699q25 0 38 3q1 11 1 32l1 320q0 139 -35 160 +q-18 11 -58.5 12.5t-66 13t-25.5 49.5q0 26 12.5 48t37.5 22q44 0 132 -3.5t132 -3.5q43 0 129 3.5t129 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17.5 -44t-40 -14.5t-51.5 -3t-44 -12.5q-35 -23 -35 -161l1 -943q0 -119 34 -140q16 -10 46 -13.5t53.5 -4.5t41.5 -15.5t18 -44.5 +q0 -26 -12 -48t-36 -22z" /> + <glyph glyph-name="_448" unicode="&#xf1dd;" horiz-adv-x="1280" +d="M1278 1347v-73q0 -29 -18.5 -61t-42.5 -32q-50 0 -54 -1q-26 -6 -32 -31q-3 -11 -3 -64v-1152q0 -25 -18 -43t-43 -18h-108q-25 0 -43 18t-18 43v1218h-143v-1218q0 -25 -17.5 -43t-43.5 -18h-108q-26 0 -43.5 18t-17.5 43v496q-147 12 -245 59q-126 58 -192 179 +q-64 117 -64 259q0 166 88 286q88 118 209 159q111 37 417 37h479q25 0 43 -18t18 -43z" /> + <glyph glyph-name="_449" unicode="&#xf1de;" +d="M352 128v-128h-352v128h352zM704 256q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM864 640v-128h-864v128h864zM224 1152v-128h-224v128h224zM1536 128v-128h-736v128h736zM576 1280q26 0 45 -19t19 -45v-256 +q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1216 768q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1536 640v-128h-224v128h224zM1536 1152v-128h-864v128h864z" /> + <glyph glyph-name="uniF1E0" unicode="&#xf1e0;" +d="M1216 512q133 0 226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5t-226.5 93.5t-93.5 226.5q0 12 2 34l-360 180q-92 -86 -218 -86q-133 0 -226.5 93.5t-93.5 226.5t93.5 226.5t226.5 93.5q126 0 218 -86l360 180q-2 22 -2 34q0 133 93.5 226.5t226.5 93.5 +t226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5q-126 0 -218 86l-360 -180q2 -22 2 -34t-2 -34l360 -180q92 86 218 86z" /> + <glyph glyph-name="_451" unicode="&#xf1e1;" +d="M1280 341q0 88 -62.5 151t-150.5 63q-84 0 -145 -58l-241 120q2 16 2 23t-2 23l241 120q61 -58 145 -58q88 0 150.5 63t62.5 151t-62.5 150.5t-150.5 62.5t-151 -62.5t-63 -150.5q0 -7 2 -23l-241 -120q-62 57 -145 57q-88 0 -150.5 -62.5t-62.5 -150.5t62.5 -150.5 +t150.5 -62.5q83 0 145 57l241 -120q-2 -16 -2 -23q0 -88 63 -150.5t151 -62.5t150.5 62.5t62.5 150.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="_452" unicode="&#xf1e2;" horiz-adv-x="1792" +d="M571 947q-10 25 -34 35t-49 0q-108 -44 -191 -127t-127 -191q-10 -25 0 -49t35 -34q13 -5 24 -5q42 0 60 40q34 84 98.5 148.5t148.5 98.5q25 11 35 35t0 49zM1513 1303l46 -46l-244 -243l68 -68q19 -19 19 -45.5t-19 -45.5l-64 -64q89 -161 89 -343q0 -143 -55.5 -273.5 +t-150 -225t-225 -150t-273.5 -55.5t-273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5q182 0 343 -89l64 64q19 19 45.5 19t45.5 -19l68 -68zM1521 1359q-10 -10 -22 -10q-13 0 -23 10l-91 90q-9 10 -9 23t9 23q10 9 23 9t23 -9l90 -91 +q10 -9 10 -22.5t-10 -22.5zM1751 1129q-11 -9 -23 -9t-23 9l-90 91q-10 9 -10 22.5t10 22.5q9 10 22.5 10t22.5 -10l91 -90q9 -10 9 -23t-9 -23zM1792 1312q0 -14 -9 -23t-23 -9h-96q-14 0 -23 9t-9 23t9 23t23 9h96q14 0 23 -9t9 -23zM1600 1504v-96q0 -14 -9 -23t-23 -9 +t-23 9t-9 23v96q0 14 9 23t23 9t23 -9t9 -23zM1751 1449l-91 -90q-10 -10 -22 -10q-13 0 -23 10q-10 9 -10 22.5t10 22.5l90 91q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" /> + <glyph glyph-name="_453" unicode="&#xf1e3;" horiz-adv-x="1792" +d="M609 720l287 208l287 -208l-109 -336h-355zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM1515 186q149 203 149 454v3l-102 -89l-240 224l63 323 +l134 -12q-150 206 -389 282l53 -124l-287 -159l-287 159l53 124q-239 -76 -389 -282l135 12l62 -323l-240 -224l-102 89v-3q0 -251 149 -454l30 132l326 -40l139 -298l-116 -69q117 -39 240 -39t240 39l-116 69l139 298l326 40z" /> + <glyph glyph-name="_454" unicode="&#xf1e4;" horiz-adv-x="1792" +d="M448 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM256 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM832 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 +v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM66 768q-28 0 -47 19t-19 46v129h514v-129q0 -27 -19 -46t-46 -19h-383zM1216 224v-192q0 -14 -9 -23t-23 -9h-192 +q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1600 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23 +zM1408 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1016v-13h-514v10q0 104 -382 102q-382 -1 -382 -102v-10h-514v13q0 17 8.5 43t34 64t65.5 75.5t110.5 76t160 67.5t224 47.5t293.5 18.5t293 -18.5t224 -47.5 +t160.5 -67.5t110.5 -76t65.5 -75.5t34 -64t8.5 -43zM1792 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 962v-129q0 -27 -19 -46t-46 -19h-384q-27 0 -46 19t-19 46v129h514z" /> + <glyph glyph-name="_455" unicode="&#xf1e5;" horiz-adv-x="1792" +d="M704 1216v-768q0 -26 -19 -45t-45 -19v-576q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v512l249 873q7 23 31 23h424zM1024 1216v-704h-256v704h256zM1792 320v-512q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v576q-26 0 -45 19t-19 45v768h424q24 0 31 -23z +M736 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23zM1408 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23z" /> + <glyph glyph-name="_456" unicode="&#xf1e6;" horiz-adv-x="1792" +d="M1755 1083q37 -38 37 -90.5t-37 -90.5l-401 -400l150 -150l-160 -160q-163 -163 -389.5 -186.5t-411.5 100.5l-362 -362h-181v181l362 362q-124 185 -100.5 411.5t186.5 389.5l160 160l150 -150l400 401q38 37 91 37t90 -37t37 -90.5t-37 -90.5l-400 -401l234 -234 +l401 400q38 37 91 37t90 -37z" /> + <glyph glyph-name="_457" unicode="&#xf1e7;" horiz-adv-x="1792" +d="M873 796q0 -83 -63.5 -142.5t-152.5 -59.5t-152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59t152.5 -59t63.5 -143zM1375 796q0 -83 -63 -142.5t-153 -59.5q-89 0 -152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59q90 0 153 -59t63 -143zM1600 616v667q0 87 -32 123.5 +t-111 36.5h-1112q-83 0 -112.5 -34t-29.5 -126v-673q43 -23 88.5 -40t81 -28t81 -18.5t71 -11t70 -4t58.5 -0.5t56.5 2t44.5 2q68 1 95 -27q6 -6 10 -9q26 -25 61 -51q7 91 118 87q5 0 36.5 -1.5t43 -2t45.5 -1t53 1t54.5 4.5t61 8.5t62 13.5t67 19.5t67.5 27t72 34.5z +M1763 621q-121 -149 -372 -252q84 -285 -23 -465q-66 -113 -183 -148q-104 -32 -182 15q-86 51 -82 164l-1 326v1q-8 2 -24.5 6t-23.5 5l-1 -338q4 -114 -83 -164q-79 -47 -183 -15q-117 36 -182 150q-105 180 -22 463q-251 103 -372 252q-25 37 -4 63t60 -1q4 -2 11.5 -7 +t10.5 -8v694q0 72 47 123t114 51h1257q67 0 114 -51t47 -123v-694l21 15q39 27 60 1t-4 -63z" /> + <glyph glyph-name="_458" unicode="&#xf1e8;" horiz-adv-x="1792" +d="M896 1102v-434h-145v434h145zM1294 1102v-434h-145v434h145zM1294 342l253 254v795h-1194v-1049h326v-217l217 217h398zM1692 1536v-1013l-434 -434h-326l-217 -217h-217v217h-398v1158l109 289h1483z" /> + <glyph glyph-name="_459" unicode="&#xf1e9;" +d="M773 217v-127q-1 -292 -6 -305q-12 -32 -51 -40q-54 -9 -181.5 38t-162.5 89q-13 15 -17 36q-1 12 4 26q4 10 34 47t181 216q1 0 60 70q15 19 39.5 24.5t49.5 -3.5q24 -10 37.5 -29t12.5 -42zM624 468q-3 -55 -52 -70l-120 -39q-275 -88 -292 -88q-35 2 -54 36 +q-12 25 -17 75q-8 76 1 166.5t30 124.5t56 32q13 0 202 -77q71 -29 115 -47l84 -34q23 -9 35.5 -30.5t11.5 -48.5zM1450 171q-7 -54 -91.5 -161t-135.5 -127q-37 -14 -63 7q-14 10 -184 287l-47 77q-14 21 -11.5 46t19.5 46q35 43 83 26q1 -1 119 -40q203 -66 242 -79.5 +t47 -20.5q28 -22 22 -61zM778 803q5 -102 -54 -122q-58 -17 -114 71l-378 598q-8 35 19 62q41 43 207.5 89.5t224.5 31.5q40 -10 49 -45q3 -18 22 -305.5t24 -379.5zM1440 695q3 -39 -26 -59q-15 -10 -329 -86q-67 -15 -91 -23l1 2q-23 -6 -46 4t-37 32q-30 47 0 87 +q1 1 75 102q125 171 150 204t34 39q28 19 65 2q48 -23 123 -133.5t81 -167.5v-3z" /> + <glyph glyph-name="_460" unicode="&#xf1ea;" horiz-adv-x="2048" +d="M1024 1024h-384v-384h384v384zM1152 384v-128h-640v128h640zM1152 1152v-640h-640v640h640zM1792 384v-128h-512v128h512zM1792 640v-128h-512v128h512zM1792 896v-128h-512v128h512zM1792 1152v-128h-512v128h512zM256 192v960h-128v-960q0 -26 19 -45t45 -19t45 19 +t19 45zM1920 192v1088h-1536v-1088q0 -33 -11 -64h1483q26 0 45 19t19 45zM2048 1408v-1216q0 -80 -56 -136t-136 -56h-1664q-80 0 -136 56t-56 136v1088h256v128h1792z" /> + <glyph glyph-name="_461" unicode="&#xf1eb;" horiz-adv-x="2048" +d="M1024 13q-20 0 -93 73.5t-73 93.5q0 32 62.5 54t103.5 22t103.5 -22t62.5 -54q0 -20 -73 -93.5t-93 -73.5zM1294 284q-2 0 -40 25t-101.5 50t-128.5 25t-128.5 -25t-101 -50t-40.5 -25q-18 0 -93.5 75t-75.5 93q0 13 10 23q78 77 196 121t233 44t233 -44t196 -121 +q10 -10 10 -23q0 -18 -75.5 -93t-93.5 -75zM1567 556q-11 0 -23 8q-136 105 -252 154.5t-268 49.5q-85 0 -170.5 -22t-149 -53t-113.5 -62t-79 -53t-31 -22q-17 0 -92 75t-75 93q0 12 10 22q132 132 320 205t380 73t380 -73t320 -205q10 -10 10 -22q0 -18 -75 -93t-92 -75z +M1838 827q-11 0 -22 9q-179 157 -371.5 236.5t-420.5 79.5t-420.5 -79.5t-371.5 -236.5q-11 -9 -22 -9q-17 0 -92.5 75t-75.5 93q0 13 10 23q187 186 445 288t527 102t527 -102t445 -288q10 -10 10 -23q0 -18 -75.5 -93t-92.5 -75z" /> + <glyph glyph-name="_462" unicode="&#xf1ec;" horiz-adv-x="1792" +d="M384 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 +t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5 +t37.5 90.5zM384 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 768q0 53 -37.5 90.5t-90.5 37.5 +t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1536 0v384q0 52 -38 90t-90 38t-90 -38t-38 -90v-384q0 -52 38 -90t90 -38t90 38t38 90zM1152 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z +M1536 1088v256q0 26 -19 45t-45 19h-1280q-26 0 -45 -19t-19 -45v-256q0 -26 19 -45t45 -19h1280q26 0 45 19t19 45zM1536 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1408v-1536q0 -52 -38 -90t-90 -38 +h-1408q-52 0 -90 38t-38 90v1536q0 52 38 90t90 38h1408q52 0 90 -38t38 -90z" /> + <glyph glyph-name="_463" unicode="&#xf1ed;" +d="M1519 890q18 -84 -4 -204q-87 -444 -565 -444h-44q-25 0 -44 -16.5t-24 -42.5l-4 -19l-55 -346l-2 -15q-5 -26 -24.5 -42.5t-44.5 -16.5h-251q-21 0 -33 15t-9 36q9 56 26.5 168t26.5 168t27 167.5t27 167.5q5 37 43 37h131q133 -2 236 21q175 39 287 144q102 95 155 246 +q24 70 35 133q1 6 2.5 7.5t3.5 1t6 -3.5q79 -59 98 -162zM1347 1172q0 -107 -46 -236q-80 -233 -302 -315q-113 -40 -252 -42q0 -1 -90 -1l-90 1q-100 0 -118 -96q-2 -8 -85 -530q-1 -10 -12 -10h-295q-22 0 -36.5 16.5t-11.5 38.5l232 1471q5 29 27.5 48t51.5 19h598 +q34 0 97.5 -13t111.5 -32q107 -41 163.5 -123t56.5 -196z" /> + <glyph glyph-name="_464" unicode="&#xf1ee;" horiz-adv-x="1792" +d="M441 864q33 0 52 -26q266 -364 362 -774h-446q-127 441 -367 749q-12 16 -3 33.5t29 17.5h373zM1000 507q-49 -199 -125 -393q-79 310 -256 594q40 221 44 449q211 -340 337 -650zM1099 1216q235 -324 384.5 -698.5t184.5 -773.5h-451q-41 665 -553 1472h435zM1792 640 +q0 -424 -101 -812q-67 560 -359 1083q-25 301 -106 584q-4 16 5.5 28.5t25.5 12.5h359q21 0 38.5 -13t22.5 -33q115 -409 115 -850z" /> + <glyph glyph-name="uniF1F0" unicode="&#xf1f0;" horiz-adv-x="2304" +d="M1975 546h-138q14 37 66 179l3 9q4 10 10 26t9 26l12 -55zM531 611l-58 295q-11 54 -75 54h-268l-2 -13q311 -79 403 -336zM710 960l-162 -438l-17 89q-26 70 -85 129.5t-131 88.5l135 -510h175l261 641h-176zM849 318h166l104 642h-166zM1617 944q-69 27 -149 27 +q-123 0 -201 -59t-79 -153q-1 -102 145 -174q48 -23 67 -41t19 -39q0 -30 -30 -46t-69 -16q-86 0 -156 33l-22 11l-23 -144q74 -34 185 -34q130 -1 208.5 59t80.5 160q0 106 -140 174q-49 25 -71 42t-22 38q0 22 24.5 38.5t70.5 16.5q70 1 124 -24l15 -8zM2042 960h-128 +q-65 0 -87 -54l-246 -588h174l35 96h212q5 -22 20 -96h154zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" /> + <glyph glyph-name="_466" unicode="&#xf1f1;" horiz-adv-x="2304" +d="M1119 1195q-128 85 -281 85q-103 0 -197.5 -40.5t-162.5 -108.5t-108.5 -162t-40.5 -197q0 -104 40.5 -198t108.5 -162t162 -108.5t198 -40.5q153 0 281 85q-131 107 -178 265.5t0.5 316.5t177.5 265zM1152 1171q-126 -99 -172 -249.5t-0.5 -300.5t172.5 -249 +q127 99 172.5 249t-0.5 300.5t-172 249.5zM1185 1195q130 -107 177.5 -265.5t0.5 -317t-178 -264.5q128 -85 281 -85q104 0 198 40.5t162 108.5t108.5 162t40.5 198q0 103 -40.5 197t-108.5 162t-162.5 108.5t-197.5 40.5q-153 0 -281 -85zM1926 473h7v3h-17v-3h7v-17h3v17z +M1955 456h4v20h-5l-6 -13l-6 13h-5v-20h3v15l6 -13h4l5 13v-15zM1947 16v-2h-2h-3v3h3h2v-1zM1947 7h3l-4 5h2l1 1q1 1 1 3t-1 3l-1 1h-3h-6v-13h3v5h1zM685 75q0 19 11 31t30 12q18 0 29 -12.5t11 -30.5q0 -19 -11 -31t-29 -12q-19 0 -30 12t-11 31zM1158 119q30 0 35 -32 +h-70q5 32 35 32zM1514 75q0 19 11 31t29 12t29.5 -12.5t11.5 -30.5q0 -19 -11 -31t-30 -12q-18 0 -29 12t-11 31zM1786 75q0 18 11.5 30.5t29.5 12.5t29.5 -12.5t11.5 -30.5q0 -19 -11.5 -31t-29.5 -12t-29.5 12.5t-11.5 30.5zM1944 3q-2 0 -4 1q-1 0 -3 2t-2 3q-1 2 -1 4 +q0 3 1 4q0 2 2 4l1 1q2 0 2 1q2 1 4 1q3 0 4 -1l4 -2l2 -4v-1q1 -2 1 -3l-1 -1v-3t-1 -1l-1 -2q-2 -2 -4 -2q-1 -1 -4 -1zM599 7h30v85q0 24 -14.5 38.5t-39.5 15.5q-32 0 -47 -24q-14 24 -45 24q-24 0 -39 -20v16h-30v-135h30v75q0 36 33 36q30 0 30 -36v-75h29v75 +q0 36 33 36q30 0 30 -36v-75zM765 7h29v68v67h-29v-16q-17 20 -43 20q-29 0 -48 -20t-19 -51t19 -51t48 -20q28 0 43 20v-17zM943 48q0 34 -47 40l-14 2q-23 4 -23 14q0 15 25 15q23 0 43 -11l12 24q-22 14 -55 14q-26 0 -41 -12t-15 -32q0 -33 47 -39l13 -2q24 -4 24 -14 +q0 -17 -31 -17q-25 0 -45 14l-13 -23q25 -17 58 -17q29 0 45.5 12t16.5 32zM1073 14l-8 25q-13 -7 -26 -7q-19 0 -19 22v61h48v27h-48v41h-30v-41h-28v-27h28v-61q0 -50 47 -50q21 0 36 10zM1159 146q-29 0 -48 -20t-19 -51q0 -32 19.5 -51.5t49.5 -19.5q33 0 55 19l-14 22 +q-18 -15 -39 -15q-34 0 -41 33h101v12q0 32 -18 51.5t-46 19.5zM1318 146q-23 0 -35 -20v16h-30v-135h30v76q0 35 29 35q10 0 18 -4l9 28q-9 4 -21 4zM1348 75q0 -31 19.5 -51t52.5 -20q29 0 48 16l-14 24q-18 -13 -35 -12q-18 0 -29.5 12t-11.5 31t11.5 31t29.5 12 +q19 0 35 -12l14 24q-20 16 -48 16q-33 0 -52.5 -20t-19.5 -51zM1593 7h30v68v67h-30v-16q-15 20 -42 20q-29 0 -48.5 -20t-19.5 -51t19.5 -51t48.5 -20q28 0 42 20v-17zM1726 146q-23 0 -35 -20v16h-29v-135h29v76q0 35 29 35q10 0 18 -4l9 28q-8 4 -21 4zM1866 7h29v68v122 +h-29v-71q-15 20 -43 20t-47.5 -20.5t-19.5 -50.5t19.5 -50.5t47.5 -20.5q29 0 43 20v-17zM1944 27l-2 -1h-3q-2 -1 -4 -3q-3 -1 -3 -4q-1 -2 -1 -6q0 -3 1 -5q0 -2 3 -4q2 -2 4 -3t5 -1q4 0 6 1q0 1 2 2l2 1q1 1 3 4q1 2 1 5q0 4 -1 6q-1 1 -3 4q0 1 -2 2l-2 1q-1 0 -3 0.5 +t-3 0.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" /> + <glyph glyph-name="_467" unicode="&#xf1f2;" horiz-adv-x="2304" +d="M313 759q0 -51 -36 -84q-29 -26 -89 -26h-17v220h17q61 0 89 -27q36 -31 36 -83zM2089 824q0 -52 -64 -52h-19v101h20q63 0 63 -49zM380 759q0 74 -50 120.5t-129 46.5h-95v-333h95q74 0 119 38q60 51 60 128zM410 593h65v333h-65v-333zM730 694q0 40 -20.5 62t-75.5 42 +q-29 10 -39.5 19t-10.5 23q0 16 13.5 26.5t34.5 10.5q29 0 53 -27l34 44q-41 37 -98 37q-44 0 -74 -27.5t-30 -67.5q0 -35 18 -55.5t64 -36.5q37 -13 45 -19q19 -12 19 -34q0 -20 -14 -33.5t-36 -13.5q-48 0 -71 44l-42 -40q44 -64 115 -64q51 0 83 30.5t32 79.5zM1008 604 +v77q-37 -37 -78 -37q-49 0 -80.5 32.5t-31.5 82.5q0 48 31.5 81.5t77.5 33.5q43 0 81 -38v77q-40 20 -80 20q-74 0 -125.5 -50.5t-51.5 -123.5t51 -123.5t125 -50.5q42 0 81 19zM2240 0v527q-65 -40 -144.5 -84t-237.5 -117t-329.5 -137.5t-417.5 -134.5t-504 -118h1569 +q26 0 45 19t19 45zM1389 757q0 75 -53 128t-128 53t-128 -53t-53 -128t53 -128t128 -53t128 53t53 128zM1541 584l144 342h-71l-90 -224l-89 224h-71l142 -342h35zM1714 593h184v56h-119v90h115v56h-115v74h119v57h-184v-333zM2105 593h80l-105 140q76 16 76 94q0 47 -31 73 +t-87 26h-97v-333h65v133h9zM2304 1274v-1268q0 -56 -38.5 -95t-93.5 -39h-2040q-55 0 -93.5 39t-38.5 95v1268q0 56 38.5 95t93.5 39h2040q55 0 93.5 -39t38.5 -95z" /> + <glyph glyph-name="f1f3" unicode="&#xf1f3;" horiz-adv-x="2304" +d="M119 854h89l-45 108zM740 328l74 79l-70 79h-163v-49h142v-55h-142v-54h159zM898 406l99 -110v217zM1186 453q0 33 -40 33h-84v-69h83q41 0 41 36zM1475 457q0 29 -42 29h-82v-61h81q43 0 43 32zM1197 923q0 29 -42 29h-82v-60h81q43 0 43 31zM1656 854h89l-44 108z +M699 1009v-271h-66v212l-94 -212h-57l-94 212v-212h-132l-25 60h-135l-25 -60h-70l116 271h96l110 -257v257h106l85 -184l77 184h108zM1255 453q0 -20 -5.5 -35t-14 -25t-22.5 -16.5t-26 -10t-31.5 -4.5t-31.5 -1t-32.5 0.5t-29.5 0.5v-91h-126l-80 90l-83 -90h-256v271h260 +l80 -89l82 89h207q109 0 109 -89zM964 794v-56h-217v271h217v-57h-152v-49h148v-55h-148v-54h152zM2304 235v-229q0 -55 -38.5 -94.5t-93.5 -39.5h-2040q-55 0 -93.5 39.5t-38.5 94.5v678h111l25 61h55l25 -61h218v46l19 -46h113l20 47v-47h541v99l10 1q10 0 10 -14v-86h279 +v23q23 -12 55 -18t52.5 -6.5t63 0.5t51.5 1l25 61h56l25 -61h227v58l34 -58h182v378h-180v-44l-25 44h-185v-44l-23 44h-249q-69 0 -109 -22v22h-172v-22q-24 22 -73 22h-628l-43 -97l-43 97h-198v-44l-22 44h-169l-78 -179v391q0 55 38.5 94.5t93.5 39.5h2040 +q55 0 93.5 -39.5t38.5 -94.5v-678h-120q-51 0 -81 -22v22h-177q-55 0 -78 -22v22h-316v-22q-31 22 -87 22h-209v-22q-23 22 -91 22h-234l-54 -58l-50 58h-349v-378h343l55 59l52 -59h211v89h21q59 0 90 13v-102h174v99h8q8 0 10 -2t2 -10v-87h529q57 0 88 24v-24h168 +q60 0 95 17zM1546 469q0 -23 -12 -43t-34 -29q25 -9 34 -26t9 -46v-54h-65v45q0 33 -12 43.5t-46 10.5h-69v-99h-65v271h154q48 0 77 -15t29 -58zM1269 936q0 -24 -12.5 -44t-33.5 -29q26 -9 34.5 -25.5t8.5 -46.5v-53h-65q0 9 0.5 26.5t0 25t-3 18.5t-8.5 16t-17.5 8.5 +t-29.5 3.5h-70v-98h-64v271l153 -1q49 0 78 -14.5t29 -57.5zM1798 327v-56h-216v271h216v-56h-151v-49h148v-55h-148v-54zM1372 1009v-271h-66v271h66zM2065 357q0 -86 -102 -86h-126v58h126q34 0 34 25q0 16 -17 21t-41.5 5t-49.5 3.5t-42 22.5t-17 55q0 39 26 60t66 21 +h130v-57h-119q-36 0 -36 -25q0 -16 17.5 -20.5t42 -4t49 -2.5t42 -21.5t17.5 -54.5zM2304 407v-101q-24 -35 -88 -35h-125v58h125q33 0 33 25q0 13 -12.5 19t-31 5.5t-40 2t-40 8t-31 24t-12.5 48.5q0 39 26.5 60t66.5 21h129v-57h-118q-36 0 -36 -25q0 -20 29 -22t68.5 -5 +t56.5 -26zM2139 1008v-270h-92l-122 203v-203h-132l-26 60h-134l-25 -60h-75q-129 0 -129 133q0 138 133 138h63v-59q-7 0 -28 1t-28.5 0.5t-23 -2t-21.5 -6.5t-14.5 -13.5t-11.5 -23t-3 -33.5q0 -38 13.5 -58t49.5 -20h29l92 213h97l109 -256v256h99l114 -188v188h66z" /> + <glyph glyph-name="_469" unicode="&#xf1f4;" horiz-adv-x="2304" +d="M745 630q0 -37 -25.5 -61.5t-62.5 -24.5q-29 0 -46.5 16t-17.5 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM1530 779q0 -42 -22 -57t-66 -15l-32 -1l17 107q2 11 13 11h18q22 0 35 -2t25 -12.5t12 -30.5zM1881 630q0 -36 -25.5 -61t-61.5 -25q-29 0 -47 16 +t-18 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM513 801q0 59 -38.5 85.5t-100.5 26.5h-160q-19 0 -21 -19l-65 -408q-1 -6 3 -11t10 -5h76q20 0 22 19l18 110q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM822 489l41 261q1 6 -3 11t-10 5h-76 +q-14 0 -17 -33q-27 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q28 0 58 12t48 32q-4 -12 -4 -21q0 -16 13 -16h69q19 0 22 19zM1269 752q0 5 -4 9.5t-9 4.5h-77q-11 0 -18 -10l-106 -156l-44 150q-5 16 -22 16h-75q-5 0 -9 -4.5t-4 -9.5q0 -2 19.5 -59 +t42 -123t23.5 -70q-82 -112 -82 -120q0 -13 13 -13h77q11 0 18 10l255 368q2 2 2 7zM1649 801q0 59 -38.5 85.5t-100.5 26.5h-159q-20 0 -22 -19l-65 -408q-1 -6 3 -11t10 -5h82q12 0 16 13l18 116q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM1958 489 +l41 261q1 6 -3 11t-10 5h-76q-14 0 -17 -33q-26 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q29 0 59 12t47 32q0 -1 -2 -9t-2 -12q0 -16 13 -16h69q19 0 22 19zM2176 898v1q0 14 -13 14h-74q-11 0 -13 -11l-65 -416l-1 -2q0 -5 4 -9.5t10 -4.5h66 +q19 0 21 19zM392 764q-5 -35 -26 -46t-60 -11l-33 -1l17 107q2 11 13 11h19q40 0 58 -11.5t12 -48.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" /> + <glyph glyph-name="_470" unicode="&#xf1f5;" horiz-adv-x="2304" +d="M1597 633q0 -69 -21 -106q-19 -35 -52 -35q-23 0 -41 9v224q29 30 57 30q57 0 57 -122zM2035 669h-110q6 98 56 98q51 0 54 -98zM476 534q0 59 -33 91.5t-101 57.5q-36 13 -52 24t-16 25q0 26 38 26q58 0 124 -33l18 112q-67 32 -149 32q-77 0 -123 -38q-48 -39 -48 -109 +q0 -58 32.5 -90.5t99.5 -56.5q39 -14 54.5 -25.5t15.5 -27.5q0 -31 -48 -31q-29 0 -70 12.5t-72 30.5l-18 -113q72 -41 168 -41q81 0 129 37q51 41 51 117zM771 749l19 111h-96v135l-129 -21l-18 -114l-46 -8l-17 -103h62v-219q0 -84 44 -120q38 -30 111 -30q32 0 79 11v118 +q-32 -7 -44 -7q-42 0 -42 50v197h77zM1087 724v139q-15 3 -28 3q-32 0 -55.5 -16t-33.5 -46l-10 56h-131v-471h150v306q26 31 82 31q16 0 26 -2zM1124 389h150v471h-150v-471zM1746 638q0 122 -45 179q-40 52 -111 52q-64 0 -117 -56l-8 47h-132v-645l150 25v151 +q36 -11 68 -11q83 0 134 56q61 65 61 202zM1278 986q0 33 -23 56t-56 23t-56 -23t-23 -56t23 -56.5t56 -23.5t56 23.5t23 56.5zM2176 629q0 113 -48 176q-50 64 -144 64q-96 0 -151.5 -66t-55.5 -180q0 -128 63 -188q55 -55 161 -55q101 0 160 40l-16 103q-57 -31 -128 -31 +q-43 0 -63 19q-23 19 -28 66h248q2 14 2 52zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" /> + <glyph glyph-name="_471" unicode="&#xf1f6;" horiz-adv-x="2048" +d="M1558 684q61 -356 298 -556q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5zM1024 -176q16 0 16 16t-16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5zM2026 1424q8 -10 7.5 -23.5t-10.5 -22.5 +l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5 +l418 363q10 8 23.5 7t21.5 -11z" /> + <glyph glyph-name="_472" unicode="&#xf1f7;" horiz-adv-x="2048" +d="M1040 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM503 315l877 760q-42 88 -132.5 146.5t-223.5 58.5q-93 0 -169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -384 -137 -645zM1856 128 +q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5l149 129h757q-166 187 -227 459l111 97q61 -356 298 -556zM1942 1520l84 -96q8 -10 7.5 -23.5t-10.5 -22.5l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161 +q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5l418 363q10 8 23.5 7t21.5 -11z" /> + <glyph glyph-name="_473" unicode="&#xf1f8;" horiz-adv-x="1408" +d="M512 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM768 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1024 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704 +q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167 +q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" /> + <glyph glyph-name="_474" unicode="&#xf1f9;" +d="M1150 462v-109q0 -50 -36.5 -89t-94 -60.5t-118 -32.5t-117.5 -11q-205 0 -342.5 139t-137.5 346q0 203 136 339t339 136q34 0 75.5 -4.5t93 -18t92.5 -34t69 -56.5t28 -81v-109q0 -16 -16 -16h-118q-16 0 -16 16v70q0 43 -65.5 67.5t-137.5 24.5q-140 0 -228.5 -91.5 +t-88.5 -237.5q0 -151 91.5 -249.5t233.5 -98.5q68 0 138 24t70 66v70q0 7 4.5 11.5t10.5 4.5h119q6 0 11 -4.5t5 -11.5zM768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5 +t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="_475" unicode="&#xf1fa;" +d="M972 761q0 108 -53.5 169t-147.5 61q-63 0 -124 -30.5t-110 -84.5t-79.5 -137t-30.5 -180q0 -112 53.5 -173t150.5 -61q96 0 176 66.5t122.5 166t42.5 203.5zM1536 640q0 -111 -37 -197t-98.5 -135t-131.5 -74.5t-145 -27.5q-6 0 -15.5 -0.5t-16.5 -0.5q-95 0 -142 53 +q-28 33 -33 83q-52 -66 -131.5 -110t-173.5 -44q-161 0 -249.5 95.5t-88.5 269.5q0 157 66 290t179 210.5t246 77.5q87 0 155 -35.5t106 -99.5l2 19l11 56q1 6 5.5 12t9.5 6h118q5 0 13 -11q5 -5 3 -16l-120 -614q-5 -24 -5 -48q0 -39 12.5 -52t44.5 -13q28 1 57 5.5t73 24 +t77 50t57 89.5t24 137q0 292 -174 466t-466 174q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51q228 0 405 144q11 9 24 8t21 -12l41 -49q8 -12 7 -24q-2 -13 -12 -22q-102 -83 -227.5 -128t-258.5 -45q-156 0 -298 61 +t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q344 0 556 -212t212 -556z" /> + <glyph glyph-name="_476" unicode="&#xf1fb;" horiz-adv-x="1792" +d="M1698 1442q94 -94 94 -226.5t-94 -225.5l-225 -223l104 -104q10 -10 10 -23t-10 -23l-210 -210q-10 -10 -23 -10t-23 10l-105 105l-603 -603q-37 -37 -90 -37h-203l-256 -128l-64 64l128 256v203q0 53 37 90l603 603l-105 105q-10 10 -10 23t10 23l210 210q10 10 23 10 +t23 -10l104 -104l223 225q93 94 225.5 94t226.5 -94zM512 64l576 576l-192 192l-576 -576v-192h192z" /> + <glyph glyph-name="f1fc" unicode="&#xf1fc;" horiz-adv-x="1792" +d="M1615 1536q70 0 122.5 -46.5t52.5 -116.5q0 -63 -45 -151q-332 -629 -465 -752q-97 -91 -218 -91q-126 0 -216.5 92.5t-90.5 219.5q0 128 92 212l638 579q59 54 130 54zM706 502q39 -76 106.5 -130t150.5 -76l1 -71q4 -213 -129.5 -347t-348.5 -134q-123 0 -218 46.5 +t-152.5 127.5t-86.5 183t-29 220q7 -5 41 -30t62 -44.5t59 -36.5t46 -17q41 0 55 37q25 66 57.5 112.5t69.5 76t88 47.5t103 25.5t125 10.5z" /> + <glyph glyph-name="_478" unicode="&#xf1fd;" horiz-adv-x="1792" +d="M1792 128v-384h-1792v384q45 0 85 14t59 27.5t47 37.5q30 27 51.5 38t56.5 11q24 0 44 -7t31 -15t33 -27q29 -25 47 -38t58 -27t86 -14q45 0 85 14.5t58 27t48 37.5q21 19 32.5 27t31 15t43.5 7q35 0 56.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14t85 14t59 27.5 +t47 37.5q30 27 51.5 38t56.5 11q34 0 55.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14zM1792 448v-192q-24 0 -44 7t-31 15t-33 27q-29 25 -47 38t-58 27t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-22 -19 -33 -27t-31 -15t-44 -7q-35 0 -56.5 11t-51.5 38q-29 25 -47 38 +t-58 27t-86 14q-45 0 -85 -14.5t-58 -27t-48 -37.5q-21 -19 -32.5 -27t-31 -15t-43.5 -7q-35 0 -56.5 11t-51.5 38q-28 24 -47 37.5t-59 27.5t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-30 -27 -51.5 -38t-56.5 -11v192q0 80 56 136t136 56h64v448h256v-448h256v448h256v-448 +h256v448h256v-448h64q80 0 136 -56t56 -136zM512 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1024 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5 +q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1536 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150z" /> + <glyph glyph-name="_479" unicode="&#xf1fe;" horiz-adv-x="2048" +d="M2048 0v-128h-2048v1536h128v-1408h1920zM1664 1024l256 -896h-1664v576l448 576l576 -576z" /> + <glyph glyph-name="_480" unicode="&#xf200;" horiz-adv-x="1792" +d="M768 646l546 -546q-106 -108 -247.5 -168t-298.5 -60q-209 0 -385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103v-762zM955 640h773q0 -157 -60 -298.5t-168 -247.5zM1664 768h-768v768q209 0 385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="_481" unicode="&#xf201;" horiz-adv-x="2048" +d="M2048 0v-128h-2048v1536h128v-1408h1920zM1920 1248v-435q0 -21 -19.5 -29.5t-35.5 7.5l-121 121l-633 -633q-10 -10 -23 -10t-23 10l-233 233l-416 -416l-192 192l585 585q10 10 23 10t23 -10l233 -233l464 464l-121 121q-16 16 -7.5 35.5t29.5 19.5h435q14 0 23 -9 +t9 -23z" /> + <glyph glyph-name="_482" unicode="&#xf202;" horiz-adv-x="1792" +d="M1292 832q0 -6 10 -41q10 -29 25 -49.5t41 -34t44 -20t55 -16.5q325 -91 325 -332q0 -146 -105.5 -242.5t-254.5 -96.5q-59 0 -111.5 18.5t-91.5 45.5t-77 74.5t-63 87.5t-53.5 103.5t-43.5 103t-39.5 106.5t-35.5 95q-32 81 -61.5 133.5t-73.5 96.5t-104 64t-142 20 +q-96 0 -183 -55.5t-138 -144.5t-51 -185q0 -160 106.5 -279.5t263.5 -119.5q177 0 258 95q56 63 83 116l84 -152q-15 -34 -44 -70l1 -1q-131 -152 -388 -152q-147 0 -269.5 79t-190.5 207.5t-68 274.5q0 105 43.5 206t116 176.5t172 121.5t204.5 46q87 0 159 -19t123.5 -50 +t95 -80t72.5 -99t58.5 -117t50.5 -124.5t50 -130.5t55 -127q96 -200 233 -200q81 0 138.5 48.5t57.5 128.5q0 42 -19 72t-50.5 46t-72.5 31.5t-84.5 27t-87.5 34t-81 52t-65 82t-39 122.5q-3 16 -3 33q0 110 87.5 192t198.5 78q78 -3 120.5 -14.5t90.5 -53.5h-1 +q12 -11 23 -24.5t26 -36t19 -27.5l-129 -99q-26 49 -54 70v1q-23 21 -97 21q-49 0 -84 -33t-35 -83z" /> + <glyph glyph-name="_483" unicode="&#xf203;" +d="M1432 484q0 173 -234 239q-35 10 -53 16.5t-38 25t-29 46.5q0 2 -2 8.5t-3 12t-1 7.5q0 36 24.5 59.5t60.5 23.5q54 0 71 -15h-1q20 -15 39 -51l93 71q-39 54 -49 64q-33 29 -67.5 39t-85.5 10q-80 0 -142 -57.5t-62 -137.5q0 -7 2 -23q16 -96 64.5 -140t148.5 -73 +q29 -8 49 -15.5t45 -21.5t38.5 -34.5t13.5 -46.5v-5q1 -58 -40.5 -93t-100.5 -35q-97 0 -167 144q-23 47 -51.5 121.5t-48 125.5t-54 110.5t-74 95.5t-103.5 60.5t-147 24.5q-101 0 -192 -56t-144 -148t-50 -192v-1q4 -108 50.5 -199t133.5 -147.5t196 -56.5q186 0 279 110 +q20 27 31 51l-60 109q-42 -80 -99 -116t-146 -36q-115 0 -191 87t-76 204q0 105 82 189t186 84q112 0 170 -53.5t104 -172.5q8 -21 25.5 -68.5t28.5 -76.5t31.5 -74.5t38.5 -74t45.5 -62.5t55.5 -53.5t66 -33t80 -13.5q107 0 183 69.5t76 174.5zM1536 1120v-960 +q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="_484" unicode="&#xf204;" horiz-adv-x="2048" +d="M1152 640q0 104 -40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM1920 640q0 104 -40.5 198.5 +t-109.5 163.5t-163.5 109.5t-198.5 40.5h-386q119 -90 188.5 -224t69.5 -288t-69.5 -288t-188.5 -224h386q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM2048 640q0 -130 -51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5 +t-136.5 204t-51 248.5t51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5z" /> + <glyph glyph-name="_485" unicode="&#xf205;" horiz-adv-x="2048" +d="M0 640q0 130 51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5t-51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5t-136.5 204t-51 248.5zM1408 128q104 0 198.5 40.5t163.5 109.5 +t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5z" /> + <glyph glyph-name="_486" unicode="&#xf206;" horiz-adv-x="2304" +d="M762 384h-314q-40 0 -57.5 35t6.5 67l188 251q-65 31 -137 31q-132 0 -226 -94t-94 -226t94 -226t226 -94q115 0 203 72.5t111 183.5zM576 512h186q-18 85 -75 148zM1056 512l288 384h-480l-99 -132q105 -103 126 -252h165zM2176 448q0 132 -94 226t-226 94 +q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94t226 94t94 226zM2304 448q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 97 39.5 183.5t109.5 149.5l-65 98l-353 -469 +q-18 -26 -51 -26h-197q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q114 0 215 -55l137 183h-224q-26 0 -45 19t-19 45t19 45t45 19h384v-128h435l-85 128h-222q-26 0 -45 19t-19 45t19 45t45 19h256q33 0 53 -28l267 -400 +q91 44 192 44q185 0 316.5 -131.5t131.5 -316.5z" /> + <glyph glyph-name="_487" unicode="&#xf207;" +d="M384 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1362 716l-72 384q-5 23 -22.5 37.5t-40.5 14.5 +h-918q-23 0 -40.5 -14.5t-22.5 -37.5l-72 -384q-5 -30 14 -53t49 -23h1062q30 0 49 23t14 53zM1136 1328q0 20 -14 34t-34 14h-640q-20 0 -34 -14t-14 -34t14 -34t34 -14h640q20 0 34 14t14 34zM1536 603v-603h-128v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 +t-37.5 90.5v128h-768v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5v128h-128v603q0 112 25 223l103 454q9 78 97.5 137t230 89t312.5 30t312.5 -30t230 -89t97.5 -137l105 -454q23 -102 23 -223z" /> + <glyph glyph-name="_488" unicode="&#xf208;" horiz-adv-x="2048" +d="M1463 704q0 -35 -25 -60.5t-61 -25.5h-702q-36 0 -61 25.5t-25 60.5t25 60.5t61 25.5h702q36 0 61 -25.5t25 -60.5zM1677 704q0 86 -23 170h-982q-36 0 -61 25t-25 60q0 36 25 61t61 25h908q-88 143 -235 227t-320 84q-177 0 -327.5 -87.5t-238 -237.5t-87.5 -327 +q0 -86 23 -170h982q36 0 61 -25t25 -60q0 -36 -25 -61t-61 -25h-908q88 -143 235.5 -227t320.5 -84q132 0 253 51.5t208 139t139 208t52 253.5zM2048 959q0 -35 -25 -60t-61 -25h-131q17 -85 17 -170q0 -167 -65.5 -319.5t-175.5 -263t-262.5 -176t-319.5 -65.5 +q-246 0 -448.5 133t-301.5 350h-189q-36 0 -61 25t-25 61q0 35 25 60t61 25h132q-17 85 -17 170q0 167 65.5 319.5t175.5 263t262.5 176t320.5 65.5q245 0 447.5 -133t301.5 -350h188q36 0 61 -25t25 -61z" /> + <glyph glyph-name="_489" unicode="&#xf209;" horiz-adv-x="1280" +d="M953 1158l-114 -328l117 -21q165 451 165 518q0 56 -38 56q-57 0 -130 -225zM654 471l33 -88q37 42 71 67l-33 5.5t-38.5 7t-32.5 8.5zM362 1367q0 -98 159 -521q17 10 49 10q15 0 75 -5l-121 351q-75 220 -123 220q-19 0 -29 -17.5t-10 -37.5zM283 608q0 -36 51.5 -119 +t117.5 -153t100 -70q14 0 25.5 13t11.5 27q0 24 -32 102q-13 32 -32 72t-47.5 89t-61.5 81t-62 32q-20 0 -45.5 -27t-25.5 -47zM125 273q0 -41 25 -104q59 -145 183.5 -227t281.5 -82q227 0 382 170q152 169 152 427q0 43 -1 67t-11.5 62t-30.5 56q-56 49 -211.5 75.5 +t-270.5 26.5q-37 0 -49 -11q-12 -5 -12 -35q0 -34 21.5 -60t55.5 -40t77.5 -23.5t87.5 -11.5t85 -4t70 0h23q24 0 40 -19q15 -19 19 -55q-28 -28 -96 -54q-61 -22 -93 -46q-64 -46 -108.5 -114t-44.5 -137q0 -31 18.5 -88.5t18.5 -87.5l-3 -12q-4 -12 -4 -14 +q-137 10 -146 216q-8 -2 -41 -2q2 -7 2 -21q0 -53 -40.5 -89.5t-94.5 -36.5q-82 0 -166.5 78t-84.5 159q0 34 33 67q52 -64 60 -76q77 -104 133 -104q12 0 26.5 8.5t14.5 20.5q0 34 -87.5 145t-116.5 111q-43 0 -70 -44.5t-27 -90.5zM11 264q0 101 42.5 163t136.5 88 +q-28 74 -28 104q0 62 61 123t122 61q29 0 70 -15q-163 462 -163 567q0 80 41 130.5t119 50.5q131 0 325 -581q6 -17 8 -23q6 16 29 79.5t43.5 118.5t54 127.5t64.5 123t70.5 86.5t76.5 36q71 0 112 -49t41 -122q0 -108 -159 -550q61 -15 100.5 -46t58.5 -78t26 -93.5 +t7 -110.5q0 -150 -47 -280t-132 -225t-211 -150t-278 -55q-111 0 -223 42q-149 57 -258 191.5t-109 286.5z" /> + <glyph glyph-name="_490" unicode="&#xf20a;" horiz-adv-x="2048" +d="M785 528h207q-14 -158 -98.5 -248.5t-214.5 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-203q-5 64 -35.5 99t-81.5 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t40 -51.5t66 -18q95 0 109 139zM1497 528h206 +q-14 -158 -98 -248.5t-214 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-204q-4 64 -35 99t-81 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t39.5 -51.5t65.5 -18q49 0 76.5 38t33.5 101zM1856 647q0 207 -15.5 307 +t-60.5 161q-6 8 -13.5 14t-21.5 15t-16 11q-86 63 -697 63q-625 0 -710 -63q-5 -4 -17.5 -11.5t-21 -14t-14.5 -14.5q-45 -60 -60 -159.5t-15 -308.5q0 -208 15 -307.5t60 -160.5q6 -8 15 -15t20.5 -14t17.5 -12q44 -33 239.5 -49t470.5 -16q610 0 697 65q5 4 17 11t20.5 14 +t13.5 16q46 60 61 159t15 309zM2048 1408v-1536h-2048v1536h2048z" /> + <glyph glyph-name="_491" unicode="&#xf20b;" +d="M992 912v-496q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v496q0 112 -80 192t-192 80h-272v-1152q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v1344q0 14 9 23t23 9h464q135 0 249 -66.5t180.5 -180.5t66.5 -249zM1376 1376v-880q0 -135 -66.5 -249t-180.5 -180.5 +t-249 -66.5h-464q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h160q14 0 23 -9t9 -23v-768h272q112 0 192 80t80 192v880q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" /> + <glyph glyph-name="_492" unicode="&#xf20c;" +d="M1311 694v-114q0 -24 -13.5 -38t-37.5 -14h-202q-24 0 -38 14t-14 38v114q0 24 14 38t38 14h202q24 0 37.5 -14t13.5 -38zM821 464v250q0 53 -32.5 85.5t-85.5 32.5h-133q-68 0 -96 -52q-28 52 -96 52h-130q-53 0 -85.5 -32.5t-32.5 -85.5v-250q0 -22 21 -22h55 +q22 0 22 22v230q0 24 13.5 38t38.5 14h94q24 0 38 -14t14 -38v-230q0 -22 21 -22h54q22 0 22 22v230q0 24 14 38t38 14h97q24 0 37.5 -14t13.5 -38v-230q0 -22 22 -22h55q21 0 21 22zM1410 560v154q0 53 -33 85.5t-86 32.5h-264q-53 0 -86 -32.5t-33 -85.5v-410 +q0 -21 22 -21h55q21 0 21 21v180q31 -42 94 -42h191q53 0 86 32.5t33 85.5zM1536 1176v-1072q0 -96 -68 -164t-164 -68h-1072q-96 0 -164 68t-68 164v1072q0 96 68 164t164 68h1072q96 0 164 -68t68 -164z" /> + <glyph glyph-name="_493" unicode="&#xf20d;" +d="M915 450h-294l147 551zM1001 128h311l-324 1024h-440l-324 -1024h311l383 314zM1536 1120v-960q0 -118 -85 -203t-203 -85h-960q-118 0 -203 85t-85 203v960q0 118 85 203t203 85h960q118 0 203 -85t85 -203z" /> + <glyph glyph-name="_494" unicode="&#xf20e;" horiz-adv-x="2048" +d="M2048 641q0 -21 -13 -36.5t-33 -19.5l-205 -356q3 -9 3 -18q0 -20 -12.5 -35.5t-32.5 -19.5l-193 -337q3 -8 3 -16q0 -23 -16.5 -40t-40.5 -17q-25 0 -41 18h-400q-17 -20 -43 -20t-43 20h-399q-17 -20 -43 -20q-23 0 -40 16.5t-17 40.5q0 8 4 20l-193 335 +q-20 4 -32.5 19.5t-12.5 35.5q0 9 3 18l-206 356q-20 5 -32.5 20.5t-12.5 35.5q0 21 13.5 36.5t33.5 19.5l199 344q0 1 -0.5 3t-0.5 3q0 36 34 51l209 363q-4 10 -4 18q0 24 17 40.5t40 16.5q26 0 44 -21h396q16 21 43 21t43 -21h398q18 21 44 21q23 0 40 -16.5t17 -40.5 +q0 -6 -4 -18l207 -358q23 -1 39 -17.5t16 -38.5q0 -13 -7 -27l187 -324q19 -4 31.5 -19.5t12.5 -35.5zM1063 -158h389l-342 354h-143l-342 -354h360q18 16 39 16t39 -16zM112 654q1 -4 1 -13q0 -10 -2 -15l208 -360l15 -6l188 199v347l-187 194q-13 -8 -29 -10zM986 1438 +h-388l190 -200l554 200h-280q-16 -16 -38 -16t-38 16zM1689 226q1 6 5 11l-64 68l-17 -79h76zM1583 226l22 105l-252 266l-296 -307l63 -64h463zM1495 -142l16 28l65 310h-427l333 -343q8 4 13 5zM578 -158h5l342 354h-373v-335l4 -6q14 -5 22 -13zM552 226h402l64 66 +l-309 321l-157 -166v-221zM359 226h163v189l-168 -177q4 -8 5 -12zM358 1051q0 -1 0.5 -2t0.5 -2q0 -16 -8 -29l171 -177v269zM552 1121v-311l153 -157l297 314l-223 236zM556 1425l-4 -8v-264l205 74l-191 201q-6 -2 -10 -3zM1447 1438h-16l-621 -224l213 -225zM1023 946 +l-297 -315l311 -319l296 307zM688 634l-136 141v-284zM1038 270l-42 -44h85zM1374 618l238 -251l132 624l-3 5l-1 1zM1718 1018q-8 13 -8 29v2l-216 376q-5 1 -13 5l-437 -463l310 -327zM522 1142v223l-163 -282zM522 196h-163l163 -283v283zM1607 196l-48 -227l130 227h-82 +zM1729 266l207 361q-2 10 -2 14q0 1 3 16l-171 296l-129 -612l77 -82q5 3 15 7z" /> + <glyph glyph-name="f210" unicode="&#xf210;" +d="M0 856q0 131 91.5 226.5t222.5 95.5h742l352 358v-1470q0 -132 -91.5 -227t-222.5 -95h-780q-131 0 -222.5 95t-91.5 227v790zM1232 102l-176 180v425q0 46 -32 79t-78 33h-484q-46 0 -78 -33t-32 -79v-492q0 -46 32.5 -79.5t77.5 -33.5h770z" /> + <glyph glyph-name="_496" unicode="&#xf211;" +d="M934 1386q-317 -121 -556 -362.5t-358 -560.5q-20 89 -20 176q0 208 102.5 384.5t278.5 279t384 102.5q82 0 169 -19zM1203 1267q93 -65 164 -155q-389 -113 -674.5 -400.5t-396.5 -676.5q-93 72 -155 162q112 386 395 671t667 399zM470 -67q115 356 379.5 622t619.5 384 +q40 -92 54 -195q-292 -120 -516 -345t-343 -518q-103 14 -194 52zM1536 -125q-193 50 -367 115q-135 -84 -290 -107q109 205 274 370.5t369 275.5q-21 -152 -101 -284q65 -175 115 -370z" /> + <glyph glyph-name="f212" unicode="&#xf212;" horiz-adv-x="2048" +d="M1893 1144l155 -1272q-131 0 -257 57q-200 91 -393 91q-226 0 -374 -148q-148 148 -374 148q-193 0 -393 -91q-128 -57 -252 -57h-5l155 1272q224 127 482 127q233 0 387 -106q154 106 387 106q258 0 482 -127zM1398 157q129 0 232 -28.5t260 -93.5l-124 1021 +q-171 78 -368 78q-224 0 -374 -141q-150 141 -374 141q-197 0 -368 -78l-124 -1021q105 43 165.5 65t148.5 39.5t178 17.5q202 0 374 -108q172 108 374 108zM1438 191l-55 907q-211 -4 -359 -155q-152 155 -374 155q-176 0 -336 -66l-114 -941q124 51 228.5 76t221.5 25 +q209 0 374 -102q172 107 374 102z" /> + <glyph glyph-name="_498" unicode="&#xf213;" horiz-adv-x="2048" +d="M1500 165v733q0 21 -15 36t-35 15h-93q-20 0 -35 -15t-15 -36v-733q0 -20 15 -35t35 -15h93q20 0 35 15t15 35zM1216 165v531q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-531q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM924 165v429q0 20 -15 35t-35 15h-101 +q-20 0 -35 -15t-15 -35v-429q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM632 165v362q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-362q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM2048 311q0 -166 -118 -284t-284 -118h-1244q-166 0 -284 118t-118 284 +q0 116 63 214.5t168 148.5q-10 34 -10 73q0 113 80.5 193.5t193.5 80.5q102 0 180 -67q45 183 194 300t338 117q149 0 275 -73.5t199.5 -199.5t73.5 -275q0 -66 -14 -122q135 -33 221 -142.5t86 -247.5z" /> + <glyph glyph-name="_499" unicode="&#xf214;" +d="M0 1536h1536v-1392l-776 -338l-760 338v1392zM1436 209v926h-1336v-926l661 -294zM1436 1235v201h-1336v-201h1336zM181 937v-115h-37v115h37zM181 789v-115h-37v115h37zM181 641v-115h-37v115h37zM181 493v-115h-37v115h37zM181 345v-115h-37v115h37zM207 202l15 34 +l105 -47l-15 -33zM343 142l15 34l105 -46l-15 -34zM478 82l15 34l105 -46l-15 -34zM614 23l15 33l104 -46l-15 -34zM797 10l105 46l15 -33l-105 -47zM932 70l105 46l15 -34l-105 -46zM1068 130l105 46l15 -34l-105 -46zM1203 189l105 47l15 -34l-105 -46zM259 1389v-36h-114 +v36h114zM421 1389v-36h-115v36h115zM583 1389v-36h-115v36h115zM744 1389v-36h-114v36h114zM906 1389v-36h-114v36h114zM1068 1389v-36h-115v36h115zM1230 1389v-36h-115v36h115zM1391 1389v-36h-114v36h114zM181 1049v-79h-37v115h115v-36h-78zM421 1085v-36h-115v36h115z +M583 1085v-36h-115v36h115zM744 1085v-36h-114v36h114zM906 1085v-36h-114v36h114zM1068 1085v-36h-115v36h115zM1230 1085v-36h-115v36h115zM1355 970v79h-78v36h115v-115h-37zM1355 822v115h37v-115h-37zM1355 674v115h37v-115h-37zM1355 526v115h37v-115h-37zM1355 378 +v115h37v-115h-37zM1355 230v115h37v-115h-37zM760 265q-129 0 -221 91.5t-92 221.5q0 129 92 221t221 92q130 0 221.5 -92t91.5 -221q0 -130 -91.5 -221.5t-221.5 -91.5zM595 646q0 -36 19.5 -56.5t49.5 -25t64 -7t64 -2t49.5 -9t19.5 -30.5q0 -49 -112 -49q-97 0 -123 51 +h-3l-31 -63q67 -42 162 -42q29 0 56.5 5t55.5 16t45.5 33t17.5 53q0 46 -27.5 69.5t-67.5 27t-79.5 3t-67 5t-27.5 25.5q0 21 20.5 33t40.5 15t41 3q34 0 70.5 -11t51.5 -34h3l30 58q-3 1 -21 8.5t-22.5 9t-19.5 7t-22 7t-20 4.5t-24 4t-23 1q-29 0 -56.5 -5t-54 -16.5 +t-43 -34t-16.5 -53.5z" /> + <glyph glyph-name="_500" unicode="&#xf215;" horiz-adv-x="2048" +d="M863 504q0 112 -79.5 191.5t-191.5 79.5t-191 -79.5t-79 -191.5t79 -191t191 -79t191.5 79t79.5 191zM1726 505q0 112 -79 191t-191 79t-191.5 -79t-79.5 -191q0 -113 79.5 -192t191.5 -79t191 79.5t79 191.5zM2048 1314v-1348q0 -44 -31.5 -75.5t-76.5 -31.5h-1832 +q-45 0 -76.5 31.5t-31.5 75.5v1348q0 44 31.5 75.5t76.5 31.5h431q44 0 76 -31.5t32 -75.5v-161h754v161q0 44 32 75.5t76 31.5h431q45 0 76.5 -31.5t31.5 -75.5z" /> + <glyph glyph-name="_501" unicode="&#xf216;" horiz-adv-x="2048" +d="M1430 953zM1690 749q148 0 253 -98.5t105 -244.5q0 -157 -109 -261.5t-267 -104.5q-85 0 -162 27.5t-138 73.5t-118 106t-109 126t-103.5 132.5t-108.5 126.5t-117 106t-136 73.5t-159 27.5q-154 0 -251.5 -91.5t-97.5 -244.5q0 -157 104 -250t263 -93q100 0 208 37.5 +t193 98.5q5 4 21 18.5t30 24t22 9.5q14 0 24.5 -10.5t10.5 -24.5q0 -24 -60 -77q-101 -88 -234.5 -142t-260.5 -54q-133 0 -245.5 58t-180 165t-67.5 241q0 205 141.5 341t347.5 136q120 0 226.5 -43.5t185.5 -113t151.5 -153t139 -167.5t133.5 -153.5t149.5 -113 +t172.5 -43.5q102 0 168.5 61.5t66.5 162.5q0 95 -64.5 159t-159.5 64q-30 0 -81.5 -18.5t-68.5 -18.5q-20 0 -35.5 15t-15.5 35q0 18 8.5 57t8.5 59q0 159 -107.5 263t-266.5 104q-58 0 -111.5 -18.5t-84 -40.5t-55.5 -40.5t-33 -18.5q-15 0 -25.5 10.5t-10.5 25.5 +q0 19 25 46q59 67 147 103.5t182 36.5q191 0 318 -125.5t127 -315.5q0 -37 -4 -66q57 15 115 15z" /> + <glyph glyph-name="_502" unicode="&#xf217;" horiz-adv-x="1664" +d="M1216 832q0 26 -19 45t-45 19h-128v128q0 26 -19 45t-45 19t-45 -19t-19 -45v-128h-128q-26 0 -45 -19t-19 -45t19 -45t45 -19h128v-128q0 -26 19 -45t45 -19t45 19t19 45v128h128q26 0 45 19t19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 +t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 +q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" /> + <glyph glyph-name="_503" unicode="&#xf218;" horiz-adv-x="1664" +d="M1280 832q0 26 -19 45t-45 19t-45 -19l-147 -146v293q0 26 -19 45t-45 19t-45 -19t-19 -45v-293l-147 146q-19 19 -45 19t-45 -19t-19 -45t19 -45l256 -256q19 -19 45 -19t45 19l256 256q19 19 19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 +t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 +q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" /> + <glyph glyph-name="_504" unicode="&#xf219;" horiz-adv-x="2048" +d="M212 768l623 -665l-300 665h-323zM1024 -4l349 772h-698zM538 896l204 384h-262l-288 -384h346zM1213 103l623 665h-323zM683 896h682l-204 384h-274zM1510 896h346l-288 384h-262zM1651 1382l384 -512q14 -18 13 -41.5t-17 -40.5l-960 -1024q-18 -20 -47 -20t-47 20 +l-960 1024q-16 17 -17 40.5t13 41.5l384 512q18 26 51 26h1152q33 0 51 -26z" /> + <glyph glyph-name="_505" unicode="&#xf21a;" horiz-adv-x="2048" +d="M1811 -19q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 +q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83 +q19 19 45 19t45 -19l83 -83zM237 19q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -82l83 82q19 19 45 19t45 -19l83 -82l64 64v293l-210 314q-17 26 -7 56.5t40 40.5l177 58v299h128v128h256v128h256v-128h256v-128h128v-299l177 -58q30 -10 40 -40.5t-7 -56.5l-210 -314 +v-293l19 18q19 19 45 19t45 -19l83 -82l83 82q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 +q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83zM640 1152v-128l384 128l384 -128v128h-128v128h-512v-128h-128z" /> + <glyph glyph-name="_506" unicode="&#xf21b;" +d="M576 0l96 448l-96 128l-128 64zM832 0l128 640l-128 -64l-96 -128zM992 1010q-2 4 -4 6q-10 8 -96 8q-70 0 -167 -19q-7 -2 -21 -2t-21 2q-97 19 -167 19q-86 0 -96 -8q-2 -2 -4 -6q2 -18 4 -27q2 -3 7.5 -6.5t7.5 -10.5q2 -4 7.5 -20.5t7 -20.5t7.5 -17t8.5 -17t9 -14 +t12 -13.5t14 -9.5t17.5 -8t20.5 -4t24.5 -2q36 0 59 12.5t32.5 30t14.5 34.5t11.5 29.5t17.5 12.5h12q11 0 17.5 -12.5t11.5 -29.5t14.5 -34.5t32.5 -30t59 -12.5q13 0 24.5 2t20.5 4t17.5 8t14 9.5t12 13.5t9 14t8.5 17t7.5 17t7 20.5t7.5 20.5q2 7 7.5 10.5t7.5 6.5 +q2 9 4 27zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 61 4.5 118t19 125.5t37.5 123.5t63.5 103.5t93.5 74.5l-90 220h214q-22 64 -22 128q0 12 2 32q-194 40 -194 96q0 57 210 99q17 62 51.5 134t70.5 114q32 37 76 37q30 0 84 -31t84 -31t84 31 +t84 31q44 0 76 -37q36 -42 70.5 -114t51.5 -134q210 -42 210 -99q0 -56 -194 -96q7 -81 -20 -160h214l-82 -225q63 -33 107.5 -96.5t65.5 -143.5t29 -151.5t8 -148.5z" /> + <glyph glyph-name="_507" unicode="&#xf21c;" horiz-adv-x="2304" +d="M2301 500q12 -103 -22 -198.5t-99 -163.5t-158.5 -106t-196.5 -31q-161 11 -279.5 125t-134.5 274q-12 111 27.5 210.5t118.5 170.5l-71 107q-96 -80 -151 -194t-55 -244q0 -27 -18.5 -46.5t-45.5 -19.5h-256h-69q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5 +t-131.5 316.5t131.5 316.5t316.5 131.5q76 0 152 -27l24 45q-123 110 -304 110h-64q-26 0 -45 19t-19 45t19 45t45 19h128q78 0 145 -13.5t116.5 -38.5t71.5 -39.5t51 -36.5h512h115l-85 128h-222q-30 0 -49 22.5t-14 52.5q4 23 23 38t43 15h253q33 0 53 -28l70 -105 +l114 114q19 19 46 19h101q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-179l115 -172q131 63 275 36q143 -26 244 -134.5t118 -253.5zM448 128q115 0 203 72.5t111 183.5h-314q-35 0 -55 31q-18 32 -1 63l147 277q-47 13 -91 13q-132 0 -226 -94t-94 -226t94 -226 +t226 -94zM1856 128q132 0 226 94t94 226t-94 226t-226 94q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94z" /> + <glyph glyph-name="_508" unicode="&#xf21d;" +d="M1408 0q0 -63 -61.5 -113.5t-164 -81t-225 -46t-253.5 -15.5t-253.5 15.5t-225 46t-164 81t-61.5 113.5q0 49 33 88.5t91 66.5t118 44.5t131 29.5q26 5 48 -10.5t26 -41.5q5 -26 -10.5 -48t-41.5 -26q-58 -10 -106 -23.5t-76.5 -25.5t-48.5 -23.5t-27.5 -19.5t-8.5 -12 +q3 -11 27 -26.5t73 -33t114 -32.5t160.5 -25t201.5 -10t201.5 10t160.5 25t114 33t73 33.5t27 27.5q-1 4 -8.5 11t-27.5 19t-48.5 23.5t-76.5 25t-106 23.5q-26 4 -41.5 26t-10.5 48q4 26 26 41.5t48 10.5q71 -12 131 -29.5t118 -44.5t91 -66.5t33 -88.5zM1024 896v-384 +q0 -26 -19 -45t-45 -19h-64v-384q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v384h-64q-26 0 -45 19t-19 45v384q0 53 37.5 90.5t90.5 37.5h384q53 0 90.5 -37.5t37.5 -90.5zM928 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5 +t158.5 -65.5t65.5 -158.5z" /> + <glyph glyph-name="_509" unicode="&#xf21e;" horiz-adv-x="1792" +d="M1280 512h305q-5 -6 -10 -10.5t-9 -7.5l-3 -4l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-5 2 -21 20h369q22 0 39.5 13.5t22.5 34.5l70 281l190 -667q6 -20 23 -33t39 -13q21 0 38 13t23 33l146 485l56 -112q18 -35 57 -35zM1792 940q0 -145 -103 -300h-369l-111 221 +q-8 17 -25.5 27t-36.5 8q-45 -5 -56 -46l-129 -430l-196 686q-6 20 -23.5 33t-39.5 13t-39 -13.5t-22 -34.5l-116 -464h-423q-103 155 -103 300q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124 +t127 -344z" /> + <glyph glyph-name="venus" unicode="&#xf221;" horiz-adv-x="1280" +d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292 +q11 134 80.5 249t182 188t245.5 88q170 19 319 -54t236 -212t87 -306zM128 960q0 -185 131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5z" /> + <glyph glyph-name="_511" unicode="&#xf222;" +d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-382 -383q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5 +q203 0 359 -126l382 382h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" /> + <glyph glyph-name="_512" unicode="&#xf223;" horiz-adv-x="1280" +d="M830 1220q145 -72 233.5 -210.5t88.5 -305.5q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5 +t-147.5 384.5q0 167 88.5 305.5t233.5 210.5q-165 96 -228 273q-6 16 3.5 29.5t26.5 13.5h69q21 0 29 -20q44 -106 140 -171t214 -65t214 65t140 171q8 20 37 20h61q17 0 26.5 -13.5t3.5 -29.5q-63 -177 -228 -273zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 +t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" /> + <glyph glyph-name="_513" unicode="&#xf224;" +d="M1024 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 +q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-149 16 -270.5 103t-186.5 223.5t-53 291.5q16 204 160 353.5t347 172.5q118 14 228 -19t198 -103l255 254h-134q-14 0 -23 9t-9 23v64zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 +t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" /> + <glyph glyph-name="_514" unicode="&#xf225;" horiz-adv-x="1792" +d="M1280 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 +q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5t-147.5 384.5q0 201 126 359l-52 53l-101 -111q-9 -10 -22 -10.5t-23 7.5l-48 44q-10 8 -10.5 21.5t8.5 23.5l105 115l-111 112v-134q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9 +t-9 23v288q0 26 19 45t45 19h288q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-133l106 -107l86 94q9 10 22 10.5t23 -7.5l48 -44q10 -8 10.5 -21.5t-8.5 -23.5l-90 -99l57 -56q158 126 359 126t359 -126l255 254h-134q-14 0 -23 9t-9 23v64zM832 256q185 0 316.5 131.5 +t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" /> + <glyph glyph-name="_515" unicode="&#xf226;" horiz-adv-x="1792" +d="M1790 1007q12 -155 -52.5 -292t-186 -224t-271.5 -103v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-512v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23 +t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292q17 206 164.5 356.5t352.5 169.5q206 21 377 -94q171 115 377 94q205 -19 352.5 -169.5t164.5 -356.5zM896 647q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM576 512q115 0 218 57q-154 165 -154 391 +q0 224 154 391q-103 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5zM1152 128v260q-137 15 -256 94q-119 -79 -256 -94v-260h512zM1216 512q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5q-115 0 -218 -57q154 -167 154 -391 +q0 -226 -154 -391q103 -57 218 -57z" /> + <glyph glyph-name="_516" unicode="&#xf227;" horiz-adv-x="1920" +d="M1536 1120q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-31 -182 -166 -312t-318 -156q-210 -29 -384.5 80t-241.5 300q-117 6 -221 57.5t-177.5 133t-113.5 192.5t-32 230 +q9 135 78 252t182 191.5t248 89.5q118 14 227.5 -19t198.5 -103l255 254h-134q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q59 -74 93 -169q182 -9 328 -124l255 254h-134q-14 0 -23 9 +t-9 23v64zM1024 704q0 20 -4 58q-162 -25 -271 -150t-109 -292q0 -20 4 -58q162 25 271 150t109 292zM128 704q0 -168 111 -294t276 -149q-3 29 -3 59q0 210 135 369.5t338 196.5q-53 120 -163.5 193t-245.5 73q-185 0 -316.5 -131.5t-131.5 -316.5zM1088 -128 +q185 0 316.5 131.5t131.5 316.5q0 168 -111 294t-276 149q3 -28 3 -59q0 -210 -135 -369.5t-338 -196.5q53 -120 163.5 -193t245.5 -73z" /> + <glyph glyph-name="_517" unicode="&#xf228;" horiz-adv-x="2048" +d="M1664 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-32 -180 -164.5 -310t-313.5 -157q-223 -34 -409 90q-117 -78 -256 -93v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23 +t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-155 17 -279.5 109.5t-187 237.5t-39.5 307q25 187 159.5 322.5t320.5 164.5q224 34 410 -90q146 97 320 97q201 0 359 -126l255 254h-134q-14 0 -23 9 +t-9 23v64zM896 391q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM128 704q0 -185 131.5 -316.5t316.5 -131.5q117 0 218 57q-154 167 -154 391t154 391q-101 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5zM1216 256q185 0 316.5 131.5t131.5 316.5 +t-131.5 316.5t-316.5 131.5q-117 0 -218 -57q154 -167 154 -391t-154 -391q101 -57 218 -57z" /> + <glyph glyph-name="_518" unicode="&#xf229;" +d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-213 -214l140 -140q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-140 141l-78 -79q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5 +t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5q203 0 359 -126l78 78l-172 172q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l172 -172l213 213h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 +t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" /> + <glyph glyph-name="_519" unicode="&#xf22a;" horiz-adv-x="1280" +d="M640 892q217 -24 364.5 -187.5t147.5 -384.5q0 -167 -87 -306t-236 -212t-319 -54q-133 15 -245.5 88t-182 188t-80.5 249q-12 155 52.5 292t186 224t271.5 103v132h-160q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h160v165l-92 -92q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22 +t9 23l202 201q19 19 45 19t45 -19l202 -201q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-92 92v-165h160q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-160v-132zM576 -128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5 +t131.5 -316.5t316.5 -131.5z" /> + <glyph glyph-name="_520" unicode="&#xf22b;" horiz-adv-x="2048" +d="M1901 621q19 -19 19 -45t-19 -45l-294 -294q-9 -10 -22.5 -10t-22.5 10l-45 45q-10 9 -10 22.5t10 22.5l185 185h-294v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-132q-24 -217 -187.5 -364.5t-384.5 -147.5q-167 0 -306 87t-212 236t-54 319q15 133 88 245.5 +t188 182t249 80.5q155 12 292 -52.5t224 -186t103 -271.5h132v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224h294l-185 185q-10 9 -10 22.5t10 22.5l45 45q9 10 22.5 10t22.5 -10zM576 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5 +t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" /> + <glyph glyph-name="_521" unicode="&#xf22c;" horiz-adv-x="1280" +d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-612q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v612q-217 24 -364.5 187.5t-147.5 384.5q0 117 45.5 223.5t123 184t184 123t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5zM576 512q185 0 316.5 131.5 +t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" /> + <glyph glyph-name="_522" unicode="&#xf22d;" horiz-adv-x="1280" +d="M1024 576q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1152 576q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123 +t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5z" /> + <glyph glyph-name="_523" unicode="&#xf22e;" horiz-adv-x="1792" + /> + <glyph glyph-name="_524" unicode="&#xf22f;" horiz-adv-x="1792" + /> + <glyph glyph-name="_525" unicode="&#xf230;" +d="M1451 1408q35 0 60 -25t25 -60v-1366q0 -35 -25 -60t-60 -25h-391v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-735q-35 0 -60 25t-25 60v1366q0 35 25 60t60 25h1366z" /> + <glyph glyph-name="_526" unicode="&#xf231;" horiz-adv-x="1280" +d="M0 939q0 108 37.5 203.5t103.5 166.5t152 123t185 78t202 26q158 0 294 -66.5t221 -193.5t85 -287q0 -96 -19 -188t-60 -177t-100 -149.5t-145 -103t-189 -38.5q-68 0 -135 32t-96 88q-10 -39 -28 -112.5t-23.5 -95t-20.5 -71t-26 -71t-32 -62.5t-46 -77.5t-62 -86.5 +l-14 -5l-9 10q-15 157 -15 188q0 92 21.5 206.5t66.5 287.5t52 203q-32 65 -32 169q0 83 52 156t132 73q61 0 95 -40.5t34 -102.5q0 -66 -44 -191t-44 -187q0 -63 45 -104.5t109 -41.5q55 0 102 25t78.5 68t56 95t38 110.5t20 111t6.5 99.5q0 173 -109.5 269.5t-285.5 96.5 +q-200 0 -334 -129.5t-134 -328.5q0 -44 12.5 -85t27 -65t27 -45.5t12.5 -30.5q0 -28 -15 -73t-37 -45q-2 0 -17 3q-51 15 -90.5 56t-61 94.5t-32.5 108t-11 106.5z" /> + <glyph glyph-name="_527" unicode="&#xf232;" +d="M985 562q13 0 97.5 -44t89.5 -53q2 -5 2 -15q0 -33 -17 -76q-16 -39 -71 -65.5t-102 -26.5q-57 0 -190 62q-98 45 -170 118t-148 185q-72 107 -71 194v8q3 91 74 158q24 22 52 22q6 0 18 -1.5t19 -1.5q19 0 26.5 -6.5t15.5 -27.5q8 -20 33 -88t25 -75q0 -21 -34.5 -57.5 +t-34.5 -46.5q0 -7 5 -15q34 -73 102 -137q56 -53 151 -101q12 -7 22 -7q15 0 54 48.5t52 48.5zM782 32q127 0 243.5 50t200.5 134t134 200.5t50 243.5t-50 243.5t-134 200.5t-200.5 134t-243.5 50t-243.5 -50t-200.5 -134t-134 -200.5t-50 -243.5q0 -203 120 -368l-79 -233 +l242 77q158 -104 345 -104zM782 1414q153 0 292.5 -60t240.5 -161t161 -240.5t60 -292.5t-60 -292.5t-161 -240.5t-240.5 -161t-292.5 -60q-195 0 -365 94l-417 -134l136 405q-108 178 -108 389q0 153 60 292.5t161 240.5t240.5 161t292.5 60z" /> + <glyph glyph-name="_528" unicode="&#xf233;" horiz-adv-x="1792" +d="M128 128h1024v128h-1024v-128zM128 640h1024v128h-1024v-128zM1696 192q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM128 1152h1024v128h-1024v-128zM1696 704q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1696 1216 +q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1792 384v-384h-1792v384h1792zM1792 896v-384h-1792v384h1792zM1792 1408v-384h-1792v384h1792z" /> + <glyph glyph-name="_529" unicode="&#xf234;" horiz-adv-x="2048" +d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1664 512h352q13 0 22.5 -9.5t9.5 -22.5v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-352q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5 +t-9.5 22.5v352h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5v-352zM928 288q0 -52 38 -90t90 -38h256v-238q-68 -50 -171 -50h-874q-121 0 -194 69t-73 190q0 53 3.5 103.5t14 109t26.5 108.5 +t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q79 -61 154.5 -91.5t164.5 -30.5t164.5 30.5t154.5 91.5q20 17 39 17q132 0 217 -96h-223q-52 0 -90 -38t-38 -90v-192z" /> + <glyph glyph-name="_530" unicode="&#xf235;" horiz-adv-x="2048" +d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1781 320l249 -249q9 -9 9 -23q0 -13 -9 -22l-136 -136q-9 -9 -22 -9q-14 0 -23 9l-249 249l-249 -249q-9 -9 -23 -9q-13 0 -22 9l-136 136 +q-9 9 -9 22q0 14 9 23l249 249l-249 249q-9 9 -9 23q0 13 9 22l136 136q9 9 22 9q14 0 23 -9l249 -249l249 249q9 9 23 9q13 0 22 -9l136 -136q9 -9 9 -22q0 -14 -9 -23zM1283 320l-181 -181q-37 -37 -37 -91q0 -53 37 -90l83 -83q-21 -3 -44 -3h-874q-121 0 -194 69 +t-73 190q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q154 -122 319 -122t319 122q20 17 39 17q28 0 57 -6q-28 -27 -41 -50t-13 -56q0 -54 37 -91z" /> + <glyph glyph-name="_531" unicode="&#xf236;" horiz-adv-x="2048" +d="M256 512h1728q26 0 45 -19t19 -45v-448h-256v256h-1536v-256h-256v1216q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-704zM832 832q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM2048 576v64q0 159 -112.5 271.5t-271.5 112.5h-704 +q-26 0 -45 -19t-19 -45v-384h1152z" /> + <glyph glyph-name="_532" unicode="&#xf237;" +d="M1536 1536l-192 -448h192v-192h-274l-55 -128h329v-192h-411l-357 -832l-357 832h-411v192h329l-55 128h-274v192h192l-192 448h256l323 -768h378l323 768h256zM768 320l108 256h-216z" /> + <glyph glyph-name="_533" unicode="&#xf238;" +d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM768 192q80 0 136 56t56 136t-56 136t-136 56 +t-136 -56t-56 -136t56 -136t136 -56zM1344 768v512h-1152v-512h1152z" /> + <glyph glyph-name="_534" unicode="&#xf239;" +d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM288 224q66 0 113 47t47 113t-47 113t-113 47 +t-113 -47t-47 -113t47 -113t113 -47zM704 768v512h-544v-512h544zM1248 224q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM1408 768v512h-576v-512h576z" /> + <glyph glyph-name="_535" unicode="&#xf23a;" horiz-adv-x="1792" +d="M597 1115v-1173q0 -25 -12.5 -42.5t-36.5 -17.5q-17 0 -33 8l-465 233q-21 10 -35.5 33.5t-14.5 46.5v1140q0 20 10 34t29 14q14 0 44 -15l511 -256q3 -3 3 -5zM661 1014l534 -866l-534 266v600zM1792 996v-1054q0 -25 -14 -40.5t-38 -15.5t-47 13l-441 220zM1789 1116 +q0 -3 -256.5 -419.5t-300.5 -487.5l-390 634l324 527q17 28 52 28q14 0 26 -6l541 -270q4 -2 4 -6z" /> + <glyph glyph-name="_536" unicode="&#xf23b;" +d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1408v-1536h-1536v1536h1536z" /> + <glyph glyph-name="_537" unicode="&#xf23c;" horiz-adv-x="2296" +d="M478 -139q-8 -16 -27 -34.5t-37 -25.5q-25 -9 -51.5 3.5t-28.5 31.5q-1 22 40 55t68 38q23 4 34 -21.5t2 -46.5zM1819 -139q7 -16 26 -34.5t38 -25.5q25 -9 51.5 3.5t27.5 31.5q2 22 -39.5 55t-68.5 38q-22 4 -33 -21.5t-2 -46.5zM1867 -30q13 -27 56.5 -59.5t77.5 -41.5 +q45 -13 82 4.5t37 50.5q0 46 -67.5 100.5t-115.5 59.5q-40 5 -63.5 -37.5t-6.5 -76.5zM428 -30q-13 -27 -56 -59.5t-77 -41.5q-45 -13 -82 4.5t-37 50.5q0 46 67.5 100.5t115.5 59.5q40 5 63 -37.5t6 -76.5zM1158 1094h1q-41 0 -76 -15q27 -8 44 -30.5t17 -49.5 +q0 -35 -27 -60t-65 -25q-52 0 -80 43q-5 -23 -5 -42q0 -74 56 -126.5t135 -52.5q80 0 136 52.5t56 126.5t-56 126.5t-136 52.5zM1462 1312q-99 109 -220.5 131.5t-245.5 -44.5q27 60 82.5 96.5t118 39.5t121.5 -17t99.5 -74.5t44.5 -131.5zM2212 73q8 -11 -11 -42 +q7 -23 7 -40q1 -56 -44.5 -112.5t-109.5 -91.5t-118 -37q-48 -2 -92 21.5t-66 65.5q-687 -25 -1259 0q-23 -41 -66.5 -65t-92.5 -22q-86 3 -179.5 80.5t-92.5 160.5q2 22 7 40q-19 31 -11 42q6 10 31 1q14 22 41 51q-7 29 2 38q11 10 39 -4q29 20 59 34q0 29 13 37 +q23 12 51 -16q35 5 61 -2q18 -4 38 -19v73q-11 0 -18 2q-53 10 -97 44.5t-55 87.5q-9 38 0 81q15 62 93 95q2 17 19 35.5t36 23.5t33 -7.5t19 -30.5h13q46 -5 60 -23q3 -3 5 -7q10 1 30.5 3.5t30.5 3.5q-15 11 -30 17q-23 40 -91 43q0 6 1 10q-62 2 -118.5 18.5t-84.5 47.5 +q-32 36 -42.5 92t-2.5 112q16 126 90 179q23 16 52 4.5t32 -40.5q0 -1 1.5 -14t2.5 -21t3 -20t5.5 -19t8.5 -10q27 -14 76 -12q48 46 98 74q-40 4 -162 -14l47 46q61 58 163 111q145 73 282 86q-20 8 -41 15.5t-47 14t-42.5 10.5t-47.5 11t-43 10q595 126 904 -139 +q98 -84 158 -222q85 -10 121 9h1q5 3 8.5 10t5.5 19t3 19.5t3 21.5l1 14q3 28 32 40t52 -5q73 -52 91 -178q7 -57 -3.5 -113t-42.5 -91q-28 -32 -83.5 -48.5t-115.5 -18.5v-10q-71 -2 -95 -43q-14 -5 -31 -17q11 -1 32 -3.5t30 -3.5q1 5 5 8q16 18 60 23h13q5 18 19 30t33 8 +t36 -23t19 -36q79 -32 93 -95q9 -40 1 -81q-12 -53 -56 -88t-97 -44q-10 -2 -17 -2q0 -49 -1 -73q20 15 38 19q26 7 61 2q28 28 51 16q14 -9 14 -37q33 -16 59 -34q27 13 38 4q10 -10 2 -38q28 -30 41 -51q23 8 31 -1zM1937 1025q0 -29 -9 -54q82 -32 112 -132 +q4 37 -9.5 98.5t-41.5 90.5q-20 19 -36 17t-16 -20zM1859 925q35 -42 47.5 -108.5t-0.5 -124.5q67 13 97 45q13 14 18 28q-3 64 -31 114.5t-79 66.5q-15 -15 -52 -21zM1822 921q-30 0 -44 1q42 -115 53 -239q21 0 43 3q16 68 1 135t-53 100zM258 839q30 100 112 132 +q-9 25 -9 54q0 18 -16.5 20t-35.5 -17q-28 -29 -41.5 -90.5t-9.5 -98.5zM294 737q29 -31 97 -45q-13 58 -0.5 124.5t47.5 108.5v0q-37 6 -52 21q-51 -16 -78.5 -66t-31.5 -115q9 -17 18 -28zM471 683q14 124 73 235q-19 -4 -55 -18l-45 -19v1q-46 -89 -20 -196q25 -3 47 -3z +M1434 644q8 -38 16.5 -108.5t11.5 -89.5q3 -18 9.5 -21.5t23.5 4.5q40 20 62 85.5t23 125.5q-24 2 -146 4zM1152 1285q-116 0 -199 -82.5t-83 -198.5q0 -117 83 -199.5t199 -82.5t199 82.5t83 199.5q0 116 -83 198.5t-199 82.5zM1380 646q-105 2 -211 0v1q-1 -27 2.5 -86 +t13.5 -66q29 -14 93.5 -14.5t95.5 10.5q9 3 11 39t-0.5 69.5t-4.5 46.5zM1112 447q8 4 9.5 48t-0.5 88t-4 63v1q-212 -3 -214 -3q-4 -20 -7 -62t0 -83t14 -46q34 -15 101 -16t101 10zM718 636q-16 -59 4.5 -118.5t77.5 -84.5q15 -8 24 -5t12 21q3 16 8 90t10 103 +q-69 -2 -136 -6zM591 510q3 -23 -34 -36q132 -141 271.5 -240t305.5 -154q172 49 310.5 146t293.5 250q-33 13 -30 34q0 2 0.5 3.5t1.5 3t1 2.5v1v-1q-17 2 -50 5.5t-48 4.5q-26 -90 -82 -132q-51 -38 -82 1q-5 6 -9 14q-7 13 -17 62q-2 -5 -5 -9t-7.5 -7t-8 -5.5t-9.5 -4 +l-10 -2.5t-12 -2l-12 -1.5t-13.5 -1t-13.5 -0.5q-106 -9 -163 11q-4 -17 -10 -26.5t-21 -15t-23 -7t-36 -3.5q-6 -1 -9 -1q-179 -17 -203 40q-2 -63 -56 -54q-47 8 -91 54q-12 13 -20 26q-17 29 -26 65q-58 -6 -87 -10q1 -2 4 -10zM507 -118q3 14 3 30q-17 71 -51 130 +t-73 70q-41 12 -101.5 -14.5t-104.5 -80t-39 -107.5q35 -53 100 -93t119 -42q51 -2 94 28t53 79zM510 53q23 -63 27 -119q195 113 392 174q-98 52 -180.5 120t-179.5 165q-6 -4 -29 -13q0 -1 -1 -4t-1 -5q31 -18 22 -37q-12 -23 -56 -34q-10 -13 -29 -24h-1q-2 -83 1 -150 +q19 -34 35 -73zM579 -113q532 -21 1145 0q-254 147 -428 196q-76 -35 -156 -57q-8 -3 -16 0q-65 21 -129 49q-208 -60 -416 -188h-1v-1q1 0 1 1zM1763 -67q4 54 28 120q14 38 33 71l-1 -1q3 77 3 153q-15 8 -30 25q-42 9 -56 33q-9 20 22 38q-2 4 -2 9q-16 4 -28 12 +q-204 -190 -383 -284q198 -59 414 -176zM2155 -90q5 54 -39 107.5t-104 80t-102 14.5q-38 -11 -72.5 -70.5t-51.5 -129.5q0 -16 3 -30q10 -49 53 -79t94 -28q54 2 119 42t100 93z" /> + <glyph glyph-name="_538" unicode="&#xf23d;" horiz-adv-x="2304" +d="M1524 -25q0 -68 -48 -116t-116 -48t-116.5 48t-48.5 116t48.5 116.5t116.5 48.5t116 -48.5t48 -116.5zM775 -25q0 -68 -48.5 -116t-116.5 -48t-116 48t-48 116t48 116.5t116 48.5t116.5 -48.5t48.5 -116.5zM0 1469q57 -60 110.5 -104.5t121 -82t136 -63t166 -45.5 +t200 -31.5t250 -18.5t304 -9.5t372.5 -2.5q139 0 244.5 -5t181 -16.5t124 -27.5t71 -39.5t24 -51.5t-19.5 -64t-56.5 -76.5t-89.5 -91t-116 -104.5t-139 -119q-185 -157 -286 -247q29 51 76.5 109t94 105.5t94.5 98.5t83 91.5t54 80.5t13 70t-45.5 55.5t-116.5 41t-204 23.5 +t-304 5q-168 -2 -314 6t-256 23t-204.5 41t-159.5 51.5t-122.5 62.5t-91.5 66.5t-68 71.5t-50.5 69.5t-40 68t-36.5 59.5z" /> + <glyph glyph-name="_539" unicode="&#xf23e;" horiz-adv-x="1792" +d="M896 1472q-169 0 -323 -66t-265.5 -177.5t-177.5 -265.5t-66 -323t66 -323t177.5 -265.5t265.5 -177.5t323 -66t323 66t265.5 177.5t177.5 265.5t66 323t-66 323t-177.5 265.5t-265.5 177.5t-323 66zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348 +t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM496 704q16 0 16 -16v-480q0 -16 -16 -16h-32q-16 0 -16 16v480q0 16 16 16h32zM896 640q53 0 90.5 -37.5t37.5 -90.5q0 -35 -17.5 -64t-46.5 -46v-114q0 -14 -9 -23 +t-23 -9h-64q-14 0 -23 9t-9 23v114q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5zM896 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM544 928v-96 +q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 93 65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5v-96q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 146 -103 249t-249 103t-249 -103t-103 -249zM1408 192v512q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-512 +q0 -26 19 -45t45 -19h896q26 0 45 19t19 45z" /> + <glyph glyph-name="_540" unicode="&#xf240;" horiz-adv-x="2304" +d="M1920 1024v-768h-1664v768h1664zM2048 448h128v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288zM2304 832v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113 +v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160q53 0 90.5 -37.5t37.5 -90.5z" /> + <glyph glyph-name="_541" unicode="&#xf241;" horiz-adv-x="2304" +d="M256 256v768h1280v-768h-1280zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 +h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" /> + <glyph glyph-name="_542" unicode="&#xf242;" horiz-adv-x="2304" +d="M256 256v768h896v-768h-896zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 +h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" /> + <glyph glyph-name="_543" unicode="&#xf243;" horiz-adv-x="2304" +d="M256 256v768h512v-768h-512zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 +h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" /> + <glyph glyph-name="_544" unicode="&#xf244;" horiz-adv-x="2304" +d="M2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23 +v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" /> + <glyph glyph-name="_545" unicode="&#xf245;" horiz-adv-x="1280" +d="M1133 493q31 -30 14 -69q-17 -40 -59 -40h-382l201 -476q10 -25 0 -49t-34 -35l-177 -75q-25 -10 -49 0t-35 34l-191 452l-312 -312q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v1504q0 42 40 59q12 5 24 5q27 0 45 -19z" /> + <glyph glyph-name="_546" unicode="&#xf246;" horiz-adv-x="1024" +d="M832 1408q-320 0 -320 -224v-416h128v-128h-128v-544q0 -224 320 -224h64v-128h-64q-272 0 -384 146q-112 -146 -384 -146h-64v128h64q320 0 320 224v544h-128v128h128v416q0 224 -320 224h-64v128h64q272 0 384 -146q112 146 384 146h64v-128h-64z" /> + <glyph glyph-name="_547" unicode="&#xf247;" horiz-adv-x="2048" +d="M2048 1152h-128v-1024h128v-384h-384v128h-1280v-128h-384v384h128v1024h-128v384h384v-128h1280v128h384v-384zM1792 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 -128v128h-128v-128h128zM1664 0v128h128v1024h-128v128h-1280v-128h-128v-1024h128v-128 +h1280zM1920 -128v128h-128v-128h128zM1280 896h384v-768h-896v256h-384v768h896v-256zM512 512h640v512h-640v-512zM1536 256v512h-256v-384h-384v-128h640z" /> + <glyph glyph-name="_548" unicode="&#xf248;" horiz-adv-x="2304" +d="M2304 768h-128v-640h128v-384h-384v128h-896v-128h-384v384h128v128h-384v-128h-384v384h128v640h-128v384h384v-128h896v128h384v-384h-128v-128h384v128h384v-384zM2048 1024v-128h128v128h-128zM1408 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 256 +v128h-128v-128h128zM1536 384h-128v-128h128v128zM384 384h896v128h128v640h-128v128h-896v-128h-128v-640h128v-128zM896 -128v128h-128v-128h128zM2176 -128v128h-128v-128h128zM2048 128v640h-128v128h-384v-384h128v-384h-384v128h-384v-128h128v-128h896v128h128z" /> + <glyph glyph-name="_549" unicode="&#xf249;" +d="M1024 288v-416h-928q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68v-928h-416q-40 0 -68 -28t-28 -68zM1152 256h381q-15 -82 -65 -132l-184 -184q-50 -50 -132 -65v381z" /> + <glyph glyph-name="_550" unicode="&#xf24a;" +d="M1400 256h-248v-248q29 10 41 22l185 185q12 12 22 41zM1120 384h288v896h-1280v-1280h896v288q0 40 28 68t68 28zM1536 1312v-1024q0 -40 -20 -88t-48 -76l-184 -184q-28 -28 -76 -48t-88 -20h-1024q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68 +z" /> + <glyph glyph-name="_551" unicode="&#xf24b;" horiz-adv-x="2304" +d="M1951 538q0 -26 -15.5 -44.5t-38.5 -23.5q-8 -2 -18 -2h-153v140h153q10 0 18 -2q23 -5 38.5 -23.5t15.5 -44.5zM1933 751q0 -25 -15 -42t-38 -21q-3 -1 -15 -1h-139v129h139q3 0 8.5 -0.5t6.5 -0.5q23 -4 38 -21.5t15 -42.5zM728 587v308h-228v-308q0 -58 -38 -94.5 +t-105 -36.5q-108 0 -229 59v-112q53 -15 121 -23t109 -9l42 -1q328 0 328 217zM1442 403v113q-99 -52 -200 -59q-108 -8 -169 41t-61 142t61 142t169 41q101 -7 200 -58v112q-48 12 -100 19.5t-80 9.5l-28 2q-127 6 -218.5 -14t-140.5 -60t-71 -88t-22 -106t22 -106t71 -88 +t140.5 -60t218.5 -14q101 4 208 31zM2176 518q0 54 -43 88.5t-109 39.5v3q57 8 89 41.5t32 79.5q0 55 -41 88t-107 36q-3 0 -12 0.5t-14 0.5h-455v-510h491q74 0 121.5 36.5t47.5 96.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90 +t90 38h2048q52 0 90 -38t38 -90z" /> + <glyph glyph-name="_552" unicode="&#xf24c;" horiz-adv-x="2304" +d="M858 295v693q-106 -41 -172 -135.5t-66 -211.5t66 -211.5t172 -134.5zM1362 641q0 117 -66 211.5t-172 135.5v-694q106 41 172 135.5t66 211.5zM1577 641q0 -159 -78.5 -294t-213.5 -213.5t-294 -78.5q-119 0 -227.5 46.5t-187 125t-125 187t-46.5 227.5q0 159 78.5 294 +t213.5 213.5t294 78.5t294 -78.5t213.5 -213.5t78.5 -294zM1960 634q0 139 -55.5 261.5t-147.5 205.5t-213.5 131t-252.5 48h-301q-176 0 -323.5 -81t-235 -230t-87.5 -335q0 -171 87 -317.5t236 -231.5t323 -85h301q129 0 251.5 50.5t214.5 135t147.5 202.5t55.5 246z +M2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" /> + <glyph glyph-name="_553" unicode="&#xf24d;" horiz-adv-x="1792" +d="M1664 -96v1088q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5zM1792 992v-1088q0 -66 -47 -113t-113 -47h-1088q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113 +zM1408 1376v-160h-128v160q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h160v-128h-160q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113z" /> + <glyph glyph-name="_554" unicode="&#xf24e;" horiz-adv-x="2304" +d="M1728 1088l-384 -704h768zM448 1088l-384 -704h768zM1269 1280q-14 -40 -45.5 -71.5t-71.5 -45.5v-1291h608q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1344q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h608v1291q-40 14 -71.5 45.5t-45.5 71.5h-491q-14 0 -23 9t-9 23v64 +q0 14 9 23t23 9h491q21 57 70 92.5t111 35.5t111 -35.5t70 -92.5h491q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-491zM1088 1264q33 0 56.5 23.5t23.5 56.5t-23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5zM2176 384q0 -73 -46.5 -131t-117.5 -91 +t-144.5 -49.5t-139.5 -16.5t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81zM896 384q0 -73 -46.5 -131t-117.5 -91t-144.5 -49.5t-139.5 -16.5 +t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81z" /> + <glyph glyph-name="_555" unicode="&#xf250;" +d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 +t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-77 -29 -149 -92.5 +t-129.5 -152.5t-92.5 -210t-35 -253h1024q0 132 -35 253t-92.5 210t-129.5 152.5t-149 92.5q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" /> + <glyph glyph-name="_556" unicode="&#xf251;" +d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 +t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -66 9 -128h1006q9 61 9 128zM1280 -128q0 130 -34 249.5t-90.5 208t-126.5 152t-146 94.5h-230q-76 -31 -146 -94.5t-126.5 -152t-90.5 -208t-34 -249.5h1024z" /> + <glyph glyph-name="_557" unicode="&#xf252;" +d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 +t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -206 85 -384h854q85 178 85 384zM1223 192q-54 141 -145.5 241.5t-194.5 142.5h-230q-103 -42 -194.5 -142.5t-145.5 -241.5h910z" /> + <glyph glyph-name="_558" unicode="&#xf253;" +d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 +t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-137 -51 -244 -196 +h700q-107 145 -244 196q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" /> + <glyph glyph-name="_559" unicode="&#xf254;" +d="M1504 -64q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472zM130 0q3 55 16 107t30 95t46 87t53.5 76t64.5 69.5t66 60t70.5 55t66.5 47.5t65 43q-43 28 -65 43t-66.5 47.5t-70.5 55t-66 60t-64.5 69.5t-53.5 76t-46 87 +t-30 95t-16 107h1276q-3 -55 -16 -107t-30 -95t-46 -87t-53.5 -76t-64.5 -69.5t-66 -60t-70.5 -55t-66.5 -47.5t-65 -43q43 -28 65 -43t66.5 -47.5t70.5 -55t66 -60t64.5 -69.5t53.5 -76t46 -87t30 -95t16 -107h-1276zM1504 1536q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9 +h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472z" /> + <glyph glyph-name="_560" unicode="&#xf255;" +d="M768 1152q-53 0 -90.5 -37.5t-37.5 -90.5v-128h-32v93q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-429l-32 30v172q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-224q0 -47 35 -82l310 -296q39 -39 39 -102q0 -26 19 -45t45 -19h640q26 0 45 19t19 45v25 +q0 41 10 77l108 436q10 36 10 77v246q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-32h-32v125q0 40 -25 72.5t-64 40.5q-14 2 -23 2q-46 0 -79 -33t-33 -79v-128h-32v122q0 51 -32.5 89.5t-82.5 43.5q-5 1 -13 1zM768 1280q84 0 149 -50q57 34 123 34q59 0 111 -27 +t86 -76q27 7 59 7q100 0 170 -71.5t70 -171.5v-246q0 -51 -13 -108l-109 -436q-6 -24 -6 -71q0 -80 -56 -136t-136 -56h-640q-84 0 -138 58.5t-54 142.5l-308 296q-76 73 -76 175v224q0 99 70.5 169.5t169.5 70.5q11 0 16 -1q6 95 75.5 160t164.5 65q52 0 98 -21 +q72 69 174 69z" /> + <glyph glyph-name="_561" unicode="&#xf256;" horiz-adv-x="1792" +d="M880 1408q-46 0 -79 -33t-33 -79v-656h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528v-256l-154 205q-38 51 -102 51q-53 0 -90.5 -37.5t-37.5 -90.5q0 -43 26 -77l384 -512q38 -51 102 -51h688q34 0 61 22t34 56l76 405q5 32 5 59v498q0 46 -33 79t-79 33t-79 -33 +t-33 -79v-272h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528h-32v656q0 46 -33 79t-79 33zM880 1536q68 0 125.5 -35.5t88.5 -96.5q19 4 42 4q99 0 169.5 -70.5t70.5 -169.5v-17q105 6 180.5 -64t75.5 -175v-498q0 -40 -8 -83l-76 -404q-14 -79 -76.5 -131t-143.5 -52 +h-688q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 106 75 181t181 75q78 0 128 -34v434q0 99 70.5 169.5t169.5 70.5q23 0 42 -4q31 61 88.5 96.5t125.5 35.5z" /> + <glyph glyph-name="_562" unicode="&#xf257;" horiz-adv-x="1792" +d="M1073 -128h-177q-163 0 -226 141q-23 49 -23 102v5q-62 30 -98.5 88.5t-36.5 127.5q0 38 5 48h-261q-106 0 -181 75t-75 181t75 181t181 75h113l-44 17q-74 28 -119.5 93.5t-45.5 145.5q0 106 75 181t181 75q46 0 91 -17l628 -239h401q106 0 181 -75t75 -181v-668 +q0 -88 -54 -157.5t-140 -90.5l-339 -85q-92 -23 -186 -23zM1024 583l-155 -71l-163 -74q-30 -14 -48 -41.5t-18 -60.5q0 -46 33 -79t79 -33q26 0 46 10l338 154q-49 10 -80.5 50t-31.5 90v55zM1344 272q0 46 -33 79t-79 33q-26 0 -46 -10l-290 -132q-28 -13 -37 -17 +t-30.5 -17t-29.5 -23.5t-16 -29t-8 -40.5q0 -50 31.5 -82t81.5 -32q20 0 38 9l352 160q30 14 48 41.5t18 60.5zM1112 1024l-650 248q-24 8 -46 8q-53 0 -90.5 -37.5t-37.5 -90.5q0 -40 22.5 -73t59.5 -47l526 -200v-64h-640q-53 0 -90.5 -37.5t-37.5 -90.5t37.5 -90.5 +t90.5 -37.5h535l233 106v198q0 63 46 106l111 102h-69zM1073 0q82 0 155 19l339 85q43 11 70 45.5t27 78.5v668q0 53 -37.5 90.5t-90.5 37.5h-308l-136 -126q-36 -33 -36 -82v-296q0 -46 33 -77t79 -31t79 35t33 81v208h32v-208q0 -70 -57 -114q52 -8 86.5 -48.5t34.5 -93.5 +q0 -42 -23 -78t-61 -53l-310 -141h91z" /> + <glyph glyph-name="_563" unicode="&#xf258;" horiz-adv-x="2048" +d="M1151 1536q61 0 116 -28t91 -77l572 -781q118 -159 118 -359v-355q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v177l-286 143h-546q-80 0 -136 56t-56 136v32q0 119 84.5 203.5t203.5 84.5h420l42 128h-686q-100 0 -173.5 67.5t-81.5 166.5q-65 79 -65 182v32 +q0 80 56 136t136 56h959zM1920 -64v355q0 157 -93 284l-573 781q-39 52 -103 52h-959q-26 0 -45 -19t-19 -45q0 -32 1.5 -49.5t9.5 -40.5t25 -43q10 31 35.5 50t56.5 19h832v-32h-832q-26 0 -45 -19t-19 -45q0 -44 3 -58q8 -44 44 -73t81 -29h640h91q40 0 68 -28t28 -68 +q0 -15 -5 -30l-64 -192q-10 -29 -35 -47.5t-56 -18.5h-443q-66 0 -113 -47t-47 -113v-32q0 -26 19 -45t45 -19h561q16 0 29 -7l317 -158q24 -13 38.5 -36t14.5 -50v-197q0 -26 19 -45t45 -19h384q26 0 45 19t19 45z" /> + <glyph glyph-name="_564" unicode="&#xf259;" horiz-adv-x="2048" +d="M459 -256q-77 0 -137.5 47.5t-79.5 122.5l-101 401q-13 57 -13 108q0 45 -5 67l-116 477q-7 27 -7 57q0 93 62 161t155 78q17 85 82.5 139t152.5 54q83 0 148 -51.5t85 -132.5l83 -348l103 428q20 81 85 132.5t148 51.5q89 0 155.5 -57.5t80.5 -144.5q92 -10 152 -79 +t60 -162q0 -24 -7 -59l-123 -512q10 7 37.5 28.5t38.5 29.5t35 23t41 20.5t41.5 11t49.5 5.5q105 0 180 -74t75 -179q0 -62 -28.5 -118t-78.5 -94l-507 -380q-68 -51 -153 -51h-694zM1104 1408q-38 0 -68.5 -24t-39.5 -62l-164 -682h-127l-145 602q-9 38 -39.5 62t-68.5 24 +q-48 0 -80 -33t-32 -80q0 -15 3 -28l132 -547h-26l-99 408q-9 37 -40 62.5t-69 25.5q-47 0 -80 -33t-33 -79q0 -14 3 -26l116 -478q7 -28 9 -86t10 -88l100 -401q8 -32 34 -52.5t59 -20.5h694q42 0 76 26l507 379q56 43 56 110q0 52 -37.5 88.5t-89.5 36.5q-43 0 -77 -26 +l-307 -230v227q0 4 32 138t68 282t39 161q4 18 4 29q0 47 -32 81t-79 34q-39 0 -69.5 -24t-39.5 -62l-116 -482h-26l150 624q3 14 3 28q0 48 -31.5 82t-79.5 34z" /> + <glyph glyph-name="_565" unicode="&#xf25a;" horiz-adv-x="1792" +d="M640 1408q-53 0 -90.5 -37.5t-37.5 -90.5v-512v-384l-151 202q-41 54 -107 54q-52 0 -89 -38t-37 -90q0 -43 26 -77l384 -512q38 -51 102 -51h718q22 0 39.5 13.5t22.5 34.5l92 368q24 96 24 194v217q0 41 -28 71t-68 30t-68 -28t-28 -68h-32v61q0 48 -32 81.5t-80 33.5 +q-46 0 -79 -33t-33 -79v-64h-32v90q0 55 -37 94.5t-91 39.5q-53 0 -90.5 -37.5t-37.5 -90.5v-96h-32v570q0 55 -37 94.5t-91 39.5zM640 1536q107 0 181.5 -77.5t74.5 -184.5v-220q22 2 32 2q99 0 173 -69q47 21 99 21q113 0 184 -87q27 7 56 7q94 0 159 -67.5t65 -161.5 +v-217q0 -116 -28 -225l-92 -368q-16 -64 -68 -104.5t-118 -40.5h-718q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 105 74.5 180.5t179.5 75.5q71 0 130 -35v547q0 106 75 181t181 75zM768 128v384h-32v-384h32zM1024 128v384h-32v-384h32zM1280 128v384h-32 +v-384h32z" /> + <glyph glyph-name="_566" unicode="&#xf25b;" +d="M1288 889q60 0 107 -23q141 -63 141 -226v-177q0 -94 -23 -186l-85 -339q-21 -86 -90.5 -140t-157.5 -54h-668q-106 0 -181 75t-75 181v401l-239 628q-17 45 -17 91q0 106 75 181t181 75q80 0 145.5 -45.5t93.5 -119.5l17 -44v113q0 106 75 181t181 75t181 -75t75 -181 +v-261q27 5 48 5q69 0 127.5 -36.5t88.5 -98.5zM1072 896q-33 0 -60.5 -18t-41.5 -48l-74 -163l-71 -155h55q50 0 90 -31.5t50 -80.5l154 338q10 20 10 46q0 46 -33 79t-79 33zM1293 761q-22 0 -40.5 -8t-29 -16t-23.5 -29.5t-17 -30.5t-17 -37l-132 -290q-10 -20 -10 -46 +q0 -46 33 -79t79 -33q33 0 60.5 18t41.5 48l160 352q9 18 9 38q0 50 -32 81.5t-82 31.5zM128 1120q0 -22 8 -46l248 -650v-69l102 111q43 46 106 46h198l106 233v535q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5v-640h-64l-200 526q-14 37 -47 59.5t-73 22.5 +q-53 0 -90.5 -37.5t-37.5 -90.5zM1180 -128q44 0 78.5 27t45.5 70l85 339q19 73 19 155v91l-141 -310q-17 -38 -53 -61t-78 -23q-53 0 -93.5 34.5t-48.5 86.5q-44 -57 -114 -57h-208v32h208q46 0 81 33t35 79t-31 79t-77 33h-296q-49 0 -82 -36l-126 -136v-308 +q0 -53 37.5 -90.5t90.5 -37.5h668z" /> + <glyph glyph-name="_567" unicode="&#xf25c;" horiz-adv-x="1973" +d="M857 992v-117q0 -13 -9.5 -22t-22.5 -9h-298v-812q0 -13 -9 -22.5t-22 -9.5h-135q-13 0 -22.5 9t-9.5 23v812h-297q-13 0 -22.5 9t-9.5 22v117q0 14 9 23t23 9h793q13 0 22.5 -9.5t9.5 -22.5zM1895 995l77 -961q1 -13 -8 -24q-10 -10 -23 -10h-134q-12 0 -21 8.5 +t-10 20.5l-46 588l-189 -425q-8 -19 -29 -19h-120q-20 0 -29 19l-188 427l-45 -590q-1 -12 -10 -20.5t-21 -8.5h-135q-13 0 -23 10q-9 10 -9 24l78 961q1 12 10 20.5t21 8.5h142q20 0 29 -19l220 -520q10 -24 20 -51q3 7 9.5 24.5t10.5 26.5l221 520q9 19 29 19h141 +q13 0 22 -8.5t10 -20.5z" /> + <glyph glyph-name="_568" unicode="&#xf25d;" horiz-adv-x="1792" +d="M1042 833q0 88 -60 121q-33 18 -117 18h-123v-281h162q66 0 102 37t36 105zM1094 548l205 -373q8 -17 -1 -31q-8 -16 -27 -16h-152q-20 0 -28 17l-194 365h-155v-350q0 -14 -9 -23t-23 -9h-134q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h294q128 0 190 -24q85 -31 134 -109 +t49 -180q0 -92 -42.5 -165.5t-115.5 -109.5q6 -10 9 -16zM896 1376q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM1792 640 +q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" /> + <glyph glyph-name="_569" unicode="&#xf25e;" horiz-adv-x="1792" +d="M605 303q153 0 257 104q14 18 3 36l-45 82q-6 13 -24 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13.5t-23.5 -14.5t-28.5 -13t-33.5 -9.5t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78 +q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-148 0 -246 -96.5t-98 -240.5q0 -146 97 -241.5t247 -95.5zM1235 303q153 0 257 104q14 18 4 36l-45 82q-8 14 -25 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13.5t-23.5 -14.5t-28.5 -13t-33.5 -9.5 +t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-147 0 -245.5 -96.5t-98.5 -240.5q0 -146 97 -241.5t247 -95.5zM896 1376 +q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191 +t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71z" /> + <glyph glyph-name="f260" unicode="&#xf260;" horiz-adv-x="2048" +d="M736 736l384 -384l-384 -384l-672 672l672 672l168 -168l-96 -96l-72 72l-480 -480l480 -480l193 193l-289 287zM1312 1312l672 -672l-672 -672l-168 168l96 96l72 -72l480 480l-480 480l-193 -193l289 -287l-96 -96l-384 384z" /> + <glyph glyph-name="f261" unicode="&#xf261;" horiz-adv-x="1792" +d="M717 182l271 271l-279 279l-88 -88l192 -191l-96 -96l-279 279l279 279l40 -40l87 87l-127 128l-454 -454zM1075 190l454 454l-454 454l-271 -271l279 -279l88 88l-192 191l96 96l279 -279l-279 -279l-40 40l-87 -88zM1792 640q0 -182 -71 -348t-191 -286t-286 -191 +t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" /> + <glyph glyph-name="_572" unicode="&#xf262;" horiz-adv-x="2304" +d="M651 539q0 -39 -27.5 -66.5t-65.5 -27.5q-39 0 -66.5 27.5t-27.5 66.5q0 38 27.5 65.5t66.5 27.5q38 0 65.5 -27.5t27.5 -65.5zM1805 540q0 -39 -27.5 -66.5t-66.5 -27.5t-66.5 27.5t-27.5 66.5t27.5 66t66.5 27t66.5 -27t27.5 -66zM765 539q0 79 -56.5 136t-136.5 57 +t-136.5 -56.5t-56.5 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM1918 540q0 80 -56.5 136.5t-136.5 56.5q-79 0 -136 -56.5t-57 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM850 539q0 -116 -81.5 -197.5t-196.5 -81.5q-116 0 -197.5 82t-81.5 197 +t82 196.5t197 81.5t196.5 -81.5t81.5 -196.5zM2004 540q0 -115 -81.5 -196.5t-197.5 -81.5q-115 0 -196.5 81.5t-81.5 196.5t81.5 196.5t196.5 81.5q116 0 197.5 -81.5t81.5 -196.5zM1040 537q0 191 -135.5 326.5t-326.5 135.5q-125 0 -231 -62t-168 -168.5t-62 -231.5 +t62 -231.5t168 -168.5t231 -62q191 0 326.5 135.5t135.5 326.5zM1708 1110q-254 111 -556 111q-319 0 -573 -110q117 0 223 -45.5t182.5 -122.5t122 -183t45.5 -223q0 115 43.5 219.5t118 180.5t177.5 123t217 50zM2187 537q0 191 -135 326.5t-326 135.5t-326.5 -135.5 +t-135.5 -326.5t135.5 -326.5t326.5 -135.5t326 135.5t135 326.5zM1921 1103h383q-44 -51 -75 -114.5t-40 -114.5q110 -151 110 -337q0 -156 -77 -288t-209 -208.5t-287 -76.5q-133 0 -249 56t-196 155q-47 -56 -129 -179q-11 22 -53.5 82.5t-74.5 97.5 +q-80 -99 -196.5 -155.5t-249.5 -56.5q-155 0 -287 76.5t-209 208.5t-77 288q0 186 110 337q-9 51 -40 114.5t-75 114.5h365q149 100 355 156.5t432 56.5q224 0 421 -56t348 -157z" /> + <glyph glyph-name="f263" unicode="&#xf263;" horiz-adv-x="1280" +d="M640 629q-188 0 -321 133t-133 320q0 188 133 321t321 133t321 -133t133 -321q0 -187 -133 -320t-321 -133zM640 1306q-92 0 -157.5 -65.5t-65.5 -158.5q0 -92 65.5 -157.5t157.5 -65.5t157.5 65.5t65.5 157.5q0 93 -65.5 158.5t-157.5 65.5zM1163 574q13 -27 15 -49.5 +t-4.5 -40.5t-26.5 -38.5t-42.5 -37t-61.5 -41.5q-115 -73 -315 -94l73 -72l267 -267q30 -31 30 -74t-30 -73l-12 -13q-31 -30 -74 -30t-74 30q-67 68 -267 268l-267 -268q-31 -30 -74 -30t-73 30l-12 13q-31 30 -31 73t31 74l267 267l72 72q-203 21 -317 94 +q-39 25 -61.5 41.5t-42.5 37t-26.5 38.5t-4.5 40.5t15 49.5q10 20 28 35t42 22t56 -2t65 -35q5 -4 15 -11t43 -24.5t69 -30.5t92 -24t113 -11q91 0 174 25.5t120 50.5l38 25q33 26 65 35t56 2t42 -22t28 -35z" /> + <glyph glyph-name="_574" unicode="&#xf264;" +d="M927 956q0 -66 -46.5 -112.5t-112.5 -46.5t-112.5 46.5t-46.5 112.5t46.5 112.5t112.5 46.5t112.5 -46.5t46.5 -112.5zM1141 593q-10 20 -28 32t-47.5 9.5t-60.5 -27.5q-10 -8 -29 -20t-81 -32t-127 -20t-124 18t-86 36l-27 18q-31 25 -60.5 27.5t-47.5 -9.5t-28 -32 +q-22 -45 -2 -74.5t87 -73.5q83 -53 226 -67l-51 -52q-142 -142 -191 -190q-22 -22 -22 -52.5t22 -52.5l9 -9q22 -22 52.5 -22t52.5 22l191 191q114 -115 191 -191q22 -22 52.5 -22t52.5 22l9 9q22 22 22 52.5t-22 52.5l-191 190l-52 52q141 14 225 67q67 44 87 73.5t-2 74.5 +zM1092 956q0 134 -95 229t-229 95t-229 -95t-95 -229t95 -229t229 -95t229 95t95 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="_575" unicode="&#xf265;" horiz-adv-x="1720" +d="M1565 1408q65 0 110 -45.5t45 -110.5v-519q0 -176 -68 -336t-182.5 -275t-274 -182.5t-334.5 -67.5q-176 0 -335.5 67.5t-274.5 182.5t-183 275t-68 336v519q0 64 46 110t110 46h1409zM861 344q47 0 82 33l404 388q37 35 37 85q0 49 -34.5 83.5t-83.5 34.5q-47 0 -82 -33 +l-323 -310l-323 310q-35 33 -81 33q-49 0 -83.5 -34.5t-34.5 -83.5q0 -51 36 -85l405 -388q33 -33 81 -33z" /> + <glyph glyph-name="_576" unicode="&#xf266;" horiz-adv-x="2304" +d="M1494 -103l-295 695q-25 -49 -158.5 -305.5t-198.5 -389.5q-1 -1 -27.5 -0.5t-26.5 1.5q-82 193 -255.5 587t-259.5 596q-21 50 -66.5 107.5t-103.5 100.5t-102 43q0 5 -0.5 24t-0.5 27h583v-50q-39 -2 -79.5 -16t-66.5 -43t-10 -64q26 -59 216.5 -499t235.5 -540 +q31 61 140 266.5t131 247.5q-19 39 -126 281t-136 295q-38 69 -201 71v50l513 -1v-47q-60 -2 -93.5 -25t-12.5 -69q33 -70 87 -189.5t86 -187.5q110 214 173 363q24 55 -10 79.5t-129 26.5q1 7 1 25v24q64 0 170.5 0.5t180 1t92.5 0.5v-49q-62 -2 -119 -33t-90 -81 +l-213 -442q13 -33 127.5 -290t121.5 -274l441 1017q-14 38 -49.5 62.5t-65 31.5t-55.5 8v50l460 -4l1 -2l-1 -44q-139 -4 -201 -145q-526 -1216 -559 -1291h-49z" /> + <glyph glyph-name="_577" unicode="&#xf267;" horiz-adv-x="1792" +d="M949 643q0 -26 -16.5 -45t-41.5 -19q-26 0 -45 16.5t-19 41.5q0 26 17 45t42 19t44 -16.5t19 -41.5zM964 585l350 581q-9 -8 -67.5 -62.5t-125.5 -116.5t-136.5 -127t-117 -110.5t-50.5 -51.5l-349 -580q7 7 67 62t126 116.5t136 127t117 111t50 50.5zM1611 640 +q0 -201 -104 -371q-3 2 -17 11t-26.5 16.5t-16.5 7.5q-13 0 -13 -13q0 -10 59 -44q-74 -112 -184.5 -190.5t-241.5 -110.5l-16 67q-1 10 -15 10q-5 0 -8 -5.5t-2 -9.5l16 -68q-72 -15 -146 -15q-199 0 -372 105q1 2 13 20.5t21.5 33.5t9.5 19q0 13 -13 13q-6 0 -17 -14.5 +t-22.5 -34.5t-13.5 -23q-113 75 -192 187.5t-110 244.5l69 15q10 3 10 15q0 5 -5.5 8t-10.5 2l-68 -15q-14 72 -14 139q0 206 109 379q2 -1 18.5 -12t30 -19t17.5 -8q13 0 13 12q0 6 -12.5 15.5t-32.5 21.5l-20 12q77 112 189 189t244 107l15 -67q2 -10 15 -10q5 0 8 5.5 +t2 10.5l-15 66q71 13 134 13q204 0 379 -109q-39 -56 -39 -65q0 -13 12 -13q11 0 48 64q111 -75 187.5 -186t107.5 -241l-56 -12q-10 -2 -10 -16q0 -5 5.5 -8t9.5 -2l57 13q14 -72 14 -140zM1696 640q0 163 -63.5 311t-170.5 255t-255 170.5t-311 63.5t-311 -63.5 +t-255 -170.5t-170.5 -255t-63.5 -311t63.5 -311t170.5 -255t255 -170.5t311 -63.5t311 63.5t255 170.5t170.5 255t63.5 311zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191 +t191 -286t71 -348z" /> + <glyph glyph-name="_578" unicode="&#xf268;" horiz-adv-x="1792" +d="M893 1536q240 2 451 -120q232 -134 352 -372l-742 39q-160 9 -294 -74.5t-185 -229.5l-276 424q128 159 311 245.5t383 87.5zM146 1131l337 -663q72 -143 211 -217t293 -45l-230 -451q-212 33 -385 157.5t-272.5 316t-99.5 411.5q0 267 146 491zM1732 962 +q58 -150 59.5 -310.5t-48.5 -306t-153 -272t-246 -209.5q-230 -133 -498 -119l405 623q88 131 82.5 290.5t-106.5 277.5zM896 942q125 0 213.5 -88.5t88.5 -213.5t-88.5 -213.5t-213.5 -88.5t-213.5 88.5t-88.5 213.5t88.5 213.5t213.5 88.5z" /> + <glyph glyph-name="_579" unicode="&#xf269;" horiz-adv-x="1792" +d="M903 -256q-283 0 -504.5 150.5t-329.5 398.5q-58 131 -67 301t26 332.5t111 312t179 242.5l-11 -281q11 14 68 15.5t70 -15.5q42 81 160.5 138t234.5 59q-54 -45 -119.5 -148.5t-58.5 -163.5q25 -8 62.5 -13.5t63 -7.5t68 -4t50.5 -3q15 -5 9.5 -45.5t-30.5 -75.5 +q-5 -7 -16.5 -18.5t-56.5 -35.5t-101 -34l15 -189l-139 67q-18 -43 -7.5 -81.5t36 -66.5t65.5 -41.5t81 -6.5q51 9 98 34.5t83.5 45t73.5 17.5q61 -4 89.5 -33t19.5 -65q-1 -2 -2.5 -5.5t-8.5 -12.5t-18 -15.5t-31.5 -10.5t-46.5 -1q-60 -95 -144.5 -135.5t-209.5 -29.5 +q74 -61 162.5 -82.5t168.5 -6t154.5 52t128 87.5t80.5 104q43 91 39 192.5t-37.5 188.5t-78.5 125q87 -38 137 -79.5t77 -112.5q15 170 -57.5 343t-209.5 284q265 -77 412 -279.5t151 -517.5q2 -127 -40.5 -255t-123.5 -238t-189 -196t-247.5 -135.5t-288.5 -49.5z" /> + <glyph glyph-name="_580" unicode="&#xf26a;" horiz-adv-x="1792" +d="M1493 1308q-165 110 -359 110q-155 0 -293 -73t-240 -200q-75 -93 -119.5 -218t-48.5 -266v-42q4 -141 48.5 -266t119.5 -218q102 -127 240 -200t293 -73q194 0 359 110q-121 -108 -274.5 -168t-322.5 -60q-29 0 -43 1q-175 8 -333 82t-272 193t-181 281t-67 339 +q0 182 71 348t191 286t286 191t348 71h3q168 -1 320.5 -60.5t273.5 -167.5zM1792 640q0 -192 -77 -362.5t-213 -296.5q-104 -63 -222 -63q-137 0 -255 84q154 56 253.5 233t99.5 405q0 227 -99 404t-253 234q119 83 254 83q119 0 226 -65q135 -125 210.5 -295t75.5 -361z +" /> + <glyph glyph-name="_581" unicode="&#xf26b;" horiz-adv-x="1792" +d="M1792 599q0 -56 -7 -104h-1151q0 -146 109.5 -244.5t257.5 -98.5q99 0 185.5 46.5t136.5 130.5h423q-56 -159 -170.5 -281t-267.5 -188.5t-321 -66.5q-187 0 -356 83q-228 -116 -394 -116q-237 0 -237 263q0 115 45 275q17 60 109 229q199 360 475 606 +q-184 -79 -427 -354q63 274 283.5 449.5t501.5 175.5q30 0 45 -1q255 117 433 117q64 0 116 -13t94.5 -40.5t66.5 -76.5t24 -115q0 -116 -75 -286q101 -182 101 -390zM1722 1239q0 83 -53 132t-137 49q-108 0 -254 -70q121 -47 222.5 -131.5t170.5 -195.5q51 135 51 216z +M128 2q0 -86 48.5 -132.5t134.5 -46.5q115 0 266 83q-122 72 -213.5 183t-137.5 245q-98 -205 -98 -332zM632 715h728q-5 142 -113 237t-251 95q-144 0 -251.5 -95t-112.5 -237z" /> + <glyph glyph-name="_582" unicode="&#xf26c;" horiz-adv-x="2048" +d="M1792 288v960q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1248v-960q0 -66 -47 -113t-113 -47h-736v-128h352q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23 +v64q0 14 9 23t23 9h352v128h-736q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" /> + <glyph glyph-name="_583" unicode="&#xf26d;" horiz-adv-x="1792" +d="M138 1408h197q-70 -64 -126 -149q-36 -56 -59 -115t-30 -125.5t-8.5 -120t10.5 -132t21 -126t28 -136.5q4 -19 6 -28q51 -238 81 -329q57 -171 152 -275h-272q-48 0 -82 34t-34 82v1304q0 48 34 82t82 34zM1346 1408h308q48 0 82 -34t34 -82v-1304q0 -48 -34 -82t-82 -34 +h-178q212 210 196 565l-469 -101q-2 -45 -12 -82t-31 -72t-59.5 -59.5t-93.5 -36.5q-123 -26 -199 40q-32 27 -53 61t-51.5 129t-64.5 258q-35 163 -45.5 263t-5.5 139t23 77q20 41 62.5 73t102.5 45q45 12 83.5 6.5t67 -17t54 -35t43 -48t34.5 -56.5l468 100 +q-68 175 -180 287z" /> + <glyph glyph-name="_584" unicode="&#xf26e;" +d="M1401 -11l-6 -6q-113 -113 -259 -175q-154 -64 -317 -64q-165 0 -317 64q-148 63 -259 175q-113 112 -175 258q-42 103 -54 189q-4 28 48 36q51 8 56 -20q1 -1 1 -4q18 -90 46 -159q50 -124 152 -226q98 -98 226 -152q132 -56 276 -56q143 0 276 56q128 55 225 152l6 6 +q10 10 25 6q12 -3 33 -22q36 -37 17 -58zM929 604l-66 -66l63 -63q21 -21 -7 -49q-17 -17 -32 -17q-10 0 -19 10l-62 61l-66 -66q-5 -5 -15 -5q-15 0 -31 16l-2 2q-18 15 -18 29q0 7 8 17l66 65l-66 66q-16 16 14 45q18 18 31 18q6 0 13 -5l65 -66l65 65q18 17 48 -13 +q27 -27 11 -44zM1400 547q0 -118 -46 -228q-45 -105 -126 -186q-80 -80 -187 -126t-228 -46t-228 46t-187 126q-82 82 -125 186q-15 33 -15 40h-1q-9 27 43 44q50 16 60 -12q37 -99 97 -167h1v339v2q3 136 102 232q105 103 253 103q147 0 251 -103t104 -249 +q0 -147 -104.5 -251t-250.5 -104q-58 0 -112 16q-28 11 -13 61q16 51 44 43l14 -3q14 -3 33 -6t30 -3q104 0 176 71.5t72 174.5q0 101 -72 171q-71 71 -175 71q-107 0 -178 -80q-64 -72 -64 -160v-413q110 -67 242 -67q96 0 185 36.5t156 103.5t103.5 155t36.5 183 +q0 198 -141 339q-140 140 -339 140q-200 0 -340 -140q-53 -53 -77 -87l-2 -2q-8 -11 -13 -15.5t-21.5 -9.5t-38.5 3q-21 5 -36.5 16.5t-15.5 26.5v680q0 15 10.5 26.5t27.5 11.5h877q30 0 30 -55t-30 -55h-811v-483h1q40 42 102 84t108 61q109 46 231 46q121 0 228 -46 +t187 -126q81 -81 126 -186q46 -112 46 -229zM1369 1128q9 -8 9 -18t-5.5 -18t-16.5 -21q-26 -26 -39 -26q-9 0 -16 7q-106 91 -207 133q-128 56 -276 56q-133 0 -262 -49q-27 -10 -45 37q-9 25 -8 38q3 16 16 20q130 57 299 57q164 0 316 -64q137 -58 235 -152z" /> + <glyph glyph-name="_585" unicode="&#xf270;" horiz-adv-x="1792" +d="M1551 60q15 6 26 3t11 -17.5t-15 -33.5q-13 -16 -44 -43.5t-95.5 -68t-141 -74t-188 -58t-229.5 -24.5q-119 0 -238 31t-209 76.5t-172.5 104t-132.5 105t-84 87.5q-8 9 -10 16.5t1 12t8 7t11.5 2t11.5 -4.5q192 -117 300 -166q389 -176 799 -90q190 40 391 135z +M1758 175q11 -16 2.5 -69.5t-28.5 -102.5q-34 -83 -85 -124q-17 -14 -26 -9t0 24q21 45 44.5 121.5t6.5 98.5q-5 7 -15.5 11.5t-27 6t-29.5 2.5t-35 0t-31.5 -2t-31 -3t-22.5 -2q-6 -1 -13 -1.5t-11 -1t-8.5 -1t-7 -0.5h-5.5h-4.5t-3 0.5t-2 1.5l-1.5 3q-6 16 47 40t103 30 +q46 7 108 1t76 -24zM1364 618q0 -31 13.5 -64t32 -58t37.5 -46t33 -32l13 -11l-227 -224q-40 37 -79 75.5t-58 58.5l-19 20q-11 11 -25 33q-38 -59 -97.5 -102.5t-127.5 -63.5t-140 -23t-137.5 21t-117.5 65.5t-83 113t-31 162.5q0 84 28 154t72 116.5t106.5 83t122.5 57 +t130 34.5t119.5 18.5t99.5 6.5v127q0 65 -21 97q-34 53 -121 53q-6 0 -16.5 -1t-40.5 -12t-56 -29.5t-56 -59.5t-48 -96l-294 27q0 60 22 119t67 113t108 95t151.5 65.5t190.5 24.5q100 0 181 -25t129.5 -61.5t81 -83t45 -86t12.5 -73.5v-589zM692 597q0 -86 70 -133 +q66 -44 139 -22q84 25 114 123q14 45 14 101v162q-59 -2 -111 -12t-106.5 -33.5t-87 -71t-32.5 -114.5z" /> + <glyph glyph-name="_586" unicode="&#xf271;" horiz-adv-x="1792" +d="M1536 1280q52 0 90 -38t38 -90v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128zM1152 1376v-288q0 -14 9 -23t23 -9 +h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 1376v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM1536 -128v1024h-1408v-1024h1408zM896 448h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224 +v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224z" /> + <glyph glyph-name="_587" unicode="&#xf272;" horiz-adv-x="1792" +d="M1152 416v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23 +t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47 +t47 -113v-96h128q52 0 90 -38t38 -90z" /> + <glyph glyph-name="_588" unicode="&#xf273;" horiz-adv-x="1792" +d="M1111 151l-46 -46q-9 -9 -22 -9t-23 9l-188 189l-188 -189q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22t9 23l189 188l-189 188q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l188 -188l188 188q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23l-188 -188l188 -188q9 -10 9 -23t-9 -22z +M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 +q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" /> + <glyph glyph-name="_589" unicode="&#xf274;" horiz-adv-x="1792" +d="M1303 572l-512 -512q-10 -9 -23 -9t-23 9l-288 288q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l220 -220l444 444q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23 +t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47 +t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" /> + <glyph glyph-name="_590" unicode="&#xf275;" horiz-adv-x="1792" +d="M448 1536q26 0 45 -19t19 -45v-891l536 429q17 14 40 14q26 0 45 -19t19 -45v-379l536 429q17 14 40 14q26 0 45 -19t19 -45v-1152q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h384z" /> + <glyph glyph-name="_591" unicode="&#xf276;" horiz-adv-x="1024" +d="M512 448q66 0 128 15v-655q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v655q62 -15 128 -15zM512 1536q212 0 362 -150t150 -362t-150 -362t-362 -150t-362 150t-150 362t150 362t362 150zM512 1312q14 0 23 9t9 23t-9 23t-23 9q-146 0 -249 -103t-103 -249 +q0 -14 9 -23t23 -9t23 9t9 23q0 119 84.5 203.5t203.5 84.5z" /> + <glyph glyph-name="_592" unicode="&#xf277;" horiz-adv-x="1792" +d="M1745 1239q10 -10 10 -23t-10 -23l-141 -141q-28 -28 -68 -28h-1344q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h576v64q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-64h512q40 0 68 -28zM768 320h256v-512q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v512zM1600 768 +q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-1344q-40 0 -68 28l-141 141q-10 10 -10 23t10 23l141 141q28 28 68 28h512v192h256v-192h576z" /> + <glyph glyph-name="_593" unicode="&#xf278;" horiz-adv-x="2048" +d="M2020 1525q28 -20 28 -53v-1408q0 -20 -11 -36t-29 -23l-640 -256q-24 -11 -48 0l-616 246l-616 -246q-10 -5 -24 -5q-19 0 -36 11q-28 20 -28 53v1408q0 20 11 36t29 23l640 256q24 11 48 0l616 -246l616 246q32 13 60 -6zM736 1390v-1270l576 -230v1270zM128 1173 +v-1270l544 217v1270zM1920 107v1270l-544 -217v-1270z" /> + <glyph glyph-name="_594" unicode="&#xf279;" horiz-adv-x="1792" +d="M512 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472q0 20 17 28l480 256q7 4 15 4zM1760 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472 +q0 20 17 28l480 256q7 4 15 4zM640 1536q8 0 14 -3l512 -256q18 -10 18 -29v-1472q0 -13 -9.5 -22.5t-22.5 -9.5q-8 0 -14 3l-512 256q-18 10 -18 29v1472q0 13 9.5 22.5t22.5 9.5z" /> + <glyph glyph-name="_595" unicode="&#xf27a;" horiz-adv-x="1792" +d="M640 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 640q0 53 -37.5 90.5t-90.5 37.5 +t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-110 0 -211 18q-173 -173 -435 -229q-52 -10 -86 -13q-12 -1 -22 6t-13 18q-4 15 20 37q5 5 23.5 21.5t25.5 23.5t23.5 25.5t24 31.5t20.5 37 +t20 48t14.5 57.5t12.5 72.5q-146 90 -229.5 216.5t-83.5 269.5q0 174 120 321.5t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" /> + <glyph glyph-name="_596" unicode="&#xf27b;" horiz-adv-x="1792" +d="M640 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 -53 -37.5 -90.5t-90.5 -37.5 +t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5 +t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51 +t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 130 71 248.5t191 204.5t286 136.5t348 50.5t348 -50.5t286 -136.5t191 -204.5t71 -248.5z" /> + <glyph glyph-name="_597" unicode="&#xf27c;" horiz-adv-x="1024" +d="M512 345l512 295v-591l-512 -296v592zM0 640v-591l512 296zM512 1527v-591l-512 -296v591zM512 936l512 295v-591z" /> + <glyph glyph-name="_598" unicode="&#xf27d;" horiz-adv-x="1792" +d="M1709 1018q-10 -236 -332 -651q-333 -431 -562 -431q-142 0 -240 263q-44 160 -132 482q-72 262 -157 262q-18 0 -127 -76l-77 98q24 21 108 96.5t130 115.5q156 138 241 146q95 9 153 -55.5t81 -203.5q44 -287 66 -373q55 -249 120 -249q51 0 154 161q101 161 109 246 +q13 139 -109 139q-57 0 -121 -26q120 393 459 382q251 -8 236 -326z" /> + <glyph glyph-name="f27e" unicode="&#xf27e;" +d="M0 1408h1536v-1536h-1536v1536zM1085 293l-221 631l221 297h-634l221 -297l-221 -631l317 -304z" /> + <glyph glyph-name="uniF280" unicode="&#xf280;" +d="M0 1408h1536v-1536h-1536v1536zM908 1088l-12 -33l75 -83l-31 -114l25 -25l107 57l107 -57l25 25l-31 114l75 83l-12 33h-95l-53 96h-32l-53 -96h-95zM641 925q32 0 44.5 -16t11.5 -63l174 21q0 55 -17.5 92.5t-50.5 56t-69 25.5t-85 7q-133 0 -199 -57.5t-66 -182.5v-72 +h-96v-128h76q20 0 20 -8v-382q0 -14 -5 -20t-18 -7l-73 -7v-88h448v86l-149 14q-6 1 -8.5 1.5t-3.5 2.5t-0.5 4t1 7t0.5 10v387h191l38 128h-231q-6 0 -2 6t4 9v80q0 27 1.5 40.5t7.5 28t19.5 20t36.5 5.5zM1248 96v86l-54 9q-7 1 -9.5 2.5t-2.5 3t1 7.5t1 12v520h-275 +l-23 -101l83 -22q23 -7 23 -27v-370q0 -14 -6 -18.5t-20 -6.5l-70 -9v-86h352z" /> + <glyph glyph-name="uniF281" unicode="&#xf281;" horiz-adv-x="1792" +d="M1792 690q0 -58 -29.5 -105.5t-79.5 -72.5q12 -46 12 -96q0 -155 -106.5 -287t-290.5 -208.5t-400 -76.5t-399.5 76.5t-290 208.5t-106.5 287q0 47 11 94q-51 25 -82 73.5t-31 106.5q0 82 58 140.5t141 58.5q85 0 145 -63q218 152 515 162l116 521q3 13 15 21t26 5 +l369 -81q18 37 54 59.5t79 22.5q62 0 106 -43.5t44 -105.5t-44 -106t-106 -44t-105.5 43.5t-43.5 105.5l-334 74l-104 -472q300 -9 519 -160q58 61 143 61q83 0 141 -58.5t58 -140.5zM418 491q0 -62 43.5 -106t105.5 -44t106 44t44 106t-44 105.5t-106 43.5q-61 0 -105 -44 +t-44 -105zM1228 136q11 11 11 26t-11 26q-10 10 -25 10t-26 -10q-41 -42 -121 -62t-160 -20t-160 20t-121 62q-11 10 -26 10t-25 -10q-11 -10 -11 -25.5t11 -26.5q43 -43 118.5 -68t122.5 -29.5t91 -4.5t91 4.5t122.5 29.5t118.5 68zM1225 341q62 0 105.5 44t43.5 106 +q0 61 -44 105t-105 44q-62 0 -106 -43.5t-44 -105.5t44 -106t106 -44z" /> + <glyph glyph-name="_602" unicode="&#xf282;" horiz-adv-x="1792" +d="M69 741h1q16 126 58.5 241.5t115 217t167.5 176t223.5 117.5t276.5 43q231 0 414 -105.5t294 -303.5q104 -187 104 -442v-188h-1125q1 -111 53.5 -192.5t136.5 -122.5t189.5 -57t213 -3t208 46.5t173.5 84.5v-377q-92 -55 -229.5 -92t-312.5 -38t-316 53 +q-189 73 -311.5 249t-124.5 372q-3 242 111 412t325 268q-48 -60 -78 -125.5t-46 -159.5h635q8 77 -8 140t-47 101.5t-70.5 66.5t-80.5 41t-75 20.5t-56 8.5l-22 1q-135 -5 -259.5 -44.5t-223.5 -104.5t-176 -140.5t-138 -163.5z" /> + <glyph glyph-name="_603" unicode="&#xf283;" horiz-adv-x="2304" +d="M0 32v608h2304v-608q0 -66 -47 -113t-113 -47h-1984q-66 0 -113 47t-47 113zM640 256v-128h384v128h-384zM256 256v-128h256v128h-256zM2144 1408q66 0 113 -47t47 -113v-224h-2304v224q0 66 47 113t113 47h1984z" /> + <glyph glyph-name="_604" unicode="&#xf284;" horiz-adv-x="1792" +d="M1584 246l-218 111q-74 -120 -196.5 -189t-263.5 -69q-147 0 -271 72t-196 196t-72 270q0 110 42.5 209.5t115 172t172 115t209.5 42.5q131 0 247.5 -60.5t192.5 -168.5l215 125q-110 169 -286.5 265t-378.5 96q-161 0 -308 -63t-253 -169t-169 -253t-63 -308t63 -308 +t169 -253t253 -169t308 -63q213 0 397.5 107t290.5 292zM1030 643l693 -352q-116 -253 -334.5 -400t-492.5 -147q-182 0 -348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71q260 0 470.5 -133.5t335.5 -366.5zM1543 640h-39v-160h-96v352h136q32 0 54.5 -20 +t28.5 -48t1 -56t-27.5 -48t-57.5 -20z" /> + <glyph glyph-name="uniF285" unicode="&#xf285;" horiz-adv-x="1792" +d="M1427 827l-614 386l92 151h855zM405 562l-184 116v858l1183 -743zM1424 697l147 -95v-858l-532 335zM1387 718l-500 -802h-855l356 571z" /> + <glyph glyph-name="uniF286" unicode="&#xf286;" horiz-adv-x="1792" +d="M640 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1152 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1664 496v-752h-640v320q0 80 -56 136t-136 56t-136 -56t-56 -136v-320h-640v752q0 16 16 16h96 +q16 0 16 -16v-112h128v624q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 6 2.5 9.5t8.5 5t9.5 2t11.5 0t9 -0.5v391q-32 15 -32 50q0 23 16.5 39t38.5 16t38.5 -16t16.5 -39q0 -35 -32 -50v-17q45 10 83 10q21 0 59.5 -7.5t54.5 -7.5 +q17 0 47 7.5t37 7.5q16 0 16 -16v-210q0 -15 -35 -21.5t-62 -6.5q-18 0 -54.5 7.5t-55.5 7.5q-40 0 -90 -12v-133q1 0 9 0.5t11.5 0t9.5 -2t8.5 -5t2.5 -9.5v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-624h128v112q0 16 16 16h96 +q16 0 16 -16z" /> + <glyph glyph-name="_607" unicode="&#xf287;" horiz-adv-x="2304" +d="M2288 731q16 -8 16 -27t-16 -27l-320 -192q-8 -5 -16 -5q-9 0 -16 4q-16 10 -16 28v128h-858q37 -58 83 -165q16 -37 24.5 -55t24 -49t27 -47t27 -34t31.5 -26t33 -8h96v96q0 14 9 23t23 9h320q14 0 23 -9t9 -23v-320q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v96h-96 +q-32 0 -61 10t-51 23.5t-45 40.5t-37 46t-33.5 57t-28.5 57.5t-28 60.5q-23 53 -37 81.5t-36 65t-44.5 53.5t-46.5 17h-360q-22 -84 -91 -138t-157 -54q-106 0 -181 75t-75 181t75 181t181 75q88 0 157 -54t91 -138h104q24 0 46.5 17t44.5 53.5t36 65t37 81.5q19 41 28 60.5 +t28.5 57.5t33.5 57t37 46t45 40.5t51 23.5t61 10h107q21 57 70 92.5t111 35.5q80 0 136 -56t56 -136t-56 -136t-136 -56q-62 0 -111 35.5t-70 92.5h-107q-17 0 -33 -8t-31.5 -26t-27 -34t-27 -47t-24 -49t-24.5 -55q-46 -107 -83 -165h1114v128q0 18 16 28t32 -1z" /> + <glyph glyph-name="_608" unicode="&#xf288;" horiz-adv-x="1792" +d="M1150 774q0 -56 -39.5 -95t-95.5 -39h-253v269h253q56 0 95.5 -39.5t39.5 -95.5zM1329 774q0 130 -91.5 222t-222.5 92h-433v-896h180v269h253q130 0 222 91.5t92 221.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348 +t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" /> + <glyph glyph-name="_609" unicode="&#xf289;" horiz-adv-x="2304" +d="M1645 438q0 59 -34 106.5t-87 68.5q-7 -45 -23 -92q-7 -24 -27.5 -38t-44.5 -14q-12 0 -24 3q-31 10 -45 38.5t-4 58.5q23 71 23 143q0 123 -61 227.5t-166 165.5t-228 61q-134 0 -247 -73t-167 -194q108 -28 188 -106q22 -23 22 -55t-22 -54t-54 -22t-55 22 +q-75 75 -180 75q-106 0 -181 -74.5t-75 -180.5t75 -180.5t181 -74.5h1046q79 0 134.5 55.5t55.5 133.5zM1798 438q0 -142 -100.5 -242t-242.5 -100h-1046q-169 0 -289 119.5t-120 288.5q0 153 100 267t249 136q62 184 221 298t354 114q235 0 408.5 -158.5t196.5 -389.5 +q116 -25 192.5 -118.5t76.5 -214.5zM2048 438q0 -175 -97 -319q-23 -33 -64 -33q-24 0 -43 13q-26 17 -32 48.5t12 57.5q71 104 71 233t-71 233q-18 26 -12 57t32 49t57.5 11.5t49.5 -32.5q97 -142 97 -318zM2304 438q0 -244 -134 -443q-23 -34 -64 -34q-23 0 -42 13 +q-26 18 -32.5 49t11.5 57q108 164 108 358q0 195 -108 357q-18 26 -11.5 57.5t32.5 48.5q26 18 57 12t49 -33q134 -198 134 -442z" /> + <glyph glyph-name="_610" unicode="&#xf28a;" +d="M1500 -13q0 -89 -63 -152.5t-153 -63.5t-153.5 63.5t-63.5 152.5q0 90 63.5 153.5t153.5 63.5t153 -63.5t63 -153.5zM1267 268q-115 -15 -192.5 -102.5t-77.5 -205.5q0 -74 33 -138q-146 -78 -379 -78q-109 0 -201 21t-153.5 54.5t-110.5 76.5t-76 85t-44.5 83 +t-23.5 66.5t-6 39.5q0 19 4.5 42.5t18.5 56t36.5 58t64 43.5t94.5 18t94 -17.5t63 -41t35.5 -53t17.5 -49t4 -33.5q0 -34 -23 -81q28 -27 82 -42t93 -17l40 -1q115 0 190 51t75 133q0 26 -9 48.5t-31.5 44.5t-49.5 41t-74 44t-93.5 47.5t-119.5 56.5q-28 13 -43 20 +q-116 55 -187 100t-122.5 102t-72 125.5t-20.5 162.5q0 78 20.5 150t66 137.5t112.5 114t166.5 77t221.5 28.5q120 0 220 -26t164.5 -67t109.5 -94t64 -105.5t19 -103.5q0 -46 -15 -82.5t-36.5 -58t-48.5 -36t-49 -19.5t-39 -5h-8h-32t-39 5t-44 14t-41 28t-37 46t-24 70.5 +t-10 97.5q-15 16 -59 25.5t-81 10.5l-37 1q-68 0 -117.5 -31t-70.5 -70t-21 -76q0 -24 5 -43t24 -46t53 -51t97 -53.5t150 -58.5q76 -25 138.5 -53.5t109 -55.5t83 -59t60.5 -59.5t41 -62.5t26.5 -62t14.5 -63.5t6 -62t1 -62.5z" /> + <glyph glyph-name="_611" unicode="&#xf28b;" +d="M704 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1152 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103 +t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="_612" unicode="&#xf28c;" +d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 +t73 -273t198 -198t273 -73zM864 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192z" /> + <glyph glyph-name="_613" unicode="&#xf28d;" +d="M1088 352v576q0 14 -9 23t-23 9h-576q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h576q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 +t103 -385.5z" /> + <glyph glyph-name="_614" unicode="&#xf28e;" +d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 +t73 -273t198 -198t273 -73zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h576q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-576z" /> + <glyph glyph-name="_615" unicode="&#xf290;" horiz-adv-x="1792" +d="M1757 128l35 -313q3 -28 -16 -50q-19 -21 -48 -21h-1664q-29 0 -48 21q-19 22 -16 50l35 313h1722zM1664 967l86 -775h-1708l86 775q3 24 21 40.5t43 16.5h256v-128q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5v128h384v-128q0 -53 37.5 -90.5t90.5 -37.5 +t90.5 37.5t37.5 90.5v128h256q25 0 43 -16.5t21 -40.5zM1280 1152v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 159 112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" /> + <glyph glyph-name="_616" unicode="&#xf291;" horiz-adv-x="2048" +d="M1920 768q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5h-15l-115 -662q-8 -46 -44 -76t-82 -30h-1280q-46 0 -82 30t-44 76l-115 662h-15q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5h1792zM485 -32q26 2 43.5 22.5t15.5 46.5l-32 416q-2 26 -22.5 43.5 +t-46.5 15.5t-43.5 -22.5t-15.5 -46.5l32 -416q2 -25 20.5 -42t43.5 -17h5zM896 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1280 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1632 27l32 416 +q2 26 -15.5 46.5t-43.5 22.5t-46.5 -15.5t-22.5 -43.5l-32 -416q-2 -26 15.5 -46.5t43.5 -22.5h5q25 0 43.5 17t20.5 42zM476 1244l-93 -412h-132l101 441q19 88 89 143.5t160 55.5h167q0 26 19 45t45 19h384q26 0 45 -19t19 -45h167q90 0 160 -55.5t89 -143.5l101 -441 +h-132l-93 412q-11 44 -45.5 72t-79.5 28h-167q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45h-167q-45 0 -79.5 -28t-45.5 -72z" /> + <glyph glyph-name="_617" unicode="&#xf292;" horiz-adv-x="1792" +d="M991 512l64 256h-254l-64 -256h254zM1759 1016l-56 -224q-7 -24 -31 -24h-327l-64 -256h311q15 0 25 -12q10 -14 6 -28l-56 -224q-5 -24 -31 -24h-327l-81 -328q-7 -24 -31 -24h-224q-16 0 -26 12q-9 12 -6 28l78 312h-254l-81 -328q-7 -24 -31 -24h-225q-15 0 -25 12 +q-9 12 -6 28l78 312h-311q-15 0 -25 12q-9 12 -6 28l56 224q7 24 31 24h327l64 256h-311q-15 0 -25 12q-10 14 -6 28l56 224q5 24 31 24h327l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h254l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h311 +q15 0 25 -12q9 -12 6 -28z" /> + <glyph glyph-name="_618" unicode="&#xf293;" +d="M841 483l148 -148l-149 -149zM840 1094l149 -149l-148 -148zM710 -130l464 464l-306 306l306 306l-464 464v-611l-255 255l-93 -93l320 -321l-320 -321l93 -93l255 255v-611zM1429 640q0 -209 -32 -365.5t-87.5 -257t-140.5 -162.5t-181.5 -86.5t-219.5 -24.5 +t-219.5 24.5t-181.5 86.5t-140.5 162.5t-87.5 257t-32 365.5t32 365.5t87.5 257t140.5 162.5t181.5 86.5t219.5 24.5t219.5 -24.5t181.5 -86.5t140.5 -162.5t87.5 -257t32 -365.5z" /> + <glyph glyph-name="_619" unicode="&#xf294;" horiz-adv-x="1024" +d="M596 113l173 172l-173 172v-344zM596 823l173 172l-173 172v-344zM628 640l356 -356l-539 -540v711l-297 -296l-108 108l372 373l-372 373l108 108l297 -296v711l539 -540z" /> + <glyph glyph-name="_620" unicode="&#xf295;" +d="M1280 256q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM512 1024q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5 +t112.5 -271.5zM1440 1344q0 -20 -13 -38l-1056 -1408q-19 -26 -51 -26h-160q-26 0 -45 19t-19 45q0 20 13 38l1056 1408q19 26 51 26h160q26 0 45 -19t19 -45zM768 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 +t271.5 -112.5t112.5 -271.5z" /> + <glyph glyph-name="_621" unicode="&#xf296;" horiz-adv-x="1792" +d="M104 830l792 -1015l-868 630q-18 13 -25 34.5t0 42.5l101 308v0zM566 830h660l-330 -1015v0zM368 1442l198 -612h-462l198 612q8 23 33 23t33 -23zM1688 830l101 -308q7 -21 0 -42.5t-25 -34.5l-868 -630l792 1015v0zM1688 830h-462l198 612q8 23 33 23t33 -23z" /> + <glyph glyph-name="_622" unicode="&#xf297;" horiz-adv-x="1792" +d="M384 704h160v224h-160v-224zM1221 372v92q-104 -36 -243 -38q-135 -1 -259.5 46.5t-220.5 122.5l1 -96q88 -80 212 -128.5t272 -47.5q129 0 238 49zM640 704h640v224h-640v-224zM1792 736q0 -187 -99 -352q89 -102 89 -229q0 -157 -129.5 -268t-313.5 -111 +q-122 0 -225 52.5t-161 140.5q-19 -1 -57 -1t-57 1q-58 -88 -161 -140.5t-225 -52.5q-184 0 -313.5 111t-129.5 268q0 127 89 229q-99 165 -99 352q0 209 120 385.5t326.5 279.5t449.5 103t449.5 -103t326.5 -279.5t120 -385.5z" /> + <glyph glyph-name="_623" unicode="&#xf298;" +d="M515 625v-128h-252v128h252zM515 880v-127h-252v127h252zM1273 369v-128h-341v128h341zM1273 625v-128h-672v128h672zM1273 880v-127h-672v127h672zM1408 20v1240q0 8 -6 14t-14 6h-32l-378 -256l-210 171l-210 -171l-378 256h-32q-8 0 -14 -6t-6 -14v-1240q0 -8 6 -14 +t14 -6h1240q8 0 14 6t6 14zM553 1130l185 150h-406zM983 1130l221 150h-406zM1536 1260v-1240q0 -62 -43 -105t-105 -43h-1240q-62 0 -105 43t-43 105v1240q0 62 43 105t105 43h1240q62 0 105 -43t43 -105z" /> + <glyph glyph-name="_624" unicode="&#xf299;" horiz-adv-x="1792" +d="M896 720q-104 196 -160 278q-139 202 -347 318q-34 19 -70 36q-89 40 -94 32t34 -38l39 -31q62 -43 112.5 -93.5t94.5 -116.5t70.5 -113t70.5 -131q9 -17 13 -25q44 -84 84 -153t98 -154t115.5 -150t131 -123.5t148.5 -90.5q153 -66 154 -60q1 3 -49 37q-53 36 -81 57 +q-77 58 -179 211t-185 310zM549 177q-76 60 -132.5 125t-98 143.5t-71 154.5t-58.5 186t-52 209t-60.5 252t-76.5 289q273 0 497.5 -36t379 -92t271 -144.5t185.5 -172.5t110 -198.5t56 -199.5t12.5 -198.5t-9.5 -173t-20 -143.5t-13 -107l323 -327h-104l-281 285 +q-22 -2 -91.5 -14t-121.5 -19t-138 -6t-160.5 17t-167.5 59t-179 111z" /> + <glyph glyph-name="_625" unicode="&#xf29a;" horiz-adv-x="1792" +d="M1374 879q-6 26 -28.5 39.5t-48.5 7.5q-261 -62 -401 -62t-401 62q-26 6 -48.5 -7.5t-28.5 -39.5t7.5 -48.5t39.5 -28.5q194 -46 303 -58q-2 -158 -15.5 -269t-26.5 -155.5t-41 -115.5l-9 -21q-10 -25 1 -49t36 -34q9 -4 23 -4q44 0 60 41l8 20q54 139 71 259h42 +q17 -120 71 -259l8 -20q16 -41 60 -41q14 0 23 4q25 10 36 34t1 49l-9 21q-28 71 -41 115.5t-26.5 155.5t-15.5 269q109 12 303 58q26 6 39.5 28.5t7.5 48.5zM1024 1024q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z +M1600 640q0 -143 -55.5 -273.5t-150 -225t-225 -150t-273.5 -55.5t-273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5zM896 1408q-156 0 -298 -61t-245 -164t-164 -245t-61 -298t61 -298 +t164 -245t245 -164t298 -61t298 61t245 164t164 245t61 298t-61 298t-164 245t-245 164t-298 61zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" /> + <glyph glyph-name="_626" unicode="&#xf29b;" +d="M1438 723q34 -35 29 -82l-44 -551q-4 -42 -34.5 -70t-71.5 -28q-6 0 -9 1q-44 3 -72.5 36.5t-25.5 77.5l35 429l-143 -8q55 -113 55 -240q0 -216 -148 -372l-137 137q91 101 91 235q0 145 -102.5 248t-247.5 103q-134 0 -236 -92l-137 138q120 114 284 141l264 300 +l-149 87l-181 -161q-33 -30 -77 -27.5t-73 35.5t-26.5 77t34.5 73l239 213q26 23 60 26.5t64 -14.5l488 -283q36 -21 48 -68q17 -67 -26 -117l-205 -232l371 20q49 3 83 -32zM1240 1180q-74 0 -126 52t-52 126t52 126t126 52t126.5 -52t52.5 -126t-52.5 -126t-126.5 -52z +M613 -62q106 0 196 61l139 -139q-146 -116 -335 -116q-148 0 -273.5 73t-198.5 198t-73 273q0 188 116 336l139 -139q-60 -88 -60 -197q0 -145 102.5 -247.5t247.5 -102.5z" /> + <glyph glyph-name="_627" unicode="&#xf29c;" +d="M880 336v-160q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v160q0 14 9 23t23 9h160q14 0 23 -9t9 -23zM1136 832q0 -50 -15 -90t-45.5 -69t-52 -44t-59.5 -36q-32 -18 -46.5 -28t-26 -24t-11.5 -29v-32q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v68q0 35 10.5 64.5 +t24 47.5t39 35.5t41 25.5t44.5 21q53 25 75 43t22 49q0 42 -43.5 71.5t-95.5 29.5q-56 0 -95 -27q-29 -20 -80 -83q-9 -12 -25 -12q-11 0 -19 6l-108 82q-10 7 -12 20t5 23q122 192 349 192q129 0 238.5 -89.5t109.5 -214.5zM768 1280q-130 0 -248.5 -51t-204 -136.5 +t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5 +t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="_628" unicode="&#xf29d;" horiz-adv-x="1408" +d="M366 1225q-64 0 -110 45.5t-46 110.5q0 64 46 109.5t110 45.5t109.5 -45.5t45.5 -109.5q0 -65 -45.5 -110.5t-109.5 -45.5zM917 583q0 -50 -30 -67.5t-63.5 -6.5t-47.5 34l-367 438q-7 12 -14 15.5t-11 1.5l-3 -3q-7 -8 4 -21l122 -139l1 -354l-161 -457 +q-67 -192 -92 -234q-15 -26 -28 -32q-50 -26 -103 -1q-29 13 -41.5 43t-9.5 57q2 17 197 618l5 416l-85 -164l35 -222q4 -24 -1 -42t-14 -27.5t-19 -16t-17 -7.5l-7 -2q-19 -3 -34.5 3t-24 16t-14 22t-7.5 19.5t-2 9.5l-46 299l211 381q23 34 113 34q75 0 107 -40l424 -521 +q7 -5 14 -17l3 -3l-1 -1q7 -13 7 -29zM514 433q43 -113 88.5 -225t69.5 -168l24 -55q36 -93 42 -125q11 -70 -36 -97q-35 -22 -66 -16t-51 22t-29 35h-1q-6 16 -8 25l-124 351zM1338 -159q31 -49 31 -57q0 -5 -3 -7q-9 -5 -14.5 0.5t-15.5 26t-16 30.5q-114 172 -423 661 +q3 -1 7 1t7 4l3 2q11 9 11 17z" /> + <glyph glyph-name="_629" unicode="&#xf29e;" horiz-adv-x="2304" +d="M504 542h171l-1 265zM1530 641q0 87 -50.5 140t-146.5 53h-54v-388h52q91 0 145 57t54 138zM956 1018l1 -756q0 -14 -9.5 -24t-23.5 -10h-216q-14 0 -23.5 10t-9.5 24v62h-291l-55 -81q-10 -15 -28 -15h-267q-21 0 -30.5 18t3.5 35l556 757q9 14 27 14h332q14 0 24 -10 +t10 -24zM1783 641q0 -193 -125.5 -303t-324.5 -110h-270q-14 0 -24 10t-10 24v756q0 14 10 24t24 10h268q200 0 326 -109t126 -302zM1939 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-44.5 -108t-73.5 -102.5h-51q38 45 66.5 104.5t41.5 112t21 98t9 72.5l1 27q0 8 -0.5 22.5 +t-7.5 60t-20 91.5t-41 111.5t-66 124.5h43q41 -47 72 -107t45.5 -111.5t23 -96t10.5 -70.5zM2123 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-45 -108t-74 -102.5h-51q38 45 66.5 104.5t41.5 112t21 98t9 72.5l1 27q0 8 -0.5 22.5t-7.5 60t-19.5 91.5t-40.5 111.5t-66 124.5 +h43q41 -47 72 -107t45.5 -111.5t23 -96t10.5 -70.5zM2304 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-44.5 -108t-73.5 -102.5h-51q38 45 66 104.5t41 112t21 98t9 72.5l1 27q0 8 -0.5 22.5t-7.5 60t-19.5 91.5t-40.5 111.5t-66 124.5h43q41 -47 72 -107t45.5 -111.5t23 -96 +t9.5 -70.5z" /> + <glyph glyph-name="uniF2A0" unicode="&#xf2a0;" horiz-adv-x="1408" +d="M617 -153q0 11 -13 58t-31 107t-20 69q-1 4 -5 26.5t-8.5 36t-13.5 21.5q-15 14 -51 14q-23 0 -70 -5.5t-71 -5.5q-34 0 -47 11q-6 5 -11 15.5t-7.5 20t-6.5 24t-5 18.5q-37 128 -37 255t37 255q1 4 5 18.5t6.5 24t7.5 20t11 15.5q13 11 47 11q24 0 71 -5.5t70 -5.5 +q36 0 51 14q9 8 13.5 21.5t8.5 36t5 26.5q2 9 20 69t31 107t13 58q0 22 -43.5 52.5t-75.5 42.5q-20 8 -45 8q-34 0 -98 -18q-57 -17 -96.5 -40.5t-71 -66t-46 -70t-45.5 -94.5q-6 -12 -9 -19q-49 -107 -68 -216t-19 -244t19 -244t68 -216q56 -122 83 -161q63 -91 179 -127 +l6 -2q64 -18 98 -18q25 0 45 8q32 12 75.5 42.5t43.5 52.5zM776 760q-26 0 -45 19t-19 45.5t19 45.5q37 37 37 90q0 52 -37 91q-19 19 -19 45t19 45t45 19t45 -19q75 -75 75 -181t-75 -181q-21 -19 -45 -19zM957 579q-27 0 -45 19q-19 19 -19 45t19 45q112 114 112 272 +t-112 272q-19 19 -19 45t19 45t45 19t45 -19q150 -150 150 -362t-150 -362q-18 -19 -45 -19zM1138 398q-27 0 -45 19q-19 19 -19 45t19 45q90 91 138.5 208t48.5 245t-48.5 245t-138.5 208q-19 19 -19 45t19 45t45 19t45 -19q109 -109 167 -249t58 -294t-58 -294t-167 -249 +q-18 -19 -45 -19z" /> + <glyph glyph-name="uniF2A1" unicode="&#xf2a1;" horiz-adv-x="2176" +d="M192 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM704 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM704 864q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1472 352 +q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1472 864q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 864 +q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 1376q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 192q0 -80 -56 -136 +t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 1216q0 -80 -56 -136t-136 -56 +t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM2176 192q0 -80 -56 -136t-136 -56t-136 56 +t-56 136t56 136t136 56t136 -56t56 -136zM1664 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM2176 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136 +t56 136t136 56t136 -56t56 -136zM2176 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136z" /> + <glyph glyph-name="uniF2A2" unicode="&#xf2a2;" horiz-adv-x="1792" +d="M128 -192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM320 0q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM365 365l256 -256l-90 -90l-256 256zM704 384q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45z +M1411 704q0 -59 -11.5 -108.5t-37.5 -93.5t-44 -67.5t-53 -64.5q-31 -35 -45.5 -54t-33.5 -50t-26.5 -64t-7.5 -74q0 -159 -112.5 -271.5t-271.5 -112.5q-26 0 -45 19t-19 45t19 45t45 19q106 0 181 75t75 181q0 57 11.5 105.5t37 91t43.5 66.5t52 63q40 46 59.5 72 +t37.5 74.5t18 103.5q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 117 45.5 223.5t123 184t184 123t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5zM896 576q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45 +t45 19t45 -19t19 -45zM1184 704q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 93 -65.5 158.5t-158.5 65.5q-92 0 -158 -65.5t-66 -158.5q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 146 103 249t249 103t249 -103t103 -249zM1578 993q10 -25 -1 -49t-36 -34q-9 -4 -23 -4 +q-19 0 -35.5 11t-23.5 30q-68 178 -224 295q-21 16 -25 42t12 47q17 21 43 25t47 -12q183 -137 266 -351zM1788 1074q9 -25 -1.5 -49t-35.5 -34q-11 -4 -23 -4q-44 0 -60 41q-92 238 -297 393q-22 16 -25.5 42t12.5 47q16 22 42 25.5t47 -12.5q235 -175 341 -449z" /> + <glyph glyph-name="uniF2A3" unicode="&#xf2a3;" horiz-adv-x="2304" +d="M1032 576q-59 2 -84 55q-17 34 -48 53.5t-68 19.5q-53 0 -90.5 -37.5t-37.5 -90.5q0 -56 36 -89l10 -8q34 -31 82 -31q37 0 68 19.5t48 53.5q25 53 84 55zM1600 704q0 56 -36 89l-10 8q-34 31 -82 31q-37 0 -68 -19.5t-48 -53.5q-25 -53 -84 -55q59 -2 84 -55 +q17 -34 48 -53.5t68 -19.5q53 0 90.5 37.5t37.5 90.5zM1174 925q-17 -35 -55 -48t-73 4q-62 31 -134 31q-51 0 -99 -17q3 0 9.5 0.5t9.5 0.5q92 0 170.5 -50t118.5 -133q17 -36 3.5 -73.5t-49.5 -54.5q-18 -9 -39 -9q21 0 39 -9q36 -17 49.5 -54.5t-3.5 -73.5 +q-40 -83 -118.5 -133t-170.5 -50h-6q-16 2 -44 4l-290 27l-239 -120q-14 -7 -29 -7q-40 0 -57 35l-160 320q-11 23 -4 47.5t29 37.5l209 119l148 267q17 155 91.5 291.5t195.5 236.5q31 25 70.5 21.5t64.5 -34.5t21.5 -70t-34.5 -65q-70 -59 -117 -128q123 84 267 101 +q40 5 71.5 -19t35.5 -64q5 -40 -19 -71.5t-64 -35.5q-84 -10 -159 -55q46 10 99 10q115 0 218 -50q36 -18 49 -55.5t-5 -73.5zM2137 1085l160 -320q11 -23 4 -47.5t-29 -37.5l-209 -119l-148 -267q-17 -155 -91.5 -291.5t-195.5 -236.5q-26 -22 -61 -22q-45 0 -74 35 +q-25 31 -21.5 70t34.5 65q70 59 117 128q-123 -84 -267 -101q-4 -1 -12 -1q-36 0 -63.5 24t-31.5 60q-5 40 19 71.5t64 35.5q84 10 159 55q-46 -10 -99 -10q-115 0 -218 50q-36 18 -49 55.5t5 73.5q17 35 55 48t73 -4q62 -31 134 -31q51 0 99 17q-3 0 -9.5 -0.5t-9.5 -0.5 +q-92 0 -170.5 50t-118.5 133q-17 36 -3.5 73.5t49.5 54.5q18 9 39 9q-21 0 -39 9q-36 17 -49.5 54.5t3.5 73.5q40 83 118.5 133t170.5 50h6h1q14 -2 42 -4l291 -27l239 120q14 7 29 7q40 0 57 -35z" /> + <glyph glyph-name="uniF2A4" unicode="&#xf2a4;" horiz-adv-x="1792" +d="M1056 704q0 -26 19 -45t45 -19t45 19t19 45q0 146 -103 249t-249 103t-249 -103t-103 -249q0 -26 19 -45t45 -19t45 19t19 45q0 93 66 158.5t158 65.5t158 -65.5t66 -158.5zM835 1280q-117 0 -223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5q0 -26 19 -45t45 -19t45 19 +t19 45q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -55 -18 -103.5t-37.5 -74.5t-59.5 -72q-34 -39 -52 -63t-43.5 -66.5t-37 -91t-11.5 -105.5q0 -106 -75 -181t-181 -75q-26 0 -45 -19t-19 -45t19 -45t45 -19q159 0 271.5 112.5t112.5 271.5q0 41 7.5 74 +t26.5 64t33.5 50t45.5 54q35 41 53 64.5t44 67.5t37.5 93.5t11.5 108.5q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5zM591 561l226 -226l-579 -579q-12 -12 -29 -12t-29 12l-168 168q-12 12 -12 29t12 29zM1612 1524l168 -168q12 -12 12 -29t-12 -30l-233 -233 +l-26 -25l-71 -71q-66 153 -195 258l91 91l207 207q13 12 30 12t29 -12z" /> + <glyph glyph-name="uniF2A5" unicode="&#xf2a5;" +d="M866 1021q0 -27 -13 -94q-11 -50 -31.5 -150t-30.5 -150q-2 -11 -4.5 -12.5t-13.5 -2.5q-20 -2 -31 -2q-58 0 -84 49.5t-26 113.5q0 88 35 174t103 124q28 14 51 14q28 0 36.5 -16.5t8.5 -47.5zM1352 597q0 14 -39 75.5t-52 66.5q-21 8 -34 8q-91 0 -226 -77l-2 2 +q3 22 27.5 135t24.5 178q0 233 -242 233q-24 0 -68 -6q-94 -17 -168.5 -89.5t-111.5 -166.5t-37 -189q0 -146 80.5 -225t227.5 -79q25 0 25 -3t-1 -5q-4 -34 -26 -117q-14 -52 -51.5 -101t-82.5 -49q-42 0 -42 47q0 24 10.5 47.5t25 39.5t29.5 28.5t26 20t11 8.5q0 3 -7 10 +q-24 22 -58.5 36.5t-65.5 14.5q-35 0 -63.5 -34t-41 -75t-12.5 -75q0 -88 51.5 -142t138.5 -54q82 0 155 53t117.5 126t65.5 153q6 22 15.5 66.5t14.5 66.5q3 12 14 18q118 60 227 60q48 0 127 -18q1 -1 4 -1q5 0 9.5 4.5t4.5 8.5zM1536 1120v-960q0 -119 -84.5 -203.5 +t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="uniF2A6" unicode="&#xf2a6;" horiz-adv-x="1535" +d="M744 1231q0 24 -2 38.5t-8.5 30t-21 23t-37.5 7.5q-39 0 -78 -23q-105 -58 -159 -190.5t-54 -269.5q0 -44 8.5 -85.5t26.5 -80.5t52.5 -62.5t81.5 -23.5q4 0 18 -0.5t20 0t16 3t15 8.5t7 16q16 77 48 231.5t48 231.5q19 91 19 146zM1498 575q0 -7 -7.5 -13.5t-15.5 -6.5 +l-6 1q-22 3 -62 11t-72 12.5t-63 4.5q-167 0 -351 -93q-15 -8 -21 -27q-10 -36 -24.5 -105.5t-22.5 -100.5q-23 -91 -70 -179.5t-112.5 -164.5t-154.5 -123t-185 -47q-135 0 -214.5 83.5t-79.5 219.5q0 53 19.5 117t63 116.5t97.5 52.5q38 0 120 -33.5t83 -61.5 +q0 -1 -16.5 -12.5t-39.5 -31t-46 -44.5t-39 -61t-16 -74q0 -33 16.5 -53t48.5 -20q45 0 85 31.5t66.5 78t48 105.5t32.5 107t16 90v9q0 2 -3.5 3.5t-8.5 1.5h-10t-10 -0.5t-6 -0.5q-227 0 -352 122.5t-125 348.5q0 108 34.5 221t96 210t156 167.5t204.5 89.5q52 9 106 9 +q374 0 374 -360q0 -98 -38 -273t-43 -211l3 -3q101 57 182.5 88t167.5 31q22 0 53 -13q19 -7 80 -102.5t61 -116.5z" /> + <glyph glyph-name="uniF2A7" unicode="&#xf2a7;" horiz-adv-x="1664" +d="M831 863q32 0 59 -18l222 -148q61 -40 110 -97l146 -170q40 -46 29 -106l-72 -413q-6 -32 -29.5 -53.5t-55.5 -25.5l-527 -56l-352 -32h-9q-39 0 -67.5 28t-28.5 68q0 37 27 64t65 32l260 32h-448q-41 0 -69.5 30t-26.5 71q2 39 32 65t69 26l442 1l-521 64q-41 5 -66 37 +t-19 73q6 35 34.5 57.5t65.5 22.5h10l481 -60l-351 94q-38 10 -62 41.5t-18 68.5q6 36 33 58.5t62 22.5q6 0 20 -2l448 -96l217 -37q1 0 3 -0.5t3 -0.5q23 0 30.5 23t-12.5 36l-186 125q-35 23 -42 63.5t18 73.5q27 38 76 38zM761 661l186 -125l-218 37l-5 2l-36 38 +l-238 262q-1 1 -2.5 3.5t-2.5 3.5q-24 31 -18.5 70t37.5 64q31 23 68 17.5t64 -33.5l142 -147q-2 -1 -5 -3.5t-4 -4.5q-32 -45 -23 -99t55 -85zM1648 1115l15 -266q4 -73 -11 -147l-48 -219q-12 -59 -67 -87l-106 -54q2 62 -39 109l-146 170q-53 61 -117 103l-222 148 +q-34 23 -76 23q-51 0 -88 -37l-235 312q-25 33 -18 73.5t41 63.5q33 22 71.5 14t62.5 -40l266 -352l-262 455q-21 35 -10.5 75t47.5 59q35 18 72.5 6t57.5 -46l241 -420l-136 337q-15 35 -4.5 74t44.5 56q37 19 76 6t56 -51l193 -415l101 -196q8 -15 23 -17.5t27 7.5t11 26 +l-12 224q-2 41 26 71t69 31q39 0 67 -28.5t30 -67.5z" /> + <glyph glyph-name="uniF2A8" unicode="&#xf2a8;" horiz-adv-x="1792" +d="M335 180q-2 0 -6 2q-86 57 -168.5 145t-139.5 180q-21 30 -21 69q0 9 2 19t4 18t7 18t8.5 16t10.5 17t10 15t12 15.5t11 14.5q184 251 452 365q-110 198 -110 211q0 19 17 29q116 64 128 64q18 0 28 -16l124 -229q92 19 192 19q266 0 497.5 -137.5t378.5 -369.5 +q20 -31 20 -69t-20 -69q-91 -142 -218.5 -253.5t-278.5 -175.5q110 -198 110 -211q0 -20 -17 -29q-116 -64 -127 -64q-19 0 -29 16l-124 229l-64 119l-444 820l7 7q-58 -24 -99 -47q3 -5 127 -234t243 -449t119 -223q0 -7 -9 -9q-13 -3 -72 -3q-57 0 -60 7l-456 841 +q-39 -28 -82 -68q24 -43 214 -393.5t190 -354.5q0 -10 -11 -10q-14 0 -82.5 22t-72.5 28l-106 197l-224 413q-44 -53 -78 -106q2 -3 18 -25t23 -34l176 -327q0 -10 -10 -10zM1165 282l49 -91q273 111 450 385q-180 277 -459 389q67 -64 103 -148.5t36 -176.5 +q0 -106 -47 -200.5t-132 -157.5zM848 896q0 -20 14 -34t34 -14q86 0 147 -61t61 -147q0 -20 14 -34t34 -14t34 14t14 34q0 126 -89 215t-215 89q-20 0 -34 -14t-14 -34zM1214 961l-9 4l7 -7z" /> + <glyph glyph-name="uniF2A9" unicode="&#xf2a9;" horiz-adv-x="1280" +d="M1050 430q0 -215 -147 -374q-148 -161 -378 -161q-232 0 -378 161q-147 159 -147 374q0 147 68 270.5t189 196.5t268 73q96 0 182 -31q-32 -62 -39 -126q-66 28 -143 28q-167 0 -280.5 -123t-113.5 -291q0 -170 112.5 -288.5t281.5 -118.5t281 118.5t112 288.5 +q0 89 -32 166q66 13 123 49q41 -98 41 -212zM846 619q0 -192 -79.5 -345t-238.5 -253l-14 -1q-29 0 -62 5q83 32 146.5 102.5t99.5 154.5t58.5 189t30 192.5t7.5 178.5q0 69 -3 103q55 -160 55 -326zM791 947v-2q-73 214 -206 440q88 -59 142.5 -186.5t63.5 -251.5z +M1035 744q-83 0 -160 75q218 120 290 247q19 37 21 56q-42 -94 -139.5 -166.5t-204.5 -97.5q-35 54 -35 113q0 37 17 79t43 68q46 44 157 74q59 16 106 58.5t74 100.5q74 -105 74 -253q0 -109 -24 -170q-32 -77 -88.5 -130.5t-130.5 -53.5z" /> + <glyph glyph-name="uniF2AA" unicode="&#xf2aa;" +d="M1050 495q0 78 -28 147q-41 -25 -85 -34q22 -50 22 -114q0 -117 -77 -198.5t-193 -81.5t-193.5 81.5t-77.5 198.5q0 115 78 199.5t193 84.5q53 0 98 -19q4 43 27 87q-60 21 -125 21q-154 0 -257.5 -108.5t-103.5 -263.5t103.5 -261t257.5 -106t257.5 106.5t103.5 260.5z +M872 850q2 -24 2 -71q0 -63 -5 -123t-20.5 -132.5t-40.5 -130t-68.5 -106t-100.5 -70.5q21 -3 42 -3h10q219 139 219 411q0 116 -38 225zM872 850q-4 80 -44 171.5t-98 130.5q92 -156 142 -302zM1207 955q0 102 -51 174q-41 -86 -124 -109q-69 -19 -109 -53.5t-40 -99.5 +q0 -40 24 -77q74 17 140.5 67t95.5 115q-4 -52 -74.5 -111.5t-138.5 -97.5q52 -52 110 -52q51 0 90 37t60 90q17 42 17 117zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 +t84.5 -203.5z" /> + <glyph glyph-name="uniF2AB" unicode="&#xf2ab;" +d="M1279 388q0 22 -22 27q-67 15 -118 59t-80 108q-7 19 -7 25q0 15 19.5 26t43 17t43 20.5t19.5 36.5q0 19 -18.5 31.5t-38.5 12.5q-12 0 -32 -8t-31 -8q-4 0 -12 2q5 95 5 114q0 79 -17 114q-36 78 -103 121.5t-152 43.5q-199 0 -275 -165q-17 -35 -17 -114q0 -19 5 -114 +q-4 -2 -14 -2q-12 0 -32 7.5t-30 7.5q-21 0 -38.5 -12t-17.5 -32q0 -21 19.5 -35.5t43 -20.5t43 -17t19.5 -26q0 -6 -7 -25q-64 -138 -198 -167q-22 -5 -22 -27q0 -46 137 -68q2 -5 6 -26t11.5 -30.5t23.5 -9.5q12 0 37.5 4.5t39.5 4.5q35 0 67 -15t54 -32.5t57.5 -32.5 +t76.5 -15q43 0 79 15t57.5 32.5t53.5 32.5t67 15q14 0 39.5 -4t38.5 -4q16 0 23 10t11 30t6 25q137 22 137 68zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 +t103 -385.5z" /> + <glyph glyph-name="uniF2AC" unicode="&#xf2ac;" horiz-adv-x="1664" +d="M848 1408q134 1 240.5 -68.5t163.5 -192.5q27 -58 27 -179q0 -47 -9 -191q14 -7 28 -7q18 0 51 13.5t51 13.5q29 0 56 -18t27 -46q0 -32 -31.5 -54t-69 -31.5t-69 -29t-31.5 -47.5q0 -15 12 -43q37 -82 102.5 -150t144.5 -101q28 -12 80 -23q28 -6 28 -35 +q0 -70 -219 -103q-7 -11 -11 -39t-14 -46.5t-33 -18.5q-20 0 -62 6.5t-64 6.5q-37 0 -62 -5q-32 -5 -63 -22.5t-58 -38t-58 -40.5t-76 -33.5t-99 -13.5q-52 0 -96.5 13.5t-75 33.5t-57.5 40.5t-58 38t-62 22.5q-26 5 -63 5q-24 0 -65.5 -7.5t-58.5 -7.5q-25 0 -35 18.5 +t-14 47.5t-11 40q-219 33 -219 103q0 29 28 35q52 11 80 23q78 32 144.5 101t102.5 150q12 28 12 43q0 28 -31.5 47.5t-69.5 29.5t-69.5 31.5t-31.5 52.5q0 27 26 45.5t55 18.5q15 0 48 -13t53 -13q18 0 32 7q-9 142 -9 190q0 122 27 180q64 137 172 198t264 63z" /> + <glyph glyph-name="uniF2AD" unicode="&#xf2ad;" +d="M1280 388q0 22 -22 27q-67 14 -118 58t-80 109q-7 14 -7 25q0 15 19.5 26t42.5 17t42.5 20.5t19.5 36.5q0 19 -18.5 31.5t-38.5 12.5q-11 0 -31 -8t-32 -8q-4 0 -12 2q5 63 5 115q0 78 -17 114q-36 78 -102.5 121.5t-152.5 43.5q-198 0 -275 -165q-18 -38 -18 -115 +q0 -38 6 -114q-10 -2 -15 -2q-11 0 -31.5 8t-30.5 8q-20 0 -37.5 -12.5t-17.5 -32.5q0 -21 19.5 -35.5t42.5 -20.5t42.5 -17t19.5 -26q0 -11 -7 -25q-64 -138 -198 -167q-22 -5 -22 -27q0 -47 138 -69q2 -5 6 -26t11 -30.5t23 -9.5q13 0 38.5 5t38.5 5q35 0 67.5 -15 +t54.5 -32.5t57.5 -32.5t76.5 -15q43 0 79 15t57.5 32.5t54 32.5t67.5 15q13 0 39 -4.5t39 -4.5q15 0 22.5 9.5t11.5 31t5 24.5q138 22 138 69zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960 +q119 0 203.5 -84.5t84.5 -203.5z" /> + <glyph glyph-name="uniF2AE" unicode="&#xf2ae;" horiz-adv-x="2304" +d="M2304 1536q-69 -46 -125 -92t-89 -81t-59.5 -71.5t-37.5 -57.5t-22 -44.5t-14 -29.5q-10 -18 -35.5 -136.5t-48.5 -164.5q-15 -29 -50 -60.5t-67.5 -50.5t-72.5 -41t-48 -28q-47 -31 -151 -231q-341 14 -630 -158q-92 -53 -303 -179q47 16 86 31t55 22l15 7 +q71 27 163 64.5t133.5 53.5t108 34.5t142.5 31.5q186 31 465 -7q1 0 10 -3q11 -6 14 -17t-3 -22l-194 -345q-15 -29 -47 -22q-128 24 -354 24q-146 0 -402 -44.5t-392 -46.5q-82 -1 -149 13t-107 37t-61 40t-33 34l-1 1v2q0 6 6 6q138 0 371 55q192 366 374.5 524t383.5 158 +q5 0 14.5 -0.5t38 -5t55 -12t61.5 -24.5t63 -39.5t54 -59t40 -82.5l102 177q2 4 21 42.5t44.5 86.5t61 109.5t84 133.5t100.5 137q66 82 128 141.5t121.5 96.5t92.5 53.5t88 39.5z" /> + <glyph glyph-name="uniF2B0" unicode="&#xf2b0;" +d="M1322 640q0 -45 -5 -76l-236 14l224 -78q-19 -73 -58 -141l-214 103l177 -158q-44 -61 -107 -108l-157 178l103 -215q-61 -37 -140 -59l-79 228l14 -240q-38 -6 -76 -6t-76 6l14 238l-78 -226q-74 19 -140 59l103 215l-157 -178q-59 43 -108 108l178 158l-214 -104 +q-39 69 -58 141l224 79l-237 -14q-5 42 -5 76q0 35 5 77l238 -14l-225 79q19 73 58 140l214 -104l-177 159q46 61 107 108l158 -178l-103 215q67 39 140 58l77 -224l-13 236q36 6 75 6q38 0 76 -6l-14 -237l78 225q74 -19 140 -59l-103 -214l158 178q61 -47 107 -108 +l-177 -159l213 104q37 -62 58 -141l-224 -78l237 14q5 -31 5 -77zM1352 640q0 160 -78.5 295.5t-213 214t-292.5 78.5q-119 0 -227 -46.5t-186.5 -125t-124.5 -187.5t-46 -229q0 -119 46 -228t124.5 -187.5t186.5 -125t227 -46.5q158 0 292.5 78.5t213 214t78.5 294.5z +M1425 1023v-766l-657 -383l-657 383v766l657 383zM768 -183l708 412v823l-708 411l-708 -411v-823zM1536 1088v-896l-768 -448l-768 448v896l768 448z" /> + <glyph glyph-name="uniF2B1" unicode="&#xf2b1;" horiz-adv-x="1664" +d="M339 1318h691l-26 -72h-665q-110 0 -188.5 -79t-78.5 -189v-771q0 -95 60.5 -169.5t153.5 -93.5q23 -5 98 -5v-72h-45q-140 0 -239.5 100t-99.5 240v771q0 140 99.5 240t239.5 100zM1190 1536h247l-482 -1294q-23 -61 -40.5 -103.5t-45 -98t-54 -93.5t-64.5 -78.5 +t-79.5 -65t-95.5 -41t-116 -18.5v195q163 26 220 182q20 52 20 105q0 54 -20 106l-285 733h228l187 -585zM1664 978v-1111h-795q37 55 45 73h678v1038q0 85 -49.5 155t-129.5 99l25 67q101 -34 163.5 -123.5t62.5 -197.5z" /> + <glyph glyph-name="uniF2B2" unicode="&#xf2b2;" horiz-adv-x="1792" +d="M852 1227q0 -29 -17 -52.5t-45 -23.5t-45 23.5t-17 52.5t17 52.5t45 23.5t45 -23.5t17 -52.5zM688 -149v114q0 30 -20.5 51.5t-50.5 21.5t-50 -21.5t-20 -51.5v-114q0 -30 20.5 -52t49.5 -22q30 0 50.5 22t20.5 52zM860 -149v114q0 30 -20 51.5t-50 21.5t-50.5 -21.5 +t-20.5 -51.5v-114q0 -30 20.5 -52t50.5 -22q29 0 49.5 22t20.5 52zM1034 -149v114q0 30 -20.5 51.5t-50.5 21.5t-50.5 -21.5t-20.5 -51.5v-114q0 -30 20.5 -52t50.5 -22t50.5 22t20.5 52zM1208 -149v114q0 30 -20.5 51.5t-50.5 21.5t-50.5 -21.5t-20.5 -51.5v-114 +q0 -30 20.5 -52t50.5 -22t50.5 22t20.5 52zM1476 535q-84 -160 -232 -259.5t-323 -99.5q-123 0 -229.5 51.5t-178.5 137t-113 197.5t-41 232q0 88 21 174q-104 -175 -104 -390q0 -162 65 -312t185 -251q30 57 91 57q56 0 86 -50q32 50 87 50q56 0 86 -50q32 50 87 50t87 -50 +q30 50 86 50q28 0 52.5 -15.5t37.5 -40.5q112 94 177 231.5t73 287.5zM1326 564q0 75 -72 75q-17 0 -47 -6q-95 -19 -149 -19q-226 0 -226 243q0 86 30 204q-83 -127 -83 -275q0 -150 89 -260.5t235 -110.5q111 0 210 70q13 48 13 79zM884 1223q0 50 -32 89.5t-81 39.5 +t-81 -39.5t-32 -89.5q0 -51 31.5 -90.5t81.5 -39.5t81.5 39.5t31.5 90.5zM1513 884q0 96 -37.5 179t-113 137t-173.5 54q-77 0 -149 -35t-127 -94q-48 -159 -48 -268q0 -104 45.5 -157t147.5 -53q53 0 142 19q36 6 53 6q51 0 77.5 -28t26.5 -80q0 -26 -4 -46 +q75 68 117.5 165.5t42.5 200.5zM1792 667q0 -111 -33.5 -249.5t-93.5 -204.5q-58 -64 -195 -142.5t-228 -104.5l-4 -1v-114q0 -43 -29.5 -75t-72.5 -32q-56 0 -86 50q-32 -50 -87 -50t-87 50q-30 -50 -86 -50q-55 0 -87 50q-30 -50 -86 -50q-47 0 -75 33.5t-28 81.5 +q-90 -68 -198 -68q-118 0 -211 80q54 1 106 20q-113 31 -182 127q32 -7 71 -7q89 0 164 46q-192 192 -240 306q-24 56 -24 160q0 57 9 125.5t31.5 146.5t55 141t86.5 105t120 42q59 0 81 -52q19 29 42 54q2 3 12 13t13 16q10 15 23 38t25 42t28 39q87 111 211.5 177 +t260.5 66q35 0 62 -4q59 64 146 64q83 0 140 -57q5 -5 5 -12q0 -5 -6 -13.5t-12.5 -16t-16 -17l-10.5 -10.5q17 -6 36 -18t19 -24q0 -6 -16 -25q157 -138 197 -378q25 30 60 30q45 0 100 -49q90 -80 90 -279z" /> + <glyph glyph-name="uniF2B3" unicode="&#xf2b3;" +d="M917 631q0 33 -6 64h-362v-132h217q-12 -76 -74.5 -120.5t-142.5 -44.5q-99 0 -169 71.5t-70 170.5t70 170.5t169 71.5q93 0 153 -59l104 101q-108 100 -257 100q-160 0 -272 -112.5t-112 -271.5t112 -271.5t272 -112.5q165 0 266.5 105t101.5 270zM1262 585h109v110 +h-109v110h-110v-110h-110v-110h110v-110h110v110zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> + <glyph glyph-name="uniF2B4" unicode="&#xf2b4;" +d="M1536 1024v-839q0 -48 -49 -62q-174 -52 -338 -52q-73 0 -215.5 29.5t-227.5 29.5q-164 0 -370 -48v-338h-160v1368q-63 25 -101 81t-38 124q0 91 64 155t155 64t155 -64t64 -155q0 -68 -38 -124t-101 -81v-68q190 44 343 44q99 0 198 -15q14 -2 111.5 -22.5t149.5 -20.5 +q77 0 165 18q11 2 80 21t89 19q26 0 45 -19t19 -45z" /> + <glyph glyph-name="uniF2B5" unicode="&#xf2b5;" horiz-adv-x="2304" +d="M192 384q40 0 56 32t0 64t-56 32t-56 -32t0 -64t56 -32zM1665 442q-10 13 -38.5 50t-41.5 54t-38 49t-42.5 53t-40.5 47t-45 49l-125 -140q-83 -94 -208.5 -92t-205.5 98q-57 69 -56.5 158t58.5 157l177 206q-22 11 -51 16.5t-47.5 6t-56.5 -0.5t-49 -1q-92 0 -158 -66 +l-158 -158h-155v-544q5 0 21 0.5t22 0t19.5 -2t20.5 -4.5t17.5 -8.5t18.5 -13.5l297 -292q115 -111 227 -111q78 0 125 47q57 -20 112.5 8t72.5 85q74 -6 127 44q20 18 36 45.5t14 50.5q10 -10 43 -10q43 0 77 21t49.5 53t12 71.5t-30.5 73.5zM1824 384h96v512h-93l-157 180 +q-66 76 -169 76h-167q-89 0 -146 -67l-209 -243q-28 -33 -28 -75t27 -75q43 -51 110 -52t111 49l193 218q25 23 53.5 21.5t47 -27t8.5 -56.5q16 -19 56 -63t60 -68q29 -36 82.5 -105.5t64.5 -84.5q52 -66 60 -140zM2112 384q40 0 56 32t0 64t-56 32t-56 -32t0 -64t56 -32z +M2304 960v-640q0 -26 -19 -45t-45 -19h-434q-27 -65 -82 -106.5t-125 -51.5q-33 -48 -80.5 -81.5t-102.5 -45.5q-42 -53 -104.5 -81.5t-128.5 -24.5q-60 -34 -126 -39.5t-127.5 14t-117 53.5t-103.5 81l-287 282h-358q-26 0 -45 19t-19 45v672q0 26 19 45t45 19h421 +q14 14 47 48t47.5 48t44 40t50.5 37.5t51 25.5t62 19.5t68 5.5h117q99 0 181 -56q82 56 181 56h167q35 0 67 -6t56.5 -14.5t51.5 -26.5t44.5 -31t43 -39.5t39 -42t41 -48t41.5 -48.5h355q26 0 45 -19t19 -45z" /> + <glyph glyph-name="uniF2B6" unicode="&#xf2b6;" horiz-adv-x="1792" +d="M1792 882v-978q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v978q0 15 11 24q8 7 39 34.5t41.5 36t45.5 37.5t70 55.5t96 73t143.5 107t192.5 140.5q5 4 52.5 40t71.5 52.5t64 35t69 18.5t69 -18.5t65 -35.5t71 -52t52 -40q110 -80 192.5 -140.5t143.5 -107 +t96 -73t70 -55.5t45.5 -37.5t41.5 -36t39 -34.5q11 -9 11 -24zM1228 297q263 191 345 252q11 8 12.5 20.5t-6.5 23.5l-38 52q-8 11 -21 12.5t-24 -6.5q-231 -169 -343 -250q-5 -3 -52 -39t-71.5 -52.5t-64.5 -35t-69 -18.5t-69 18.5t-64.5 35t-71.5 52.5t-52 39 +q-186 134 -343 250q-11 8 -24 6.5t-21 -12.5l-38 -52q-8 -11 -6.5 -23.5t12.5 -20.5q82 -61 345 -252q10 -8 50 -38t65 -47t64 -39.5t77.5 -33.5t75.5 -11t75.5 11t79 34.5t64.5 39.5t65 47.5t48 36.5z" /> + <glyph glyph-name="uniF2B7" unicode="&#xf2b7;" horiz-adv-x="1792" +d="M1474 623l39 -51q8 -11 6.5 -23.5t-11.5 -20.5q-43 -34 -126.5 -98.5t-146.5 -113t-67 -51.5q-39 -32 -60 -48t-60.5 -41t-76.5 -36.5t-74 -11.5h-1h-1q-37 0 -74 11.5t-76 36.5t-61 41.5t-60 47.5q-5 4 -65 50.5t-143.5 111t-122.5 94.5q-11 8 -12.5 20.5t6.5 23.5 +l37 52q8 11 21.5 13t24.5 -7q94 -73 306 -236q5 -4 43.5 -35t60.5 -46.5t56.5 -32.5t58.5 -17h1h1q24 0 58.5 17t56.5 32.5t60.5 46.5t43.5 35q258 198 313 242q11 8 24 6.5t21 -12.5zM1664 -96v928q-90 83 -159 139q-91 74 -389 304q-3 2 -43 35t-61 48t-56 32.5t-59 17.5 +h-1h-1q-24 0 -59 -17.5t-56 -32.5t-61 -48t-43 -35q-215 -166 -315.5 -245.5t-129.5 -104t-82 -74.5q-14 -12 -21 -19v-928q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 832v-928q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v928q0 56 41 94 +q123 114 350 290.5t233 181.5q36 30 59 47.5t61.5 42t76 36.5t74.5 12h1h1q37 0 74.5 -12t76 -36.5t61.5 -42t59 -47.5q43 -36 156 -122t226 -177t201 -173q41 -38 41 -94z" /> + <glyph glyph-name="uniF2B8" unicode="&#xf2b8;" +d="M330 1l202 -214l-34 236l-216 213zM556 -225l274 218l-11 245l-300 -215zM245 413l227 -213l-48 327l-245 204zM495 189l317 214l-14 324l-352 -200zM843 178l95 -80l-2 239l-103 79q0 -1 1 -8.5t0 -12t-5 -7.5l-78 -52l85 -70q7 -6 7 -88zM138 930l256 -200l-68 465 +l-279 173zM1173 267l15 234l-230 -164l2 -240zM417 722l373 194l-19 441l-423 -163zM1270 357l20 233l-226 142l-2 -105l144 -95q6 -4 4 -9l-7 -119zM1461 496l30 222l-179 -128l-20 -228zM1273 329l-71 49l-8 -117q0 -5 -4 -8l-234 -187q-7 -5 -14 0l-98 83l7 -161 +q0 -5 -4 -8l-293 -234q-4 -2 -6 -2q-8 2 -8 3l-228 242q-4 4 -59 277q-2 7 5 11l61 37q-94 86 -95 92l-72 351q-2 7 6 12l94 45q-133 100 -135 108l-96 466q-2 10 7 13l433 135q5 0 8 -1l317 -153q6 -4 6 -9l20 -463q0 -7 -6 -10l-118 -61l126 -85q5 -2 5 -8l5 -123l121 74 +q5 4 11 0l84 -56l3 110q0 6 5 9l206 126q6 3 11 0l245 -135q4 -4 5 -7t-6.5 -60t-17.5 -124.5t-10 -70.5q0 -5 -4 -7l-191 -153q-6 -5 -13 0z" /> + <glyph glyph-name="uniF2B9" unicode="&#xf2b9;" horiz-adv-x="1664" +d="M1201 298q0 57 -5.5 107t-21 100.5t-39.5 86t-64 58t-91 22.5q-6 -4 -33.5 -20.5t-42.5 -24.5t-40.5 -20t-49 -17t-46.5 -5t-46.5 5t-49 17t-40.5 20t-42.5 24.5t-33.5 20.5q-51 0 -91 -22.5t-64 -58t-39.5 -86t-21 -100.5t-5.5 -107q0 -73 42 -121.5t103 -48.5h576 +q61 0 103 48.5t42 121.5zM1028 892q0 108 -76.5 184t-183.5 76t-183.5 -76t-76.5 -184q0 -107 76.5 -183t183.5 -76t183.5 76t76.5 183zM1664 352v-192q0 -14 -9 -23t-23 -9h-96v-224q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v1472q0 66 47 113t113 47h1216 +q66 0 113 -47t47 -113v-224h96q14 0 23 -9t9 -23v-192q0 -14 -9 -23t-23 -9h-96v-128h96q14 0 23 -9t9 -23v-192q0 -14 -9 -23t-23 -9h-96v-128h96q14 0 23 -9t9 -23z" /> + <glyph glyph-name="uniF2BA" unicode="&#xf2ba;" horiz-adv-x="1664" +d="M1028 892q0 -107 -76.5 -183t-183.5 -76t-183.5 76t-76.5 183q0 108 76.5 184t183.5 76t183.5 -76t76.5 -184zM980 672q46 0 82.5 -17t60 -47.5t39.5 -67t24 -81t11.5 -82.5t3.5 -79q0 -67 -39.5 -118.5t-105.5 -51.5h-576q-66 0 -105.5 51.5t-39.5 118.5q0 48 4.5 93.5 +t18.5 98.5t36.5 91.5t63 64.5t93.5 26h5q7 -4 32 -19.5t35.5 -21t33 -17t37 -16t35 -9t39.5 -4.5t39.5 4.5t35 9t37 16t33 17t35.5 21t32 19.5zM1664 928q0 -13 -9.5 -22.5t-22.5 -9.5h-96v-128h96q13 0 22.5 -9.5t9.5 -22.5v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-96v-128h96 +q13 0 22.5 -9.5t9.5 -22.5v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-96v-224q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v1472q0 66 47 113t113 47h1216q66 0 113 -47t47 -113v-224h96q13 0 22.5 -9.5t9.5 -22.5v-192zM1408 -96v1472q0 13 -9.5 22.5t-22.5 9.5h-1216 +q-13 0 -22.5 -9.5t-9.5 -22.5v-1472q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5z" /> + <glyph glyph-name="uniF2BB" unicode="&#xf2bb;" horiz-adv-x="2048" +d="M1024 405q0 64 -9 117.5t-29.5 103t-60.5 78t-97 28.5q-6 -4 -30 -18t-37.5 -21.5t-35.5 -17.5t-43 -14.5t-42 -4.5t-42 4.5t-43 14.5t-35.5 17.5t-37.5 21.5t-30 18q-57 0 -97 -28.5t-60.5 -78t-29.5 -103t-9 -117.5t37 -106.5t91 -42.5h512q54 0 91 42.5t37 106.5z +M867 925q0 94 -66.5 160.5t-160.5 66.5t-160.5 -66.5t-66.5 -160.5t66.5 -160.5t160.5 -66.5t160.5 66.5t66.5 160.5zM1792 416v64q0 14 -9 23t-23 9h-576q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h576q14 0 23 9t9 23zM1792 676v56q0 15 -10.5 25.5t-25.5 10.5h-568 +q-15 0 -25.5 -10.5t-10.5 -25.5v-56q0 -15 10.5 -25.5t25.5 -10.5h568q15 0 25.5 10.5t10.5 25.5zM1792 928v64q0 14 -9 23t-23 9h-576q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h576q14 0 23 9t9 23zM2048 1248v-1216q0 -66 -47 -113t-113 -47h-352v96q0 14 -9 23t-23 9 +h-64q-14 0 -23 -9t-9 -23v-96h-768v96q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-96h-352q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1728q66 0 113 -47t47 -113z" /> + <glyph glyph-name="uniF2BC" unicode="&#xf2bc;" horiz-adv-x="2048" +d="M1024 405q0 -64 -37 -106.5t-91 -42.5h-512q-54 0 -91 42.5t-37 106.5t9 117.5t29.5 103t60.5 78t97 28.5q6 -4 30 -18t37.5 -21.5t35.5 -17.5t43 -14.5t42 -4.5t42 4.5t43 14.5t35.5 17.5t37.5 21.5t30 18q57 0 97 -28.5t60.5 -78t29.5 -103t9 -117.5zM867 925 +q0 -94 -66.5 -160.5t-160.5 -66.5t-160.5 66.5t-66.5 160.5t66.5 160.5t160.5 66.5t160.5 -66.5t66.5 -160.5zM1792 480v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM1792 732v-56q0 -15 -10.5 -25.5t-25.5 -10.5h-568 +q-15 0 -25.5 10.5t-10.5 25.5v56q0 15 10.5 25.5t25.5 10.5h568q15 0 25.5 -10.5t10.5 -25.5zM1792 992v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM1920 32v1216q0 13 -9.5 22.5t-22.5 9.5h-1728q-13 0 -22.5 -9.5 +t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h352v96q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-96h768v96q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-96h352q13 0 22.5 9.5t9.5 22.5zM2048 1248v-1216q0 -66 -47 -113t-113 -47h-1728q-66 0 -113 47t-47 113v1216q0 66 47 113 +t113 47h1728q66 0 113 -47t47 -113z" /> + <glyph glyph-name="uniF2BD" unicode="&#xf2bd;" horiz-adv-x="1792" +d="M1523 197q-22 155 -87.5 257.5t-184.5 118.5q-67 -74 -159.5 -115.5t-195.5 -41.5t-195.5 41.5t-159.5 115.5q-119 -16 -184.5 -118.5t-87.5 -257.5q106 -150 271 -237.5t356 -87.5t356 87.5t271 237.5zM1280 896q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5 +t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1792 640q0 -182 -71 -347.5t-190.5 -286t-285.5 -191.5t-349 -71q-182 0 -348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" /> + <glyph glyph-name="uniF2BE" unicode="&#xf2be;" horiz-adv-x="1792" +d="M896 1536q182 0 348 -71t286 -191t191 -286t71 -348q0 -181 -70.5 -347t-190.5 -286t-286 -191.5t-349 -71.5t-349 71t-285.5 191.5t-190.5 286t-71 347.5t71 348t191 286t286 191t348 71zM1515 185q149 205 149 455q0 156 -61 298t-164 245t-245 164t-298 61t-298 -61 +t-245 -164t-164 -245t-61 -298q0 -250 149 -455q66 327 306 327q131 -128 313 -128t313 128q240 0 306 -327zM1280 832q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5z" /> + <glyph glyph-name="uniF2C0" unicode="&#xf2c0;" +d="M1201 752q47 -14 89.5 -38t89 -73t79.5 -115.5t55 -172t22 -236.5q0 -154 -100 -263.5t-241 -109.5h-854q-141 0 -241 109.5t-100 263.5q0 131 22 236.5t55 172t79.5 115.5t89 73t89.5 38q-79 125 -79 272q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5 +t198.5 -40.5t163.5 -109.5t109.5 -163.5t40.5 -198.5q0 -147 -79 -272zM768 1408q-159 0 -271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5t-112.5 271.5t-271.5 112.5zM1195 -128q88 0 150.5 71.5t62.5 173.5q0 239 -78.5 377t-225.5 145 +q-145 -127 -336 -127t-336 127q-147 -7 -225.5 -145t-78.5 -377q0 -102 62.5 -173.5t150.5 -71.5h854z" /> + <glyph glyph-name="uniF2C1" unicode="&#xf2c1;" horiz-adv-x="1280" +d="M1024 278q0 -64 -37 -107t-91 -43h-512q-54 0 -91 43t-37 107t9 118t29.5 104t61 78.5t96.5 28.5q80 -75 188 -75t188 75q56 0 96.5 -28.5t61 -78.5t29.5 -104t9 -118zM870 797q0 -94 -67.5 -160.5t-162.5 -66.5t-162.5 66.5t-67.5 160.5t67.5 160.5t162.5 66.5 +t162.5 -66.5t67.5 -160.5zM1152 -96v1376h-1024v-1376q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1280 1376v-1472q0 -66 -47 -113t-113 -47h-960q-66 0 -113 47t-47 113v1472q0 66 47 113t113 47h352v-96q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v96h352 +q66 0 113 -47t47 -113z" /> + <glyph glyph-name="uniF2C2" unicode="&#xf2c2;" horiz-adv-x="2048" +d="M896 324q0 54 -7.5 100.5t-24.5 90t-51 68.5t-81 25q-64 -64 -156 -64t-156 64q-47 0 -81 -25t-51 -68.5t-24.5 -90t-7.5 -100.5q0 -55 31.5 -93.5t75.5 -38.5h426q44 0 75.5 38.5t31.5 93.5zM768 768q0 80 -56 136t-136 56t-136 -56t-56 -136t56 -136t136 -56t136 56 +t56 136zM1792 288v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1408 544v64q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1792 544v64q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23 +v-64q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1792 800v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM128 1152h1792v96q0 14 -9 23t-23 9h-1728q-14 0 -23 -9t-9 -23v-96zM2048 1248v-1216q0 -66 -47 -113t-113 -47h-1728 +q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1728q66 0 113 -47t47 -113z" /> + <glyph glyph-name="uniF2C3" unicode="&#xf2c3;" horiz-adv-x="2048" +d="M896 324q0 -55 -31.5 -93.5t-75.5 -38.5h-426q-44 0 -75.5 38.5t-31.5 93.5q0 54 7.5 100.5t24.5 90t51 68.5t81 25q64 -64 156 -64t156 64q47 0 81 -25t51 -68.5t24.5 -90t7.5 -100.5zM768 768q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136z +M1792 352v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM1408 608v-64q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h320q14 0 23 -9t9 -23zM1792 608v-64q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v64 +q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 864v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM1920 32v1120h-1792v-1120q0 -13 9.5 -22.5t22.5 -9.5h1728q13 0 22.5 9.5t9.5 22.5zM2048 1248v-1216q0 -66 -47 -113t-113 -47 +h-1728q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1728q66 0 113 -47t47 -113z" /> + <glyph glyph-name="uniF2C4" unicode="&#xf2c4;" horiz-adv-x="1792" +d="M1255 749q0 318 -105 474.5t-330 156.5q-222 0 -326 -157t-104 -474q0 -316 104 -471.5t326 -155.5q74 0 131 17q-22 43 -39 73t-44 65t-53.5 56.5t-63 36t-77.5 14.5q-46 0 -79 -16l-49 97q105 91 276 91q132 0 215.5 -54t150.5 -155q67 149 67 402zM1645 117h117 +q3 -27 -2 -67t-26.5 -95t-58 -100.5t-107 -78t-162.5 -32.5q-71 0 -130.5 19t-105.5 56t-79 78t-66 96q-97 -27 -205 -27q-150 0 -292.5 58t-253 158.5t-178 249t-67.5 317.5q0 170 67.5 319.5t178.5 250.5t253.5 159t291.5 58q121 0 238.5 -36t217 -106t176 -164.5 +t119.5 -219t43 -261.5q0 -190 -80.5 -347.5t-218.5 -264.5q47 -70 93.5 -106.5t104.5 -36.5q61 0 94 37.5t38 85.5z" /> + <glyph glyph-name="uniF2C5" unicode="&#xf2c5;" horiz-adv-x="2304" +d="M453 -101q0 -21 -16 -37.5t-37 -16.5q-1 0 -13 3q-63 15 -162 140q-225 284 -225 676q0 341 213 614q39 51 95 103.5t94 52.5q19 0 35 -13.5t16 -32.5q0 -27 -63 -90q-98 -102 -147 -184q-119 -199 -119 -449q0 -281 123 -491q50 -85 136 -173q2 -3 14.5 -16t19.5 -21 +t17 -20.5t14.5 -23.5t4.5 -21zM1796 33q0 -29 -17.5 -48.5t-46.5 -19.5h-1081q-26 0 -45 19t-19 45q0 29 17.5 48.5t46.5 19.5h1081q26 0 45 -19t19 -45zM1581 644q0 -134 -67 -233q-25 -38 -69.5 -78.5t-83.5 -60.5q-16 -10 -27 -10q-7 0 -15 6t-8 12q0 9 19 30t42 46 +t42 67.5t19 88.5q0 76 -35 130q-29 42 -46 42q-3 0 -3 -5q0 -12 7.5 -35.5t7.5 -36.5q0 -22 -21.5 -35t-44.5 -13q-66 0 -66 76q0 15 1.5 44t1.5 44q0 25 -10 46q-13 25 -42 53.5t-51 28.5q-5 0 -7 -0.5t-3.5 -2.5t-1.5 -6q0 -2 16 -26t16 -54q0 -37 -19 -68t-46 -54 +t-53.5 -46t-45.5 -54t-19 -68q0 -98 42 -160q29 -43 79 -63q16 -5 17 -10q1 -2 1 -5q0 -16 -18 -16q-6 0 -33 11q-119 43 -195 139.5t-76 218.5q0 55 24.5 115.5t60 115t70.5 108.5t59.5 113.5t24.5 111.5q0 53 -25 94q-29 48 -56 64q-19 9 -19 21q0 20 41 20q50 0 110 -29 +q41 -19 71 -44.5t49.5 -51t33.5 -62.5t22 -69t16 -80q0 -1 3 -17.5t4.5 -25t5.5 -25t9 -27t11 -21.5t14.5 -16.5t18.5 -5.5q23 0 37 14t14 37q0 25 -20 67t-20 52t10 10q27 0 93 -70q72 -76 102.5 -156t30.5 -186zM2304 615q0 -274 -138 -503q-19 -32 -48 -72t-68 -86.5 +t-81 -77t-74 -30.5q-16 0 -31 15.5t-15 31.5q0 15 29 50.5t68.5 77t48.5 52.5q183 230 183 531q0 131 -20.5 235t-72.5 211q-58 119 -163 228q-2 3 -13 13.5t-16.5 16.5t-15 17.5t-15 20t-9.5 18.5t-4 19q0 19 16 35.5t35 16.5q70 0 196 -169q98 -131 146 -273t60 -314 +q2 -42 2 -64z" /> + <glyph glyph-name="uniF2C6" unicode="&#xf2c6;" horiz-adv-x="1792" +d="M1189 229l147 693q9 44 -10.5 63t-51.5 7l-864 -333q-29 -11 -39.5 -25t-2.5 -26.5t32 -19.5l221 -69l513 323q21 14 32 6q7 -5 -4 -15l-415 -375v0v0l-16 -228q23 0 45 22l108 104l224 -165q64 -36 81 38zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71 +t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" /> + <glyph glyph-name="uniF2C7" unicode="&#xf2c7;" horiz-adv-x="1024" +d="M640 192q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 60 35 110t93 71v907h128v-907q58 -21 93 -71t35 -110zM768 192q0 77 -34 144t-94 112v768q0 80 -56 136t-136 56t-136 -56t-56 -136v-768q-60 -45 -94 -112t-34 -144q0 -133 93.5 -226.5t226.5 -93.5t226.5 93.5 +t93.5 226.5zM896 192q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 182 128 313v711q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5v-711q128 -131 128 -313zM1024 768v-128h-192v128h192zM1024 1024v-128h-192v128h192zM1024 1280v-128h-192 +v128h192z" /> + <glyph glyph-name="uniF2C8" unicode="&#xf2c8;" horiz-adv-x="1024" +d="M640 192q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 60 35 110t93 71v651h128v-651q58 -21 93 -71t35 -110zM768 192q0 77 -34 144t-94 112v768q0 80 -56 136t-136 56t-136 -56t-56 -136v-768q-60 -45 -94 -112t-34 -144q0 -133 93.5 -226.5t226.5 -93.5t226.5 93.5 +t93.5 226.5zM896 192q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 182 128 313v711q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5v-711q128 -131 128 -313zM1024 768v-128h-192v128h192zM1024 1024v-128h-192v128h192zM1024 1280v-128h-192 +v128h192z" /> + <glyph glyph-name="uniF2C9" unicode="&#xf2c9;" horiz-adv-x="1024" +d="M640 192q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 60 35 110t93 71v395h128v-395q58 -21 93 -71t35 -110zM768 192q0 77 -34 144t-94 112v768q0 80 -56 136t-136 56t-136 -56t-56 -136v-768q-60 -45 -94 -112t-34 -144q0 -133 93.5 -226.5t226.5 -93.5t226.5 93.5 +t93.5 226.5zM896 192q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 182 128 313v711q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5v-711q128 -131 128 -313zM1024 768v-128h-192v128h192zM1024 1024v-128h-192v128h192zM1024 1280v-128h-192 +v128h192z" /> + <glyph glyph-name="uniF2CA" unicode="&#xf2ca;" horiz-adv-x="1024" +d="M640 192q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 60 35 110t93 71v139h128v-139q58 -21 93 -71t35 -110zM768 192q0 77 -34 144t-94 112v768q0 80 -56 136t-136 56t-136 -56t-56 -136v-768q-60 -45 -94 -112t-34 -144q0 -133 93.5 -226.5t226.5 -93.5t226.5 93.5 +t93.5 226.5zM896 192q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 182 128 313v711q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5v-711q128 -131 128 -313zM1024 768v-128h-192v128h192zM1024 1024v-128h-192v128h192zM1024 1280v-128h-192 +v128h192z" /> + <glyph glyph-name="uniF2CB" unicode="&#xf2cb;" horiz-adv-x="1024" +d="M640 192q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 79 56 135.5t136 56.5t136 -56.5t56 -135.5zM768 192q0 77 -34 144t-94 112v768q0 80 -56 136t-136 56t-136 -56t-56 -136v-768q-60 -45 -94 -112t-34 -144q0 -133 93.5 -226.5t226.5 -93.5t226.5 93.5t93.5 226.5z +M896 192q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 182 128 313v711q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5v-711q128 -131 128 -313zM1024 768v-128h-192v128h192zM1024 1024v-128h-192v128h192zM1024 1280v-128h-192v128h192z" /> + <glyph glyph-name="uniF2CC" unicode="&#xf2cc;" horiz-adv-x="1920" +d="M1433 1287q10 -10 10 -23t-10 -23l-626 -626q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l44 44q-72 91 -81.5 207t46.5 215q-74 71 -176 71q-106 0 -181 -75t-75 -181v-1280h-256v1280q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5q106 0 201 -41 +t166 -115q94 39 197 24.5t185 -79.5l44 44q10 10 23 10t23 -10zM1344 1024q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1600 896q-26 0 -45 19t-19 45t19 45t45 19t45 -19t19 -45t-19 -45t-45 -19zM1856 1024q26 0 45 -19t19 -45t-19 -45t-45 -19 +t-45 19t-19 45t19 45t45 19zM1216 896q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1408 832q0 26 19 45t45 19t45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45zM1728 896q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1088 768 +q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1344 640q-26 0 -45 19t-19 45t19 45t45 19t45 -19t19 -45t-19 -45t-45 -19zM1600 768q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1216 512q-26 0 -45 19t-19 45t19 45t45 19t45 -19 +t19 -45t-19 -45t-45 -19zM1472 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1088 512q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1344 512q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1216 384 +q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1088 256q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19z" /> + <glyph glyph-name="uniF2CD" unicode="&#xf2cd;" horiz-adv-x="1792" +d="M1664 448v-192q0 -169 -128 -286v-194q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v118q-63 -22 -128 -22h-768q-65 0 -128 22v-110q0 -17 -9.5 -28.5t-22.5 -11.5h-64q-13 0 -22.5 11.5t-9.5 28.5v186q-128 117 -128 286v192h1536zM704 864q0 -14 -9 -23t-23 -9t-23 9 +t-9 23t9 23t23 9t23 -9t9 -23zM768 928q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM704 992q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM832 992q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM768 1056q0 -14 -9 -23t-23 -9t-23 9 +t-9 23t9 23t23 9t23 -9t9 -23zM704 1120q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM1792 608v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v640q0 106 75 181t181 75q108 0 184 -78q46 19 98 12t93 -39l22 22q11 11 22 0l42 -42 +q11 -11 0 -22l-314 -314q-11 -11 -22 0l-42 42q-11 11 0 22l22 22q-36 46 -40.5 104t23.5 108q-37 35 -88 35q-53 0 -90.5 -37.5t-37.5 -90.5v-640h1504q14 0 23 -9t9 -23zM896 1056q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM832 1120q0 -14 -9 -23t-23 -9 +t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM768 1184q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM960 1120q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM896 1184q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM832 1248q0 -14 -9 -23 +t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM1024 1184q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM960 1248q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM1088 1248q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23z" /> + <glyph glyph-name="uniF2CE" unicode="&#xf2ce;" +d="M994 344q0 -86 -17 -197q-31 -215 -55 -313q-22 -90 -152 -90t-152 90q-24 98 -55 313q-17 110 -17 197q0 168 224 168t224 -168zM1536 768q0 -240 -134 -434t-350 -280q-8 -3 -15 3t-6 15q7 48 10 66q4 32 6 47q1 9 9 12q159 81 255.5 234t96.5 337q0 180 -91 330.5 +t-247 234.5t-337 74q-124 -7 -237 -61t-193.5 -140.5t-128 -202t-46.5 -240.5q1 -184 99 -336.5t257 -231.5q7 -3 9 -12q3 -21 6 -45q1 -9 5 -32.5t6 -35.5q1 -9 -6.5 -15t-15.5 -2q-148 58 -261 169.5t-173.5 264t-52.5 319.5q7 143 66 273.5t154.5 227t225 157.5t272.5 70 +q164 10 315.5 -46.5t261 -160.5t175 -250.5t65.5 -308.5zM994 800q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5zM1282 768q0 -122 -53.5 -228.5t-146.5 -177.5q-8 -6 -16 -2t-10 14q-6 52 -29 92q-7 10 3 20 +q58 54 91 127t33 155q0 111 -58.5 204t-157.5 141.5t-212 36.5q-133 -15 -229 -113t-109 -231q-10 -92 23.5 -176t98.5 -144q10 -10 3 -20q-24 -41 -29 -93q-2 -9 -10 -13t-16 2q-95 74 -148.5 183t-51.5 234q3 131 69 244t177 181.5t241 74.5q144 7 268 -60t196.5 -187.5 +t72.5 -263.5z" /> + <glyph glyph-name="uniF2D0" unicode="&#xf2d0;" horiz-adv-x="1792" +d="M256 128h1280v768h-1280v-768zM1792 1248v-1216q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" /> + <glyph glyph-name="uniF2D1" unicode="&#xf2d1;" horiz-adv-x="1792" +d="M1792 224v-192q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" /> + <glyph glyph-name="uniF2D2" unicode="&#xf2d2;" horiz-adv-x="2048" +d="M256 0h768v512h-768v-512zM1280 512h512v768h-768v-256h96q66 0 113 -47t47 -113v-352zM2048 1376v-960q0 -66 -47 -113t-113 -47h-608v-352q0 -66 -47 -113t-113 -47h-960q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h608v352q0 66 47 113t113 47h960q66 0 113 -47 +t47 -113z" /> + <glyph glyph-name="uniF2D3" unicode="&#xf2d3;" horiz-adv-x="1792" +d="M1175 215l146 146q10 10 10 23t-10 23l-233 233l233 233q10 10 10 23t-10 23l-146 146q-10 10 -23 10t-23 -10l-233 -233l-233 233q-10 10 -23 10t-23 -10l-146 -146q-10 -10 -10 -23t10 -23l233 -233l-233 -233q-10 -10 -10 -23t10 -23l146 -146q10 -10 23 -10t23 10 +l233 233l233 -233q10 -10 23 -10t23 10zM1792 1248v-1216q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" /> + <glyph glyph-name="uniF2D4" unicode="&#xf2d4;" horiz-adv-x="1792" +d="M1257 425l-146 -146q-10 -10 -23 -10t-23 10l-169 169l-169 -169q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l169 169l-169 169q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l169 -169l169 169q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23 +l-169 -169l169 -169q10 -10 10 -23t-10 -23zM256 128h1280v1024h-1280v-1024zM1792 1248v-1216q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" /> + <glyph glyph-name="uniF2D5" unicode="&#xf2d5;" horiz-adv-x="1792" +d="M1070 358l306 564h-654l-306 -564h654zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" /> + <glyph glyph-name="uniF2D6" unicode="&#xf2d6;" horiz-adv-x="1794" +d="M1291 1060q-15 17 -35 8.5t-26 -28.5t5 -38q14 -17 40 -14.5t34 20.5t-18 52zM895 814q-8 -8 -19.5 -8t-18.5 8q-8 8 -8 19t8 18q7 8 18.5 8t19.5 -8q7 -7 7 -18t-7 -19zM1060 740l-35 -35q-12 -13 -29.5 -13t-30.5 13l-38 38q-12 13 -12 30t12 30l35 35q12 12 29.5 12 +t30.5 -12l38 -39q12 -12 12 -29.5t-12 -29.5zM951 870q-7 -8 -18.5 -8t-19.5 8q-7 8 -7 19t7 19q8 8 19 8t19 -8t8 -19t-8 -19zM1354 968q-34 -64 -107.5 -85.5t-127.5 16.5q-38 28 -61 66.5t-21 87.5t39 92t75.5 53t70.5 -5t70 -51q2 -2 13 -12.5t14.5 -13.5t13 -13.5 +t12.5 -15.5t10 -15.5t8.5 -18t4 -18.5t1 -21t-5 -22t-9.5 -24zM1555 486q3 20 -8.5 34.5t-27.5 21.5t-33 17t-23 20q-40 71 -84 98.5t-113 11.5q19 13 40 18.5t33 4.5l12 -1q2 45 -34 90q6 20 6.5 40.5t-2.5 30.5l-3 10q43 24 71 65t34 91q10 84 -43 150.5t-137 76.5 +q-60 7 -114 -18.5t-82 -74.5q-30 -51 -33.5 -101t14.5 -87t43.5 -64t56.5 -42q-45 4 -88 36t-57 88q-28 108 32 222q-16 21 -29 32q-50 0 -89 -19q19 24 42 37t36 14l13 1q0 50 -13 78q-10 21 -32.5 28.5t-47 -3.5t-37.5 -40q2 4 4 7q-7 -28 -6.5 -75.5t19 -117t48.5 -122.5 +q-25 -14 -47 -36q-35 -16 -85.5 -70.5t-84.5 -101.5l-33 -46q-90 -34 -181 -125.5t-75 -162.5q1 -16 11 -27q-15 -12 -30 -30q-21 -25 -21 -54t21.5 -40t63.5 6q41 19 77 49.5t55 60.5q-2 2 -6.5 5t-20.5 7.5t-33 3.5q23 5 51 12.5t40 10t27.5 6t26 4t23.5 0.5q14 -7 22 34 +q7 37 7 90q0 102 -40 150q106 -103 101 -219q-1 -29 -15 -50t-27 -27l-13 -6q-4 -7 -19 -32t-26 -45.5t-26.5 -52t-25 -61t-17 -63t-6.5 -66.5t10 -63q-35 54 -37 80q-22 -24 -34.5 -39t-33.5 -42t-30.5 -46t-16.5 -41t-0.5 -38t25.5 -27q45 -25 144 64t190.5 221.5 +t122.5 228.5q86 52 145 115.5t86 119.5q47 -93 154 -178q104 -83 167 -80q39 2 46 43zM1794 640q0 -182 -71 -348t-191 -286t-286.5 -191t-348.5 -71t-348.5 71t-286.5 191t-191 286t-71 348t71 348t191 286t286.5 191t348.5 71t348.5 -71t286.5 -191t191 -286t71 -348z" /> + <glyph glyph-name="uniF2D7" unicode="&#xf2d7;" +d="M518 1353v-655q103 -1 191.5 1.5t125.5 5.5l37 3q68 2 90.5 24.5t39.5 94.5l33 142h103l-14 -322l7 -319h-103l-29 127q-15 68 -45 93t-84 26q-87 8 -352 8v-556q0 -78 43.5 -115.5t133.5 -37.5h357q35 0 59.5 2t55 7.5t54 18t48.5 32t46 50.5t39 73l93 216h89 +q-6 -37 -31.5 -252t-30.5 -276q-146 5 -263.5 8t-162.5 4h-44h-628l-376 -12v102l127 25q67 13 91.5 37t25.5 79l8 643q3 402 -8 645q-2 61 -25.5 84t-91.5 36l-127 24v102l376 -12h702q139 0 374 27q-6 -68 -14 -194.5t-12 -219.5l-5 -92h-93l-32 124q-31 121 -74 179.5 +t-113 58.5h-548q-28 0 -35.5 -8.5t-7.5 -30.5z" /> + <glyph glyph-name="uniF2D8" unicode="&#xf2d8;" +d="M922 739v-182q0 -4 0.5 -15t0 -15l-1.5 -12t-3.5 -11.5t-6.5 -7.5t-11 -5.5t-16 -1.5v309q9 0 16 -1t11 -5t6.5 -5.5t3.5 -9.5t1 -10.5v-13.5v-14zM1238 643v-121q0 -1 0.5 -12.5t0 -15.5t-2.5 -11.5t-7.5 -10.5t-13.5 -3q-9 0 -14 9q-4 10 -4 165v7v8.5v9t1.5 8.5l3.5 7 +t5 5.5t8 1.5q6 0 10 -1.5t6.5 -4.5t4 -6t2 -8.5t0.5 -8v-9.5v-9zM180 407h122v472h-122v-472zM614 407h106v472h-159l-28 -221q-20 148 -32 221h-158v-472h107v312l45 -312h76l43 319v-319zM1039 712q0 67 -5 90q-3 16 -11 28.5t-17 20.5t-25 14t-26.5 8.5t-31 4t-29 1.5 +h-29.5h-12h-91v-472h56q169 -1 197 24.5t25 180.5q-1 62 -1 100zM1356 515v133q0 29 -2 45t-9.5 33.5t-24.5 25t-46 7.5q-46 0 -77 -34v154h-117v-472h110l7 30q30 -36 77 -36q50 0 66 30.5t16 83.5zM1536 1248v-1216q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113 +v1216q0 66 47 113t113 47h1216q66 0 113 -47t47 -113z" /> + <glyph glyph-name="uniF2D9" unicode="&#xf2d9;" horiz-adv-x="2176" +d="M1143 -197q-6 1 -11 4q-13 8 -36 23t-86 65t-116.5 104.5t-112 140t-89.5 172.5q-17 3 -175 37q66 -213 235 -362t391 -184zM502 409l168 -28q-25 76 -41 167.5t-19 145.5l-4 53q-84 -82 -121 -224q5 -65 17 -114zM612 1018q-43 -64 -77 -148q44 46 74 68zM2049 584 +q0 161 -62 307t-167.5 252t-250.5 168.5t-304 62.5q-147 0 -281 -52.5t-240 -148.5q-30 -58 -45 -160q60 51 143 83.5t158.5 43t143 13.5t108.5 -1l40 -3q33 -1 53 -15.5t24.5 -33t6.5 -37t-1 -28.5q-126 11 -227.5 0.5t-183 -43.5t-142.5 -71.5t-131 -98.5 +q4 -36 11.5 -92.5t35.5 -178t62 -179.5q123 -6 247.5 14.5t214.5 53.5t162.5 67t109.5 59l37 24q22 16 39.5 20.5t30.5 -5t17 -34.5q14 -97 -39 -121q-208 -97 -467 -134q-135 -20 -317 -16q41 -96 110 -176.5t137 -127t130.5 -79t101.5 -43.5l39 -12q143 -23 263 15 +q195 99 314 289t119 418zM2123 621q-14 -135 -40 -212q-70 -208 -181.5 -346.5t-318.5 -253.5q-48 -33 -82 -44q-72 -26 -163 -16q-36 -3 -73 -3q-283 0 -504.5 173t-295.5 442q-1 0 -4 0.5t-5 0.5q-6 -50 2.5 -112.5t26 -115t36 -98t31.5 -71.5l14 -26q8 -12 54 -82 +q-71 38 -124.5 106.5t-78.5 140t-39.5 137t-17.5 107.5l-2 42q-5 2 -33.5 12.5t-48.5 18t-53 20.5t-57.5 25t-50 25.5t-42.5 27t-25 25.5q19 -10 50.5 -25.5t113 -45.5t145.5 -38l2 32q11 149 94 290q41 202 176 365q28 115 81 214q15 28 32 45t49 32q158 74 303.5 104 +t302 11t306.5 -97q220 -115 333 -336t87 -474z" /> + <glyph glyph-name="uniF2DA" unicode="&#xf2da;" horiz-adv-x="1792" +d="M1341 752q29 44 -6.5 129.5t-121.5 142.5q-58 39 -125.5 53.5t-118 4.5t-68.5 -37q-12 -23 -4.5 -28t42.5 -10q23 -3 38.5 -5t44.5 -9.5t56 -17.5q36 -13 67.5 -31.5t53 -37t40 -38.5t30.5 -38t22 -34.5t16.5 -28.5t12 -18.5t10.5 -6t11 9.5zM1704 178 +q-52 -127 -148.5 -220t-214.5 -141.5t-253 -60.5t-266 13.5t-251 91t-210 161.5t-141.5 235.5t-46.5 303.5q1 41 8.5 84.5t12.5 64t24 80.5t23 73q-51 -208 1 -397t173 -318t291 -206t346 -83t349 74.5t289 244.5q20 27 18 14q0 -4 -4 -14zM1465 627q0 -104 -40.5 -199 +t-108.5 -164t-162 -109.5t-198 -40.5t-198 40.5t-162 109.5t-108.5 164t-40.5 199t40.5 199t108.5 164t162 109.5t198 40.5t198 -40.5t162 -109.5t108.5 -164t40.5 -199zM1752 915q-65 147 -180.5 251t-253 153.5t-292 53.5t-301 -36.5t-275.5 -129t-220 -211.5t-131 -297 +t-10 -373q-49 161 -51.5 311.5t35.5 272.5t109 227t165.5 180.5t207 126t232 71t242.5 9t236 -54t216 -124.5t178 -197q33 -50 62 -121t31 -112zM1690 573q12 244 -136.5 416t-396.5 240q-8 0 -10 5t24 8q125 -4 230 -50t173 -120t116 -168.5t58.5 -199t-1 -208 +t-61.5 -197.5t-122.5 -167t-185 -117.5t-248.5 -46.5q108 30 201.5 80t174 123t129.5 176.5t55 225.5z" /> + <glyph glyph-name="uniF2DB" unicode="&#xf2db;" +d="M192 256v-128h-112q-16 0 -16 16v16h-48q-16 0 -16 16v32q0 16 16 16h48v16q0 16 16 16h112zM192 512v-128h-112q-16 0 -16 16v16h-48q-16 0 -16 16v32q0 16 16 16h48v16q0 16 16 16h112zM192 768v-128h-112q-16 0 -16 16v16h-48q-16 0 -16 16v32q0 16 16 16h48v16 +q0 16 16 16h112zM192 1024v-128h-112q-16 0 -16 16v16h-48q-16 0 -16 16v32q0 16 16 16h48v16q0 16 16 16h112zM192 1280v-128h-112q-16 0 -16 16v16h-48q-16 0 -16 16v32q0 16 16 16h48v16q0 16 16 16h112zM1280 1440v-1472q0 -40 -28 -68t-68 -28h-832q-40 0 -68 28 +t-28 68v1472q0 40 28 68t68 28h832q40 0 68 -28t28 -68zM1536 208v-32q0 -16 -16 -16h-48v-16q0 -16 -16 -16h-112v128h112q16 0 16 -16v-16h48q16 0 16 -16zM1536 464v-32q0 -16 -16 -16h-48v-16q0 -16 -16 -16h-112v128h112q16 0 16 -16v-16h48q16 0 16 -16zM1536 720v-32 +q0 -16 -16 -16h-48v-16q0 -16 -16 -16h-112v128h112q16 0 16 -16v-16h48q16 0 16 -16zM1536 976v-32q0 -16 -16 -16h-48v-16q0 -16 -16 -16h-112v128h112q16 0 16 -16v-16h48q16 0 16 -16zM1536 1232v-32q0 -16 -16 -16h-48v-16q0 -16 -16 -16h-112v128h112q16 0 16 -16v-16 +h48q16 0 16 -16z" /> + <glyph glyph-name="uniF2DC" unicode="&#xf2dc;" horiz-adv-x="1664" +d="M1566 419l-167 -33l186 -107q23 -13 29.5 -38.5t-6.5 -48.5q-14 -23 -39 -29.5t-48 6.5l-186 106l55 -160q13 -38 -12 -63.5t-60.5 -20.5t-48.5 42l-102 300l-271 156v-313l208 -238q16 -18 17 -39t-11 -36.5t-28.5 -25t-37 -5.5t-36.5 22l-112 128v-214q0 -26 -19 -45 +t-45 -19t-45 19t-19 45v214l-112 -128q-16 -18 -36.5 -22t-37 5.5t-28.5 25t-11 36.5t17 39l208 238v313l-271 -156l-102 -300q-13 -37 -48.5 -42t-60.5 20.5t-12 63.5l55 160l-186 -106q-23 -13 -48 -6.5t-39 29.5q-13 23 -6.5 48.5t29.5 38.5l186 107l-167 33 +q-29 6 -42 29t-8.5 46.5t25.5 40t50 10.5l310 -62l271 157l-271 157l-310 -62q-4 -1 -13 -1q-27 0 -44 18t-19 40t11 43t40 26l167 33l-186 107q-23 13 -29.5 38.5t6.5 48.5t39 30t48 -7l186 -106l-55 160q-13 38 12 63.5t60.5 20.5t48.5 -42l102 -300l271 -156v313 +l-208 238q-16 18 -17 39t11 36.5t28.5 25t37 5.5t36.5 -22l112 -128v214q0 26 19 45t45 19t45 -19t19 -45v-214l112 128q16 18 36.5 22t37 -5.5t28.5 -25t11 -36.5t-17 -39l-208 -238v-313l271 156l102 300q13 37 48.5 42t60.5 -20.5t12 -63.5l-55 -160l186 106 +q23 13 48 6.5t39 -29.5q13 -23 6.5 -48.5t-29.5 -38.5l-186 -107l167 -33q27 -5 40 -26t11 -43t-19 -40t-44 -18q-9 0 -13 1l-310 62l-271 -157l271 -157l310 62q29 6 50 -10.5t25.5 -40t-8.5 -46.5t-42 -29z" /> + <glyph glyph-name="uniF2DD" unicode="&#xf2dd;" horiz-adv-x="1792" +d="M1473 607q7 118 -33 226.5t-113 189t-177 131t-221 57.5q-116 7 -225.5 -32t-192 -110.5t-135 -175t-59.5 -220.5q-7 -118 33 -226.5t113 -189t177.5 -131t221.5 -57.5q155 -9 293 59t224 195.5t94 283.5zM1792 1536l-349 -348q120 -117 180.5 -272t50.5 -321 +q-11 -183 -102 -339t-241 -255.5t-332 -124.5l-999 -132l347 347q-120 116 -180.5 271.5t-50.5 321.5q11 184 102 340t241.5 255.5t332.5 124.5q167 22 500 66t500 66z" /> + <glyph glyph-name="uniF2DE" unicode="&#xf2de;" horiz-adv-x="1792" +d="M948 508l163 -329h-51l-175 350l-171 -350h-49l179 374l-78 33l21 49l240 -102l-21 -50zM563 1100l304 -130l-130 -304l-304 130zM907 915l240 -103l-103 -239l-239 102zM1188 765l191 -81l-82 -190l-190 81zM1680 640q0 159 -62 304t-167.5 250.5t-250.5 167.5t-304 62 +t-304 -62t-250.5 -167.5t-167.5 -250.5t-62 -304t62 -304t167.5 -250.5t250.5 -167.5t304 -62t304 62t250.5 167.5t167.5 250.5t62 304zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71 +t286 -191t191 -286t71 -348z" /> + <glyph glyph-name="uniF2E0" unicode="&#xf2e0;" horiz-adv-x="1920" +d="M1334 302q-4 24 -27.5 34t-49.5 10.5t-48.5 12.5t-25.5 38q-5 47 33 139.5t75 181t32 127.5q-14 101 -117 103q-45 1 -75 -16l-3 -2l-5 -2.5t-4.5 -2t-5 -2t-5 -0.5t-6 1.5t-6 3.5t-6.5 5q-3 2 -9 8.5t-9 9t-8.5 7.5t-9.5 7.5t-9.5 5.5t-11 4.5t-11.5 2.5q-30 5 -48 -3 +t-45 -31q-1 -1 -9 -8.5t-12.5 -11t-15 -10t-16.5 -5.5t-17 3q-54 27 -84 40q-41 18 -94 -5t-76 -65q-16 -28 -41 -98.5t-43.5 -132.5t-40 -134t-21.5 -73q-22 -69 18.5 -119t110.5 -46q30 2 50.5 15t38.5 46q7 13 79 199.5t77 194.5q6 11 21.5 18t29.5 0q27 -15 21 -53 +q-2 -18 -51 -139.5t-50 -132.5q-6 -38 19.5 -56.5t60.5 -7t55 49.5q4 8 45.5 92t81.5 163.5t46 88.5q20 29 41 28q29 0 25 -38q-2 -16 -65.5 -147.5t-70.5 -159.5q-12 -53 13 -103t74 -74q17 -9 51 -15.5t71.5 -8t62.5 14t20 48.5zM383 86q3 -15 -5 -27.5t-23 -15.5 +q-14 -3 -26.5 5t-15.5 23q-3 14 5 27t22 16t27 -5t16 -23zM953 -177q12 -17 8.5 -37.5t-20.5 -32.5t-37.5 -8t-32.5 21q-11 17 -7.5 37.5t20.5 32.5t37.5 8t31.5 -21zM177 635q-18 -27 -49.5 -33t-57.5 13q-26 18 -32 50t12 58q18 27 49.5 33t57.5 -12q26 -19 32 -50.5 +t-12 -58.5zM1467 -42q19 -28 13 -61.5t-34 -52.5t-60.5 -13t-51.5 34t-13 61t33 53q28 19 60.5 13t52.5 -34zM1579 562q69 -113 42.5 -244.5t-134.5 -207.5q-90 -63 -199 -60q-20 -80 -84.5 -127t-143.5 -44.5t-140 57.5q-12 -9 -13 -10q-103 -71 -225 -48.5t-193 126.5 +q-50 73 -53 164q-83 14 -142.5 70.5t-80.5 128t-2 152t81 138.5q-36 60 -38 128t24.5 125t79.5 98.5t121 50.5q32 85 99 148t146.5 91.5t168 17t159.5 -66.5q72 21 140 17.5t128.5 -36t104.5 -80t67.5 -115t17.5 -140.5q52 -16 87 -57t45.5 -89t-5.5 -99.5t-58 -87.5z +M455 1222q14 -20 9.5 -44.5t-24.5 -38.5q-19 -14 -43.5 -9.5t-37.5 24.5q-14 20 -9.5 44.5t24.5 38.5q19 14 43.5 9.5t37.5 -24.5zM614 1503q4 -16 -5 -30.5t-26 -18.5t-31 5.5t-18 26.5q-3 17 6.5 31t25.5 18q17 4 31 -5.5t17 -26.5zM1800 555q4 -20 -6.5 -37t-30.5 -21 +q-19 -4 -36 6.5t-21 30.5t6.5 37t30.5 22q20 4 36.5 -7.5t20.5 -30.5zM1136 1448q16 -27 8.5 -58.5t-35.5 -47.5q-27 -16 -57.5 -8.5t-46.5 34.5q-16 28 -8.5 59t34.5 48t58 9t47 -36zM1882 792q4 -15 -4 -27.5t-23 -16.5q-15 -3 -27.5 5.5t-15.5 22.5q-3 15 5 28t23 16 +q14 3 26.5 -5t15.5 -23zM1691 1033q15 -22 10.5 -49t-26.5 -43q-22 -15 -49 -10t-42 27t-10 49t27 43t48.5 11t41.5 -28z" /> + <glyph glyph-name="uniF2E1" unicode="&#xf2e1;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2E2" unicode="&#xf2e2;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2E3" unicode="&#xf2e3;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2E4" unicode="&#xf2e4;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2E5" unicode="&#xf2e5;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2E6" unicode="&#xf2e6;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2E7" unicode="&#xf2e7;" horiz-adv-x="1792" + /> + <glyph glyph-name="_698" unicode="&#xf2e8;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2E9" unicode="&#xf2e9;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2EA" unicode="&#xf2ea;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2EB" unicode="&#xf2eb;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2EC" unicode="&#xf2ec;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2ED" unicode="&#xf2ed;" horiz-adv-x="1792" + /> + <glyph glyph-name="uniF2EE" unicode="&#xf2ee;" horiz-adv-x="1792" + /> + <glyph glyph-name="lessequal" unicode="&#xf500;" horiz-adv-x="1792" + /> + </font> +</defs></svg>
diff --git go-ethereum/docs/static/fonts/fontawesome-webfont.ttf celo/docs/static/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..35acda2fa1196aad98c2adf4378a7611dd713aa3 Binary files /dev/null and celo/docs/static/fonts/fontawesome-webfont.ttf differ
diff --git go-ethereum/docs/static/fonts/fontawesome-webfont.woff celo/docs/static/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..400014a4b06eee3d0c0d54402a47ab2601b2862b Binary files /dev/null and celo/docs/static/fonts/fontawesome-webfont.woff differ
diff --git go-ethereum/docs/static/fonts/fontawesome-webfont.woff2 celo/docs/static/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..4d13fc60404b91e398a37200c4a77b645cfd9586 Binary files /dev/null and celo/docs/static/fonts/fontawesome-webfont.woff2 differ
diff --git go-ethereum/docs/static/images/celo-logo.png celo/docs/static/images/celo-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2631b486ea554a68eab629216ea54af8d67b37dc Binary files /dev/null and celo/docs/static/images/celo-logo.png differ
diff --git go-ethereum/docs/static/images/emoji/+1.png celo/docs/static/images/emoji/+1.png new file mode 100644 index 0000000000000000000000000000000000000000..81786c1d8f5ed810fd8351f74996f9b64dbf5ffb Binary files /dev/null and celo/docs/static/images/emoji/+1.png differ
diff --git go-ethereum/docs/static/images/emoji/-1.png celo/docs/static/images/emoji/-1.png new file mode 100644 index 0000000000000000000000000000000000000000..41c6b825d6a6abf736a1b882c951c1eb942afabf Binary files /dev/null and celo/docs/static/images/emoji/-1.png differ
diff --git go-ethereum/docs/static/images/emoji/100.png celo/docs/static/images/emoji/100.png new file mode 100644 index 0000000000000000000000000000000000000000..ca3bb9bcf6304ded9679998f00959e1732d5a960 Binary files /dev/null and celo/docs/static/images/emoji/100.png differ
diff --git go-ethereum/docs/static/images/emoji/1234.png celo/docs/static/images/emoji/1234.png new file mode 100644 index 0000000000000000000000000000000000000000..c47c2e1f9f0d0e86bc78e3a9ee96e643b83a57f1 Binary files /dev/null and celo/docs/static/images/emoji/1234.png differ
diff --git go-ethereum/docs/static/images/emoji/8ball.png celo/docs/static/images/emoji/8ball.png new file mode 100644 index 0000000000000000000000000000000000000000..c2c710d45019ca3110a50771ef41d19da878e708 Binary files /dev/null and celo/docs/static/images/emoji/8ball.png differ
diff --git go-ethereum/docs/static/images/emoji/a.png celo/docs/static/images/emoji/a.png new file mode 100644 index 0000000000000000000000000000000000000000..09ff6d6f184399df9750220cd697392017b85170 Binary files /dev/null and celo/docs/static/images/emoji/a.png differ
diff --git go-ethereum/docs/static/images/emoji/ab.png celo/docs/static/images/emoji/ab.png new file mode 100644 index 0000000000000000000000000000000000000000..2a522204767b0aafef8129675cb7042f3d01b975 Binary files /dev/null and celo/docs/static/images/emoji/ab.png differ
diff --git go-ethereum/docs/static/images/emoji/abc.png celo/docs/static/images/emoji/abc.png new file mode 100644 index 0000000000000000000000000000000000000000..505d40a15572fcaf3b9455d5db41986513360ba4 Binary files /dev/null and celo/docs/static/images/emoji/abc.png differ
diff --git go-ethereum/docs/static/images/emoji/abcd.png celo/docs/static/images/emoji/abcd.png new file mode 100644 index 0000000000000000000000000000000000000000..5218470b63c93b4074c814b879f5c7c542c3b855 Binary files /dev/null and celo/docs/static/images/emoji/abcd.png differ
diff --git go-ethereum/docs/static/images/emoji/accept.png celo/docs/static/images/emoji/accept.png new file mode 100644 index 0000000000000000000000000000000000000000..2d200903188ccc0c59156798e3a939c2dc88ab7e Binary files /dev/null and celo/docs/static/images/emoji/accept.png differ
diff --git go-ethereum/docs/static/images/emoji/aerial_tramway.png celo/docs/static/images/emoji/aerial_tramway.png new file mode 100644 index 0000000000000000000000000000000000000000..38f6dfe2334f4f48136eec16f5fbf4a507af5306 Binary files /dev/null and celo/docs/static/images/emoji/aerial_tramway.png differ
diff --git go-ethereum/docs/static/images/emoji/airplane.png celo/docs/static/images/emoji/airplane.png new file mode 100644 index 0000000000000000000000000000000000000000..8407cb6757565f0561b470f63a0d2a101fcb50f4 Binary files /dev/null and celo/docs/static/images/emoji/airplane.png differ
diff --git go-ethereum/docs/static/images/emoji/alarm_clock.png celo/docs/static/images/emoji/alarm_clock.png new file mode 100644 index 0000000000000000000000000000000000000000..86ca8c8ed450d0b249c5820f15d43b0504dd413d Binary files /dev/null and celo/docs/static/images/emoji/alarm_clock.png differ
diff --git go-ethereum/docs/static/images/emoji/alien.png celo/docs/static/images/emoji/alien.png new file mode 100644 index 0000000000000000000000000000000000000000..416de47be465f67782f65262c32c4c10a353b7f2 Binary files /dev/null and celo/docs/static/images/emoji/alien.png differ
diff --git go-ethereum/docs/static/images/emoji/ambulance.png celo/docs/static/images/emoji/ambulance.png new file mode 100644 index 0000000000000000000000000000000000000000..b740f45dba20daf0e9e917feed179467640e232d Binary files /dev/null and celo/docs/static/images/emoji/ambulance.png differ
diff --git go-ethereum/docs/static/images/emoji/anchor.png celo/docs/static/images/emoji/anchor.png new file mode 100644 index 0000000000000000000000000000000000000000..0c5192e64739dce90d59415bf5c3301db224ef61 Binary files /dev/null and celo/docs/static/images/emoji/anchor.png differ
diff --git go-ethereum/docs/static/images/emoji/angel.png celo/docs/static/images/emoji/angel.png new file mode 100644 index 0000000000000000000000000000000000000000..da52c310c64fd4b2b745df3c44b58aa1c9cdbf6c Binary files /dev/null and celo/docs/static/images/emoji/angel.png differ
diff --git go-ethereum/docs/static/images/emoji/anger.png celo/docs/static/images/emoji/anger.png new file mode 100644 index 0000000000000000000000000000000000000000..6fb4dca1854fdc7585ba22a7a953f35b42d1b935 Binary files /dev/null and celo/docs/static/images/emoji/anger.png differ
diff --git go-ethereum/docs/static/images/emoji/angry.png celo/docs/static/images/emoji/angry.png new file mode 100644 index 0000000000000000000000000000000000000000..f95bfa897ed9a5bf105a66a3e962eb11d6ccd1a4 Binary files /dev/null and celo/docs/static/images/emoji/angry.png differ
diff --git go-ethereum/docs/static/images/emoji/anguished.png celo/docs/static/images/emoji/anguished.png new file mode 100644 index 0000000000000000000000000000000000000000..c62594726df27dc216ed753173b576578522efd2 Binary files /dev/null and celo/docs/static/images/emoji/anguished.png differ
diff --git go-ethereum/docs/static/images/emoji/ant.png celo/docs/static/images/emoji/ant.png new file mode 100644 index 0000000000000000000000000000000000000000..b92d1cc14bde3bd6bb43ba03416719cec806d803 Binary files /dev/null and celo/docs/static/images/emoji/ant.png differ
diff --git go-ethereum/docs/static/images/emoji/apple.png celo/docs/static/images/emoji/apple.png new file mode 100644 index 0000000000000000000000000000000000000000..08aa17b95137ef962f1957a447485dfef8a69339 Binary files /dev/null and celo/docs/static/images/emoji/apple.png differ
diff --git go-ethereum/docs/static/images/emoji/aquarius.png celo/docs/static/images/emoji/aquarius.png new file mode 100644 index 0000000000000000000000000000000000000000..cbff66edcf3c5308ad51cd25a8168158934747ce Binary files /dev/null and celo/docs/static/images/emoji/aquarius.png differ
diff --git go-ethereum/docs/static/images/emoji/aries.png celo/docs/static/images/emoji/aries.png new file mode 100644 index 0000000000000000000000000000000000000000..aab5e8809f5bc85e0a9af9615a12c3945d464f8a Binary files /dev/null and celo/docs/static/images/emoji/aries.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_backward.png celo/docs/static/images/emoji/arrow_backward.png new file mode 100644 index 0000000000000000000000000000000000000000..088621834f79d886414b5bce8038f51848600d2f Binary files /dev/null and celo/docs/static/images/emoji/arrow_backward.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_double_down.png celo/docs/static/images/emoji/arrow_double_down.png new file mode 100644 index 0000000000000000000000000000000000000000..2ecbebcda13081ebc7167ad60c915c76af94d3a8 Binary files /dev/null and celo/docs/static/images/emoji/arrow_double_down.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_double_up.png celo/docs/static/images/emoji/arrow_double_up.png new file mode 100644 index 0000000000000000000000000000000000000000..2bd6659b142ecf5dba7ba843e762639fcefd28fc Binary files /dev/null and celo/docs/static/images/emoji/arrow_double_up.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_down.png celo/docs/static/images/emoji/arrow_down.png new file mode 100644 index 0000000000000000000000000000000000000000..e6702f02baeebbf7d31c6da3a59e52002a574935 Binary files /dev/null and celo/docs/static/images/emoji/arrow_down.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_down_small.png celo/docs/static/images/emoji/arrow_down_small.png new file mode 100644 index 0000000000000000000000000000000000000000..22d383a9205ca11e50fb302316d4f2415f1e78ca Binary files /dev/null and celo/docs/static/images/emoji/arrow_down_small.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_forward.png celo/docs/static/images/emoji/arrow_forward.png new file mode 100644 index 0000000000000000000000000000000000000000..fbfe711b64debda17e7a38e48ef0e3d0f05f85c3 Binary files /dev/null and celo/docs/static/images/emoji/arrow_forward.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_heading_down.png celo/docs/static/images/emoji/arrow_heading_down.png new file mode 100644 index 0000000000000000000000000000000000000000..56dd3b9d3c82b414776df4825c2cffe22a191695 Binary files /dev/null and celo/docs/static/images/emoji/arrow_heading_down.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_heading_up.png celo/docs/static/images/emoji/arrow_heading_up.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f670a1ef0d6507b478c10aef33805db936a958 Binary files /dev/null and celo/docs/static/images/emoji/arrow_heading_up.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_left.png celo/docs/static/images/emoji/arrow_left.png new file mode 100644 index 0000000000000000000000000000000000000000..d64ac619c9d23bfd68d8cbd3bde58419f57d9719 Binary files /dev/null and celo/docs/static/images/emoji/arrow_left.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_lower_left.png celo/docs/static/images/emoji/arrow_lower_left.png new file mode 100644 index 0000000000000000000000000000000000000000..55fb03c412185cd5f976dfad3c0ea0e2161e45dd Binary files /dev/null and celo/docs/static/images/emoji/arrow_lower_left.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_lower_right.png celo/docs/static/images/emoji/arrow_lower_right.png new file mode 100644 index 0000000000000000000000000000000000000000..da8fb8294d91d46426752555c1b32e9543f6f4f1 Binary files /dev/null and celo/docs/static/images/emoji/arrow_lower_right.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_right.png celo/docs/static/images/emoji/arrow_right.png new file mode 100644 index 0000000000000000000000000000000000000000..6d483b5144f38155564201bb77b7de0921c23de7 Binary files /dev/null and celo/docs/static/images/emoji/arrow_right.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_right_hook.png celo/docs/static/images/emoji/arrow_right_hook.png new file mode 100644 index 0000000000000000000000000000000000000000..8b4ea6e1720e19534d9bdcddb67d4a3a7a4c72a0 Binary files /dev/null and celo/docs/static/images/emoji/arrow_right_hook.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_up.png celo/docs/static/images/emoji/arrow_up.png new file mode 100644 index 0000000000000000000000000000000000000000..b5b0688d3c60baa9174e99c9d427c32afe6b43c2 Binary files /dev/null and celo/docs/static/images/emoji/arrow_up.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_up_down.png celo/docs/static/images/emoji/arrow_up_down.png new file mode 100644 index 0000000000000000000000000000000000000000..be423de78020a7f0eb436173dac2b2b63b5a2dee Binary files /dev/null and celo/docs/static/images/emoji/arrow_up_down.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_up_small.png celo/docs/static/images/emoji/arrow_up_small.png new file mode 100644 index 0000000000000000000000000000000000000000..3f40bfb89b2619f143d3bcaf30b9f0acc42961f9 Binary files /dev/null and celo/docs/static/images/emoji/arrow_up_small.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_upper_left.png celo/docs/static/images/emoji/arrow_upper_left.png new file mode 100644 index 0000000000000000000000000000000000000000..2950ae2f986bb2f1a5c87a8099ed3eead29df0a2 Binary files /dev/null and celo/docs/static/images/emoji/arrow_upper_left.png differ
diff --git go-ethereum/docs/static/images/emoji/arrow_upper_right.png celo/docs/static/images/emoji/arrow_upper_right.png new file mode 100644 index 0000000000000000000000000000000000000000..e23790ba18d5c2cf1efad64293af7c131b2ff7c1 Binary files /dev/null and celo/docs/static/images/emoji/arrow_upper_right.png differ
diff --git go-ethereum/docs/static/images/emoji/arrows_clockwise.png celo/docs/static/images/emoji/arrows_clockwise.png new file mode 100644 index 0000000000000000000000000000000000000000..5f84d7e72b79e1ada173447a15f44444dcc1b5e8 Binary files /dev/null and celo/docs/static/images/emoji/arrows_clockwise.png differ
diff --git go-ethereum/docs/static/images/emoji/arrows_counterclockwise.png celo/docs/static/images/emoji/arrows_counterclockwise.png new file mode 100644 index 0000000000000000000000000000000000000000..3e06f5b3ceccf54bf64e4823bd074d31f22f87b9 Binary files /dev/null and celo/docs/static/images/emoji/arrows_counterclockwise.png differ
diff --git go-ethereum/docs/static/images/emoji/art.png celo/docs/static/images/emoji/art.png new file mode 100644 index 0000000000000000000000000000000000000000..d45212b0340418f08b4c2dc74bbcc9a837af917f Binary files /dev/null and celo/docs/static/images/emoji/art.png differ
diff --git go-ethereum/docs/static/images/emoji/articulated_lorry.png celo/docs/static/images/emoji/articulated_lorry.png new file mode 100644 index 0000000000000000000000000000000000000000..81ec1f917411f42a24c21a66e249e0c2a89a8972 Binary files /dev/null and celo/docs/static/images/emoji/articulated_lorry.png differ
diff --git go-ethereum/docs/static/images/emoji/astonished.png celo/docs/static/images/emoji/astonished.png new file mode 100644 index 0000000000000000000000000000000000000000..858a83484a8d7dcacf867dff9f7513f4b4a668af Binary files /dev/null and celo/docs/static/images/emoji/astonished.png differ
diff --git go-ethereum/docs/static/images/emoji/atm.png celo/docs/static/images/emoji/atm.png new file mode 100644 index 0000000000000000000000000000000000000000..c2846e792183b0957c0821eb27ae38c4dedf4c66 Binary files /dev/null and celo/docs/static/images/emoji/atm.png differ
diff --git go-ethereum/docs/static/images/emoji/b.png celo/docs/static/images/emoji/b.png new file mode 100644 index 0000000000000000000000000000000000000000..8742b3d2e3ebc335f654f4279519d5413badb699 Binary files /dev/null and celo/docs/static/images/emoji/b.png differ
diff --git go-ethereum/docs/static/images/emoji/baby.png celo/docs/static/images/emoji/baby.png new file mode 100644 index 0000000000000000000000000000000000000000..3b29da40b602d1fc659acd93d9f02ab60e9a5bed Binary files /dev/null and celo/docs/static/images/emoji/baby.png differ
diff --git go-ethereum/docs/static/images/emoji/baby_bottle.png celo/docs/static/images/emoji/baby_bottle.png new file mode 100644 index 0000000000000000000000000000000000000000..1b2cfe5e30142e5d17119050a1ea452a33130e7a Binary files /dev/null and celo/docs/static/images/emoji/baby_bottle.png differ
diff --git go-ethereum/docs/static/images/emoji/baby_chick.png celo/docs/static/images/emoji/baby_chick.png new file mode 100644 index 0000000000000000000000000000000000000000..9be8d2930062b1e0c798fa553fb8818aa6615465 Binary files /dev/null and celo/docs/static/images/emoji/baby_chick.png differ
diff --git go-ethereum/docs/static/images/emoji/baby_symbol.png celo/docs/static/images/emoji/baby_symbol.png new file mode 100644 index 0000000000000000000000000000000000000000..2e58725cf56563837391f3e9e0f2203582095a1c Binary files /dev/null and celo/docs/static/images/emoji/baby_symbol.png differ
diff --git go-ethereum/docs/static/images/emoji/back.png celo/docs/static/images/emoji/back.png new file mode 100644 index 0000000000000000000000000000000000000000..0cde62876292119cf05390e445a27b7ef38686fd Binary files /dev/null and celo/docs/static/images/emoji/back.png differ
diff --git go-ethereum/docs/static/images/emoji/baggage_claim.png celo/docs/static/images/emoji/baggage_claim.png new file mode 100644 index 0000000000000000000000000000000000000000..59ae044a45e8849549f93a241794c0d3daf5540d Binary files /dev/null and celo/docs/static/images/emoji/baggage_claim.png differ
diff --git go-ethereum/docs/static/images/emoji/balloon.png celo/docs/static/images/emoji/balloon.png new file mode 100644 index 0000000000000000000000000000000000000000..0344897025624a967bb3efcf1d2abfa469eb6f7a Binary files /dev/null and celo/docs/static/images/emoji/balloon.png differ
diff --git go-ethereum/docs/static/images/emoji/ballot_box_with_check.png celo/docs/static/images/emoji/ballot_box_with_check.png new file mode 100644 index 0000000000000000000000000000000000000000..f07a466c77847298d320668d6d2911377cc51139 Binary files /dev/null and celo/docs/static/images/emoji/ballot_box_with_check.png differ
diff --git go-ethereum/docs/static/images/emoji/bamboo.png celo/docs/static/images/emoji/bamboo.png new file mode 100644 index 0000000000000000000000000000000000000000..fc858d0fc2c2431826a012572b7c565585fe4ade Binary files /dev/null and celo/docs/static/images/emoji/bamboo.png differ
diff --git go-ethereum/docs/static/images/emoji/banana.png celo/docs/static/images/emoji/banana.png new file mode 100644 index 0000000000000000000000000000000000000000..a0563afb9584524209bf7616ddfa7ecf751feb96 Binary files /dev/null and celo/docs/static/images/emoji/banana.png differ
diff --git go-ethereum/docs/static/images/emoji/bangbang.png celo/docs/static/images/emoji/bangbang.png new file mode 100644 index 0000000000000000000000000000000000000000..7270f0afe6e66920a0cc4059ef29d04e50dc1eb5 Binary files /dev/null and celo/docs/static/images/emoji/bangbang.png differ
diff --git go-ethereum/docs/static/images/emoji/bank.png celo/docs/static/images/emoji/bank.png new file mode 100644 index 0000000000000000000000000000000000000000..1faa8777e42a17dcf75fe2e702638c83e9d0fcbb Binary files /dev/null and celo/docs/static/images/emoji/bank.png differ
diff --git go-ethereum/docs/static/images/emoji/bar_chart.png celo/docs/static/images/emoji/bar_chart.png new file mode 100644 index 0000000000000000000000000000000000000000..09d7301c4d88b5244625b6608f5f7f45175519d0 Binary files /dev/null and celo/docs/static/images/emoji/bar_chart.png differ
diff --git go-ethereum/docs/static/images/emoji/barber.png celo/docs/static/images/emoji/barber.png new file mode 100644 index 0000000000000000000000000000000000000000..a10cb232286d1b1fa0a5f0d6b77cbcc468311e9b Binary files /dev/null and celo/docs/static/images/emoji/barber.png differ
diff --git go-ethereum/docs/static/images/emoji/baseball.png celo/docs/static/images/emoji/baseball.png new file mode 100644 index 0000000000000000000000000000000000000000..da004e2ead04f570b01aa9c0723a27117b182838 Binary files /dev/null and celo/docs/static/images/emoji/baseball.png differ
diff --git go-ethereum/docs/static/images/emoji/basketball.png celo/docs/static/images/emoji/basketball.png new file mode 100644 index 0000000000000000000000000000000000000000..ef694bec4c92aa8837c12eae7fe3c51dfc8d7cca Binary files /dev/null and celo/docs/static/images/emoji/basketball.png differ
diff --git go-ethereum/docs/static/images/emoji/bath.png celo/docs/static/images/emoji/bath.png new file mode 100644 index 0000000000000000000000000000000000000000..8f75d1d2499e46ef31b25c735c76eae87f01c2ee Binary files /dev/null and celo/docs/static/images/emoji/bath.png differ
diff --git go-ethereum/docs/static/images/emoji/bathtub.png celo/docs/static/images/emoji/bathtub.png new file mode 100644 index 0000000000000000000000000000000000000000..1c3f844ab262b2ad6c00b61bcafeea1cc13ab861 Binary files /dev/null and celo/docs/static/images/emoji/bathtub.png differ
diff --git go-ethereum/docs/static/images/emoji/battery.png celo/docs/static/images/emoji/battery.png new file mode 100644 index 0000000000000000000000000000000000000000..aa7eedce4bbcb3e2d4c7f4a6fcfd321e7e5ecafa Binary files /dev/null and celo/docs/static/images/emoji/battery.png differ
diff --git go-ethereum/docs/static/images/emoji/bear.png celo/docs/static/images/emoji/bear.png new file mode 100644 index 0000000000000000000000000000000000000000..f5afe920e8e23c2f033d9cb9476a303a6e520d01 Binary files /dev/null and celo/docs/static/images/emoji/bear.png differ
diff --git go-ethereum/docs/static/images/emoji/bee.png celo/docs/static/images/emoji/bee.png new file mode 100644 index 0000000000000000000000000000000000000000..f53733953afad79cb582a1e7edc8d847b8767f1a Binary files /dev/null and celo/docs/static/images/emoji/bee.png differ
diff --git go-ethereum/docs/static/images/emoji/beer.png celo/docs/static/images/emoji/beer.png new file mode 100644 index 0000000000000000000000000000000000000000..cd78bed7440fee98515a0748851cceddb4efae1b Binary files /dev/null and celo/docs/static/images/emoji/beer.png differ
diff --git go-ethereum/docs/static/images/emoji/beers.png celo/docs/static/images/emoji/beers.png new file mode 100644 index 0000000000000000000000000000000000000000..cc5e4ab5aa968151058aa335f14b1c458f34f57f Binary files /dev/null and celo/docs/static/images/emoji/beers.png differ
diff --git go-ethereum/docs/static/images/emoji/beetle.png celo/docs/static/images/emoji/beetle.png new file mode 100644 index 0000000000000000000000000000000000000000..222577ca7ea582012b50223a28095511ae143146 Binary files /dev/null and celo/docs/static/images/emoji/beetle.png differ
diff --git go-ethereum/docs/static/images/emoji/beginner.png celo/docs/static/images/emoji/beginner.png new file mode 100644 index 0000000000000000000000000000000000000000..1f022d175dac777aacc371be6ea0b56ffd398ba8 Binary files /dev/null and celo/docs/static/images/emoji/beginner.png differ
diff --git go-ethereum/docs/static/images/emoji/bell.png celo/docs/static/images/emoji/bell.png new file mode 100644 index 0000000000000000000000000000000000000000..69acceb286ef2a3ede896e8ea2427e25062bb962 Binary files /dev/null and celo/docs/static/images/emoji/bell.png differ
diff --git go-ethereum/docs/static/images/emoji/bento.png celo/docs/static/images/emoji/bento.png new file mode 100644 index 0000000000000000000000000000000000000000..d6801124a4637a792c683ea0d245c2b9d0b004ab Binary files /dev/null and celo/docs/static/images/emoji/bento.png differ
diff --git go-ethereum/docs/static/images/emoji/bicyclist.png celo/docs/static/images/emoji/bicyclist.png new file mode 100644 index 0000000000000000000000000000000000000000..cbbd7c3863762177d949c509ff0209d7871f81f9 Binary files /dev/null and celo/docs/static/images/emoji/bicyclist.png differ
diff --git go-ethereum/docs/static/images/emoji/bike.png celo/docs/static/images/emoji/bike.png new file mode 100644 index 0000000000000000000000000000000000000000..65738602722677c81440b37a42db23c11624312a Binary files /dev/null and celo/docs/static/images/emoji/bike.png differ
diff --git go-ethereum/docs/static/images/emoji/bikini.png celo/docs/static/images/emoji/bikini.png new file mode 100644 index 0000000000000000000000000000000000000000..4ff63b40f8868b49a803bfcc642c229622f17f39 Binary files /dev/null and celo/docs/static/images/emoji/bikini.png differ
diff --git go-ethereum/docs/static/images/emoji/bird.png celo/docs/static/images/emoji/bird.png new file mode 100644 index 0000000000000000000000000000000000000000..e6be8c027866f2ccdaee2d6389da50c0cea5c227 Binary files /dev/null and celo/docs/static/images/emoji/bird.png differ
diff --git go-ethereum/docs/static/images/emoji/birthday.png celo/docs/static/images/emoji/birthday.png new file mode 100644 index 0000000000000000000000000000000000000000..36e8edcbec4e8a21b060753be80e4ff4ba36d8e3 Binary files /dev/null and celo/docs/static/images/emoji/birthday.png differ
diff --git go-ethereum/docs/static/images/emoji/black_circle.png celo/docs/static/images/emoji/black_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..e46f9df615f92e1c1f00e9a45f010a0a9138c13b Binary files /dev/null and celo/docs/static/images/emoji/black_circle.png differ
diff --git go-ethereum/docs/static/images/emoji/black_joker.png celo/docs/static/images/emoji/black_joker.png new file mode 100644 index 0000000000000000000000000000000000000000..4c78f3614d798dd3fd8a1c0331c2c340b85785e8 Binary files /dev/null and celo/docs/static/images/emoji/black_joker.png differ
diff --git go-ethereum/docs/static/images/emoji/black_medium_small_square.png celo/docs/static/images/emoji/black_medium_small_square.png new file mode 100644 index 0000000000000000000000000000000000000000..25bfe9c4534f50109e16bd3bd738093ef9575069 Binary files /dev/null and celo/docs/static/images/emoji/black_medium_small_square.png differ
diff --git go-ethereum/docs/static/images/emoji/black_medium_square.png celo/docs/static/images/emoji/black_medium_square.png new file mode 100644 index 0000000000000000000000000000000000000000..204cce12c27a53210ee7e0824da4febc8e8dd369 Binary files /dev/null and celo/docs/static/images/emoji/black_medium_square.png differ
diff --git go-ethereum/docs/static/images/emoji/black_nib.png celo/docs/static/images/emoji/black_nib.png new file mode 100644 index 0000000000000000000000000000000000000000..29f6994c11a79dcdf0ec9790fc33e79a0d24adbe Binary files /dev/null and celo/docs/static/images/emoji/black_nib.png differ
diff --git go-ethereum/docs/static/images/emoji/black_small_square.png celo/docs/static/images/emoji/black_small_square.png new file mode 100644 index 0000000000000000000000000000000000000000..a247751ece7d2a4d36d2c842a46957018cc637b2 Binary files /dev/null and celo/docs/static/images/emoji/black_small_square.png differ
diff --git go-ethereum/docs/static/images/emoji/black_square.png celo/docs/static/images/emoji/black_square.png new file mode 100644 index 0000000000000000000000000000000000000000..71da10de81ced24b98b721a771147c9b2e91242d Binary files /dev/null and celo/docs/static/images/emoji/black_square.png differ
diff --git go-ethereum/docs/static/images/emoji/black_square_button.png celo/docs/static/images/emoji/black_square_button.png new file mode 100644 index 0000000000000000000000000000000000000000..f2597e95661f2c7ff48d9bfefa23d63a2aaf290a Binary files /dev/null and celo/docs/static/images/emoji/black_square_button.png differ
diff --git go-ethereum/docs/static/images/emoji/blossom.png celo/docs/static/images/emoji/blossom.png new file mode 100644 index 0000000000000000000000000000000000000000..55a97353b474ba5faebf8322a235f7f98a318c53 Binary files /dev/null and celo/docs/static/images/emoji/blossom.png differ
diff --git go-ethereum/docs/static/images/emoji/blowfish.png celo/docs/static/images/emoji/blowfish.png new file mode 100644 index 0000000000000000000000000000000000000000..d3ad465851923b937ef6f1d22e0e33f5a9b1d4a5 Binary files /dev/null and celo/docs/static/images/emoji/blowfish.png differ
diff --git go-ethereum/docs/static/images/emoji/blue_book.png celo/docs/static/images/emoji/blue_book.png new file mode 100644 index 0000000000000000000000000000000000000000..e2b9e8c797ab05460602c8ce06abb2702412eadb Binary files /dev/null and celo/docs/static/images/emoji/blue_book.png differ
diff --git go-ethereum/docs/static/images/emoji/blue_car.png celo/docs/static/images/emoji/blue_car.png new file mode 100644 index 0000000000000000000000000000000000000000..978291e087d6f61a717757fd8c9be70bde621f30 Binary files /dev/null and celo/docs/static/images/emoji/blue_car.png differ
diff --git go-ethereum/docs/static/images/emoji/blue_heart.png celo/docs/static/images/emoji/blue_heart.png new file mode 100644 index 0000000000000000000000000000000000000000..baa29b31bcde24931dcbd256976695ff01c08f74 Binary files /dev/null and celo/docs/static/images/emoji/blue_heart.png differ
diff --git go-ethereum/docs/static/images/emoji/blush.png celo/docs/static/images/emoji/blush.png new file mode 100644 index 0000000000000000000000000000000000000000..3a95eb61a3af84e6de44eaf5b4e165fdf1d1d90d Binary files /dev/null and celo/docs/static/images/emoji/blush.png differ
diff --git go-ethereum/docs/static/images/emoji/boar.png celo/docs/static/images/emoji/boar.png new file mode 100644 index 0000000000000000000000000000000000000000..8196ad4a14b97d1b79d3e19c257f94396360022d Binary files /dev/null and celo/docs/static/images/emoji/boar.png differ
diff --git go-ethereum/docs/static/images/emoji/boat.png celo/docs/static/images/emoji/boat.png new file mode 100644 index 0000000000000000000000000000000000000000..ff656dc62bb78682ba3a1775eb4100459ac95bf5 Binary files /dev/null and celo/docs/static/images/emoji/boat.png differ
diff --git go-ethereum/docs/static/images/emoji/bomb.png celo/docs/static/images/emoji/bomb.png new file mode 100644 index 0000000000000000000000000000000000000000..3289787dcf9d5ce7aeabbcd377a2523e893de33d Binary files /dev/null and celo/docs/static/images/emoji/bomb.png differ
diff --git go-ethereum/docs/static/images/emoji/book.png celo/docs/static/images/emoji/book.png new file mode 100644 index 0000000000000000000000000000000000000000..8b698415c3df31dfe15d7ccf13b158404557e024 Binary files /dev/null and celo/docs/static/images/emoji/book.png differ
diff --git go-ethereum/docs/static/images/emoji/bookmark.png celo/docs/static/images/emoji/bookmark.png new file mode 100644 index 0000000000000000000000000000000000000000..6fc4ed90230d812bbb0e7459292a59072068b48a Binary files /dev/null and celo/docs/static/images/emoji/bookmark.png differ
diff --git go-ethereum/docs/static/images/emoji/bookmark_tabs.png celo/docs/static/images/emoji/bookmark_tabs.png new file mode 100644 index 0000000000000000000000000000000000000000..83782ff0c600647b8209754828babf82f4fb3eae Binary files /dev/null and celo/docs/static/images/emoji/bookmark_tabs.png differ
diff --git go-ethereum/docs/static/images/emoji/books.png celo/docs/static/images/emoji/books.png new file mode 100644 index 0000000000000000000000000000000000000000..dca06a1ad99323ebebd02ee97a07c8e12a07e307 Binary files /dev/null and celo/docs/static/images/emoji/books.png differ
diff --git go-ethereum/docs/static/images/emoji/boom.png celo/docs/static/images/emoji/boom.png new file mode 100644 index 0000000000000000000000000000000000000000..9d5bd0401401336f9e8d76cf25f68f6510500aab Binary files /dev/null and celo/docs/static/images/emoji/boom.png differ
diff --git go-ethereum/docs/static/images/emoji/boot.png celo/docs/static/images/emoji/boot.png new file mode 100644 index 0000000000000000000000000000000000000000..58d0fdbcd0cf81be6e14ec0ae8e65edf8317c91e Binary files /dev/null and celo/docs/static/images/emoji/boot.png differ
diff --git go-ethereum/docs/static/images/emoji/bouquet.png celo/docs/static/images/emoji/bouquet.png new file mode 100644 index 0000000000000000000000000000000000000000..ce637832e17e2e2abea71a72367ad5f9e0c176c3 Binary files /dev/null and celo/docs/static/images/emoji/bouquet.png differ
diff --git go-ethereum/docs/static/images/emoji/bow.png celo/docs/static/images/emoji/bow.png new file mode 100644 index 0000000000000000000000000000000000000000..024cb610492b005e11d41c368fdf0519c0ecba1c Binary files /dev/null and celo/docs/static/images/emoji/bow.png differ
diff --git go-ethereum/docs/static/images/emoji/bowling.png celo/docs/static/images/emoji/bowling.png new file mode 100644 index 0000000000000000000000000000000000000000..13d8ece2ee5489a81434c7988f2264eed327d8d0 Binary files /dev/null and celo/docs/static/images/emoji/bowling.png differ
diff --git go-ethereum/docs/static/images/emoji/bowtie.png celo/docs/static/images/emoji/bowtie.png new file mode 100644 index 0000000000000000000000000000000000000000..28ff0c787d533e2322317778b67baee8f5c441d6 Binary files /dev/null and celo/docs/static/images/emoji/bowtie.png differ
diff --git go-ethereum/docs/static/images/emoji/boy.png celo/docs/static/images/emoji/boy.png new file mode 100644 index 0000000000000000000000000000000000000000..f79f1f29807f76432586afa2304b475ee3abdb6e Binary files /dev/null and celo/docs/static/images/emoji/boy.png differ
diff --git go-ethereum/docs/static/images/emoji/bread.png celo/docs/static/images/emoji/bread.png new file mode 100644 index 0000000000000000000000000000000000000000..7e7c63753d3b1f8b94a4a96569339ec15c487e88 Binary files /dev/null and celo/docs/static/images/emoji/bread.png differ
diff --git go-ethereum/docs/static/images/emoji/bride_with_veil.png celo/docs/static/images/emoji/bride_with_veil.png new file mode 100644 index 0000000000000000000000000000000000000000..dd0b0cfdad1ca4d627d0ecbff9aaaec706e08a54 Binary files /dev/null and celo/docs/static/images/emoji/bride_with_veil.png differ
diff --git go-ethereum/docs/static/images/emoji/bridge_at_night.png celo/docs/static/images/emoji/bridge_at_night.png new file mode 100644 index 0000000000000000000000000000000000000000..495b06c3dfe7cb55791cf54a934d1453acf54da1 Binary files /dev/null and celo/docs/static/images/emoji/bridge_at_night.png differ
diff --git go-ethereum/docs/static/images/emoji/briefcase.png celo/docs/static/images/emoji/briefcase.png new file mode 100644 index 0000000000000000000000000000000000000000..46e82b0010c7d7056986d6651f6f26871e38117a Binary files /dev/null and celo/docs/static/images/emoji/briefcase.png differ
diff --git go-ethereum/docs/static/images/emoji/broken_heart.png celo/docs/static/images/emoji/broken_heart.png new file mode 100644 index 0000000000000000000000000000000000000000..a1bc850ecb4cfcdf75f8668bd0c668537a764223 Binary files /dev/null and celo/docs/static/images/emoji/broken_heart.png differ
diff --git go-ethereum/docs/static/images/emoji/bug.png celo/docs/static/images/emoji/bug.png new file mode 100644 index 0000000000000000000000000000000000000000..c2eaf7a708d8d66baa0203174ce825aa52906f25 Binary files /dev/null and celo/docs/static/images/emoji/bug.png differ
diff --git go-ethereum/docs/static/images/emoji/bulb.png celo/docs/static/images/emoji/bulb.png new file mode 100644 index 0000000000000000000000000000000000000000..23afca1c73f07aab1a20a73c41d67114ec031970 Binary files /dev/null and celo/docs/static/images/emoji/bulb.png differ
diff --git go-ethereum/docs/static/images/emoji/bullettrain_front.png celo/docs/static/images/emoji/bullettrain_front.png new file mode 100644 index 0000000000000000000000000000000000000000..16651acff8ebb8c7359f15db2e1eb9654ceb37be Binary files /dev/null and celo/docs/static/images/emoji/bullettrain_front.png differ
diff --git go-ethereum/docs/static/images/emoji/bullettrain_side.png celo/docs/static/images/emoji/bullettrain_side.png new file mode 100644 index 0000000000000000000000000000000000000000..8eca368458a3847fa2da6c92e0ad4c1a127bbb49 Binary files /dev/null and celo/docs/static/images/emoji/bullettrain_side.png differ
diff --git go-ethereum/docs/static/images/emoji/bus.png celo/docs/static/images/emoji/bus.png new file mode 100644 index 0000000000000000000000000000000000000000..823aa39e49d233729f3f3f1572f982c5c53fe36c Binary files /dev/null and celo/docs/static/images/emoji/bus.png differ
diff --git go-ethereum/docs/static/images/emoji/busstop.png celo/docs/static/images/emoji/busstop.png new file mode 100644 index 0000000000000000000000000000000000000000..94894847b54350bae3d3d7d53b4464c2af563d53 Binary files /dev/null and celo/docs/static/images/emoji/busstop.png differ
diff --git go-ethereum/docs/static/images/emoji/bust_in_silhouette.png celo/docs/static/images/emoji/bust_in_silhouette.png new file mode 100644 index 0000000000000000000000000000000000000000..dd7defe28655db13dd9733fe755db06d4c8f4f87 Binary files /dev/null and celo/docs/static/images/emoji/bust_in_silhouette.png differ
diff --git go-ethereum/docs/static/images/emoji/busts_in_silhouette.png celo/docs/static/images/emoji/busts_in_silhouette.png new file mode 100644 index 0000000000000000000000000000000000000000..1f3aabcff60a29ea1da4a0816c282c25817d31d5 Binary files /dev/null and celo/docs/static/images/emoji/busts_in_silhouette.png differ
diff --git go-ethereum/docs/static/images/emoji/cactus.png celo/docs/static/images/emoji/cactus.png new file mode 100644 index 0000000000000000000000000000000000000000..5a2c3cc725e393a3a9836280a8efd8fe414906a6 Binary files /dev/null and celo/docs/static/images/emoji/cactus.png differ
diff --git go-ethereum/docs/static/images/emoji/cake.png celo/docs/static/images/emoji/cake.png new file mode 100644 index 0000000000000000000000000000000000000000..efeb9b4b2145398db357367573993a03da9c30e3 Binary files /dev/null and celo/docs/static/images/emoji/cake.png differ
diff --git go-ethereum/docs/static/images/emoji/calendar.png celo/docs/static/images/emoji/calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..900b868bb9435bb3d410ef3ae5ee04f680c79bd9 Binary files /dev/null and celo/docs/static/images/emoji/calendar.png differ
diff --git go-ethereum/docs/static/images/emoji/calling.png celo/docs/static/images/emoji/calling.png new file mode 100644 index 0000000000000000000000000000000000000000..837897f261b2f2a2e9fb3d040b4cec6f501b81de Binary files /dev/null and celo/docs/static/images/emoji/calling.png differ
diff --git go-ethereum/docs/static/images/emoji/camel.png celo/docs/static/images/emoji/camel.png new file mode 100644 index 0000000000000000000000000000000000000000..496c186ae6cec08ebd74dd6c2778e5ffea0a6a5e Binary files /dev/null and celo/docs/static/images/emoji/camel.png differ
diff --git go-ethereum/docs/static/images/emoji/camera.png celo/docs/static/images/emoji/camera.png new file mode 100644 index 0000000000000000000000000000000000000000..397d03b39351adf90023aa3ed00cee36a4ff1d61 Binary files /dev/null and celo/docs/static/images/emoji/camera.png differ
diff --git go-ethereum/docs/static/images/emoji/cancer.png celo/docs/static/images/emoji/cancer.png new file mode 100644 index 0000000000000000000000000000000000000000..ea43a4a2a048ab575c02240833133f1456652898 Binary files /dev/null and celo/docs/static/images/emoji/cancer.png differ
diff --git go-ethereum/docs/static/images/emoji/candy.png celo/docs/static/images/emoji/candy.png new file mode 100644 index 0000000000000000000000000000000000000000..33722f236e96f848610a7ad2536a79adb4ca8f9b Binary files /dev/null and celo/docs/static/images/emoji/candy.png differ
diff --git go-ethereum/docs/static/images/emoji/capital_abcd.png celo/docs/static/images/emoji/capital_abcd.png new file mode 100644 index 0000000000000000000000000000000000000000..ffc0cba4b4362c1c3a11affb2d6534cb211525bb Binary files /dev/null and celo/docs/static/images/emoji/capital_abcd.png differ
diff --git go-ethereum/docs/static/images/emoji/capricorn.png celo/docs/static/images/emoji/capricorn.png new file mode 100644 index 0000000000000000000000000000000000000000..f2044e78935a95143d9139503ca5efff1927603b Binary files /dev/null and celo/docs/static/images/emoji/capricorn.png differ
diff --git go-ethereum/docs/static/images/emoji/car.png celo/docs/static/images/emoji/car.png new file mode 100644 index 0000000000000000000000000000000000000000..d70a2f06263fa99a8553765a6bfbb8a9a86a5597 Binary files /dev/null and celo/docs/static/images/emoji/car.png differ
diff --git go-ethereum/docs/static/images/emoji/card_index.png celo/docs/static/images/emoji/card_index.png new file mode 100644 index 0000000000000000000000000000000000000000..374e94e9e8466c0891b05f22af3929a016960034 Binary files /dev/null and celo/docs/static/images/emoji/card_index.png differ
diff --git go-ethereum/docs/static/images/emoji/carousel_horse.png celo/docs/static/images/emoji/carousel_horse.png new file mode 100644 index 0000000000000000000000000000000000000000..765d2c0a8bd2d11c61e9ba4f71e71d3aa42c744e Binary files /dev/null and celo/docs/static/images/emoji/carousel_horse.png differ
diff --git go-ethereum/docs/static/images/emoji/cat.png celo/docs/static/images/emoji/cat.png new file mode 100644 index 0000000000000000000000000000000000000000..09b9ef79a7d1aad4e9dd8fa8163b3ef5e9567b11 Binary files /dev/null and celo/docs/static/images/emoji/cat.png differ
diff --git go-ethereum/docs/static/images/emoji/cat2.png celo/docs/static/images/emoji/cat2.png new file mode 100644 index 0000000000000000000000000000000000000000..6dbc4c71e4b3ba86dde1265510001a2c5a03fd7e Binary files /dev/null and celo/docs/static/images/emoji/cat2.png differ
diff --git go-ethereum/docs/static/images/emoji/cd.png celo/docs/static/images/emoji/cd.png new file mode 100644 index 0000000000000000000000000000000000000000..baff835c489475511ac4e189a8d25ec52687735a Binary files /dev/null and celo/docs/static/images/emoji/cd.png differ
diff --git go-ethereum/docs/static/images/emoji/chart.png celo/docs/static/images/emoji/chart.png new file mode 100644 index 0000000000000000000000000000000000000000..ac2c4bb093e395db35ce002e96aebf82ea362a0b Binary files /dev/null and celo/docs/static/images/emoji/chart.png differ
diff --git go-ethereum/docs/static/images/emoji/chart_with_downwards_trend.png celo/docs/static/images/emoji/chart_with_downwards_trend.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0d2a113c70eb01e78006d76eb123f4c07006a5 Binary files /dev/null and celo/docs/static/images/emoji/chart_with_downwards_trend.png differ
diff --git go-ethereum/docs/static/images/emoji/chart_with_upwards_trend.png celo/docs/static/images/emoji/chart_with_upwards_trend.png new file mode 100644 index 0000000000000000000000000000000000000000..7c66745c9875d3e8ae73a183194e54d8921805c8 Binary files /dev/null and celo/docs/static/images/emoji/chart_with_upwards_trend.png differ
diff --git go-ethereum/docs/static/images/emoji/checkered_flag.png celo/docs/static/images/emoji/checkered_flag.png new file mode 100644 index 0000000000000000000000000000000000000000..ead4a68dd37d2d8d817e8452f2430e85b9ab6211 Binary files /dev/null and celo/docs/static/images/emoji/checkered_flag.png differ
diff --git go-ethereum/docs/static/images/emoji/cherries.png celo/docs/static/images/emoji/cherries.png new file mode 100644 index 0000000000000000000000000000000000000000..8d3e044f2f57a83f1d1a86c6607bd5eb871b7a29 Binary files /dev/null and celo/docs/static/images/emoji/cherries.png differ
diff --git go-ethereum/docs/static/images/emoji/cherry_blossom.png celo/docs/static/images/emoji/cherry_blossom.png new file mode 100644 index 0000000000000000000000000000000000000000..e03155499902f52b0d728ba83559c07f554d9dc9 Binary files /dev/null and celo/docs/static/images/emoji/cherry_blossom.png differ
diff --git go-ethereum/docs/static/images/emoji/chestnut.png celo/docs/static/images/emoji/chestnut.png new file mode 100644 index 0000000000000000000000000000000000000000..066fb6bf6df4880c20715dc3809f1c367ff9a4d3 Binary files /dev/null and celo/docs/static/images/emoji/chestnut.png differ
diff --git go-ethereum/docs/static/images/emoji/chicken.png celo/docs/static/images/emoji/chicken.png new file mode 100644 index 0000000000000000000000000000000000000000..6d25c0ef4ad549d92e1e5da3bdf14c19cde2d268 Binary files /dev/null and celo/docs/static/images/emoji/chicken.png differ
diff --git go-ethereum/docs/static/images/emoji/children_crossing.png celo/docs/static/images/emoji/children_crossing.png new file mode 100644 index 0000000000000000000000000000000000000000..b0302ae625866bf2cb5f9b9e3637e9d6b6a05cba Binary files /dev/null and celo/docs/static/images/emoji/children_crossing.png differ
diff --git go-ethereum/docs/static/images/emoji/chocolate_bar.png celo/docs/static/images/emoji/chocolate_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..c7ec19d07965810f020c25ed0ecf2160dc3c0360 Binary files /dev/null and celo/docs/static/images/emoji/chocolate_bar.png differ
diff --git go-ethereum/docs/static/images/emoji/christmas_tree.png celo/docs/static/images/emoji/christmas_tree.png new file mode 100644 index 0000000000000000000000000000000000000000..d813b9593dc12881db3146fcbace5ee62538ce82 Binary files /dev/null and celo/docs/static/images/emoji/christmas_tree.png differ
diff --git go-ethereum/docs/static/images/emoji/church.png celo/docs/static/images/emoji/church.png new file mode 100644 index 0000000000000000000000000000000000000000..4c07c6b9ea5beb5b036c498ef4516bc4de7dacbc Binary files /dev/null and celo/docs/static/images/emoji/church.png differ
diff --git go-ethereum/docs/static/images/emoji/cinema.png celo/docs/static/images/emoji/cinema.png new file mode 100644 index 0000000000000000000000000000000000000000..a990ccf99c25023f9850ca214a7ab7f081e93985 Binary files /dev/null and celo/docs/static/images/emoji/cinema.png differ
diff --git go-ethereum/docs/static/images/emoji/circus_tent.png celo/docs/static/images/emoji/circus_tent.png new file mode 100644 index 0000000000000000000000000000000000000000..4af8719aa0313843fa3393e5f224d980efa9fb99 Binary files /dev/null and celo/docs/static/images/emoji/circus_tent.png differ
diff --git go-ethereum/docs/static/images/emoji/city_sunrise.png celo/docs/static/images/emoji/city_sunrise.png new file mode 100644 index 0000000000000000000000000000000000000000..91ca2a40b69fa81434a2f48447eb193bec5a1637 Binary files /dev/null and celo/docs/static/images/emoji/city_sunrise.png differ
diff --git go-ethereum/docs/static/images/emoji/city_sunset.png celo/docs/static/images/emoji/city_sunset.png new file mode 100644 index 0000000000000000000000000000000000000000..7cb178a2cc6686f8282cf53ab972e86fb1d5440a Binary files /dev/null and celo/docs/static/images/emoji/city_sunset.png differ
diff --git go-ethereum/docs/static/images/emoji/cl.png celo/docs/static/images/emoji/cl.png new file mode 100644 index 0000000000000000000000000000000000000000..15ac67525aad1469e911760a403654cde6c5f840 Binary files /dev/null and celo/docs/static/images/emoji/cl.png differ
diff --git go-ethereum/docs/static/images/emoji/clap.png celo/docs/static/images/emoji/clap.png new file mode 100644 index 0000000000000000000000000000000000000000..d01c982a75acf2968d07cbaccd4d601e9660eadb Binary files /dev/null and celo/docs/static/images/emoji/clap.png differ
diff --git go-ethereum/docs/static/images/emoji/clapper.png celo/docs/static/images/emoji/clapper.png new file mode 100644 index 0000000000000000000000000000000000000000..4e1dc111d76018cfd2c1986f039d930bf3a7818a Binary files /dev/null and celo/docs/static/images/emoji/clapper.png differ
diff --git go-ethereum/docs/static/images/emoji/clipboard.png celo/docs/static/images/emoji/clipboard.png new file mode 100644 index 0000000000000000000000000000000000000000..e2c74e6df82322b4b0aa361c48d390a29635a234 Binary files /dev/null and celo/docs/static/images/emoji/clipboard.png differ
diff --git go-ethereum/docs/static/images/emoji/clock1.png celo/docs/static/images/emoji/clock1.png new file mode 100644 index 0000000000000000000000000000000000000000..9174d4e0bc027c423e65853819207b256b63a334 Binary files /dev/null and celo/docs/static/images/emoji/clock1.png differ
diff --git go-ethereum/docs/static/images/emoji/clock10.png celo/docs/static/images/emoji/clock10.png new file mode 100644 index 0000000000000000000000000000000000000000..39f590d69458d276f2132d95c23fd7b231636d6e Binary files /dev/null and celo/docs/static/images/emoji/clock10.png differ
diff --git go-ethereum/docs/static/images/emoji/clock1030.png celo/docs/static/images/emoji/clock1030.png new file mode 100644 index 0000000000000000000000000000000000000000..0483b30594616521c23ddd2773ea3ab8e14eb2ed Binary files /dev/null and celo/docs/static/images/emoji/clock1030.png differ
diff --git go-ethereum/docs/static/images/emoji/clock11.png celo/docs/static/images/emoji/clock11.png new file mode 100644 index 0000000000000000000000000000000000000000..ddb53fadad64f8a8b578331b843686028593fb80 Binary files /dev/null and celo/docs/static/images/emoji/clock11.png differ
diff --git go-ethereum/docs/static/images/emoji/clock1130.png celo/docs/static/images/emoji/clock1130.png new file mode 100644 index 0000000000000000000000000000000000000000..415999ec838cd611aa6d68a71f53684a3ffba11c Binary files /dev/null and celo/docs/static/images/emoji/clock1130.png differ
diff --git go-ethereum/docs/static/images/emoji/clock12.png celo/docs/static/images/emoji/clock12.png new file mode 100644 index 0000000000000000000000000000000000000000..87b132878b701dfe1bc24596fce80766f8dbda9a Binary files /dev/null and celo/docs/static/images/emoji/clock12.png differ
diff --git go-ethereum/docs/static/images/emoji/clock1230.png celo/docs/static/images/emoji/clock1230.png new file mode 100644 index 0000000000000000000000000000000000000000..a6527154d1f40f0f4dadbbf0b568e8ba3bb49cd9 Binary files /dev/null and celo/docs/static/images/emoji/clock1230.png differ
diff --git go-ethereum/docs/static/images/emoji/clock130.png celo/docs/static/images/emoji/clock130.png new file mode 100644 index 0000000000000000000000000000000000000000..90ea5b91449c2974d3664227492f7bf2cc0b73af Binary files /dev/null and celo/docs/static/images/emoji/clock130.png differ
diff --git go-ethereum/docs/static/images/emoji/clock2.png celo/docs/static/images/emoji/clock2.png new file mode 100644 index 0000000000000000000000000000000000000000..65b3b3af0e1a2833dde63772070f1f26438374c2 Binary files /dev/null and celo/docs/static/images/emoji/clock2.png differ
diff --git go-ethereum/docs/static/images/emoji/clock230.png celo/docs/static/images/emoji/clock230.png new file mode 100644 index 0000000000000000000000000000000000000000..f12c6912af7f530416329acf7e3022ff31ecf4ce Binary files /dev/null and celo/docs/static/images/emoji/clock230.png differ
diff --git go-ethereum/docs/static/images/emoji/clock3.png celo/docs/static/images/emoji/clock3.png new file mode 100644 index 0000000000000000000000000000000000000000..3e44d64e2fb5698c8d8459643d77c517d807372d Binary files /dev/null and celo/docs/static/images/emoji/clock3.png differ
diff --git go-ethereum/docs/static/images/emoji/clock330.png celo/docs/static/images/emoji/clock330.png new file mode 100644 index 0000000000000000000000000000000000000000..1dc9628ea2426bf2ac5cda1607e51ea571053ead Binary files /dev/null and celo/docs/static/images/emoji/clock330.png differ
diff --git go-ethereum/docs/static/images/emoji/clock4.png celo/docs/static/images/emoji/clock4.png new file mode 100644 index 0000000000000000000000000000000000000000..948ed1a380cc8523f6cdbd6ae16d5b58f9198c9a Binary files /dev/null and celo/docs/static/images/emoji/clock4.png differ
diff --git go-ethereum/docs/static/images/emoji/clock430.png celo/docs/static/images/emoji/clock430.png new file mode 100644 index 0000000000000000000000000000000000000000..5d6b16a2d9c62baa0b4d739e20267b340d801c2c Binary files /dev/null and celo/docs/static/images/emoji/clock430.png differ
diff --git go-ethereum/docs/static/images/emoji/clock5.png celo/docs/static/images/emoji/clock5.png new file mode 100644 index 0000000000000000000000000000000000000000..b010b4f8aaf6fd55b0cd1099812bf35897f76cae Binary files /dev/null and celo/docs/static/images/emoji/clock5.png differ
diff --git go-ethereum/docs/static/images/emoji/clock530.png celo/docs/static/images/emoji/clock530.png new file mode 100644 index 0000000000000000000000000000000000000000..e08d4ad2bac10aa88703e8c08424dbf012975f40 Binary files /dev/null and celo/docs/static/images/emoji/clock530.png differ
diff --git go-ethereum/docs/static/images/emoji/clock6.png celo/docs/static/images/emoji/clock6.png new file mode 100644 index 0000000000000000000000000000000000000000..76bf8cf1854be600717d388ce7307b3f5180ae0a Binary files /dev/null and celo/docs/static/images/emoji/clock6.png differ
diff --git go-ethereum/docs/static/images/emoji/clock630.png celo/docs/static/images/emoji/clock630.png new file mode 100644 index 0000000000000000000000000000000000000000..46f0681f1c48f269e97898bb57f1fb7e8c64cdd9 Binary files /dev/null and celo/docs/static/images/emoji/clock630.png differ
diff --git go-ethereum/docs/static/images/emoji/clock7.png celo/docs/static/images/emoji/clock7.png new file mode 100644 index 0000000000000000000000000000000000000000..d48f645d8350f54f0e83dc24184599c363df32b6 Binary files /dev/null and celo/docs/static/images/emoji/clock7.png differ
diff --git go-ethereum/docs/static/images/emoji/clock730.png celo/docs/static/images/emoji/clock730.png new file mode 100644 index 0000000000000000000000000000000000000000..f2807de2f27b1c634b90cdf0b606cb34c06cb552 Binary files /dev/null and celo/docs/static/images/emoji/clock730.png differ
diff --git go-ethereum/docs/static/images/emoji/clock8.png celo/docs/static/images/emoji/clock8.png new file mode 100644 index 0000000000000000000000000000000000000000..74c770d891c5434631f6c00f289636ad8d501fae Binary files /dev/null and celo/docs/static/images/emoji/clock8.png differ
diff --git go-ethereum/docs/static/images/emoji/clock830.png celo/docs/static/images/emoji/clock830.png new file mode 100644 index 0000000000000000000000000000000000000000..f58f3dadda0f37b796a545c342f81b32360c84ec Binary files /dev/null and celo/docs/static/images/emoji/clock830.png differ
diff --git go-ethereum/docs/static/images/emoji/clock9.png celo/docs/static/images/emoji/clock9.png new file mode 100644 index 0000000000000000000000000000000000000000..f009d14ac114dba4a03ea41dc6b0b9539a7620ac Binary files /dev/null and celo/docs/static/images/emoji/clock9.png differ
diff --git go-ethereum/docs/static/images/emoji/clock930.png celo/docs/static/images/emoji/clock930.png new file mode 100644 index 0000000000000000000000000000000000000000..fd35221428f989a438afbf433275d118ae79cc16 Binary files /dev/null and celo/docs/static/images/emoji/clock930.png differ
diff --git go-ethereum/docs/static/images/emoji/closed_book.png celo/docs/static/images/emoji/closed_book.png new file mode 100644 index 0000000000000000000000000000000000000000..484029c5ebcfa44e922172b5d908631fc469cde4 Binary files /dev/null and celo/docs/static/images/emoji/closed_book.png differ
diff --git go-ethereum/docs/static/images/emoji/closed_lock_with_key.png celo/docs/static/images/emoji/closed_lock_with_key.png new file mode 100644 index 0000000000000000000000000000000000000000..e6fdf6cb2040e18f7faf326d6211c23753485422 Binary files /dev/null and celo/docs/static/images/emoji/closed_lock_with_key.png differ
diff --git go-ethereum/docs/static/images/emoji/closed_umbrella.png celo/docs/static/images/emoji/closed_umbrella.png new file mode 100644 index 0000000000000000000000000000000000000000..0b719f086b152fd4d7f619038bf47ec404cc66ac Binary files /dev/null and celo/docs/static/images/emoji/closed_umbrella.png differ
diff --git go-ethereum/docs/static/images/emoji/cloud.png celo/docs/static/images/emoji/cloud.png new file mode 100644 index 0000000000000000000000000000000000000000..b31c08c0b883444326948789ca5bd9ddb2fcbe97 Binary files /dev/null and celo/docs/static/images/emoji/cloud.png differ
diff --git go-ethereum/docs/static/images/emoji/clubs.png celo/docs/static/images/emoji/clubs.png new file mode 100644 index 0000000000000000000000000000000000000000..bfab5365695d72bd44f75802700818e8a76b4c4a Binary files /dev/null and celo/docs/static/images/emoji/clubs.png differ
diff --git go-ethereum/docs/static/images/emoji/cn.png celo/docs/static/images/emoji/cn.png new file mode 100644 index 0000000000000000000000000000000000000000..b30dcc53df9fc194f69e1e058b079ce69e08ccdc Binary files /dev/null and celo/docs/static/images/emoji/cn.png differ
diff --git go-ethereum/docs/static/images/emoji/cocktail.png celo/docs/static/images/emoji/cocktail.png new file mode 100644 index 0000000000000000000000000000000000000000..28b45ea51455841835cae67032bfbfa687739190 Binary files /dev/null and celo/docs/static/images/emoji/cocktail.png differ
diff --git go-ethereum/docs/static/images/emoji/coffee.png celo/docs/static/images/emoji/coffee.png new file mode 100644 index 0000000000000000000000000000000000000000..57e1adcb04ac2d22d05664a54aebb718b83801a9 Binary files /dev/null and celo/docs/static/images/emoji/coffee.png differ
diff --git go-ethereum/docs/static/images/emoji/cold_sweat.png celo/docs/static/images/emoji/cold_sweat.png new file mode 100644 index 0000000000000000000000000000000000000000..b9e39bc60fb0324d4bba4551ba2be497344dc2b5 Binary files /dev/null and celo/docs/static/images/emoji/cold_sweat.png differ
diff --git go-ethereum/docs/static/images/emoji/collision.png celo/docs/static/images/emoji/collision.png new file mode 100644 index 0000000000000000000000000000000000000000..9d5bd0401401336f9e8d76cf25f68f6510500aab Binary files /dev/null and celo/docs/static/images/emoji/collision.png differ
diff --git go-ethereum/docs/static/images/emoji/computer.png celo/docs/static/images/emoji/computer.png new file mode 100644 index 0000000000000000000000000000000000000000..d4d2687627e06cfbf8aaf405ccd239d397053106 Binary files /dev/null and celo/docs/static/images/emoji/computer.png differ
diff --git go-ethereum/docs/static/images/emoji/confetti_ball.png celo/docs/static/images/emoji/confetti_ball.png new file mode 100644 index 0000000000000000000000000000000000000000..bd293e3d8746d7e7536f04c85cf4c29a93921c1c Binary files /dev/null and celo/docs/static/images/emoji/confetti_ball.png differ
diff --git go-ethereum/docs/static/images/emoji/confounded.png celo/docs/static/images/emoji/confounded.png new file mode 100644 index 0000000000000000000000000000000000000000..762c3766abc2276eed29f9a3727b5ba9813f36c4 Binary files /dev/null and celo/docs/static/images/emoji/confounded.png differ
diff --git go-ethereum/docs/static/images/emoji/confused.png celo/docs/static/images/emoji/confused.png new file mode 100644 index 0000000000000000000000000000000000000000..8dc494db0833d0a5d87c8d7a5f1f219e297ea61f Binary files /dev/null and celo/docs/static/images/emoji/confused.png differ
diff --git go-ethereum/docs/static/images/emoji/congratulations.png celo/docs/static/images/emoji/congratulations.png new file mode 100644 index 0000000000000000000000000000000000000000..85814e33c3c88ff26c18e1723b1ea9621345ddb2 Binary files /dev/null and celo/docs/static/images/emoji/congratulations.png differ
diff --git go-ethereum/docs/static/images/emoji/construction.png celo/docs/static/images/emoji/construction.png new file mode 100644 index 0000000000000000000000000000000000000000..523e9f10bf6cfdb64715cea6e6044e0affde5ed2 Binary files /dev/null and celo/docs/static/images/emoji/construction.png differ
diff --git go-ethereum/docs/static/images/emoji/construction_worker.png celo/docs/static/images/emoji/construction_worker.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6486047864e90e06df51f298ba06094ed6e0c6 Binary files /dev/null and celo/docs/static/images/emoji/construction_worker.png differ
diff --git go-ethereum/docs/static/images/emoji/convenience_store.png celo/docs/static/images/emoji/convenience_store.png new file mode 100644 index 0000000000000000000000000000000000000000..671696c2dfdbd8970c72696f49a35bbc178ee3a9 Binary files /dev/null and celo/docs/static/images/emoji/convenience_store.png differ
diff --git go-ethereum/docs/static/images/emoji/cookie.png celo/docs/static/images/emoji/cookie.png new file mode 100644 index 0000000000000000000000000000000000000000..653edb258c62caa52e7f05b199b0974099e14eec Binary files /dev/null and celo/docs/static/images/emoji/cookie.png differ
diff --git go-ethereum/docs/static/images/emoji/cool.png celo/docs/static/images/emoji/cool.png new file mode 100644 index 0000000000000000000000000000000000000000..937dcd792105b847e41b6f8a1146d73d31ea3236 Binary files /dev/null and celo/docs/static/images/emoji/cool.png differ
diff --git go-ethereum/docs/static/images/emoji/cop.png celo/docs/static/images/emoji/cop.png new file mode 100644 index 0000000000000000000000000000000000000000..43a5a84f821473f9a2b3eb5700e2a503a080d339 Binary files /dev/null and celo/docs/static/images/emoji/cop.png differ
diff --git go-ethereum/docs/static/images/emoji/copyright.png celo/docs/static/images/emoji/copyright.png new file mode 100644 index 0000000000000000000000000000000000000000..38493c33fcaf95787fffd6d6f1630c93513f5a71 Binary files /dev/null and celo/docs/static/images/emoji/copyright.png differ
diff --git go-ethereum/docs/static/images/emoji/corn.png celo/docs/static/images/emoji/corn.png new file mode 100644 index 0000000000000000000000000000000000000000..fe5d8b1287e27fc852b48262327547aa0a951d74 Binary files /dev/null and celo/docs/static/images/emoji/corn.png differ
diff --git go-ethereum/docs/static/images/emoji/couple.png celo/docs/static/images/emoji/couple.png new file mode 100644 index 0000000000000000000000000000000000000000..9e51f40e16ecfaa96b25c2fa4854d6e9935fb1b8 Binary files /dev/null and celo/docs/static/images/emoji/couple.png differ
diff --git go-ethereum/docs/static/images/emoji/couple_with_heart.png celo/docs/static/images/emoji/couple_with_heart.png new file mode 100644 index 0000000000000000000000000000000000000000..c503f40a931aa79cad2f9f5f72d768fe2ed53561 Binary files /dev/null and celo/docs/static/images/emoji/couple_with_heart.png differ
diff --git go-ethereum/docs/static/images/emoji/couplekiss.png celo/docs/static/images/emoji/couplekiss.png new file mode 100644 index 0000000000000000000000000000000000000000..d02790822ea0c8ac8903eb525364778e95debbb4 Binary files /dev/null and celo/docs/static/images/emoji/couplekiss.png differ
diff --git go-ethereum/docs/static/images/emoji/cow.png celo/docs/static/images/emoji/cow.png new file mode 100644 index 0000000000000000000000000000000000000000..12e1ab6c0bd8d6923b028e2a5336ac388b977d60 Binary files /dev/null and celo/docs/static/images/emoji/cow.png differ
diff --git go-ethereum/docs/static/images/emoji/cow2.png celo/docs/static/images/emoji/cow2.png new file mode 100644 index 0000000000000000000000000000000000000000..594c92155bc52116674fb8dbd94738fd2e0a32b1 Binary files /dev/null and celo/docs/static/images/emoji/cow2.png differ
diff --git go-ethereum/docs/static/images/emoji/credit_card.png celo/docs/static/images/emoji/credit_card.png new file mode 100644 index 0000000000000000000000000000000000000000..be1c1dd3063120ab9b0844346731b7e2cdf88671 Binary files /dev/null and celo/docs/static/images/emoji/credit_card.png differ
diff --git go-ethereum/docs/static/images/emoji/crescent_moon.png celo/docs/static/images/emoji/crescent_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..afdb450d1dff9e742955efbb03bef606e1aaba16 Binary files /dev/null and celo/docs/static/images/emoji/crescent_moon.png differ
diff --git go-ethereum/docs/static/images/emoji/crocodile.png celo/docs/static/images/emoji/crocodile.png new file mode 100644 index 0000000000000000000000000000000000000000..7435d5ab3c485879624a025d8b46185e5b28dbb3 Binary files /dev/null and celo/docs/static/images/emoji/crocodile.png differ
diff --git go-ethereum/docs/static/images/emoji/crossed_flags.png celo/docs/static/images/emoji/crossed_flags.png new file mode 100644 index 0000000000000000000000000000000000000000..2397bcd0fc9892531b3c714cd34d65a58dfa3d89 Binary files /dev/null and celo/docs/static/images/emoji/crossed_flags.png differ
diff --git go-ethereum/docs/static/images/emoji/crown.png celo/docs/static/images/emoji/crown.png new file mode 100644 index 0000000000000000000000000000000000000000..39da1d52873ed8694b5285f2baf5b413bdc067c5 Binary files /dev/null and celo/docs/static/images/emoji/crown.png differ
diff --git go-ethereum/docs/static/images/emoji/cry.png celo/docs/static/images/emoji/cry.png new file mode 100644 index 0000000000000000000000000000000000000000..6d0d9afd284b9a22107defc3979c9384c192af74 Binary files /dev/null and celo/docs/static/images/emoji/cry.png differ
diff --git go-ethereum/docs/static/images/emoji/crying_cat_face.png celo/docs/static/images/emoji/crying_cat_face.png new file mode 100644 index 0000000000000000000000000000000000000000..42d4c27cabfaafc0c17b92483050aeaeeecbed25 Binary files /dev/null and celo/docs/static/images/emoji/crying_cat_face.png differ
diff --git go-ethereum/docs/static/images/emoji/crystal_ball.png celo/docs/static/images/emoji/crystal_ball.png new file mode 100644 index 0000000000000000000000000000000000000000..6d2c6c42d448ff1e77ef23906f4048649ccbbf1d Binary files /dev/null and celo/docs/static/images/emoji/crystal_ball.png differ
diff --git go-ethereum/docs/static/images/emoji/cupid.png celo/docs/static/images/emoji/cupid.png new file mode 100644 index 0000000000000000000000000000000000000000..4987284767c6d850e249f1af95d80a11b73e736b Binary files /dev/null and celo/docs/static/images/emoji/cupid.png differ
diff --git go-ethereum/docs/static/images/emoji/curly_loop.png celo/docs/static/images/emoji/curly_loop.png new file mode 100644 index 0000000000000000000000000000000000000000..7dd841d006af803488abd06f754f6b05ba219b8a Binary files /dev/null and celo/docs/static/images/emoji/curly_loop.png differ
diff --git go-ethereum/docs/static/images/emoji/currency_exchange.png celo/docs/static/images/emoji/currency_exchange.png new file mode 100644 index 0000000000000000000000000000000000000000..6ebebe70afb01c3f179258d04eb9a8a22ff5b543 Binary files /dev/null and celo/docs/static/images/emoji/currency_exchange.png differ
diff --git go-ethereum/docs/static/images/emoji/curry.png celo/docs/static/images/emoji/curry.png new file mode 100644 index 0000000000000000000000000000000000000000..7983c706a40ebd81c1d6431c52dc7fdbd75d6ba1 Binary files /dev/null and celo/docs/static/images/emoji/curry.png differ
diff --git go-ethereum/docs/static/images/emoji/custard.png celo/docs/static/images/emoji/custard.png new file mode 100644 index 0000000000000000000000000000000000000000..9f843b4c13016805cb3f8a64cc02b83706528dec Binary files /dev/null and celo/docs/static/images/emoji/custard.png differ
diff --git go-ethereum/docs/static/images/emoji/customs.png celo/docs/static/images/emoji/customs.png new file mode 100644 index 0000000000000000000000000000000000000000..92691e3117c69c9a4acf0ac264770f4b9528d46e Binary files /dev/null and celo/docs/static/images/emoji/customs.png differ
diff --git go-ethereum/docs/static/images/emoji/cyclone.png celo/docs/static/images/emoji/cyclone.png new file mode 100644 index 0000000000000000000000000000000000000000..5fd2e4512f9165e5a9f5fcc5e84e057560fe91b7 Binary files /dev/null and celo/docs/static/images/emoji/cyclone.png differ
diff --git go-ethereum/docs/static/images/emoji/dancer.png celo/docs/static/images/emoji/dancer.png new file mode 100644 index 0000000000000000000000000000000000000000..7a7bf59f1799da978c740bc7f000a3e1a6366e6c Binary files /dev/null and celo/docs/static/images/emoji/dancer.png differ
diff --git go-ethereum/docs/static/images/emoji/dancers.png celo/docs/static/images/emoji/dancers.png new file mode 100644 index 0000000000000000000000000000000000000000..2dfb451a73a1566fb1d916861002b3c6dfe4572a Binary files /dev/null and celo/docs/static/images/emoji/dancers.png differ
diff --git go-ethereum/docs/static/images/emoji/dango.png celo/docs/static/images/emoji/dango.png new file mode 100644 index 0000000000000000000000000000000000000000..2d042aebeb589162683b4eb8976acc2e4a3c21e7 Binary files /dev/null and celo/docs/static/images/emoji/dango.png differ
diff --git go-ethereum/docs/static/images/emoji/dart.png celo/docs/static/images/emoji/dart.png new file mode 100644 index 0000000000000000000000000000000000000000..5f16864cbedf2f081a950771a5c171e4fb8aea99 Binary files /dev/null and celo/docs/static/images/emoji/dart.png differ
diff --git go-ethereum/docs/static/images/emoji/dash.png celo/docs/static/images/emoji/dash.png new file mode 100644 index 0000000000000000000000000000000000000000..dc2c0a8f4684186167596b0cbd7ca1fda70744a3 Binary files /dev/null and celo/docs/static/images/emoji/dash.png differ
diff --git go-ethereum/docs/static/images/emoji/date.png celo/docs/static/images/emoji/date.png new file mode 100644 index 0000000000000000000000000000000000000000..6ad2efa5fdcc5fb377fef6d565a0e72cef0990ca Binary files /dev/null and celo/docs/static/images/emoji/date.png differ
diff --git go-ethereum/docs/static/images/emoji/de.png celo/docs/static/images/emoji/de.png new file mode 100644 index 0000000000000000000000000000000000000000..16a28548c9e5b2007a7d5443b58696c9bdf93509 Binary files /dev/null and celo/docs/static/images/emoji/de.png differ
diff --git go-ethereum/docs/static/images/emoji/deciduous_tree.png celo/docs/static/images/emoji/deciduous_tree.png new file mode 100644 index 0000000000000000000000000000000000000000..3fdf8c00707e975940f9d6dd50930149bd29a63a Binary files /dev/null and celo/docs/static/images/emoji/deciduous_tree.png differ
diff --git go-ethereum/docs/static/images/emoji/department_store.png celo/docs/static/images/emoji/department_store.png new file mode 100644 index 0000000000000000000000000000000000000000..68d959c507de127674ab7f3176ce0adc2389306a Binary files /dev/null and celo/docs/static/images/emoji/department_store.png differ
diff --git go-ethereum/docs/static/images/emoji/diamond_shape_with_a_dot_inside.png celo/docs/static/images/emoji/diamond_shape_with_a_dot_inside.png new file mode 100644 index 0000000000000000000000000000000000000000..dfd1098b3940104aaae590dfeb533fb22b230c9c Binary files /dev/null and celo/docs/static/images/emoji/diamond_shape_with_a_dot_inside.png differ
diff --git go-ethereum/docs/static/images/emoji/diamonds.png celo/docs/static/images/emoji/diamonds.png new file mode 100644 index 0000000000000000000000000000000000000000..fe0827758b1b72c6668f208721a4e6c183305967 Binary files /dev/null and celo/docs/static/images/emoji/diamonds.png differ
diff --git go-ethereum/docs/static/images/emoji/disappointed.png celo/docs/static/images/emoji/disappointed.png new file mode 100644 index 0000000000000000000000000000000000000000..82552008719d2841dbab81f34008db4ef830e6a1 Binary files /dev/null and celo/docs/static/images/emoji/disappointed.png differ
diff --git go-ethereum/docs/static/images/emoji/disappointed_relieved.png celo/docs/static/images/emoji/disappointed_relieved.png new file mode 100644 index 0000000000000000000000000000000000000000..fa5f9e7f9f94a3ba9bdf23f3932cdd7c8a0a3fbc Binary files /dev/null and celo/docs/static/images/emoji/disappointed_relieved.png differ
diff --git go-ethereum/docs/static/images/emoji/dizzy.png celo/docs/static/images/emoji/dizzy.png new file mode 100644 index 0000000000000000000000000000000000000000..3702b6131c0feb028174aa0578b4c055d9632946 Binary files /dev/null and celo/docs/static/images/emoji/dizzy.png differ
diff --git go-ethereum/docs/static/images/emoji/dizzy_face.png celo/docs/static/images/emoji/dizzy_face.png new file mode 100644 index 0000000000000000000000000000000000000000..8001d6ff8f0b8380298d77a6238188f516e5a188 Binary files /dev/null and celo/docs/static/images/emoji/dizzy_face.png differ
diff --git go-ethereum/docs/static/images/emoji/do_not_litter.png celo/docs/static/images/emoji/do_not_litter.png new file mode 100644 index 0000000000000000000000000000000000000000..38c7ae7af239c5ba902718a37c1d9afeb2c0169c Binary files /dev/null and celo/docs/static/images/emoji/do_not_litter.png differ
diff --git go-ethereum/docs/static/images/emoji/dog.png celo/docs/static/images/emoji/dog.png new file mode 100644 index 0000000000000000000000000000000000000000..389a02bf282628757e93ae97fe811379f0be592e Binary files /dev/null and celo/docs/static/images/emoji/dog.png differ
diff --git go-ethereum/docs/static/images/emoji/dog2.png celo/docs/static/images/emoji/dog2.png new file mode 100644 index 0000000000000000000000000000000000000000..c7f6a24ac80bce9a79f66f4baf3c9de0a3a496aa Binary files /dev/null and celo/docs/static/images/emoji/dog2.png differ
diff --git go-ethereum/docs/static/images/emoji/dollar.png celo/docs/static/images/emoji/dollar.png new file mode 100644 index 0000000000000000000000000000000000000000..63de884951966d1e0b09c3ce71542d1d59cf43d7 Binary files /dev/null and celo/docs/static/images/emoji/dollar.png differ
diff --git go-ethereum/docs/static/images/emoji/dolls.png celo/docs/static/images/emoji/dolls.png new file mode 100644 index 0000000000000000000000000000000000000000..47ce33900ca53b7372c0b6028aed797f8fa8f0b7 Binary files /dev/null and celo/docs/static/images/emoji/dolls.png differ
diff --git go-ethereum/docs/static/images/emoji/dolphin.png celo/docs/static/images/emoji/dolphin.png new file mode 100644 index 0000000000000000000000000000000000000000..9326077a927c4901c4c2f6f058f34d6366ce9627 Binary files /dev/null and celo/docs/static/images/emoji/dolphin.png differ
diff --git go-ethereum/docs/static/images/emoji/donut.png celo/docs/static/images/emoji/donut.png new file mode 100644 index 0000000000000000000000000000000000000000..ccf869129602e60a8b20e5d90d0258b8fda14591 Binary files /dev/null and celo/docs/static/images/emoji/donut.png differ
diff --git go-ethereum/docs/static/images/emoji/door.png celo/docs/static/images/emoji/door.png new file mode 100644 index 0000000000000000000000000000000000000000..83c819ae4669c093912601820f7f147bd37fc7e7 Binary files /dev/null and celo/docs/static/images/emoji/door.png differ
diff --git go-ethereum/docs/static/images/emoji/doughnut.png celo/docs/static/images/emoji/doughnut.png new file mode 100644 index 0000000000000000000000000000000000000000..ccf869129602e60a8b20e5d90d0258b8fda14591 Binary files /dev/null and celo/docs/static/images/emoji/doughnut.png differ
diff --git go-ethereum/docs/static/images/emoji/dragon.png celo/docs/static/images/emoji/dragon.png new file mode 100644 index 0000000000000000000000000000000000000000..88d4784b8bb9e8e4d2d17dce25cf3ba122209aaf Binary files /dev/null and celo/docs/static/images/emoji/dragon.png differ
diff --git go-ethereum/docs/static/images/emoji/dragon_face.png celo/docs/static/images/emoji/dragon_face.png new file mode 100644 index 0000000000000000000000000000000000000000..e5e556bd105c9a40a33b1b92fb226e68420a3d76 Binary files /dev/null and celo/docs/static/images/emoji/dragon_face.png differ
diff --git go-ethereum/docs/static/images/emoji/dress.png celo/docs/static/images/emoji/dress.png new file mode 100644 index 0000000000000000000000000000000000000000..6434e2e2f3982266931f943f41e2640eb0ca7c91 Binary files /dev/null and celo/docs/static/images/emoji/dress.png differ
diff --git go-ethereum/docs/static/images/emoji/dromedary_camel.png celo/docs/static/images/emoji/dromedary_camel.png new file mode 100644 index 0000000000000000000000000000000000000000..c8c7b9ffa0fa6d78ce7f1e53ff9a6a20cd0bd217 Binary files /dev/null and celo/docs/static/images/emoji/dromedary_camel.png differ
diff --git go-ethereum/docs/static/images/emoji/droplet.png celo/docs/static/images/emoji/droplet.png new file mode 100644 index 0000000000000000000000000000000000000000..cae7f4951ad10fd85875960c5818aaa27e45c5d6 Binary files /dev/null and celo/docs/static/images/emoji/droplet.png differ
diff --git go-ethereum/docs/static/images/emoji/dvd.png celo/docs/static/images/emoji/dvd.png new file mode 100644 index 0000000000000000000000000000000000000000..363c83d01c527179f47c224de01b2d29f750290a Binary files /dev/null and celo/docs/static/images/emoji/dvd.png differ
diff --git go-ethereum/docs/static/images/emoji/e-mail.png celo/docs/static/images/emoji/e-mail.png new file mode 100644 index 0000000000000000000000000000000000000000..176a8e1e825a05c3d0b116ca4b29ece5f3250901 Binary files /dev/null and celo/docs/static/images/emoji/e-mail.png differ
diff --git go-ethereum/docs/static/images/emoji/ear.png celo/docs/static/images/emoji/ear.png new file mode 100644 index 0000000000000000000000000000000000000000..2bbbf10c9ef475424d699cbfbd03a33b33b24570 Binary files /dev/null and celo/docs/static/images/emoji/ear.png differ
diff --git go-ethereum/docs/static/images/emoji/ear_of_rice.png celo/docs/static/images/emoji/ear_of_rice.png new file mode 100644 index 0000000000000000000000000000000000000000..a9bba5c2c14682fef7ce33e17a968e2dd4a4ea18 Binary files /dev/null and celo/docs/static/images/emoji/ear_of_rice.png differ
diff --git go-ethereum/docs/static/images/emoji/earth_africa.png celo/docs/static/images/emoji/earth_africa.png new file mode 100644 index 0000000000000000000000000000000000000000..44ce5ecb621c885ae92e8567b1e819b16c4bca1d Binary files /dev/null and celo/docs/static/images/emoji/earth_africa.png differ
diff --git go-ethereum/docs/static/images/emoji/earth_americas.png celo/docs/static/images/emoji/earth_americas.png new file mode 100644 index 0000000000000000000000000000000000000000..97d717671369fe6519a315de350b53a1445fa4ba Binary files /dev/null and celo/docs/static/images/emoji/earth_americas.png differ
diff --git go-ethereum/docs/static/images/emoji/earth_asia.png celo/docs/static/images/emoji/earth_asia.png new file mode 100644 index 0000000000000000000000000000000000000000..95ec357ca87f342ca738361cd71be741cc0e350c Binary files /dev/null and celo/docs/static/images/emoji/earth_asia.png differ
diff --git go-ethereum/docs/static/images/emoji/egg.png celo/docs/static/images/emoji/egg.png new file mode 100644 index 0000000000000000000000000000000000000000..c3de6ae4ea0d11983133e477346e6b93e4eaec00 Binary files /dev/null and celo/docs/static/images/emoji/egg.png differ
diff --git go-ethereum/docs/static/images/emoji/eggplant.png celo/docs/static/images/emoji/eggplant.png new file mode 100644 index 0000000000000000000000000000000000000000..66f25fce447f20aaa4770ecdf722ac416a0c6ca6 Binary files /dev/null and celo/docs/static/images/emoji/eggplant.png differ
diff --git go-ethereum/docs/static/images/emoji/eight.png celo/docs/static/images/emoji/eight.png new file mode 100644 index 0000000000000000000000000000000000000000..7bdb422327c4b93b3de666b37e3f487eddac4930 Binary files /dev/null and celo/docs/static/images/emoji/eight.png differ
diff --git go-ethereum/docs/static/images/emoji/eight_pointed_black_star.png celo/docs/static/images/emoji/eight_pointed_black_star.png new file mode 100644 index 0000000000000000000000000000000000000000..2420a7768e33d958bdf7207f6434257501d3d37a Binary files /dev/null and celo/docs/static/images/emoji/eight_pointed_black_star.png differ
diff --git go-ethereum/docs/static/images/emoji/eight_spoked_asterisk.png celo/docs/static/images/emoji/eight_spoked_asterisk.png new file mode 100644 index 0000000000000000000000000000000000000000..946a20333a2aa283469ed8937f1d2916e4e1a365 Binary files /dev/null and celo/docs/static/images/emoji/eight_spoked_asterisk.png differ
diff --git go-ethereum/docs/static/images/emoji/electric_plug.png celo/docs/static/images/emoji/electric_plug.png new file mode 100644 index 0000000000000000000000000000000000000000..2837bab4f00dbc48113f294c0d6cc06d0d6c717b Binary files /dev/null and celo/docs/static/images/emoji/electric_plug.png differ
diff --git go-ethereum/docs/static/images/emoji/elephant.png celo/docs/static/images/emoji/elephant.png new file mode 100644 index 0000000000000000000000000000000000000000..5ca04570e2425a77b566a17767864394f7657e93 Binary files /dev/null and celo/docs/static/images/emoji/elephant.png differ
diff --git go-ethereum/docs/static/images/emoji/email.png celo/docs/static/images/emoji/email.png new file mode 100644 index 0000000000000000000000000000000000000000..0e01fd5f0522bc64bd55fead245085e1c35dcf71 Binary files /dev/null and celo/docs/static/images/emoji/email.png differ
diff --git go-ethereum/docs/static/images/emoji/end.png celo/docs/static/images/emoji/end.png new file mode 100644 index 0000000000000000000000000000000000000000..61a4399ad83c2712569b93ce6a0fa33126151982 Binary files /dev/null and celo/docs/static/images/emoji/end.png differ
diff --git go-ethereum/docs/static/images/emoji/envelope.png celo/docs/static/images/emoji/envelope.png new file mode 100644 index 0000000000000000000000000000000000000000..3631861bbfdcd90101dd9a2b3a2299c63e983af9 Binary files /dev/null and celo/docs/static/images/emoji/envelope.png differ
diff --git go-ethereum/docs/static/images/emoji/es.png celo/docs/static/images/emoji/es.png new file mode 100644 index 0000000000000000000000000000000000000000..71b30bff352e05df99c09a3cfb88fd0b9cb072ad Binary files /dev/null and celo/docs/static/images/emoji/es.png differ
diff --git go-ethereum/docs/static/images/emoji/euro.png celo/docs/static/images/emoji/euro.png new file mode 100644 index 0000000000000000000000000000000000000000..1c5904b7144328c158169abbd1306a827334e4c5 Binary files /dev/null and celo/docs/static/images/emoji/euro.png differ
diff --git go-ethereum/docs/static/images/emoji/european_castle.png celo/docs/static/images/emoji/european_castle.png new file mode 100644 index 0000000000000000000000000000000000000000..8229b8a8a942abe7aa2474f3477c6f0d235dac31 Binary files /dev/null and celo/docs/static/images/emoji/european_castle.png differ
diff --git go-ethereum/docs/static/images/emoji/european_post_office.png celo/docs/static/images/emoji/european_post_office.png new file mode 100644 index 0000000000000000000000000000000000000000..0f65b1453056ac5c346693892dae0766c8617373 Binary files /dev/null and celo/docs/static/images/emoji/european_post_office.png differ
diff --git go-ethereum/docs/static/images/emoji/evergreen_tree.png celo/docs/static/images/emoji/evergreen_tree.png new file mode 100644 index 0000000000000000000000000000000000000000..ae8ad103763821e235450d70b2dacc46377341fb Binary files /dev/null and celo/docs/static/images/emoji/evergreen_tree.png differ
diff --git go-ethereum/docs/static/images/emoji/exclamation.png celo/docs/static/images/emoji/exclamation.png new file mode 100644 index 0000000000000000000000000000000000000000..77bbdeabcf49dbc4f8b5278bd01f7e14292724b1 Binary files /dev/null and celo/docs/static/images/emoji/exclamation.png differ
diff --git go-ethereum/docs/static/images/emoji/expressionless.png celo/docs/static/images/emoji/expressionless.png new file mode 100644 index 0000000000000000000000000000000000000000..913ff4e2fa7008b636c8baf39fcf1ca152bd20c0 Binary files /dev/null and celo/docs/static/images/emoji/expressionless.png differ
diff --git go-ethereum/docs/static/images/emoji/eyeglasses.png celo/docs/static/images/emoji/eyeglasses.png new file mode 100644 index 0000000000000000000000000000000000000000..a3cf75a27a1c67f17e16d98b07a8dc5bfeab89f0 Binary files /dev/null and celo/docs/static/images/emoji/eyeglasses.png differ
diff --git go-ethereum/docs/static/images/emoji/eyes.png celo/docs/static/images/emoji/eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac24a615b2872cd560e3dc113071b20238a8eee Binary files /dev/null and celo/docs/static/images/emoji/eyes.png differ
diff --git go-ethereum/docs/static/images/emoji/facepunch.png celo/docs/static/images/emoji/facepunch.png new file mode 100644 index 0000000000000000000000000000000000000000..277047b7c460cf2532412c6fbe66e172fb13e13b Binary files /dev/null and celo/docs/static/images/emoji/facepunch.png differ
diff --git go-ethereum/docs/static/images/emoji/factory.png celo/docs/static/images/emoji/factory.png new file mode 100644 index 0000000000000000000000000000000000000000..6404634793e288a6c77d19918f71bf3b3b7e3b22 Binary files /dev/null and celo/docs/static/images/emoji/factory.png differ
diff --git go-ethereum/docs/static/images/emoji/fallen_leaf.png celo/docs/static/images/emoji/fallen_leaf.png new file mode 100644 index 0000000000000000000000000000000000000000..d49f9c1757d3ae2325089fc6e60fbcece999d181 Binary files /dev/null and celo/docs/static/images/emoji/fallen_leaf.png differ
diff --git go-ethereum/docs/static/images/emoji/family.png celo/docs/static/images/emoji/family.png new file mode 100644 index 0000000000000000000000000000000000000000..b4b365f3a5c033af7e6cad6c67b5c5f5135a7340 Binary files /dev/null and celo/docs/static/images/emoji/family.png differ
diff --git go-ethereum/docs/static/images/emoji/fast_forward.png celo/docs/static/images/emoji/fast_forward.png new file mode 100644 index 0000000000000000000000000000000000000000..8830e146e598f29e6b05e00cb08301dccd2ed348 Binary files /dev/null and celo/docs/static/images/emoji/fast_forward.png differ
diff --git go-ethereum/docs/static/images/emoji/fax.png celo/docs/static/images/emoji/fax.png new file mode 100644 index 0000000000000000000000000000000000000000..62be2c958f416e04a4533187fc929a123595a10d Binary files /dev/null and celo/docs/static/images/emoji/fax.png differ
diff --git go-ethereum/docs/static/images/emoji/fearful.png celo/docs/static/images/emoji/fearful.png new file mode 100644 index 0000000000000000000000000000000000000000..513fce47b685b10757d425ec63bfd8b69258ca08 Binary files /dev/null and celo/docs/static/images/emoji/fearful.png differ
diff --git go-ethereum/docs/static/images/emoji/feelsgood.png celo/docs/static/images/emoji/feelsgood.png new file mode 100644 index 0000000000000000000000000000000000000000..361f969bc1b55695a8a68bca66abe045fd3af8a6 Binary files /dev/null and celo/docs/static/images/emoji/feelsgood.png differ
diff --git go-ethereum/docs/static/images/emoji/feet.png celo/docs/static/images/emoji/feet.png new file mode 100644 index 0000000000000000000000000000000000000000..1b0147b1d234c4043f8a07294c64888461324883 Binary files /dev/null and celo/docs/static/images/emoji/feet.png differ
diff --git go-ethereum/docs/static/images/emoji/ferris_wheel.png celo/docs/static/images/emoji/ferris_wheel.png new file mode 100644 index 0000000000000000000000000000000000000000..54a1dcfa1ef4f97ff8987581c7b0af4d1187c59b Binary files /dev/null and celo/docs/static/images/emoji/ferris_wheel.png differ
diff --git go-ethereum/docs/static/images/emoji/file_folder.png celo/docs/static/images/emoji/file_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..4d8bebf8a90354c38612398a95b9c23a6109af5e Binary files /dev/null and celo/docs/static/images/emoji/file_folder.png differ
diff --git go-ethereum/docs/static/images/emoji/finnadie.png celo/docs/static/images/emoji/finnadie.png new file mode 100644 index 0000000000000000000000000000000000000000..bfc5a0d93de6a433bb15d1b994ade85cefe1ce67 Binary files /dev/null and celo/docs/static/images/emoji/finnadie.png differ
diff --git go-ethereum/docs/static/images/emoji/fire.png celo/docs/static/images/emoji/fire.png new file mode 100644 index 0000000000000000000000000000000000000000..f2a3149bbfdaccae39985a6e0f37b1630a49a351 Binary files /dev/null and celo/docs/static/images/emoji/fire.png differ
diff --git go-ethereum/docs/static/images/emoji/fire_engine.png celo/docs/static/images/emoji/fire_engine.png new file mode 100644 index 0000000000000000000000000000000000000000..9e6c59c99763489eb36b9641f515fbed096244b2 Binary files /dev/null and celo/docs/static/images/emoji/fire_engine.png differ
diff --git go-ethereum/docs/static/images/emoji/fireworks.png celo/docs/static/images/emoji/fireworks.png new file mode 100644 index 0000000000000000000000000000000000000000..b4eccd5775b319e38e4a9fb94661aa51cffbcb2c Binary files /dev/null and celo/docs/static/images/emoji/fireworks.png differ
diff --git go-ethereum/docs/static/images/emoji/first_quarter_moon.png celo/docs/static/images/emoji/first_quarter_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..f38c236937fa9e9dd0346392a72dd01d937a3cbe Binary files /dev/null and celo/docs/static/images/emoji/first_quarter_moon.png differ
diff --git go-ethereum/docs/static/images/emoji/first_quarter_moon_with_face.png celo/docs/static/images/emoji/first_quarter_moon_with_face.png new file mode 100644 index 0000000000000000000000000000000000000000..85ae2ce72dc273d392eb8b2270c1c5f81b93e7ab Binary files /dev/null and celo/docs/static/images/emoji/first_quarter_moon_with_face.png differ
diff --git go-ethereum/docs/static/images/emoji/fish.png celo/docs/static/images/emoji/fish.png new file mode 100644 index 0000000000000000000000000000000000000000..90bdda2c9203d6af0c0796434cd5138034a5305b Binary files /dev/null and celo/docs/static/images/emoji/fish.png differ
diff --git go-ethereum/docs/static/images/emoji/fish_cake.png celo/docs/static/images/emoji/fish_cake.png new file mode 100644 index 0000000000000000000000000000000000000000..a8f22614d62a0398d77e51ae64f1e8e0031f32b8 Binary files /dev/null and celo/docs/static/images/emoji/fish_cake.png differ
diff --git go-ethereum/docs/static/images/emoji/fishing_pole_and_fish.png celo/docs/static/images/emoji/fishing_pole_and_fish.png new file mode 100644 index 0000000000000000000000000000000000000000..d84609c3b7bced2ed86fbb9e88ee08a017c04316 Binary files /dev/null and celo/docs/static/images/emoji/fishing_pole_and_fish.png differ
diff --git go-ethereum/docs/static/images/emoji/fist.png celo/docs/static/images/emoji/fist.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc8874c2fdb9dac387bd6765ac8081034a75c85 Binary files /dev/null and celo/docs/static/images/emoji/fist.png differ
diff --git go-ethereum/docs/static/images/emoji/five.png celo/docs/static/images/emoji/five.png new file mode 100644 index 0000000000000000000000000000000000000000..794321aa22a38dfab90990ee34c608cbd50bb460 Binary files /dev/null and celo/docs/static/images/emoji/five.png differ
diff --git go-ethereum/docs/static/images/emoji/flags.png celo/docs/static/images/emoji/flags.png new file mode 100644 index 0000000000000000000000000000000000000000..540164e84e438ae269178f83a7d2a0cfb080a5c5 Binary files /dev/null and celo/docs/static/images/emoji/flags.png differ
diff --git go-ethereum/docs/static/images/emoji/flashlight.png celo/docs/static/images/emoji/flashlight.png new file mode 100644 index 0000000000000000000000000000000000000000..215940aa8f1bf0b8a0b077d6e93f98c8f60f1ab2 Binary files /dev/null and celo/docs/static/images/emoji/flashlight.png differ
diff --git go-ethereum/docs/static/images/emoji/floppy_disk.png celo/docs/static/images/emoji/floppy_disk.png new file mode 100644 index 0000000000000000000000000000000000000000..4ad56315ae65f7c669bfb8350514d22f58e8f8c0 Binary files /dev/null and celo/docs/static/images/emoji/floppy_disk.png differ
diff --git go-ethereum/docs/static/images/emoji/flower_playing_cards.png celo/docs/static/images/emoji/flower_playing_cards.png new file mode 100644 index 0000000000000000000000000000000000000000..cc46a6a1fa2e54b59f48a5896c1c6eec18ceff10 Binary files /dev/null and celo/docs/static/images/emoji/flower_playing_cards.png differ
diff --git go-ethereum/docs/static/images/emoji/flushed.png celo/docs/static/images/emoji/flushed.png new file mode 100644 index 0000000000000000000000000000000000000000..74b78c9c07a5643bdfaf33b89677e00fefb11b92 Binary files /dev/null and celo/docs/static/images/emoji/flushed.png differ
diff --git go-ethereum/docs/static/images/emoji/foggy.png celo/docs/static/images/emoji/foggy.png new file mode 100644 index 0000000000000000000000000000000000000000..3c7b8b04b9575a750a51567b444fcd09354c240c Binary files /dev/null and celo/docs/static/images/emoji/foggy.png differ
diff --git go-ethereum/docs/static/images/emoji/football.png celo/docs/static/images/emoji/football.png new file mode 100644 index 0000000000000000000000000000000000000000..0e4e168fa8f93b343b0cc90ec5f5a3b1cec52a49 Binary files /dev/null and celo/docs/static/images/emoji/football.png differ
diff --git go-ethereum/docs/static/images/emoji/fork_and_knife.png celo/docs/static/images/emoji/fork_and_knife.png new file mode 100644 index 0000000000000000000000000000000000000000..8ba4bc6535efd138b2a43e0d6314851c54bca378 Binary files /dev/null and celo/docs/static/images/emoji/fork_and_knife.png differ
diff --git go-ethereum/docs/static/images/emoji/fountain.png celo/docs/static/images/emoji/fountain.png new file mode 100644 index 0000000000000000000000000000000000000000..da126e6486e1f857f43f13c532dd18cd15afeff2 Binary files /dev/null and celo/docs/static/images/emoji/fountain.png differ
diff --git go-ethereum/docs/static/images/emoji/four.png celo/docs/static/images/emoji/four.png new file mode 100644 index 0000000000000000000000000000000000000000..14782ba23b94d4f26e7ea5803d0474a184ba651e Binary files /dev/null and celo/docs/static/images/emoji/four.png differ
diff --git go-ethereum/docs/static/images/emoji/four_leaf_clover.png celo/docs/static/images/emoji/four_leaf_clover.png new file mode 100644 index 0000000000000000000000000000000000000000..f2014bea44f84460551b0393a7de85cd1fb84a07 Binary files /dev/null and celo/docs/static/images/emoji/four_leaf_clover.png differ
diff --git go-ethereum/docs/static/images/emoji/fr.png celo/docs/static/images/emoji/fr.png new file mode 100644 index 0000000000000000000000000000000000000000..6311c91159e91db4beb5846355e0cb1ff33f4a44 Binary files /dev/null and celo/docs/static/images/emoji/fr.png differ
diff --git go-ethereum/docs/static/images/emoji/free.png celo/docs/static/images/emoji/free.png new file mode 100644 index 0000000000000000000000000000000000000000..c886cf2494c1367bec7134e9179519a52cc4bb1b Binary files /dev/null and celo/docs/static/images/emoji/free.png differ
diff --git go-ethereum/docs/static/images/emoji/fried_shrimp.png celo/docs/static/images/emoji/fried_shrimp.png new file mode 100644 index 0000000000000000000000000000000000000000..c8c284bf14af14f06f4dbd45e61e035e3b8db52e Binary files /dev/null and celo/docs/static/images/emoji/fried_shrimp.png differ
diff --git go-ethereum/docs/static/images/emoji/fries.png celo/docs/static/images/emoji/fries.png new file mode 100644 index 0000000000000000000000000000000000000000..cfef66966a729c48ccf081661403d43d802c6105 Binary files /dev/null and celo/docs/static/images/emoji/fries.png differ
diff --git go-ethereum/docs/static/images/emoji/frog.png celo/docs/static/images/emoji/frog.png new file mode 100644 index 0000000000000000000000000000000000000000..cfe11b18ff0e6865a22e204eaf7d435c23f79f32 Binary files /dev/null and celo/docs/static/images/emoji/frog.png differ
diff --git go-ethereum/docs/static/images/emoji/frowning.png celo/docs/static/images/emoji/frowning.png new file mode 100644 index 0000000000000000000000000000000000000000..487b77016035d92a6484dcc9d6918c7339d4d60a Binary files /dev/null and celo/docs/static/images/emoji/frowning.png differ
diff --git go-ethereum/docs/static/images/emoji/fu.png celo/docs/static/images/emoji/fu.png new file mode 100644 index 0000000000000000000000000000000000000000..61a3fee8d764677e5e62fc6a0a6cdead0a5d7863 Binary files /dev/null and celo/docs/static/images/emoji/fu.png differ
diff --git go-ethereum/docs/static/images/emoji/fuelpump.png celo/docs/static/images/emoji/fuelpump.png new file mode 100644 index 0000000000000000000000000000000000000000..54c29aeb1db3ba1c39626e80e7929d07f7e874c6 Binary files /dev/null and celo/docs/static/images/emoji/fuelpump.png differ
diff --git go-ethereum/docs/static/images/emoji/full_moon.png celo/docs/static/images/emoji/full_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..8ff657a25936753687d44e607519b8b53e66b708 Binary files /dev/null and celo/docs/static/images/emoji/full_moon.png differ
diff --git go-ethereum/docs/static/images/emoji/full_moon_with_face.png celo/docs/static/images/emoji/full_moon_with_face.png new file mode 100644 index 0000000000000000000000000000000000000000..d42b3f0fbe5e176e9faff916357319234d3d51f0 Binary files /dev/null and celo/docs/static/images/emoji/full_moon_with_face.png differ
diff --git go-ethereum/docs/static/images/emoji/game_die.png celo/docs/static/images/emoji/game_die.png new file mode 100644 index 0000000000000000000000000000000000000000..cff2bd8b7768d0df6764cb33c7d9d892b924685a Binary files /dev/null and celo/docs/static/images/emoji/game_die.png differ
diff --git go-ethereum/docs/static/images/emoji/gb.png celo/docs/static/images/emoji/gb.png new file mode 100644 index 0000000000000000000000000000000000000000..2a62c7a0810a8b4f3d06279330e0308bf2a78b68 Binary files /dev/null and celo/docs/static/images/emoji/gb.png differ
diff --git go-ethereum/docs/static/images/emoji/gem.png celo/docs/static/images/emoji/gem.png new file mode 100644 index 0000000000000000000000000000000000000000..8a5d8dad5c3ab032d0d986be788d309fe55cf3ad Binary files /dev/null and celo/docs/static/images/emoji/gem.png differ
diff --git go-ethereum/docs/static/images/emoji/gemini.png celo/docs/static/images/emoji/gemini.png new file mode 100644 index 0000000000000000000000000000000000000000..d926f6e88e908cfbb7f7d116fdd50421826131d2 Binary files /dev/null and celo/docs/static/images/emoji/gemini.png differ
diff --git go-ethereum/docs/static/images/emoji/ghost.png celo/docs/static/images/emoji/ghost.png new file mode 100644 index 0000000000000000000000000000000000000000..671dd0c9e2ef5c61a08f7695b19defaa454f1e77 Binary files /dev/null and celo/docs/static/images/emoji/ghost.png differ
diff --git go-ethereum/docs/static/images/emoji/gift.png celo/docs/static/images/emoji/gift.png new file mode 100644 index 0000000000000000000000000000000000000000..552cfdc2b982d5d3ac41d44892872f038dfc2d60 Binary files /dev/null and celo/docs/static/images/emoji/gift.png differ
diff --git go-ethereum/docs/static/images/emoji/gift_heart.png celo/docs/static/images/emoji/gift_heart.png new file mode 100644 index 0000000000000000000000000000000000000000..f31c26a3fcc75e5e47d7e1092ac51aabaaee2e3f Binary files /dev/null and celo/docs/static/images/emoji/gift_heart.png differ
diff --git go-ethereum/docs/static/images/emoji/girl.png celo/docs/static/images/emoji/girl.png new file mode 100644 index 0000000000000000000000000000000000000000..ea4126941f726eeeee2eabe1d429d6a87619cfe3 Binary files /dev/null and celo/docs/static/images/emoji/girl.png differ
diff --git go-ethereum/docs/static/images/emoji/globe_with_meridians.png celo/docs/static/images/emoji/globe_with_meridians.png new file mode 100644 index 0000000000000000000000000000000000000000..b198646670cf4cf4d4d307ad92949cae4af0f100 Binary files /dev/null and celo/docs/static/images/emoji/globe_with_meridians.png differ
diff --git go-ethereum/docs/static/images/emoji/goat.png celo/docs/static/images/emoji/goat.png new file mode 100644 index 0000000000000000000000000000000000000000..4be9cf30404771e0d7ec6d6e0d8a63dc4096f11a Binary files /dev/null and celo/docs/static/images/emoji/goat.png differ
diff --git go-ethereum/docs/static/images/emoji/goberserk.png celo/docs/static/images/emoji/goberserk.png new file mode 100644 index 0000000000000000000000000000000000000000..59a742aaaa549287a5f9efbb07767ca97788bf40 Binary files /dev/null and celo/docs/static/images/emoji/goberserk.png differ
diff --git go-ethereum/docs/static/images/emoji/godmode.png celo/docs/static/images/emoji/godmode.png new file mode 100644 index 0000000000000000000000000000000000000000..7e75ab2081ba7f225146bad89ff4d5fd4d990e5c Binary files /dev/null and celo/docs/static/images/emoji/godmode.png differ
diff --git go-ethereum/docs/static/images/emoji/golf.png celo/docs/static/images/emoji/golf.png new file mode 100644 index 0000000000000000000000000000000000000000..cba2116a7e2d627728bcfbbac86b95cfabef667f Binary files /dev/null and celo/docs/static/images/emoji/golf.png differ
diff --git go-ethereum/docs/static/images/emoji/grapes.png celo/docs/static/images/emoji/grapes.png new file mode 100644 index 0000000000000000000000000000000000000000..0f9f007a12fbbad58ee2a8edf4e459aff73b4b2e Binary files /dev/null and celo/docs/static/images/emoji/grapes.png differ
diff --git go-ethereum/docs/static/images/emoji/green_apple.png celo/docs/static/images/emoji/green_apple.png new file mode 100644 index 0000000000000000000000000000000000000000..337205cd125385e16d1b67fe21e2568f207e3de8 Binary files /dev/null and celo/docs/static/images/emoji/green_apple.png differ
diff --git go-ethereum/docs/static/images/emoji/green_book.png celo/docs/static/images/emoji/green_book.png new file mode 100644 index 0000000000000000000000000000000000000000..e86651e5c5c372afaad34b4ba16f84be46acc9fc Binary files /dev/null and celo/docs/static/images/emoji/green_book.png differ
diff --git go-ethereum/docs/static/images/emoji/green_heart.png celo/docs/static/images/emoji/green_heart.png new file mode 100644 index 0000000000000000000000000000000000000000..7289cb8147c75c638ef01ce52fd777b166e2f661 Binary files /dev/null and celo/docs/static/images/emoji/green_heart.png differ
diff --git go-ethereum/docs/static/images/emoji/grey_exclamation.png celo/docs/static/images/emoji/grey_exclamation.png new file mode 100644 index 0000000000000000000000000000000000000000..cf027dda5d12952e0904b41f2a1c7ae235c2681f Binary files /dev/null and celo/docs/static/images/emoji/grey_exclamation.png differ
diff --git go-ethereum/docs/static/images/emoji/grey_question.png celo/docs/static/images/emoji/grey_question.png new file mode 100644 index 0000000000000000000000000000000000000000..fb97ba752fdc88e948c09be05f0358c007a80cca Binary files /dev/null and celo/docs/static/images/emoji/grey_question.png differ
diff --git go-ethereum/docs/static/images/emoji/grimacing.png celo/docs/static/images/emoji/grimacing.png new file mode 100644 index 0000000000000000000000000000000000000000..1219ba7d3c11e8b2722a230e9a8e2e40a01375c4 Binary files /dev/null and celo/docs/static/images/emoji/grimacing.png differ
diff --git go-ethereum/docs/static/images/emoji/grin.png celo/docs/static/images/emoji/grin.png new file mode 100644 index 0000000000000000000000000000000000000000..591cfcef8bb86b07f085e81b99e16bb261c40e15 Binary files /dev/null and celo/docs/static/images/emoji/grin.png differ
diff --git go-ethereum/docs/static/images/emoji/grinning.png celo/docs/static/images/emoji/grinning.png new file mode 100644 index 0000000000000000000000000000000000000000..7e812b7ed45c5eaa63657c6feeb4a62202230f4f Binary files /dev/null and celo/docs/static/images/emoji/grinning.png differ
diff --git go-ethereum/docs/static/images/emoji/guardsman.png celo/docs/static/images/emoji/guardsman.png new file mode 100644 index 0000000000000000000000000000000000000000..b67b335d68744d99bc23bb6b69b74f455e40987c Binary files /dev/null and celo/docs/static/images/emoji/guardsman.png differ
diff --git go-ethereum/docs/static/images/emoji/guitar.png celo/docs/static/images/emoji/guitar.png new file mode 100644 index 0000000000000000000000000000000000000000..2b7fa43c941d33a524665cbff0cd178be13b8680 Binary files /dev/null and celo/docs/static/images/emoji/guitar.png differ
diff --git go-ethereum/docs/static/images/emoji/gun.png celo/docs/static/images/emoji/gun.png new file mode 100644 index 0000000000000000000000000000000000000000..c49dc52c6cbfea3614046401144b5e7cf5601aeb Binary files /dev/null and celo/docs/static/images/emoji/gun.png differ
diff --git go-ethereum/docs/static/images/emoji/haircut.png celo/docs/static/images/emoji/haircut.png new file mode 100644 index 0000000000000000000000000000000000000000..902d273f6c4428d4a7b4746dd116c0c14e87a389 Binary files /dev/null and celo/docs/static/images/emoji/haircut.png differ
diff --git go-ethereum/docs/static/images/emoji/hamburger.png celo/docs/static/images/emoji/hamburger.png new file mode 100644 index 0000000000000000000000000000000000000000..9f1a3fdff6ec6d7ef4a52587f434b928b6b6b27e Binary files /dev/null and celo/docs/static/images/emoji/hamburger.png differ
diff --git go-ethereum/docs/static/images/emoji/hammer.png celo/docs/static/images/emoji/hammer.png new file mode 100644 index 0000000000000000000000000000000000000000..482b1c747d1a515e09863a24f34d7451b7ebf852 Binary files /dev/null and celo/docs/static/images/emoji/hammer.png differ
diff --git go-ethereum/docs/static/images/emoji/hamster.png celo/docs/static/images/emoji/hamster.png new file mode 100644 index 0000000000000000000000000000000000000000..addfd2e6b03d1742832fecc47990ec2da4374c34 Binary files /dev/null and celo/docs/static/images/emoji/hamster.png differ
diff --git go-ethereum/docs/static/images/emoji/hand.png celo/docs/static/images/emoji/hand.png new file mode 100644 index 0000000000000000000000000000000000000000..5e45c25a56c6148a8122fb762179efbbf3cd2c62 Binary files /dev/null and celo/docs/static/images/emoji/hand.png differ
diff --git go-ethereum/docs/static/images/emoji/handbag.png celo/docs/static/images/emoji/handbag.png new file mode 100644 index 0000000000000000000000000000000000000000..d7adf04ddf2238ae16955df83d1d42819477e6eb Binary files /dev/null and celo/docs/static/images/emoji/handbag.png differ
diff --git go-ethereum/docs/static/images/emoji/hankey.png celo/docs/static/images/emoji/hankey.png new file mode 100644 index 0000000000000000000000000000000000000000..73a4dc840085c7f42c7464d827751348b58acfba Binary files /dev/null and celo/docs/static/images/emoji/hankey.png differ
diff --git go-ethereum/docs/static/images/emoji/hash.png celo/docs/static/images/emoji/hash.png new file mode 100644 index 0000000000000000000000000000000000000000..6765d7d3c2ef48438b5c8d629e0d499566c73693 Binary files /dev/null and celo/docs/static/images/emoji/hash.png differ
diff --git go-ethereum/docs/static/images/emoji/hatched_chick.png celo/docs/static/images/emoji/hatched_chick.png new file mode 100644 index 0000000000000000000000000000000000000000..39c25bc7ccde42bc18a5fb0aaaed47d88490bf98 Binary files /dev/null and celo/docs/static/images/emoji/hatched_chick.png differ
diff --git go-ethereum/docs/static/images/emoji/hatching_chick.png celo/docs/static/images/emoji/hatching_chick.png new file mode 100644 index 0000000000000000000000000000000000000000..005a55519f1c2668cc3b664dd1f7307f53cf3102 Binary files /dev/null and celo/docs/static/images/emoji/hatching_chick.png differ
diff --git go-ethereum/docs/static/images/emoji/headphones.png celo/docs/static/images/emoji/headphones.png new file mode 100644 index 0000000000000000000000000000000000000000..ad83000e687632a3da46502187b0b72a675a56d5 Binary files /dev/null and celo/docs/static/images/emoji/headphones.png differ
diff --git go-ethereum/docs/static/images/emoji/hear_no_evil.png celo/docs/static/images/emoji/hear_no_evil.png new file mode 100644 index 0000000000000000000000000000000000000000..f97a1f9a0901f16e373e349e32bcbaf7c6c659de Binary files /dev/null and celo/docs/static/images/emoji/hear_no_evil.png differ
diff --git go-ethereum/docs/static/images/emoji/heart.png celo/docs/static/images/emoji/heart.png new file mode 100644 index 0000000000000000000000000000000000000000..7d7790ce4dfc69a8dcbbf706b75757dffec862e2 Binary files /dev/null and celo/docs/static/images/emoji/heart.png differ
diff --git go-ethereum/docs/static/images/emoji/heart_decoration.png celo/docs/static/images/emoji/heart_decoration.png new file mode 100644 index 0000000000000000000000000000000000000000..b8be44db343839258d488353c30890df963d169b Binary files /dev/null and celo/docs/static/images/emoji/heart_decoration.png differ
diff --git go-ethereum/docs/static/images/emoji/heart_eyes.png celo/docs/static/images/emoji/heart_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..0e5794270ea393cd6295e0553ec87838e0a4d5b9 Binary files /dev/null and celo/docs/static/images/emoji/heart_eyes.png differ
diff --git go-ethereum/docs/static/images/emoji/heart_eyes_cat.png celo/docs/static/images/emoji/heart_eyes_cat.png new file mode 100644 index 0000000000000000000000000000000000000000..eeba240e533783c92e0d221489e78c7f54586027 Binary files /dev/null and celo/docs/static/images/emoji/heart_eyes_cat.png differ
diff --git go-ethereum/docs/static/images/emoji/heartbeat.png celo/docs/static/images/emoji/heartbeat.png new file mode 100644 index 0000000000000000000000000000000000000000..b6628f6fa70ab24c6e83172221781e4a5077c481 Binary files /dev/null and celo/docs/static/images/emoji/heartbeat.png differ
diff --git go-ethereum/docs/static/images/emoji/heartpulse.png celo/docs/static/images/emoji/heartpulse.png new file mode 100644 index 0000000000000000000000000000000000000000..a7491cbeae62ee924046a012219ada18aa4975fe Binary files /dev/null and celo/docs/static/images/emoji/heartpulse.png differ
diff --git go-ethereum/docs/static/images/emoji/hearts.png celo/docs/static/images/emoji/hearts.png new file mode 100644 index 0000000000000000000000000000000000000000..e894715385710c5382bd052ad9a501c1fe6be376 Binary files /dev/null and celo/docs/static/images/emoji/hearts.png differ
diff --git go-ethereum/docs/static/images/emoji/heavy_check_mark.png celo/docs/static/images/emoji/heavy_check_mark.png new file mode 100644 index 0000000000000000000000000000000000000000..d0f010b4a0a249bc19db54dbf88eedddd9a388ba Binary files /dev/null and celo/docs/static/images/emoji/heavy_check_mark.png differ
diff --git go-ethereum/docs/static/images/emoji/heavy_division_sign.png celo/docs/static/images/emoji/heavy_division_sign.png new file mode 100644 index 0000000000000000000000000000000000000000..e193fd252fa1e146ff1614ac801079ca13a19db1 Binary files /dev/null and celo/docs/static/images/emoji/heavy_division_sign.png differ
diff --git go-ethereum/docs/static/images/emoji/heavy_dollar_sign.png celo/docs/static/images/emoji/heavy_dollar_sign.png new file mode 100644 index 0000000000000000000000000000000000000000..5eddfc52b241b0acd24a80891f5fcdc72af35b9b Binary files /dev/null and celo/docs/static/images/emoji/heavy_dollar_sign.png differ
diff --git go-ethereum/docs/static/images/emoji/heavy_exclamation_mark.png celo/docs/static/images/emoji/heavy_exclamation_mark.png new file mode 100644 index 0000000000000000000000000000000000000000..4c560f5e3f46d3ec6361aebee9d689ef79525b71 Binary files /dev/null and celo/docs/static/images/emoji/heavy_exclamation_mark.png differ
diff --git go-ethereum/docs/static/images/emoji/heavy_minus_sign.png celo/docs/static/images/emoji/heavy_minus_sign.png new file mode 100644 index 0000000000000000000000000000000000000000..4a33f905ab5ab4928907a8e36d7b9a998ea15e67 Binary files /dev/null and celo/docs/static/images/emoji/heavy_minus_sign.png differ
diff --git go-ethereum/docs/static/images/emoji/heavy_multiplication_x.png celo/docs/static/images/emoji/heavy_multiplication_x.png new file mode 100644 index 0000000000000000000000000000000000000000..13d666078656729c570e62e9bf287f74391b8d36 Binary files /dev/null and celo/docs/static/images/emoji/heavy_multiplication_x.png differ
diff --git go-ethereum/docs/static/images/emoji/heavy_plus_sign.png celo/docs/static/images/emoji/heavy_plus_sign.png new file mode 100644 index 0000000000000000000000000000000000000000..ade3c3a858bcb2b0723e346531969fcc9dbd6fc4 Binary files /dev/null and celo/docs/static/images/emoji/heavy_plus_sign.png differ
diff --git go-ethereum/docs/static/images/emoji/helicopter.png celo/docs/static/images/emoji/helicopter.png new file mode 100644 index 0000000000000000000000000000000000000000..8e82a0d587692e6210f28ac1e3f0015e27b2639b Binary files /dev/null and celo/docs/static/images/emoji/helicopter.png differ
diff --git go-ethereum/docs/static/images/emoji/herb.png celo/docs/static/images/emoji/herb.png new file mode 100644 index 0000000000000000000000000000000000000000..de1ff1b73bf01beac7f18a3343778fba7d7206ec Binary files /dev/null and celo/docs/static/images/emoji/herb.png differ
diff --git go-ethereum/docs/static/images/emoji/hibiscus.png celo/docs/static/images/emoji/hibiscus.png new file mode 100644 index 0000000000000000000000000000000000000000..9365ae2169f448da7f194d21cf6f097155a91a9c Binary files /dev/null and celo/docs/static/images/emoji/hibiscus.png differ
diff --git go-ethereum/docs/static/images/emoji/high_brightness.png celo/docs/static/images/emoji/high_brightness.png new file mode 100644 index 0000000000000000000000000000000000000000..ba9de7d409c008610ea2228074f7a8b03f732bd5 Binary files /dev/null and celo/docs/static/images/emoji/high_brightness.png differ
diff --git go-ethereum/docs/static/images/emoji/high_heel.png celo/docs/static/images/emoji/high_heel.png new file mode 100644 index 0000000000000000000000000000000000000000..525b6a0dd6947b98e1950c822b8448c2ecdc0c2c Binary files /dev/null and celo/docs/static/images/emoji/high_heel.png differ
diff --git go-ethereum/docs/static/images/emoji/hocho.png celo/docs/static/images/emoji/hocho.png new file mode 100644 index 0000000000000000000000000000000000000000..3f05193c7230a1afb38e044581c795cae37f69e0 Binary files /dev/null and celo/docs/static/images/emoji/hocho.png differ
diff --git go-ethereum/docs/static/images/emoji/honey_pot.png celo/docs/static/images/emoji/honey_pot.png new file mode 100644 index 0000000000000000000000000000000000000000..73278898a4c568b23ac5bcd54b9de37d66aab1a0 Binary files /dev/null and celo/docs/static/images/emoji/honey_pot.png differ
diff --git go-ethereum/docs/static/images/emoji/honeybee.png celo/docs/static/images/emoji/honeybee.png new file mode 100644 index 0000000000000000000000000000000000000000..f53733953afad79cb582a1e7edc8d847b8767f1a Binary files /dev/null and celo/docs/static/images/emoji/honeybee.png differ
diff --git go-ethereum/docs/static/images/emoji/horse.png celo/docs/static/images/emoji/horse.png new file mode 100644 index 0000000000000000000000000000000000000000..78d580ad3e9cb256589834fd6d61013127324b29 Binary files /dev/null and celo/docs/static/images/emoji/horse.png differ
diff --git go-ethereum/docs/static/images/emoji/horse_racing.png celo/docs/static/images/emoji/horse_racing.png new file mode 100644 index 0000000000000000000000000000000000000000..e3bbaec1d6c8f8e7257b73023ea7d52ed73b5ea8 Binary files /dev/null and celo/docs/static/images/emoji/horse_racing.png differ
diff --git go-ethereum/docs/static/images/emoji/hospital.png celo/docs/static/images/emoji/hospital.png new file mode 100644 index 0000000000000000000000000000000000000000..c05c49377fe19057ebaab5654041a9bd70f7e1b0 Binary files /dev/null and celo/docs/static/images/emoji/hospital.png differ
diff --git go-ethereum/docs/static/images/emoji/hotel.png celo/docs/static/images/emoji/hotel.png new file mode 100644 index 0000000000000000000000000000000000000000..d29f276a1805e58cdef33235312cc2f41be26c0f Binary files /dev/null and celo/docs/static/images/emoji/hotel.png differ
diff --git go-ethereum/docs/static/images/emoji/hotsprings.png celo/docs/static/images/emoji/hotsprings.png new file mode 100644 index 0000000000000000000000000000000000000000..a0bc9d75f21878b0d9a663dc72b129c345215e55 Binary files /dev/null and celo/docs/static/images/emoji/hotsprings.png differ
diff --git go-ethereum/docs/static/images/emoji/hourglass.png celo/docs/static/images/emoji/hourglass.png new file mode 100644 index 0000000000000000000000000000000000000000..405aab41beb391ab072c9a0225038950417c40e4 Binary files /dev/null and celo/docs/static/images/emoji/hourglass.png differ
diff --git go-ethereum/docs/static/images/emoji/hourglass_flowing_sand.png celo/docs/static/images/emoji/hourglass_flowing_sand.png new file mode 100644 index 0000000000000000000000000000000000000000..b68eb6957801a9316a48a473b92f134b3b5a2863 Binary files /dev/null and celo/docs/static/images/emoji/hourglass_flowing_sand.png differ
diff --git go-ethereum/docs/static/images/emoji/house.png celo/docs/static/images/emoji/house.png new file mode 100644 index 0000000000000000000000000000000000000000..95b9ee09480cb635876ddec024d6aba6e8d636bf Binary files /dev/null and celo/docs/static/images/emoji/house.png differ
diff --git go-ethereum/docs/static/images/emoji/house_with_garden.png celo/docs/static/images/emoji/house_with_garden.png new file mode 100644 index 0000000000000000000000000000000000000000..3338fb717be87387a8d423875625a7c40e7e5134 Binary files /dev/null and celo/docs/static/images/emoji/house_with_garden.png differ
diff --git go-ethereum/docs/static/images/emoji/hurtrealbad.png celo/docs/static/images/emoji/hurtrealbad.png new file mode 100644 index 0000000000000000000000000000000000000000..146ef1a6a87c299c712aaba364f513a1f06203ff Binary files /dev/null and celo/docs/static/images/emoji/hurtrealbad.png differ
diff --git go-ethereum/docs/static/images/emoji/hushed.png celo/docs/static/images/emoji/hushed.png new file mode 100644 index 0000000000000000000000000000000000000000..bbd2cd4bc00682196f8c7129b74fc87f5f2cec09 Binary files /dev/null and celo/docs/static/images/emoji/hushed.png differ
diff --git go-ethereum/docs/static/images/emoji/ice_cream.png celo/docs/static/images/emoji/ice_cream.png new file mode 100644 index 0000000000000000000000000000000000000000..190be01650ed17340c4bfcdcf4ff82b4df5256a4 Binary files /dev/null and celo/docs/static/images/emoji/ice_cream.png differ
diff --git go-ethereum/docs/static/images/emoji/icecream.png celo/docs/static/images/emoji/icecream.png new file mode 100644 index 0000000000000000000000000000000000000000..871ce097689eb1fc452fcd8b2a0fc00833849ce6 Binary files /dev/null and celo/docs/static/images/emoji/icecream.png differ
diff --git go-ethereum/docs/static/images/emoji/id.png celo/docs/static/images/emoji/id.png new file mode 100644 index 0000000000000000000000000000000000000000..47437a76d39258a854a52ce98d7b2839b7f4f9a5 Binary files /dev/null and celo/docs/static/images/emoji/id.png differ
diff --git go-ethereum/docs/static/images/emoji/ideograph_advantage.png celo/docs/static/images/emoji/ideograph_advantage.png new file mode 100644 index 0000000000000000000000000000000000000000..3c1334d164f6e18cd8182ac40bd483d7e05bbdcf Binary files /dev/null and celo/docs/static/images/emoji/ideograph_advantage.png differ
diff --git go-ethereum/docs/static/images/emoji/imp.png celo/docs/static/images/emoji/imp.png new file mode 100644 index 0000000000000000000000000000000000000000..fa7d9dc10ab9abe5a32fe73a8cc64e495385a348 Binary files /dev/null and celo/docs/static/images/emoji/imp.png differ
diff --git go-ethereum/docs/static/images/emoji/inbox_tray.png celo/docs/static/images/emoji/inbox_tray.png new file mode 100644 index 0000000000000000000000000000000000000000..e2df0f8970524c92f3ed147c889fe5e66a3acf5c Binary files /dev/null and celo/docs/static/images/emoji/inbox_tray.png differ
diff --git go-ethereum/docs/static/images/emoji/incoming_envelope.png celo/docs/static/images/emoji/incoming_envelope.png new file mode 100644 index 0000000000000000000000000000000000000000..afc827125108517b06c96d0f94d7813c3e3a40aa Binary files /dev/null and celo/docs/static/images/emoji/incoming_envelope.png differ
diff --git go-ethereum/docs/static/images/emoji/information_desk_person.png celo/docs/static/images/emoji/information_desk_person.png new file mode 100644 index 0000000000000000000000000000000000000000..52c0a50a3f61ada64000b53eac14887f228aebc8 Binary files /dev/null and celo/docs/static/images/emoji/information_desk_person.png differ
diff --git go-ethereum/docs/static/images/emoji/information_source.png celo/docs/static/images/emoji/information_source.png new file mode 100644 index 0000000000000000000000000000000000000000..9cb8b09b249492037ebcec0b0c319f728c6641f6 Binary files /dev/null and celo/docs/static/images/emoji/information_source.png differ
diff --git go-ethereum/docs/static/images/emoji/innocent.png celo/docs/static/images/emoji/innocent.png new file mode 100644 index 0000000000000000000000000000000000000000..503b614f8dcc32bad7a6f17f49d12602792215b0 Binary files /dev/null and celo/docs/static/images/emoji/innocent.png differ
diff --git go-ethereum/docs/static/images/emoji/interrobang.png celo/docs/static/images/emoji/interrobang.png new file mode 100644 index 0000000000000000000000000000000000000000..64304b9f5fb0120bf666e4ab13e6def7f9fbb846 Binary files /dev/null and celo/docs/static/images/emoji/interrobang.png differ
diff --git go-ethereum/docs/static/images/emoji/iphone.png celo/docs/static/images/emoji/iphone.png new file mode 100644 index 0000000000000000000000000000000000000000..df007103b0bd410bfd23be425d6654b0aa3eeb16 Binary files /dev/null and celo/docs/static/images/emoji/iphone.png differ
diff --git go-ethereum/docs/static/images/emoji/it.png celo/docs/static/images/emoji/it.png new file mode 100644 index 0000000000000000000000000000000000000000..70bc9f32463c11f26cf129ab031795a271f15b35 Binary files /dev/null and celo/docs/static/images/emoji/it.png differ
diff --git go-ethereum/docs/static/images/emoji/izakaya_lantern.png celo/docs/static/images/emoji/izakaya_lantern.png new file mode 100644 index 0000000000000000000000000000000000000000..18730ad559737ba68461687736483c1cb9bedb23 Binary files /dev/null and celo/docs/static/images/emoji/izakaya_lantern.png differ
diff --git go-ethereum/docs/static/images/emoji/jack_o_lantern.png celo/docs/static/images/emoji/jack_o_lantern.png new file mode 100644 index 0000000000000000000000000000000000000000..1f7667ea45897409431a8069a56d8a2683d7b345 Binary files /dev/null and celo/docs/static/images/emoji/jack_o_lantern.png differ
diff --git go-ethereum/docs/static/images/emoji/japan.png celo/docs/static/images/emoji/japan.png new file mode 100644 index 0000000000000000000000000000000000000000..45932803597c1ce7e8d073cc5fce00aeaecd01db Binary files /dev/null and celo/docs/static/images/emoji/japan.png differ
diff --git go-ethereum/docs/static/images/emoji/japanese_castle.png celo/docs/static/images/emoji/japanese_castle.png new file mode 100644 index 0000000000000000000000000000000000000000..f225ab217c0ed0e570c9266369304b2f93e907ac Binary files /dev/null and celo/docs/static/images/emoji/japanese_castle.png differ
diff --git go-ethereum/docs/static/images/emoji/japanese_goblin.png celo/docs/static/images/emoji/japanese_goblin.png new file mode 100644 index 0000000000000000000000000000000000000000..bd21b187570ae2263db7e1d50cc1aaee27358c4f Binary files /dev/null and celo/docs/static/images/emoji/japanese_goblin.png differ
diff --git go-ethereum/docs/static/images/emoji/japanese_ogre.png celo/docs/static/images/emoji/japanese_ogre.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5471c9a28822445e6382e1c71dd9bda013c43 Binary files /dev/null and celo/docs/static/images/emoji/japanese_ogre.png differ
diff --git go-ethereum/docs/static/images/emoji/jeans.png celo/docs/static/images/emoji/jeans.png new file mode 100644 index 0000000000000000000000000000000000000000..d721cea54c3a2c831b80fe6af108c2ec28a456cb Binary files /dev/null and celo/docs/static/images/emoji/jeans.png differ
diff --git go-ethereum/docs/static/images/emoji/joy.png celo/docs/static/images/emoji/joy.png new file mode 100644 index 0000000000000000000000000000000000000000..47df693d424f94fc2db1a0392148ca8f2cdb4923 Binary files /dev/null and celo/docs/static/images/emoji/joy.png differ
diff --git go-ethereum/docs/static/images/emoji/joy_cat.png celo/docs/static/images/emoji/joy_cat.png new file mode 100644 index 0000000000000000000000000000000000000000..6c60cb0efc83a744ab2774a5ce4f5f774c7b3de9 Binary files /dev/null and celo/docs/static/images/emoji/joy_cat.png differ
diff --git go-ethereum/docs/static/images/emoji/jp.png celo/docs/static/images/emoji/jp.png new file mode 100644 index 0000000000000000000000000000000000000000..b786efbbd8aa214f0dcc30b4859629a6f3feff47 Binary files /dev/null and celo/docs/static/images/emoji/jp.png differ
diff --git go-ethereum/docs/static/images/emoji/key.png celo/docs/static/images/emoji/key.png new file mode 100644 index 0000000000000000000000000000000000000000..34673213f64990f557e373c5736693b94e47dce1 Binary files /dev/null and celo/docs/static/images/emoji/key.png differ
diff --git go-ethereum/docs/static/images/emoji/keycap_ten.png celo/docs/static/images/emoji/keycap_ten.png new file mode 100644 index 0000000000000000000000000000000000000000..71dac1c1cc0484787804a817c702bec235264acd Binary files /dev/null and celo/docs/static/images/emoji/keycap_ten.png differ
diff --git go-ethereum/docs/static/images/emoji/kimono.png celo/docs/static/images/emoji/kimono.png new file mode 100644 index 0000000000000000000000000000000000000000..34ffe137dcd21b83bdd62ea8e0f1ef247d181a27 Binary files /dev/null and celo/docs/static/images/emoji/kimono.png differ
diff --git go-ethereum/docs/static/images/emoji/kiss.png celo/docs/static/images/emoji/kiss.png new file mode 100644 index 0000000000000000000000000000000000000000..14fd9918d582e6165d4aed39fc87a4a41487b485 Binary files /dev/null and celo/docs/static/images/emoji/kiss.png differ
diff --git go-ethereum/docs/static/images/emoji/kissing.png celo/docs/static/images/emoji/kissing.png new file mode 100644 index 0000000000000000000000000000000000000000..f3c8dcd7831bd2a05f3d248ef7fda33bd05a47c2 Binary files /dev/null and celo/docs/static/images/emoji/kissing.png differ
diff --git go-ethereum/docs/static/images/emoji/kissing_cat.png celo/docs/static/images/emoji/kissing_cat.png new file mode 100644 index 0000000000000000000000000000000000000000..adc62fbe3ce8ef1b016dff846de3b0067734e7e7 Binary files /dev/null and celo/docs/static/images/emoji/kissing_cat.png differ
diff --git go-ethereum/docs/static/images/emoji/kissing_closed_eyes.png celo/docs/static/images/emoji/kissing_closed_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..449de197048a504b2ac4ff5bc8b86fa5cfa098de Binary files /dev/null and celo/docs/static/images/emoji/kissing_closed_eyes.png differ
diff --git go-ethereum/docs/static/images/emoji/kissing_face.png celo/docs/static/images/emoji/kissing_face.png new file mode 100644 index 0000000000000000000000000000000000000000..449de197048a504b2ac4ff5bc8b86fa5cfa098de Binary files /dev/null and celo/docs/static/images/emoji/kissing_face.png differ
diff --git go-ethereum/docs/static/images/emoji/kissing_heart.png celo/docs/static/images/emoji/kissing_heart.png new file mode 100644 index 0000000000000000000000000000000000000000..af9a80b7f09fcd4926ddeb4b8849220514274b48 Binary files /dev/null and celo/docs/static/images/emoji/kissing_heart.png differ
diff --git go-ethereum/docs/static/images/emoji/kissing_smiling_eyes.png celo/docs/static/images/emoji/kissing_smiling_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..57f7b4935607e03c052802a8ac0b7385ca29797f Binary files /dev/null and celo/docs/static/images/emoji/kissing_smiling_eyes.png differ
diff --git go-ethereum/docs/static/images/emoji/koala.png celo/docs/static/images/emoji/koala.png new file mode 100644 index 0000000000000000000000000000000000000000..e17bd3cf531f0a752a4214ef1d272c51184826ad Binary files /dev/null and celo/docs/static/images/emoji/koala.png differ
diff --git go-ethereum/docs/static/images/emoji/koko.png celo/docs/static/images/emoji/koko.png new file mode 100644 index 0000000000000000000000000000000000000000..3bef28c9fdbdb466221b47929c92869add843558 Binary files /dev/null and celo/docs/static/images/emoji/koko.png differ
diff --git go-ethereum/docs/static/images/emoji/kr.png celo/docs/static/images/emoji/kr.png new file mode 100644 index 0000000000000000000000000000000000000000..b4c0c1b673d7c490888041a189d14e833730c7c6 Binary files /dev/null and celo/docs/static/images/emoji/kr.png differ
diff --git go-ethereum/docs/static/images/emoji/large_blue_circle.png celo/docs/static/images/emoji/large_blue_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..a5b4ad4aaa2cc405cc6ea52c6b30728feca77af8 Binary files /dev/null and celo/docs/static/images/emoji/large_blue_circle.png differ
diff --git go-ethereum/docs/static/images/emoji/large_blue_diamond.png celo/docs/static/images/emoji/large_blue_diamond.png new file mode 100644 index 0000000000000000000000000000000000000000..f4598ec0f20cadcdda6f1b9edfce8cfe1ece80f8 Binary files /dev/null and celo/docs/static/images/emoji/large_blue_diamond.png differ
diff --git go-ethereum/docs/static/images/emoji/large_orange_diamond.png celo/docs/static/images/emoji/large_orange_diamond.png new file mode 100644 index 0000000000000000000000000000000000000000..803725aad713f9c544cbf3868dae56ba3a9fa4c6 Binary files /dev/null and celo/docs/static/images/emoji/large_orange_diamond.png differ
diff --git go-ethereum/docs/static/images/emoji/last_quarter_moon.png celo/docs/static/images/emoji/last_quarter_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..6ae30d6c2c96e0b3f56063cf54f41e22ce9a9499 Binary files /dev/null and celo/docs/static/images/emoji/last_quarter_moon.png differ
diff --git go-ethereum/docs/static/images/emoji/last_quarter_moon_with_face.png celo/docs/static/images/emoji/last_quarter_moon_with_face.png new file mode 100644 index 0000000000000000000000000000000000000000..9ece82dfec67560c8791e5e803657583a6fd84bf Binary files /dev/null and celo/docs/static/images/emoji/last_quarter_moon_with_face.png differ
diff --git go-ethereum/docs/static/images/emoji/laughing.png celo/docs/static/images/emoji/laughing.png new file mode 100644 index 0000000000000000000000000000000000000000..11c91eb22e6a9ebb879071045a8d40ae9d15bce2 Binary files /dev/null and celo/docs/static/images/emoji/laughing.png differ
diff --git go-ethereum/docs/static/images/emoji/leaves.png celo/docs/static/images/emoji/leaves.png new file mode 100644 index 0000000000000000000000000000000000000000..5229e06bdd097f639b4860f386e138ea072b4ec8 Binary files /dev/null and celo/docs/static/images/emoji/leaves.png differ
diff --git go-ethereum/docs/static/images/emoji/ledger.png celo/docs/static/images/emoji/ledger.png new file mode 100644 index 0000000000000000000000000000000000000000..e4f72aceacfc3b715a504edcf14dd1b3e30e6bcd Binary files /dev/null and celo/docs/static/images/emoji/ledger.png differ
diff --git go-ethereum/docs/static/images/emoji/left_luggage.png celo/docs/static/images/emoji/left_luggage.png new file mode 100644 index 0000000000000000000000000000000000000000..1c08b464db124bbad7c8f8b52f72f51d6c1b3683 Binary files /dev/null and celo/docs/static/images/emoji/left_luggage.png differ
diff --git go-ethereum/docs/static/images/emoji/left_right_arrow.png celo/docs/static/images/emoji/left_right_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..b9fd11c5158ba6e8d51e22a15ffaef865a6ef642 Binary files /dev/null and celo/docs/static/images/emoji/left_right_arrow.png differ
diff --git go-ethereum/docs/static/images/emoji/leftwards_arrow_with_hook.png celo/docs/static/images/emoji/leftwards_arrow_with_hook.png new file mode 100644 index 0000000000000000000000000000000000000000..bc45dfefd4adc427af9a75ecacb825784bf7b54d Binary files /dev/null and celo/docs/static/images/emoji/leftwards_arrow_with_hook.png differ
diff --git go-ethereum/docs/static/images/emoji/lemon.png celo/docs/static/images/emoji/lemon.png new file mode 100644 index 0000000000000000000000000000000000000000..9814dc95989fd9655f21d125f7afe40a058e32dd Binary files /dev/null and celo/docs/static/images/emoji/lemon.png differ
diff --git go-ethereum/docs/static/images/emoji/leo.png celo/docs/static/images/emoji/leo.png new file mode 100644 index 0000000000000000000000000000000000000000..e025933b2f875103d9ceb3faca054c3c15193c82 Binary files /dev/null and celo/docs/static/images/emoji/leo.png differ
diff --git go-ethereum/docs/static/images/emoji/leopard.png celo/docs/static/images/emoji/leopard.png new file mode 100644 index 0000000000000000000000000000000000000000..3e738d2d3c8cfd1f495e2882eee166f5b64b331e Binary files /dev/null and celo/docs/static/images/emoji/leopard.png differ
diff --git go-ethereum/docs/static/images/emoji/libra.png celo/docs/static/images/emoji/libra.png new file mode 100644 index 0000000000000000000000000000000000000000..6f4a927cd0eb2aefc4dc7d0182462a1f876cb055 Binary files /dev/null and celo/docs/static/images/emoji/libra.png differ
diff --git go-ethereum/docs/static/images/emoji/light_rail.png celo/docs/static/images/emoji/light_rail.png new file mode 100644 index 0000000000000000000000000000000000000000..bcfe801eec6f4165b37c3907527918ba6aae98c3 Binary files /dev/null and celo/docs/static/images/emoji/light_rail.png differ
diff --git go-ethereum/docs/static/images/emoji/link.png celo/docs/static/images/emoji/link.png new file mode 100644 index 0000000000000000000000000000000000000000..0239e48e4f87bd3fdad8dbdb79a1294651ddf884 Binary files /dev/null and celo/docs/static/images/emoji/link.png differ
diff --git go-ethereum/docs/static/images/emoji/lips.png celo/docs/static/images/emoji/lips.png new file mode 100644 index 0000000000000000000000000000000000000000..826ed1102dc6f3f74c008b3ce7bd03a4f0a44fa7 Binary files /dev/null and celo/docs/static/images/emoji/lips.png differ
diff --git go-ethereum/docs/static/images/emoji/lipstick.png celo/docs/static/images/emoji/lipstick.png new file mode 100644 index 0000000000000000000000000000000000000000..82f990c5679533d838d93215512229cb13f8043f Binary files /dev/null and celo/docs/static/images/emoji/lipstick.png differ
diff --git go-ethereum/docs/static/images/emoji/lock.png celo/docs/static/images/emoji/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..4892b023558f0b074010755353cf14fce785ca81 Binary files /dev/null and celo/docs/static/images/emoji/lock.png differ
diff --git go-ethereum/docs/static/images/emoji/lock_with_ink_pen.png celo/docs/static/images/emoji/lock_with_ink_pen.png new file mode 100644 index 0000000000000000000000000000000000000000..375e67e8253397b51cdbcfa1e8e3d308a24867b1 Binary files /dev/null and celo/docs/static/images/emoji/lock_with_ink_pen.png differ
diff --git go-ethereum/docs/static/images/emoji/lollipop.png celo/docs/static/images/emoji/lollipop.png new file mode 100644 index 0000000000000000000000000000000000000000..ba55e7093f125762c5e2bd38ac7c7afc69029eb7 Binary files /dev/null and celo/docs/static/images/emoji/lollipop.png differ
diff --git go-ethereum/docs/static/images/emoji/loop.png celo/docs/static/images/emoji/loop.png new file mode 100644 index 0000000000000000000000000000000000000000..ef34df3a404ab5ce6597c202d695665ea7c2870d Binary files /dev/null and celo/docs/static/images/emoji/loop.png differ
diff --git go-ethereum/docs/static/images/emoji/loudspeaker.png celo/docs/static/images/emoji/loudspeaker.png new file mode 100644 index 0000000000000000000000000000000000000000..752385e523d44d308bbb86aaf2484c09cd6797ae Binary files /dev/null and celo/docs/static/images/emoji/loudspeaker.png differ
diff --git go-ethereum/docs/static/images/emoji/love_hotel.png celo/docs/static/images/emoji/love_hotel.png new file mode 100644 index 0000000000000000000000000000000000000000..44d7db828ad1c6ffa0a782a83c506d99ea91c33f Binary files /dev/null and celo/docs/static/images/emoji/love_hotel.png differ
diff --git go-ethereum/docs/static/images/emoji/love_letter.png celo/docs/static/images/emoji/love_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..e29981f44533ec09c0c639a21ee8b7f4298f64e1 Binary files /dev/null and celo/docs/static/images/emoji/love_letter.png differ
diff --git go-ethereum/docs/static/images/emoji/low_brightness.png celo/docs/static/images/emoji/low_brightness.png new file mode 100644 index 0000000000000000000000000000000000000000..ea15bde4f0d4653bcbe6bc31de976d7522c7d248 Binary files /dev/null and celo/docs/static/images/emoji/low_brightness.png differ
diff --git go-ethereum/docs/static/images/emoji/m.png celo/docs/static/images/emoji/m.png new file mode 100644 index 0000000000000000000000000000000000000000..7e3a3bffb65526cb8ef388aa71b37a38d029ad33 Binary files /dev/null and celo/docs/static/images/emoji/m.png differ
diff --git go-ethereum/docs/static/images/emoji/mag.png celo/docs/static/images/emoji/mag.png new file mode 100644 index 0000000000000000000000000000000000000000..aa5b1d7c46fbfb27876d4e5facd5d0dfa5436d44 Binary files /dev/null and celo/docs/static/images/emoji/mag.png differ
diff --git go-ethereum/docs/static/images/emoji/mag_right.png celo/docs/static/images/emoji/mag_right.png new file mode 100644 index 0000000000000000000000000000000000000000..6e6cf11e6d7693f8f7d4b97d4910e56e5213d7ac Binary files /dev/null and celo/docs/static/images/emoji/mag_right.png differ
diff --git go-ethereum/docs/static/images/emoji/mahjong.png celo/docs/static/images/emoji/mahjong.png new file mode 100644 index 0000000000000000000000000000000000000000..f51ce65fdde8e3c2c0f69fbc1beb78ad7f83fa10 Binary files /dev/null and celo/docs/static/images/emoji/mahjong.png differ
diff --git go-ethereum/docs/static/images/emoji/mailbox.png celo/docs/static/images/emoji/mailbox.png new file mode 100644 index 0000000000000000000000000000000000000000..8351e70760cc342e4ece73aac4eafa921e3f90f0 Binary files /dev/null and celo/docs/static/images/emoji/mailbox.png differ
diff --git go-ethereum/docs/static/images/emoji/mailbox_closed.png celo/docs/static/images/emoji/mailbox_closed.png new file mode 100644 index 0000000000000000000000000000000000000000..a5982b69bb5059a3448bf949aa17d3db6ad5d44b Binary files /dev/null and celo/docs/static/images/emoji/mailbox_closed.png differ
diff --git go-ethereum/docs/static/images/emoji/mailbox_with_mail.png celo/docs/static/images/emoji/mailbox_with_mail.png new file mode 100644 index 0000000000000000000000000000000000000000..dae34594367e43481d6af557562b4486d3ee4ddb Binary files /dev/null and celo/docs/static/images/emoji/mailbox_with_mail.png differ
diff --git go-ethereum/docs/static/images/emoji/mailbox_with_no_mail.png celo/docs/static/images/emoji/mailbox_with_no_mail.png new file mode 100644 index 0000000000000000000000000000000000000000..59f15c5d7da88d816a9ed2f5a33cc23faa8633a7 Binary files /dev/null and celo/docs/static/images/emoji/mailbox_with_no_mail.png differ
diff --git go-ethereum/docs/static/images/emoji/man.png celo/docs/static/images/emoji/man.png new file mode 100644 index 0000000000000000000000000000000000000000..d9bfa26a67451b5c24e2b5f4c8e1fd2653e7404a Binary files /dev/null and celo/docs/static/images/emoji/man.png differ
diff --git go-ethereum/docs/static/images/emoji/man_with_gua_pi_mao.png celo/docs/static/images/emoji/man_with_gua_pi_mao.png new file mode 100644 index 0000000000000000000000000000000000000000..7aad74b55e332c4b6dd2b5b93e6e06a325fd9845 Binary files /dev/null and celo/docs/static/images/emoji/man_with_gua_pi_mao.png differ
diff --git go-ethereum/docs/static/images/emoji/man_with_turban.png celo/docs/static/images/emoji/man_with_turban.png new file mode 100644 index 0000000000000000000000000000000000000000..036604caf2a4c8772c2544e9f76117cbd6025e94 Binary files /dev/null and celo/docs/static/images/emoji/man_with_turban.png differ
diff --git go-ethereum/docs/static/images/emoji/mans_shoe.png celo/docs/static/images/emoji/mans_shoe.png new file mode 100644 index 0000000000000000000000000000000000000000..ecba9ba7d04187bb878e987cc5512d7f2d436131 Binary files /dev/null and celo/docs/static/images/emoji/mans_shoe.png differ
diff --git go-ethereum/docs/static/images/emoji/maple_leaf.png celo/docs/static/images/emoji/maple_leaf.png new file mode 100644 index 0000000000000000000000000000000000000000..4e9b47207de601d11c1a76fdbcdfe63f978bb432 Binary files /dev/null and celo/docs/static/images/emoji/maple_leaf.png differ
diff --git go-ethereum/docs/static/images/emoji/mask.png celo/docs/static/images/emoji/mask.png new file mode 100644 index 0000000000000000000000000000000000000000..05887e99c6bb601fc395d9180efc7db2e684d86e Binary files /dev/null and celo/docs/static/images/emoji/mask.png differ
diff --git go-ethereum/docs/static/images/emoji/massage.png celo/docs/static/images/emoji/massage.png new file mode 100644 index 0000000000000000000000000000000000000000..dd30d15975564504dd4fae61923675bfbeaa679b Binary files /dev/null and celo/docs/static/images/emoji/massage.png differ
diff --git go-ethereum/docs/static/images/emoji/meat_on_bone.png celo/docs/static/images/emoji/meat_on_bone.png new file mode 100644 index 0000000000000000000000000000000000000000..5b79a660c6da12af62419984f1d36419b6bf5d3e Binary files /dev/null and celo/docs/static/images/emoji/meat_on_bone.png differ
diff --git go-ethereum/docs/static/images/emoji/mega.png celo/docs/static/images/emoji/mega.png new file mode 100644 index 0000000000000000000000000000000000000000..022df2f8d64a85484f6ac8053da61029aab73a2d Binary files /dev/null and celo/docs/static/images/emoji/mega.png differ
diff --git go-ethereum/docs/static/images/emoji/melon.png celo/docs/static/images/emoji/melon.png new file mode 100644 index 0000000000000000000000000000000000000000..11c13cbbd44ff49892319b060b669b5adf6fc860 Binary files /dev/null and celo/docs/static/images/emoji/melon.png differ
diff --git go-ethereum/docs/static/images/emoji/memo.png celo/docs/static/images/emoji/memo.png new file mode 100644 index 0000000000000000000000000000000000000000..fc97ddbc92b0af4590d3d552bd771fd8d011828c Binary files /dev/null and celo/docs/static/images/emoji/memo.png differ
diff --git go-ethereum/docs/static/images/emoji/mens.png celo/docs/static/images/emoji/mens.png new file mode 100644 index 0000000000000000000000000000000000000000..abccfc9f2c6f8f965d2234fc5f96146adea9f821 Binary files /dev/null and celo/docs/static/images/emoji/mens.png differ
diff --git go-ethereum/docs/static/images/emoji/metal.png celo/docs/static/images/emoji/metal.png new file mode 100644 index 0000000000000000000000000000000000000000..94f1fda2241a789dff11af72f6ca93dcd780b6f5 Binary files /dev/null and celo/docs/static/images/emoji/metal.png differ
diff --git go-ethereum/docs/static/images/emoji/metro.png celo/docs/static/images/emoji/metro.png new file mode 100644 index 0000000000000000000000000000000000000000..4acf5ab3e29ffd56ea4ff96a272117f4e26c7047 Binary files /dev/null and celo/docs/static/images/emoji/metro.png differ
diff --git go-ethereum/docs/static/images/emoji/microphone.png celo/docs/static/images/emoji/microphone.png new file mode 100644 index 0000000000000000000000000000000000000000..68c74adada1c82c1e9e03f409fa5027d181371cb Binary files /dev/null and celo/docs/static/images/emoji/microphone.png differ
diff --git go-ethereum/docs/static/images/emoji/microscope.png celo/docs/static/images/emoji/microscope.png new file mode 100644 index 0000000000000000000000000000000000000000..8b7a5e4e67909f82156f41977145bd761b3d7f9d Binary files /dev/null and celo/docs/static/images/emoji/microscope.png differ
diff --git go-ethereum/docs/static/images/emoji/milky_way.png celo/docs/static/images/emoji/milky_way.png new file mode 100644 index 0000000000000000000000000000000000000000..901090a126598a9801c2c41296ae56416cd4ec47 Binary files /dev/null and celo/docs/static/images/emoji/milky_way.png differ
diff --git go-ethereum/docs/static/images/emoji/minibus.png celo/docs/static/images/emoji/minibus.png new file mode 100644 index 0000000000000000000000000000000000000000..c52cef23407809763b67a356783b9645bf949456 Binary files /dev/null and celo/docs/static/images/emoji/minibus.png differ
diff --git go-ethereum/docs/static/images/emoji/minidisc.png celo/docs/static/images/emoji/minidisc.png new file mode 100644 index 0000000000000000000000000000000000000000..e19cc5d01507966a1112cdf0a4456499cc7141cc Binary files /dev/null and celo/docs/static/images/emoji/minidisc.png differ
diff --git go-ethereum/docs/static/images/emoji/mobile_phone_off.png celo/docs/static/images/emoji/mobile_phone_off.png new file mode 100644 index 0000000000000000000000000000000000000000..fa16c763c946c0a5de997dee5f5fb02a6ff26844 Binary files /dev/null and celo/docs/static/images/emoji/mobile_phone_off.png differ
diff --git go-ethereum/docs/static/images/emoji/money_with_wings.png celo/docs/static/images/emoji/money_with_wings.png new file mode 100644 index 0000000000000000000000000000000000000000..581a82449025c14e50ff50515f73d090e2340c7d Binary files /dev/null and celo/docs/static/images/emoji/money_with_wings.png differ
diff --git go-ethereum/docs/static/images/emoji/moneybag.png celo/docs/static/images/emoji/moneybag.png new file mode 100644 index 0000000000000000000000000000000000000000..5546c04bad46b89e58906261f1d70a88a57832f1 Binary files /dev/null and celo/docs/static/images/emoji/moneybag.png differ
diff --git go-ethereum/docs/static/images/emoji/monkey.png celo/docs/static/images/emoji/monkey.png new file mode 100644 index 0000000000000000000000000000000000000000..64070359776727df50caf08cb59df7845a46c53a Binary files /dev/null and celo/docs/static/images/emoji/monkey.png differ
diff --git go-ethereum/docs/static/images/emoji/monkey_face.png celo/docs/static/images/emoji/monkey_face.png new file mode 100644 index 0000000000000000000000000000000000000000..6964cf4d51ac3f2a67f2162e7a52807993c19b7b Binary files /dev/null and celo/docs/static/images/emoji/monkey_face.png differ
diff --git go-ethereum/docs/static/images/emoji/monorail.png celo/docs/static/images/emoji/monorail.png new file mode 100644 index 0000000000000000000000000000000000000000..913d300246261bd48ea29813b5c693dcfa4a1404 Binary files /dev/null and celo/docs/static/images/emoji/monorail.png differ
diff --git go-ethereum/docs/static/images/emoji/mortar_board.png celo/docs/static/images/emoji/mortar_board.png new file mode 100644 index 0000000000000000000000000000000000000000..84513f6bac440096818ae7922e7f041452007e07 Binary files /dev/null and celo/docs/static/images/emoji/mortar_board.png differ
diff --git go-ethereum/docs/static/images/emoji/mount_fuji.png celo/docs/static/images/emoji/mount_fuji.png new file mode 100644 index 0000000000000000000000000000000000000000..4c313e583f02754e0b3ffc3ca2a1b208d000de12 Binary files /dev/null and celo/docs/static/images/emoji/mount_fuji.png differ
diff --git go-ethereum/docs/static/images/emoji/mountain_bicyclist.png celo/docs/static/images/emoji/mountain_bicyclist.png new file mode 100644 index 0000000000000000000000000000000000000000..b698897566a33f40ab827c3cec20b7912867f3c5 Binary files /dev/null and celo/docs/static/images/emoji/mountain_bicyclist.png differ
diff --git go-ethereum/docs/static/images/emoji/mountain_cableway.png celo/docs/static/images/emoji/mountain_cableway.png new file mode 100644 index 0000000000000000000000000000000000000000..5688bb239a7411950c9cf0a06c6f7efe3374f2dd Binary files /dev/null and celo/docs/static/images/emoji/mountain_cableway.png differ
diff --git go-ethereum/docs/static/images/emoji/mountain_railway.png celo/docs/static/images/emoji/mountain_railway.png new file mode 100644 index 0000000000000000000000000000000000000000..1f3d1aab56c9af7ffe317c068161571dd28c1458 Binary files /dev/null and celo/docs/static/images/emoji/mountain_railway.png differ
diff --git go-ethereum/docs/static/images/emoji/mouse.png celo/docs/static/images/emoji/mouse.png new file mode 100644 index 0000000000000000000000000000000000000000..8ff162e2dbb09513aa1acae740710b2ea00b104c Binary files /dev/null and celo/docs/static/images/emoji/mouse.png differ
diff --git go-ethereum/docs/static/images/emoji/mouse2.png celo/docs/static/images/emoji/mouse2.png new file mode 100644 index 0000000000000000000000000000000000000000..2d777e5e1ac5c9cdf8cb01f53a26112e19d2315a Binary files /dev/null and celo/docs/static/images/emoji/mouse2.png differ
diff --git go-ethereum/docs/static/images/emoji/movie_camera.png celo/docs/static/images/emoji/movie_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..9c1438409255b3f5211e6c14bf09af71e59743b0 Binary files /dev/null and celo/docs/static/images/emoji/movie_camera.png differ
diff --git go-ethereum/docs/static/images/emoji/moyai.png celo/docs/static/images/emoji/moyai.png new file mode 100644 index 0000000000000000000000000000000000000000..61a1a9c21a4246898e00359ff05123f2ad9f0ebe Binary files /dev/null and celo/docs/static/images/emoji/moyai.png differ
diff --git go-ethereum/docs/static/images/emoji/muscle.png celo/docs/static/images/emoji/muscle.png new file mode 100644 index 0000000000000000000000000000000000000000..19f92efb66e8220e0d461e670898203084ef0c89 Binary files /dev/null and celo/docs/static/images/emoji/muscle.png differ
diff --git go-ethereum/docs/static/images/emoji/mushroom.png celo/docs/static/images/emoji/mushroom.png new file mode 100644 index 0000000000000000000000000000000000000000..5eeed8e7900e14bdae84de4949541976ec127d54 Binary files /dev/null and celo/docs/static/images/emoji/mushroom.png differ
diff --git go-ethereum/docs/static/images/emoji/musical_keyboard.png celo/docs/static/images/emoji/musical_keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..93647a4a32d9f967020c63e11c80380311c825e4 Binary files /dev/null and celo/docs/static/images/emoji/musical_keyboard.png differ
diff --git go-ethereum/docs/static/images/emoji/musical_note.png celo/docs/static/images/emoji/musical_note.png new file mode 100644 index 0000000000000000000000000000000000000000..68b261bcba6db5a4f20de1ba33f1d8dd95b3ab33 Binary files /dev/null and celo/docs/static/images/emoji/musical_note.png differ
diff --git go-ethereum/docs/static/images/emoji/musical_score.png celo/docs/static/images/emoji/musical_score.png new file mode 100644 index 0000000000000000000000000000000000000000..c99e3381f1a3949fe4e9bc0c8617dac14a41e4df Binary files /dev/null and celo/docs/static/images/emoji/musical_score.png differ
diff --git go-ethereum/docs/static/images/emoji/mute.png celo/docs/static/images/emoji/mute.png new file mode 100644 index 0000000000000000000000000000000000000000..4cf67c367d366b60a3ae4a7aaeeee0674ddb16c7 Binary files /dev/null and celo/docs/static/images/emoji/mute.png differ
diff --git go-ethereum/docs/static/images/emoji/nail_care.png celo/docs/static/images/emoji/nail_care.png new file mode 100644 index 0000000000000000000000000000000000000000..6a66e63d2adb572ef0bee6a588d51f5ef2c70d29 Binary files /dev/null and celo/docs/static/images/emoji/nail_care.png differ
diff --git go-ethereum/docs/static/images/emoji/name_badge.png celo/docs/static/images/emoji/name_badge.png new file mode 100644 index 0000000000000000000000000000000000000000..2b712dcd55ac1be241aa4869a4eb7addbb52b84b Binary files /dev/null and celo/docs/static/images/emoji/name_badge.png differ
diff --git go-ethereum/docs/static/images/emoji/neckbeard.png celo/docs/static/images/emoji/neckbeard.png new file mode 100644 index 0000000000000000000000000000000000000000..6e31d1652837e70f630c3330718a12f0f6693031 Binary files /dev/null and celo/docs/static/images/emoji/neckbeard.png differ
diff --git go-ethereum/docs/static/images/emoji/necktie.png celo/docs/static/images/emoji/necktie.png new file mode 100644 index 0000000000000000000000000000000000000000..80461c66f3a087c1f67d66b831b39bdf39eebaf6 Binary files /dev/null and celo/docs/static/images/emoji/necktie.png differ
diff --git go-ethereum/docs/static/images/emoji/negative_squared_cross_mark.png celo/docs/static/images/emoji/negative_squared_cross_mark.png new file mode 100644 index 0000000000000000000000000000000000000000..b47a0cece5c9608953ff98cd6eeb1f60bb72153e Binary files /dev/null and celo/docs/static/images/emoji/negative_squared_cross_mark.png differ
diff --git go-ethereum/docs/static/images/emoji/neutral_face.png celo/docs/static/images/emoji/neutral_face.png new file mode 100644 index 0000000000000000000000000000000000000000..682a1ba066d8526f94fec4af76ca7fb83d0f0d8f Binary files /dev/null and celo/docs/static/images/emoji/neutral_face.png differ
diff --git go-ethereum/docs/static/images/emoji/new.png celo/docs/static/images/emoji/new.png new file mode 100644 index 0000000000000000000000000000000000000000..28d1570e0a6fdbdb2b9138ff01009a6d193e1f89 Binary files /dev/null and celo/docs/static/images/emoji/new.png differ
diff --git go-ethereum/docs/static/images/emoji/new_moon.png celo/docs/static/images/emoji/new_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..72492cb903d7ca5658eee106a103f9821a7c4961 Binary files /dev/null and celo/docs/static/images/emoji/new_moon.png differ
diff --git go-ethereum/docs/static/images/emoji/new_moon_with_face.png celo/docs/static/images/emoji/new_moon_with_face.png new file mode 100644 index 0000000000000000000000000000000000000000..21a696eb9910509c7d92a417dc9919186ca290aa Binary files /dev/null and celo/docs/static/images/emoji/new_moon_with_face.png differ
diff --git go-ethereum/docs/static/images/emoji/newspaper.png celo/docs/static/images/emoji/newspaper.png new file mode 100644 index 0000000000000000000000000000000000000000..60c3394dc4fe6de0c4d96e08ff9296ec62238984 Binary files /dev/null and celo/docs/static/images/emoji/newspaper.png differ
diff --git go-ethereum/docs/static/images/emoji/ng.png celo/docs/static/images/emoji/ng.png new file mode 100644 index 0000000000000000000000000000000000000000..2ca180ae397ac5c12d5be7d481957e5a3834a7e8 Binary files /dev/null and celo/docs/static/images/emoji/ng.png differ
diff --git go-ethereum/docs/static/images/emoji/nine.png celo/docs/static/images/emoji/nine.png new file mode 100644 index 0000000000000000000000000000000000000000..8006cc909f35f5fc9ecd5722cb197d03f1e9f1c7 Binary files /dev/null and celo/docs/static/images/emoji/nine.png differ
diff --git go-ethereum/docs/static/images/emoji/no_bell.png celo/docs/static/images/emoji/no_bell.png new file mode 100644 index 0000000000000000000000000000000000000000..613b81cd21eb4816aabaa082b3b4979bea6d0802 Binary files /dev/null and celo/docs/static/images/emoji/no_bell.png differ
diff --git go-ethereum/docs/static/images/emoji/no_bicycles.png celo/docs/static/images/emoji/no_bicycles.png new file mode 100644 index 0000000000000000000000000000000000000000..4b2621664551902ff88f24fabe7f2fa4f32a5be6 Binary files /dev/null and celo/docs/static/images/emoji/no_bicycles.png differ
diff --git go-ethereum/docs/static/images/emoji/no_entry.png celo/docs/static/images/emoji/no_entry.png new file mode 100644 index 0000000000000000000000000000000000000000..cf2086a8e7470f33d323705e9ec3e54f8b3350ca Binary files /dev/null and celo/docs/static/images/emoji/no_entry.png differ
diff --git go-ethereum/docs/static/images/emoji/no_entry_sign.png celo/docs/static/images/emoji/no_entry_sign.png new file mode 100644 index 0000000000000000000000000000000000000000..b3231f66d47a99c9608cbe8c13698671c9b1d69e Binary files /dev/null and celo/docs/static/images/emoji/no_entry_sign.png differ
diff --git go-ethereum/docs/static/images/emoji/no_good.png celo/docs/static/images/emoji/no_good.png new file mode 100644 index 0000000000000000000000000000000000000000..d459a35bc1f462c1d830f993abda44b1bb584a9e Binary files /dev/null and celo/docs/static/images/emoji/no_good.png differ
diff --git go-ethereum/docs/static/images/emoji/no_mobile_phones.png celo/docs/static/images/emoji/no_mobile_phones.png new file mode 100644 index 0000000000000000000000000000000000000000..41df57cf827e44fda8123082612713ce8cf573bb Binary files /dev/null and celo/docs/static/images/emoji/no_mobile_phones.png differ
diff --git go-ethereum/docs/static/images/emoji/no_mouth.png celo/docs/static/images/emoji/no_mouth.png new file mode 100644 index 0000000000000000000000000000000000000000..e67802046ac81f6b5d519ce53c3f30e2dd9b1db2 Binary files /dev/null and celo/docs/static/images/emoji/no_mouth.png differ
diff --git go-ethereum/docs/static/images/emoji/no_pedestrians.png celo/docs/static/images/emoji/no_pedestrians.png new file mode 100644 index 0000000000000000000000000000000000000000..53ee0f92750197c698de6e9a66581084826e8bf8 Binary files /dev/null and celo/docs/static/images/emoji/no_pedestrians.png differ
diff --git go-ethereum/docs/static/images/emoji/no_smoking.png celo/docs/static/images/emoji/no_smoking.png new file mode 100644 index 0000000000000000000000000000000000000000..5880ddfd18897946a5da38910eb2325c90acffce Binary files /dev/null and celo/docs/static/images/emoji/no_smoking.png differ
diff --git go-ethereum/docs/static/images/emoji/non-potable_water.png celo/docs/static/images/emoji/non-potable_water.png new file mode 100644 index 0000000000000000000000000000000000000000..1b29d35b98b45e6c394c61d0a1e3f76888b658cf Binary files /dev/null and celo/docs/static/images/emoji/non-potable_water.png differ
diff --git go-ethereum/docs/static/images/emoji/nose.png celo/docs/static/images/emoji/nose.png new file mode 100644 index 0000000000000000000000000000000000000000..ad17c16c29eb60604aefac1705e4a23a542826b9 Binary files /dev/null and celo/docs/static/images/emoji/nose.png differ
diff --git go-ethereum/docs/static/images/emoji/notebook.png celo/docs/static/images/emoji/notebook.png new file mode 100644 index 0000000000000000000000000000000000000000..5f0a5f6a2550b41c7b20740d846d497f24388209 Binary files /dev/null and celo/docs/static/images/emoji/notebook.png differ
diff --git go-ethereum/docs/static/images/emoji/notebook_with_decorative_cover.png celo/docs/static/images/emoji/notebook_with_decorative_cover.png new file mode 100644 index 0000000000000000000000000000000000000000..4f3b14c85f33fd602150d5338a94c75bc8a7f338 Binary files /dev/null and celo/docs/static/images/emoji/notebook_with_decorative_cover.png differ
diff --git go-ethereum/docs/static/images/emoji/notes.png celo/docs/static/images/emoji/notes.png new file mode 100644 index 0000000000000000000000000000000000000000..0956d6ab2a83d1e8b30478102148dc22c5dab9e3 Binary files /dev/null and celo/docs/static/images/emoji/notes.png differ
diff --git go-ethereum/docs/static/images/emoji/nut_and_bolt.png celo/docs/static/images/emoji/nut_and_bolt.png new file mode 100644 index 0000000000000000000000000000000000000000..bddfa72a7d3b82384faf011ac15f95182c41af9e Binary files /dev/null and celo/docs/static/images/emoji/nut_and_bolt.png differ
diff --git go-ethereum/docs/static/images/emoji/o.png celo/docs/static/images/emoji/o.png new file mode 100644 index 0000000000000000000000000000000000000000..1ff846c1913b0ea6f09b42f3224da431b97b840b Binary files /dev/null and celo/docs/static/images/emoji/o.png differ
diff --git go-ethereum/docs/static/images/emoji/o2.png celo/docs/static/images/emoji/o2.png new file mode 100644 index 0000000000000000000000000000000000000000..d85f9fb98c7763d87db79971db65d49d39ae8806 Binary files /dev/null and celo/docs/static/images/emoji/o2.png differ
diff --git go-ethereum/docs/static/images/emoji/ocean.png celo/docs/static/images/emoji/ocean.png new file mode 100644 index 0000000000000000000000000000000000000000..f8d520cd49018adb042f879aba0466ca50843960 Binary files /dev/null and celo/docs/static/images/emoji/ocean.png differ
diff --git go-ethereum/docs/static/images/emoji/octocat.png celo/docs/static/images/emoji/octocat.png new file mode 100644 index 0000000000000000000000000000000000000000..d296f25fe067d0cab6aff8314f3c16ceb998e2e9 Binary files /dev/null and celo/docs/static/images/emoji/octocat.png differ
diff --git go-ethereum/docs/static/images/emoji/octopus.png celo/docs/static/images/emoji/octopus.png new file mode 100644 index 0000000000000000000000000000000000000000..52ce64b46879ac282918695007cbf8b3f2bb3783 Binary files /dev/null and celo/docs/static/images/emoji/octopus.png differ
diff --git go-ethereum/docs/static/images/emoji/oden.png celo/docs/static/images/emoji/oden.png new file mode 100644 index 0000000000000000000000000000000000000000..73add1c73cfa766f29e2314d4ebd61c4ca051f31 Binary files /dev/null and celo/docs/static/images/emoji/oden.png differ
diff --git go-ethereum/docs/static/images/emoji/office.png celo/docs/static/images/emoji/office.png new file mode 100644 index 0000000000000000000000000000000000000000..53c3ef8d12aa1ca8f682fc1b9da9201a6a785421 Binary files /dev/null and celo/docs/static/images/emoji/office.png differ
diff --git go-ethereum/docs/static/images/emoji/ok.png celo/docs/static/images/emoji/ok.png new file mode 100644 index 0000000000000000000000000000000000000000..6433d1a90a91d05fe34e6b9331d4d69e0302cf93 Binary files /dev/null and celo/docs/static/images/emoji/ok.png differ
diff --git go-ethereum/docs/static/images/emoji/ok_hand.png celo/docs/static/images/emoji/ok_hand.png new file mode 100644 index 0000000000000000000000000000000000000000..80c5aebb68091298f8995abba5e81e095938be25 Binary files /dev/null and celo/docs/static/images/emoji/ok_hand.png differ
diff --git go-ethereum/docs/static/images/emoji/ok_woman.png celo/docs/static/images/emoji/ok_woman.png new file mode 100644 index 0000000000000000000000000000000000000000..e8b98194edbf33071c1e0a445ba16b0243e8f65f Binary files /dev/null and celo/docs/static/images/emoji/ok_woman.png differ
diff --git go-ethereum/docs/static/images/emoji/older_man.png celo/docs/static/images/emoji/older_man.png new file mode 100644 index 0000000000000000000000000000000000000000..149f0cfb8e132a709d353d7e24130e0085371b15 Binary files /dev/null and celo/docs/static/images/emoji/older_man.png differ
diff --git go-ethereum/docs/static/images/emoji/older_woman.png celo/docs/static/images/emoji/older_woman.png new file mode 100644 index 0000000000000000000000000000000000000000..f839565f478d5889125f05b0b6e4749006be5b99 Binary files /dev/null and celo/docs/static/images/emoji/older_woman.png differ
diff --git go-ethereum/docs/static/images/emoji/on.png celo/docs/static/images/emoji/on.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd69a15dedcd265b097459c8eb9793c09f3faad Binary files /dev/null and celo/docs/static/images/emoji/on.png differ
diff --git go-ethereum/docs/static/images/emoji/oncoming_automobile.png celo/docs/static/images/emoji/oncoming_automobile.png new file mode 100644 index 0000000000000000000000000000000000000000..cb46de22cbbf544055f4fae8ff90624488420aef Binary files /dev/null and celo/docs/static/images/emoji/oncoming_automobile.png differ
diff --git go-ethereum/docs/static/images/emoji/oncoming_bus.png celo/docs/static/images/emoji/oncoming_bus.png new file mode 100644 index 0000000000000000000000000000000000000000..3695f762353f05c80466f41673a45239196cc4e8 Binary files /dev/null and celo/docs/static/images/emoji/oncoming_bus.png differ
diff --git go-ethereum/docs/static/images/emoji/oncoming_police_car.png celo/docs/static/images/emoji/oncoming_police_car.png new file mode 100644 index 0000000000000000000000000000000000000000..af20e7eff03422580d2ddd045027c626bfc7ff1a Binary files /dev/null and celo/docs/static/images/emoji/oncoming_police_car.png differ
diff --git go-ethereum/docs/static/images/emoji/oncoming_taxi.png celo/docs/static/images/emoji/oncoming_taxi.png new file mode 100644 index 0000000000000000000000000000000000000000..f78cf3103b8e3b6937a2e47430bd216880843d2e Binary files /dev/null and celo/docs/static/images/emoji/oncoming_taxi.png differ
diff --git go-ethereum/docs/static/images/emoji/one.png celo/docs/static/images/emoji/one.png new file mode 100644 index 0000000000000000000000000000000000000000..2d1f9f8c49d7698fc333bcf023ec3a0376ef367a Binary files /dev/null and celo/docs/static/images/emoji/one.png differ
diff --git go-ethereum/docs/static/images/emoji/open_file_folder.png celo/docs/static/images/emoji/open_file_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..2bbbbf5e7cd63e53726d1acd25c07eaa1a6b0803 Binary files /dev/null and celo/docs/static/images/emoji/open_file_folder.png differ
diff --git go-ethereum/docs/static/images/emoji/open_hands.png celo/docs/static/images/emoji/open_hands.png new file mode 100644 index 0000000000000000000000000000000000000000..cef9f428bc2160c51e1e78b0043d735fd11b31b1 Binary files /dev/null and celo/docs/static/images/emoji/open_hands.png differ
diff --git go-ethereum/docs/static/images/emoji/open_mouth.png celo/docs/static/images/emoji/open_mouth.png new file mode 100644 index 0000000000000000000000000000000000000000..daf914274a629ace49b611e9d8acd019f9a7e3c5 Binary files /dev/null and celo/docs/static/images/emoji/open_mouth.png differ
diff --git go-ethereum/docs/static/images/emoji/ophiuchus.png celo/docs/static/images/emoji/ophiuchus.png new file mode 100644 index 0000000000000000000000000000000000000000..4eef715bc28ef474baac0025f704e330112651ca Binary files /dev/null and celo/docs/static/images/emoji/ophiuchus.png differ
diff --git go-ethereum/docs/static/images/emoji/orange_book.png celo/docs/static/images/emoji/orange_book.png new file mode 100644 index 0000000000000000000000000000000000000000..49650d59e598a330323eb3fa90211e2689adedb0 Binary files /dev/null and celo/docs/static/images/emoji/orange_book.png differ
diff --git go-ethereum/docs/static/images/emoji/outbox_tray.png celo/docs/static/images/emoji/outbox_tray.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad15e649de070daf071f76d4f7452a66bd82e52 Binary files /dev/null and celo/docs/static/images/emoji/outbox_tray.png differ
diff --git go-ethereum/docs/static/images/emoji/ox.png celo/docs/static/images/emoji/ox.png new file mode 100644 index 0000000000000000000000000000000000000000..8d98194625448ebdbc97beceae1dcc23522f8b75 Binary files /dev/null and celo/docs/static/images/emoji/ox.png differ
diff --git go-ethereum/docs/static/images/emoji/package.png celo/docs/static/images/emoji/package.png new file mode 100644 index 0000000000000000000000000000000000000000..26602af9d81aa857e67fa0741f1649915b54f365 Binary files /dev/null and celo/docs/static/images/emoji/package.png differ
diff --git go-ethereum/docs/static/images/emoji/page_facing_up.png celo/docs/static/images/emoji/page_facing_up.png new file mode 100644 index 0000000000000000000000000000000000000000..804c0d739fa2696513b4aba55a462df41f185e7b Binary files /dev/null and celo/docs/static/images/emoji/page_facing_up.png differ
diff --git go-ethereum/docs/static/images/emoji/page_with_curl.png celo/docs/static/images/emoji/page_with_curl.png new file mode 100644 index 0000000000000000000000000000000000000000..37cb4de50c143ca92cd19bebe6dac42862c05d8e Binary files /dev/null and celo/docs/static/images/emoji/page_with_curl.png differ
diff --git go-ethereum/docs/static/images/emoji/pager.png celo/docs/static/images/emoji/pager.png new file mode 100644 index 0000000000000000000000000000000000000000..e3e1fc44ee5eac6241a6fca608f00292f3e912c4 Binary files /dev/null and celo/docs/static/images/emoji/pager.png differ
diff --git go-ethereum/docs/static/images/emoji/palm_tree.png celo/docs/static/images/emoji/palm_tree.png new file mode 100644 index 0000000000000000000000000000000000000000..d13b7c6206b29318ab9da7bab0253c86fd1ccaf6 Binary files /dev/null and celo/docs/static/images/emoji/palm_tree.png differ
diff --git go-ethereum/docs/static/images/emoji/panda_face.png celo/docs/static/images/emoji/panda_face.png new file mode 100644 index 0000000000000000000000000000000000000000..a794fb17f677b487e8d20b1237349512df3e159e Binary files /dev/null and celo/docs/static/images/emoji/panda_face.png differ
diff --git go-ethereum/docs/static/images/emoji/paperclip.png celo/docs/static/images/emoji/paperclip.png new file mode 100644 index 0000000000000000000000000000000000000000..677669a83d9e7ef06fd43f5ac84b71c290e6600f Binary files /dev/null and celo/docs/static/images/emoji/paperclip.png differ
diff --git go-ethereum/docs/static/images/emoji/parking.png celo/docs/static/images/emoji/parking.png new file mode 100644 index 0000000000000000000000000000000000000000..c24af81ccf6b07f74ae25a2a77f80cb161c46cfa Binary files /dev/null and celo/docs/static/images/emoji/parking.png differ
diff --git go-ethereum/docs/static/images/emoji/part_alternation_mark.png celo/docs/static/images/emoji/part_alternation_mark.png new file mode 100644 index 0000000000000000000000000000000000000000..1e5855f8d5b077c9f80ceac5228056036509dfd2 Binary files /dev/null and celo/docs/static/images/emoji/part_alternation_mark.png differ
diff --git go-ethereum/docs/static/images/emoji/partly_sunny.png celo/docs/static/images/emoji/partly_sunny.png new file mode 100644 index 0000000000000000000000000000000000000000..b3f5bcfdd0e3b0d7e6087a047c15ccc0372d8a9a Binary files /dev/null and celo/docs/static/images/emoji/partly_sunny.png differ
diff --git go-ethereum/docs/static/images/emoji/passport_control.png celo/docs/static/images/emoji/passport_control.png new file mode 100644 index 0000000000000000000000000000000000000000..675b76d378cd498a1c9b073ca0e999b2acefcaaa Binary files /dev/null and celo/docs/static/images/emoji/passport_control.png differ
diff --git go-ethereum/docs/static/images/emoji/paw_prints.png celo/docs/static/images/emoji/paw_prints.png new file mode 100644 index 0000000000000000000000000000000000000000..89b9fec9efac16d86556d666309b9730666f0a7d Binary files /dev/null and celo/docs/static/images/emoji/paw_prints.png differ
diff --git go-ethereum/docs/static/images/emoji/peach.png celo/docs/static/images/emoji/peach.png new file mode 100644 index 0000000000000000000000000000000000000000..ee2139ecb884f0a39fe41aee800a1b2b3b94c574 Binary files /dev/null and celo/docs/static/images/emoji/peach.png differ
diff --git go-ethereum/docs/static/images/emoji/pear.png celo/docs/static/images/emoji/pear.png new file mode 100644 index 0000000000000000000000000000000000000000..f24aca8c0a8679992d680951eba831b9740843df Binary files /dev/null and celo/docs/static/images/emoji/pear.png differ
diff --git go-ethereum/docs/static/images/emoji/pencil.png celo/docs/static/images/emoji/pencil.png new file mode 100644 index 0000000000000000000000000000000000000000..fc97ddbc92b0af4590d3d552bd771fd8d011828c Binary files /dev/null and celo/docs/static/images/emoji/pencil.png differ
diff --git go-ethereum/docs/static/images/emoji/pencil2.png celo/docs/static/images/emoji/pencil2.png new file mode 100644 index 0000000000000000000000000000000000000000..64c2d9b79b9f4d39bb799eb4fab7e7f91dc57401 Binary files /dev/null and celo/docs/static/images/emoji/pencil2.png differ
diff --git go-ethereum/docs/static/images/emoji/penguin.png celo/docs/static/images/emoji/penguin.png new file mode 100644 index 0000000000000000000000000000000000000000..d8edbcb8fa9115a6f3e8c544c3ba5050c1d9b170 Binary files /dev/null and celo/docs/static/images/emoji/penguin.png differ
diff --git go-ethereum/docs/static/images/emoji/pensive.png celo/docs/static/images/emoji/pensive.png new file mode 100644 index 0000000000000000000000000000000000000000..4159f3c42ff0511a839076185ded2cacf4bc3f49 Binary files /dev/null and celo/docs/static/images/emoji/pensive.png differ
diff --git go-ethereum/docs/static/images/emoji/performing_arts.png celo/docs/static/images/emoji/performing_arts.png new file mode 100644 index 0000000000000000000000000000000000000000..899fbe5a7919025f9874440918a519bc654b0679 Binary files /dev/null and celo/docs/static/images/emoji/performing_arts.png differ
diff --git go-ethereum/docs/static/images/emoji/persevere.png celo/docs/static/images/emoji/persevere.png new file mode 100644 index 0000000000000000000000000000000000000000..f99f6da47c18dfe21bd7344d2522d3f60b4510d8 Binary files /dev/null and celo/docs/static/images/emoji/persevere.png differ
diff --git go-ethereum/docs/static/images/emoji/person_frowning.png celo/docs/static/images/emoji/person_frowning.png new file mode 100644 index 0000000000000000000000000000000000000000..6f34d5e159df433d493c57a427ff5a23f566b796 Binary files /dev/null and celo/docs/static/images/emoji/person_frowning.png differ
diff --git go-ethereum/docs/static/images/emoji/person_with_blond_hair.png celo/docs/static/images/emoji/person_with_blond_hair.png new file mode 100644 index 0000000000000000000000000000000000000000..c144301cbb8ee5634916231c06b7e65f963e70b7 Binary files /dev/null and celo/docs/static/images/emoji/person_with_blond_hair.png differ
diff --git go-ethereum/docs/static/images/emoji/person_with_pouting_face.png celo/docs/static/images/emoji/person_with_pouting_face.png new file mode 100644 index 0000000000000000000000000000000000000000..c4a95c3b2a2b10e60c565d173eb7cee4921b859f Binary files /dev/null and celo/docs/static/images/emoji/person_with_pouting_face.png differ
diff --git go-ethereum/docs/static/images/emoji/phone.png celo/docs/static/images/emoji/phone.png new file mode 100644 index 0000000000000000000000000000000000000000..87d2559b55256935f097f9be4aea134a0800e099 Binary files /dev/null and celo/docs/static/images/emoji/phone.png differ
diff --git go-ethereum/docs/static/images/emoji/pig.png celo/docs/static/images/emoji/pig.png new file mode 100644 index 0000000000000000000000000000000000000000..f7f273c733b0255a6d3fbcbf1422c656d9326a12 Binary files /dev/null and celo/docs/static/images/emoji/pig.png differ
diff --git go-ethereum/docs/static/images/emoji/pig2.png celo/docs/static/images/emoji/pig2.png new file mode 100644 index 0000000000000000000000000000000000000000..fec3374d7096996be28e2be16931f5c3bd493291 Binary files /dev/null and celo/docs/static/images/emoji/pig2.png differ
diff --git go-ethereum/docs/static/images/emoji/pig_nose.png celo/docs/static/images/emoji/pig_nose.png new file mode 100644 index 0000000000000000000000000000000000000000..38d612446eb39e29ffd8c6312cd02a8936e360bc Binary files /dev/null and celo/docs/static/images/emoji/pig_nose.png differ
diff --git go-ethereum/docs/static/images/emoji/pill.png celo/docs/static/images/emoji/pill.png new file mode 100644 index 0000000000000000000000000000000000000000..cd84a78ff75b02b4811b09704e6eeea41f1981a5 Binary files /dev/null and celo/docs/static/images/emoji/pill.png differ
diff --git go-ethereum/docs/static/images/emoji/pineapple.png celo/docs/static/images/emoji/pineapple.png new file mode 100644 index 0000000000000000000000000000000000000000..d6f8e28769287d169cf8a8640c4af9a575039d77 Binary files /dev/null and celo/docs/static/images/emoji/pineapple.png differ
diff --git go-ethereum/docs/static/images/emoji/pisces.png celo/docs/static/images/emoji/pisces.png new file mode 100644 index 0000000000000000000000000000000000000000..6db2c3d504266c0100eeb0c68c6f0376845c79fc Binary files /dev/null and celo/docs/static/images/emoji/pisces.png differ
diff --git go-ethereum/docs/static/images/emoji/pizza.png celo/docs/static/images/emoji/pizza.png new file mode 100644 index 0000000000000000000000000000000000000000..460367d02cd70cca7f4ab509bcf4a145d7e895e6 Binary files /dev/null and celo/docs/static/images/emoji/pizza.png differ
diff --git go-ethereum/docs/static/images/emoji/plus1.png celo/docs/static/images/emoji/plus1.png new file mode 100644 index 0000000000000000000000000000000000000000..81786c1d8f5ed810fd8351f74996f9b64dbf5ffb Binary files /dev/null and celo/docs/static/images/emoji/plus1.png differ
diff --git go-ethereum/docs/static/images/emoji/point_down.png celo/docs/static/images/emoji/point_down.png new file mode 100644 index 0000000000000000000000000000000000000000..658c6d91875c8d13a3be0a9785238586133c436f Binary files /dev/null and celo/docs/static/images/emoji/point_down.png differ
diff --git go-ethereum/docs/static/images/emoji/point_left.png celo/docs/static/images/emoji/point_left.png new file mode 100644 index 0000000000000000000000000000000000000000..38a99b43f7f3fb2bae43bca0ea80abbae1485d79 Binary files /dev/null and celo/docs/static/images/emoji/point_left.png differ
diff --git go-ethereum/docs/static/images/emoji/point_right.png celo/docs/static/images/emoji/point_right.png new file mode 100644 index 0000000000000000000000000000000000000000..6f9f029a420f06dadd6f66b04801771b4f92c48f Binary files /dev/null and celo/docs/static/images/emoji/point_right.png differ
diff --git go-ethereum/docs/static/images/emoji/point_up.png celo/docs/static/images/emoji/point_up.png new file mode 100644 index 0000000000000000000000000000000000000000..01896e214aaa68dc307a2f9bea3fc3142f593494 Binary files /dev/null and celo/docs/static/images/emoji/point_up.png differ
diff --git go-ethereum/docs/static/images/emoji/point_up_2.png celo/docs/static/images/emoji/point_up_2.png new file mode 100644 index 0000000000000000000000000000000000000000..1cfe73672c0c48159a60bc287c5705f19ac4d873 Binary files /dev/null and celo/docs/static/images/emoji/point_up_2.png differ
diff --git go-ethereum/docs/static/images/emoji/police_car.png celo/docs/static/images/emoji/police_car.png new file mode 100644 index 0000000000000000000000000000000000000000..b8f17275ee16b155a5c8e1b3b6ea1c3d018051d4 Binary files /dev/null and celo/docs/static/images/emoji/police_car.png differ
diff --git go-ethereum/docs/static/images/emoji/poodle.png celo/docs/static/images/emoji/poodle.png new file mode 100644 index 0000000000000000000000000000000000000000..adac80bd97ad54de91b813874bb45bddc91cba75 Binary files /dev/null and celo/docs/static/images/emoji/poodle.png differ
diff --git go-ethereum/docs/static/images/emoji/poop.png celo/docs/static/images/emoji/poop.png new file mode 100644 index 0000000000000000000000000000000000000000..73a4dc840085c7f42c7464d827751348b58acfba Binary files /dev/null and celo/docs/static/images/emoji/poop.png differ
diff --git go-ethereum/docs/static/images/emoji/post_office.png celo/docs/static/images/emoji/post_office.png new file mode 100644 index 0000000000000000000000000000000000000000..43b59e30ec2bb655c8b5229aabe25e8afcec6a63 Binary files /dev/null and celo/docs/static/images/emoji/post_office.png differ
diff --git go-ethereum/docs/static/images/emoji/postal_horn.png celo/docs/static/images/emoji/postal_horn.png new file mode 100644 index 0000000000000000000000000000000000000000..13a151418fdc8884dd93ce07af5c68c7d1737587 Binary files /dev/null and celo/docs/static/images/emoji/postal_horn.png differ
diff --git go-ethereum/docs/static/images/emoji/postbox.png celo/docs/static/images/emoji/postbox.png new file mode 100644 index 0000000000000000000000000000000000000000..ce04b7008ba911ec11299d8e1504a6872a606b24 Binary files /dev/null and celo/docs/static/images/emoji/postbox.png differ
diff --git go-ethereum/docs/static/images/emoji/potable_water.png celo/docs/static/images/emoji/potable_water.png new file mode 100644 index 0000000000000000000000000000000000000000..e9fd56079ca59aae56a8f5ef1d86cb8479f97116 Binary files /dev/null and celo/docs/static/images/emoji/potable_water.png differ
diff --git go-ethereum/docs/static/images/emoji/pouch.png celo/docs/static/images/emoji/pouch.png new file mode 100644 index 0000000000000000000000000000000000000000..dc35ae8e5f6a200a0fa13398df8b1900184ccc9e Binary files /dev/null and celo/docs/static/images/emoji/pouch.png differ
diff --git go-ethereum/docs/static/images/emoji/poultry_leg.png celo/docs/static/images/emoji/poultry_leg.png new file mode 100644 index 0000000000000000000000000000000000000000..43ad8596518e8102620b4ed8bf6a22250b1170fe Binary files /dev/null and celo/docs/static/images/emoji/poultry_leg.png differ
diff --git go-ethereum/docs/static/images/emoji/pound.png celo/docs/static/images/emoji/pound.png new file mode 100644 index 0000000000000000000000000000000000000000..f8be91d7a4b68bdc84f4d374c16b27b6b1f8f982 Binary files /dev/null and celo/docs/static/images/emoji/pound.png differ
diff --git go-ethereum/docs/static/images/emoji/pouting_cat.png celo/docs/static/images/emoji/pouting_cat.png new file mode 100644 index 0000000000000000000000000000000000000000..4325fd48dd7e5aa0fc9be53991ee9526975c1e56 Binary files /dev/null and celo/docs/static/images/emoji/pouting_cat.png differ
diff --git go-ethereum/docs/static/images/emoji/pray.png celo/docs/static/images/emoji/pray.png new file mode 100644 index 0000000000000000000000000000000000000000..f86c992d5a7a086c52fc373803eff8d38a44eeaf Binary files /dev/null and celo/docs/static/images/emoji/pray.png differ
diff --git go-ethereum/docs/static/images/emoji/princess.png celo/docs/static/images/emoji/princess.png new file mode 100644 index 0000000000000000000000000000000000000000..1ebb2ce9b13e9ec415872e9a123d4471cd17e5bd Binary files /dev/null and celo/docs/static/images/emoji/princess.png differ
diff --git go-ethereum/docs/static/images/emoji/punch.png celo/docs/static/images/emoji/punch.png new file mode 100644 index 0000000000000000000000000000000000000000..277047b7c460cf2532412c6fbe66e172fb13e13b Binary files /dev/null and celo/docs/static/images/emoji/punch.png differ
diff --git go-ethereum/docs/static/images/emoji/purple_heart.png celo/docs/static/images/emoji/purple_heart.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f875043f00db01084fb1df635120e286bcab83 Binary files /dev/null and celo/docs/static/images/emoji/purple_heart.png differ
diff --git go-ethereum/docs/static/images/emoji/purse.png celo/docs/static/images/emoji/purse.png new file mode 100644 index 0000000000000000000000000000000000000000..8f06a2b932c55f7bf77168af46dba3c2baa3ba8e Binary files /dev/null and celo/docs/static/images/emoji/purse.png differ
diff --git go-ethereum/docs/static/images/emoji/pushpin.png celo/docs/static/images/emoji/pushpin.png new file mode 100644 index 0000000000000000000000000000000000000000..540c4ecb885c4c28c9f67a98d6bfea6d33affc01 Binary files /dev/null and celo/docs/static/images/emoji/pushpin.png differ
diff --git go-ethereum/docs/static/images/emoji/put_litter_in_its_place.png celo/docs/static/images/emoji/put_litter_in_its_place.png new file mode 100644 index 0000000000000000000000000000000000000000..c2e350c2dc6b7ce3fa277ad51b11426643c13405 Binary files /dev/null and celo/docs/static/images/emoji/put_litter_in_its_place.png differ
diff --git go-ethereum/docs/static/images/emoji/question.png celo/docs/static/images/emoji/question.png new file mode 100644 index 0000000000000000000000000000000000000000..38cedf560fb5b16ba5605f351fa5dc2cf4020158 Binary files /dev/null and celo/docs/static/images/emoji/question.png differ
diff --git go-ethereum/docs/static/images/emoji/rabbit.png celo/docs/static/images/emoji/rabbit.png new file mode 100644 index 0000000000000000000000000000000000000000..5cb3ef6f0c6a49c648661eebd66ba2b02fe6fd1f Binary files /dev/null and celo/docs/static/images/emoji/rabbit.png differ
diff --git go-ethereum/docs/static/images/emoji/rabbit2.png celo/docs/static/images/emoji/rabbit2.png new file mode 100644 index 0000000000000000000000000000000000000000..a9fd24dc14a119af78970eb3670e3a720f2c8482 Binary files /dev/null and celo/docs/static/images/emoji/rabbit2.png differ
diff --git go-ethereum/docs/static/images/emoji/racehorse.png celo/docs/static/images/emoji/racehorse.png new file mode 100644 index 0000000000000000000000000000000000000000..4d09c64de7e1036c03827a24b28d346691d460af Binary files /dev/null and celo/docs/static/images/emoji/racehorse.png differ
diff --git go-ethereum/docs/static/images/emoji/radio.png celo/docs/static/images/emoji/radio.png new file mode 100644 index 0000000000000000000000000000000000000000..ea589efe32cde0bdae7ae71d4c0c4f43a4b9f5cc Binary files /dev/null and celo/docs/static/images/emoji/radio.png differ
diff --git go-ethereum/docs/static/images/emoji/radio_button.png celo/docs/static/images/emoji/radio_button.png new file mode 100644 index 0000000000000000000000000000000000000000..63755eec258a2de526b4f1fc025646d34fe96923 Binary files /dev/null and celo/docs/static/images/emoji/radio_button.png differ
diff --git go-ethereum/docs/static/images/emoji/rage.png celo/docs/static/images/emoji/rage.png new file mode 100644 index 0000000000000000000000000000000000000000..c65ddff552a9d58e30e2463ccce112c09ed3ad89 Binary files /dev/null and celo/docs/static/images/emoji/rage.png differ
diff --git go-ethereum/docs/static/images/emoji/rage1.png celo/docs/static/images/emoji/rage1.png new file mode 100644 index 0000000000000000000000000000000000000000..1506ba4032a549e9cb95ecb0b8767be3454d40bc Binary files /dev/null and celo/docs/static/images/emoji/rage1.png differ
diff --git go-ethereum/docs/static/images/emoji/rage2.png celo/docs/static/images/emoji/rage2.png new file mode 100644 index 0000000000000000000000000000000000000000..f792e063b49c70f2e3bdf2bc3ed3ab233c205190 Binary files /dev/null and celo/docs/static/images/emoji/rage2.png differ
diff --git go-ethereum/docs/static/images/emoji/rage3.png celo/docs/static/images/emoji/rage3.png new file mode 100644 index 0000000000000000000000000000000000000000..58764cbcb3bcbfc88fab9ae50e5a47679494f79e Binary files /dev/null and celo/docs/static/images/emoji/rage3.png differ
diff --git go-ethereum/docs/static/images/emoji/rage4.png celo/docs/static/images/emoji/rage4.png new file mode 100644 index 0000000000000000000000000000000000000000..c726c94a295f57a22d7af38b53e68a31a15b0176 Binary files /dev/null and celo/docs/static/images/emoji/rage4.png differ
diff --git go-ethereum/docs/static/images/emoji/railway_car.png celo/docs/static/images/emoji/railway_car.png new file mode 100644 index 0000000000000000000000000000000000000000..22361158fb3a6749ff3bd6d33cc9fa2ff0a48cd7 Binary files /dev/null and celo/docs/static/images/emoji/railway_car.png differ
diff --git go-ethereum/docs/static/images/emoji/rainbow.png celo/docs/static/images/emoji/rainbow.png new file mode 100644 index 0000000000000000000000000000000000000000..6b1faa0379326da692fdc7bb1cdc27c82987e166 Binary files /dev/null and celo/docs/static/images/emoji/rainbow.png differ
diff --git go-ethereum/docs/static/images/emoji/raised_hand.png celo/docs/static/images/emoji/raised_hand.png new file mode 100644 index 0000000000000000000000000000000000000000..5e45c25a56c6148a8122fb762179efbbf3cd2c62 Binary files /dev/null and celo/docs/static/images/emoji/raised_hand.png differ
diff --git go-ethereum/docs/static/images/emoji/raised_hands.png celo/docs/static/images/emoji/raised_hands.png new file mode 100644 index 0000000000000000000000000000000000000000..e03142bdce92e3088e5a521f4ca6ec2b2ed33cfa Binary files /dev/null and celo/docs/static/images/emoji/raised_hands.png differ
diff --git go-ethereum/docs/static/images/emoji/raising_hand.png celo/docs/static/images/emoji/raising_hand.png new file mode 100644 index 0000000000000000000000000000000000000000..e1741a40e7431a704247b0e3d00480889113b6fe Binary files /dev/null and celo/docs/static/images/emoji/raising_hand.png differ
diff --git go-ethereum/docs/static/images/emoji/ram.png celo/docs/static/images/emoji/ram.png new file mode 100644 index 0000000000000000000000000000000000000000..5ea7bfbc0d845c6ddc1850cb1957c83b22254060 Binary files /dev/null and celo/docs/static/images/emoji/ram.png differ
diff --git go-ethereum/docs/static/images/emoji/ramen.png celo/docs/static/images/emoji/ramen.png new file mode 100644 index 0000000000000000000000000000000000000000..78dc7d537fb320a022c7dd5a53cb62b016a9acd4 Binary files /dev/null and celo/docs/static/images/emoji/ramen.png differ
diff --git go-ethereum/docs/static/images/emoji/rat.png celo/docs/static/images/emoji/rat.png new file mode 100644 index 0000000000000000000000000000000000000000..fa7dd401c51a7538832b3cecdf7311fd7c94e21d Binary files /dev/null and celo/docs/static/images/emoji/rat.png differ
diff --git go-ethereum/docs/static/images/emoji/recycle.png celo/docs/static/images/emoji/recycle.png new file mode 100644 index 0000000000000000000000000000000000000000..99104c0e9cd55f67d2099f1da6ba7f7e9eddd9b6 Binary files /dev/null and celo/docs/static/images/emoji/recycle.png differ
diff --git go-ethereum/docs/static/images/emoji/red_car.png celo/docs/static/images/emoji/red_car.png new file mode 100644 index 0000000000000000000000000000000000000000..d70a2f06263fa99a8553765a6bfbb8a9a86a5597 Binary files /dev/null and celo/docs/static/images/emoji/red_car.png differ
diff --git go-ethereum/docs/static/images/emoji/red_circle.png celo/docs/static/images/emoji/red_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..b391289b203d6a802ee488582ae05534afcbcafe Binary files /dev/null and celo/docs/static/images/emoji/red_circle.png differ
diff --git go-ethereum/docs/static/images/emoji/registered.png celo/docs/static/images/emoji/registered.png new file mode 100644 index 0000000000000000000000000000000000000000..31c68a80b08a5d203c922852dbed886da2dddf9a Binary files /dev/null and celo/docs/static/images/emoji/registered.png differ
diff --git go-ethereum/docs/static/images/emoji/relaxed.png celo/docs/static/images/emoji/relaxed.png new file mode 100644 index 0000000000000000000000000000000000000000..bbab82d3bb5a809afa49e5781e28aae2ff435eb2 Binary files /dev/null and celo/docs/static/images/emoji/relaxed.png differ
diff --git go-ethereum/docs/static/images/emoji/relieved.png celo/docs/static/images/emoji/relieved.png new file mode 100644 index 0000000000000000000000000000000000000000..fe5629f431e411f6870f9afe7662fe0b8022ecf4 Binary files /dev/null and celo/docs/static/images/emoji/relieved.png differ
diff --git go-ethereum/docs/static/images/emoji/repeat.png celo/docs/static/images/emoji/repeat.png new file mode 100644 index 0000000000000000000000000000000000000000..80113b6929b42158d5e5d7cc0cbedef74334b219 Binary files /dev/null and celo/docs/static/images/emoji/repeat.png differ
diff --git go-ethereum/docs/static/images/emoji/repeat_one.png celo/docs/static/images/emoji/repeat_one.png new file mode 100644 index 0000000000000000000000000000000000000000..3c47bcc1f3346c771a77ae3bda866be04b6c62c7 Binary files /dev/null and celo/docs/static/images/emoji/repeat_one.png differ
diff --git go-ethereum/docs/static/images/emoji/restroom.png celo/docs/static/images/emoji/restroom.png new file mode 100644 index 0000000000000000000000000000000000000000..d6c111b29b9fd50e40ab47979d38f0525d7c0aa1 Binary files /dev/null and celo/docs/static/images/emoji/restroom.png differ
diff --git go-ethereum/docs/static/images/emoji/revolving_hearts.png celo/docs/static/images/emoji/revolving_hearts.png new file mode 100644 index 0000000000000000000000000000000000000000..ea3317c47fbe90c47b91f5887eaae7d73e89b8d9 Binary files /dev/null and celo/docs/static/images/emoji/revolving_hearts.png differ
diff --git go-ethereum/docs/static/images/emoji/rewind.png celo/docs/static/images/emoji/rewind.png new file mode 100644 index 0000000000000000000000000000000000000000..26289dc3d2c2e9ed0111aed63f70a678d99c0c55 Binary files /dev/null and celo/docs/static/images/emoji/rewind.png differ
diff --git go-ethereum/docs/static/images/emoji/ribbon.png celo/docs/static/images/emoji/ribbon.png new file mode 100644 index 0000000000000000000000000000000000000000..63ee5ba5af22c884181d2a284802fa810e926b21 Binary files /dev/null and celo/docs/static/images/emoji/ribbon.png differ
diff --git go-ethereum/docs/static/images/emoji/rice.png celo/docs/static/images/emoji/rice.png new file mode 100644 index 0000000000000000000000000000000000000000..1fd22027bc5672bd967d014253192c195ccccc7f Binary files /dev/null and celo/docs/static/images/emoji/rice.png differ
diff --git go-ethereum/docs/static/images/emoji/rice_ball.png celo/docs/static/images/emoji/rice_ball.png new file mode 100644 index 0000000000000000000000000000000000000000..ade7c45d3bd9f3c445ad01c4696edc05d28a00d0 Binary files /dev/null and celo/docs/static/images/emoji/rice_ball.png differ
diff --git go-ethereum/docs/static/images/emoji/rice_cracker.png celo/docs/static/images/emoji/rice_cracker.png new file mode 100644 index 0000000000000000000000000000000000000000..954c901e935b349cf6f1eed29fb6ade58ceb7e7c Binary files /dev/null and celo/docs/static/images/emoji/rice_cracker.png differ
diff --git go-ethereum/docs/static/images/emoji/rice_scene.png celo/docs/static/images/emoji/rice_scene.png new file mode 100644 index 0000000000000000000000000000000000000000..14361988db7781e2103db212c5c21a8aec6a9669 Binary files /dev/null and celo/docs/static/images/emoji/rice_scene.png differ
diff --git go-ethereum/docs/static/images/emoji/ring.png celo/docs/static/images/emoji/ring.png new file mode 100644 index 0000000000000000000000000000000000000000..8a57fd68bac147d622d3eceb13d9812b6977cd89 Binary files /dev/null and celo/docs/static/images/emoji/ring.png differ
diff --git go-ethereum/docs/static/images/emoji/rocket.png celo/docs/static/images/emoji/rocket.png new file mode 100644 index 0000000000000000000000000000000000000000..783078d379839356e24e6346d594e33acdeadaed Binary files /dev/null and celo/docs/static/images/emoji/rocket.png differ
diff --git go-ethereum/docs/static/images/emoji/roller_coaster.png celo/docs/static/images/emoji/roller_coaster.png new file mode 100644 index 0000000000000000000000000000000000000000..9180b9861dca156d934e7444925aad60a449d7e7 Binary files /dev/null and celo/docs/static/images/emoji/roller_coaster.png differ
diff --git go-ethereum/docs/static/images/emoji/rooster.png celo/docs/static/images/emoji/rooster.png new file mode 100644 index 0000000000000000000000000000000000000000..fab23ad3625ef60563c93b96bedaf044332628d3 Binary files /dev/null and celo/docs/static/images/emoji/rooster.png differ
diff --git go-ethereum/docs/static/images/emoji/rose.png celo/docs/static/images/emoji/rose.png new file mode 100644 index 0000000000000000000000000000000000000000..3479fbcbbd4b259651bc6bc450e364daa10dfb25 Binary files /dev/null and celo/docs/static/images/emoji/rose.png differ
diff --git go-ethereum/docs/static/images/emoji/rotating_light.png celo/docs/static/images/emoji/rotating_light.png new file mode 100644 index 0000000000000000000000000000000000000000..6cf4a775e0a42a07c796d135920fa4e3f9bdbcc1 Binary files /dev/null and celo/docs/static/images/emoji/rotating_light.png differ
diff --git go-ethereum/docs/static/images/emoji/round_pushpin.png celo/docs/static/images/emoji/round_pushpin.png new file mode 100644 index 0000000000000000000000000000000000000000..e498e92cf6ad3c040ba595a914e3223a60924b25 Binary files /dev/null and celo/docs/static/images/emoji/round_pushpin.png differ
diff --git go-ethereum/docs/static/images/emoji/rowboat.png celo/docs/static/images/emoji/rowboat.png new file mode 100644 index 0000000000000000000000000000000000000000..e370d0fb1d565a3aa07a3027f760ff35edff7153 Binary files /dev/null and celo/docs/static/images/emoji/rowboat.png differ
diff --git go-ethereum/docs/static/images/emoji/ru.png celo/docs/static/images/emoji/ru.png new file mode 100644 index 0000000000000000000000000000000000000000..55fcf3549e239bad99eb3469bc6c87a6f67a7379 Binary files /dev/null and celo/docs/static/images/emoji/ru.png differ
diff --git go-ethereum/docs/static/images/emoji/rugby_football.png celo/docs/static/images/emoji/rugby_football.png new file mode 100644 index 0000000000000000000000000000000000000000..f8db67d7018102e8c6dcb6024d5764f832322c1e Binary files /dev/null and celo/docs/static/images/emoji/rugby_football.png differ
diff --git go-ethereum/docs/static/images/emoji/runner.png celo/docs/static/images/emoji/runner.png new file mode 100644 index 0000000000000000000000000000000000000000..cb00429623d9c4d867a94e58faf8d53d21d021b3 Binary files /dev/null and celo/docs/static/images/emoji/runner.png differ
diff --git go-ethereum/docs/static/images/emoji/running.png celo/docs/static/images/emoji/running.png new file mode 100644 index 0000000000000000000000000000000000000000..cb00429623d9c4d867a94e58faf8d53d21d021b3 Binary files /dev/null and celo/docs/static/images/emoji/running.png differ
diff --git go-ethereum/docs/static/images/emoji/running_shirt_with_sash.png celo/docs/static/images/emoji/running_shirt_with_sash.png new file mode 100644 index 0000000000000000000000000000000000000000..0d68bba09109711d4b3fb0d58040aac22dbcf558 Binary files /dev/null and celo/docs/static/images/emoji/running_shirt_with_sash.png differ
diff --git go-ethereum/docs/static/images/emoji/sa.png celo/docs/static/images/emoji/sa.png new file mode 100644 index 0000000000000000000000000000000000000000..387f098b99ce7b29cf335f6ba320f655cae8fd92 Binary files /dev/null and celo/docs/static/images/emoji/sa.png differ
diff --git go-ethereum/docs/static/images/emoji/sagittarius.png celo/docs/static/images/emoji/sagittarius.png new file mode 100644 index 0000000000000000000000000000000000000000..8b5435baaa9390991a35ccbb0023014319ee6e77 Binary files /dev/null and celo/docs/static/images/emoji/sagittarius.png differ
diff --git go-ethereum/docs/static/images/emoji/sailboat.png celo/docs/static/images/emoji/sailboat.png new file mode 100644 index 0000000000000000000000000000000000000000..ff656dc62bb78682ba3a1775eb4100459ac95bf5 Binary files /dev/null and celo/docs/static/images/emoji/sailboat.png differ
diff --git go-ethereum/docs/static/images/emoji/sake.png celo/docs/static/images/emoji/sake.png new file mode 100644 index 0000000000000000000000000000000000000000..1f69907e58a0256df362cd7590042c547931cc1f Binary files /dev/null and celo/docs/static/images/emoji/sake.png differ
diff --git go-ethereum/docs/static/images/emoji/sandal.png celo/docs/static/images/emoji/sandal.png new file mode 100644 index 0000000000000000000000000000000000000000..0bb3f663f5c0be51ee8e68b0b8100bba53d91fb5 Binary files /dev/null and celo/docs/static/images/emoji/sandal.png differ
diff --git go-ethereum/docs/static/images/emoji/santa.png celo/docs/static/images/emoji/santa.png new file mode 100644 index 0000000000000000000000000000000000000000..a2240c07e7af2be38a5b9d27eca65b9a5c9222c3 Binary files /dev/null and celo/docs/static/images/emoji/santa.png differ
diff --git go-ethereum/docs/static/images/emoji/satellite.png celo/docs/static/images/emoji/satellite.png new file mode 100644 index 0000000000000000000000000000000000000000..3481cc2ef4a26d49f5f750db7f61f8d7d224242e Binary files /dev/null and celo/docs/static/images/emoji/satellite.png differ
diff --git go-ethereum/docs/static/images/emoji/satisfied.png celo/docs/static/images/emoji/satisfied.png new file mode 100644 index 0000000000000000000000000000000000000000..11c91eb22e6a9ebb879071045a8d40ae9d15bce2 Binary files /dev/null and celo/docs/static/images/emoji/satisfied.png differ
diff --git go-ethereum/docs/static/images/emoji/saxophone.png celo/docs/static/images/emoji/saxophone.png new file mode 100644 index 0000000000000000000000000000000000000000..011559a7673a28f96f7fba76d66d072574faea03 Binary files /dev/null and celo/docs/static/images/emoji/saxophone.png differ
diff --git go-ethereum/docs/static/images/emoji/school.png celo/docs/static/images/emoji/school.png new file mode 100644 index 0000000000000000000000000000000000000000..afd922bf13719d861e3c2f072f74db0946755d18 Binary files /dev/null and celo/docs/static/images/emoji/school.png differ
diff --git go-ethereum/docs/static/images/emoji/school_satchel.png celo/docs/static/images/emoji/school_satchel.png new file mode 100644 index 0000000000000000000000000000000000000000..edfb19aec916ed49a693b1e4493754d1b46d2c30 Binary files /dev/null and celo/docs/static/images/emoji/school_satchel.png differ
diff --git go-ethereum/docs/static/images/emoji/scissors.png celo/docs/static/images/emoji/scissors.png new file mode 100644 index 0000000000000000000000000000000000000000..d99b8aea06a9218e4744fe4a129cdf33b0da6111 Binary files /dev/null and celo/docs/static/images/emoji/scissors.png differ
diff --git go-ethereum/docs/static/images/emoji/scorpius.png celo/docs/static/images/emoji/scorpius.png new file mode 100644 index 0000000000000000000000000000000000000000..67fcea1658acdede6fd5d1b0cb211278274fb0ea Binary files /dev/null and celo/docs/static/images/emoji/scorpius.png differ
diff --git go-ethereum/docs/static/images/emoji/scream.png celo/docs/static/images/emoji/scream.png new file mode 100644 index 0000000000000000000000000000000000000000..9e93c885dbadcd5911704300344360d52dc6acfd Binary files /dev/null and celo/docs/static/images/emoji/scream.png differ
diff --git go-ethereum/docs/static/images/emoji/scream_cat.png celo/docs/static/images/emoji/scream_cat.png new file mode 100644 index 0000000000000000000000000000000000000000..d94cd34ff5dafe1082a3e7627ac87453ca1da853 Binary files /dev/null and celo/docs/static/images/emoji/scream_cat.png differ
diff --git go-ethereum/docs/static/images/emoji/scroll.png celo/docs/static/images/emoji/scroll.png new file mode 100644 index 0000000000000000000000000000000000000000..c5a10e6b8f7efed52348a728b639238338214ae7 Binary files /dev/null and celo/docs/static/images/emoji/scroll.png differ
diff --git go-ethereum/docs/static/images/emoji/seat.png celo/docs/static/images/emoji/seat.png new file mode 100644 index 0000000000000000000000000000000000000000..d1cb864b4bf442e9e0132ff351f50ed9f0c91c31 Binary files /dev/null and celo/docs/static/images/emoji/seat.png differ
diff --git go-ethereum/docs/static/images/emoji/secret.png celo/docs/static/images/emoji/secret.png new file mode 100644 index 0000000000000000000000000000000000000000..82e383a60d1b71ea1d01e0c3afd848407faef5f9 Binary files /dev/null and celo/docs/static/images/emoji/secret.png differ
diff --git go-ethereum/docs/static/images/emoji/see_no_evil.png celo/docs/static/images/emoji/see_no_evil.png new file mode 100644 index 0000000000000000000000000000000000000000..0890a622279c4b6b379984640e7f6b15b5f011a6 Binary files /dev/null and celo/docs/static/images/emoji/see_no_evil.png differ
diff --git go-ethereum/docs/static/images/emoji/seedling.png celo/docs/static/images/emoji/seedling.png new file mode 100644 index 0000000000000000000000000000000000000000..2ab079310503fcccde5bfbcbd6e5211311af3bc9 Binary files /dev/null and celo/docs/static/images/emoji/seedling.png differ
diff --git go-ethereum/docs/static/images/emoji/seven.png celo/docs/static/images/emoji/seven.png new file mode 100644 index 0000000000000000000000000000000000000000..354e89ae75a0d10b68ae3f03cc8dc8fe52519bd7 Binary files /dev/null and celo/docs/static/images/emoji/seven.png differ
diff --git go-ethereum/docs/static/images/emoji/shaved_ice.png celo/docs/static/images/emoji/shaved_ice.png new file mode 100644 index 0000000000000000000000000000000000000000..0d0b382c22b20af966fbb889b93aa63a2e04d4cd Binary files /dev/null and celo/docs/static/images/emoji/shaved_ice.png differ
diff --git go-ethereum/docs/static/images/emoji/sheep.png celo/docs/static/images/emoji/sheep.png new file mode 100644 index 0000000000000000000000000000000000000000..c7277d2898e27ce1e6e77101b8d5988b6fc5eb78 Binary files /dev/null and celo/docs/static/images/emoji/sheep.png differ
diff --git go-ethereum/docs/static/images/emoji/shell.png celo/docs/static/images/emoji/shell.png new file mode 100644 index 0000000000000000000000000000000000000000..3145b5649631a38f82ffb6326a709334137a3fa1 Binary files /dev/null and celo/docs/static/images/emoji/shell.png differ
diff --git go-ethereum/docs/static/images/emoji/ship.png celo/docs/static/images/emoji/ship.png new file mode 100644 index 0000000000000000000000000000000000000000..5d2d8b602bb56c64f1d73d7f6b2acd8f118dd936 Binary files /dev/null and celo/docs/static/images/emoji/ship.png differ
diff --git go-ethereum/docs/static/images/emoji/shipit.png celo/docs/static/images/emoji/shipit.png new file mode 100644 index 0000000000000000000000000000000000000000..a58a47f62f9ec4d8dba2ec2ff443ab984597d9ef Binary files /dev/null and celo/docs/static/images/emoji/shipit.png differ
diff --git go-ethereum/docs/static/images/emoji/shirt.png celo/docs/static/images/emoji/shirt.png new file mode 100644 index 0000000000000000000000000000000000000000..297a6d63ed32a414f10aff5bd1e2856e606ef592 Binary files /dev/null and celo/docs/static/images/emoji/shirt.png differ
diff --git go-ethereum/docs/static/images/emoji/shit.png celo/docs/static/images/emoji/shit.png new file mode 100644 index 0000000000000000000000000000000000000000..73a4dc840085c7f42c7464d827751348b58acfba Binary files /dev/null and celo/docs/static/images/emoji/shit.png differ
diff --git go-ethereum/docs/static/images/emoji/shoe.png celo/docs/static/images/emoji/shoe.png new file mode 100644 index 0000000000000000000000000000000000000000..45b82e61cf2e0a449f8f9e1c319db6bf3864afcf Binary files /dev/null and celo/docs/static/images/emoji/shoe.png differ
diff --git go-ethereum/docs/static/images/emoji/shower.png celo/docs/static/images/emoji/shower.png new file mode 100644 index 0000000000000000000000000000000000000000..0d72ab86ba580e9ed2de91bc5e6912d5fca376c8 Binary files /dev/null and celo/docs/static/images/emoji/shower.png differ
diff --git go-ethereum/docs/static/images/emoji/signal_strength.png celo/docs/static/images/emoji/signal_strength.png new file mode 100644 index 0000000000000000000000000000000000000000..a4bd23ebf70f3ec3168425a277596edfe1efe2cd Binary files /dev/null and celo/docs/static/images/emoji/signal_strength.png differ
diff --git go-ethereum/docs/static/images/emoji/six.png celo/docs/static/images/emoji/six.png new file mode 100644 index 0000000000000000000000000000000000000000..56880556577fae986257256cc11bb2d2d323a67d Binary files /dev/null and celo/docs/static/images/emoji/six.png differ
diff --git go-ethereum/docs/static/images/emoji/six_pointed_star.png celo/docs/static/images/emoji/six_pointed_star.png new file mode 100644 index 0000000000000000000000000000000000000000..c11af14c804315cad0e8e249c750500c90597e12 Binary files /dev/null and celo/docs/static/images/emoji/six_pointed_star.png differ
diff --git go-ethereum/docs/static/images/emoji/ski.png celo/docs/static/images/emoji/ski.png new file mode 100644 index 0000000000000000000000000000000000000000..98f5cb0f4608827c08cc12568735ffe623dbeb97 Binary files /dev/null and celo/docs/static/images/emoji/ski.png differ
diff --git go-ethereum/docs/static/images/emoji/skull.png celo/docs/static/images/emoji/skull.png new file mode 100644 index 0000000000000000000000000000000000000000..bd4ee38297aeb81713d926b2a8a936b494af47bd Binary files /dev/null and celo/docs/static/images/emoji/skull.png differ
diff --git go-ethereum/docs/static/images/emoji/sleeping.png celo/docs/static/images/emoji/sleeping.png new file mode 100644 index 0000000000000000000000000000000000000000..093b852365a0fca84541f74f6a6149c5d8cba883 Binary files /dev/null and celo/docs/static/images/emoji/sleeping.png differ
diff --git go-ethereum/docs/static/images/emoji/sleepy.png celo/docs/static/images/emoji/sleepy.png new file mode 100644 index 0000000000000000000000000000000000000000..df4f55efd9a45c4b5327b6531129ebe34908bad6 Binary files /dev/null and celo/docs/static/images/emoji/sleepy.png differ
diff --git go-ethereum/docs/static/images/emoji/slot_machine.png celo/docs/static/images/emoji/slot_machine.png new file mode 100644 index 0000000000000000000000000000000000000000..26f114830b8ce5fcf5264d88e1b00e16ae5c7e6c Binary files /dev/null and celo/docs/static/images/emoji/slot_machine.png differ
diff --git go-ethereum/docs/static/images/emoji/small_blue_diamond.png celo/docs/static/images/emoji/small_blue_diamond.png new file mode 100644 index 0000000000000000000000000000000000000000..8cd49205fd06a3b237ac91f5555dda5489ce4bb8 Binary files /dev/null and celo/docs/static/images/emoji/small_blue_diamond.png differ
diff --git go-ethereum/docs/static/images/emoji/small_orange_diamond.png celo/docs/static/images/emoji/small_orange_diamond.png new file mode 100644 index 0000000000000000000000000000000000000000..04941d37b631baed5ff3af98070a5b6aded63962 Binary files /dev/null and celo/docs/static/images/emoji/small_orange_diamond.png differ
diff --git go-ethereum/docs/static/images/emoji/small_red_triangle.png celo/docs/static/images/emoji/small_red_triangle.png new file mode 100644 index 0000000000000000000000000000000000000000..8c4428da8fa57e44c82e759bb571a08c4df8d06f Binary files /dev/null and celo/docs/static/images/emoji/small_red_triangle.png differ
diff --git go-ethereum/docs/static/images/emoji/small_red_triangle_down.png celo/docs/static/images/emoji/small_red_triangle_down.png new file mode 100644 index 0000000000000000000000000000000000000000..94832f060c453e5ffa2d190cff0616f034324df9 Binary files /dev/null and celo/docs/static/images/emoji/small_red_triangle_down.png differ
diff --git go-ethereum/docs/static/images/emoji/smile.png celo/docs/static/images/emoji/smile.png new file mode 100644 index 0000000000000000000000000000000000000000..81a8396899617698e6d4459d78bed8d2b968a2a9 Binary files /dev/null and celo/docs/static/images/emoji/smile.png differ
diff --git go-ethereum/docs/static/images/emoji/smile_cat.png celo/docs/static/images/emoji/smile_cat.png new file mode 100644 index 0000000000000000000000000000000000000000..ad333ba3b6bbbbf6525061dbea98aa32733f9ab3 Binary files /dev/null and celo/docs/static/images/emoji/smile_cat.png differ
diff --git go-ethereum/docs/static/images/emoji/smiley.png celo/docs/static/images/emoji/smiley.png new file mode 100644 index 0000000000000000000000000000000000000000..77b581d68faee6e9211a690b2ab3828a2ac5f70c Binary files /dev/null and celo/docs/static/images/emoji/smiley.png differ
diff --git go-ethereum/docs/static/images/emoji/smiley_cat.png celo/docs/static/images/emoji/smiley_cat.png new file mode 100644 index 0000000000000000000000000000000000000000..dbf1b0276ab6648e9de49ba0d6ce0b5fcc7bf888 Binary files /dev/null and celo/docs/static/images/emoji/smiley_cat.png differ
diff --git go-ethereum/docs/static/images/emoji/smiling_imp.png celo/docs/static/images/emoji/smiling_imp.png new file mode 100644 index 0000000000000000000000000000000000000000..d904049309c60ff5cd4772f8aa55389847d8cad3 Binary files /dev/null and celo/docs/static/images/emoji/smiling_imp.png differ
diff --git go-ethereum/docs/static/images/emoji/smirk.png celo/docs/static/images/emoji/smirk.png new file mode 100644 index 0000000000000000000000000000000000000000..bc6e5082c8c62adbc3620c2c72c0fc28ee8ba3b9 Binary files /dev/null and celo/docs/static/images/emoji/smirk.png differ
diff --git go-ethereum/docs/static/images/emoji/smirk_cat.png celo/docs/static/images/emoji/smirk_cat.png new file mode 100644 index 0000000000000000000000000000000000000000..351565e2461bfe0bfbb826a6344f28118f821d08 Binary files /dev/null and celo/docs/static/images/emoji/smirk_cat.png differ
diff --git go-ethereum/docs/static/images/emoji/smoking.png celo/docs/static/images/emoji/smoking.png new file mode 100644 index 0000000000000000000000000000000000000000..4aad6cbd7c4064b69a83c718f7d4a891b8115404 Binary files /dev/null and celo/docs/static/images/emoji/smoking.png differ
diff --git go-ethereum/docs/static/images/emoji/snail.png celo/docs/static/images/emoji/snail.png new file mode 100644 index 0000000000000000000000000000000000000000..e75e69a84d351704e0d518b441d34a728f0a9510 Binary files /dev/null and celo/docs/static/images/emoji/snail.png differ
diff --git go-ethereum/docs/static/images/emoji/snake.png celo/docs/static/images/emoji/snake.png new file mode 100644 index 0000000000000000000000000000000000000000..ef58933e2b2f3caa55daa2d2aec81c1fcdd0292c Binary files /dev/null and celo/docs/static/images/emoji/snake.png differ
diff --git go-ethereum/docs/static/images/emoji/snowboarder.png celo/docs/static/images/emoji/snowboarder.png new file mode 100644 index 0000000000000000000000000000000000000000..aeda5c8d872734428cf6f88af5e3db3441ce3407 Binary files /dev/null and celo/docs/static/images/emoji/snowboarder.png differ
diff --git go-ethereum/docs/static/images/emoji/snowflake.png celo/docs/static/images/emoji/snowflake.png new file mode 100644 index 0000000000000000000000000000000000000000..54b68ff4f136bda881912c379c2d77837bfcc965 Binary files /dev/null and celo/docs/static/images/emoji/snowflake.png differ
diff --git go-ethereum/docs/static/images/emoji/snowman.png celo/docs/static/images/emoji/snowman.png new file mode 100644 index 0000000000000000000000000000000000000000..a97902e53043d8393eb3a928da38b3686812e2be Binary files /dev/null and celo/docs/static/images/emoji/snowman.png differ
diff --git go-ethereum/docs/static/images/emoji/sob.png celo/docs/static/images/emoji/sob.png new file mode 100644 index 0000000000000000000000000000000000000000..1561df92eee930f89483b6e5cf4377bab40649fc Binary files /dev/null and celo/docs/static/images/emoji/sob.png differ
diff --git go-ethereum/docs/static/images/emoji/soccer.png celo/docs/static/images/emoji/soccer.png new file mode 100644 index 0000000000000000000000000000000000000000..1e118b5b1849619bb65488eb3ed219e3f77fa0d3 Binary files /dev/null and celo/docs/static/images/emoji/soccer.png differ
diff --git go-ethereum/docs/static/images/emoji/soon.png celo/docs/static/images/emoji/soon.png new file mode 100644 index 0000000000000000000000000000000000000000..2cf46df254a5ea14f892e7019962ca3b3872ee84 Binary files /dev/null and celo/docs/static/images/emoji/soon.png differ
diff --git go-ethereum/docs/static/images/emoji/sos.png celo/docs/static/images/emoji/sos.png new file mode 100644 index 0000000000000000000000000000000000000000..e3e16ef73f830bb9922c37bab7c66a6179127e94 Binary files /dev/null and celo/docs/static/images/emoji/sos.png differ
diff --git go-ethereum/docs/static/images/emoji/sound.png celo/docs/static/images/emoji/sound.png new file mode 100644 index 0000000000000000000000000000000000000000..6aa4dbff4c0ac5730ba5e65fbbcc192f0e8c49ee Binary files /dev/null and celo/docs/static/images/emoji/sound.png differ
diff --git go-ethereum/docs/static/images/emoji/space_invader.png celo/docs/static/images/emoji/space_invader.png new file mode 100644 index 0000000000000000000000000000000000000000..384049167473a2c58a94e006050f44f4b92a4323 Binary files /dev/null and celo/docs/static/images/emoji/space_invader.png differ
diff --git go-ethereum/docs/static/images/emoji/spades.png celo/docs/static/images/emoji/spades.png new file mode 100644 index 0000000000000000000000000000000000000000..133a1aba8a3d58bea8956a8ad670075e508889fc Binary files /dev/null and celo/docs/static/images/emoji/spades.png differ
diff --git go-ethereum/docs/static/images/emoji/spaghetti.png celo/docs/static/images/emoji/spaghetti.png new file mode 100644 index 0000000000000000000000000000000000000000..08de243f554582f3ec082531844c5ab63504a143 Binary files /dev/null and celo/docs/static/images/emoji/spaghetti.png differ
diff --git go-ethereum/docs/static/images/emoji/sparkle.png celo/docs/static/images/emoji/sparkle.png new file mode 100644 index 0000000000000000000000000000000000000000..23a68ceb2035a3d6a50002f30cd9fb68000a7c9a Binary files /dev/null and celo/docs/static/images/emoji/sparkle.png differ
diff --git go-ethereum/docs/static/images/emoji/sparkler.png celo/docs/static/images/emoji/sparkler.png new file mode 100644 index 0000000000000000000000000000000000000000..4aabd7e0ed3b5f9b9abe0dcb3e312be35fbc9aeb Binary files /dev/null and celo/docs/static/images/emoji/sparkler.png differ
diff --git go-ethereum/docs/static/images/emoji/sparkles.png celo/docs/static/images/emoji/sparkles.png new file mode 100644 index 0000000000000000000000000000000000000000..51307bcfc220d592fa2945ae927e925570c56d86 Binary files /dev/null and celo/docs/static/images/emoji/sparkles.png differ
diff --git go-ethereum/docs/static/images/emoji/sparkling_heart.png celo/docs/static/images/emoji/sparkling_heart.png new file mode 100644 index 0000000000000000000000000000000000000000..64ac06663d01f93c7a4563fe3e162df858497f2b Binary files /dev/null and celo/docs/static/images/emoji/sparkling_heart.png differ
diff --git go-ethereum/docs/static/images/emoji/speak_no_evil.png celo/docs/static/images/emoji/speak_no_evil.png new file mode 100644 index 0000000000000000000000000000000000000000..87944c4de543adf12926af9e33a4d15f8a863998 Binary files /dev/null and celo/docs/static/images/emoji/speak_no_evil.png differ
diff --git go-ethereum/docs/static/images/emoji/speaker.png celo/docs/static/images/emoji/speaker.png new file mode 100644 index 0000000000000000000000000000000000000000..470476e182eda3a17110e7610cb32c76e2a60069 Binary files /dev/null and celo/docs/static/images/emoji/speaker.png differ
diff --git go-ethereum/docs/static/images/emoji/speech_balloon.png celo/docs/static/images/emoji/speech_balloon.png new file mode 100644 index 0000000000000000000000000000000000000000..2896c278886cd6782383f19a58ca7e477d09ac57 Binary files /dev/null and celo/docs/static/images/emoji/speech_balloon.png differ
diff --git go-ethereum/docs/static/images/emoji/speedboat.png celo/docs/static/images/emoji/speedboat.png new file mode 100644 index 0000000000000000000000000000000000000000..da6689b3be7e17acfd71ecb71a2a00e43fdedfd1 Binary files /dev/null and celo/docs/static/images/emoji/speedboat.png differ
diff --git go-ethereum/docs/static/images/emoji/squirrel.png celo/docs/static/images/emoji/squirrel.png new file mode 100644 index 0000000000000000000000000000000000000000..a58a47f62f9ec4d8dba2ec2ff443ab984597d9ef Binary files /dev/null and celo/docs/static/images/emoji/squirrel.png differ
diff --git go-ethereum/docs/static/images/emoji/star.png celo/docs/static/images/emoji/star.png new file mode 100644 index 0000000000000000000000000000000000000000..1bfddc8625535ef0c33e80f391ffffb0a7c1e3c5 Binary files /dev/null and celo/docs/static/images/emoji/star.png differ
diff --git go-ethereum/docs/static/images/emoji/star2.png celo/docs/static/images/emoji/star2.png new file mode 100644 index 0000000000000000000000000000000000000000..8b40ff4c8c8fbc0fa88a827d8deb4daaab6b7fdf Binary files /dev/null and celo/docs/static/images/emoji/star2.png differ
diff --git go-ethereum/docs/static/images/emoji/stars.png celo/docs/static/images/emoji/stars.png new file mode 100644 index 0000000000000000000000000000000000000000..097a84241c1f6761a6dba4553c3dc53cd990a8c4 Binary files /dev/null and celo/docs/static/images/emoji/stars.png differ
diff --git go-ethereum/docs/static/images/emoji/station.png celo/docs/static/images/emoji/station.png new file mode 100644 index 0000000000000000000000000000000000000000..e77daa8a75fee7c6414af812146d9c54065fd4a8 Binary files /dev/null and celo/docs/static/images/emoji/station.png differ
diff --git go-ethereum/docs/static/images/emoji/statue_of_liberty.png celo/docs/static/images/emoji/statue_of_liberty.png new file mode 100644 index 0000000000000000000000000000000000000000..9ad902806895b5340313afccecafc6083fc6fe41 Binary files /dev/null and celo/docs/static/images/emoji/statue_of_liberty.png differ
diff --git go-ethereum/docs/static/images/emoji/steam_locomotive.png celo/docs/static/images/emoji/steam_locomotive.png new file mode 100644 index 0000000000000000000000000000000000000000..5495077667beb605feaf8caf77184295448968dd Binary files /dev/null and celo/docs/static/images/emoji/steam_locomotive.png differ
diff --git go-ethereum/docs/static/images/emoji/stew.png celo/docs/static/images/emoji/stew.png new file mode 100644 index 0000000000000000000000000000000000000000..e9687f9ec26ecf5399896cd142e578741423dbc8 Binary files /dev/null and celo/docs/static/images/emoji/stew.png differ
diff --git go-ethereum/docs/static/images/emoji/straight_ruler.png celo/docs/static/images/emoji/straight_ruler.png new file mode 100644 index 0000000000000000000000000000000000000000..d96658ea1e0a054b0555f9d24a942774d1c52e14 Binary files /dev/null and celo/docs/static/images/emoji/straight_ruler.png differ
diff --git go-ethereum/docs/static/images/emoji/strawberry.png celo/docs/static/images/emoji/strawberry.png new file mode 100644 index 0000000000000000000000000000000000000000..13eb827ab870e4e2efc7af686317819dfabeea2d Binary files /dev/null and celo/docs/static/images/emoji/strawberry.png differ
diff --git go-ethereum/docs/static/images/emoji/stuck_out_tongue.png celo/docs/static/images/emoji/stuck_out_tongue.png new file mode 100644 index 0000000000000000000000000000000000000000..fa7b58e23120ca2920dcb297bb806e6434a6afdc Binary files /dev/null and celo/docs/static/images/emoji/stuck_out_tongue.png differ
diff --git go-ethereum/docs/static/images/emoji/stuck_out_tongue_closed_eyes.png celo/docs/static/images/emoji/stuck_out_tongue_closed_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..333716ee1fec3a36e0db015d73ec3c686660ec28 Binary files /dev/null and celo/docs/static/images/emoji/stuck_out_tongue_closed_eyes.png differ
diff --git go-ethereum/docs/static/images/emoji/stuck_out_tongue_winking_eye.png celo/docs/static/images/emoji/stuck_out_tongue_winking_eye.png new file mode 100644 index 0000000000000000000000000000000000000000..6ae9d497d304b776aa9db1e8423d5d2c104e3fe8 Binary files /dev/null and celo/docs/static/images/emoji/stuck_out_tongue_winking_eye.png differ
diff --git go-ethereum/docs/static/images/emoji/sun_with_face.png celo/docs/static/images/emoji/sun_with_face.png new file mode 100644 index 0000000000000000000000000000000000000000..ee276636fa405736a56a0e320d6e52bbc7d53481 Binary files /dev/null and celo/docs/static/images/emoji/sun_with_face.png differ
diff --git go-ethereum/docs/static/images/emoji/sunflower.png celo/docs/static/images/emoji/sunflower.png new file mode 100644 index 0000000000000000000000000000000000000000..d9bad194a210204ff2596feaca1da3e6308fc05e Binary files /dev/null and celo/docs/static/images/emoji/sunflower.png differ
diff --git go-ethereum/docs/static/images/emoji/sunglasses.png celo/docs/static/images/emoji/sunglasses.png new file mode 100644 index 0000000000000000000000000000000000000000..f2e524787c8d8054260ff7e1335f9bbe92929fa9 Binary files /dev/null and celo/docs/static/images/emoji/sunglasses.png differ
diff --git go-ethereum/docs/static/images/emoji/sunny.png celo/docs/static/images/emoji/sunny.png new file mode 100644 index 0000000000000000000000000000000000000000..d23c095e0804a23bc911a027530e12e86bdd7eda Binary files /dev/null and celo/docs/static/images/emoji/sunny.png differ
diff --git go-ethereum/docs/static/images/emoji/sunrise.png celo/docs/static/images/emoji/sunrise.png new file mode 100644 index 0000000000000000000000000000000000000000..ec58dcc94ffb3be7451064ca34daa35e83bd2ffc Binary files /dev/null and celo/docs/static/images/emoji/sunrise.png differ
diff --git go-ethereum/docs/static/images/emoji/sunrise_over_mountains.png celo/docs/static/images/emoji/sunrise_over_mountains.png new file mode 100644 index 0000000000000000000000000000000000000000..ebc3db146808ab1730f1c64457fa9e174f1c4417 Binary files /dev/null and celo/docs/static/images/emoji/sunrise_over_mountains.png differ
diff --git go-ethereum/docs/static/images/emoji/surfer.png celo/docs/static/images/emoji/surfer.png new file mode 100644 index 0000000000000000000000000000000000000000..b067e8cb32353197a5cd57e713f28638799bc1cc Binary files /dev/null and celo/docs/static/images/emoji/surfer.png differ
diff --git go-ethereum/docs/static/images/emoji/sushi.png celo/docs/static/images/emoji/sushi.png new file mode 100644 index 0000000000000000000000000000000000000000..0d179bd975689446130a87bec6a2ff0da0089fed Binary files /dev/null and celo/docs/static/images/emoji/sushi.png differ
diff --git go-ethereum/docs/static/images/emoji/suspect.png celo/docs/static/images/emoji/suspect.png new file mode 100644 index 0000000000000000000000000000000000000000..58e8921c0a72566c7ef109e5c3619b623f06bf28 Binary files /dev/null and celo/docs/static/images/emoji/suspect.png differ
diff --git go-ethereum/docs/static/images/emoji/suspension_railway.png celo/docs/static/images/emoji/suspension_railway.png new file mode 100644 index 0000000000000000000000000000000000000000..aaa45f61f1fddd0d26351dd35be7ec5ac20c9578 Binary files /dev/null and celo/docs/static/images/emoji/suspension_railway.png differ
diff --git go-ethereum/docs/static/images/emoji/sweat.png celo/docs/static/images/emoji/sweat.png new file mode 100644 index 0000000000000000000000000000000000000000..e894b7699606b17f68ec6059fc697c31416d8b83 Binary files /dev/null and celo/docs/static/images/emoji/sweat.png differ
diff --git go-ethereum/docs/static/images/emoji/sweat_drops.png celo/docs/static/images/emoji/sweat_drops.png new file mode 100644 index 0000000000000000000000000000000000000000..a83b3e960cdbaf111579370c614536f8e96460c2 Binary files /dev/null and celo/docs/static/images/emoji/sweat_drops.png differ
diff --git go-ethereum/docs/static/images/emoji/sweat_smile.png celo/docs/static/images/emoji/sweat_smile.png new file mode 100644 index 0000000000000000000000000000000000000000..3903f717f31eb58d5878cb89c16a810ec4cff914 Binary files /dev/null and celo/docs/static/images/emoji/sweat_smile.png differ
diff --git go-ethereum/docs/static/images/emoji/sweet_potato.png celo/docs/static/images/emoji/sweet_potato.png new file mode 100644 index 0000000000000000000000000000000000000000..cde7880a14c198550e3ba6d9f5b95f871b5ab92f Binary files /dev/null and celo/docs/static/images/emoji/sweet_potato.png differ
diff --git go-ethereum/docs/static/images/emoji/swimmer.png celo/docs/static/images/emoji/swimmer.png new file mode 100644 index 0000000000000000000000000000000000000000..d3878a0652559d1d1341222401a97ae58bffca1d Binary files /dev/null and celo/docs/static/images/emoji/swimmer.png differ
diff --git go-ethereum/docs/static/images/emoji/symbols.png celo/docs/static/images/emoji/symbols.png new file mode 100644 index 0000000000000000000000000000000000000000..16bc1da921f9a9a7d784197ac11a541b63ef32ea Binary files /dev/null and celo/docs/static/images/emoji/symbols.png differ
diff --git go-ethereum/docs/static/images/emoji/syringe.png celo/docs/static/images/emoji/syringe.png new file mode 100644 index 0000000000000000000000000000000000000000..36aa8fed54b2f09906688db16c091d298baa481e Binary files /dev/null and celo/docs/static/images/emoji/syringe.png differ
diff --git go-ethereum/docs/static/images/emoji/tada.png celo/docs/static/images/emoji/tada.png new file mode 100644 index 0000000000000000000000000000000000000000..7411b5266a03ca75ec27e5c309fad815d99f3512 Binary files /dev/null and celo/docs/static/images/emoji/tada.png differ
diff --git go-ethereum/docs/static/images/emoji/tanabata_tree.png celo/docs/static/images/emoji/tanabata_tree.png new file mode 100644 index 0000000000000000000000000000000000000000..6dea4b2d42f34389d18f85b593d96afaf0ad26b2 Binary files /dev/null and celo/docs/static/images/emoji/tanabata_tree.png differ
diff --git go-ethereum/docs/static/images/emoji/tangerine.png celo/docs/static/images/emoji/tangerine.png new file mode 100644 index 0000000000000000000000000000000000000000..fc9d4f82ad9a116cabf5dbe690272a01ae62cdd7 Binary files /dev/null and celo/docs/static/images/emoji/tangerine.png differ
diff --git go-ethereum/docs/static/images/emoji/taurus.png celo/docs/static/images/emoji/taurus.png new file mode 100644 index 0000000000000000000000000000000000000000..6af582f69d27a33c8015e0b4a8d682fdf2a6f8b6 Binary files /dev/null and celo/docs/static/images/emoji/taurus.png differ
diff --git go-ethereum/docs/static/images/emoji/taxi.png celo/docs/static/images/emoji/taxi.png new file mode 100644 index 0000000000000000000000000000000000000000..60a50d365a41e99778338bc28198b0a654f69aa5 Binary files /dev/null and celo/docs/static/images/emoji/taxi.png differ
diff --git go-ethereum/docs/static/images/emoji/tea.png celo/docs/static/images/emoji/tea.png new file mode 100644 index 0000000000000000000000000000000000000000..3ece0b708af19a9d9b16ae5228ff5a7cf5325f6f Binary files /dev/null and celo/docs/static/images/emoji/tea.png differ
diff --git go-ethereum/docs/static/images/emoji/telephone.png celo/docs/static/images/emoji/telephone.png new file mode 100644 index 0000000000000000000000000000000000000000..87d2559b55256935f097f9be4aea134a0800e099 Binary files /dev/null and celo/docs/static/images/emoji/telephone.png differ
diff --git go-ethereum/docs/static/images/emoji/telephone_receiver.png celo/docs/static/images/emoji/telephone_receiver.png new file mode 100644 index 0000000000000000000000000000000000000000..36e21e0123df2541043ae49139870a976769453a Binary files /dev/null and celo/docs/static/images/emoji/telephone_receiver.png differ
diff --git go-ethereum/docs/static/images/emoji/telescope.png celo/docs/static/images/emoji/telescope.png new file mode 100644 index 0000000000000000000000000000000000000000..98e57558acbf33bf578b7cf8be32b3a078db3a00 Binary files /dev/null and celo/docs/static/images/emoji/telescope.png differ
diff --git go-ethereum/docs/static/images/emoji/tennis.png celo/docs/static/images/emoji/tennis.png new file mode 100644 index 0000000000000000000000000000000000000000..278d904ee20101322293f94ebb941480210ce2c3 Binary files /dev/null and celo/docs/static/images/emoji/tennis.png differ
diff --git go-ethereum/docs/static/images/emoji/tent.png celo/docs/static/images/emoji/tent.png new file mode 100644 index 0000000000000000000000000000000000000000..5c0d20e48b6ec00123333e91654bc104b0c43312 Binary files /dev/null and celo/docs/static/images/emoji/tent.png differ
diff --git go-ethereum/docs/static/images/emoji/thought_balloon.png celo/docs/static/images/emoji/thought_balloon.png new file mode 100644 index 0000000000000000000000000000000000000000..febe30d05901c572e6d022242f0ff908a775284e Binary files /dev/null and celo/docs/static/images/emoji/thought_balloon.png differ
diff --git go-ethereum/docs/static/images/emoji/three.png celo/docs/static/images/emoji/three.png new file mode 100644 index 0000000000000000000000000000000000000000..55644c9900c5a61a86309ec7a3764896891faf82 Binary files /dev/null and celo/docs/static/images/emoji/three.png differ
diff --git go-ethereum/docs/static/images/emoji/thumbsdown.png celo/docs/static/images/emoji/thumbsdown.png new file mode 100644 index 0000000000000000000000000000000000000000..41c6b825d6a6abf736a1b882c951c1eb942afabf Binary files /dev/null and celo/docs/static/images/emoji/thumbsdown.png differ
diff --git go-ethereum/docs/static/images/emoji/thumbsup.png celo/docs/static/images/emoji/thumbsup.png new file mode 100644 index 0000000000000000000000000000000000000000..81786c1d8f5ed810fd8351f74996f9b64dbf5ffb Binary files /dev/null and celo/docs/static/images/emoji/thumbsup.png differ
diff --git go-ethereum/docs/static/images/emoji/ticket.png celo/docs/static/images/emoji/ticket.png new file mode 100644 index 0000000000000000000000000000000000000000..cdacf1a70be4f6952922b7e07755072582f711ea Binary files /dev/null and celo/docs/static/images/emoji/ticket.png differ
diff --git go-ethereum/docs/static/images/emoji/tiger.png celo/docs/static/images/emoji/tiger.png new file mode 100644 index 0000000000000000000000000000000000000000..d6cc84a3ba980695dd5e2430a174572d8ea4c955 Binary files /dev/null and celo/docs/static/images/emoji/tiger.png differ
diff --git go-ethereum/docs/static/images/emoji/tiger2.png celo/docs/static/images/emoji/tiger2.png new file mode 100644 index 0000000000000000000000000000000000000000..b0c7d8dc3ec2326be0b6bd349ce4bf91d06980ec Binary files /dev/null and celo/docs/static/images/emoji/tiger2.png differ
diff --git go-ethereum/docs/static/images/emoji/tired_face.png celo/docs/static/images/emoji/tired_face.png new file mode 100644 index 0000000000000000000000000000000000000000..77b783453e9c22fdc12016ee124b4067583b5645 Binary files /dev/null and celo/docs/static/images/emoji/tired_face.png differ
diff --git go-ethereum/docs/static/images/emoji/tm.png celo/docs/static/images/emoji/tm.png new file mode 100644 index 0000000000000000000000000000000000000000..c7dec75a33adb03d4710789c4cfa0daa36e53843 Binary files /dev/null and celo/docs/static/images/emoji/tm.png differ
diff --git go-ethereum/docs/static/images/emoji/toilet.png celo/docs/static/images/emoji/toilet.png new file mode 100644 index 0000000000000000000000000000000000000000..e5cc4119a15d3346474b666ed1fd1bb8f3de7e79 Binary files /dev/null and celo/docs/static/images/emoji/toilet.png differ
diff --git go-ethereum/docs/static/images/emoji/tokyo_tower.png celo/docs/static/images/emoji/tokyo_tower.png new file mode 100644 index 0000000000000000000000000000000000000000..e1cbd7a3c5d7e8d7474bed03b17c762cf714a8f7 Binary files /dev/null and celo/docs/static/images/emoji/tokyo_tower.png differ
diff --git go-ethereum/docs/static/images/emoji/tomato.png celo/docs/static/images/emoji/tomato.png new file mode 100644 index 0000000000000000000000000000000000000000..a129700bbb5911fccd94b7d1546445d6dc1df6c3 Binary files /dev/null and celo/docs/static/images/emoji/tomato.png differ
diff --git go-ethereum/docs/static/images/emoji/tongue.png celo/docs/static/images/emoji/tongue.png new file mode 100644 index 0000000000000000000000000000000000000000..b0bab12078fb12f9f1d2425cd4419c060c87ad7c Binary files /dev/null and celo/docs/static/images/emoji/tongue.png differ
diff --git go-ethereum/docs/static/images/emoji/top.png celo/docs/static/images/emoji/top.png new file mode 100644 index 0000000000000000000000000000000000000000..5aa4dd442da574464fbde3f5978730e4e9cad5f1 Binary files /dev/null and celo/docs/static/images/emoji/top.png differ
diff --git go-ethereum/docs/static/images/emoji/tophat.png celo/docs/static/images/emoji/tophat.png new file mode 100644 index 0000000000000000000000000000000000000000..7d27134d6a5d7c6b164712ee3e08af3ec1a11c5e Binary files /dev/null and celo/docs/static/images/emoji/tophat.png differ
diff --git go-ethereum/docs/static/images/emoji/tractor.png celo/docs/static/images/emoji/tractor.png new file mode 100644 index 0000000000000000000000000000000000000000..058fd3eda551a9a80eea4b9ada7c9444a2794958 Binary files /dev/null and celo/docs/static/images/emoji/tractor.png differ
diff --git go-ethereum/docs/static/images/emoji/traffic_light.png celo/docs/static/images/emoji/traffic_light.png new file mode 100644 index 0000000000000000000000000000000000000000..1facb276c4206323b6f582f89acaf44a1997e0ce Binary files /dev/null and celo/docs/static/images/emoji/traffic_light.png differ
diff --git go-ethereum/docs/static/images/emoji/train.png celo/docs/static/images/emoji/train.png new file mode 100644 index 0000000000000000000000000000000000000000..3202d80ea9f438edffcb382df6a4a1f34a59c2f6 Binary files /dev/null and celo/docs/static/images/emoji/train.png differ
diff --git go-ethereum/docs/static/images/emoji/train2.png celo/docs/static/images/emoji/train2.png new file mode 100644 index 0000000000000000000000000000000000000000..9c0d3ab640757428e2840ae089f84fe1dea4e4ef Binary files /dev/null and celo/docs/static/images/emoji/train2.png differ
diff --git go-ethereum/docs/static/images/emoji/tram.png celo/docs/static/images/emoji/tram.png new file mode 100644 index 0000000000000000000000000000000000000000..5eb29fb71cdcc8cf8b8b7f2f6d40003c7846d229 Binary files /dev/null and celo/docs/static/images/emoji/tram.png differ
diff --git go-ethereum/docs/static/images/emoji/triangular_flag_on_post.png celo/docs/static/images/emoji/triangular_flag_on_post.png new file mode 100644 index 0000000000000000000000000000000000000000..f9a3f32d7118988613c63f944691f0f2ae39b88a Binary files /dev/null and celo/docs/static/images/emoji/triangular_flag_on_post.png differ
diff --git go-ethereum/docs/static/images/emoji/triangular_ruler.png celo/docs/static/images/emoji/triangular_ruler.png new file mode 100644 index 0000000000000000000000000000000000000000..383677cb74cb96f089804d8f488127707ccfb7e9 Binary files /dev/null and celo/docs/static/images/emoji/triangular_ruler.png differ
diff --git go-ethereum/docs/static/images/emoji/trident.png celo/docs/static/images/emoji/trident.png new file mode 100644 index 0000000000000000000000000000000000000000..d79a7b4cce5aceb3efb24c0bde909646f3b5b140 Binary files /dev/null and celo/docs/static/images/emoji/trident.png differ
diff --git go-ethereum/docs/static/images/emoji/triumph.png celo/docs/static/images/emoji/triumph.png new file mode 100644 index 0000000000000000000000000000000000000000..92f93bd1025035241022d89eb701bf7920a1a636 Binary files /dev/null and celo/docs/static/images/emoji/triumph.png differ
diff --git go-ethereum/docs/static/images/emoji/trolleybus.png celo/docs/static/images/emoji/trolleybus.png new file mode 100644 index 0000000000000000000000000000000000000000..b9740a53f87a245eca78d521c75ab53d00ca4608 Binary files /dev/null and celo/docs/static/images/emoji/trolleybus.png differ
diff --git go-ethereum/docs/static/images/emoji/trollface.png celo/docs/static/images/emoji/trollface.png new file mode 100644 index 0000000000000000000000000000000000000000..119d77e73a1fc9894adfe0600e341c936d619e4b Binary files /dev/null and celo/docs/static/images/emoji/trollface.png differ
diff --git go-ethereum/docs/static/images/emoji/trophy.png celo/docs/static/images/emoji/trophy.png new file mode 100644 index 0000000000000000000000000000000000000000..95d3b63f524646adc51e00c3a6ca85b8a98e681f Binary files /dev/null and celo/docs/static/images/emoji/trophy.png differ
diff --git go-ethereum/docs/static/images/emoji/tropical_drink.png celo/docs/static/images/emoji/tropical_drink.png new file mode 100644 index 0000000000000000000000000000000000000000..55ca9eeda75fd4e8ae6b010af6257b269497e708 Binary files /dev/null and celo/docs/static/images/emoji/tropical_drink.png differ
diff --git go-ethereum/docs/static/images/emoji/tropical_fish.png celo/docs/static/images/emoji/tropical_fish.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d734987bb33df8f0ff5ed6a7952987d69cec12 Binary files /dev/null and celo/docs/static/images/emoji/tropical_fish.png differ
diff --git go-ethereum/docs/static/images/emoji/truck.png celo/docs/static/images/emoji/truck.png new file mode 100644 index 0000000000000000000000000000000000000000..3f25ba1f92a16371f50ca9b38221ce00059fd531 Binary files /dev/null and celo/docs/static/images/emoji/truck.png differ
diff --git go-ethereum/docs/static/images/emoji/trumpet.png celo/docs/static/images/emoji/trumpet.png new file mode 100644 index 0000000000000000000000000000000000000000..c84cfb13e10ef152b05978b6b14debd15cb4cc26 Binary files /dev/null and celo/docs/static/images/emoji/trumpet.png differ
diff --git go-ethereum/docs/static/images/emoji/tshirt.png celo/docs/static/images/emoji/tshirt.png new file mode 100644 index 0000000000000000000000000000000000000000..297a6d63ed32a414f10aff5bd1e2856e606ef592 Binary files /dev/null and celo/docs/static/images/emoji/tshirt.png differ
diff --git go-ethereum/docs/static/images/emoji/tulip.png celo/docs/static/images/emoji/tulip.png new file mode 100644 index 0000000000000000000000000000000000000000..b3ee1102a538d1fd1be9764c86b4c49f7327caf0 Binary files /dev/null and celo/docs/static/images/emoji/tulip.png differ
diff --git go-ethereum/docs/static/images/emoji/turtle.png celo/docs/static/images/emoji/turtle.png new file mode 100644 index 0000000000000000000000000000000000000000..04d1d9684709721989f9e0a4ae81c7f6ff4f9a62 Binary files /dev/null and celo/docs/static/images/emoji/turtle.png differ
diff --git go-ethereum/docs/static/images/emoji/tv.png celo/docs/static/images/emoji/tv.png new file mode 100644 index 0000000000000000000000000000000000000000..803dc3d412fcc0053b2d14798f17b29f5ac7ee53 Binary files /dev/null and celo/docs/static/images/emoji/tv.png differ
diff --git go-ethereum/docs/static/images/emoji/twisted_rightwards_arrows.png celo/docs/static/images/emoji/twisted_rightwards_arrows.png new file mode 100644 index 0000000000000000000000000000000000000000..25cde18b250190797ff713f1dc3047d60e74712d Binary files /dev/null and celo/docs/static/images/emoji/twisted_rightwards_arrows.png differ
diff --git go-ethereum/docs/static/images/emoji/two.png celo/docs/static/images/emoji/two.png new file mode 100644 index 0000000000000000000000000000000000000000..c191f8a3221b516b2e739a23c69a65fe1a261aaf Binary files /dev/null and celo/docs/static/images/emoji/two.png differ
diff --git go-ethereum/docs/static/images/emoji/two_hearts.png celo/docs/static/images/emoji/two_hearts.png new file mode 100644 index 0000000000000000000000000000000000000000..b189e9aea82ee62a9b483f480267b4ff2cab7bcb Binary files /dev/null and celo/docs/static/images/emoji/two_hearts.png differ
diff --git go-ethereum/docs/static/images/emoji/two_men_holding_hands.png celo/docs/static/images/emoji/two_men_holding_hands.png new file mode 100644 index 0000000000000000000000000000000000000000..d1099f21ffe1046dbf70e62f101764cba29d35ac Binary files /dev/null and celo/docs/static/images/emoji/two_men_holding_hands.png differ
diff --git go-ethereum/docs/static/images/emoji/two_women_holding_hands.png celo/docs/static/images/emoji/two_women_holding_hands.png new file mode 100644 index 0000000000000000000000000000000000000000..619646c4e02aa3887877851c1e462485a28e37b9 Binary files /dev/null and celo/docs/static/images/emoji/two_women_holding_hands.png differ
diff --git go-ethereum/docs/static/images/emoji/u5272.png celo/docs/static/images/emoji/u5272.png new file mode 100644 index 0000000000000000000000000000000000000000..2148253fc1045a53593347a7d79c6fbd23178a1f Binary files /dev/null and celo/docs/static/images/emoji/u5272.png differ
diff --git go-ethereum/docs/static/images/emoji/u5408.png celo/docs/static/images/emoji/u5408.png new file mode 100644 index 0000000000000000000000000000000000000000..03ab0d8746ef374e63946de5dc323adeefc8baad Binary files /dev/null and celo/docs/static/images/emoji/u5408.png differ
diff --git go-ethereum/docs/static/images/emoji/u55b6.png celo/docs/static/images/emoji/u55b6.png new file mode 100644 index 0000000000000000000000000000000000000000..ba946d3f339a999b72af3424b099370f01b14b8f Binary files /dev/null and celo/docs/static/images/emoji/u55b6.png differ
diff --git go-ethereum/docs/static/images/emoji/u6307.png celo/docs/static/images/emoji/u6307.png new file mode 100644 index 0000000000000000000000000000000000000000..6557f5672fb8cd1be9303787f7c00eddf0cd6b83 Binary files /dev/null and celo/docs/static/images/emoji/u6307.png differ
diff --git go-ethereum/docs/static/images/emoji/u6708.png celo/docs/static/images/emoji/u6708.png new file mode 100644 index 0000000000000000000000000000000000000000..e4dfe5aa7626af6907ffb8821282c33f354183d2 Binary files /dev/null and celo/docs/static/images/emoji/u6708.png differ
diff --git go-ethereum/docs/static/images/emoji/u6709.png celo/docs/static/images/emoji/u6709.png new file mode 100644 index 0000000000000000000000000000000000000000..cd8fb3f62a59cbf5b9ca3d5d718b6502f0331bc4 Binary files /dev/null and celo/docs/static/images/emoji/u6709.png differ
diff --git go-ethereum/docs/static/images/emoji/u6e80.png celo/docs/static/images/emoji/u6e80.png new file mode 100644 index 0000000000000000000000000000000000000000..5df1cb878f70303b0d295c10a74a92464940102a Binary files /dev/null and celo/docs/static/images/emoji/u6e80.png differ
diff --git go-ethereum/docs/static/images/emoji/u7121.png celo/docs/static/images/emoji/u7121.png new file mode 100644 index 0000000000000000000000000000000000000000..25f694ed3ff0b8865f7fc69e5115d8e5f9488997 Binary files /dev/null and celo/docs/static/images/emoji/u7121.png differ
diff --git go-ethereum/docs/static/images/emoji/u7533.png celo/docs/static/images/emoji/u7533.png new file mode 100644 index 0000000000000000000000000000000000000000..fc4a9901b461cf3f8a95d3ea2b775885632f7923 Binary files /dev/null and celo/docs/static/images/emoji/u7533.png differ
diff --git go-ethereum/docs/static/images/emoji/u7981.png celo/docs/static/images/emoji/u7981.png new file mode 100644 index 0000000000000000000000000000000000000000..f550a573da73ab7362b322ca22942516a2bc36d6 Binary files /dev/null and celo/docs/static/images/emoji/u7981.png differ
diff --git go-ethereum/docs/static/images/emoji/u7a7a.png celo/docs/static/images/emoji/u7a7a.png new file mode 100644 index 0000000000000000000000000000000000000000..c05f5cff73ba4cce11c158826a30839149c8e50c Binary files /dev/null and celo/docs/static/images/emoji/u7a7a.png differ
diff --git go-ethereum/docs/static/images/emoji/uk.png celo/docs/static/images/emoji/uk.png new file mode 100644 index 0000000000000000000000000000000000000000..2a62c7a0810a8b4f3d06279330e0308bf2a78b68 Binary files /dev/null and celo/docs/static/images/emoji/uk.png differ
diff --git go-ethereum/docs/static/images/emoji/umbrella.png celo/docs/static/images/emoji/umbrella.png new file mode 100644 index 0000000000000000000000000000000000000000..1db722fa661ea4e74fca2dc29e7972716843d812 Binary files /dev/null and celo/docs/static/images/emoji/umbrella.png differ
diff --git go-ethereum/docs/static/images/emoji/unamused.png celo/docs/static/images/emoji/unamused.png new file mode 100644 index 0000000000000000000000000000000000000000..3722e6f57538db37176290abf5920c6c4949e448 Binary files /dev/null and celo/docs/static/images/emoji/unamused.png differ
diff --git go-ethereum/docs/static/images/emoji/underage.png celo/docs/static/images/emoji/underage.png new file mode 100644 index 0000000000000000000000000000000000000000..a789b3c6200cc2ef95766ed1404f4239a88eba03 Binary files /dev/null and celo/docs/static/images/emoji/underage.png differ
diff --git go-ethereum/docs/static/images/emoji/unlock.png celo/docs/static/images/emoji/unlock.png new file mode 100644 index 0000000000000000000000000000000000000000..22b429cd0219e50affa739049b7d30e661486b4d Binary files /dev/null and celo/docs/static/images/emoji/unlock.png differ
diff --git go-ethereum/docs/static/images/emoji/up.png celo/docs/static/images/emoji/up.png new file mode 100644 index 0000000000000000000000000000000000000000..829219a868adf6e28def3d4bf6c7e131e97a6e52 Binary files /dev/null and celo/docs/static/images/emoji/up.png differ
diff --git go-ethereum/docs/static/images/emoji/us.png celo/docs/static/images/emoji/us.png new file mode 100644 index 0000000000000000000000000000000000000000..38137669aa9c889c10740b42f2dd628c2e46fe39 Binary files /dev/null and celo/docs/static/images/emoji/us.png differ
diff --git go-ethereum/docs/static/images/emoji/v.png celo/docs/static/images/emoji/v.png new file mode 100644 index 0000000000000000000000000000000000000000..f61267c281ded65c918ef42bc2c98042315f5c39 Binary files /dev/null and celo/docs/static/images/emoji/v.png differ
diff --git go-ethereum/docs/static/images/emoji/vertical_traffic_light.png celo/docs/static/images/emoji/vertical_traffic_light.png new file mode 100644 index 0000000000000000000000000000000000000000..7a5ba35f09d9a0f80fb46ee148d996f78ea1c953 Binary files /dev/null and celo/docs/static/images/emoji/vertical_traffic_light.png differ
diff --git go-ethereum/docs/static/images/emoji/vhs.png celo/docs/static/images/emoji/vhs.png new file mode 100644 index 0000000000000000000000000000000000000000..881081c17784ead2520ea5214664997afec38702 Binary files /dev/null and celo/docs/static/images/emoji/vhs.png differ
diff --git go-ethereum/docs/static/images/emoji/vibration_mode.png celo/docs/static/images/emoji/vibration_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..a716e96c6353cfcc026eaaf8517f207122c2cb06 Binary files /dev/null and celo/docs/static/images/emoji/vibration_mode.png differ
diff --git go-ethereum/docs/static/images/emoji/video_camera.png celo/docs/static/images/emoji/video_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..274cecdd6d4d1415fa251d88b6c206e0335a3320 Binary files /dev/null and celo/docs/static/images/emoji/video_camera.png differ
diff --git go-ethereum/docs/static/images/emoji/video_game.png celo/docs/static/images/emoji/video_game.png new file mode 100644 index 0000000000000000000000000000000000000000..e265a3bd8cc5a0fae4063352bf7a122752edce1c Binary files /dev/null and celo/docs/static/images/emoji/video_game.png differ
diff --git go-ethereum/docs/static/images/emoji/violin.png celo/docs/static/images/emoji/violin.png new file mode 100644 index 0000000000000000000000000000000000000000..69347b545862da2031ae2241679081a93d5402f6 Binary files /dev/null and celo/docs/static/images/emoji/violin.png differ
diff --git go-ethereum/docs/static/images/emoji/virgo.png celo/docs/static/images/emoji/virgo.png new file mode 100644 index 0000000000000000000000000000000000000000..72e1763f5739ada7bce3cc5519d0b4760e7f2875 Binary files /dev/null and celo/docs/static/images/emoji/virgo.png differ
diff --git go-ethereum/docs/static/images/emoji/volcano.png celo/docs/static/images/emoji/volcano.png new file mode 100644 index 0000000000000000000000000000000000000000..9b434539b05655036d465500e296ccce84711ffe Binary files /dev/null and celo/docs/static/images/emoji/volcano.png differ
diff --git go-ethereum/docs/static/images/emoji/vs.png celo/docs/static/images/emoji/vs.png new file mode 100644 index 0000000000000000000000000000000000000000..863638850e1e30e39cf82aa8cf9500e70468ec7f Binary files /dev/null and celo/docs/static/images/emoji/vs.png differ
diff --git go-ethereum/docs/static/images/emoji/walking.png celo/docs/static/images/emoji/walking.png new file mode 100644 index 0000000000000000000000000000000000000000..52bc0381c72c99ac4fc7bb01ee796d5737488a4b Binary files /dev/null and celo/docs/static/images/emoji/walking.png differ
diff --git go-ethereum/docs/static/images/emoji/waning_crescent_moon.png celo/docs/static/images/emoji/waning_crescent_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..30387780fec8cd386766df2abe350c8afe3de37f Binary files /dev/null and celo/docs/static/images/emoji/waning_crescent_moon.png differ
diff --git go-ethereum/docs/static/images/emoji/waning_gibbous_moon.png celo/docs/static/images/emoji/waning_gibbous_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..510099070700a9f1faf4e75e09e9c21aad199beb Binary files /dev/null and celo/docs/static/images/emoji/waning_gibbous_moon.png differ
diff --git go-ethereum/docs/static/images/emoji/warning.png celo/docs/static/images/emoji/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..db6f96f02bc38785fd73cda8c1fbf5c3b60085f0 Binary files /dev/null and celo/docs/static/images/emoji/warning.png differ
diff --git go-ethereum/docs/static/images/emoji/watch.png celo/docs/static/images/emoji/watch.png new file mode 100644 index 0000000000000000000000000000000000000000..d503bb87c22990dc8fc76bf47343a6602a6a8e44 Binary files /dev/null and celo/docs/static/images/emoji/watch.png differ
diff --git go-ethereum/docs/static/images/emoji/water_buffalo.png celo/docs/static/images/emoji/water_buffalo.png new file mode 100644 index 0000000000000000000000000000000000000000..3bcde3edd9536e4c4e298daec98e18f0f244b479 Binary files /dev/null and celo/docs/static/images/emoji/water_buffalo.png differ
diff --git go-ethereum/docs/static/images/emoji/watermelon.png celo/docs/static/images/emoji/watermelon.png new file mode 100644 index 0000000000000000000000000000000000000000..fc212be784482c07bb3c49168c48784d6ef9e046 Binary files /dev/null and celo/docs/static/images/emoji/watermelon.png differ
diff --git go-ethereum/docs/static/images/emoji/wave.png celo/docs/static/images/emoji/wave.png new file mode 100644 index 0000000000000000000000000000000000000000..56e6e822b124ee8f78ceec940dfa256e231338de Binary files /dev/null and celo/docs/static/images/emoji/wave.png differ
diff --git go-ethereum/docs/static/images/emoji/wavy_dash.png celo/docs/static/images/emoji/wavy_dash.png new file mode 100644 index 0000000000000000000000000000000000000000..5a74e5c7aeb396f29a00aac7c28c365f3efbc4c3 Binary files /dev/null and celo/docs/static/images/emoji/wavy_dash.png differ
diff --git go-ethereum/docs/static/images/emoji/waxing_crescent_moon.png celo/docs/static/images/emoji/waxing_crescent_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f13dd31c8d4035ebf1450601561f76fe3066e5 Binary files /dev/null and celo/docs/static/images/emoji/waxing_crescent_moon.png differ
diff --git go-ethereum/docs/static/images/emoji/waxing_gibbous_moon.png celo/docs/static/images/emoji/waxing_gibbous_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..54e7ec6711a57c8618df57e2e93fa3a3e365d348 Binary files /dev/null and celo/docs/static/images/emoji/waxing_gibbous_moon.png differ
diff --git go-ethereum/docs/static/images/emoji/wc.png celo/docs/static/images/emoji/wc.png new file mode 100644 index 0000000000000000000000000000000000000000..dfe84d2a73a115232d71c5ef6cee80d97270f39b Binary files /dev/null and celo/docs/static/images/emoji/wc.png differ
diff --git go-ethereum/docs/static/images/emoji/weary.png celo/docs/static/images/emoji/weary.png new file mode 100644 index 0000000000000000000000000000000000000000..0c5475411c11e45c977dc062aff314cedce15d6b Binary files /dev/null and celo/docs/static/images/emoji/weary.png differ
diff --git go-ethereum/docs/static/images/emoji/wedding.png celo/docs/static/images/emoji/wedding.png new file mode 100644 index 0000000000000000000000000000000000000000..ead19d52cfb41c033796c39dfc8ba877d304df1e Binary files /dev/null and celo/docs/static/images/emoji/wedding.png differ
diff --git go-ethereum/docs/static/images/emoji/whale.png celo/docs/static/images/emoji/whale.png new file mode 100644 index 0000000000000000000000000000000000000000..5bb113e4289400d3ae646aea7499b6a1be8fc03d Binary files /dev/null and celo/docs/static/images/emoji/whale.png differ
diff --git go-ethereum/docs/static/images/emoji/whale2.png celo/docs/static/images/emoji/whale2.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef4ea94f6dd4fb948602e56b2269c2d663d6f78 Binary files /dev/null and celo/docs/static/images/emoji/whale2.png differ
diff --git go-ethereum/docs/static/images/emoji/wheelchair.png celo/docs/static/images/emoji/wheelchair.png new file mode 100644 index 0000000000000000000000000000000000000000..eddcdd7977aa74caed3a4546628ba44aa9863d8d Binary files /dev/null and celo/docs/static/images/emoji/wheelchair.png differ
diff --git go-ethereum/docs/static/images/emoji/white_check_mark.png celo/docs/static/images/emoji/white_check_mark.png new file mode 100644 index 0000000000000000000000000000000000000000..61dc0583cfac2ce9b3b63e0a0f8aef78202874f3 Binary files /dev/null and celo/docs/static/images/emoji/white_check_mark.png differ
diff --git go-ethereum/docs/static/images/emoji/white_circle.png celo/docs/static/images/emoji/white_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..3f648d1bd1f79f8ed62f11a1b5885ea5c098e1a1 Binary files /dev/null and celo/docs/static/images/emoji/white_circle.png differ
diff --git go-ethereum/docs/static/images/emoji/white_flower.png celo/docs/static/images/emoji/white_flower.png new file mode 100644 index 0000000000000000000000000000000000000000..c0929d0dd9947a58766c19db54bef0c25edadff9 Binary files /dev/null and celo/docs/static/images/emoji/white_flower.png differ
diff --git go-ethereum/docs/static/images/emoji/white_large_square.png celo/docs/static/images/emoji/white_large_square.png new file mode 100644 index 0000000000000000000000000000000000000000..60cb19a1371a7de1d486d120f9b050970237a3b1 Binary files /dev/null and celo/docs/static/images/emoji/white_large_square.png differ
diff --git go-ethereum/docs/static/images/emoji/white_medium_small_square.png celo/docs/static/images/emoji/white_medium_small_square.png new file mode 100644 index 0000000000000000000000000000000000000000..a115cdc49e53f5a24bf09b426dfb3da4875d38f7 Binary files /dev/null and celo/docs/static/images/emoji/white_medium_small_square.png differ
diff --git go-ethereum/docs/static/images/emoji/white_medium_square.png celo/docs/static/images/emoji/white_medium_square.png new file mode 100644 index 0000000000000000000000000000000000000000..199808bcf21f7056daba21250bb49f62c33be173 Binary files /dev/null and celo/docs/static/images/emoji/white_medium_square.png differ
diff --git go-ethereum/docs/static/images/emoji/white_small_square.png celo/docs/static/images/emoji/white_small_square.png new file mode 100644 index 0000000000000000000000000000000000000000..24ba879f46496fee51fc00a8e8307b4851653f55 Binary files /dev/null and celo/docs/static/images/emoji/white_small_square.png differ
diff --git go-ethereum/docs/static/images/emoji/white_square_button.png celo/docs/static/images/emoji/white_square_button.png new file mode 100644 index 0000000000000000000000000000000000000000..ad54d55c0cd727b947f81277073462c71faf31d4 Binary files /dev/null and celo/docs/static/images/emoji/white_square_button.png differ
diff --git go-ethereum/docs/static/images/emoji/wind_chime.png celo/docs/static/images/emoji/wind_chime.png new file mode 100644 index 0000000000000000000000000000000000000000..efacf5dd4be4decef0704614b26f8121bfc2a250 Binary files /dev/null and celo/docs/static/images/emoji/wind_chime.png differ
diff --git go-ethereum/docs/static/images/emoji/wine_glass.png celo/docs/static/images/emoji/wine_glass.png new file mode 100644 index 0000000000000000000000000000000000000000..82b0f00057d62354d3e51f1c166e0f50e1326109 Binary files /dev/null and celo/docs/static/images/emoji/wine_glass.png differ
diff --git go-ethereum/docs/static/images/emoji/wink.png celo/docs/static/images/emoji/wink.png new file mode 100644 index 0000000000000000000000000000000000000000..756766dd3e9702e42082555bca7c71ba2aadf544 Binary files /dev/null and celo/docs/static/images/emoji/wink.png differ
diff --git go-ethereum/docs/static/images/emoji/wolf.png celo/docs/static/images/emoji/wolf.png new file mode 100644 index 0000000000000000000000000000000000000000..c60c96895f7eb489a5ce2521302ebf20b59372e2 Binary files /dev/null and celo/docs/static/images/emoji/wolf.png differ
diff --git go-ethereum/docs/static/images/emoji/woman.png celo/docs/static/images/emoji/woman.png new file mode 100644 index 0000000000000000000000000000000000000000..6bf0d2b129cce03be314dbeb433d821c3d8e33a6 Binary files /dev/null and celo/docs/static/images/emoji/woman.png differ
diff --git go-ethereum/docs/static/images/emoji/womans_clothes.png celo/docs/static/images/emoji/womans_clothes.png new file mode 100644 index 0000000000000000000000000000000000000000..aa297c7b65e5cf94553a915cc300a8c6175403bd Binary files /dev/null and celo/docs/static/images/emoji/womans_clothes.png differ
diff --git go-ethereum/docs/static/images/emoji/womans_hat.png celo/docs/static/images/emoji/womans_hat.png new file mode 100644 index 0000000000000000000000000000000000000000..4cb2e6a6934f102c624bf793303448a6b56c4660 Binary files /dev/null and celo/docs/static/images/emoji/womans_hat.png differ
diff --git go-ethereum/docs/static/images/emoji/womens.png celo/docs/static/images/emoji/womens.png new file mode 100644 index 0000000000000000000000000000000000000000..2fab2966009cfe8675e78f0a68c97ab5be35735f Binary files /dev/null and celo/docs/static/images/emoji/womens.png differ
diff --git go-ethereum/docs/static/images/emoji/worried.png celo/docs/static/images/emoji/worried.png new file mode 100644 index 0000000000000000000000000000000000000000..bfa1856c019155ca3fd7b3959a361a1bafa03258 Binary files /dev/null and celo/docs/static/images/emoji/worried.png differ
diff --git go-ethereum/docs/static/images/emoji/wrench.png celo/docs/static/images/emoji/wrench.png new file mode 100644 index 0000000000000000000000000000000000000000..a87072ad132df27ee739cc5b241a8dfa64919702 Binary files /dev/null and celo/docs/static/images/emoji/wrench.png differ
diff --git go-ethereum/docs/static/images/emoji/x.png celo/docs/static/images/emoji/x.png new file mode 100644 index 0000000000000000000000000000000000000000..dff9efa8b43b02c432cec1624199a479576ce547 Binary files /dev/null and celo/docs/static/images/emoji/x.png differ
diff --git go-ethereum/docs/static/images/emoji/yellow_heart.png celo/docs/static/images/emoji/yellow_heart.png new file mode 100644 index 0000000000000000000000000000000000000000..fa41ce78ac44f13cffe227d24c4f25663372454e Binary files /dev/null and celo/docs/static/images/emoji/yellow_heart.png differ
diff --git go-ethereum/docs/static/images/emoji/yen.png celo/docs/static/images/emoji/yen.png new file mode 100644 index 0000000000000000000000000000000000000000..139bc936e0f33bd6c546c1f50de941f901af13fe Binary files /dev/null and celo/docs/static/images/emoji/yen.png differ
diff --git go-ethereum/docs/static/images/emoji/yum.png celo/docs/static/images/emoji/yum.png new file mode 100644 index 0000000000000000000000000000000000000000..fc39637ecd81ba4da58e15352442be6929d0941c Binary files /dev/null and celo/docs/static/images/emoji/yum.png differ
diff --git go-ethereum/docs/static/images/emoji/zap.png celo/docs/static/images/emoji/zap.png new file mode 100644 index 0000000000000000000000000000000000000000..260c531b9e23171ca25cd30971ba06e28a3d7c74 Binary files /dev/null and celo/docs/static/images/emoji/zap.png differ
diff --git go-ethereum/docs/static/images/emoji/zero.png celo/docs/static/images/emoji/zero.png new file mode 100644 index 0000000000000000000000000000000000000000..6e57b3343adae7fe290485ceb885e1ccb5381c21 Binary files /dev/null and celo/docs/static/images/emoji/zero.png differ
diff --git go-ethereum/docs/static/images/emoji/zzz.png celo/docs/static/images/emoji/zzz.png new file mode 100644 index 0000000000000000000000000000000000000000..30be04655af5ff06c524ef43ace5b4ab10bad850 Binary files /dev/null and celo/docs/static/images/emoji/zzz.png differ
diff --git go-ethereum/docs/static/images/favicon.ico celo/docs/static/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..347b5c6d86f80172357d977621ff11a30b4fb024 Binary files /dev/null and celo/docs/static/images/favicon.ico differ
diff --git go-ethereum/docs/static/images/mascot.png celo/docs/static/images/mascot.png new file mode 100644 index 0000000000000000000000000000000000000000..f3c28eaef49caf1bfc79526bddbbd5d2fe87ceb2 Binary files /dev/null and celo/docs/static/images/mascot.png differ
diff --git go-ethereum/docs/static/scripts/bootstrap.min.js celo/docs/static/scripts/bootstrap.min.js new file mode 100644 index 0000000000000000000000000000000000000000..9bcd2fccaed9442f1460191d6670ca5e8e08520c --- /dev/null +++ celo/docs/static/scripts/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under the MIT license + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",c).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){d.$element.one("mouseup.dismiss.bs.modal",function(b){a(b.target).is(d.$element)&&(d.ignoreBackdropClick=!0)})}),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in"),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$dialog.one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){document===a.target||this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a(document.createElement("div")).addClass("modal-backdrop "+e).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth<a,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",a,b)};c.VERSION="3.3.7",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-m<o.top?"bottom":"right"==h&&k.right+l>o.width?"left":"left"==h&&k.left-l<o.left?"right":h,f.removeClass(n).addClass(h)}var p=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(p,h);var q=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",q).emulateTransitionEnd(c.TRANSITION_DURATION):q()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top+=g,b.left+=h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element&&e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=a(this.$tip),g=a.Event("hide.bs."+this.type);if(this.$element.trigger(g),!g.isDefaultPrevented())return f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=window.SVGElement&&c instanceof window.SVGElement,g=d?{top:0,left:0}:f?null:b.offset(),h={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},i=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,h,i,g)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(void 0===e[a+1]||b<e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){ +this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.7",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e<c&&"top";if("bottom"==this.affixed)return null!=c?!(e+this.unpin<=f.top)&&"bottom":!(e+g<=a-d)&&"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&e<=c?"top":null!=d&&i+j>=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file
diff --git go-ethereum/docs/static/scripts/custom/polyfills.js celo/docs/static/scripts/custom/polyfills.js new file mode 100644 index 0000000000000000000000000000000000000000..735d6cbc092731c9b959b1ae42ee6043d5181abd --- /dev/null +++ celo/docs/static/scripts/custom/polyfills.js @@ -0,0 +1,15 @@ +// Polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes +if (!String.prototype.includes) { + String.prototype.includes = function(search, start) { + 'use strict'; + if (typeof start !== 'number') { + start = 0; + } + + if (start + search.length > this.length) { + return false; + } else { + return this.indexOf(search, start) !== -1; + } + }; +}
diff --git go-ethereum/docs/static/scripts/emojify.min.js celo/docs/static/scripts/emojify.min.js new file mode 100644 index 0000000000000000000000000000000000000000..4fedf320523db94e2e023c78eda319e4248ae07c --- /dev/null +++ celo/docs/static/scripts/emojify.min.js @@ -0,0 +1,4 @@ +/*! emojify.js - v1.0.5 - + * Copyright (c) Hassan Khan 2015 + */ +!function(e,a){"use strict";"function"==typeof define&&define.amd?define([],a):"object"==typeof exports?module.exports=a():e.emojify=a()}(this,function(){"use strict";var e=function(){function e(){var e={named:/:([a-z0-9A-Z_-]+):/,smile:/:-?\)/g,open_mouth:/:o/gi,scream:/:-o/gi,smirk:/[:;]-?]/g,grinning:/[:;]-?d/gi,stuck_out_tongue_closed_eyes:/x-d/gi,stuck_out_tongue_winking_eye:/[:;]-?p/gi,rage:/:-?[\[@]/g,frowning:/:-?\(/g,sob:/:['’]-?\(|:&#x27;\(/g,kissing_heart:/:-?\*/g,wink:/;-?\)/g,pensive:/:-?\//g,confounded:/:-?s/gi,flushed:/:-?\|/g,relaxed:/:-?\$/g,mask:/:-x/gi,heart:/<3|&lt;3/g,broken_heart:/<\/3|&lt;&#x2F;3/g,thumbsup:/:\+1:/g,thumbsdown:/:\-1:/g};return d.ignore_emoticons&&(e={named:/:([a-z0-9A-Z_-]+):/,thumbsup:/:\+1:/g,thumbsdown:/:\-1:/g}),Object.keys(e).map(function(a){return[e[a],a]})}function a(){var e=_.map(function(e){var a=e[0],o=a.source||a;return o=o.replace(/(^|[^\[])\^/g,"$1"),"("+o+")"}).join("|");return new RegExp(e,"gi")}function o(e){return" "===e||" "===e||"\r"===e||"\n"===e||""===e||e===String.fromCharCode(160)}function r(e){var a=null;if(e.replacer)a=e.replacer.apply({config:d},[":"+e.emojiName+":",e.emojiName]);else{var o=d.tag_type||h[d.mode];a=e.win.document.createElement(o),"img"!==o?a.setAttribute("class","emoji emoji-"+e.emojiName):(a.setAttribute("align","absmiddle"),a.setAttribute("alt",":"+e.emojiName+":"),a.setAttribute("class","emoji"),a.setAttribute("src",d.img_dir+"/"+e.emojiName+".png")),a.setAttribute("title",":"+e.emojiName+":")}e.node.splitText(e.match.index),e.node.nextSibling.nodeValue=e.node.nextSibling.nodeValue.substr(e.match[0].length,e.node.nextSibling.nodeValue.length),a.appendChild(e.node.splitText(e.match.index)),e.node.parentNode.insertBefore(a,e.node.nextSibling)}function t(e){if(e[1]&&e[2]){var a=e[2];if(m[a])return a}else for(var o=3;o<e.length-1;o++)if(e[o])return _[o-2][1]}function i(e,a){var o=this.config.tag_type||h[this.config.mode];return"img"!==o?"<"+o+" class='emoji emoji-"+a+"' title=':"+a+":'></"+o+">":"<img align='absmiddle' alt=':"+a+":' class='emoji' src='"+this.config.img_dir+"/"+a+".png' title=':"+a+":' />"}function n(){this.lastEmojiTerminatedAt=-1}function s(o,r){if(!o)return o;r||(r=i),_=e(),c=a();var t=new n;return o.replace(c,function(){var e=Array.prototype.slice.call(arguments,0,-2),a=arguments[arguments.length-2],o=arguments[arguments.length-1],i=t.validate(e,a,o);return i?r.apply({config:d},[arguments[0],i]):arguments[0]})}function l(o,i){"undefined"==typeof o&&(o=d.only_crawl_id?document.getElementById(d.only_crawl_id):document.body);var s=o.ownerDocument,l=s.defaultView||s.parentWindow,u=function(e,a){var o;if(e.hasChildNodes())for(o=e.firstChild;o;)a(o)&&u(o,a),o=o.nextSibling},g=function(e){for(var a,o=[],s=new n;null!==(a=c.exec(e.data));)s.validate(a,a.index,a.input)&&o.push(a);for(var _=o.length;_-->0;){var u=t(o[_]);r({node:e,match:o[_],emojiName:u,replacer:i,win:l})}};_=e(),c=a();var m=[],h=new RegExp(d.blacklist.elements.join("|"),"i"),p=new RegExp(d.blacklist.classes.join("|"),"i");if("undefined"!=typeof l.document.createTreeWalker)for(var b,f=l.document.createTreeWalker(o,l.NodeFilter.SHOW_TEXT|l.NodeFilter.SHOW_ELEMENT,function(e){return 1!==e.nodeType?l.NodeFilter.FILTER_ACCEPT:e.tagName.match(h)||"svg"===e.tagName||e.className.match(p)?l.NodeFilter.FILTER_REJECT:l.NodeFilter.FILTER_SKIP},!1);null!==(b=f.nextNode());)m.push(b);else u(o,function(e){return"undefined"!=typeof e.tagName&&e.tagName.match(h)||"undefined"!=typeof e.className&&e.className.match(p)?!1:1===e.nodeType?!0:(m.push(e),!0)});m.forEach(g)}var _,c,u="+1,-1,100,1234,8ball,a,ab,abc,abcd,accept,aerial_tramway,airplane,alarm_clock,alien,ambulance,anchor,angel,anger,angry,anguished,ant,apple,aquarius,aries,arrow_backward,arrow_double_down,arrow_double_up,arrow_down,arrow_down_small,arrow_forward,arrow_heading_down,arrow_heading_up,arrow_left,arrow_lower_left,arrow_lower_right,arrow_right,arrow_right_hook,arrow_up,arrow_up_down,arrow_up_small,arrow_upper_left,arrow_upper_right,arrows_clockwise,arrows_counterclockwise,art,articulated_lorry,astonished,atm,b,baby,baby_bottle,baby_chick,baby_symbol,back,baggage_claim,balloon,ballot_box_with_check,bamboo,banana,bangbang,bank,bar_chart,barber,baseball,basketball,bath,bathtub,battery,bear,bee,beer,beers,beetle,beginner,bell,bento,bicyclist,bike,bikini,bird,birthday,black_circle,black_joker,black_medium_small_square,black_medium_square,black_nib,black_small_square,black_square,black_square_button,blossom,blowfish,blue_book,blue_car,blue_heart,blush,boar,boat,bomb,book,bookmark,bookmark_tabs,books,boom,boot,bouquet,bow,bowling,bowtie,boy,bread,bride_with_veil,bridge_at_night,briefcase,broken_heart,bug,bulb,bullettrain_front,bullettrain_side,bus,busstop,bust_in_silhouette,busts_in_silhouette,cactus,cake,calendar,calling,camel,camera,cancer,candy,capital_abcd,capricorn,car,card_index,carousel_horse,cat,cat2,cd,chart,chart_with_downwards_trend,chart_with_upwards_trend,checkered_flag,cherries,cherry_blossom,chestnut,chicken,children_crossing,chocolate_bar,christmas_tree,church,cinema,circus_tent,city_sunrise,city_sunset,cl,clap,clapper,clipboard,clock1,clock10,clock1030,clock11,clock1130,clock12,clock1230,clock130,clock2,clock230,clock3,clock330,clock4,clock430,clock5,clock530,clock6,clock630,clock7,clock730,clock8,clock830,clock9,clock930,closed_book,closed_lock_with_key,closed_umbrella,cloud,clubs,cn,cocktail,coffee,cold_sweat,collision,computer,confetti_ball,confounded,confused,congratulations,construction,construction_worker,convenience_store,cookie,cool,cop,copyright,corn,couple,couple_with_heart,couplekiss,cow,cow2,credit_card,crescent_moon,crocodile,crossed_flags,crown,cry,crying_cat_face,crystal_ball,cupid,curly_loop,currency_exchange,curry,custard,customs,cyclone,dancer,dancers,dango,dart,dash,date,de,deciduous_tree,department_store,diamond_shape_with_a_dot_inside,diamonds,disappointed,disappointed_relieved,dizzy,dizzy_face,do_not_litter,dog,dog2,dollar,dolls,dolphin,donut,door,doughnut,dragon,dragon_face,dress,dromedary_camel,droplet,dvd,e-mail,ear,ear_of_rice,earth_africa,earth_americas,earth_asia,egg,eggplant,eight,eight_pointed_black_star,eight_spoked_asterisk,electric_plug,elephant,email,end,envelope,es,euro,european_castle,european_post_office,evergreen_tree,exclamation,expressionless,eyeglasses,eyes,facepunch,factory,fallen_leaf,family,fast_forward,fax,fearful,feelsgood,feet,ferris_wheel,file_folder,finnadie,fire,fire_engine,fireworks,first_quarter_moon,first_quarter_moon_with_face,fish,fish_cake,fishing_pole_and_fish,fist,five,flags,flashlight,floppy_disk,flower_playing_cards,flushed,foggy,football,fork_and_knife,fountain,four,four_leaf_clover,fr,free,fried_shrimp,fries,frog,frowning,fu,fuelpump,full_moon,full_moon_with_face,game_die,gb,gem,gemini,ghost,gift,gift_heart,girl,globe_with_meridians,goat,goberserk,godmode,golf,grapes,green_apple,green_book,green_heart,grey_exclamation,grey_question,grimacing,grin,grinning,guardsman,guitar,gun,haircut,hamburger,hammer,hamster,hand,handbag,hankey,hash,hatched_chick,hatching_chick,headphones,hear_no_evil,heart,heart_decoration,heart_eyes,heart_eyes_cat,heartbeat,heartpulse,hearts,heavy_check_mark,heavy_division_sign,heavy_dollar_sign,heavy_exclamation_mark,heavy_minus_sign,heavy_multiplication_x,heavy_plus_sign,helicopter,herb,hibiscus,high_brightness,high_heel,hocho,honey_pot,honeybee,horse,horse_racing,hospital,hotel,hotsprings,hourglass,hourglass_flowing_sand,house,house_with_garden,hurtrealbad,hushed,ice_cream,icecream,id,ideograph_advantage,imp,inbox_tray,incoming_envelope,information_desk_person,information_source,innocent,interrobang,iphone,it,izakaya_lantern,jack_o_lantern,japan,japanese_castle,japanese_goblin,japanese_ogre,jeans,joy,joy_cat,jp,key,keycap_ten,kimono,kiss,kissing,kissing_cat,kissing_closed_eyes,kissing_face,kissing_heart,kissing_smiling_eyes,koala,koko,kr,large_blue_circle,large_blue_diamond,large_orange_diamond,last_quarter_moon,last_quarter_moon_with_face,laughing,leaves,ledger,left_luggage,left_right_arrow,leftwards_arrow_with_hook,lemon,leo,leopard,libra,light_rail,link,lips,lipstick,lock,lock_with_ink_pen,lollipop,loop,loudspeaker,love_hotel,love_letter,low_brightness,m,mag,mag_right,mahjong,mailbox,mailbox_closed,mailbox_with_mail,mailbox_with_no_mail,man,man_with_gua_pi_mao,man_with_turban,mans_shoe,maple_leaf,mask,massage,meat_on_bone,mega,melon,memo,mens,metal,metro,microphone,microscope,milky_way,minibus,minidisc,mobile_phone_off,money_with_wings,moneybag,monkey,monkey_face,monorail,mortar_board,mount_fuji,mountain_bicyclist,mountain_cableway,mountain_railway,mouse,mouse2,movie_camera,moyai,muscle,mushroom,musical_keyboard,musical_note,musical_score,mute,nail_care,name_badge,neckbeard,necktie,negative_squared_cross_mark,neutral_face,new,new_moon,new_moon_with_face,newspaper,ng,nine,no_bell,no_bicycles,no_entry,no_entry_sign,no_good,no_mobile_phones,no_mouth,no_pedestrians,no_smoking,non-potable_water,nose,notebook,notebook_with_decorative_cover,notes,nut_and_bolt,o,o2,ocean,octocat,octopus,oden,office,ok,ok_hand,ok_woman,older_man,older_woman,on,oncoming_automobile,oncoming_bus,oncoming_police_car,oncoming_taxi,one,open_file_folder,open_hands,open_mouth,ophiuchus,orange_book,outbox_tray,ox,package,page_facing_up,page_with_curl,pager,palm_tree,panda_face,paperclip,parking,part_alternation_mark,partly_sunny,passport_control,paw_prints,peach,pear,pencil,pencil2,penguin,pensive,performing_arts,persevere,person_frowning,person_with_blond_hair,person_with_pouting_face,phone,pig,pig2,pig_nose,pill,pineapple,pisces,pizza,plus1,point_down,point_left,point_right,point_up,point_up_2,police_car,poodle,poop,post_office,postal_horn,postbox,potable_water,pouch,poultry_leg,pound,pouting_cat,pray,princess,punch,purple_heart,purse,pushpin,put_litter_in_its_place,question,rabbit,rabbit2,racehorse,radio,radio_button,rage,rage1,rage2,rage3,rage4,railway_car,rainbow,raised_hand,raised_hands,raising_hand,ram,ramen,rat,recycle,red_car,red_circle,registered,relaxed,relieved,repeat,repeat_one,restroom,revolving_hearts,rewind,ribbon,rice,rice_ball,rice_cracker,rice_scene,ring,rocket,roller_coaster,rooster,rose,rotating_light,round_pushpin,rowboat,ru,rugby_football,runner,running,running_shirt_with_sash,sa,sagittarius,sailboat,sake,sandal,santa,satellite,satisfied,saxophone,school,school_satchel,scissors,scorpius,scream,scream_cat,scroll,seat,secret,see_no_evil,seedling,seven,shaved_ice,sheep,shell,ship,shipit,shirt,shit,shoe,shower,signal_strength,six,six_pointed_star,ski,skull,sleeping,sleepy,slot_machine,small_blue_diamond,small_orange_diamond,small_red_triangle,small_red_triangle_down,smile,smile_cat,smiley,smiley_cat,smiling_imp,smirk,smirk_cat,smoking,snail,snake,snowboarder,snowflake,snowman,sob,soccer,soon,sos,sound,space_invader,spades,spaghetti,sparkle,sparkler,sparkles,sparkling_heart,speak_no_evil,speaker,speech_balloon,speedboat,squirrel,star,star2,stars,station,statue_of_liberty,steam_locomotive,stew,straight_ruler,strawberry,stuck_out_tongue,stuck_out_tongue_closed_eyes,stuck_out_tongue_winking_eye,sun_with_face,sunflower,sunglasses,sunny,sunrise,sunrise_over_mountains,surfer,sushi,suspect,suspension_railway,sweat,sweat_drops,sweat_smile,sweet_potato,swimmer,symbols,syringe,tada,tanabata_tree,tangerine,taurus,taxi,tea,telephone,telephone_receiver,telescope,tennis,tent,thought_balloon,three,thumbsdown,thumbsup,ticket,tiger,tiger2,tired_face,tm,toilet,tokyo_tower,tomato,tongue,top,tophat,tractor,traffic_light,train,train2,tram,triangular_flag_on_post,triangular_ruler,trident,triumph,trolleybus,trollface,trophy,tropical_drink,tropical_fish,truck,trumpet,tshirt,tulip,turtle,tv,twisted_rightwards_arrows,two,two_hearts,two_men_holding_hands,two_women_holding_hands,u5272,u5408,u55b6,u6307,u6708,u6709,u6e80,u7121,u7533,u7981,u7a7a,uk,umbrella,unamused,underage,unlock,up,us,v,vertical_traffic_light,vhs,vibration_mode,video_camera,video_game,violin,virgo,volcano,vs,walking,waning_crescent_moon,waning_gibbous_moon,warning,watch,water_buffalo,watermelon,wave,wavy_dash,waxing_crescent_moon,waxing_gibbous_moon,wc,weary,wedding,whale,whale2,wheelchair,white_check_mark,white_circle,white_flower,white_large_square,white_medium_small_square,white_medium_square,white_small_square,white_square_button,wind_chime,wine_glass,wink,wolf,woman,womans_clothes,womans_hat,womens,worried,wrench,x,yellow_heart,yen,yum,zap,zero,zzz",g=u.split(/,/),m=g.reduce(function(e,a){return e[a]=!0,e},{}),d={blacklist:{ids:[],classes:["no-emojify"],elements:["script","textarea","a","pre","code"]},tag_type:null,only_crawl_id:null,img_dir:"images/emoji",ignore_emoticons:!1,mode:"img"},h={img:"img",sprite:"span","data-uri":"span"};return n.prototype={validate:function(e,a,r){function i(){return n.lastEmojiTerminatedAt=_+a,s}var n=this,s=t(e);if(s){var l=e[0],_=l.length;if(0===a)return i();if(r.length===l.length+a)return i();var c=this.lastEmojiTerminatedAt===a;if(c)return i();if(o(r.charAt(a-1)))return i();var u=o(r.charAt(l.length+a));return u&&c?i():void 0}}},{defaultConfig:d,emojiNames:g,setConfig:function(e){Object.keys(d).forEach(function(a){a in e&&(d[a]=e[a])})},replace:s,run:l}}();return e}); \ No newline at end of file
diff --git go-ethereum/docs/static/scripts/filesize.min.js celo/docs/static/scripts/filesize.min.js new file mode 100644 index 0000000000000000000000000000000000000000..2654691dda7de8053aecf739ecc8236c82f2463a --- /dev/null +++ celo/docs/static/scripts/filesize.min.js @@ -0,0 +1,6 @@ +/* + 2016 + @version 3.3.0 + */ +"use strict";!function(a){function b(a){var b=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],e=[],f=0,g=void 0,h=void 0,i=void 0,j=void 0,k=void 0,l=void 0,m=void 0,n=void 0,o=void 0,p=void 0,q=void 0,r=void 0;if(isNaN(a))throw new Error("Invalid arguments");return i=b.bits===!0,o=b.unix===!0,h=b.base||2,n=void 0!==b.round?b.round:o?1:2,p=void 0!==b.spacer?b.spacer:o?"":" ",r=b.symbols||b.suffixes||{},q=2===h?b.standard||"jedec":"jedec",m=b.output||"string",g=void 0!==b.exponent?b.exponent:-1,l=Number(a),k=0>l,j=h>2?1e3:1024,k&&(l=-l),0===l?(e[0]=0,e[1]=o?"":i?"b":"B"):((-1===g||isNaN(g))&&(g=Math.floor(Math.log(l)/Math.log(j)),0>g&&(g=0)),g>8&&(g=8),f=2===h?l/Math.pow(2,10*g):l/Math.pow(1e3,g),i&&(f=8*f,f>j&&8>g&&(f/=j,g++)),e[0]=Number(f.toFixed(g>0?n:0)),e[1]=10===h&&1===g?i?"kb":"kB":d[q][i?"bits":"bytes"][g],o&&(e[1]="jedec"===q?e[1].charAt(0):g>0?e[1].replace(/B$/,""):e[1],c.test(e[1])&&(e[0]=Math.floor(e[0]),e[1]=""))),k&&(e[0]=-e[0]),e[1]=r[e[1]]||e[1],"array"===m?e:"exponent"===m?g:"object"===m?{value:e[0],suffix:e[1],symbol:e[1]}:e.join(p)}var c=/^(b|B)$/,d={iec:{bits:["b","Kib","Mib","Gib","Tib","Pib","Eib","Zib","Yib"],bytes:["B","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"]},jedec:{bits:["b","Kb","Mb","Gb","Tb","Pb","Eb","Zb","Yb"],bytes:["B","KB","MB","GB","TB","PB","EB","ZB","YB"]}};"undefined"!=typeof exports?module.exports=b:"function"==typeof define&&define.amd?define(function(){return b}):a.filesize=b}("undefined"!=typeof window?window:global); +//# sourceMappingURL=filesize.min.js.map \ No newline at end of file
diff --git go-ethereum/docs/static/scripts/highlight-go.min.js celo/docs/static/scripts/highlight-go.min.js new file mode 100644 index 0000000000000000000000000000000000000000..cf58a7e0e16bd37b803f2368839c27f66191f398 --- /dev/null +++ celo/docs/static/scripts/highlight-go.min.js @@ -0,0 +1 @@ +hljs.registerLanguage("go",function(e){var t={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",literal:"true false iota nil",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{aliases:["golang"],k:t,i:"</",c:[e.CLCM,e.CBCM,{cN:"string",v:[e.QSM,{b:"'",e:"[^\\\\]'"},{b:"`",e:"`"}]},{cN:"number",v:[{b:e.CNR+"[dflsi]",r:1},e.CNM]},{b:/:=/},{cN:"function",bK:"func",e:/\s*\{/,eE:!0,c:[e.TM,{cN:"params",b:/\(/,e:/\)/,k:t,i:/["']/}]}]}}); \ No newline at end of file
diff --git go-ethereum/docs/static/scripts/highlight-gradle.min.js celo/docs/static/scripts/highlight-gradle.min.js new file mode 100644 index 0000000000000000000000000000000000000000..498f21d0ff932e01dabce381747ab47c9a8ea0bf --- /dev/null +++ celo/docs/static/scripts/highlight-gradle.min.js @@ -0,0 +1 @@ +hljs.registerLanguage("gradle",function(e){return{cI:!0,k:{keyword:"task project allprojects subprojects artifacts buildscript configurations dependencies repositories sourceSets description delete from into include exclude source classpath destinationDir includes options sourceCompatibility targetCompatibility group flatDir doLast doFirst flatten todir fromdir ant def abstract break case catch continue default do else extends final finally for if implements instanceof native new private protected public return static switch synchronized throw throws transient try volatile while strictfp package import false null super this true antlrtask checkstyle codenarc copy boolean byte char class double float int interface long short void compile runTime file fileTree abs any append asList asWritable call collect compareTo count div dump each eachByte eachFile eachLine every find findAll flatten getAt getErr getIn getOut getText grep immutable inject inspect intersect invokeMethods isCase join leftShift minus multiply newInputStream newOutputStream newPrintWriter newReader newWriter next plus pop power previous print println push putAt read readBytes readLines reverse reverseEach round size sort splitEachLine step subMap times toInteger toList tokenize upto waitForOrKill withPrintWriter withReader withStream withWriter withWriterAppend write writeLine"},c:[e.CLCM,e.CBCM,e.ASM,e.QSM,e.NM,e.RM]}}); \ No newline at end of file
diff --git go-ethereum/docs/static/scripts/highlight-java.min.js celo/docs/static/scripts/highlight-java.min.js new file mode 100644 index 0000000000000000000000000000000000000000..fd42651026e8826e9d128a26931b27753a6eac30 --- /dev/null +++ celo/docs/static/scripts/highlight-java.min.js @@ -0,0 +1 @@ +hljs.registerLanguage("java",function(e){var a="[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",t=a+"(<"+a+"(\\s*,\\s*"+a+")*>)?",r="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",s="\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",c={cN:"number",b:s,r:0};return{aliases:["jsp"],k:r,i:/<\/|#/,c:[e.C("/\\*\\*","\\*/",{r:0,c:[{b:/\w+@/,r:0},{cN:"doctag",b:"@[A-Za-z]+"}]}),e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return else",r:0},{cN:"function",b:"("+t+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:r,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:r,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},c,{cN:"meta",b:"@[A-Za-z]+"}]}}); \ No newline at end of file
diff --git go-ethereum/docs/static/scripts/highlight-swift.min.js celo/docs/static/scripts/highlight-swift.min.js new file mode 100644 index 0000000000000000000000000000000000000000..a019d25c900ddf78077a97d497e11cfb18d59054 --- /dev/null +++ celo/docs/static/scripts/highlight-swift.min.js @@ -0,0 +1 @@ +hljs.registerLanguage("swift",function(e){var t={keyword:"__COLUMN__ __FILE__ __FUNCTION__ __LINE__ as as! as? associativity break case catch class continue convenience default defer deinit didSet do dynamic dynamicType else enum extension fallthrough false final for func get guard if import in indirect infix init inout internal is lazy left let mutating nil none nonmutating operator optional override postfix precedence prefix private protocol Protocol public repeat required rethrows return right self Self set static struct subscript super switch throw throws true try try! try? Type typealias unowned var weak where while willSet",literal:"true false nil",built_in:"abs advance alignof alignofValue anyGenerator assert assertionFailure bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal fatalError filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced isUniquelyReferencedNonObjC join lazy lexicographicalCompare map max maxElement min minElement numericCast overlaps partition posix precondition preconditionFailure print println quickSort readLine reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith stride strideof strideofValue swap toString transcode underestimateCount unsafeAddressOf unsafeBitCast unsafeDowncast unsafeUnwrap unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafeMutablePointer withUnsafeMutablePointers withUnsafePointer withUnsafePointers withVaList zip"},i={cN:"type",b:"\\b[A-Z][\\wÀ-ʸ']*",r:0},n=e.C("/\\*","\\*/",{c:["self"]}),r={cN:"subst",b:/\\\(/,e:"\\)",k:t,c:[]},a={cN:"number",b:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",r:0},o=e.inherit(e.QSM,{c:[r,e.BE]});return r.c=[a],{k:t,c:[o,e.CLCM,n,i,a,{cN:"function",bK:"func",e:"{",eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{b:/</,e:/>/},{cN:"params",b:/\(/,e:/\)/,endsParent:!0,k:t,c:["self",a,o,e.CBCM,{b:":"}],i:/["']/}],i:/\[|%/},{cN:"class",bK:"struct protocol class extension enum",k:t,e:"\\{",eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{cN:"meta",b:"(@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain)"},{bK:"import",e:/$/,c:[e.CLCM,n]}]}}); \ No newline at end of file
diff --git go-ethereum/docs/static/scripts/highlight.min.js celo/docs/static/scripts/highlight.min.js new file mode 100644 index 0000000000000000000000000000000000000000..a31cffe67346bb5ba6ffd0e2114f0bb535e7a05a --- /dev/null +++ celo/docs/static/scripts/highlight.min.js @@ -0,0 +1,3 @@ +/*! highlight.js v9.9.0 | BSD3 License | git.io/hljslicense */ +!function(e){var t="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):t&&(t.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return t.hljs}))}(function(e){function t(e){return e.replace(/[&<>]/gm,function(e){return L[e]})}function r(e){return e.nodeName.toLowerCase()}function a(e,t){var r=e&&e.exec(t);return r&&0===r.index}function n(e){return C.test(e)}function i(e){var t,r,a,i,s=e.className+" ";if(s+=e.parentNode?e.parentNode.className:"",r=E.exec(s))return y(r[1])?r[1]:"no-highlight";for(s=s.split(/\s+/),t=0,a=s.length;a>t;t++)if(i=s[t],n(i)||y(i))return i}function s(e,t){var r,a={};for(r in e)a[r]=e[r];if(t)for(r in t)a[r]=t[r];return a}function c(e){var t=[];return function a(e,n){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?n+=i.nodeValue.length:1===i.nodeType&&(t.push({event:"start",offset:n,node:i}),n=a(i,n),r(i).match(/br|hr|img|input/)||t.push({event:"stop",offset:n,node:i}));return n}(e,0),t}function o(e,a,n){function i(){return e.length&&a.length?e[0].offset!==a[0].offset?e[0].offset<a[0].offset?e:a:"start"===a[0].event?e:a:e.length?e:a}function s(e){function a(e){return" "+e.nodeName+'="'+t(e.value)+'"'}u+="<"+r(e)+w.map.call(e.attributes,a).join("")+">"}function c(e){u+="</"+r(e)+">"}function o(e){("start"===e.event?s:c)(e.node)}for(var l=0,u="",d=[];e.length||a.length;){var b=i();if(u+=t(n.substring(l,b[0].offset)),l=b[0].offset,b===e){d.reverse().forEach(c);do o(b.splice(0,1)[0]),b=i();while(b===e&&b.length&&b[0].offset===l);d.reverse().forEach(s)}else"start"===b[0].event?d.push(b[0].node):d.pop(),o(b.splice(0,1)[0])}return u+t(n.substr(l))}function l(e){function t(e){return e&&e.source||e}function r(r,a){return new RegExp(t(r),"m"+(e.cI?"i":"")+(a?"g":""))}function a(n,i){if(!n.compiled){if(n.compiled=!0,n.k=n.k||n.bK,n.k){var c={},o=function(t,r){e.cI&&(r=r.toLowerCase()),r.split(" ").forEach(function(e){var r=e.split("|");c[r[0]]=[t,r[1]?Number(r[1]):1]})};"string"==typeof n.k?o("keyword",n.k):N(n.k).forEach(function(e){o(e,n.k[e])}),n.k=c}n.lR=r(n.l||/\w+/,!0),i&&(n.bK&&(n.b="\\b("+n.bK.split(" ").join("|")+")\\b"),n.b||(n.b=/\B|\b/),n.bR=r(n.b),n.e||n.eW||(n.e=/\B|\b/),n.e&&(n.eR=r(n.e)),n.tE=t(n.e)||"",n.eW&&i.tE&&(n.tE+=(n.e?"|":"")+i.tE)),n.i&&(n.iR=r(n.i)),null==n.r&&(n.r=1),n.c||(n.c=[]);var l=[];n.c.forEach(function(e){e.v?e.v.forEach(function(t){l.push(s(e,t))}):l.push("self"===e?n:e)}),n.c=l,n.c.forEach(function(e){a(e,n)}),n.starts&&a(n.starts,i);var u=n.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([n.tE,n.i]).map(t).filter(Boolean);n.t=u.length?r(u.join("|"),!0):{exec:function(){return null}}}}a(e)}function u(e,r,n,i){function s(e,t){var r,n;for(r=0,n=t.c.length;n>r;r++)if(a(t.c[r].bR,e))return t.c[r]}function c(e,t){if(a(e.eR,t)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?c(e.parent,t):void 0}function o(e,t){return!n&&a(t.iR,e)}function b(e,t){var r=v.cI?t[0].toLowerCase():t[0];return e.k.hasOwnProperty(r)&&e.k[r]}function p(e,t,r,a){var n=a?"":S.classPrefix,i='<span class="'+n,s=r?"":B;return i+=e+'">',i+t+s}function m(){var e,r,a,n;if(!N.k)return t(E);for(n="",r=0,N.lR.lastIndex=0,a=N.lR.exec(E);a;)n+=t(E.substring(r,a.index)),e=b(N,a),e?(M+=e[1],n+=p(e[0],t(a[0]))):n+=t(a[0]),r=N.lR.lastIndex,a=N.lR.exec(E);return n+t(E.substr(r))}function f(){var e="string"==typeof N.sL;if(e&&!k[N.sL])return t(E);var r=e?u(N.sL,E,!0,x[N.sL]):d(E,N.sL.length?N.sL:void 0);return N.r>0&&(M+=r.r),e&&(x[N.sL]=r.top),p(r.language,r.value,!1,!0)}function g(){C+=null!=N.sL?f():m(),E=""}function _(e){C+=e.cN?p(e.cN,"",!0):"",N=Object.create(e,{parent:{value:N}})}function h(e,t){if(E+=e,null==t)return g(),0;var r=s(t,N);if(r)return r.skip?E+=t:(r.eB&&(E+=t),g(),r.rB||r.eB||(E=t)),_(r,t),r.rB?0:t.length;var a=c(N,t);if(a){var n=N;n.skip?E+=t:(n.rE||n.eE||(E+=t),g(),n.eE&&(E=t));do N.cN&&(C+=B),N.skip||(M+=N.r),N=N.parent;while(N!==a.parent);return a.starts&&_(a.starts,""),n.rE?0:t.length}if(o(t,N))throw new Error('Illegal lexeme "'+t+'" for mode "'+(N.cN||"<unnamed>")+'"');return E+=t,t.length||1}var v=y(e);if(!v)throw new Error('Unknown language: "'+e+'"');l(v);var w,N=i||v,x={},C="";for(w=N;w!==v;w=w.parent)w.cN&&(C=p(w.cN,"",!0)+C);var E="",M=0;try{for(var L,R,A=0;;){if(N.t.lastIndex=A,L=N.t.exec(r),!L)break;R=h(r.substring(A,L.index),L[0]),A=L.index+R}for(h(r.substr(A)),w=N;w.parent;w=w.parent)w.cN&&(C+=B);return{r:M,value:C,language:e,top:N}}catch($){if($.message&&-1!==$.message.indexOf("Illegal"))return{r:0,value:t(r)};throw $}}function d(e,r){r=r||S.languages||N(k);var a={r:0,value:t(e)},n=a;return r.filter(y).forEach(function(t){var r=u(t,e,!1);r.language=t,r.r>n.r&&(n=r),r.r>a.r&&(n=a,a=r)}),n.language&&(a.second_best=n),a}function b(e){return S.tabReplace||S.useBR?e.replace(M,function(e,t){return S.useBR&&"\n"===e?"<br>":S.tabReplace?t.replace(/\t/g,S.tabReplace):void 0}):e}function p(e,t,r){var a=t?x[t]:r,n=[e.trim()];return e.match(/\bhljs\b/)||n.push("hljs"),-1===e.indexOf(a)&&n.push(a),n.join(" ").trim()}function m(e){var t,r,a,s,l,m=i(e);n(m)||(S.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")):t=e,l=t.textContent,a=m?u(m,l,!0):d(l),r=c(t),r.length&&(s=document.createElementNS("http://www.w3.org/1999/xhtml","div"),s.innerHTML=a.value,a.value=o(r,c(s),l)),a.value=b(a.value),e.innerHTML=a.value,e.className=p(e.className,m,a.language),e.result={language:a.language,re:a.r},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.r}))}function f(e){S=s(S,e)}function g(){if(!g.called){g.called=!0;var e=document.querySelectorAll("pre code");w.forEach.call(e,m)}}function _(){addEventListener("DOMContentLoaded",g,!1),addEventListener("load",g,!1)}function h(t,r){var a=k[t]=r(e);a.aliases&&a.aliases.forEach(function(e){x[e]=t})}function v(){return N(k)}function y(e){return e=(e||"").toLowerCase(),k[e]||k[x[e]]}var w=[],N=Object.keys,k={},x={},C=/^(no-?highlight|plain|text)$/i,E=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,B="</span>",S={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},L={"&":"&amp;","<":"&lt;",">":"&gt;"};return e.highlight=u,e.highlightAuto=d,e.fixMarkup=b,e.highlightBlock=m,e.configure=f,e.initHighlighting=g,e.initHighlightingOnLoad=_,e.registerLanguage=h,e.listLanguages=v,e.getLanguage=y,e.inherit=s,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(t,r,a){var n=e.inherit({cN:"comment",b:t,e:r,c:[]},a||{});return n.c.push(e.PWM),n.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),n},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e.registerLanguage("apache",function(e){var t={cN:"number",b:"[\\$%]\\d+"};return{aliases:["apacheconf"],cI:!0,c:[e.HCM,{cN:"section",b:"</?",e:">"},{cN:"attribute",b:/\w+/,r:0,k:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{e:/$/,r:0,k:{literal:"on off all"},c:[{cN:"meta",b:"\\s\\[",e:"\\]$"},{cN:"variable",b:"[\\$%]\\{",e:"\\}",c:["self",t]},t,e.QSM]}}],i:/\S/}}),e.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},r={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/-?[a-z\._]+/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"meta",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,r,a,t]}}),e.registerLanguage("coffeescript",function(e){var t={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger super yield import export from as default await then unless until loop of by when and or is isnt not",literal:"true false null undefined yes no on off",built_in:"npm require console print module global window document"},r="[A-Za-z$_][0-9A-Za-z$_]*",a={cN:"subst",b:/#\{/,e:/}/,k:t},n=[e.BNM,e.inherit(e.CNM,{starts:{e:"(\\s*/)?",r:0}}),{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,a]},{b:/"/,e:/"/,c:[e.BE,a]}]},{cN:"regexp",v:[{b:"///",e:"///",c:[a,e.HCM]},{b:"//[gim]*",r:0},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{b:"@"+r},{sL:"javascript",eB:!0,eE:!0,v:[{b:"```",e:"```"},{b:"`",e:"`"}]}];a.c=n;var i=e.inherit(e.TM,{b:r}),s="(\\(.*\\))?\\s*\\B[-=]>",c={cN:"params",b:"\\([^\\(]",rB:!0,c:[{b:/\(/,e:/\)/,k:t,c:["self"].concat(n)}]};return{aliases:["coffee","cson","iced"],k:t,i:/\/\*/,c:n.concat([e.C("###","###"),e.HCM,{cN:"function",b:"^\\s*"+r+"\\s*=\\s*"+s,e:"[-=]>",rB:!0,c:[i,c]},{b:/[:\(,=]\s*/,r:0,c:[{cN:"function",b:s,e:"[-=]>",rB:!0,c:[c]}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[i]},i]},{b:r+":",e:":",rB:!0,rE:!0,r:0}])}}),e.registerLanguage("cpp",function(e){var t={cN:"keyword",b:"\\b[a-z\\d_]*_t\\b"},r={cN:"string",v:[{b:'(u8?|U)?L?"',e:'"',i:"\\n",c:[e.BE]},{b:'(u8?|U)?R"',e:'"',c:[e.BE]},{b:"'\\\\?.",e:"'",i:"."}]},a={cN:"number",v:[{b:"\\b(0b[01']+)"},{b:"\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{b:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],r:0},n={cN:"meta",b:/#\s*[a-z]+\b/,e:/$/,k:{"meta-keyword":"if else elif endif define undef warning error line pragma ifdef ifndef include"},c:[{b:/\\\n/,r:0},e.inherit(r,{cN:"meta-string"}),{cN:"meta-string",b:"<",e:">",i:"\\n"},e.CLCM,e.CBCM]},i=e.IR+"\\s*\\(",s={keyword:"int float while private char catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using class asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignof constexpr decltype noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return",built_in:"std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr",literal:"true false nullptr NULL"},c=[t,e.CLCM,e.CBCM,a,r];return{aliases:["c","cc","h","c++","h++","hpp"],k:s,i:"</",c:c.concat([n,{b:"\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",e:">",k:s,c:["self",t]},{b:e.IR+"::",k:s},{v:[{b:/=/,e:/;/},{b:/\(/,e:/\)/},{bK:"new throw return else",e:/;/}],k:s,c:c.concat([{b:/\(/,e:/\)/,k:s,c:c.concat(["self"]),r:0}]),r:0},{cN:"function",b:"("+e.IR+"[\\*&\\s]+)+"+i,rB:!0,e:/[{;=]/,eE:!0,k:s,i:/[^\w\s\*&]/,c:[{b:i,rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:s,r:0,c:[e.CLCM,e.CBCM,r,a,t]},e.CLCM,e.CBCM,n]}]),exports:{preprocessor:n,strings:r,k:s}}}),e.registerLanguage("cs",function(e){var t={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double else enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while nameof add alias ascending async await by descending dynamic equals from get global group into join let on orderby partial remove select set value var where yield",literal:"null false true"},r={cN:"string",b:'@"',e:'"',c:[{b:'""'}]},a=e.inherit(r,{i:/\n/}),n={cN:"subst",b:"{",e:"}",k:t},i=e.inherit(n,{i:/\n/}),s={cN:"string",b:/\$"/,e:'"',i:/\n/,c:[{b:"{{"},{b:"}}"},e.BE,i]},c={cN:"string",b:/\$@"/,e:'"',c:[{b:"{{"},{b:"}}"},{b:'""'},n]},o=e.inherit(c,{i:/\n/,c:[{b:"{{"},{b:"}}"},{b:'""'},i]});n.c=[c,s,r,e.ASM,e.QSM,e.CNM,e.CBCM],i.c=[o,s,a,e.ASM,e.QSM,e.CNM,e.inherit(e.CBCM,{i:/\n/})];var l={v:[c,s,r,e.ASM,e.QSM]},u=e.IR+"(<"+e.IR+"(\\s*,\\s*"+e.IR+")*>)?(\\[\\])?";return{aliases:["csharp"],k:t,i:/::/,c:[e.C("///","$",{rB:!0,c:[{cN:"doctag",v:[{b:"///",r:0},{b:"<!--|-->"},{b:"</?",e:">"}]}]}),e.CLCM,e.CBCM,{cN:"meta",b:"#",e:"$",k:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},l,e.CNM,{bK:"class interface",e:/[{;=]/,i:/[^\s:]/,c:[e.TM,e.CLCM,e.CBCM]},{bK:"namespace",e:/[{;=]/,i:/[^\s:]/,c:[e.inherit(e.TM,{b:"[a-zA-Z](\\.?\\w)*"}),e.CLCM,e.CBCM]},{bK:"new return throw await",r:0},{cN:"function",b:"("+u+"\\s+)+"+e.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:t,c:[{b:e.IR+"\\s*\\(",rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,r:0,c:[l,e.CNM,e.CBCM]},e.CLCM,e.CBCM]}]}}),e.registerLanguage("css",function(e){var t="[a-zA-Z-][a-zA-Z0-9_-]*",r={b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{eW:!0,eE:!0,c:[{b:/[\w-]+\(/,rB:!0,c:[{cN:"built_in",b:/[\w-]+/},{b:/\(/,e:/\)/,c:[e.ASM,e.QSM]}]},e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"number",b:"#[0-9A-Fa-f]+"},{cN:"meta",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"selector-id",b:/#[A-Za-z0-9_-]+/},{cN:"selector-class",b:/\.[A-Za-z0-9_-]+/},{cN:"selector-attr",b:/\[/,e:/\]/,i:"$"},{cN:"selector-pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{b:"@",e:"[{;]",i:/:/,c:[{cN:"keyword",b:/\w+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[e.ASM,e.QSM,e.CSSNM]}]},{cN:"selector-tag",b:t,r:0},{b:"{",e:"}",i:/\S/,c:[e.CBCM,r]}]}}),e.registerLanguage("diff",function(e){return{aliases:["patch"],c:[{cN:"meta",r:10,v:[{b:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"comment",v:[{b:/Index: /,e:/$/},{b:/={3,}/,e:/$/},{b:/^\-{3}/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+{3}/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"addition",b:"^\\!",e:"$"}]}}),e.registerLanguage("http",function(e){var t="HTTP/[0-9\\.]+";return{aliases:["https"],i:"\\S",c:[{b:"^"+t,e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{b:"^[A-Z]+ (.*?) "+t+"$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0},{b:t},{cN:"keyword",b:"[A-Z]+"}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{e:"$",r:0}},{b:"\\n\\n",starts:{sL:[],eW:!0}}]}}),e.registerLanguage("ini",function(e){var t={cN:"string",c:[e.BE],v:[{b:"'''",e:"'''",r:10},{b:'"""',e:'"""',r:10},{b:'"',e:'"'},{b:"'",e:"'"}]};return{aliases:["toml"],cI:!0,i:/\S/,c:[e.C(";","$"),e.HCM,{cN:"section",b:/^\s*\[+/,e:/\]+/},{b:/^[a-z0-9\[\]_-]+\s*=\s*/,e:"$",rB:!0,c:[{cN:"attr",b:/[a-z0-9\[\]_-]+/},{b:/=/,eW:!0,r:0,c:[{cN:"literal",b:/\bon|off|true|false|yes|no\b/},{cN:"variable",v:[{b:/\$[\w\d"][\w\d_]*/},{b:/\$\{(.*?)}/}]},t,{cN:"number",b:/([\+\-]+)?[\d]+_[\d_]+/},e.NM]}]}]}}),e.registerLanguage("java",function(e){var t="[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",r=t+"(<"+t+"(\\s*,\\s*"+t+")*>)?",a="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",n="\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",i={cN:"number",b:n,r:0};return{aliases:["jsp"],k:a,i:/<\/|#/,c:[e.C("/\\*\\*","\\*/",{r:0,c:[{b:/\w+@/,r:0},{cN:"doctag",b:"@[A-Za-z]+"}]}),e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return else",r:0},{cN:"function",b:"("+r+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:a,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:a,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},i,{cN:"meta",b:"@[A-Za-z]+"}]}}),e.registerLanguage("javascript",function(e){var t="[A-Za-z$_][0-9A-Za-z$_]*",r={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},n={cN:"subst",b:"\\$\\{",e:"\\}",k:r,c:[]},i={cN:"string",b:"`",e:"`",c:[e.BE,n]};n.c=[e.ASM,e.QSM,i,a,e.RM];var s=n.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:r,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,i,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:t+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:t,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+t+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:t},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:r,c:s}]}]},{b:/</,e:/(\/\w+|\w+\/)>/,sL:"xml",c:[{b:/<\w+\s*\/>/,skip:!0},{b:/<\w+/,e:/(\/\w+|\w+\/)>/,skip:!0,c:[{b:/<\w+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:t}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:s}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}}),e.registerLanguage("json",function(e){var t={literal:"true false null"},r=[e.QSM,e.CNM],a={e:",",eW:!0,eE:!0,c:r,k:t},n={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(a,{b:/:/})],i:"\\S"},i={b:"\\[",e:"\\]",c:[e.inherit(a)],i:"\\S"};return r.splice(r.length,0,n,i),{c:r,k:t,i:"\\S"}}),e.registerLanguage("makefile",function(e){var t={cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]};return{aliases:["mk","mak"],c:[e.HCM,{b:/^\w+\s*\W*=/,rB:!0,r:0,starts:{e:/\s*\W*=/,eE:!0,starts:{e:/$/,r:0,c:[t]}}},{cN:"section",b:/^[\w]+:\s*$/},{cN:"meta",b:/^\.PHONY:/,e:/$/,k:{"meta-keyword":".PHONY"},l:/[\.\w]+/},{b:/^\t+/,e:/$/,r:0,c:[e.QSM,t]}]}}),e.registerLanguage("xml",function(e){var t="[A-Za-z0-9\\._:-]+",r={eW:!0,i:/</,r:0,c:[{cN:"attr",b:t,r:0},{b:/=\s*/,r:0,c:[{cN:"string",endsParent:!0,v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s"'=<>`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],cI:!0,c:[{cN:"meta",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},e.C("<!--","-->",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0}]},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{name:"style"},c:[r],starts:{e:"</style>",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{name:"script"},c:[r],starts:{e:"</script>",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"meta",v:[{b:/<\?xml/,e:/\?>/,r:10},{b:/<\?\w+/,e:/\?>/}]},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"name",b:/[^\/><\s]+/,r:0},r]}]}}),e.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"section",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"quote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"^```w*s*$",e:"^```s*$"},{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"string",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"symbol",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:/^\[[^\n]+\]:/,rB:!0,c:[{cN:"symbol",b:/\[/,e:/\]/,eB:!0,eE:!0},{cN:"link",b:/:\s*/,e:/$/,eB:!0}]}]}}),e.registerLanguage("nginx",function(e){var t={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},r={eW:!0,l:"[a-z/_]+",k:{literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,t],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[t]},{cN:"regexp",c:[e.BE,t],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},t]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s+{",rB:!0,e:"{",c:[{cN:"section",b:e.UIR}],r:0},{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"attribute",b:e.UIR,starts:r}],r:0}],i:"[^\\s\\}]"}}),e.registerLanguage("objectivec",function(e){var t={cN:"built_in",b:"\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\w+"},r={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},a=/[a-zA-Z@][a-zA-Z0-9_]*/,n="@interface @class @protocol @implementation";return{aliases:["mm","objc","obj-c"],k:r,l:a,i:"</",c:[t,e.CLCM,e.CBCM,e.CNM,e.QSM,{cN:"string",v:[{b:'@"',e:'"',i:"\\n",c:[e.BE]},{b:"'",e:"[^\\\\]'",i:"[^\\\\][^']"}]},{cN:"meta",b:"#",e:"$",c:[{cN:"meta-string",v:[{b:'"',e:'"'},{b:"<",e:">"}]}]},{cN:"class",b:"("+n.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:n,l:a,c:[e.UTM]},{b:"\\."+e.UIR,r:0}]}}),e.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",r={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},a={b:"->{",e:"}"},n={v:[{b:/\$\d/},{b:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{b:/[\$%@][^\s\w{]/,r:0}]},i=[e.BE,r,n],s=[n,e.HCM,e.C("^\\=\\w","\\=cut",{eW:!0}),a,{cN:"string",c:i,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"function",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",eE:!0,r:5,c:[e.TM]},{b:"-\\w\\b",r:0},{b:"^__DATA__$",e:"^__END__$",sL:"mojolicious",c:[{b:"^@@.*",e:"$",cN:"comment"}]}];return r.c=s,a.c=s,{aliases:["pl","pm"],l:/[\w\.]+/,k:t,c:s}}),e.registerLanguage("php",function(e){var t={b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},r={cN:"meta",b:/<\?(php)?|\?>/},a={cN:"string",c:[e.BE,r],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},n={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.HCM,e.C("//","$",{c:[r]}),e.C("/\\*","\\*/",{c:[{cN:"doctag",b:"@[A-Za-z]+"}]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:/<<<['"]?\w+['"]?$/,e:/^\w+;?$/,c:[e.BE,{cN:"subst",v:[{b:/\$\w+/},{b:/\{\$/,e:/\}/}]}]},r,{cN:"keyword",b:/\$this\b/},t,{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",t,e.CBCM,a,n]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},a,n]}}),e.registerLanguage("python",function(e){var t={cN:"meta",b:/^(>>>|\.\.\.) /},r={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[t],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[t],r:10},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},e.ASM,e.QSM]},a={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},n={cN:"params",b:/\(/,e:/\)/,c:["self",t,a,r]};return{aliases:["py","gyp"],k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},i:/(<\/|->|\?)|=>/,c:[t,a,r,e.HCM,{v:[{cN:"function",bK:"def"},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n,]/,c:[e.UTM,n,{b:/->/,eW:!0,k:"None"}]},{cN:"meta",b:/^[\t ]*@/,e:/$/},{b:/\b(print|exec)\(/}]}}),e.registerLanguage("ruby",function(e){var t="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},a={cN:"doctag",b:"@[A-Za-z]+"},n={b:"#<",e:">"},i=[e.C("#","$",{c:[a]}),e.C("^\\=begin","^\\=end",{c:[a],r:10}),e.C("^__END__","\\n$")],s={cN:"subst",b:"#\\{",e:"}",k:r},c={cN:"string",c:[e.BE,s],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{b:/<<(-?)\w+$/,e:/^\s*\w+$/}]},o={cN:"params",b:"\\(",e:"\\)",endsParent:!0,k:r},l=[c,n,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?" +}),{b:"<\\s*",c:[{b:"("+e.IR+"::)?"+e.IR}]}].concat(i)},{cN:"function",bK:"def",e:"$|;",c:[e.inherit(e.TM,{b:t}),o].concat(i)},{b:e.IR+"::"},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":(?!\\s)",c:[c,{b:t}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{cN:"params",b:/\|/,e:/\|/,k:r},{b:"("+e.RSR+"|unless)\\s*",c:[n,{cN:"regexp",c:[e.BE,s],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}].concat(i),r:0}].concat(i);s.c=l,o.c=l;var u="[>?]>",d="[\\w#]+\\(\\w+\\):\\d+:\\d+>",b="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",p=[{b:/^\s*=>/,starts:{e:"$",c:l}},{cN:"meta",b:"^("+u+"|"+d+"|"+b+")",starts:{e:"$",c:l}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,i:/\/\*/,c:i.concat(p).concat(l)}}),e.registerLanguage("sql",function(e){var t=e.C("--","$");return{cI:!0,i:/[<>{}*#]/,c:[{bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment",e:/;/,eW:!0,l:/[\w\.]+/,k:{keyword:"abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias allocate allow alter always analyze ancillary and any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second section securefile security seed segment select self sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text varchar varying void"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}}),e}); \ No newline at end of file
diff --git go-ethereum/docs/static/scripts/jquery.min.js celo/docs/static/scripts/jquery.min.js new file mode 100644 index 0000000000000000000000000000000000000000..4c5be4c0fbe230e81d95718a18829e965a2d14b2 --- /dev/null +++ celo/docs/static/scripts/jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=r.isArray(d)))?(e?(e=!1,f=c&&r.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function ra(){}ra.prototype=d.filters=d.pseudos,d.setFilters=new ra,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=Q.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function sa(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e);return!1}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}return!1}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d<e;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i<f;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;e<f;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i<e&&ya(a.slice(i,e)),e<f&&ya(a=a.slice(e)),e<f&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b<d;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;b<d;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a<c;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;d<e;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){r.each(b,function(b,c){r.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==r.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return r.each(arguments,function(a,b){var c;while((c=r.inArray(b,f,c))>-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b<f)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,M,e),g(f,c,N,e)):(f++,j.call(a,g(f,c,M,e),g(f,c,N,e),g(f,c,M,c.notifyWith))):(d!==M&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), +a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},T=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function U(){this.expando=r.expando+U.uid++}U.uid=1,U.prototype={cache:function(a){var b=a[this.expando];return b||(b={},T(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){r.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(K)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var V=new U,W=new U,X=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Y=/[A-Z]/g;function Z(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:X.test(a)?JSON.parse(a):a)}function $(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Y,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c=Z(c)}catch(e){}W.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return W.hasData(a)||V.hasData(a)},data:function(a,b,c){return W.access(a,b,c)},removeData:function(a,b){W.remove(a,b)},_data:function(a,b,c){return V.access(a,b,c)},_removeData:function(a,b){V.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=W.get(f),1===f.nodeType&&!V.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),$(f,d,e[d])));V.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){W.set(this,a)}):S(this,function(b){var c;if(f&&void 0===b){if(c=W.get(f,a),void 0!==c)return c;if(c=$(f,a),void 0!==c)return c}else this.each(function(){W.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=V.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var _=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,aa=new RegExp("^(?:([+-])=|)("+_+")([a-z%]*)$","i"),ba=["Top","Right","Bottom","Left"],ca=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},da=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function ea(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&aa.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var fa={};function ga(a){var b,c=a.ownerDocument,d=a.nodeName,e=fa[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),fa[d]=e,e)}function ha(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=V.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&ca(d)&&(e[f]=ga(d))):"none"!==c&&(e[f]="none",V.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ha(this,!0)},hide:function(){return ha(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){ca(this)?r(this).show():r(this).hide()})}});var ia=/^(?:checkbox|radio)$/i,ja=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c<d;c++)V.set(a[c],"globalEval",!b||V.get(b[c],"globalEval"))}var oa=/<|&#?\w+;/;function pa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;n<o;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(oa.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ja.exec(f)||["",""])[1].toLowerCase(),i=la[h]||la._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c<arguments.length;c++)i[c]=arguments[c];if(b.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,b)!==!1){h=r.event.handlers.call(this,b,j),c=0;while((f=h[c++])&&!b.isPropagationStopped()){b.currentTarget=f.elem,d=0;while((g=f.handlers[d++])&&!b.isImmediatePropagationStopped())b.rnamespace&&!b.rnamespace.test(g.namespace)||(b.handleObj=g,b.data=g.data,e=((r.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(b.result=e)===!1&&(b.preventDefault(),b.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,b),b.result}},handlers:function(a,b){var c,d,e,f,g,h=[],i=b.delegateCount,j=a.target;if(i&&j.nodeType&&!("click"===a.type&&a.button>=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c<i;c++)d=b[c],e=d.selector+" ",void 0===g[e]&&(g[e]=d.needsContext?r(e,this).index(j)>-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i<b.length&&h.push({elem:j,handlers:b.slice(i)}),h},addProp:function(a,b){Object.defineProperty(r.Event.prototype,a,{enumerable:!0,configurable:!0,get:r.isFunction(b)?function(){if(this.originalEvent)return b(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[a]},set:function(b){Object.defineProperty(this,a,{enumerable:!0,configurable:!0,writable:!0,value:b})}})},fix:function(a){return a[r.expando]?a:new r.Event(a)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==wa()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===wa()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&r.nodeName(this,"input"))return this.click(),!1},_default:function(a){return r.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}}},r.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)},r.Event=function(a,b){return this instanceof r.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ua:va,this.target=a.target&&3===a.target.nodeType?a.target.parentNode:a.target,this.currentTarget=a.currentTarget,this.relatedTarget=a.relatedTarget):this.type=a,b&&r.extend(this,b),this.timeStamp=a&&a.timeStamp||r.now(),void(this[r.expando]=!0)):new r.Event(a,b)},r.Event.prototype={constructor:r.Event,isDefaultPrevented:va,isPropagationStopped:va,isImmediatePropagationStopped:va,isSimulated:!1,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ua,a&&!this.isSimulated&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ua,a&&!this.isSimulated&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ua,a&&!this.isSimulated&&a.stopImmediatePropagation(),this.stopPropagation()}},r.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(a){var b=a.button;return null==a.which&&ra.test(a.type)?null!=a.charCode?a.charCode:a.keyCode:!a.which&&void 0!==b&&sa.test(a.type)?1&b?1:2&b?3:4&b?2:0:a.which}},r.event.addProp),r.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){r.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||r.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),r.fn.extend({on:function(a,b,c,d){return xa(this,a,b,c,d)},one:function(a,b,c,d){return xa(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,r(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=va),this.each(function(){r.event.remove(this,a,c,b)})}});var ya=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/<script|<style|<link/i,Aa=/checked\s*(?:[^=]|=\s*.checked.)/i,Ba=/^true\/(.*)/,Ca=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c<d;c++)r.event.add(b,e,j[e][c])}W.hasData(a)&&(h=W.access(a),i=r.extend({},h),W.set(b,i))}}function Ha(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ia.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ia(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l<m;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,ma(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Fa),l=0;l<i;l++)j=h[l],ka.test(j.type||"")&&!V.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Ca,""),k))}return a}function Ja(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(ma(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&na(ma(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(ya,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d<e;d++)Ha(f[d],g[d]);if(b)if(c)for(f=f||ma(a),g=g||ma(h),d=0,e=f.length;d<e;d++)Ga(f[d],g[d]);else Ga(a,h);return g=ma(h,"script"),g.length>0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c<d;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(ma(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ia(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(ma(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;g<=f;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var Ka=/^margin/,La=new RegExp("^("+_+")(?!px)[a-z%]+$","i"),Ma=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",qa.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,qa.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Na(a,b,c){var d,e,f,g,h=a.style;return c=c||Ma(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&La.test(g)&&Ka.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Oa(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Pa=/^(none|table(?!-c[ea]).+)/,Qa={position:"absolute",visibility:"hidden",display:"block"},Ra={letterSpacing:"0",fontWeight:"400"},Sa=["Webkit","Moz","ms"],Ta=d.createElement("div").style;function Ua(a){if(a in Ta)return a;var b=a[0].toUpperCase()+a.slice(1),c=Sa.length;while(c--)if(a=Sa[c]+b,a in Ta)return a}function Va(a,b,c){var d=aa.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Wa(a,b,c,d,e){var f,g=0;for(f=c===(d?"border":"content")?4:"width"===b?1:0;f<4;f+=2)"margin"===c&&(g+=r.css(a,c+ba[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+ba[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+ba[f]+"Width",!0,e))):(g+=r.css(a,"padding"+ba[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+ba[f]+"Width",!0,e)));return g}function Xa(a,b,c){var d,e=!0,f=Ma(a),g="border-box"===r.css(a,"boxSizing",!1,f);if(a.getClientRects().length&&(d=a.getBoundingClientRect()[b]),d<=0||null==d){if(d=Na(a,b,f),(d<0||null==d)&&(d=a.style[b]),La.test(d))return d;e=g&&(o.boxSizingReliable()||d===a.style[b]),d=parseFloat(d)||0}return d+Wa(a,b,c||(g?"border":"content"),e,f)+"px"}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Na(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=a.style;return b=r.cssProps[h]||(r.cssProps[h]=Ua(h)||h),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=aa.exec(c))&&e[1]&&(c=ea(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b);return b=r.cssProps[h]||(r.cssProps[h]=Ua(h)||h),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Na(a,b,d)),"normal"===e&&b in Ra&&(e=Ra[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){if(c)return!Pa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?Xa(a,b,d):da(a,Qa,function(){return Xa(a,b,d)})},set:function(a,c,d){var e,f=d&&Ma(a),g=d&&Wa(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=aa.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Va(a,c,g)}}}),r.cssHooks.marginLeft=Oa(o.reliableMarginLeft,function(a,b){if(b)return(parseFloat(Na(a,"marginLeft"))||a.getBoundingClientRect().left-da(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];d<4;d++)e[a+ba[d]+b]=f[d]||f[d-2]||f[0];return e}},Ka.test(a)||(r.cssHooks[a+b].set=Va)}),r.fn.extend({css:function(a,b){return S(this,function(a,b,c){var d,e,f={},g=0;if(r.isArray(b)){for(d=Ma(a),e=b.length;g<e;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f<g;f++)if(d=e[f].call(c,b,a))return d}function fb(a,b,c){var d,e,f,g,h,i,j,k,l="width"in b||"height"in b,m=this,n={},o=a.style,p=a.nodeType&&ca(a),q=V.get(a,"fxshow");c.queue||(g=r._queueHooks(a,"fx"),null==g.unqueued&&(g.unqueued=0,h=g.empty.fire,g.empty.fire=function(){g.unqueued||h()}),g.unqueued++,m.always(function(){m.always(function(){g.unqueued--,r.queue(a,"fx").length||g.empty.fire()})}));for(d in b)if(e=b[d],_a.test(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}n[d]=q&&q[d]||r.style(a,d)}if(i=!r.isEmptyObject(b),i||!r.isEmptyObject(n)){l&&1===a.nodeType&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=q&&q.display,null==j&&(j=V.get(a,"display")),k=r.css(a,"display"),"none"===k&&(j?k=j:(ha([a],!0),j=a.style.display||j,k=r.css(a,"display"),ha([a]))),("inline"===k||"inline-block"===k&&null!=j)&&"none"===r.css(a,"float")&&(i||(m.done(function(){o.display=j}),null==j&&(k=o.display,j="none"===k?"":k)),o.display="inline-block")),c.overflow&&(o.overflow="hidden",m.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]})),i=!1;for(d in n)i||(q?"hidden"in q&&(p=q.hidden):q=V.access(a,"fxshow",{display:j}),f&&(q.hidden=!p),p&&ha([a],!0),m.done(function(){p||ha([a]),V.remove(a,"fxshow");for(d in n)r.style(a,d,n[d])})),i=eb(p?q[d]:0,d,m),d in q||(q[d]=i.start,p&&(i.end=i.start,i.start=0))}}function gb(a,b){var c,d,e,f,g;for(c in a)if(d=r.camelCase(c),e=b[d],f=a[c],r.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=r.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function hb(a,b,c){var d,e,f=0,g=hb.prefilters.length,h=r.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Za||cb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;g<i;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),f<1&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:r.extend({},b),opts:r.extend(!0,{specialEasing:{},easing:r.easing._default},c),originalProperties:b,originalOptions:c,startTime:Za||cb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=r.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;c<d;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(gb(k,j.opts.specialEasing);f<g;f++)if(d=hb.prefilters[f].call(j,a,k,j.opts))return r.isFunction(d.stop)&&(r._queueHooks(j.elem,j.opts.queue).stop=r.proxy(d.stop,d)),d;return r.map(k,eb,j),r.isFunction(j.opts.start)&&j.opts.start.call(a,j),r.fx.timer(r.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}r.Animation=r.extend(hb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return ea(c.elem,a,aa.exec(b),c),c}]},tweener:function(a,b){r.isFunction(a)?(b=a,a=["*"]):a=a.match(K);for(var c,d=0,e=a.length;d<e;d++)c=a[d],hb.tweeners[c]=hb.tweeners[c]||[],hb.tweeners[c].unshift(b)},prefilters:[fb],prefilter:function(a,b){b?hb.prefilters.unshift(a):hb.prefilters.push(a)}}),r.speed=function(a,b,c){var e=a&&"object"==typeof a?r.extend({},a):{complete:c||!c&&b||r.isFunction(a)&&a,duration:a,easing:c&&b||b&&!r.isFunction(b)&&b};return r.fx.off||d.hidden?e.duration=0:"number"!=typeof e.duration&&(e.duration in r.fx.speeds?e.duration=r.fx.speeds[e.duration]:e.duration=r.fx.speeds._default),null!=e.queue&&e.queue!==!0||(e.queue="fx"),e.old=e.complete,e.complete=function(){r.isFunction(e.old)&&e.old.call(this),e.queue&&r.dequeue(this,e.queue)},e},r.fn.extend({fadeTo:function(a,b,c,d){return this.filter(ca).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=r.isEmptyObject(a),f=r.speed(b,c,d),g=function(){var b=hb(this,r.extend({},a),f);(e||V.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=r.timers,g=V.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&ab.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||r.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=V.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=r.timers,g=d?d.length:0;for(c.finish=!0,r.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;b<g;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),r.each(["toggle","show","hide"],function(a,b){var c=r.fn[b];r.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(db(b,!0),a,d,e)}}),r.each({slideDown:db("show"),slideUp:db("hide"),slideToggle:db("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){r.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),r.timers=[],r.fx.tick=function(){var a,b=0,c=r.timers;for(Za=r.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||r.fx.stop(),Za=void 0},r.fx.timer=function(a){r.timers.push(a),a()?r.fx.start():r.timers.pop()},r.fx.interval=13,r.fx.start=function(){$a||($a=a.requestAnimationFrame?a.requestAnimationFrame(bb):a.setInterval(r.fx.tick,r.fx.interval))},r.fx.stop=function(){a.cancelAnimationFrame?a.cancelAnimationFrame($a):a.clearInterval($a),$a=null},r.fx.speeds={slow:600,fast:200,_default:400},r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var ib,jb=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return S(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), +void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d<i;d++)if(c=e[d],(c.selected||d===f)&&!c.disabled&&(!c.parentNode.disabled||!r.nodeName(c.parentNode,"optgroup"))){if(b=r(c).val(),g)return b;h.push(b)}return h},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("<script>").prop({charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&f("error"===a.type?404:200,a.type)}),d.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Qb=[],Rb=/(=)\?(?=&|$)|\?\?/;r.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Qb.pop()||r.expando+"_"+rb++;return this[a]=!0,a}}),r.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Rb.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Rb.test(b.data)&&"data");if(h||"jsonp"===b.dataTypes[0])return e=b.jsonpCallback=r.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Rb,"$1"+e):b.jsonp!==!1&&(b.url+=(sb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||r.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?r(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Qb.push(e)),g&&r.isFunction(f)&&f(g[0]),g=f=void 0}),"script"}),o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="<form></form><form></form>",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=B.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=pa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))},r.fn.load=function(a,b,c){var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=mb(a.slice(h)),a=a.slice(0,h)),r.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&r.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?r("<div>").append(r.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(this,f||[a.responseText,b,a])})}),this},r.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){r.fn[b]=function(a){return this.on(b,a)}}),r.expr.pseudos.animated=function(a){return r.grep(r.timers,function(b){return a===b.elem}).length};function Sb(a){return r.isWindow(a)?a:9===a.nodeType&&a.defaultView}r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),d.width||d.height?(e=f.ownerDocument,c=Sb(e),b=e.documentElement,{top:d.top+c.pageYOffset-b.clientTop,left:d.left+c.pageXOffset-b.clientLeft}):d):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),r.nodeName(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||qa})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return S(this,function(a,d,e){var f=Sb(a);return void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Oa(o.pixelPosition,function(a,c){if(c)return c=Na(a,b),La.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return S(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),r.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),r.parseJSON=JSON.parse,"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var Tb=a.jQuery,Ub=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=Ub),b&&a.jQuery===r&&(a.jQuery=Tb),r},b||(a.jQuery=a.$=r),r});
diff --git go-ethereum/docs/static/scripts/marked.min.js celo/docs/static/scripts/marked.min.js new file mode 100644 index 0000000000000000000000000000000000000000..de7fbfe1c20af709681ec59f83fb647a255a7490 --- /dev/null +++ celo/docs/static/scripts/marked.min.js @@ -0,0 +1,2 @@ +(function(){function e(e){this.tokens=[],this.tokens.links={},this.options=e||a.defaults,this.rules=p.normal,this.options.gfm&&(this.options.tables?this.rules=p.tables:this.rules=p.gfm)}function t(e,t){if(this.options=t||a.defaults,this.links=e,this.rules=u.normal,this.renderer=this.options.renderer||new n,this.renderer.options=this.options,!this.links)throw new Error("Tokens array requires a `links` property.");this.options.gfm?this.options.breaks?this.rules=u.breaks:this.rules=u.gfm:this.options.pedantic&&(this.rules=u.pedantic)}function n(e){this.options=e||{}}function r(e){this.tokens=[],this.token=null,this.options=e||a.defaults,this.options.renderer=this.options.renderer||new n,this.renderer=this.options.renderer,this.renderer.options=this.options}function s(e,t){return e.replace(t?/&/g:/&(?!#?\w+;)/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function i(e){return e.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g,function(e,t){return t=t.toLowerCase(),"colon"===t?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""})}function l(e,t){return e=e.source,t=t||"",function n(r,s){return r?(s=s.source||s,s=s.replace(/(^|[^\[])\^/g,"$1"),e=e.replace(r,s),n):new RegExp(e,t)}}function o(){}function h(e){for(var t,n,r=1;r<arguments.length;r++){t=arguments[r];for(n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])}return e}function a(t,n,i){if(i||"function"==typeof n){i||(i=n,n=null),n=h({},a.defaults,n||{});var l,o,p=n.highlight,u=0;try{l=e.lex(t,n)}catch(c){return i(c)}o=l.length;var g=function(e){if(e)return n.highlight=p,i(e);var t;try{t=r.parse(l,n)}catch(s){e=s}return n.highlight=p,e?i(e):i(null,t)};if(!p||p.length<3)return g();if(delete n.highlight,!o)return g();for(;u<l.length;u++)!function(e){return"code"!==e.type?--o||g():p(e.text,e.lang,function(t,n){return t?g(t):null==n||n===e.text?--o||g():(e.text=n,e.escaped=!0,void(--o||g()))})}(l[u])}else try{return n&&(n=h({},a.defaults,n)),r.parse(e.lex(t,n),n)}catch(c){if(c.message+="\nPlease report this to https://github.com/chjj/marked.",(n||a.defaults).silent)return"<p>An error occured:</p><pre>"+s(c.message+"",!0)+"</pre>";throw c}}var p={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:o,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:o,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:o,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};p.bullet=/(?:[*+-]|\d+\.)/,p.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/,p.item=l(p.item,"gm")(/bull/g,p.bullet)(),p.list=l(p.list)(/bull/g,p.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+p.def.source+")")(),p.blockquote=l(p.blockquote)("def",p.def)(),p._tag="(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b",p.html=l(p.html)("comment",/<!--[\s\S]*?-->/)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)(/tag/g,p._tag)(),p.paragraph=l(p.paragraph)("hr",p.hr)("heading",p.heading)("lheading",p.lheading)("blockquote",p.blockquote)("tag","<"+p._tag)("def",p.def)(),p.normal=h({},p),p.gfm=h({},p.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/}),p.gfm.paragraph=l(p.paragraph)("(?!","(?!"+p.gfm.fences.source.replace("\\1","\\2")+"|"+p.list.source.replace("\\1","\\3")+"|")(),p.tables=h({},p.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/}),e.rules=p,e.lex=function(t,n){var r=new e(n);return r.lex(t)},e.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},e.prototype.token=function(e,t,n){for(var r,s,i,l,o,h,a,u,c,e=e.replace(/^ +$/gm,"");e;)if((i=this.rules.newline.exec(e))&&(e=e.substring(i[0].length),i[0].length>1&&this.tokens.push({type:"space"})),i=this.rules.code.exec(e))e=e.substring(i[0].length),i=i[0].replace(/^ {4}/gm,""),this.tokens.push({type:"code",text:this.options.pedantic?i:i.replace(/\n+$/,"")});else if(i=this.rules.fences.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"code",lang:i[2],text:i[3]||""});else if(i=this.rules.heading.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"heading",depth:i[1].length,text:i[2]});else if(t&&(i=this.rules.nptable.exec(e))){for(e=e.substring(i[0].length),h={type:"table",header:i[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:i[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:i[3].replace(/\n$/,"").split("\n")},u=0;u<h.align.length;u++)/^ *-+: *$/.test(h.align[u])?h.align[u]="right":/^ *:-+: *$/.test(h.align[u])?h.align[u]="center":/^ *:-+ *$/.test(h.align[u])?h.align[u]="left":h.align[u]=null;for(u=0;u<h.cells.length;u++)h.cells[u]=h.cells[u].split(/ *\| */);this.tokens.push(h)}else if(i=this.rules.lheading.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"heading",depth:"="===i[2]?1:2,text:i[1]});else if(i=this.rules.hr.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"hr"});else if(i=this.rules.blockquote.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"blockquote_start"}),i=i[0].replace(/^ *> ?/gm,""),this.token(i,t,!0),this.tokens.push({type:"blockquote_end"});else if(i=this.rules.list.exec(e)){for(e=e.substring(i[0].length),l=i[2],this.tokens.push({type:"list_start",ordered:l.length>1}),i=i[0].match(this.rules.item),r=!1,c=i.length,u=0;c>u;u++)h=i[u],a=h.length,h=h.replace(/^ *([*+-]|\d+\.) +/,""),~h.indexOf("\n ")&&(a-=h.length,h=this.options.pedantic?h.replace(/^ {1,4}/gm,""):h.replace(new RegExp("^ {1,"+a+"}","gm"),"")),this.options.smartLists&&u!==c-1&&(o=p.bullet.exec(i[u+1])[0],l===o||l.length>1&&o.length>1||(e=i.slice(u+1).join("\n")+e,u=c-1)),s=r||/\n\n(?!\s*$)/.test(h),u!==c-1&&(r="\n"===h.charAt(h.length-1),s||(s=r)),this.tokens.push({type:s?"loose_item_start":"list_item_start"}),this.token(h,!1,n),this.tokens.push({type:"list_item_end"});this.tokens.push({type:"list_end"})}else if(i=this.rules.html.exec(e))e=e.substring(i[0].length),this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&("pre"===i[1]||"script"===i[1]||"style"===i[1]),text:i[0]});else if(!n&&t&&(i=this.rules.def.exec(e)))e=e.substring(i[0].length),this.tokens.links[i[1].toLowerCase()]={href:i[2],title:i[3]};else if(t&&(i=this.rules.table.exec(e))){for(e=e.substring(i[0].length),h={type:"table",header:i[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:i[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:i[3].replace(/(?: *\| *)?\n$/,"").split("\n")},u=0;u<h.align.length;u++)/^ *-+: *$/.test(h.align[u])?h.align[u]="right":/^ *:-+: *$/.test(h.align[u])?h.align[u]="center":/^ *:-+ *$/.test(h.align[u])?h.align[u]="left":h.align[u]=null;for(u=0;u<h.cells.length;u++)h.cells[u]=h.cells[u].replace(/^ *\| *| *\| *$/g,"").split(/ *\| */);this.tokens.push(h)}else if(t&&(i=this.rules.paragraph.exec(e)))e=e.substring(i[0].length),this.tokens.push({type:"paragraph",text:"\n"===i[1].charAt(i[1].length-1)?i[1].slice(0,-1):i[1]});else if(i=this.rules.text.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"text",text:i[0]});else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0));return this.tokens};var u={escape:/^\\([\\`*{}\[\]()#+\-.!_>])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:o,tag:/^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:o,text:/^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/};u._inside=/(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/,u._href=/\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/,u.link=l(u.link)("inside",u._inside)("href",u._href)(),u.reflink=l(u.reflink)("inside",u._inside)(),u.normal=h({},u),u.pedantic=h({},u.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/}),u.gfm=h({},u.normal,{escape:l(u.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:l(u.text)("]|","~]|")("|","|https?://|")()}),u.breaks=h({},u.gfm,{br:l(u.br)("{2,}","*")(),text:l(u.gfm.text)("{2,}","*")()}),t.rules=u,t.output=function(e,n,r){var s=new t(n,r);return s.output(e)},t.prototype.output=function(e){for(var t,n,r,i,l="";e;)if(i=this.rules.escape.exec(e))e=e.substring(i[0].length),l+=i[1];else if(i=this.rules.autolink.exec(e))e=e.substring(i[0].length),"@"===i[2]?(n=":"===i[1].charAt(6)?this.mangle(i[1].substring(7)):this.mangle(i[1]),r=this.mangle("mailto:")+n):(n=s(i[1]),r=n),l+=this.renderer.link(r,null,n);else if(this.inLink||!(i=this.rules.url.exec(e))){if(i=this.rules.tag.exec(e))!this.inLink&&/^<a /i.test(i[0])?this.inLink=!0:this.inLink&&/^<\/a>/i.test(i[0])&&(this.inLink=!1),e=e.substring(i[0].length),l+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(i[0]):s(i[0]):i[0];else if(i=this.rules.link.exec(e))e=e.substring(i[0].length),this.inLink=!0,l+=this.outputLink(i,{href:i[2],title:i[3]}),this.inLink=!1;else if((i=this.rules.reflink.exec(e))||(i=this.rules.nolink.exec(e))){if(e=e.substring(i[0].length),t=(i[2]||i[1]).replace(/\s+/g," "),t=this.links[t.toLowerCase()],!t||!t.href){l+=i[0].charAt(0),e=i[0].substring(1)+e;continue}this.inLink=!0,l+=this.outputLink(i,t),this.inLink=!1}else if(i=this.rules.strong.exec(e))e=e.substring(i[0].length),l+=this.renderer.strong(this.output(i[2]||i[1]));else if(i=this.rules.em.exec(e))e=e.substring(i[0].length),l+=this.renderer.em(this.output(i[2]||i[1]));else if(i=this.rules.code.exec(e))e=e.substring(i[0].length),l+=this.renderer.codespan(s(i[2],!0));else if(i=this.rules.br.exec(e))e=e.substring(i[0].length),l+=this.renderer.br();else if(i=this.rules.del.exec(e))e=e.substring(i[0].length),l+=this.renderer.del(this.output(i[1]));else if(i=this.rules.text.exec(e))e=e.substring(i[0].length),l+=this.renderer.text(s(this.smartypants(i[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else e=e.substring(i[0].length),n=s(i[1]),r=n,l+=this.renderer.link(r,null,n);return l},t.prototype.outputLink=function(e,t){var n=s(t.href),r=t.title?s(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,s(e[1]))},t.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014\/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014\/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},t.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,s=0;r>s;s++)t=e.charCodeAt(s),Math.random()>.5&&(t="x"+t.toString(16)),n+="&#"+t+";";return n},n.prototype.code=function(e,t,n){if(this.options.highlight){var r=this.options.highlight(e,t);null!=r&&r!==e&&(n=!0,e=r)}return t?'<pre><code class="'+this.options.langPrefix+s(t,!0)+'">'+(n?e:s(e,!0))+"\n</code></pre>\n":"<pre><code>"+(n?e:s(e,!0))+"\n</code></pre>"},n.prototype.blockquote=function(e){return"<blockquote>\n"+e+"</blockquote>\n"},n.prototype.html=function(e){return e},n.prototype.heading=function(e,t,n){return"<h"+t+' id="'+this.options.headerPrefix+n.toLowerCase().replace(/[^\w]+/g,"-")+'">'+e+"</h"+t+">\n"},n.prototype.hr=function(){return this.options.xhtml?"<hr/>\n":"<hr>\n"},n.prototype.list=function(e,t){var n=t?"ol":"ul";return"<"+n+">\n"+e+"</"+n+">\n"},n.prototype.listitem=function(e){return"<li>"+e+"</li>\n"},n.prototype.paragraph=function(e){return"<p>"+e+"</p>\n"},n.prototype.table=function(e,t){return"<table>\n<thead>\n"+e+"</thead>\n<tbody>\n"+t+"</tbody>\n</table>\n"},n.prototype.tablerow=function(e){return"<tr>\n"+e+"</tr>\n"},n.prototype.tablecell=function(e,t){var n=t.header?"th":"td",r=t.align?"<"+n+' style="text-align:'+t.align+'">':"<"+n+">";return r+e+"</"+n+">\n"},n.prototype.strong=function(e){return"<strong>"+e+"</strong>"},n.prototype.em=function(e){return"<em>"+e+"</em>"},n.prototype.codespan=function(e){return"<code>"+e+"</code>"},n.prototype.br=function(){return this.options.xhtml?"<br/>":"<br>"},n.prototype.del=function(e){return"<del>"+e+"</del>"},n.prototype.link=function(e,t,n){if(this.options.sanitize){try{var r=decodeURIComponent(i(e)).replace(/[^\w:]/g,"").toLowerCase()}catch(s){return""}if(0===r.indexOf("javascript:")||0===r.indexOf("vbscript:"))return""}var l='<a href="'+e+'"';return t&&(l+=' title="'+t+'"'),l+=">"+n+"</a>"},n.prototype.image=function(e,t,n){var r='<img src="'+e+'" alt="'+n+'"';return t&&(r+=' title="'+t+'"'),r+=this.options.xhtml?"/>":">"},n.prototype.text=function(e){return e},r.parse=function(e,t,n){var s=new r(t,n);return s.parse(e)},r.prototype.parse=function(e){this.inline=new t(e.links,this.options,this.renderer),this.tokens=e.reverse();for(var n="";this.next();)n+=this.tok();return n},r.prototype.next=function(){return this.token=this.tokens.pop()},r.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},r.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},r.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,s,i="",l="";for(n="",e=0;e<this.token.header.length;e++)r={header:!0,align:this.token.align[e]},n+=this.renderer.tablecell(this.inline.output(this.token.header[e]),{header:!0,align:this.token.align[e]});for(i+=this.renderer.tablerow(n),e=0;e<this.token.cells.length;e++){for(t=this.token.cells[e],n="",s=0;s<t.length;s++)n+=this.renderer.tablecell(this.inline.output(t[s]),{header:!1,align:this.token.align[s]});l+=this.renderer.tablerow(n)}return this.renderer.table(i,l);case"blockquote_start":for(var l="";"blockquote_end"!==this.next().type;)l+=this.tok();return this.renderer.blockquote(l);case"list_start":for(var l="",o=this.token.ordered;"list_end"!==this.next().type;)l+=this.tok();return this.renderer.list(l,o);case"list_item_start":for(var l="";"list_item_end"!==this.next().type;)l+="text"===this.token.type?this.parseText():this.tok();return this.renderer.listitem(l);case"loose_item_start":for(var l="";"list_item_end"!==this.next().type;)l+=this.tok();return this.renderer.listitem(l);case"html":var h=this.token.pre||this.options.pedantic?this.token.text:this.inline.output(this.token.text);return this.renderer.html(h);case"paragraph":return this.renderer.paragraph(this.inline.output(this.token.text));case"text":return this.renderer.paragraph(this.parseText())}},o.exec=o,a.options=a.setOptions=function(e){return h(a.defaults,e),a},a.defaults={gfm:!0,tables:!0,breaks:!1,pedantic:!1,sanitize:!1,sanitizer:null,mangle:!0,smartLists:!1,silent:!1,highlight:null,langPrefix:"lang-",smartypants:!1,headerPrefix:"",renderer:new n,xhtml:!1},a.Parser=r,a.parser=r.parse,a.Renderer=n,a.Lexer=e,a.lexer=e.lex,a.InlineLexer=t,a.inlineLexer=t.output,a.parse=a,"undefined"!=typeof module&&"object"==typeof exports?module.exports=a:"function"==typeof define&&define.amd?define(function(){return a}):this.marked=a}).call(function(){return this||("undefined"!=typeof window?window:global)}()); +//# sourceMappingURL=marked.min.js.map \ No newline at end of file
diff --git go-ethereum/docs/static/scripts/moment.min.js celo/docs/static/scripts/moment.min.js new file mode 100644 index 0000000000000000000000000000000000000000..6ea916a1a92d2b70385f39cebe6c562fdcb9ddee --- /dev/null +++ celo/docs/static/scripts/moment.min.js @@ -0,0 +1,548 @@ +//! moment.js +//! version : 2.15.2 +//! authors : Tim Wood, Iskren Chernev, Moment.js contributors +//! license : MIT +//! momentjs.com +!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return md.apply(null,arguments)} +// This is done to register the method called with moment() +// without creating circular dependencies. +function b(a){md=a}function c(a){return a instanceof Array||"[object Array]"===Object.prototype.toString.call(a)}function d(a){ +// IE8 will treat undefined and null as object if it wasn't for +// input != null +return null!=a&&"[object Object]"===Object.prototype.toString.call(a)}function e(a){var b;for(b in a) +// even if its not own property I'd still call it non-empty +return!1;return!0}function f(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function g(a,b){var c,d=[];for(c=0;c<a.length;++c)d.push(b(a[c],c));return d}function h(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function i(a,b){for(var c in b)h(b,c)&&(a[c]=b[c]);return h(b,"toString")&&(a.toString=b.toString),h(b,"valueOf")&&(a.valueOf=b.valueOf),a}function j(a,b,c,d){return qb(a,b,c,d,!0).utc()}function k(){ +// We need to deep clone this object. +return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null}}function l(a){return null==a._pf&&(a._pf=k()),a._pf}function m(a){if(null==a._isValid){var b=l(a),c=nd.call(b.parsedDateParts,function(a){return null!=a}),d=!isNaN(a._d.getTime())&&b.overflow<0&&!b.empty&&!b.invalidMonth&&!b.invalidWeekday&&!b.nullInput&&!b.invalidFormat&&!b.userInvalidated&&(!b.meridiem||b.meridiem&&c);if(a._strict&&(d=d&&0===b.charsLeftOver&&0===b.unusedTokens.length&&void 0===b.bigHour),null!=Object.isFrozen&&Object.isFrozen(a))return d;a._isValid=d}return a._isValid}function n(a){var b=j(NaN);return null!=a?i(l(b),a):l(b).userInvalidated=!0,b}function o(a){return void 0===a}function p(a,b){var c,d,e;if(o(b._isAMomentObject)||(a._isAMomentObject=b._isAMomentObject),o(b._i)||(a._i=b._i),o(b._f)||(a._f=b._f),o(b._l)||(a._l=b._l),o(b._strict)||(a._strict=b._strict),o(b._tzm)||(a._tzm=b._tzm),o(b._isUTC)||(a._isUTC=b._isUTC),o(b._offset)||(a._offset=b._offset),o(b._pf)||(a._pf=l(b)),o(b._locale)||(a._locale=b._locale),od.length>0)for(c in od)d=od[c],e=b[d],o(e)||(a[d]=e);return a} +// Moment prototype object +function q(b){p(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN), +// Prevent infinite loop in case updateOffset creates new moment +// objects. +pd===!1&&(pd=!0,a.updateOffset(this),pd=!1)}function r(a){return a instanceof q||null!=a&&null!=a._isAMomentObject}function s(a){return a<0?Math.ceil(a)||0:Math.floor(a)}function t(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=s(b)),c} +// compare two arrays, return the number of differences +function u(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;d<e;d++)(c&&a[d]!==b[d]||!c&&t(a[d])!==t(b[d]))&&g++;return g+f}function v(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function w(b,c){var d=!0;return i(function(){if(null!=a.deprecationHandler&&a.deprecationHandler(null,b),d){for(var e,f=[],g=0;g<arguments.length;g++){if(e="","object"==typeof arguments[g]){e+="\n["+g+"] ";for(var h in arguments[0])e+=h+": "+arguments[0][h]+", ";e=e.slice(0,-2)}else e=arguments[g];f.push(e)}v(b+"\nArguments: "+Array.prototype.slice.call(f).join("")+"\n"+(new Error).stack),d=!1}return c.apply(this,arguments)},c)}function x(b,c){null!=a.deprecationHandler&&a.deprecationHandler(b,c),qd[b]||(v(c),qd[b]=!0)}function y(a){return a instanceof Function||"[object Function]"===Object.prototype.toString.call(a)}function z(a){var b,c;for(c in a)b=a[c],y(b)?this[c]=b:this["_"+c]=b;this._config=a, +// Lenient ordinal parsing accepts just a number in addition to +// number + (possibly) stuff coming from _ordinalParseLenient. +this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function A(a,b){var c,e=i({},a);for(c in b)h(b,c)&&(d(a[c])&&d(b[c])?(e[c]={},i(e[c],a[c]),i(e[c],b[c])):null!=b[c]?e[c]=b[c]:delete e[c]);for(c in a)h(a,c)&&!h(b,c)&&d(a[c])&&( +// make sure changes to properties don't modify parent config +e[c]=i({},e[c]));return e}function B(a){null!=a&&this.set(a)}function C(a,b,c){var d=this._calendar[a]||this._calendar.sameElse;return y(d)?d.call(b,c):d}function D(a){var b=this._longDateFormat[a],c=this._longDateFormat[a.toUpperCase()];return b||!c?b:(this._longDateFormat[a]=c.replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a])}function E(){return this._invalidDate}function F(a){return this._ordinal.replace("%d",a)}function G(a,b,c,d){var e=this._relativeTime[c];return y(e)?e(a,b,c,d):e.replace(/%d/i,a)}function H(a,b){var c=this._relativeTime[a>0?"future":"past"];return y(c)?c(b):c.replace(/%s/i,b)}function I(a,b){var c=a.toLowerCase();zd[c]=zd[c+"s"]=zd[b]=a}function J(a){return"string"==typeof a?zd[a]||zd[a.toLowerCase()]:void 0}function K(a){var b,c,d={};for(c in a)h(a,c)&&(b=J(c),b&&(d[b]=a[c]));return d}function L(a,b){Ad[a]=b}function M(a){var b=[];for(var c in a)b.push({unit:c,priority:Ad[c]});return b.sort(function(a,b){return a.priority-b.priority}),b}function N(b,c){return function(d){return null!=d?(P(this,b,d),a.updateOffset(this,c),this):O(this,b)}}function O(a,b){return a.isValid()?a._d["get"+(a._isUTC?"UTC":"")+b]():NaN}function P(a,b,c){a.isValid()&&a._d["set"+(a._isUTC?"UTC":"")+b](c)} +// MOMENTS +function Q(a){return a=J(a),y(this[a])?this[a]():this}function R(a,b){if("object"==typeof a){a=K(a);for(var c=M(a),d=0;d<c.length;d++)this[c[d].unit](a[c[d].unit])}else if(a=J(a),y(this[a]))return this[a](b);return this}function S(a,b,c){var d=""+Math.abs(a),e=b-d.length,f=a>=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d} +// token: 'M' +// padded: ['MM', 2] +// ordinal: 'Mo' +// callback: function () { this.month() + 1 } +function T(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Ed[a]=e),b&&(Ed[b[0]]=function(){return S(e.apply(this,arguments),b[1],b[2])}),c&&(Ed[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function U(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function V(a){var b,c,d=a.match(Bd);for(b=0,c=d.length;b<c;b++)Ed[d[b]]?d[b]=Ed[d[b]]:d[b]=U(d[b]);return function(b){var e,f="";for(e=0;e<c;e++)f+=d[e]instanceof Function?d[e].call(b,a):d[e];return f}} +// format date using native date object +function W(a,b){return a.isValid()?(b=X(b,a.localeData()),Dd[b]=Dd[b]||V(b),Dd[b](a)):a.localeData().invalidDate()}function X(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Cd.lastIndex=0;d>=0&&Cd.test(a);)a=a.replace(Cd,c),Cd.lastIndex=0,d-=1;return a}function Y(a,b,c){Wd[a]=y(b)?b:function(a,d){return a&&c?c:b}}function Z(a,b){return h(Wd,a)?Wd[a](b._strict,b._locale):new RegExp($(a))} +// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript +function $(a){return _(a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}))}function _(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function aa(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),"number"==typeof b&&(d=function(a,c){c[b]=t(a)}),c=0;c<a.length;c++)Xd[a[c]]=d}function ba(a,b){aa(a,function(a,c,d,e){d._w=d._w||{},b(a,d._w,d,e)})}function ca(a,b,c){null!=b&&h(Xd,a)&&Xd[a](b,c._a,c,a)}function da(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function ea(a,b){return a?c(this._months)?this._months[a.month()]:this._months[(this._months.isFormat||fe).test(b)?"format":"standalone"][a.month()]:this._months}function fa(a,b){return a?c(this._monthsShort)?this._monthsShort[a.month()]:this._monthsShort[fe.test(b)?"format":"standalone"][a.month()]:this._monthsShort}function ga(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._monthsParse)for( +// this is not used +this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],d=0;d<12;++d)f=j([2e3,d]),this._shortMonthsParse[d]=this.monthsShort(f,"").toLocaleLowerCase(),this._longMonthsParse[d]=this.months(f,"").toLocaleLowerCase();return c?"MMM"===b?(e=sd.call(this._shortMonthsParse,g),e!==-1?e:null):(e=sd.call(this._longMonthsParse,g),e!==-1?e:null):"MMM"===b?(e=sd.call(this._shortMonthsParse,g),e!==-1?e:(e=sd.call(this._longMonthsParse,g),e!==-1?e:null)):(e=sd.call(this._longMonthsParse,g),e!==-1?e:(e=sd.call(this._shortMonthsParse,g),e!==-1?e:null))}function ha(a,b,c){var d,e,f;if(this._monthsParseExact)return ga.call(this,a,b,c); +// TODO: add sorting +// Sorting makes sure if one month (or abbr) is a prefix of another +// see sorting in computeMonthsParse +for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),d=0;d<12;d++){ +// test the regex +if( +// make the regex if we don't have it already +e=j([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}} +// MOMENTS +function ia(a,b){var c;if(!a.isValid()) +// No op +return a;if("string"==typeof b)if(/^\d+$/.test(b))b=t(b);else +// TODO: Another silent failure? +if(b=a.localeData().monthsParse(b),"number"!=typeof b)return a;return c=Math.min(a.date(),da(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a}function ja(b){return null!=b?(ia(this,b),a.updateOffset(this,!0),this):O(this,"Month")}function ka(){return da(this.year(),this.month())}function la(a){return this._monthsParseExact?(h(this,"_monthsRegex")||na.call(this),a?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,"_monthsShortRegex")||(this._monthsShortRegex=ie),this._monthsShortStrictRegex&&a?this._monthsShortStrictRegex:this._monthsShortRegex)}function ma(a){return this._monthsParseExact?(h(this,"_monthsRegex")||na.call(this),a?this._monthsStrictRegex:this._monthsRegex):(h(this,"_monthsRegex")||(this._monthsRegex=je),this._monthsStrictRegex&&a?this._monthsStrictRegex:this._monthsRegex)}function na(){function a(a,b){return b.length-a.length}var b,c,d=[],e=[],f=[];for(b=0;b<12;b++) +// make the regex if we don't have it already +c=j([2e3,b]),d.push(this.monthsShort(c,"")),e.push(this.months(c,"")),f.push(this.months(c,"")),f.push(this.monthsShort(c,""));for( +// Sorting makes sure if one month (or abbr) is a prefix of another it +// will match the longer piece. +d.sort(a),e.sort(a),f.sort(a),b=0;b<12;b++)d[b]=_(d[b]),e[b]=_(e[b]);for(b=0;b<24;b++)f[b]=_(f[b]);this._monthsRegex=new RegExp("^("+f.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+e.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+d.join("|")+")","i")} +// HELPERS +function oa(a){return pa(a)?366:365}function pa(a){return a%4===0&&a%100!==0||a%400===0}function qa(){return pa(this.year())}function ra(a,b,c,d,e,f,g){ +//can't just apply() to create a date: +//http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply +var h=new Date(a,b,c,d,e,f,g); +//the date constructor remaps years 0-99 to 1900-1999 +return a<100&&a>=0&&isFinite(h.getFullYear())&&h.setFullYear(a),h}function sa(a){var b=new Date(Date.UTC.apply(null,arguments)); +//the Date.UTC function remaps years 0-99 to 1900-1999 +return a<100&&a>=0&&isFinite(b.getUTCFullYear())&&b.setUTCFullYear(a),b} +// start-of-first-week - start-of-year +function ta(a,b,c){var// first-week day -- which january is always in the first week (4 for iso, 1 for other) +d=7+b-c, +// first-week day local weekday -- which local weekday is fwd +e=(7+sa(a,0,d).getUTCDay()-b)%7;return-e+d-1} +//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday +function ua(a,b,c,d,e){var f,g,h=(7+c-d)%7,i=ta(a,d,e),j=1+7*(b-1)+h+i;return j<=0?(f=a-1,g=oa(f)+j):j>oa(a)?(f=a+1,g=j-oa(a)):(f=a,g=j),{year:f,dayOfYear:g}}function va(a,b,c){var d,e,f=ta(a.year(),b,c),g=Math.floor((a.dayOfYear()-f-1)/7)+1;return g<1?(e=a.year()-1,d=g+wa(e,b,c)):g>wa(a.year(),b,c)?(d=g-wa(a.year(),b,c),e=a.year()+1):(e=a.year(),d=g),{week:d,year:e}}function wa(a,b,c){var d=ta(a,b,c),e=ta(a+1,b,c);return(oa(a)-d+e)/7} +// HELPERS +// LOCALES +function xa(a){return va(a,this._week.dow,this._week.doy).week}function ya(){return this._week.dow}function za(){return this._week.doy} +// MOMENTS +function Aa(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function Ba(a){var b=va(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")} +// HELPERS +function Ca(a,b){return"string"!=typeof a?a:isNaN(a)?(a=b.weekdaysParse(a),"number"==typeof a?a:null):parseInt(a,10)}function Da(a,b){return"string"==typeof a?b.weekdaysParse(a)%7||7:isNaN(a)?null:a}function Ea(a,b){return a?c(this._weekdays)?this._weekdays[a.day()]:this._weekdays[this._weekdays.isFormat.test(b)?"format":"standalone"][a.day()]:this._weekdays}function Fa(a){return a?this._weekdaysShort[a.day()]:this._weekdaysShort}function Ga(a){return a?this._weekdaysMin[a.day()]:this._weekdaysMin}function Ha(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],d=0;d<7;++d)f=j([2e3,1]).day(d),this._minWeekdaysParse[d]=this.weekdaysMin(f,"").toLocaleLowerCase(),this._shortWeekdaysParse[d]=this.weekdaysShort(f,"").toLocaleLowerCase(),this._weekdaysParse[d]=this.weekdays(f,"").toLocaleLowerCase();return c?"dddd"===b?(e=sd.call(this._weekdaysParse,g),e!==-1?e:null):"ddd"===b?(e=sd.call(this._shortWeekdaysParse,g),e!==-1?e:null):(e=sd.call(this._minWeekdaysParse,g),e!==-1?e:null):"dddd"===b?(e=sd.call(this._weekdaysParse,g),e!==-1?e:(e=sd.call(this._shortWeekdaysParse,g),e!==-1?e:(e=sd.call(this._minWeekdaysParse,g),e!==-1?e:null))):"ddd"===b?(e=sd.call(this._shortWeekdaysParse,g),e!==-1?e:(e=sd.call(this._weekdaysParse,g),e!==-1?e:(e=sd.call(this._minWeekdaysParse,g),e!==-1?e:null))):(e=sd.call(this._minWeekdaysParse,g),e!==-1?e:(e=sd.call(this._weekdaysParse,g),e!==-1?e:(e=sd.call(this._shortWeekdaysParse,g),e!==-1?e:null)))}function Ia(a,b,c){var d,e,f;if(this._weekdaysParseExact)return Ha.call(this,a,b,c);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),d=0;d<7;d++){ +// test the regex +if( +// make the regex if we don't have it already +e=j([2e3,1]).day(d),c&&!this._fullWeekdaysParse[d]&&(this._fullWeekdaysParse[d]=new RegExp("^"+this.weekdays(e,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[d]=new RegExp("^"+this.weekdaysShort(e,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[d]=new RegExp("^"+this.weekdaysMin(e,"").replace(".",".?")+"$","i")),this._weekdaysParse[d]||(f="^"+this.weekdays(e,"")+"|^"+this.weekdaysShort(e,"")+"|^"+this.weekdaysMin(e,""),this._weekdaysParse[d]=new RegExp(f.replace(".",""),"i")),c&&"dddd"===b&&this._fullWeekdaysParse[d].test(a))return d;if(c&&"ddd"===b&&this._shortWeekdaysParse[d].test(a))return d;if(c&&"dd"===b&&this._minWeekdaysParse[d].test(a))return d;if(!c&&this._weekdaysParse[d].test(a))return d}} +// MOMENTS +function Ja(a){if(!this.isValid())return null!=a?this:NaN;var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Ca(a,this.localeData()),this.add(a-b,"d")):b}function Ka(a){if(!this.isValid())return null!=a?this:NaN;var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function La(a){if(!this.isValid())return null!=a?this:NaN; +// behaves the same as moment#day except +// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) +// as a setter, sunday should belong to the previous week. +if(null!=a){var b=Da(a,this.localeData());return this.day(this.day()%7?b:b-7)}return this.day()||7}function Ma(a){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Pa.call(this),a?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,"_weekdaysRegex")||(this._weekdaysRegex=pe),this._weekdaysStrictRegex&&a?this._weekdaysStrictRegex:this._weekdaysRegex)}function Na(a){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Pa.call(this),a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=qe),this._weekdaysShortStrictRegex&&a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function Oa(a){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Pa.call(this),a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=re),this._weekdaysMinStrictRegex&&a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function Pa(){function a(a,b){return b.length-a.length}var b,c,d,e,f,g=[],h=[],i=[],k=[];for(b=0;b<7;b++) +// make the regex if we don't have it already +c=j([2e3,1]).day(b),d=this.weekdaysMin(c,""),e=this.weekdaysShort(c,""),f=this.weekdays(c,""),g.push(d),h.push(e),i.push(f),k.push(d),k.push(e),k.push(f);for( +// Sorting makes sure if one weekday (or abbr) is a prefix of another it +// will match the longer piece. +g.sort(a),h.sort(a),i.sort(a),k.sort(a),b=0;b<7;b++)h[b]=_(h[b]),i[b]=_(i[b]),k[b]=_(k[b]);this._weekdaysRegex=new RegExp("^("+k.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+h.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+g.join("|")+")","i")} +// FORMATTING +function Qa(){return this.hours()%12||12}function Ra(){return this.hours()||24}function Sa(a,b){T(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})} +// PARSING +function Ta(a,b){return b._meridiemParse} +// LOCALES +function Ua(a){ +// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays +// Using charAt should be more compatible. +return"p"===(a+"").toLowerCase().charAt(0)}function Va(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Wa(a){return a?a.toLowerCase().replace("_","-"):a} +// pick the locale from the array +// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each +// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root +function Xa(a){for(var b,c,d,e,f=0;f<a.length;){for(e=Wa(a[f]).split("-"),b=e.length,c=Wa(a[f+1]),c=c?c.split("-"):null;b>0;){if(d=Ya(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&u(e,c,!0)>=b-1) +//the next array item is better than a shallower substring of this one +break;b--}f++}return null}function Ya(a){var b=null; +// TODO: Find a better way to register and load all the locales in Node +if(!we[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=se._abbr,require("./locale/"+a), +// because defineLocale currently also sets the global locale, we +// want to undo that for lazy loaded locales +Za(b)}catch(a){}return we[a]} +// This function will load locale and then set the global locale. If +// no arguments are passed in, it will simply return the current global +// locale key. +function Za(a,b){var c; +// moment.duration._locale = moment._locale = data; +return a&&(c=o(b)?ab(a):$a(a,b),c&&(se=c)),se._abbr}function $a(a,b){if(null!==b){var c=ve; +// treat as if there is no base config +// backwards compat for now: also set the locale +return b.abbr=a,null!=we[a]?(x("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),c=we[a]._config):null!=b.parentLocale&&(null!=we[b.parentLocale]?c=we[b.parentLocale]._config:x("parentLocaleUndefined","specified parentLocale is not defined yet. See http://momentjs.com/guides/#/warnings/parent-locale/")),we[a]=new B(A(c,b)),Za(a),we[a]} +// useful for testing +return delete we[a],null}function _a(a,b){if(null!=b){var c,d=ve; +// MERGE +null!=we[a]&&(d=we[a]._config),b=A(d,b),c=new B(b),c.parentLocale=we[a],we[a]=c, +// backwards compat for now: also set the locale +Za(a)}else +// pass null for config to unupdate, useful for tests +null!=we[a]&&(null!=we[a].parentLocale?we[a]=we[a].parentLocale:null!=we[a]&&delete we[a]);return we[a]} +// returns locale data +function ab(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return se;if(!c(a)){if( +//short-circuit everything else +b=Ya(a))return b;a=[a]}return Xa(a)}function bb(){return rd(we)}function cb(a){var b,c=a._a;return c&&l(a).overflow===-2&&(b=c[Zd]<0||c[Zd]>11?Zd:c[$d]<1||c[$d]>da(c[Yd],c[Zd])?$d:c[_d]<0||c[_d]>24||24===c[_d]&&(0!==c[ae]||0!==c[be]||0!==c[ce])?_d:c[ae]<0||c[ae]>59?ae:c[be]<0||c[be]>59?be:c[ce]<0||c[ce]>999?ce:-1,l(a)._overflowDayOfYear&&(b<Yd||b>$d)&&(b=$d),l(a)._overflowWeeks&&b===-1&&(b=de),l(a)._overflowWeekday&&b===-1&&(b=ee),l(a).overflow=b),a} +// date from iso format +function db(a){var b,c,d,e,f,g,h=a._i,i=xe.exec(h)||ye.exec(h);if(i){for(l(a).iso=!0,b=0,c=Ae.length;b<c;b++)if(Ae[b][1].exec(i[1])){e=Ae[b][0],d=Ae[b][2]!==!1;break}if(null==e)return void(a._isValid=!1);if(i[3]){for(b=0,c=Be.length;b<c;b++)if(Be[b][1].exec(i[3])){ +// match[2] should be 'T' or space +f=(i[2]||" ")+Be[b][0];break}if(null==f)return void(a._isValid=!1)}if(!d&&null!=f)return void(a._isValid=!1);if(i[4]){if(!ze.exec(i[4]))return void(a._isValid=!1);g="Z"}a._f=e+(f||"")+(g||""),jb(a)}else a._isValid=!1} +// date from iso format or fallback +function eb(b){var c=Ce.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(db(b),void(b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b))))} +// Pick the first defined of two or three arguments. +function fb(a,b,c){return null!=a?a:null!=b?b:c}function gb(b){ +// hooks is actually the exported moment object +var c=new Date(a.now());return b._useUTC?[c.getUTCFullYear(),c.getUTCMonth(),c.getUTCDate()]:[c.getFullYear(),c.getMonth(),c.getDate()]} +// convert an array to a date. +// the array should mirror the parameters below +// note: all values past the year are optional and will default to the lowest possible value. +// [year, month, day , hour, minute, second, millisecond] +function hb(a){var b,c,d,e,f=[];if(!a._d){ +// Default to current date. +// * if no year, month, day of month are given, default to today +// * if day of month is given, default month and year +// * if month is given, default only year +// * if year is given, don't default anything +for(d=gb(a), +//compute day of the year from weeks and weekdays +a._w&&null==a._a[$d]&&null==a._a[Zd]&&ib(a), +//if the day of the year is set, figure out what it is +a._dayOfYear&&(e=fb(a._a[Yd],d[Yd]),a._dayOfYear>oa(e)&&(l(a)._overflowDayOfYear=!0),c=sa(e,0,a._dayOfYear),a._a[Zd]=c.getUTCMonth(),a._a[$d]=c.getUTCDate()),b=0;b<3&&null==a._a[b];++b)a._a[b]=f[b]=d[b]; +// Zero out whatever was not defaulted, including time +for(;b<7;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b]; +// Check for 24:00:00.000 +24===a._a[_d]&&0===a._a[ae]&&0===a._a[be]&&0===a._a[ce]&&(a._nextDay=!0,a._a[_d]=0),a._d=(a._useUTC?sa:ra).apply(null,f), +// Apply timezone offset from input. The actual utcOffset can be changed +// with parseZone. +null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[_d]=24)}}function ib(a){var b,c,d,e,f,g,h,i;b=a._w,null!=b.GG||null!=b.W||null!=b.E?(f=1,g=4, +// TODO: We need to take the current isoWeekYear, but that depends on +// how we interpret now (local, utc, fixed offset). So create +// a now version of current config (take local/utc/offset flags, and +// create now). +c=fb(b.GG,a._a[Yd],va(rb(),1,4).year),d=fb(b.W,1),e=fb(b.E,1),(e<1||e>7)&&(i=!0)):(f=a._locale._week.dow,g=a._locale._week.doy,c=fb(b.gg,a._a[Yd],va(rb(),f,g).year),d=fb(b.w,1),null!=b.d?( +// weekday -- low day numbers are considered next week +e=b.d,(e<0||e>6)&&(i=!0)):null!=b.e?( +// local weekday -- counting starts from begining of week +e=b.e+f,(b.e<0||b.e>6)&&(i=!0)): +// default to begining of week +e=f),d<1||d>wa(c,f,g)?l(a)._overflowWeeks=!0:null!=i?l(a)._overflowWeekday=!0:(h=ua(c,d,e,f,g),a._a[Yd]=h.year,a._dayOfYear=h.dayOfYear)} +// date from string and format string +function jb(b){ +// TODO: Move this to another part of the creation flow to prevent circular deps +if(b._f===a.ISO_8601)return void db(b);b._a=[],l(b).empty=!0; +// This array is used to make a Date, either with `new Date` or `Date.UTC` +var c,d,e,f,g,h=""+b._i,i=h.length,j=0;for(e=X(b._f,b._locale).match(Bd)||[],c=0;c<e.length;c++)f=e[c],d=(h.match(Z(f,b))||[])[0], +// console.log('token', token, 'parsedInput', parsedInput, +// 'regex', getParseRegexForToken(token, config)); +d&&(g=h.substr(0,h.indexOf(d)),g.length>0&&l(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),j+=d.length), +// don't parse if it's not a known token +Ed[f]?(d?l(b).empty=!1:l(b).unusedTokens.push(f),ca(f,d,b)):b._strict&&!d&&l(b).unusedTokens.push(f); +// add remaining unparsed input length to the string +l(b).charsLeftOver=i-j,h.length>0&&l(b).unusedInput.push(h), +// clear _12h flag if hour is <= 12 +b._a[_d]<=12&&l(b).bigHour===!0&&b._a[_d]>0&&(l(b).bigHour=void 0),l(b).parsedDateParts=b._a.slice(0),l(b).meridiem=b._meridiem, +// handle meridiem +b._a[_d]=kb(b._locale,b._a[_d],b._meridiem),hb(b),cb(b)}function kb(a,b,c){var d; +// Fallback +return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&b<12&&(b+=12),d||12!==b||(b=0),b):b} +// date from string and array of format strings +function lb(a){var b,c,d,e,f;if(0===a._f.length)return l(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;e<a._f.length;e++)f=0,b=p({},a),null!=a._useUTC&&(b._useUTC=a._useUTC),b._f=a._f[e],jb(b),m(b)&&( +// if there is any input that was not parsed add a penalty for that format +f+=l(b).charsLeftOver, +//or tokens +f+=10*l(b).unusedTokens.length,l(b).score=f,(null==d||f<d)&&(d=f,c=b));i(a,c||b)}function mb(a){if(!a._d){var b=K(a._i);a._a=g([b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],function(a){return a&&parseInt(a,10)}),hb(a)}}function nb(a){var b=new q(cb(ob(a))); +// Adding is smart enough around DST +return b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b}function ob(a){var b=a._i,d=a._f;return a._locale=a._locale||ab(a._l),null===b||void 0===d&&""===b?n({nullInput:!0}):("string"==typeof b&&(a._i=b=a._locale.preparse(b)),r(b)?new q(cb(b)):(c(d)?lb(a):f(b)?a._d=b:d?jb(a):pb(a),m(a)||(a._d=null),a))}function pb(b){var d=b._i;void 0===d?b._d=new Date(a.now()):f(d)?b._d=new Date(d.valueOf()):"string"==typeof d?eb(b):c(d)?(b._a=g(d.slice(0),function(a){return parseInt(a,10)}),hb(b)):"object"==typeof d?mb(b):"number"==typeof d? +// from milliseconds +b._d=new Date(d):a.createFromInputFallback(b)}function qb(a,b,f,g,h){var i={}; +// object construction must be done this way. +// https://github.com/moment/moment/issues/1423 +return"boolean"==typeof f&&(g=f,f=void 0),(d(a)&&e(a)||c(a)&&0===a.length)&&(a=void 0),i._isAMomentObject=!0,i._useUTC=i._isUTC=h,i._l=f,i._i=a,i._f=b,i._strict=g,nb(i)}function rb(a,b,c,d){return qb(a,b,c,d,!1)} +// Pick a moment m from moments so that m[fn](other) is true for all +// other. This relies on the function fn to be transitive. +// +// moments should either be an array of moment objects or an array, whose +// first element is an array of moment objects. +function sb(a,b){var d,e;if(1===b.length&&c(b[0])&&(b=b[0]),!b.length)return rb();for(d=b[0],e=1;e<b.length;++e)b[e].isValid()&&!b[e][a](d)||(d=b[e]);return d} +// TODO: Use [].sort instead? +function tb(){var a=[].slice.call(arguments,0);return sb("isBefore",a)}function ub(){var a=[].slice.call(arguments,0);return sb("isAfter",a)}function vb(a){var b=K(a),c=b.year||0,d=b.quarter||0,e=b.month||0,f=b.week||0,g=b.day||0,h=b.hour||0,i=b.minute||0,j=b.second||0,k=b.millisecond||0; +// representation for dateAddRemove +this._milliseconds=+k+1e3*j+// 1000 +6e4*i+// 1000 * 60 +1e3*h*60*60,//using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 +// Because of dateAddRemove treats 24 hours as different from a +// day when working around DST, we need to store them separately +this._days=+g+7*f, +// It is impossible translate months into days without knowing +// which months you are are talking about, so we have to store +// it separately. +this._months=+e+3*d+12*c,this._data={},this._locale=ab(),this._bubble()}function wb(a){return a instanceof vb}function xb(a){return a<0?Math.round(-1*a)*-1:Math.round(a)} +// FORMATTING +function yb(a,b){T(a,0,0,function(){var a=this.utcOffset(),c="+";return a<0&&(a=-a,c="-"),c+S(~~(a/60),2)+b+S(~~a%60,2)})}function zb(a,b){var c=(b||"").match(a)||[],d=c[c.length-1]||[],e=(d+"").match(Ge)||["-",0,0],f=+(60*e[1])+t(e[2]);return"+"===e[0]?f:-f} +// Return a moment from input, that is local/utc/zone equivalent to model. +function Ab(b,c){var d,e; +// Use low-level api, because this fn is low-level api. +return c._isUTC?(d=c.clone(),e=(r(b)||f(b)?b.valueOf():rb(b).valueOf())-d.valueOf(),d._d.setTime(d._d.valueOf()+e),a.updateOffset(d,!1),d):rb(b).local()}function Bb(a){ +// On Firefox.24 Date#getTimezoneOffset returns a floating point. +// https://github.com/moment/moment/pull/1871 +return 15*-Math.round(a._d.getTimezoneOffset()/15)} +// MOMENTS +// keepLocalTime = true means only change the timezone, without +// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> +// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset +// +0200, so we adjust the time as needed, to be valid. +// +// Keeping the time actually adds/subtracts (one hour) +// from the actual represented time. That is why we call updateOffset +// a second time. In case it wants us to change the offset again +// _changeInProgress == true case, then we have to adjust, because +// there is no such time in the given timezone. +function Cb(b,c){var d,e=this._offset||0;return this.isValid()?null!=b?("string"==typeof b?b=zb(Td,b):Math.abs(b)<16&&(b=60*b),!this._isUTC&&c&&(d=Bb(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?Sb(this,Nb(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?e:Bb(this):null!=b?this:NaN}function Db(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Eb(a){return this.utcOffset(0,a)}function Fb(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Bb(this),"m")),this}function Gb(){if(this._tzm)this.utcOffset(this._tzm);else if("string"==typeof this._i){var a=zb(Sd,this._i);0===a?this.utcOffset(0,!0):this.utcOffset(zb(Sd,this._i))}return this}function Hb(a){return!!this.isValid()&&(a=a?rb(a).utcOffset():0,(this.utcOffset()-a)%60===0)}function Ib(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Jb(){if(!o(this._isDSTShifted))return this._isDSTShifted;var a={};if(p(a,this),a=ob(a),a._a){var b=a._isUTC?j(a._a):rb(a._a);this._isDSTShifted=this.isValid()&&u(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Kb(){return!!this.isValid()&&!this._isUTC}function Lb(){return!!this.isValid()&&this._isUTC}function Mb(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}function Nb(a,b){var c,d,e,f=a, +// matching against regexp is expensive, do it on demand +g=null;// checks for null or undefined +return wb(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(f={},b?f[b]=a:f.milliseconds=a):(g=He.exec(a))?(c="-"===g[1]?-1:1,f={y:0,d:t(g[$d])*c,h:t(g[_d])*c,m:t(g[ae])*c,s:t(g[be])*c,ms:t(xb(1e3*g[ce]))*c}):(g=Ie.exec(a))?(c="-"===g[1]?-1:1,f={y:Ob(g[2],c),M:Ob(g[3],c),w:Ob(g[4],c),d:Ob(g[5],c),h:Ob(g[6],c),m:Ob(g[7],c),s:Ob(g[8],c)}):null==f?f={}:"object"==typeof f&&("from"in f||"to"in f)&&(e=Qb(rb(f.from),rb(f.to)),f={},f.ms=e.milliseconds,f.M=e.months),d=new vb(f),wb(a)&&h(a,"_locale")&&(d._locale=a._locale),d}function Ob(a,b){ +// We'd normally use ~~inp for this, but unfortunately it also +// converts floats to ints. +// inp may be undefined, so careful calling replace on it. +var c=a&&parseFloat(a.replace(",",".")); +// apply sign while we're at it +return(isNaN(c)?0:c)*b}function Pb(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function Qb(a,b){var c;return a.isValid()&&b.isValid()?(b=Ab(b,a),a.isBefore(b)?c=Pb(a,b):(c=Pb(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c):{milliseconds:0,months:0}} +// TODO: remove 'name' arg after deprecation is removed +function Rb(a,b){return function(c,d){var e,f; +//invert the arguments, but complain about it +return null===d||isNaN(+d)||(x(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Nb(c,d),Sb(this,e,a),this}}function Sb(b,c,d,e){var f=c._milliseconds,g=xb(c._days),h=xb(c._months);b.isValid()&&(e=null==e||e,f&&b._d.setTime(b._d.valueOf()+f*d),g&&P(b,"Date",O(b,"Date")+g*d),h&&ia(b,O(b,"Month")+h*d),e&&a.updateOffset(b,g||h))}function Tb(a,b){var c=a.diff(b,"days",!0);return c<-6?"sameElse":c<-1?"lastWeek":c<0?"lastDay":c<1?"sameDay":c<2?"nextDay":c<7?"nextWeek":"sameElse"}function Ub(b,c){ +// We want to compare the start of today, vs this. +// Getting start-of-today depends on whether we're local/utc/offset or not. +var d=b||rb(),e=Ab(d,this).startOf("day"),f=a.calendarFormat(this,e)||"sameElse",g=c&&(y(c[f])?c[f].call(this,d):c[f]);return this.format(g||this.localeData().calendar(f,this,rb(d)))}function Vb(){return new q(this)}function Wb(a,b){var c=r(a)?a:rb(a);return!(!this.isValid()||!c.isValid())&&(b=J(o(b)?"millisecond":b),"millisecond"===b?this.valueOf()>c.valueOf():c.valueOf()<this.clone().startOf(b).valueOf())}function Xb(a,b){var c=r(a)?a:rb(a);return!(!this.isValid()||!c.isValid())&&(b=J(o(b)?"millisecond":b),"millisecond"===b?this.valueOf()<c.valueOf():this.clone().endOf(b).valueOf()<c.valueOf())}function Yb(a,b,c,d){return d=d||"()",("("===d[0]?this.isAfter(a,c):!this.isBefore(a,c))&&(")"===d[1]?this.isBefore(b,c):!this.isAfter(b,c))}function Zb(a,b){var c,d=r(a)?a:rb(a);return!(!this.isValid()||!d.isValid())&&(b=J(b||"millisecond"),"millisecond"===b?this.valueOf()===d.valueOf():(c=d.valueOf(),this.clone().startOf(b).valueOf()<=c&&c<=this.clone().endOf(b).valueOf()))}function $b(a,b){return this.isSame(a,b)||this.isAfter(a,b)}function _b(a,b){return this.isSame(a,b)||this.isBefore(a,b)}function ac(a,b,c){var d,e,f,g;// 1000 +// 1000 * 60 +// 1000 * 60 * 60 +// 1000 * 60 * 60 * 24, negate dst +// 1000 * 60 * 60 * 24 * 7, negate dst +return this.isValid()?(d=Ab(a,this),d.isValid()?(e=6e4*(d.utcOffset()-this.utcOffset()),b=J(b),"year"===b||"month"===b||"quarter"===b?(g=bc(this,d),"quarter"===b?g/=3:"year"===b&&(g/=12)):(f=this-d,g="second"===b?f/1e3:"minute"===b?f/6e4:"hour"===b?f/36e5:"day"===b?(f-e)/864e5:"week"===b?(f-e)/6048e5:f),c?g:s(g)):NaN):NaN}function bc(a,b){ +// difference in months +var c,d,e=12*(b.year()-a.year())+(b.month()-a.month()), +// b is in (anchor - 1 month, anchor + 1 month) +f=a.clone().add(e,"months"); +//check for negative zero, return zero if negative zero +// linear across the month +// linear across the month +return b-f<0?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)||0}function cc(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function dc(){var a=this.clone().utc();return 0<a.year()&&a.year()<=9999?y(Date.prototype.toISOString)?this.toDate().toISOString():W(a,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):W(a,"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]")}function ec(b){b||(b=this.isUtc()?a.defaultFormatUtc:a.defaultFormat);var c=W(this,b);return this.localeData().postformat(c)}function fc(a,b){return this.isValid()&&(r(a)&&a.isValid()||rb(a).isValid())?Nb({to:this,from:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function gc(a){return this.from(rb(),a)}function hc(a,b){return this.isValid()&&(r(a)&&a.isValid()||rb(a).isValid())?Nb({from:this,to:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function ic(a){return this.to(rb(),a)} +// If passed a locale key, it will set the locale for this +// instance. Otherwise, it will return the locale configuration +// variables for this instance. +function jc(a){var b;return void 0===a?this._locale._abbr:(b=ab(a),null!=b&&(this._locale=b),this)}function kc(){return this._locale}function lc(a){ +// the following switch intentionally omits break keywords +// to utilize falling through the cases. +switch(a=J(a)){case"year":this.month(0);/* falls through */ +case"quarter":case"month":this.date(1);/* falls through */ +case"week":case"isoWeek":case"day":case"date":this.hours(0);/* falls through */ +case"hour":this.minutes(0);/* falls through */ +case"minute":this.seconds(0);/* falls through */ +case"second":this.milliseconds(0)} +// weeks are a special case +// quarters are also special +return"week"===a&&this.weekday(0),"isoWeek"===a&&this.isoWeekday(1),"quarter"===a&&this.month(3*Math.floor(this.month()/3)),this}function mc(a){ +// 'date' is an alias for 'day', so it should be considered as such. +return a=J(a),void 0===a||"millisecond"===a?this:("date"===a&&(a="day"),this.startOf(a).add(1,"isoWeek"===a?"week":a).subtract(1,"ms"))}function nc(){return this._d.valueOf()-6e4*(this._offset||0)}function oc(){return Math.floor(this.valueOf()/1e3)}function pc(){return new Date(this.valueOf())}function qc(){var a=this;return[a.year(),a.month(),a.date(),a.hour(),a.minute(),a.second(),a.millisecond()]}function rc(){var a=this;return{years:a.year(),months:a.month(),date:a.date(),hours:a.hours(),minutes:a.minutes(),seconds:a.seconds(),milliseconds:a.milliseconds()}}function sc(){ +// new Date(NaN).toJSON() === null +return this.isValid()?this.toISOString():null}function tc(){return m(this)}function uc(){return i({},l(this))}function vc(){return l(this).overflow}function wc(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}}function xc(a,b){T(0,[a,a.length],0,b)} +// MOMENTS +function yc(a){return Cc.call(this,a,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)}function zc(a){return Cc.call(this,a,this.isoWeek(),this.isoWeekday(),1,4)}function Ac(){return wa(this.year(),1,4)}function Bc(){var a=this.localeData()._week;return wa(this.year(),a.dow,a.doy)}function Cc(a,b,c,d,e){var f;return null==a?va(this,d,e).year:(f=wa(a,d,e),b>f&&(b=f),Dc.call(this,a,b,c,d,e))}function Dc(a,b,c,d,e){var f=ua(a,b,c,d,e),g=sa(f.year,0,f.dayOfYear);return this.year(g.getUTCFullYear()),this.month(g.getUTCMonth()),this.date(g.getUTCDate()),this} +// MOMENTS +function Ec(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)} +// HELPERS +// MOMENTS +function Fc(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function Gc(a,b){b[ce]=t(1e3*("0."+a))} +// MOMENTS +function Hc(){return this._isUTC?"UTC":""}function Ic(){return this._isUTC?"Coordinated Universal Time":""}function Jc(a){return rb(1e3*a)}function Kc(){return rb.apply(null,arguments).parseZone()}function Lc(a){return a}function Mc(a,b,c,d){var e=ab(),f=j().set(d,b);return e[c](f,a)}function Nc(a,b,c){if("number"==typeof a&&(b=a,a=void 0),a=a||"",null!=b)return Mc(a,b,c,"month");var d,e=[];for(d=0;d<12;d++)e[d]=Mc(a,d,c,"month");return e} +// () +// (5) +// (fmt, 5) +// (fmt) +// (true) +// (true, 5) +// (true, fmt, 5) +// (true, fmt) +function Oc(a,b,c,d){"boolean"==typeof a?("number"==typeof b&&(c=b,b=void 0),b=b||""):(b=a,c=b,a=!1,"number"==typeof b&&(c=b,b=void 0),b=b||"");var e=ab(),f=a?e._week.dow:0;if(null!=c)return Mc(b,(c+f)%7,d,"day");var g,h=[];for(g=0;g<7;g++)h[g]=Mc(b,(g+f)%7,d,"day");return h}function Pc(a,b){return Nc(a,b,"months")}function Qc(a,b){return Nc(a,b,"monthsShort")}function Rc(a,b,c){return Oc(a,b,c,"weekdays")}function Sc(a,b,c){return Oc(a,b,c,"weekdaysShort")}function Tc(a,b,c){return Oc(a,b,c,"weekdaysMin")}function Uc(){var a=this._data;return this._milliseconds=Ue(this._milliseconds),this._days=Ue(this._days),this._months=Ue(this._months),a.milliseconds=Ue(a.milliseconds),a.seconds=Ue(a.seconds),a.minutes=Ue(a.minutes),a.hours=Ue(a.hours),a.months=Ue(a.months),a.years=Ue(a.years),this}function Vc(a,b,c,d){var e=Nb(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()} +// supports only 2.0-style add(1, 's') or add(duration) +function Wc(a,b){return Vc(this,a,b,1)} +// supports only 2.0-style subtract(1, 's') or subtract(duration) +function Xc(a,b){return Vc(this,a,b,-1)}function Yc(a){return a<0?Math.floor(a):Math.ceil(a)}function Zc(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data; +// if we have a mix of positive and negative values, bubble down first +// check: https://github.com/moment/moment/issues/2166 +// The following code bubbles up values, see the tests for +// examples of what that means. +// convert days to months +// 12 months -> 1 year +return f>=0&&g>=0&&h>=0||f<=0&&g<=0&&h<=0||(f+=864e5*Yc(_c(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=s(f/1e3),i.seconds=a%60,b=s(a/60),i.minutes=b%60,c=s(b/60),i.hours=c%24,g+=s(c/24),e=s($c(g)),h+=e,g-=Yc(_c(e)),d=s(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function $c(a){ +// 400 years have 146097 days (taking into account leap year rules) +// 400 years have 12 months === 4800 +return 4800*a/146097}function _c(a){ +// the reverse of daysToMonths +return 146097*a/4800}function ad(a){var b,c,d=this._milliseconds;if(a=J(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+$c(b),"month"===a?c:c/12;switch( +// handle milliseconds separately because of floating point math errors (issue #1867) +b=this._days+Math.round(_c(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3; +// Math.floor prevents floating point math errors here +case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}} +// TODO: Use this.as('ms')? +function bd(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*t(this._months/12)}function cd(a){return function(){return this.as(a)}}function dd(a){return a=J(a),this[a+"s"]()}function ed(a){return function(){return this._data[a]}}function fd(){return s(this.days()/7)} +// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize +function gd(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function hd(a,b,c){var d=Nb(a).abs(),e=jf(d.as("s")),f=jf(d.as("m")),g=jf(d.as("h")),h=jf(d.as("d")),i=jf(d.as("M")),j=jf(d.as("y")),k=e<kf.s&&["s",e]||f<=1&&["m"]||f<kf.m&&["mm",f]||g<=1&&["h"]||g<kf.h&&["hh",g]||h<=1&&["d"]||h<kf.d&&["dd",h]||i<=1&&["M"]||i<kf.M&&["MM",i]||j<=1&&["y"]||["yy",j];return k[2]=b,k[3]=+a>0,k[4]=c,gd.apply(null,k)} +// This function allows you to set the rounding function for relative time strings +function id(a){return void 0===a?jf:"function"==typeof a&&(jf=a,!0)} +// This function allows you to set a threshold for relative time strings +function jd(a,b){return void 0!==kf[a]&&(void 0===b?kf[a]:(kf[a]=b,!0))}function kd(a){var b=this.localeData(),c=hd(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function ld(){ +// for ISO strings we do not use the normal bubbling rules: +// * milliseconds bubble up until they become hours +// * days do not bubble at all +// * months bubble up until they become years +// This is because there is no context-free conversion between hours and days +// (think of clock changes) +// and also not between days and months (28-31 days per month) +var a,b,c,d=lf(this._milliseconds)/1e3,e=lf(this._days),f=lf(this._months); +// 3600 seconds -> 60 minutes -> 1 hour +a=s(d/60),b=s(a/60),d%=60,a%=60, +// 12 months -> 1 year +c=s(f/12),f%=12; +// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js +var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(m<0?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}var md,nd;nd=Array.prototype.some?Array.prototype.some:function(a){for(var b=Object(this),c=b.length>>>0,d=0;d<c;d++)if(d in b&&a.call(this,b[d],d,b))return!0;return!1}; +// Plugins that add properties should also add the key here (null value), +// so we can properly clone ourselves. +var od=a.momentProperties=[],pd=!1,qd={};a.suppressDeprecationWarnings=!1,a.deprecationHandler=null;var rd;rd=Object.keys?Object.keys:function(a){var b,c=[];for(b in a)h(a,b)&&c.push(b);return c};var sd,td={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},ud={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},vd="Invalid date",wd="%d",xd=/\d{1,2}/,yd={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},zd={},Ad={},Bd=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,Cd=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Dd={},Ed={},Fd=/\d/,Gd=/\d\d/,Hd=/\d{3}/,Id=/\d{4}/,Jd=/[+-]?\d{6}/,Kd=/\d\d?/,Ld=/\d\d\d\d?/,Md=/\d\d\d\d\d\d?/,Nd=/\d{1,3}/,Od=/\d{1,4}/,Pd=/[+-]?\d{1,6}/,Qd=/\d+/,Rd=/[+-]?\d+/,Sd=/Z|[+-]\d\d:?\d\d/gi,Td=/Z|[+-]\d\d(?::?\d\d)?/gi,Ud=/[+-]?\d+(\.\d{1,3})?/,Vd=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Wd={},Xd={},Yd=0,Zd=1,$d=2,_d=3,ae=4,be=5,ce=6,de=7,ee=8;sd=Array.prototype.indexOf?Array.prototype.indexOf:function(a){ +// I know +var b;for(b=0;b<this.length;++b)if(this[b]===a)return b;return-1}, +// FORMATTING +T("M",["MM",2],"Mo",function(){return this.month()+1}),T("MMM",0,0,function(a){return this.localeData().monthsShort(this,a)}),T("MMMM",0,0,function(a){return this.localeData().months(this,a)}), +// ALIASES +I("month","M"), +// PRIORITY +L("month",8), +// PARSING +Y("M",Kd),Y("MM",Kd,Gd),Y("MMM",function(a,b){return b.monthsShortRegex(a)}),Y("MMMM",function(a,b){return b.monthsRegex(a)}),aa(["M","MM"],function(a,b){b[Zd]=t(a)-1}),aa(["MMM","MMMM"],function(a,b,c,d){var e=c._locale.monthsParse(a,d,c._strict); +// if we didn't find a month name, mark the date as invalid. +null!=e?b[Zd]=e:l(c).invalidMonth=a}); +// LOCALES +var fe=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,ge="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),he="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),ie=Vd,je=Vd; +// FORMATTING +T("Y",0,0,function(){var a=this.year();return a<=9999?""+a:"+"+a}),T(0,["YY",2],0,function(){return this.year()%100}),T(0,["YYYY",4],0,"year"),T(0,["YYYYY",5],0,"year"),T(0,["YYYYYY",6,!0],0,"year"), +// ALIASES +I("year","y"), +// PRIORITIES +L("year",1), +// PARSING +Y("Y",Rd),Y("YY",Kd,Gd),Y("YYYY",Od,Id),Y("YYYYY",Pd,Jd),Y("YYYYYY",Pd,Jd),aa(["YYYYY","YYYYYY"],Yd),aa("YYYY",function(b,c){c[Yd]=2===b.length?a.parseTwoDigitYear(b):t(b)}),aa("YY",function(b,c){c[Yd]=a.parseTwoDigitYear(b)}),aa("Y",function(a,b){b[Yd]=parseInt(a,10)}), +// HOOKS +a.parseTwoDigitYear=function(a){return t(a)+(t(a)>68?1900:2e3)}; +// MOMENTS +var ke=N("FullYear",!0); +// FORMATTING +T("w",["ww",2],"wo","week"),T("W",["WW",2],"Wo","isoWeek"), +// ALIASES +I("week","w"),I("isoWeek","W"), +// PRIORITIES +L("week",5),L("isoWeek",5), +// PARSING +Y("w",Kd),Y("ww",Kd,Gd),Y("W",Kd),Y("WW",Kd,Gd),ba(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=t(a)});var le={dow:0,// Sunday is the first day of the week. +doy:6}; +// FORMATTING +T("d",0,"do","day"),T("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),T("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),T("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),T("e",0,0,"weekday"),T("E",0,0,"isoWeekday"), +// ALIASES +I("day","d"),I("weekday","e"),I("isoWeekday","E"), +// PRIORITY +L("day",11),L("weekday",11),L("isoWeekday",11), +// PARSING +Y("d",Kd),Y("e",Kd),Y("E",Kd),Y("dd",function(a,b){return b.weekdaysMinRegex(a)}),Y("ddd",function(a,b){return b.weekdaysShortRegex(a)}),Y("dddd",function(a,b){return b.weekdaysRegex(a)}),ba(["dd","ddd","dddd"],function(a,b,c,d){var e=c._locale.weekdaysParse(a,d,c._strict); +// if we didn't get a weekday name, mark the date as invalid +null!=e?b.d=e:l(c).invalidWeekday=a}),ba(["d","e","E"],function(a,b,c,d){b[d]=t(a)}); +// LOCALES +var me="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),ne="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),oe="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),pe=Vd,qe=Vd,re=Vd;T("H",["HH",2],0,"hour"),T("h",["hh",2],0,Qa),T("k",["kk",2],0,Ra),T("hmm",0,0,function(){return""+Qa.apply(this)+S(this.minutes(),2)}),T("hmmss",0,0,function(){return""+Qa.apply(this)+S(this.minutes(),2)+S(this.seconds(),2)}),T("Hmm",0,0,function(){return""+this.hours()+S(this.minutes(),2)}),T("Hmmss",0,0,function(){return""+this.hours()+S(this.minutes(),2)+S(this.seconds(),2)}),Sa("a",!0),Sa("A",!1), +// ALIASES +I("hour","h"), +// PRIORITY +L("hour",13),Y("a",Ta),Y("A",Ta),Y("H",Kd),Y("h",Kd),Y("HH",Kd,Gd),Y("hh",Kd,Gd),Y("hmm",Ld),Y("hmmss",Md),Y("Hmm",Ld),Y("Hmmss",Md),aa(["H","HH"],_d),aa(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),aa(["h","hh"],function(a,b,c){b[_d]=t(a),l(c).bigHour=!0}),aa("hmm",function(a,b,c){var d=a.length-2;b[_d]=t(a.substr(0,d)),b[ae]=t(a.substr(d)),l(c).bigHour=!0}),aa("hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[_d]=t(a.substr(0,d)),b[ae]=t(a.substr(d,2)),b[be]=t(a.substr(e)),l(c).bigHour=!0}),aa("Hmm",function(a,b,c){var d=a.length-2;b[_d]=t(a.substr(0,d)),b[ae]=t(a.substr(d))}),aa("Hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[_d]=t(a.substr(0,d)),b[ae]=t(a.substr(d,2)),b[be]=t(a.substr(e))});var se,te=/[ap]\.?m?\.?/i,ue=N("Hours",!0),ve={calendar:td,longDateFormat:ud,invalidDate:vd,ordinal:wd,ordinalParse:xd,relativeTime:yd,months:ge,monthsShort:he,week:le,weekdays:me,weekdaysMin:oe,weekdaysShort:ne,meridiemParse:te},we={},xe=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/,ye=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/,ze=/Z|[+-]\d\d(?::?\d\d)?/,Ae=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/], +// YYYYMM is NOT allowed by the standard +["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],Be=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Ce=/^\/?Date\((\-?\d+)/i;a.createFromInputFallback=w("value provided is not in a recognized ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}), +// constant that refers to the ISO standard +a.ISO_8601=function(){};var De=w("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=rb.apply(null,arguments);return this.isValid()&&a.isValid()?a<this?this:a:n()}),Ee=w("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=rb.apply(null,arguments);return this.isValid()&&a.isValid()?a>this?this:a:n()}),Fe=function(){return Date.now?Date.now():+new Date};yb("Z",":"),yb("ZZ",""), +// PARSING +Y("Z",Td),Y("ZZ",Td),aa(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=zb(Td,a)}); +// HELPERS +// timezone chunker +// '+10:00' > ['10', '00'] +// '-1530' > ['-15', '30'] +var Ge=/([\+\-]|\d\d)/gi; +// HOOKS +// This function will be called whenever a moment is mutated. +// It is intended to keep the offset in sync with the timezone. +a.updateOffset=function(){}; +// ASP.NET json date format regex +var He=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Ie=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Nb.fn=vb.prototype;var Je=Rb(1,"add"),Ke=Rb(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Le=w("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)}); +// FORMATTING +T(0,["gg",2],0,function(){return this.weekYear()%100}),T(0,["GG",2],0,function(){return this.isoWeekYear()%100}),xc("gggg","weekYear"),xc("ggggg","weekYear"),xc("GGGG","isoWeekYear"),xc("GGGGG","isoWeekYear"), +// ALIASES +I("weekYear","gg"),I("isoWeekYear","GG"), +// PRIORITY +L("weekYear",1),L("isoWeekYear",1), +// PARSING +Y("G",Rd),Y("g",Rd),Y("GG",Kd,Gd),Y("gg",Kd,Gd),Y("GGGG",Od,Id),Y("gggg",Od,Id),Y("GGGGG",Pd,Jd),Y("ggggg",Pd,Jd),ba(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=t(a)}),ba(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}), +// FORMATTING +T("Q",0,"Qo","quarter"), +// ALIASES +I("quarter","Q"), +// PRIORITY +L("quarter",7), +// PARSING +Y("Q",Fd),aa("Q",function(a,b){b[Zd]=3*(t(a)-1)}), +// FORMATTING +T("D",["DD",2],"Do","date"), +// ALIASES +I("date","D"), +// PRIOROITY +L("date",9), +// PARSING +Y("D",Kd),Y("DD",Kd,Gd),Y("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),aa(["D","DD"],$d),aa("Do",function(a,b){b[$d]=t(a.match(Kd)[0],10)}); +// MOMENTS +var Me=N("Date",!0); +// FORMATTING +T("DDD",["DDDD",3],"DDDo","dayOfYear"), +// ALIASES +I("dayOfYear","DDD"), +// PRIORITY +L("dayOfYear",4), +// PARSING +Y("DDD",Nd),Y("DDDD",Hd),aa(["DDD","DDDD"],function(a,b,c){c._dayOfYear=t(a)}), +// FORMATTING +T("m",["mm",2],0,"minute"), +// ALIASES +I("minute","m"), +// PRIORITY +L("minute",14), +// PARSING +Y("m",Kd),Y("mm",Kd,Gd),aa(["m","mm"],ae); +// MOMENTS +var Ne=N("Minutes",!1); +// FORMATTING +T("s",["ss",2],0,"second"), +// ALIASES +I("second","s"), +// PRIORITY +L("second",15), +// PARSING +Y("s",Kd),Y("ss",Kd,Gd),aa(["s","ss"],be); +// MOMENTS +var Oe=N("Seconds",!1); +// FORMATTING +T("S",0,0,function(){return~~(this.millisecond()/100)}),T(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),T(0,["SSS",3],0,"millisecond"),T(0,["SSSS",4],0,function(){return 10*this.millisecond()}),T(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),T(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),T(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),T(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),T(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}), +// ALIASES +I("millisecond","ms"), +// PRIORITY +L("millisecond",16), +// PARSING +Y("S",Nd,Fd),Y("SS",Nd,Gd),Y("SSS",Nd,Hd);var Pe;for(Pe="SSSS";Pe.length<=9;Pe+="S")Y(Pe,Qd);for(Pe="S";Pe.length<=9;Pe+="S")aa(Pe,Gc); +// MOMENTS +var Qe=N("Milliseconds",!1); +// FORMATTING +T("z",0,0,"zoneAbbr"),T("zz",0,0,"zoneName");var Re=q.prototype;Re.add=Je,Re.calendar=Ub,Re.clone=Vb,Re.diff=ac,Re.endOf=mc,Re.format=ec,Re.from=fc,Re.fromNow=gc,Re.to=hc,Re.toNow=ic,Re.get=Q,Re.invalidAt=vc,Re.isAfter=Wb,Re.isBefore=Xb,Re.isBetween=Yb,Re.isSame=Zb,Re.isSameOrAfter=$b,Re.isSameOrBefore=_b,Re.isValid=tc,Re.lang=Le,Re.locale=jc,Re.localeData=kc,Re.max=Ee,Re.min=De,Re.parsingFlags=uc,Re.set=R,Re.startOf=lc,Re.subtract=Ke,Re.toArray=qc,Re.toObject=rc,Re.toDate=pc,Re.toISOString=dc,Re.toJSON=sc,Re.toString=cc,Re.unix=oc,Re.valueOf=nc,Re.creationData=wc, +// Year +Re.year=ke,Re.isLeapYear=qa, +// Week Year +Re.weekYear=yc,Re.isoWeekYear=zc, +// Quarter +Re.quarter=Re.quarters=Ec, +// Month +Re.month=ja,Re.daysInMonth=ka, +// Week +Re.week=Re.weeks=Aa,Re.isoWeek=Re.isoWeeks=Ba,Re.weeksInYear=Bc,Re.isoWeeksInYear=Ac, +// Day +Re.date=Me,Re.day=Re.days=Ja,Re.weekday=Ka,Re.isoWeekday=La,Re.dayOfYear=Fc, +// Hour +Re.hour=Re.hours=ue, +// Minute +Re.minute=Re.minutes=Ne, +// Second +Re.second=Re.seconds=Oe, +// Millisecond +Re.millisecond=Re.milliseconds=Qe, +// Offset +Re.utcOffset=Cb,Re.utc=Eb,Re.local=Fb,Re.parseZone=Gb,Re.hasAlignedHourOffset=Hb,Re.isDST=Ib,Re.isLocal=Kb,Re.isUtcOffset=Lb,Re.isUtc=Mb,Re.isUTC=Mb, +// Timezone +Re.zoneAbbr=Hc,Re.zoneName=Ic, +// Deprecations +Re.dates=w("dates accessor is deprecated. Use date instead.",Me),Re.months=w("months accessor is deprecated. Use month instead",ja),Re.years=w("years accessor is deprecated. Use year instead",ke),Re.zone=w("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",Db),Re.isDSTShifted=w("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",Jb);var Se=Re,Te=B.prototype;Te.calendar=C,Te.longDateFormat=D,Te.invalidDate=E,Te.ordinal=F,Te.preparse=Lc,Te.postformat=Lc,Te.relativeTime=G,Te.pastFuture=H,Te.set=z, +// Month +Te.months=ea,Te.monthsShort=fa,Te.monthsParse=ha,Te.monthsRegex=ma,Te.monthsShortRegex=la, +// Week +Te.week=xa,Te.firstDayOfYear=za,Te.firstDayOfWeek=ya, +// Day of Week +Te.weekdays=Ea,Te.weekdaysMin=Ga,Te.weekdaysShort=Fa,Te.weekdaysParse=Ia,Te.weekdaysRegex=Ma,Te.weekdaysShortRegex=Na,Te.weekdaysMinRegex=Oa, +// Hours +Te.isPM=Ua,Te.meridiem=Va,Za("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===t(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}), +// Side effect imports +a.lang=w("moment.lang is deprecated. Use moment.locale instead.",Za),a.langData=w("moment.langData is deprecated. Use moment.localeData instead.",ab);var Ue=Math.abs,Ve=cd("ms"),We=cd("s"),Xe=cd("m"),Ye=cd("h"),Ze=cd("d"),$e=cd("w"),_e=cd("M"),af=cd("y"),bf=ed("milliseconds"),cf=ed("seconds"),df=ed("minutes"),ef=ed("hours"),ff=ed("days"),gf=ed("months"),hf=ed("years"),jf=Math.round,kf={s:45,// seconds to minute +m:45,// minutes to hour +h:22,// hours to day +d:26,// days to month +M:11},lf=Math.abs,mf=vb.prototype;mf.abs=Uc,mf.add=Wc,mf.subtract=Xc,mf.as=ad,mf.asMilliseconds=Ve,mf.asSeconds=We,mf.asMinutes=Xe,mf.asHours=Ye,mf.asDays=Ze,mf.asWeeks=$e,mf.asMonths=_e,mf.asYears=af,mf.valueOf=bd,mf._bubble=Zc,mf.get=dd,mf.milliseconds=bf,mf.seconds=cf,mf.minutes=df,mf.hours=ef,mf.days=ff,mf.weeks=fd,mf.months=gf,mf.years=hf,mf.humanize=kd,mf.toISOString=ld,mf.toString=ld,mf.toJSON=ld,mf.locale=jc,mf.localeData=kc, +// Deprecations +mf.toIsoString=w("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",ld),mf.lang=Le, +// Side effect imports +// FORMATTING +T("X",0,0,"unix"),T("x",0,0,"valueOf"), +// PARSING +Y("x",Rd),Y("X",Ud),aa("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),aa("x",function(a,b,c){c._d=new Date(t(a))}), +// Side effect imports +a.version="2.15.2",b(rb),a.fn=Se,a.min=tb,a.max=ub,a.now=Fe,a.utc=j,a.unix=Jc,a.months=Pc,a.isDate=f,a.locale=Za,a.invalid=n,a.duration=Nb,a.isMoment=r,a.weekdays=Rc,a.parseZone=Kc,a.localeData=ab,a.isDuration=wb,a.monthsShort=Qc,a.weekdaysMin=Tc,a.defineLocale=$a,a.updateLocale=_a,a.locales=bb,a.weekdaysShort=Sc,a.normalizeUnits=J,a.relativeTimeRounding=id,a.relativeTimeThreshold=jd,a.calendarFormat=Tb,a.prototype=Se;var nf=a;return nf}); \ No newline at end of file
diff --git go-ethereum/docs/static/styles/bootstrap.min.css celo/docs/static/styles/bootstrap.min.css new file mode 100644 index 0000000000000000000000000000000000000000..ed3905e0e0c91d4ed7d8aa14412dffeb038745ff --- /dev/null +++ celo/docs/static/styles/bootstrap.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file
diff --git go-ethereum/docs/static/styles/custom/common.css celo/docs/static/styles/custom/common.css new file mode 100644 index 0000000000000000000000000000000000000000..a6140ab0c7dab545b1e95cba2c033c8fb298395a --- /dev/null +++ celo/docs/static/styles/custom/common.css @@ -0,0 +1,56 @@ +body { + padding-top: 50px; +} + +h1, h2, h3 { + margin-top: 36px; + margin-bottom: 24px; +} + +pre code { + white-space: pre; +} + +code { + color: #7b8a8b; + background-color: #ecf0f1; + overflow-x: scroll; + overflow-y: hidden; +} + +a code { + color: #18bc9c; +} + +.hljs { + background-color: inherit; +} + +.jumbotron h2 { + margin-top: 21px; + margin-bottom: 10px; +} + +.fa { + margin-right: 4px; +} + +.emoji { + width: 1.5em; + height: 1.5em; + display: inline-block; + margin-bottom: -0.25em; +} + +table { + margin-top: 16px; + margin-bottom: 16px; +} +table thead tr th, table tbody tr td { + border-bottom: 1px solid #ddd; + padding: 4px; +} + +.navbar-input-group-fixup { + margin-top: 5px; +}
diff --git go-ethereum/docs/static/styles/custom/downloads.css celo/docs/static/styles/custom/downloads.css new file mode 100644 index 0000000000000000000000000000000000000000..e80f9b9b0b0968977abab5ddd8b918d633417d20 --- /dev/null +++ celo/docs/static/styles/custom/downloads.css @@ -0,0 +1,12 @@ +.jumbotron p { + font-size: 15px; +} +.table-hover tbody tr.latest:nth-child(even) td { + background-color: #ecf0f1; +} +.table-hover tbody tr.latest:nth-child(odd) td { + background-color: #dde4e6; +} +.table-hover tbody tr.latest:hover td { + background-color: #cfd8db; +}
diff --git go-ethereum/docs/static/styles/custom/home.css celo/docs/static/styles/custom/home.css new file mode 100644 index 0000000000000000000000000000000000000000..f266eb498efc345c3d11a2270b11742714d656d7 --- /dev/null +++ celo/docs/static/styles/custom/home.css @@ -0,0 +1,44 @@ +.jumbotron { + text-align: center; + padding: 20pt 0; +} + +.jumbotron h1 { + font-size: 56px; + font-weight: bold; + margin-top: 0; +} + +.jumbotron h2 { + font-size: 28px; +} + +.mascot-mobile { + text-align: center; +} + +.hide-on-small-screen { + display: none; +} + +.featurette-divider { + margin: 80px 0; +} + +@media (min-width: 768px) { + .mascot-mobile { + display: none; + } + .hide-on-small-screen { + display: block; + } + .featurette-heading { + font-size: 50px; + } +} + +@media (min-width: 992px) { + .featurette-image { + margin-top: 30px; + } +}
diff --git go-ethereum/docs/static/styles/flatly.min.css celo/docs/static/styles/flatly.min.css new file mode 100644 index 0000000000000000000000000000000000000000..afeeb3bf58fb25c3955157b56c5844b1e6e6e763 --- /dev/null +++ celo/docs/static/styles/flatly.min.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Lato:400,700,400italic");/*! + * bootswatch v3.3.7 + * Homepage: http://bootswatch.com + * Copyright 2012-2016 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;-webkit-box-shadow:none !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:'Glyphicons Halflings';src:url('../fonts/glyphicons-halflings-regular.eot');src:url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'),url('../fonts/glyphicons-halflings-regular.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:15px;line-height:1.42857143;color:#2c3e50;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#18bc9c;text-decoration:none}a:hover,a:focus{color:#18bc9c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #ecf0f1;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:21px;margin-bottom:21px;border:0;border-top:1px solid #ecf0f1}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:400;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#b4bcc2}h1,.h1,h2,.h2,h3,.h3{margin-top:21px;margin-bottom:10.5px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10.5px;margin-bottom:10.5px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:39px}h2,.h2{font-size:32px}h3,.h3{font-size:26px}h4,.h4{font-size:19px}h5,.h5{font-size:15px}h6,.h6{font-size:13px}p{margin:0 0 10.5px}.lead{margin-bottom:21px;font-size:17px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:22.5px}}small,.small{font-size:86%}mark,.mark{background-color:#f39c12;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#b4bcc2}.text-primary{color:#2c3e50}a.text-primary:hover,a.text-primary:focus{color:#1a242f}.text-success{color:#ffffff}a.text-success:hover,a.text-success:focus{color:#e6e6e6}.text-info{color:#ffffff}a.text-info:hover,a.text-info:focus{color:#e6e6e6}.text-warning{color:#ffffff}a.text-warning:hover,a.text-warning:focus{color:#e6e6e6}.text-danger{color:#ffffff}a.text-danger:hover,a.text-danger:focus{color:#e6e6e6}.bg-primary{color:#fff;background-color:#2c3e50}a.bg-primary:hover,a.bg-primary:focus{background-color:#1a242f}.bg-success{background-color:#18bc9c}a.bg-success:hover,a.bg-success:focus{background-color:#128f76}.bg-info{background-color:#3498db}a.bg-info:hover,a.bg-info:focus{background-color:#217dbb}.bg-warning{background-color:#f39c12}a.bg-warning:hover,a.bg-warning:focus{background-color:#c87f0a}.bg-danger{background-color:#e74c3c}a.bg-danger:hover,a.bg-danger:focus{background-color:#d62c1a}.page-header{padding-bottom:9.5px;margin:42px 0 21px;border-bottom:1px solid transparent}ul,ol{margin-top:0;margin-bottom:10.5px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:21px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #b4bcc2}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10.5px 21px;margin:0 0 21px;font-size:18.75px;border-left:5px solid #ecf0f1}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#b4bcc2}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #ecf0f1;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:21px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:bold;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:10px;margin:0 0 10.5px;font-size:14px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#7b8a8b;background-color:#ecf0f1;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#b4bcc2;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:21px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ecf0f1}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ecf0f1}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ecf0f1}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ecf0f1}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ecf0f1}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#ecf0f1}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#ecf0f1}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#dde4e6}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#18bc9c}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#15a589}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#3498db}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#258cd1}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#f39c12}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#e08e0b}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#e74c3c}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#e43725}.table-responsive{overflow-x:auto;min-height:0.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15.75px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ecf0f1}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:21px;font-size:22.5px;line-height:inherit;color:#2c3e50;border:0;border-bottom:1px solid transparent}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:11px;font-size:15px;line-height:1.42857143;color:#2c3e50}.form-control{display:block;width:100%;height:45px;padding:10px 15px;font-size:15px;line-height:1.42857143;color:#2c3e50;background-color:#ffffff;background-image:none;border:1px solid #dce4ec;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#2c3e50;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(44,62,80,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(44,62,80,0.6)}.form-control::-moz-placeholder{color:#acb6c0;opacity:1}.form-control:-ms-input-placeholder{color:#acb6c0}.form-control::-webkit-input-placeholder{color:#acb6c0}.form-control::-ms-expand{border:0;background-color:transparent}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#ecf0f1;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:45px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:35px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:66px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:21px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:11px;padding-bottom:11px;margin-bottom:0;min-height:36px}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.input-sm{height:35px;padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}select.input-sm{height:35px;line-height:35px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:35px;padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:35px;line-height:35px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:35px;min-height:34px;padding:7px 9px;font-size:13px;line-height:1.5}.input-lg{height:66px;padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}select.input-lg{height:66px;line-height:66px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:66px;padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:66px;line-height:66px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:66px;min-height:40px;padding:19px 27px;font-size:19px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:56.25px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:45px;height:45px;line-height:45px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:66px;height:66px;line-height:66px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:35px;height:35px;line-height:35px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#ffffff}.has-success .form-control{border-color:#ffffff;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#e6e6e6;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-success .input-group-addon{color:#ffffff;border-color:#ffffff;background-color:#18bc9c}.has-success .form-control-feedback{color:#ffffff}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#ffffff}.has-warning .form-control{border-color:#ffffff;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#e6e6e6;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-warning .input-group-addon{color:#ffffff;border-color:#ffffff;background-color:#f39c12}.has-warning .form-control-feedback{color:#ffffff}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#ffffff}.has-error .form-control{border-color:#ffffff;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#e6e6e6;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-error .input-group-addon{color:#ffffff;border-color:#ffffff;background-color:#e74c3c}.has-error .form-control-feedback{color:#ffffff}.has-feedback label~.form-control-feedback{top:26px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#597ea2}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:11px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:32px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:11px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:19px;font-size:19px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:7px;font-size:13px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:10px 15px;font-size:15px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#95a5a6;border-color:#95a5a6}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#798d8f;border-color:#566566}.btn-default:hover{color:#ffffff;background-color:#798d8f;border-color:#74898a}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#798d8f;border-color:#74898a}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#687b7c;border-color:#566566}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#95a5a6;border-color:#95a5a6}.btn-default .badge{color:#95a5a6;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#2c3e50;border-color:#2c3e50}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#1a242f;border-color:#000000}.btn-primary:hover{color:#ffffff;background-color:#1a242f;border-color:#161f29}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#1a242f;border-color:#161f29}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#0d1318;border-color:#000000}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#2c3e50;border-color:#2c3e50}.btn-primary .badge{color:#2c3e50;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#18bc9c;border-color:#18bc9c}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#128f76;border-color:#0a4b3e}.btn-success:hover{color:#ffffff;background-color:#128f76;border-color:#11866f}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#128f76;border-color:#11866f}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#0e6f5c;border-color:#0a4b3e}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#18bc9c;border-color:#18bc9c}.btn-success .badge{color:#18bc9c;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#3498db;border-color:#3498db}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#217dbb;border-color:#16527a}.btn-info:hover{color:#ffffff;background-color:#217dbb;border-color:#2077b2}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#217dbb;border-color:#2077b2}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#1c699d;border-color:#16527a}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#3498db;border-color:#3498db}.btn-info .badge{color:#3498db;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#f39c12;border-color:#f39c12}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#c87f0a;border-color:#7f5006}.btn-warning:hover{color:#ffffff;background-color:#c87f0a;border-color:#be780a}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#c87f0a;border-color:#be780a}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#a66908;border-color:#7f5006}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f39c12;border-color:#f39c12}.btn-warning .badge{color:#f39c12;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#e74c3c;border-color:#e74c3c}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#d62c1a;border-color:#921e12}.btn-danger:hover{color:#ffffff;background-color:#d62c1a;border-color:#cd2a19}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#d62c1a;border-color:#cd2a19}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#b62516;border-color:#921e12}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#e74c3c;border-color:#e74c3c}.btn-danger .badge{color:#e74c3c;background-color:#ffffff}.btn-link{color:#18bc9c;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#18bc9c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#b4bcc2;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:13px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height, visibility;-o-transition-property:height, visibility;transition-property:height, visibility;-webkit-transition-duration:0.35s;-o-transition-duration:0.35s;transition-duration:0.35s;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:15px;text-align:left;background-color:#ffffff;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);-webkit-background-clip:padding-box;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#7b8a8b;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#ffffff;background-color:#2c3e50}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;outline:0;background-color:#2c3e50}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#b4bcc2}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:13px;line-height:1.42857143;color:#b4bcc2;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-top-left-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:66px;padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:66px;line-height:66px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:35px;padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:35px;line-height:35px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:10px 15px;font-size:15px;font-weight:normal;line-height:1;color:#2c3e50;text-align:center;background-color:#ecf0f1;border:1px solid #dce4ec;border-radius:4px}.input-group-addon.input-sm{padding:6px 9px;font-size:13px;border-radius:3px}.input-group-addon.input-lg{padding:18px 27px;font-size:19px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#ecf0f1}.nav>li.disabled>a{color:#b4bcc2}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#b4bcc2;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#ecf0f1;border-color:#18bc9c}.nav .nav-divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ecf0f1}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#ecf0f1 #ecf0f1 #ecf0f1}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#2c3e50;background-color:#ffffff;border:1px solid #ecf0f1;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ecf0f1}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ecf0f1;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#2c3e50}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ecf0f1}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ecf0f1;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:60px;margin-bottom:21px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:19.5px 15px;font-size:19px;line-height:21px;height:60px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:13px;margin-bottom:13px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:9.75px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:21px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:21px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:19.5px;padding-bottom:19.5px}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:7.5px;margin-bottom:7.5px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:7.5px;margin-bottom:7.5px}.navbar-btn.btn-sm{margin-top:12.5px;margin-bottom:12.5px}.navbar-btn.btn-xs{margin-top:19px;margin-bottom:19px}.navbar-text{margin-top:19.5px;margin-bottom:19.5px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#2c3e50;border-color:transparent}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#18bc9c;background-color:transparent}.navbar-default .navbar-text{color:#ffffff}.navbar-default .navbar-nav>li>a{color:#ffffff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#18bc9c;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#1a242f}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#1a242f}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#1a242f}.navbar-default .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#1a242f;color:#ffffff}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#18bc9c;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#1a242f}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-link{color:#ffffff}.navbar-default .navbar-link:hover{color:#18bc9c}.navbar-default .btn-link{color:#ffffff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#18bc9c}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#18bc9c;border-color:transparent}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#2c3e50;background-color:transparent}.navbar-inverse .navbar-text{color:#ffffff}.navbar-inverse .navbar-nav>li>a{color:#ffffff}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#2c3e50;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#15a589}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#128f76}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#128f76}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#149c82}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#15a589;color:#ffffff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#2c3e50;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#15a589}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-inverse .navbar-link{color:#ffffff}.navbar-inverse .navbar-link:hover{color:#2c3e50}.navbar-inverse .btn-link{color:#ffffff}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#2c3e50}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#cccccc}.breadcrumb{padding:8px 15px;margin-bottom:21px;list-style:none;background-color:#ecf0f1;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#cccccc}.breadcrumb>.active{color:#95a5a6}.pagination{display:inline-block;padding-left:0;margin:21px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:10px 15px;line-height:1.42857143;text-decoration:none;color:#ffffff;background-color:#18bc9c;border:1px solid transparent;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#ffffff;background-color:#0f7864;border-color:transparent}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;background-color:#0f7864;border-color:transparent;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#ecf0f1;background-color:#3be6c4;border-color:transparent;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:18px 27px;font-size:19px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:6px 9px;font-size:13px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:21px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#18bc9c;border:1px solid transparent;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#0f7864}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#ffffff;background-color:#18bc9c;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#95a5a6}.label-default[href]:hover,.label-default[href]:focus{background-color:#798d8f}.label-primary{background-color:#2c3e50}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#1a242f}.label-success{background-color:#18bc9c}.label-success[href]:hover,.label-success[href]:focus{background-color:#128f76}.label-info{background-color:#3498db}.label-info[href]:hover,.label-info[href]:focus{background-color:#217dbb}.label-warning{background-color:#f39c12}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#c87f0a}.label-danger{background-color:#e74c3c}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#d62c1a}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:13px;font-weight:bold;color:#ffffff;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#2c3e50;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#2c3e50;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#ecf0f1}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:23px;font-weight:200}.jumbotron>hr{border-top-color:#cfd9db}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px;padding-left:15px;padding-right:15px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:68px}}.thumbnail{display:block;padding:4px;margin-bottom:21px;line-height:1.42857143;background-color:#ffffff;border:1px solid #ecf0f1;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#18bc9c}.thumbnail .caption{padding:9px;color:#2c3e50}.alert{padding:15px;margin-bottom:21px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#18bc9c;border-color:#18bc9c;color:#ffffff}.alert-success hr{border-top-color:#15a589}.alert-success .alert-link{color:#e6e6e6}.alert-info{background-color:#3498db;border-color:#3498db;color:#ffffff}.alert-info hr{border-top-color:#258cd1}.alert-info .alert-link{color:#e6e6e6}.alert-warning{background-color:#f39c12;border-color:#f39c12;color:#ffffff}.alert-warning hr{border-top-color:#e08e0b}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{background-color:#e74c3c;border-color:#e74c3c;color:#ffffff}.alert-danger hr{border-top-color:#e43725}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:21px;margin-bottom:21px;background-color:#ecf0f1;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:13px;line-height:21px;color:#ffffff;text-align:center;background-color:#2c3e50;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#18bc9c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#3498db}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f39c12}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#e74c3c}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{zoom:1;overflow:hidden}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #ecf0f1}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{text-decoration:none;color:#555555;background-color:#ecf0f1}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{background-color:#ecf0f1;color:#b4bcc2;cursor:not-allowed}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#b4bcc2}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#2c3e50;border-color:#2c3e50}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#8aa4be}.list-group-item-success{color:#ffffff;background-color:#18bc9c}a.list-group-item-success,button.list-group-item-success{color:#ffffff}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#ffffff;background-color:#15a589}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-info{color:#ffffff;background-color:#3498db}a.list-group-item-info,button.list-group-item-info{color:#ffffff}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#ffffff;background-color:#258cd1}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-warning{color:#ffffff;background-color:#f39c12}a.list-group-item-warning,button.list-group-item-warning{color:#ffffff}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#ffffff;background-color:#e08e0b}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-danger{color:#ffffff;background-color:#e74c3c}a.list-group-item-danger,button.list-group-item-danger{color:#ffffff}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#ffffff;background-color:#e43725}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:21px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:17px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#ecf0f1;border-top:1px solid #ecf0f1;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-left:15px;padding-right:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ecf0f1}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:21px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ecf0f1}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ecf0f1}.panel-default{border-color:#ecf0f1}.panel-default>.panel-heading{color:#2c3e50;background-color:#ecf0f1;border-color:#ecf0f1}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ecf0f1}.panel-default>.panel-heading .badge{color:#ecf0f1;background-color:#2c3e50}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ecf0f1}.panel-primary{border-color:#2c3e50}.panel-primary>.panel-heading{color:#ffffff;background-color:#2c3e50;border-color:#2c3e50}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#2c3e50}.panel-primary>.panel-heading .badge{color:#2c3e50;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#2c3e50}.panel-success{border-color:#18bc9c}.panel-success>.panel-heading{color:#ffffff;background-color:#18bc9c;border-color:#18bc9c}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#18bc9c}.panel-success>.panel-heading .badge{color:#18bc9c;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#18bc9c}.panel-info{border-color:#3498db}.panel-info>.panel-heading{color:#ffffff;background-color:#3498db;border-color:#3498db}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#3498db}.panel-info>.panel-heading .badge{color:#3498db;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#3498db}.panel-warning{border-color:#f39c12}.panel-warning>.panel-heading{color:#ffffff;background-color:#f39c12;border-color:#f39c12}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f39c12}.panel-warning>.panel-heading .badge{color:#f39c12;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f39c12}.panel-danger{border-color:#e74c3c}.panel-danger>.panel-heading{color:#ffffff;background-color:#e74c3c;border-color:#e74c3c}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#e74c3c}.panel-danger>.panel-heading .badge{color:#e74c3c;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#e74c3c}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;left:0;bottom:0;height:100%;width:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#ecf0f1;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:22.5px;font-weight:bold;line-height:1;color:#000000;text-shadow:none;opacity:0.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;opacity:0.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);-webkit-background-clip:padding-box;background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:0.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:13px;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:0.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{bottom:0;right:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:15px;background-color:#ffffff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:15px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#ffffff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#ffffff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#ffffff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:0.5;filter:alpha(opacity=50);font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0)}.carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:-webkit-gradient(linear, left top, right top, from(rgba(0,0,0,0.5)), to(rgba(0,0,0,0.0001)));background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:-webkit-gradient(linear, left top, right top, from(rgba(0,0,0,0.0001)), to(rgba(0,0,0,0.5)));background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;margin-top:-10px;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;line-height:1;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #ffffff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#ffffff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{border-width:0}.navbar-default .badge{background-color:#fff;color:#2c3e50}.navbar-inverse .badge{background-color:#fff;color:#18bc9c}.navbar-brand{line-height:1}.btn{border-width:2px}.btn:active{-webkit-box-shadow:none;box-shadow:none}.btn-group.open .dropdown-toggle{-webkit-box-shadow:none;box-shadow:none}.text-primary,.text-primary:hover{color:#2c3e50}.text-success,.text-success:hover{color:#18bc9c}.text-danger,.text-danger:hover{color:#e74c3c}.text-warning,.text-warning:hover{color:#f39c12}.text-info,.text-info:hover{color:#3498db}table a:not(.btn),.table a:not(.btn){text-decoration:underline}table .dropdown-menu a,.table .dropdown-menu a{text-decoration:none}table .success,.table .success,table .warning,.table .warning,table .danger,.table .danger,table .info,.table .info{color:#fff}table .success>th>a,.table .success>th>a,table .warning>th>a,.table .warning>th>a,table .danger>th>a,.table .danger>th>a,table .info>th>a,.table .info>th>a,table .success>td>a,.table .success>td>a,table .warning>td>a,.table .warning>td>a,table .danger>td>a,.table .danger>td>a,table .info>td>a,.table .info>td>a,table .success>a,.table .success>a,table .warning>a,.table .warning>a,table .danger>a,.table .danger>a,table .info>a,.table .info>a{color:#fff}table>thead>tr>th,.table>thead>tr>th,table>tbody>tr>th,.table>tbody>tr>th,table>tfoot>tr>th,.table>tfoot>tr>th,table>thead>tr>td,.table>thead>tr>td,table>tbody>tr>td,.table>tbody>tr>td,table>tfoot>tr>td,.table>tfoot>tr>td{border:none}table-bordered>thead>tr>th,.table-bordered>thead>tr>th,table-bordered>tbody>tr>th,.table-bordered>tbody>tr>th,table-bordered>tfoot>tr>th,.table-bordered>tfoot>tr>th,table-bordered>thead>tr>td,.table-bordered>thead>tr>td,table-bordered>tbody>tr>td,.table-bordered>tbody>tr>td,table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ecf0f1}.form-control,input{border-width:2px;-webkit-box-shadow:none;box-shadow:none}.form-control:focus,input:focus{-webkit-box-shadow:none;box-shadow:none}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#f39c12}.has-warning .form-control,.has-warning .form-control:focus{border:2px solid #f39c12}.has-warning .input-group-addon{border-color:#f39c12}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#e74c3c}.has-error .form-control,.has-error .form-control:focus{border:2px solid #e74c3c}.has-error .input-group-addon{border-color:#e74c3c}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#18bc9c}.has-success .form-control,.has-success .form-control:focus{border:2px solid #18bc9c}.has-success .input-group-addon{border-color:#18bc9c}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:transparent}.pager a,.pager a:hover{color:#fff}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{background-color:#3be6c4}.close{color:#fff;text-decoration:none;opacity:0.4}.close:hover,.close:focus{color:#fff;opacity:1}.alert .alert-link{color:#fff;text-decoration:underline}.progress{height:10px;-webkit-box-shadow:none;box-shadow:none}.progress .progress-bar{font-size:10px;line-height:10px}.well{-webkit-box-shadow:none;box-shadow:none}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{border-color:#ecf0f1}a.list-group-item-success.active{background-color:#18bc9c}a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{background-color:#15a589}a.list-group-item-warning.active{background-color:#f39c12}a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{background-color:#e08e0b}a.list-group-item-danger.active{background-color:#e74c3c}a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{background-color:#e43725}.panel-default .close{color:#2c3e50}.modal .close{color:#2c3e50}.popover{color:#2c3e50} \ No newline at end of file
diff --git go-ethereum/docs/static/styles/font-awesome.min.css celo/docs/static/styles/font-awesome.min.css new file mode 100644 index 0000000000000000000000000000000000000000..540440ce89f2a408aa699b65100e18f15e0f09ca --- /dev/null +++ celo/docs/static/styles/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
diff --git go-ethereum/docs/static/styles/highlight.min.css celo/docs/static/styles/highlight.min.css new file mode 100644 index 0000000000000000000000000000000000000000..7ba3eec2d57976a36c13b27b974c255dad8eec7e --- /dev/null +++ celo/docs/static/styles/highlight.min.css @@ -0,0 +1 @@ +.hljs{display:block;overflow-x:auto;padding:0.5em;color:#000;background:#f8f8ff}.hljs-comment,.hljs-quote{color:#408080;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-literal,.hljs-subst{color:#954121}.hljs-number{color:#40a070}.hljs-string,.hljs-doctag{color:#219161}.hljs-selector-id,.hljs-selector-class,.hljs-section,.hljs-type{color:#19469d}.hljs-params{color:#00f}.hljs-title{color:#458;font-weight:bold}.hljs-tag,.hljs-name,.hljs-attribute{color:#000080;font-weight:normal}.hljs-variable,.hljs-template-variable{color:#008080}.hljs-regexp,.hljs-link{color:#b68}.hljs-symbol,.hljs-bullet{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:bold}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} \ No newline at end of file
diff --git go-ethereum/e2e_test/ethersjs-api-check/.gitignore celo/e2e_test/ethersjs-api-check/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4175924eca078e1b0c5004a2ab52da487f41d33e --- /dev/null +++ celo/e2e_test/ethersjs-api-check/.gitignore @@ -0,0 +1,3 @@ +node_modules +dist +
diff --git go-ethereum/e2e_test/ethersjs-api-check/package-lock.json celo/e2e_test/ethersjs-api-check/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..e511c2e62370600b2f503bc3b062bf13246a4420 --- /dev/null +++ celo/e2e_test/ethersjs-api-check/package-lock.json @@ -0,0 +1,1344 @@ +{ + "name": "ethersjs-api-check", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "requires": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "requires": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "requires": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "requires": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "requires": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "requires": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "requires": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "requires": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==" + }, + "@ethersproject/networks": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.0.tgz", + "integrity": "sha512-MG6oHSQHd4ebvJrleEQQ4HhVu8Ichr0RDYEfHzsVAVjHNM+w36x9wp9r+hf1JstMXtseXDtkiVoARAG6M959AA==", + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/providers": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.0.tgz", + "integrity": "sha512-+TTrrINMzZ0aXtlwO/95uhAggKm4USLm1PbeCBR/3XZ7+Oey+3pMyddzZEyRhizHpy1HXV0FRWRMI1O3EGYibA==", + "requires": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "requires": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "requires": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "@ethersproject/web": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.0.tgz", + "integrity": "sha512-ApHcbbj+muRASVDSCl/tgxaH2LBkRMEYfLOLVa0COipx0+nlu0QKet7U2lEg0vdkh8XRSLf2nd1f1Uk9SrVSGA==", + "requires": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + }, + "@types/chai": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", + "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", + "dev": true + }, + "@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true + }, + "@types/node": { + "version": "18.7.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.17.tgz", + "integrity": "sha512-0UyfUnt02zIuqp7yC8RYtDkp/vo8bFaQ13KkSEvUAohPOAlnVNbj5Fi3fgPSuwzakS+EvvnnZ4x9y7i6ASaSPQ==", + "dev": true + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==" + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "ethers": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.0.tgz", + "integrity": "sha512-5Xhzp2ZQRi0Em+0OkOcRHxPzCfoBfgtOQA+RUylSkuHbhTEaQklnYi2hsWbRgs3ztJsXVXd9VKBcO1ScWL8YfA==", + "requires": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.0", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.0", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.0", + "@ethersproject/wordlists": "5.7.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } + } + }, + "mocha": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + } + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "typescript": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +}
diff --git go-ethereum/e2e_test/ethersjs-api-check/package.json celo/e2e_test/ethersjs-api-check/package.json new file mode 100644 index 0000000000000000000000000000000000000000..6af7981b7ba4d7c15b31f6740cf0628ecd91b323 --- /dev/null +++ celo/e2e_test/ethersjs-api-check/package.json @@ -0,0 +1,23 @@ +{ + "name": "ethersjs-api-check", + "version": "1.0.0", + "description": "runs ethers.js against an api to check compatibility", + "main": "index.js", + "scripts": { + "test": "mocha -r ts-node/register test/*.ts" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@types/chai": "^4.3.3", + "@types/mocha": "^9.1.1", + "@types/node": "^18.7.16", + "chai": "^4.3.6", + "mocha": "^10.0.0", + "typescript": "^4.8.3" + }, + "dependencies": { + "ethers": "^5.7.0", + "ts-node": "^10.9.1" + } +}
diff --git go-ethereum/e2e_test/ethersjs-api-check/tsconfig.json celo/e2e_test/ethersjs-api-check/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..1f56fffaaa623ae8a2074f13eb830d4a1b7bcd3d --- /dev/null +++ celo/e2e_test/ethersjs-api-check/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2016", + "module": "commonjs", + "esModuleInterop": true, + "strict": true, + "rootDir": ".", + "outDir": "dist", + "moduleResolution": "node", + "useUnknownInCatchVariables": true + } +}
(new)
(binary file)
diff --git go-ethereum/geth-sources.jar celo/geth-sources.jar new file mode 100644 index 0000000000000000000000000000000000000000..c9a5b96f8a029c1a73903c4bbaeed2a21a496d48 Binary files /dev/null and celo/geth-sources.jar differ
diff --git go-ethereum/go.mod celo/go.mod index 5f4330af3ea9e53325fa151921c98159b8b2e814..53523b856bc2c815989b5cbccab2811b9d0a73fc 100644 --- go-ethereum/go.mod +++ celo/go.mod @@ -12,21 +12,24 @@ github.com/aws/aws-sdk-go-v2 v1.2.0 github.com/aws/aws-sdk-go-v2/config v1.1.1 github.com/aws/aws-sdk-go-v2/credentials v1.1.1 github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1 - github.com/btcsuite/btcd v0.20.1-beta + github.com/btcsuite/btcd v0.23.2 + github.com/btcsuite/btcd/btcec/v2 v2.1.3 + github.com/btcsuite/btcd/btcutil v1.1.1 + github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72 + github.com/celo-org/celo-bls-go v0.3.4 github.com/cespare/cp v0.1.0 + github.com/cespare/xxhash/v2 v2.1.1 github.com/cloudflare/cloudflare-go v0.14.0 github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea github.com/deepmap/oapi-codegen v1.8.2 // indirect github.com/dlclark/regexp2 v1.2.0 // indirect - github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf + github.com/docker/docker v1.6.1 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 - github.com/edsrzf/mmap-go v1.0.0 github.com/fatih/color v1.7.0 github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 - github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff - github.com/go-ole/go-ole v1.2.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-sourcemap/sourcemap v2.1.2+incompatible // indirect github.com/go-stack/stack v1.8.0 github.com/golang/protobuf v1.4.3 @@ -34,7 +37,7 @@ github.com/golang/snappy v0.0.4 github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa github.com/google/uuid v1.1.5 github.com/gorilla/websocket v1.4.2 - github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29 + github.com/graph-gophers/graphql-go v1.3.0 github.com/hashicorp/go-bexpr v0.1.10 github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d github.com/holiman/bloomfilter/v2 v2.0.3 @@ -48,17 +51,19 @@ github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e github.com/julienschmidt/httprouter v1.2.0 github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 github.com/kylelemons/godebug v1.1.0 // indirect + github.com/logrusorgru/aurora v2.0.3+incompatible github.com/mattn/go-colorable v0.1.8 github.com/mattn/go-isatty v0.0.12 github.com/naoina/go-stringutil v0.1.0 // indirect github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 github.com/olekukonko/tablewriter v0.0.5 + github.com/onsi/gomega v1.10.1 github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 github.com/prometheus/tsdb v0.7.1 github.com/rjeczalik/notify v0.9.1 github.com/rs/cors v1.7.0 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible - github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 + github.com/shopspring/decimal v1.2.0 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tklauser/go-sysconf v0.3.5 // indirect @@ -67,11 +72,13 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 - golang.org/x/text v0.3.6 golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 gopkg.in/urfave/cli.v1 v1.20.0 gopkg.in/yaml.v2 v2.4.0 // indirect - gotest.tools v2.2.0+incompatible // indirect + gotest.tools v2.2.0+incompatible ) + +// Use our fork which disables bitcode +replace golang.org/x/mobile => github.com/celo-org/mobile v0.0.0-20210324213558-66ac87d7fb95
diff --git go-ethereum/go.sum celo/go.sum index 0890b8c5e04cff5152d055378e9f3d7d969b4b66..db51eed72c7383a20cd4cc700f1d3e81a40330f1 100644 --- go-ethereum/go.sum +++ celo/go.sum @@ -18,6 +18,8 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.0.0-alpha.2 h1:EWbZLqGEPSIj2W69gx04KtNVkyPIfe3uj0DhDQJonbQ= +filippo.io/edwards25519 v1.0.0-alpha.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= @@ -77,16 +79,48 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.2 h1:/YOgUp25sdCnP5ho6Hl3s0E438zlX+Kak7E6TgBgoT0= +github.com/btcsuite/btcd v0.23.2/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.1 h1:hDcDaXiP0uEzR8Biqo2weECKqEw0uHDZ9ixIWevVQqY= +github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72 h1:fUmDBbSvv1uOzo/t8WaxZMVb7BxJ8JECo5lGoR9c5bA= +github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72/go.mod h1:OEE5igu/CDjGegM1Jn6ZMo7R6LlV/JChAkjfQQIRLpg= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= +github.com/celo-org/celo-bls-go v0.3.4 h1:slNePT/gVjgUi7f8M4KTwBz/YYgv3JWU6XqyY0xKN84= +github.com/celo-org/celo-bls-go v0.3.4/go.mod h1:qDZHMC3bBqOw5qle28cRtKlEyJhslZtckcc2Tomqdks= +github.com/celo-org/celo-bls-go-android v0.3.3 h1:iZ2Gragn3JItkptmppeq1SENmNVc1f1W25UE4u231HY= +github.com/celo-org/celo-bls-go-android v0.3.3/go.mod h1:cFgtFRH8+6x5b+EyG5SqniXY3aKd03NBSGDgITscX34= +github.com/celo-org/celo-bls-go-ios v0.3.3 h1:/yHaEYft9WfXyPIGuJz7V2/r+tp2IqSpkvKsMsVgbuY= +github.com/celo-org/celo-bls-go-ios v0.3.3/go.mod h1:eaSoMpx29YV5oF7jXVChzJpNfxeZHnAa8G4PjL5CyW0= +github.com/celo-org/celo-bls-go-linux v0.3.3 h1:ukSQSIRyFCQeC1i7LJJunRKvlLuG1JMwNZ6DQZC51fE= +github.com/celo-org/celo-bls-go-linux v0.3.3/go.mod h1:DVpJadg22OrxBtMb0ub6iNVdqDBL/r6EDdWVAA0bHa0= +github.com/celo-org/celo-bls-go-macos v0.3.3 h1:H4ZGc+kS3e/w9Q6qru6FtlkYtVDS8eIQgw6UURB/Jlo= +github.com/celo-org/celo-bls-go-macos v0.3.3/go.mod h1:mYPuRqGMVxj6yZUeL6Q6ggtP52HPBS1jz+FvBPXQ7QA= +github.com/celo-org/celo-bls-go-other v0.3.3 h1:/Q9SLJK22hibPm/WI/OPxbBmgXTgUhjUgobfzz7qj/w= +github.com/celo-org/celo-bls-go-other v0.3.3/go.mod h1:tNxZNfekzyT7TdYQbyNPhkfpcYtA3KCU/IKX5FNxM/U= +github.com/celo-org/celo-bls-go-windows v0.3.3 h1:0IP+Ad9l+op50TIfkmFr+j7+TIjKksVROe+EoF7Ixa4= +github.com/celo-org/celo-bls-go-windows v0.3.3/go.mod h1:82GC5iJA9Qw5gynhYqR8ht3J+l/MO8eSNzgSTMI8UdA= +github.com/celo-org/mobile v0.0.0-20210324213558-66ac87d7fb95/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -112,6 +146,11 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0= github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= @@ -121,13 +160,11 @@ github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= -github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.6.1 h1:4xYASHy5cScPkLD7PO0uTmnVc860m9NarPN1X8zeMe8= +github.com/docker/docker v1.6.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bacAU/eSnV3dAxVpepaghAdhGoQ= github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= @@ -138,8 +175,6 @@ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= -github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -153,8 +188,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug= @@ -214,8 +249,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29 h1:sezaKhEfPFg8W0Enm61B9Gs911H8iesGY5R8NDPtd1M= -github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -251,6 +286,7 @@ github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e h1:UvSe12bq+Uj2hWd8aOlwPmoZ+CITRFrdit+sDGfAg8U= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -287,6 +323,8 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= +github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= @@ -331,6 +369,7 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= @@ -376,6 +415,8 @@ github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -384,8 +425,6 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -433,8 +472,8 @@ golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= @@ -450,13 +489,13 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -512,6 +551,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -535,7 +575,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 h1:uCLL3g5wH2xjxVREVuAbP9JM5PPKjRbXKRa6IBjkzmU= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -576,6 +615,7 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git go-ethereum/monorepo_commit celo/monorepo_commit new file mode 100644 index 0000000000000000000000000000000000000000..24626ebc9a8125e90c13817966ca15302c5c0c68 --- /dev/null +++ celo/monorepo_commit @@ -0,0 +1 @@ +f9c26529aac16e226d054fcdf51183e6efb28af8
diff --git go-ethereum/oss-fuzz.sh celo/oss-fuzz.sh index 9a24f6b1751d2d9d263d93eca6f26f14407557eb..7d0d7d1cc9da45fb326f33546070a1f69488fb78 100644 --- go-ethereum/oss-fuzz.sh +++ celo/oss-fuzz.sh @@ -60,12 +60,7 @@ cd - }   function compile_fuzzer { - # Inputs: - # $1: The package to fuzz, within go-ethereum - # $2: The name of the fuzzing function - # $3: The name to give to the final fuzzing-binary - - path=$GOPATH/src/github.com/ethereum/go-ethereum/$1 + path=$SRC/celo-blockchain/$1 func=$2 fuzzer=$3
diff --git go-ethereum/scripts/check_imports.sh celo/scripts/check_imports.sh new file mode 100755 index 0000000000000000000000000000000000000000..052d8612e30349ce72688fa5060ba4589aa3192d --- /dev/null +++ celo/scripts/check_imports.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +grep --exclude-dir=compiled-system-contracts --files-with-matches "[^https://]github.com/ethereum/go-ethereum" --recursive . --include="*.go" +if [ "$?" -gt "0" ]; then + exit 0 +else + echo The above files reference "github.com/ethereum/go-ethereum" instead of "github.com/ethereum/go-ethereum" + echo Run ./scripts/rename_imports.sh to fix. + exit 1 +fi
diff --git go-ethereum/scripts/hooks/pre-commit celo/scripts/hooks/pre-commit new file mode 100755 index 0000000000000000000000000000000000000000..7c119aad917c38439db8624f7aa0ff3b12fabd25 --- /dev/null +++ celo/scripts/hooks/pre-commit @@ -0,0 +1,57 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=$(git hash-object -t tree /dev/null) +fi + +# If you want to allow non-ASCII filenames set this variable to true. +allownonascii=$(git config --bool hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ASCII filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF + exit 1 +fi + +# Check imports +grep --exclude-dir=compiled-system-contracts --files-with-matches "[^https://]github.com/ethereum/go-ethereum" --recursive . --include="*.go" +if [ "$?" -gt "0" ]; then + exit 0 +else + echo The above files reference "github.com/ethereum/go-ethereum" instead of "github.com/ethereum/go-ethereum" + echo "Run ./scripts/rename_imports.sh to fix." + echo "Use --no-verify (-n) to commit anyways." + exit 1 +fi
diff --git go-ethereum/scripts/hooks/pre-merge-commit celo/scripts/hooks/pre-merge-commit new file mode 100755 index 0000000000000000000000000000000000000000..399eab1924e39da570b389b0bef1ca713b3b05c3 --- /dev/null +++ celo/scripts/hooks/pre-merge-commit @@ -0,0 +1,13 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git merge" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message to +# stderr if it wants to stop the merge commit. +# +# To enable this hook, rename this file to "pre-merge-commit". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" +:
diff --git go-ethereum/scripts/publish_private_docker_image.sh celo/scripts/publish_private_docker_image.sh new file mode 100755 index 0000000000000000000000000000000000000000..44878b05691272bb1af7a056a16b33f49cb6ec3b --- /dev/null +++ celo/scripts/publish_private_docker_image.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +COMMIT_SHA=$(git rev-list HEAD | head -n 1) + +docker build --build-arg COMMIT_SHA="$COMMIT_SHA" -t gcr.io/celo-testnet/geth:"$COMMIT_SHA" . +docker build --build-arg COMMIT_SHA="$COMMIT_SHA" -t gcr.io/celo-testnet/geth-all:"$COMMIT_SHA" -f Dockerfile.alltools . +docker push gcr.io/celo-testnet/geth:"$COMMIT_SHA" +docker push gcr.io/celo-testnet/geth-all:"$COMMIT_SHA"
diff --git go-ethereum/scripts/rename_imports.sh celo/scripts/rename_imports.sh new file mode 100755 index 0000000000000000000000000000000000000000..6fae355d037984d132cbddf174c5f4dad65f1108 --- /dev/null +++ celo/scripts/rename_imports.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +find . \ + -not -path "./compiled-system-contracts/*" \ + -type f \ + -name '*.go' \ + -exec sed -i "" "/https\:\/\//! s|github.com/ethereum/go-ethereum|github.com/ethereum/go-ethereum|" {} \;
diff --git go-ethereum/scripts/run_geth_in_docker.sh celo/scripts/run_geth_in_docker.sh new file mode 100755 index 0000000000000000000000000000000000000000..2e16e3fde7c65385345b167575296574fa3115c9 --- /dev/null +++ celo/scripts/run_geth_in_docker.sh @@ -0,0 +1,5 @@ +# Prevent error related to running geth without a tty +# 150 is an arbitrary choice, which seems good enough +stty cols 150 +sleep 1 +exec geth "$@"
diff --git go-ethereum/scripts/setup_git_hooks.sh celo/scripts/setup_git_hooks.sh new file mode 100755 index 0000000000000000000000000000000000000000..fe2adcd87ae339d3c036baead974c89d01e94e12 --- /dev/null +++ celo/scripts/setup_git_hooks.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +cp ./scripts/hooks/* .git/hooks
diff --git go-ethereum/scripts/sync_test.sh celo/scripts/sync_test.sh new file mode 100755 index 0000000000000000000000000000000000000000..f0ebb0c9df99fd57b423e9d020aa0df50ecdfce3 --- /dev/null +++ celo/scripts/sync_test.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# We consider a good sync if the latest block synced is OLDEST_ACCEPTABLE seconds old +OLDEST_ACCEPTABLE=30 + +if [ -z "$DATADIR" ]; then + echo "Set DATADIR to the desired datadir folder" + exit 3 +fi + +if [ -z "$MODE" ]; then + echo "Set MODE to the sync mode" + exit 3 +fi + +MARK=`date +%s` +# Do the sync +echo "Running geth sync" +echo "-----------------" +build/bin/geth --datadir $DATADIR --syncmode $MODE --exitwhensynced + +echo "------------------------------------" +echo "Geth exited, checking sync status..." +echo "------------------------------------" + +# Now check what the latest block is +ATTEMPTS=10 +RETRY_SLEEP=3 +# We attempt to check it several times since, sometimes the command +# fails with "No peers available" +for ATTEMPT in $(seq 1 $ATTEMPTS); do + echo "Attempt $ATTEMPT/$ATTEMPTS of getting the latest block timestamp" + LATEST=`build/bin/geth --datadir $DATADIR --verbosity 0 --maxpeers 0 console --syncmode $MODE --exec "parseInt(eth.getHeaderByNumber(eth.blockNumber).timestamp)"` + RESULT=$? + # If the execution returned 0, and the output is a number... + if [ $RESULT -eq 0 ] && [ $LATEST -eq $LATEST 2> /dev/null ]; then + DIFF="$(($LATEST - $MARK))" + SUM="$((DIFF + OLDEST_ACCEPTABLE))" + if [ "$SUM" -gt 0 ]; then + echo "Sync successful" + exit 0 + else + echo "Sync failed. Latest block is $DIFF seconds old" + exit 1 + fi + else + # retry + echo "Attempt $ATTEMPT failed, got: $LATEST" + sleep $RETRY_SLEEP + fi +done +echo "Failed to check the latest block after $ATTEMPTS attempts" +exit 5